@leejpsd/nextjs-cache-handler 0.2.0 → 0.3.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +59 -2
  2. package/README.md +47 -25
  3. package/dist/cache-components/index.cjs +235 -46
  4. package/dist/cache-components/index.cjs.map +1 -1
  5. package/dist/cache-components/index.d.cts +1 -1
  6. package/dist/cache-components/index.d.ts +1 -1
  7. package/dist/cache-components/index.js +227 -46
  8. package/dist/cache-components/index.js.map +1 -1
  9. package/dist/incremental/index.cjs +231 -41
  10. package/dist/incremental/index.cjs.map +1 -1
  11. package/dist/incremental/index.d.cts +42 -3
  12. package/dist/incremental/index.d.ts +42 -3
  13. package/dist/incremental/index.js +223 -41
  14. package/dist/incremental/index.js.map +1 -1
  15. package/dist/index.cjs +313 -70
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.js +305 -70
  20. package/dist/index.js.map +1 -1
  21. package/dist/ops/index.d.cts +1 -1
  22. package/dist/ops/index.d.ts +1 -1
  23. package/dist/otel/index.cjs +72 -0
  24. package/dist/otel/index.cjs.map +1 -0
  25. package/dist/otel/index.d.cts +72 -0
  26. package/dist/otel/index.d.ts +72 -0
  27. package/dist/otel/index.js +64 -0
  28. package/dist/otel/index.js.map +1 -0
  29. package/dist/shared/client/adapter-ioredis.cjs +49 -10
  30. package/dist/shared/client/adapter-ioredis.cjs.map +1 -1
  31. package/dist/shared/client/adapter-ioredis.d.cts +5 -2
  32. package/dist/shared/client/adapter-ioredis.d.ts +5 -2
  33. package/dist/shared/client/adapter-ioredis.js +43 -11
  34. package/dist/shared/client/adapter-ioredis.js.map +1 -1
  35. package/dist/shared/client/adapter-redis.cjs +22 -7
  36. package/dist/shared/client/adapter-redis.cjs.map +1 -1
  37. package/dist/shared/client/adapter-redis.d.cts +1 -1
  38. package/dist/shared/client/adapter-redis.d.ts +1 -1
  39. package/dist/shared/client/adapter-redis.js +16 -7
  40. package/dist/shared/client/adapter-redis.js.map +1 -1
  41. package/dist/{types-Dq1q6qXd.d.cts → types-Bl-rkLKX.d.cts} +37 -1
  42. package/dist/{types-Dq1q6qXd.d.ts → types-Bl-rkLKX.d.ts} +37 -1
  43. package/package.json +20 -3
@@ -1,11 +1,11 @@
1
+ import path2 from 'path';
2
+ import 'url';
3
+ import { createRequire } from 'module';
4
+ import { promisify } from 'util';
5
+ import { gzip, gunzip, brotliCompress, brotliDecompress, constants } from 'zlib';
6
+
1
7
  var __defProp = Object.defineProperty;
2
8
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
- }) : x)(function(x) {
6
- if (typeof require !== "undefined") return require.apply(this, arguments);
7
- throw Error('Dynamic require of "' + x + '" is not supported');
8
- });
9
9
  var __esm = (fn, res) => function __init() {
10
10
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
11
  };
@@ -13,6 +13,27 @@ var __export = (target, all) => {
13
13
  for (var name in all)
14
14
  __defProp(target, name, { get: all[name], enumerable: true });
15
15
  };
16
+ var init_esm_shims = __esm({
17
+ "node_modules/tsup/assets/esm_shims.js"() {
18
+ }
19
+ });
20
+ function nodeRequire(id) {
21
+ try {
22
+ return packageRequire(id);
23
+ } catch {
24
+ return cwdRequire(id);
25
+ }
26
+ }
27
+ var packageRequire, cwdRequire;
28
+ var init_node_require = __esm({
29
+ "src/shared/node-require.ts"() {
30
+ init_esm_shims();
31
+ packageRequire = createRequire(import.meta.url);
32
+ cwdRequire = createRequire(
33
+ path2.join(process.cwd(), "__resolve-anchor__.js")
34
+ );
35
+ }
36
+ });
16
37
 
