Published
Edited
Jul 2, 2022
1 fork
5 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
endpoint = deploy("default", async (req, res) => {
if (req.url === "/offline.js") {
return res
.header("content-type", "text/javascript")
.send(offline.toString().substring(13, offline.toString().length - 2));
} else if (req.url === "/untar.js") {
return res
.header("content-type", "text/javascript")
.send(untar.toString().substring(13, untar.toString().length - 2));
} else {
return res.header("content-type", "text/html").send(offlineHtml);
}
})
Insert cell
Insert cell
Insert cell
md`link to [offline.html](${baseURL}/offline.html)`
Insert cell
Insert cell
Insert cell
offline = function () {
console.log("Service worker exec");
self.importScripts(
"https://cdnjs.cloudflare.com/ajax/libs/pako/2.0.3/pako.es5.min.js",
"./untar.js"
);

self.addEventListener("install", function (event) {
console.log("service worker has been installed");
});

function mime(name) {
if (name.endsWith(".js")) return "text/javascript";
if (name.endsWith(".html")) return "text/html";
if (name.endsWith(".css")) return "text/css";
if (name.endsWith(".jpg")) return "image/jpeg";
if (name.endsWith(".jpeg")) return "image/jpeg";
if (name.endsWith(".png")) return "image/png";
return "application/octet-stream";
}
self.addEventListener("fetch", function (event) {
let match = undefined;
async function answer() {
debugger;
if (
(match = event.request.url.match(
/observablehq\.com\/@[^/]*\/offline\/(.*\.tgz(?:[?]v=\d)?)(\/[^?#]*)?$/
))
) {
let archiveURL = match[1];
if (
// Express mangles the URL
archiveURL.startsWith("https:/") &&
!archiveURL.startsWith("https://")
) {
archiveURL = archiveURL.replace("https:/", "https://");
}
if (!match[2])
return new Response("", {
status: 301,
headers: {
Location: `${event.request.url}/index.html`
}
});
const path = "." + match[2];

const archiveRequest = new Request(archiveURL);
const cache = await caches.open("archives");
const cachedResponse = await cache.match(event.request);
const archiveResponse =
cachedResponse ||
(await fetch(archiveRequest).then((response) => {
if (response.status >= 200 && response.status < 400)
cache.put(archiveRequest, response.clone());
return response;
}));

if (archiveResponse.status !== 200) {
return archiveResponse;
}

const buffer = new Uint8Array(await archiveResponse.arrayBuffer());
const uncompressed = await pako.ungzip(buffer);

const reader = new UntarFileStream(uncompressed.buffer);
const contents = {};
while (reader.hasNext()) {
const file = reader.next();
contents[file.name] = file;
}

if (contents[path]) {
const type = mime(path);
return new Response(contents[path].buffer, {
headers: {
"content-type": type
}
});
} else {
return new Response(`Not found: ${path}`, {
status: 404
});
}
} else {
// Normal events response but cache, and use network if we are offline
const cache = await caches.open("offline");
try {
const response = await fetch(event.request);
if (event.request.method == "GET") {
cache.put(event.request, response.clone());
}
return response;
} catch (err) {
// we are offline!
const cachedReponse = cache.match(event.request);
return cachedReponse;
}

return fetch(event.request);
}
}

event.respondWith(answer());
});
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import { viewof notebook, link } from '@endpointservices/tzg'
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more