@knaus94/prisma-extension-cache-manager 2.0.0 → 2.0.2
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 +1 -4
- package/dist/index.js +52 -149
- package/dist/types.d.ts +0 -2
- package/package.json +3 -2
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,8 +1,5 @@
|
|
|
1
1
|
import { ModelExtension, PrismaRedisCacheConfig } from "./types";
|
|
2
|
-
|
|
3
|
-
* Prisma extension that adds transparent Redis caching.
|
|
4
|
-
*/
|
|
5
|
-
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<{}, {
|
|
6
3
|
$allModels: ModelExtension;
|
|
7
4
|
}, {}, {
|
|
8
5
|
$cache: import("cache-manager").Cache;
|
package/dist/index.js
CHANGED
|
@@ -1,167 +1,70 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const object_code_1 = require("object-code");
|
|
3
4
|
const types_1 = require("./types");
|
|
4
|
-
const crypto_1 = require("crypto");
|
|
5
5
|
const extension_1 = require("@prisma/client/extension");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
let keysToDelete = [];
|
|
26
|
-
if (typeof uncacheOption === "function") {
|
|
27
|
-
const keys = uncacheOption(result);
|
|
28
|
-
keysToDelete = Array.isArray(keys) ? keys : [keys];
|
|
29
|
-
}
|
|
30
|
-
else if (typeof uncacheOption === "string") {
|
|
31
|
-
keysToDelete = [uncacheOption];
|
|
32
|
-
}
|
|
33
|
-
else if (Array.isArray(uncacheOption)) {
|
|
34
|
-
if (typeof uncacheOption[0] === "string") {
|
|
35
|
-
keysToDelete = uncacheOption;
|
|
36
|
-
}
|
|
37
|
-
else if (typeof uncacheOption[0] === "object") {
|
|
38
|
-
keysToDelete = uncacheOption.map((obj) => obj.namespace ? `${obj.namespace}:${obj.key}` : obj.key);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
if (keysToDelete.length === 0)
|
|
42
|
-
return true;
|
|
43
|
-
try {
|
|
44
|
-
await cache.mdel(keysToDelete);
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Decide whether cache option is valid.
|
|
53
|
-
*/
|
|
54
|
-
const shouldUseCache = (opt) => opt !== undefined &&
|
|
55
|
-
["boolean", "object", "number", "string"].includes(typeof opt);
|
|
56
|
-
/**
|
|
57
|
-
* Decide whether uncache option is valid.
|
|
58
|
-
*/
|
|
59
|
-
const shouldUseUncache = (opt) => opt !== undefined &&
|
|
60
|
-
(typeof opt === "function" || typeof opt === "string" || Array.isArray(opt));
|
|
61
|
-
/**
|
|
62
|
-
* Prisma extension that adds transparent Redis caching.
|
|
63
|
-
*/
|
|
64
|
-
exports.default = ({ cache, debug, ttl: defaultTTL }) => extension_1.Prisma.defineExtension({
|
|
6
|
+
/* helpers -------------------------------------------------------------- */
|
|
7
|
+
const composedKey = (m, a) => `${m}@${(0, object_code_1.hash)(a)}`;
|
|
8
|
+
const ns = (k, n) => (n ? `${n}:${k}` : k);
|
|
9
|
+
const isWrite = (op) => ["create", "createMany", "updateMany", "upsert", "update"].includes(op);
|
|
10
|
+
/* uncache → массив строк */
|
|
11
|
+
const toUncache = (opt, res) => !opt
|
|
12
|
+
? []
|
|
13
|
+
: typeof opt === "function"
|
|
14
|
+
? [].concat(opt(res))
|
|
15
|
+
: typeof opt === "string"
|
|
16
|
+
? [opt]
|
|
17
|
+
: opt.map((o) => typeof o === "string" ? o : ns(o.key, o.namespace));
|
|
18
|
+
const processUncache = async (cache, opt, res) => {
|
|
19
|
+
const keys = toUncache(opt, res);
|
|
20
|
+
if (keys.length)
|
|
21
|
+
await cache.mdel(keys).catch(() => { });
|
|
22
|
+
};
|
|
23
|
+
/* extension ------------------------------------------------------------ */
|
|
24
|
+
exports.default = ({ cache }) => extension_1.Prisma.defineExtension({
|
|
65
25
|
name: "prisma-extension-cache-manager",
|
|
66
|
-
client: {
|
|
67
|
-
|
|
68
|
-
},
|
|
69
|
-
model: {
|
|
70
|
-
$allModels: {},
|
|
71
|
-
},
|
|
26
|
+
client: { $cache: cache },
|
|
27
|
+
model: { $allModels: {} },
|
|
72
28
|
query: {
|
|
73
29
|
$allModels: {
|
|
74
30
|
async $allOperations({ model, operation, args, query }) {
|
|
75
|
-
if (!types_1.CACHE_OPERATIONS.includes(operation))
|
|
31
|
+
if (!types_1.CACHE_OPERATIONS.includes(operation))
|
|
76
32
|
return query(args);
|
|
33
|
+
const { cache: cOpt, uncache, ...queryArgs } = args;
|
|
34
|
+
if (cOpt === undefined) {
|
|
35
|
+
const db = await query(queryArgs);
|
|
36
|
+
await processUncache(cache, uncache, db);
|
|
37
|
+
return db;
|
|
77
38
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"
|
|
84
|
-
|
|
85
|
-
const { cache: cacheOption, uncache: uncacheOption, ...queryArgs } = args;
|
|
86
|
-
const useCache = shouldUseCache(cacheOption);
|
|
87
|
-
const useUncache = shouldUseUncache(uncacheOption);
|
|
88
|
-
// --- No cache requested -----------------------------------------
|
|
89
|
-
if (!useCache) {
|
|
90
|
-
const res = await query(queryArgs);
|
|
91
|
-
if (useUncache)
|
|
92
|
-
await processUncache(cache, uncacheOption, res);
|
|
93
|
-
return res;
|
|
94
|
-
}
|
|
95
|
-
// --- Build key & ttl --------------------------------------------
|
|
96
|
-
let cacheKey;
|
|
97
|
-
let ttl;
|
|
98
|
-
if (["boolean", "number", "string"].includes(typeof cacheOption)) {
|
|
99
|
-
cacheKey =
|
|
100
|
-
typeof cacheOption === "string"
|
|
101
|
-
? cacheOption
|
|
102
|
-
: generateComposedKey({ model, queryArgs });
|
|
103
|
-
ttl = typeof cacheOption === "number" ? cacheOption : undefined;
|
|
39
|
+
let key;
|
|
40
|
+
let ttlMs;
|
|
41
|
+
if (typeof cOpt !== "object" || Array.isArray(cOpt)) {
|
|
42
|
+
key =
|
|
43
|
+
typeof cOpt === "string" ? cOpt : composedKey(model, queryArgs);
|
|
44
|
+
if (typeof cOpt === "number")
|
|
45
|
+
ttlMs = cOpt;
|
|
104
46
|
}
|
|
105
|
-
else if (typeof
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (ttl && ttl > 0) {
|
|
113
|
-
await cache.set(cacheKey, res, Math.ceil(ttl / 1000));
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
await cache.set(cacheKey, res);
|
|
117
|
-
}
|
|
118
|
-
if (debug)
|
|
119
|
-
console.log("Data cached (fn key):", cacheKey);
|
|
120
|
-
return res;
|
|
47
|
+
else if (typeof cOpt.key === "function") {
|
|
48
|
+
const db = await query(queryArgs);
|
|
49
|
+
await processUncache(cache, uncache, db);
|
|
50
|
+
key = cOpt.key(db);
|
|
51
|
+
ttlMs = cOpt.ttl ?? undefined;
|
|
52
|
+
await cache.set(key, db, Math.ceil((ttlMs ?? 0) / 1000));
|
|
53
|
+
return db;
|
|
121
54
|
}
|
|
122
55
|
else {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
generateComposedKey({ model, queryArgs });
|
|
126
|
-
ttl = cacheOption.ttl;
|
|
127
|
-
}
|
|
128
|
-
// --- Try cache for read ops -------------------------------------
|
|
129
|
-
if (!isWriteOp) {
|
|
130
|
-
try {
|
|
131
|
-
const cached = await cache.get(cacheKey);
|
|
132
|
-
if (cached !== undefined && cached !== null) {
|
|
133
|
-
if (debug)
|
|
134
|
-
console.log("Cache hit:", cacheKey);
|
|
135
|
-
return cached;
|
|
136
|
-
}
|
|
137
|
-
if (debug)
|
|
138
|
-
console.log("Cache miss:", cacheKey);
|
|
139
|
-
}
|
|
140
|
-
catch (e) {
|
|
141
|
-
if (debug)
|
|
142
|
-
console.error("Cache get failed", e);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
// --- Fallback to DB ---------------------------------------------
|
|
146
|
-
const res = await query(queryArgs);
|
|
147
|
-
if (useUncache)
|
|
148
|
-
await processUncache(cache, uncacheOption, res);
|
|
149
|
-
ttl = ttl ?? defaultTTL;
|
|
150
|
-
try {
|
|
151
|
-
if (ttl && ttl > 0) {
|
|
152
|
-
await cache.set(cacheKey, res, Math.ceil(ttl / 1000));
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
await cache.set(cacheKey, res);
|
|
156
|
-
}
|
|
157
|
-
if (debug)
|
|
158
|
-
console.log("Data cached:", cacheKey);
|
|
56
|
+
key = ns(cOpt.key ?? composedKey(model, queryArgs), cOpt.namespace);
|
|
57
|
+
ttlMs = cOpt.ttl ?? undefined;
|
|
159
58
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
59
|
+
if (!isWrite(operation)) {
|
|
60
|
+
const hit = await cache.get(key).catch(() => undefined);
|
|
61
|
+
if (hit !== undefined && hit !== null)
|
|
62
|
+
return hit;
|
|
163
63
|
}
|
|
164
|
-
|
|
64
|
+
const db = await query(queryArgs);
|
|
65
|
+
await processUncache(cache, uncache, db);
|
|
66
|
+
await cache.set(key, db, ttlMs !== undefined ? Math.ceil(ttlMs / 1000) : undefined);
|
|
67
|
+
return db;
|
|
165
68
|
},
|
|
166
69
|
},
|
|
167
70
|
},
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knaus94/prisma-extension-cache-manager",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/knaus94/prisma-extension-cache-manager.git"
|
|
@@ -55,7 +55,8 @@
|
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@prisma/client": "^6.8.2",
|
|
58
|
-
"cache-manager": "^6.4.3"
|
|
58
|
+
"cache-manager": "^6.4.3",
|
|
59
|
+
"object-code": "^1.3.3"
|
|
59
60
|
},
|
|
60
61
|
"peerDependencies": {
|
|
61
62
|
"@nestjs/cache-manager": "^3.0.1",
|