@frak-labs/frame-connector 0.1.0-beta.8d103039 → 0.1.0-beta.d9302e66
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/package.json +10 -13
- package/dist/index.cjs +0 -1
- package/dist/index.d.cts +0 -837
- package/dist/index.d.ts +0 -837
- package/dist/index.js +0 -1
- package/dist/middleware.cjs +0 -1
- package/dist/middleware.d.cts +0 -251
- package/dist/middleware.d.ts +0 -251
- package/dist/middleware.js +0 -1
package/dist/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{CborDecoder as e,CborEncoder as r}from"@jsonjoy.com/json-pack/lib/cbor/index.js";import{sha256 as t}from"viem";let o={parseError:-32700,invalidRequest:-32600,methodNotFound:-32601,invalidParams:-32602,internalError:-32603,serverError:-32e3,clientNotConnected:-32001,configError:-32002,corruptedResponse:-32003,clientAborted:-32004,walletNotConnected:-32005,serverErrorForInteractionDelegation:-32006,userRejected:-32007};class n extends Error{code;data;constructor(e,r,t){super(r),this.code=e,this.data=t}toJSON(){return{code:this.code,message:this.message,data:this.data}}}class i extends n{constructor(e,r){super(o.methodNotFound,e,{method:r})}}class a extends n{constructor(e){super(o.internalError,e)}}class s extends n{constructor(){super(o.clientNotConnected,"Client not found")}}class c{_promise;_resolve;_reject;constructor(){this._promise=new Promise((e,r)=>{this._resolve=e,this._reject=r})}get promise(){return this._promise}resolve=e=>{this._resolve?.(e)};reject=e=>{this._reject?.(e)}}function l(e){let{emittingTransport:r,listeningTransport:t,targetOrigin:o,middleware:i=[],lifecycleHandlers:a}=e,s=new Map;async function l(e){try{"clientLifecycle"in e&&a?.clientLifecycle?await a.clientLifecycle(e,{origin:o,source:null}):"iframeLifecycle"in e&&a?.iframeLifecycle&&await a.iframeLifecycle(e,{origin:o,source:null})}catch(e){console.error("[RPC Client] Lifecycle handler error:",e)}}async function d(e){let r={origin:o,source:null};for(let t of i)t.onRequest&&await t.onRequest(e,r);return e}async function u(e,r){let t={origin:o,source:null},n=r;for(let r of i)r.onResponse&&(n=await r.onResponse(e,n,t));return n}async function f(e){var r,t;let n;try{let r=new URL(e.origin).origin.toLowerCase(),t=new URL(o).origin.toLowerCase();if(r!==t)return void console.log("Not expected origin",r,t)}catch(e){console.error("[RPC Client] Invalid origin",e);return}if("object"==typeof(r=e.data)&&r&&("clientLifecycle"in r||"iframeLifecycle"in r))return void await l(e.data);if(!("object"==typeof(t=e.data)&&t&&"id"in t&&"topic"in t&&"data"in t))return;try{let r=e.data.data,t=r instanceof Uint8Array||ArrayBuffer.isView(r);n=await u(e.data,t?{result:r}:r)}catch(e){console.error("[RPC Client] Middleware error on response:",e);return}let i=s.get(e.data.id);i&&i(n)}async function p(e){let t=e;try{t=await d(e)}catch(e){throw console.error("[RPC Client] Middleware error on request:",e),e}r.postMessage(t,o)}function m(){return`${Date.now()}-${Math.random().toString(36).substring(2,9)}`}return t.addEventListener("message",f),{request:function(e){let r=m(),t=new c;return s.set(r,e=>{e.error?t.reject(new n(e.error.code,e.error.message,e.error.data)):t.resolve(e.result),s.delete(r)}),p({id:r,topic:e.method,data:{method:e.method,params:e.params}}).catch(e=>{s.delete(r),t.reject(e)}),t.promise},listen:function(e,r){let t=m();return s.set(t,e=>{e.error?(console.error("[RPC Client] Listener error:",e.error),s.delete(t)):r(e.result)}),p({id:t,topic:e.method,data:{method:e.method,params:e.params}}).catch(e=>{console.error("[RPC Client] Failed to send listener request:",e),s.delete(t)}),()=>{s.delete(t)}},sendLifecycle:function(e){r.postMessage(e,o)},cleanup:function(){t.removeEventListener("message",f),s.clear()}}}function d(e){let{transport:r,allowedOrigins:t,middleware:i=[],lifecycleHandlers:a}=e,s=Array.isArray(t)?t:[t],c=new Map,l=new Map;async function d(e,r){let t=r;for(let r of i)r.onRequest&&(t=await r.onRequest(e,t));return t}async function u(e,r,t){let o=r;for(let r of i)r.onResponse&&(o=await r.onResponse(e,o,t));return o}function f(e,r,t,o,n){if(!e)return void console.error("[RPC Listener] No source to send response to");let i={id:t,topic:o,data:!n.error&&(n.result instanceof Uint8Array||ArrayBuffer.isView(n.result))?n.result:n};"postMessage"in e&&"function"==typeof e.postMessage&&e.postMessage(i,{targetOrigin:r})}function p(e,r,t,i,a){f(e,r,t,i,{error:a instanceof n?a.toJSON():{code:o.internalError,message:a.message}})}async function m(e,r){try{"clientLifecycle"in e&&a?.clientLifecycle?await a.clientLifecycle(e,r):"iframeLifecycle"in e&&a?.iframeLifecycle&&await a.iframeLifecycle(e,r)}catch(e){console.error("[RPC Listener] Lifecycle handler error:",e)}}async function h(e){var r,t;let o;if(!function(e){try{if(s.includes("*"))return!0;let r=new URL(e).origin.toLowerCase();return s.some(e=>{let t=new URL(e).origin.toLowerCase();return r===t})}catch(e){return console.error("[RPC Listener] Invalid origin",e),!1}}(e.origin))return void console.warn("[RPC Listener] Message from disallowed origin:",e.origin);let n={origin:e.origin,source:e.source};if("object"==typeof(r=e.data)&&r&&("clientLifecycle"in r||"iframeLifecycle"in r))return void await m(e.data,n);if("object"==typeof(t=e.data)&&t&&"id"in t&&"topic"in t&&"data"in t){try{o=await d(e.data,n)}catch(r){p(e.source,e.origin,e.data.id,e.data.topic,r instanceof Error?r:Error(String(r)));return}try{await y(e,o)}catch(r){p(e.source,e.origin,e.data.id,e.data.topic,r instanceof Error?r:Error(String(r)))}}}async function y(e,r){let{id:t,topic:i,data:a}=e.data,s=c.get(i);if(s){let o=await s(a,r),n=await u(e.data,{result:o},r);f(e.source,e.origin,t,i,n);return}let d=l.get(i);if(d){let o=async o=>{let n;try{n=await u(e.data,{result:o},r)}catch(e){console.error("[RPC Listener] Middleware failed on stream chunk:",e);return}f(e.source,e.origin,t,i,n)};await d(a,o,r);return}console.error("[RPC Listener] No handler found for method:",{topic:i,handlers:l.keys(),promiseHandler:c.keys()}),p(e.source,e.origin,t,i,new n(o.methodNotFound,`Method not found: ${i}`))}return r.addEventListener("message",h),{handle:function(e,r){c.set(e,r)},handleStream:function(e,r){l.set(e,r)},unregister:function(e){c.delete(e),l.delete(e)},cleanup:function(){r.removeEventListener("message",h),c.clear(),l.clear()}}}let u=new r,f=new e;function p(e){let r={...e,validationHash:w(e??{})};return u.encode(r)}function m(e){if(!e.length)throw new n(o.corruptedResponse,"Missing compressed data");let r=y(e);if(!r)throw new n(o.corruptedResponse,"Invalid compressed data");if(!r?.validationHash)throw new n(o.corruptedResponse,"Missing validation hash");let{validationHash:t,...i}=r,a=w(i);if(a!==r.validationHash)throw console.warn("Validation error",{validationHash:t,rawResultData:i,parsedData:r,expectedValidationHashes:a,recomputedHash:w(void 0)}),new n(o.corruptedResponse,"Invalid validation hash");return r}function h(e){return u.encode(e)}function y(e){try{return f.decode(e)}catch(r){return console.error("Invalid compressed data",{e:r,data:e}),null}}function w(e){return t(u.encode(e))}let g=()=>({onRequest:(e,r)=>{if(e.data instanceof Uint8Array||ArrayBuffer.isView(e.data))return r;try{e.data=p(e.data)}catch(e){console.error("[Compression Middleware] Failed to compress request",e)}return r},onResponse:(e,r,t)=>{if(r.error||!(r.result instanceof Uint8Array||ArrayBuffer.isView(r.result)))return r;try{let{validationHash:e,...t}=m(r.result);"object"==typeof t&&null!==t&&"result"in t?r.result=t.result:r.result=t}catch(e){console.error("[Compression Middleware] Failed to decompress response",e)}return r}}),L=()=>({onRequest:(e,r)=>{if(!(e.data instanceof Uint8Array||ArrayBuffer.isView(e.data)))return r;try{let{validationHash:r,...t}=m(e.data);"object"==typeof t&&"params"in t?e.data=t.params:e.data=t}catch(e){throw console.error("[Compression Middleware] Failed to decompress request",e),e}return r},onResponse:(e,r,t)=>{if(r.error||r.result instanceof Uint8Array||ArrayBuffer.isView(r.result))return r;try{let t={method:e.topic,result:r.result};r.result=p(t)}catch(e){console.error("[Compression Middleware] Failed to compress response",e)}return r}});export{s as ClientNotFound,c as Deferred,n as FrakRpcError,a as InternalError,i as MethodNotFoundError,o as RpcErrorCodes,h as compressJson,g as createClientCompressionMiddleware,L as createListenerCompressionMiddleware,l as createRpcClient,d as createRpcListener,m as decompressDataAndCheckHash,y as decompressJson,p as hashAndCompressData};
|
package/dist/middleware.cjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";let __rslib_import_meta_url__="undefined"==typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_require__={};__webpack_require__.d=(e,r)=>{for(var o in r)__webpack_require__.o(r,o)&&!__webpack_require__.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:r[o]})},__webpack_require__.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),__webpack_require__.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{createListenerCompressionMiddleware:()=>createListenerCompressionMiddleware,createClientCompressionMiddleware:()=>createClientCompressionMiddleware});let index_js_namespaceObject=require("@jsonjoy.com/json-pack/lib/cbor/index.js"),external_viem_namespaceObject=require("viem"),RpcErrorCodes={parseError:-32700,invalidRequest:-32600,methodNotFound:-32601,invalidParams:-32602,internalError:-32603,serverError:-32e3,clientNotConnected:-32001,configError:-32002,corruptedResponse:-32003,clientAborted:-32004,walletNotConnected:-32005,serverErrorForInteractionDelegation:-32006,userRejected:-32007};class FrakRpcError extends Error{code;data;constructor(e,r,o){super(r),this.code=e,this.data=o}toJSON(){return{code:this.code,message:this.message,data:this.data}}}class MethodNotFoundError extends FrakRpcError{constructor(e,r){super(RpcErrorCodes.methodNotFound,e,{method:r})}}class InternalError extends FrakRpcError{constructor(e){super(RpcErrorCodes.internalError,e)}}class ClientNotFound extends FrakRpcError{constructor(){super(RpcErrorCodes.clientNotConnected,"Client not found")}}let encoder=new index_js_namespaceObject.CborEncoder,decoder=new index_js_namespaceObject.CborDecoder;function hashAndCompressData(e){let r={...e,validationHash:hashJson(e??{})};return encoder.encode(r)}function decompressDataAndCheckHash(e){if(!e.length)throw new FrakRpcError(RpcErrorCodes.corruptedResponse,"Missing compressed data");let r=decompressJson(e);if(!r)throw new FrakRpcError(RpcErrorCodes.corruptedResponse,"Invalid compressed data");if(!r?.validationHash)throw new FrakRpcError(RpcErrorCodes.corruptedResponse,"Missing validation hash");let{validationHash:o,...t}=r,s=hashJson(t);if(s!==r.validationHash)throw console.warn("Validation error",{validationHash:o,rawResultData:t,parsedData:r,expectedValidationHashes:s,recomputedHash:hashJson(void 0)}),new FrakRpcError(RpcErrorCodes.corruptedResponse,"Invalid validation hash");return r}function compressJson(e){return encoder.encode(e)}function decompressJson(e){try{return decoder.decode(e)}catch(r){return console.error("Invalid compressed data",{e:r,data:e}),null}}function hashJson(e){return(0,external_viem_namespaceObject.sha256)(encoder.encode(e))}let createClientCompressionMiddleware=()=>({onRequest:(e,r)=>{if(e.data instanceof Uint8Array||ArrayBuffer.isView(e.data))return r;try{e.data=hashAndCompressData(e.data)}catch(e){console.error("[Compression Middleware] Failed to compress request",e)}return r},onResponse:(e,r,o)=>{if(r.error||!(r.result instanceof Uint8Array||ArrayBuffer.isView(r.result)))return r;try{let{validationHash:e,...o}=decompressDataAndCheckHash(r.result);"object"==typeof o&&null!==o&&"result"in o?r.result=o.result:r.result=o}catch(e){console.error("[Compression Middleware] Failed to decompress response",e)}return r}}),createListenerCompressionMiddleware=()=>({onRequest:(e,r)=>{if(!(e.data instanceof Uint8Array||ArrayBuffer.isView(e.data)))return r;try{let{validationHash:r,...o}=decompressDataAndCheckHash(e.data);"object"==typeof o&&"params"in o?e.data=o.params:e.data=o}catch(e){throw console.error("[Compression Middleware] Failed to decompress request",e),e}return r},onResponse:(e,r,o)=>{if(r.error||r.result instanceof Uint8Array||ArrayBuffer.isView(r.result))return r;try{let o={method:e.topic,result:r.result};r.result=hashAndCompressData(o)}catch(e){console.error("[Compression Middleware] Failed to compress response",e)}return r}});for(var __webpack_i__ in exports.createClientCompressionMiddleware=__webpack_exports__.createClientCompressionMiddleware,exports.createListenerCompressionMiddleware=__webpack_exports__.createListenerCompressionMiddleware,__webpack_exports__)-1===["createClientCompressionMiddleware","createListenerCompressionMiddleware"].indexOf(__webpack_i__)&&(exports[__webpack_i__]=__webpack_exports__[__webpack_i__]);Object.defineProperty(exports,"__esModule",{value:!0});
|
package/dist/middleware.d.cts
DELETED
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client-side compression middleware
|
|
3
|
-
*
|
|
4
|
-
* Compresses outgoing requests and decompresses incoming responses.
|
|
5
|
-
* Always uses the format: {method: string, params: unknown}
|
|
6
|
-
*
|
|
7
|
-
* @example Client side
|
|
8
|
-
* ```ts
|
|
9
|
-
* const client = createRpcClient({
|
|
10
|
-
* transport: iframe.contentWindow,
|
|
11
|
-
* targetOrigin: 'https://wallet.frak.id',
|
|
12
|
-
* middleware: [createClientCompressionMiddleware()]
|
|
13
|
-
* })
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
export declare const createClientCompressionMiddleware: <TSchema extends RpcSchema, TContext>() => RpcMiddleware<TSchema, TContext>;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Listener-side compression middleware
|
|
20
|
-
*
|
|
21
|
-
* Decompresses incoming requests and compresses outgoing responses.
|
|
22
|
-
* Always uses the format: {method: string, params: unknown}
|
|
23
|
-
*
|
|
24
|
-
* @example Listener side
|
|
25
|
-
* ```ts
|
|
26
|
-
* const listener = createRpcListener({
|
|
27
|
-
* transport: window,
|
|
28
|
-
* allowedOrigins: ['https://example.com'],
|
|
29
|
-
* middleware: [createListenerCompressionMiddleware()]
|
|
30
|
-
* })
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
export declare const createListenerCompressionMiddleware: <TSchema extends RpcSchema, TContext>() => RpcMiddleware<TSchema, TContext>;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Extract method names from a schema
|
|
37
|
-
*
|
|
38
|
-
* @typeParam TSchema - The RPC schema type
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* ```ts
|
|
42
|
-
* type Methods = ExtractMethod<MySchema>
|
|
43
|
-
* // "greet" | "watchTime"
|
|
44
|
-
* ```
|
|
45
|
-
*/
|
|
46
|
-
declare type ExtractMethod<TSchema extends RpcSchema> = TSchema[number]["Method"];
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* RPC error object
|
|
50
|
-
*/
|
|
51
|
-
declare type RpcError = {
|
|
52
|
-
code: number;
|
|
53
|
-
message: string;
|
|
54
|
-
data?: unknown;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* RPC message format (maintains backward compatibility)
|
|
59
|
-
* This is the exact format sent over the wire
|
|
60
|
-
*
|
|
61
|
-
* @typeParam TMethod - The method name type (defaults to string for flexibility)
|
|
62
|
-
*/
|
|
63
|
-
declare type RpcMessage<TMethod extends string = string> = {
|
|
64
|
-
/**
|
|
65
|
-
* Unique message identifier for correlating requests and responses
|
|
66
|
-
*/
|
|
67
|
-
id: string;
|
|
68
|
-
/**
|
|
69
|
-
* The RPC method name (topic for backward compatibility)
|
|
70
|
-
*/
|
|
71
|
-
topic: TMethod;
|
|
72
|
-
/**
|
|
73
|
-
* The message payload (compressed data) or raw params
|
|
74
|
-
*/
|
|
75
|
-
data: unknown;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Unified middleware function for RPC requests (both listener and client)
|
|
80
|
-
* Works on both listener-side (with context augmentation) and client-side (empty context)
|
|
81
|
-
*
|
|
82
|
-
* Key features:
|
|
83
|
-
* - Can mutate message.data directly for efficiency (compression, validation)
|
|
84
|
-
* - Can mutate response.result directly for transformation
|
|
85
|
-
* - Listener-side: Can augment context by returning modified context
|
|
86
|
-
* - Client-side: Uses TContext = {} (empty context), always returns unchanged
|
|
87
|
-
*
|
|
88
|
-
* @typeParam TSchema - The RPC schema type
|
|
89
|
-
* @typeParam TContext - Custom context type to augment base context (empty {} for client-side)
|
|
90
|
-
*
|
|
91
|
-
* @example Listener-side with context augmentation
|
|
92
|
-
* ```ts
|
|
93
|
-
* type WalletContext = { productId: string, sourceUrl: string }
|
|
94
|
-
* const contextMiddleware: RpcMiddleware<MySchema, WalletContext> = {
|
|
95
|
-
* onRequest: async (message, context) => {
|
|
96
|
-
* // Read from store and augment context
|
|
97
|
-
* const productId = await getProductId(context.origin)
|
|
98
|
-
* return { ...context, productId, sourceUrl: context.origin }
|
|
99
|
-
* }
|
|
100
|
-
* }
|
|
101
|
-
* ```
|
|
102
|
-
*
|
|
103
|
-
* @example Client-side (empty context)
|
|
104
|
-
* ```ts
|
|
105
|
-
* const compressionMiddleware: RpcMiddleware<MySchema> = {
|
|
106
|
-
* onRequest: async (message, context) => {
|
|
107
|
-
* // Mutate message.data directly
|
|
108
|
-
* message.data = compress(message.data)
|
|
109
|
-
* return context // Empty context, unchanged
|
|
110
|
-
* },
|
|
111
|
-
* onResponse: async (message, response, context) => {
|
|
112
|
-
* // Mutate response.result directly
|
|
113
|
-
* response.result = decompress(response.result)
|
|
114
|
-
* return response
|
|
115
|
-
* }
|
|
116
|
-
* }
|
|
117
|
-
* ```
|
|
118
|
-
*
|
|
119
|
-
* @example Shared middleware (works on both sides)
|
|
120
|
-
* ```ts
|
|
121
|
-
* const loggingMiddleware: RpcMiddleware<MySchema> = {
|
|
122
|
-
* onRequest: async (message, context) => {
|
|
123
|
-
* console.log(`[RPC] ${message.topic}`, context.origin || 'client')
|
|
124
|
-
* return context
|
|
125
|
-
* },
|
|
126
|
-
* onResponse: async (message, response, context) => {
|
|
127
|
-
* console.log(`[RPC] ${message.topic} completed`)
|
|
128
|
-
* return response
|
|
129
|
-
* }
|
|
130
|
-
* }
|
|
131
|
-
* ```
|
|
132
|
-
*/
|
|
133
|
-
declare type RpcMiddleware<TSchema extends RpcSchema, TContext = Record<string, never>> = {
|
|
134
|
-
/**
|
|
135
|
-
* Called before handler execution (listener) or before sending (client)
|
|
136
|
-
*
|
|
137
|
-
* For listener: Can augment context and mutate message
|
|
138
|
-
* For client: Can mutate message, context is empty {}
|
|
139
|
-
*
|
|
140
|
-
* @param message - The RPC message (can be mutated)
|
|
141
|
-
* @param context - Request context (listener-side) or empty (client-side)
|
|
142
|
-
* @returns Updated context (listener mutates this, client returns unchanged)
|
|
143
|
-
* @throws FrakRpcError to reject the request with a specific error code
|
|
144
|
-
*/
|
|
145
|
-
onRequest?: (message: RpcMessage<ExtractMethod<TSchema>>, context: RpcMiddlewareContext<TContext>) => Promise<RpcMiddlewareContext<TContext>> | RpcMiddlewareContext<TContext>;
|
|
146
|
-
/**
|
|
147
|
-
* Called after handler execution (listener) or after receiving (client)
|
|
148
|
-
*
|
|
149
|
-
* @param message - The original RPC message
|
|
150
|
-
* @param response - The response (can be mutated)
|
|
151
|
-
* @param context - Request context (listener-side) or empty (client-side)
|
|
152
|
-
* @returns Transformed response
|
|
153
|
-
* @throws Error to send an error response instead
|
|
154
|
-
*/
|
|
155
|
-
onResponse?: (message: RpcMessage<ExtractMethod<TSchema>>, response: RpcResponse, context: RpcMiddlewareContext<TContext>) => Promise<RpcResponse> | RpcResponse;
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Middleware context that can be augmented with custom fields
|
|
160
|
-
* Generic type parameter allows domain-specific context augmentation
|
|
161
|
-
*
|
|
162
|
-
* @typeParam TCustomContext - Custom context fields to merge with base context
|
|
163
|
-
*
|
|
164
|
-
* @example
|
|
165
|
-
* ```ts
|
|
166
|
-
* type WalletContext = RpcMiddlewareContext<{
|
|
167
|
-
* productId: string
|
|
168
|
-
* sourceUrl: string
|
|
169
|
-
* isAutoContext: boolean
|
|
170
|
-
* }>
|
|
171
|
-
* // { origin: string, source: MessageEventSource | null, productId: string, sourceUrl: string, isAutoContext: boolean }
|
|
172
|
-
* ```
|
|
173
|
-
*/
|
|
174
|
-
declare type RpcMiddlewareContext<TCustomContext = Record<string, never>> = RpcRequestContext & TCustomContext;
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Request context for handlers
|
|
178
|
-
* Contains information about the origin and source of the request
|
|
179
|
-
*/
|
|
180
|
-
declare type RpcRequestContext = {
|
|
181
|
-
/**
|
|
182
|
-
* Origin of the request
|
|
183
|
-
*/
|
|
184
|
-
origin: string;
|
|
185
|
-
/**
|
|
186
|
-
* Message source (for responding)
|
|
187
|
-
*/
|
|
188
|
-
source: MessageEventSource | null;
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* RPC response wrapper
|
|
193
|
-
* Contains either a successful result or an error
|
|
194
|
-
*/
|
|
195
|
-
declare type RpcResponse<TResult = unknown> = {
|
|
196
|
-
result: TResult;
|
|
197
|
-
error?: never;
|
|
198
|
-
} | {
|
|
199
|
-
result?: never;
|
|
200
|
-
error: RpcError;
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* An RPC schema is a readonly array of schema entries
|
|
205
|
-
*
|
|
206
|
-
* @example
|
|
207
|
-
* ```ts
|
|
208
|
-
* type MySchema = [
|
|
209
|
-
* {
|
|
210
|
-
* Method: "greet";
|
|
211
|
-
* Parameters: [name: string];
|
|
212
|
-
* ReturnType: string;
|
|
213
|
-
* ResponseType: "promise";
|
|
214
|
-
* },
|
|
215
|
-
* {
|
|
216
|
-
* Method: "watchTime";
|
|
217
|
-
* Parameters?: undefined;
|
|
218
|
-
* ReturnType: number;
|
|
219
|
-
* ResponseType: "stream";
|
|
220
|
-
* }
|
|
221
|
-
* ]
|
|
222
|
-
* ```
|
|
223
|
-
*/
|
|
224
|
-
declare type RpcSchema = readonly RpcSchemaEntry[];
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Generic shape of a single RPC schema entry
|
|
228
|
-
*
|
|
229
|
-
* Each entry defines a method with its parameters, return type, and response kind
|
|
230
|
-
*
|
|
231
|
-
* @typeParam TMethod - The method name (string literal)
|
|
232
|
-
* @typeParam TParams - The parameters type (can be undefined for no parameters)
|
|
233
|
-
* @typeParam TReturn - The return type
|
|
234
|
-
* @typeParam TResponseKind - Either "promise" or "stream"
|
|
235
|
-
*/
|
|
236
|
-
declare type RpcSchemaEntry<TMethod extends string = string, TParams = unknown, TReturn = unknown> = {
|
|
237
|
-
/**
|
|
238
|
-
* The method name (e.g., "frak_sendInteraction")
|
|
239
|
-
*/
|
|
240
|
-
Method: TMethod;
|
|
241
|
-
/**
|
|
242
|
-
* The parameters type (undefined if no parameters)
|
|
243
|
-
*/
|
|
244
|
-
Parameters?: TParams;
|
|
245
|
-
/**
|
|
246
|
-
* The return type
|
|
247
|
-
*/
|
|
248
|
-
ReturnType: TReturn;
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
export { }
|
package/dist/middleware.d.ts
DELETED
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client-side compression middleware
|
|
3
|
-
*
|
|
4
|
-
* Compresses outgoing requests and decompresses incoming responses.
|
|
5
|
-
* Always uses the format: {method: string, params: unknown}
|
|
6
|
-
*
|
|
7
|
-
* @example Client side
|
|
8
|
-
* ```ts
|
|
9
|
-
* const client = createRpcClient({
|
|
10
|
-
* transport: iframe.contentWindow,
|
|
11
|
-
* targetOrigin: 'https://wallet.frak.id',
|
|
12
|
-
* middleware: [createClientCompressionMiddleware()]
|
|
13
|
-
* })
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
export declare const createClientCompressionMiddleware: <TSchema extends RpcSchema, TContext>() => RpcMiddleware<TSchema, TContext>;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Listener-side compression middleware
|
|
20
|
-
*
|
|
21
|
-
* Decompresses incoming requests and compresses outgoing responses.
|
|
22
|
-
* Always uses the format: {method: string, params: unknown}
|
|
23
|
-
*
|
|
24
|
-
* @example Listener side
|
|
25
|
-
* ```ts
|
|
26
|
-
* const listener = createRpcListener({
|
|
27
|
-
* transport: window,
|
|
28
|
-
* allowedOrigins: ['https://example.com'],
|
|
29
|
-
* middleware: [createListenerCompressionMiddleware()]
|
|
30
|
-
* })
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
export declare const createListenerCompressionMiddleware: <TSchema extends RpcSchema, TContext>() => RpcMiddleware<TSchema, TContext>;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Extract method names from a schema
|
|
37
|
-
*
|
|
38
|
-
* @typeParam TSchema - The RPC schema type
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* ```ts
|
|
42
|
-
* type Methods = ExtractMethod<MySchema>
|
|
43
|
-
* // "greet" | "watchTime"
|
|
44
|
-
* ```
|
|
45
|
-
*/
|
|
46
|
-
declare type ExtractMethod<TSchema extends RpcSchema> = TSchema[number]["Method"];
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* RPC error object
|
|
50
|
-
*/
|
|
51
|
-
declare type RpcError = {
|
|
52
|
-
code: number;
|
|
53
|
-
message: string;
|
|
54
|
-
data?: unknown;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* RPC message format (maintains backward compatibility)
|
|
59
|
-
* This is the exact format sent over the wire
|
|
60
|
-
*
|
|
61
|
-
* @typeParam TMethod - The method name type (defaults to string for flexibility)
|
|
62
|
-
*/
|
|
63
|
-
declare type RpcMessage<TMethod extends string = string> = {
|
|
64
|
-
/**
|
|
65
|
-
* Unique message identifier for correlating requests and responses
|
|
66
|
-
*/
|
|
67
|
-
id: string;
|
|
68
|
-
/**
|
|
69
|
-
* The RPC method name (topic for backward compatibility)
|
|
70
|
-
*/
|
|
71
|
-
topic: TMethod;
|
|
72
|
-
/**
|
|
73
|
-
* The message payload (compressed data) or raw params
|
|
74
|
-
*/
|
|
75
|
-
data: unknown;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Unified middleware function for RPC requests (both listener and client)
|
|
80
|
-
* Works on both listener-side (with context augmentation) and client-side (empty context)
|
|
81
|
-
*
|
|
82
|
-
* Key features:
|
|
83
|
-
* - Can mutate message.data directly for efficiency (compression, validation)
|
|
84
|
-
* - Can mutate response.result directly for transformation
|
|
85
|
-
* - Listener-side: Can augment context by returning modified context
|
|
86
|
-
* - Client-side: Uses TContext = {} (empty context), always returns unchanged
|
|
87
|
-
*
|
|
88
|
-
* @typeParam TSchema - The RPC schema type
|
|
89
|
-
* @typeParam TContext - Custom context type to augment base context (empty {} for client-side)
|
|
90
|
-
*
|
|
91
|
-
* @example Listener-side with context augmentation
|
|
92
|
-
* ```ts
|
|
93
|
-
* type WalletContext = { productId: string, sourceUrl: string }
|
|
94
|
-
* const contextMiddleware: RpcMiddleware<MySchema, WalletContext> = {
|
|
95
|
-
* onRequest: async (message, context) => {
|
|
96
|
-
* // Read from store and augment context
|
|
97
|
-
* const productId = await getProductId(context.origin)
|
|
98
|
-
* return { ...context, productId, sourceUrl: context.origin }
|
|
99
|
-
* }
|
|
100
|
-
* }
|
|
101
|
-
* ```
|
|
102
|
-
*
|
|
103
|
-
* @example Client-side (empty context)
|
|
104
|
-
* ```ts
|
|
105
|
-
* const compressionMiddleware: RpcMiddleware<MySchema> = {
|
|
106
|
-
* onRequest: async (message, context) => {
|
|
107
|
-
* // Mutate message.data directly
|
|
108
|
-
* message.data = compress(message.data)
|
|
109
|
-
* return context // Empty context, unchanged
|
|
110
|
-
* },
|
|
111
|
-
* onResponse: async (message, response, context) => {
|
|
112
|
-
* // Mutate response.result directly
|
|
113
|
-
* response.result = decompress(response.result)
|
|
114
|
-
* return response
|
|
115
|
-
* }
|
|
116
|
-
* }
|
|
117
|
-
* ```
|
|
118
|
-
*
|
|
119
|
-
* @example Shared middleware (works on both sides)
|
|
120
|
-
* ```ts
|
|
121
|
-
* const loggingMiddleware: RpcMiddleware<MySchema> = {
|
|
122
|
-
* onRequest: async (message, context) => {
|
|
123
|
-
* console.log(`[RPC] ${message.topic}`, context.origin || 'client')
|
|
124
|
-
* return context
|
|
125
|
-
* },
|
|
126
|
-
* onResponse: async (message, response, context) => {
|
|
127
|
-
* console.log(`[RPC] ${message.topic} completed`)
|
|
128
|
-
* return response
|
|
129
|
-
* }
|
|
130
|
-
* }
|
|
131
|
-
* ```
|
|
132
|
-
*/
|
|
133
|
-
declare type RpcMiddleware<TSchema extends RpcSchema, TContext = Record<string, never>> = {
|
|
134
|
-
/**
|
|
135
|
-
* Called before handler execution (listener) or before sending (client)
|
|
136
|
-
*
|
|
137
|
-
* For listener: Can augment context and mutate message
|
|
138
|
-
* For client: Can mutate message, context is empty {}
|
|
139
|
-
*
|
|
140
|
-
* @param message - The RPC message (can be mutated)
|
|
141
|
-
* @param context - Request context (listener-side) or empty (client-side)
|
|
142
|
-
* @returns Updated context (listener mutates this, client returns unchanged)
|
|
143
|
-
* @throws FrakRpcError to reject the request with a specific error code
|
|
144
|
-
*/
|
|
145
|
-
onRequest?: (message: RpcMessage<ExtractMethod<TSchema>>, context: RpcMiddlewareContext<TContext>) => Promise<RpcMiddlewareContext<TContext>> | RpcMiddlewareContext<TContext>;
|
|
146
|
-
/**
|
|
147
|
-
* Called after handler execution (listener) or after receiving (client)
|
|
148
|
-
*
|
|
149
|
-
* @param message - The original RPC message
|
|
150
|
-
* @param response - The response (can be mutated)
|
|
151
|
-
* @param context - Request context (listener-side) or empty (client-side)
|
|
152
|
-
* @returns Transformed response
|
|
153
|
-
* @throws Error to send an error response instead
|
|
154
|
-
*/
|
|
155
|
-
onResponse?: (message: RpcMessage<ExtractMethod<TSchema>>, response: RpcResponse, context: RpcMiddlewareContext<TContext>) => Promise<RpcResponse> | RpcResponse;
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Middleware context that can be augmented with custom fields
|
|
160
|
-
* Generic type parameter allows domain-specific context augmentation
|
|
161
|
-
*
|
|
162
|
-
* @typeParam TCustomContext - Custom context fields to merge with base context
|
|
163
|
-
*
|
|
164
|
-
* @example
|
|
165
|
-
* ```ts
|
|
166
|
-
* type WalletContext = RpcMiddlewareContext<{
|
|
167
|
-
* productId: string
|
|
168
|
-
* sourceUrl: string
|
|
169
|
-
* isAutoContext: boolean
|
|
170
|
-
* }>
|
|
171
|
-
* // { origin: string, source: MessageEventSource | null, productId: string, sourceUrl: string, isAutoContext: boolean }
|
|
172
|
-
* ```
|
|
173
|
-
*/
|
|
174
|
-
declare type RpcMiddlewareContext<TCustomContext = Record<string, never>> = RpcRequestContext & TCustomContext;
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Request context for handlers
|
|
178
|
-
* Contains information about the origin and source of the request
|
|
179
|
-
*/
|
|
180
|
-
declare type RpcRequestContext = {
|
|
181
|
-
/**
|
|
182
|
-
* Origin of the request
|
|
183
|
-
*/
|
|
184
|
-
origin: string;
|
|
185
|
-
/**
|
|
186
|
-
* Message source (for responding)
|
|
187
|
-
*/
|
|
188
|
-
source: MessageEventSource | null;
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* RPC response wrapper
|
|
193
|
-
* Contains either a successful result or an error
|
|
194
|
-
*/
|
|
195
|
-
declare type RpcResponse<TResult = unknown> = {
|
|
196
|
-
result: TResult;
|
|
197
|
-
error?: never;
|
|
198
|
-
} | {
|
|
199
|
-
result?: never;
|
|
200
|
-
error: RpcError;
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* An RPC schema is a readonly array of schema entries
|
|
205
|
-
*
|
|
206
|
-
* @example
|
|
207
|
-
* ```ts
|
|
208
|
-
* type MySchema = [
|
|
209
|
-
* {
|
|
210
|
-
* Method: "greet";
|
|
211
|
-
* Parameters: [name: string];
|
|
212
|
-
* ReturnType: string;
|
|
213
|
-
* ResponseType: "promise";
|
|
214
|
-
* },
|
|
215
|
-
* {
|
|
216
|
-
* Method: "watchTime";
|
|
217
|
-
* Parameters?: undefined;
|
|
218
|
-
* ReturnType: number;
|
|
219
|
-
* ResponseType: "stream";
|
|
220
|
-
* }
|
|
221
|
-
* ]
|
|
222
|
-
* ```
|
|
223
|
-
*/
|
|
224
|
-
declare type RpcSchema = readonly RpcSchemaEntry[];
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Generic shape of a single RPC schema entry
|
|
228
|
-
*
|
|
229
|
-
* Each entry defines a method with its parameters, return type, and response kind
|
|
230
|
-
*
|
|
231
|
-
* @typeParam TMethod - The method name (string literal)
|
|
232
|
-
* @typeParam TParams - The parameters type (can be undefined for no parameters)
|
|
233
|
-
* @typeParam TReturn - The return type
|
|
234
|
-
* @typeParam TResponseKind - Either "promise" or "stream"
|
|
235
|
-
*/
|
|
236
|
-
declare type RpcSchemaEntry<TMethod extends string = string, TParams = unknown, TReturn = unknown> = {
|
|
237
|
-
/**
|
|
238
|
-
* The method name (e.g., "frak_sendInteraction")
|
|
239
|
-
*/
|
|
240
|
-
Method: TMethod;
|
|
241
|
-
/**
|
|
242
|
-
* The parameters type (undefined if no parameters)
|
|
243
|
-
*/
|
|
244
|
-
Parameters?: TParams;
|
|
245
|
-
/**
|
|
246
|
-
* The return type
|
|
247
|
-
*/
|
|
248
|
-
ReturnType: TReturn;
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
export { }
|
package/dist/middleware.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{CborDecoder as e,CborEncoder as r}from"@jsonjoy.com/json-pack/lib/cbor/index.js";import{sha256 as t}from"viem";let o={corruptedResponse:-32003};class s extends Error{code;data;constructor(e,r,t){super(r),this.code=e,this.data=t}toJSON(){return{code:this.code,message:this.message,data:this.data}}}let a=new r,n=new e;function i(e){let r={...e,validationHash:l(e??{})};return a.encode(r)}function d(e){if(!e.length)throw new s(o.corruptedResponse,"Missing compressed data");let r=function(e){try{return n.decode(e)}catch(r){return console.error("Invalid compressed data",{e:r,data:e}),null}}(e);if(!r)throw new s(o.corruptedResponse,"Invalid compressed data");if(!r?.validationHash)throw new s(o.corruptedResponse,"Missing validation hash");let{validationHash:t,...a}=r,i=l(a);if(i!==r.validationHash)throw console.warn("Validation error",{validationHash:t,rawResultData:a,parsedData:r,expectedValidationHashes:i,recomputedHash:l(void 0)}),new s(o.corruptedResponse,"Invalid validation hash");return r}function l(e){return t(a.encode(e))}let c=()=>({onRequest:(e,r)=>{if(e.data instanceof Uint8Array||ArrayBuffer.isView(e.data))return r;try{e.data=i(e.data)}catch(e){console.error("[Compression Middleware] Failed to compress request",e)}return r},onResponse:(e,r,t)=>{if(r.error||!(r.result instanceof Uint8Array||ArrayBuffer.isView(r.result)))return r;try{let{validationHash:e,...t}=d(r.result);"object"==typeof t&&null!==t&&"result"in t?r.result=t.result:r.result=t}catch(e){console.error("[Compression Middleware] Failed to decompress response",e)}return r}}),u=()=>({onRequest:(e,r)=>{if(!(e.data instanceof Uint8Array||ArrayBuffer.isView(e.data)))return r;try{let{validationHash:r,...t}=d(e.data);"object"==typeof t&&"params"in t?e.data=t.params:e.data=t}catch(e){throw console.error("[Compression Middleware] Failed to decompress request",e),e}return r},onResponse:(e,r,t)=>{if(r.error||r.result instanceof Uint8Array||ArrayBuffer.isView(r.result))return r;try{let t={method:e.topic,result:r.result};r.result=i(t)}catch(e){console.error("[Compression Middleware] Failed to compress response",e)}return r}});export{c as createClientCompressionMiddleware,u as createListenerCompressionMiddleware};
|