diff --git a/_headers b/_headers new file mode 100644 index 00000000..7ad78b0a --- /dev/null +++ b/_headers @@ -0,0 +1,82 @@ +/* + content-security-policy: default-src 'self';script-src 'self' 'wasm-unsafe-eval' 'sha256-j3WY7tmWihTphHtAgJq+d5hIO5BncdM67vnYHPKGZQ4=' 'sha256-Rv0XCoOhq4H0QyKE7rEhr+e9GI5gsmGcC04fY0HPORc=' 'sha256-28NJWgGMi7z1BsySG4SYZCjth/ys7dkElS3oIl5ZEqM=' 'sha256-nUHIts9QUqQq4nfffteH1WG3ZeWESwmxZn6bWMNWsiM=' 'sha256-MGLg9fH15qQqEcT+iTfwx/cfVp2MgjSrVt08u3NVKa8=' 'sha256-OQjxgqHHnjfZwkCEsAo2MRjd3GuPmg+RvmjrZd35TN4=' 'sha256-sS3nggZVNGyoYqI7U/PSwnwI4CymIdHNgJwW49qztWo=' 'sha256-aASq1hOJ8PP2cfK9QGXaCLdqgtkDXDb5VFXlSyrpX/M=' 'sha256-1ujkGrbsh0Yx/bquh2I9gkG1ZaZetCkjre6vciK2u7U=';worker-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' * data: blob:;media-src 'self' *;connect-src 'self' * data: blob:;frame-src 'none';frame-ancestors 'none';object-src 'none';manifest-src 'self';form-action 'self';base-uri 'self' + referrer-policy: no-referrer + strict-transport-security: max-age=15552000; includeSubDomains + permissions-policy: sync-xhr=(),document-domain=(),interest-cohort=() + x-content-type-options: nosniff + x-download-options: noopen + x-frame-options: DENY + x-xss-protection: 1;mode=block + cross-origin-opener-policy: same-origin + +/ + Cache-Control: public,max-age=3600 + +/notifications + Cache-Control: public,max-age=3600 + +/local + Cache-Control: public,max-age=3600 + +/community + Cache-Control: public,max-age=3600 + +/federated + Cache-Control: public,max-age=3600 + +/favorites + Cache-Control: public,max-age=3600 + +/direct + Cache-Control: public,max-age=3600 + +/bookmarks + Cache-Control: public,max-age=3600 + +/muted + Cache-Control: public,max-age=3600 + +/blocked + Cache-Control: public,max-age=3600 + +/pinned + Cache-Control: public,max-age=3600 + +/search + Cache-Control: public,max-age=3600 + +/settings + Cache-Control: public,max-age=3600 + +/settings/* + Cache-Control: public,max-age=3600 + +/service-worker-index + Cache-Control: public,max-age=3600 + +/service-worker.js + Cache-Control: public,max-age=0 + +/*.png + Cache-Control: public,max-age=31536000,immutable + +/*.jpg + Cache-Control: public,max-age=31536000,immutable + +/*.jpeg + Cache-Control: public,max-age=31536000,immutable + +/client/*.js + Cache-Control: public,max-age=31536000,immutable + +/client/*.css + Cache-Control: public,max-age=31536000,immutable + +/client/*.map + Cache-Control: public,max-age=31536000,immutable + +/client/*.LICENSE + Cache-Control: public,max-age=31536000,immutable + +/client/*.txt + Cache-Control: public,max-age=31536000,immutable diff --git a/bin/build-for-cloudflare.sh b/bin/build-for-cloudflare.sh new file mode 100755 index 00000000..27a4abfe --- /dev/null +++ b/bin/build-for-cloudflare.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +yarn +IS_CLOUDFLARE=1 yarn build +cp _headers __sapper__/export/ +cd __sapper__/export +rm -f /tmp/pinafore.zip +zip -r /tmp/pinafore.zip * diff --git a/package.json b/package.json index c8a50b3b..08cdf276 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "2.6.0", "type": "module", "engines": { - "node": "^12.20.0 || ^14.13.1 || ^16.0.0 || ^18.0.0 || ^20.0.0" + "node": "^12.20.0 || ^14.13.1 || ^16.0.0 || ^18.0.0 || ^20.0.0 || ^22.0.0" }, "scripts": { "lint": "standard && standard --plugin html 'src/routes/**/*.html'", @@ -201,6 +201,7 @@ }, "volta": { "node": "14.21.1", - "yarn": "1.22.19" - } + "yarn": "1.22.22" + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/routes/_utils/serviceWorkerClient.js b/src/routes/_utils/serviceWorkerClient.js index 0fccdf1a..4ca72fb9 100644 --- a/src/routes/_utils/serviceWorkerClient.js +++ b/src/routes/_utils/serviceWorkerClient.js @@ -15,7 +15,10 @@ function onUpdateFound (registration) { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { snackbar.announce('intl.updateAvailable', 'intl.reload', async () => { await skipWaiting() - document.location.reload(true) + // Slight delay since otherwise Chrome loads infinitely for some reason + setTimeout(() => { + document.location.reload(true) + }, 200) }) } }) diff --git a/src/service-worker.js b/src/service-worker.js index b170eada..981fbdc7 100644 --- a/src/service-worker.js +++ b/src/service-worker.js @@ -38,6 +38,8 @@ const ON_DEMAND_CACHE = [ // `static` is an array of everything in the `static` directory const assets = __assets__ .map(file => file.startsWith('/') ? file : `/${file}`) + // Fix for Cloudflare 308 redirects of /service-worker-index.html to /service-worker + .map(file => file === '/service-worker-index.html' && process.env.IS_CLOUDFLARE ? '/service-worker-index' : file) .filter(filename => !filename.endsWith('.map')) .filter(filename => filename !== '/robots.txt') .filter(filename => !filename.includes('traineddata.gz')) // cache on-demand @@ -156,7 +158,10 @@ self.addEventListener('fetch', event => { // for routes, serve the /service-worker-index.html file from the most recent // static cache if (routes.find(route => route.pattern.test(url.pathname))) { - const response = await caches.match('/service-worker-index.html') + const response = await caches.match( + // Fix for Cloudflare 308 redirects of /service-worker-index.html to /service-worker + process.env.IS_CLOUDFLARE ? '/service-worker-index' : '/service-worker-index.html' + ) if (response) { return response } diff --git a/webpack/service-worker.config.js b/webpack/service-worker.config.js index b231cee0..8cd8f6de 100644 --- a/webpack/service-worker.config.js +++ b/webpack/service-worker.config.js @@ -37,7 +37,8 @@ export default { 'process.env.NODE_ENV': JSON.stringify(mode), 'process.env.SAPPER_TIMESTAMP': process.env.SAPPER_TIMESTAMP || Date.now(), 'process.env.LOCALE': JSON.stringify(LOCALE), - 'process.env.IS_SERVICE_WORKER': 'true' + 'process.env.IS_SERVICE_WORKER': 'true', + 'process.env.IS_CLOUDFLARE': JSON.stringify(!!process.env.IS_CLOUDFLARE), }) ].filter(Boolean) }