17
38
  // src/shared/client/adapter-redis.ts
18
39
  var adapter_redis_exports = {};
@@ -24,7 +45,7 @@ function adaptRedisV5(client) {
24
45
  return client;
25
46
  }
26
47
  function createRedisV5Client(config) {
27
- const { createClient } = __require("redis");
48
+ const { createClient } = nodeRequire("redis");
28
49
  const useTls = config.tls ?? config.url.startsWith("rediss://");
29
50
  const opts = {
30
51
  url: config.url,
@@ -39,6 +60,8 @@ function createRedisV5Client(config) {
39
60
  }
40
61
  var init_adapter_redis = __esm({
41
62
  "src/shared/client/adapter-redis.ts"() {
63
+ init_esm_shims();
64
+ init_node_require();
42
65
  }
43
66
  });
44
67
 
@@ -48,7 +71,8 @@ __export(adapter_ioredis_exports, {
48
71
  adaptCluster: () => adaptCluster,
49
72
  adaptIoredis: () => adaptIoredis,
50
73
  createIoredisClient: () => createIoredisClient,
51
- createIoredisCluster: () => createIoredisCluster
74
+ createIoredisCluster: () => createIoredisCluster,
75
+ createIoredisSentinel: () => createIoredisSentinel
52
76
  });
53
77
  function adapt(client, isClusterClient = false) {
54
78
  return {
@@ -131,6 +155,9 @@ function adapt(client, isClusterClient = false) {
131
155
  on: (event, listener) => {
132
156
  client.on(event, listener);
133
157
  return client;
158
+ },
159
+ dispose: () => {
160
+ client.disconnect();
134
161
  }
135
162
  };
136
163
  }
@@ -141,18 +168,36 @@ function adaptCluster(cluster) {
141
168
  return adapt(cluster, true);
142
169
  }
143
170
  function createIoredisClient(config) {
144
- const IORedis = __require("ioredis");
171
+ const IORedis = nodeRequire("ioredis");
145
172
  const Ctor = IORedis.default ?? IORedis;
146
173
  return new Ctor(config.url, {
147
174
  password: config.password,
148
175
  connectTimeout: config.connectTimeout ?? 1500,
149
176
  tls: config.tls ? {} : void 0,
150
177
  lazyConnect: true,
151
- maxRetriesPerRequest: 1
178
+ maxRetriesPerRequest: 1,
179
+ // Batch same-tick commands into one RTT (matches redis@5's default
180
+ // behavior) — makes Promise.all fan-outs a single pipeline.
181
+ enableAutoPipelining: true
182
+ });
183
+ }
184
+ function createIoredisSentinel(config) {
185
+ const IORedis = nodeRequire("ioredis");
186
+ const Ctor = IORedis.default ?? IORedis;
187
+ return new Ctor({
188
+ sentinels: config.sentinels,
189
+ name: config.name,
190
+ password: config.password,
191
+ sentinelPassword: config.sentinelPassword,
192
+ connectTimeout: config.connectTimeout ?? 1500,
193
+ tls: config.tls ? {} : void 0,
194
+ lazyConnect: true,
195
+ maxRetriesPerRequest: 1,
196
+ enableAutoPipelining: true
152
197
  });
153
198
  }
154
199
  function createIoredisCluster(config) {
155
- const IORedis = __require("ioredis");
200
+ const IORedis = nodeRequire("ioredis");
156
201
  const ClusterCtor = IORedis.Cluster ?? IORedis.default.Cluster;
157
202
  return new ClusterCtor(config.nodes, {
158
203
  redisOptions: {
@@ -160,15 +205,26 @@ function createIoredisCluster(config) {
160
205
  tls: config.tls ? {} : void 0
161
206
  },
162
207
  enableReadyCheck: true,
163
- lazyConnect: true
208
+ lazyConnect: true,
209
+ // Same-slot commands issued in the same tick share one pipeline.
210
+ enableAutoPipelining: true
164
211
  });
165
212
  }
166
213
  var init_adapter_ioredis = __esm({
167
214
  "src/shared/client/adapter-ioredis.ts"() {
215
+ init_esm_shims();
216
+ init_node_require();
168
217
  }
169
218
  });
170
219
 
220
+ // src/incremental/index.ts
221
+ init_esm_shims();
222
+
223
+ // src/incremental/handler.ts
224
+ init_esm_shims();
225
+
171
226
  // src/shared/abort.ts
227
+ init_esm_shims();
172
228
  var CacheTimeoutError = class extends Error {
173
229
  constructor(opName, ms) {
174
230
  super(`[@leejpsd/nextjs-cache-handler] ${opName} timed out after ${ms}ms`);
@@ -200,7 +256,41 @@ async function withAbortSignal(opName, ms, fn) {
200
256
  }
201
257
  }
202
258
 
259
+ // src/shared/compress.ts
260
+ init_esm_shims();
261
+ var gzipAsync = promisify(gzip);
262
+ var gunzipAsync = promisify(gunzip);
263
+ var brotliCompressAsync = promisify(brotliCompress);
264
+ var brotliDecompressAsync = promisify(brotliDecompress);
265
+ var GZIP_MARKER = "__ncgz__:";
266
+ var BROTLI_MARKER = "__ncbr__:";
267
+ var MIN_COMPRESS_BYTES = 1024;
268
+ var BROTLI_OPTS = {
269
+ params: { [constants.BROTLI_PARAM_QUALITY]: 5 }
270
+ };
271
+ async function compressValue(value, algo) {
272
+ if (!algo || Buffer.byteLength(value) < MIN_COMPRESS_BYTES) return value;
273
+ const input = Buffer.from(value);
274
+ if (algo === "gzip") {
275
+ return GZIP_MARKER + (await gzipAsync(input)).toString("base64");
276
+ }
277
+ return BROTLI_MARKER + (await brotliCompressAsync(input, BROTLI_OPTS)).toString("base64");
278
+ }
279
+ async function decompressValue(value) {
280
+ if (value === null) return null;
281
+ if (value.startsWith(GZIP_MARKER)) {
282
+ const buf = Buffer.from(value.slice(GZIP_MARKER.length), "base64");
283
+ return (await gunzipAsync(buf)).toString("utf8");
284
+ }
285
+ if (value.startsWith(BROTLI_MARKER)) {
286
+ const buf = Buffer.from(value.slice(BROTLI_MARKER.length), "base64");
287
+ return (await brotliDecompressAsync(buf)).toString("utf8");
288
+ }
289
+ return value;
290
+ }
291
+
203
292
  // src/shared/client/index.ts
293
+ init_esm_shims();
204
294
  async function buildClient(source) {
205
295
  if (typeof source === "function") {
206
296
  return await source();
@@ -218,6 +308,10 @@ async function buildClient(source) {
218
308
  const { adaptCluster: adaptCluster2, createIoredisCluster: createIoredisCluster2 } = await Promise.resolve().then(() => (init_adapter_ioredis(), adapter_ioredis_exports));
219
309
  return adaptCluster2(createIoredisCluster2(source));
220
310
  }
311
+ case "sentinel": {
312
+ const { adaptIoredis: adaptIoredis2, createIoredisSentinel: createIoredisSentinel2 } = await Promise.resolve().then(() => (init_adapter_ioredis(), adapter_ioredis_exports));
313
+ return adaptIoredis2(createIoredisSentinel2(source));
314
+ }
221
315
  default: {
222
316
  const exhaustive = source;
223
317
  throw new Error(
@@ -227,28 +321,45 @@ async function buildClient(source) {
227
321
  }
228
322
  }
229
323
  var ConnectionManager = class {
230
- constructor(source, onError) {
324
+ constructor(source, onError, retry = {}) {
231
325
  this.source = source;
232
326
  this.onError = onError;
327
+ this.baseCooldownMs = retry.baseCooldownMs ?? 1e3;
328
+ this.maxCooldownMs = retry.maxCooldownMs ?? 3e4;
233
329
  }
234
330
  source;
235
331
  onError;
236
332
  client = null;
237
333
  connecting = null;
238
- failed = false;
334
+ consecutiveFailures = 0;
335
+ lastFailureAt = 0;
336
+ baseCooldownMs;
337
+ maxCooldownMs;
338
+ cooldownMs() {
339
+ if (this.consecutiveFailures === 0) return 0;
340
+ return Math.min(
341
+ this.maxCooldownMs,
342
+ this.baseCooldownMs * 2 ** (this.consecutiveFailures - 1)
343
+ );
344
+ }
239
345
  async getOrConnect() {
240
346
  if (this.client?.isOpen) return this.client;
241
- if (this.failed) return null;
242
347
  if (this.connecting) return this.connecting;
348
+ if (Date.now() - this.lastFailureAt < this.cooldownMs()) return null;
349
+ const stale = this.client;
350
+ this.client = null;
243
351
  this.connecting = (async () => {
352
+ disposeQuietly(stale);
244
353
  try {
245
354
  const c = await buildClient(this.source);
246
355
  c.on("error", (err) => this.onError(err));
247
356
  if (!c.isOpen) await c.connect();
248
357
  this.client = c;
358
+ this.consecutiveFailures = 0;
249
359
  return c;
250
360
  } catch (err) {
251
- this.failed = true;
361
+ this.consecutiveFailures += 1;
362
+ this.lastFailureAt = Date.now();
252
363
  this.onError(err);
253
364
  return null;
254
365
  } finally {
@@ -258,13 +369,24 @@ var ConnectionManager = class {
258
369
  return this.connecting;
259
370
  }
260
371
  reset() {
372
+ disposeQuietly(this.client);
261
373
  this.client = null;
262
374
  this.connecting = null;
263
- this.failed = false;
375
+ this.consecutiveFailures = 0;
376
+ this.lastFailureAt = 0;
264
377
  }
265
378
  };
379
+ function disposeQuietly(client) {
380
+ if (!client) return;
381
+ const c = client;
382
+ try {
383
+ (c.dispose ?? c.destroy ?? c.disconnect)?.call(c);
384
+ } catch {
385
+ }
386
+ }
266
387
 
267
388
  // src/shared/logger.ts
389
+ init_esm_shims();
268
390
  var defaultLogger = {
269
391
  debug: () => {
270
392
  },
@@ -281,13 +403,19 @@ var defaultLogger = {
281
403
  };
282
404
 
283
405
  // src/shared/memory-fallback.ts
406
+ init_esm_shims();
284
407
  var MAX_TIMER_MS = 2147483647;
285
408
  function clampTimerMs(ms) {
286
409
  return Math.min(Math.max(0, ms), MAX_TIMER_MS);
287
410
  }
411
+ var DEFAULT_MAX_ENTRIES = 1e3;
288
412
  var MemoryStore = class {
289
413
  kv = /* @__PURE__ */ new Map();
290
414
  timers = /* @__PURE__ */ new Map();
415
+ maxEntries;
416
+ constructor(maxEntries = DEFAULT_MAX_ENTRIES) {
417
+ this.maxEntries = Math.max(1, maxEntries);
418
+ }
291
419
  set(key, value, ttlSeconds) {
292
420
  this.delete(key);
293
421
  const expiresAt = Date.now() + ttlSeconds * 1e3;
@@ -297,6 +425,10 @@ var MemoryStore = class {
297
425
  timer.unref();
298
426
  }
299
427
  this.timers.set(key, timer);
428
+ while (this.kv.size > this.maxEntries) {
429
+ const oldest = this.kv.keys().next().value;
430
+ this.delete(oldest);
431
+ }
300
432
  }
301
433
  get(key) {
302
434
  const entry = this.kv.get(key);
@@ -305,6 +437,8 @@ var MemoryStore = class {
305
437
  this.delete(key);
306
438
  return null;
307
439
  }
440
+ this.kv.delete(key);
441
+ this.kv.set(key, entry);
308
442
  return entry.value;
309
443
  }
310
444
  delete(key) {
@@ -337,6 +471,7 @@ var MemoryStore = class {
337
471
  };
338
472
 
339
473
  // src/shared/metrics.ts
474
+ init_esm_shims();
340
475
  var counters = /* @__PURE__ */ new Map();
341
476
  function bump(event) {
342
477
  const existing = counters.get(event.type);
@@ -365,6 +500,7 @@ function createEmitter(onMetric) {
365
500
  }
366
501
 
367
502
  // src/shared/namespace.ts
503
+ init_esm_shims();
368
504
  var FALLBACK = "unversioned";
369
505
  function resolveBuildNamespace(override) {
370
506
  if (override !== void 0) {
@@ -378,6 +514,7 @@ function buildKey(prefix, namespace, rest, hashTag = false) {
378
514
  }
379
515
 
380
516
  // src/shared/build-phase.ts
517
+ init_esm_shims();
381
518
  var PHASE_PRODUCTION_BUILD = "phase-production-build";
382
519
  function isBuildPhase() {
383
520
  return process.env.NEXT_PHASE === PHASE_PRODUCTION_BUILD;
@@ -390,6 +527,7 @@ function shouldUseRedis(opts) {
390
527
  }
391
528
 
392
529
  // src/incremental/serialize.ts
530
+ init_esm_shims();
393
531
  function isEnvelope(value) {
394
532
  return typeof value === "object" && value !== null && "__type" in value && typeof value.__type === "string";
395
533
  }
@@ -471,7 +609,7 @@ function init(opts) {
471
609
  logger,
472
610
  emit,
473
611
  conn,
474
- memEntries: new MemoryStore(),
612
+ memEntries: new MemoryStore(opts.memoryMaxEntries),
475
613
  memTagStates: /* @__PURE__ */ new Map(),
476
614
  resolveNs: () => resolveBuildNamespace(opts.buildNamespace)
477
615
  };
@@ -530,7 +668,7 @@ function normalizeTtlSeconds(data, ctx) {
530
668
  return ONE_YEAR_SEC;
531
669
  }
532
670
  }
533
- const revalidate = ctx?.cacheControl?.revalidate;
671
+ const revalidate = ctx?.cacheControl?.revalidate ?? ctx?.revalidate;
534
672
  if (revalidate === false || revalidate === void 0) return ONE_YEAR_SEC;
535
673
  if (typeof revalidate === "number" && revalidate > 0) {
536
674
  return Math.min(Math.max(Math.ceil(revalidate), MIN_TTL_SEC), ONE_YEAR_SEC);
@@ -568,10 +706,49 @@ async function readTagStates(state, tags) {
568
706
  var IncrementalRedisCacheHandler = class {
569
707
  state;
570
708
  revalidatedTags;
709
+ /**
710
+ * Request-scoped memo of Redis entry reads. Next.js instantiates this class
711
+ * per request (and calls resetRequestCache() at request boundaries when it
712
+ * reuses an instance), so entries live at most one request. Bounded by the
713
+ * number of distinct cache keys a single request touches.
714
+ */
715
+ requestMemo = /* @__PURE__ */ new Map();
571
716
  constructor(state, ctx) {
572
717
  this.state = state;
573
718
  this.revalidatedTags = normalizeTags(ctx?.revalidatedTags);
574
719
  }
720
+ /**
721
+ * Redis entry read, deduplicated per request: parallel RSC renders or
722
+ * repeated fetch() calls hitting the same cache key share one GET. The
723
+ * memoized promise never rejects — errors are converted to a miss (with
724
+ * the same timeout metric the inline path emitted before).
725
+ */
726
+ readEntryDeduped(eKey) {
727
+ const memoized = this.requestMemo.get(eKey);
728
+ if (memoized) {
729
+ this.state.emit({ type: "cache.get.deduped" });
730
+ return memoized;
731
+ }
732
+ const read = withAbortSignal(
733
+ "incremental.get",
734
+ this.state.opts.abortTimeoutMs,
735
+ async () => {
736
+ const client = await this.state.conn.getOrConnect();
737
+ if (!client) return null;
738
+ return await decompressValue(await client.get(eKey));
739
+ }
740
+ ).catch((err) => {
741
+ if (err instanceof CacheTimeoutError) {
742
+ this.state.emit({
743
+ type: "redis.timeout",
744
+ meta: { op: "incremental.get" }
745
+ });
746
+ }
747
+ return null;
748
+ });
749
+ this.requestMemo.set(eKey, read);
750
+ return read;
751
+ }
575
752
  async get(cacheKey, ctx) {
576
753
  try {
577
754
  const requestedTags = normalizeTags([
@@ -583,24 +760,11 @@ var IncrementalRedisCacheHandler = class {
583
760
  const useRedis = !useLocal && gateRedis(this.state);
584
761
  let raw = null;
585
762
  if (useRedis) {
586
- try {
587
- raw = await withAbortSignal(
588
- "incremental.get",
589
- this.state.opts.abortTimeoutMs,
590
- async () => {
591
- const client = await this.state.conn.getOrConnect();
592
- if (!client) return null;
593
- return await client.get(eKey);
594
- }
595
- );
596
- } catch (err) {
597
- if (err instanceof CacheTimeoutError) {
598
- this.state.emit({ type: "redis.timeout", meta: { op: "incremental.get" } });
599
- }
600
- raw = null;
601
- }
763
+ raw = await this.readEntryDeduped(eKey);
764
+ }
765
+ if (raw === null && (useLocal || this.state.opts.fallback !== "never")) {
766
+ raw = this.state.memEntries.get(eKey);
602
767
  }
603
- if (raw === null) raw = this.state.memEntries.get(eKey);
604
768
  if (raw === null) {
605
769
  this.state.emit({ type: "cache.miss" });
606
770
  return null;
@@ -613,7 +777,7 @@ var IncrementalRedisCacheHandler = class {
613
777
  return null;
614
778
  }
615
779
  const tagStates = await readTagStates(this.state, tags);
616
- const isFetch = ctx?.kind === "FETCH";
780
+ const isFetch = ctx?.kind === "FETCH" || ctx?.kindHint?.toLowerCase() === "fetch" || typeof parsed.value === "object" && parsed.value !== null && parsed.value.kind === "FETCH";
617
781
  if (tagStates.some((s) => isTagStateExpired(s, parsed.lastModified, isFetch))) {
618
782
  this.state.emit({ type: "cache.miss", meta: { reason: "tag-state-expired" } });
619
783
  return null;
@@ -636,6 +800,7 @@ var IncrementalRedisCacheHandler = class {
636
800
  const record = { lastModified: Date.now(), value: data };
637
801
  const serialized = serializeCacheRecord(record);
638
802
  const eKey = entryKey(this.state, cacheKey);
803
+ this.requestMemo.delete(eKey);
639
804
  const useRedis = !useLocal && gateRedis(this.state);
640
805
  if (useRedis) {
641
806
  try {
@@ -645,10 +810,19 @@ var IncrementalRedisCacheHandler = class {
645
810
  async () => {
646
811
  const client = await this.state.conn.getOrConnect();
647
812
  if (!client) {
648
- this.state.memEntries.set(eKey, serialized, ttl);
813
+ if (this.state.opts.fallback !== "never") {
814
+ this.state.memEntries.set(eKey, serialized, ttl);
815
+ }
649
816
  return;
650
817
  }
651
- await client.set(eKey, serialized, { EX: ttl });
818
+ await client.set(
819
+ eKey,
820
+ await compressValue(
821
+ serialized,
822
+ this.state.opts.rest.compression
823
+ ),
824
+ { EX: ttl }
825
+ );
652
826
  }
653
827
  );
654
828
  this.state.emit({ type: "cache.set", meta: { backend: "redis" } });
@@ -659,8 +833,15 @@ var IncrementalRedisCacheHandler = class {
659
833
  }
660
834
  }
661
835
  }
662
- this.state.memEntries.set(eKey, serialized, ttl);
663
- this.state.emit({ type: "cache.set", meta: { backend: "memory" } });
836
+ if (useLocal || this.state.opts.fallback !== "never") {
837
+ this.state.memEntries.set(eKey, serialized, ttl);
838
+ this.state.emit({ type: "cache.set", meta: { backend: "memory" } });
839
+ } else {
840
+ this.state.emit({
841
+ type: "cache.set.failed",
842
+ meta: { reason: "no-fallback" }
843
+ });
844
+ }
664
845
  } catch (err) {
665
846
  this.state.logger.error("set() error", {
666
847
  message: err.message
@@ -714,6 +895,7 @@ var IncrementalRedisCacheHandler = class {
714
895
  }
715
896
  }
716
897
  resetRequestCache() {
898
+ this.requestMemo.clear();
717
899
  }
718
900
  };
719
901
  function createIncrementalCacheHandler(opts) {