@knaus94/prisma-extension-cache-manager 1.4.2 → 1.5.0

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/dist/index.d.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  import { ModelExtension, PrismaRedisCacheConfig } from "./types";
2
+ /**
3
+ * Основная функция расширения Prisma для управления кешированием с использованием Redis.
4
+ * @param config - Конфигурация для кеша Redis и TTL по умолчанию.
5
+ * @returns Prisma расширение.
6
+ */
2
7
  declare const _default: ({ cache, defaultTTL }: PrismaRedisCacheConfig) => (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/library").InternalArgs<{}, {
3
8
  $allModels: ModelExtension;
4
9
  }, {}, {
5
- $cache: import("cache-manager").Cache;
10
+ $cache: import("cache-manager-ioredis-yet").RedisCache;
6
11
  }>>;
7
12
  export default _default;
package/dist/index.js CHANGED
@@ -1,23 +1,124 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const extension_1 = require("@prisma/client/extension");
4
6
  const types_1 = require("./types");
5
7
  const crypto_1 = require("crypto");
8
+ const library_1 = require("@prisma/client/runtime/library");
9
+ const extension_1 = require("@prisma/client/extension");
10
+ const msgpack5_1 = __importDefault(require("msgpack5"));
11
+ /**
12
+ * Генерирует уникальный ключ для кеширования на основе модели и аргументов запроса.
13
+ * @param options - Опции для генерации ключа.
14
+ * @returns Сгенерированный ключ.
15
+ */
6
16
  function generateComposedKey(options) {
7
17
  const hash = (0, crypto_1.createHash)("md5")
8
- .update(JSON.stringify(options?.queryArgs))
18
+ .update(JSON.stringify(options.queryArgs))
9
19
  .digest("hex");
10
20
  return `${options.model}@${hash}`;
11
21
  }
22
+ /**
23
+ * Создаёт ключ с опциональным пространством имен.
24
+ * @param key - Основной ключ.
25
+ * @param namespace - Пространство имен.
26
+ * @returns Полный ключ с пространством имен, если оно указано.
27
+ */
12
28
  function createKey(key, namespace) {
13
29
  return namespace ? `${namespace}:${key}` : key;
14
30
  }
31
+ // Универсальная функция для сериализации данных
15
32
  function serializeData(data) {
16
- return JSON.stringify({ data });
33
+ if (data instanceof Date) {
34
+ return { __type: "Date", value: data.toISOString() };
35
+ }
36
+ else if (data instanceof library_1.Decimal) {
37
+ return { __type: "Decimal", value: data.toString() };
38
+ }
39
+ else if (Array.isArray(data)) {
40
+ return data.map(serializeData); // Рекурсивно обрабатываем массивы
41
+ }
42
+ else if (data !== null && typeof data === "object") {
43
+ return Object.fromEntries(Object.entries(data).map(([key, value]) => [key, serializeData(value)]));
44
+ }
45
+ return data; // Простые значения возвращаем как есть
46
+ }
47
+ // Универсальная функция для десериализации данных
48
+ function deserializeData(data) {
49
+ if (data && data.__type === "Date") {
50
+ return new Date(data.value);
51
+ }
52
+ else if (data && data.__type === "Decimal") {
53
+ return new library_1.Decimal(data.value);
54
+ }
55
+ else if (Array.isArray(data)) {
56
+ return data.map(deserializeData); // Рекурсивно обрабатываем массивы
57
+ }
58
+ else if (data !== null && typeof data === "object") {
59
+ return Object.fromEntries(Object.entries(data).map(([key, value]) => [key, deserializeData(value)]));
60
+ }
61
+ return data; // Простые значения возвращаем как есть
17
62
  }
