summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/content/index.html22
-rw-r--r--src/content/papo.js34
-rw-r--r--src/content/style.css0
-rw-r--r--src/content/sw.js131
-rwxr-xr-xsrc/exported.sh7
-rw-r--r--src/sw-main.js5
6 files changed, 199 insertions, 0 deletions
diff --git a/src/content/index.html b/src/content/index.html
new file mode 100644
index 0000000..ca012d4
--- /dev/null
+++ b/src/content/index.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en" manifest="manifest.appcache">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <!--
+ FIXME
+ - metadata
+ - canonical link
+ -->
+ <link rel="stylesheet" type="text/css" href="./style.css" />
+ <link rel="shortcut icon" type="image/svg+xml" href="./img/favicon.svg" />
+ <title>Chat with Freedom | Papo FIXME</title>
+ <script type="module">
+ import { main } from "./papo.js";
+ main();
+ </script>
+ </head>
+ <body>
+ <p id="static-loading">Loading...</p>
+ </body>
+</html>
diff --git a/src/content/papo.js b/src/content/papo.js
new file mode 100644
index 0000000..83dd1b1
--- /dev/null
+++ b/src/content/papo.js
@@ -0,0 +1,34 @@
+// import * as assert from "node:assert/strict";
+
+
+// assert.deepEqual({a: 1}, {a: 1});
+// console.log(123);
+// console.log({ navigator });
+
+
+const f1 = x => x + 1;
+const f2 = x => x - 1;
+
+const registerServiceWorker = async ({ serviceWorkerPath, err }) => {
+ try {
+ await navigator.serviceWorker?.register(path)
+ } catch (e) {
+ err(`Service worker registration failed: ${e}`);
+ }
+};
+
+export const main = async ({
+ serviceWorkerPath = "./service-worker.js",
+ out = console.log,
+ err = console.warn,
+} = {}) => {
+ const env = {
+ serviceWorkerPath,
+ out,
+ err,
+ };
+ await registerServiceWorker(env);
+ out("main called");
+};
+
+// <body onload="setOnline()" ononline="setOnline()" onoffline="setOnline()" >
diff --git a/src/content/style.css b/src/content/style.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/content/style.css
diff --git a/src/content/sw.js b/src/content/sw.js
new file mode 100644
index 0000000..9e17707
--- /dev/null
+++ b/src/content/sw.js
@@ -0,0 +1,131 @@
+const CACHE_NAME = "static-shared-assets";
+
+const FALLBACK_PATHS = {
+ img: "/fallback-image-FIXME.svg",
+ data: {
+ static: "/fallback-data-FIXME.json",
+ },
+};
+
+const leafValues = tree =>
+ Object.values(tree).map(x =>
+ typeof x !== "object"
+ ? x
+ : leafValues(x)
+ );
+
+const collectLeaves = tree =>
+ leafValues(tree).flat(Infinity);
+
+const DEFAULT_INSTALL_PATHS = [
+ "/",
+ "index.html",
+ "style.css",
+ "img/favicon.svg",
+ "papo.js",
+].concat(collectLeaves(FALLBACK_PATHS));
+
+const mkInstallHandler = ({ self, caches }) => event => {
+ self.skipWaiting();
+ event.waitUntil(
+ caches.open(CACHE_NAME).then(
+ cache => cache.addAll(DEFAULT_INSTALL_PATHS),
+ ),
+ );
+};
+
+const store = async (caches, request, response) => {
+ const cache = await caches.open(CACHE_NAME);
+ await cache.put(request, response);
+};
+
+const getPrefixIn = (paths, segments) => {
+ if (paths === undefined) {
+ return null;
+ }
+
+ if (segments.length === 0) {
+ return null;
+ }
+
+ if (typeof paths === "string") {
+ return paths;
+ }
+
+ return getPrefixIn(paths[segments[0]], segments.slice(1));
+};
+
+const maybeFallback = async (caches, request, paths = FALLBACK_PATHS) => {
+ const url = new URL(request.url)
+ const segments = url.pathname.split("/").filter(s => !!s)
+ const fallbackPath = getPrefixIn(paths, segments);
+ if (fallbackPath) {
+ return await caches.match(fallbackPath);
+ }
+
+ return null;
+}
+
+const fromCache = async (caches, fetch, { request, preloadResponse }) => {
+ const cachedResponse = await caches.match(request);
+ if (cachedResponse) {
+ fetch(request).then(async freshResponse =>
+ store(caches, request, freshResponse));
+ return cachedResponse;
+ }
+
+ const preloadedResponse = await preloadResponse;
+ if (preloadedResponse) {
+ store(caches, request, preloadeResponse.clone());
+ return preloadedResponse;
+ }
+
+ try {
+ // FIXME: do integration test with real lack of internet.
+ const fetchedResponse = await fetch(request);
+ store(caches, request, fetchedResponse.clone());
+ return fetchedResponse;
+ } catch (e) {
+ const fallbackResponse = await maybeFallback(caches, request);
+ if (fallbackResponse) {
+ return fallbackResponse;
+ }
+
+ throw e;
+ }
+};
+
+const mkFetchHandler = ({ caches }) => (event, mkfetch = () => fetch) => {
+ if (event.request.method !== "GET") {
+ return;
+ }
+
+ event.respondWith(fromCache(caches, mkfetch(), event));
+};
+
+const mkActivateHandler = ({ self, clients }) => event =>
+ event.waitUntil(Promise.all([
+ clients.claim(),
+ self.registration.navigationPreload?.enable(),
+ ]));
+
+const registerListeners = env => {
+ env.self.addEventListener("install", mkInstallHandler(env));
+ env.self.addEventListener("activate", mkActivateHandler(env));
+ env.self.addEventListener("fetch", mkFetchHandler(env));
+};
+
+const main = ({
+ self,
+ caches,
+ clients,
+ out = console.log,
+ err = console.warn,
+} = {}) =>
+ registerListeners({
+ self,
+ caches,
+ clients,
+ out,
+ err,
+ });
diff --git a/src/exported.sh b/src/exported.sh
new file mode 100755
index 0000000..15e99f6
--- /dev/null
+++ b/src/exported.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+set -eu
+
+cat "$1"
+printf '%s\n\n\nexport {\n'
+awk '/^const / { printf "\t%s,\n", $2 }' "$1"
+printf '}\n'
diff --git a/src/sw-main.js b/src/sw-main.js
new file mode 100644
index 0000000..be45ba4
--- /dev/null
+++ b/src/sw-main.js
@@ -0,0 +1,5 @@
+main({
+ self,
+ caches,
+ clients,
+});