diff --git a/.env.development.example b/.env.development.example
index e5dc4a2d..a88377eb 100644
--- a/.env.development.example
+++ b/.env.development.example
@@ -8,7 +8,6 @@ KIRBY_DEV_PORT=8080
KIRBY_DEV_PROTOCOL=http
VITE_DEV_PORT=3000
-VITE_SERVICE_WORKER=false
VITE_STALE_WHILE_REVALIDATE=false
# API slug to fetch JSON-encoded page data from
diff --git a/.env.production.example b/.env.production.example
index 103dc228..d1bfcbe4 100644
--- a/.env.production.example
+++ b/.env.production.example
@@ -8,7 +8,6 @@ KIRBY_DEV_PORT=8080
KIRBY_DEV_PROTOCOL=http
VITE_DEV_PORT=3000
-VITE_SERVICE_WORKER=false
VITE_STALE_WHILE_REVALIDATE=false
# API slug to fetch JSON-encoded page data from
diff --git a/.github/kirby-vue-3-cache-and-store.png b/.github/kirby-vue-3-cache-and-store.png
index 491633c1..699fc0fb 100644
Binary files a/.github/kirby-vue-3-cache-and-store.png and b/.github/kirby-vue-3-cache-and-store.png differ
diff --git a/.gitignore b/.gitignore
index 73e1eb59..e4781b9b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,4 +22,3 @@ vendor
# Build assets
/public/assets
/public/dist
-/public/service-worker.js
diff --git a/README.md b/README.md
index f0979d40..bf669f2a 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,6 @@
- 🔍 SEO-friendly: [server-side generated](https://github.com/johannschopplich/kirby-extended/blob/main/docs/meta.md) meta tags
- 🌐 [Multi-language](#multi-language) support
- ♿ Accessible frontend routing
-- 🚝 [Offline-first](#caching--offline-capability-with-service-worker)
- 💫 [Stale-while-revalidate](#stale-while-revalidate) page data
## Alternatives
@@ -60,12 +59,6 @@ kirby-vue3-starterkit/
| | # Kirby's media folder for thumbnails and more (not tracked by Git)
| └── media/
|
-| # Various development-related Node scripts
-├── scripts/
-| |
-| | # Service worker generator (optional)
-| └── buildServiceWorker.js
-|
| # Kirby's core folder containing templates, blueprints, etc.
├── site/
| ├── config/
@@ -116,9 +109,6 @@ kirby-vue3-starterkit/
| | | # Returns page data for the current path, similarly to Kirby's `$page` object
| | ├── usePage.js
| | |
-| | | # Various service worker methods like registering
-| | ├── useServiceWorker.js
-| | |
| | | # Returns a object corresponding to Kirby's global `$site`
| | └── useSite.js
| |
@@ -137,8 +127,7 @@ kirby-vue3-starterkit/
| |
| ├── App.vue
| ├── index.css
-| ├── index.js
-| └── serviceWorker.js
+| └── index.js
|
| # Contains everything content and user data related (not tracked by Git)
├── storage/
@@ -160,20 +149,12 @@ kirby-vue3-starterkit/
-## Caching & Offline Capability With Service Worker
+## Caching
-Even without a service worker installed, the frontend will store pages between individual routes/views. When the tab get reloaded, the data for each page is freshly fetched from the API once again.
-
-For offline capability of your Vue app, you can choose to activate the included [service worker](src/serviceWorker.js).
-
-A visual explanation of both methods can be found in the following flow chart:
+The frontend will store pages between individual routes/views. When the tab get reloaded, the data for each page is freshly fetched from the API once again.
![Caching for Kirby and Vue 3 starterkit](./.github/kirby-vue-3-cache-and-store.png)
-The service worker precaches all CSS & JS assets required by the Vue app and caches the data of every requested page. All assets are versioned and served from the service worker cache directly.
-
-Each JSON request will be freshly fetched from the network and saved to the cache. If the user's navigator turns out to be offline, the cached page request will be returned.
-
## Stale-While-Revalidate
The stale-while-revalidate mechanism for the [`usePage`](src/composables/usePage.js) hook allows you to respond as quickly as possible with cached page data if available, falling back to the network request if it's not cached. The network request is then used to update the cached page data – which directly affects the view after lazily assigning changes (if any), thanks to Vue's reactivity.
@@ -292,14 +273,6 @@ Then, visit the panel and add new languages by your liking. The Panel **automati
Language data is provided by the global `site` object, which can be accessed via the `useSite()` hook.
-### Service Worker
-
-To enable the **service worker** which precaches essential assets and page API calls for offline capability, set:
-
-- `VITE_SERVICE_WORKER` to `true`
-
-> ⚠️ Don't change the `CONTENT_API_SLUG` once you deployed your app publicly and thus a service worker is installed on clients. Otherwise fetch requests will fail and a blank page will show until the new service worker is activated, which then is only possible by closing the tab/PWA.
-
### Stale-While-Revalidating
To keep page data fresh with **stale-while-revalidate**, set:
diff --git a/package-lock.json b/package-lock.json
index 79b19c04..ae2213f2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,13 +16,11 @@
"@types/node": "^18.7.4",
"@vitejs/plugin-vue": "^3.0.3",
"concurrently": "^7.3.0",
- "consola": "^2.15.3",
"dotenv": "^16.0.1",
"env-cmd": "^10.1.0",
"eslint": "^8.22.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-vue": "^9.3.0",
- "nanoid": "^4.0.0",
"prettier": "2.7.1",
"shx": "^0.3.4",
"unplugin-vue-components": "^0.22.4",
@@ -164,9 +162,9 @@
}
},
"node_modules/@types/node": {
- "version": "18.7.4",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.4.tgz",
- "integrity": "sha512-RzRcw8c0B8LzryWOR4Wj7YOTFXvdYKwvrb6xQQyuDfnlTxwYXGCV5RZ/TEbq5L5kn+w3rliHAUyRcG1RtbmTFg==",
+ "version": "18.7.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.5.tgz",
+ "integrity": "sha512-NcKK6Ts+9LqdHJaW6HQmgr7dT/i3GOHG+pt6BiWv++5SnjtRd4NXeiuN2kA153SjhXPR/AhHIPHPbrsbpUVOww==",
"dev": true
},
"node_modules/@vitejs/plugin-vue": {
@@ -580,12 +578,6 @@
"node": "^12.20.0 || ^14.13.0 || >=16.0.0"
}
},
- "node_modules/consola": {
- "version": "2.15.3",
- "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz",
- "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==",
- "dev": true
- },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -1816,15 +1808,14 @@
"dev": true
},
"node_modules/nanoid": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz",
- "integrity": "sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==",
- "dev": true,
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"bin": {
- "nanoid": "bin/nanoid.js"
+ "nanoid": "bin/nanoid.cjs"
},
"engines": {
- "node": "^14 || ^16 || >=18"
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/natural-compare": {
@@ -2033,17 +2024,6 @@
"node": ">=4"
}
},
- "node_modules/postcss/node_modules/nanoid": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
- "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -2911,9 +2891,9 @@
}
},
"@types/node": {
- "version": "18.7.4",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.4.tgz",
- "integrity": "sha512-RzRcw8c0B8LzryWOR4Wj7YOTFXvdYKwvrb6xQQyuDfnlTxwYXGCV5RZ/TEbq5L5kn+w3rliHAUyRcG1RtbmTFg==",
+ "version": "18.7.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.5.tgz",
+ "integrity": "sha512-NcKK6Ts+9LqdHJaW6HQmgr7dT/i3GOHG+pt6BiWv++5SnjtRd4NXeiuN2kA153SjhXPR/AhHIPHPbrsbpUVOww==",
"dev": true
},
"@vitejs/plugin-vue": {
@@ -3255,12 +3235,6 @@
"yargs": "^17.3.1"
}
},
- "consola": {
- "version": "2.15.3",
- "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz",
- "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==",
- "dev": true
- },
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -4080,10 +4054,9 @@
"dev": true
},
"nanoid": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz",
- "integrity": "sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==",
- "dev": true
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
},
"natural-compare": {
"version": "1.4.0",
@@ -4221,13 +4194,6 @@
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
- },
- "dependencies": {
- "nanoid": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
- "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
- }
}
},
"postcss-selector-parser": {
diff --git a/package.json b/package.json
index 1869c580..8417c56d 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"scripts": {
"kirby": "env-cmd --use-shell \"php -S \\$KIRBY_DEV_HOSTNAME:\\$KIRBY_DEV_PORT -t public vendor/getkirby/cms/router.php\"",
"dev": "shx touch src/.lock && concurrently \"npm:kirby\" \"vite\"",
- "build": "shx rm -f src/.lock && vite build && node scripts/buildServiceWorker.mjs",
+ "build": "shx rm -f src/.lock && vite build",
"lint": "eslint \"src/**/*.{js,vue}\"",
"lint:fix": "npm run lint -- --fix",
"format": "prettier --write \"src/**/*.{js,vue}\""
@@ -19,13 +19,11 @@
"@types/node": "^18.7.4",
"@vitejs/plugin-vue": "^3.0.3",
"concurrently": "^7.3.0",
- "consola": "^2.15.3",
"dotenv": "^16.0.1",
"env-cmd": "^10.1.0",
"eslint": "^8.22.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-vue": "^9.3.0",
- "nanoid": "^4.0.0",
"prettier": "2.7.1",
"shx": "^0.3.4",
"unplugin-vue-components": "^0.22.4",
diff --git a/scripts/buildServiceWorker.mjs b/scripts/buildServiceWorker.mjs
deleted file mode 100644
index cb847830..00000000
--- a/scripts/buildServiceWorker.mjs
+++ /dev/null
@@ -1,36 +0,0 @@
-// @ts-check
-
-import "dotenv/config";
-import { readFile, writeFile } from "fs/promises";
-import { transform } from "esbuild";
-import { nanoid } from "nanoid";
-import consola from "consola";
-
-const srcPath = "src/serviceWorker.js";
-const distPath = "public/service-worker.js";
-
-async function main() {
- if (process.env.VITE_SERVICE_WORKER !== "true") return;
-
- consola.start("building service worker...");
-
- const swManifest = JSON.parse(
- await readFile("public/dist/manifest.json", "utf-8")
- );
-
- const assets = Object.values(swManifest).map((i) => `/dist/${i.file}`);
- const bundle = `
- self.__PRECACHE_MANIFEST = [${assets.map((i) => `"${i}"`).join(",")}]
- const VERSION = "${nanoid()}"
- const KIRBY_API_SLUG = "${process.env.KIRBY_API_SLUG || "api"}"
- const CONTENT_API_SLUG = "${process.env.CONTENT_API_SLUG}"
- ${await readFile(srcPath)}
- `;
-
- const { code } = await transform(bundle, { minify: true });
- await writeFile(distPath, code);
-
- consola.success(`${assets.length} assets added to precache`);
-}
-
-main().catch((err) => consola.error(err));
diff --git a/src/App.vue b/src/App.vue
index 3be18efc..cc7c17b5 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -9,7 +9,6 @@
-
diff --git a/src/components/Footer.vue b/src/components/Footer.vue
index 0112ac53..d5e6f8ec 100644
--- a/src/components/Footer.vue
+++ b/src/components/Footer.vue
@@ -18,7 +18,7 @@ import { useSite } from "~/composables";
const site = useSite();
-
diff --git a/src/composables/index.js b/src/composables/index.js
index f2e09663..2b5c1ea1 100644
--- a/src/composables/index.js
+++ b/src/composables/index.js
@@ -2,5 +2,4 @@ export { default as useAnnouncer } from "./useAnnouncer";
export { default as useKirbyApi } from "./useKirbyApi";
export { default as useLanguages } from "./useLanguages";
export { default as usePage } from "./usePage";
-export { default as useServiceWorker } from "./useServiceWorker";
export { default as useSite } from "./useSite";
diff --git a/src/composables/usePage.js b/src/composables/usePage.js
index edb725e6..ff96af42 100644
--- a/src/composables/usePage.js
+++ b/src/composables/usePage.js
@@ -54,15 +54,6 @@ export default (path) => {
router.replace({ path: "/error" });
return;
}
-
- // Redirect to offline page if page hasn't been cached either in-store or
- // by the service worker and the offline fallback JSON was returned
- // Note: data for `home` and `offline` pages are always available since they
- // are precached by the service worker
- if (data.__isOffline) {
- router.replace({ path: "/offline" });
- return;
- }
}
// Append page data to reactive page object
diff --git a/src/composables/useServiceWorker.js b/src/composables/useServiceWorker.js
deleted file mode 100644
index 00481a30..00000000
--- a/src/composables/useServiceWorker.js
+++ /dev/null
@@ -1,183 +0,0 @@
-/* eslint-disable no-unused-vars */
-import { ref } from "vue";
-
-/**
- * Reactive boolean indicating if a service worker update is available
- */
-const hasNewWorker = ref(false);
-
-/**
- * The new service worker waiting to be installed (if any)
- */
-let newWorker;
-
-/**
- * A promise resolving when the document and all sub-resources have
- * finished loading
- */
-const documentLoaded = new Promise((resolve) => {
- if (document.readyState === "complete") {
- resolve();
- } else {
- window.addEventListener("load", resolve);
- }
-});
-
-/**
- * Registers a service worker
- *
- * @param {string} [swUrl="/service-worker.js"] Absolute URL for the worker to register
- * @param {object} [hooks={}] Object of hooks for registration events
- */
-const register = async (swUrl = "/service-worker.js", hooks = {}) => {
- const { registrationOptions = {} } = hooks;
- delete hooks.registrationOptions;
-
- const emit = (hook, ...args) => {
- if (hooks && hooks[hook]) {
- hooks[hook](...args);
- }
- };
-
- try {
- const registration = await navigator.serviceWorker.register(
- swUrl,
- registrationOptions
- );
- emit("registered", registration);
-
- // Handle service worker updates
- // @see https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#handling_updates
- if (registration.waiting) {
- newWorker = registration.waiting;
- emit("updated", registration);
- } else {
- registration.addEventListener("updatefound", () => {
- emit("updatefound", registration);
- const installingWorker = registration.installing;
-
- // Handle state changes of new service worker
- installingWorker.addEventListener("statechange", () => {
- // Make sure new service worker installation is complete
- if (installingWorker.state !== "installed") return;
-
- if (navigator.serviceWorker.controller) {
- // At this point, the old content will have been purged and
- // the fresh content will have been added to the cache
- // Perfect time to notify the user that a new service worker
- // is ready to be installed
- newWorker = registration.waiting;
- emit("updated", registration);
- } else {
- // At this point, everything has been precached
- // Perfect time to notify the user that content is cached
- // for offline use
- emit("cached", registration);
- }
- });
- });
- }
- } catch (error) {
- if (!navigator.onLine) emit("offline");
- emit("error", error);
- }
-
- navigator.serviceWorker.ready.then((registration) => {
- emit("ready", registration);
- });
-};
-
-/**
- * Unregisters existing service workers
- */
-const unregister = async () => {
- const registration = await navigator.serviceWorker.ready;
- registration.unregister();
-};
-
-/**
- * Activates the new service worker (like after an update notification)
- */
-const activateNewWorker = () => {
- if (!newWorker) return;
- newWorker.postMessage({ command: "skipWaiting" });
-};
-
-/**
- * Handles the service worker registration process
- */
-const initSw = async () => {
- if (!("serviceWorker" in navigator)) return;
-
- const enableWorker = import.meta.env.VITE_SERVICE_WORKER === "true";
- const hasExistingWorker = !!navigator.serviceWorker.controller;
-
- if (enableWorker) {
- await documentLoaded;
- await register("/service-worker.js", {
- // Thanks to Evan You for this pattern
- // @see https://github.com/yyx990803/register-service-worker
-
- // registrationOptions: { scope: './' },
- ready(registration) {
- if (import.meta.env.DEV) console.log("Service worker is active.");
- },
- registered(registration) {
- if (import.meta.env.DEV)
- console.log("Service worker has been registered.");
- },
- cached(registration) {
- if (import.meta.env.DEV)
- console.log("Content has been cached for offline use.");
- },
- updatefound(registration) {
- if (import.meta.env.DEV) console.log("New content is downloading.");
- },
- updated(registration) {
- if (import.meta.env.DEV)
- console.log("New content is available; please refresh.");
- hasNewWorker.value = true;
- },
- offline() {
- if (import.meta.env.DEV)
- console.log(
- "No internet connection found. App is running in offline mode."
- );
- },
- error(error) {
- console.error("Error during service worker registration:", error);
- },
- });
-
- // Wait for the current service worker controlling this page to change,
- // specifically when the new worker has skipped waiting and become
- // the new active worker
- let isRefreshing = false;
- navigator.serviceWorker.addEventListener("controllerchange", () => {
- if (!isRefreshing) {
- isRefreshing = true;
- window.location.reload();
- }
- });
-
- if (hasExistingWorker) {
- navigator.serviceWorker.controller.postMessage({ command: "trimCaches" });
- }
- } else if (hasExistingWorker) {
- unregister();
- }
-};
-
-/**
- * Returns methods for handling service worker registrations and updates
- *
- * @returns {object} Service worker related methods
- */
-export default () => ({
- register,
- unregister,
- hasNewWorker,
- newWorker,
- activateNewWorker,
- initSw,
-});
diff --git a/src/main.js b/src/main.js
index 15310401..5187dbf9 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,5 +1,4 @@
import { createApp } from "vue";
-import { useServiceWorker } from "./composables";
import App from "./App.vue";
import "./styles/main.css";
@@ -12,6 +11,3 @@ for (const m of Object.values(
}
app.mount("#app");
-
-const { initSw } = useServiceWorker();
-initSw();
diff --git a/src/serviceWorker.js b/src/serviceWorker.js
deleted file mode 100644
index b1ace2e9..00000000
--- a/src/serviceWorker.js
+++ /dev/null
@@ -1,171 +0,0 @@
-/* eslint-env serviceworker */
-/* global VERSION, KIRBY_API_SLUG, CONTENT_API_SLUG */
-
-const MAX_CACHED_PAGES = false;
-const MAX_CACHED_IMAGES = 50;
-const FETCH_TIMEOUT = 5000;
-
-const CACHE_KEYS = {
- STATIC: `static-${VERSION}`,
- PAGES: `pages-${VERSION}`,
- IMAGES: "images",
-};
-
-const ALLOWED_HOSTS = [self.location.host];
-
-const EXCLUDED_PATH_PREFIXES = [
- `/${KIRBY_API_SLUG}/`,
- "/panel/",
- "/media/panel/",
- "/media/plugins/",
-];
-
-const PRECACHE_URLS = [
- ...(self.__PRECACHE_MANIFEST || []),
- "/",
- `/${CONTENT_API_SLUG}/home.json`,
- `/${CONTENT_API_SLUG}/error.json`,
- `/${CONTENT_API_SLUG}/offline.json`,
-];
-
-/**
- * Stash an item in specified cache
- *
- * @param {string} cacheName Name of cache
- * @param {Request} request Request data
- * @param {Response} response Cloned fetch response
- */
-async function stashInCache(cacheName, request, response) {
- const cache = await caches.open(cacheName);
- cache.put(request, response);
-}
-
-/**
- * Limit the number of items in a specified cache
- *
- * @param {string} cacheName Name of cache
- * @param {number} maxItems Limit of images to cache
- */
-async function trimCache(cacheName, maxItems) {
- const cache = await caches.open(cacheName);
- const keys = await cache.keys();
- if (keys.length > maxItems) {
- await cache.delete(keys[0]);
- trimCache(cacheName, maxItems);
- }
-}
-
-self.addEventListener("message", ({ data }) => {
- if (data?.command === "skipWaiting") {
- self.skipWaiting();
- }
-
- if (data?.command === "trimCaches") {
- if (MAX_CACHED_PAGES) trimCache(CACHE_KEYS.PAGES, MAX_CACHED_PAGES);
- if (MAX_CACHED_IMAGES) trimCache(CACHE_KEYS.IMAGES, MAX_CACHED_IMAGES);
- }
-});
-
-self.addEventListener("install", (event) => {
- // These items must be cached for the service worker to complete installation
- event.waitUntil(
- (async () => {
- const cache = await caches.open(CACHE_KEYS.STATIC);
- await cache.addAll(
- PRECACHE_URLS.map((url) => new Request(url, { credentials: "include" }))
- );
- })()
- );
-});
-
-self.addEventListener("activate", (event) => {
- // Remove caches whose name is no longer valid
- event.waitUntil(
- (async () => {
- const keys = await caches.keys();
- for (const key of keys) {
- if (!Object.values(CACHE_KEYS).includes(key)) {
- await caches.delete(key);
- }
- }
- })()
- );
-});
-
-self.addEventListener("fetch", (event) => {
- const { request } = event;
- const url = new URL(request.url);
- const destination = request.headers.get("Accept");
-
- if (request.method !== "GET") return;
- if (!ALLOWED_HOSTS.find((host) => url.host === host)) return;
- if (EXCLUDED_PATH_PREFIXES.some((path) => url.pathname.startsWith(path)))
- return;
-
- const isHTML = destination.startsWith("text/html");
- const isImage = destination.startsWith("image");
- const isAsset = /^\/(assets|dist)\//.test(url.pathname);
- const isJSON = url.pathname.endsWith(".json");
-
- // Cache-first strategy for static assets and images,
- // network-first strategy for everything else
- event.respondWith(
- (async function () {
- // Lookup cached response of the given request
- const cachedResponse = await caches.match(request);
-
- // Return cached HTML, asset or image, if available
- if (cachedResponse && (isHTML || isImage || isAsset))
- return cachedResponse;
-
- // Create a controller to abort fetch requests after timeout
- const controller = new AbortController();
- const { signal } = controller;
-
- const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT);
-
- try {
- const response = await fetch(request, { signal });
- const copy = response.clone();
- clearTimeout(timeoutId);
-
- if (
- PRECACHE_URLS.includes(url.pathname) ||
- PRECACHE_URLS.includes(url.pathname + "/")
- ) {
- stashInCache(CACHE_KEYS.STATIC, request, copy);
- } else if (isJSON) {
- stashInCache(CACHE_KEYS.PAGES, request, copy);
- } else if (isImage) {
- stashInCache(CACHE_KEYS.IMAGES, request, copy);
- }
-
- return response;
- } catch (error) {
- if (error.name === "AbortError")
- console.log("Fetch aborted after timeout for", request.url);
-
- // Return cached response, if available
- if (cachedResponse) return cachedResponse;
-
- // Return HTML of index page, the frontend will handle redirecting
- // to offline page if applicable
- if (isHTML) return await caches.match("/");
-
- // When offline and JSON data for the requested page wasn't cached
- // before, return a fallback JSON
- if (isJSON) {
- return new Response(JSON.stringify({ __isOffline: true }), {
- headers: {
- "Content-Type": "application/json",
- "Cache-Control": "no-store",
- },
- });
- }
-
- console.error(error);
- return new Response(null, { status: 504 });
- }
- })()
- );
-});
diff --git a/storage/content/offline/default.txt b/storage/content/offline/default.txt
deleted file mode 100644
index 256ff767..00000000
--- a/storage/content/offline/default.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-Title: Offline
-
-----
-
-Intro: Offline
-
-----
-
-Text: It looks like you're offline right now.
-
-Not to worry, much of this site is available regardless of network connection.
-
-- (link: home text: Home)
-- (link: photography text: Photography)
-- (link: notes text: Notes)
-
-Along with any other pages you've previously viewed.