-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplugin.ts
More file actions
122 lines (103 loc) · 3.12 KB
/
plugin.ts
File metadata and controls
122 lines (103 loc) · 3.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import { definePlugin } from '@zenstackhq/orm'
import { stableHash } from 'stable-hash'
import murmurhash from 'murmurhash'
import { cacheEnvelopeSchema } from './schemas'
import type {
CacheEnvelope,
CacheInvalidationOptions,
CachePluginOptions,
CacheStatus,
} from './types'
import { entryIsFresh, entryIsStale } from './utils'
function lowerCaseFirst(input: string) {
return input.charAt(0).toLowerCase() + input.slice(1)
}
export function defineCachePlugin(pluginOptions: CachePluginOptions) {
let status: CacheStatus | null = null
let revalidation: Promise<unknown> | null = null
return definePlugin({
id: 'cache',
name: 'Cache',
description: 'Optionally caches read queries.',
queryArgs: {
$read: cacheEnvelopeSchema,
},
client: {
$cache: {
invalidate: (options: CacheInvalidationOptions) => {
return pluginOptions.provider.invalidate(options)
},
invalidateAll() {
return pluginOptions.provider.invalidateAll()
},
/**
* Returns the status of the last result returned, or `null`
* if a result has yet to be returned.
*/
get status() {
return status
},
/**
* Returns a `Promise` that fulfills when the last stale result
* returned has been revalidated, or `null` if a stale result has
* yet to be returned.
*/
get revalidation() {
return revalidation
},
},
},
onQuery: async ({ args, model, operation, proceed, client }) => {
if (args && 'cache' in args) {
const authId = client.$auth
? client.$schema.models[client.$schema.authType!]!.idFields.map(key => client.$auth![key])
: undefined
const json = stableHash({
args,
model,
operation,
authId,
})
if (!json) {
throw new Error(
`Failed to serialize cache entry for ${lowerCaseFirst(model)}.${operation}`,
)
}
const cache = pluginOptions.provider
const options = (args as CacheEnvelope).cache!
const key = murmurhash.v3(json).toString()
const entry = await cache.get(key)
if (entry) {
if (entryIsFresh(entry)) {
status = 'hit'
return entry.result
} else if (entryIsStale(entry)) {
revalidation = proceed(args).then(result => {
cache
.set(key, {
createdAt: Date.now(),
options,
result,
})
.catch(err => console.error(`Failed to cache query result: ${err}`))
return result
})
status = 'stale'
return entry.result
}
}
const result = await proceed(args)
cache
.set(key, {
createdAt: Date.now(),
options,
result,
})
.catch(err => console.error(`Failed to cache query result: ${err}`))
status = 'miss'
return result
}
return proceed(args)
},
})
}