@keq-request/cache 5.0.0-alpha.22 → 5.0.0-alpha.23
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/CHANGELOG.md +19 -0
- package/dist/cache-entry/cache-entry.d.ts.map +1 -1
- package/dist/cache.d.ts +13 -4
- package/dist/cache.d.ts.map +1 -1
- package/dist/constants/index.d.ts +5 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/max-expired-at.d.ts +2 -0
- package/dist/constants/max-expired-at.d.ts.map +1 -0
- package/dist/exceptions/cache-exception.d.ts +5 -0
- package/dist/exceptions/cache-exception.d.ts.map +1 -0
- package/dist/exceptions/index.d.ts +2 -0
- package/dist/exceptions/index.d.ts.map +1 -0
- package/dist/index.d.ts +4 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +295 -224
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +295 -224
- package/dist/index.mjs.map +1 -1
- package/dist/request-cache-handler/index.d.ts +3 -0
- package/dist/request-cache-handler/index.d.ts.map +1 -0
- package/dist/request-cache-handler/request-cache-handler.d.ts +22 -0
- package/dist/request-cache-handler/request-cache-handler.d.ts.map +1 -0
- package/dist/request-cache-handler/types/index.d.ts +2 -0
- package/dist/request-cache-handler/types/index.d.ts.map +1 -0
- package/dist/{types/keq-cache-option.d.ts → request-cache-handler/types/request-cache-options.d.ts} +6 -4
- package/dist/request-cache-handler/types/request-cache-options.d.ts.map +1 -0
- package/dist/storage/index.d.ts +4 -4
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/indexed-db-storage/base-indexed-db-storage.d.ts +2 -4
- package/dist/storage/indexed-db-storage/base-indexed-db-storage.d.ts.map +1 -1
- package/dist/storage/indexed-db-storage/index.d.ts +3 -0
- package/dist/storage/indexed-db-storage/index.d.ts.map +1 -0
- package/dist/storage/indexed-db-storage/indexed-db-storage.d.ts.map +1 -1
- package/dist/storage/indexed-db-storage/types/index.d.ts +7 -0
- package/dist/storage/indexed-db-storage/types/index.d.ts.map +1 -0
- package/dist/storage/indexed-db-storage/types/indexed-db-storage-options.d.ts +1 -1
- package/dist/storage/indexed-db-storage/types/indexed-db-storage-options.d.ts.map +1 -1
- package/dist/storage/internal-storage/internal-storage.d.ts +1 -1
- package/dist/storage/internal-storage/internal-storage.d.ts.map +1 -1
- package/dist/storage/memory-storage/base-memory-storage.d.ts +5 -4
- package/dist/storage/memory-storage/base-memory-storage.d.ts.map +1 -1
- package/dist/storage/memory-storage/index.d.ts +3 -0
- package/dist/storage/memory-storage/index.d.ts.map +1 -0
- package/dist/storage/memory-storage/memory-storage.d.ts +1 -0
- package/dist/storage/memory-storage/memory-storage.d.ts.map +1 -1
- package/dist/storage/memory-storage/types/index.d.ts +3 -0
- package/dist/storage/memory-storage/types/index.d.ts.map +1 -0
- package/dist/storage/memory-storage/types/memory-storage-options.d.ts +1 -1
- package/dist/storage/memory-storage/types/memory-storage-options.d.ts.map +1 -1
- package/dist/storage/memory-storage/utils/humanize-size.d.ts +6 -0
- package/dist/storage/memory-storage/utils/humanize-size.d.ts.map +1 -0
- package/dist/storage/memory-storage/utils/index.d.ts +3 -0
- package/dist/storage/memory-storage/utils/index.d.ts.map +1 -0
- package/dist/storage/memory-storage/utils/serialize-response-body.d.ts +6 -0
- package/dist/storage/memory-storage/utils/serialize-response-body.d.ts.map +1 -0
- package/dist/storage/multi-tier-storage/index.d.ts +1 -0
- package/dist/storage/multi-tier-storage/index.d.ts.map +1 -1
- package/dist/storage/multi-tier-storage/types/index.d.ts +2 -0
- package/dist/storage/multi-tier-storage/types/index.d.ts.map +1 -0
- package/dist/storage/tier-storage/index.d.ts +2 -2
- package/dist/storage/tier-storage/index.d.ts.map +1 -1
- package/dist/storage/tier-storage/types/index.d.ts +2 -0
- package/dist/storage/tier-storage/types/index.d.ts.map +1 -0
- package/dist/strategies/cache-first.d.ts.map +1 -1
- package/dist/strategies/index.d.ts +5 -0
- package/dist/strategies/index.d.ts.map +1 -0
- package/dist/strategies/network-first.d.ts.map +1 -1
- package/dist/strategies/network-only.d.ts.map +1 -1
- package/dist/strategies/stale-while-revalidate.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/keq-cache-key.d.ts +2 -1
- package/dist/types/keq-cache-key.d.ts.map +1 -1
- package/dist/types/keq-cache-pattern.d.ts +3 -0
- package/dist/types/keq-cache-pattern.d.ts.map +1 -0
- package/dist/types/keq-cache-rule.d.ts +4 -4
- package/dist/types/keq-cache-rule.d.ts.map +1 -1
- package/dist/types/keq-cache-strategy.d.ts +2 -2
- package/dist/types/keq-cache-strategy.d.ts.map +1 -1
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/logger.d.ts +5 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/package.json +6 -11
- package/dist/strategies/utils/cache-context.d.ts +0 -5
- package/dist/strategies/utils/cache-context.d.ts.map +0 -1
- package/dist/strategies/utils/index.d.ts +0 -2
- package/dist/strategies/utils/index.d.ts.map +0 -1
- package/dist/types/keq-cache-option.d.ts.map +0 -1
- package/dist/types/keq-cache-parameters.d.ts +0 -12
- package/dist/types/keq-cache-parameters.d.ts.map +0 -1
- package/dist/types/strategies-options.d.ts +0 -14
- package/dist/types/strategies-options.d.ts.map +0 -1
- package/dist/utils/debug.d.ts +0 -2
- package/dist/utils/debug.d.ts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -3,45 +3,21 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
|
|
5
5
|
// src/cache.ts
|
|
6
|
+
import * as R2 from "ramda";
|
|
7
|
+
|
|
8
|
+
// src/request-cache-handler/request-cache-handler.ts
|
|
6
9
|
import * as R from "ramda";
|
|
10
|
+
|
|
11
|
+
// src/exceptions/cache-exception.ts
|
|
7
12
|
import { Exception } from "keq";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
let cOpt = ctx.options.cache;
|
|
17
|
-
const rule = rules.find((rule2) => {
|
|
18
|
-
if (rule2.pattern === void 0 || rule2.pattern === true) return true;
|
|
19
|
-
if (typeof rule2.pattern === "function") return rule2.pattern(ctx);
|
|
20
|
-
return rule2.pattern.test(ctx.request.__url__.href);
|
|
21
|
-
});
|
|
22
|
-
if (rule) cOpt = R.mergeRight(rule, cOpt || {});
|
|
23
|
-
if (!cOpt || R.isEmpty(cOpt)) {
|
|
24
|
-
await next();
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
let key = ctx.locationId;
|
|
28
|
-
if (cOpt.key) {
|
|
29
|
-
if (typeof cOpt.key === "function") key = cOpt.key(ctx);
|
|
30
|
-
else key = cOpt.key;
|
|
31
|
-
} else if (opts == null ? void 0 : opts.keyFactory) {
|
|
32
|
-
key = opts.keyFactory(ctx);
|
|
33
|
-
}
|
|
34
|
-
if (!key) throw new Exception("Cache key is required");
|
|
35
|
-
const strategy = cOpt.strategy;
|
|
36
|
-
const opt = {
|
|
37
|
-
key,
|
|
38
|
-
storage,
|
|
39
|
-
ttl: cOpt.ttl,
|
|
40
|
-
exclude: cOpt.exclude
|
|
41
|
-
};
|
|
42
|
-
await strategy(opt)(ctx, next);
|
|
43
|
-
};
|
|
44
|
-
}
|
|
13
|
+
var CacheException = class extends Exception {
|
|
14
|
+
constructor(message) {
|
|
15
|
+
super("[@keq-request/cache] ".concat(message));
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/cache-entry/cache-entry.ts
|
|
20
|
+
import { createProxyResponse } from "keq";
|
|
45
21
|
|
|
46
22
|
// src/utils/get-response-bytes.ts
|
|
47
23
|
async function getResponseBytes(response) {
|
|
@@ -53,6 +29,124 @@ async function getResponseBytes(response) {
|
|
|
53
29
|
return arrayBuffer.byteLength;
|
|
54
30
|
}
|
|
55
31
|
|
|
32
|
+
// src/constants/max-expired-at.ts
|
|
33
|
+
var MAX_EXPIRED_AT = /* @__PURE__ */ new Date(864e13);
|
|
34
|
+
|
|
35
|
+
// src/constants/eviction.enum.ts
|
|
36
|
+
var Eviction = /* @__PURE__ */ ((Eviction2) => {
|
|
37
|
+
Eviction2["LRU"] = "lru";
|
|
38
|
+
Eviction2["LFU"] = "lfu";
|
|
39
|
+
Eviction2["RANDOM"] = "random";
|
|
40
|
+
Eviction2["TTL"] = "ttl";
|
|
41
|
+
return Eviction2;
|
|
42
|
+
})(Eviction || {});
|
|
43
|
+
|
|
44
|
+
// src/constants/size.enum.ts
|
|
45
|
+
var Size = /* @__PURE__ */ ((Size2) => {
|
|
46
|
+
Size2[Size2["B"] = 1] = "B";
|
|
47
|
+
Size2[Size2["KB"] = 1024] = "KB";
|
|
48
|
+
Size2[Size2["MB"] = 1048576] = "MB";
|
|
49
|
+
Size2[Size2["GB"] = 1073741824] = "GB";
|
|
50
|
+
return Size2;
|
|
51
|
+
})(Size || {});
|
|
52
|
+
|
|
53
|
+
// src/strategies/cache-first.ts
|
|
54
|
+
var cacheFirst = async function cacheFirst2(handler, context, next) {
|
|
55
|
+
const [cacheKey, cacheValue] = await handler.getCache(context);
|
|
56
|
+
if (cacheValue) {
|
|
57
|
+
context.emitter.emit("cache:hit", { key: cacheKey, response: cacheValue.response, context });
|
|
58
|
+
context.res = cacheValue.response;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
context.emitter.emit("cache:miss", { key: cacheKey, context });
|
|
62
|
+
await next();
|
|
63
|
+
const [, entry] = await handler.setCache(context);
|
|
64
|
+
if (entry) {
|
|
65
|
+
context.emitter.emit("cache:update", {
|
|
66
|
+
key: entry.key,
|
|
67
|
+
oldResponse: void 0,
|
|
68
|
+
newResponse: entry.response,
|
|
69
|
+
context
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// src/strategies/network-first.ts
|
|
75
|
+
var networkFirst = async function networkFirst2(handler, context, next) {
|
|
76
|
+
try {
|
|
77
|
+
await next();
|
|
78
|
+
const [, cache2] = await handler.getCache(context);
|
|
79
|
+
const [, entry] = await handler.setCache(context);
|
|
80
|
+
if (entry) {
|
|
81
|
+
context.emitter.emit("cache:update", {
|
|
82
|
+
key: entry.key,
|
|
83
|
+
oldResponse: cache2 == null ? void 0 : cache2.response,
|
|
84
|
+
newResponse: entry.response,
|
|
85
|
+
context
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
} catch (err) {
|
|
89
|
+
const [key, cache2] = await handler.getCache(context);
|
|
90
|
+
if (!cache2) {
|
|
91
|
+
context.emitter.emit("cache:miss", { key, context });
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
context.emitter.emit("cache:hit", { key, response: cache2.response, context });
|
|
95
|
+
context.res = cache2.response;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// src/strategies/network-only.ts
|
|
100
|
+
var networkOnly = async function(handler, context, next) {
|
|
101
|
+
await next();
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// src/strategies/stale-while-revalidate.ts
|
|
105
|
+
var staleWhileRevalidate = async function(handler, context, next) {
|
|
106
|
+
const [key, cache2] = await handler.getCache(context);
|
|
107
|
+
if (cache2) {
|
|
108
|
+
context.emitter.emit("cache:hit", { key, response: cache2.response, context });
|
|
109
|
+
const orchestrator = context.orchestration.fork();
|
|
110
|
+
context.res = cache2.response;
|
|
111
|
+
setTimeout(async () => {
|
|
112
|
+
try {
|
|
113
|
+
await orchestrator.execute();
|
|
114
|
+
const context2 = orchestrator.context;
|
|
115
|
+
const [, entry] = await handler.setCache(context2);
|
|
116
|
+
if (entry) {
|
|
117
|
+
context2.emitter.emit("cache:update", {
|
|
118
|
+
key,
|
|
119
|
+
oldResponse: cache2.response,
|
|
120
|
+
newResponse: entry.response,
|
|
121
|
+
context: context2
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
} catch (err) {
|
|
125
|
+
}
|
|
126
|
+
}, 1);
|
|
127
|
+
} else {
|
|
128
|
+
context.emitter.emit("cache:miss", { key, context });
|
|
129
|
+
await next();
|
|
130
|
+
const [, entry] = await handler.setCache(context);
|
|
131
|
+
if (entry) {
|
|
132
|
+
context.emitter.emit("cache:update", {
|
|
133
|
+
key,
|
|
134
|
+
oldResponse: void 0,
|
|
135
|
+
newResponse: entry.response,
|
|
136
|
+
context
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// src/constants/strategy.enum.ts
|
|
143
|
+
var Strategy = {
|
|
144
|
+
STALE_WHILE_REVALIDATE: staleWhileRevalidate,
|
|
145
|
+
NETWORK_FIRST: networkFirst,
|
|
146
|
+
NETWORK_ONLY: networkOnly,
|
|
147
|
+
CACHE_FIRST: cacheFirst
|
|
148
|
+
};
|
|
149
|
+
|
|
56
150
|
// src/cache-entry/cache-entry.ts
|
|
57
151
|
var CacheEntry = class _CacheEntry {
|
|
58
152
|
constructor(options) {
|
|
@@ -66,13 +160,13 @@ var CacheEntry = class _CacheEntry {
|
|
|
66
160
|
__publicField(this, "expiredAt");
|
|
67
161
|
var _a;
|
|
68
162
|
this.key = options.key;
|
|
69
|
-
this.response = options.response;
|
|
163
|
+
this.response = createProxyResponse(options.response);
|
|
70
164
|
this.size = options.size;
|
|
71
|
-
this.expiredAt = (_a = options.expiredAt) != null ? _a :
|
|
165
|
+
this.expiredAt = (_a = options.expiredAt) != null ? _a : MAX_EXPIRED_AT;
|
|
72
166
|
}
|
|
73
167
|
static async build(options) {
|
|
74
168
|
var _a;
|
|
75
|
-
const expiredAt = "expiredAt" in options ? options.expiredAt : "ttl" in options && typeof options.ttl === "number" && options.ttl > 0 ? new Date(Date.now() + options.ttl * 1e3) :
|
|
169
|
+
const expiredAt = "expiredAt" in options ? options.expiredAt : "ttl" in options && typeof options.ttl === "number" && options.ttl > 0 ? new Date(Date.now() + options.ttl * 1e3) : MAX_EXPIRED_AT;
|
|
76
170
|
const response = options.response.clone();
|
|
77
171
|
return new _CacheEntry({
|
|
78
172
|
key: options.key,
|
|
@@ -98,164 +192,106 @@ var CacheEntry = class _CacheEntry {
|
|
|
98
192
|
}
|
|
99
193
|
};
|
|
100
194
|
|
|
101
|
-
// src/
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
195
|
+
// src/request-cache-handler/request-cache-handler.ts
|
|
196
|
+
var RequestCacheHandler = class {
|
|
197
|
+
constructor(storage, options) {
|
|
198
|
+
this.storage = storage;
|
|
199
|
+
this.options = options;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Get cache key for request
|
|
203
|
+
*/
|
|
204
|
+
getRequestCacheKey(context) {
|
|
205
|
+
const options = this.options;
|
|
206
|
+
if (typeof options.key === "string") return options.key;
|
|
207
|
+
else if (typeof options.key === "function") return options.key(context);
|
|
208
|
+
else if (R.isNil(options.key) && context.locationId) return context.locationId;
|
|
209
|
+
else throw new CacheException("Cannot resolve cache key");
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Get cache from storage
|
|
213
|
+
*/
|
|
214
|
+
async getCache(context) {
|
|
215
|
+
const key = this.getRequestCacheKey(context);
|
|
216
|
+
return [key, await this.storage.get(key)];
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Store response that in context to storage
|
|
220
|
+
*/
|
|
221
|
+
async setCache(context) {
|
|
222
|
+
const options = this.options;
|
|
223
|
+
const key = this.getRequestCacheKey(context);
|
|
224
|
+
if (!context.response) return [key, void 0];
|
|
225
|
+
if (options.exclude && await options.exclude(context.response)) return [key, void 0];
|
|
226
|
+
const entry = await CacheEntry.build({
|
|
227
|
+
key,
|
|
228
|
+
response: context.response,
|
|
229
|
+
ttl: options.ttl
|
|
230
|
+
});
|
|
231
|
+
void this.storage.set(entry);
|
|
232
|
+
return [key, entry];
|
|
233
|
+
}
|
|
234
|
+
};
|
|
115
235
|
|
|
116
|
-
// src/
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
context.res = cache2.response;
|
|
236
|
+
// src/cache.ts
|
|
237
|
+
function cache(options) {
|
|
238
|
+
const storage = options.storage;
|
|
239
|
+
const rules = (options == null ? void 0 : options.rules) || [];
|
|
240
|
+
return async function cache2(ctx, next) {
|
|
241
|
+
if (ctx.options.cache === false) {
|
|
242
|
+
await next();
|
|
124
243
|
return;
|
|
125
244
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
context
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
// src/strategies/network-first.ts
|
|
141
|
-
var networkFirst = function(opts) {
|
|
142
|
-
const { key, storage } = opts;
|
|
143
|
-
return async function(context, next) {
|
|
144
|
-
try {
|
|
245
|
+
let requestCacheOptions = ctx.options.cache;
|
|
246
|
+
const rule = rules.find((rule2) => {
|
|
247
|
+
if (rule2.pattern === void 0 || rule2.pattern === true) return true;
|
|
248
|
+
if (typeof rule2.pattern === "function") return rule2.pattern(ctx);
|
|
249
|
+
return rule2.pattern.test(ctx.request.__url__.href);
|
|
250
|
+
});
|
|
251
|
+
if (rule) requestCacheOptions = R2.mergeRight(rule, requestCacheOptions || {});
|
|
252
|
+
if (!requestCacheOptions || R2.isEmpty(requestCacheOptions)) {
|
|
145
253
|
await next();
|
|
146
|
-
|
|
147
|
-
const entry = await cacheContext(opts, context);
|
|
148
|
-
if (entry) {
|
|
149
|
-
context.emitter.emit("cache:update", {
|
|
150
|
-
key,
|
|
151
|
-
oldResponse: cache2 == null ? void 0 : cache2.response,
|
|
152
|
-
newResponse: entry.response,
|
|
153
|
-
context
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
} catch (err) {
|
|
157
|
-
const cache2 = await storage.get(key);
|
|
158
|
-
if (!cache2) {
|
|
159
|
-
context.emitter.emit("cache:miss", { key, context });
|
|
160
|
-
throw err;
|
|
161
|
-
}
|
|
162
|
-
context.emitter.emit("cache:hit", { key, response: cache2.response, context });
|
|
163
|
-
context.res = cache2.response;
|
|
254
|
+
return;
|
|
164
255
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// src/strategies/network-only.ts
|
|
169
|
-
var networkOnly = function(opts) {
|
|
170
|
-
return async function(ctx, next) {
|
|
171
|
-
await next();
|
|
172
|
-
};
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// src/strategies/stale-while-revalidate.ts
|
|
176
|
-
var staleWhileRevalidate = function(opts) {
|
|
177
|
-
const { key, storage } = opts;
|
|
178
|
-
return async function(context, next) {
|
|
179
|
-
const cache2 = await storage.get(key);
|
|
180
|
-
if (cache2) {
|
|
181
|
-
context.emitter.emit("cache:hit", { key, response: cache2.response, context });
|
|
182
|
-
const orchestrator = context.orchestration.fork();
|
|
183
|
-
context.res = cache2.response;
|
|
184
|
-
setTimeout(async () => {
|
|
185
|
-
try {
|
|
186
|
-
await orchestrator.execute();
|
|
187
|
-
const context2 = orchestrator.context;
|
|
188
|
-
const entry = await cacheContext(opts, context2);
|
|
189
|
-
if (entry) {
|
|
190
|
-
context2.emitter.emit("cache:update", {
|
|
191
|
-
key,
|
|
192
|
-
oldResponse: cache2.response,
|
|
193
|
-
newResponse: entry.response,
|
|
194
|
-
context: context2
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
} catch (err) {
|
|
198
|
-
}
|
|
199
|
-
}, 1);
|
|
200
|
-
} else {
|
|
201
|
-
context.emitter.emit("cache:miss", { key, context });
|
|
256
|
+
if (!requestCacheOptions.key) requestCacheOptions.key = options.keyFactory;
|
|
257
|
+
if (!ctx.locationId && !requestCacheOptions.key) {
|
|
258
|
+
console.warn("[@keq/cache] Warning: Cannot resolve Cache Key. Cache is skipped.");
|
|
202
259
|
await next();
|
|
203
|
-
|
|
204
|
-
if (entry) {
|
|
205
|
-
context.emitter.emit("cache:update", {
|
|
206
|
-
key,
|
|
207
|
-
oldResponse: void 0,
|
|
208
|
-
newResponse: entry.response,
|
|
209
|
-
context
|
|
210
|
-
});
|
|
211
|
-
}
|
|
260
|
+
return;
|
|
212
261
|
}
|
|
262
|
+
const handler = new RequestCacheHandler(storage, requestCacheOptions);
|
|
263
|
+
const strategy = requestCacheOptions.strategy;
|
|
264
|
+
await strategy(handler, ctx, next);
|
|
213
265
|
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// src/constants/strategy.enum.ts
|
|
217
|
-
var Strategy = {
|
|
218
|
-
STALE_WHILE_REVALIDATE: staleWhileRevalidate,
|
|
219
|
-
NETWORK_FIRST: networkFirst,
|
|
220
|
-
NETWORK_ONLY: networkOnly,
|
|
221
|
-
CACHE_FIRST: cacheFirst
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
// src/constants/eviction.enum.ts
|
|
225
|
-
var Eviction = /* @__PURE__ */ ((Eviction2) => {
|
|
226
|
-
Eviction2["LRU"] = "lru";
|
|
227
|
-
Eviction2["LFU"] = "lfu";
|
|
228
|
-
Eviction2["RANDOM"] = "random";
|
|
229
|
-
Eviction2["TTL"] = "ttl";
|
|
230
|
-
return Eviction2;
|
|
231
|
-
})(Eviction || {});
|
|
232
|
-
|
|
233
|
-
// src/constants/size.enum.ts
|
|
234
|
-
var Size = /* @__PURE__ */ ((Size2) => {
|
|
235
|
-
Size2[Size2["B"] = 1] = "B";
|
|
236
|
-
Size2[Size2["KB"] = 1024] = "KB";
|
|
237
|
-
Size2[Size2["MB"] = 1048576] = "MB";
|
|
238
|
-
Size2[Size2["GB"] = 1073741824] = "GB";
|
|
239
|
-
return Size2;
|
|
240
|
-
})(Size || {});
|
|
266
|
+
}
|
|
241
267
|
|
|
242
268
|
// src/storage/memory-storage/ttl-memory-storage.ts
|
|
243
269
|
import dayjs2 from "dayjs";
|
|
244
|
-
import * as
|
|
270
|
+
import * as R4 from "ramda";
|
|
245
271
|
|
|
246
272
|
// src/storage/memory-storage/base-memory-storage.ts
|
|
247
273
|
import dayjs from "dayjs";
|
|
248
|
-
import * as
|
|
249
|
-
|
|
250
|
-
// src/utils/debug.ts
|
|
251
|
-
function debug(...args) {
|
|
252
|
-
console.debug("[@keq-cache] [DEBUG] ", ...args);
|
|
253
|
-
}
|
|
274
|
+
import * as R3 from "ramda";
|
|
254
275
|
|
|
255
276
|
// src/storage/keq-cache-storage.ts
|
|
256
277
|
var KeqCacheStorage = class {
|
|
257
278
|
};
|
|
258
279
|
|
|
280
|
+
// src/utils/logger.ts
|
|
281
|
+
var Logger = class {
|
|
282
|
+
static debug(...args) {
|
|
283
|
+
console.debug("[@keq-cache] [DEBUG] ", ...args);
|
|
284
|
+
}
|
|
285
|
+
static error(...args) {
|
|
286
|
+
console.error("[@keq-cache] [ERROR] ", ...args);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// src/utils/random.ts
|
|
291
|
+
function random(min, max) {
|
|
292
|
+
return Math.floor(Math.random() * (max - min)) + min;
|
|
293
|
+
}
|
|
294
|
+
|
|
259
295
|
// src/storage/internal-storage/internal-storage.ts
|
|
260
296
|
var InternalStorage = class extends KeqCacheStorage {
|
|
261
297
|
constructor(options) {
|
|
@@ -270,7 +306,7 @@ var InternalStorage = class extends KeqCacheStorage {
|
|
|
270
306
|
__publicField(this, "__onCacheEvict__");
|
|
271
307
|
__publicField(this, "__onCacheExpired__");
|
|
272
308
|
if ((options == null ? void 0 : options.size) && (typeof (options == null ? void 0 : options.size) !== "number" || options.size <= 0)) {
|
|
273
|
-
throw
|
|
309
|
+
throw new CacheException("Invalid size: ".concat(String(options == null ? void 0 : options.size)));
|
|
274
310
|
}
|
|
275
311
|
this.__size__ = (_a = options == null ? void 0 : options.size) != null ? _a : Infinity;
|
|
276
312
|
this.__debug__ = !!(options == null ? void 0 : options.debug);
|
|
@@ -283,12 +319,38 @@ var InternalStorage = class extends KeqCacheStorage {
|
|
|
283
319
|
debug(fn) {
|
|
284
320
|
if (this.__debug__) {
|
|
285
321
|
fn((...args) => {
|
|
286
|
-
debug("[Storage(".concat(this.__id__, ")]"), ...args);
|
|
322
|
+
Logger.debug("[Storage(".concat(this.__id__, ")]"), ...args);
|
|
287
323
|
});
|
|
288
324
|
}
|
|
289
325
|
}
|
|
290
326
|
};
|
|
291
327
|
|
|
328
|
+
// src/storage/memory-storage/utils/humanize-size.ts
|
|
329
|
+
function humanizeSize(size) {
|
|
330
|
+
if (size < 1024) return "".concat(size, " B");
|
|
331
|
+
if (size < 1024 * 1024) return "".concat((size / 1024).toFixed(2), " KB");
|
|
332
|
+
if (size < 1024 * 1024 * 1024) return "".concat((size / (1024 * 1024)).toFixed(2), " MB");
|
|
333
|
+
return "".concat((size / (1024 * 1024 * 1024)).toFixed(2), " GB");
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/storage/memory-storage/utils/serialize-response-body.ts
|
|
337
|
+
async function serializeResponseBody(response) {
|
|
338
|
+
var _a;
|
|
339
|
+
const contentType = (_a = response.headers.get("content-type")) != null ? _a : "";
|
|
340
|
+
try {
|
|
341
|
+
if (contentType.includes("application/json")) {
|
|
342
|
+
const json = await response.json();
|
|
343
|
+
return JSON.stringify(json);
|
|
344
|
+
}
|
|
345
|
+
if (contentType.includes("text/") || contentType.includes("application/xml") || contentType.includes("application/javascript")) {
|
|
346
|
+
return await response.text();
|
|
347
|
+
}
|
|
348
|
+
return "[Binary or unsupported content]";
|
|
349
|
+
} catch {
|
|
350
|
+
return "[Unable to serialize]";
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
292
354
|
// src/storage/memory-storage/base-memory-storage.ts
|
|
293
355
|
var BaseMemoryStorage = class extends InternalStorage {
|
|
294
356
|
constructor() {
|
|
@@ -299,7 +361,7 @@ var BaseMemoryStorage = class extends InternalStorage {
|
|
|
299
361
|
__publicField(this, "lastEvictExpiredTime", dayjs());
|
|
300
362
|
}
|
|
301
363
|
get size() {
|
|
302
|
-
const used =
|
|
364
|
+
const used = R3.sum(R3.pluck("size", [...this.storage.values()]));
|
|
303
365
|
const free = this.__size__ > used ? this.__size__ - used : 0;
|
|
304
366
|
return {
|
|
305
367
|
used,
|
|
@@ -342,23 +404,6 @@ var BaseMemoryStorage = class extends InternalStorage {
|
|
|
342
404
|
remove(key) {
|
|
343
405
|
this.__remove__([key]);
|
|
344
406
|
}
|
|
345
|
-
/**
|
|
346
|
-
* Print all cache entries
|
|
347
|
-
*/
|
|
348
|
-
print() {
|
|
349
|
-
const entries = Array.from(this.storage.entries()).map(([key, entry]) => {
|
|
350
|
-
var _a;
|
|
351
|
-
return {
|
|
352
|
-
key,
|
|
353
|
-
size: entry.size,
|
|
354
|
-
expiredAt: entry.expiredAt ? dayjs(entry.expiredAt).format("YYYY-MM-DD HH:mm:ss") : "N/A",
|
|
355
|
-
visitCount: (_a = this.visitCountRecords.get(key)) != null ? _a : 0,
|
|
356
|
-
lastVisit: this.visitTimeRecords.get(key) ? dayjs(this.visitTimeRecords.get(key)).format("YYYY-MM-DD HH:mm:ss") : "N/A"
|
|
357
|
-
};
|
|
358
|
-
});
|
|
359
|
-
console.table(entries);
|
|
360
|
-
console.log("Total: ".concat(entries.length, " entries, Used: ").concat(this.size.used, ", Free: ").concat(this.size.free));
|
|
361
|
-
}
|
|
362
407
|
/**
|
|
363
408
|
* @zh 清除过期的缓存
|
|
364
409
|
*/
|
|
@@ -386,6 +431,33 @@ var BaseMemoryStorage = class extends InternalStorage {
|
|
|
386
431
|
const size = this.size;
|
|
387
432
|
return size.free >= expectSize;
|
|
388
433
|
}
|
|
434
|
+
/**
|
|
435
|
+
* @en Print all cached data using console.table for debugging
|
|
436
|
+
* @zh 使用 console.table 打印所有缓存数据,用于调试
|
|
437
|
+
*/
|
|
438
|
+
async print() {
|
|
439
|
+
if (this.storage.size === 0) {
|
|
440
|
+
console.log("MemoryStorage is empty");
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
const entries = await Promise.all(
|
|
444
|
+
[...this.storage.entries()].map(async ([key, entry]) => {
|
|
445
|
+
var _a, _b, _c;
|
|
446
|
+
const body = await serializeResponseBody(entry.response.clone());
|
|
447
|
+
return {
|
|
448
|
+
key,
|
|
449
|
+
size: humanizeSize(entry.size),
|
|
450
|
+
"Expired Time": entry.expiredAt.getTime() >= MAX_EXPIRED_AT.getTime() ? "-" : entry.expiredAt.toISOString(),
|
|
451
|
+
"Visit Count": (_a = this.visitCountRecords.get(key)) != null ? _a : 0,
|
|
452
|
+
"Last Visit Time": (_c = (_b = this.visitTimeRecords.get(key)) == null ? void 0 : _b.toISOString()) != null ? _c : "-",
|
|
453
|
+
"Response Status": entry.response.status,
|
|
454
|
+
"Response URL": entry.response.url,
|
|
455
|
+
"Response Body": body
|
|
456
|
+
};
|
|
457
|
+
})
|
|
458
|
+
);
|
|
459
|
+
console.table(entries);
|
|
460
|
+
}
|
|
389
461
|
};
|
|
390
462
|
|
|
391
463
|
// src/storage/memory-storage/ttl-memory-storage.ts
|
|
@@ -423,7 +495,7 @@ var TTLMemoryStorage = class extends BaseMemoryStorage {
|
|
|
423
495
|
const bExpiredAt = dayjs2(b.expiredAt);
|
|
424
496
|
return aExpiredAt.isBefore(bExpiredAt) ? 1 : -1;
|
|
425
497
|
});
|
|
426
|
-
if (
|
|
498
|
+
if (R4.sum(R4.pluck("size", entries)) < deficitSize) {
|
|
427
499
|
this.debug((log) => log("Storage Size Not Enough: ", this.size.free, " < ", deficitSize));
|
|
428
500
|
return false;
|
|
429
501
|
}
|
|
@@ -439,11 +511,6 @@ var TTLMemoryStorage = class extends BaseMemoryStorage {
|
|
|
439
511
|
}
|
|
440
512
|
};
|
|
441
513
|
|
|
442
|
-
// src/utils/random.ts
|
|
443
|
-
function random(min, max) {
|
|
444
|
-
return Math.floor(Math.random() * (max - min)) + min;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
514
|
// src/storage/memory-storage/random-memory-storage.ts
|
|
448
515
|
var RandomMemoryStorage = class extends BaseMemoryStorage {
|
|
449
516
|
constructor(options) {
|
|
@@ -614,15 +681,18 @@ var MemoryStorage = class extends KeqCacheStorage {
|
|
|
614
681
|
remove(key) {
|
|
615
682
|
return this.storage.remove(key);
|
|
616
683
|
}
|
|
684
|
+
async print() {
|
|
685
|
+
return this.storage.print();
|
|
686
|
+
}
|
|
617
687
|
};
|
|
618
688
|
|
|
619
689
|
// src/storage/indexed-db-storage/random-indexed-db-storage.ts
|
|
620
|
-
import * as
|
|
690
|
+
import * as R6 from "ramda";
|
|
621
691
|
|
|
622
692
|
// src/storage/indexed-db-storage/base-indexed-db-storage.ts
|
|
623
|
-
import * as
|
|
624
|
-
import { openDB } from "idb";
|
|
693
|
+
import * as R5 from "ramda";
|
|
625
694
|
import dayjs4 from "dayjs";
|
|
695
|
+
import { openDB } from "idb";
|
|
626
696
|
|
|
627
697
|
// src/storage/indexed-db-storage/constants/default-table-name.ts
|
|
628
698
|
var DEFAULT_TABLE_NAME = "keq_cache_indexed_db_storage";
|
|
@@ -633,9 +703,9 @@ var BaseIndexedDBStorage = class extends InternalStorage {
|
|
|
633
703
|
super(options);
|
|
634
704
|
__publicField(this, "tableName", DEFAULT_TABLE_NAME);
|
|
635
705
|
__publicField(this, "db");
|
|
636
|
-
__publicField(this, "lastEvictExpiredTime", dayjs4());
|
|
706
|
+
__publicField(this, "lastEvictExpiredTime", dayjs4(0));
|
|
637
707
|
if ((options == null ? void 0 : options.tableName) === DEFAULT_TABLE_NAME) {
|
|
638
|
-
throw new
|
|
708
|
+
throw new CacheException('IndexedDBStorage name cannot be "'.concat(DEFAULT_TABLE_NAME, '"'));
|
|
639
709
|
}
|
|
640
710
|
this.tableName = (options == null ? void 0 : options.tableName) || DEFAULT_TABLE_NAME;
|
|
641
711
|
}
|
|
@@ -659,13 +729,13 @@ var BaseIndexedDBStorage = class extends InternalStorage {
|
|
|
659
729
|
}
|
|
660
730
|
},
|
|
661
731
|
blocked() {
|
|
662
|
-
|
|
732
|
+
Logger.error("[@keq-request/cache] IndexedDB Table ".concat(tableName, " is blocked"));
|
|
663
733
|
},
|
|
664
734
|
blocking() {
|
|
665
|
-
|
|
735
|
+
Logger.error("[@keq-request/cache] IndexedDB Table ".concat(tableName, " is blocking"));
|
|
666
736
|
},
|
|
667
737
|
terminated() {
|
|
668
|
-
|
|
738
|
+
Logger.error("[@keq-request/cache] IndexedDB Table ".concat(tableName, " is terminated"));
|
|
669
739
|
}
|
|
670
740
|
});
|
|
671
741
|
this.db = db;
|
|
@@ -674,7 +744,7 @@ var BaseIndexedDBStorage = class extends InternalStorage {
|
|
|
674
744
|
async getSize() {
|
|
675
745
|
const db = await this.openDB();
|
|
676
746
|
const items = await db.getAll("metadata");
|
|
677
|
-
const used =
|
|
747
|
+
const used = R5.sum(items.map((entry) => entry.size));
|
|
678
748
|
const free = this.__size__ - used;
|
|
679
749
|
return { used, free };
|
|
680
750
|
}
|
|
@@ -750,7 +820,7 @@ var BaseIndexedDBStorage = class extends InternalStorage {
|
|
|
750
820
|
}
|
|
751
821
|
async __remove__(tx, keys) {
|
|
752
822
|
await Promise.all(
|
|
753
|
-
|
|
823
|
+
R5.unnest(
|
|
754
824
|
keys.map((key) => [
|
|
755
825
|
tx.objectStore("metadata").delete(key),
|
|
756
826
|
tx.objectStore("response").delete(key),
|
|
@@ -776,15 +846,15 @@ var BaseIndexedDBStorage = class extends InternalStorage {
|
|
|
776
846
|
var _a;
|
|
777
847
|
const now = dayjs4();
|
|
778
848
|
if (now.diff(this.lastEvictExpiredTime, "second") < 1) return;
|
|
849
|
+
this.lastEvictExpiredTime = now;
|
|
779
850
|
try {
|
|
780
|
-
const now2 = dayjs4();
|
|
781
851
|
const db = await this.openDB();
|
|
782
852
|
const tx = db.transaction(["metadata", "response", "visits"], "readwrite");
|
|
783
853
|
const metadataStore = tx.objectStore("metadata");
|
|
784
|
-
let cursor = await metadataStore.index("expiredAt").openCursor(IDBKeyRange.upperBound(
|
|
854
|
+
let cursor = await metadataStore.index("expiredAt").openCursor(IDBKeyRange.upperBound(now.toDate()));
|
|
785
855
|
const expiredKeys = [];
|
|
786
856
|
while (cursor) {
|
|
787
|
-
if (dayjs4(cursor.value.expiredAt).isBefore(
|
|
857
|
+
if (dayjs4(cursor.value.expiredAt).isBefore(now)) {
|
|
788
858
|
expiredKeys.push(cursor.value.key);
|
|
789
859
|
cursor = await cursor.continue();
|
|
790
860
|
} else {
|
|
@@ -831,7 +901,7 @@ var RandomIndexedDBStorage = class extends BaseIndexedDBStorage {
|
|
|
831
901
|
const tx = db.transaction(["metadata", "response", "visits"], "readwrite");
|
|
832
902
|
const metadataStore = tx.objectStore("metadata");
|
|
833
903
|
const metadatas = await metadataStore.getAll();
|
|
834
|
-
const totalSize =
|
|
904
|
+
const totalSize = R6.sum(metadatas.map((m) => m.size));
|
|
835
905
|
if (totalSize < deficitSize) {
|
|
836
906
|
this.debug((log) => log("Storage Size Not Enough, deficit size: ".concat(deficitSize - totalSize)));
|
|
837
907
|
tx.abort();
|
|
@@ -1034,7 +1104,7 @@ var IndexedDBStorage = class extends KeqCacheStorage {
|
|
|
1034
1104
|
} else if (eviction === "ttl" /* TTL */) {
|
|
1035
1105
|
this.storage = new TTLIndexedDBStorage(options);
|
|
1036
1106
|
} else {
|
|
1037
|
-
throw
|
|
1107
|
+
throw new CacheException("Not Supported Eviction: ".concat(String(options == null ? void 0 : options.eviction)));
|
|
1038
1108
|
}
|
|
1039
1109
|
}
|
|
1040
1110
|
set(entry) {
|
|
@@ -1084,7 +1154,7 @@ var MultiTierStorage = class extends KeqCacheStorage {
|
|
|
1084
1154
|
* @zh 并发写入所有层的缓存
|
|
1085
1155
|
*/
|
|
1086
1156
|
async set(entry) {
|
|
1087
|
-
const promises = this.storages.map((storage) => storage.set(entry));
|
|
1157
|
+
const promises = this.storages.map(async (storage) => storage.set(entry));
|
|
1088
1158
|
await Promise.all(promises);
|
|
1089
1159
|
}
|
|
1090
1160
|
/**
|
|
@@ -1092,7 +1162,7 @@ var MultiTierStorage = class extends KeqCacheStorage {
|
|
|
1092
1162
|
* @zh 从所有层删除缓存条目
|
|
1093
1163
|
*/
|
|
1094
1164
|
async remove(key) {
|
|
1095
|
-
const promises = this.storages.map((storage) => storage.remove(key));
|
|
1165
|
+
const promises = this.storages.map(async (storage) => storage.remove(key));
|
|
1096
1166
|
await Promise.all(promises);
|
|
1097
1167
|
}
|
|
1098
1168
|
/**
|
|
@@ -1104,7 +1174,7 @@ var MultiTierStorage = class extends KeqCacheStorage {
|
|
|
1104
1174
|
if (lowerTierStorages.length === 0) {
|
|
1105
1175
|
return;
|
|
1106
1176
|
}
|
|
1107
|
-
const promises = lowerTierStorages.map((storage) => storage.set(entry.clone()));
|
|
1177
|
+
const promises = lowerTierStorages.map(async (storage) => storage.set(entry.clone()));
|
|
1108
1178
|
try {
|
|
1109
1179
|
await Promise.all(promises);
|
|
1110
1180
|
} catch (error) {
|
|
@@ -1124,10 +1194,11 @@ var TierStorage = class extends MultiTierStorage {
|
|
|
1124
1194
|
}
|
|
1125
1195
|
};
|
|
1126
1196
|
export {
|
|
1127
|
-
KeqCacheStorage as CacheStorage,
|
|
1128
1197
|
Eviction,
|
|
1129
1198
|
IndexedDBStorage,
|
|
1199
|
+
KeqCacheStorage,
|
|
1130
1200
|
MemoryStorage,
|
|
1201
|
+
MultiTierStorage,
|
|
1131
1202
|
Size,
|
|
1132
1203
|
Strategy,
|
|
1133
1204
|
TierStorage,
|