ncounter.git

git clone https://git.crispbyte.dev/ncounter.git

commit
7fa7dd4
parent
bb40c09
author
CheddarCrisp
date
2020-03-14 02:35:35 +0100 CET
Create UI to prompt for reload when an
update is available
7 files changed,  +466, -37
M package-lock.json
+348, -22
  1@@ -339,6 +339,16 @@
  2       "integrity": "sha1-SCIQFAWCo2uDw+NC4c/ryqkkCUg=",
  3       "dev": true
  4     },
  5+    "aggregate-error": {
  6+      "version": "3.0.1",
  7+      "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz",
  8+      "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==",
  9+      "dev": true,
 10+      "requires": {
 11+        "clean-stack": "^2.0.0",
 12+        "indent-string": "^4.0.0"
 13+      }
 14+    },
 15     "ajv": {
 16       "version": "6.10.0",
 17       "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
 18@@ -1027,6 +1037,12 @@
 19         }
 20       }
 21     },
 22+    "clean-stack": {
 23+      "version": "2.2.0",
 24+      "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
 25+      "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
 26+      "dev": true
 27+    },
 28     "clean-webpack-plugin": {
 29       "version": "3.0.0",
 30       "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz",
 31@@ -2330,6 +2346,15 @@
 32         "readable-stream": "^2.0.0"
 33       }
 34     },
 35+    "fs-minipass": {
 36+      "version": "2.1.0",
 37+      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
 38+      "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
 39+      "dev": true,
 40+      "requires": {
 41+        "minipass": "^3.0.0"
 42+      }
 43+    },
 44     "fs-write-stream-atomic": {
 45       "version": "1.0.10",
 46       "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
 47@@ -3403,6 +3428,12 @@
 48       "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
 49       "dev": true
 50     },
 51+    "indent-string": {
 52+      "version": "4.0.0",
 53+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
 54+      "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
 55+      "dev": true
 56+    },
 57     "indexes-of": {
 58       "version": "1.0.1",
 59       "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
 60@@ -3703,6 +3734,33 @@
 61       "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
 62       "dev": true
 63     },
 64+    "jest-worker": {
 65+      "version": "25.1.0",
 66+      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz",
 67+      "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==",
 68+      "dev": true,
 69+      "requires": {
 70+        "merge-stream": "^2.0.0",
 71+        "supports-color": "^7.0.0"
 72+      },
 73+      "dependencies": {
 74+        "has-flag": {
 75+          "version": "4.0.0",
 76+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
 77+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
 78+          "dev": true
 79+        },
 80+        "supports-color": {
 81+          "version": "7.1.0",
 82+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
 83+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
 84+          "dev": true,
 85+          "requires": {
 86+            "has-flag": "^4.0.0"
 87+          }
 88+        }
 89+      }
 90+    },
 91     "json-parse-better-errors": {
 92       "version": "1.0.2",
 93       "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
 94@@ -3889,6 +3947,12 @@
 95       "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
 96       "dev": true
 97     },
 98+    "merge-stream": {
 99+      "version": "2.0.0",
100+      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
101+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
102+      "dev": true
103+    },
104     "methods": {
105       "version": "1.1.2",
106       "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
107@@ -3980,6 +4044,50 @@
108       "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
109       "dev": true
110     },
111+    "minipass": {
112+      "version": "3.1.1",
113+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz",
114+      "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==",
115+      "dev": true,
116+      "requires": {
117+        "yallist": "^4.0.0"
118+      },
119+      "dependencies": {
120+        "yallist": {
121+          "version": "4.0.0",
122+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
123+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
124+          "dev": true
125+        }
126+      }
127+    },
128+    "minipass-collect": {
129+      "version": "1.0.2",
130+      "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
131+      "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
132+      "dev": true,
133+      "requires": {
134+        "minipass": "^3.0.0"
135+      }
136+    },
137+    "minipass-flush": {
138+      "version": "1.0.5",
139+      "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
140+      "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
141+      "dev": true,
142+      "requires": {
143+        "minipass": "^3.0.0"
144+      }
145+    },
146+    "minipass-pipeline": {
147+      "version": "1.2.2",
148+      "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz",
149+      "integrity": "sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA==",
150+      "dev": true,
151+      "requires": {
152+        "minipass": "^3.0.0"
153+      }
154+    },
155     "mississippi": {
156       "version": "3.0.0",
157       "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
158@@ -5225,15 +5333,6 @@
159         "send": "0.17.1"
160       }
161     },
162-    "serviceworker-webpack-plugin": {
163-      "version": "1.0.1",
164-      "resolved": "https://registry.npmjs.org/serviceworker-webpack-plugin/-/serviceworker-webpack-plugin-1.0.1.tgz",
165-      "integrity": "sha512-VgDEkZ3pA0HajsRaWtl5w6bLxAXx0Y+4dm7YeTcIxVmvC9YXvstex38HOBDuYETeDS5fUlBy/47gC0QYBrG0nw==",
166-      "dev": true,
167-      "requires": {
168-        "minimatch": "^3.0.4"
169-      }
170-    },
171     "set-blocking": {
172       "version": "2.0.0",
173       "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
174@@ -5853,9 +5952,9 @@
175       "dev": true
176     },
177     "terser": {
178-      "version": "4.6.3",
179-      "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz",
180-      "integrity": "sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==",
181+      "version": "4.6.6",
182+      "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.6.tgz",
183+      "integrity": "sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g==",
184       "dev": true,
185       "requires": {
186         "commander": "^2.20.0",
187@@ -5872,28 +5971,200 @@
188       }
189     },
190     "terser-webpack-plugin": {
191-      "version": "1.4.3",
192-      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
193-      "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
194+      "version": "2.3.5",
195+      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.5.tgz",
196+      "integrity": "sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w==",
197       "dev": true,
198       "requires": {
199-        "cacache": "^12.0.2",
200-        "find-cache-dir": "^2.1.0",
201-        "is-wsl": "^1.1.0",
202-        "schema-utils": "^1.0.0",
203+        "cacache": "^13.0.1",
204+        "find-cache-dir": "^3.2.0",
205+        "jest-worker": "^25.1.0",
206+        "p-limit": "^2.2.2",
207+        "schema-utils": "^2.6.4",
208         "serialize-javascript": "^2.1.2",
209         "source-map": "^0.6.1",
210-        "terser": "^4.1.2",
211-        "webpack-sources": "^1.4.0",
212-        "worker-farm": "^1.7.0"
213+        "terser": "^4.4.3",
214+        "webpack-sources": "^1.4.3"
215       },
216       "dependencies": {
217+        "ajv": {
218+          "version": "6.12.0",
219+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
220+          "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
221+          "dev": true,
222+          "requires": {
223+            "fast-deep-equal": "^3.1.1",
224+            "fast-json-stable-stringify": "^2.0.0",
225+            "json-schema-traverse": "^0.4.1",
226+            "uri-js": "^4.2.2"
227+          }
228+        },
229+        "ajv-keywords": {
230+          "version": "3.4.1",
231+          "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
232+          "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
233+          "dev": true
234+        },
235+        "cacache": {
236+          "version": "13.0.1",
237+          "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz",
238+          "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==",
239+          "dev": true,
240+          "requires": {
241+            "chownr": "^1.1.2",
242+            "figgy-pudding": "^3.5.1",
243+            "fs-minipass": "^2.0.0",
244+            "glob": "^7.1.4",
245+            "graceful-fs": "^4.2.2",
246+            "infer-owner": "^1.0.4",
247+            "lru-cache": "^5.1.1",
248+            "minipass": "^3.0.0",
249+            "minipass-collect": "^1.0.2",
250+            "minipass-flush": "^1.0.5",
251+            "minipass-pipeline": "^1.2.2",
252+            "mkdirp": "^0.5.1",
253+            "move-concurrently": "^1.0.1",
254+            "p-map": "^3.0.0",
255+            "promise-inflight": "^1.0.1",
256+            "rimraf": "^2.7.1",
257+            "ssri": "^7.0.0",
258+            "unique-filename": "^1.1.1"
259+          }
260+        },
261+        "fast-deep-equal": {
262+          "version": "3.1.1",
263+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
264+          "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==",
265+          "dev": true
266+        },
267+        "find-cache-dir": {
268+          "version": "3.3.1",
269+          "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz",
270+          "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==",
271+          "dev": true,
272+          "requires": {
273+            "commondir": "^1.0.1",
274+            "make-dir": "^3.0.2",
275+            "pkg-dir": "^4.1.0"
276+          }
277+        },
278+        "find-up": {
279+          "version": "4.1.0",
280+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
281+          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
282+          "dev": true,
283+          "requires": {
284+            "locate-path": "^5.0.0",
285+            "path-exists": "^4.0.0"
286+          }
287+        },
288+        "graceful-fs": {
289+          "version": "4.2.3",
290+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
291+          "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
292+          "dev": true
293+        },
294+        "locate-path": {
295+          "version": "5.0.0",
296+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
297+          "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
298+          "dev": true,
299+          "requires": {
300+            "p-locate": "^4.1.0"
301+          }
302+        },
303+        "make-dir": {
304+          "version": "3.0.2",
305+          "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
306+          "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
307+          "dev": true,
308+          "requires": {
309+            "semver": "^6.0.0"
310+          }
311+        },
312+        "p-limit": {
313+          "version": "2.2.2",
314+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
315+          "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
316+          "dev": true,
317+          "requires": {
318+            "p-try": "^2.0.0"
319+          }
320+        },
321+        "p-locate": {
322+          "version": "4.1.0",
323+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
324+          "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
325+          "dev": true,
326+          "requires": {
327+            "p-limit": "^2.2.0"
328+          }
329+        },
330+        "p-map": {
331+          "version": "3.0.0",
332+          "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
333+          "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
334+          "dev": true,
335+          "requires": {
336+            "aggregate-error": "^3.0.0"
337+          }
338+        },
339+        "path-exists": {
340+          "version": "4.0.0",
341+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
342+          "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
343+          "dev": true
344+        },
345+        "pkg-dir": {
346+          "version": "4.2.0",
347+          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
348+          "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
349+          "dev": true,
350+          "requires": {
351+            "find-up": "^4.0.0"
352+          }
353+        },
354+        "rimraf": {
355+          "version": "2.7.1",
356+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
357+          "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
358+          "dev": true,
359+          "requires": {
360+            "glob": "^7.1.3"
361+          }
362+        },
363+        "schema-utils": {
364+          "version": "2.6.5",
365+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz",
366+          "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==",
367+          "dev": true,
368+          "requires": {
369+            "ajv": "^6.12.0",
370+            "ajv-keywords": "^3.4.1"
371+          }
372+        },
373+        "semver": {
374+          "version": "6.3.0",
375+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
376+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
377+          "dev": true
378+        },
379         "source-map": {
380           "version": "0.6.1",
381           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
382           "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
383           "dev": true
384         },
385+        "ssri": {
386+          "version": "7.1.0",
387+          "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz",
388+          "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==",
389+          "dev": true,
390+          "requires": {
391+            "figgy-pudding": "^3.5.1",
392+            "minipass": "^3.1.1"
393+          }
394+        },
395         "webpack-sources": {
396           "version": "1.4.3",
397           "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
398@@ -6306,6 +6577,43 @@
399         "terser-webpack-plugin": "^1.1.0",
400         "watchpack": "^1.5.0",
401         "webpack-sources": "^1.3.0"
402+      },
403+      "dependencies": {
404+        "source-map": {
405+          "version": "0.6.1",
406+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
407+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
408+          "dev": true
409+        },
410+        "terser-webpack-plugin": {
411+          "version": "1.4.3",
412+          "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
413+          "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
414+          "dev": true,
415+          "requires": {
416+            "cacache": "^12.0.2",
417+            "find-cache-dir": "^2.1.0",
418+            "is-wsl": "^1.1.0",
419+            "schema-utils": "^1.0.0",
420+            "serialize-javascript": "^2.1.2",
421+            "source-map": "^0.6.1",
422+            "terser": "^4.1.2",
423+            "webpack-sources": "^1.4.0",
424+            "worker-farm": "^1.7.0"
425+          },
426+          "dependencies": {
427+            "webpack-sources": {
428+              "version": "1.4.3",
429+              "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
430+              "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
431+              "dev": true,
432+              "requires": {
433+                "source-list-map": "^2.0.0",
434+                "source-map": "~0.6.1"
435+              }
436+            }
437+          }
438+        }
439       }
440     },
441     "webpack-cli": {
442@@ -6514,6 +6822,15 @@
443       "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
444       "dev": true
445     },
446+    "workbox-broadcast-update": {
447+      "version": "5.0.0",
448+      "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-5.0.0.tgz",
449+      "integrity": "sha512-6iA/ubUoGLdyvQh6/RBCKbl/ghzl/bSjz6tVvzJKKYKQRHMQs4OeZCud81PTYynFq3AWaGaMp/KDZhhycjH39Q==",
450+      "dev": true,
451+      "requires": {
452+        "workbox-core": "^5.0.0"
453+      }
454+    },
455     "workbox-cacheable-response": {
456       "version": "5.0.0",
457       "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-5.0.0.tgz",
458@@ -6557,6 +6874,15 @@
459         "workbox-routing": "^5.0.0"
460       }
461     },
462+    "workbox-window": {
463+      "version": "5.0.0",
464+      "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-5.0.0.tgz",
465+      "integrity": "sha512-zowcWjR+fpjgAU9vu+0QZNEY2biecPxY7M5qn9Ki7UA3p1CCl7aIF0/itCN9H4AJ3CP/Ohbd6dxE7MnuDkPceA==",
466+      "dev": true,
467+      "requires": {
468+        "workbox-core": "^5.0.0"
469+      }
470+    },
471     "worker-farm": {
472       "version": "1.7.0",
473       "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
M package.json
+4, -2
 1@@ -22,20 +22,22 @@
 2     "html-webpack-plugin": "^3.2.0",
 3     "idb-keyval": "^3.2.0",
 4     "reset-css": "^5.0.1",
 5-    "serviceworker-webpack-plugin": "^1.0.1",
 6     "string-replace-loader": "^2.2.0",
 7     "style-loader": "^1.1.3",
 8     "svelte": "^3.4.1",
 9     "svelte-loader": "^2.13.4",
10+    "terser-webpack-plugin": "^2.3.5",
11     "uuid": "^3.4.0",
12     "webpack": "^4.31.0",
13     "webpack-cli": "^3.3.2",
14     "webpack-dev-server": "^3.4.1",
15     "webpack-license-plugin": "^4.1.1",
16     "webpack-merge": "^4.2.2",
17+    "workbox-broadcast-update": "^5.0.0",
18     "workbox-cacheable-response": "^5.0.0",
19     "workbox-expiration": "^5.0.0",
20     "workbox-routing": "^5.0.0",
21-    "workbox-strategies": "^5.0.0"
22+    "workbox-strategies": "^5.0.0",
23+    "workbox-window": "^5.0.0"
24   }
25 }
M src/index.js
+12, -4
 1@@ -1,10 +1,18 @@
 2 import App from './ncounter/App.svelte';
 3-import runtime from 'serviceworker-webpack-plugin/lib/runtime';
 4 import 'reset-css/reset.css';
 5 import './ncounter/site.css';
 6+import {Workbox} from 'workbox-window/Workbox.mjs';
 7+
 8+const app = new App({ target: document.body });
 9 
