diff --git a/jsconfig.json b/jsconfig.json index 306cb6b6..d1365282 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -21,6 +21,7 @@ ".svelte-kit/ambient.d.ts", "vite.config.js", "vite.config.ts", + "src/app.d.ts", "src/lib/**/*.js", "src/lib/**/*.ts", "src/lib/**/*.svelte", diff --git a/src/app.d.ts b/src/app.d.ts index ede601ab..a6cb12cf 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -8,6 +8,16 @@ declare global { // interface PageState {} // interface Platform {} } + + namespace svelteHTML { + interface HTMLAttributes { + "on:vite:preloadError"?: (event: any) => any; + } + + interface HTMLProps { + "on:vite:preloadError"?: (event: any) => any; + } + } } export {}; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index eb28d97e..5e1c1630 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,5 +1,8 @@ - @@ -16,4 +32,6 @@ {/if} + + diff --git a/src/service-worker.ts b/src/service-worker.ts new file mode 100644 index 00000000..aa169f42 --- /dev/null +++ b/src/service-worker.ts @@ -0,0 +1,86 @@ +/// +/// +/// +/// + +const sw = self as unknown as ServiceWorkerGlobalScope; + +import { build, files, version } from "$service-worker"; + +// Create a unique cache name for this deployment +const CACHE = `cache-${version}`; + +const ASSETS = [ + ...build, // the app itself + ...files, // everything in `static` +]; + +sw.addEventListener("install", (event) => { + // Create a new cache and add all files to it + async function addFilesToCache() { + const cache = await caches.open(CACHE); + await cache.addAll(ASSETS); + } + + event.waitUntil(addFilesToCache()); +}); + +sw.addEventListener("activate", (event) => { + // Remove previous cached data from disk + async function deleteOldCaches() { + for (const key of await caches.keys()) { + if (key !== CACHE) await caches.delete(key); + } + } + + event.waitUntil(deleteOldCaches()); +}); + +sw.addEventListener("fetch", (event) => { + // ignore POST requests etc + if (event.request.method !== "GET") return; + + async function respond() { + const url = new URL(event.request.url); + const cache = await caches.open(CACHE); + + // `build`/`files` can always be served from the cache + if (ASSETS.includes(url.pathname)) { + const response = await cache.match(url.pathname); + + if (response) { + return response; + } + } + + // for everything else, try the network first, but + // fall back to the cache if we're offline + try { + const response = await fetch(event.request); + + // if we're offline, fetch can return a value that is not a Response + // instead of throwing - and we can't pass this non-Response to respondWith + if (!(response instanceof Response)) { + throw new Error("invalid response from fetch"); + } + + if (response.status === 200) { + cache.put(event.request, response.clone()); + } + + return response; + } catch (err) { + const response = await cache.match(event.request); + + if (response) { + return response; + } + + // if there's no cache, then just error out + // as there is nothing we can do to respond to this request + throw err; + } + } + + event.respondWith(respond()); +}); diff --git a/tsconfig.json b/tsconfig.json index d1efc4d0..b1bc480b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "src/**/*.ts", "src/**/*.svelte", "src/**/*.js", + "src/app.d.ts", ".svelte-kit/ambient.d.ts" ], "exclude": ["public/*"]