|
| 1 | +/** |
| 2 | + * Type-safe IPC protocol for VS Code webview communication. |
| 3 | + * |
| 4 | + * Inspired by tRPC's approach: types are carried in a phantom `_types` property |
| 5 | + * that exists only for TypeScript inference, not at runtime. |
| 6 | + */ |
| 7 | + |
| 8 | +// --- Message definitions --- |
| 9 | + |
| 10 | +/** Request definition: params P, response R */ |
| 11 | +export interface RequestDef<P = void, R = void> { |
| 12 | + readonly method: string; |
| 13 | + readonly scope?: string; |
| 14 | + /** @internal Phantom types for inference - not present at runtime */ |
| 15 | + readonly _types?: { params: P; response: R }; |
| 16 | +} |
| 17 | + |
| 18 | +/** Command definition: params P, no response */ |
| 19 | +export interface CommandDef<P = void> { |
| 20 | + readonly method: string; |
| 21 | + readonly scope?: string; |
| 22 | + /** @internal Phantom type for inference - not present at runtime */ |
| 23 | + readonly _types?: { params: P }; |
| 24 | +} |
| 25 | + |
| 26 | +/** Notification definition: data D (extension to webview) */ |
| 27 | +export interface NotificationDef<D = void> { |
| 28 | + readonly method: string; |
| 29 | + readonly scope?: string; |
| 30 | + /** @internal Phantom type for inference - not present at runtime */ |
| 31 | + readonly _types?: { data: D }; |
| 32 | +} |
| 33 | + |
| 34 | +// --- Factory functions --- |
| 35 | + |
| 36 | +/** Define a request with typed params and response */ |
| 37 | +export function defineRequest<P = void, R = void>( |
| 38 | + method: string, |
| 39 | + scope?: string, |
| 40 | +): RequestDef<P, R> { |
| 41 | + return { method, scope } as RequestDef<P, R>; |
| 42 | +} |
| 43 | + |
| 44 | +/** Define a fire-and-forget command */ |
| 45 | +export function defineCommand<P = void>( |
| 46 | + method: string, |
| 47 | + scope?: string, |
| 48 | +): CommandDef<P> { |
| 49 | + return { method, scope } as CommandDef<P>; |
| 50 | +} |
| 51 | + |
| 52 | +/** Define a push notification (extension to webview) */ |
| 53 | +export function defineNotification<D = void>( |
| 54 | + method: string, |
| 55 | + scope?: string, |
| 56 | +): NotificationDef<D> { |
| 57 | + return { method, scope } as NotificationDef<D>; |
| 58 | +} |
| 59 | + |
| 60 | +// --- Wire format --- |
| 61 | + |
| 62 | +/** Request from webview to extension */ |
| 63 | +export interface IpcRequest<P = unknown> { |
| 64 | + readonly requestId: string; |
| 65 | + readonly method: string; |
| 66 | + readonly scope?: string; |
| 67 | + readonly params?: P; |
| 68 | +} |
| 69 | + |
| 70 | +/** Response from extension to webview */ |
| 71 | +export interface IpcResponse<T = unknown> { |
| 72 | + readonly requestId: string; |
| 73 | + readonly method: string; |
| 74 | + readonly success: boolean; |
| 75 | + readonly data?: T; |
| 76 | + readonly error?: string; |
| 77 | +} |
| 78 | + |
| 79 | +/** Push notification from extension to webview */ |
| 80 | +export interface IpcNotification<D = unknown> { |
| 81 | + readonly type: string; |
| 82 | + readonly data?: D; |
| 83 | +} |
| 84 | + |
| 85 | +// --- Handler utilities --- |
| 86 | + |
| 87 | +/** Extract params type from a request/command definition */ |
| 88 | +export type ParamsOf<T> = T extends { _types?: { params: infer P } } ? P : void; |
| 89 | + |
| 90 | +/** Extract response type from a request definition */ |
| 91 | +export type ResponseOf<T> = T extends { _types?: { response: infer R } } |
| 92 | + ? R |
| 93 | + : void; |
| 94 | + |
| 95 | +/** Type-safe request handler - infers params and return type from definition */ |
| 96 | +export function requestHandler<P, R>( |
| 97 | + _def: RequestDef<P, R>, |
| 98 | + fn: (params: P) => Promise<R>, |
| 99 | +): (params: unknown) => Promise<unknown> { |
| 100 | + return fn as (params: unknown) => Promise<unknown>; |
| 101 | +} |
| 102 | + |
| 103 | +/** Type-safe command handler - infers params type from definition */ |
| 104 | +export function commandHandler<P>( |
| 105 | + _def: CommandDef<P>, |
| 106 | + fn: (params: P) => void | Promise<void>, |
| 107 | +): (params: unknown) => void | Promise<void> { |
| 108 | + return fn as (params: unknown) => void | Promise<void>; |
| 109 | +} |
0 commit comments