10 if ('serviceWorker' in navigator) {
11-    const registration = runtime.register();
12-}
13+    const wb = new Workbox('sw.js');
14+
15+    wb.addEventListener('message', async (event) => {
16+        if (event.data.type === 'CACHE_UPDATED') {
17+            app.SetUpdateAvailable();
18+        }
19+    });
20 
21-new App({ target: document.body });
22+    wb.register();
23+}
M src/ncounter/App.svelte
+80, -1
  1@@ -4,8 +4,16 @@
  2         <Counter counterId={ counter.id }></Counter>
  3         {/each}
  4     </div>
  5+    {#if showUpdateMessage}
  6+    <div class="update-message">
  7+        An update is available. <button class="reload" on:click="{ () => { window.location.reload(); } }">Load now</button><button class="later" on:click="{ () => { showUpdateMessage = false; } }">Later</button>
  8+    </div>
  9+    {/if}
 10     <div class="tool-bar">
 11         <button class="add" on:click="{ showAdd }"><i class="material-icons">add</i></button>
 12+        {#if updateAvailable}<button class="update"
 13+            class:animate="{!ignoreUpdate}"
 14+            on:click={() => { ignoreUpdate = true; showUpdateMessage = !showUpdateMessage; }}><i class="material-icons">arrow_upward</i></button>{/if}
 15         <button class="about" on:click="{ () => { showAbout = true; } }">?</button>
 16     </div>
 17 </div>
 18@@ -36,6 +44,10 @@ let showAbout = false;
 19 let newCounter = null;
 20 let dialogForm = null;
 21 
 22+let updateAvailable = false;
 23+let ignoreUpdate = false;
 24+let showUpdateMessage = false;
 25+
 26 function focus(node){
 27     node.focus();
 28 }
 29@@ -68,6 +80,10 @@ function add(){
 30 
 31     showAddDialog = false;
 32 }
 33+
 34+export function SetUpdateAvailable(){
 35+    updateAvailable = true;
 36+}
 37 </script>
 38 <style lang="scss">
 39     .app {
 40@@ -117,8 +133,71 @@ function add(){
 41         border: 2px solid var(--highlight-color);
 42     }
 43 
 44+    .add {
 45+        margin-right: auto;
 46+    }
 47+
 48+    @keyframes pulse {
 49+        0% {
 50+            -webkit-text-stroke: 0 rgba(204, 204, 0, 0);
 51+        }
 52+
 53+        15% {
 54+            -webkit-text-stroke: 3px rgba(204, 204, 0, 1);
 55+        }
 56+
 57+        30% {
 58+            -webkit-text-stroke: 0 rgba(204, 204, 0, 0);
 59+        }
 60+    }
 61+
 62+    .update {
 63+        margin-right: 10px;
 64+    }
 65+
 66+    .update.animate {
 67+        animation-name: pulse;
 68+        animation-duration: 5s;
 69+        animation-iteration-count: infinite;
 70+    }
 71+
 72     .about {
 73-        margin-left: auto;
 74         font-size: 20px;
 75     }
 76+
 77+    .update-message {
 78+        display: flex;
 79+        align-items: center;
 80+
 81+        padding: 12px;
 82+
 83+        background-color: var(--background-primary);
 84+        color: var(--text-primary);
 85+
 86+        box-shadow: 0 -1px 3px rgba(0,0,0,0.12), 0 -1px 2px rgba(0,0,0,0.24);
 87+    }
 88+
 89+    .update-message > button {
 90+        width: auto;
 91+        height: 24px;
 92+        font-size: 16px;
 93+    }
 94+
 95+    @media(hover: hover){
 96+        .update-message > button:hover {
 97+            outline: 2px solid var(--button-color);
 98+        }
 99+    }
100+
101+    .update-message > button:focus {
102+        outline: 2px solid var(--button-color);
103+    }
104+
105+    .reload {
106+        margin-left: auto;
107+    }
108+
109+    .later {
110+        margin-left: 16px;
111+    }
112 </style>
M src/sw.js
+4, -0
 1@@ -2,6 +2,7 @@ import {registerRoute,setDefaultHandler} from 'workbox-routing';
 2 import {CacheFirst, StaleWhileRevalidate, NetworkFirst} from 'workbox-strategies';
 3 import {CacheableResponsePlugin} from 'workbox-cacheable-response';
 4 import {ExpirationPlugin} from 'workbox-expiration';
 5+import {BroadcastUpdatePlugin} from 'workbox-broadcast-update';
 6 
 7 // Cache the Google Fonts stylesheets with a stale-while-revalidate strategy.
 8 registerRoute(
 9@@ -32,6 +33,9 @@ registerRoute(
10     /\.(?:js|css|png)$/,
11     new StaleWhileRevalidate({
12       cacheName: 'static-resources',
13+      plugins: [
14+        new BroadcastUpdatePlugin(),
15+      ],
16     })
17   );
18 
M webpack.common.js
+6, -8
 1@@ -2,7 +2,6 @@ const path = require('path');
 2 const HtmlWebpackPlugin = require('html-webpack-plugin');
 3 const GoogleFontsPlugin = require('@beyonk/google-fonts-webpack-plugin');
 4 const { CleanWebpackPlugin } = require('clean-webpack-plugin');
 5-const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
 6 const LicensePlugin = require('webpack-license-plugin');
 7 const CopyWebpackPlugin = require('copy-webpack-plugin');
 8 
 9@@ -11,11 +10,14 @@ const year = now.getFullYear().toString();
10 const releaseDate = now.getFullYear() + "-" + (now.getMonth() + 1).toString().padStart(2, '0') + "-" + now.getDate().toString().padStart(2, '0');
11 
12 module.exports = {
13-    entry: './src/index.js',
14+    entry: {
15+        index: './src/index.js',
16+        sw: './src/sw.js'
17+    },
18     plugins: [
19         new CleanWebpackPlugin(),
20         new HtmlWebpackPlugin({
21-            title: 'NCounter',
22+            excludeChunks: ['sw'],
23             template: 'src/index.html'
24         }),
25         new GoogleFontsPlugin({
26@@ -24,10 +26,6 @@ module.exports = {
27             ],
28             local: false
29         }),
30-        new ServiceWorkerWebpackPlugin({
31-            entry: path.join(__dirname, 'src/sw.js'),
32-            publicPath: './'
33-        }),
34         new LicensePlugin(),
35         new CopyWebpackPlugin([
36             {
37@@ -48,7 +46,7 @@ module.exports = {
38         ])
39     ],
40     output: {
41-        filename: 'index.js',
42+        filename: '[name].js',
43         path: path.resolve(__dirname, 'dist')
44     },
45     module: {
M webpack.prod.js
+12, -0
 1@@ -1,6 +1,18 @@
 2+const webpack = require('webpack');
 3 const merge = require('webpack-merge');
 4 const common = require('./webpack.common.js');
 5+const Terser = require('terser-webpack-plugin');
 6 
 7 module.exports = merge(common, {
 8     mode: 'production',
 9+    plugins: [
10+        new webpack.DefinePlugin({
11+            'process.env.NODE_ENV': JSON.stringify('production'),
12+        }),
13+    ],
14+    optimization:{
15+        minimizer: [new Terser({
16+            test: /\.m?js$/,
17+        })]
18+    }
19 });