keating 0.3.6 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/dist/src/cli/main.js +3 -0
  2. package/dist/src/core/config.js +4 -1
  3. package/dist/src/core/evolution.js +3 -31
  4. package/dist/src/core/map-elites.js +2 -29
  5. package/dist/src/core/map.js +3 -1
  6. package/dist/src/core/mutation.js +29 -0
  7. package/dist/src/core/policy.js +40 -14
  8. package/dist/src/core/project.js +21 -23
  9. package/dist/src/core/topics.js +6 -1
  10. package/dist/src/pi/hyperteacher-extension.js +1 -1
  11. package/dist/src/runtime/pi.js +6 -1
  12. package/package.json +1 -1
  13. package/web/dist/assets/{anthropic-BT6Vfzb1.js → anthropic-mDOoXVZn.js} +1 -1
  14. package/web/dist/assets/arc-BgYOxBdg.js +1 -0
  15. package/web/dist/assets/architecture-YZFGNWBL-MLoSDt-a.js +1 -0
  16. package/web/dist/assets/architectureDiagram-Q4EWVU46-Yh6i8NsV.js +36 -0
  17. package/web/dist/assets/{azure-openai-responses-CommX3YJ.js → azure-openai-responses-lDjfaDoj.js} +1 -1
  18. package/web/dist/assets/band-mY0qgSES.js +1 -0
  19. package/web/dist/assets/blockDiagram-DXYQGD6D-DUOEnS09.js +132 -0
  20. package/web/dist/assets/{c4Diagram-AHTNJAMY-VFfRZWWA.js → c4Diagram-AHTNJAMY-XzNOUnLt.js} +6 -6
  21. package/web/dist/assets/channel-BumC_1wb.js +1 -0
  22. package/web/dist/assets/{chunk-2KRD3SAO-B-AqvS0u.js → chunk-2KRD3SAO-mLbXjVje.js} +1 -1
  23. package/web/dist/assets/chunk-336JU56O-CQcnUKNW.js +2 -0
  24. package/web/dist/assets/chunk-426QAEUC-AjoT_j_q.js +1 -0
  25. package/web/dist/assets/{chunk-4BX2VUAB-0Z13aFAn.js → chunk-4BX2VUAB-DRZjGMDM.js} +1 -1
  26. package/web/dist/assets/{chunk-4TB4RGXK-DqC0Zwm7.js → chunk-4TB4RGXK-CAwQq2uM.js} +6 -6
  27. package/web/dist/assets/chunk-55IACEB6-C19HKnvd.js +1 -0
  28. package/web/dist/assets/{chunk-5FUZZQ4R-CApli0xX.js → chunk-5FUZZQ4R-Bw3BFJJs.js} +31 -31
  29. package/web/dist/assets/chunk-5PVQY5BW-Ciel-YVc.js +2 -0
  30. package/web/dist/assets/{chunk-67CJDMHE-Cx7uJS4d.js → chunk-67CJDMHE-DNZKE79k.js} +1 -1
  31. package/web/dist/assets/{chunk-7N4EOEYR-CYPNsFus.js → chunk-7N4EOEYR-BGZXIfBD.js} +1 -1
  32. package/web/dist/assets/{chunk-AA7GKIK3-rU0uhR_u.js → chunk-AA7GKIK3-CLWjm_Gk.js} +1 -1
  33. package/web/dist/assets/{chunk-BSJP7CBP-5VmcfR4-.js → chunk-BSJP7CBP-DwT1eVSc.js} +1 -1
  34. package/web/dist/assets/{chunk-CIAEETIT-CHJ-L8H1.js → chunk-CIAEETIT-DaQsGVwH.js} +1 -1
  35. package/web/dist/assets/{chunk-EDXVE4YY-DZHAJjMI.js → chunk-EDXVE4YY-DNeRJTiS.js} +1 -1
  36. package/web/dist/assets/{chunk-ENJZ2VHE-DbUDFa7w.js → chunk-ENJZ2VHE-Crfow0Ab.js} +6 -6
  37. package/web/dist/assets/{chunk-FMBD7UC4-BsYE5e_h.js → chunk-FMBD7UC4-BWSstEYx.js} +1 -1
  38. package/web/dist/assets/{chunk-FOC6F5B3-Cm6aoTv7.js → chunk-FOC6F5B3-BrTsf5ix.js} +1 -1
  39. package/web/dist/assets/{chunk-ICPOFSXX-C5eNZ4L6.js → chunk-ICPOFSXX-C04NFfJk.js} +5 -5
  40. package/web/dist/assets/{chunk-K5T4RW27-R7dAJ4rq.js → chunk-K5T4RW27-C6ErEFB0.js} +1 -1
  41. package/web/dist/assets/{chunk-KGLVRYIC-MO99YZXL.js → chunk-KGLVRYIC-DWgMrjZr.js} +1 -1
  42. package/web/dist/assets/{chunk-LIHQZDEY-DUJ656sT.js → chunk-LIHQZDEY-D3DWGnJT.js} +1 -1
  43. package/web/dist/assets/{chunk-ORNJ4GCN-DXuuEC1n.js → chunk-ORNJ4GCN-D0nhq27e.js} +1 -1
  44. package/web/dist/assets/{chunk-OYMX7WX6-pJlEprWq.js → chunk-OYMX7WX6-CQLx0cU3.js} +5 -5
  45. package/web/dist/assets/chunk-QZHKN3VN-X2zPgEFk.js +1 -0
  46. package/web/dist/assets/{chunk-U2HBQHQK-Mh_l9PLe.js → chunk-U2HBQHQK-FqDOW_Dq.js} +6 -6
  47. package/web/dist/assets/{chunk-X2U36JSP-BOeiJW0w.js → chunk-X2U36JSP-DiSftNGg.js} +1 -1
  48. package/web/dist/assets/{chunk-XPW4576I-fQ9SDvr_.js → chunk-XPW4576I-CTCFDURY.js} +1 -1
  49. package/web/dist/assets/chunk-YZCP3GAM-BhxShnp0.js +1 -0
  50. package/web/dist/assets/chunk-ZZ45TVLE-DMfGJuPz.js +1 -0
  51. package/web/dist/assets/classDiagram-6PBFFD2Q-qESk_xyE.js +1 -0
  52. package/web/dist/assets/classDiagram-v2-HSJHXN6E-BM1nmtfm.js +1 -0
  53. package/web/dist/assets/clone-CVy2b825.js +1 -0
  54. package/web/dist/assets/cose-bilkent-S5V4N54A-BWrDpSLZ.js +1 -0
  55. package/web/dist/assets/custom-tts-DA7XkgNJ.js +1 -0
  56. package/web/dist/assets/dagre-C_Y5Jrtl.js +1 -0
  57. package/web/dist/assets/dagre-KV5264BT-DcrvRRX1.js +4 -0
  58. package/web/dist/assets/diagram-5BDNPKRD-BsChRck6.js +10 -0
  59. package/web/dist/assets/{diagram-G4DWMVQ6-CtICKUFi.js → diagram-G4DWMVQ6-raeAsgoe.js} +12 -12
  60. package/web/dist/assets/diagram-MMDJMWI5-CuKsP84I.js +43 -0
  61. package/web/dist/assets/diagram-TYMM5635-T7ILcS3d.js +24 -0
  62. package/web/dist/assets/dist-DQ41Rxbw.js +1 -0
  63. package/web/dist/assets/dist-oYdMaa1U.js +10 -0
  64. package/web/dist/assets/{erDiagram-SMLLAGMA-uT88sBlT.js → erDiagram-SMLLAGMA-C9z6kYxH.js} +6 -6
  65. package/web/dist/assets/{flatten-C-u5nd5-.js → flatten-n72VCU_F.js} +1 -1
  66. package/web/dist/assets/flowDiagram-DWJPFMVM-BA3HBIvp.js +162 -0
  67. package/web/dist/assets/ganttDiagram-T4ZO3ILL-BXKKRsbk.js +292 -0
  68. package/web/dist/assets/gemini-live-dK40vCc5.js +2 -0
  69. package/web/dist/assets/gitGraph-7Q5UKJZL-Cy0KloCM.js +1 -0
  70. package/web/dist/assets/gitGraphDiagram-UUTBAWPF-CfGAccI8.js +106 -0
  71. package/web/dist/assets/{google-BdYNeCP_.js → google-BdS4iTyC.js} +1 -1
  72. package/web/dist/assets/{google-gemini-cli-DpxAL3K4.js → google-gemini-cli-ECA0sP3z.js} +1 -1
  73. package/web/dist/assets/{google-shared-DyQdgtsI.js → google-shared-DFv4ubOG.js} +1 -1
  74. package/web/dist/assets/{google-vertex-CKRybaXj.js → google-vertex-Duei77qD.js} +1 -1
  75. package/web/dist/assets/graphlib-sK54_wxh.js +1 -0
  76. package/web/dist/assets/index-B_MGHHww.js +3187 -0
  77. package/web/dist/assets/index-C_k9n6xi.css +2 -0
  78. package/web/dist/assets/info-OMHHGYJF-DiTlPM51.js +1 -0
  79. package/web/dist/assets/{infoDiagram-42DDH7IO-BbES7X_c.js → infoDiagram-42DDH7IO-CXoykyfr.js} +1 -1
  80. package/web/dist/assets/init-DugVGdkd.js +1 -0
  81. package/web/dist/assets/{isEmpty-DssUW35f.js → isEmpty-Lz_aOZWI.js} +1 -1
  82. package/web/dist/assets/{ishikawaDiagram-UXIWVN3A-DxQ28rho.js → ishikawaDiagram-UXIWVN3A-BJieXVMt.js} +6 -6
  83. package/web/dist/assets/{journeyDiagram-VCZTEJTY-D0X8qQ0P.js → journeyDiagram-VCZTEJTY-CwCyrnqT.js} +6 -6
  84. package/web/dist/assets/{kanban-definition-6JOO6SKY-DWYfSlpl.js → kanban-definition-6JOO6SKY-8ACmDHsl.js} +1 -1
  85. package/web/dist/assets/line-BqZCcbso.js +1 -0
  86. package/web/dist/assets/linear-CXf5Bx3D.js +1 -0
  87. package/web/dist/assets/math-Csj_9zpx.js +1 -0
  88. package/web/dist/assets/{mermaid-parser.core-Cy4iY_Dy.js → mermaid-parser.core-C8ylgsWV.js} +2 -2
  89. package/web/dist/assets/mermaid.core-BHCT6V24.js +11 -0
  90. package/web/dist/assets/{mindmap-definition-QFDTVHPH-BBnKdtQh.js → mindmap-definition-QFDTVHPH-DrDAZL9Q.js} +1 -1
  91. package/web/dist/assets/{mistral-BWaUMIgd.js → mistral-BCIirCkW.js} +1 -1
  92. package/web/dist/assets/{openai-codex-responses-CHBgKhmb.js → openai-codex-responses-BgRTWVD_.js} +1 -1
  93. package/web/dist/assets/{openai-completions-kcXmmaHI.js → openai-completions-D5AUQEua.js} +1 -1
  94. package/web/dist/assets/openai-realtime-BCZmIToJ.js +1 -0
  95. package/web/dist/assets/{openai-responses-Cqq3H3p3.js → openai-responses-CTlQ1a-G.js} +1 -1
  96. package/web/dist/assets/{openai-responses-shared-CTNuo9ci.js → openai-responses-shared-CLzQyVyy.js} +1 -1
  97. package/web/dist/assets/openai-tts-BRg2goic.js +1 -0
  98. package/web/dist/assets/{ordinal-_K3x1fkz.js → ordinal-CHOvK70a.js} +1 -1
  99. package/web/dist/assets/ort.bundle.min-Drf2fDrC.js +2805 -0
  100. package/web/dist/assets/packet-4T2RLAQJ-EXgAtQjm.js +1 -0
  101. package/web/dist/assets/path-BVVvcbiy.js +1 -0
  102. package/web/dist/assets/pie-ZZUOXDRM-dKU8mzlj.js +1 -0
  103. package/web/dist/assets/pieDiagram-DEJITSTG-CSnulv9o.js +30 -0
  104. package/web/dist/assets/{quadrantDiagram-34T5L4WZ-DfBSEept.js → quadrantDiagram-34T5L4WZ-DiM-1bw4.js} +6 -6
  105. package/web/dist/assets/radar-PYXPWWZC-B4d2wh2H.js +1 -0
  106. package/web/dist/assets/{reduce-836A2NiQ.js → reduce-r7gP4wdg.js} +1 -1
  107. package/web/dist/assets/renderer-BHoCfdWO-BFpsm4dw.js +1 -0
  108. package/web/dist/assets/requirementDiagram-MS252O5E-CunJUnIN.js +84 -0
  109. package/web/dist/assets/{sankeyDiagram-XADWPNL6-He3x9tNT.js → sankeyDiagram-XADWPNL6-DaA_56zq.js} +7 -7
  110. package/web/dist/assets/{sequenceDiagram-FGHM5R23-DfCDpvrT.js → sequenceDiagram-FGHM5R23-CG_SeqVO.js} +13 -13
  111. package/web/dist/assets/speech-ZNExxXBR.js +2 -0
  112. package/web/dist/assets/src-BfaOho0B.js +1 -0
  113. package/web/dist/assets/state-CrA0YVPh-wJ4Rmm6M.js +27 -0
  114. package/web/dist/assets/stateDiagram-FHFEXIEX-CSUigMtP.js +1 -0
  115. package/web/dist/assets/stateDiagram-v2-QKLJ7IA2-CgpYzp7D.js +1 -0
  116. package/web/dist/assets/step-qalMyYGy.js +1 -0
  117. package/web/dist/assets/string-DPssBiak.js +1 -0
  118. package/web/dist/assets/supertonic-hDdNdgtO.js +2 -0
  119. package/web/dist/assets/time-DaCo2z7n.js +1 -0
  120. package/web/dist/assets/{timeline-definition-GMOUNBTQ-BWunHgBC.js → timeline-definition-GMOUNBTQ-DcMiH-gG.js} +7 -7
  121. package/web/dist/assets/transformers.web-R9eWI1xg.js +14 -0
  122. package/web/dist/assets/treeView-SZITEDCU-V29H5f16.js +1 -0
  123. package/web/dist/assets/treemap-W4RFUUIX-DsAU74TP.js +1 -0
  124. package/web/dist/assets/vennDiagram-DHZGUBPP-C6A75Q3q.js +34 -0
  125. package/web/dist/assets/wardley-RL74JXVD-lGAI4Cps.js +1 -0
  126. package/web/dist/assets/{wardleyDiagram-NUSXRM2D-DTcVscPH.js → wardleyDiagram-NUSXRM2D-XmYD0oYm.js} +1 -1
  127. package/web/dist/assets/xychartDiagram-5P7HB3ND-BgSoKcS9.js +7 -0
  128. package/web/dist/index.html +16 -3
  129. package/web/dist/sw.js +647 -1
  130. package/web/dist/workbox-2ae722a1.js +4390 -0
  131. package/web/dist/assets/arc-x2nTilpc.js +0 -1
  132. package/web/dist/assets/architecture-YZFGNWBL-B1hlUWjX.js +0 -1
  133. package/web/dist/assets/architectureDiagram-Q4EWVU46-CMApWFyw.js +0 -36
  134. package/web/dist/assets/blockDiagram-DXYQGD6D-DOQbsNRY.js +0 -132
  135. package/web/dist/assets/channel-KY2Tg8Ba.js +0 -1
  136. package/web/dist/assets/chunk-336JU56O-DlYgPyl6.js +0 -2
  137. package/web/dist/assets/chunk-426QAEUC-CsVoBkfR.js +0 -1
  138. package/web/dist/assets/chunk-55IACEB6-CWE_u-IY.js +0 -1
  139. package/web/dist/assets/chunk-5PVQY5BW-Cbzhfhln.js +0 -2
  140. package/web/dist/assets/chunk-QZHKN3VN-_pQxbbiW.js +0 -1
  141. package/web/dist/assets/chunk-YZCP3GAM-eboO4P5S.js +0 -1
  142. package/web/dist/assets/chunk-ZZ45TVLE-Cky0eqlr.js +0 -1
  143. package/web/dist/assets/classDiagram-6PBFFD2Q-DEPsZSU3.js +0 -1
  144. package/web/dist/assets/classDiagram-v2-HSJHXN6E-DhmIOEpX.js +0 -1
  145. package/web/dist/assets/clone-DeTzYqo8.js +0 -1
  146. package/web/dist/assets/cose-bilkent-S5V4N54A-N4zWUJ7C.js +0 -1
  147. package/web/dist/assets/dagre-IpK1aoMm.js +0 -1
  148. package/web/dist/assets/dagre-KV5264BT-DCytJuju.js +0 -4
  149. package/web/dist/assets/diagram-5BDNPKRD-Cv4miBae.js +0 -10
  150. package/web/dist/assets/diagram-MMDJMWI5-Cn7aGorh.js +0 -43
  151. package/web/dist/assets/diagram-TYMM5635-CCUWDPsC.js +0 -24
  152. package/web/dist/assets/dist-Dm98VvTW.js +0 -1
  153. package/web/dist/assets/flowDiagram-DWJPFMVM-Bl3O7S1m.js +0 -162
  154. package/web/dist/assets/ganttDiagram-T4ZO3ILL-B1FhwV45.js +0 -292
  155. package/web/dist/assets/gitGraph-7Q5UKJZL-Bc_7vzer.js +0 -1
  156. package/web/dist/assets/gitGraphDiagram-UUTBAWPF-DfW6svMS.js +0 -106
  157. package/web/dist/assets/graphlib-CMTVFyOZ.js +0 -1
  158. package/web/dist/assets/index-Bdb7P7gx.css +0 -2
  159. package/web/dist/assets/index-DNxepp8B.js +0 -2891
  160. package/web/dist/assets/info-OMHHGYJF-BGcxeaZt.js +0 -1
  161. package/web/dist/assets/init-DlZdxViB.js +0 -1
  162. package/web/dist/assets/line-CuHce5JG.js +0 -1
  163. package/web/dist/assets/linear-Ca0Vkwuj.js +0 -1
  164. package/web/dist/assets/mermaid.core-6PGkQdYc.js +0 -11
  165. package/web/dist/assets/packet-4T2RLAQJ-D35ZLSBH.js +0 -1
  166. package/web/dist/assets/path-6uRLdFF7.js +0 -1
  167. package/web/dist/assets/pie-ZZUOXDRM-DRoETpJX.js +0 -1
  168. package/web/dist/assets/pieDiagram-DEJITSTG-DfMjfTQz.js +0 -30
  169. package/web/dist/assets/radar-PYXPWWZC-DLKxRJ0V.js +0 -1
  170. package/web/dist/assets/requirementDiagram-MS252O5E-BPkxJQkz.js +0 -84
  171. package/web/dist/assets/src-DdOdIreR.js +0 -1
  172. package/web/dist/assets/stateDiagram-FHFEXIEX-fuww6347.js +0 -1
  173. package/web/dist/assets/stateDiagram-v2-QKLJ7IA2-U6voafO3.js +0 -1
  174. package/web/dist/assets/transformers.web-DKUtmSAi.js +0 -2818
  175. package/web/dist/assets/treeView-SZITEDCU-BCx0xSAm.js +0 -1
  176. package/web/dist/assets/treemap-W4RFUUIX-2CvghWJK.js +0 -1
  177. package/web/dist/assets/vennDiagram-DHZGUBPP-CBXRutSP.js +0 -34
  178. package/web/dist/assets/wardley-RL74JXVD-BkPL_mhd.js +0 -1
  179. package/web/dist/assets/xychartDiagram-5P7HB3ND-CZLgX9Fe.js +0 -7
  180. package/web/dist/workbox-66610c77.js +0 -1
  181. /package/web/dist/assets/{_baseFor-B_cjfMB6.js → _baseFor-2QgQJAzW.js} +0 -0
  182. /package/web/dist/assets/{array-B9UHiPd-.js → array-B4O_ir3H.js} +0 -0
  183. /package/web/dist/assets/{cytoscape.esm-BBMd0vGm.js → cytoscape.esm-CdzdaUMP.js} +0 -0
  184. /package/web/dist/assets/{defaultLocale-5eAKkKJC.js → defaultLocale-Bhb3osZR.js} +0 -0
  185. /package/web/dist/assets/{github-copilot-headers-L39QqneT.js → github-copilot-headers-BuUn5v2j.js} +0 -0
  186. /package/web/dist/assets/{hash-kZ2KD_no.js → hash-CQjDwcjR.js} +0 -0
  187. /package/web/dist/assets/{openai-D4NSaQIs.js → openai-CRGaNv9i.js} +0 -0
  188. /package/web/dist/assets/{rough.esm-Djo4Abte.js → rough.esm-D5NinLFK.js} +0 -0
  189. /package/web/dist/assets/{transform-messages-CqKEdRVp.js → transform-messages-DXDWLrnO.js} +0 -0
  190. /package/web/dist/assets/{web-CMKYLKbT.js → web-jLdm8a1-.js} +0 -0
