@paulwer/prisma-extension-cache-manager
Version:
A caching extension for [Prisma](https://www.prisma.io/), fully compatible with [cache-manager](https://www.npmjs.com/package/cache-manager), predefined uncaching strategies and custom handlers for key generation and uncaching.
125 lines (124 loc) • 5.96 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateComposedKey = generateComposedKey;
exports.createKey = createKey;
exports.serializeData = serializeData;
exports.deserializeData = deserializeData;
exports.getInvolvedModels = getInvolvedModels;
const library_1 = require("@prisma/client/runtime/library");
const crypto_1 = require("crypto");
const safe_stable_stringify_1 = __importDefault(require("safe-stable-stringify"));
function generateComposedKey(options) {
const hash = (0, crypto_1.createHash)("md5")
.update(JSON.stringify(options?.queryArgs, (_, v) => typeof v === "bigint" ? v.toString() : v))
.digest("hex");
return `${options.namespace ? options.namespace : ""}:${[options.model, options.operation].join(":")}@${hash}`;
}
function createKey(key, namespace) {
return [namespace, key].filter((e) => !!e).join(":");
}
function serializeData(data, prefixes) {
function serializeCustomClasses(data) {
if (library_1.Decimal.isDecimal(data))
return `${prefixes?.Decimal || "___de_"}${data.toString()}`;
if (typeof data === "bigint")
return `${prefixes?.BigInt || "___bi_"}${data.toString()}`;
if (Buffer.isBuffer(data))
return `${prefixes?.Buffer || "___bu_"}${data.toString()}`;
if (data instanceof Uint8Array)
return `${prefixes?.Uint8Array || "___u8_"}${data.toString()}`;
if (data instanceof Uint16Array)
return `${prefixes?.Uint16Array || "___u16_"}${data.toString()}`;
if (data instanceof Uint32Array)
return `${prefixes?.Uint32Array || "___u32_"}${data.toString()}`;
if (data instanceof Date)
return `${prefixes?.Date || "___da_"}${data.toISOString()}`;
else if (Array.isArray(data))
return data.map(serializeCustomClasses); // Handle arrays
else if (data && typeof data === "object") {
const out = {};
for (const key in data)
out[key] = serializeCustomClasses(data[key]); // Recursively serialize
return out;
}
else
return data;
}
return (0, safe_stable_stringify_1.default)({ data: serializeCustomClasses(data) });
}
function deserializeData(serializedData, prefixes) {
return JSON.parse(serializedData, (_key, value) => {
// Check if the value contains the custom marker and convert back to original class/type
if (typeof value === "string" &&
value.startsWith(prefixes?.Decimal || "___de_"))
return new library_1.Decimal(value.replace(prefixes?.Decimal || "___de_", ""));
if (typeof value === "string" &&
value.startsWith(prefixes?.Buffer || "___bu_"))
return Buffer.from(value.replace(prefixes?.Buffer || "___bu_", ""));
if (typeof value === "string" &&
value.startsWith(prefixes?.Uint8Array || "___u8_"))
return new Uint8Array(value
.replace(prefixes?.Uint8Array || "___u8_", "")
.split(",")
.map(Number));
if (typeof value === "string" &&
value.startsWith(prefixes?.Uint16Array || "___u16_"))
return new Uint8Array(value
.replace(prefixes?.Uint16Array || "___u16_", "")
.split(",")
.map(Number));
if (typeof value === "string" &&
value.startsWith(prefixes?.Uint32Array || "___u32_"))
return new Uint8Array(value
.replace(prefixes?.Uint32Array || "___u32_", "")
.split(",")
.map(Number));
if (typeof value === "string" &&
value.startsWith(prefixes?.BigInt || "___bi_"))
return BigInt(value.replace(prefixes?.BigInt || "___bi_", ""));
if (typeof value === "string" &&
value.startsWith(prefixes?.Date || "___da_"))
return new Date(value.replace(prefixes?.Date || "___da_", ""));
return value;
}).data;
}
// Utility to detect related models from query arguments
function getInvolvedModels(prisma, modelName, operation, args) {
const model = prisma.dmmf.datamodel.models.find((m) => m.name === modelName);
const involvedModels = [];
const checkInvolvedModels = (modelName, args) => {
involvedModels.push(modelName);
for (const field in args) {
if (model.fields.some((f) => f.name === field && f.kind === "object")) {
// If the field represents a relation, add it to involvedModels
const relatedField = model.fields.find((f) => f.name === field);
if (relatedField) {
const relatedModelName = relatedField.type;
involvedModels.push(relatedModelName);
// Recursively check if there are further nested models
if (typeof args[field] === "object") {
for (const relatedMethodName in args[field]) {
checkInvolvedModels(relatedModelName, args[field][relatedMethodName]);
}
}
}
}
}
};
if (operation == "create" ||
operation == "update" ||
operation == "updateMany")
checkInvolvedModels(modelName, args.data);
else if (operation == "createMany" || operation == "createManyAndReturn")
[args.data].flat().map((row) => checkInvolvedModels(modelName, row));
else if (operation == "upsert" && args.create)
checkInvolvedModels(modelName, args.create);
else if (operation == "upsert" && args.update)
checkInvolvedModels(modelName, args.update);
else
involvedModels.push(modelName);
return [...new Set(involvedModels)];
}