UNPKG

5.31 kBJavaScriptView Raw
1var __rest = (this && this.__rest) || function (s, e) {
2 var t = {};
3 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4 t[p] = s[p];
5 if (s != null && typeof Object.getOwnPropertySymbols === "function")
6 for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7 if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8 t[p[i]] = s[p[i]];
9 }
10 return t;
11};
12import fetch, { Request } from "node-fetch";
13import CachePolicy from "http-cache-semantics";
14import { promises as fsp } from "fs";
15import { CACHE_DIR } from "./build";
16import { join } from "path";
17import { createHash } from "crypto";
18import cache from "cacache";
19export async function getCacheEntry(method, fileName) {
20 const cacheKey = `${method}:${fileName}`;
21 const { data = null, meta: { prefetch = null, hash = null } = {}, } = await cache.get(CACHE_DIR, cacheKey).catch(() => ({}));
22 return { data, meta: { prefetch, hash } };
23}
24export function createPrefetch(previousKey) {
25 return async function prefetch(info, init = {}) {
26 // file mode
27 if (typeof info === "string" && !info.startsWith("http")) {
28 const key = await createFileKey(info);
29 return Buffer.from(JSON.stringify({ type: "fs", key })).toString("base64");
30 }
31 // fetch mode
32 let req = new Request(info, Object.assign(Object.assign({}, init), { method: "HEAD" }));
33 let previousPolicy = null;
34 let previousRes = null;
35 if (previousKey) {
36 const { policy, response } = JSON.parse(Buffer.from(previousKey, "base64").toString("utf8"));
37 previousPolicy = CachePolicy.fromObject(policy);
38 previousRes = response;
39 }
40 if (previousPolicy && previousPolicy.satisfiesWithoutRevalidation(req)) {
41 previousRes.headers = previousPolicy.responseHeaders();
42 const response = !previousPolicy.storable()
43 ? null
44 : serializeRes(previousRes);
45 return Buffer.from(JSON.stringify({
46 type: "network",
47 policy: previousPolicy.toObject(),
48 response,
49 })).toString("base64");
50 }
51 if (previousPolicy) {
52 req = new Request(info, Object.assign(Object.assign({}, init), { method: "HEAD", headers: Object.assign(Object.assign({}, req.headers), previousPolicy.revalidationHeaders(serializeReq(req))) }));
53 const res = await fetch(req);
54 const { policy, modified } = previousPolicy.revalidatedPolicy(req, res);
55 const response = !policy.storable() ? null : serializeRes(res);
56 if (modified) {
57 return Buffer.from(JSON.stringify({
58 type: "network",
59 policy: policy.toObject(),
60 response,
61 })).toString("base64");
62 }
63 return Buffer.from(JSON.stringify({
64 type: "network",
65 policy: previousPolicy.toObject(),
66 response,
67 })).toString("base64");
68 }
69 const res = await fetch(req);
70 const policy = createPolicy(req, res);
71 const response = !policy.storable() ? null : serializeRes(res);
72 return Buffer.from(JSON.stringify({ policy: policy.toObject(), response })).toString("base64");
73 };
74}
75export const isKeyValid = (previousKey, key) => {
76 if (!previousKey || !key)
77 return false;
78 const { type } = JSON.parse(Buffer.from(previousKey, "base64").toString("utf8"));
79 if (type === "fs")
80 return previousKey === key;
81 const _a = JSON.parse(Buffer.from(key, "base64").toString("utf8")), { type: _type } = _a, current = __rest(_a, ["type"]);
82 return current.response.status === 200 || current.response.status === 304;
83};
84const serializeReq = (req) => ({
85 url: req.url,
86 method: req.method,
87 headers: iterableToObject(req.headers),
88});
89const serializeRes = (res) => ({
90 status: res.status,
91 headers: iterableToObject(res.headers),
92});
93const iterableToObject = (iter) => {
94 if (typeof iter.keys !== "function")
95 return iter;
96 let obj = {};
97 for (const key of iter.keys()) {
98 obj[key] = iter.get(key);
99 }
100 return obj;
101};
102const createPolicy = (req, res) => new CachePolicy(serializeReq(req), serializeRes(res), { shared: false });
103async function createFileKey(p) {
104 const hash = createHash("sha1");
105 const stat = await fsp.stat(p);
106 if (stat.isDirectory()) {
107 for (const ent of await addDir(p, { mode: "dir" })) {
108 hash.update(ent);
109 }
110 }
111 else {
112 hash.update(await addFile(p, { mode: "file" }));
113 }
114 return hash.digest("base64");
115}
116const addDir = async (p, { mode }) => {
117 const ents = await fsp.readdir(p, { withFileTypes: true });
118 const results = await Promise.all(ents.map((ent) => ent.isDirectory()
119 ? addDir(join(p, ent.name), { mode })
120 : addFile(join(p, ent.name), { mode })));
121 return [].concat(...results);
122};
123const addFile = async (p, { mode }) => {
124 if (mode === "dir") {
125 return p;
126 }
127 else if (mode === "file") {
128 const content = await fsp.readFile(p);
129 return content;
130 }
131};