@knaus94/prisma-extension-cache-manager 1.5.72 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/dist/index.d.ts +2 -7
- package/dist/index.js +76 -277
- package/dist/types.d.ts +2 -2
- package/package.json +9 -12
package/README.md
CHANGED
|
@@ -6,7 +6,6 @@ A caching extension for [Prisma](https://www.prisma.io/), compatible with [cache
|
|
|
6
6
|
|
|
7
7
|
- [cache-manager](https://www.npmjs.com/package/cache-manager) compatibility
|
|
8
8
|
- Only model queries can be cacheable (no $query or $queryRaw)
|
|
9
|
-
- Supported Decimal
|
|
10
9
|
|
|
11
10
|
## Installation
|
|
12
11
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { ModelExtension, PrismaRedisCacheConfig } from "./types";
|
|
2
|
-
|
|
3
|
-
* Основная функция расширения Prisma для управления кешированием с использованием Redis.
|
|
4
|
-
* @param config - Конфигурация для кеша Redis и TTL по умолчанию.
|
|
5
|
-
* @returns Prisma расширение.
|
|
6
|
-
*/
|
|
7
|
-
declare const _default: ({ cache, debug, ttl: defaultTTL }: PrismaRedisCacheConfig) => (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/library").InternalArgs<{}, {
|
|
2
|
+
declare const _default: ({ cache }: PrismaRedisCacheConfig) => (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/library").InternalArgs<{}, {
|
|
8
3
|
$allModels: ModelExtension;
|
|
9
4
|
}, {}, {
|
|
10
|
-
$cache: import("cache-manager
|
|
5
|
+
$cache: import("cache-manager").Cache;
|
|
11
6
|
}>>;
|
|
12
7
|
export default _default;
|
package/dist/index.js
CHANGED
|
@@ -1,286 +1,85 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const types_1 = require("./types");
|
|
4
|
-
const
|
|
5
|
-
const library_1 = require("@prisma/client/runtime/library");
|
|
4
|
+
const object_code_1 = require("object-code");
|
|
6
5
|
const extension_1 = require("@prisma/client/extension");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Сериализация данных
|
|
59
|
-
*/
|
|
60
|
-
function serialize(data) {
|
|
61
|
-
return (0, msgpack_lite_1.encode)(data, { codec });
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Десериализация данных
|
|
65
|
-
*/
|
|
66
|
-
function deserialize(buffer) {
|
|
67
|
-
return (0, msgpack_lite_1.decode)(buffer, { codec });
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Обрабатывает удаление ключей из кеша после операций записи.
|
|
71
|
-
* @param cache - Кеш-менеджер.
|
|
72
|
-
* @param uncacheOption - Опции удаления кеша.
|
|
73
|
-
* @param result - Результат операции.
|
|
74
|
-
* @returns Promise<boolean> указывающий на успешность удаления.
|
|
75
|
-
*/
|
|
76
|
-
async function processUncache(cache, uncacheOption, result) {
|
|
77
|
-
let keysToDelete = [];
|
|
78
|
-
// 1) Если uncacheOption — функция, она может вернуть ключ(и) для удаления.
|
|
79
|
-
if (typeof uncacheOption === "function") {
|
|
80
|
-
const keys = uncacheOption(result);
|
|
81
|
-
keysToDelete = Array.isArray(keys) ? keys : [keys];
|
|
82
|
-
}
|
|
83
|
-
// 2) Если строка — просто один ключ.
|
|
84
|
-
else if (typeof uncacheOption === "string") {
|
|
85
|
-
keysToDelete = [uncacheOption];
|
|
86
|
-
}
|
|
87
|
-
// 3) Если массив:
|
|
88
|
-
else if (Array.isArray(uncacheOption)) {
|
|
89
|
-
// 3a) Простой массив строк.
|
|
90
|
-
if (typeof uncacheOption[0] === "string") {
|
|
91
|
-
keysToDelete = uncacheOption;
|
|
92
|
-
}
|
|
93
|
-
// 3b) Массив объектов { namespace, key }.
|
|
94
|
-
else if (typeof uncacheOption[0] === "object") {
|
|
95
|
-
keysToDelete = uncacheOption.map((obj) => obj.namespace ? `${obj.namespace}:${obj.key}` : obj.key);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
if (keysToDelete.length === 0)
|
|
99
|
-
return true;
|
|
100
|
-
try {
|
|
101
|
-
await cache.store.mdel(...keysToDelete);
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
catch (error) {
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Определяет, следует ли использовать кеширование для текущей операции.
|
|
110
|
-
* @param cacheOption - Опции кеширования.
|
|
111
|
-
* @returns boolean указывающий, использовать ли кеш.
|
|
112
|
-
*/
|
|
113
|
-
function shouldUseCache(cacheOption) {
|
|
114
|
-
return (cacheOption !== undefined &&
|
|
115
|
-
["boolean", "object", "number", "string"].includes(typeof cacheOption));
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Определяет, следует ли использовать удаление из кеша для текущей операции.
|
|
119
|
-
* @param uncacheOption - Опции удаления кеша.
|
|
120
|
-
* @returns boolean указывающий, использовать ли удаление кеша.
|
|
121
|
-
*/
|
|
122
|
-
function shouldUseUncache(uncacheOption) {
|
|
123
|
-
return (uncacheOption !== undefined &&
|
|
124
|
-
(typeof uncacheOption === "function" ||
|
|
125
|
-
typeof uncacheOption === "string" ||
|
|
126
|
-
Array.isArray(uncacheOption)));
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Основная функция расширения Prisma для управления кешированием с использованием Redis.
|
|
130
|
-
* @param config - Конфигурация для кеша Redis и TTL по умолчанию.
|
|
131
|
-
* @returns Prisma расширение.
|
|
132
|
-
*/
|
|
133
|
-
exports.default = ({ cache, debug, ttl: defaultTTL }) => {
|
|
134
|
-
return extension_1.Prisma.defineExtension({
|
|
135
|
-
name: "prisma-extension-cache-manager",
|
|
136
|
-
client: {
|
|
137
|
-
// Делаем кеш доступным в клиенте через $cache
|
|
138
|
-
$cache: cache,
|
|
139
|
-
},
|
|
140
|
-
model: {
|
|
141
|
-
$allModels: {},
|
|
142
|
-
},
|
|
143
|
-
query: {
|
|
144
|
-
$allModels: {
|
|
145
|
-
/**
|
|
146
|
-
* Обрабатывает все операции моделей, добавляя логику кеширования.
|
|
147
|
-
* @param params - Параметры операции.
|
|
148
|
-
* @returns Результат операции, возможно из кеша.
|
|
149
|
-
*/
|
|
150
|
-
async $allOperations({ model, operation, args, query }) {
|
|
151
|
-
// Проверяем, относится ли операция к кешируемым
|
|
152
|
-
if (!types_1.CACHE_OPERATIONS.includes(operation)) {
|
|
153
|
-
return query(args);
|
|
154
|
-
}
|
|
155
|
-
// Операции, при которых данные в БД меняются
|
|
156
|
-
const isWriteOperation = [
|
|
157
|
-
"create",
|
|
158
|
-
"createMany",
|
|
159
|
-
"updateMany",
|
|
160
|
-
"upsert",
|
|
161
|
-
"update",
|
|
162
|
-
].includes(operation);
|
|
163
|
-
// Извлекаем специальные поля cache / uncache (если есть)
|
|
164
|
-
const { cache: cacheOption, uncache: uncacheOption, ...queryArgs } = args;
|
|
165
|
-
const useCache = shouldUseCache(cacheOption);
|
|
166
|
-
const useUncache = shouldUseUncache(uncacheOption);
|
|
167
|
-
// 1. Если кеш не нужен, просто выполняем запрос и,
|
|
168
|
-
// при необходимости, очищаем кеш (uncache).
|
|
169
|
-
if (!useCache) {
|
|
170
|
-
const result = await query(queryArgs);
|
|
171
|
-
if (useUncache) {
|
|
172
|
-
await processUncache(cache, uncacheOption, result);
|
|
173
|
-
}
|
|
174
|
-
return result;
|
|
175
|
-
}
|
|
176
|
-
// 2. Генерируем ключ кеша + TTL
|
|
177
|
-
let cacheKey;
|
|
178
|
-
let ttl;
|
|
179
|
-
// 2a) Простые варианты cacheOption (true, число, строка)
|
|
180
|
-
if (["boolean", "number", "string"].includes(typeof cacheOption)) {
|
|
181
|
-
cacheKey =
|
|
182
|
-
typeof cacheOption === "string"
|
|
183
|
-
? cacheOption // Если cacheOption — строка, используем её как ключ напрямую
|
|
184
|
-
: generateComposedKey({ model, queryArgs }); // Иначе генерируем ключ
|
|
185
|
-
// Если cacheOption — число, оно означает TTL
|
|
186
|
-
ttl = typeof cacheOption === "number" ? cacheOption : undefined;
|
|
187
|
-
}
|
|
188
|
-
// 2b) Если cacheOption — объект с key: function,
|
|
189
|
-
// нужно сначала сделать запрос к БД, чтобы функция могла сгенерировать ключ
|
|
190
|
-
else if (typeof cacheOption.key === "function") {
|
|
191
|
-
// Выполняем запрос к базе
|
|
192
|
-
const result = await query(queryArgs);
|
|
193
|
-
// Если нужно, удаляем из кеша
|
|
194
|
-
if (useUncache) {
|
|
195
|
-
await processUncache(cache, uncacheOption, result);
|
|
196
|
-
}
|
|
197
|
-
// Функция генерирует ключ на основе результатов
|
|
198
|
-
cacheKey = cacheOption.key(result);
|
|
199
|
-
ttl = cacheOption.ttl;
|
|
200
|
-
if (ttl === undefined) {
|
|
201
|
-
ttl = defaultTTL;
|
|
202
|
-
}
|
|
203
|
-
if (ttl) {
|
|
204
|
-
ttl = Math.ceil(ttl / 1000);
|
|
205
|
-
}
|
|
206
|
-
// Сохраняем результат в кеш
|
|
207
|
-
try {
|
|
208
|
-
const encoded = serialize(result);
|
|
209
|
-
await (ttl && ttl > 0
|
|
210
|
-
? cache.store.client.set(cacheKey, encoded, "EX", ttl)
|
|
211
|
-
: cache.store.client.set(cacheKey, encoded));
|
|
212
|
-
if (debug) {
|
|
213
|
-
console.log("Data cached with key (function):", cacheKey, "encoded:", encoded, "decoded:", result);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
catch (e) {
|
|
217
|
-
if (debug) {
|
|
218
|
-
console.error("Failed to set cache", e);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
return result;
|
|
222
|
-
}
|
|
223
|
-
// 2c) Иначе берём ключ/namespace/ttl из объекта
|
|
224
|
-
else {
|
|
225
|
-
cacheKey =
|
|
226
|
-
createKey(cacheOption.key, cacheOption.namespace) ||
|
|
227
|
-
generateComposedKey({ model, queryArgs });
|
|
228
|
-
ttl = cacheOption.ttl;
|
|
229
|
-
}
|
|
230
|
-
// 3. Если это операция чтения, пробуем вернуть данные из кеша
|
|
231
|
-
if (!isWriteOperation) {
|
|
232
|
-
try {
|
|
233
|
-
// Используем getBuffer, т.к. сохраняем бинарные данные
|
|
234
|
-
const cached = await cache.store.client.getBuffer(cacheKey);
|
|
235
|
-
if (cached) {
|
|
236
|
-
const data = deserialize(cached);
|
|
237
|
-
if (debug) {
|
|
238
|
-
console.log("Cache hit for key:", cacheKey, "data", cached, "decoded", data);
|
|
239
|
-
}
|
|
240
|
-
return data;
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
if (debug) {
|
|
244
|
-
console.log("Cache miss for key:", cacheKey);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
catch (e) {
|
|
249
|
-
if (debug) {
|
|
250
|
-
console.error("Failed to get cache", e);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
// 4. Выполняем запрос к БД (операция чтения или записи)
|
|
255
|
-
const result = await query(queryArgs);
|
|
256
|
-
// 5. Если нужно, удаляем ключи из кеша
|
|
257
|
-
if (useUncache) {
|
|
258
|
-
await processUncache(cache, uncacheOption, result);
|
|
259
|
-
}
|
|
260
|
-
if (ttl === undefined) {
|
|
261
|
-
ttl = defaultTTL;
|
|
262
|
-
}
|
|
263
|
-
if (ttl) {
|
|
264
|
-
ttl = Math.ceil(ttl / 1000);
|
|
265
|
-
}
|
|
266
|
-
// 6. Сохраняем результат запроса в кеш
|
|
6
|
+
/* helpers -------------------------------------------------------------- */
|
|
7
|
+
const hashKey = (m, a) => `${m}@${(0, object_code_1.hash)(a)}`;
|
|
8
|
+
const ns = (k, n) => (n ? `${n}:${k}` : k);
|
|
9
|
+
const WRITE_OPS = new Set([
|
|
10
|
+
"create",
|
|
11
|
+
"createMany",
|
|
12
|
+
"updateMany",
|
|
13
|
+
"upsert",
|
|
14
|
+
"update",
|
|
15
|
+
]);
|
|
16
|
+
const toUncache = (opt, res) => !opt
|
|
17
|
+
? []
|
|
18
|
+
: typeof opt === "function"
|
|
19
|
+
? Array(opt(res))
|
|
20
|
+
: typeof opt === "string"
|
|
21
|
+
? [opt]
|
|
22
|
+
: opt.map((o) => typeof o === "string" ? o : ns(o.key, o.namespace));
|
|
23
|
+
/* extension ------------------------------------------------------------ */
|
|
24
|
+
exports.default = ({ cache }) => extension_1.Prisma.defineExtension({
|
|
25
|
+
name: "prisma-extension-cache-manager",
|
|
26
|
+
client: { $cache: cache },
|
|
27
|
+
model: { $allModels: {} },
|
|
28
|
+
query: {
|
|
29
|
+
$allModels: {
|
|
30
|
+
async $allOperations({ model, operation, args, query }) {
|
|
31
|
+
if (!types_1.CACHE_OPERATIONS.includes(operation))
|
|
32
|
+
return query(args);
|
|
33
|
+
const { cache: cOpt, uncache, ...queryArgs } = args;
|
|
34
|
+
const finish = async (res) => {
|
|
35
|
+
const dels = toUncache(uncache, res);
|
|
36
|
+
if (dels.length)
|
|
37
|
+
cache.mdel(dels).catch(() => { });
|
|
38
|
+
return res;
|
|
39
|
+
};
|
|
40
|
+
if (cOpt === undefined)
|
|
41
|
+
return finish(await query(queryArgs));
|
|
42
|
+
/* key + ttl */
|
|
43
|
+
let key;
|
|
44
|
+
let ttl;
|
|
45
|
+
if (typeof cOpt !== "object" || Array.isArray(cOpt)) {
|
|
46
|
+
key = typeof cOpt === "string" ? cOpt : hashKey(model, queryArgs);
|
|
47
|
+
if (typeof cOpt === "number")
|
|
48
|
+
ttl = cOpt;
|
|
49
|
+
}
|
|
50
|
+
else if (typeof cOpt.key === "function") {
|
|
51
|
+
const res = await query(queryArgs);
|
|
52
|
+
await finish(res);
|
|
53
|
+
key = cOpt.key(res);
|
|
54
|
+
ttl = cOpt.ttl; // как есть
|
|
267
55
|
try {
|
|
268
|
-
|
|
269
|
-
await (ttl && ttl > 0
|
|
270
|
-
? cache.store.client.set(cacheKey, encoded, "EX", ttl)
|
|
271
|
-
: cache.store.client.set(cacheKey, encoded));
|
|
272
|
-
if (debug) {
|
|
273
|
-
console.log("Data cached with key:", cacheKey, "encoded:", encoded, "decoded:", result);
|
|
274
|
-
}
|
|
56
|
+
await cache.set(key, res, ttl);
|
|
275
57
|
}
|
|
276
|
-
catch
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
58
|
+
catch { }
|
|
59
|
+
return res;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
key = ns(cOpt.key ?? hashKey(model, queryArgs), cOpt.namespace);
|
|
63
|
+
ttl = cOpt.ttl;
|
|
64
|
+
}
|
|
65
|
+
/* try cache for reads */
|
|
66
|
+
if (!WRITE_OPS.has(operation)) {
|
|
67
|
+
try {
|
|
68
|
+
const hit = await cache.get(key);
|
|
69
|
+
if (hit != null)
|
|
70
|
+
return hit;
|
|
280
71
|
}
|
|
281
|
-
|
|
282
|
-
}
|
|
72
|
+
catch { }
|
|
73
|
+
}
|
|
74
|
+
/* DB round-trip */
|
|
75
|
+
const res = await query(queryArgs);
|
|
76
|
+
await finish(res);
|
|
77
|
+
try {
|
|
78
|
+
await cache.set(key, res, ttl);
|
|
79
|
+
}
|
|
80
|
+
catch { }
|
|
81
|
+
return res;
|
|
283
82
|
},
|
|
284
83
|
},
|
|
285
|
-
}
|
|
286
|
-
};
|
|
84
|
+
},
|
|
85
|
+
});
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Prisma } from "@prisma/client/extension";
|
|
2
|
-
import {
|
|
2
|
+
import { Cache } from "cache-manager";
|
|
3
3
|
export declare const REQUIRED_ARGS_OPERATIONS: readonly ["delete", "findUnique", "findUniqueOrThrow", "aggregate", "groupBy", "update", "upsert", "create", "createMany", "updateMany"];
|
|
4
4
|
export declare const OPTIONAL_ARGS_OPERATIONS: readonly ["findMany", "findFirst", "findFirstOrThrow", "count"];
|
|
5
5
|
export declare const CACHE_OPERATIONS: readonly ["delete", "findUnique", "findUniqueOrThrow", "aggregate", "groupBy", "update", "upsert", "create", "createMany", "updateMany", "findMany", "findFirst", "findFirstOrThrow", "count"];
|
|
@@ -34,7 +34,7 @@ export interface PrismaCacheArgs<T, A, O extends RequiredArgsOperation | Optiona
|
|
|
34
34
|
}[];
|
|
35
35
|
}
|
|
36
36
|
export interface PrismaRedisCacheConfig {
|
|
37
|
-
cache:
|
|
37
|
+
cache: Cache;
|
|
38
38
|
debug?: boolean;
|
|
39
39
|
ttl?: number;
|
|
40
40
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knaus94/prisma-extension-cache-manager",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/knaus94/prisma-extension-cache-manager.git"
|
|
@@ -40,11 +40,10 @@
|
|
|
40
40
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@types/
|
|
44
|
-
"
|
|
45
|
-
"prettier": "^3.1.1",
|
|
43
|
+
"@types/node": "^22.15.18",
|
|
44
|
+
"prettier": "^3.5.3",
|
|
46
45
|
"rimraf": "^5.0.5",
|
|
47
|
-
"typescript": "^5.
|
|
46
|
+
"typescript": "^5.8.3"
|
|
48
47
|
},
|
|
49
48
|
"publishConfig": {
|
|
50
49
|
"access": "public"
|
|
@@ -55,14 +54,12 @@
|
|
|
55
54
|
]
|
|
56
55
|
},
|
|
57
56
|
"dependencies": {
|
|
58
|
-
"@prisma/client": "^
|
|
59
|
-
"cache-manager": "^
|
|
60
|
-
"
|
|
61
|
-
"msgpack-lite": "^0.1.26"
|
|
57
|
+
"@prisma/client": "^6.8.2",
|
|
58
|
+
"cache-manager": "^6.4.3",
|
|
59
|
+
"object-code": "^1.3.3"
|
|
62
60
|
},
|
|
63
61
|
"peerDependencies": {
|
|
64
|
-
"@nestjs/cache-manager": "^
|
|
65
|
-
"cache-manager": "^
|
|
66
|
-
"cache-manager-ioredis-yet": "^2.1.1"
|
|
62
|
+
"@nestjs/cache-manager": "^3.0.1",
|
|
63
|
+
"cache-manager": "^6.4.3"
|
|
67
64
|
}
|
|
68
65
|
}
|