1// @ts-ignore 2try { 3 self['workbox:core:7.3.0'] && _(); 4} 5catch (e) { } 6 7const fallback = (code, ...args) => { 8 let msg = code; 9 if (args.length > 0) { 10 msg += ` :: ${JSON.stringify(args)}`; 11 } 12 return msg; 13}; 14const messageGenerator = fallback ; 15 16/* 17 Copyright 2018 Google LLC 18 19 Use of this source code is governed by an MIT-style 20 license that can be found in the LICENSE file or at 21 https://opensource.org/licenses/MIT. 22*/ 23/** 24 * Workbox errors should be thrown with this class. 25 * This allows use to ensure the type easily in tests, 26 * helps developers identify errors from workbox 27 * easily and allows use to optimise error 28 * messages correctly. 29 * 30 * @private 31 */ 32class WorkboxError extends Error { 33 /** 34 * 35 * @param {string} errorCode The error code that 36 * identifies this particular error. 37 * @param {Object=} details Any relevant arguments 38 * that will help developers identify issues should 39 * be added as a key on the context object. 40 */ 41 constructor(errorCode, details) { 42 const message = messageGenerator(errorCode, details); 43 super(message); 44 this.name = errorCode; 45 this.details = details; 46 } 47} 48 49/* 50 Copyright 2018 Google LLC 51 52 Use of this source code is governed by an MIT-style 53 license that can be found in the LICENSE file or at 54 https://opensource.org/licenses/MIT. 55*/ 56const _cacheNameDetails = { 57 googleAnalytics: 'googleAnalytics', 58 precache: 'precache-v2', 59 prefix: 'workbox', 60 runtime: 'runtime', 61 suffix: typeof registration !== 'undefined' ? registration.scope : '', 62}; 63const _createCacheName = (cacheName) => { 64 return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix] 65 .filter((value) => value && value.length > 0) 66 .join('-'); 67}; 68const eachCacheNameDetail = (fn) => { 69 for (const key of Object.keys(_cacheNameDetails)) { 70 fn(key); 71 } 72}; 73const cacheNames = { 74 updateDetails: (details) => { 75 eachCacheNameDetail((key) => { 76 if (typeof details[key] === 'string') { 77 _cacheNameDetails[key] = details[key]; 78 } 79 }); 80 }, 81 getGoogleAnalyticsName: (userCacheName) => { 82 return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics); 83 }, 84 getPrecacheName: (userCacheName) => { 85 return userCacheName || _createCacheName(_cacheNameDetails.precache); 86 }, 87 getPrefix: () => { 88 return _cacheNameDetails.prefix; 89 }, 90 getRuntimeName: (userCacheName) => { 91 return userCacheName || _createCacheName(_cacheNameDetails.runtime); 92 }, 93 getSuffix: () => { 94 return _cacheNameDetails.suffix; 95 }, 96}; 97 98const logger = null ; 99 100/* 101 Copyright 2020 Google LLC 102 Use of this source code is governed by an MIT-style 103 license that can be found in the LICENSE file or at 104 https://opensource.org/licenses/MIT. 105*/ 106/** 107 * A utility method that makes it easier to use `event.waitUntil` with 108 * async functions and return the result. 109 * 110 * @param {ExtendableEvent} event 111 * @param {Function} asyncFn 112 * @return {Function} 113 * @private 114 */ 115function waitUntil(event, asyncFn) { 116 const returnPromise = asyncFn(); 117 event.waitUntil(returnPromise); 118 return returnPromise; 119} 120 121// @ts-ignore 122try { 123 self['workbox:precaching:7.3.0'] && _(); 124} 125catch (e) { } 126 127/* 128 Copyright 2018 Google LLC 129 130 Use of this source code is governed by an MIT-style 131 license that can be found in the LICENSE file or at 132 https://opensource.org/licenses/MIT. 133*/ 134// Name of the search parameter used to store revision info. 135const REVISION_SEARCH_PARAM = '__WB_REVISION__'; 136/** 137 * Converts a manifest entry into a versioned URL suitable for precaching. 138 * 139 * @param {Object|string} entry 140 * @return {string} A URL with versioning info. 141 * 142 * @private 143 * @memberof workbox-precaching 144 */ 145function createCacheKey(entry) { 146 if (!entry) { 147 throw new WorkboxError('add-to-cache-list-unexpected-type', { entry }); 148 } 149 // If a precache manifest entry is a string, it's assumed to be a versioned 150 // URL, like '/app.abcd1234.js'. Return as-is. 151 if (typeof entry === 'string') { 152 const urlObject = new URL(entry, location.href); 153 return { 154 cacheKey: urlObject.href, 155 url: urlObject.href, 156 }; 157 } 158 const { revision, url } = entry; 159 if (!url) { 160 throw new WorkboxError('add-to-cache-list-unexpected-type', { entry }); 161 } 162 // If there's just a URL and no revision, then it's also assumed to be a 163 // versioned URL. 164 if (!revision) { 165 const urlObject = new URL(url, location.href); 166 return { 167 cacheKey: urlObject.href, 168 url: urlObject.href, 169 }; 170 } 171 // Otherwise, construct a properly versioned URL using the custom Workbox 172 // search parameter along with the revision info. 173 const cacheKeyURL = new URL(url, location.href); 174 const originalURL = new URL(url, location.href); 175 cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision); 176 return { 177 cacheKey: cacheKeyURL.href, 178 url: originalURL.href, 179 }; 180} 181 182/* 183 Copyright 2020 Google LLC 184 185 Use of this source code is governed by an MIT-style 186 license that can be found in the LICENSE file or at 187 https://opensource.org/licenses/MIT.
188*/ 189/** 190 * A plugin, designed to be used with PrecacheController, to determine the 191 * of assets that were updated (or not updated) during the install event. 192 * 193 * @private 194 */ 195class PrecacheInstallReportPlugin { 196 constructor() { 197 this.updatedURLs = []; 198 this.notUpdatedURLs = []; 199 this.handlerWillStart = async ({ request, state, }) => { 200 // TODO: `state` should never be undefined... 201 if (state) { 202 state.originalRequest = request; 203 } 204 }; 205 this.cachedResponseWillBeUsed = async ({ event, state, cachedResponse, }) => { 206 if (event.type === 'install') { 207 if (state && 208 state.originalRequest && 209 state.originalRequest instanceof Request) { 210 // TODO: `state` should never be undefined... 211 const url = state.originalRequest.url; 212 if (cachedResponse) { 213 this.notUpdatedURLs.push(url); 214 } 215 else { 216 this.updatedURLs.push(url); 217 } 218 } 219 } 220 return cachedResponse; 221 }; 222 } 223} 224 225/* 226 Copyright 2020 Google LLC 227 228 Use of this source code is governed by an MIT-style 229 license that can be found in the LICENSE file or at 230 https://opensource.org/licenses/MIT. 231*/ 232/** 233 * A plugin, designed to be used with PrecacheController, to translate URLs into 234 * the corresponding cache key, based on the current revision info. 235 * 236 * @private 237 */ 238class PrecacheCacheKeyPlugin { 239 constructor({ precacheController }) { 240 this.cacheKeyWillBeUsed = async ({ request, params, }) => { 241 // Params is type any, can't change right now. 242 /* eslint-disable */ 243 const cacheKey = (params === null || params === void 0 ? void 0 : params.cacheKey) || 244 this._precacheController.getCacheKeyForURL(request.url); 245 /* eslint-enable */ 246 return cacheKey 247 ? new Request(cacheKey, { headers: request.headers }) 248 : request; 249 }; 250 this._precacheController = precacheController; 251 } 252} 253 254/* 255 Copyright 2019 Google LLC 256 257 Use of this source code is governed by an MIT-style 258 license that can be found in the LICENSE file or at 259 https://opensource.org/licenses/MIT. 260*/ 261let supportStatus; 262/** 263 * A utility function that determines whether the current browser supports 264 * constructing a new `Response` from a `response.body` stream. 265 * 266 * @return {boolean} `true`, if the current browser can successfully 267 * construct a `Response` from a `response.body` stream, `false` otherwise. 268 * 269 * @private 270 */ 271function canConstructResponseFromBodyStream() { 272 if (supportStatus === undefined) { 273 const testResponse = new Response(''); 274 if ('body' in testResponse) { 275 try { 276 new Response(testResponse.body); 277 supportStatus = true; 278 } 279 catch (error) { 280 supportStatus = false; 281 } 282 } 283 supportStatus = false; 284 } 285 return supportStatus; 286} 287 288/* 289 Copyright 2019 Google LLC 290 291 Use of this source code is governed by an MIT-style 292 license that can be found in the LICENSE file or at 293 https://opensource.org/licenses/MIT. 294*/ 295/** 296 * Allows developers to copy a response and modify its `headers`, `status`, 297 * or `statusText` values (the values settable via a 298 * [`ResponseInit`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#Syntax} 299 * object in the constructor). 300 * To modify these values, pass a function as the second argument. That 301 * function will be invoked with a single object with the response properties 302 * `{headers, status, statusText}`. The return value of this function will 303 * be used as the `ResponseInit` for the new `Response`. To change the values 304 * either modify the passed parameter(s) and return it, or return a totally 305 * new object. 306 * 307 * This method is intentionally limited to same-origin responses, regardless of 308 * whether CORS was used or not. 309 * 310 * @param {Response} response 311 * @param {Function} modifier 312 * @memberof workbox-core 313 */ 314async function copyResponse(response, modifier) { 315 let origin = null; 316 // If response.url isn't set, assume it's cross-origin and keep origin null. 317 if (response.url) { 318 const responseURL = new URL(response.url); 319 origin = responseURL.origin; 320 } 321 if (origin !== self.location.origin) { 322 throw new WorkboxError('cross-origin-copy-response', { origin }); 323 } 324 const clonedResponse = response.clone(); 325 // Create a fresh `ResponseInit` object by cloning the headers. 326 const responseInit = { 327 headers: new Headers(clonedResponse.headers), 328 status: clonedResponse.status, 329 statusText: clonedResponse.statusText, 330 }; 331 // Apply any user modifications. 332 const modifiedResponseInit = responseInit; 333 // Create the new response from the body stream and `ResponseInit`
334 // modifications. Note: not all browsers support the Response.body stream, 335 // so fall back to reading the entire body into memory as a blob. 336 const body = canConstructResponseFromBodyStream() 337 ? clonedResponse.body 338 : await clonedResponse.blob(); 339 return new Response(body, modifiedResponseInit); 340} 341 342/* 343 Copyright 2018 Google LLC 344 345 Use of this source code is governed by an MIT-style 346 license that can be found in the LICENSE file or at 347 https://opensource.org/licenses/MIT. 348*/ 349const getFriendlyURL = (url) => { 350 const urlObj = new URL(String(url), location.href); 351 // See https://github.com/GoogleChrome/workbox/issues/2323 352 // We want to include everything, except for the origin if it's same-origin. 353 return urlObj.href.replace(new RegExp(`^${location.origin}`), ''); 354}; 355 356/* 357 Copyright 2020 Google LLC 358 Use of this source code is governed by an MIT-style 359 license that can be found in the LICENSE file or at 360 https://opensource.org/licenses/MIT. 361*/ 362function stripParams(fullURL, ignoreParams) { 363 const strippedURL = new URL(fullURL); 364 for (const param of ignoreParams) { 365 strippedURL.searchParams.delete(param); 366 } 367 return strippedURL.href; 368} 369/** 370 * Matches an item in the cache, ignoring specific URL params. This is similar 371 * to the `ignoreSearch` option, but it allows you to ignore just specific 372 * params (while continuing to match on the others). 373 * 374 * @private 375 * @param {Cache} cache 376 * @param {Request} request 377 * @param {Object} matchOptions 378 * @param {Array<string>} ignoreParams 379 * @return {Promise<Response|undefined>} 380 */ 381async function cacheMatchIgnoreParams(cache, request, ignoreParams, matchOptions) { 382 const strippedRequestURL = stripParams(request.url, ignoreParams); 383 // If the request doesn't include any ignored params, match as normal. 384 if (request.url === strippedRequestURL) { 385 return cache.match(request, matchOptions); 386 } 387 // Otherwise, match by comparing keys 388 const keysOptions = Object.assign(Object.assign({}, matchOptions), { ignoreSearch: true }); 389 const cacheKeys = await cache.keys(request, keysOptions); 390 for (const cacheKey of cacheKeys) { 391 const strippedCacheKeyURL = stripParams(cacheKey.url, ignoreParams); 392 if (strippedRequestURL === strippedCacheKeyURL) { 393 return cache.match(cacheKey, matchOptions); 394 } 395 } 396 return; 397} 398 399/* 400 Copyright 2018 Google LLC 401 402 Use of this source code is governed by an MIT-style 403 license that can be found in the LICENSE file or at 404 https://opensource.org/licenses/MIT. 405*/ 406/** 407 * The Deferred class composes Promises in a way that allows for them to be 408 * resolved or rejected from outside the constructor. In most cases promises 409 * should be used directly, but Deferreds can be necessary when the logic to 410 * resolve a promise must be separate. 411 * 412 * @private 413 */ 414class Deferred { 415 /** 416 * Creates a promise and exposes its resolve and reject functions as methods. 417 */ 418 constructor() { 419 this.promise = new Promise((resolve, reject) => { 420 this.resolve = resolve; 421 this.reject = reject; 422 }); 423 } 424} 425 426/* 427 Copyright 2018 Google LLC 428 429 Use of this source code is governed by an MIT-style 430 license that can be found in the LICENSE file or at 431 https://opensource.org/licenses/MIT. 432*/ 433// Callbacks to be executed whenever there's a quota error. 434// Can't change Function type right now. 435// eslint-disable-next-line @typescript-eslint/ban-types 436const quotaErrorCallbacks = new Set(); 437 438async function executeQuotaErrorCallbacks() { 439 for (const callback of quotaErrorCallbacks) { 440 await callback(); 441 } 442} 443 444/* 445 Copyright 2019 Google LLC 446 Use of this source code is governed by an MIT-style 447 license that can be found in the LICENSE file or at 448 https://opensource.org/licenses/MIT. 449*/ 450/** 451 * Returns a promise that resolves and the passed number of milliseconds. 452 * This utility is an async/await-friendly version of `setTimeout`. 453 * 454 * @param {number} ms 455 * @return {Promise} 456 * @private 457 */ 458function timeout(ms) { 459 return new Promise((resolve) => setTimeout(resolve, ms)); 460} 461 462// @ts-ignore 463try { 464 self['workbox:strategies:7.3.0'] && _(); 465} 466catch (e) { } 467 468function toRequest(input) {
469 return typeof input === "string" ? new Request(input) : input; 470} 471class StrategyHandler { 472 /** 473 * Creates a new instance associated with the passed strategy and event 474 * that's handling the request. 475 * 476 * The constructor also initializes the state that will be passed to each of 477 * the plugins handling this request. 478 * 479 * @param {workbox-strategies.Strategy} strategy 480 * @param {Object} options 481 * @param {Request|string} options.request A request to run this strategy for. 482 * @param {ExtendableEvent} options.event The event associated with the 483 * request. 484 * @param {URL} [options.url] 485 * @param {*} [options.params] The return value from the 486 * {@link workbox-routing~matchCallback} (if applicable). 487 */ 488 constructor(strategy, options) { 489 this._cacheKeys = {}; 490 Object.assign(this, options); 491 this.event = options.event; 492 this._strategy = strategy; 493 this._handlerDeferred = new Deferred(); 494 this._extendLifetimePromises = []; 495 this._plugins = [...strategy.plugins]; 496 this._pluginStateMap = /* @__PURE__ */ new Map(); 497 for (const plugin of this._plugins) { 498 this._pluginStateMap.set(plugin, {}); 499 } 500 this.event.waitUntil(this._handlerDeferred.promise); 501 } 502 /** 503 * Fetches a given request (and invokes any applicable plugin callback 504 * methods) using the `fetchOptions` (for non-navigation requests) and 505 * `plugins` defined on the `Strategy` object. 506 * 507 * The following plugin lifecycle methods are invoked when using this method: 508 * - `requestWillFetch()` 509 * - `fetchDidSucceed()` 510 * - `fetchDidFail()` 511 * 512 * @param {Request|string} input The URL or request to fetch. 513 * @return {Promise<Response>} 514 */ 515 async fetch(input) { 516 const { event } = this; 517 let request = toRequest(input); 518 if (request.mode === "navigate" && event instanceof FetchEvent && event.preloadResponse) { 519 const possiblePreloadResponse = await event.preloadResponse; 520 if (possiblePreloadResponse) { 521 return possiblePreloadResponse; 522 } 523 } 524 const originalRequest = this.hasCallback("fetchDidFail") ? request.clone() : null; 525 try { 526 for (const cb of this.iterateCallbacks("requestWillFetch")) { 527 request = await cb({ request: request.clone(), event }); 528 } 529 } catch (err) { 530 if (err instanceof Error) { 531 throw new WorkboxError("plugin-error-request-will-fetch", { 532 thrownErrorMessage: err.message 533 }); 534 } 535 } 536 const pluginFilteredRequest = request.clone(); 537 try { 538 let fetchResponse; 539 fetchResponse = await fetch(request, request.mode === "navigate" ? void 0 : this._strategy.fetchOptions); 540 if (false) ; 541 for (const callback of this.iterateCallbacks("fetchDidSucceed")) { 542 fetchResponse = await callback({ 543 event, 544 request: pluginFilteredRequest, 545 response: fetchResponse 546 }); 547 } 548 return fetchResponse; 549 } catch (error) { 550 if (originalRequest) { 551 await this.runCallbacks("fetchDidFail", { 552 error, 553 event, 554 originalRequest: originalRequest.clone(), 555 request: pluginFilteredRequest.clone() 556 }); 557 } 558 throw error; 559 } 560 } 561 /** 562 * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on 563 * the response generated by `this.fetch()`. 564 * 565 * The call to `this.cachePut()` automatically invokes `this.waitUntil()`, 566 * so you do not have to manually call `waitUntil()` on the event. 567 * 568 * @param {Request|string} input The request or URL to fetch and cache. 569 * @return {Promise<Response>} 570 */ 571 async fetchAndCachePut(input) { 572 const response = await this.fetch(input); 573 const responseClone = response.clone(); 574 void this.waitUntil(this.cachePut(input, responseClone)); 575 return response; 576 } 577 /** 578 * Matches a request from the cache (and invokes any applicable plugin 579 * callback methods) using the `cacheName`, `matchOptions`, and `plugins` 580 * defined on the strategy object. 581 * 582 * The following plugin lifecycle methods are invoked when using this method: 583 * - cacheKeyWillBeUsed() 584 * - cachedResponseWillBeUsed() 585 * 586 * @param {Request|string} key The Request or URL to use as the cache key. 587 * @return {Promise<Response|undefined>} A matching response, if found. 588 */ 589 async cacheMatch(key) { 590 const request = toRequest(key);
591 let cachedResponse; 592 const { cacheName, matchOptions } = this._strategy; 593 const effectiveRequest = await this.getCacheKey(request, "read"); 594 const multiMatchOptions = Object.assign(Object.assign({}, matchOptions), { cacheName }); 595 cachedResponse = await caches.match(effectiveRequest, multiMatchOptions); 596 for (const callback of this.iterateCallbacks("cachedResponseWillBeUsed")) { 597 cachedResponse = await callback({ 598 cacheName, 599 matchOptions, 600 cachedResponse, 601 request: effectiveRequest, 602 event: this.event 603 }) || void 0; 604 } 605 return cachedResponse; 606 } 607 /** 608 * Puts a request/response pair in the cache (and invokes any applicable 609 * plugin callback methods) using the `cacheName` and `plugins` defined on 610 * the strategy object. 611 * 612 * The following plugin lifecycle methods are invoked when using this method: 613 * - cacheKeyWillBeUsed() 614 * - cacheWillUpdate() 615 * - cacheDidUpdate() 616 * 617 * @param {Request|string} key The request or URL to use as the cache key. 618 * @param {Response} response The response to cache. 619 * @return {Promise<boolean>} `false` if a cacheWillUpdate caused the response 620 * not be cached, and `true` otherwise. 621 */ 622 async cachePut(key, response) { 623 const request = toRequest(key); 624 await timeout(0); 625 const effectiveRequest = await this.getCacheKey(request, "write"); 626 if (!response) { 627 throw new WorkboxError("cache-put-with-no-response", { 628 url: getFriendlyURL(effectiveRequest.url) 629 }); 630 } 631 const responseToCache = await this._ensureResponseSafeToCache(response); 632 if (!responseToCache) { 633 return false; 634 } 635 const { cacheName, matchOptions } = this._strategy; 636 const cache = await self.caches.open(cacheName); 637 const hasCacheUpdateCallback = this.hasCallback("cacheDidUpdate"); 638 const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams( 639 // TODO(philipwalton): the `__WB_REVISION__` param is a precaching 640 // feature. Consider into ways to only add this behavior if using 641 // precaching. 642 cache, 643 effectiveRequest.clone(), 644 ["__WB_REVISION__"], 645 matchOptions 646 ) : null; 647 try { 648 await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache); 649 } catch (error) { 650 if (error instanceof Error) { 651 if (error.name === "QuotaExceededError") { 652 await executeQuotaErrorCallbacks(); 653 } 654 throw error; 655 } 656 } 657 for (const callback of this.iterateCallbacks("cacheDidUpdate")) { 658 await callback({ 659 cacheName, 660 oldResponse, 661 newResponse: responseToCache.clone(), 662 request: effectiveRequest, 663 event: this.event 664 }); 665 } 666 return true; 667 } 668 /** 669 * Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and 670 * executes any of those callbacks found in sequence. The final `Request` 671 * object returned by the last plugin is treated as the cache key for cache 672 * reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have 673 * been registered, the passed request is returned unmodified 674 * 675 * @param {Request} request 676 * @param {string} mode 677 * @return {Promise<Request>} 678 */ 679 async getCacheKey(request, mode) { 680 const key = `${request.url} | ${mode}`; 681 if (!this._cacheKeys[key]) { 682 let effectiveRequest = request; 683 for (const callback of this.iterateCallbacks("cacheKeyWillBeUsed")) { 684 effectiveRequest = toRequest(await callback({ 685 mode, 686 request: effectiveRequest, 687 event: this.event, 688 // params has a type any can't change right now. 689 params: this.params 690 // eslint-disable-line 691 })); 692 } 693 this._cacheKeys[key] = effectiveRequest; 694 } 695 return this._cacheKeys[key]; 696 } 697 /** 698 * Returns true if the strategy has at least one plugin with the given 699 * callback. 700 * 701 * @param {string} name The name of the callback to check for. 702 * @return {boolean} 703 */ 704 hasCallback(name) { 705 for (const plugin of this._strategy.plugins) { 706 if (name in plugin) { 707 return true; 708 } 709 } 710 return false;
711 } 712 /** 713 * Runs all plugin callbacks matching the given name, in order, passing the 714 * given param object (merged ith the current plugin state) as the only 715 * argument. 716 * 717 * Note: since this method runs all plugins, it's not suitable for cases 718 * where the return value of a callback needs to be applied prior to calling 719 * the next callback. See 720 * {@link workbox-strategies.StrategyHandler#iterateCallbacks} 721 * below for how to handle that case. 722 * 723 * @param {string} name The name of the callback to run within each plugin. 724 * @param {Object} param The object to pass as the first (and only) param 725 * when executing each callback. This object will be merged with the 726 * current plugin state prior to callback execution. 727 */ 728 async runCallbacks(name, param) { 729 for (const callback of this.iterateCallbacks(name)) { 730 await callback(param); 731 } 732 } 733 /** 734 * Accepts a callback and returns an iterable of matching plugin callbacks, 735 * where each callback is wrapped with the current handler state (i.e. when 736 * you call each callback, whatever object parameter you pass it will 737 * be merged with the plugin's current state). 738 * 739 * @param {string} name The name fo the callback to run 740 * @return {Array<Function>} 741 */ 742 *iterateCallbacks(name) { 743 for (const plugin of this._strategy.plugins) { 744 if (typeof plugin[name] === "function") { 745 const state = this._pluginStateMap.get(plugin); 746 const statefulCallback = (param) => { 747 const statefulParam = Object.assign(Object.assign({}, param), { state }); 748 return plugin[name](statefulParam); 749 }; 750 yield statefulCallback; 751 } 752 } 753 } 754 /** 755 * Adds a promise to the 756 * [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises} 757 * of the event associated with the request being handled (usually a 758 * `FetchEvent`). 759 * 760 * Note: you can await 761 * {@link workbox-strategies.StrategyHandler~doneWaiting} 762 * to know when all added promises have settled. 763 * 764 * @param {Promise} promise A promise to add to the extend lifetime promises 765 * of the event that triggered the request. 766 */ 767 waitUntil(promise) { 768 this._extendLifetimePromises.push(promise); 769 return promise; 770 } 771 /** 772 * Returns a promise that resolves once all promises passed to 773 * {@link workbox-strategies.StrategyHandler~waitUntil} 774 * have settled. 775 * 776 * Note: any work done after `doneWaiting()` settles should be manually 777 * passed to an event's `waitUntil()` method (not this handler's 778 * `waitUntil()` method), otherwise the service worker thread may be killed 779 * prior to your work completing. 780 */ 781 async doneWaiting() { 782 while (this._extendLifetimePromises.length) { 783 const promises = this._extendLifetimePromises.splice(0); 784 const result = await Promise.allSettled(promises); 785 const firstRejection = result.find((i) => i.status === "rejected"); 786 if (firstRejection) { 787 throw firstRejection.reason; 788 } 789 } 790 } 791 /** 792 * Stops running the strategy and immediately resolves any pending 793 * `waitUntil()` promises. 794 */ 795 destroy() { 796 this._handlerDeferred.resolve(null); 797 } 798 /** 799 * This method will call cacheWillUpdate on the available plugins (or use 800 * status === 200) to determine if the Response is safe and valid to cache. 801 * 802 * @param {Request} options.request 803 * @param {Response} options.response 804 * @return {Promise<Response|undefined>} 805 * 806 * @private 807 */ 808 async _ensureResponseSafeToCache(response) { 809 let responseToCache = response; 810 let pluginsUsed = false; 811 for (const callback of this.iterateCallbacks("cacheWillUpdate")) { 812 responseToCache = await callback({ 813 request: this.request, 814 response: responseToCache, 815 event: this.event 816 }) || void 0; 817 pluginsUsed = true; 818 if (!responseToCache) { 819 break; 820 } 821 } 822 if (!pluginsUsed) { 823 if (responseToCache && responseToCache.status !== 200) { 824 responseToCache = void 0; 825 } 826 } 827 return responseToCache; 828 } 829} 830 831class Strategy { 832 /** 833 * Creates a new instance of the strategy and sets all documented option 834 * properties as public instance properties. 835 * 836 * Note: if a custom strategy class extends the base Strategy class and does 837 * not need more than these properties, it does not need to define its own 838 * constructor. 839 * 840 * @param {Object} [options] 841 * @param {string} [options.cacheName] Cache name to store and retrieve 842 * requests. Defaults to the cache names provided by 843 * {@link workbox-core.cacheNames}. 844 * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} 845 * to use in conjunction with this caching strategy. 846 * @param {Object} [options.fetchOptions] Values passed along to the 847 * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) 848 * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
849 * `fetch()` requests made by this strategy. 850 * @param {Object} [options.matchOptions] The 851 * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions} 852 * for any `cache.match()` or `cache.put()` calls made by this strategy. 853 */ 854 constructor(options = {}) { 855 this.cacheName = cacheNames.getRuntimeName(options.cacheName); 856 this.plugins = options.plugins || []; 857 this.fetchOptions = options.fetchOptions; 858 this.matchOptions = options.matchOptions; 859 } 860 /** 861 * Perform a request strategy and returns a `Promise` that will resolve with 862 * a `Response`, invoking all relevant plugin callbacks. 863 * 864 * When a strategy instance is registered with a Workbox 865 * {@link workbox-routing.Route}, this method is automatically 866 * called when the route matches. 867 * 868 * Alternatively, this method can be used in a standalone `FetchEvent` 869 * listener by passing it to `event.respondWith()`. 870 * 871 * @param {FetchEvent|Object} options A `FetchEvent` or an object with the 872 * properties listed below. 873 * @param {Request|string} options.request A request to run this strategy for. 874 * @param {ExtendableEvent} options.event The event associated with the 875 * request. 876 * @param {URL} [options.url] 877 * @param {*} [options.params] 878 */ 879 handle(options) { 880 const [responseDone] = this.handleAll(options); 881 return responseDone; 882 } 883 /** 884 * Similar to {@link workbox-strategies.Strategy~handle}, but 885 * instead of just returning a `Promise` that resolves to a `Response` it 886 * it will return an tuple of `[response, done]` promises, where the former 887 * (`response`) is equivalent to what `handle()` returns, and the latter is a 888 * Promise that will resolve once any promises that were added to 889 * `event.waitUntil()` as part of performing the strategy have completed. 890 * 891 * You can await the `done` promise to ensure any extra work performed by 892 * the strategy (usually caching responses) completes successfully. 893 * 894 * @param {FetchEvent|Object} options A `FetchEvent` or an object with the 895 * properties listed below. 896 * @param {Request|string} options.request A request to run this strategy for. 897 * @param {ExtendableEvent} options.event The event associated with the 898 * request. 899 * @param {URL} [options.url] 900 * @param {*} [options.params] 901 * @return {Array<Promise>} A tuple of [response, done] 902 * promises that can be used to determine when the response resolves as 903 * well as when the handler has completed all its work. 904 */ 905 handleAll(options) { 906 if (options instanceof FetchEvent) { 907 options = { 908 event: options, 909 request: options.request 910 }; 911 } 912 const event = options.event; 913 const request = typeof options.request === "string" ? new Request(options.request) : options.request;
914 const params = "params" in options ? options.params : void 0; 915 const handler = new StrategyHandler(this, { event, request, params }); 916 const responseDone = this._getResponse(handler, request, event); 917 const handlerDone = this._awaitComplete(responseDone, handler, request, event); 918 return [responseDone, handlerDone]; 919 } 920 async _getResponse(handler, request, event) { 921 await handler.runCallbacks("handlerWillStart", { event, request }); 922 let response = void 0; 923 try { 924 response = await this._handle(request, handler); 925 if (!response || response.type === "error") { 926 throw new WorkboxError("no-response", { url: request.url }); 927 } 928 } catch (error) { 929 if (error instanceof Error) { 930 for (const callback of handler.iterateCallbacks("handlerDidError")) { 931 response = await callback({ error, event, request }); 932 if (response) { 933 break; 934 } 935 } 936 } 937 if (!response) { 938 throw error; 939 } 940 } 941 for (const callback of handler.iterateCallbacks("handlerWillRespond")) { 942 response = await callback({ event, request, response }); 943 } 944 return response; 945 } 946 async _awaitComplete(responseDone, handler, request, event) { 947 let response; 948 let error; 949 try { 950 response = await responseDone; 951 } catch (error2) { 952 } 953 try { 954 await handler.runCallbacks("handlerDidRespond", { 955 event, 956 request, 957 response 958 }); 959 await handler.doneWaiting(); 960 } catch (waitUntilError) { 961 if (waitUntilError instanceof Error) { 962 error = waitUntilError; 963 } 964 } 965 await handler.runCallbacks("handlerDidComplete", { 966 event, 967 request, 968 response, 969 error 970 }); 971 handler.destroy(); 972 if (error) { 973 throw error; 974 } 975 } 976} 977 978class PrecacheStrategy extends Strategy { 979 /** 980 * 981 * @param {Object} [options] 982 * @param {string} [options.cacheName] Cache name to store and retrieve 983 * requests. Defaults to the cache names provided by 984 * {@link workbox-core.cacheNames}. 985 * @param {Array<Object>} [options.plugins] {@link https://developers.google.com/web/tools/workbox/guides/using-plugins|Plugins} 986 * to use in conjunction with this caching strategy. 987 * @param {Object} [options.fetchOptions] Values passed along to the 988 * {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters|init} 989 * of all fetch() requests made by this strategy. 990 * @param {Object} [options.matchOptions] The 991 * {@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions|CacheQueryOptions} 992 * for any `cache.match()` or `cache.put()` calls made by this strategy. 993 * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to 994 * get the response from the network if there's a precache miss. 995 */ 996 constructor(options = {}) { 997 options.cacheName = cacheNames.getPrecacheName(options.cacheName); 998 super(options); 999 this._fallbackToNetwork = options.fallbackToNetwork === false ? false : true; 1000 this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin); 1001 } 1002 /** 1003 * @private 1004 * @param {Request|string} request A request to run this strategy for. 1005 * @param {workbox-strategies.StrategyHandler} handler The event that 1006 * triggered the request. 1007 * @return {Promise<Response>} 1008 */ 1009 async _handle(request, handler) { 1010 const response = await handler.cacheMatch(request); 1011 if (response) { 1012 return response; 1013 } 1014 if (handler.event && handler.event.type === "install") { 1015 return await this._handleInstall(request, handler); 1016 } 1017 return await this._handleFetch(request, handler); 1018 } 1019 async _handleFetch(request, handler) { 1020 let response; 1021 const params = handler.params || {}; 1022 if (this._fallbackToNetwork) { 1023 const integrityInManifest = params.integrity; 1024 const integrityInRequest = request.integrity; 1025 const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest;
1026 response = await handler.fetch(new Request(request, { 1027 integrity: request.mode !== "no-cors" ? integrityInRequest || integrityInManifest : void 0 1028 })); 1029 if (integrityInManifest && noIntegrityConflict && request.mode !== "no-cors") { 1030 this._useDefaultCacheabilityPluginIfNeeded(); 1031 await handler.cachePut(request, response.clone()); 1032 } 1033 } else { 1034 throw new WorkboxError("missing-precache-entry", { 1035 cacheName: this.cacheName, 1036 url: request.url 1037 }); 1038 } 1039 return response; 1040 } 1041 async _handleInstall(request, handler) { 1042 this._useDefaultCacheabilityPluginIfNeeded(); 1043 const response = await handler.fetch(request); 1044 const wasCached = await handler.cachePut(request, response.clone()); 1045 if (!wasCached) { 1046 throw new WorkboxError("bad-precaching-response", { 1047 url: request.url, 1048 status: response.status 1049 }); 1050 } 1051 return response; 1052 } 1053 /** 1054 * This method is complex, as there a number of things to account for: 1055 * 1056 * The `plugins` array can be set at construction, and/or it might be added to 1057 * to at any time before the strategy is used. 1058 * 1059 * At the time the strategy is used (i.e. during an `install` event), there 1060 * needs to be at least one plugin that implements `cacheWillUpdate` in the 1061 * array, other than `copyRedirectedCacheableResponsesPlugin`. 1062 * 1063 * - If this method is called and there are no suitable `cacheWillUpdate` 1064 * plugins, we need to add `defaultPrecacheCacheabilityPlugin`. 1065 * 1066 * - If this method is called and there is exactly one `cacheWillUpdate`, then 1067 * we don't have to do anything (this might be a previously added 1068 * `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin). 1069 * 1070 * - If this method is called and there is more than one `cacheWillUpdate`, 1071 * then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so, 1072 * we need to remove it. (This situation is unlikely, but it could happen if 1073 * the strategy is used multiple times, the first without a `cacheWillUpdate`, 1074 * and then later on after manually adding a custom `cacheWillUpdate`.) 1075 * 1076 * See https://github.com/GoogleChrome/workbox/issues/2737 for more context. 1077 * 1078 * @private 1079 */ 1080 _useDefaultCacheabilityPluginIfNeeded() { 1081 let defaultPluginIndex = null; 1082 let cacheWillUpdatePluginCount = 0; 1083 for (const [index, plugin] of this.plugins.entries()) { 1084 if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) { 1085 continue; 1086 } 1087 if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) { 1088 defaultPluginIndex = index; 1089 } 1090 if (plugin.cacheWillUpdate) { 1091 cacheWillUpdatePluginCount++; 1092 } 1093 } 1094 if (cacheWillUpdatePluginCount === 0) { 1095 this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin); 1096 } else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) { 1097 this.plugins.splice(defaultPluginIndex, 1); 1098 } 1099 } 1100} 1101PrecacheStrategy.defaultPrecacheCacheabilityPlugin = { 1102 async cacheWillUpdate({ response }) { 1103 if (!response || response.status >= 400) { 1104 return null; 1105 } 1106 return response; 1107 } 1108}; 1109PrecacheStrategy.copyRedirectedCacheableResponsesPlugin = { 1110 async cacheWillUpdate({ response }) { 1111 return response.redirected ? await copyResponse(response) : response; 1112 } 1113}; 1114 1115class PrecacheController { 1116 /** 1117 * Create a new PrecacheController. 1118 * 1119 * @param {Object} [options] 1120 * @param {string} [options.cacheName] The cache to use for precaching. 1121 * @param {string} [options.plugins] Plugins to use when precaching as well 1122 * as responding to fetch events for precached assets. 1123 * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to 1124 * get the response from the network if there's a precache miss. 1125 */ 1126 constructor({ cacheName, plugins = [], fallbackToNetwork = true } = {}) { 1127 this._urlsToCacheKeys = /* @__PURE__ */ new Map(); 1128 this._urlsToCacheModes = /* @__PURE__ */ new Map(); 1129 this._cacheKeysToIntegrities = /* @__PURE__ */ new Map(); 1130 this._strategy = new PrecacheStrategy({ 1131 cacheName: cacheNames.getPrecacheName(cacheName), 1132 plugins: [ 1133 ...plugins, 1134 new PrecacheCacheKeyPlugin({ precacheController: this }) 1135 ], 1136 fallbackToNetwork 1137 }); 1138 this.install = this.install.bind(this); 1139 this.activate = this.activate.bind(this); 1140 } 1141 /** 1142 * @type {workbox-precaching.PrecacheStrategy} The strategy created by this controller and 1143 * used to cache assets and respond to fetch events. 1144 */ 1145 get strategy() { 1146 return this._strategy; 1147 } 1148 /**
1149 * Adds items to the precache list, removing any duplicates and 1150 * stores the files in the 1151 * {@link workbox-core.cacheNames|"precache cache"} when the service 1152 * worker installs. 1153 * 1154 * This method can be called multiple times. 1155 * 1156 * @param {Array<Object|string>} [entries=[]] Array of entries to precache. 1157 */ 1158 precache(entries) { 1159 this.addToCacheList(entries); 1160 if (!this._installAndActiveListenersAdded) { 1161 self.addEventListener("install", this.install); 1162 self.addEventListener("activate", this.activate); 1163 this._installAndActiveListenersAdded = true; 1164 } 1165 } 1166 /** 1167 * This method will add items to the precache list, removing duplicates 1168 * and ensuring the information is valid. 1169 * 1170 * @param {Array<workbox-precaching.PrecacheController.PrecacheEntry|string>} entries 1171 * Array of entries to precache. 1172 */ 1173 addToCacheList(entries) { 1174 const urlsToWarnAbout = []; 1175 for (const entry of entries) { 1176 if (typeof entry === "string") { 1177 urlsToWarnAbout.push(entry); 1178 } else if (entry && entry.revision === void 0) { 1179 urlsToWarnAbout.push(entry.url); 1180 } 1181 const { cacheKey, url } = createCacheKey(entry); 1182 const cacheMode = typeof entry !== "string" && entry.revision ? "reload" : "default"; 1183 if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) { 1184 throw new WorkboxError("add-to-cache-list-conflicting-entries", { 1185 firstEntry: this._urlsToCacheKeys.get(url), 1186 secondEntry: cacheKey 1187 }); 1188 } 1189 if (typeof entry !== "string" && entry.integrity) { 1190 if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) { 1191 throw new WorkboxError("add-to-cache-list-conflicting-integrities", { 1192 url 1193 }); 1194 } 1195 this._cacheKeysToIntegrities.set(cacheKey, entry.integrity); 1196 } 1197 this._urlsToCacheKeys.set(url, cacheKey); 1198 this._urlsToCacheModes.set(url, cacheMode); 1199 if (urlsToWarnAbout.length > 0) { 1200 const warningMessage = `Workbox is precaching URLs without revision info: ${urlsToWarnAbout.join(", ")} 1201This is generally NOT safe. Learn more at https://bit.ly/wb-precache`; 1202 { 1203 console.warn(warningMessage); 1204 } 1205 } 1206 } 1207 } 1208 /** 1209 * Precaches new and updated assets. Call this method from the service worker 1210 * install event. 1211 * 1212 * Note: this method calls `event.waitUntil()` for you, so you do not need 1213 * to call it yourself in your event handlers. 1214 * 1215 * @param {ExtendableEvent} event 1216 * @return {Promise<workbox-precaching.InstallResult>} 1217 */ 1218 install(event) { 1219 return waitUntil(event, async () => { 1220 const installReportPlugin = new PrecacheInstallReportPlugin(); 1221 this.strategy.plugins.push(installReportPlugin); 1222 for (const [url, cacheKey] of this._urlsToCacheKeys) { 1223 const integrity = this._cacheKeysToIntegrities.get(cacheKey); 1224 const cacheMode = this._urlsToCacheModes.get(url); 1225 const request = new Request(url, { 1226 integrity, 1227 cache: cacheMode, 1228 credentials: "same-origin" 1229 }); 1230 await Promise.all(this.strategy.handleAll({ 1231 params: { cacheKey }, 1232 request, 1233 event 1234 })); 1235 } 1236 const { updatedURLs, notUpdatedURLs } = installReportPlugin; 1237 return { updatedURLs, notUpdatedURLs }; 1238 }); 1239 } 1240 /** 1241 * Deletes assets that are no longer present in the current precache manifest. 1242 * Call this method from the service worker activate event. 1243 * 1244 * Note: this method calls `event.waitUntil()` for you, so you do not need 1245 * to call it yourself in your event handlers. 1246 * 1247 * @param {ExtendableEvent} event 1248 * @return {Promise<workbox-precaching.CleanupResult>} 1249 */ 1250 activate(event) { 1251 return waitUntil(event, async () => { 1252 const cache = await self.caches.open(this.strategy.cacheName); 1253 const currentlyCachedRequests = await cache.keys(); 1254 const expectedCacheKeys = new Set(this._urlsToCacheKeys.values()); 1255 const deletedURLs = []; 1256 for (const request of currentlyCachedRequests) { 1257 if (!expectedCacheKeys.has(request.url)) { 1258 await cache.delete(request); 1259 deletedURLs.push(request.url); 1260 } 1261 } 1262 return { deletedURLs }; 1263 }); 1264 } 1265 /** 1266 * Returns a mapping of a precached URL to the corresponding cache key, taking 1267 * into account the revision information for the URL. 1268 * 1269 * @return {Map<string, string>} A URL to cache key mapping. 1270 */ 1271 getURLsToCacheKeys() { 1272 return this._urlsToCacheKeys; 1273 } 1274 /** 1275 * Returns a list of all the URLs that have been precached by the current 1276 * service worker. 1277 * 1278 * @return {Array<string>} The precached URLs.
1279 */ 1280 getCachedURLs() { 1281 return [...this._urlsToCacheKeys.keys()]; 1282 } 1283 /** 1284 * Returns the cache key used for storing a given URL. If that URL is 1285 * unversioned, like `/index.html', then the cache key will be the original 1286 * URL with a search parameter appended to it. 1287 * 1288 * @param {string} url A URL whose cache key you want to look up. 1289 * @return {string} The versioned URL that corresponds to a cache key 1290 * for the original URL, or undefined if that URL isn't precached. 1291 */ 1292 getCacheKeyForURL(url) { 1293 const urlObject = new URL(url, location.href); 1294 return this._urlsToCacheKeys.get(urlObject.href); 1295 } 1296 /** 1297 * @param {string} url A cache key whose SRI you want to look up. 1298 * @return {string} The subresource integrity associated with the cache key, 1299 * or undefined if it's not set. 1300 */ 1301 getIntegrityForCacheKey(cacheKey) { 1302 return this._cacheKeysToIntegrities.get(cacheKey); 1303 } 1304 /** 1305 * This acts as a drop-in replacement for 1306 * [`cache.match()`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match) 1307 * with the following differences: 1308 * 1309 * - It knows what the name of the precache is, and only checks in that cache. 1310 * - It allows you to pass in an "original" URL without versioning parameters, 1311 * and it will automatically look up the correct cache key for the currently 1312 * active revision of that URL. 1313 * 1314 * E.g., `matchPrecache('index.html')` will find the correct precached 1315 * response for the currently active service worker, even if the actual cache 1316 * key is `'/index.html?__WB_REVISION__=1234abcd'`. 1317 * 1318 * @param {string|Request} request The key (without revisioning parameters) 1319 * to look up in the precache. 1320 * @return {Promise<Response|undefined>} 1321 */ 1322 async matchPrecache(request) { 1323 const url = request instanceof Request ? request.url : request; 1324 const cacheKey = this.getCacheKeyForURL(url); 1325 if (cacheKey) { 1326 const cache = await self.caches.open(this.strategy.cacheName); 1327 return cache.match(cacheKey); 1328 } 1329 return void 0; 1330 } 1331 /** 1332 * Returns a function that looks up `url` in the precache (taking into 1333 * account revision information), and returns the corresponding `Response`. 1334 * 1335 * @param {string} url The precached URL which will be used to lookup the 1336 * `Response`. 1337 * @return {workbox-routing~handlerCallback} 1338 */ 1339 createHandlerBoundToURL(url) { 1340 const cacheKey = this.getCacheKeyForURL(url); 1341 if (!cacheKey) { 1342 throw new WorkboxError("non-precached-url", { url }); 1343 } 1344 return (options) => { 1345 options.request = new Request(url); 1346 options.params = Object.assign({ cacheKey }, options.params); 1347 return this.strategy.handle(options); 1348 }; 1349 } 1350} 1351 1352/* 1353 Copyright 2019 Google LLC 1354 1355 Use of this source code is governed by an MIT-style 1356 license that can be found in the LICENSE file or at 1357 https://opensource.org/licenses/MIT. 1358*/ 1359let precacheController; 1360/** 1361 * @return {PrecacheController} 1362 * @private 1363 */ 1364const getOrCreatePrecacheController = () => { 1365 if (!precacheController) { 1366 precacheController = new PrecacheController(); 1367 } 1368 return precacheController; 1369}; 1370 1371// @ts-ignore 1372try { 1373 self['workbox:routing:7.3.0'] && _(); 1374} 1375catch (e) { } 1376 1377/* 1378 Copyright 2018 Google LLC 1379 1380 Use of this source code is governed by an MIT-style 1381 license that can be found in the LICENSE file or at 1382 https://opensource.org/licenses/MIT. 1383*/ 1384/** 1385 * The default HTTP method, 'GET', used when there's no specific method 1386 * configured for a route. 1387 * 1388 * @type {string} 1389 * 1390 * @private 1391 */ 1392const defaultMethod = 'GET'; 1393 1394const normalizeHandler = (handler) => { 1395 if (handler && typeof handler === "object") { 1396 return handler; 1397 } else { 1398 return { handle: handler }; 1399 } 1400}; 1401 1402class Route { 1403 /** 1404 * Constructor for Route class. 1405 * 1406 * @param {workbox-routing~matchCallback} match 1407 * A callback function that determines whether the route matches a given 1408 * `fetch` event by returning a non-falsy value. 1409 * @param {workbox-routing~handlerCallback} handler A callback 1410 * function that returns a Promise resolving to a Response. 1411 * @param {string} [method='GET'] The HTTP method to match the Route 1412 * against. 1413 */ 1414 constructor(match, handler, method = defaultMethod) { 1415 this.handler = normalizeHandler(handler); 1416 this.match = match; 1417 this.method = method; 1418 } 1419 /** 1420 * 1421 * @param {workbox-routing-handlerCallback}
1421 handler A callback 1422 * function that returns a Promise resolving to a Response 1423 */ 1424 setCatchHandler(handler) { 1425 this.catchHandler = normalizeHandler(handler); 1426 } 1427} 1428 1429class RegExpRoute extends Route { 1430 /** 1431 * If the regular expression contains 1432 * [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references}, 1433 * the captured values will be passed to the 1434 * {@link workbox-routing~handlerCallback} `params` 1435 * argument. 1436 * 1437 * @param {RegExp} regExp The regular expression to match against URLs. 1438 * @param {workbox-routing~handlerCallback} handler A callback 1439 * function that returns a Promise resulting in a Response. 1440 * @param {string} [method='GET'] The HTTP method to match the Route 1441 * against. 1442 */ 1443 constructor(regExp, handler, method) { 1444 const match = ({ url }) => { 1445 const result = regExp.exec(url.href); 1446 if (!result) { 1447 return; 1448 } 1449 if (url.origin !== location.origin && result.index !== 0) { 1450 return; 1451 } 1452 return result.slice(1); 1453 }; 1454 super(match, handler, method); 1455 } 1456} 1457 1458class Router { 1459 /** 1460 * Initializes a new Router. 1461 */ 1462 constructor() { 1463 this._routes = /* @__PURE__ */ new Map(); 1464 this._defaultHandlerMap = /* @__PURE__ */ new Map(); 1465 } 1466 /** 1467 * @return {Map<string, Array<workbox-routing.Route>>} routes A `Map` of HTTP 1468 * method name ('GET', etc.) to an array of all the corresponding `Route` 1469 * instances that are registered. 1470 */ 1471 get routes() { 1472 return this._routes; 1473 } 1474 /** 1475 * Adds a fetch event listener to respond to events when a route matches 1476 * the event's request. 1477 */ 1478 addFetchListener() { 1479 self.addEventListener("fetch", ((event) => { 1480 const { request } = event; 1481 const responsePromise = this.handleRequest({ request, event }); 1482 if (responsePromise) { 1483 event.respondWith(responsePromise); 1484 } 1485 })); 1486 } 1487 /** 1488 * Adds a message event listener for URLs to cache from the window. 1489 * This is useful to cache resources loaded on the page prior to when the 1490 * service worker started controlling it. 1491 * 1492 * The format of the message data sent from the window should be as follows. 1493 * Where the `urlsToCache` array may consist of URL strings or an array of 1494 * URL string + `requestInit` object (the same as you'd pass to `fetch()`). 1495 * 1496 * ``` 1497 * { 1498 * type: 'CACHE_URLS', 1499 * payload: { 1500 * urlsToCache: [ 1501 * './script1.js', 1502 * './script2.js', 1503 * ['./script3.js', {mode: 'no-cors'}], 1504 * ], 1505 * }, 1506 * } 1507 * ``` 1508 */ 1509 addCacheListener() { 1510 self.addEventListener("message", ((event) => { 1511 if (event.data && event.data.type === "CACHE_URLS") { 1512 const { payload } = event.data; 1513 const requestPromises = Promise.all(payload.urlsToCache.map((entry) => { 1514 if (typeof entry === "string") { 1515 entry = [entry]; 1516 } 1517 const request = new Request(...entry); 1518 return this.handleRequest({ request, event }); 1519 })); 1520 event.waitUntil(requestPromises); 1521 if (event.ports && event.ports[0]) { 1522 void requestPromises.then(() => event.ports[0].postMessage(true)); 1523 } 1524 } 1525 })); 1526 } 1527 /** 1528 * Apply the routing rules to a FetchEvent object to get a Response from an 1529 * appropriate Route's handler. 1530 * 1531 * @param {Object} options 1532 * @param {Request} options.request The request to handle. 1533 * @param {ExtendableEvent} options.event The event that triggered the 1534 * request. 1535 * @return {Promise<Response>|undefined} A promise is returned if a 1536 * registered route can handle the request. If there is no matching 1537 * route and there's no `defaultHandler`, `undefined` is returned. 1538 */ 1539 handleRequest({ request, event }) { 1540 const url = new URL(request.url, location.href); 1541 if (!url.protocol.startsWith("http")) { 1542 return; 1543 } 1544 const sameOrigin = url.origin === location.origin; 1545 const { params, route } = this.findMatchingRoute({ 1546 event, 1547 request, 1548 sameOrigin, 1549 url 1550 }); 1551 let handler = route && route.handler; 1552 const method = request.method; 1553 if (!handler && this._defaultHandlerMap.has(method)) { 1554 handler = this._defaultHandlerMap.get(method); 1555 } 1556 if (!handler) { 1557 return; 1558 } 1559 let responsePromise; 1560 try { 1561 responsePromise = handler.handle({ url, request, event, params }); 1562 } catch (err) { 1563 responsePromise = Promise.reject(err); 1564 } 1565 const catchHandler = route && route.catchHandler; 1566 if (responsePromise instanceof Promise && (this._catchHandler || catchHandler)) { 1567 responsePromise = responsePromise.catch(async (err) => { 1568 if (catchHandler) { 1569 try { 1570 return await catchHandler.handle({ url, request, event, params }); 1571 } catch (catchErr) { 1572 if (catchErr instanceof Error) { 1573 err = catchErr; 1574 } 1575 } 1576 } 1577 if (this._catchHandler) { 1578 return this._catchHandler.handle({ url, request, event }); 1579 } 1580 throw err; 1581 }); 1582 } 1583 return responsePromise; 1584 } 1585 /** 1586 * Checks a request and URL (and optionally an event) against the list of 1587 * registered routes, and if there's a match, returns the corresponding 1588 * route along with any params generated by the match. 1589 * 1590 * @param {Object} options 1591 * @param {URL} options.url 1592 * @param {boolean} options.sameOrigin The result of comparing `url.origin` 1593 * against the current origin. 1594 * @param {Request} options.request The request to match. 1595 * @param {Event} options.event The corresponding event. 1596 * @return {Object} An object with `route` and `params` properties. 1597 * They are populated if a matching route was found or `undefined` 1598 * otherwise. 1599 */ 1600 findMatchingRoute({ url, sameOrigin, request, event }) { 1601 const routes = this._routes.get(request.method) || []; 1602 for (const route of routes) { 1603 let params; 1604 const matchResult = route.match({ url, sameOrigin, request, event }); 1605 if (matchResult) { 1606 params = matchResult; 1607 if (Array.isArray(params) && params.length === 0) { 1608 params = void 0; 1609 } else if (matchResult.constructor === Object && // eslint-disable-line 1610 Object.keys(matchResult).length === 0) { 1611 params = void 0; 1612 } else if (typeof matchResult === "boolean") { 1613 params = void 0; 1614 } 1615 return { route, params }; 1616 } 1617 } 1618 return {}; 1619 } 1620 /** 1621 * Define a default `handler` that's called when no routes explicitly 1622 * match the incoming request. 1623 * 1624 * Each HTTP method ('GET', 'POST', etc.) gets its own default handler. 1625 * 1626 * Without a default handler, unmatched requests will go against the 1627 * network as if there were no service worker present. 1628 * 1629 * @param {workbox-routing~handlerCallback}
1629 handler A callback 1630 * function that returns a Promise resulting in a Response. 1631 * @param {string} [method='GET'] The HTTP method to associate with this 1632 * default handler. Each method has its own default. 1633 */ 1634 setDefaultHandler(handler, method = defaultMethod) { 1635 this._defaultHandlerMap.set(method, normalizeHandler(handler)); 1636 } 1637 /** 1638 * If a Route throws an error while handling a request, this `handler` 1639 * will be called and given a chance to provide a response. 1640 * 1641 * @param {workbox-routing~handlerCallback} handler A callback 1642 * function that returns a Promise resulting in a Response. 1643 */ 1644 setCatchHandler(handler) { 1645 this._catchHandler = normalizeHandler(handler); 1646 } 1647 /** 1648 * Registers a route with the router. 1649 * 1650 * @param {workbox-routing.Route} route The route to register. 1651 */ 1652 registerRoute(route) { 1653 if (!this._routes.has(route.method)) { 1654 this._routes.set(route.method, []); 1655 } 1656 this._routes.get(route.method).push(route); 1657 } 1658 /** 1659 * Unregisters a route with the router. 1660 * 1661 * @param {workbox-routing.Route} route The route to unregister. 1662 */ 1663 unregisterRoute(route) { 1664 if (!this._routes.has(route.method)) { 1665 throw new WorkboxError("unregister-route-but-not-found-with-method", { 1666 method: route.method 1667 }); 1668 } 1669 const routeIndex = this._routes.get(route.method).indexOf(route); 1670 if (routeIndex > -1) { 1671 this._routes.get(route.method).splice(routeIndex, 1); 1672 } else { 1673 throw new WorkboxError("unregister-route-route-not-registered"); 1674 } 1675 } 1676} 1677 1678/* 1679 Copyright 2019 Google LLC 1680 1681 Use of this source code is governed by an MIT-style 1682 license that can be found in the LICENSE file or at 1683 https://opensource.org/licenses/MIT. 1684*/ 1685let defaultRouter; 1686/** 1687 * Creates a new, singleton Router instance if one does not exist. If one 1688 * does already exist, that instance is returned. 1689 * 1690 * @private 1691 * @return {Router} 1692 */ 1693const getOrCreateDefaultRouter = () => { 1694 if (!defaultRouter) { 1695 defaultRouter = new Router(); 1696 // The helpers that use the default Router assume these listeners exist. 1697 defaultRouter.addFetchListener(); 1698 defaultRouter.addCacheListener(); 1699 } 1700 return defaultRouter; 1701}; 1702 1703function registerRoute(capture, handler, method) { 1704 let route; 1705 if (typeof capture === "string") { 1706 const captureUrl = new URL(capture, location.href); 1707 const matchCallback = ({ url }) => { 1708 return url.href === captureUrl.href; 1709 }; 1710 route = new Route(matchCallback, handler, method); 1711 } else if (capture instanceof RegExp) { 1712 route = new RegExpRoute(capture, handler, method); 1713 } else if (typeof capture === "function") { 1714 route = new Route(capture, handler, method); 1715 } else if (capture instanceof Route) { 1716 route = capture; 1717 } else { 1718 throw new WorkboxError("unsupported-route-type", { 1719 moduleName: "workbox-routing", 1720 funcName: "registerRoute", 1721 paramName: "capture" 1722 }); 1723 } 1724 const defaultRouter = getOrCreateDefaultRouter(); 1725 defaultRouter.registerRoute(route); 1726 return route; 1727} 1728 1729/* 1730 Copyright 2018 Google LLC 1731 1732 Use of this source code is governed by an MIT-style 1733 license that can be found in the LICENSE file or at 1734 https://opensource.org/licenses/MIT. 1735*/ 1736/** 1737 * Removes any URL search parameters that should be ignored. 1738 * 1739 * @param {URL} urlObject The original URL. 1740 * @param {Array<RegExp>} ignoreURLParametersMatching RegExps to test against 1741 * each search parameter name. Matches mean that the search parameter should be 1742 * ignored. 1743 * @return {URL} The URL with any ignored search parameters removed. 1744 * 1745 * @private 1746 * @memberof workbox-precaching 1747 */ 1748function removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching = []) { 1749 // Convert the iterable into an array at the start of the loop to make sure 1750 // deletion doesn't mess up iteration. 1751 for (const paramName of [...urlObject.searchParams.keys()]) { 1752 if (ignoreURLParametersMatching.some((regExp) => regExp.test(paramName))) { 1753 urlObject.searchParams.delete(paramName); 1754 } 1755 } 1756 return urlObject; 1757} 1758
1759/* 1760 Copyright 2019 Google LLC 1761 1762 Use of this source code is governed by an MIT-style 1763 license that can be found in the LICENSE file or at 1764 https://opensource.org/licenses/MIT. 1765*/ 1766/** 1767 * Generator function that yields possible variations on the original URL to 1768 * check, one at a time. 1769 * 1770 * @param {string} url 1771 * @param {Object} options 1772 * 1773 * @private 1774 * @memberof workbox-precaching 1775 */ 1776function* generateURLVariations(url, { ignoreURLParametersMatching = [/^utm_/, /^fbclid$/], directoryIndex = 'index.html', cleanURLs = true, urlManipulation, } = {}) { 1777 const urlObject = new URL(url, location.href); 1778 urlObject.hash = ''; 1779 yield urlObject.href; 1780 const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching); 1781 yield urlWithoutIgnoredParams.href; 1782 if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) { 1783 const directoryURL = new URL(urlWithoutIgnoredParams.href); 1784 directoryURL.pathname += directoryIndex; 1785 yield directoryURL.href; 1786 } 1787 if (cleanURLs) { 1788 const cleanURL = new URL(urlWithoutIgnoredParams.href); 1789 cleanURL.pathname += '.html'; 1790 yield cleanURL.href; 1791 } 1792 if (urlManipulation) { 1793 const additionalURLs = urlManipulation({ url: urlObject }); 1794 for (const urlToAttempt of additionalURLs) { 1795 yield urlToAttempt.href; 1796 } 1797 } 1798} 1799 1800class PrecacheRoute extends Route { 1801 /** 1802 * @param {PrecacheController} precacheController A `PrecacheController` 1803 * instance used to both match requests and respond to fetch events. 1804 * @param {Object} [options] Options to control how requests are matched 1805 * against the list of precached URLs. 1806 * @param {string} [options.directoryIndex=index.html] The `directoryIndex` will 1807 * check cache entries for a URLs ending with '/' to see if there is a hit when 1808 * appending the `directoryIndex` value. 1809 * @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/, /^fbclid$/]] An 1810 * array of regex's to remove search params when looking for a cache match. 1811 * @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will 1812 * check the cache for the URL with a `.html` added to the end of the end. 1813 * @param {workbox-precaching~urlManipulation} [options.urlManipulation] 1814 * This is a function that should take a URL and return an array of 1815 * alternative URLs that should be checked for precache matches. 1816 */ 1817 constructor(precacheController, options) { 1818 const match = ({ request }) => { 1819 const urlsToCacheKeys = precacheController.getURLsToCacheKeys(); 1820 for (const possibleURL of generateURLVariations(request.url, options)) { 1821 const cacheKey = urlsToCacheKeys.get(possibleURL); 1822 if (cacheKey) { 1823 const integrity = precacheController.getIntegrityForCacheKey(cacheKey); 1824 return { cacheKey, integrity }; 1825 } 1826 } 1827 return; 1828 }; 1829 super(match, precacheController.strategy); 1830 } 1831} 1832 1833/* 1834 Copyright 2019 Google LLC 1835 Use of this source code is governed by an MIT-style 1836 license that can be found in the LICENSE file or at 1837 https://opensource.org/licenses/MIT. 1838*/ 1839/** 1840 * Add a `fetch` listener to the service worker that will 1841 * respond to 1842 * [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests} 1843 * with precached assets. 1844 * 1845 * Requests for assets that aren't precached, the `FetchEvent` will not be 1846 * responded to, allowing the event to fall through to other `fetch` event 1847 * listeners. 1848 * 1849 * @param {Object} [options] See the {@link workbox-precaching.PrecacheRoute} 1850 * options. 1851 * 1852 * @memberof workbox-precaching 1853 */ 1854function addRoute(options) { 1855 const precacheController = getOrCreatePrecacheController(); 1856 const precacheRoute = new PrecacheRoute(precacheController, options); 1857 registerRoute(precacheRoute); 1858} 1859 1860/* 1861 Copyright 2019 Google LLC 1862 1863 Use of this source code is governed by an MIT-style 1864 license that can be found in the LICENSE file or at 1865 https://opensource.org/licenses/MIT.
1866*/ 1867/** 1868 * Helper function that calls 1869 * {@link PrecacheController#matchPrecache} on the default 1870 * {@link PrecacheController} instance. 1871 * 1872 * If you are creating your own {@link PrecacheController}, then call 1873 * {@link PrecacheController#matchPrecache} on that instance, 1874 * instead of using this function. 1875 * 1876 * @param {string|Request} request The key (without revisioning parameters) 1877 * to look up in the precache. 1878 * @return {Promise<Response|undefined>} 1879 * 1880 * @memberof workbox-precaching 1881 */ 1882function matchPrecache(request) { 1883 const precacheController = getOrCreatePrecacheController(); 1884 return precacheController.matchPrecache(request); 1885} 1886 1887/* 1888 Copyright 2019 Google LLC 1889 1890 Use of this source code is governed by an MIT-style 1891 license that can be found in the LICENSE file or at 1892 https://opensource.org/licenses/MIT. 1893*/ 1894/** 1895 * Adds items to the precache list, removing any duplicates and 1896 * stores the files in the 1897 * {@link workbox-core.cacheNames|"precache cache"} when the service 1898 * worker installs. 1899 * 1900 * This method can be called multiple times. 1901 * 1902 * Please note: This method **will not** serve any of the cached files for you. 1903 * It only precaches files. To respond to a network request you call 1904 * {@link workbox-precaching.addRoute}. 1905 * 1906 * If you have a single array of files to precache, you can just call 1907 * {@link workbox-precaching.precacheAndRoute}. 1908 * 1909 * @param {Array<Object|string>} [entries=[]] Array of entries to precache. 1910 * 1911 * @memberof workbox-precaching 1912 */ 1913function precache(entries) { 1914 const precacheController = getOrCreatePrecacheController(); 1915 precacheController.precache(entries); 1916} 1917 1918/* 1919 Copyright 2019 Google LLC 1920 1921 Use of this source code is governed by an MIT-style 1922 license that can be found in the LICENSE file or at 1923 https://opensource.org/licenses/MIT. 1924*/ 1925/** 1926 * This method will add entries to the precache list and add a route to 1927 * respond to fetch events. 1928 * 1929 * This is a convenience method that will call 1930 * {@link workbox-precaching.precache} and 1931 * {@link workbox-precaching.addRoute} in a single call. 1932 * 1933 * @param {Array<Object|string>} entries Array of entries to precache. 1934 * @param {Object} [options] See the 1935 * {@link workbox-precaching.PrecacheRoute} options. 1936 * 1937 * @memberof workbox-precaching 1938 */ 1939function precacheAndRoute(entries, options) { 1940 precache(entries); 1941 addRoute(options); 1942} 1943 1944class NavigationRoute extends Route { 1945 /** 1946 * If both `denylist` and `allowlist` are provided, the `denylist` will 1947 * take precedence and the request will not match this route. 1948 * 1949 * The regular expressions in `allowlist` and `denylist` 1950 * are matched against the concatenated 1951 * [`pathname`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname} 1952 * and [`search`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/search} 1953 * portions of the requested URL. 1954 * 1955 * *Note*: These RegExps may be evaluated against every destination URL during 1956 * a navigation. Avoid using 1957 * [complex RegExps](https://github.com/GoogleChrome/workbox/issues/3077), 1958 * or else your users may see delays when navigating your site. 1959 * 1960 * @param {workbox-routing~handlerCallback} handler A callback 1961 * function that returns a Promise resulting in a Response. 1962 * @param {Object} options 1963 * @param {Array<RegExp>} [options.denylist] If any of these patterns match, 1964 * the route will not handle the request (even if a allowlist RegExp matches). 1965 * @param {Array<RegExp>} [options.allowlist=[/./]] If any of these patterns 1966 * match the URL's pathname and search parameter, the route will handle the 1967 * request (assuming the denylist doesn't match). 1968 */ 1969 constructor(handler, { allowlist = [/./], denylist = [] } = {}) { 1970 super((options) => this._match(options), handler); 1971 this._allowlist = allowlist; 1972 this._denylist = denylist; 1973 } 1974 /** 1975 * Routes match handler. 1976 * 1977 * @param {Object} options 1978 * @param {URL} options.url 1979 * @param {Request} options.request 1980 * @return {boolean} 1981 * 1982 * @private 1983 */ 1984 _match({ url, request }) { 1985 if (request && request.mode !== "navigate") { 1986 return false;
1987 } 1988 const pathnameAndSearch = url.pathname + url.search; 1989 for (const regExp of this._denylist) { 1990 if (regExp.test(pathnameAndSearch)) { 1991 return false; 1992 } 1993 } 1994 if (this._allowlist.some((regExp) => regExp.test(pathnameAndSearch))) { 1995 return true; 1996 } 1997 return false; 1998 } 1999} 2000 2001class CacheFirst extends Strategy { 2002 /** 2003 * @private 2004 * @param {Request|string} request A request to run this strategy for. 2005 * @param {workbox-strategies.StrategyHandler} handler The event that 2006 * triggered the request. 2007 * @return {Promise<Response>} 2008 */ 2009 async _handle(request, handler) { 2010 let response = await handler.cacheMatch(request); 2011 let error = void 0; 2012 if (!response) { 2013 try { 2014 response = await handler.fetchAndCachePut(request); 2015 } catch (err) { 2016 if (err instanceof Error) { 2017 error = err; 2018 } 2019 } 2020 } 2021 if (!response) { 2022 throw new WorkboxError("no-response", { url: request.url, error }); 2023 } 2024 return response; 2025 } 2026} 2027 2028/* 2029 Copyright 2018 Google LLC 2030 2031 Use of this source code is governed by an MIT-style 2032 license that can be found in the LICENSE file or at 2033 https://opensource.org/licenses/MIT. 2034*/ 2035const cacheOkAndOpaquePlugin = { 2036 /** 2037 * Returns a valid response (to allow caching) if the status is 200 (OK) or 2038 * 0 (opaque). 2039 * 2040 * @param {Object} options 2041 * @param {Response} options.response 2042 * @return {Response|null} 2043 * 2044 * @private 2045 */ 2046 cacheWillUpdate: async ({ response }) => { 2047 if (response.status === 200 || response.status === 0) { 2048 return response; 2049 } 2050 return null; 2051 }, 2052}; 2053 2054class NetworkFirst extends Strategy { 2055 /** 2056 * @param {Object} [options] 2057 * @param {string} [options.cacheName] Cache name to store and retrieve 2058 * requests. Defaults to cache names provided by 2059 * {@link workbox-core.cacheNames}. 2060 * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} 2061 * to use in conjunction with this caching strategy. 2062 * @param {Object} [options.fetchOptions] Values passed along to the 2063 * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) 2064 * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796) 2065 * `fetch()` requests made by this strategy. 2066 * @param {Object} [options.matchOptions] [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions) 2067 * @param {number} [options.networkTimeoutSeconds] If set, any network requests 2068 * that fail to respond within the timeout will fallback to the cache. 2069 * 2070 * This option can be used to combat 2071 * "[lie-fi]{@link https://developers.google.com/web/fundamentals/performance/poor-connectivity/#lie-fi}" 2072 * scenarios. 2073 */ 2074 constructor(options = {}) { 2075 super(options); 2076 if (!this.plugins.some((p) => "cacheWillUpdate" in p)) { 2077 this.plugins.unshift(cacheOkAndOpaquePlugin); 2078 } 2079 this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0; 2080 } 2081 /** 2082 * @private 2083 * @param {Request|string} request A request to run this strategy for. 2084 * @param {workbox-strategies.StrategyHandler} handler The event that 2085 * triggered the request. 2086 * @return {Promise<Response>} 2087 */ 2088 async _handle(request, handler) { 2089 const logs = []; 2090 const promises = []; 2091 let timeoutId; 2092 if (this._networkTimeoutSeconds) { 2093 const { id, promise } = this._getTimeoutPromise({ request, logs, handler }); 2094 timeoutId = id; 2095 promises.push(promise); 2096 } 2097 const networkPromise = this._getNetworkPromise({ 2098 timeoutId, 2099 request, 2100 logs, 2101 handler 2102 }); 2103 promises.push(networkPromise); 2104 const response = await handler.waitUntil((async () => { 2105 return await handler.waitUntil(Promise.race(promises)) || // If Promise.race() resolved with null, it might be due to a network 2106 // timeout + a cache miss. If that were to happen, we'd rather wait until 2107 // the networkPromise resolves instead of returning null. 2108 // Note that it's fine to await an already-resolved promise, so we don't 2109 // have to check to see if it's still "in flight". 2110 await networkPromise; 2111 })()); 2112 if (!response) { 2113 throw new WorkboxError("no-response", { url: request.url }); 2114 } 2115 return response; 2116 } 2117 /** 2118 * @param {Object} options 2119 * @param {Request} options.request 2120 * @param {Array} options.logs A reference to the logs array 2121 * @param {Event} options.event 2122 * @return {Promise<Response>} 2123 * 2124 * @private 2125 */ 2126 _getTimeoutPromise({ request, logs, handler }) { 2127 let timeoutId; 2128 const timeoutPromise = new Promise((resolve) => { 2129 const onNetworkTimeout = async () => { 2130 resolve(await handler.cacheMatch(request)); 2131 }; 2132 timeoutId = setTimeout(onNetworkTimeout, this._networkTimeoutSeconds * 1e3); 2133 }); 2134 return { 2135 promise: timeoutPromise, 2136 id: timeoutId 2137 }; 2138 } 2139 /** 2140 * @param {Object} options 2141 * @param {number|undefined} options.timeoutId 2142 * @param {Request} options.request 2143 * @param {Array} options.logs A reference to the logs Array. 2144 * @param {Event} options.event 2145 * @return {Promise<Response>} 2146 * 2147 * @private 2148 */ 2149 async _getNetworkPromise({ timeoutId, request, logs, handler }) { 2150 let error; 2151 let response; 2152 try { 2153 response = await handler.fetchAndCachePut(request); 2154 } catch (fetchError) { 2155 if (fetchError instanceof Error) { 2156 error = fetchError; 2157 } 2158 } 2159 if (timeoutId) { 2160 clearTimeout(timeoutId); 2161 } 2162 if (error || !response) { 2163 response = await handler.cacheMatch(request); 2164 } 2165 return response; 2166 } 2167} 2168 2169/* 2170 Copyright 2019 Google LLC 2171 Use of this source code is governed by an MIT-style 2172 license that can be found in the LICENSE file or at 2173 https://opensource.org/licenses/MIT.
vendor: 5,282 bytes, lines 2174-2319
2174*/ 2175/** 2176 * A helper function that prevents a promise from being flagged as unused. 2177 * 2178 * @private 2179 **/ 2180function dontWaitFor(promise) { 2181 // Effective no-op. 2182 void promise.then(() => { }); 2183} 2184 2185const instanceOfAny = (object, constructors) => constructors.some((c) => object instanceof c); 2186 2187let idbProxyableTypes; 2188let cursorAdvanceMethods; 2189// This is a function to prevent it throwing up in node environments. 2190function getIdbProxyableTypes() { 2191 return (idbProxyableTypes || 2192 (idbProxyableTypes = [ 2193 IDBDatabase, 2194 IDBObjectStore, 2195 IDBIndex, 2196 IDBCursor, 2197 IDBTransaction, 2198 ])); 2199} 2200// This is a function to prevent it throwing up in node environments. 2201function getCursorAdvanceMethods() { 2202 return (cursorAdvanceMethods || 2203 (cursorAdvanceMethods = [ 2204 IDBCursor.prototype.advance, 2205 IDBCursor.prototype.continue, 2206 IDBCursor.prototype.continuePrimaryKey, 2207 ])); 2208} 2209const cursorRequestMap = new WeakMap(); 2210const transactionDoneMap = new WeakMap(); 2211const transactionStoreNamesMap = new WeakMap(); 2212const transformCache = new WeakMap(); 2213const reverseTransformCache = new WeakMap(); 2214function promisifyRequest(request) { 2215 const promise = new Promise((resolve, reject) => { 2216 const unlisten = () => { 2217 request.removeEventListener('success', success); 2218 request.removeEventListener('error', error); 2219 }; 2220 const success = () => { 2221 resolve(wrap(request.result)); 2222 unlisten(); 2223 }; 2224 const error = () => { 2225 reject(request.error); 2226 unlisten(); 2227 }; 2228 request.addEventListener('success', success); 2229 request.addEventListener('error', error); 2230 }); 2231 promise 2232 .then((value) => { 2233 // Since cursoring reuses the IDBRequest (*sigh*), we cache it for later retrieval 2234 // (see wrapFunction). 2235 if (value instanceof IDBCursor) { 2236 cursorRequestMap.set(value, request); 2237 } 2238 // Catching to avoid "Uncaught Promise exceptions" 2239 }) 2240 .catch(() => { }); 2241 // This mapping exists in reverseTransformCache but doesn't doesn't exist in transformCache. This 2242 // is because we create many promises from a single IDBRequest. 2243 reverseTransformCache.set(promise, request); 2244 return promise; 2245} 2246function cacheDonePromiseForTransaction(tx) { 2247 // Early bail if we've already created a done promise for this transaction. 2248 if (transactionDoneMap.has(tx)) 2249 return; 2250 const done = new Promise((resolve, reject) => { 2251 const unlisten = () => { 2252 tx.removeEventListener('complete', complete); 2253 tx.removeEventListener('error', error); 2254 tx.removeEventListener('abort', error); 2255 }; 2256 const complete = () => { 2257 resolve(); 2258 unlisten(); 2259 }; 2260 const error = () => { 2261 reject(tx.error || new DOMException('AbortError', 'AbortError')); 2262 unlisten(); 2263 }; 2264 tx.addEventListener('complete', complete); 2265 tx.addEventListener('error', error); 2266 tx.addEventListener('abort', error); 2267 }); 2268 // Cache it for later retrieval. 2269 transactionDoneMap.set(tx, done); 2270} 2271let idbProxyTraps = { 2272 get(target, prop, receiver) { 2273 if (target instanceof IDBTransaction) { 2274 // Special handling for transaction.done. 2275 if (prop === 'done') 2276 return transactionDoneMap.get(target); 2277 // Polyfill for objectStoreNames because of Edge. 2278 if (prop === 'objectStoreNames') { 2279 return target.objectStoreNames || transactionStoreNamesMap.get(target); 2280 } 2281 // Make tx.store return the only store in the transaction, or undefined if there are many. 2282 if (prop === 'store') { 2283 return receiver.objectStoreNames[1] 2284 ? undefined 2285 : receiver.objectStore(receiver.objectStoreNames[0]); 2286 } 2287 } 2288 // Else transform whatever we get back. 2289 return wrap(target[prop]); 2290 }, 2291 set(target, prop, value) { 2292 target[prop] = value; 2293 return true; 2294 }, 2295 has(target, prop) { 2296 if (target instanceof IDBTransaction && 2297 (prop === 'done' || prop === 'store')) { 2298 return true; 2299 } 2300 return prop in target; 2301 }, 2302}; 2303function replaceTraps(callback) { 2304 idbProxyTraps = callback(idbProxyTraps); 2305} 2306function wrapFunction(func) { 2307 // Due to expected object equality (which is enforced by the caching in `wrap`), we 2308 // only create one new func per func. 2309 // Edge doesn't support objectStoreNames (booo), so we polyfill it here. 2310 if (func === IDBDatabase.prototype.transaction && 2311 !('objectStoreNames' in IDBTransaction.prototype)) { 2312 return function (storeNames, ...args) { 2313 const tx = func.call(unwrap(this), storeNames, ...args); 2314 transactionStoreNamesMap.set(tx, storeNames.sort ? storeNames.sort() : [storeNames]); 2315 return wrap(tx); 2316 }; 2317 } 2318 // Cursor methods are special, as the behaviour is a little more different to standard IDB. In 2319 // IDB, you advance the cursor and wait for a new 'success' on the ID
vendor: 5,931 bytes, lines 2319-2471
2319BRequest that gave you the 2320 // cursor. It's kinda like a promise that can resolve with many values. That doesn't make sense 2321 // with real promises, so each advance methods returns a new promise for the cursor object, or 2322 // undefined if the end of the cursor has been reached. 2323 if (getCursorAdvanceMethods().includes(func)) { 2324 return function (...args) { 2325 // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use 2326 // the original object. 2327 func.apply(unwrap(this), args); 2328 return wrap(cursorRequestMap.get(this)); 2329 }; 2330 } 2331 return function (...args) { 2332 // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use 2333 // the original object. 2334 return wrap(func.apply(unwrap(this), args)); 2335 }; 2336} 2337function transformCachableValue(value) { 2338 if (typeof value === 'function') 2339 return wrapFunction(value); 2340 // This doesn't return, it just creates a 'done' promise for the transaction, 2341 // which is later returned for transaction.done (see idbObjectHandler). 2342 if (value instanceof IDBTransaction) 2343 cacheDonePromiseForTransaction(value); 2344 if (instanceOfAny(value, getIdbProxyableTypes())) 2345 return new Proxy(value, idbProxyTraps); 2346 // Return the same value back if we're not going to transform it. 2347 return value; 2348} 2349function wrap(value) { 2350 // We sometimes generate multiple promises from a single IDBRequest (eg when cursoring), because 2351 // IDB is weird and a single IDBRequest can yield many responses, so these can't be cached. 2352 if (value instanceof IDBRequest) 2353 return promisifyRequest(value); 2354 // If we've already transformed this value before, reuse the transformed value. 2355 // This is faster, but it also provides object equality. 2356 if (transformCache.has(value)) 2357 return transformCache.get(value); 2358 const newValue = transformCachableValue(value); 2359 // Not all types are transformed. 2360 // These may be primitive types, so they can't be WeakMap keys. 2361 if (newValue !== value) { 2362 transformCache.set(value, newValue); 2363 reverseTransformCache.set(newValue, value); 2364 } 2365 return newValue; 2366} 2367const unwrap = (value) => reverseTransformCache.get(value); 2368 2369/** 2370 * Open a database. 2371 * 2372 * @param name Name of the database. 2373 * @param version Schema version. 2374 * @param callbacks Additional callbacks. 2375 */ 2376function openDB(name, version, { blocked, upgrade, blocking, terminated } = {}) { 2377 const request = indexedDB.open(name, version); 2378 const openPromise = wrap(request); 2379 if (upgrade) { 2380 request.addEventListener('upgradeneeded', (event) => { 2381 upgrade(wrap(request.result), event.oldVersion, event.newVersion, wrap(request.transaction), event); 2382 }); 2383 } 2384 if (blocked) { 2385 request.addEventListener('blocked', (event) => blocked( 2386 // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405 2387 event.oldVersion, event.newVersion, event)); 2388 } 2389 openPromise 2390 .then((db) => { 2391 if (terminated) 2392 db.addEventListener('close', () => terminated()); 2393 if (blocking) { 2394 db.addEventListener('versionchange', (event) => blocking(event.oldVersion, event.newVersion, event)); 2395 } 2396 }) 2397 .catch(() => { }); 2398 return openPromise; 2399} 2400/** 2401 * Delete a database. 2402 * 2403 * @param name Name of the database. 2404 */ 2405function deleteDB(name, { blocked } = {}) { 2406 const request = indexedDB.deleteDatabase(name); 2407 if (blocked) { 2408 request.addEventListener('blocked', (event) => blocked( 2409 // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405 2410 event.oldVersion, event)); 2411 } 2412 return wrap(request).then(() => undefined); 2413} 2414 2415const readMethods = ['get', 'getKey', 'getAll', 'getAllKeys', 'count']; 2416const writeMethods = ['put', 'add', 'delete', 'clear']; 2417const cachedMethods = new Map(); 2418function getMethod(target, prop) { 2419 if (!(target instanceof IDBDatabase && 2420 !(prop in target) && 2421 typeof prop === 'string')) { 2422 return; 2423 } 2424 if (cachedMethods.get(prop)) 2425 return cachedMethods.get(prop); 2426 const targetFuncName = prop.replace(/FromIndex$/, ''); 2427 const useIndex = prop !== targetFuncName; 2428 const isWrite = writeMethods.includes(targetFuncName); 2429 if ( 2430 // Bail if the target doesn't exist on the target. Eg, getAll isn't in Edge. 2431 !(targetFuncName in (useIndex ? IDBIndex : IDBObjectStore).prototype) || 2432 !(isWrite || readMethods.includes(targetFuncName))) { 2433 return; 2434 } 2435 const method = async function (storeName, ...args) { 2436 // isWrite ? 'readwrite' : undefined gzipps better, but fails in Edge :( 2437 const tx = this.transaction(storeName, isWrite ? 'readwrite' : 'readonly'); 2438 let target = tx.store; 2439 if (useIndex) 2440 target = target.index(args.shift()); 2441 // Must reject if op rejects. 2442 // If it's a write operation, must reject if tx.done rejects. 2443 // Must reject with op rejection first. 2444 // Must resolve with op value. 2445 // Must handle both promises (no unhandled rejections) 2446 return (await Promise.all([ 2447 target[targetFuncName](...args), 2448 isWrite && tx.done, 2449 ]))[0]; 2450 }; 2451 cachedMethods.set(prop, method); 2452 return method; 2453} 2454replaceTraps((oldTraps) => ({ 2455 ...oldTraps, 2456 get: (target, prop, receiver) => getMethod(target, prop) || oldTraps.get(target, prop, receiver), 2457 has: (target, prop) => !!getMethod(target, prop) || oldTraps.has(target, prop), 2458})); 2459 2460// @ts-ignore 2461try { 2462 self['workbox:expiration:7.3.0'] && _(); 2463} 2464catch (e) { } 2465 2466/* 2467 Copyright 2018 Google LLC 2468 2469 Use of this source code is governed by an MIT-style 2470 license that can be found in the LICENSE file or at 2471 https://opensource.org/licenses/MIT.
2472*/ 2473const DB_NAME = 'workbox-expiration'; 2474const CACHE_OBJECT_STORE = 'cache-entries'; 2475const normalizeURL = (unNormalizedUrl) => { 2476 const url = new URL(unNormalizedUrl, location.href); 2477 url.hash = ''; 2478 return url.href; 2479}; 2480/** 2481 * Returns the timestamp model. 2482 * 2483 * @private 2484 */ 2485class CacheTimestampsModel { 2486 /** 2487 * 2488 * @param {string} cacheName 2489 * 2490 * @private 2491 */ 2492 constructor(cacheName) { 2493 this._db = null; 2494 this._cacheName = cacheName; 2495 } 2496 /** 2497 * Performs an upgrade of indexedDB. 2498 * 2499 * @param {IDBPDatabase<CacheDbSchema>} db 2500 * 2501 * @private 2502 */ 2503 _upgradeDb(db) { 2504 // TODO(philipwalton): EdgeHTML doesn't support arrays as a keyPath, so we 2505 // have to use the `id` keyPath here and create our own values (a 2506 // concatenation of `url + cacheName`) instead of simply using 2507 // `keyPath: ['url', 'cacheName']`, which is supported in other browsers. 2508 const objStore = db.createObjectStore(CACHE_OBJECT_STORE, { keyPath: 'id' });
2509 // TODO(philipwalton): once we don't have to support EdgeHTML, we can 2510 // create a single index with the keyPath `['cacheName', 'timestamp']` 2511 // instead of doing both these indexes. 2512 objStore.createIndex('cacheName', 'cacheName', { unique: false }); 2513 objStore.createIndex('timestamp', 'timestamp', { unique: false }); 2514 } 2515 /** 2516 * Performs an upgrade of indexedDB and deletes deprecated DBs. 2517 * 2518 * @param {IDBPDatabase<CacheDbSchema>} db 2519 * 2520 * @private 2521 */ 2522 _upgradeDbAndDeleteOldDbs(db) { 2523 this._upgradeDb(db); 2524 if (this._cacheName) { 2525 void deleteDB(this._cacheName); 2526 } 2527 } 2528 /** 2529 * @param {string} url 2530 * @param {number} timestamp 2531 * 2532 * @private 2533 */ 2534 async setTimestamp(url, timestamp) { 2535 url = normalizeURL(url); 2536 const entry = { 2537 url, 2538 timestamp, 2539 cacheName: this._cacheName, 2540 // Creating an ID from the URL and cache name won't be necessary once 2541 // Edge switches to Chromium and all browsers we support work with 2542 // array keyPaths. 2543 id: this._getId(url), 2544 }; 2545 const db = await this.getDb(); 2546 const tx = db.transaction(CACHE_OBJECT_STORE, 'readwrite', { 2547 durability: 'relaxed', 2548 }); 2549 await tx.store.put(entry); 2550 await tx.done; 2551 } 2552 /** 2553 * Returns the timestamp stored for a given URL. 2554 * 2555 * @param {string} url 2556 * @return {number | undefined} 2557 * 2558 * @private 2559 */ 2560 async getTimestamp(url) { 2561 const db = await this.getDb(); 2562 const entry = await db.get(CACHE_OBJECT_STORE, this._getId(url)); 2563 return entry === null || entry === void 0 ? void 0 : entry.timestamp; 2564 } 2565 /** 2566 * Iterates through all the entries in the object store (from newest to 2567 * oldest) and removes entries once either `maxCount` is reached or the 2568 * entry's timestamp is less than `minTimestamp`. 2569 * 2570 * @param {number} minTimestamp 2571 * @param {number} maxCount 2572 * @return {Array<string>} 2573 * 2574 * @private 2575 */ 2576 async expireEntries(minTimestamp, maxCount) { 2577 const db = await this.getDb(); 2578 let cursor = await db 2579 .transaction(CACHE_OBJECT_STORE) 2580 .store.index('timestamp') 2581 .openCursor(null, 'prev'); 2582 const entriesToDelete = []; 2583 let entriesNotDeletedCount = 0; 2584 while (cursor) { 2585 const result = cursor.value; 2586 // TODO(philipwalton): once we can use a multi-key index, we 2587 // won't have to check `cacheName` here. 2588 if (result.cacheName === this._cacheName) { 2589 // Delete an entry if it's older than the max age or 2590 // if we already have the max number allowed. 2591 if ((minTimestamp && result.timestamp < minTimestamp) || 2592 (maxCount && entriesNotDeletedCount >= maxCount)) { 2593 // TODO(philipwalton): we should be able to delete the 2594 // entry right here, but doing so causes an iteration 2595 // bug in Safari stable (fixed in TP). Instead we can 2596 // store the keys of the entries to delete, and then 2597 // delete the separate transactions. 2598 // https://github.com/GoogleChrome/workbox/issues/1978 2599 // cursor.delete(); 2600 // We only need to return the URL, not the whole entry. 2601 entriesToDelete.push(cursor.value); 2602 } 2603 else { 2604 entriesNotDeletedCount++; 2605 } 2606 } 2607 cursor = await cursor.continue(); 2608 } 2609 // TODO(philipwalton): once the Safari bug in the following issue is fixed, 2610 // we should be able to remove this loop and do the entry deletion in the 2611 // cursor loop above: 2612 // https://github.com/GoogleChrome/workbox/issues/1978 2613 const urlsDeleted = []; 2614 for (const entry of entriesToDelete) { 2615 await db.delete(CACHE_OBJECT_STORE, entry.id); 2616 urlsDeleted.push(entry.url); 2617 } 2618 return urlsDeleted; 2619 } 2620 /** 2621 * Takes a URL and returns an ID that will be unique in the object store. 2622 * 2623 * @param {string} url 2624 * @return {string} 2625 * 2626 * @private 2627 */ 2628 _getId(url) { 2629 // Creating an ID from the URL and cache name won't be necessary once 2630 // Edge switches to Chromium and all browsers we support work with 2631 // array keyPaths. 2632 return this._cacheName + '|' + normalizeURL(url); 2633 } 2634 /** 2635 * Returns an open connection to the database. 2636 * 2637 * @private 2638 */ 2639 async getDb() { 2640 if (!this._db) { 2641 this._db = await openDB(DB_NAME, 1, { 2642 upgrade: this._upgradeDbAndDeleteOldDbs.bind(this), 2643 }); 2644 } 2645 return this._db; 2646 } 2647} 2648 2649class CacheExpiration { 2650 /** 2651 * To construct a new CacheExpiration instance you must provide at least 2652 * one of the `config` properties. 2653 * 2654 * @param {string} cacheName Name of the cache to apply restrictions to. 2655 * @param {Object} config 2656 * @param {number} [config.maxEntries] The maximum number of entries to cache. 2657 * Entries used the least will be removed as the maximum is reached. 2658 * @param {number} [config.maxAgeSeconds] The maximum age of an entry before 2659 * it's treated as stale and removed. 2660 * @param {Object} [config.matchOptions] The [`CacheQueryOptions`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/delete#Parameters) 2661 * that will be used when calling `delete()` on the cache. 2662 */ 2663 constructor(cacheName, config = {}) { 2664 this._isRunning = false;
2665 this._rerunRequested = false; 2666 this._maxEntries = config.maxEntries; 2667 this._maxAgeSeconds = config.maxAgeSeconds; 2668 this._matchOptions = config.matchOptions; 2669 this._cacheName = cacheName; 2670 this._timestampModel = new CacheTimestampsModel(cacheName); 2671 } 2672 /** 2673 * Expires entries for the given cache and given criteria. 2674 */ 2675 async expireEntries() { 2676 if (this._isRunning) { 2677 this._rerunRequested = true; 2678 return; 2679 } 2680 this._isRunning = true; 2681 const minTimestamp = this._maxAgeSeconds ? Date.now() - this._maxAgeSeconds * 1e3 : 0; 2682 const urlsExpired = await this._timestampModel.expireEntries(minTimestamp, this._maxEntries); 2683 const cache = await self.caches.open(this._cacheName); 2684 for (const url of urlsExpired) { 2685 await cache.delete(url, this._matchOptions); 2686 } 2687 this._isRunning = false; 2688 if (this._rerunRequested) { 2689 this._rerunRequested = false; 2690 dontWaitFor(this.expireEntries()); 2691 } 2692 } 2693 /** 2694 * Update the timestamp for the given URL. This ensures the when 2695 * removing entries based on maximum entries, most recently used 2696 * is accurate or when expiring, the timestamp is up-to-date. 2697 * 2698 * @param {string} url 2699 */ 2700 async updateTimestamp(url) { 2701 await this._timestampModel.setTimestamp(url, Date.now()); 2702 } 2703 /** 2704 * Can be used to check if a URL has expired or not before it's used. 2705 * 2706 * This requires a look up from IndexedDB, so can be slow. 2707 * 2708 * Note: This method will not remove the cached entry, call 2709 * `expireEntries()` to remove indexedDB and Cache entries. 2710 * 2711 * @param {string} url 2712 * @return {boolean} 2713 */ 2714 async isURLExpired(url) { 2715 if (!this._maxAgeSeconds) { 2716 return false; 2717 } else { 2718 const timestamp = await this._timestampModel.getTimestamp(url); 2719 const expireOlderThan = Date.now() - this._maxAgeSeconds * 1e3; 2720 return timestamp !== void 0 ? timestamp < expireOlderThan : true; 2721 } 2722 } 2723 /** 2724 * Removes the IndexedDB object store used to keep track of cache expiration 2725 * metadata. 2726 */ 2727 async delete() { 2728 this._rerunRequested = false; 2729 await this._timestampModel.expireEntries(Infinity); 2730 } 2731} 2732 2733function registerQuotaErrorCallback(callback) { 2734 quotaErrorCallbacks.add(callback); 2735} 2736 2737class ExpirationPlugin { 2738 /** 2739 * @param {ExpirationPluginOptions} config 2740 * @param {number} [config.maxEntries] The maximum number of entries to cache. 2741 * Entries used the least will be removed as the maximum is reached. 2742 * @param {number} [config.maxAgeSeconds] The maximum age of an entry before 2743 * it's treated as stale and removed. 2744 * @param {Object} [config.matchOptions] The [`CacheQueryOptions`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/delete#Parameters) 2745 * that will be used when calling `delete()` on the cache. 2746 * @param {boolean} [config.purgeOnQuotaError] Whether to opt this cache in to 2747 * automatic deletion if the available storage quota has been exceeded. 2748 */ 2749 constructor(config = {}) { 2750 this.cachedResponseWillBeUsed = async ({ event, request, cacheName, cachedResponse }) => { 2751 if (!cachedResponse) { 2752 return null; 2753 } 2754 const isFresh = this._isResponseDateFresh(cachedResponse); 2755 const cacheExpiration = this._getCacheExpiration(cacheName); 2756 dontWaitFor(cacheExpiration.expireEntries()); 2757 const updateTimestampDone = cacheExpiration.updateTimestamp(request.url); 2758 if (event) { 2759 try { 2760 event.waitUntil(updateTimestampDone); 2761 } catch (error) { 2762 } 2763 } 2764 return isFresh ? cachedResponse : null; 2765 }; 2766 this.cacheDidUpdate = async ({ cacheName, request }) => { 2767 const cacheExpiration = this._getCacheExpiration(cacheName); 2768 await cacheExpiration.updateTimestamp(request.url); 2769 await cacheExpiration.expireEntries(); 2770 }; 2771 this._config = config; 2772 this._maxAgeSeconds = config.maxAgeSeconds; 2773 this._cacheExpirations = /* @__PURE__ */ new Map(); 2774 if (config.purgeOnQuotaError) { 2775 registerQuotaErrorCallback(() => this.deleteCacheAndMetadata()); 2776 } 2777 } 2778 /**
2779 * A simple helper method to return a CacheExpiration instance for a given 2780 * cache name. 2781 * 2782 * @param {string} cacheName 2783 * @return {CacheExpiration} 2784 * 2785 * @private 2786 */ 2787 _getCacheExpiration(cacheName) { 2788 if (cacheName === cacheNames.getRuntimeName()) { 2789 throw new WorkboxError("expire-custom-caches-only"); 2790 } 2791 let cacheExpiration = this._cacheExpirations.get(cacheName); 2792 if (!cacheExpiration) { 2793 cacheExpiration = new CacheExpiration(cacheName, this._config); 2794 this._cacheExpirations.set(cacheName, cacheExpiration); 2795 } 2796 return cacheExpiration; 2797 } 2798 /** 2799 * @param {Response} cachedResponse 2800 * @return {boolean} 2801 * 2802 * @private 2803 */ 2804 _isResponseDateFresh(cachedResponse) { 2805 if (!this._maxAgeSeconds) { 2806 return true; 2807 } 2808 const dateHeaderTimestamp = this._getDateHeaderTimestamp(cachedResponse); 2809 if (dateHeaderTimestamp === null) { 2810 return true; 2811 } 2812 const now = Date.now(); 2813 return dateHeaderTimestamp >= now - this._maxAgeSeconds * 1e3; 2814 } 2815 /** 2816 * This method will extract the data header and parse it into a useful 2817 * value. 2818 * 2819 * @param {Response} cachedResponse 2820 * @return {number|null} 2821 * 2822 * @private 2823 */ 2824 _getDateHeaderTimestamp(cachedResponse) { 2825 if (!cachedResponse.headers.has("date")) { 2826 return null; 2827 } 2828 const dateHeader = cachedResponse.headers.get("date"); 2829 const parsedDate = new Date(dateHeader); 2830 const headerTime = parsedDate.getTime(); 2831 if (isNaN(headerTime)) { 2832 return null; 2833 } 2834 return headerTime; 2835 } 2836 /** 2837 * This is a helper method that performs two operations: 2838 * 2839 * - Deletes *all* the underlying Cache instances associated with this plugin 2840 * instance, by calling caches.delete() on your behalf. 2841 * - Deletes the metadata from IndexedDB used to keep track of expiration 2842 * details for each Cache instance. 2843 * 2844 * When using cache expiration, calling this method is preferable to calling 2845 * `caches.delete()` directly, since this will ensure that the IndexedDB 2846 * metadata is also cleanly removed and open IndexedDB instances are deleted. 2847 * 2848 * Note that if you're *not* using cache expiration for a given cache, calling 2849 * `caches.delete()` and passing in the cache's name should be sufficient. 2850 * There is no Workbox-specific method needed for cleanup in that case. 2851 */ 2852 async deleteCacheAndMetadata() { 2853 for (const [cacheName, cacheExpiration] of this._cacheExpirations) { 2854 await self.caches.delete(cacheName); 2855 await cacheExpiration.delete(); 2856 } 2857 this._cacheExpirations = /* @__PURE__ */ new Map(); 2858 } 2859} 2860 2861// @ts-ignore 2862try { 2863 self['workbox:cacheable-response:7.3.0'] && _(); 2864} 2865catch (e) { } 2866 2867class CacheableResponse { 2868 /** 2869 * To construct a new CacheableResponse instance you must provide at least 2870 * one of the `config` properties. 2871 * 2872 * If both `statuses` and `headers` are specified, then both conditions must 2873 * be met for the `Response` to be considered cacheable. 2874 * 2875 * @param {Object} config 2876 * @param {Array<number>} [config.statuses] One or more status codes that a 2877 * `Response` can have and be considered cacheable. 2878 * @param {Object<string,string>} [config.headers] A mapping of header names 2879 * and expected values that a `Response` can have and be considered cacheable. 2880 * If multiple headers are provided, only one needs to be present. 2881 */ 2882 constructor(config = {}) { 2883 this._statuses = config.statuses; 2884 this._headers = config.headers; 2885 } 2886 /** 2887 * Checks a response to see whether it's cacheable or not, based on this 2888 * object's configuration. 2889 * 2890 * @param {Response} response The response whose cacheability is being 2891 * checked. 2892 * @return {boolean} `true` if the `Response` is cacheable, and `false` 2893 * otherwise. 2894 */ 2895 isResponseCacheable(response) { 2896 let cacheable = true; 2897 if (this._statuses) { 2898 cacheable = this._statuses.includes(response.status); 2899 } 2900 if (this._headers && cacheable) {
2901 cacheable = Object.keys(this._headers).some((headerName) => { 2902 return response.headers.get(headerName) === this._headers[headerName]; 2903 }); 2904 } 2905 return cacheable; 2906 } 2907} 2908 2909/* 2910 Copyright 2018 Google LLC 2911 2912 Use of this source code is governed by an MIT-style 2913 license that can be found in the LICENSE file or at 2914 https://opensource.org/licenses/MIT. 2915*/ 2916/** 2917 * A class implementing the `cacheWillUpdate` lifecycle callback. This makes it 2918 * easier to add in cacheability checks to requests made via Workbox's built-in 2919 * strategies. 2920 * 2921 * @memberof workbox-cacheable-response 2922 */ 2923class CacheableResponsePlugin { 2924 /** 2925 * To construct a new CacheableResponsePlugin instance you must provide at 2926 * least one of the `config` properties. 2927 * 2928 * If both `statuses` and `headers` are specified, then both conditions must 2929 * be met for the `Response` to be considered cacheable. 2930 * 2931 * @param {Object} config 2932 * @param {Array<number>} [config.statuses] One or more status codes that a 2933 * `Response` can have and be considered cacheable. 2934 * @param {Object<string,string>} [config.headers] A mapping of header names 2935 * and expected values that a `Response` can have and be considered cacheable. 2936 * If multiple headers are provided, only one needs to be present. 2937 */ 2938 constructor(config) { 2939 /** 2940 * @param {Object} options 2941 * @param {Response} options.response 2942 * @return {Response|null} 2943 * @private 2944 */ 2945 this.cacheWillUpdate = async ({ response }) => { 2946 if (this._cacheableResponse.isResponseCacheable(response)) { 2947 return response; 2948 } 2949 return null; 2950 }; 2951 this._cacheableResponse = new CacheableResponse(config); 2952 } 2953} 2954 2955precacheAndRoute([{"revision":"3f6ddb4405a506f37de408adf084a447","url":"sitemap-index.xml"},{"revision":"c05247762e2110b0ccc882c5f61fd677","url":"sitemap-0.xml"},{"revision":"63c2344db9f057dc27539d28a22e37f0","url":"rss.xml"},{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"d238dd45c029adab8aed4ca255af556d","url":"pwa-512x512.png"},{"revision":"ad1672d3c7c6a53f531a067df66b38f9","url":"pwa-192x192.png"},{"revision":"9b9693cecffdfeab302e29b54a94343f","url":"/"},{"revision":"8c13f40658976ea1dfb8ba37a8be0e8d","url":"favicon.svg"},{"revision":"b966553a1a0de83ff2a41b2ae9ed485f","url":"404"},{"revision":"075a3744600a2f9007d269d7d428e1a9","url":"tags"},{"revision":"d26e987a41c15190dda7359b5eb0a747","url":"tags/é¨ç½²"},{"revision":"c4ce67c0caa88973a64b8afbefe71d61","url":"tags/设计模å¼"},{"revision":"41892ef09cdb0dfdc8863124deb0460f","url":"tags/ç¼ç¨"},{"revision":"c707fd68a11f831312069bf2c3a458b0","url":"tags/çæ¬æ§å¶"},{"revision":"63b2b531bb75dd6b843a9fd375d114e2","url":"tags/æ ·å¼"},{"revision":"98d0da85e1b9b591e705874c855d5f1c","url":"tags/æ¶æ"},{"revision":"dcf9d0a09bd5378912ba9e868937eced","url":"tags/æä½³å®è·µ"},{"revision":"312d2838a713eff585d9cad410e0932d","url":"tags/æ°å¦"},{"revision":"92b13b01fd213e0a656d1f100b03044a","url":"tags/æç¨"},{"revision":"0682b4be6c9c0bf96cbf313a004d548c","url":"tags/æ§è½"},{"revision":"e529a091f7cb2a56749fc147f73674f4","url":"tags/å¾®å端"},{"revision":"54ba90dce245d9819792d0d31764b4a2","url":"tags/å¸å±"},{"revision":"a689443f443a1628f7d54cd04fda6f8d","url":"tags/å®å ¨"},{"revision":"a29bf4d34b3f93fae1156e3a30b3c715","url":"tags/å端"},{"revision":"49d9edea847ca95eaaa52ad2f5665682","url":"tags/åä½"},{"revision":"1a0bfd7c0c6c03e28a5ad08c284c9f29","url":"tags/å端"},{"revision":"91b550c852b404d37e55d56ef9ae821e","url":"tags/ä¼å"},{"revision":"8691135c5c91bdd481d789bc65978b68","url":"tags/WebAssembly"},{"revision":"6e5cfc8b49d35c4a7aced7643943f1ee","url":"tags/Web"},{"revision":"31784b041e3e8c7f38c70f2d46e59861","url":"tags/Vue"},{"revision":"7b909f930556bd3aedafbcf61506ef90","url":"tags/TypeScript"},{"revision":"480803b8496c966c2ae0dae24ecc3c75","url":"tags/Test"},{"revision":"ad8018fec72ea9af1028abc3bae9678d","url":"tags/Style"},{"revision":"758d29a1b2156b1f847e42825fa3539d","url":"tags/React"},{"revision":"23a0de74d3f5b52d12fa75fa92f196fd","url":"tags/Node.js"}
2955,{"revision":"0cddb00cf0e926fe0dfdd69d8f12f3f0","url":"tags/Markdown"},{"revision":"631ee8b0c3b561cecd32536cec78ae14","url":"tags/MDX"},{"revision":"d2939766b9c08698e324db180aebb272","url":"tags/Lorem"},{"revision":"e5091456452701e82382e3166bb664ca","url":"tags/KaTeX"},{"revision":"aea17709b86af3074382e7c5f00253d5","url":"tags/JavaScript"},{"revision":"4e056ca3a705c0744d700126c38aa703","url":"tags/Ipsum"},{"revision":"02a414960075ab685a77b0953279822c","url":"tags/Hooks"},{"revision":"32104fd6c963517b5d71c51fd8eb73df","url":"tags/Guide"},{"revision":"a5a22551103c93e4143c98a444991ef2","url":"tags/GraphQL"},{"revision":"57d78c9bf1f31f2471452d8e3c8f07e0","url":"tags/Git"},{"revision":"2a66d897c6071dd1df74221153ebac2a","url":"tags/Docker"},{"revision":"3be5bee4f8e59072349aa3560b3cd9ec","url":"tags/DevOps"},{"revision":"dab608fdfef4cb66d90e71733662c138","url":"tags/CSS"},{"revision":"efd7ed089a3023154a686270057af85b","url":"tags/Astro"},{"revision":"4ea3bd55c11a10af345ca99c7c58bcd6","url":"tags/API"},{"revision":"e8dceb1cce3dbb84fc16606c64e62cdf","url":"spine_assets/plana/NP0035_spr.skel"},{"revision":"045a64c70016a8f8f5a1525fa54b8618","url":"spine_assets/plana/NP0035_spr.png"},{"revision":"573b904a86b9a188727157eacec80c65","url":"spine_assets/plana/NP0035_spr.atlas"},{"revision":"b5344b34173d2a8e1d862f943c047b80","url":"spine_assets/plana/audio/zh/plana_05.ogg"},{"revision":"70b0c0e74182fa7cd053c06dd7c421fb","url":"spine_assets/plana/audio/zh/plana_04.ogg"},{"revision":"cb8d91a4222bc9926df6ce44950f8e18","url":"spine_assets/plana/audio/zh/plana_03.ogg"},{"revision":"bed174e5313ef40ad39b5688c6991b83","url":"spine_assets/plana/audio/zh/plana_02.ogg"},{"revision":"73d495bb0c14bd63db631251d2ce6967","url":"spine_assets/plana/audio/zh/plana_01.ogg"},{"revision":"de43fb33032d3036e34f4a6881541e41","url":"spine_assets/kei/CH0335_noweapon_spr.skel"},{"revision":"228bcd8e09ac5a1e60752fa47e376ed5","url":"spine_assets/kei/CH0335_noweapon_spr.png"},{"revision":"d92ab6c1c065a33ed418bf66bb1d5b02","url":"spine_assets/kei/CH0335_noweapon_spr.atlas"},{"revision":"135373f92351ee326335e876f755ec67","url":"spine_assets/kei/audio/kei_copy.ogg"},{"revision":"9cdff0e9ce2aaae52322ef9c42912dc9","url":"spine_assets/kei/audio/kei_05.ogg"},{"revision":"0a1423b525fbee7e5459eae6d0054063","url":"spine_assets/kei/audio/kei_04.ogg"},{"revision":"4528d9a67fd8e729da7fde89d565e0af","url":"spine_assets/kei/audio/kei_03.ogg"},{"revision":"a11a531b1c3cb6b493869cc1abeeb5cd","url":"spine_assets/kei/audio/kei_02.ogg"},{"revision":"218f5834e28cb7f47c683dff6fb25897","url":"spine_assets/kei/audio/kei_01.ogg"},{"revision":"b72e3969ac4fba0de33be9ff1eedbec8","url":"spine_assets/arona/arona_spr.skel"},{"revision":"16e418937d583dcffea752249e5c89c7","url":"spine_assets/arona/arona_spr.png"},{"revision":"c8a2be841f987abab4e9bd03c33f9506","url":"spine_assets/arona/arona_spr.atlas"},{"revision":"584faa39f2275d29688eb97b72c5c6d0","url":"spine_assets/arona/audio/zh/arona_06.ogg"},{"revision":"e27550c325120121bbf93a492905bbb9","url":"spine_assets/arona/audio/zh/arona_05.ogg"},{"revision":"64f27ac13ee9aa99fb098d9d2035e9ef","url":"spine_assets/arona/audio/zh/arona_04.ogg"},{"revision":"1d81761cf7f6ee294c401381d3d9c1e3","url":"spine_assets/arona/audio/zh/arona_03.ogg"},{"revision":"b97b0b783803878ba8a3ef17690bd8b7","url":"spine_assets/arona/audio/zh/arona_02.ogg"},{"revision":"780c1e87c4fa97934b6ff6faf8f37cc8","url":"spine_assets/arona/audio/zh/arona_01.ogg"},{"revision":"57052e7ced3f82166eeffb947fb9dff9","url":"spine_assets/aris/aris_noweapon_spr.skel"},{"revision":"54201d9fd6ab2f03bc56e271313d9cc0","url":"spine_assets/aris/aris_noweapon_spr.png"},{"revision":"022de286f29063e008167a03e31d1ca5","url":"spine_assets/aris/aris_noweapon_spr.atlas"},{"revision":"9c7614bf5d852838621733cbc812920b","url":"spine_assets/aris/audio/aris_copy.mp3"},{"revision":"860c02662838ae41d48b86647cb2d634","url":"spine_assets/aris/audio/aris_05.ogg"},{"revision":"88af00da67efe8c333eb115acdcf2c28","url":"spine_assets/aris/audio/aris_04.ogg"},{"revision":"f440dccfb385317333d7ba8df19c7a08","url":"spine_assets/aris/audio/aris_03.ogg"},{"revision":"6bbd0b2bf4ed371aa73c41c7e73736a1","url":"spine_assets/aris/audio/aris_02.ogg"},{"revision":"89059fc8319212557577217eb74e8462","url":"spine_assets/aris/audio/aris_01.ogg"},{"revision":"f12d7bc7a269aa78d66104f863ea6e22","url":"project"},{"revision":"6af9cf960e2e2581832564d165db883c","url":"offline"},{"revision":"e0404f6d3d3ef9f4f69b04cd1256700b","url":"friends"},{"revision":"1265e63133cb9ca51dcb5c3d8afe1f79","url":"donate/donate.png"},{"revision":"de556cbaad572a755d7442b914bf510d","url":"blog/using-mdx"},{"revision":"260670ede4a64d3c40158b2654dac852","url":"blog/third-post"},{"revision":"cc650441a2102769c26d6348e1d1ba4a","url":"blog/test-post-15"},{"revision":"b22b2718160d43817c63479d81c935d5","url":"blog/test-post-14"},{"revision":"dde9ef6b3683b8b2664661ddbcb24000","url":"blog/test-post-13"},{"revision":"111288277b08972edd771fb756eee227","url":"blog/test-post-12"},{"revision":"cda67b7e2d27696ec30da479b6c837d5","url":"blog/test-post-11"},{"revision":"8dc32dcf81ac0e514271e76b1cdce427","url":"blog/test-post-10"},{"revision":"e2b94cfbf94b25d5f559f70c6b1fac52","url":"blog/test-post-09"},{"revision":"90e90a32e1dabe81915e953d82fe8b3d","url":"blog/test-post-08"},{"revision":"930aa87c5a89a3fbf2c36a0fe77f51ac","url":"blog/test-post-07"},{"revision":"6590ec6cbe455745be34233f5ed25be0","url":"blog/test-post-06"},{"revision":"9189c4c3a70ab1c284a5bb3a4f46143e","url":"blog/test-post-05"}
2955,{"revision":"d2a6930788a57c4f56ecedb84867da6b","url":"blog/test-post-04"},{"revision":"831f76d588462cba4966df559eca3cb0","url":"blog/test-post-03"},{"revision":"445cd12e2d2ca64d57c8752f7bcb6723","url":"blog/test-post-02"},{"revision":"25dccd657871e7bd681097b404d9b396","url":"blog/test-post-01"},{"revision":"fe0ec4ac66ceb1cac79ee7abc16031bd","url":"blog/second-post"},{"revision":"11915c4a86f5196b2189937237bc976a","url":"blog/markdown-style-guide"},{"revision":"e158690b8ae35d148582cf6d30ad78fc","url":"blog/katex-math-guide"},{"revision":"54e21435b1adb1ce694d36c1be9417c1","url":"blog/first-post"},{"revision":"7986802e84c372648a0b2c0ccd10d772","url":"bangumi"},{"revision":"9341f757e58a9a37600dbc32bbc0f028","url":"about"},{"revision":null,"url":"_astro/webworkerAll.CQrcKCR1.js"},{"revision":null,"url":"_astro/toTop.DI_oygYQ.svg"},{"revision":null,"url":"_astro/runtime-dom.esm-bundler.BkDFwomG.js"},{"revision":null,"url":"_astro/runtime-core.esm-bundler.z36Am-N8.js"},{"revision":null,"url":"_astro/preload-helper.CVfkMyKi.js"},{"revision":null,"url":"_astro/photoswipe.esm.BXDdABy_.js"},{"revision":null,"url":"_astro/page.D6zt-6VW.js"},{"revision":null,"url":"_astro/navLogo.DKp2Tzp-_t9aIe.svg"},{"revision":null,"url":"_astro/navLogo.DKp2Tzp-.svg"},{"revision":null,"url":"_astro/musicService.l9si1emE.js"},{"revision":null,"url":"_astro/index.CsdR7HZi.js"},{"revision":null,"url":"_astro/footLogo.D9ml5nCJ_CuOqe.webp"},{"revision":null,"url":"_astro/ff12251e855df71bf279d4515dad38ec.B-zWNHtg.woff2"},{"revision":null,"url":"_astro/fd87ef562fdc5482b83f0fa36471f907.eA17sHVe.woff2"},{"revision":null,"url":"_astro/fcb0cd2a64c0ab97f8126a35b371408d.DpXQCDz5.woff2"},{"revision":null,"url":"_astro/fb24b9e9fdae4c9f016986fc58858fe1.DQmW9uTs.woff2"},{"revision":null,"url":"_astro/fad53f716f9c6aa59576fb49db534aec.Jp4YL7cj.woff2"},{"revision":null,"url":"_astro/fa3c854f415968cc787150e588af56ef.Dc8MSxRk.woff2"},{"revision":null,"url":"_astro/fa3959305d542834978bdc6e93e6f70b.Cn5SPx9h.woff2"},{"revision":null,"url":"_astro/f9325783447077fb5ed8c124bea5045b._kkrlcuP.woff2"},{"revision":null,"url":"_astro/f8d7b5b9a468b0fc0dad60a256d4df95.CVbmofh5.woff2"},{"revision":null,"url":"_astro/f88836afeaec700c8482aade50f79f94.mhgcRjb3.woff2"},{"revision":null,"url":"_astro/f844f48480bedad6f5937e97f4541ce3.C-0-3mbB.woff2"},{"revision":null,"url":"_astro/f6a5b130905cfc33ad47e30f3afb432e.SA5d4DuG.woff2"},{"revision":null,"url":"_astro/f65c77dff61c3e49de327bb71bfc6a08.BTVFu2r1.woff2"},{"revision":null,"url":"_astro/f65819e9eb6e3816c96dc5a1d4b344c2.1JZ7F9km.woff2"},{"revision":null,"url":"_astro/f563c7d10ff3103c857077669bb51466.BRpx3nvb.woff2"},{"revision":null,"url":"_astro/f42a59aab9dae269f1b15671990462ff.CqTJfK86.woff2"},{"revision":null,"url":"_astro/f11250628ce107bff95eeaca8fdba77b.BCV8In0F.woff2"},{"revision":null,"url":"_astro/f0e23961c13272b9dbed35416158abd3.BybEEenW.woff2"},{"revision":null,"url":"_astro/f0ca746af58679df44d7811117f60d78.CemmgVOd.woff2"},{"revision":null,"url":"_astro/f0ab2aabde883947ad62ee036317a92e.Ca6mkPGl.woff2"},{"revision":null,"url":"_astro/ef46c803c347808452c6277389c6c410.Wi8af_AM.woff2"},{"revision":null,"url":"_astro/ee6ee404e89ca278979fd1ed0c7a458a.B2dm3REe.woff2"},{"revision":null,"url":"_astro/edb4b6bf99bb860536485b2eb6ffac7b.wQkYllQZ.woff2"},{"revision":null,"url":"_astro/ecb94b20aebb5b9c81bba793861084e5.BSco4Ybu.woff2"},{"revision":null,"url":"_astro/ec.r0ya5.css"},{"revision":null,"url":"_astro/ec.0vx5m.js"},{"revision":null,"url":"_astro/ebc1e58598229499aad1d807746a1986.CAgVSL77.woff2"},{"revision":null,"url":"_astro/eb150ee725c154742ab86fd2f4596f01.BStBQ2ua.woff2"},{"revision":null,"url":"_astro/eab12f54efd5bf564cdec31202af2439.BDurIvle.woff2"},{"revision":null,"url":"_astro/e95c273f1d9249e7974b6a22fa994e83.CfyF3etY.woff2"},{"revision":null,"url":"_astro/e8d94c876f0238b1017cc3b5b145232f.Dan62l-l.woff2"},{"revision":null,"url":"_astro/e8d072a10ec0fd205c8ac5ef9c0e1132.iohlxCH0.woff2"},{"revision":null,"url":"_astro/e8318075f6fb9cd25bfd6017fb05ea10.DrPWUX2j.woff2"},{"revision":null,"url":"_astro/e79bfb720d70d6d4c365277f2ca8ceee.BtIrG5wN.woff2"},{"revision":null,"url":"_astro/e711770afef84b38ec7e3623b9b893bc.HpIII6OG.woff2"},{"revision":null,"url":"_astro/e664b418b9be4d1560668c2ee4ddcf23.Dve_5JcY.woff2"},{"revision":null,"url":"_astro/e60748baabda7250fb381e18305f79cf.DfI6xKaW.woff2"},{"revision":null,"url":"_astro/e559bde330882ecc8b8dabd734a19b77.BTMKaaEv.woff2"},{"revision":null,"url":"_astro/e556f1852ad59c6c83a8e030801e5bb8.Bm9xjiZI.woff2"},{"revision":null,"url":"_astro/e51ca539aa6937ee2185584de12c1e82.DVTkLEAV.woff2"}
2955,{"revision":null,"url":"_astro/e26ad4c05c69b701b4b7beb7502087a3.D4wwEM9e.woff2"},{"revision":null,"url":"_astro/e0d9c587747ce19356d22c8ed5aec5c4.bmyEzyfJ.woff2"},{"revision":null,"url":"_astro/e0d67bdd35e3596d5e6411cac5ed3fe4.CPKNEjza.woff2"},{"revision":null,"url":"_astro/dfb99b42b38424a4f88da968d3e1c6fb.CBFCkdsH.woff2"},{"revision":null,"url":"_astro/df5ad27029623cff4859ad3e4a306862.1PQ1L-l8.woff2"},{"revision":null,"url":"_astro/deccfd02d6f82e5ab1f602cc1412c20c.DzQqK0MR.woff2"},{"revision":null,"url":"_astro/ddfc8c9c903dc392df82de9968c9a2c4.CtWX47M9.woff2"},{"revision":null,"url":"_astro/dd69e5dae6afd7aed50b5d3296cdd8b0.CA6393Et.woff2"},{"revision":null,"url":"_astro/dd4cc2f18408a5c6f3a0ed73dd37786c.BiNagD0b.woff2"},{"revision":null,"url":"_astro/dd3d3dffbb94202c7e4ea636c6f9d99a.CnptsRfB.woff2"},{"revision":null,"url":"_astro/daa5316eaba34d3382df376eabc373ee.DBFwQaN8.woff2"},{"revision":null,"url":"_astro/da5ed59e47b5dd162c1ae4020828cdb1.BN-acKxA.woff2"},{"revision":null,"url":"_astro/d91b2d8f2636b9ee348ab63fc5a264ad.B9Xv8LPf.woff2"},{"revision":null,"url":"_astro/d89725022f18af677f9cd6f20b9b165d.CY1ytNa0.woff2"},{"revision":null,"url":"_astro/d86bca4ba3064aff38f2786638588c32.gsc-JcPH.woff2"}
2955,{"revision":null,"url":"_astro/d8357a54f4f7d3272e7e147bb19d1e8c.CTlnfN6N.woff2"},{"revision":null,"url":"_astro/d63ec43ef34e9f75aa777475932a304a.BBoYHPB3.woff2"},{"revision":null,"url":"_astro/d5c6c76766f25b15988618efa51285cb.DOL-1aLe.woff2"},{"revision":null,"url":"_astro/d5ad898a826b323d257b8936bf6653ef.MTvJYni0.woff2"},{"revision":null,"url":"_astro/d56e874603219870d94198422fe405f4.S8CwERwo.woff2"},{"revision":null,"url":"_astro/d55850cb2128fc84fd4396f8e0db3805.Cgy5wrqV.woff2"},{"revision":null,"url":"_astro/d4af19a1aa6855f87516dfe51fff3ab7.7HIEd6Su.woff2"},{"revision":null,"url":"_astro/d497396acc5f68edbb4c10cd34fa6f17.CaM1C3z1.woff2"},{"revision":null,"url":"_astro/d4449af9ccd98fed27bf9b2021898222.Bw8Z-VhU.woff2"},{"revision":null,"url":"_astro/d41e81c12cf01b523fb09a62f1693921.D6cvaUot.woff2"},{"revision":null,"url":"_astro/d4138b727d70efd386255a67414d06af.sx-zhdie.woff2"},{"revision":null,"url":"_astro/d394d21164a0edd4a0e9b2632511fa24.B4AvrZD0.woff2"},{"revision":null,"url":"_astro/d37c65540ddd23290afe73628f893452.D_YRlvtx.woff2"},{"revision":null,"url":"_astro/d322563755e807ee04f1185467e65908.DwEa01uO.woff2"},{"revision":null,"url":"_astro/d28de52d6342d2ea1fe79ad88d1e7ad5.CQLqLXfk.woff2"},{"revision":null,"url":"_astro/d244ac0134c51330d257f9f2276bc3e3.DDQbDT5P.woff2"},{"revision":null,"url":"_astro/d0e017c3f4b6d9b64394caef0c6f9096.CL3F-XTV.woff2"},{"revision":null,"url":"_astro/d0af9a5f02191ef1c6d5982f61bf05ba.6k0JTVWD.woff2"},{"revision":null,"url":"_astro/d08c91beb40adf52ed2f79fd91fbc379.CED9ht_R.woff2"},{"revision":null,"url":"_astro/client.CrwFu2hE.js"},{"revision":null,"url":"_astro/cfec4e3bf632f5d9441e9cc80ec3cc7b.SIxj4pqi.woff2"},{"revision":null,"url":"_astro/cef32627bd7e3abd2392fe1287a36cc6.B9Q9r9Sh.woff2"},{"revision":null,"url":"_astro/ce3701e71fb3a3be451badf661b3cc10.CKXpgpm8.woff2"},{"revision":null,"url":"_astro/cc848efcdc3c77262b0977445a0ed8f7.DMW-7hYY.woff2"},{"revision":null,"url":"_astro/cba9c42b58ae234b681ac2c8e0533e65.Exzr4e52.woff2"},{"revision":null,"url":"_astro/c9a60417da0818ac72f62bdb04925cd4.CPuqBwY6.woff2"},{"revision":null,"url":"_astro/c8b50292ca4e40aa1fa9812999b34cf9.D63lWpg-.woff2"},{"revision":null,"url":"_astro/c88e3dbea4d5eb73c50277f29f6f8d10.6Zg1kDEk.woff2"},{"revision":null,"url":"_astro/c865b08298af8395f5473667f37b4aeb.DQPMnnCd.woff2"},{"revision":null,"url":"_astro/c761f7cd4b49081b8d7104fd38151313.B3UoU3fU.woff2"},{"revision":null,"url":"_astro/c7513312111bd5757a7a6cc7f7a6fe55.Di2ZpOkO.woff2"},{"revision":null,"url":"_astro/c736127dbc967a4842ae54091c788848.C_9nGw5i.woff2"},{"revision":null,"url":"_astro/c56ca2cdc6e4c8ad69a9820ce64e664d.B7nIzRz6.woff2"},{"revision":null,"url":"_astro/c52d1743d24ad460b6a0714a1fb09407.S_ijwmYo.woff2"},{"revision":null,"url":"_astro/c4f591f757854eff8b2ce164316d2c27.CpQDlY1y.woff2"},{"revision":null,"url":"_astro/c409e7fb74fa7469714754eb1ed407f2.DKEpZ7DI.woff2"},{"revision":null,"url":"_astro/c31f637e1ded8cf6e5daf137813f7e1b.CAEzit8_.woff2"},{"revision":null,"url":"_astro/browserAll.C24f_xP3.js"},{"revision":null,"url":"_astro/blog-placeholder-about.BtEdEmGp_Z2srqQK.webp"},{"revision":null,"url":"_astro/blog-placeholder-about.BtEdEmGp.jpg"},{"revision":null,"url":"_astro/blog-placeholder-5.CB3Xi-gp_j9V0O.webp"},{"revision":null,"url":"_astro/blog-placeholder-5.CB3Xi-gp.jpg"},{"revision":null,"url":"_astro/blog-placeholder-4.gLBdjEDe_Z15Ss3d.webp"},{"revision":null,"url":"_astro/blog-placeholder-4.gLBdjEDe.jpg"},{"revision":null,"url":"_astro/blog-placeholder-3.ijrf8Ohr_Z1373cG.webp"},{"revision":null,"url":"_astro/blog-placeholder-3.ijrf8Ohr.jpg"},{"revision":null,"url":"_astro/blog-placeholder-2.1WQRLJGH_15TD1q.webp"},{"revision":null,"url":"_astro/blog-placeholder-2.1WQRLJGH.jpg"},{"revision":null,"url":"_astro/blog-placeholder-1.Bx0Zcyzv_Z1v5W1C.webp"},{"revision":null,"url":"_astro/blog-placeholder-1.Bx0Zcyzv.jpg"},{"revision":null,"url":"_astro/bf6230a42422aceec39b3811c4c75756.BMs5o7V9.woff2"},{"revision":null,"url":"_astro/be917719d42ca31eff7134f3b719e491.BwABGLsm.woff2"},{"revision":null,"url":"_astro/be756c71ee61176d435b30cfb607daca.BwuwGb_4.woff2"},{"revision":null,"url":"_astro/be70ccadd88a80b876ae0d524e29d923.dFPJJROn.woff2"},{"revision":null,"url":"_astro/bdecd81e8cb1843d6785d9b0810fea6f.jCAn2Db_.woff2"},{"revision":null,"url":"_astro/bdac513db59a662f04893281212efc21.DHrsj0fB.woff2"},{"revision":null,"url":"_astro/bc7c333983f6a98f64947186e7462f28.DXFjIK13.woff2"},{"revision":null,"url":"_astro/bc4e1caf225389a3a4dfc6add134354a.DIABC9G9.woff2"},{"revision":null,"url":"_astro/bc2b0ed7b41aff7a56580e4fd141ffec.CKD96T1N.woff2"},{"revision":null,"url":"_astro/bbda0e671bc631b0f036f344fe62029e.Bq74HGf1.woff2"},{"revision":null,"url":"_astro/bbbe025b78361933dd8a499a1ada6d2b.EUMqSC7J.woff2"},{"revision":null,"url":"_astro/bbafb8ca6b798ae03a68227f82dcf648.CzVvAghM.woff2"},{"revision":null,"url":"_astro/banner_dark.DYi3kWFc.webp"},{"revision":null,"url":"_astro/banner.H_N5Rg0p.webp"},{"revision":null,"url":"_astro/background_dark.AFOWX2L5.svg"},{"revision":null,"url":"_astro/background.BBwJmske.svg"},{"revision":null,"url":"_astro/bab64f63d2d5ec98fb42a241b6acc30b.BSyrVr1o.woff2"},{"revision":null,"url":"_astro/ba1fadf246a745a30e7b337c508d3edd.BPqGLiFj.woff2"},{"revision":null,"url":"_astro/b9a013be5c5aeb625bd58991626b6916.8QODTq21.woff2"},{"revision":null,"url":"_astro/b96d65a2ff7f6f10e1f6fe10f3de66a5.qNjVyTyD.woff2"},{"revision":null,"url":"_astro/b883d719baaf1d6e927de9c263f1d300.BIV7TJLC.woff2"},{"revision":null,"url":"_astro/b843dac4a47827c9b1a350eaef890609.BbrqTvWJ.woff2"},{"revision":null,"url":"_astro/b7d2448d1850f355bcaaf43723e27bce.CUXP5dFQ.woff2"},{"revision":null,"url":"_astro/b73ee08ca7b322934953a91d680a7228.6u4lmf7Y.woff2"},{"revision":null,"url":"_astro/b6b3df5870cc24dbc31a96b27f642674.D819KYAO.woff2"},{"revision":null,"url":"_astro/b5431d1aaf4c8c073a5cb99f8c137c71.BDxl3GG3.woff2"},{"revision":null,"url":"_astro/b3f267f4155ebd4df445cf2002146506.DRTubLyr.woff2"},{"revision":null,"url":"_astro/b2b41b5ef7d7f11c75da5b58d0266510.Dukge0O0.woff2"},{"revision":null,"url":"_astro/b249ae133c24159ec268e6903948f3ee.CioEFe9x.woff2"},{"revision":null,"url":"_astro/b20c4a6706e0f610695d9221eba4d57e.Cbdfi1Db.woff2"},{"revision":null,"url":"_astro/b124767dfdf43b236ae1ae3a5e1cc287.Bs7b8Yhy.woff2"},{"revision":null,"url":"_astro/b10a6803d0c3fc732ef6804d04781054.lZnH0BGZ.woff2"},{"revision":null,"url":"_astro/b0b14b205d69b49057e5f8fda47571e5.DKjOM9-x.woff2"},{"revision":null,"url":"_astro/b0240d566d3d07a8f349211305f494af.Du6_hzay.woff2"},{"revision":null,"url":"_astro/avatar.BdKdfcnM_1Prj8X.webp"},{"revision":null,"url":"_astro/acdc6809717a1eb16b30eb40c8a2db4c.CVuS2BGf.woff2"},{"revision":null,"url":"_astro/acbff24f5dbae085d3abb24b09d5f852.BzyI50EI.woff2"},{"revision":null,"url":"_astro/acaec8d522cbb4fc8d004406ab61680a.Dcj11mlH.woff2"},{"revision":null,"url":"_astro/ac6a03c30289930eeb15581449862ae3.2EMkwnwL.woff2"},{"revision":null,"url":"_astro/ac48a61d8b2a98264b77f638ad6e9575.C34VuwIg.woff2"},{"revision":null,"url":"_astro/abdc2a1b51428ba2cc333e9261460c26.Bqohx2HC.woff2"},{"revision":null,"url":"_astro/ab858dff1dbf586ccacc4c30e9281d6e.DeKnD3CQ.woff2"},{"revision":null,"url":"_astro/a91c2becc29a2b894ecb22ffe863e84c.lNMBmmk2.woff2"},{"revision":null,"url":"_astro/a8947f6542b9ea94d35f7909086b8683.CRgQNEiF.woff2"},{"revision":null,"url":"_astro/a74b3d4b081bf6119d8c34439ff665c3.B08NJUnd.woff2"},{"revision":null,"url":"_astro/a746dc16fc3f756da7994203b24fd9c6.BJH8RFvc.woff2"},{"revision":null,"url":"_astro/a69577262f43d322f3f56895209f7fc6.gDO3VCUy.woff2"}
2955,{"revision":null,"url":"_astro/a5ec1e4f3189665a3831ad236441ae43.CvOsMiWl.woff2"},{"revision":null,"url":"_astro/a4bf789e77b35512c92ef6b8ebdc1c2f.DOj7Uclv.woff2"},{"revision":null,"url":"_astro/a41ed0ad32a6262e9ed4eebfe5aa3c91.uUcm__mb.woff2"},{"revision":null,"url":"_astro/a3c8e3e722624ccfd9c6f211e7156705.DpiaxIkI.woff2"},{"revision":null,"url":"_astro/a1a392e7dce5c17794ea0f72d507ac87.DQt9dRok.woff2"},{"revision":null,"url":"_astro/a1857674556f27a164c062e35d5d6a31.BT7LJzBk.woff2"},{"revision":null,"url":"_astro/a0305a767f29e73662f2abc59551a07b.BuxjLP3u.woff2"},{"revision":null,"url":"_astro/a019ca4c217b61e8cafdbf95a50f3e3f.D5feljuJ.woff2"},{"revision":null,"url":"_astro/_plugin-vue_export-helper.DlAUqK2U.js"},{"revision":null,"url":"_astro/_commonjsHelpers.Cpj98o6Y.js"},{"revision":null,"url":"_astro/WelcomeBoxController.DHZ7_0PH.js"},{"revision":null,"url":"_astro/WebGPURenderer.B8NSNx_K.js"},{"revision":null,"url":"_astro/WebGLRenderer.BQ6YDYJg.js"},{"revision":null,"url":"_astro/WalineComment.astro_astro_type_script_index_0_lang.CwXO04sd.js"},{"revision":null,"url":"_astro/SpinePlayer.BTZ4ixmH.js"},{"revision":null,"url":"_astro/SpinePlayer.BHeVZB3M.js"},{"revision":null,"url":"_astro/RenderTargetSystem.bDRyAEyo.js"},{"revision":null,"url":"_astro/PhotoSwipe.astro_astro_type_script_index_0_lang.rGeJF7Tz.js"},{"revision":null,"url":"_astro/PageTransition.astro_astro_type_script_index_0_lang.D8bAg6kx.js"},{"revision":null,"url":"_astro/NotFound.BbqB1acd.webp"},{"revision":null,"url":"_astro/MusicControl.astro_astro_type_script_index_0_lang.yylIi8F0.js"},{"revision":null,"url":"_astro/KaTeX_Typewriter-Regular.D3Ib7_Hf.ttf"},{"revision":null,"url":"_astro/KaTeX_Typewriter-Regular.CO6r4hn1.woff2"},{"revision":null,"url":"_astro/KaTeX_Typewriter-Regular.C0xS9mPB.woff"},{"revision":null,"url":"_astro/KaTeX_Size4-Regular.Dl5lxZxV.woff2"},{"revision":null,"url":"_astro/KaTeX_Size4-Regular.DWFBv043.ttf"},{"revision":null,"url":"_astro/KaTeX_Size4-Regular.BF-4gkZK.woff"},{"revision":null,"url":"_astro/KaTeX_Size3-Regular.DgpXs0kz.ttf"},{"revision":null,"url":"_astro/KaTeX_Size3-Regular.CTq5MqoE.woff"},{"revision":null,"url":"_astro/KaTeX_Size2-Regular.oD1tc_U0.woff"},{"revision":null,"url":"_astro/KaTeX_Size2-Regular.Dy4dx90m.woff2"},{"revision":null,"url":"_astro/KaTeX_Size2-Regular.B7gKUWhC.ttf"},{"revision":null,"url":"_astro/KaTeX_Size1-Regular.mCD8mA8B.woff2"},{"revision":null,"url":"_astro/KaTeX_Size1-Regular.Dbsnue_I.ttf"},{"revision":null,"url":"_astro/KaTeX_Size1-Regular.C195tn64.woff"},{"revision":null,"url":"_astro/KaTeX_Script-Regular.D5yQViql.woff"},{"revision":null,"url":"_astro/KaTeX_Script-Regular.D3wIWfF6.woff2"},{"revision":null,"url":"_astro/KaTeX_Script-Regular.C5JkGWo-.ttf"},{"revision":null,"url":"_astro/KaTeX_SansSerif-Regular.DDBCnlJ7.woff2"},{"revision":null,"url":"_astro/KaTeX_SansSerif-Regular.CS6fqUqJ.woff"},{"revision":null,"url":"_astro/KaTeX_SansSerif-Regular.BNo7hRIc.ttf"},{"revision":null,"url":"_astro/KaTeX_SansSerif-Italic.YYjJ1zSn.ttf"},{"revision":null,"url":"_astro/KaTeX_SansSerif-Italic.DN2j7dab.woff"},{"revision":null,"url":"_astro/KaTeX_SansSerif-Italic.C3H0VqGB.woff2"},{"revision":null,"url":"_astro/KaTeX_SansSerif-Bold.DbIhKOiC.woff"},{"revision":null,"url":"_astro/KaTeX_SansSerif-Bold.D1sUS0GD.woff2"},{"revision":null,"url":"_astro/KaTeX_SansSerif-Bold.CFMepnvq.ttf"},{"revision":null,"url":"_astro/KaTeX_Math-Italic.t53AETM-.woff2"},{"revision":null,"url":"_astro/KaTeX_Math-Italic.flOr_0UB.ttf"},{"revision":null,"url":"_astro/KaTeX_Math-Italic.DA0__PXp.woff"},{"revision":null,"url":"_astro/KaTeX_Math-BoldItalic.iY-2wyZ7.woff"},{"revision":null,"url":"_astro/KaTeX_Math-BoldItalic.CZnvNsCZ.woff2"},{"revision":null,"url":"_astro/KaTeX_Math-BoldItalic.B3XSjfu4.ttf"},{"revision":null,"url":"_astro/KaTeX_Main-Regular.ypZvNtVU.ttf"},{"revision":null,"url":"_astro/KaTeX_Main-Regular.Dr94JaBh.woff"},{"revision":null,"url":"_astro/KaTeX_Main-Regular.B22Nviop.woff2"},{"revision":null,"url":"_astro/KaTeX_Main-Italic.NWA7e6Wa.woff2"},{"revision":null,"url":"_astro/KaTeX_Main-Italic.BMLOBm91.woff"},{"revision":null,"url":"_astro/KaTeX_Main-Italic.3WenGoN9.ttf"}
2955,{"revision":null,"url":"_astro/KaTeX_Main-BoldItalic.SpSLRI95.woff"},{"revision":null,"url":"_astro/KaTeX_Main-BoldItalic.DzxPMmG6.ttf"},{"revision":null,"url":"_astro/KaTeX_Main-BoldItalic.DxDJ3AOS.woff2"},{"revision":null,"url":"_astro/KaTeX_Main-Bold.waoOVXN0.ttf"},{"revision":null,"url":"_astro/KaTeX_Main-Bold.Jm3AIy58.woff"},{"revision":null,"url":"_astro/KaTeX_Main-Bold.Cx986IdX.woff2"},{"revision":null,"url":"_astro/KaTeX_Fraktur-Regular.Dxdc4cR9.woff"},{"revision":null,"url":"_astro/KaTeX_Fraktur-Regular.CTYiF6lA.woff2"},{"revision":null,"url":"_astro/KaTeX_Fraktur-Regular.CB_wures.ttf"},{"revision":null,"url":"_astro/KaTeX_Fraktur-Bold.CL6g_b3V.woff2"},{"revision":null,"url":"_astro/KaTeX_Fraktur-Bold.BsDP51OF.woff"},{"revision":null,"url":"_astro/KaTeX_Fraktur-Bold.BdnERNNW.ttf"},{"revision":null,"url":"_astro/KaTeX_Caligraphic-Regular.wX97UBjC.ttf"},{"revision":null,"url":"_astro/KaTeX_Caligraphic-Regular.Di6jR-x-.woff2"},{"revision":null,"url":"_astro/KaTeX_Caligraphic-Regular.CTRA-rTL.woff"},{"revision":null,"url":"_astro/KaTeX_Caligraphic-Bold.Dq_IR9rO.woff2"},{"revision":null,"url":"_astro/KaTeX_Caligraphic-Bold.BEiXGLvX.woff"},{"revision":null,"url":"_astro/KaTeX_Caligraphic-Bold.ATXxdsX0.ttf"},{"revision":null,"url":"_astro/KaTeX_AMS-Regular.DRggAlZN.ttf"},{"revision":null,"url":"_astro/KaTeX_AMS-Regular.DMm9YOAa.woff"},{"revision":null,"url":"_astro/KaTeX_AMS-Regular.BQhdFMY1.woff2"},{"revision":null,"url":"_astro/JetBrainsMono-Regular.BQaDgvhP.woff2"},{"revision":null,"url":"_astro/JetBrainsMono-Italic.CFFf43z2.woff2"},{"revision":null,"url":"_astro/Filter.D4bURMG6.js"},{"revision":null,"url":"_astro/ClientRouter.astro_astro_type_script_index_0_lang.D3CcSXtg.js"},{"revision":null,"url":"_astro/CanvasRenderer.B9uB1Edj.js"},{"revision":null,"url":"_astro/CanvasPool.DxMD5ozV.js"},{"revision":null,"url":"_astro/BufferResource.CIFSdiOa.js"},{"revision":null,"url":"_astro/BitmapFont.G2RZkRQk.js"},{"revision":null,"url":"_astro/Banner.astro_astro_type_script_index_0_lang.DPDBQxub.js"},{"revision":null,"url":"_astro/9f426dfa68b6b86a3b24918cd61ba5f8.C0cilLQj.woff2"},{"revision":null,"url":"_astro/9e8b2d4048301fc4de76a8cdf37a24f8.Dk8mtBhb.woff2"},{"revision":null,"url":"_astro/9e7560d0c89c56055d6587e772be8d13.CUJYFFFy.woff2"},{"revision":null,"url":"_astro/9db92973ab842839c8c66b948b9ac556.Dp0rpGCg.woff2"},{"revision":null,"url":"_astro/9d12af886db1f38cf45613dc55951a84.BFVh_8eD.woff2"},{"revision":null,"url":"_astro/9cc37dfae8f1b2fcd2cf9790cb9c11bc.Kic_lj0K.woff2"},{"revision":null,"url":"_astro/9c10b4c648bc944285db8e9be8c5e80f.K4AcwB0H.woff2"},{"revision":null,"url":"_astro/9afa37f9d9f6b7fb2d696723ee42677a.DlLOaanD.woff2"},{"revision":null,"url":"_astro/9980fd4d4c9601e55ead1dc2cb0bbccc.BOTChj9u.woff2"},{"revision":null,"url":"_astro/98de76e32608d9ce0953e16e359e144d.17cZlNdL.woff2"},{"revision":null,"url":"_astro/987198a47bb3d73caf710b83a7bc95e0.DhX1OdOP.woff2"},{"revision":null,"url":"_astro/985e7ced486b92724d567ff9cec240e0.Bk4ciX-3.woff2"},{"revision":null,"url":"_astro/98503b1ed30513381f5d29c34effe577.9t3URzeG.woff2"},{"revision":null,"url":"_astro/97fc1df31ac590764bdc1d35f32bbc39.rQnuFjFb.woff2"},{"revision":null,"url":"_astro/979ac97c1d7b2efd73bef1fb0f8f7a64.Cg7hYL3o.woff2"},{"revision":null,"url":"_astro/9659a3f5bf35f901a1b2f9def1109bae.ClPmWOLu.woff2"},{"revision":null,"url":"_astro/95be0ad9c37dee07a08ff8fad5589697.BWZ67Pqk.woff2"},{"revision":null,"url":"_astro/954d43246b1ec91ecdc34df561b1889b.Daf3Rj-R.woff2"},{"revision":null,"url":"_astro/950c4fdb64891fcc0c47730236b9e7a4.vYLLpEmw.woff2"},{"revision":null,"url":"_astro/93dad0ee659730b176d79ddde2641e16.TvFdhMXw.woff2"},{"revision":null,"url":"_astro/93d6c1263ff8ba18377bc5ea624e08aa.BhIASOL_.woff2"},{"revision":null,"url":"_astro/926fe3470d3d721c5197eaef53c4c9cf.BHgoVFsh.woff2"},{"revision":null,"url":"_astro/91579fb71f97d487fb9790508ffd9d0c.gjjTYrRt.woff2"},{"revision":null,"url":"_astro/90b3cea36b6d0cd394c0f5cf1c171d7c.B_WU4OhY.woff2"},{"revision":null,"url":"_astro/90871848d354e4924248b92b0235389f.ucm1fAHA.woff2"},{"revision":null,"url":"_astro/9072534829ceedddcabab2a121683afa.Bft69dUL.woff2"},{"revision":null,"url":"_astro/903044fcc7aa39c7c6d9ec0472735e65.CVOqGt7M.woff2"},{"revision":null,"url":"_astro/8fd2770cc627eda56823ee1512970ed9.BkgxtcF3.woff2"},{"revision":null,"url":"_astro/8fb0d45b1286017cc4e04985d0a294d0.C0n38kAB.woff2"},{"revision":null,"url":"_astro/8f6b433817601eb2ed876d48696f7dfc.DqkThtra.woff2"},{"revision":null,"url":"_astro/8ecff440a70e97f55265040ae381c0b1.BTPL83Cm.woff2"},{"revision":null,"url":"_astro/8ecb74fb476a2e4c57d461181771dde1.OTVJR2qa.woff2"},{"revision":null,"url":"_astro/8e688bd424db073d51a206b2638f3bde.BpXvMZwr.woff2"},{"revision":null,"url":"_astro/8e5963f4af3922002144ab88fe137ac6.CZqTuwI3.woff2"},{"revision":null,"url":"_astro/8e298129bb11765ed5dec3e164bfa15a.BloofOp5.woff2"},{"revision":null,"url":"_astro/8d0c089e243235d7ff20952e29a47932.DzWCfXi8.woff2"},{"revision":null,"url":"_astro/8c09f2a621db40775b42aee6ef3c9ee8.BdGyLl5u.woff2"},{"revision":null,"url":"_astro/8ba45d7d00203bc91bc37acdd490d8a9.75woyrZ0.woff2"},{"revision":null,"url":"_astro/8ba21d555b89c7aa99da88282e952363.B873nFp0.woff2"},{"revision":null,"url":"_astro/8b57899f6995071bbc9160529bbe17b8.C-t-6Gxc.woff2"},{"revision":null,"url":"_astro/8b4cd6678fb13992489bea9b7ac9c220.Du-be5bi.woff2"},{"revision":null,"url":"_astro/891505f3587a9d9fd1d0198f2f7c9cd7.D6ehzCdo.woff2"},{"revision":null,"url":"_astro/88b72f0fc737ea052056f6c2aeea44a3.kFBOKHMb.woff2"},{"revision":null,"url":"_astro/8887b5bf51593b9fc9c25b376961c28d.SLYFOQ6B.woff2"},{"revision":null,"url":"_astro/8835dd6747879c0fba990bd87ecd2301.CE0rVYr2.woff2"},{"revision":null,"url":"_astro/87d4bc8c69dcb27ef4de7d59b7709b0d.Di1KlkL1.woff2"},{"revision":null,"url":"_astro/8724be47dff2dfa9d93959096c5ecb46.CgcLMC7R.woff2"},{"revision":null,"url":"_astro/860c568e2eff98d81cda61d46fcecca1.DsqANwHz.woff2"},{"revision":null,"url":"_astro/8454772e4bc3595d2e908dd191a7bd29.ChLjv6UK.woff2"},{"revision":null,"url":"_astro/844ab646a77389c13720fbf14c0093c3.C90zS1gA.woff2"},{"revision":null,"url":"_astro/8405a331c60fdb56463b89656bb49cd4.BvZxfoDj.woff2"},{"revision":null,"url":"_astro/828cc88997f658acf911380de2f6d320.C28F5emo.woff2"},{"revision":null,"url":"_astro/81f2a172727f431d7ca1cc57284a41d9.CsUVNPaV.woff2"},{"revision":null,"url":"_astro/81447e78a0661abe408d0d3da783afc7.CDNDMDvr.woff2"},{"revision":null,"url":"_astro/80e837e4292c4e443a14bf6a63645320.D2qpPMSm.woff2"},{"revision":null,"url":"_astro/80a8feea65f4077fa429813c8726259d.DD-BeWht.woff2"},{"revision":null,"url":"_astro/809b5c3ad4776a8607ccfe5bb4cfa724.CiwnhhAW.woff2"},{"revision":null,"url":"_astro/7f7b6d5117c9e90741bad29ce16f4b23.CKmth-x3.w
2955off2"},{"revision":null,"url":"_astro/7ece3cbfc188d27ae566cf1e90b245fb.CL2erN5z.woff2"},{"revision":null,"url":"_astro/7df84da8dea8bf937b6cd6498b02562c.ZTo6xLPh.woff2"},{"revision":null,"url":"_astro/7de6db0fcc32ead9e132bb862789058e.DKbR9tXw.woff2"},{"revision":null,"url":"_astro/7db7dd37453f81cf7b32d7d7385affdb.CLVR3iW7.woff2"},{"revision":null,"url":"_astro/7da6a25ea397f993b1635e9ec61b6e7d.D9N3YUUF.woff2"},{"revision":null,"url":"_astro/7d5debdadff5205554730a1db3667690.De4I6wZV.woff2"},{"revision":null,"url":"_astro/7d01df4023fdc4af4a248ec626a9dd5a.DSePzLyP.woff2"},{"revision":null,"url":"_astro/7c7b31a6751aa044a5f97fce6245fbbf.Bm1Pt4Ab.woff2"},{"revision":null,"url":"_astro/7b275413b16c0d9560f917c63b6015a4.CIlDAo7V.woff2"},{"revision":null,"url":"_astro/7a681defbe746aab93deb5c75f8a9b4d.CwqvcN8e.woff2"},{"revision":null,"url":"_astro/7a0548edc531408ac5f247e1aa4e11b3.CoHujI9h.woff2"},{"revision":null,"url":"_astro/77d2de639a2e881d67b90cf4ed67d235.CMczvquw.woff2"},{"revision":null,"url":"_astro/77c17c226061f43701e4c8d7c190aae1.DPMf8f5F.woff2"},{"revision":null,"url":"_astro/76f61cbd4f1635fb86f93c062308c7c2.DkP9CYvR.woff2"},{"revision":null,"url":"_astro/76728302ec8e84f94d98e08d4c238ce6.2mA2imie.woff2"},{"revision":null,"url":"_astro/760e4045bf23ccfe5173b9d39b5321b7.DTYCv5wA.woff2"},{"revision":null,"url":"_astro/7544ada24c2d81778c97a307cfc69328.C0Tuh6f0.woff2"},{"revision":null,"url":"_astro/74e2c8c8d31671e7c90233fbc3ec1f9b.jo_6QXKN.woff2"},{"revision":null,"url":"_astro/72c3e410ffc5ee945e88bdcf7ff2b74d.BzXdccKS.woff2"},{"revision":null,"url":"_astro/7275d086574970760b966e1ab403a203.nbB4aqfh.woff2"},{"revision":null,"url":"_astro/720c3fbae0336ef7f8d2815627135892.CxXeieSr.woff2"},{"revision":null,"url":"_astro/71d061f6ee948114c50a46402e7f08ea.BfLCrOMU.woff2"},{"revision":null,"url":"_astro/70edca627d1654b61d2c66eba8a60923.CTjNlL3u.woff2"},{"revision":null,"url":"_astro/70e6c4446907c6fb588091d6b1ffb290.8gMWenc6.woff2"},{"revision":null,"url":"_astro/6f69323fe2d59ce9051b948992e91faa.DjIWnxvv.woff2"},{"revision":null,"url":"_astro/6e8899581ce28e59cdc324b8ff17afba.Dy2g4XDY.woff2"},{"revision":null,"url":"_astro/6e514460f660c6f1872a317933a2b58c.DMWizJmW.woff2"},{"revision":null,"url":"_astro/6cca0055d8112d032cb64a39c99314e8.ChdyOHsi.woff2"},{"revision":null,"url":"_astro/6c9d122ae88e93ec5aa7b5f9e30ec809.BR8112gD.woff2"},{"revision":null,"url":"_astro/6c8432daf49acb058ce0524ec7caa2ce.LBZA2ZGU.woff2"},{"revision":null,"url":"_astro/6b6957d0be5f2ad3c70f7d16abc5db4e.BIkJY-oQ.woff2"},{"revision":null,"url":"_astro/6b4a027abe59e37b4a601aefa813c457.CIchla6q.woff2"},{"revision":null,"url":"_astro/6b22adec943714eea6f3d1462891c76b.Dw_mm8Bk.woff2"},{"revision":null,"url":"_astro/693e8cc50bd8ca9334c9596beaa34e69.D7CW_GVi.woff2"},{"revision":null,"url":"_astro/69365138119fde8520e981b2e7022343.DAwL5-FB.woff2"},{"revision":null,"url":"_astro/68af459315ffdcc08c9adfde1765a6e4.CDWXJhzx.woff2"},{"revision":null,"url":"_astro/67f207248641834075674a7af3f2bda9.DvHfnduI.woff2"},{"revision":null,"url":"_astro/67b2af74e450c3e7dc7747b4840c3d92.CB0OYZlO.woff2"},{"revision":null,"url":"_astro/6707ecacd98f6f610e556c42ab25bc03.CNu0NEAq.woff2"},{"revision":null,"url":"_astro/667aa20fe5275d0f7b32cd19cc659658.mU4IgL0B.woff2"},{"revision":null,"url":"_astro/660290fdb8cb91d68b2355e0e108e0b2.B2iO8qJH.woff2"},{"revision":null,"url":"_astro/657bffaa4d4adfde663ae4174e8d28e3.DW93Uep1.woff2"},{"revision":null,"url":"_astro/64fcae53bcb2f59944eff4ebab77fd54.BB5dUjFI.woff2"},{"revision":null,"url":"_astro/64a09c11d4b3f49f2fed35ac0bd5215f.R6WSTs3R.woff2"},{"revision":null,"url":"_astro/63700f2ecbb2cfbac90fb381acbe5291.BZXymL7f.woff2"},{"revision":null,"url":"_astro/6345d5d44a3b7e2b144b64aad17cde2a.D6sEDxjn.woff2"},{"revision":null,"url":"_astro/62db972cd6d47902c56153bec79e23b0.W_reqkq3.woff2"},{"revision":null,"url":"_astro/62c086b02bfd861e859eb47f04e83aa6.CwtUu09n.woff2"},{"revision":null,"url":"_astro/6299112ff79802e5f8e8549fe22ce16c.BCuJjNqn.woff2"},{"revision":null,"url":"_astro/62834ea0d492cb5300cbbf44efc5a8e9.BcmgJ2j6.woff2"},{"revision":null,"url":"_astro/6202f0acb1fc24e989b49de80581cd90.BkYufQAq.woff2"},{"revision":null,"url":"_astro/61e09798c8329db8af99de9e48d7b531.R9qNq8bI.woff2"},{"revision":null,"url":"_astro/600074b0221150dd5b0d33bffc32e76d.EYJdzUW0.woff2"},{"revision":null,"url":"_astro/5ee0277db963efad764d7a479662312c.Dy3PX8yX.woff2"},{"revision":null,"url":"_astro/5e2ca114d1ad98793013edaa18552ff2.PSln3fYs.woff2"},{"revision":null,"url":"_astro/5e0f405bd14b183833cbf3546a792f5e.Dx4hbNX-.woff2"},{"revision":null,"url":"_astro/5cf217f562f6723e191b6cb95260ea30.VTn2fWFJ.woff2"},{"revision":null,"url":"_astro/5afd67baa20f3b8cc6c2bd49366ac79a.BceNt_Se.woff2"},{"revision":null,"url":"_astro/5a7c22819e17c2175e1a4bc8770f8c25.BG_A5aFU.woff2"}
2955,{"revision":null,"url":"_astro/59c29ab83d1b862cb747ce7fedfa279e.BS1Qv5ih.woff2"},{"revision":null,"url":"_astro/59b4ad1378d0c0dcbc6c0c6361b78be9.DFcZIYd3.woff2"},{"revision":null,"url":"_astro/57ca409b5982110458b2516b5c4bd6ad.CTh6jHHn.woff2"},{"revision":null,"url":"_astro/56f687dfdac5da0a0aefed1db80d4e58.DXZtdEYA.woff2"},{"revision":null,"url":"_astro/5641f14be9d16d1b9b31597d29891d83.BJwJrr6Z.woff2"},{"revision":null,"url":"_astro/55f1c5bd358f15c04387ff082bce86e8.Crqjepn4.woff2"},{"revision":null,"url":"_astro/54537a6c9342c843f9b85bf05bc2185f.RLRzlbP1.woff2"},{"revision":null,"url":"_astro/5438ed1f05fcaaf0495d861f25671f74.uRTZFTWL.woff2"},{"revision":null,"url":"_astro/53e69d0ea7fc22511afadf84b5c66842.CAJVbwtG.woff2"},{"revision":null,"url":"_astro/5287ad504bd0fa1370c314085b43526c.Bb2c8cn1.woff2"},{"revision":null,"url":"_astro/51d23a1a04b443ae8c4845d4275d8e41.Dc-rpXTc.woff2"},{"revision":null,"url":"_astro/51b07279ab3006929f0e515b737fab4b.DgCGnEjV.woff2"},{"revision":null,"url":"_astro/51117d6dfd5c148863f17547244486c4.D7J7H6SU.woff2"},{"revision":null,"url":"_astro/501ed35cdd38ac05f709eec011022790.B0Yk7LIA.woff2"},{"revision":null,"url":"_astro/4f5286db12432fa6d8f58b0702163346.Dz_tZ1xY.woff2"},{"revision":null,"url":"_astro/4e8028e6c455b85b743792396ca9880c.a38TtmcL.woff2"},{"revision":null,"url":"_astro/4e6248a6570a9123394171f97d9ebd8c.BceGM8Kt.woff2"},{"revision":null,"url":"_astro/4dde70e24c3a64c1192536fee958c9b6.Dyau0_Ns.woff2"},{"revision":null,"url":"_astro/4d903b8f9ed7be68b9b0374e9bbf77c9.BejcBHQ_.woff2"},{"revision":null,"url":"_astro/4d12b96edd37375dba2cea250a4b25e3.CVwyx5gp.woff2"},{"revision":null,"url":"_astro/4c04d7fcb047d23a4cd98ced9d839762.CjDGYgc4.woff2"},{"revision":null,"url":"_astro/4beef87f3c54ab14deeb465f1fa9bd73.Rtvrdd_w.woff2"},{"revision":null,"url":"_astro/4b4b0666000672dee7828b1fb9c27163.BqRyLvZu.woff2"},{"revision":null,"url":"_astro/4a82586d4fb6e211700ac238cb9cd755.B6w-KioT.woff2"},{"revision":null,"url":"_astro/4a7d292ecad080c2bf0d37a0b33b7f23.lJlyhi9g.woff2"},{"revision":null,"url":"_astro/49d4250019e47c31cc5875db0dbaa32b.CuAB22ni.woff2"},{"revision":null,"url":"_astro/4807e17635e955f0ab52ab4e76b7698b.CNUL7-wp.woff2"},{"revision":null,"url":"_astro/473ddd24f07f47016e02154ad18f371b.CfqTXSLD.woff2"},{"revision":null,"url":"_astro/46518d7ddb66462f4b41973b5e17409c.D6In597Q.woff2"},{"revision":null,"url":"_astro/460d619058f131b8ef3b6b382de470bb.SQNK9hP-.woff2"},{"revision":null,"url":"_astro/455b7aed93a79160421f9373d69ea945.DQktKVxy.woff2"},{"revision":null,"url":"_astro/44ab4dcef1e4f4bbea301a7f0105ccce.Ds7dqQvn.woff2"},{"revision":null,"url":"_astro/43a6b96960c64dd86fb8612735d2d0b9.CdJ6qdy5.woff2"},{"revision":null,"url":"_astro/42e2798adff5ad44aacae9f30a4d6c92.BDWNy1k9.woff2"},{"revision":null,"url":"_astro/4291ea01d000c90c18ed23edcd1a4c15.CO84emt4.woff2"},{"revision":null,"url":"_astro/427f0e86064cbe68b914856654da8e8a.D0TPkNgx.woff2"},{"revision":null,"url":"_astro/4274d4eaae4972a019be518ee25a931d.SHRVIUKD.woff2"},{"revision":null,"url":"_astro/426e7c36c0f4e05294e1a69327e0bd2b.CXNgE0v4.woff2"},{"revision":null,"url":"_astro/426c695308ffc39993c1f22772a08664.Dmiv6mVj.woff2"},{"revision":null,"url":"_astro/40f3457e449a018c65d0ac059e48b54f.BaOqRsl-.woff2"},{"revision":null,"url":"_astro/40e6246fbc5d11485fcfb54dc32c0363.DC3yoYfY.woff2"},{"revision":null,"url":"_astro/3f1dbd461b7c1e926231eda7b2aaee1e.DUnu5BGB.woff2"},{"revision":null,"url":"_astro/3e6a0ef3c3dfe8b8fbf9bb48a1495191.B42oHf78.woff2"},{"revision":null,"url":"_astro/3ce8ba2d58210c7c50e09a42c5004e34.OywkoiKB.woff2"},{"revision":null,"url":"_astro/3cab4ba353d75841d369a1349dc82f35.BQeCm8DC.woff2"},{"revision":null,"url":"_astro/3c412aa119fc85931c21df7d91ced9c1.tSA1Cti8.woff2"},{"revision":null,"url":"_astro/3a094b4809c2d00f59eb3f1b29c829bd.BcxgNpGE.woff2"},{"revision":null,"url":"_astro/394d5654057abbe8e789c97920117635.psd9a9aO.woff2"},{"revision":null,"url":"_astro/391462ae7cfe20135546c5924e3ae10a.DM2-L0Up.woff2"},{"revision":null,"url":"_astro/38dc7781102e0e245becfac0f8de8195.B-fapLaO.woff2"},{"revision":null,"url":"_astro/36348738c65453b81b0f1e922d0672c2.DjrA7_qQ.woff2"},{"revision":null,"url":"_astro/35d64fc3622b203e349358027ad7ed26.wvqweQYm.woff2"},{"revision":null,"url":"_astro/35d3d9e65bc9a183c52f94fd05a34298.Cml-JRn8.woff2"},{"revision":null,"url":"_astro/359ee3b1f2acc4f4394bc0dcb860e638.DWmJUyXo.woff2"},{"revision":null,"url":"_astro/329e2ec8d131fa86b4a38eae4400c436.BqJEuUjO.woff2"},{"revision":null,"url":"_astro/326c3c530db1e8c87c3da58205b27384.B0cjo-xW.woff2"},{"revision":null,"url":"_astro/32280a576d21714b6fd1f0fc54877723.BU-Qlo5B.woff2"},{"revision":null,"url":"_astro/31d21fea36802fd26731ba905053b5e1.WHKdftaa.woff2"},{"revision":null,"url":"_astro/31b20bb2b4b4660c5b2f61f0fb878ed2.XUfOrk-x.woff2"}
2955,{"revision":null,"url":"_astro/30ae6efd00b6d50eb170e89d369c5da4.B2JewftU.woff2"},{"revision":null,"url":"_astro/303885cb4dd323dca512a48856087537.BJ9QUTR5.woff2"},{"revision":null,"url":"_astro/2f7ece1fa44eab24788058e5967fadb9.Ddox0joj.woff2"},{"revision":null,"url":"_astro/2f5945165e5fa89b8633e63766d396cc.PPrQFvgR.woff2"},{"revision":null,"url":"_astro/2e953d39337bcf4203644c082d1a0594.8JJVJ8aW.woff2"},{"revision":null,"url":"_astro/2e34e5e187cff69c094779e98909ccbd.Cbs9bSjv.woff2"},{"revision":null,"url":"_astro/2d79daf27dd5262e45dc2b534e076e18.CirQ7_TS.woff2"},{"revision":null,"url":"_astro/2bfbe338209485e03ff6f86782cc2f0d.DQnx94Rd.woff2"},{"revision":null,"url":"_astro/2b9e9f750a3186ac278a5acc47bbf23b.DLiNFfgP.woff2"},{"revision":null,"url":"_astro/2b2e305e9a294bf33766c5a17f0ddd9c.CmvBiA3u.woff2"},{"revision":null,"url":"_astro/2aad45d305edfc63941e4390aafe19a4.GQvdK46J.woff2"},{"revision":null,"url":"_astro/2a8b00bb519456d2bccb05908240e940.CtuHGhOk.woff2"},{"revision":null,"url":"_astro/2a298e831291dd6a598b51168c358083.BU3y2cmr.woff2"},{"revision":null,"url":"_astro/294ac3315e0dc49e3e4a6916ed5d17b0.Cx3UWLja.woff2"},{"revision":null,"url":"_astro/2939ad84c5a30dee36ba8428c2019b7d.DTz4mdPg.woff2"},{"revision":null,"url":"_astro/287b99508d6ff4464137ed12558f8b6b.CjhQIQt7.woff2"},{"revision":null,"url":"_astro/27ce7719282413933ce6686e8f747b1a.W4xwQZ8Q.woff2"},{"revision":null,"url":"_astro/27166528ebc4fb74aeb087820007751c.CZhTzk0D.woff2"},{"revision":null,"url":"_astro/2671a22b21da4c0a39bfdee3ac93d512.DWM22gRC.woff2"},{"revision":null,"url":"_astro/262153698eda6d1677a28e5232378bde.BN8e-HKl.woff2"},{"revision":null,"url":"_astro/260b050a8443134b85bf1f19849158f5.B77Je7a7.woff2"},{"revision":null,"url":"_astro/253b3d53d2fdb063079dbbf8dd7e4f33.k8CKhXiF.woff2"},{"revision":null,"url":"_astro/23d32f88ee46f9f934a2488ec1442c4f.Dtl_tWby.woff2"},{"revision":null,"url":"_astro/2355396cf85e45e2e9e1b21f8517ac4e.CVk4-4z_.woff2"},{"revision":null,"url":"_astro/22d1538b36ff82be8468d166d642a4c4.BHsRsate.woff2"},{"revision":null,"url":"_astro/228df352fc34f2fe54f51a8c72337e87.BB55DhhX.woff2"},{"revision":null,"url":"_astro/21362f29d67479a523bff9a24a58bc92.Cz9Sp4M1.woff2"},{"revision":null,"url":"_astro/20a145fd28e36ff7f4814786e86d7874.DR6OxlxU.woff2"},{"revision":null,"url":"_astro/201853860f51e355ff72f8552c964c96.CCw4wcGK.woff2"},{"revision":null,"url":"_astro/1fd9cda8a04331cdd8d8c554dd855de9.haA1fokS.woff2"},{"revision":null,"url":"_astro/1f9bfc0ca4dde0fa15ba2dbe32c665fe.rAQ3B0Cz.woff2"},{"revision":null,"url":"_astro/1f5b29147c23302cecd34bec87adf23b.C4NcL3wN.woff2"},{"revision":null,"url":"_astro/1ccb869ac8c763adece9ad84e05a150a.vCGhC3BF.woff2"},{"revision":null,"url":"_astro/1ca23775faddd4640fa7c77dcfdf91c3.UpvJXqrQ.woff2"},{"revision":null,"url":"_astro/1c59b3f58d5e7a7bc27f3c575a01efb5.YZ_JdoM3.woff2"},{"revision":null,"url":"_astro/1b31ddf38192066f1aaf2809468cf98b.MVQvthkd.woff2"},{"revision":null,"url":"_astro/1adebf75f69e18f4a7c96470d95da3d5.Di2QzDSc.woff2"},{"revision":null,"url":"_astro/1aaa9111c8184b0cb9762bfcd81a9361.WQlYzs0d.woff2"},{"revision":null,"url":"_astro/196fab94b07f089780db0d63d313da53.u2SFI8-j.woff2"},{"revision":null,"url":"_astro/194d55502bf82379f699fe0d6b9f0ec5.fBarZzvB.woff2"},{"revision":null,"url":"_astro/194729d6094a4724ea62dcd2b99c1a8d.CwAqBYY3.woff2"},{"revision":null,"url":"_astro/18faece5a4fb8df5cc288be50c4f768d.QCo7MIoq.woff2"},{"revision":null,"url":"_astro/1658351b56edef7ce6a6fa9918a2d9f5.EGwXpRjW.woff2"},{"revision":null,"url":"_astro/162079c2ee90ab1639c9e31e3f528595.CGkCqn-n.woff2"},{"revision":null,"url":"_astro/160a7522c18cc9acf51b9050457356e0.DyVKSvwa.woff2"},{"revision":null,"url":"_astro/156f39e70b2579219dbe3cf02957c63a.vx7Ukdtz.woff2"},{"revision":null,"url":"_astro/1482b212487bf8a34c3a2c89088caa63.CnHTcqCB.woff2"},{"revision":null,"url":"_astro/142ad2ddfd9c9e2dcdffa982fbd8d089.Bt1A6DMt.woff2"},{"revision":null,"url":"_astro/14264c10c57a4e8ab8731facff7877a0.DLcW7Q85.woff2"},{"revision":null,"url":"_astro/13fe422cabb86e7fb47533304a1c733b.D50P5GMb.woff2"},{"revision":null,"url":"_astro/1395274a0bc8725e9c9f9527789cb565.Hdkorzws.woff2"},{"revision":null,"url":"_astro/130be03859da4f7562185f51bbaeaf8f.CwUDseZS.woff2"},{"revision":null,"url":"_astro/128dfabba7da1f854ed0e61f74d7a5ff.gsjsmJZX.woff2"},{"revision":null,"url":"_astro/127a7b7251349dc96b1a297dc1b8eac1.RI6vHyYO.woff2"},{"revision":null,"url":"_astro/12783f8265f7fdc08b7e81d1fcf54dd5.D0-164i0.woff2"},{"revision":null,"url":"_astro/121697e6fce00dc08db328eff101db83.BJEc0QJc.woff2"},{"revision":null,"url":"_astro/0f7f64f8c8ed525fc81da46fbe91846e.wl6A-LSm.woff2"},{"revision":null,"url":"_astro/0f6ade4260b39adb23911be392d328fb.DHQL_vLe.woff2"},{"revision":null,"url":"_astro/0e74f0bee002338f20c98a62550ec78a.CGneRNRR.woff2"},{"revision":null,"url":"_astro/0e7441bb24f42a46a293194448a64420.COMl2fWD.woff2"},{"revision":null,"url":"_astro/0e63fc43ea96ad55f00b2e561d3cf45c.D3J
295511xDS.woff2"},{"revision":null,"url":"_astro/0e052d5eb321b5b078f29e26ac9431fd.rxJ2UMa3.woff2"},{"revision":null,"url":"_astro/0deb80682bb502f01147132e61bdc122.BgOHUDpu.woff2"},{"revision":null,"url":"_astro/0c50381020d9590e6891739968e77d3b.Xeq-FnRu.woff2"},{"revision":null,"url":"_astro/0c42ce464f36c87f511779e9b960b8f9.D2_ZWMWL.woff2"},{"revision":null,"url":"_astro/0beae3787db54f1e9ce25c9d1cd8e6ac.B1FG1yFK.woff2"},{"revision":null,"url":"_astro/0bbd3e8edd9cc83168028cc21af34d00.B3RMI-nZ.woff2"},{"revision":null,"url":"_astro/0b84a60dfe1bb98d6d6a27c965cca5a8.Cb-U4HU8.woff2"},{"revision":null,"url":"_astro/0aa95b56f95e3ddc3b328dbf951937f2.DqeJ0Nj7.woff2"},{"revision":null,"url":"_astro/0a6d5dfe13a290d4afc8b69fca5f4590.DgBhvXQV.woff2"},{"revision":null,"url":"_astro/09609802320013e8f69366a19a7d13bb.BtTDEAEg.woff2"},{"revision":null,"url":"_astro/094b3eccf58b71b4105f90860f7b85b1.CMaoD92y.woff2"},{"revision":null,"url":"_astro/0912c393c6f009eec98646766cfc4d27.D4wZ53Qu.woff2"},{"revision":null,"url":"_astro/0697d64297e842068df4e27ee622e52a.NnJDwtIo.woff2"},{"revision":null,"url":"_astro/04a7c2f0d0f5edb15ec92dea84222f4a.1kqigEEN.woff2"},{"revision":null,"url":"_astro/03e3ca3b28326cfe4a95f7067da4447c.CqGX8CHw.woff2"},{"revision":null,"url":"_astro/038493ffe66e4e34a5b3d02b00a5a189.CDy_Mr-a.woff2"},{"revision":null,"url":"_astro/0373e623dbd35b6a425b9eeebce4c54d.C41fY3fx.woff2"},{"revision":null,"url":"_astro/03696db054b35553c1eb56737007c73e.CEBjo5NE.woff2"},{"revision":null,"url":"_astro/03163167e684fc2632d1872370bcc208.B83W5DfL.woff2"},{"revision":null,"url":"_astro/02c858bb77c7d97d857c4919aa664256.BP3f9lrO.woff2"},{"revision":null,"url":"_astro/013133458d8bff752f948ecb6cd05f37.D5oHfPbF.woff2"},{"revision":null,"url":"_astro/00799f84d031710cfa62c7e70bc23a37.E7rK4khI.woff2"},{"revision":null,"url":"_astro/001d7dfc173d83977c289df204ec4eb3.htUeu1rX.woff2"},{"revision":null,"url":"/offline/index.html"},{"revision":null,"url":"/404.html"},{"revision":"6d8609ea51b1cd8ea6452fcb6fc3806a","url":"manifest.webmanifest"},{"revision":"6d8609ea51b1cd8ea6452fcb6fc3806a","url":"manifest.webmanifest"},{"revision":"6d8609ea51b1cd8ea6452fcb6fc3806a","url":"manifest.webmanifest"},{"revision":"6d8609ea51b1cd8ea6452fcb6fc3806a","url":"manifest.webmanifest"},{"revision":"6d8609ea51b1cd8ea6452fcb6fc3806a","url":"manifest.webmanifest"}]); 2956 2957const OFFLINE_PAGE = '/offline/index.html'; 2958const PAGE_CACHE = 'pages-cache'; 2959 2960const navigationRoute = new NavigationRoute(async ({ event }) => { 2961 const request = event.request; 2962 2963 try { 2964 const response = await fetch(request); 2965 2966 if (response.ok) { 2967 const responseClone = response.clone(); 2968 const cache = await caches.open(PAGE_CACHE); 2969 await cache.put(request, responseClone); 2970 } 2971 2972 return response; 2973 } catch (error) { 2974 console.log('Network request failed, serving offline page:', error); 2975 2976 const cachedResponse = await caches.match(request); 2977 if (cachedResponse) { 2978 return cachedResponse; 2979 } 2980 2981 const cachedOffline = await matchPrecache(OFFLINE_PAGE); 2982 if (cachedOffline) { 2983 return cachedOffline; 2984 } 2985 2986 return new Response('Offline', { status: 503 }); 2987 } 2988}, { 2989 denylist: [ 2990 /^\/.well-known\/host\.txt$/, 2991 /^\/.well-known\//, 2992 /^\/api\//, 2993 /^\/admin\//, 2994 ], 2995}); 2996 2997registerRoute(navigationRoute); 2998 2999registerRoute( 3000 /\/blog\/.*/i, 3001 new NetworkFirst({ 3002 cacheName: 'blog-posts-cache', 3003 networkTimeoutSeconds: 10, 3004 plugins: [ 3005 new CacheableResponsePlugin({ 3006 statuses: [0, 200], 3007 }), 3008 new ExpirationPlugin({ 3009 maxEntries: 100, 3010 maxAgeSeconds: 60 * 60 * 24 * 30, 3011 }), 3012 ], 3013 }) 3014); 3015 3016registerRoute( 3017 /\.(?:png|jpg|jpeg|gif|webp|svg|ico)$/i, 3018 new CacheFirst({ 3019 cacheName: 'images-cache', 3020 plugins: [ 3021 new CacheableResponsePlugin({ 3022 statuses: [0, 200], 3023 }), 3024 new ExpirationPlugin({ 3025 maxEntries: 100, 3026 maxAgeSeconds: 60 * 60 * 24 * 30, 3027 }), 3028 ], 3029 }) 3030); 3031 3032registerRoute( 3033 /\.(?:js|css)$/i, 3034 new NetworkFirst({ 3035 cacheName: 'static-resources-cache', 3036 networkTimeoutSeconds: 10, 3037 plugins: [ 3038 new CacheableResponsePlugin({ 3039 statuses: [0, 200], 3040 }), 3041 new ExpirationPlugin({ 3042 maxEntries: 100,
3043 maxAgeSeconds: 60 * 60 * 24 * 30, 3044 }), 3045 ], 3046 }) 3047); 3048 3049registerRoute( 3050 /\/(rss\.xml|sitemap-index\.xml|sitemap-\d+\.xml)$/i, 3051 new NetworkFirst({ 3052 cacheName: 'seo-files-cache', 3053 networkTimeoutSeconds: 10, 3054 plugins: [ 3055 new CacheableResponsePlugin({ 3056 statuses: [0, 200], 3057 }), 3058 new ExpirationPlugin({ 3059 maxEntries: 10, 3060 maxAgeSeconds: 60 * 60 * 24, 3061 }), 3062 ], 3063 }) 3064); 3065 3066self.addEventListener('install', () => { 3067 console.log('Service Worker installed'); 3068 self.skipWaiting(); 3069}); 3070 3071self.addEventListener('activate', (event) => { 3072 console.log('Service Worker activated'); 3073 event.waitUntil(self.clients.claim()); 3074});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.