Skip to content
This repository was archived by the owner on Mar 1, 2026. It is now read-only.

Commit 6b9cc95

Browse files
committed
swr support
1 parent fc2f9ee commit 6b9cc95

4 files changed

Lines changed: 117 additions & 3 deletions

File tree

packages/plugins/cache/src/plugin.ts

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ import { definePlugin } from '@zenstackhq/orm';
33
import stableStringify from 'json-stable-stringify';
44
import murmurhash from 'murmurhash';
55
import { cacheEnvelopeSchema } from './schemas';
6-
import type { CacheEnvelope, CacheInvalidationOptions, CachePluginOptions } from './types';
6+
import type { CacheEnvelope, CacheInvalidationOptions, CachePluginOptions, CacheStatus } from './types';
7+
import { entryIsFresh, entryIsStale } from './utils'
78

89
export function defineCachePlugin(pluginOptions: CachePluginOptions) {
10+
let status: CacheStatus | null = null;
11+
let revalidation: Promise<void> | null = null;
12+
913
return definePlugin({
1014
id: 'cache',
1115
name: 'Cache',
@@ -24,6 +28,23 @@ export function defineCachePlugin(pluginOptions: CachePluginOptions) {
2428
invalidateAll() {
2529
return pluginOptions.provider.invalidateAll();
2630
},
31+
32+
/**
33+
* Returns the status of the last result returned, or `null`
34+
* if a result has yet to be returned.
35+
*/
36+
get status() {
37+
return status;
38+
},
39+
40+
/**
41+
* Returns a `Promise` that fulfills when the last stale result
42+
* returned has been revalidated, or `null` if a stale result has
43+
* yet to be returned.
44+
*/
45+
get revalidation() {
46+
return revalidation;
47+
}
2748
},
2849
},
2950

@@ -45,7 +66,26 @@ export function defineCachePlugin(pluginOptions: CachePluginOptions) {
4566
const queryResultEntry = await cache.getQueryResult(key);
4667

4768
if (queryResultEntry) {
48-
return queryResultEntry.result;
69+
if (entryIsFresh(queryResultEntry)) {
70+
status = 'hit';
71+
return queryResultEntry.result;
72+
} else if (entryIsStale(queryResultEntry)) {
73+
revalidation = proceed(args).then(async (result) => {
74+
try {
75+
await cache.setQueryResult(key, {
76+
createdAt: Date.now(),
77+
options,
78+
result,
79+
})
80+
}
81+
catch (err) {
82+
console.error(`Failed to cache query result: ${err}`)
83+
}
84+
});
85+
86+
status = 'stale';
87+
return queryResultEntry.result;
88+
}
4989
}
5090

5191
const result = await proceed(args);
@@ -56,6 +96,7 @@ export function defineCachePlugin(pluginOptions: CachePluginOptions) {
5696
result,
5797
}).catch((err) => console.error(`Failed to cache query result: ${err}`));
5898

99+
status = 'miss';
59100
return result;
60101
}
61102

packages/plugins/cache/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@ export type CacheQueryResultEntry = CacheEntry & {
3232
export type CachePluginOptions = {
3333
provider: CacheProvider;
3434
};
35+
36+
export type CacheStatus = 'hit' | 'miss' | 'stale';

packages/plugins/cache/src/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function entryIsFresh(entry: CacheEntry) {
1212

1313
export function entryIsStale(entry: CacheEntry) {
1414
return entry.options.swr
15-
? Date.now() <= getTotalTTL(entry)
15+
? Date.now() <= entry.createdAt + getTotalTTL(entry)
1616
: false;
1717
}
1818

tests/e2e/orm/cache/memory.test.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,77 @@ describe('Cache plugin (memory)', () => {
292292
})).resolves.toHaveLength(2);
293293
});
294294

295+
it('respects swr', async () => {
296+
const extDb = db.$use(defineCachePlugin({
297+
provider: new MemoryCache(),
298+
}));
299+
300+
const user = await extDb.user.create({
301+
data: {
302+
email: 'test@email.com',
303+
},
304+
});
305+
306+
await extDb.user.findFirst({
307+
where: {
308+
id: user.id,
309+
},
310+
311+
cache: {
312+
swr: 60,
313+
},
314+
});
315+
316+
await extDb.user.update({
317+
data: {
318+
name: 'newname',
319+
},
320+
321+
where: {
322+
id: user.id,
323+
},
324+
});
325+
326+
await expect(extDb.user.findFirst({
327+
where: {
328+
id: user.id,
329+
},
330+
331+
cache: {
332+
swr: 60,
333+
},
334+
})).resolves.toMatchObject({
335+
name: null,
336+
});
337+
338+
await expect(extDb.user.findFirst({
339+
where: {
340+
id: user.id,
341+
},
342+
343+
cache: {
344+
swr: 60,
345+
},
346+
})).resolves.toMatchObject({
347+
name: null,
348+
});
349+
350+
expect(extDb.$cache.status).toBe('stale');
351+
await extDb.$cache.revalidation;
352+
353+
await expect(extDb.user.findFirst({
354+
where: {
355+
id: user.id,
356+
},
357+
358+
cache: {
359+
swr: 60,
360+
},
361+
})).resolves.toMatchObject({
362+
name: 'newname',
363+
});
364+
});
365+
295366
it('supports invalidating all entries', async () => {
296367
const extDb = db.$use(defineCachePlugin({
297368
provider: new MemoryCache(),

0 commit comments

Comments
 (0)