@@ -0,0 +1,4390 @@
1
+ define(['exports'], (function (exports) { 'use strict';
2
+
3
+ // @ts-ignore
4
+ try {
5
+ self['workbox:core:7.3.0'] && _();
6
+ } catch (e) {}
7
+
8
+ /*
9
+ Copyright 2019 Google LLC
10
+ Use of this source code is governed by an MIT-style
11
+ license that can be found in the LICENSE file or at
12
+ https://opensource.org/licenses/MIT.
13
+ */
14
+ const logger = (() => {
15
+ // Don't overwrite this value if it's already set.
16
+ // See https://github.com/GoogleChrome/workbox/pull/2284#issuecomment-560470923
17
+ if (!('__WB_DISABLE_DEV_LOGS' in globalThis)) {
18
+ self.__WB_DISABLE_DEV_LOGS = false;
19
+ }
20
+ let inGroup = false;
21
+ const methodToColorMap = {
22
+ debug: `#7f8c8d`,
23
+ log: `#2ecc71`,
24
+ warn: `#f39c12`,
25
+ error: `#c0392b`,
26
+ groupCollapsed: `#3498db`,
27
+ groupEnd: null // No colored prefix on groupEnd
28
+ };
29
+ const print = function (method, args) {
30
+ if (self.__WB_DISABLE_DEV_LOGS) {
31
+ return;
32
+ }
33
+ if (method === 'groupCollapsed') {
34
+ // Safari doesn't print all console.groupCollapsed() arguments:
35
+ // https://bugs.webkit.org/show_bug.cgi?id=182754
36
+ if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
37
+ console[method](...args);
38
+ return;
39
+ }
40
+ }
41
+ const styles = [`background: ${methodToColorMap[method]}`, `border-radius: 0.5em`, `color: white`, `font-weight: bold`, `padding: 2px 0.5em`];
42
+ // When in a group, the workbox prefix is not displayed.
43
+ const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')];
44
+ console[method](...logPrefix, ...args);
45
+ if (method === 'groupCollapsed') {
46
+ inGroup = true;
47
+ }
48
+ if (method === 'groupEnd') {
49
+ inGroup = false;
50
+ }
51
+ };
52
+ // eslint-disable-next-line @typescript-eslint/ban-types
53
+ const api = {};
54
+ const loggerMethods = Object.keys(methodToColorMap);
55
+ for (const key of loggerMethods) {
56
+ const method = key;
57
+ api[method] = (...args) => {
58
+ print(method, args);
59
+ };
60
+ }
61
+ return api;
62
+ })();
63
+
64
+ /*
65
+ Copyright 2018 Google LLC
66
+
67
+ Use of this source code is governed by an MIT-style
68
+ license that can be found in the LICENSE file or at
69
+ https://opensource.org/licenses/MIT.
70
+ */
71
+ const messages$1 = {
72
+ 'invalid-value': ({
73
+ paramName,
74
+ validValueDescription,
75
+ value
76
+ }) => {
77
+ if (!paramName || !validValueDescription) {
78
+ throw new Error(`Unexpected input to 'invalid-value' error.`);
79
+ }
80
+ return `The '${paramName}' parameter was given a value with an ` + `unexpected value. ${validValueDescription} Received a value of ` + `${JSON.stringify(value)}.`;
81
+ },
82
+ 'not-an-array': ({
83
+ moduleName,
84
+ className,
85
+ funcName,
86
+ paramName
87
+ }) => {
88
+ if (!moduleName || !className || !funcName || !paramName) {
89
+ throw new Error(`Unexpected input to 'not-an-array' error.`);
90
+ }
91
+ return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className}.${funcName}()' must be an array.`;
92
+ },
93
+ 'incorrect-type': ({
94
+ expectedType,
95
+ paramName,
96
+ moduleName,
97
+ className,
98
+ funcName
99
+ }) => {
100
+ if (!expectedType || !paramName || !moduleName || !funcName) {
101
+ throw new Error(`Unexpected input to 'incorrect-type' error.`);
102
+ }
103
+ const classNameStr = className ? `${className}.` : '';
104
+ return `The parameter '${paramName}' passed into ` + `'${moduleName}.${classNameStr}` + `${funcName}()' must be of type ${expectedType}.`;
105
+ },
106
+ 'incorrect-class': ({
107
+ expectedClassName,
108
+ paramName,
109
+ moduleName,
110
+ className,
111
+ funcName,
112
+ isReturnValueProblem
113
+ }) => {
114
+ if (!expectedClassName || !moduleName || !funcName) {
115
+ throw new Error(`Unexpected input to 'incorrect-class' error.`);
116
+ }
117
+ const classNameStr = className ? `${className}.` : '';
118
+ if (isReturnValueProblem) {
119
+ return `The return value from ` + `'${moduleName}.${classNameStr}${funcName}()' ` + `must be an instance of class ${expectedClassName}.`;
120
+ }
121
+ return `The parameter '${paramName}' passed into ` + `'${moduleName}.${classNameStr}${funcName}()' ` + `must be an instance of class ${expectedClassName}.`;
122
+ },
123
+ 'missing-a-method': ({
124
+ expectedMethod,
125
+ paramName,
126
+ moduleName,
127
+ className,
128
+ funcName
129
+ }) => {
130
+ if (!expectedMethod || !paramName || !moduleName || !className || !funcName) {
131
+ throw new Error(`Unexpected input to 'missing-a-method' error.`);
132
+ }
133
+ return `${moduleName}.${className}.${funcName}() expected the ` + `'${paramName}' parameter to expose a '${expectedMethod}' method.`;
134
+ },
135
+ 'add-to-cache-list-unexpected-type': ({
136
+ entry
137
+ }) => {
138
+ return `An unexpected entry was passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' The entry ` + `'${JSON.stringify(entry)}' isn't supported. You must supply an array of ` + `strings with one or more characters, objects with a url property or ` + `Request objects.`;
139
+ },
140
+ 'add-to-cache-list-conflicting-entries': ({
141
+ firstEntry,
142
+ secondEntry
143
+ }) => {
144
+ if (!firstEntry || !secondEntry) {
145
+ throw new Error(`Unexpected input to ` + `'add-to-cache-list-duplicate-entries' error.`);
146
+ }
147
+ return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${firstEntry} but different revision details. Workbox is ` + `unable to cache and version the asset correctly. Please remove one ` + `of the entries.`;
148
+ },
149
+ 'plugin-error-request-will-fetch': ({
150
+ thrownErrorMessage
151
+ }) => {
152
+ if (!thrownErrorMessage) {
153
+ throw new Error(`Unexpected input to ` + `'plugin-error-request-will-fetch', error.`);
154
+ }
155
+ return `An error was thrown by a plugins 'requestWillFetch()' method. ` + `The thrown error message was: '${thrownErrorMessage}'.`;
156
+ },
157
+ 'invalid-cache-name': ({
158
+ cacheNameId,
159
+ value
160
+ }) => {
161
+ if (!cacheNameId) {
162
+ throw new Error(`Expected a 'cacheNameId' for error 'invalid-cache-name'`);
163
+ }
164
+ return `You must provide a name containing at least one character for ` + `setCacheDetails({${cacheNameId}: '...'}). Received a value of ` + `'${JSON.stringify(value)}'`;
165
+ },
166
+ 'unregister-route-but-not-found-with-method': ({
167
+ method
168
+ }) => {
169
+ if (!method) {
170
+ throw new Error(`Unexpected input to ` + `'unregister-route-but-not-found-with-method' error.`);
171
+ }
172
+ return `The route you're trying to unregister was not previously ` + `registered for the method type '${method}'.`;
173
+ },
174
+ 'unregister-route-route-not-registered': () => {
175
+ return `The route you're trying to unregister was not previously ` + `registered.`;
176
+ },
177
+ 'queue-replay-failed': ({
178
+ name
179
+ }) => {
180
+ return `Replaying the background sync queue '${name}' failed.`;
181
+ },
182
+ 'duplicate-queue-name': ({
183
+ name
184
+ }) => {
185
+ return `The Queue name '${name}' is already being used. ` + `All instances of backgroundSync.Queue must be given unique names.`;
186
+ },
187
+ 'expired-test-without-max-age': ({
188
+ methodName,
189
+ paramName
190
+ }) => {
191
+ return `The '${methodName}()' method can only be used when the ` + `'${paramName}' is used in the constructor.`;
192
+ },
193
+ 'unsupported-route-type': ({
194
+ moduleName,
195
+ className,
196
+ funcName,
197
+ paramName
198
+ }) => {
199
+ return `The supplied '${paramName}' parameter was an unsupported type. ` + `Please check the docs for ${moduleName}.${className}.${funcName} for ` + `valid input types.`;
200
+ },
201
+ 'not-array-of-class': ({
202
+ value,
203
+ expectedClass,
204
+ moduleName,
205
+ className,
206
+ funcName,
207
+ paramName
208
+ }) => {
209
+ return `The supplied '${paramName}' parameter must be an array of ` + `'${expectedClass}' objects. Received '${JSON.stringify(value)},'. ` + `Please check the call to ${moduleName}.${className}.${funcName}() ` + `to fix the issue.`;
210
+ },
211
+ 'max-entries-or-age-required': ({
212
+ moduleName,
213
+ className,
214
+ funcName
215
+ }) => {
216
+ return `You must define either config.maxEntries or config.maxAgeSeconds` + `in ${moduleName}.${className}.${funcName}`;
217
+ },
218
+ 'statuses-or-headers-required': ({
219
+ moduleName,
220
+ className,
221
+ funcName
222
+ }) => {
223
+ return `You must define either config.statuses or config.headers` + `in ${moduleName}.${className}.${funcName}`;
224
+ },
225
+ 'invalid-string': ({
226
+ moduleName,
227
+ funcName,
228
+ paramName
229
+ }) => {
230
+ if (!paramName || !moduleName || !funcName) {
231
+ throw new Error(`Unexpected input to 'invalid-string' error.`);
232
+ }
233
+ return `When using strings, the '${paramName}' parameter must start with ` + `'http' (for cross-origin matches) or '/' (for same-origin matches). ` + `Please see the docs for ${moduleName}.${funcName}() for ` + `more info.`;
234
+ },
235
+ 'channel-name-required': () => {
236
+ return `You must provide a channelName to construct a ` + `BroadcastCacheUpdate instance.`;
237
+ },
238
+ 'invalid-responses-are-same-args': () => {
239
+ return `The arguments passed into responsesAreSame() appear to be ` + `invalid. Please ensure valid Responses are used.`;
240
+ },
241
+ 'expire-custom-caches-only': () => {
242
+ return `You must provide a 'cacheName' property when using the ` + `expiration plugin with a runtime caching strategy.`;
243
+ },
244
+ 'unit-must-be-bytes': ({
245
+ normalizedRangeHeader
246
+ }) => {
247
+ if (!normalizedRangeHeader) {
248
+ throw new Error(`Unexpected input to 'unit-must-be-bytes' error.`);
249
+ }
250
+ return `The 'unit' portion of the Range header must be set to 'bytes'. ` + `The Range header provided was "${normalizedRangeHeader}"`;
251
+ },
252
+ 'single-range-only': ({
253
+ normalizedRangeHeader
254
+ }) => {
255
+ if (!normalizedRangeHeader) {
256
+ throw new Error(`Unexpected input to 'single-range-only' error.`);
257
+ }
258
+ return `Multiple ranges are not supported. Please use a single start ` + `value, and optional end value. The Range header provided was ` + `"${normalizedRangeHeader}"`;
259
+ },
260
+ 'invalid-range-values': ({
261
+ normalizedRangeHeader
262
+ }) => {
263
+ if (!normalizedRangeHeader) {
264
+ throw new Error(`Unexpected input to 'invalid-range-values' error.`);
265
+ }
266
+ return `The Range header is missing both start and end values. At least ` + `one of those values is needed. The Range header provided was ` + `"${normalizedRangeHeader}"`;
267
+ },
268
+ 'no-range-header': () => {
269
+ return `No Range header was found in the Request provided.`;
270
+ },
271
+ 'range-not-satisfiable': ({
272
+ size,
273
+ start,
274
+ end
275
+ }) => {
276
+ return `The start (${start}) and end (${end}) values in the Range are ` + `not satisfiable by the cached response, which is ${size} bytes.`;
277
+ },
278
+ 'attempt-to-cache-non-get-request': ({
279
+ url,
280
+ method
281
+ }) => {
282
+ return `Unable to cache '${url}' because it is a '${method}' request and ` + `only 'GET' requests can be cached.`;
283
+ },
284
+ 'cache-put-with-no-response': ({
285
+ url
286
+ }) => {
287
+ return `There was an attempt to cache '${url}' but the response was not ` + `defined.`;
288
+ },
289
+ 'no-response': ({
290
+ url,
291
+ error
292
+ }) => {
293
+ let message = `The strategy could not generate a response for '${url}'.`;
294
+ if (error) {
295
+ message += ` The underlying error is ${error}.`;
296
+ }
297
+ return message;
298
+ },
299
+ 'bad-precaching-response': ({
300
+ url,
301
+ status
302
+ }) => {
303
+ return `The precaching request for '${url}' failed` + (status ? ` with an HTTP status of ${status}.` : `.`);
304
+ },
305
+ 'non-precached-url': ({
306
+ url
307
+ }) => {
308
+ return `createHandlerBoundToURL('${url}') was called, but that URL is ` + `not precached. Please pass in a URL that is precached instead.`;
309
+ },
310
+ 'add-to-cache-list-conflicting-integrities': ({
311
+ url
312
+ }) => {
313
+ return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${url} with different integrity values. Please remove one of them.`;
314
+ },
315
+ 'missing-precache-entry': ({
316
+ cacheName,
317
+ url
318
+ }) => {
319
+ return `Unable to find a precached response in ${cacheName} for ${url}.`;
320
+ },
321
+ 'cross-origin-copy-response': ({
322
+ origin
323
+ }) => {
324
+ return `workbox-core.copyResponse() can only be used with same-origin ` + `responses. It was passed a response with origin ${origin}.`;
325
+ },
326
+ 'opaque-streams-source': ({
327
+ type
328
+ }) => {
329
+ const message = `One of the workbox-streams sources resulted in an ` + `'${type}' response.`;
330
+ if (type === 'opaqueredirect') {
331
+ return `${message} Please do not use a navigation request that results ` + `in a redirect as a source.`;
332
+ }
333
+ return `${message} Please ensure your sources are CORS-enabled.`;
334
+ }
335
+ };
336
+
337
+ /*
338
+ Copyright 2018 Google LLC
339
+
340
+ Use of this source code is governed by an MIT-style
341
+ license that can be found in the LICENSE file or at
342
+ https://opensource.org/licenses/MIT.
343
+ */
344
+ const generatorFunction = (code, details = {}) => {
345
+ const message = messages$1[code];
346
+ if (!message) {
347
+ throw new Error(`Unable to find message for code '${code}'.`);
348
+ }
349
+ return message(details);
350
+ };
351
+ const messageGenerator = generatorFunction;
352
+
353
+ /*
354
+ Copyright 2018 Google LLC
355
+
356
+ Use of this source code is governed by an MIT-style
357
+ license that can be found in the LICENSE file or at
358
+ https://opensource.org/licenses/MIT.
359
+ */
360
+ /**
361
+ * Workbox errors should be thrown with this class.
362
+ * This allows use to ensure the type easily in tests,
363
+ * helps developers identify errors from workbox
364
+ * easily and allows use to optimise error
365
+ * messages correctly.
366
+ *
367
+ * @private
368
+ */
369
+ class WorkboxError extends Error {
370
+ /**
371
+ *
372
+ * @param {string} errorCode The error code that
373
+ * identifies this particular error.
374
+ * @param {Object=} details Any relevant arguments
375
+ * that will help developers identify issues should
376
+ * be added as a key on the context object.
377
+ */
378
+ constructor(errorCode, details) {
379
+ const message = messageGenerator(errorCode, details);
380
+ super(message);
381
+ this.name = errorCode;
382
+ this.details = details;
383
+ }
384
+ }
385
+
386
+ /*
387
+ Copyright 2018 Google LLC
388
+
389
+ Use of this source code is governed by an MIT-style
390
+ license that can be found in the LICENSE file or at
391
+ https://opensource.org/licenses/MIT.
392
+ */
393
+ /*
394
+ * This method throws if the supplied value is not an array.
395
+ * The destructed values are required to produce a meaningful error for users.
396
+ * The destructed and restructured object is so it's clear what is
397
+ * needed.
398
+ */
399
+ const isArray = (value, details) => {
400
+ if (!Array.isArray(value)) {
401
+ throw new WorkboxError('not-an-array', details);
402
+ }
403
+ };
404
+ const hasMethod = (object, expectedMethod, details) => {
405
+ const type = typeof object[expectedMethod];
406
+ if (type !== 'function') {
407
+ details['expectedMethod'] = expectedMethod;
408
+ throw new WorkboxError('missing-a-method', details);
409
+ }
410
+ };
411
+ const isType = (object, expectedType, details) => {
412
+ if (typeof object !== expectedType) {
413
+ details['expectedType'] = expectedType;
414
+ throw new WorkboxError('incorrect-type', details);
415
+ }
416
+ };
417
+ const isInstance = (object,
418
+ // Need the general type to do the check later.
419
+ // eslint-disable-next-line @typescript-eslint/ban-types
420
+ expectedClass, details) => {
421
+ if (!(object instanceof expectedClass)) {
422
+ details['expectedClassName'] = expectedClass.name;
423
+ throw new WorkboxError('incorrect-class', details);
424
+ }
425
+ };
426
+ const isOneOf = (value, validValues, details) => {
427
+ if (!validValues.includes(value)) {
428
+ details['validValueDescription'] = `Valid values are ${JSON.stringify(validValues)}.`;
429
+ throw new WorkboxError('invalid-value', details);
430
+ }
431
+ };
432
+ const isArrayOfClass = (value,
433
+ // Need general type to do check later.
434
+ expectedClass,
435
+ // eslint-disable-line
436
+ details) => {
437
+ const error = new WorkboxError('not-array-of-class', details);
438
+ if (!Array.isArray(value)) {
439
+ throw error;
440
+ }
441
+ for (const item of value) {
442
+ if (!(item instanceof expectedClass)) {
443
+ throw error;
444
+ }
445
+ }
446
+ };
447
+ const finalAssertExports = {
448
+ hasMethod,
449
+ isArray,
450
+ isInstance,
451
+ isOneOf,
452
+ isType,
453
+ isArrayOfClass
454
+ };
455
+
456
+ // @ts-ignore
457
+ try {
458
+ self['workbox:routing:7.3.0'] && _();
459
+ } catch (e) {}
460
+
461
+ /*
462
+ Copyright 2018 Google LLC
463
+
464
+ Use of this source code is governed by an MIT-style
465
+ license that can be found in the LICENSE file or at
466
+ https://opensource.org/licenses/MIT.
467
+ */
468
+ /**
469
+ * The default HTTP method, 'GET', used when there's no specific method
470
+ * configured for a route.
471
+ *
472
+ * @type {string}
473
+ *
474
+ * @private
475
+ */
476
+ const defaultMethod = 'GET';
477
+ /**
478
+ * The list of valid HTTP methods associated with requests that could be routed.
479
+ *
480
+ * @type {Array<string>}
481
+ *
482
+ * @private
483
+ */
484
+ const validMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT'];
485
+
486
+ /*
487
+ Copyright 2018 Google LLC
488
+
489
+ Use of this source code is governed by an MIT-style
490
+ license that can be found in the LICENSE file or at
491
+ https://opensource.org/licenses/MIT.
492
+ */
493
+ /**
494
+ * @param {function()|Object} handler Either a function, or an object with a
495
+ * 'handle' method.
496
+ * @return {Object} An object with a handle method.
497
+ *
498
+ * @private
499
+ */
500
+ const normalizeHandler = handler => {
501
+ if (handler && typeof handler === 'object') {
502
+ {
503
+ finalAssertExports.hasMethod(handler, 'handle', {
504
+ moduleName: 'workbox-routing',
505
+ className: 'Route',
506
+ funcName: 'constructor',
507
+ paramName: 'handler'
508
+ });
509
+ }
510
+ return handler;
511
+ } else {
512
+ {
513
+ finalAssertExports.isType(handler, 'function', {
514
+ moduleName: 'workbox-routing',
515
+ className: 'Route',
516
+ funcName: 'constructor',
517
+ paramName: 'handler'
518
+ });
519
+ }
520
+ return {
521
+ handle: handler
522
+ };
523
+ }
524
+ };
525
+
526
+ /*
527
+ Copyright 2018 Google LLC
528
+
529
+ Use of this source code is governed by an MIT-style
530
+ license that can be found in the LICENSE file or at
531
+ https://opensource.org/licenses/MIT.
532
+ */
533
+ /**
534
+ * A `Route` consists of a pair of callback functions, "match" and "handler".
535
+ * The "match" callback determine if a route should be used to "handle" a
536
+ * request by returning a non-falsy value if it can. The "handler" callback
537
+ * is called when there is a match and should return a Promise that resolves
538
+ * to a `Response`.
539
+ *
540
+ * @memberof workbox-routing
541
+ */
542
+ class Route {
543
+ /**
544
+ * Constructor for Route class.
545
+ *
546
+ * @param {workbox-routing~matchCallback} match
547
+ * A callback function that determines whether the route matches a given
548
+ * `fetch` event by returning a non-falsy value.
549
+ * @param {workbox-routing~handlerCallback} handler A callback
550
+ * function that returns a Promise resolving to a Response.
551
+ * @param {string} [method='GET'] The HTTP method to match the Route
552
+ * against.
553
+ */
554
+ constructor(match, handler, method = defaultMethod) {
555
+ {
556
+ finalAssertExports.isType(match, 'function', {
557
+ moduleName: 'workbox-routing',
558
+ className: 'Route',
559
+ funcName: 'constructor',
560
+ paramName: 'match'
561
+ });
562
+ if (method) {
563
+ finalAssertExports.isOneOf(method, validMethods, {
564
+ paramName: 'method'
565
+ });
566
+ }
567
+ }
568
+ // These values are referenced directly by Router so cannot be
569
+ // altered by minificaton.
570
+ this.handler = normalizeHandler(handler);
571
+ this.match = match;
572
+ this.method = method;
573
+ }
574
+ /**
575
+ *
576
+ * @param {workbox-routing-handlerCallback} handler A callback
577
+ * function that returns a Promise resolving to a Response
578
+ */
579
+ setCatchHandler(handler) {
580
+ this.catchHandler = normalizeHandler(handler);
581
+ }
582
+ }
583
+
584
+ /*
585
+ Copyright 2018 Google LLC
586
+
587
+ Use of this source code is governed by an MIT-style
588
+ license that can be found in the LICENSE file or at
589
+ https://opensource.org/licenses/MIT.
590
+ */
591
+ /**
592
+ * RegExpRoute makes it easy to create a regular expression based
593
+ * {@link workbox-routing.Route}.
594
+ *
595
+ * For same-origin requests the RegExp only needs to match part of the URL. For
596
+ * requests against third-party servers, you must define a RegExp that matches
597
+ * the start of the URL.
598
+ *
599
+ * @memberof workbox-routing
600
+ * @extends workbox-routing.Route
601
+ */
602
+ class RegExpRoute extends Route {
603
+ /**
604
+ * If the regular expression contains
605
+ * [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references},
606
+ * the captured values will be passed to the
607
+ * {@link workbox-routing~handlerCallback} `params`
608
+ * argument.
609
+ *
610
+ * @param {RegExp} regExp The regular expression to match against URLs.
611
+ * @param {workbox-routing~handlerCallback} handler A callback
612
+ * function that returns a Promise resulting in a Response.
613
+ * @param {string} [method='GET'] The HTTP method to match the Route
614
+ * against.
615
+ */
616
+ constructor(regExp, handler, method) {
617
+ {
618
+ finalAssertExports.isInstance(regExp, RegExp, {
619
+ moduleName: 'workbox-routing',
620
+ className: 'RegExpRoute',
621
+ funcName: 'constructor',
622
+ paramName: 'pattern'
623
+ });
624
+ }
625
+ const match = ({
626
+ url
627
+ }) => {
628
+ const result = regExp.exec(url.href);
629
+ // Return immediately if there's no match.
630
+ if (!result) {
631
+ return;
632
+ }
633
+ // Require that the match start at the first character in the URL string
634
+ // if it's a cross-origin request.
635
+ // See https://github.com/GoogleChrome/workbox/issues/281 for the context
636
+ // behind this behavior.
637
+ if (url.origin !== location.origin && result.index !== 0) {
638
+ {
639
+ logger.debug(`The regular expression '${regExp.toString()}' only partially matched ` + `against the cross-origin URL '${url.toString()}'. RegExpRoute's will only ` + `handle cross-origin requests if they match the entire URL.`);
640
+ }
641
+ return;
642
+ }
643
+ // If the route matches, but there aren't any capture groups defined, then
644
+ // this will return [], which is truthy and therefore sufficient to
645
+ // indicate a match.
646
+ // If there are capture groups, then it will return their values.
647
+ return result.slice(1);
648
+ };
649
+ super(match, handler, method);
650
+ }
651
+ }
652
+
653
+ /*
654
+ Copyright 2018 Google LLC
655
+
656
+ Use of this source code is governed by an MIT-style
657
+ license that can be found in the LICENSE file or at
658
+ https://opensource.org/licenses/MIT.
659
+ */
660
+ const getFriendlyURL = url => {
661
+ const urlObj = new URL(String(url), location.href);
662
+ // See https://github.com/GoogleChrome/workbox/issues/2323
663
+ // We want to include everything, except for the origin if it's same-origin.
664
+ return urlObj.href.replace(new RegExp(`^${location.origin}`), '');
665
+ };
666
+
667
+ /*
668
+ Copyright 2018 Google LLC
669
+
670
+ Use of this source code is governed by an MIT-style
671
+ license that can be found in the LICENSE file or at
672
+ https://opensource.org/licenses/MIT.
673
+ */
674
+ /**
675
+ * The Router can be used to process a `FetchEvent` using one or more
676
+ * {@link workbox-routing.Route}, responding with a `Response` if
677
+ * a matching route exists.
678
+ *
679
+ * If no route matches a given a request, the Router will use a "default"
680
+ * handler if one is defined.
681
+ *
682
+ * Should the matching Route throw an error, the Router will use a "catch"
683
+ * handler if one is defined to gracefully deal with issues and respond with a
684
+ * Request.
685
+ *
686
+ * If a request matches multiple routes, the **earliest** registered route will
687
+ * be used to respond to the request.
688
+ *
689
+ * @memberof workbox-routing
690
+ */
691
+ class Router {
692
+ /**
693
+ * Initializes a new Router.
694
+ */
695
+ constructor() {
696
+ this._routes = new Map();
697
+ this._defaultHandlerMap = new Map();
698
+ }
699
+ /**
700
+ * @return {Map<string, Array<workbox-routing.Route>>} routes A `Map` of HTTP
701
+ * method name ('GET', etc.) to an array of all the corresponding `Route`
702
+ * instances that are registered.
703
+ */
704
+ get routes() {
705
+ return this._routes;
706
+ }
707
+ /**
708
+ * Adds a fetch event listener to respond to events when a route matches
709
+ * the event's request.
710
+ */
711
+ addFetchListener() {
712
+ // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705
713
+ self.addEventListener('fetch', event => {
714
+ const {
715
+ request
716
+ } = event;
717
+ const responsePromise = this.handleRequest({
718
+ request,
719
+ event
720
+ });
721
+ if (responsePromise) {
722
+ event.respondWith(responsePromise);
723
+ }
724
+ });
725
+ }
726
+ /**
727
+ * Adds a message event listener for URLs to cache from the window.
728
+ * This is useful to cache resources loaded on the page prior to when the
729
+ * service worker started controlling it.
730
+ *
731
+ * The format of the message data sent from the window should be as follows.
732
+ * Where the `urlsToCache` array may consist of URL strings or an array of
733
+ * URL string + `requestInit` object (the same as you'd pass to `fetch()`).
734
+ *
735
+ * ```
736
+ * {
737
+ * type: 'CACHE_URLS',
738
+ * payload: {
739
+ * urlsToCache: [
740
+ * './script1.js',
741
+ * './script2.js',
742
+ * ['./script3.js', {mode: 'no-cors'}],
743
+ * ],
744
+ * },
745
+ * }
746
+ * ```
747
+ */
748
+ addCacheListener() {
749
+ // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705
750
+ self.addEventListener('message', event => {
751
+ // event.data is type 'any'
752
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
753
+ if (event.data && event.data.type === 'CACHE_URLS') {
754
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
755
+ const {
756
+ payload
757
+ } = event.data;
758
+ {
759
+ logger.debug(`Caching URLs from the window`, payload.urlsToCache);
760
+ }
761
+ const requestPromises = Promise.all(payload.urlsToCache.map(entry => {
762
+ if (typeof entry === 'string') {
763
+ entry = [entry];
764
+ }
765
+ const request = new Request(...entry);
766
+ return this.handleRequest({
767
+ request,
768
+ event
769
+ });
770
+ // TODO(philipwalton): TypeScript errors without this typecast for
771
+ // some reason (probably a bug). The real type here should work but
772
+ // doesn't: `Array<Promise<Response> | undefined>`.
773
+ })); // TypeScript
774
+ event.waitUntil(requestPromises);
775
+ // If a MessageChannel was used, reply to the message on success.
776
+ if (event.ports && event.ports[0]) {
777
+ void requestPromises.then(() => event.ports[0].postMessage(true));
778
+ }
779
+ }
780
+ });
781
+ }
782
+ /**
783
+ * Apply the routing rules to a FetchEvent object to get a Response from an
784
+ * appropriate Route's handler.
785
+ *
786
+ * @param {Object} options
787
+ * @param {Request} options.request The request to handle.
788
+ * @param {ExtendableEvent} options.event The event that triggered the
789
+ * request.
790
+ * @return {Promise<Response>|undefined} A promise is returned if a
791
+ * registered route can handle the request. If there is no matching
792
+ * route and there's no `defaultHandler`, `undefined` is returned.
793
+ */
794
+ handleRequest({
795
+ request,
796
+ event
797
+ }) {
798
+ {
799
+ finalAssertExports.isInstance(request, Request, {
800
+ moduleName: 'workbox-routing',
801
+ className: 'Router',
802
+ funcName: 'handleRequest',
803
+ paramName: 'options.request'
804
+ });
805
+ }
806
+ const url = new URL(request.url, location.href);
807
+ if (!url.protocol.startsWith('http')) {
808
+ {
809
+ logger.debug(`Workbox Router only supports URLs that start with 'http'.`);
810
+ }
811
+ return;
812
+ }
813
+ const sameOrigin = url.origin === location.origin;
814
+ const {
815
+ params,
816
+ route
817
+ } = this.findMatchingRoute({
818
+ event,
819
+ request,
820
+ sameOrigin,
821
+ url
822
+ });
823
+ let handler = route && route.handler;
824
+ const debugMessages = [];
825
+ {
826
+ if (handler) {
827
+ debugMessages.push([`Found a route to handle this request:`, route]);
828
+ if (params) {
829
+ debugMessages.push([`Passing the following params to the route's handler:`, params]);
830
+ }
831
+ }
832
+ }
833
+ // If we don't have a handler because there was no matching route, then
834
+ // fall back to defaultHandler if that's defined.
835
+ const method = request.method;
836
+ if (!handler && this._defaultHandlerMap.has(method)) {
837
+ {
838
+ debugMessages.push(`Failed to find a matching route. Falling ` + `back to the default handler for ${method}.`);
839
+ }
840
+ handler = this._defaultHandlerMap.get(method);
841
+ }
842
+ if (!handler) {
843
+ {
844
+ // No handler so Workbox will do nothing. If logs is set of debug
845
+ // i.e. verbose, we should print out this information.
846
+ logger.debug(`No route found for: ${getFriendlyURL(url)}`);
847
+ }
848
+ return;
849
+ }
850
+ {
851
+ // We have a handler, meaning Workbox is going to handle the route.
852
+ // print the routing details to the console.
853
+ logger.groupCollapsed(`Router is responding to: ${getFriendlyURL(url)}`);
854
+ debugMessages.forEach(msg => {
855
+ if (Array.isArray(msg)) {
856
+ logger.log(...msg);
857
+ } else {
858
+ logger.log(msg);
859
+ }
860
+ });
861
+ logger.groupEnd();
862
+ }
863
+ // Wrap in try and catch in case the handle method throws a synchronous
864
+ // error. It should still callback to the catch handler.
865
+ let responsePromise;
866
+ try {
867
+ responsePromise = handler.handle({
868
+ url,
869
+ request,
870
+ event,
871
+ params
872
+ });
873
+ } catch (err) {
874
+ responsePromise = Promise.reject(err);
875
+ }
876
+ // Get route's catch handler, if it exists
877
+ const catchHandler = route && route.catchHandler;
878
+ if (responsePromise instanceof Promise && (this._catchHandler || catchHandler)) {
879
+ responsePromise = responsePromise.catch(async err => {
880
+ // If there's a route catch handler, process that first
881
+ if (catchHandler) {
882
+ {
883
+ // Still include URL here as it will be async from the console group
884
+ // and may not make sense without the URL
885
+ logger.groupCollapsed(`Error thrown when responding to: ` + ` ${getFriendlyURL(url)}. Falling back to route's Catch Handler.`);
886
+ logger.error(`Error thrown by:`, route);
887
+ logger.error(err);
888
+ logger.groupEnd();
889
+ }
890
+ try {
891
+ return await catchHandler.handle({
892
+ url,
893
+ request,
894
+ event,
895
+ params
896
+ });
897
+ } catch (catchErr) {
898
+ if (catchErr instanceof Error) {
899
+ err = catchErr;
900
+ }
901
+ }
902
+ }
903
+ if (this._catchHandler) {
904
+ {
905
+ // Still include URL here as it will be async from the console group
906
+ // and may not make sense without the URL
907
+ logger.groupCollapsed(`Error thrown when responding to: ` + ` ${getFriendlyURL(url)}. Falling back to global Catch Handler.`);
908
+ logger.error(`Error thrown by:`, route);
909
+ logger.error(err);
910
+ logger.groupEnd();
911
+ }
912
+ return this._catchHandler.handle({
913
+ url,
914
+ request,
915
+ event
916
+ });
917
+ }
918
+ throw err;
919
+ });
920
+ }
921
+ return responsePromise;
922
+ }
923
+ /**
924
+ * Checks a request and URL (and optionally an event) against the list of
925
+ * registered routes, and if there's a match, returns the corresponding
926
+ * route along with any params generated by the match.
927
+ *
928
+ * @param {Object} options
929
+ * @param {URL} options.url
930
+ * @param {boolean} options.sameOrigin The result of comparing `url.origin`
931
+ * against the current origin.
932
+ * @param {Request} options.request The request to match.
933
+ * @param {Event} options.event The corresponding event.
934
+ * @return {Object} An object with `route` and `params` properties.
935
+ * They are populated if a matching route was found or `undefined`
936
+ * otherwise.
937
+ */
938
+ findMatchingRoute({
939
+ url,
940
+ sameOrigin,
941
+ request,
942
+ event
943
+ }) {
944
+ const routes = this._routes.get(request.method) || [];
945
+ for (const route of routes) {
946
+ let params;
947
+ // route.match returns type any, not possible to change right now.
948
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
949
+ const matchResult = route.match({
950
+ url,
951
+ sameOrigin,
952
+ request,
953
+ event
954
+ });
955
+ if (matchResult) {
956
+ {
957
+ // Warn developers that using an async matchCallback is almost always
958
+ // not the right thing to do.
959
+ if (matchResult instanceof Promise) {
960
+ logger.warn(`While routing ${getFriendlyURL(url)}, an async ` + `matchCallback function was used. Please convert the ` + `following route to use a synchronous matchCallback function:`, route);
961
+ }
962
+ }
963
+ // See https://github.com/GoogleChrome/workbox/issues/2079
964
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
965
+ params = matchResult;
966
+ if (Array.isArray(params) && params.length === 0) {
967
+ // Instead of passing an empty array in as params, use undefined.
968
+ params = undefined;
969
+ } else if (matchResult.constructor === Object &&
970
+ // eslint-disable-line
971
+ Object.keys(matchResult).length === 0) {
972
+ // Instead of passing an empty object in as params, use undefined.
973
+ params = undefined;
974
+ } else if (typeof matchResult === 'boolean') {
975
+ // For the boolean value true (rather than just something truth-y),
976
+ // don't set params.
977
+ // See https://github.com/GoogleChrome/workbox/pull/2134#issuecomment-513924353
978
+ params = undefined;
979
+ }
980
+ // Return early if have a match.
981
+ return {
982
+ route,
983
+ params
984
+ };
985
+ }
986
+ }
987
+ // If no match was found above, return and empty object.
988
+ return {};
989
+ }
990
+ /**
991
+ * Define a default `handler` that's called when no routes explicitly
992
+ * match the incoming request.
993
+ *
994
+ * Each HTTP method ('GET', 'POST', etc.) gets its own default handler.
995
+ *
996
+ * Without a default handler, unmatched requests will go against the
997
+ * network as if there were no service worker present.
998
+ *
999
+ * @param {workbox-routing~handlerCallback} handler A callback
1000
+ * function that returns a Promise resulting in a Response.
1001
+ * @param {string} [method='GET'] The HTTP method to associate with this
1002
+ * default handler. Each method has its own default.
1003
+ */
1004
+ setDefaultHandler(handler, method = defaultMethod) {
1005
+ this._defaultHandlerMap.set(method, normalizeHandler(handler));
1006
+ }
1007
+ /**
1008
+ * If a Route throws an error while handling a request, this `handler`
1009
+ * will be called and given a chance to provide a response.
1010
+ *
1011
+ * @param {workbox-routing~handlerCallback} handler A callback
1012
+ * function that returns a Promise resulting in a Response.
1013
+ */
1014
+ setCatchHandler(handler) {
1015
+ this._catchHandler = normalizeHandler(handler);
1016
+ }
1017
+ /**
1018
+ * Registers a route with the router.
1019
+ *
1020
+ * @param {workbox-routing.Route} route The route to register.
1021
+ */
1022
+ registerRoute(route) {
1023
+ {
1024
+ finalAssertExports.isType(route, 'object', {
1025
+ moduleName: 'workbox-routing',
1026
+ className: 'Router',
1027
+ funcName: 'registerRoute',
1028
+ paramName: 'route'
1029
+ });
1030
+ finalAssertExports.hasMethod(route, 'match', {
1031
+ moduleName: 'workbox-routing',
1032
+ className: 'Router',
1033
+ funcName: 'registerRoute',
1034
+ paramName: 'route'
1035
+ });
1036
+ finalAssertExports.isType(route.handler, 'object', {
1037
+ moduleName: 'workbox-routing',
1038
+ className: 'Router',
1039
+ funcName: 'registerRoute',
1040
+ paramName: 'route'
1041
+ });
1042
+ finalAssertExports.hasMethod(route.handler, 'handle', {
1043
+ moduleName: 'workbox-routing',
1044
+ className: 'Router',
1045
+ funcName: 'registerRoute',
1046
+ paramName: 'route.handler'
1047
+ });
1048
+ finalAssertExports.isType(route.method, 'string', {
1049
+ moduleName: 'workbox-routing',
1050
+ className: 'Router',
1051
+ funcName: 'registerRoute',
1052
+ paramName: 'route.method'
1053
+ });
1054
+ }
1055
+ if (!this._routes.has(route.method)) {
1056
+ this._routes.set(route.method, []);
1057
+ }
1058
+ // Give precedence to all of the earlier routes by adding this additional
1059
+ // route to the end of the array.
1060
+ this._routes.get(route.method).push(route);
1061
+ }
1062
+ /**
1063
+ * Unregisters a route with the router.
1064
+ *
1065
+ * @param {workbox-routing.Route} route The route to unregister.
1066
+ */
1067
+ unregisterRoute(route) {
1068
+ if (!this._routes.has(route.method)) {
1069
+ throw new WorkboxError('unregister-route-but-not-found-with-method', {
1070
+ method: route.method
1071
+ });
1072
+ }
1073
+ const routeIndex = this._routes.get(route.method).indexOf(route);
1074
+ if (routeIndex > -1) {
1075
+ this._routes.get(route.method).splice(routeIndex, 1);
1076
+ } else {
1077
+ throw new WorkboxError('unregister-route-route-not-registered');
1078
+ }
1079
+ }
1080
+ }
1081
+
1082
+ /*
1083
+ Copyright 2019 Google LLC
1084
+
1085
+ Use of this source code is governed by an MIT-style
1086
+ license that can be found in the LICENSE file or at
1087
+ https://opensource.org/licenses/MIT.
1088
+ */
1089
+ let defaultRouter;
1090
+ /**
1091
+ * Creates a new, singleton Router instance if one does not exist. If one
1092
+ * does already exist, that instance is returned.
1093
+ *
1094
+ * @private
1095
+ * @return {Router}
1096
+ */
1097
+ const getOrCreateDefaultRouter = () => {
1098
+ if (!defaultRouter) {
1099
+ defaultRouter = new Router();
1100
+ // The helpers that use the default Router assume these listeners exist.
1101
+ defaultRouter.addFetchListener();
1102
+ defaultRouter.addCacheListener();
1103
+ }
1104
+ return defaultRouter;
1105
+ };
1106
+
1107
+ /*
1108
+ Copyright 2019 Google LLC
1109
+
1110
+ Use of this source code is governed by an MIT-style
1111
+ license that can be found in the LICENSE file or at
1112
+ https://opensource.org/licenses/MIT.
1113
+ */
1114
+ /**
1115
+ * Easily register a RegExp, string, or function with a caching
1116
+ * strategy to a singleton Router instance.
1117
+ *
1118
+ * This method will generate a Route for you if needed and
1119
+ * call {@link workbox-routing.Router#registerRoute}.
1120
+ *
1121
+ * @param {RegExp|string|workbox-routing.Route~matchCallback|workbox-routing.Route} capture
1122
+ * If the capture param is a `Route`, all other arguments will be ignored.
1123
+ * @param {workbox-routing~handlerCallback} [handler] A callback
1124
+ * function that returns a Promise resulting in a Response. This parameter
1125
+ * is required if `capture` is not a `Route` object.
1126
+ * @param {string} [method='GET'] The HTTP method to match the Route
1127
+ * against.
1128
+ * @return {workbox-routing.Route} The generated `Route`.
1129
+ *
1130
+ * @memberof workbox-routing
1131
+ */
1132
+ function registerRoute(capture, handler, method) {
1133
+ let route;
1134
+ if (typeof capture === 'string') {
1135
+ const captureUrl = new URL(capture, location.href);
1136
+ {
1137
+ if (!(capture.startsWith('/') || capture.startsWith('http'))) {
1138
+ throw new WorkboxError('invalid-string', {
1139
+ moduleName: 'workbox-routing',
1140
+ funcName: 'registerRoute',
1141
+ paramName: 'capture'
1142
+ });
1143
+ }
1144
+ // We want to check if Express-style wildcards are in the pathname only.
1145
+ // TODO: Remove this log message in v4.
1146
+ const valueToCheck = capture.startsWith('http') ? captureUrl.pathname : capture;
1147
+ // See https://github.com/pillarjs/path-to-regexp#parameters
1148
+ const wildcards = '[*:?+]';
1149
+ if (new RegExp(`${wildcards}`).exec(valueToCheck)) {
1150
+ logger.debug(`The '$capture' parameter contains an Express-style wildcard ` + `character (${wildcards}). Strings are now always interpreted as ` + `exact matches; use a RegExp for partial or wildcard matches.`);
1151
+ }
1152
+ }
1153
+ const matchCallback = ({
1154
+ url
1155
+ }) => {
1156
+ {
1157
+ if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) {
1158
+ logger.debug(`${capture} only partially matches the cross-origin URL ` + `${url.toString()}. This route will only handle cross-origin requests ` + `if they match the entire URL.`);
1159
+ }
1160
+ }
1161
+ return url.href === captureUrl.href;
1162
+ };
1163
+ // If `capture` is a string then `handler` and `method` must be present.
1164
+ route = new Route(matchCallback, handler, method);
1165
+ } else if (capture instanceof RegExp) {
1166
+ // If `capture` is a `RegExp` then `handler` and `method` must be present.
1167
+ route = new RegExpRoute(capture, handler, method);
1168
+ } else if (typeof capture === 'function') {
1169
+ // If `capture` is a function then `handler` and `method` must be present.
1170
+ route = new Route(capture, handler, method);
1171
+ } else if (capture instanceof Route) {
1172
+ route = capture;
1173
+ } else {
1174
+ throw new WorkboxError('unsupported-route-type', {
1175
+ moduleName: 'workbox-routing',
1176
+ funcName: 'registerRoute',
1177
+ paramName: 'capture'
1178
+ });
1179
+ }
1180
+ const defaultRouter = getOrCreateDefaultRouter();
1181
+ defaultRouter.registerRoute(route);
1182
+ return route;
1183
+ }
1184
+
1185
+ /*
1186
+ Copyright 2018 Google LLC
1187
+
1188
+ Use of this source code is governed by an MIT-style
1189
+ license that can be found in the LICENSE file or at
1190
+ https://opensource.org/licenses/MIT.
1191
+ */
1192
+ const _cacheNameDetails = {
1193
+ googleAnalytics: 'googleAnalytics',
1194
+ precache: 'precache-v2',
1195
+ prefix: 'workbox',
1196
+ runtime: 'runtime',
1197
+ suffix: typeof registration !== 'undefined' ? registration.scope : ''
1198
+ };
1199
+ const _createCacheName = cacheName => {
1200
+ return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix].filter(value => value && value.length > 0).join('-');
1201
+ };
1202
+ const eachCacheNameDetail = fn => {
1203
+ for (const key of Object.keys(_cacheNameDetails)) {
1204
+ fn(key);
1205
+ }
1206
+ };
1207
+ const cacheNames = {
1208
+ updateDetails: details => {
1209
+ eachCacheNameDetail(key => {
1210
+ if (typeof details[key] === 'string') {
1211
+ _cacheNameDetails[key] = details[key];
1212
+ }
1213
+ });
1214
+ },
1215
+ getGoogleAnalyticsName: userCacheName => {
1216
+ return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics);
1217
+ },
1218
+ getPrecacheName: userCacheName => {
1219
+ return userCacheName || _createCacheName(_cacheNameDetails.precache);
1220
+ },
1221
+ getPrefix: () => {
1222
+ return _cacheNameDetails.prefix;
1223
+ },
1224
+ getRuntimeName: userCacheName => {
1225
+ return userCacheName || _createCacheName(_cacheNameDetails.runtime);
1226
+ },
1227
+ getSuffix: () => {
1228
+ return _cacheNameDetails.suffix;
1229
+ }
1230
+ };
1231
+
1232
+ /*
1233
+ Copyright 2019 Google LLC
1234
+ Use of this source code is governed by an MIT-style
1235
+ license that can be found in the LICENSE file or at
1236
+ https://opensource.org/licenses/MIT.
1237
+ */
1238
+ /**
1239
+ * A helper function that prevents a promise from being flagged as unused.
1240
+ *
1241
+ * @private
1242
+ **/
1243
+ function dontWaitFor(promise) {
1244
+ // Effective no-op.
1245
+ void promise.then(() => {});
1246
+ }
1247
+
1248
+ /*
1249
+ Copyright 2018 Google LLC
1250
+
1251
+ Use of this source code is governed by an MIT-style
1252
+ license that can be found in the LICENSE file or at
1253
+ https://opensource.org/licenses/MIT.
1254
+ */
1255
+ // Callbacks to be executed whenever there's a quota error.
1256
+ // Can't change Function type right now.
1257
+ // eslint-disable-next-line @typescript-eslint/ban-types
1258
+ const quotaErrorCallbacks = new Set();
1259
+
1260
+ /*
1261
+ Copyright 2019 Google LLC
1262
+
1263
+ Use of this source code is governed by an MIT-style
1264
+ license that can be found in the LICENSE file or at
1265
+ https://opensource.org/licenses/MIT.
1266
+ */
1267
+ /**
1268
+ * Adds a function to the set of quotaErrorCallbacks that will be executed if
1269
+ * there's a quota error.
1270
+ *
1271
+ * @param {Function} callback
1272
+ * @memberof workbox-core
1273
+ */
1274
+ // Can't change Function type
1275
+ // eslint-disable-next-line @typescript-eslint/ban-types
1276
+ function registerQuotaErrorCallback(callback) {
1277
+ {
1278
+ finalAssertExports.isType(callback, 'function', {
1279
+ moduleName: 'workbox-core',
1280
+ funcName: 'register',
1281
+ paramName: 'callback'
1282
+ });
1283
+ }
1284
+ quotaErrorCallbacks.add(callback);
1285
+ {
1286
+ logger.log('Registered a callback to respond to quota errors.', callback);
1287
+ }
1288
+ }
1289
+
1290
+ function _extends() {
1291
+ return _extends = Object.assign ? Object.assign.bind() : function (n) {
1292
+ for (var e = 1; e < arguments.length; e++) {
1293
+ var t = arguments[e];
1294
+ for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
1295
+ }
1296
+ return n;
1297
+ }, _extends.apply(null, arguments);
1298
+ }
1299
+
1300
+ const instanceOfAny = (object, constructors) => constructors.some(c => object instanceof c);
1301
+ let idbProxyableTypes;
1302
+ let cursorAdvanceMethods;
1303
+ // This is a function to prevent it throwing up in node environments.
1304
+ function getIdbProxyableTypes() {
1305
+ return idbProxyableTypes || (idbProxyableTypes = [IDBDatabase, IDBObjectStore, IDBIndex, IDBCursor, IDBTransaction]);
1306
+ }
1307
+ // This is a function to prevent it throwing up in node environments.
1308
+ function getCursorAdvanceMethods() {
1309
+ return cursorAdvanceMethods || (cursorAdvanceMethods = [IDBCursor.prototype.advance, IDBCursor.prototype.continue, IDBCursor.prototype.continuePrimaryKey]);
1310
+ }
1311
+ const cursorRequestMap = new WeakMap();
1312
+ const transactionDoneMap = new WeakMap();
1313
+ const transactionStoreNamesMap = new WeakMap();
1314
+ const transformCache = new WeakMap();
1315
+ const reverseTransformCache = new WeakMap();
1316
+ function promisifyRequest(request) {
1317
+ const promise = new Promise((resolve, reject) => {
1318
+ const unlisten = () => {
1319
+ request.removeEventListener('success', success);
1320
+ request.removeEventListener('error', error);
1321
+ };
1322
+ const success = () => {
1323
+ resolve(wrap(request.result));
1324
+ unlisten();
1325
+ };
1326
+ const error = () => {
1327
+ reject(request.error);
1328
+ unlisten();
1329
+ };
1330
+ request.addEventListener('success', success);
1331
+ request.addEventListener('error', error);
1332
+ });
1333
+ promise.then(value => {
1334
+ // Since cursoring reuses the IDBRequest (*sigh*), we cache it for later retrieval
1335
+ // (see wrapFunction).
1336
+ if (value instanceof IDBCursor) {
1337
+ cursorRequestMap.set(value, request);
1338
+ }
1339
+ // Catching to avoid "Uncaught Promise exceptions"
1340
+ }).catch(() => {});
1341
+ // This mapping exists in reverseTransformCache but doesn't doesn't exist in transformCache. This
1342
+ // is because we create many promises from a single IDBRequest.
1343
+ reverseTransformCache.set(promise, request);
1344
+ return promise;
1345
+ }
1346
+ function cacheDonePromiseForTransaction(tx) {
1347
+ // Early bail if we've already created a done promise for this transaction.
1348
+ if (transactionDoneMap.has(tx)) return;
1349
+ const done = new Promise((resolve, reject) => {
1350
+ const unlisten = () => {
1351
+ tx.removeEventListener('complete', complete);
1352
+ tx.removeEventListener('error', error);
1353
+ tx.removeEventListener('abort', error);
1354
+ };
1355
+ const complete = () => {
1356
+ resolve();
1357
+ unlisten();
1358
+ };
1359
+ const error = () => {
1360
+ reject(tx.error || new DOMException('AbortError', 'AbortError'));
1361
+ unlisten();
1362
+ };
1363
+ tx.addEventListener('complete', complete);
1364
+ tx.addEventListener('error', error);
1365
+ tx.addEventListener('abort', error);
1366
+ });
1367
+ // Cache it for later retrieval.
1368
+ transactionDoneMap.set(tx, done);
1369
+ }
1370
+ let idbProxyTraps = {
1371
+ get(target, prop, receiver) {
1372
+ if (target instanceof IDBTransaction) {
1373
+ // Special handling for transaction.done.
1374
+ if (prop === 'done') return transactionDoneMap.get(target);
1375
+ // Polyfill for objectStoreNames because of Edge.
1376
+ if (prop === 'objectStoreNames') {
1377
+ return target.objectStoreNames || transactionStoreNamesMap.get(target);
1378
+ }
1379
+ // Make tx.store return the only store in the transaction, or undefined if there are many.
1380
+ if (prop === 'store') {
1381
+ return receiver.objectStoreNames[1] ? undefined : receiver.objectStore(receiver.objectStoreNames[0]);
1382
+ }
1383
+ }
1384
+ // Else transform whatever we get back.
1385
+ return wrap(target[prop]);
1386
+ },
1387
+ set(target, prop, value) {
1388
+ target[prop] = value;
1389
+ return true;
1390
+ },
1391
+ has(target, prop) {
1392
+ if (target instanceof IDBTransaction && (prop === 'done' || prop === 'store')) {
1393
+ return true;
1394
+ }
1395
+ return prop in target;
1396
+ }
1397
+ };
1398
+ function replaceTraps(callback) {
1399
+ idbProxyTraps = callback(idbProxyTraps);
1400
+ }
1401
+ function wrapFunction(func) {
1402
+ // Due to expected object equality (which is enforced by the caching in `wrap`), we
1403
+ // only create one new func per func.
1404
+ // Edge doesn't support objectStoreNames (booo), so we polyfill it here.
1405
+ if (func === IDBDatabase.prototype.transaction && !('objectStoreNames' in IDBTransaction.prototype)) {
1406
+ return function (storeNames, ...args) {
1407
+ const tx = func.call(unwrap(this), storeNames, ...args);
1408
+ transactionStoreNamesMap.set(tx, storeNames.sort ? storeNames.sort() : [storeNames]);
1409
+ return wrap(tx);
1410
+ };
1411
+ }
1412
+ // Cursor methods are special, as the behaviour is a little more different to standard IDB. In
1413
+ // IDB, you advance the cursor and wait for a new 'success' on the IDBRequest that gave you the
1414
+ // cursor. It's kinda like a promise that can resolve with many values. That doesn't make sense
1415
+ // with real promises, so each advance methods returns a new promise for the cursor object, or
1416
+ // undefined if the end of the cursor has been reached.
1417
+ if (getCursorAdvanceMethods().includes(func)) {
1418
+ return function (...args) {
1419
+ // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use
1420
+ // the original object.
1421
+ func.apply(unwrap(this), args);
1422
+ return wrap(cursorRequestMap.get(this));
1423
+ };
1424
+ }
1425
+ return function (...args) {
1426
+ // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use
1427
+ // the original object.
1428
+ return wrap(func.apply(unwrap(this), args));
1429
+ };
1430
+ }
1431
+ function transformCachableValue(value) {
1432
+ if (typeof value === 'function') return wrapFunction(value);
1433
+ // This doesn't return, it just creates a 'done' promise for the transaction,
1434
+ // which is later returned for transaction.done (see idbObjectHandler).
1435
+ if (value instanceof IDBTransaction) cacheDonePromiseForTransaction(value);
1436
+ if (instanceOfAny(value, getIdbProxyableTypes())) return new Proxy(value, idbProxyTraps);
1437
+ // Return the same value back if we're not going to transform it.
1438
+ return value;
1439
+ }
1440
+ function wrap(value) {
1441
+ // We sometimes generate multiple promises from a single IDBRequest (eg when cursoring), because
1442
+ // IDB is weird and a single IDBRequest can yield many responses, so these can't be cached.
1443
+ if (value instanceof IDBRequest) return promisifyRequest(value);
1444
+ // If we've already transformed this value before, reuse the transformed value.
1445
+ // This is faster, but it also provides object equality.
1446
+ if (transformCache.has(value)) return transformCache.get(value);
1447
+ const newValue = transformCachableValue(value);
1448
+ // Not all types are transformed.
1449
+ // These may be primitive types, so they can't be WeakMap keys.
1450
+ if (newValue !== value) {
1451
+ transformCache.set(value, newValue);
1452
+ reverseTransformCache.set(newValue, value);
1453
+ }
1454
+ return newValue;
1455
+ }
1456
+ const unwrap = value => reverseTransformCache.get(value);
1457
+
1458
+ /**
1459
+ * Open a database.
1460
+ *
1461
+ * @param name Name of the database.
1462
+ * @param version Schema version.
1463
+ * @param callbacks Additional callbacks.
1464
+ */
1465
+ function openDB(name, version, {
1466
+ blocked,
1467
+ upgrade,
1468
+ blocking,
1469
+ terminated
1470
+ } = {}) {
1471
+ const request = indexedDB.open(name, version);
1472
+ const openPromise = wrap(request);
1473
+ if (upgrade) {
1474
+ request.addEventListener('upgradeneeded', event => {
1475
+ upgrade(wrap(request.result), event.oldVersion, event.newVersion, wrap(request.transaction), event);
1476
+ });
1477
+ }
1478
+ if (blocked) {
1479
+ request.addEventListener('blocked', event => blocked(
1480
+ // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405
1481
+ event.oldVersion, event.newVersion, event));
1482
+ }
1483
+ openPromise.then(db => {
1484
+ if (terminated) db.addEventListener('close', () => terminated());
1485
+ if (blocking) {
1486
+ db.addEventListener('versionchange', event => blocking(event.oldVersion, event.newVersion, event));
1487
+ }
1488
+ }).catch(() => {});
1489
+ return openPromise;
1490
+ }
1491
+ /**
1492
+ * Delete a database.
1493
+ *
1494
+ * @param name Name of the database.
1495
+ */
1496
+ function deleteDB(name, {
1497
+ blocked
1498
+ } = {}) {
1499
+ const request = indexedDB.deleteDatabase(name);
1500
+ if (blocked) {
1501
+ request.addEventListener('blocked', event => blocked(
1502
+ // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405
1503
+ event.oldVersion, event));
1504
+ }
1505
+ return wrap(request).then(() => undefined);
1506
+ }
1507
+ const readMethods = ['get', 'getKey', 'getAll', 'getAllKeys', 'count'];
1508
+ const writeMethods = ['put', 'add', 'delete', 'clear'];
1509
+ const cachedMethods = new Map();
1510
+ function getMethod(target, prop) {
1511
+ if (!(target instanceof IDBDatabase && !(prop in target) && typeof prop === 'string')) {
1512
+ return;
1513
+ }
1514
+ if (cachedMethods.get(prop)) return cachedMethods.get(prop);
1515
+ const targetFuncName = prop.replace(/FromIndex$/, '');
1516
+ const useIndex = prop !== targetFuncName;
1517
+ const isWrite = writeMethods.includes(targetFuncName);
1518
+ if (
1519
+ // Bail if the target doesn't exist on the target. Eg, getAll isn't in Edge.
1520
+ !(targetFuncName in (useIndex ? IDBIndex : IDBObjectStore).prototype) || !(isWrite || readMethods.includes(targetFuncName))) {
1521
+ return;
1522
+ }
1523
+ const method = async function (storeName, ...args) {
1524
+ // isWrite ? 'readwrite' : undefined gzipps better, but fails in Edge :(
1525
+ const tx = this.transaction(storeName, isWrite ? 'readwrite' : 'readonly');
1526
+ let target = tx.store;
1527
+ if (useIndex) target = target.index(args.shift());
1528
+ // Must reject if op rejects.
1529
+ // If it's a write operation, must reject if tx.done rejects.
1530
+ // Must reject with op rejection first.
1531
+ // Must resolve with op value.
1532
+ // Must handle both promises (no unhandled rejections)
1533
+ return (await Promise.all([target[targetFuncName](...args), isWrite && tx.done]))[0];
1534
+ };
1535
+ cachedMethods.set(prop, method);
1536
+ return method;
1537
+ }
1538
+ replaceTraps(oldTraps => _extends({}, oldTraps, {
1539
+ get: (target, prop, receiver) => getMethod(target, prop) || oldTraps.get(target, prop, receiver),
1540
+ has: (target, prop) => !!getMethod(target, prop) || oldTraps.has(target, prop)
1541
+ }));
1542
+
1543
+ // @ts-ignore
1544
+ try {
1545
+ self['workbox:expiration:7.3.0'] && _();
1546
+ } catch (e) {}
1547
+
1548
+ /*
1549
+ Copyright 2018 Google LLC
1550
+
1551
+ Use of this source code is governed by an MIT-style
1552
+ license that can be found in the LICENSE file or at
1553
+ https://opensource.org/licenses/MIT.
1554
+ */
1555
+ const DB_NAME = 'workbox-expiration';
1556
+ const CACHE_OBJECT_STORE = 'cache-entries';
1557
+ const normalizeURL = unNormalizedUrl => {
1558
+ const url = new URL(unNormalizedUrl, location.href);
1559
+ url.hash = '';
1560
+ return url.href;
1561
+ };
1562
+ /**
1563
+ * Returns the timestamp model.
1564
+ *
1565
+ * @private
1566
+ */
1567
+ class CacheTimestampsModel {
1568
+ /**
1569
+ *
1570
+ * @param {string} cacheName
1571
+ *
1572
+ * @private
1573
+ */
1574
+ constructor(cacheName) {
1575
+ this._db = null;
1576
+ this._cacheName = cacheName;
1577
+ }
1578
+ /**
1579
+ * Performs an upgrade of indexedDB.
1580
+ *
1581
+ * @param {IDBPDatabase<CacheDbSchema>} db
1582
+ *
1583
+ * @private
1584
+ */
1585
+ _upgradeDb(db) {
1586
+ // TODO(philipwalton): EdgeHTML doesn't support arrays as a keyPath, so we
1587
+ // have to use the `id` keyPath here and create our own values (a
1588
+ // concatenation of `url + cacheName`) instead of simply using
1589
+ // `keyPath: ['url', 'cacheName']`, which is supported in other browsers.
1590
+ const objStore = db.createObjectStore(CACHE_OBJECT_STORE, {
1591
+ keyPath: 'id'
1592
+ });
1593
+ // TODO(philipwalton): once we don't have to support EdgeHTML, we can
1594
+ // create a single index with the keyPath `['cacheName', 'timestamp']`
1595
+ // instead of doing both these indexes.
1596
+ objStore.createIndex('cacheName', 'cacheName', {
1597
+ unique: false
1598
+ });
1599
+ objStore.createIndex('timestamp', 'timestamp', {
1600
+ unique: false
1601
+ });
1602
+ }
1603
+ /**
1604
+ * Performs an upgrade of indexedDB and deletes deprecated DBs.
1605
+ *
1606
+ * @param {IDBPDatabase<CacheDbSchema>} db
1607
+ *
1608
+ * @private
1609
+ */
1610
+ _upgradeDbAndDeleteOldDbs(db) {
1611
+ this._upgradeDb(db);
1612
+ if (this._cacheName) {
1613
+ void deleteDB(this._cacheName);
1614
+ }
1615
+ }
1616
+ /**
1617
+ * @param {string} url
1618
+ * @param {number} timestamp
1619
+ *
1620
+ * @private
1621
+ */
1622
+ async setTimestamp(url, timestamp) {
1623
+ url = normalizeURL(url);
1624
+ const entry = {
1625
+ url,
1626
+ timestamp,
1627
+ cacheName: this._cacheName,
1628
+ // Creating an ID from the URL and cache name won't be necessary once
1629
+ // Edge switches to Chromium and all browsers we support work with
1630
+ // array keyPaths.
1631
+ id: this._getId(url)
1632
+ };
1633
+ const db = await this.getDb();
1634
+ const tx = db.transaction(CACHE_OBJECT_STORE, 'readwrite', {
1635
+ durability: 'relaxed'
1636
+ });
1637
+ await tx.store.put(entry);
1638
+ await tx.done;
1639
+ }
1640
+ /**
1641
+ * Returns the timestamp stored for a given URL.
1642
+ *
1643
+ * @param {string} url
1644
+ * @return {number | undefined}
1645
+ *
1646
+ * @private
1647
+ */
1648
+ async getTimestamp(url) {
1649
+ const db = await this.getDb();
1650
+ const entry = await db.get(CACHE_OBJECT_STORE, this._getId(url));
1651
+ return entry === null || entry === void 0 ? void 0 : entry.timestamp;
1652
+ }
1653
+ /**
1654
+ * Iterates through all the entries in the object store (from newest to
1655
+ * oldest) and removes entries once either `maxCount` is reached or the
1656
+ * entry's timestamp is less than `minTimestamp`.
1657
+ *
1658
+ * @param {number} minTimestamp
1659
+ * @param {number} maxCount
1660
+ * @return {Array<string>}
1661
+ *
1662
+ * @private
1663
+ */
1664
+ async expireEntries(minTimestamp, maxCount) {
1665
+ const db = await this.getDb();
1666
+ let cursor = await db.transaction(CACHE_OBJECT_STORE).store.index('timestamp').openCursor(null, 'prev');
1667
+ const entriesToDelete = [];
1668
+ let entriesNotDeletedCount = 0;
1669
+ while (cursor) {
1670
+ const result = cursor.value;
1671
+ // TODO(philipwalton): once we can use a multi-key index, we
1672
+ // won't have to check `cacheName` here.
1673
+ if (result.cacheName === this._cacheName) {
1674
+ // Delete an entry if it's older than the max age or
1675
+ // if we already have the max number allowed.
1676
+ if (minTimestamp && result.timestamp < minTimestamp || maxCount && entriesNotDeletedCount >= maxCount) {
1677
+ // TODO(philipwalton): we should be able to delete the
1678
+ // entry right here, but doing so causes an iteration
1679
+ // bug in Safari stable (fixed in TP). Instead we can
1680
+ // store the keys of the entries to delete, and then
1681
+ // delete the separate transactions.
1682
+ // https://github.com/GoogleChrome/workbox/issues/1978
1683
+ // cursor.delete();
1684
+ // We only need to return the URL, not the whole entry.
1685
+ entriesToDelete.push(cursor.value);
1686
+ } else {
1687
+ entriesNotDeletedCount++;
1688
+ }
1689
+ }
1690
+ cursor = await cursor.continue();
1691
+ }
1692
+ // TODO(philipwalton): once the Safari bug in the following issue is fixed,
1693
+ // we should be able to remove this loop and do the entry deletion in the
1694
+ // cursor loop above:
1695
+ // https://github.com/GoogleChrome/workbox/issues/1978
1696
+ const urlsDeleted = [];
1697
+ for (const entry of entriesToDelete) {
1698
+ await db.delete(CACHE_OBJECT_STORE, entry.id);
1699
+ urlsDeleted.push(entry.url);
1700
+ }
1701
+ return urlsDeleted;
1702
+ }
1703
+ /**
1704
+ * Takes a URL and returns an ID that will be unique in the object store.
1705
+ *
1706
+ * @param {string} url
1707
+ * @return {string}
1708
+ *
1709
+ * @private
1710
+ */
1711
+ _getId(url) {
1712
+ // Creating an ID from the URL and cache name won't be necessary once
1713
+ // Edge switches to Chromium and all browsers we support work with
1714
+ // array keyPaths.
1715
+ return this._cacheName + '|' + normalizeURL(url);
1716
+ }
1717
+ /**
1718
+ * Returns an open connection to the database.
1719
+ *
1720
+ * @private
1721
+ */
1722
+ async getDb() {
1723
+ if (!this._db) {
1724
+ this._db = await openDB(DB_NAME, 1, {
1725
+ upgrade: this._upgradeDbAndDeleteOldDbs.bind(this)
1726
+ });
1727
+ }
1728
+ return this._db;
1729
+ }
1730
+ }
1731
+
1732
+ /*
1733
+ Copyright 2018 Google LLC
1734
+
1735
+ Use of this source code is governed by an MIT-style
1736
+ license that can be found in the LICENSE file or at
1737
+ https://opensource.org/licenses/MIT.
1738
+ */
1739
+ /**
1740
+ * The `CacheExpiration` class allows you define an expiration and / or
1741
+ * limit on the number of responses stored in a
1742
+ * [`Cache`](https://developer.mozilla.org/en-US/docs/Web/API/Cache).
1743
+ *
1744
+ * @memberof workbox-expiration
1745
+ */
1746
+ class CacheExpiration {
1747
+ /**
1748
+ * To construct a new CacheExpiration instance you must provide at least
1749
+ * one of the `config` properties.
1750
+ *
1751
+ * @param {string} cacheName Name of the cache to apply restrictions to.
1752
+ * @param {Object} config
1753
+ * @param {number} [config.maxEntries] The maximum number of entries to cache.
1754
+ * Entries used the least will be removed as the maximum is reached.
1755
+ * @param {number} [config.maxAgeSeconds] The maximum age of an entry before
1756
+ * it's treated as stale and removed.
1757
+ * @param {Object} [config.matchOptions] The [`CacheQueryOptions`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/delete#Parameters)
1758
+ * that will be used when calling `delete()` on the cache.
1759
+ */
1760
+ constructor(cacheName, config = {}) {
1761
+ this._isRunning = false;
1762
+ this._rerunRequested = false;
1763
+ {
1764
+ finalAssertExports.isType(cacheName, 'string', {
1765
+ moduleName: 'workbox-expiration',
1766
+ className: 'CacheExpiration',
1767
+ funcName: 'constructor',
1768
+ paramName: 'cacheName'
1769
+ });
1770
+ if (!(config.maxEntries || config.maxAgeSeconds)) {
1771
+ throw new WorkboxError('max-entries-or-age-required', {
1772
+ moduleName: 'workbox-expiration',
1773
+ className: 'CacheExpiration',
1774
+ funcName: 'constructor'
1775
+ });
1776
+ }
1777
+ if (config.maxEntries) {
1778
+ finalAssertExports.isType(config.maxEntries, 'number', {
1779
+ moduleName: 'workbox-expiration',
1780
+ className: 'CacheExpiration',
1781
+ funcName: 'constructor',
1782
+ paramName: 'config.maxEntries'
1783
+ });
1784
+ }
1785
+ if (config.maxAgeSeconds) {
1786
+ finalAssertExports.isType(config.maxAgeSeconds, 'number', {
1787
+ moduleName: 'workbox-expiration',
1788
+ className: 'CacheExpiration',
1789
+ funcName: 'constructor',
1790
+ paramName: 'config.maxAgeSeconds'
1791
+ });
1792
+ }
1793
+ }
1794
+ this._maxEntries = config.maxEntries;
1795
+ this._maxAgeSeconds = config.maxAgeSeconds;
1796
+ this._matchOptions = config.matchOptions;
1797
+ this._cacheName = cacheName;
1798
+ this._timestampModel = new CacheTimestampsModel(cacheName);
1799
+ }
1800
+ /**
1801
+ * Expires entries for the given cache and given criteria.
1802
+ */
1803
+ async expireEntries() {
1804
+ if (this._isRunning) {
1805
+ this._rerunRequested = true;
1806
+ return;
1807
+ }
1808
+ this._isRunning = true;
1809
+ const minTimestamp = this._maxAgeSeconds ? Date.now() - this._maxAgeSeconds * 1000 : 0;
1810
+ const urlsExpired = await this._timestampModel.expireEntries(minTimestamp, this._maxEntries);
1811
+ // Delete URLs from the cache
1812
+ const cache = await self.caches.open(this._cacheName);
1813
+ for (const url of urlsExpired) {
1814
+ await cache.delete(url, this._matchOptions);
1815
+ }
1816
+ {
1817
+ if (urlsExpired.length > 0) {
1818
+ logger.groupCollapsed(`Expired ${urlsExpired.length} ` + `${urlsExpired.length === 1 ? 'entry' : 'entries'} and removed ` + `${urlsExpired.length === 1 ? 'it' : 'them'} from the ` + `'${this._cacheName}' cache.`);
1819
+ logger.log(`Expired the following ${urlsExpired.length === 1 ? 'URL' : 'URLs'}:`);
1820
+ urlsExpired.forEach(url => logger.log(` ${url}`));
1821
+ logger.groupEnd();
1822
+ } else {
1823
+ logger.debug(`Cache expiration ran and found no entries to remove.`);
1824
+ }
1825
+ }
1826
+ this._isRunning = false;
1827
+ if (this._rerunRequested) {
1828
+ this._rerunRequested = false;
1829
+ dontWaitFor(this.expireEntries());
1830
+ }
1831
+ }
1832
+ /**
1833
+ * Update the timestamp for the given URL. This ensures the when
1834
+ * removing entries based on maximum entries, most recently used
1835
+ * is accurate or when expiring, the timestamp is up-to-date.
1836
+ *
1837
+ * @param {string} url
1838
+ */
1839
+ async updateTimestamp(url) {
1840
+ {
1841
+ finalAssertExports.isType(url, 'string', {
1842
+ moduleName: 'workbox-expiration',
1843
+ className: 'CacheExpiration',
1844
+ funcName: 'updateTimestamp',
1845
+ paramName: 'url'
1846
+ });
1847
+ }
1848
+ await this._timestampModel.setTimestamp(url, Date.now());
1849
+ }
1850
+ /**
1851
+ * Can be used to check if a URL has expired or not before it's used.
1852
+ *
1853
+ * This requires a look up from IndexedDB, so can be slow.
1854
+ *
1855
+ * Note: This method will not remove the cached entry, call
1856
+ * `expireEntries()` to remove indexedDB and Cache entries.
1857
+ *
1858
+ * @param {string} url
1859
+ * @return {boolean}
1860
+ */
1861
+ async isURLExpired(url) {
1862
+ if (!this._maxAgeSeconds) {
1863
+ {
1864
+ throw new WorkboxError(`expired-test-without-max-age`, {
1865
+ methodName: 'isURLExpired',
1866
+ paramName: 'maxAgeSeconds'
1867
+ });
1868
+ }
1869
+ } else {
1870
+ const timestamp = await this._timestampModel.getTimestamp(url);
1871
+ const expireOlderThan = Date.now() - this._maxAgeSeconds * 1000;
1872
+ return timestamp !== undefined ? timestamp < expireOlderThan : true;
1873
+ }
1874
+ }
1875
+ /**
1876
+ * Removes the IndexedDB object store used to keep track of cache expiration
1877
+ * metadata.
1878
+ */
1879
+ async delete() {
1880
+ // Make sure we don't attempt another rerun if we're called in the middle of
1881
+ // a cache expiration.
1882
+ this._rerunRequested = false;
1883
+ await this._timestampModel.expireEntries(Infinity); // Expires all.
1884
+ }
1885
+ }
1886
+
1887
+ /*
1888
+ Copyright 2018 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
+ * This plugin can be used in a `workbox-strategy` to regularly enforce a
1896
+ * limit on the age and / or the number of cached requests.
1897
+ *
1898
+ * It can only be used with `workbox-strategy` instances that have a
1899
+ * [custom `cacheName` property set](/web/tools/workbox/guides/configure-workbox#custom_cache_names_in_strategies).
1900
+ * In other words, it can't be used to expire entries in strategy that uses the
1901
+ * default runtime cache name.
1902
+ *
1903
+ * Whenever a cached response is used or updated, this plugin will look
1904
+ * at the associated cache and remove any old or extra responses.
1905
+ *
1906
+ * When using `maxAgeSeconds`, responses may be used *once* after expiring
1907
+ * because the expiration clean up will not have occurred until *after* the
1908
+ * cached response has been used. If the response has a "Date" header, then
1909
+ * a light weight expiration check is performed and the response will not be
1910
+ * used immediately.
1911
+ *
1912
+ * When using `maxEntries`, the entry least-recently requested will be removed
1913
+ * from the cache first.
1914
+ *
1915
+ * @memberof workbox-expiration
1916
+ */
1917
+ class ExpirationPlugin {
1918
+ /**
1919
+ * @param {ExpirationPluginOptions} config
1920
+ * @param {number} [config.maxEntries] The maximum number of entries to cache.
1921
+ * Entries used the least will be removed as the maximum is reached.
1922
+ * @param {number} [config.maxAgeSeconds] The maximum age of an entry before
1923
+ * it's treated as stale and removed.
1924
+ * @param {Object} [config.matchOptions] The [`CacheQueryOptions`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/delete#Parameters)
1925
+ * that will be used when calling `delete()` on the cache.
1926
+ * @param {boolean} [config.purgeOnQuotaError] Whether to opt this cache in to
1927
+ * automatic deletion if the available storage quota has been exceeded.
1928
+ */
1929
+ constructor(config = {}) {
1930
+ /**
1931
+ * A "lifecycle" callback that will be triggered automatically by the
1932
+ * `workbox-strategies` handlers when a `Response` is about to be returned
1933
+ * from a [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache) to
1934
+ * the handler. It allows the `Response` to be inspected for freshness and
1935
+ * prevents it from being used if the `Response`'s `Date` header value is
1936
+ * older than the configured `maxAgeSeconds`.
1937
+ *
1938
+ * @param {Object} options
1939
+ * @param {string} options.cacheName Name of the cache the response is in.
1940
+ * @param {Response} options.cachedResponse The `Response` object that's been
1941
+ * read from a cache and whose freshness should be checked.
1942
+ * @return {Response} Either the `cachedResponse`, if it's
1943
+ * fresh, or `null` if the `Response` is older than `maxAgeSeconds`.
1944
+ *
1945
+ * @private
1946
+ */
1947
+ this.cachedResponseWillBeUsed = async ({
1948
+ event,
1949
+ request,
1950
+ cacheName,
1951
+ cachedResponse
1952
+ }) => {
1953
+ if (!cachedResponse) {
1954
+ return null;
1955
+ }
1956
+ const isFresh = this._isResponseDateFresh(cachedResponse);
1957
+ // Expire entries to ensure that even if the expiration date has
1958
+ // expired, it'll only be used once.
1959
+ const cacheExpiration = this._getCacheExpiration(cacheName);
1960
+ dontWaitFor(cacheExpiration.expireEntries());
1961
+ // Update the metadata for the request URL to the current timestamp,
1962
+ // but don't `await` it as we don't want to block the response.
1963
+ const updateTimestampDone = cacheExpiration.updateTimestamp(request.url);
1964
+ if (event) {
1965
+ try {
1966
+ event.waitUntil(updateTimestampDone);
1967
+ } catch (error) {
1968
+ {
1969
+ // The event may not be a fetch event; only log the URL if it is.
1970
+ if ('request' in event) {
1971
+ logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache entry for ` + `'${getFriendlyURL(event.request.url)}'.`);
1972
+ }
1973
+ }
1974
+ }
1975
+ }
1976
+ return isFresh ? cachedResponse : null;
1977
+ };
1978
+ /**
1979
+ * A "lifecycle" callback that will be triggered automatically by the
1980
+ * `workbox-strategies` handlers when an entry is added to a cache.
1981
+ *
1982
+ * @param {Object} options
1983
+ * @param {string} options.cacheName Name of the cache that was updated.
1984
+ * @param {string} options.request The Request for the cached entry.
1985
+ *
1986
+ * @private
1987
+ */
1988
+ this.cacheDidUpdate = async ({
1989
+ cacheName,
1990
+ request
1991
+ }) => {
1992
+ {
1993
+ finalAssertExports.isType(cacheName, 'string', {
1994
+ moduleName: 'workbox-expiration',
1995
+ className: 'Plugin',
1996
+ funcName: 'cacheDidUpdate',
1997
+ paramName: 'cacheName'
1998
+ });
1999
+ finalAssertExports.isInstance(request, Request, {
2000
+ moduleName: 'workbox-expiration',
2001
+ className: 'Plugin',
2002
+ funcName: 'cacheDidUpdate',
2003
+ paramName: 'request'
2004
+ });
2005
+ }
2006
+ const cacheExpiration = this._getCacheExpiration(cacheName);
2007
+ await cacheExpiration.updateTimestamp(request.url);
2008
+ await cacheExpiration.expireEntries();
2009
+ };
2010
+ {
2011
+ if (!(config.maxEntries || config.maxAgeSeconds)) {
2012
+ throw new WorkboxError('max-entries-or-age-required', {
2013
+ moduleName: 'workbox-expiration',
2014
+ className: 'Plugin',
2015
+ funcName: 'constructor'
2016
+ });
2017
+ }
2018
+ if (config.maxEntries) {
2019
+ finalAssertExports.isType(config.maxEntries, 'number', {
2020
+ moduleName: 'workbox-expiration',
2021
+ className: 'Plugin',
2022
+ funcName: 'constructor',
2023
+ paramName: 'config.maxEntries'
2024
+ });
2025
+ }
2026
+ if (config.maxAgeSeconds) {
2027
+ finalAssertExports.isType(config.maxAgeSeconds, 'number', {
2028
+ moduleName: 'workbox-expiration',
2029
+ className: 'Plugin',
2030
+ funcName: 'constructor',
2031
+ paramName: 'config.maxAgeSeconds'
2032
+ });
2033
+ }
2034
+ }
2035
+ this._config = config;
2036
+ this._maxAgeSeconds = config.maxAgeSeconds;
2037
+ this._cacheExpirations = new Map();
2038
+ if (config.purgeOnQuotaError) {
2039
+ registerQuotaErrorCallback(() => this.deleteCacheAndMetadata());
2040
+ }
2041
+ }
2042
+ /**
2043
+ * A simple helper method to return a CacheExpiration instance for a given
2044
+ * cache name.
2045
+ *
2046
+ * @param {string} cacheName
2047
+ * @return {CacheExpiration}
2048
+ *
2049
+ * @private
2050
+ */
2051
+ _getCacheExpiration(cacheName) {
2052
+ if (cacheName === cacheNames.getRuntimeName()) {
2053
+ throw new WorkboxError('expire-custom-caches-only');
2054
+ }
2055
+ let cacheExpiration = this._cacheExpirations.get(cacheName);
2056
+ if (!cacheExpiration) {
2057
+ cacheExpiration = new CacheExpiration(cacheName, this._config);
2058
+ this._cacheExpirations.set(cacheName, cacheExpiration);
2059
+ }
2060
+ return cacheExpiration;
2061
+ }
2062
+ /**
2063
+ * @param {Response} cachedResponse
2064
+ * @return {boolean}
2065
+ *
2066
+ * @private
2067
+ */
2068
+ _isResponseDateFresh(cachedResponse) {
2069
+ if (!this._maxAgeSeconds) {
2070
+ // We aren't expiring by age, so return true, it's fresh
2071
+ return true;
2072
+ }
2073
+ // Check if the 'date' header will suffice a quick expiration check.
2074
+ // See https://github.com/GoogleChromeLabs/sw-toolbox/issues/164 for
2075
+ // discussion.
2076
+ const dateHeaderTimestamp = this._getDateHeaderTimestamp(cachedResponse);
2077
+ if (dateHeaderTimestamp === null) {
2078
+ // Unable to parse date, so assume it's fresh.
2079
+ return true;
2080
+ }
2081
+ // If we have a valid headerTime, then our response is fresh iff the
2082
+ // headerTime plus maxAgeSeconds is greater than the current time.
2083
+ const now = Date.now();
2084
+ return dateHeaderTimestamp >= now - this._maxAgeSeconds * 1000;
2085
+ }
2086
+ /**
2087
+ * This method will extract the data header and parse it into a useful
2088
+ * value.
2089
+ *
2090
+ * @param {Response} cachedResponse
2091
+ * @return {number|null}
2092
+ *
2093
+ * @private
2094
+ */
2095
+ _getDateHeaderTimestamp(cachedResponse) {
2096
+ if (!cachedResponse.headers.has('date')) {
2097
+ return null;
2098
+ }
2099
+ const dateHeader = cachedResponse.headers.get('date');
2100
+ const parsedDate = new Date(dateHeader);
2101
+ const headerTime = parsedDate.getTime();
2102
+ // If the Date header was invalid for some reason, parsedDate.getTime()
2103
+ // will return NaN.
2104
+ if (isNaN(headerTime)) {
2105
+ return null;
2106
+ }
2107
+ return headerTime;
2108
+ }
2109
+ /**
2110
+ * This is a helper method that performs two operations:
2111
+ *
2112
+ * - Deletes *all* the underlying Cache instances associated with this plugin
2113
+ * instance, by calling caches.delete() on your behalf.
2114
+ * - Deletes the metadata from IndexedDB used to keep track of expiration
2115
+ * details for each Cache instance.
2116
+ *
2117
+ * When using cache expiration, calling this method is preferable to calling
2118
+ * `caches.delete()` directly, since this will ensure that the IndexedDB
2119
+ * metadata is also cleanly removed and open IndexedDB instances are deleted.
2120
+ *
2121
+ * Note that if you're *not* using cache expiration for a given cache, calling
2122
+ * `caches.delete()` and passing in the cache's name should be sufficient.
2123
+ * There is no Workbox-specific method needed for cleanup in that case.
2124
+ */
2125
+ async deleteCacheAndMetadata() {
2126
+ // Do this one at a time instead of all at once via `Promise.all()` to
2127
+ // reduce the chance of inconsistency if a promise rejects.
2128
+ for (const [cacheName, cacheExpiration] of this._cacheExpirations) {
2129
+ await self.caches.delete(cacheName);
2130
+ await cacheExpiration.delete();
2131
+ }
2132
+ // Reset this._cacheExpirations to its initial state.
2133
+ this._cacheExpirations = new Map();
2134
+ }
2135
+ }
2136
+
2137
+ /*
2138
+ Copyright 2020 Google LLC
2139
+ Use of this source code is governed by an MIT-style
2140
+ license that can be found in the LICENSE file or at
2141
+ https://opensource.org/licenses/MIT.
2142
+ */
2143
+ function stripParams(fullURL, ignoreParams) {
2144
+ const strippedURL = new URL(fullURL);
2145
+ for (const param of ignoreParams) {
2146
+ strippedURL.searchParams.delete(param);
2147
+ }
2148
+ return strippedURL.href;
2149
+ }
2150
+ /**
2151
+ * Matches an item in the cache, ignoring specific URL params. This is similar
2152
+ * to the `ignoreSearch` option, but it allows you to ignore just specific
2153
+ * params (while continuing to match on the others).
2154
+ *
2155
+ * @private
2156
+ * @param {Cache} cache
2157
+ * @param {Request} request
2158
+ * @param {Object} matchOptions
2159
+ * @param {Array<string>} ignoreParams
2160
+ * @return {Promise<Response|undefined>}
2161
+ */
2162
+ async function cacheMatchIgnoreParams(cache, request, ignoreParams, matchOptions) {
2163
+ const strippedRequestURL = stripParams(request.url, ignoreParams);
2164
+ // If the request doesn't include any ignored params, match as normal.
2165
+ if (request.url === strippedRequestURL) {
2166
+ return cache.match(request, matchOptions);
2167
+ }
2168
+ // Otherwise, match by comparing keys
2169
+ const keysOptions = Object.assign(Object.assign({}, matchOptions), {
2170
+ ignoreSearch: true
2171
+ });
2172
+ const cacheKeys = await cache.keys(request, keysOptions);
2173
+ for (const cacheKey of cacheKeys) {
2174
+ const strippedCacheKeyURL = stripParams(cacheKey.url, ignoreParams);
2175
+ if (strippedRequestURL === strippedCacheKeyURL) {
2176
+ return cache.match(cacheKey, matchOptions);
2177
+ }
2178
+ }
2179
+ return;
2180
+ }
2181
+
2182
+ /*
2183
+ Copyright 2018 Google LLC
2184
+
2185
+ Use of this source code is governed by an MIT-style
2186
+ license that can be found in the LICENSE file or at
2187
+ https://opensource.org/licenses/MIT.
2188
+ */
2189
+ /**
2190
+ * The Deferred class composes Promises in a way that allows for them to be
2191
+ * resolved or rejected from outside the constructor. In most cases promises
2192
+ * should be used directly, but Deferreds can be necessary when the logic to
2193
+ * resolve a promise must be separate.
2194
+ *
2195
+ * @private
2196
+ */
2197
+ class Deferred {
2198
+ /**
2199
+ * Creates a promise and exposes its resolve and reject functions as methods.
2200
+ */
2201
+ constructor() {
2202
+ this.promise = new Promise((resolve, reject) => {
2203
+ this.resolve = resolve;
2204
+ this.reject = reject;
2205
+ });
2206
+ }
2207
+ }
2208
+
2209
+ /*
2210
+ Copyright 2018 Google LLC
2211
+
2212
+ Use of this source code is governed by an MIT-style
2213
+ license that can be found in the LICENSE file or at
2214
+ https://opensource.org/licenses/MIT.
2215
+ */
2216
+ /**
2217
+ * Runs all of the callback functions, one at a time sequentially, in the order
2218
+ * in which they were registered.
2219
+ *
2220
+ * @memberof workbox-core
2221
+ * @private
2222
+ */
2223
+ async function executeQuotaErrorCallbacks() {
2224
+ {
2225
+ logger.log(`About to run ${quotaErrorCallbacks.size} ` + `callbacks to clean up caches.`);
2226
+ }
2227
+ for (const callback of quotaErrorCallbacks) {
2228
+ await callback();
2229
+ {
2230
+ logger.log(callback, 'is complete.');
2231
+ }
2232
+ }
2233
+ {
2234
+ logger.log('Finished running callbacks.');
2235
+ }
2236
+ }
2237
+
2238
+ /*
2239
+ Copyright 2019 Google LLC
2240
+ Use of this source code is governed by an MIT-style
2241
+ license that can be found in the LICENSE file or at
2242
+ https://opensource.org/licenses/MIT.
2243
+ */
2244
+ /**
2245
+ * Returns a promise that resolves and the passed number of milliseconds.
2246
+ * This utility is an async/await-friendly version of `setTimeout`.
2247
+ *
2248
+ * @param {number} ms
2249
+ * @return {Promise}
2250
+ * @private
2251
+ */
2252
+ function timeout(ms) {
2253
+ return new Promise(resolve => setTimeout(resolve, ms));
2254
+ }
2255
+
2256
+ // @ts-ignore
2257
+ try {
2258
+ self['workbox:strategies:7.3.0'] && _();
2259
+ } catch (e) {}
2260
+
2261
+ /*
2262
+ Copyright 2020 Google LLC
2263
+
2264
+ Use of this source code is governed by an MIT-style
2265
+ license that can be found in the LICENSE file or at
2266
+ https://opensource.org/licenses/MIT.
2267
+ */
2268
+ function toRequest(input) {
2269
+ return typeof input === 'string' ? new Request(input) : input;
2270
+ }
2271
+ /**
2272
+ * A class created every time a Strategy instance calls
2273
+ * {@link workbox-strategies.Strategy~handle} or
2274
+ * {@link workbox-strategies.Strategy~handleAll} that wraps all fetch and
2275
+ * cache actions around plugin callbacks and keeps track of when the strategy
2276
+ * is "done" (i.e. all added `event.waitUntil()` promises have resolved).
2277
+ *
2278
+ * @memberof workbox-strategies
2279
+ */
2280
+ class StrategyHandler {
2281
+ /**
2282
+ * Creates a new instance associated with the passed strategy and event
2283
+ * that's handling the request.
2284
+ *
2285
+ * The constructor also initializes the state that will be passed to each of
2286
+ * the plugins handling this request.
2287
+ *
2288
+ * @param {workbox-strategies.Strategy} strategy
2289
+ * @param {Object} options
2290
+ * @param {Request|string} options.request A request to run this strategy for.
2291
+ * @param {ExtendableEvent} options.event The event associated with the
2292
+ * request.
2293
+ * @param {URL} [options.url]
2294
+ * @param {*} [options.params] The return value from the
2295
+ * {@link workbox-routing~matchCallback} (if applicable).
2296
+ */
2297
+ constructor(strategy, options) {
2298
+ this._cacheKeys = {};
2299
+ /**
2300
+ * The request the strategy is performing (passed to the strategy's
2301
+ * `handle()` or `handleAll()` method).
2302
+ * @name request
2303
+ * @instance
2304
+ * @type {Request}
2305
+ * @memberof workbox-strategies.StrategyHandler
2306
+ */
2307
+ /**
2308
+ * The event associated with this request.
2309
+ * @name event
2310
+ * @instance
2311
+ * @type {ExtendableEvent}
2312
+ * @memberof workbox-strategies.StrategyHandler
2313
+ */
2314
+ /**
2315
+ * A `URL` instance of `request.url` (if passed to the strategy's
2316
+ * `handle()` or `handleAll()` method).
2317
+ * Note: the `url` param will be present if the strategy was invoked
2318
+ * from a workbox `Route` object.
2319
+ * @name url
2320
+ * @instance
2321
+ * @type {URL|undefined}
2322
+ * @memberof workbox-strategies.StrategyHandler
2323
+ */
2324
+ /**
2325
+ * A `param` value (if passed to the strategy's
2326
+ * `handle()` or `handleAll()` method).
2327
+ * Note: the `param` param will be present if the strategy was invoked
2328
+ * from a workbox `Route` object and the
2329
+ * {@link workbox-routing~matchCallback} returned
2330
+ * a truthy value (it will be that value).
2331
+ * @name params
2332
+ * @instance
2333
+ * @type {*|undefined}
2334
+ * @memberof workbox-strategies.StrategyHandler
2335
+ */
2336
+ {
2337
+ finalAssertExports.isInstance(options.event, ExtendableEvent, {
2338
+ moduleName: 'workbox-strategies',
2339
+ className: 'StrategyHandler',
2340
+ funcName: 'constructor',
2341
+ paramName: 'options.event'
2342
+ });
2343
+ }
2344
+ Object.assign(this, options);
2345
+ this.event = options.event;
2346
+ this._strategy = strategy;
2347
+ this._handlerDeferred = new Deferred();
2348
+ this._extendLifetimePromises = [];
2349
+ // Copy the plugins list (since it's mutable on the strategy),
2350
+ // so any mutations don't affect this handler instance.
2351
+ this._plugins = [...strategy.plugins];
2352
+ this._pluginStateMap = new Map();
2353
+ for (const plugin of this._plugins) {
2354
+ this._pluginStateMap.set(plugin, {});
2355
+ }
2356
+ this.event.waitUntil(this._handlerDeferred.promise);
2357
+ }
2358
+ /**
2359
+ * Fetches a given request (and invokes any applicable plugin callback
2360
+ * methods) using the `fetchOptions` (for non-navigation requests) and
2361
+ * `plugins` defined on the `Strategy` object.
2362
+ *
2363
+ * The following plugin lifecycle methods are invoked when using this method:
2364
+ * - `requestWillFetch()`
2365
+ * - `fetchDidSucceed()`
2366
+ * - `fetchDidFail()`
2367
+ *
2368
+ * @param {Request|string} input The URL or request to fetch.
2369
+ * @return {Promise<Response>}
2370
+ */
2371
+ async fetch(input) {
2372
+ const {
2373
+ event
2374
+ } = this;
2375
+ let request = toRequest(input);
2376
+ if (request.mode === 'navigate' && event instanceof FetchEvent && event.preloadResponse) {
2377
+ const possiblePreloadResponse = await event.preloadResponse;
2378
+ if (possiblePreloadResponse) {
2379
+ {
2380
+ logger.log(`Using a preloaded navigation response for ` + `'${getFriendlyURL(request.url)}'`);
2381
+ }
2382
+ return possiblePreloadResponse;
2383
+ }
2384
+ }
2385
+ // If there is a fetchDidFail plugin, we need to save a clone of the
2386
+ // original request before it's either modified by a requestWillFetch
2387
+ // plugin or before the original request's body is consumed via fetch().
2388
+ const originalRequest = this.hasCallback('fetchDidFail') ? request.clone() : null;
2389
+ try {
2390
+ for (const cb of this.iterateCallbacks('requestWillFetch')) {
2391
+ request = await cb({
2392
+ request: request.clone(),
2393
+ event
2394
+ });
2395
+ }
2396
+ } catch (err) {
2397
+ if (err instanceof Error) {
2398
+ throw new WorkboxError('plugin-error-request-will-fetch', {
2399
+ thrownErrorMessage: err.message
2400
+ });
2401
+ }
2402
+ }
2403
+ // The request can be altered by plugins with `requestWillFetch` making
2404
+ // the original request (most likely from a `fetch` event) different
2405
+ // from the Request we make. Pass both to `fetchDidFail` to aid debugging.
2406
+ const pluginFilteredRequest = request.clone();
2407
+ try {
2408
+ let fetchResponse;
2409
+ // See https://github.com/GoogleChrome/workbox/issues/1796
2410
+ fetchResponse = await fetch(request, request.mode === 'navigate' ? undefined : this._strategy.fetchOptions);
2411
+ if ("development" !== 'production') {
2412
+ logger.debug(`Network request for ` + `'${getFriendlyURL(request.url)}' returned a response with ` + `status '${fetchResponse.status}'.`);
2413
+ }
2414
+ for (const callback of this.iterateCallbacks('fetchDidSucceed')) {
2415
+ fetchResponse = await callback({
2416
+ event,
2417
+ request: pluginFilteredRequest,
2418
+ response: fetchResponse
2419
+ });
2420
+ }
2421
+ return fetchResponse;
2422
+ } catch (error) {
2423
+ {
2424
+ logger.log(`Network request for ` + `'${getFriendlyURL(request.url)}' threw an error.`, error);
2425
+ }
2426
+ // `originalRequest` will only exist if a `fetchDidFail` callback
2427
+ // is being used (see above).
2428
+ if (originalRequest) {
2429
+ await this.runCallbacks('fetchDidFail', {
2430
+ error: error,
2431
+ event,
2432
+ originalRequest: originalRequest.clone(),
2433
+ request: pluginFilteredRequest.clone()
2434
+ });
2435
+ }
2436
+ throw error;
2437
+ }
2438
+ }
2439
+ /**
2440
+ * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on
2441
+ * the response generated by `this.fetch()`.
2442
+ *
2443
+ * The call to `this.cachePut()` automatically invokes `this.waitUntil()`,
2444
+ * so you do not have to manually call `waitUntil()` on the event.
2445
+ *
2446
+ * @param {Request|string} input The request or URL to fetch and cache.
2447
+ * @return {Promise<Response>}
2448
+ */
2449
+ async fetchAndCachePut(input) {
2450
+ const response = await this.fetch(input);
2451
+ const responseClone = response.clone();
2452
+ void this.waitUntil(this.cachePut(input, responseClone));
2453
+ return response;
2454
+ }
2455
+ /**
2456
+ * Matches a request from the cache (and invokes any applicable plugin
2457
+ * callback methods) using the `cacheName`, `matchOptions`, and `plugins`
2458
+ * defined on the strategy object.
2459
+ *
2460
+ * The following plugin lifecycle methods are invoked when using this method:
2461
+ * - cacheKeyWillBeUsed()
2462
+ * - cachedResponseWillBeUsed()
2463
+ *
2464
+ * @param {Request|string} key The Request or URL to use as the cache key.
2465
+ * @return {Promise<Response|undefined>} A matching response, if found.
2466
+ */
2467
+ async cacheMatch(key) {
2468
+ const request = toRequest(key);
2469
+ let cachedResponse;
2470
+ const {
2471
+ cacheName,
2472
+ matchOptions
2473
+ } = this._strategy;
2474
+ const effectiveRequest = await this.getCacheKey(request, 'read');
2475
+ const multiMatchOptions = Object.assign(Object.assign({}, matchOptions), {
2476
+ cacheName
2477
+ });
2478
+ cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);
2479
+ {
2480
+ if (cachedResponse) {
2481
+ logger.debug(`Found a cached response in '${cacheName}'.`);
2482
+ } else {
2483
+ logger.debug(`No cached response found in '${cacheName}'.`);
2484
+ }
2485
+ }
2486
+ for (const callback of this.iterateCallbacks('cachedResponseWillBeUsed')) {
2487
+ cachedResponse = (await callback({
2488
+ cacheName,
2489
+ matchOptions,
2490
+ cachedResponse,
2491
+ request: effectiveRequest,
2492
+ event: this.event
2493
+ })) || undefined;
2494
+ }
2495
+ return cachedResponse;
2496
+ }
2497
+ /**
2498
+ * Puts a request/response pair in the cache (and invokes any applicable
2499
+ * plugin callback methods) using the `cacheName` and `plugins` defined on
2500
+ * the strategy object.
2501
+ *
2502
+ * The following plugin lifecycle methods are invoked when using this method:
2503
+ * - cacheKeyWillBeUsed()
2504
+ * - cacheWillUpdate()
2505
+ * - cacheDidUpdate()
2506
+ *
2507
+ * @param {Request|string} key The request or URL to use as the cache key.
2508
+ * @param {Response} response The response to cache.
2509
+ * @return {Promise<boolean>} `false` if a cacheWillUpdate caused the response
2510
+ * not be cached, and `true` otherwise.
2511
+ */
2512
+ async cachePut(key, response) {
2513
+ const request = toRequest(key);
2514
+ // Run in the next task to avoid blocking other cache reads.
2515
+ // https://github.com/w3c/ServiceWorker/issues/1397
2516
+ await timeout(0);
2517
+ const effectiveRequest = await this.getCacheKey(request, 'write');
2518
+ {
2519
+ if (effectiveRequest.method && effectiveRequest.method !== 'GET') {
2520
+ throw new WorkboxError('attempt-to-cache-non-get-request', {
2521
+ url: getFriendlyURL(effectiveRequest.url),
2522
+ method: effectiveRequest.method
2523
+ });
2524
+ }
2525
+ // See https://github.com/GoogleChrome/workbox/issues/2818
2526
+ const vary = response.headers.get('Vary');
2527
+ if (vary) {
2528
+ logger.debug(`The response for ${getFriendlyURL(effectiveRequest.url)} ` + `has a 'Vary: ${vary}' header. ` + `Consider setting the {ignoreVary: true} option on your strategy ` + `to ensure cache matching and deletion works as expected.`);
2529
+ }
2530
+ }
2531
+ if (!response) {
2532
+ {
2533
+ logger.error(`Cannot cache non-existent response for ` + `'${getFriendlyURL(effectiveRequest.url)}'.`);
2534
+ }
2535
+ throw new WorkboxError('cache-put-with-no-response', {
2536
+ url: getFriendlyURL(effectiveRequest.url)
2537
+ });
2538
+ }
2539
+ const responseToCache = await this._ensureResponseSafeToCache(response);
2540
+ if (!responseToCache) {
2541
+ {
2542
+ logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' ` + `will not be cached.`, responseToCache);
2543
+ }
2544
+ return false;
2545
+ }
2546
+ const {
2547
+ cacheName,
2548
+ matchOptions
2549
+ } = this._strategy;
2550
+ const cache = await self.caches.open(cacheName);
2551
+ const hasCacheUpdateCallback = this.hasCallback('cacheDidUpdate');
2552
+ const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams(
2553
+ // TODO(philipwalton): the `__WB_REVISION__` param is a precaching
2554
+ // feature. Consider into ways to only add this behavior if using
2555
+ // precaching.
2556
+ cache, effectiveRequest.clone(), ['__WB_REVISION__'], matchOptions) : null;
2557
+ {
2558
+ logger.debug(`Updating the '${cacheName}' cache with a new Response ` + `for ${getFriendlyURL(effectiveRequest.url)}.`);
2559
+ }
2560
+ try {
2561
+ await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache);
2562
+ } catch (error) {
2563
+ if (error instanceof Error) {
2564
+ // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError
2565
+ if (error.name === 'QuotaExceededError') {
2566
+ await executeQuotaErrorCallbacks();
2567
+ }
2568
+ throw error;
2569
+ }
2570
+ }
2571
+ for (const callback of this.iterateCallbacks('cacheDidUpdate')) {
2572
+ await callback({
2573
+ cacheName,
2574
+ oldResponse,
2575
+ newResponse: responseToCache.clone(),
2576
+ request: effectiveRequest,
2577
+ event: this.event
2578
+ });
2579
+ }
2580
+ return true;
2581
+ }
2582
+ /**
2583
+ * Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and
2584
+ * executes any of those callbacks found in sequence. The final `Request`
2585
+ * object returned by the last plugin is treated as the cache key for cache
2586
+ * reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have
2587
+ * been registered, the passed request is returned unmodified
2588
+ *
2589
+ * @param {Request} request
2590
+ * @param {string} mode
2591
+ * @return {Promise<Request>}
2592
+ */
2593
+ async getCacheKey(request, mode) {
2594
+ const key = `${request.url} | ${mode}`;
2595
+ if (!this._cacheKeys[key]) {
2596
+ let effectiveRequest = request;
2597
+ for (const callback of this.iterateCallbacks('cacheKeyWillBeUsed')) {
2598
+ effectiveRequest = toRequest(await callback({
2599
+ mode,
2600
+ request: effectiveRequest,
2601
+ event: this.event,
2602
+ // params has a type any can't change right now.
2603
+ params: this.params // eslint-disable-line
2604
+ }));
2605
+ }
2606
+ this._cacheKeys[key] = effectiveRequest;
2607
+ }
2608
+ return this._cacheKeys[key];
2609
+ }
2610
+ /**
2611
+ * Returns true if the strategy has at least one plugin with the given
2612
+ * callback.
2613
+ *
2614
+ * @param {string} name The name of the callback to check for.
2615
+ * @return {boolean}
2616
+ */
2617
+ hasCallback(name) {
2618
+ for (const plugin of this._strategy.plugins) {
2619
+ if (name in plugin) {
2620
+ return true;
2621
+ }
2622
+ }
2623
+ return false;
2624
+ }
2625
+ /**
2626
+ * Runs all plugin callbacks matching the given name, in order, passing the
2627
+ * given param object (merged ith the current plugin state) as the only
2628
+ * argument.
2629
+ *
2630
+ * Note: since this method runs all plugins, it's not suitable for cases
2631
+ * where the return value of a callback needs to be applied prior to calling
2632
+ * the next callback. See
2633
+ * {@link workbox-strategies.StrategyHandler#iterateCallbacks}
2634
+ * below for how to handle that case.
2635
+ *
2636
+ * @param {string} name The name of the callback to run within each plugin.
2637
+ * @param {Object} param The object to pass as the first (and only) param
2638
+ * when executing each callback. This object will be merged with the
2639
+ * current plugin state prior to callback execution.
2640
+ */
2641
+ async runCallbacks(name, param) {
2642
+ for (const callback of this.iterateCallbacks(name)) {
2643
+ // TODO(philipwalton): not sure why `any` is needed. It seems like
2644
+ // this should work with `as WorkboxPluginCallbackParam[C]`.
2645
+ await callback(param);
2646
+ }
2647
+ }
2648
+ /**
2649
+ * Accepts a callback and returns an iterable of matching plugin callbacks,
2650
+ * where each callback is wrapped with the current handler state (i.e. when
2651
+ * you call each callback, whatever object parameter you pass it will
2652
+ * be merged with the plugin's current state).
2653
+ *
2654
+ * @param {string} name The name fo the callback to run
2655
+ * @return {Array<Function>}
2656
+ */
2657
+ *iterateCallbacks(name) {
2658
+ for (const plugin of this._strategy.plugins) {
2659
+ if (typeof plugin[name] === 'function') {
2660
+ const state = this._pluginStateMap.get(plugin);
2661
+ const statefulCallback = param => {
2662
+ const statefulParam = Object.assign(Object.assign({}, param), {
2663
+ state
2664
+ });
2665
+ // TODO(philipwalton): not sure why `any` is needed. It seems like
2666
+ // this should work with `as WorkboxPluginCallbackParam[C]`.
2667
+ return plugin[name](statefulParam);
2668
+ };
2669
+ yield statefulCallback;
2670
+ }
2671
+ }
2672
+ }
2673
+ /**
2674
+ * Adds a promise to the
2675
+ * [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises}
2676
+ * of the event associated with the request being handled (usually a
2677
+ * `FetchEvent`).
2678
+ *
2679
+ * Note: you can await
2680
+ * {@link workbox-strategies.StrategyHandler~doneWaiting}
2681
+ * to know when all added promises have settled.
2682
+ *
2683
+ * @param {Promise} promise A promise to add to the extend lifetime promises
2684
+ * of the event that triggered the request.
2685
+ */
2686
+ waitUntil(promise) {
2687
+ this._extendLifetimePromises.push(promise);
2688
+ return promise;
2689
+ }
2690
+ /**
2691
+ * Returns a promise that resolves once all promises passed to
2692
+ * {@link workbox-strategies.StrategyHandler~waitUntil}
2693
+ * have settled.
2694
+ *
2695
+ * Note: any work done after `doneWaiting()` settles should be manually
2696
+ * passed to an event's `waitUntil()` method (not this handler's
2697
+ * `waitUntil()` method), otherwise the service worker thread may be killed
2698
+ * prior to your work completing.
2699
+ */
2700
+ async doneWaiting() {
2701
+ while (this._extendLifetimePromises.length) {
2702
+ const promises = this._extendLifetimePromises.splice(0);
2703
+ const result = await Promise.allSettled(promises);
2704
+ const firstRejection = result.find(i => i.status === 'rejected');
2705
+ if (firstRejection) {
2706
+ throw firstRejection.reason;
2707
+ }
2708
+ }
2709
+ }
2710
+ /**
2711
+ * Stops running the strategy and immediately resolves any pending
2712
+ * `waitUntil()` promises.
2713
+ */
2714
+ destroy() {
2715
+ this._handlerDeferred.resolve(null);
2716
+ }
2717
+ /**
2718
+ * This method will call cacheWillUpdate on the available plugins (or use
2719
+ * status === 200) to determine if the Response is safe and valid to cache.
2720
+ *
2721
+ * @param {Request} options.request
2722
+ * @param {Response} options.response
2723
+ * @return {Promise<Response|undefined>}
2724
+ *
2725
+ * @private
2726
+ */
2727
+ async _ensureResponseSafeToCache(response) {
2728
+ let responseToCache = response;
2729
+ let pluginsUsed = false;
2730
+ for (const callback of this.iterateCallbacks('cacheWillUpdate')) {
2731
+ responseToCache = (await callback({
2732
+ request: this.request,
2733
+ response: responseToCache,
2734
+ event: this.event
2735
+ })) || undefined;
2736
+ pluginsUsed = true;
2737
+ if (!responseToCache) {
2738
+ break;
2739
+ }
2740
+ }
2741
+ if (!pluginsUsed) {
2742
+ if (responseToCache && responseToCache.status !== 200) {
2743
+ responseToCache = undefined;
2744
+ }
2745
+ {
2746
+ if (responseToCache) {
2747
+ if (responseToCache.status !== 200) {
2748
+ if (responseToCache.status === 0) {
2749
+ logger.warn(`The response for '${this.request.url}' ` + `is an opaque response. The caching strategy that you're ` + `using will not cache opaque responses by default.`);
2750
+ } else {
2751
+ logger.debug(`The response for '${this.request.url}' ` + `returned a status code of '${response.status}' and won't ` + `be cached as a result.`);
2752
+ }
2753
+ }
2754
+ }
2755
+ }
2756
+ }
2757
+ return responseToCache;
2758
+ }
2759
+ }
2760
+
2761
+ /*
2762
+ Copyright 2020 Google LLC
2763
+
2764
+ Use of this source code is governed by an MIT-style
2765
+ license that can be found in the LICENSE file or at
2766
+ https://opensource.org/licenses/MIT.
2767
+ */
2768
+ /**
2769
+ * An abstract base class that all other strategy classes must extend from:
2770
+ *
2771
+ * @memberof workbox-strategies
2772
+ */
2773
+ class Strategy {
2774
+ /**
2775
+ * Creates a new instance of the strategy and sets all documented option
2776
+ * properties as public instance properties.
2777
+ *
2778
+ * Note: if a custom strategy class extends the base Strategy class and does
2779
+ * not need more than these properties, it does not need to define its own
2780
+ * constructor.
2781
+ *
2782
+ * @param {Object} [options]
2783
+ * @param {string} [options.cacheName] Cache name to store and retrieve
2784
+ * requests. Defaults to the cache names provided by
2785
+ * {@link workbox-core.cacheNames}.
2786
+ * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
2787
+ * to use in conjunction with this caching strategy.
2788
+ * @param {Object} [options.fetchOptions] Values passed along to the
2789
+ * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
2790
+ * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
2791
+ * `fetch()` requests made by this strategy.
2792
+ * @param {Object} [options.matchOptions] The
2793
+ * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}
2794
+ * for any `cache.match()` or `cache.put()` calls made by this strategy.
2795
+ */
2796
+ constructor(options = {}) {
2797
+ /**
2798
+ * Cache name to store and retrieve
2799
+ * requests. Defaults to the cache names provided by
2800
+ * {@link workbox-core.cacheNames}.
2801
+ *
2802
+ * @type {string}
2803
+ */
2804
+ this.cacheName = cacheNames.getRuntimeName(options.cacheName);
2805
+ /**
2806
+ * The list
2807
+ * [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
2808
+ * used by this strategy.
2809
+ *
2810
+ * @type {Array<Object>}
2811
+ */
2812
+ this.plugins = options.plugins || [];
2813
+ /**
2814
+ * Values passed along to the
2815
+ * [`init`]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters}
2816
+ * of all fetch() requests made by this strategy.
2817
+ *
2818
+ * @type {Object}
2819
+ */
2820
+ this.fetchOptions = options.fetchOptions;
2821
+ /**
2822
+ * The
2823
+ * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}
2824
+ * for any `cache.match()` or `cache.put()` calls made by this strategy.
2825
+ *
2826
+ * @type {Object}
2827
+ */
2828
+ this.matchOptions = options.matchOptions;
2829
+ }
2830
+ /**
2831
+ * Perform a request strategy and returns a `Promise` that will resolve with
2832
+ * a `Response`, invoking all relevant plugin callbacks.
2833
+ *
2834
+ * When a strategy instance is registered with a Workbox
2835
+ * {@link workbox-routing.Route}, this method is automatically
2836
+ * called when the route matches.
2837
+ *
2838
+ * Alternatively, this method can be used in a standalone `FetchEvent`
2839
+ * listener by passing it to `event.respondWith()`.
2840
+ *
2841
+ * @param {FetchEvent|Object} options A `FetchEvent` or an object with the
2842
+ * properties listed below.
2843
+ * @param {Request|string} options.request A request to run this strategy for.
2844
+ * @param {ExtendableEvent} options.event The event associated with the
2845
+ * request.
2846
+ * @param {URL} [options.url]
2847
+ * @param {*} [options.params]
2848
+ */
2849
+ handle(options) {
2850
+ const [responseDone] = this.handleAll(options);
2851
+ return responseDone;
2852
+ }
2853
+ /**
2854
+ * Similar to {@link workbox-strategies.Strategy~handle}, but
2855
+ * instead of just returning a `Promise` that resolves to a `Response` it
2856
+ * it will return an tuple of `[response, done]` promises, where the former
2857
+ * (`response`) is equivalent to what `handle()` returns, and the latter is a
2858
+ * Promise that will resolve once any promises that were added to
2859
+ * `event.waitUntil()` as part of performing the strategy have completed.
2860
+ *
2861
+ * You can await the `done` promise to ensure any extra work performed by
2862
+ * the strategy (usually caching responses) completes successfully.
2863
+ *
2864
+ * @param {FetchEvent|Object} options A `FetchEvent` or an object with the
2865
+ * properties listed below.
2866
+ * @param {Request|string} options.request A request to run this strategy for.
2867
+ * @param {ExtendableEvent} options.event The event associated with the
2868
+ * request.
2869
+ * @param {URL} [options.url]
2870
+ * @param {*} [options.params]
2871
+ * @return {Array<Promise>} A tuple of [response, done]
2872
+ * promises that can be used to determine when the response resolves as
2873
+ * well as when the handler has completed all its work.
2874
+ */
2875
+ handleAll(options) {
2876
+ // Allow for flexible options to be passed.
2877
+ if (options instanceof FetchEvent) {
2878
+ options = {
2879
+ event: options,
2880
+ request: options.request
2881
+ };
2882
+ }
2883
+ const event = options.event;
2884
+ const request = typeof options.request === 'string' ? new Request(options.request) : options.request;
2885
+ const params = 'params' in options ? options.params : undefined;
2886
+ const handler = new StrategyHandler(this, {
2887
+ event,
2888
+ request,
2889
+ params
2890
+ });
2891
+ const responseDone = this._getResponse(handler, request, event);
2892
+ const handlerDone = this._awaitComplete(responseDone, handler, request, event);
2893
+ // Return an array of promises, suitable for use with Promise.all().
2894
+ return [responseDone, handlerDone];
2895
+ }
2896
+ async _getResponse(handler, request, event) {
2897
+ await handler.runCallbacks('handlerWillStart', {
2898
+ event,
2899
+ request
2900
+ });
2901
+ let response = undefined;
2902
+ try {
2903
+ response = await this._handle(request, handler);
2904
+ // The "official" Strategy subclasses all throw this error automatically,
2905
+ // but in case a third-party Strategy doesn't, ensure that we have a
2906
+ // consistent failure when there's no response or an error response.
2907
+ if (!response || response.type === 'error') {
2908
+ throw new WorkboxError('no-response', {
2909
+ url: request.url
2910
+ });
2911
+ }
2912
+ } catch (error) {
2913
+ if (error instanceof Error) {
2914
+ for (const callback of handler.iterateCallbacks('handlerDidError')) {
2915
+ response = await callback({
2916
+ error,
2917
+ event,
2918
+ request
2919
+ });
2920
+ if (response) {
2921
+ break;
2922
+ }
2923
+ }
2924
+ }
2925
+ if (!response) {
2926
+ throw error;
2927
+ } else {
2928
+ logger.log(`While responding to '${getFriendlyURL(request.url)}', ` + `an ${error instanceof Error ? error.toString() : ''} error occurred. Using a fallback response provided by ` + `a handlerDidError plugin.`);
2929
+ }
2930
+ }
2931
+ for (const callback of handler.iterateCallbacks('handlerWillRespond')) {
2932
+ response = await callback({
2933
+ event,
2934
+ request,
2935
+ response
2936
+ });
2937
+ }
2938
+ return response;
2939
+ }
2940
+ async _awaitComplete(responseDone, handler, request, event) {
2941
+ let response;
2942
+ let error;
2943
+ try {
2944
+ response = await responseDone;
2945
+ } catch (error) {
2946
+ // Ignore errors, as response errors should be caught via the `response`
2947
+ // promise above. The `done` promise will only throw for errors in
2948
+ // promises passed to `handler.waitUntil()`.
2949
+ }
2950
+ try {
2951
+ await handler.runCallbacks('handlerDidRespond', {
2952
+ event,
2953
+ request,
2954
+ response
2955
+ });
2956
+ await handler.doneWaiting();
2957
+ } catch (waitUntilError) {
2958
+ if (waitUntilError instanceof Error) {
2959
+ error = waitUntilError;
2960
+ }
2961
+ }
2962
+ await handler.runCallbacks('handlerDidComplete', {
2963
+ event,
2964
+ request,
2965
+ response,
2966
+ error: error
2967
+ });
2968
+ handler.destroy();
2969
+ if (error) {
2970
+ throw error;
2971
+ }
2972
+ }
2973
+ }
2974
+ /**
2975
+ * Classes extending the `Strategy` based class should implement this method,
2976
+ * and leverage the {@link workbox-strategies.StrategyHandler}
2977
+ * arg to perform all fetching and cache logic, which will ensure all relevant
2978
+ * cache, cache options, fetch options and plugins are used (per the current
2979
+ * strategy instance).
2980
+ *
2981
+ * @name _handle
2982
+ * @instance
2983
+ * @abstract
2984
+ * @function
2985
+ * @param {Request} request
2986
+ * @param {workbox-strategies.StrategyHandler} handler
2987
+ * @return {Promise<Response>}
2988
+ *
2989
+ * @memberof workbox-strategies.Strategy
2990
+ */
2991
+
2992
+ /*
2993
+ Copyright 2018 Google LLC
2994
+
2995
+ Use of this source code is governed by an MIT-style
2996
+ license that can be found in the LICENSE file or at
2997
+ https://opensource.org/licenses/MIT.
2998
+ */
2999
+ const messages = {
3000
+ strategyStart: (strategyName, request) => `Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`,
3001
+ printFinalResponse: response => {
3002
+ if (response) {
3003
+ logger.groupCollapsed(`View the final response here.`);
3004
+ logger.log(response || '[No response returned]');
3005
+ logger.groupEnd();
3006
+ }
3007
+ }
3008
+ };
3009
+
3010
+ /*
3011
+ Copyright 2018 Google LLC
3012
+
3013
+ Use of this source code is governed by an MIT-style
3014
+ license that can be found in the LICENSE file or at
3015
+ https://opensource.org/licenses/MIT.
3016
+ */
3017
+ /**
3018
+ * An implementation of a [cache-first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-first-falling-back-to-network)
3019
+ * request strategy.
3020
+ *
3021
+ * A cache first strategy is useful for assets that have been revisioned,
3022
+ * such as URLs like `/styles/example.a8f5f1.css`, since they
3023
+ * can be cached for long periods of time.
3024
+ *
3025
+ * If the network request fails, and there is no cache match, this will throw
3026
+ * a `WorkboxError` exception.
3027
+ *
3028
+ * @extends workbox-strategies.Strategy
3029
+ * @memberof workbox-strategies
3030
+ */
3031
+ class CacheFirst extends Strategy {
3032
+ /**
3033
+ * @private
3034
+ * @param {Request|string} request A request to run this strategy for.
3035
+ * @param {workbox-strategies.StrategyHandler} handler The event that
3036
+ * triggered the request.
3037
+ * @return {Promise<Response>}
3038
+ */
3039
+ async _handle(request, handler) {
3040
+ const logs = [];
3041
+ {
3042
+ finalAssertExports.isInstance(request, Request, {
3043
+ moduleName: 'workbox-strategies',
3044
+ className: this.constructor.name,
3045
+ funcName: 'makeRequest',
3046
+ paramName: 'request'
3047
+ });
3048
+ }
3049
+ let response = await handler.cacheMatch(request);
3050
+ let error = undefined;
3051
+ if (!response) {
3052
+ {
3053
+ logs.push(`No response found in the '${this.cacheName}' cache. ` + `Will respond with a network request.`);
3054
+ }
3055
+ try {
3056
+ response = await handler.fetchAndCachePut(request);
3057
+ } catch (err) {
3058
+ if (err instanceof Error) {
3059
+ error = err;
3060
+ }
3061
+ }
3062
+ {
3063
+ if (response) {
3064
+ logs.push(`Got response from network.`);
3065
+ } else {
3066
+ logs.push(`Unable to get a response from the network.`);
3067
+ }
3068
+ }
3069
+ } else {
3070
+ {
3071
+ logs.push(`Found a cached response in the '${this.cacheName}' cache.`);
3072
+ }
3073
+ }
3074
+ {
3075
+ logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
3076
+ for (const log of logs) {
3077
+ logger.log(log);
3078
+ }
3079
+ messages.printFinalResponse(response);
3080
+ logger.groupEnd();
3081
+ }
3082
+ if (!response) {
3083
+ throw new WorkboxError('no-response', {
3084
+ url: request.url,
3085
+ error
3086
+ });
3087
+ }
3088
+ return response;
3089
+ }
3090
+ }
3091
+
3092
+ /*
3093
+ Copyright 2019 Google LLC
3094
+
3095
+ Use of this source code is governed by an MIT-style
3096
+ license that can be found in the LICENSE file or at
3097
+ https://opensource.org/licenses/MIT.
3098
+ */
3099
+ /**
3100
+ * Claim any currently available clients once the service worker
3101
+ * becomes active. This is normally used in conjunction with `skipWaiting()`.
3102
+ *
3103
+ * @memberof workbox-core
3104
+ */
3105
+ function clientsClaim() {
3106
+ self.addEventListener('activate', () => self.clients.claim());
3107
+ }
3108
+
3109
+ /*
3110
+ Copyright 2020 Google LLC
3111
+ Use of this source code is governed by an MIT-style
3112
+ license that can be found in the LICENSE file or at
3113
+ https://opensource.org/licenses/MIT.
3114
+ */
3115
+ /**
3116
+ * A utility method that makes it easier to use `event.waitUntil` with
3117
+ * async functions and return the result.
3118
+ *
3119
+ * @param {ExtendableEvent} event
3120
+ * @param {Function} asyncFn
3121
+ * @return {Function}
3122
+ * @private
3123
+ */
3124
+ function waitUntil(event, asyncFn) {
3125
+ const returnPromise = asyncFn();
3126
+ event.waitUntil(returnPromise);
3127
+ return returnPromise;
3128
+ }
3129
+
3130
+ // @ts-ignore
3131
+ try {
3132
+ self['workbox:precaching:7.3.0'] && _();
3133
+ } catch (e) {}
3134
+
3135
+ /*
3136
+ Copyright 2018 Google LLC
3137
+
3138
+ Use of this source code is governed by an MIT-style
3139
+ license that can be found in the LICENSE file or at
3140
+ https://opensource.org/licenses/MIT.
3141
+ */
3142
+ // Name of the search parameter used to store revision info.
3143
+ const REVISION_SEARCH_PARAM = '__WB_REVISION__';
3144
+ /**
3145
+ * Converts a manifest entry into a versioned URL suitable for precaching.
3146
+ *
3147
+ * @param {Object|string} entry
3148
+ * @return {string} A URL with versioning info.
3149
+ *
3150
+ * @private
3151
+ * @memberof workbox-precaching
3152
+ */
3153
+ function createCacheKey(entry) {
3154
+ if (!entry) {
3155
+ throw new WorkboxError('add-to-cache-list-unexpected-type', {
3156
+ entry
3157
+ });
3158
+ }
3159
+ // If a precache manifest entry is a string, it's assumed to be a versioned
3160
+ // URL, like '/app.abcd1234.js'. Return as-is.
3161
+ if (typeof entry === 'string') {
3162
+ const urlObject = new URL(entry, location.href);
3163
+ return {
3164
+ cacheKey: urlObject.href,
3165
+ url: urlObject.href
3166
+ };
3167
+ }
3168
+ const {
3169
+ revision,
3170
+ url
3171
+ } = entry;
3172
+ if (!url) {
3173
+ throw new WorkboxError('add-to-cache-list-unexpected-type', {
3174
+ entry
3175
+ });
3176
+ }
3177
+ // If there's just a URL and no revision, then it's also assumed to be a
3178
+ // versioned URL.
3179
+ if (!revision) {
3180
+ const urlObject = new URL(url, location.href);
3181
+ return {
3182
+ cacheKey: urlObject.href,
3183
+ url: urlObject.href
3184
+ };
3185
+ }
3186
+ // Otherwise, construct a properly versioned URL using the custom Workbox
3187
+ // search parameter along with the revision info.
3188
+ const cacheKeyURL = new URL(url, location.href);
3189
+ const originalURL = new URL(url, location.href);
3190
+ cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision);
3191
+ return {
3192
+ cacheKey: cacheKeyURL.href,
3193
+ url: originalURL.href
3194
+ };
3195
+ }
3196
+
3197
+ /*
3198
+ Copyright 2020 Google LLC
3199
+
3200
+ Use of this source code is governed by an MIT-style
3201
+ license that can be found in the LICENSE file or at
3202
+ https://opensource.org/licenses/MIT.
3203
+ */
3204
+ /**
3205
+ * A plugin, designed to be used with PrecacheController, to determine the
3206
+ * of assets that were updated (or not updated) during the install event.
3207
+ *
3208
+ * @private
3209
+ */
3210
+ class PrecacheInstallReportPlugin {
3211
+ constructor() {
3212
+ this.updatedURLs = [];
3213
+ this.notUpdatedURLs = [];
3214
+ this.handlerWillStart = async ({
3215
+ request,
3216
+ state
3217
+ }) => {
3218
+ // TODO: `state` should never be undefined...
3219
+ if (state) {
3220
+ state.originalRequest = request;
3221
+ }
3222
+ };
3223
+ this.cachedResponseWillBeUsed = async ({
3224
+ event,
3225
+ state,
3226
+ cachedResponse
3227
+ }) => {
3228
+ if (event.type === 'install') {
3229
+ if (state && state.originalRequest && state.originalRequest instanceof Request) {
3230
+ // TODO: `state` should never be undefined...
3231
+ const url = state.originalRequest.url;
3232
+ if (cachedResponse) {
3233
+ this.notUpdatedURLs.push(url);
3234
+ } else {
3235
+ this.updatedURLs.push(url);
3236
+ }
3237
+ }
3238
+ }
3239
+ return cachedResponse;
3240
+ };
3241
+ }
3242
+ }
3243
+
3244
+ /*
3245
+ Copyright 2020 Google LLC
3246
+
3247
+ Use of this source code is governed by an MIT-style
3248
+ license that can be found in the LICENSE file or at
3249
+ https://opensource.org/licenses/MIT.
3250
+ */
3251
+ /**
3252
+ * A plugin, designed to be used with PrecacheController, to translate URLs into
3253
+ * the corresponding cache key, based on the current revision info.
3254
+ *
3255
+ * @private
3256
+ */
3257
+ class PrecacheCacheKeyPlugin {
3258
+ constructor({
3259
+ precacheController
3260
+ }) {
3261
+ this.cacheKeyWillBeUsed = async ({
3262
+ request,
3263
+ params
3264
+ }) => {
3265
+ // Params is type any, can't change right now.
3266
+ /* eslint-disable */
3267
+ const cacheKey = (params === null || params === void 0 ? void 0 : params.cacheKey) || this._precacheController.getCacheKeyForURL(request.url);
3268
+ /* eslint-enable */
3269
+ return cacheKey ? new Request(cacheKey, {
3270
+ headers: request.headers
3271
+ }) : request;
3272
+ };
3273
+ this._precacheController = precacheController;
3274
+ }
3275
+ }
3276
+
3277
+ /*
3278
+ Copyright 2018 Google LLC
3279
+
3280
+ Use of this source code is governed by an MIT-style
3281
+ license that can be found in the LICENSE file or at
3282
+ https://opensource.org/licenses/MIT.
3283
+ */
3284
+ /**
3285
+ * @param {string} groupTitle
3286
+ * @param {Array<string>} deletedURLs
3287
+ *
3288
+ * @private
3289
+ */
3290
+ const logGroup = (groupTitle, deletedURLs) => {
3291
+ logger.groupCollapsed(groupTitle);
3292
+ for (const url of deletedURLs) {
3293
+ logger.log(url);
3294
+ }
3295
+ logger.groupEnd();
3296
+ };
3297
+ /**
3298
+ * @param {Array<string>} deletedURLs
3299
+ *
3300
+ * @private
3301
+ * @memberof workbox-precaching
3302
+ */
3303
+ function printCleanupDetails(deletedURLs) {
3304
+ const deletionCount = deletedURLs.length;
3305
+ if (deletionCount > 0) {
3306
+ logger.groupCollapsed(`During precaching cleanup, ` + `${deletionCount} cached ` + `request${deletionCount === 1 ? ' was' : 's were'} deleted.`);
3307
+ logGroup('Deleted Cache Requests', deletedURLs);
3308
+ logger.groupEnd();
3309
+ }
3310
+ }
3311
+
3312
+ /*
3313
+ Copyright 2018 Google LLC
3314
+
3315
+ Use of this source code is governed by an MIT-style
3316
+ license that can be found in the LICENSE file or at
3317
+ https://opensource.org/licenses/MIT.
3318
+ */
3319
+ /**
3320
+ * @param {string} groupTitle
3321
+ * @param {Array<string>} urls
3322
+ *
3323
+ * @private
3324
+ */
3325
+ function _nestedGroup(groupTitle, urls) {
3326
+ if (urls.length === 0) {
3327
+ return;
3328
+ }
3329
+ logger.groupCollapsed(groupTitle);
3330
+ for (const url of urls) {
3331
+ logger.log(url);
3332
+ }
3333
+ logger.groupEnd();
3334
+ }
3335
+ /**
3336
+ * @param {Array<string>} urlsToPrecache
3337
+ * @param {Array<string>} urlsAlreadyPrecached
3338
+ *
3339
+ * @private
3340
+ * @memberof workbox-precaching
3341
+ */
3342
+ function printInstallDetails(urlsToPrecache, urlsAlreadyPrecached) {
3343
+ const precachedCount = urlsToPrecache.length;
3344
+ const alreadyPrecachedCount = urlsAlreadyPrecached.length;
3345
+ if (precachedCount || alreadyPrecachedCount) {
3346
+ let message = `Precaching ${precachedCount} file${precachedCount === 1 ? '' : 's'}.`;
3347
+ if (alreadyPrecachedCount > 0) {
3348
+ message += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? ' is' : 's are'} already cached.`;
3349
+ }
3350
+ logger.groupCollapsed(message);
3351
+ _nestedGroup(`View newly precached URLs.`, urlsToPrecache);
3352
+ _nestedGroup(`View previously precached URLs.`, urlsAlreadyPrecached);
3353
+ logger.groupEnd();
3354
+ }
3355
+ }
3356
+
3357
+ /*
3358
+ Copyright 2019 Google LLC
3359
+
3360
+ Use of this source code is governed by an MIT-style
3361
+ license that can be found in the LICENSE file or at
3362
+ https://opensource.org/licenses/MIT.
3363
+ */
3364
+ let supportStatus;
3365
+ /**
3366
+ * A utility function that determines whether the current browser supports
3367
+ * constructing a new `Response` from a `response.body` stream.
3368
+ *
3369
+ * @return {boolean} `true`, if the current browser can successfully
3370
+ * construct a `Response` from a `response.body` stream, `false` otherwise.
3371
+ *
3372
+ * @private
3373
+ */
3374
+ function canConstructResponseFromBodyStream() {
3375
+ if (supportStatus === undefined) {
3376
+ const testResponse = new Response('');
3377
+ if ('body' in testResponse) {
3378
+ try {
3379
+ new Response(testResponse.body);
3380
+ supportStatus = true;
3381
+ } catch (error) {
3382
+ supportStatus = false;
3383
+ }
3384
+ }
3385
+ supportStatus = false;
3386
+ }
3387
+ return supportStatus;
3388
+ }
3389
+
3390
+ /*
3391
+ Copyright 2019 Google LLC
3392
+
3393
+ Use of this source code is governed by an MIT-style
3394
+ license that can be found in the LICENSE file or at
3395
+ https://opensource.org/licenses/MIT.
3396
+ */
3397
+ /**
3398
+ * Allows developers to copy a response and modify its `headers`, `status`,
3399
+ * or `statusText` values (the values settable via a
3400
+ * [`ResponseInit`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#Syntax}
3401
+ * object in the constructor).
3402
+ * To modify these values, pass a function as the second argument. That
3403
+ * function will be invoked with a single object with the response properties
3404
+ * `{headers, status, statusText}`. The return value of this function will
3405
+ * be used as the `ResponseInit` for the new `Response`. To change the values
3406
+ * either modify the passed parameter(s) and return it, or return a totally
3407
+ * new object.
3408
+ *
3409
+ * This method is intentionally limited to same-origin responses, regardless of
3410
+ * whether CORS was used or not.
3411
+ *
3412
+ * @param {Response} response
3413
+ * @param {Function} modifier
3414
+ * @memberof workbox-core
3415
+ */
3416
+ async function copyResponse(response, modifier) {
3417
+ let origin = null;
3418
+ // If response.url isn't set, assume it's cross-origin and keep origin null.
3419
+ if (response.url) {
3420
+ const responseURL = new URL(response.url);
3421
+ origin = responseURL.origin;
3422
+ }
3423
+ if (origin !== self.location.origin) {
3424
+ throw new WorkboxError('cross-origin-copy-response', {
3425
+ origin
3426
+ });
3427
+ }
3428
+ const clonedResponse = response.clone();
3429
+ // Create a fresh `ResponseInit` object by cloning the headers.
3430
+ const responseInit = {
3431
+ headers: new Headers(clonedResponse.headers),
3432
+ status: clonedResponse.status,
3433
+ statusText: clonedResponse.statusText
3434
+ };
3435
+ // Apply any user modifications.
3436
+ const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit;
3437
+ // Create the new response from the body stream and `ResponseInit`
3438
+ // modifications. Note: not all browsers support the Response.body stream,
3439
+ // so fall back to reading the entire body into memory as a blob.
3440
+ const body = canConstructResponseFromBodyStream() ? clonedResponse.body : await clonedResponse.blob();
3441
+ return new Response(body, modifiedResponseInit);
3442
+ }
3443
+
3444
+ /*
3445
+ Copyright 2020 Google LLC
3446
+
3447
+ Use of this source code is governed by an MIT-style
3448
+ license that can be found in the LICENSE file or at
3449
+ https://opensource.org/licenses/MIT.
3450
+ */
3451
+ /**
3452
+ * A {@link workbox-strategies.Strategy} implementation
3453
+ * specifically designed to work with
3454
+ * {@link workbox-precaching.PrecacheController}
3455
+ * to both cache and fetch precached assets.
3456
+ *
3457
+ * Note: an instance of this class is created automatically when creating a
3458
+ * `PrecacheController`; it's generally not necessary to create this yourself.
3459
+ *
3460
+ * @extends workbox-strategies.Strategy
3461
+ * @memberof workbox-precaching
3462
+ */
3463
+ class PrecacheStrategy extends Strategy {
3464
+ /**
3465
+ *
3466
+ * @param {Object} [options]
3467
+ * @param {string} [options.cacheName] Cache name to store and retrieve
3468
+ * requests. Defaults to the cache names provided by
3469
+ * {@link workbox-core.cacheNames}.
3470
+ * @param {Array<Object>} [options.plugins] {@link https://developers.google.com/web/tools/workbox/guides/using-plugins|Plugins}
3471
+ * to use in conjunction with this caching strategy.
3472
+ * @param {Object} [options.fetchOptions] Values passed along to the
3473
+ * {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters|init}
3474
+ * of all fetch() requests made by this strategy.
3475
+ * @param {Object} [options.matchOptions] The
3476
+ * {@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions|CacheQueryOptions}
3477
+ * for any `cache.match()` or `cache.put()` calls made by this strategy.
3478
+ * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to
3479
+ * get the response from the network if there's a precache miss.
3480
+ */
3481
+ constructor(options = {}) {
3482
+ options.cacheName = cacheNames.getPrecacheName(options.cacheName);
3483
+ super(options);
3484
+ this._fallbackToNetwork = options.fallbackToNetwork === false ? false : true;
3485
+ // Redirected responses cannot be used to satisfy a navigation request, so
3486
+ // any redirected response must be "copied" rather than cloned, so the new
3487
+ // response doesn't contain the `redirected` flag. See:
3488
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=669363&desc=2#c1
3489
+ this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin);
3490
+ }
3491
+ /**
3492
+ * @private
3493
+ * @param {Request|string} request A request to run this strategy for.
3494
+ * @param {workbox-strategies.StrategyHandler} handler The event that
3495
+ * triggered the request.
3496
+ * @return {Promise<Response>}
3497
+ */
3498
+ async _handle(request, handler) {
3499
+ const response = await handler.cacheMatch(request);
3500
+ if (response) {
3501
+ return response;
3502
+ }
3503
+ // If this is an `install` event for an entry that isn't already cached,
3504
+ // then populate the cache.
3505
+ if (handler.event && handler.event.type === 'install') {
3506
+ return await this._handleInstall(request, handler);
3507
+ }
3508
+ // Getting here means something went wrong. An entry that should have been
3509
+ // precached wasn't found in the cache.
3510
+ return await this._handleFetch(request, handler);
3511
+ }
3512
+ async _handleFetch(request, handler) {
3513
+ let response;
3514
+ const params = handler.params || {};
3515
+ // Fall back to the network if we're configured to do so.
3516
+ if (this._fallbackToNetwork) {
3517
+ {
3518
+ logger.warn(`The precached response for ` + `${getFriendlyURL(request.url)} in ${this.cacheName} was not ` + `found. Falling back to the network.`);
3519
+ }
3520
+ const integrityInManifest = params.integrity;
3521
+ const integrityInRequest = request.integrity;
3522
+ const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest;
3523
+ // Do not add integrity if the original request is no-cors
3524
+ // See https://github.com/GoogleChrome/workbox/issues/3096
3525
+ response = await handler.fetch(new Request(request, {
3526
+ integrity: request.mode !== 'no-cors' ? integrityInRequest || integrityInManifest : undefined
3527
+ }));
3528
+ // It's only "safe" to repair the cache if we're using SRI to guarantee
3529
+ // that the response matches the precache manifest's expectations,
3530
+ // and there's either a) no integrity property in the incoming request
3531
+ // or b) there is an integrity, and it matches the precache manifest.
3532
+ // See https://github.com/GoogleChrome/workbox/issues/2858
3533
+ // Also if the original request users no-cors we don't use integrity.
3534
+ // See https://github.com/GoogleChrome/workbox/issues/3096
3535
+ if (integrityInManifest && noIntegrityConflict && request.mode !== 'no-cors') {
3536
+ this._useDefaultCacheabilityPluginIfNeeded();
3537
+ const wasCached = await handler.cachePut(request, response.clone());
3538
+ {
3539
+ if (wasCached) {
3540
+ logger.log(`A response for ${getFriendlyURL(request.url)} ` + `was used to "repair" the precache.`);
3541
+ }
3542
+ }
3543
+ }
3544
+ } else {
3545
+ // This shouldn't normally happen, but there are edge cases:
3546
+ // https://github.com/GoogleChrome/workbox/issues/1441
3547
+ throw new WorkboxError('missing-precache-entry', {
3548
+ cacheName: this.cacheName,
3549
+ url: request.url
3550
+ });
3551
+ }
3552
+ {
3553
+ const cacheKey = params.cacheKey || (await handler.getCacheKey(request, 'read'));
3554
+ // Workbox is going to handle the route.
3555
+ // print the routing details to the console.
3556
+ logger.groupCollapsed(`Precaching is responding to: ` + getFriendlyURL(request.url));
3557
+ logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`);
3558
+ logger.groupCollapsed(`View request details here.`);
3559
+ logger.log(request);
3560
+ logger.groupEnd();
3561
+ logger.groupCollapsed(`View response details here.`);
3562
+ logger.log(response);
3563
+ logger.groupEnd();
3564
+ logger.groupEnd();
3565
+ }
3566
+ return response;
3567
+ }
3568
+ async _handleInstall(request, handler) {
3569
+ this._useDefaultCacheabilityPluginIfNeeded();
3570
+ const response = await handler.fetch(request);
3571
+ // Make sure we defer cachePut() until after we know the response
3572
+ // should be cached; see https://github.com/GoogleChrome/workbox/issues/2737
3573
+ const wasCached = await handler.cachePut(request, response.clone());
3574
+ if (!wasCached) {
3575
+ // Throwing here will lead to the `install` handler failing, which
3576
+ // we want to do if *any* of the responses aren't safe to cache.
3577
+ throw new WorkboxError('bad-precaching-response', {
3578
+ url: request.url,
3579
+ status: response.status
3580
+ });
3581
+ }
3582
+ return response;
3583
+ }
3584
+ /**
3585
+ * This method is complex, as there a number of things to account for:
3586
+ *
3587
+ * The `plugins` array can be set at construction, and/or it might be added to
3588
+ * to at any time before the strategy is used.
3589
+ *
3590
+ * At the time the strategy is used (i.e. during an `install` event), there
3591
+ * needs to be at least one plugin that implements `cacheWillUpdate` in the
3592
+ * array, other than `copyRedirectedCacheableResponsesPlugin`.
3593
+ *
3594
+ * - If this method is called and there are no suitable `cacheWillUpdate`
3595
+ * plugins, we need to add `defaultPrecacheCacheabilityPlugin`.
3596
+ *
3597
+ * - If this method is called and there is exactly one `cacheWillUpdate`, then
3598
+ * we don't have to do anything (this might be a previously added
3599
+ * `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin).
3600
+ *
3601
+ * - If this method is called and there is more than one `cacheWillUpdate`,
3602
+ * then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so,
3603
+ * we need to remove it. (This situation is unlikely, but it could happen if
3604
+ * the strategy is used multiple times, the first without a `cacheWillUpdate`,
3605
+ * and then later on after manually adding a custom `cacheWillUpdate`.)
3606
+ *
3607
+ * See https://github.com/GoogleChrome/workbox/issues/2737 for more context.
3608
+ *
3609
+ * @private
3610
+ */
3611
+ _useDefaultCacheabilityPluginIfNeeded() {
3612
+ let defaultPluginIndex = null;
3613
+ let cacheWillUpdatePluginCount = 0;
3614
+ for (const [index, plugin] of this.plugins.entries()) {
3615
+ // Ignore the copy redirected plugin when determining what to do.
3616
+ if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) {
3617
+ continue;
3618
+ }
3619
+ // Save the default plugin's index, in case it needs to be removed.
3620
+ if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) {
3621
+ defaultPluginIndex = index;
3622
+ }
3623
+ if (plugin.cacheWillUpdate) {
3624
+ cacheWillUpdatePluginCount++;
3625
+ }
3626
+ }
3627
+ if (cacheWillUpdatePluginCount === 0) {
3628
+ this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin);
3629
+ } else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) {
3630
+ // Only remove the default plugin; multiple custom plugins are allowed.
3631
+ this.plugins.splice(defaultPluginIndex, 1);
3632
+ }
3633
+ // Nothing needs to be done if cacheWillUpdatePluginCount is 1
3634
+ }
3635
+ }
3636
+ PrecacheStrategy.defaultPrecacheCacheabilityPlugin = {
3637
+ async cacheWillUpdate({
3638
+ response
3639
+ }) {
3640
+ if (!response || response.status >= 400) {
3641
+ return null;
3642
+ }
3643
+ return response;
3644
+ }
3645
+ };
3646
+ PrecacheStrategy.copyRedirectedCacheableResponsesPlugin = {
3647
+ async cacheWillUpdate({
3648
+ response
3649
+ }) {
3650
+ return response.redirected ? await copyResponse(response) : response;
3651
+ }
3652
+ };
3653
+
3654
+ /*
3655
+ Copyright 2019 Google LLC
3656
+
3657
+ Use of this source code is governed by an MIT-style
3658
+ license that can be found in the LICENSE file or at
3659
+ https://opensource.org/licenses/MIT.
3660
+ */
3661
+ /**
3662
+ * Performs efficient precaching of assets.
3663
+ *
3664
+ * @memberof workbox-precaching
3665
+ */
3666
+ class PrecacheController {
3667
+ /**
3668
+ * Create a new PrecacheController.
3669
+ *
3670
+ * @param {Object} [options]
3671
+ * @param {string} [options.cacheName] The cache to use for precaching.
3672
+ * @param {string} [options.plugins] Plugins to use when precaching as well
3673
+ * as responding to fetch events for precached assets.
3674
+ * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to
3675
+ * get the response from the network if there's a precache miss.
3676
+ */
3677
+ constructor({
3678
+ cacheName,
3679
+ plugins = [],
3680
+ fallbackToNetwork = true
3681
+ } = {}) {
3682
+ this._urlsToCacheKeys = new Map();
3683
+ this._urlsToCacheModes = new Map();
3684
+ this._cacheKeysToIntegrities = new Map();
3685
+ this._strategy = new PrecacheStrategy({
3686
+ cacheName: cacheNames.getPrecacheName(cacheName),
3687
+ plugins: [...plugins, new PrecacheCacheKeyPlugin({
3688
+ precacheController: this
3689
+ })],
3690
+ fallbackToNetwork
3691
+ });
3692
+ // Bind the install and activate methods to the instance.
3693
+ this.install = this.install.bind(this);
3694
+ this.activate = this.activate.bind(this);
3695
+ }
3696
+ /**
3697
+ * @type {workbox-precaching.PrecacheStrategy} The strategy created by this controller and
3698
+ * used to cache assets and respond to fetch events.
3699
+ */
3700
+ get strategy() {
3701
+ return this._strategy;
3702
+ }
3703
+ /**
3704
+ * Adds items to the precache list, removing any duplicates and
3705
+ * stores the files in the
3706
+ * {@link workbox-core.cacheNames|"precache cache"} when the service
3707
+ * worker installs.
3708
+ *
3709
+ * This method can be called multiple times.
3710
+ *
3711
+ * @param {Array<Object|string>} [entries=[]] Array of entries to precache.
3712
+ */
3713
+ precache(entries) {
3714
+ this.addToCacheList(entries);
3715
+ if (!this._installAndActiveListenersAdded) {
3716
+ self.addEventListener('install', this.install);
3717
+ self.addEventListener('activate', this.activate);
3718
+ this._installAndActiveListenersAdded = true;
3719
+ }
3720
+ }
3721
+ /**
3722
+ * This method will add items to the precache list, removing duplicates
3723
+ * and ensuring the information is valid.
3724
+ *
3725
+ * @param {Array<workbox-precaching.PrecacheController.PrecacheEntry|string>} entries
3726
+ * Array of entries to precache.
3727
+ */
3728
+ addToCacheList(entries) {
3729
+ {
3730
+ finalAssertExports.isArray(entries, {
3731
+ moduleName: 'workbox-precaching',
3732
+ className: 'PrecacheController',
3733
+ funcName: 'addToCacheList',
3734
+ paramName: 'entries'
3735
+ });
3736
+ }
3737
+ const urlsToWarnAbout = [];
3738
+ for (const entry of entries) {
3739
+ // See https://github.com/GoogleChrome/workbox/issues/2259
3740
+ if (typeof entry === 'string') {
3741
+ urlsToWarnAbout.push(entry);
3742
+ } else if (entry && entry.revision === undefined) {
3743
+ urlsToWarnAbout.push(entry.url);
3744
+ }
3745
+ const {
3746
+ cacheKey,
3747
+ url
3748
+ } = createCacheKey(entry);
3749
+ const cacheMode = typeof entry !== 'string' && entry.revision ? 'reload' : 'default';
3750
+ if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) {
3751
+ throw new WorkboxError('add-to-cache-list-conflicting-entries', {
3752
+ firstEntry: this._urlsToCacheKeys.get(url),
3753
+ secondEntry: cacheKey
3754
+ });
3755
+ }
3756
+ if (typeof entry !== 'string' && entry.integrity) {
3757
+ if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) {
3758
+ throw new WorkboxError('add-to-cache-list-conflicting-integrities', {
3759
+ url
3760
+ });
3761
+ }
3762
+ this._cacheKeysToIntegrities.set(cacheKey, entry.integrity);
3763
+ }
3764
+ this._urlsToCacheKeys.set(url, cacheKey);
3765
+ this._urlsToCacheModes.set(url, cacheMode);
3766
+ if (urlsToWarnAbout.length > 0) {
3767
+ const warningMessage = `Workbox is precaching URLs without revision ` + `info: ${urlsToWarnAbout.join(', ')}\nThis is generally NOT safe. ` + `Learn more at https://bit.ly/wb-precache`;
3768
+ {
3769
+ logger.warn(warningMessage);
3770
+ }
3771
+ }
3772
+ }
3773
+ }
3774
+ /**
3775
+ * Precaches new and updated assets. Call this method from the service worker
3776
+ * install event.
3777
+ *
3778
+ * Note: this method calls `event.waitUntil()` for you, so you do not need
3779
+ * to call it yourself in your event handlers.
3780
+ *
3781
+ * @param {ExtendableEvent} event
3782
+ * @return {Promise<workbox-precaching.InstallResult>}
3783
+ */
3784
+ install(event) {
3785
+ // waitUntil returns Promise<any>
3786
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
3787
+ return waitUntil(event, async () => {
3788
+ const installReportPlugin = new PrecacheInstallReportPlugin();
3789
+ this.strategy.plugins.push(installReportPlugin);
3790
+ // Cache entries one at a time.
3791
+ // See https://github.com/GoogleChrome/workbox/issues/2528
3792
+ for (const [url, cacheKey] of this._urlsToCacheKeys) {
3793
+ const integrity = this._cacheKeysToIntegrities.get(cacheKey);
3794
+ const cacheMode = this._urlsToCacheModes.get(url);
3795
+ const request = new Request(url, {
3796
+ integrity,
3797
+ cache: cacheMode,
3798
+ credentials: 'same-origin'
3799
+ });
3800
+ await Promise.all(this.strategy.handleAll({
3801
+ params: {
3802
+ cacheKey
3803
+ },
3804
+ request,
3805
+ event
3806
+ }));
3807
+ }
3808
+ const {
3809
+ updatedURLs,
3810
+ notUpdatedURLs
3811
+ } = installReportPlugin;
3812
+ {
3813
+ printInstallDetails(updatedURLs, notUpdatedURLs);
3814
+ }
3815
+ return {
3816
+ updatedURLs,
3817
+ notUpdatedURLs
3818
+ };
3819
+ });
3820
+ }
3821
+ /**
3822
+ * Deletes assets that are no longer present in the current precache manifest.
3823
+ * Call this method from the service worker activate event.
3824
+ *
3825
+ * Note: this method calls `event.waitUntil()` for you, so you do not need
3826
+ * to call it yourself in your event handlers.
3827
+ *
3828
+ * @param {ExtendableEvent} event
3829
+ * @return {Promise<workbox-precaching.CleanupResult>}
3830
+ */
3831
+ activate(event) {
3832
+ // waitUntil returns Promise<any>
3833
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
3834
+ return waitUntil(event, async () => {
3835
+ const cache = await self.caches.open(this.strategy.cacheName);
3836
+ const currentlyCachedRequests = await cache.keys();
3837
+ const expectedCacheKeys = new Set(this._urlsToCacheKeys.values());
3838
+ const deletedURLs = [];
3839
+ for (const request of currentlyCachedRequests) {
3840
+ if (!expectedCacheKeys.has(request.url)) {
3841
+ await cache.delete(request);
3842
+ deletedURLs.push(request.url);
3843
+ }
3844
+ }
3845
+ {
3846
+ printCleanupDetails(deletedURLs);
3847
+ }
3848
+ return {
3849
+ deletedURLs
3850
+ };
3851
+ });
3852
+ }
3853
+ /**
3854
+ * Returns a mapping of a precached URL to the corresponding cache key, taking
3855
+ * into account the revision information for the URL.
3856
+ *
3857
+ * @return {Map<string, string>} A URL to cache key mapping.
3858
+ */
3859
+ getURLsToCacheKeys() {
3860
+ return this._urlsToCacheKeys;
3861
+ }
3862
+ /**
3863
+ * Returns a list of all the URLs that have been precached by the current
3864
+ * service worker.
3865
+ *
3866
+ * @return {Array<string>} The precached URLs.
3867
+ */
3868
+ getCachedURLs() {
3869
+ return [...this._urlsToCacheKeys.keys()];
3870
+ }
3871
+ /**
3872
+ * Returns the cache key used for storing a given URL. If that URL is
3873
+ * unversioned, like `/index.html', then the cache key will be the original
3874
+ * URL with a search parameter appended to it.
3875
+ *
3876
+ * @param {string} url A URL whose cache key you want to look up.
3877
+ * @return {string} The versioned URL that corresponds to a cache key
3878
+ * for the original URL, or undefined if that URL isn't precached.
3879
+ */
3880
+ getCacheKeyForURL(url) {
3881
+ const urlObject = new URL(url, location.href);
3882
+ return this._urlsToCacheKeys.get(urlObject.href);
3883
+ }
3884
+ /**
3885
+ * @param {string} url A cache key whose SRI you want to look up.
3886
+ * @return {string} The subresource integrity associated with the cache key,
3887
+ * or undefined if it's not set.
3888
+ */
3889
+ getIntegrityForCacheKey(cacheKey) {
3890
+ return this._cacheKeysToIntegrities.get(cacheKey);
3891
+ }
3892
+ /**
3893
+ * This acts as a drop-in replacement for
3894
+ * [`cache.match()`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match)
3895
+ * with the following differences:
3896
+ *
3897
+ * - It knows what the name of the precache is, and only checks in that cache.
3898
+ * - It allows you to pass in an "original" URL without versioning parameters,
3899
+ * and it will automatically look up the correct cache key for the currently
3900
+ * active revision of that URL.
3901
+ *
3902
+ * E.g., `matchPrecache('index.html')` will find the correct precached
3903
+ * response for the currently active service worker, even if the actual cache
3904
+ * key is `'/index.html?__WB_REVISION__=1234abcd'`.
3905
+ *
3906
+ * @param {string|Request} request The key (without revisioning parameters)
3907
+ * to look up in the precache.
3908
+ * @return {Promise<Response|undefined>}
3909
+ */
3910
+ async matchPrecache(request) {
3911
+ const url = request instanceof Request ? request.url : request;
3912
+ const cacheKey = this.getCacheKeyForURL(url);
3913
+ if (cacheKey) {
3914
+ const cache = await self.caches.open(this.strategy.cacheName);
3915
+ return cache.match(cacheKey);
3916
+ }
3917
+ return undefined;
3918
+ }
3919
+ /**
3920
+ * Returns a function that looks up `url` in the precache (taking into
3921
+ * account revision information), and returns the corresponding `Response`.
3922
+ *
3923
+ * @param {string} url The precached URL which will be used to lookup the
3924
+ * `Response`.
3925
+ * @return {workbox-routing~handlerCallback}
3926
+ */
3927
+ createHandlerBoundToURL(url) {
3928
+ const cacheKey = this.getCacheKeyForURL(url);
3929
+ if (!cacheKey) {
3930
+ throw new WorkboxError('non-precached-url', {
3931
+ url
3932
+ });
3933
+ }
3934
+ return options => {
3935
+ options.request = new Request(url);
3936
+ options.params = Object.assign({
3937
+ cacheKey
3938
+ }, options.params);
3939
+ return this.strategy.handle(options);
3940
+ };
3941
+ }
3942
+ }
3943
+
3944
+ /*
3945
+ Copyright 2019 Google LLC
3946
+
3947
+ Use of this source code is governed by an MIT-style
3948
+ license that can be found in the LICENSE file or at
3949
+ https://opensource.org/licenses/MIT.
3950
+ */
3951
+ let precacheController;
3952
+ /**
3953
+ * @return {PrecacheController}
3954
+ * @private
3955
+ */
3956
+ const getOrCreatePrecacheController = () => {
3957
+ if (!precacheController) {
3958
+ precacheController = new PrecacheController();
3959
+ }
3960
+ return precacheController;
3961
+ };
3962
+
3963
+ /*
3964
+ Copyright 2018 Google LLC
3965
+
3966
+ Use of this source code is governed by an MIT-style
3967
+ license that can be found in the LICENSE file or at
3968
+ https://opensource.org/licenses/MIT.
3969
+ */
3970
+ /**
3971
+ * Removes any URL search parameters that should be ignored.
3972
+ *
3973
+ * @param {URL} urlObject The original URL.
3974
+ * @param {Array<RegExp>} ignoreURLParametersMatching RegExps to test against
3975
+ * each search parameter name. Matches mean that the search parameter should be
3976
+ * ignored.
3977
+ * @return {URL} The URL with any ignored search parameters removed.
3978
+ *
3979
+ * @private
3980
+ * @memberof workbox-precaching
3981
+ */
3982
+ function removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching = []) {
3983
+ // Convert the iterable into an array at the start of the loop to make sure
3984
+ // deletion doesn't mess up iteration.
3985
+ for (const paramName of [...urlObject.searchParams.keys()]) {
3986
+ if (ignoreURLParametersMatching.some(regExp => regExp.test(paramName))) {
3987
+ urlObject.searchParams.delete(paramName);
3988
+ }
3989
+ }
3990
+ return urlObject;
3991
+ }
3992
+
3993
+ /*
3994
+ Copyright 2019 Google LLC
3995
+
3996
+ Use of this source code is governed by an MIT-style
3997
+ license that can be found in the LICENSE file or at
3998
+ https://opensource.org/licenses/MIT.
3999
+ */
4000
+ /**
4001
+ * Generator function that yields possible variations on the original URL to
4002
+ * check, one at a time.
4003
+ *
4004
+ * @param {string} url
4005
+ * @param {Object} options
4006
+ *
4007
+ * @private
4008
+ * @memberof workbox-precaching
4009
+ */
4010
+ function* generateURLVariations(url, {
4011
+ ignoreURLParametersMatching = [/^utm_/, /^fbclid$/],
4012
+ directoryIndex = 'index.html',
4013
+ cleanURLs = true,
4014
+ urlManipulation
4015
+ } = {}) {
4016
+ const urlObject = new URL(url, location.href);
4017
+ urlObject.hash = '';
4018
+ yield urlObject.href;
4019
+ const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching);
4020
+ yield urlWithoutIgnoredParams.href;
4021
+ if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) {
4022
+ const directoryURL = new URL(urlWithoutIgnoredParams.href);
4023
+ directoryURL.pathname += directoryIndex;
4024
+ yield directoryURL.href;
4025
+ }
4026
+ if (cleanURLs) {
4027
+ const cleanURL = new URL(urlWithoutIgnoredParams.href);
4028
+ cleanURL.pathname += '.html';
4029
+ yield cleanURL.href;
4030
+ }
4031
+ if (urlManipulation) {
4032
+ const additionalURLs = urlManipulation({
4033
+ url: urlObject
4034
+ });
4035
+ for (const urlToAttempt of additionalURLs) {
4036
+ yield urlToAttempt.href;
4037
+ }
4038
+ }
4039
+ }
4040
+
4041
+ /*
4042
+ Copyright 2020 Google LLC
4043
+
4044
+ Use of this source code is governed by an MIT-style
4045
+ license that can be found in the LICENSE file or at
4046
+ https://opensource.org/licenses/MIT.
4047
+ */
4048
+ /**
4049
+ * A subclass of {@link workbox-routing.Route} that takes a
4050
+ * {@link workbox-precaching.PrecacheController}
4051
+ * instance and uses it to match incoming requests and handle fetching
4052
+ * responses from the precache.
4053
+ *
4054
+ * @memberof workbox-precaching
4055
+ * @extends workbox-routing.Route
4056
+ */
4057
+ class PrecacheRoute extends Route {
4058
+ /**
4059
+ * @param {PrecacheController} precacheController A `PrecacheController`
4060
+ * instance used to both match requests and respond to fetch events.
4061
+ * @param {Object} [options] Options to control how requests are matched
4062
+ * against the list of precached URLs.
4063
+ * @param {string} [options.directoryIndex=index.html] The `directoryIndex` will
4064
+ * check cache entries for a URLs ending with '/' to see if there is a hit when
4065
+ * appending the `directoryIndex` value.
4066
+ * @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/, /^fbclid$/]] An
4067
+ * array of regex's to remove search params when looking for a cache match.
4068
+ * @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will
4069
+ * check the cache for the URL with a `.html` added to the end of the end.
4070
+ * @param {workbox-precaching~urlManipulation} [options.urlManipulation]
4071
+ * This is a function that should take a URL and return an array of
4072
+ * alternative URLs that should be checked for precache matches.
4073
+ */
4074
+ constructor(precacheController, options) {
4075
+ const match = ({
4076
+ request
4077
+ }) => {
4078
+ const urlsToCacheKeys = precacheController.getURLsToCacheKeys();
4079
+ for (const possibleURL of generateURLVariations(request.url, options)) {
4080
+ const cacheKey = urlsToCacheKeys.get(possibleURL);
4081
+ if (cacheKey) {
4082
+ const integrity = precacheController.getIntegrityForCacheKey(cacheKey);
4083
+ return {
4084
+ cacheKey,
4085
+ integrity
4086
+ };
4087
+ }
4088
+ }
4089
+ {
4090
+ logger.debug(`Precaching did not find a match for ` + getFriendlyURL(request.url));
4091
+ }
4092
+ return;
4093
+ };
4094
+ super(match, precacheController.strategy);
4095
+ }
4096
+ }
4097
+
4098
+ /*
4099
+ Copyright 2019 Google LLC
4100
+ Use of this source code is governed by an MIT-style
4101
+ license that can be found in the LICENSE file or at
4102
+ https://opensource.org/licenses/MIT.
4103
+ */
4104
+ /**
4105
+ * Add a `fetch` listener to the service worker that will
4106
+ * respond to
4107
+ * [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests}
4108
+ * with precached assets.
4109
+ *
4110
+ * Requests for assets that aren't precached, the `FetchEvent` will not be
4111
+ * responded to, allowing the event to fall through to other `fetch` event
4112
+ * listeners.
4113
+ *
4114
+ * @param {Object} [options] See the {@link workbox-precaching.PrecacheRoute}
4115
+ * options.
4116
+ *
4117
+ * @memberof workbox-precaching
4118
+ */
4119
+ function addRoute(options) {
4120
+ const precacheController = getOrCreatePrecacheController();
4121
+ const precacheRoute = new PrecacheRoute(precacheController, options);
4122
+ registerRoute(precacheRoute);
4123
+ }
4124
+
4125
+ /*
4126
+ Copyright 2019 Google LLC
4127
+
4128
+ Use of this source code is governed by an MIT-style
4129
+ license that can be found in the LICENSE file or at
4130
+ https://opensource.org/licenses/MIT.
4131
+ */
4132
+ /**
4133
+ * Adds items to the precache list, removing any duplicates and
4134
+ * stores the files in the
4135
+ * {@link workbox-core.cacheNames|"precache cache"} when the service
4136
+ * worker installs.
4137
+ *
4138
+ * This method can be called multiple times.
4139
+ *
4140
+ * Please note: This method **will not** serve any of the cached files for you.
4141
+ * It only precaches files. To respond to a network request you call
4142
+ * {@link workbox-precaching.addRoute}.
4143
+ *
4144
+ * If you have a single array of files to precache, you can just call
4145
+ * {@link workbox-precaching.precacheAndRoute}.
4146
+ *
4147
+ * @param {Array<Object|string>} [entries=[]] Array of entries to precache.
4148
+ *
4149
+ * @memberof workbox-precaching
4150
+ */
4151
+ function precache(entries) {
4152
+ const precacheController = getOrCreatePrecacheController();
4153
+ precacheController.precache(entries);
4154
+ }
4155
+
4156
+ /*
4157
+ Copyright 2019 Google LLC
4158
+
4159
+ Use of this source code is governed by an MIT-style
4160
+ license that can be found in the LICENSE file or at
4161
+ https://opensource.org/licenses/MIT.
4162
+ */
4163
+ /**
4164
+ * This method will add entries to the precache list and add a route to
4165
+ * respond to fetch events.
4166
+ *
4167
+ * This is a convenience method that will call
4168
+ * {@link workbox-precaching.precache} and
4169
+ * {@link workbox-precaching.addRoute} in a single call.
4170
+ *
4171
+ * @param {Array<Object|string>} entries Array of entries to precache.
4172
+ * @param {Object} [options] See the
4173
+ * {@link workbox-precaching.PrecacheRoute} options.
4174
+ *
4175
+ * @memberof workbox-precaching
4176
+ */
4177
+ function precacheAndRoute(entries, options) {
4178
+ precache(entries);
4179
+ addRoute(options);
4180
+ }
4181
+
4182
+ /*
4183
+ Copyright 2018 Google LLC
4184
+
4185
+ Use of this source code is governed by an MIT-style
4186
+ license that can be found in the LICENSE file or at
4187
+ https://opensource.org/licenses/MIT.
4188
+ */
4189
+ const SUBSTRING_TO_FIND = '-precache-';
4190
+ /**
4191
+ * Cleans up incompatible precaches that were created by older versions of
4192
+ * Workbox, by a service worker registered under the current scope.
4193
+ *
4194
+ * This is meant to be called as part of the `activate` event.
4195
+ *
4196
+ * This should be safe to use as long as you don't include `substringToFind`
4197
+ * (defaulting to `-precache-`) in your non-precache cache names.
4198
+ *
4199
+ * @param {string} currentPrecacheName The cache name currently in use for
4200
+ * precaching. This cache won't be deleted.
4201
+ * @param {string} [substringToFind='-precache-'] Cache names which include this
4202
+ * substring will be deleted (excluding `currentPrecacheName`).
4203
+ * @return {Array<string>} A list of all the cache names that were deleted.
4204
+ *
4205
+ * @private
4206
+ * @memberof workbox-precaching
4207
+ */
4208
+ const deleteOutdatedCaches = async (currentPrecacheName, substringToFind = SUBSTRING_TO_FIND) => {
4209
+ const cacheNames = await self.caches.keys();
4210
+ const cacheNamesToDelete = cacheNames.filter(cacheName => {
4211
+ return cacheName.includes(substringToFind) && cacheName.includes(self.registration.scope) && cacheName !== currentPrecacheName;
4212
+ });
4213
+ await Promise.all(cacheNamesToDelete.map(cacheName => self.caches.delete(cacheName)));
4214
+ return cacheNamesToDelete;
4215
+ };
4216
+
4217
+ /*
4218
+ Copyright 2019 Google LLC
4219
+
4220
+ Use of this source code is governed by an MIT-style
4221
+ license that can be found in the LICENSE file or at
4222
+ https://opensource.org/licenses/MIT.
4223
+ */
4224
+ /**
4225
+ * Adds an `activate` event listener which will clean up incompatible
4226
+ * precaches that were created by older versions of Workbox.
4227
+ *
4228
+ * @memberof workbox-precaching
4229
+ */
4230
+ function cleanupOutdatedCaches() {
4231
+ // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705
4232
+ self.addEventListener('activate', event => {
4233
+ const cacheName = cacheNames.getPrecacheName();
4234
+ event.waitUntil(deleteOutdatedCaches(cacheName).then(cachesDeleted => {
4235
+ {
4236
+ if (cachesDeleted.length > 0) {
4237
+ logger.log(`The following out-of-date precaches were cleaned up ` + `automatically:`, cachesDeleted);
4238
+ }
4239
+ }
4240
+ }));
4241
+ });
4242
+ }
4243
+
4244
+ /*
4245
+ Copyright 2018 Google LLC
4246
+
4247
+ Use of this source code is governed by an MIT-style
4248
+ license that can be found in the LICENSE file or at
4249
+ https://opensource.org/licenses/MIT.
4250
+ */
4251
+ /**
4252
+ * NavigationRoute makes it easy to create a
4253
+ * {@link workbox-routing.Route} that matches for browser
4254
+ * [navigation requests]{@link https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests}.
4255
+ *
4256
+ * It will only match incoming Requests whose
4257
+ * {@link https://fetch.spec.whatwg.org/#concept-request-mode|mode}
4258
+ * is set to `navigate`.
4259
+ *
4260
+ * You can optionally only apply this route to a subset of navigation requests
4261
+ * by using one or both of the `denylist` and `allowlist` parameters.
4262
+ *
4263
+ * @memberof workbox-routing
4264
+ * @extends workbox-routing.Route
4265
+ */
4266
+ class NavigationRoute extends Route {
4267
+ /**
4268
+ * If both `denylist` and `allowlist` are provided, the `denylist` will
4269
+ * take precedence and the request will not match this route.
4270
+ *
4271
+ * The regular expressions in `allowlist` and `denylist`
4272
+ * are matched against the concatenated
4273
+ * [`pathname`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname}
4274
+ * and [`search`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/search}
4275
+ * portions of the requested URL.
4276
+ *
4277
+ * *Note*: These RegExps may be evaluated against every destination URL during
4278
+ * a navigation. Avoid using
4279
+ * [complex RegExps](https://github.com/GoogleChrome/workbox/issues/3077),
4280
+ * or else your users may see delays when navigating your site.
4281
+ *
4282
+ * @param {workbox-routing~handlerCallback} handler A callback
4283
+ * function that returns a Promise resulting in a Response.
4284
+ * @param {Object} options
4285
+ * @param {Array<RegExp>} [options.denylist] If any of these patterns match,
4286
+ * the route will not handle the request (even if a allowlist RegExp matches).
4287
+ * @param {Array<RegExp>} [options.allowlist=[/./]] If any of these patterns
4288
+ * match the URL's pathname and search parameter, the route will handle the
4289
+ * request (assuming the denylist doesn't match).
4290
+ */
4291
+ constructor(handler, {
4292
+ allowlist = [/./],
4293
+ denylist = []
4294
+ } = {}) {
4295
+ {
4296
+ finalAssertExports.isArrayOfClass(allowlist, RegExp, {
4297
+ moduleName: 'workbox-routing',
4298
+ className: 'NavigationRoute',
4299
+ funcName: 'constructor',
4300
+ paramName: 'options.allowlist'
4301
+ });
4302
+ finalAssertExports.isArrayOfClass(denylist, RegExp, {
4303
+ moduleName: 'workbox-routing',
4304
+ className: 'NavigationRoute',
4305
+ funcName: 'constructor',
4306
+ paramName: 'options.denylist'
4307
+ });
4308
+ }
4309
+ super(options => this._match(options), handler);
4310
+ this._allowlist = allowlist;
4311
+ this._denylist = denylist;
4312
+ }
4313
+ /**
4314
+ * Routes match handler.
4315
+ *
4316
+ * @param {Object} options
4317
+ * @param {URL} options.url
4318
+ * @param {Request} options.request
4319
+ * @return {boolean}
4320
+ *
4321
+ * @private
4322
+ */
4323
+ _match({
4324
+ url,
4325
+ request
4326
+ }) {
4327
+ if (request && request.mode !== 'navigate') {
4328
+ return false;
4329
+ }
4330
+ const pathnameAndSearch = url.pathname + url.search;
4331
+ for (const regExp of this._denylist) {
4332
+ if (regExp.test(pathnameAndSearch)) {
4333
+ {
4334
+ logger.log(`The navigation route ${pathnameAndSearch} is not ` + `being used, since the URL matches this denylist pattern: ` + `${regExp.toString()}`);
4335
+ }
4336
+ return false;
4337
+ }
4338
+ }
4339
+ if (this._allowlist.some(regExp => regExp.test(pathnameAndSearch))) {
4340
+ {
4341
+ logger.debug(`The navigation route ${pathnameAndSearch} ` + `is being used.`);
4342
+ }
4343
+ return true;
4344
+ }
4345
+ {
4346
+ logger.log(`The navigation route ${pathnameAndSearch} is not ` + `being used, since the URL being navigated to doesn't ` + `match the allowlist.`);
4347
+ }
4348
+ return false;
4349
+ }
4350
+ }
4351
+
4352
+ /*
4353
+ Copyright 2019 Google LLC
4354
+
4355
+ Use of this source code is governed by an MIT-style
4356
+ license that can be found in the LICENSE file or at
4357
+ https://opensource.org/licenses/MIT.
4358
+ */
4359
+ /**
4360
+ * Helper function that calls
4361
+ * {@link PrecacheController#createHandlerBoundToURL} on the default
4362
+ * {@link PrecacheController} instance.
4363
+ *
4364
+ * If you are creating your own {@link PrecacheController}, then call the
4365
+ * {@link PrecacheController#createHandlerBoundToURL} on that instance,
4366
+ * instead of using this function.
4367
+ *
4368
+ * @param {string} url The precached URL which will be used to lookup the
4369
+ * `Response`.
4370
+ * @param {boolean} [fallbackToNetwork=true] Whether to attempt to get the
4371
+ * response from the network if there's a precache miss.
4372
+ * @return {workbox-routing~handlerCallback}
4373
+ *
4374
+ * @memberof workbox-precaching
4375
+ */
4376
+ function createHandlerBoundToURL(url) {
4377
+ const precacheController = getOrCreatePrecacheController();
4378
+ return precacheController.createHandlerBoundToURL(url);
4379
+ }
4380
+
4381
+ exports.CacheFirst = CacheFirst;
4382
+ exports.ExpirationPlugin = ExpirationPlugin;
4383
+ exports.NavigationRoute = NavigationRoute;
4384
+ exports.cleanupOutdatedCaches = cleanupOutdatedCaches;
4385
+ exports.clientsClaim = clientsClaim;
4386
+ exports.createHandlerBoundToURL = createHandlerBoundToURL;
4387
+ exports.precacheAndRoute = precacheAndRoute;
4388
+ exports.registerRoute = registerRoute;
4389
+
4390
+ }));