Skip to content

Commit d025a8a

Browse files
committed
refactor: cache
1 parent d91a348 commit d025a8a

File tree

2 files changed

+81
-20
lines changed

2 files changed

+81
-20
lines changed

packages/kit/src/client/rpc.ts

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,60 @@ function isNumeric(str: string | number | undefined) {
1212
return `${+str}` === `${str}`
1313
}
1414

15+
interface RpcCacheOptions {
16+
functions: string[]
17+
}
18+
19+
// @TODO: should be moved to birpc-x?
20+
class RpcCacheManager {
21+
private cacheMap = new Map<string, Map<string, unknown>>()
22+
private options: RpcCacheOptions
23+
24+
constructor(options: RpcCacheOptions) {
25+
this.options = options
26+
}
27+
28+
updateOptions(options: Partial<RpcCacheOptions>) {
29+
this.options = {
30+
...this.options,
31+
...options,
32+
}
33+
}
34+
35+
cached(m: string, a: unknown[]) {
36+
const methodCache = this.cacheMap.get(m)
37+
if (methodCache) {
38+
return methodCache.get(JSON.stringify(a))
39+
}
40+
return undefined
41+
}
42+
43+
apply(req: { m: string, a: unknown[] }, res: unknown) {
44+
const methodCache = this.cacheMap.get(req.m) || new Map<string, unknown>()
45+
methodCache.set(JSON.stringify(req.a), res)
46+
this.cacheMap.set(req.m, methodCache)
47+
}
48+
49+
validate(m: string) {
50+
return this.options.functions.includes(m)
51+
}
52+
53+
invalidate(key?: string) {
54+
if (key) {
55+
this.cacheMap.delete(key)
56+
}
57+
else {
58+
this.cacheMap.clear()
59+
}
60+
}
61+
}
62+
1563
export interface DevToolsRpcClientOptions {
1664
connectionMeta?: ConnectionMeta
1765
baseURL?: string[]
18-
cacheResponse?: boolean
1966
wsOptions?: Partial<WebSocketRpcClientOptions>
2067
rpcOptions?: Partial<BirpcOptions<DevToolsRpcServerFunctions>>
68+
cacheOptions?: boolean | Partial<RpcCacheOptions>
2169
}
2270

2371
export type DevToolsRpcClient = BirpcReturn<DevToolsRpcServerFunctions, DevToolsRpcClientFunctions>
@@ -26,19 +74,29 @@ export interface ClientRpcReturn {
2674
connectionMeta: ConnectionMeta
2775
rpc: DevToolsRpcClient
2876
clientRpc: DevToolsClientRpcHost
29-
invalidateCache: () => void
3077
}
3178

79+
export async function getDevToolsRpcClient(
80+
options: DevToolsRpcClientOptions & { cacheOptions: false },
81+
): Promise<ClientRpcReturn>
82+
export async function getDevToolsRpcClient(
83+
options: DevToolsRpcClientOptions & { cacheOptions: true },
84+
): Promise<ClientRpcReturn & { cacheManager: RpcCacheManager }>
85+
export async function getDevToolsRpcClient(
86+
options: DevToolsRpcClientOptions & { cacheOptions: Partial<RpcCacheOptions> },
87+
): Promise<ClientRpcReturn & { cacheManager: RpcCacheManager }>
88+
export async function getDevToolsRpcClient(
89+
options?: DevToolsRpcClientOptions,
90+
): Promise<ClientRpcReturn>
3291
export async function getDevToolsRpcClient(
3392
options: DevToolsRpcClientOptions = {},
3493
): Promise<ClientRpcReturn> {
3594
const {
3695
baseURL = '/.devtools/',
3796
rpcOptions = {},
38-
cacheResponse = false,
97+
cacheOptions = false,
3998
} = options
4099
const urls = Array.isArray(baseURL) ? baseURL : [baseURL]
41-
const responseCacheMap = new Map<string, unknown>()
42100
let connectionMeta: ConnectionMeta | undefined = options.connectionMeta
43101

44102
if (!connectionMeta) {
@@ -64,6 +122,7 @@ export async function getDevToolsRpcClient(
64122
? `${location.protocol.replace('http', 'ws')}//${location.hostname}:${connectionMeta.websocket}`
65123
: connectionMeta.websocket as string
66124

125+
const cacheManager = cacheOptions ? new RpcCacheManager({ functions: [], ...(typeof options.cacheOptions === 'object' ? options.cacheOptions : {}) }) : null
67126
const context: DevToolsClientContext = {
68127
rpc: undefined!,
69128
}
@@ -79,13 +138,14 @@ export async function getDevToolsRpcClient(
79138
...rpcOptions,
80139
onRequest: async (req, next, resolve) => {
81140
await rpcOptions.onRequest?.(req, next, resolve)
82-
if (cacheResponse) {
83-
const cacheKey = `${req.m}-${JSON.stringify(req.a)}`
84-
if (responseCacheMap.has(cacheKey)) {
85-
resolve(responseCacheMap.get(cacheKey))
141+
if (cacheOptions && cacheManager?.validate(req.m)) {
142+
const cached = cacheManager.cached(req.m, req.a)
143+
if (cached) {
144+
return resolve(cached)
86145
}
87146
else {
88-
responseCacheMap.set(cacheKey, await next(req))
147+
const res = await next(req)
148+
cacheManager?.apply(req, res)
89149
}
90150
}
91151
else {
@@ -98,19 +158,10 @@ export async function getDevToolsRpcClient(
98158
// @ts-expect-error assign to readonly property
99159
context.rpc = rpc
100160

101-
function invalidateCache(key?: string) {
102-
if (key) {
103-
responseCacheMap.delete(key)
104-
}
105-
else {
106-
responseCacheMap.clear()
107-
}
108-
}
109-
110161
return {
111162
connectionMeta,
112163
rpc,
113164
clientRpc,
114-
invalidateCache,
165+
...(cacheOptions ? { cacheManager } : {}),
115166
}
116167
}

packages/vite/src/app/composables/rpc.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export async function connect() {
2323
'/.devtools/',
2424
runtimeConfig.app.baseURL,
2525
],
26-
cacheResponse: true,
26+
cacheOptions: true,
2727
connectionMeta: runtimeConfig.app.connection,
2828
wsOptions: {
2929
onConnected: () => {
@@ -45,6 +45,16 @@ export async function connect() {
4545
})
4646

4747
rpc.value = result.rpc
48+
49+
const functions = await rpc.value.$call('vite:core:list-rpc-functions')
50+
51+
// TODO: add cacheable option to birpc-x and use it here
52+
// @ts-expect-error skip type check
53+
const cacheableFunctions = Object.keys(functions).filter(name => functions[name]?.cacheable)
54+
result.cacheManager.updateOptions({
55+
functions: [...cacheableFunctions],
56+
})
57+
4858
connectionState.connected = true
4959
}
5060
catch (e) {

0 commit comments

Comments
 (0)