@nimpl/cache-adapter
Version:
An adapter that allows you to use any cache handler on the client and server side and switch between them. Adds support for running next.js applications in multiple instances.
150 lines (149 loc) • 7.19 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createServer = void 0;
/* eslint-disable @typescript-eslint/no-explicit-any */
const http_1 = require("http");
const buildIds = [];
const inMemoryCache = {
get: {},
set: {},
revalidate: {},
delete: {},
};
/**
* Create server to control cache remotely
* @param cacheHandler custom cache-handler
* @param verifyRequest callback to verify request
* @returns server
*/
const createServer = (cacheHandler, verifyRequest) => {
const isFledgedCacheHandler = cacheHandler.keys && !cacheHandler.delete;
if (!isFledgedCacheHandler) {
console.error("The current cacheHandler does not support deletion of outdated data. Missing methods: keys and delete");
}
const server = (0, http_1.createServer)((req, res) => __awaiter(void 0, void 0, void 0, function* () {
var _a;
try {
if (!req.url || (verifyRequest && !verifyRequest(req)))
return res.end();
const url = new URL(req.url, "http://n");
const buildId = url.searchParams.get("buildId") || "";
const key = url.searchParams.get("key");
const method = (_a = req.method) === null || _a === void 0 ? void 0 : _a.toLowerCase();
if (!buildIds.includes(buildId)) {
buildIds.push(buildId);
}
if (!key || !method)
return res.end();
const requestKey = buildId + key;
if (method === "get") {
if (!inMemoryCache.get[requestKey]) {
inMemoryCache.get[requestKey] = cacheHandler
.get(requestKey)
.then((d) => {
delete inMemoryCache.get[requestKey];
return d;
})
.catch(() => {
return null;
});
}
const data = yield inMemoryCache.get[req.url];
if (data) {
return res.end(JSON.stringify(data));
}
else {
res.statusCode = 400;
return res.end();
}
}
if (method === "post") {
if (!inMemoryCache.set[requestKey]) {
inMemoryCache.set[requestKey] = new Promise((resolve) => {
let rowData = "";
req.on("data", (chunk) => {
rowData += chunk;
});
req.on("end", () => {
resolve(JSON.parse(rowData));
});
})
.then((body) => {
const headerTags = body.data.headers["x-next-cache-tags"].split(",");
body.data.headers["x-next-cache-tags"] = headerTags.map((r) => buildId + r).join(",");
return cacheHandler.set(requestKey, body.data, body.ctx);
})
.finally(() => {
delete inMemoryCache.set[requestKey];
});
}
yield inMemoryCache.set[requestKey];
return res.end();
}
if (method === "delete") {
if (!inMemoryCache.revalidate[requestKey]) {
inMemoryCache.revalidate[requestKey] = cacheHandler.revalidateTag(requestKey).finally(() => {
delete inMemoryCache.revalidate[requestKey];
});
}
yield inMemoryCache.revalidate[requestKey];
return res.end();
}
// new build ready
if (method === "put" && isFledgedCacheHandler) {
if (!inMemoryCache.delete[requestKey]) {
inMemoryCache.delete[requestKey] = cacheHandler.keys()
.then((cachedKeys) => __awaiter(void 0, void 0, void 0, function* () {
var _b, cachedKeys_1, cachedKeys_1_1;
var _c, e_1, _d, _e;
const targetBuildIdIndex = buildIds.indexOf(buildId);
const oldBuildIds = buildIds.slice(0, targetBuildIdIndex);
try {
for (_b = true, cachedKeys_1 = __asyncValues(cachedKeys); cachedKeys_1_1 = yield cachedKeys_1.next(), _c = cachedKeys_1_1.done, !_c; _b = true) {
_e = cachedKeys_1_1.value;
_b = false;
const cachedKey = _e;
if (oldBuildIds.some((id) => cachedKey.startsWith(id))) {
yield cacheHandler.delete(cachedKey);
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_b && !_c && (_d = cachedKeys_1.return)) yield _d.call(cachedKeys_1);
}
finally { if (e_1) throw e_1.error; }
}
}))
.finally(() => {
delete inMemoryCache.delete[requestKey];
});
}
yield inMemoryCache.delete[requestKey];
return res.end();
}
}
catch (e) {
console.log("error on cache processing", e);
}
}));
return server;
};
exports.createServer = createServer;