Skip to content

Commit 267fc35

Browse files
antfuclaude
andauthored
docs: add WeakMap pattern for sharing state across RPC functions (#259)
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent bc844ff commit 267fc35

2 files changed

Lines changed: 119 additions & 0 deletions

File tree

docs/kit/rpc.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,74 @@ export default function setup(ctx: DockClientScriptContext) {
393393
}
394394
```
395395

396+
### Sharing State Across RPC Functions
397+
398+
When multiple RPC functions need access to the same plugin-specific state (a manager instance, plugin options, cached data, etc.), use a `WeakMap` keyed by `DevToolsNodeContext` to store and retrieve that state. This avoids mutating the base context object and keeps your plugin state scoped and garbage-collectable.
399+
400+
Create a helper file with get/set functions:
401+
402+
```ts
403+
// src/node/rpc/context.ts
404+
import type { DevToolsNodeContext } from '@vitejs/devtools-kit'
405+
406+
interface MyPluginContext {
407+
dataDir: string
408+
manager: DataManager
409+
}
410+
411+
const pluginContext = new WeakMap<DevToolsNodeContext, MyPluginContext>()
412+
413+
export function getPluginContext(ctx: DevToolsNodeContext): MyPluginContext {
414+
const value = pluginContext.get(ctx)
415+
if (!value)
416+
throw new Error('Plugin context not initialized')
417+
return value
418+
}
419+
420+
export function setPluginContext(ctx: DevToolsNodeContext, value: MyPluginContext) {
421+
pluginContext.set(ctx, value)
422+
}
423+
```
424+
425+
Initialize the state in your plugin's `devtools.setup`, then access it from any RPC function's `setup`:
426+
427+
::: code-group
428+
429+
```ts [plugin.ts]
430+
import { rpcFunctions } from './rpc'
431+
import { setPluginContext } from './rpc/context'
432+
433+
const plugin: Plugin = {
434+
devtools: {
435+
setup(ctx) {
436+
setPluginContext(ctx, {
437+
dataDir: resolve(ctx.cwd, 'data'),
438+
manager: new DataManager(),
439+
})
440+
rpcFunctions.forEach(fn => ctx.rpc.register(fn))
441+
},
442+
},
443+
}
444+
```
445+
446+
```ts [functions/get-data.ts]
447+
import { defineRpcFunction } from '@vitejs/devtools-kit'
448+
import { getPluginContext } from '../context'
449+
450+
export const getData = defineRpcFunction({
451+
name: 'my-plugin:get-data',
452+
type: 'query',
453+
setup: (ctx) => {
454+
const { manager } = getPluginContext(ctx)
455+
return {
456+
handler: async () => manager.getData(),
457+
}
458+
},
459+
})
460+
```
461+
462+
:::
463+
396464
### Global Client Context
397465

398466
Use `getDevToolsClientContext()` to access the client context (`DevToolsClientContext`) from anywhere on the client side. This is set automatically when DevTools initializes in embedded or standalone mode.

skills/vite-devtools-kit/references/rpc-patterns.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,57 @@ defineRpcFunction({
8585
})
8686
```
8787

88+
## Sharing State Across RPC Functions
89+
90+
When multiple RPC functions need shared plugin state (manager instances, options, cached data), use a `WeakMap<DevToolsNodeContext, T>` with get/set helpers instead of mutating the context object:
91+
92+
```ts
93+
// src/node/rpc/context.ts
94+
import type { DevToolsNodeContext } from '@vitejs/devtools-kit'
95+
96+
interface MyPluginContext {
97+
dataDir: string
98+
manager: DataManager
99+
}
100+
101+
const pluginContext = new WeakMap<DevToolsNodeContext, MyPluginContext>()
102+
103+
export function getPluginContext(ctx: DevToolsNodeContext): MyPluginContext {
104+
const value = pluginContext.get(ctx)
105+
if (!value)
106+
throw new Error('Plugin context not initialized')
107+
return value
108+
}
109+
110+
export function setPluginContext(ctx: DevToolsNodeContext, value: MyPluginContext) {
111+
pluginContext.set(ctx, value)
112+
}
113+
```
114+
115+
Initialize in plugin setup, access in RPC functions:
116+
117+
```ts
118+
// Plugin setup
119+
devtools: {
120+
setup(ctx) {
121+
setPluginContext(ctx, { dataDir: resolve(ctx.cwd, 'data'), manager: new DataManager() })
122+
rpcFunctions.forEach(fn => ctx.rpc.register(fn))
123+
},
124+
}
125+
126+
// RPC function
127+
const getData = defineRpcFunction({
128+
name: 'my-plugin:get-data',
129+
type: 'query',
130+
setup: (ctx) => {
131+
const { manager } = getPluginContext(ctx)
132+
return {
133+
handler: async () => manager.getData(),
134+
}
135+
},
136+
})
137+
```
138+
88139
## Broadcasting Patterns
89140

90141
### Basic Broadcast

0 commit comments

Comments
 (0)