UNPKG

microsite

Version:
125 lines (124 loc) 4.97 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import fetch, { Request } from "node-fetch"; import CachePolicy from "http-cache-semantics"; import { promises as fsp } from "fs"; import { join } from "path"; import { createHash } from "crypto"; export function createPrefetch(previousKey) { return async function prefetch(info, init = {}) { // file mode if (typeof info === "string" && !info.startsWith("http")) { const key = await createFileKey(info); return Buffer.from(JSON.stringify({ type: "fs", key })).toString("base64"); } // fetch mode let req = new Request(info, Object.assign(Object.assign({}, init), { method: "HEAD" })); let previousPolicy = null; let previousRes = null; if (previousKey) { const { policy, response } = JSON.parse(Buffer.from(previousKey, "base64").toString("utf8")); previousPolicy = CachePolicy.fromObject(policy); previousRes = response; } if (previousPolicy && previousPolicy.satisfiesWithoutRevalidation(req)) { previousRes.headers = previousPolicy.responseHeaders(); const response = !previousPolicy.storable() ? null : serializeRes(previousRes); return Buffer.from(JSON.stringify({ type: "network", policy: previousPolicy.toObject(), response, })).toString("base64"); } if (previousPolicy) { req = new Request(info, Object.assign(Object.assign({}, init), { method: "HEAD", headers: Object.assign(Object.assign({}, req.headers), previousPolicy.revalidationHeaders(serializeReq(req))) })); const res = await fetch(req); const { policy, modified } = previousPolicy.revalidatedPolicy(req, res); const response = !policy.storable() ? null : serializeRes(res); if (modified) { return Buffer.from(JSON.stringify({ type: "network", policy: policy.toObject(), response, })).toString("base64"); } return Buffer.from(JSON.stringify({ type: "network", policy: previousPolicy.toObject(), response, })).toString("base64"); } const res = await fetch(req); const policy = createPolicy(req, res); const response = !policy.storable() ? null : serializeRes(res); return Buffer.from(JSON.stringify({ policy: policy.toObject(), response })).toString("base64"); }; } export const isKeyValid = (previousKey, key) => { if (!previousKey || !key) return false; const { type } = JSON.parse(Buffer.from(previousKey, "base64").toString("utf8")); if (type === "fs") return previousKey === key; const _a = JSON.parse(Buffer.from(key, "base64").toString("utf8")), { type: _type } = _a, current = __rest(_a, ["type"]); return current.response.status === 200 || current.response.status === 304; }; const serializeReq = (req) => ({ url: req.url, method: req.method, headers: iterableToObject(req.headers), }); const serializeRes = (res) => ({ status: res.status, headers: iterableToObject(res.headers), }); const iterableToObject = (iter) => { if (typeof iter.keys !== "function") return iter; let obj = {}; for (const key of iter.keys()) { obj[key] = iter.get(key); } return obj; }; const createPolicy = (req, res) => new CachePolicy(serializeReq(req), serializeRes(res), { shared: false }); async function createFileKey(p) { const hash = createHash("sha1"); const stat = await fsp.stat(p); if (stat.isDirectory()) { for (const ent of await addDir(p, { mode: "dir" })) { hash.update(ent); } } else { hash.update(await addFile(p, { mode: "file" })); } return hash.digest("base64"); } const addDir = async (p, { mode }) => { const ents = await fsp.readdir(p, { withFileTypes: true }); const results = await Promise.all(ents.map((ent) => ent.isDirectory() ? addDir(join(p, ent.name), { mode }) : addFile(join(p, ent.name), { mode }))); return [].concat(...results); }; const addFile = async (p, { mode }) => { if (mode === "dir") { return p; } else if (mode === "file") { const content = await fsp.readFile(p); return content; } };