@frak-labs/frame-connector 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +234 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +837 -0
- package/dist/index.d.ts +837 -0
- package/dist/index.js +1 -0
- package/dist/middleware.cjs +1 -0
- package/dist/middleware.d.cts +251 -0
- package/dist/middleware.d.ts +251 -0
- package/dist/middleware.js +1 -0
- package/package.json +93 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
import type { Prettify } from 'viem';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Union of all message types that can be received
|
|
5
|
+
*/
|
|
6
|
+
export declare type AnyMessage = RpcMessage | LifecycleMessage;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Lifecycle message format for client-to-iframe communication
|
|
10
|
+
* These messages handle connection lifecycle events (handshake, heartbeat, etc.)
|
|
11
|
+
*/
|
|
12
|
+
export declare type ClientLifecycleMessage = {
|
|
13
|
+
clientLifecycle: string;
|
|
14
|
+
data?: unknown;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** @ignore */
|
|
18
|
+
export declare class ClientNotFound extends FrakRpcError {
|
|
19
|
+
constructor();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The received encoded data from a client
|
|
24
|
+
* -> The encoded should contain a HashProtectedData once decoded
|
|
25
|
+
*/
|
|
26
|
+
export declare type CompressedData = Uint8Array;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Compress JSON data using CBOR encoding
|
|
30
|
+
*
|
|
31
|
+
* @param data - The data to compress
|
|
32
|
+
* @returns CBOR-encoded data
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const compressed = compressJson({ foo: 'bar' })
|
|
37
|
+
* // Returns Uint8Array with CBOR-encoded data
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function compressJson(data: unknown): Uint8Array;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Client-side compression middleware
|
|
44
|
+
*
|
|
45
|
+
* Compresses outgoing requests and decompresses incoming responses.
|
|
46
|
+
* Always uses the format: {method: string, params: unknown}
|
|
47
|
+
*
|
|
48
|
+
* @example Client side
|
|
49
|
+
* ```ts
|
|
50
|
+
* const client = createRpcClient({
|
|
51
|
+
* transport: iframe.contentWindow,
|
|
52
|
+
* targetOrigin: 'https://wallet.frak.id',
|
|
53
|
+
* middleware: [createClientCompressionMiddleware()]
|
|
54
|
+
* })
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare const createClientCompressionMiddleware: <TSchema extends RpcSchema, TContext>() => RpcMiddleware<TSchema, TContext>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Listener-side compression middleware
|
|
61
|
+
*
|
|
62
|
+
* Decompresses incoming requests and compresses outgoing responses.
|
|
63
|
+
* Always uses the format: {method: string, params: unknown}
|
|
64
|
+
*
|
|
65
|
+
* @example Listener side
|
|
66
|
+
* ```ts
|
|
67
|
+
* const listener = createRpcListener({
|
|
68
|
+
* transport: window,
|
|
69
|
+
* allowedOrigins: ['https://example.com'],
|
|
70
|
+
* middleware: [createListenerCompressionMiddleware()]
|
|
71
|
+
* })
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare const createListenerCompressionMiddleware: <TSchema extends RpcSchema, TContext>() => RpcMiddleware<TSchema, TContext>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Create an RPC client for SDK-side communication
|
|
78
|
+
*
|
|
79
|
+
* @typeParam TSchema - The RPC schema type
|
|
80
|
+
* @typeParam TLifecycleEvent - Lifecycle event union type (e.g., ClientLifecycleEvent | IFrameLifecycleEvent)
|
|
81
|
+
* @param config - Client configuration
|
|
82
|
+
* @returns RPC client instance
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* import type { IFrameRpcSchema, ClientLifecycleEvent, IFrameLifecycleEvent } from '@frak-labs/core-sdk'
|
|
87
|
+
*
|
|
88
|
+
* const client = createRpcClient<IFrameRpcSchema, ClientLifecycleEvent | IFrameLifecycleEvent>({
|
|
89
|
+
* emittingTransport: window,
|
|
90
|
+
* listeningTransport: window,
|
|
91
|
+
* targetOrigin: 'https://wallet.frak.id',
|
|
92
|
+
* lifecycleHandlers: {
|
|
93
|
+
* iframeLifecycle: (event, data) => {
|
|
94
|
+
* // event and data are now strongly typed!
|
|
95
|
+
* }
|
|
96
|
+
* }
|
|
97
|
+
* })
|
|
98
|
+
*
|
|
99
|
+
* // One-shot request
|
|
100
|
+
* const result = await client.request('frak_sendInteraction', [productId, interaction])
|
|
101
|
+
*
|
|
102
|
+
* // Listener
|
|
103
|
+
* const unsubscribe = client.listen('frak_listenToWalletStatus', (status) => {
|
|
104
|
+
* console.log('Wallet status:', status)
|
|
105
|
+
* })
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export declare function createRpcClient<TSchema extends RpcSchema, TLifecycleEvent extends LifecycleMessage = LifecycleMessage>(config: RpcClientConfig<TSchema, TLifecycleEvent>): RpcClient<TSchema, TLifecycleEvent>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Create an RPC listener for Wallet-side communication
|
|
112
|
+
*
|
|
113
|
+
* Supports multiple schemas via union types, enabling a single listener to handle
|
|
114
|
+
* different RPC protocols (e.g., IFrameRpcSchema | SsoRpcSchema).
|
|
115
|
+
*
|
|
116
|
+
* @typeParam TSchema - The RPC schema type (can be a union of multiple schemas)
|
|
117
|
+
* @typeParam TContext - Custom context type augmented by middleware
|
|
118
|
+
* @typeParam TLifecycleEvent - Lifecycle event union type (e.g., ClientLifecycleEvent | IFrameLifecycleEvent)
|
|
119
|
+
* @param config - Listener configuration
|
|
120
|
+
* @returns RPC listener instance
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* import type { IFrameRpcSchema, SsoRpcSchema, ClientLifecycleEvent, IFrameLifecycleEvent } from '@frak-labs/core-sdk'
|
|
125
|
+
*
|
|
126
|
+
* // Single schema
|
|
127
|
+
* const listener = createRpcListener<IFrameRpcSchema>({
|
|
128
|
+
* transport: window,
|
|
129
|
+
* allowedOrigins: ['https://example.com']
|
|
130
|
+
* })
|
|
131
|
+
*
|
|
132
|
+
* // Multiple schemas (union type) with lifecycle events
|
|
133
|
+
* type CombinedSchema = IFrameRpcSchema | SsoRpcSchema
|
|
134
|
+
* const listener = createRpcListener<CombinedSchema, WalletContext, ClientLifecycleEvent | IFrameLifecycleEvent>({
|
|
135
|
+
* transport: window,
|
|
136
|
+
* allowedOrigins: '*',
|
|
137
|
+
* middleware: [compressionMiddleware, contextMiddleware],
|
|
138
|
+
* lifecycleHandlers: {
|
|
139
|
+
* clientLifecycle: (event, data, context) => {
|
|
140
|
+
* // event and data are now strongly typed!
|
|
141
|
+
* }
|
|
142
|
+
* }
|
|
143
|
+
* })
|
|
144
|
+
*
|
|
145
|
+
* // Register handlers for IFrame methods
|
|
146
|
+
* listener.handle('frak_sendInteraction', async (params, context) => {
|
|
147
|
+
* return { status: 'success', hash: '0x...' }
|
|
148
|
+
* })
|
|
149
|
+
*
|
|
150
|
+
* // Register handlers for SSO methods
|
|
151
|
+
* listener.handle('sso_complete', async (params, context) => {
|
|
152
|
+
* const [session, sdkJwt, ssoId] = params
|
|
153
|
+
* return { success: true }
|
|
154
|
+
* })
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export declare function createRpcListener<TSchema extends RpcSchema, TContext = Record<string, never>, TLifecycleEvent extends LifecycleMessage = LifecycleMessage>(config: RpcListenerConfig<TSchema, TContext, TLifecycleEvent>): RpcListener<TSchema, TContext>;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Decompress and validate hash-protected data
|
|
161
|
+
*
|
|
162
|
+
* Security:
|
|
163
|
+
* - Validates hash to ensure data integrity
|
|
164
|
+
* - Throws RpcError if hash validation fails
|
|
165
|
+
* - Prevents corrupted or tampered messages from processing
|
|
166
|
+
*
|
|
167
|
+
* @param compressedData - The compressed data to decompress
|
|
168
|
+
* @returns The decompressed data with validation hash
|
|
169
|
+
* @throws {Error} If decompression fails or hash validation fails
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```ts
|
|
173
|
+
* const decompressed = decompressDataAndCheckHash(compressedData)
|
|
174
|
+
* // Returns { foo: 'bar', baz: 123, validationHash: '0x...' }
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
export declare function decompressDataAndCheckHash<T>(compressedData: CompressedData): HashProtectedData<T>;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Decompress CBOR-encoded data
|
|
181
|
+
*
|
|
182
|
+
* @param data - The compressed data
|
|
183
|
+
* @returns Decompressed data or null if decompression fails
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```ts
|
|
187
|
+
* const decompressed = decompressJson<MyType>(compressedData)
|
|
188
|
+
* if (decompressed) {
|
|
189
|
+
* // Use decompressed data
|
|
190
|
+
* }
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
export declare function decompressJson<T>(data: Uint8Array): T | null;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Simple deferred promise wrapper
|
|
197
|
+
* @ignore
|
|
198
|
+
*/
|
|
199
|
+
export declare class Deferred<T> {
|
|
200
|
+
private readonly _promise;
|
|
201
|
+
private _resolve;
|
|
202
|
+
private _reject;
|
|
203
|
+
constructor();
|
|
204
|
+
get promise(): Promise<T>;
|
|
205
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
206
|
+
reject: (reason?: unknown) => void;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Type that extract the possible parameters from a RPC Schema
|
|
211
|
+
* @ignore
|
|
212
|
+
*/
|
|
213
|
+
export declare type ExtractedParametersFromRpc<TRpcSchema extends RpcSchema> = {
|
|
214
|
+
[K in keyof TRpcSchema]: Prettify<{
|
|
215
|
+
method: TRpcSchema[K] extends TRpcSchema[number] ? TRpcSchema[K]["Method"] : string;
|
|
216
|
+
} & (TRpcSchema[K] extends TRpcSchema[number] ? TRpcSchema[K]["Parameters"] extends undefined ? {
|
|
217
|
+
params?: never;
|
|
218
|
+
} : {
|
|
219
|
+
params: TRpcSchema[K]["Parameters"];
|
|
220
|
+
} : never)>;
|
|
221
|
+
}[number];
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Type that extract the possible parameters from a RPC Schema
|
|
225
|
+
* @ignore
|
|
226
|
+
*/
|
|
227
|
+
declare type ExtractedSpecificParametersFromRpc<TRpcSchema extends RpcSchema, TMethod extends ExtractMethod<TRpcSchema>> = Extract<ExtractedParametersFromRpc<TRpcSchema>, {
|
|
228
|
+
method: TMethod;
|
|
229
|
+
}>;
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Extract method names from a schema
|
|
233
|
+
*
|
|
234
|
+
* @typeParam TSchema - The RPC schema type
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```ts
|
|
238
|
+
* type Methods = ExtractMethod<MySchema>
|
|
239
|
+
* // "greet" | "watchTime"
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
export declare type ExtractMethod<TSchema extends RpcSchema> = TSchema[number]["Method"];
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Extract parameters type for a specific method
|
|
246
|
+
*
|
|
247
|
+
* @typeParam TSchema - The RPC schema type
|
|
248
|
+
* @typeParam TMethod - The method name
|
|
249
|
+
*
|
|
250
|
+
* @example
|
|
251
|
+
* ```ts
|
|
252
|
+
* type GreetParams = ExtractParams<MySchema, "greet">
|
|
253
|
+
* // [name: string]
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
export declare type ExtractParams<TSchema extends RpcSchema, TMethod extends ExtractMethod<TSchema>> = ExtractSchemaEntry<TSchema, TMethod>["Parameters"];
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Extract return type for a specific method
|
|
260
|
+
*
|
|
261
|
+
* @typeParam TSchema - The RPC schema type
|
|
262
|
+
* @typeParam TMethod - The method name
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```ts
|
|
266
|
+
* type GreetReturn = ExtractReturnType<MySchema, "greet">
|
|
267
|
+
* // string
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
export declare type ExtractReturnType<TSchema extends RpcSchema, TMethod extends ExtractMethod<TSchema>> = ExtractSchemaEntry<TSchema, TMethod>["ReturnType"];
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Extract a specific schema entry by method name
|
|
274
|
+
*
|
|
275
|
+
* @typeParam TSchema - The RPC schema type
|
|
276
|
+
* @typeParam TMethod - The method name to extract
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```ts
|
|
280
|
+
* type GreetEntry = ExtractSchemaEntry<MySchema, "greet">
|
|
281
|
+
* // { Method: "greet"; Parameters: [name: string]; ReturnType: string; ResponseType: "promise" }
|
|
282
|
+
* ```
|
|
283
|
+
*/
|
|
284
|
+
export declare type ExtractSchemaEntry<TSchema extends RpcSchema, TMethod extends ExtractMethod<TSchema>> = Extract<TSchema[number], {
|
|
285
|
+
Method: TMethod;
|
|
286
|
+
}>;
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Generic Frak RPC error
|
|
290
|
+
* @ignore
|
|
291
|
+
*/
|
|
292
|
+
export declare class FrakRpcError<T = undefined> extends Error {
|
|
293
|
+
code: number;
|
|
294
|
+
data?: T | undefined;
|
|
295
|
+
constructor(code: number, message: string, data?: T | undefined);
|
|
296
|
+
toJSON(): RpcError;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Compress the given data with hash protection to prevent tampering
|
|
301
|
+
*
|
|
302
|
+
* Performance considerations:
|
|
303
|
+
* - CBOR encoding is more compact than JSON and faster to parse
|
|
304
|
+
* - Hash validation prevents man-in-the-middle modifications
|
|
305
|
+
* - Single-pass encoding minimizes allocations
|
|
306
|
+
*
|
|
307
|
+
* @param data - The data to compress and protect
|
|
308
|
+
* @returns Compressed CBOR-encoded data with validation hash
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```ts
|
|
312
|
+
* const compressed = hashAndCompressData({ foo: 'bar', baz: 123 })
|
|
313
|
+
* // Returns Uint8Array with CBOR-encoded data + validation hash
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
export declare function hashAndCompressData<T>(data: T): CompressedData;
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* The encoded data to send to a client / received by a client
|
|
320
|
+
*/
|
|
321
|
+
export declare type HashProtectedData<DataType> = Readonly<DataType & {
|
|
322
|
+
validationHash: string;
|
|
323
|
+
}>;
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Lifecycle message format for iframe-to-client communication
|
|
327
|
+
*/
|
|
328
|
+
export declare type IFrameLifecycleMessage = {
|
|
329
|
+
iframeLifecycle: string;
|
|
330
|
+
data?: unknown;
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
/** @ignore */
|
|
334
|
+
export declare class InternalError extends FrakRpcError {
|
|
335
|
+
constructor(message: string);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Lifecycle handler function
|
|
340
|
+
* Handles lifecycle events using discriminated unions for automatic type narrowing
|
|
341
|
+
*
|
|
342
|
+
* @typeParam TLifecycleEvent - The lifecycle event union type (e.g., ClientLifecycleEvent | IFrameLifecycleEvent)
|
|
343
|
+
*
|
|
344
|
+
* @param event - The full lifecycle event object with discriminated union
|
|
345
|
+
* @param context - Request context with origin and source
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* ```ts
|
|
349
|
+
* const handler: LifecycleHandler<ClientLifecycleEvent> = (event, context) => {
|
|
350
|
+
* if (event.clientLifecycle === "modal-css") {
|
|
351
|
+
* // event.data is automatically typed as { cssLink: string }
|
|
352
|
+
* console.log(event.data.cssLink)
|
|
353
|
+
* }
|
|
354
|
+
* }
|
|
355
|
+
* ```
|
|
356
|
+
*/
|
|
357
|
+
export declare type LifecycleHandler<TLifecycleEvent = unknown> = (event: TLifecycleEvent, context: RpcRequestContext) => void | Promise<void>;
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Union of all lifecycle message types
|
|
361
|
+
*/
|
|
362
|
+
export declare type LifecycleMessage = ClientLifecycleMessage | IFrameLifecycleMessage;
|
|
363
|
+
|
|
364
|
+
/** @ignore */
|
|
365
|
+
export declare class MethodNotFoundError extends FrakRpcError<{
|
|
366
|
+
method: string;
|
|
367
|
+
}> {
|
|
368
|
+
constructor(message: string, method: string);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* RPC Client interface
|
|
373
|
+
* Provides methods for making RPC calls to the wallet
|
|
374
|
+
*
|
|
375
|
+
* @typeParam TSchema - The RPC schema type
|
|
376
|
+
*/
|
|
377
|
+
export declare type RpcClient<TSchema extends RpcSchema, TLifecycleEvent extends LifecycleMessage> = {
|
|
378
|
+
/**
|
|
379
|
+
* Make a one-shot request that returns a promise
|
|
380
|
+
* Used for methods with ResponseType: "promise"
|
|
381
|
+
*/
|
|
382
|
+
request: <TMethod extends ExtractMethod<TSchema>>(args: ExtractedSpecificParametersFromRpc<TSchema, TMethod>) => Promise<ExtractReturnType<TSchema, TMethod>>;
|
|
383
|
+
/**
|
|
384
|
+
* Subscribe to a listener method with a callback
|
|
385
|
+
* Used for methods with ResponseType: "stream"
|
|
386
|
+
* Returns an unsubscribe function
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* ```ts
|
|
390
|
+
* const unsubscribe = client.listen('frak_listenToWalletStatus', (status) => {
|
|
391
|
+
* console.log('Status:', status)
|
|
392
|
+
* })
|
|
393
|
+
*
|
|
394
|
+
* // Later, unsubscribe
|
|
395
|
+
* unsubscribe()
|
|
396
|
+
* ```
|
|
397
|
+
*/
|
|
398
|
+
listen: <TMethod extends ExtractMethod<TSchema>>(args: ExtractedSpecificParametersFromRpc<TSchema, TMethod>, callback: (result: ExtractReturnType<TSchema, TMethod>) => void) => () => void;
|
|
399
|
+
/**
|
|
400
|
+
* Send a lifecycle event to the server
|
|
401
|
+
* Bypasses middleware and is used for connection management
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* ```ts
|
|
405
|
+
* client.sendLifecycle({ clientLifecycle: 'heartbeat' })
|
|
406
|
+
* client.sendLifecycle({ clientLifecycle: 'modal-css', data: { cssLink: '...' } })
|
|
407
|
+
* ```
|
|
408
|
+
*/
|
|
409
|
+
sendLifecycle: (message: TLifecycleEvent) => void;
|
|
410
|
+
/**
|
|
411
|
+
* Clean up resources and close connections
|
|
412
|
+
*/
|
|
413
|
+
cleanup: () => void;
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* RPC Client configuration
|
|
418
|
+
*
|
|
419
|
+
* @typeParam TSchema - The RPC schema type
|
|
420
|
+
* @typeParam TLifecycleEvent - Lifecycle event union type (e.g., ClientLifecycleEvent | IFrameLifecycleEvent)
|
|
421
|
+
*/
|
|
422
|
+
export declare type RpcClientConfig<TSchema extends RpcSchema, TLifecycleEvent = unknown> = {
|
|
423
|
+
/**
|
|
424
|
+
* The transport to use for emitting events (e.g., window or iframe.contentWindow)
|
|
425
|
+
*/
|
|
426
|
+
emittingTransport: RpcTransport;
|
|
427
|
+
/**
|
|
428
|
+
* The transport to use for listening to events (e.g., window or iframe.contentWindow)
|
|
429
|
+
*/
|
|
430
|
+
listeningTransport: RpcTransport;
|
|
431
|
+
/**
|
|
432
|
+
* The target origin for postMessage
|
|
433
|
+
*/
|
|
434
|
+
targetOrigin: string;
|
|
435
|
+
/**
|
|
436
|
+
* Middleware stack (executed in order)
|
|
437
|
+
* Middleware can transform outgoing requests and incoming responses
|
|
438
|
+
* Client-side middleware uses empty context {}
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* ```ts
|
|
442
|
+
* middleware: [
|
|
443
|
+
* compressionMiddleware, // Compress outgoing, decompress incoming
|
|
444
|
+
* loggingMiddleware, // Log RPC calls
|
|
445
|
+
* ]
|
|
446
|
+
* ```
|
|
447
|
+
*/
|
|
448
|
+
middleware?: RpcMiddleware<TSchema>[];
|
|
449
|
+
/**
|
|
450
|
+
* Lifecycle event handlers
|
|
451
|
+
* Handles incoming lifecycle events from the server
|
|
452
|
+
*
|
|
453
|
+
* @example
|
|
454
|
+
* ```ts
|
|
455
|
+
* lifecycleHandlers: {
|
|
456
|
+
* iframeLifecycle: (event, data) => {
|
|
457
|
+
* if (event === 'connected') {
|
|
458
|
+
* console.log('Wallet ready')
|
|
459
|
+
* }
|
|
460
|
+
* }
|
|
461
|
+
* }
|
|
462
|
+
* ```
|
|
463
|
+
*/
|
|
464
|
+
lifecycleHandlers?: {
|
|
465
|
+
clientLifecycle?: LifecycleHandler<Extract<TLifecycleEvent, {
|
|
466
|
+
clientLifecycle: string;
|
|
467
|
+
}>>;
|
|
468
|
+
iframeLifecycle?: LifecycleHandler<Extract<TLifecycleEvent, {
|
|
469
|
+
iframeLifecycle: string;
|
|
470
|
+
}>>;
|
|
471
|
+
};
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* RPC error object
|
|
476
|
+
*/
|
|
477
|
+
export declare type RpcError = {
|
|
478
|
+
code: number;
|
|
479
|
+
message: string;
|
|
480
|
+
data?: unknown;
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* RPC error codes
|
|
485
|
+
* Follows JSON-RPC 2.0 specification with Frak-specific extensions
|
|
486
|
+
*/
|
|
487
|
+
export declare const RpcErrorCodes: {
|
|
488
|
+
readonly parseError: -32700;
|
|
489
|
+
readonly invalidRequest: -32600;
|
|
490
|
+
readonly methodNotFound: -32601;
|
|
491
|
+
readonly invalidParams: -32602;
|
|
492
|
+
readonly internalError: -32603;
|
|
493
|
+
readonly serverError: -32000;
|
|
494
|
+
readonly clientNotConnected: -32001;
|
|
495
|
+
readonly configError: -32002;
|
|
496
|
+
readonly corruptedResponse: -32003;
|
|
497
|
+
readonly clientAborted: -32004;
|
|
498
|
+
readonly walletNotConnected: -32005;
|
|
499
|
+
readonly serverErrorForInteractionDelegation: -32006;
|
|
500
|
+
readonly userRejected: -32007;
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* RPC Listener interface
|
|
505
|
+
* Handles incoming RPC requests from the SDK
|
|
506
|
+
*
|
|
507
|
+
* @typeParam TSchema - The RPC schema type
|
|
508
|
+
* @typeParam TContext - Custom context type augmented by middleware
|
|
509
|
+
*/
|
|
510
|
+
export declare type RpcListener<TSchema extends RpcSchema, TContext = Record<string, never>> = {
|
|
511
|
+
/**
|
|
512
|
+
* Register a handler for a promise-based method
|
|
513
|
+
*/
|
|
514
|
+
handle: <TMethod extends ExtractMethod<TSchema>>(method: TMethod, handler: RpcPromiseHandler<TSchema, TMethod, TContext>) => void;
|
|
515
|
+
/**
|
|
516
|
+
* Register a handler for a streaming method
|
|
517
|
+
*/
|
|
518
|
+
handleStream: <TMethod extends ExtractMethod<TSchema>>(method: TMethod, handler: RpcStreamHandler<TSchema, TMethod, TContext>) => void;
|
|
519
|
+
/**
|
|
520
|
+
* Unregister a handler
|
|
521
|
+
*/
|
|
522
|
+
unregister: (method: ExtractMethod<TSchema>) => void;
|
|
523
|
+
/**
|
|
524
|
+
* Clean up resources
|
|
525
|
+
*/
|
|
526
|
+
cleanup: () => void;
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* RPC Listener configuration
|
|
531
|
+
*
|
|
532
|
+
* @typeParam TSchema - The RPC schema type
|
|
533
|
+
* @typeParam TContext - Custom context type to augment base context
|
|
534
|
+
* @typeParam TLifecycleEvent - Lifecycle event union type (e.g., ClientLifecycleEvent | IFrameLifecycleEvent)
|
|
535
|
+
*/
|
|
536
|
+
export declare type RpcListenerConfig<TSchema extends RpcSchema, TContext = Record<string, never>, TLifecycleEvent extends LifecycleMessage = LifecycleMessage> = {
|
|
537
|
+
/**
|
|
538
|
+
* The transport to use for communication (e.g., window)
|
|
539
|
+
*/
|
|
540
|
+
transport: RpcTransport;
|
|
541
|
+
/**
|
|
542
|
+
* Allowed origins for security
|
|
543
|
+
* Can be a single origin or array of origins
|
|
544
|
+
*/
|
|
545
|
+
allowedOrigins: string | string[];
|
|
546
|
+
/**
|
|
547
|
+
* Middleware stack (executed in order)
|
|
548
|
+
* Middleware can augment context, validate requests, and transform responses
|
|
549
|
+
*
|
|
550
|
+
* Note: Middleware only applies to RPC messages, not lifecycle or custom messages
|
|
551
|
+
*
|
|
552
|
+
* @example
|
|
553
|
+
* ```ts
|
|
554
|
+
* middleware: [
|
|
555
|
+
* loggingMiddleware,
|
|
556
|
+
* compressionMiddleware,
|
|
557
|
+
* contextAugmentationMiddleware
|
|
558
|
+
* ]
|
|
559
|
+
* ```
|
|
560
|
+
*/
|
|
561
|
+
middleware?: RpcMiddleware<TSchema, TContext>[];
|
|
562
|
+
/**
|
|
563
|
+
* Lifecycle event handlers
|
|
564
|
+
* Handles client-to-iframe and iframe-to-client lifecycle events
|
|
565
|
+
*
|
|
566
|
+
* @example
|
|
567
|
+
* ```ts
|
|
568
|
+
* lifecycleHandlers: {
|
|
569
|
+
* clientLifecycle: (event, data, context) => {
|
|
570
|
+
* if (event === 'heartbeat') {
|
|
571
|
+
* console.log('Client heartbeat received')
|
|
572
|
+
* }
|
|
573
|
+
* }
|
|
574
|
+
* }
|
|
575
|
+
* ```
|
|
576
|
+
*/
|
|
577
|
+
lifecycleHandlers?: {
|
|
578
|
+
clientLifecycle?: LifecycleHandler<Extract<TLifecycleEvent, {
|
|
579
|
+
clientLifecycle: string;
|
|
580
|
+
}>>;
|
|
581
|
+
iframeLifecycle?: LifecycleHandler<Extract<TLifecycleEvent, {
|
|
582
|
+
iframeLifecycle: string;
|
|
583
|
+
}>>;
|
|
584
|
+
};
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* RPC message format (maintains backward compatibility)
|
|
589
|
+
* This is the exact format sent over the wire
|
|
590
|
+
*
|
|
591
|
+
* @typeParam TMethod - The method name type (defaults to string for flexibility)
|
|
592
|
+
*/
|
|
593
|
+
export declare type RpcMessage<TMethod extends string = string> = {
|
|
594
|
+
/**
|
|
595
|
+
* Unique message identifier for correlating requests and responses
|
|
596
|
+
*/
|
|
597
|
+
id: string;
|
|
598
|
+
/**
|
|
599
|
+
* The RPC method name (topic for backward compatibility)
|
|
600
|
+
*/
|
|
601
|
+
topic: TMethod;
|
|
602
|
+
/**
|
|
603
|
+
* The message payload (compressed data) or raw params
|
|
604
|
+
*/
|
|
605
|
+
data: unknown;
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Unified middleware function for RPC requests (both listener and client)
|
|
610
|
+
* Works on both listener-side (with context augmentation) and client-side (empty context)
|
|
611
|
+
*
|
|
612
|
+
* Key features:
|
|
613
|
+
* - Can mutate message.data directly for efficiency (compression, validation)
|
|
614
|
+
* - Can mutate response.result directly for transformation
|
|
615
|
+
* - Listener-side: Can augment context by returning modified context
|
|
616
|
+
* - Client-side: Uses TContext = {} (empty context), always returns unchanged
|
|
617
|
+
*
|
|
618
|
+
* @typeParam TSchema - The RPC schema type
|
|
619
|
+
* @typeParam TContext - Custom context type to augment base context (empty {} for client-side)
|
|
620
|
+
*
|
|
621
|
+
* @example Listener-side with context augmentation
|
|
622
|
+
* ```ts
|
|
623
|
+
* type WalletContext = { productId: string, sourceUrl: string }
|
|
624
|
+
* const contextMiddleware: RpcMiddleware<MySchema, WalletContext> = {
|
|
625
|
+
* onRequest: async (message, context) => {
|
|
626
|
+
* // Read from store and augment context
|
|
627
|
+
* const productId = await getProductId(context.origin)
|
|
628
|
+
* return { ...context, productId, sourceUrl: context.origin }
|
|
629
|
+
* }
|
|
630
|
+
* }
|
|
631
|
+
* ```
|
|
632
|
+
*
|
|
633
|
+
* @example Client-side (empty context)
|
|
634
|
+
* ```ts
|
|
635
|
+
* const compressionMiddleware: RpcMiddleware<MySchema> = {
|
|
636
|
+
* onRequest: async (message, context) => {
|
|
637
|
+
* // Mutate message.data directly
|
|
638
|
+
* message.data = compress(message.data)
|
|
639
|
+
* return context // Empty context, unchanged
|
|
640
|
+
* },
|
|
641
|
+
* onResponse: async (message, response, context) => {
|
|
642
|
+
* // Mutate response.result directly
|
|
643
|
+
* response.result = decompress(response.result)
|
|
644
|
+
* return response
|
|
645
|
+
* }
|
|
646
|
+
* }
|
|
647
|
+
* ```
|
|
648
|
+
*
|
|
649
|
+
* @example Shared middleware (works on both sides)
|
|
650
|
+
* ```ts
|
|
651
|
+
* const loggingMiddleware: RpcMiddleware<MySchema> = {
|
|
652
|
+
* onRequest: async (message, context) => {
|
|
653
|
+
* console.log(`[RPC] ${message.topic}`, context.origin || 'client')
|
|
654
|
+
* return context
|
|
655
|
+
* },
|
|
656
|
+
* onResponse: async (message, response, context) => {
|
|
657
|
+
* console.log(`[RPC] ${message.topic} completed`)
|
|
658
|
+
* return response
|
|
659
|
+
* }
|
|
660
|
+
* }
|
|
661
|
+
* ```
|
|
662
|
+
*/
|
|
663
|
+
export declare type RpcMiddleware<TSchema extends RpcSchema, TContext = Record<string, never>> = {
|
|
664
|
+
/**
|
|
665
|
+
* Called before handler execution (listener) or before sending (client)
|
|
666
|
+
*
|
|
667
|
+
* For listener: Can augment context and mutate message
|
|
668
|
+
* For client: Can mutate message, context is empty {}
|
|
669
|
+
*
|
|
670
|
+
* @param message - The RPC message (can be mutated)
|
|
671
|
+
* @param context - Request context (listener-side) or empty (client-side)
|
|
672
|
+
* @returns Updated context (listener mutates this, client returns unchanged)
|
|
673
|
+
* @throws FrakRpcError to reject the request with a specific error code
|
|
674
|
+
*/
|
|
675
|
+
onRequest?: (message: RpcMessage<ExtractMethod<TSchema>>, context: RpcMiddlewareContext<TContext>) => Promise<RpcMiddlewareContext<TContext>> | RpcMiddlewareContext<TContext>;
|
|
676
|
+
/**
|
|
677
|
+
* Called after handler execution (listener) or after receiving (client)
|
|
678
|
+
*
|
|
679
|
+
* @param message - The original RPC message
|
|
680
|
+
* @param response - The response (can be mutated)
|
|
681
|
+
* @param context - Request context (listener-side) or empty (client-side)
|
|
682
|
+
* @returns Transformed response
|
|
683
|
+
* @throws Error to send an error response instead
|
|
684
|
+
*/
|
|
685
|
+
onResponse?: (message: RpcMessage<ExtractMethod<TSchema>>, response: RpcResponse, context: RpcMiddlewareContext<TContext>) => Promise<RpcResponse> | RpcResponse;
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Middleware context that can be augmented with custom fields
|
|
690
|
+
* Generic type parameter allows domain-specific context augmentation
|
|
691
|
+
*
|
|
692
|
+
* @typeParam TCustomContext - Custom context fields to merge with base context
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* ```ts
|
|
696
|
+
* type WalletContext = RpcMiddlewareContext<{
|
|
697
|
+
* productId: string
|
|
698
|
+
* sourceUrl: string
|
|
699
|
+
* isAutoContext: boolean
|
|
700
|
+
* }>
|
|
701
|
+
* // { origin: string, source: MessageEventSource | null, productId: string, sourceUrl: string, isAutoContext: boolean }
|
|
702
|
+
* ```
|
|
703
|
+
*/
|
|
704
|
+
export declare type RpcMiddlewareContext<TCustomContext = Record<string, never>> = RpcRequestContext & TCustomContext;
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Promise handler function type
|
|
708
|
+
* Handles one-shot requests that return a single promise
|
|
709
|
+
*
|
|
710
|
+
* @typeParam TSchema - The RPC schema type
|
|
711
|
+
* @typeParam TMethod - The method name from the schema
|
|
712
|
+
* @typeParam TContext - Custom context type augmented by middleware
|
|
713
|
+
*/
|
|
714
|
+
export declare type RpcPromiseHandler<TSchema extends RpcSchema, TMethod extends ExtractMethod<TSchema>, TContext = Record<string, never>> = (params: ExtractParams<TSchema, TMethod>, context: RpcMiddlewareContext<TContext>) => Promise<ExtractReturnType<TSchema, TMethod>>;
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* Request context for handlers
|
|
718
|
+
* Contains information about the origin and source of the request
|
|
719
|
+
*/
|
|
720
|
+
export declare type RpcRequestContext = {
|
|
721
|
+
/**
|
|
722
|
+
* Origin of the request
|
|
723
|
+
*/
|
|
724
|
+
origin: string;
|
|
725
|
+
/**
|
|
726
|
+
* Message source (for responding)
|
|
727
|
+
*/
|
|
728
|
+
source: MessageEventSource | null;
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* RPC response wrapper
|
|
733
|
+
* Contains either a successful result or an error
|
|
734
|
+
*/
|
|
735
|
+
export declare type RpcResponse<TResult = unknown> = {
|
|
736
|
+
result: TResult;
|
|
737
|
+
error?: never;
|
|
738
|
+
} | {
|
|
739
|
+
result?: never;
|
|
740
|
+
error: RpcError;
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* An RPC schema is a readonly array of schema entries
|
|
745
|
+
*
|
|
746
|
+
* @example
|
|
747
|
+
* ```ts
|
|
748
|
+
* type MySchema = [
|
|
749
|
+
* {
|
|
750
|
+
* Method: "greet";
|
|
751
|
+
* Parameters: [name: string];
|
|
752
|
+
* ReturnType: string;
|
|
753
|
+
* ResponseType: "promise";
|
|
754
|
+
* },
|
|
755
|
+
* {
|
|
756
|
+
* Method: "watchTime";
|
|
757
|
+
* Parameters?: undefined;
|
|
758
|
+
* ReturnType: number;
|
|
759
|
+
* ResponseType: "stream";
|
|
760
|
+
* }
|
|
761
|
+
* ]
|
|
762
|
+
* ```
|
|
763
|
+
*/
|
|
764
|
+
export declare type RpcSchema = readonly RpcSchemaEntry[];
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Generic shape of a single RPC schema entry
|
|
768
|
+
*
|
|
769
|
+
* Each entry defines a method with its parameters, return type, and response kind
|
|
770
|
+
*
|
|
771
|
+
* @typeParam TMethod - The method name (string literal)
|
|
772
|
+
* @typeParam TParams - The parameters type (can be undefined for no parameters)
|
|
773
|
+
* @typeParam TReturn - The return type
|
|
774
|
+
* @typeParam TResponseKind - Either "promise" or "stream"
|
|
775
|
+
*/
|
|
776
|
+
export declare type RpcSchemaEntry<TMethod extends string = string, TParams = unknown, TReturn = unknown> = {
|
|
777
|
+
/**
|
|
778
|
+
* The method name (e.g., "frak_sendInteraction")
|
|
779
|
+
*/
|
|
780
|
+
Method: TMethod;
|
|
781
|
+
/**
|
|
782
|
+
* The parameters type (undefined if no parameters)
|
|
783
|
+
*/
|
|
784
|
+
Parameters?: TParams;
|
|
785
|
+
/**
|
|
786
|
+
* The return type
|
|
787
|
+
*/
|
|
788
|
+
ReturnType: TReturn;
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Stream handler function type
|
|
793
|
+
* Handles streaming requests that can emit multiple values
|
|
794
|
+
*
|
|
795
|
+
* @typeParam TSchema - The RPC schema type
|
|
796
|
+
* @typeParam TMethod - The method name from the schema
|
|
797
|
+
* @typeParam TContext - Custom context type augmented by middleware
|
|
798
|
+
*/
|
|
799
|
+
export declare type RpcStreamHandler<TSchema extends RpcSchema, TMethod extends ExtractMethod<TSchema>, TContext = Record<string, never>> = (params: ExtractParams<TSchema, TMethod>, emitter: StreamEmitter<ExtractReturnType<TSchema, TMethod>>, context: RpcMiddlewareContext<TContext>) => Promise<void> | void;
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Transport interface for RPC communication
|
|
803
|
+
* Abstracts the underlying message passing mechanism (postMessage, etc)
|
|
804
|
+
*/
|
|
805
|
+
export declare type RpcTransport = {
|
|
806
|
+
/**
|
|
807
|
+
* Send a message through the transport
|
|
808
|
+
*/
|
|
809
|
+
postMessage: (message: RpcMessage, targetOrigin: string) => void;
|
|
810
|
+
/**
|
|
811
|
+
* Listen for messages
|
|
812
|
+
*/
|
|
813
|
+
addEventListener: (type: "message", listener: (event: MessageEvent<RpcMessage>) => void) => void;
|
|
814
|
+
/**
|
|
815
|
+
* Remove message listener
|
|
816
|
+
*/
|
|
817
|
+
removeEventListener: (type: "message", listener: (event: MessageEvent<RpcMessage>) => void) => void;
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Stream emitter function
|
|
822
|
+
* Used by stream handlers to emit multiple values
|
|
823
|
+
*/
|
|
824
|
+
export declare type StreamEmitter<TResult> = (chunk: TResult) => void;
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Type-safe request parameters
|
|
828
|
+
*
|
|
829
|
+
* @typeParam TSchema - The RPC schema type
|
|
830
|
+
* @typeParam TMethod - The method name from the schema
|
|
831
|
+
*/
|
|
832
|
+
export declare type TypedRpcRequest<TSchema extends RpcSchema, TMethod extends ExtractMethod<TSchema>> = {
|
|
833
|
+
method: TMethod;
|
|
834
|
+
params: ExtractParams<TSchema, TMethod>;
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
export { }
|