Skip to content

Commit

Permalink
feat: add isExpired helper (#122)
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiphe authored Feb 7, 2025
1 parent 55b45a8 commit 2070f4e
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 33 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ maintenance and testing these helpers might come handy.
import {
createCacheEntry,
assertCacheEntry,
isExpired,
cachified,
} from '@epic-web/cachified';

Expand Down Expand Up @@ -512,6 +513,11 @@ assertCacheEntry(entry); // will throw when entry is not a valid CacheEntry
console.log(entry.value);
// > logs "[email protected]"

/* Manually check if an entry is expired */
const expired = isExpired(entry.metadata);
console.log(expired);
// > logs true, "stale" or false

/* Manually remove an entry from cache */
cache.delete('user-1');
```
Expand Down
5 changes: 3 additions & 2 deletions src/cachified.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { CACHE_EMPTY, getCachedValue } from './getCachedValue';
import { getFreshValue } from './getFreshValue';
import { CreateReporter } from './reporter';
import { shouldRefresh } from './shouldRefresh';
import { isExpired } from './isExpired';

// This is to prevent requesting multiple fresh values in parallel
// while revalidating or getting first value
Expand Down Expand Up @@ -53,7 +53,8 @@ export async function cachified<Value>(

if (pendingValues.has(key)) {
const { value: pendingRefreshValue, metadata } = pendingValues.get(key)!;
if (!shouldRefresh(metadata)) {

if (!isExpired(metadata)) {
/* Notify batch that we handled this call using pending value */
context.getFreshValue[HANDLE]?.();
report({ name: 'getFreshValueHookPending' });
Expand Down
12 changes: 6 additions & 6 deletions src/getCachedValue.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Context, CacheEntry } from './common';
import { assertCacheEntry } from './assertCacheEntry';
import { HANDLE } from './common';
import { shouldRefresh } from './shouldRefresh';
import { isExpired } from './isExpired';
import { cachified } from './cachified';
import { Reporter } from './reporter';
import { checkValue } from './checkValue';
Expand Down Expand Up @@ -42,12 +42,12 @@ export async function getCachedValue<Value>(
return CACHE_EMPTY;
}

const refresh = shouldRefresh(cached.metadata);
const expired = isExpired(cached.metadata);
const staleRefresh =
refresh === 'stale' ||
(refresh === 'now' && staleWhileRevalidate === Infinity);
expired === 'stale' ||
(expired === true && staleWhileRevalidate === Infinity);

if (refresh === 'now') {
if (expired === true) {
report({ name: 'getCachedValueOutdated', ...cached });
}

Expand Down Expand Up @@ -75,7 +75,7 @@ export async function getCachedValue<Value>(
);
}

if (!refresh || staleRefresh) {
if (!expired || staleRefresh) {
const valueCheck = await checkValue(context, cached.value);
if (valueCheck.success) {
report({
Expand Down
5 changes: 3 additions & 2 deletions src/getFreshValue.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Context, CacheMetadata, createCacheEntry } from './common';
import { getCacheEntry, CACHE_EMPTY } from './getCachedValue';
import { shouldRefresh } from './shouldRefresh';
import { isExpired } from './isExpired';
import { Reporter } from './reporter';
import { checkValue } from './checkValue';

Expand Down Expand Up @@ -59,7 +59,8 @@ export async function getFreshValue<Value>(
}

try {
const write = shouldRefresh(metadata) !== 'now';
/* Only write to cache when the value has not already fully expired while getting it */
const write = isExpired(metadata) !== true;
if (write) {
await cache.set(key, createCacheEntry(value, metadata));
}
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export * from './reporter';
export { createBatch } from './createBatch';
export { cachified } from './cachified';
export { cachified as default } from './cachified';
export { shouldRefresh } from './shouldRefresh';
export { shouldRefresh, isExpired } from './isExpired';
export { assertCacheEntry } from './assertCacheEntry';
export { softPurge } from './softPurge';
export { configure } from './configure';
47 changes: 47 additions & 0 deletions src/isExpired.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { CacheMetadata, staleWhileRevalidate } from './common';

/**
* Check wether a cache entry is expired.
*
* @returns
* - `true` when the cache entry is expired
* - `false` when it's still valid
* - `"stale"` when it's within the stale period
*/
export function isExpired(metadata: CacheMetadata): boolean | 'stale' {
/* No TTL means the cache is permanent / never expires */
if (metadata.ttl === null) {
return false;
}

const validUntil = metadata.createdTime + (metadata.ttl || 0);
const staleUntil = validUntil + (staleWhileRevalidate(metadata) || 0);
const now = Date.now();

/* We're still within the ttl period */
if (now <= validUntil) {
return false;
}
/* We're within the stale period */
if (now <= staleUntil) {
return 'stale';
}

/* Expired */
return true;
}

/**
* @deprecated prefer using `isExpired` instead
*/
export function shouldRefresh(
metadata: CacheMetadata,
): 'now' | 'stale' | false {
const expired = isExpired(metadata);

if (expired === true) {
return 'now';
}

return expired;
}
20 changes: 0 additions & 20 deletions src/shouldRefresh.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/softPurge.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Cache, createCacheEntry, staleWhileRevalidate } from './common';
import { CACHE_EMPTY, getCacheEntry } from './getCachedValue';
import { shouldRefresh } from './shouldRefresh';
import { isExpired } from './isExpired';

interface SoftPurgeOpts {
cache: Cache;
Expand All @@ -23,7 +23,7 @@ export async function softPurge({
const swrOverwrite = swrOverwrites.swr ?? swrOverwrites.staleWhileRevalidate;
const entry = await getCacheEntry({ cache, key }, () => {});

if (entry === CACHE_EMPTY || shouldRefresh(entry.metadata)) {
if (entry === CACHE_EMPTY || isExpired(entry.metadata)) {
return;
}

Expand Down

0 comments on commit 2070f4e

Please sign in to comment.