18
- function deserializeData(serializedData) {
19
- return JSON.parse(serializedData).data;
63
+ /**
64
+ * Обрабатывает удаление ключей из кеша после операций записи.
65
+ * @param cache - Кеш-менеджер.
66
+ * @param uncacheOption - Опции удаления кеша.
67
+ * @param result - Результат операции.
68
+ * @returns Promise<boolean> указывающий на успешность удаления.
69
+ */
70
+ async function processUncache(cache, uncacheOption, result) {
71
+ let keysToDelete = [];
72
+ if (typeof uncacheOption === "function") {
73
+ const keys = uncacheOption(result);
74
+ keysToDelete = Array.isArray(keys) ? keys : [keys];
75
+ }
76
+ else if (typeof uncacheOption === "string") {
77
+ keysToDelete = [uncacheOption];
78
+ }
79
+ else if (Array.isArray(uncacheOption)) {
80
+ if (typeof uncacheOption[0] === "string") {
81
+ keysToDelete = uncacheOption;
82
+ }
83
+ else if (typeof uncacheOption[0] === "object") {
84
+ keysToDelete = uncacheOption.map((obj) => obj.namespace ? `${obj.namespace}:${obj.key}` : obj.key);
85
+ }
86
+ }
87
+ if (keysToDelete.length === 0)
88
+ return true;
89
+ try {
90
+ await cache.store.mdel(...keysToDelete);
91
+ return true;
92
+ }
93
+ catch (error) {
94
+ return false;
95
+ }
20
96
  }
97
+ /**
98
+ * Определяет, следует ли использовать кеширование для текущей операции.
99
+ * @param cacheOption - Опции кеширования.
100
+ * @returns boolean указывающий, использовать ли кеш.
101
+ */
102
+ function shouldUseCache(cacheOption) {
103
+ return (cacheOption !== undefined &&
104
+ ["boolean", "object", "number", "string"].includes(typeof cacheOption));
105
+ }
106
+ /**
107
+ * Определяет, следует ли использовать удаление из кеша для текущей операции.
108
+ * @param uncacheOption - Опции удаления кеша.
109
+ * @returns boolean указывающий, использовать ли удаление кеша.
110
+ */
111
+ function shouldUseUncache(uncacheOption) {
112
+ return (uncacheOption !== undefined &&
113
+ (typeof uncacheOption === "function" ||
114
+ typeof uncacheOption === "string" ||
115
+ Array.isArray(uncacheOption)));
116
+ }
117
+ /**
118
+ * Основная функция расширения Prisma для управления кешированием с использованием Redis.
119
+ * @param config - Конфигурация для кеша Redis и TTL по умолчанию.
120
+ * @returns Prisma расширение.
121
+ */
21
122
  exports.default = ({ cache, defaultTTL }) => {
22
123
  return extension_1.Prisma.defineExtension({
23
124
  name: "prisma-extension-cache-manager",
@@ -29,9 +130,16 @@ exports.default = ({ cache, defaultTTL }) => {
29
130
  },
30
131
  query: {
31
132
  $allModels: {
133
+ /**
134
+ * Обрабатывает все операции моделей, добавляя логику кеширования.
135
+ * @param params - Параметры операции.
136
+ * @returns Результат операции, возможно из кеша.
137
+ */
32
138
  async $allOperations({ model, operation, args, query }) {
33
- if (!types_1.CACHE_OPERATIONS.includes(operation))
139
+ // Проверяем, относится ли операция к кешируемым
140
+ if (!types_1.CACHE_OPERATIONS.includes(operation)) {
34
141
  return query(args);
142
+ }
35
143
  const isWriteOperation = [
36
144
  "create",
37
145
  "createMany",
@@ -40,88 +148,63 @@ exports.default = ({ cache, defaultTTL }) => {
40
148
  "update",
41
149
  ].includes(operation);
42
150
  const { cache: cacheOption, uncache: uncacheOption, ...queryArgs } = args;
43
- function processUncache(result) {
44
- const option = uncacheOption;
45
- let keysToDelete = [];
46
- if (typeof option === "function") {
47
- const keys = option(result);
48
- keysToDelete = Array.isArray(keys) ? keys : [keys];
49
- }
50
- else if (typeof option === "string") {
51
- keysToDelete = [option];
52
- }
53
- else if (Array.isArray(option)) {
54
- if (typeof option[0] === "string") {
55
- keysToDelete = option;
56
- }
57
- else if (typeof option[0] === "object") {
58
- keysToDelete = option.map((obj) => obj.namespace ? `${obj.namespace}:${obj.key}` : obj.key);
59
- }
60
- }
61
- if (!keysToDelete.length)
62
- return true;
63
- return cache.store
64
- .mdel(...keysToDelete)
65
- .then(() => true)
66
- .catch(() => false);
67
- }
68
- const useCache = cacheOption !== undefined &&
69
- ["boolean", "object", "number", "string"].includes(typeof cacheOption);
70
- const useUncache = uncacheOption !== undefined &&
71
- (typeof uncacheOption === "function" ||
72
- typeof uncacheOption === "string" ||
73
- Array.isArray(uncacheOption));
151
+ const useCache = shouldUseCache(cacheOption);
152
+ const useUncache = shouldUseUncache(uncacheOption);
153
+ // Если не используем кеш, просто выполняем запрос и обрабатываем удаление кеша, если требуется
74
154
  if (!useCache) {
75
155
  const result = await query(queryArgs);
76
- if (useUncache)
77
- processUncache(result);
156
+ if (useUncache) {
157
+ await processUncache(cache, uncacheOption, result);
158
+ }
78
159
  return result;
79
160
  }
161
+ // Генерация ключа кеша
162
+ let cacheKey;
163
+ let ttl;
80
164
  if (["boolean", "number", "string"].includes(typeof cacheOption)) {
81
- const cacheKey = typeof cacheOption === "string"
82
- ? cacheOption
83
- : generateComposedKey({
84
- model,
85
- queryArgs,
86
- });
87
- if (!isWriteOperation) {
88
- const cached = await cache.get(cacheKey);
89
- if (cached) {
90
- return deserializeData(cached);
91
- }
92
- }
93
- const result = await query(queryArgs);
94
- if (useUncache)
95
- processUncache(result);
96
- const ttl = typeof cacheOption === "number"
97
- ? cacheOption
98
- : defaultTTL ?? undefined;
99
- await cache.set(cacheKey, serializeData(result), ttl);
100
- return result;
165
+ cacheKey =
166
+ typeof cacheOption === "string"
167
+ ? cacheOption
168
+ : generateComposedKey({ model, queryArgs });
169
+ ttl = typeof cacheOption === "number" ? cacheOption : defaultTTL;
101
170
  }
102
- if (typeof cacheOption.key === "function") {
171
+ else if (typeof cacheOption.key === "function") {
103
172
  const result = await query(queryArgs);
104
- if (useUncache)
105
- processUncache(result);
106
- const customCacheKey = cacheOption.key(result);
107
- await cache.set(customCacheKey, serializeData(result), cacheOption.ttl ?? defaultTTL);
173
+ if (useUncache) {
174
+ await processUncache(cache, uncacheOption, result);
175
+ }
176
+ cacheKey = cacheOption.key(result);
177
+ ttl = cacheOption.ttl ?? defaultTTL;
178
+ await cache.set(cacheKey, msgpack5_1.default.encode(serializeData(result)), ttl);
108
179
  return result;
109
180
  }
110
- const customCacheKey = createKey(cacheOption.key, cacheOption.namespace) ||
111
- generateComposedKey({
112
- model,
113
- queryArgs,
114
- });
181
+ else {
182
+ cacheKey =
183
+ createKey(cacheOption.key, cacheOption.namespace) ||
184
+ generateComposedKey({ model, queryArgs });
185
+ ttl = cacheOption.ttl ?? defaultTTL;
186
+ }
187
+ // Для операций чтения пытаемся получить данные из кеша
115
188
  if (!isWriteOperation) {
116
- const cached = await cache.get(customCacheKey);
117
- if (cached) {
118
- return deserializeData(cached);
189
+ try {
190
+ const cached = await cache.store.client.getBuffer(cacheKey);
191
+ if (cached) {
192
+ return deserializeData(msgpack5_1.default.decode(cached));
193
+ }
119
194
  }
195
+ catch { }
120
196
  }
197
+ // Выполняем запрос к базе данных
121
198
  const result = await query(queryArgs);
122
- if (useUncache)
123
- processUncache(result);
124
- await cache.set(customCacheKey, serializeData(result), cacheOption.ttl ?? defaultTTL);
199
+ // Обрабатываем удаление кеша, если требуется
200
+ if (useUncache) {
201
+ await processUncache(cache, uncacheOption, result);
202
+ }
203
+ // Сохраняем результат в кеш
204
+ try {
205
+ await cache.set(cacheKey, msgpack5_1.default.encode(serializeData(result)), ttl);
206
+ }
207
+ catch { }
125
208
  return result;
126
209
  },
127
210
  },
package/dist/types.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Prisma } from "@prisma/client/extension";
2
- import { Cache } from "cache-manager";
3
- export declare const REQUIRED_ARGS_OPERATIONS: readonly ["delete", "findUnique", "findUniqueOrThrow", "groupBy", "update", "upsert", "create", "createMany", "updateMany"];
2
+ import { RedisCache } from "cache-manager-ioredis-yet";
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
- export declare const CACHE_OPERATIONS: readonly ["delete", "findUnique", "findUniqueOrThrow", "groupBy", "update", "upsert", "create", "createMany", "updateMany", "findMany", "findFirst", "findFirstOrThrow", "count"];
5
+ export declare const CACHE_OPERATIONS: readonly ["delete", "findUnique", "findUniqueOrThrow", "aggregate", "groupBy", "update", "upsert", "create", "createMany", "updateMany", "findMany", "findFirst", "findFirstOrThrow", "count"];
6
6
  type RequiredArgsOperation = (typeof REQUIRED_ARGS_OPERATIONS)[number];
7
7
  type OptionalArgsOperation = (typeof OPTIONAL_ARGS_OPERATIONS)[number];
8
8
  type RequiredArgsFunction<O extends RequiredArgsOperation> = <T, A>(this: T, args: Prisma.Exact<A, Prisma.Args<T, O> & PrismaCacheArgs<T, A, O>>) => Prisma.PrismaPromise<Prisma.Result<T, A, O>>;
@@ -34,7 +34,7 @@ export interface PrismaCacheArgs<T, A, O extends RequiredArgsOperation | Optiona
34
34
  }[];
35
35
  }
36
36
  export interface PrismaRedisCacheConfig {
37
- cache: Cache;
37
+ cache: RedisCache;
38
38
  defaultTTL?: number;
39
39
  }
40
40
  export {};
package/dist/types.js CHANGED
@@ -5,6 +5,7 @@ exports.REQUIRED_ARGS_OPERATIONS = [
5
5
  "delete",
6
6
  "findUnique",
7
7
  "findUniqueOrThrow",
8
+ "aggregate",
8
9
  "groupBy",
9
10
  "update",
10
11
  "upsert",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knaus94/prisma-extension-cache-manager",
3
- "version": "1.4.2",
3
+ "version": "1.5.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/knaus94/prisma-extension-cache-manager.git"
@@ -55,6 +55,8 @@
55
55
  },
56
56
  "dependencies": {
57
57
  "@prisma/client": "^5.7.1",
58
- "cache-manager": "^5.2.1"
58
+ "cache-manager": "^5.2.3",
59
+ "cache-manager-ioredis-yet": "^2.1.1",
60
+ "msgpack5": "^6.0.2"
59
61
  }
60
62
  }