@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 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-ioredis-yet").RedisCache;
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 crypto_1 = require("crypto");
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
- const msgpack_lite_1 = require("msgpack-lite");
8
- /**
9
- * Создаем codec
10
- * В дальнейшем используем его encode/decode для сериализации/десериализации.
11
- */
12
- const codec = (0, msgpack_lite_1.createCodec)();
13
- const TYPE_DECIMAL = 0x3F;
14
- const TYPE_DATE = 0x0d;
15
- /**
16
- * Регистрируем тип `Decimal`.
17
- * - При кодировании превращаем `Decimal` в строку.
18
- * - При декодировании восстанавливаем обратно в `Decimal`.
19
- */
20
- codec.addExtPacker(TYPE_DECIMAL, library_1.Decimal, (decimal) => {
21
- return (0, msgpack_lite_1.encode)(decimal.toString());
22
- });
23
- codec.addExtUnpacker(TYPE_DECIMAL, (buffer) => {
24
- return new library_1.Decimal((0, msgpack_lite_1.decode)(buffer));
25
- });
26
- /**
27
- * Регистрируем тип `Date`.
28
- * - При кодировании превращаем `Date` в строку.
29
- * - При декодировании восстанавливаем обратно в `Date`.
30
- */
31
- codec.addExtPacker(TYPE_DATE, Date, (date) => {
32
- return (0, msgpack_lite_1.encode)(date.toISOString());
33
- });
34
- codec.addExtUnpacker(TYPE_DATE, (buffer) => {
35
- return new Date((0, msgpack_lite_1.decode)(buffer));
36
- });
37
- /**
38
- * Генерирует уникальный ключ для кеширования на основе модели и аргументов запроса.
39
- * @param options - Опции для генерации ключа.
40
- * @returns Сгенерированный ключ.
41
- */
42
- function generateComposedKey(options) {
43
- const hash = (0, crypto_1.createHash)("md5")
44
- .update(JSON.stringify(options.queryArgs))
45
- .digest("hex");
46
- return `${options.model}@${hash}`;
47
- }
48
- /**
49
- * Создаёт ключ с опциональным пространством имен.
50
- * @param key - Основной ключ.
51
- * @param namespace - Пространство имен.
52
- * @returns Полный ключ с пространством имен, если оно указано.
53
- */
54
- function createKey(key, namespace) {
55
- return namespace ? `${namespace}:${key}` : key;
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
- const encoded = serialize(result);
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 (e) {
277
- if (debug) {
278
- console.error("Failed to set cache", e);
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
- return result;
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 { RedisCache } from "cache-manager-ioredis-yet";
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: RedisCache;
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": "1.5.72",
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/msgpack-lite": "^0.1.11",
44
- "@types/node": "^20.10.6",
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.3.3"
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": "^5.7.1",
59
- "cache-manager": "^5.2.3",
60
- "cache-manager-ioredis-yet": "^2.1.1",
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": "^1.0.0",
65
- "cache-manager": "^5.2.0",
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
  }