99
1010/** Request definition: params P, response R */
1111export interface RequestDef < P = void , R = void > {
12+ readonly kind : "request" ;
1213 readonly method : string ;
1314 /** @internal Phantom types for inference - not present at runtime */
1415 readonly _types ?: { params : P ; response : R } ;
1516}
1617
1718/** Command definition: params P, no response */
1819export interface CommandDef < P = void > {
20+ readonly kind : "command" ;
1921 readonly method : string ;
2022 /** @internal Phantom type for inference - not present at runtime */
2123 readonly _types ?: { params : P } ;
2224}
2325
2426/** Notification definition: data D (extension to webview) */
2527export interface NotificationDef < D = void > {
28+ readonly kind : "notification" ;
2629 readonly method : string ;
2730 /** @internal Phantom type for inference - not present at runtime */
2831 readonly _types ?: { data : D } ;
@@ -34,19 +37,19 @@ export interface NotificationDef<D = void> {
3437export function defineRequest < P = void , R = void > (
3538 method : string ,
3639) : RequestDef < P , R > {
37- return { method } as RequestDef < P , R > ;
40+ return { kind : "request" , method } as RequestDef < P , R > ;
3841}
3942
4043/** Define a fire-and-forget command */
4144export function defineCommand < P = void > ( method : string ) : CommandDef < P > {
42- return { method } as CommandDef < P > ;
45+ return { kind : "command" , method } as CommandDef < P > ;
4346}
4447
4548/** Define a push notification (extension to webview) */
4649export function defineNotification < D = void > (
4750 method : string ,
4851) : NotificationDef < D > {
49- return { method } as NotificationDef < D > ;
52+ return { kind : "notification" , method } as NotificationDef < D > ;
5053}
5154
5255// --- Wire format ---
@@ -73,28 +76,135 @@ export interface IpcNotification<D = unknown> {
7376 readonly data ?: D ;
7477}
7578
76- // --- Handler utilities ---
77-
78- /** Extract params type from a request/command definition */
79- export type ParamsOf < T > = T extends { _types ?: { params : infer P } } ? P : void ;
80-
81- /** Extract response type from a request definition */
82- export type ResponseOf < T > = T extends { _types ?: { response : infer R } }
83- ? R
84- : void ;
79+ // --- Mapped types for handler completeness ---
80+
81+ /** Requires a handler for every RequestDef in Api. Compile error if one is missing. */
82+ export type RequestHandlerMap < Api > = {
83+ [ K in keyof Api as Api [ K ] extends { kind : "request" }
84+ ? K
85+ : never ] : Api [ K ] extends RequestDef < infer P , infer R >
86+ ? ( params : P ) => Promise < R >
87+ : never ;
88+ } ;
89+
90+ /** Requires a handler for every CommandDef in Api. Compile error if one is missing. */
91+ export type CommandHandlerMap < Api > = {
92+ [ K in keyof Api as Api [ K ] extends { kind : "command" }
93+ ? K
94+ : never ] : Api [ K ] extends CommandDef < infer P >
95+ ? ( params : P ) => void | Promise < void >
96+ : never ;
97+ } ;
98+
99+ // --- API hook type ---
100+
101+ /** Derives a fully typed hook interface from an API definition object. */
102+ export type ApiHook < Api > = {
103+ [ K in keyof Api as Api [ K ] extends { kind : "request" }
104+ ? K
105+ : never ] : Api [ K ] extends RequestDef < infer P , infer R >
106+ ? ( ...args : P extends void ? [ ] : [ params : P ] ) => Promise < R >
107+ : never ;
108+ } & {
109+ [ K in keyof Api as Api [ K ] extends { kind : "command" }
110+ ? K
111+ : never ] : Api [ K ] extends CommandDef < infer P >
112+ ? ( ...args : P extends void ? [ ] : [ params : P ] ) => void
113+ : never ;
114+ } & {
115+ [ K in keyof Api as Api [ K ] extends { kind : "notification" }
116+ ? `on${Capitalize < K & string > } `
117+ : never ] : Api [ K ] extends NotificationDef < infer D >
118+ ? D extends void
119+ ? ( cb : ( ) => void ) => ( ) => void
120+ : ( cb : ( data : D ) => void ) => ( ) => void
121+ : never ;
122+ } ;
123+
124+ // --- Builder functions ---
125+
126+ /** Build a method-indexed map of request handlers with compile-time completeness. */
127+ export function buildRequestHandlers <
128+ Api extends Record < string , { method : string } > ,
129+ > (
130+ api : Api ,
131+ handlers : RequestHandlerMap < Api > ,
132+ ) : Record < string , ( params : unknown ) => Promise < unknown > > ;
133+ export function buildRequestHandlers (
134+ api : Record < string , { method : string } > ,
135+ handlers : Record < string , ( params : unknown ) => Promise < unknown > > ,
136+ ) {
137+ const result : Record < string , ( params : unknown ) => Promise < unknown > > = { } ;
138+ for ( const key of Object . keys ( handlers ) ) {
139+ result [ api [ key ] . method ] = handlers [ key ] ;
140+ }
141+ return result ;
142+ }
85143
86- /** Type-safe request handler - infers params and return type from definition */
87- export function requestHandler < P , R > (
88- _def : RequestDef < P , R > ,
89- fn : ( params : P ) => Promise < R > ,
90- ) : ( params : unknown ) => Promise < unknown > {
91- return fn as ( params : unknown ) => Promise < unknown > ;
144+ /** Build a method-indexed map of command handlers with compile-time completeness. */
145+ export function buildCommandHandlers <
146+ Api extends Record < string , { method : string } > ,
147+ > (
148+ api : Api ,
149+ handlers : CommandHandlerMap < Api > ,
150+ ) : Record < string , ( params : unknown ) => void | Promise < void > > ;
151+ export function buildCommandHandlers (
152+ api : Record < string , { method : string } > ,
153+ handlers : Record < string , ( params : unknown ) => void | Promise < void > > ,
154+ ) {
155+ const result : Record < string , ( params : unknown ) => void | Promise < void > > = { } ;
156+ for ( const key of Object . keys ( handlers ) ) {
157+ result [ api [ key ] . method ] = handlers [ key ] ;
158+ }
159+ return result ;
92160}
93161
94- /** Type-safe command handler - infers params type from definition */
95- export function commandHandler < P > (
96- _def : CommandDef < P > ,
97- fn : ( params : P ) => void | Promise < void > ,
98- ) : ( params : unknown ) => void | Promise < void > {
99- return fn as ( params : unknown ) => void | Promise < void > ;
162+ /** Build a typed API hook from an API definition and IPC primitives. */
163+ export function buildApiHook <
164+ Api extends Record < string , { kind : string ; method : string } > ,
165+ > (
166+ api : Api ,
167+ ipc : {
168+ request : < P , R > (
169+ def : { method : string ; _types ?: { params : P ; response : R } } ,
170+ ...args : P extends void ? [ ] : [ params : P ]
171+ ) => Promise < R > ;
172+ command : < P > (
173+ def : { method : string ; _types ?: { params : P } } ,
174+ ...args : P extends void ? [ ] : [ params : P ]
175+ ) => void ;
176+ onNotification : < D > (
177+ def : { method : string ; _types ?: { data : D } } ,
178+ cb : ( data : D ) => void ,
179+ ) => ( ) => void ;
180+ } ,
181+ ) : ApiHook < Api > ;
182+ export function buildApiHook (
183+ api : Record < string , { kind : string ; method : string } > ,
184+ ipc : {
185+ request : ( def : { method : string } , params ?: unknown ) => Promise < unknown > ;
186+ command : ( def : { method : string } , params ?: unknown ) => void ;
187+ onNotification : (
188+ def : { method : string } ,
189+ cb : ( data : unknown ) => void ,
190+ ) => ( ) => void ;
191+ } ,
192+ ) {
193+ const result : Record < string , unknown > = { } ;
194+ for ( const [ key , def ] of Object . entries ( api ) ) {
195+ switch ( def . kind ) {
196+ case "request" :
197+ result [ key ] = ( params : unknown ) => ipc . request ( def , params ) ;
198+ break ;
199+ case "command" :
200+ result [ key ] = ( params : unknown ) => ipc . command ( def , params ) ;
201+ break ;
202+ case "notification" :
203+ result [ `on${ key [ 0 ] . toUpperCase ( ) } ${ key . slice ( 1 ) } ` ] = (
204+ cb : ( data : unknown ) => void ,
205+ ) => ipc . onNotification ( def , cb ) ;
206+ break ;
207+ }
208+ }
209+ return result ;
100210}
0 commit comments