@x-oasis/async-call-rpc 0.1.38

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.
Files changed (104) hide show
  1. package/README.md +48 -0
  2. package/dist/async-call-rpc.cjs.development.js +1275 -0
  3. package/dist/async-call-rpc.cjs.development.js.map +1 -0
  4. package/dist/async-call-rpc.cjs.production.min.js +2 -0
  5. package/dist/async-call-rpc.cjs.production.min.js.map +1 -0
  6. package/dist/async-call-rpc.esm.js +1559 -0
  7. package/dist/async-call-rpc.esm.js.map +1 -0
  8. package/dist/buffer/BufferFactory.d.ts +12 -0
  9. package/dist/buffer/DataBuffer.d.ts +2 -0
  10. package/dist/buffer/MessagePackBuffer.d.ts +10 -0
  11. package/dist/buffer/ReadBaseBuffer.d.ts +5 -0
  12. package/dist/buffer/ReadBuffer.d.ts +14 -0
  13. package/dist/buffer/SerializationFormat.d.ts +16 -0
  14. package/dist/buffer/WriteBaseBuffer.d.ts +5 -0
  15. package/dist/buffer/WriteBuffer.d.ts +5 -0
  16. package/dist/buffer/examples.d.ts +10 -0
  17. package/dist/buffer/index.d.ts +8 -0
  18. package/dist/common.d.ts +4 -0
  19. package/dist/endpoint/ProxyRPCClient.d.ts +12 -0
  20. package/dist/endpoint/RPCClientHost.d.ts +12 -0
  21. package/dist/endpoint/RPCService.d.ts +20 -0
  22. package/dist/endpoint/RPCServiceHost.d.ts +10 -0
  23. package/dist/error.d.ts +21 -0
  24. package/dist/index.d.ts +11 -0
  25. package/dist/index.js +8 -0
  26. package/dist/middlewares/buffer.d.ts +18 -0
  27. package/dist/middlewares/handleDisconnectedRequest.d.ts +7 -0
  28. package/dist/middlewares/handlePortRequest.d.ts +3 -0
  29. package/dist/middlewares/handleRequest.d.ts +3 -0
  30. package/dist/middlewares/handleRequestUtils.d.ts +10 -0
  31. package/dist/middlewares/handleResponse.d.ts +3 -0
  32. package/dist/middlewares/index.d.ts +7 -0
  33. package/dist/middlewares/logger.d.ts +4 -0
  34. package/dist/middlewares/normalize.d.ts +14 -0
  35. package/dist/middlewares/prepareRequestData.d.ts +28 -0
  36. package/dist/middlewares/sendRequest.d.ts +6 -0
  37. package/dist/middlewares/updateSeqInfo.d.ts +6 -0
  38. package/dist/middlewares/utils.d.ts +3 -0
  39. package/dist/protocol/AbstractChannelProtocol.d.ts +55 -0
  40. package/dist/protocol/MessageChannel.d.ts +18 -0
  41. package/dist/protocol/WebSocketChannel.d.ts +23 -0
  42. package/dist/protocol/WorkerChannel.d.ts +11 -0
  43. package/dist/types/buffer.d.ts +4 -0
  44. package/dist/types/channel.d.ts +27 -0
  45. package/dist/types/index.d.ts +7 -0
  46. package/dist/types/middleware.d.ts +44 -0
  47. package/dist/types/proxyChannel.d.ts +3 -0
  48. package/dist/types/proxyService.d.ts +11 -0
  49. package/dist/types/rpc.d.ts +25 -0
  50. package/dist/types/rpcProtocol.d.ts +22 -0
  51. package/dist/utils/constants.d.ts +5 -0
  52. package/dist/utils/index.d.ts +2 -0
  53. package/dist/utils/jsonrpc.d.ts +36 -0
  54. package/package.json +36 -0
  55. package/src/buffer/ARCHITECTURE.md +298 -0
  56. package/src/buffer/BufferFactory.ts +124 -0
  57. package/src/buffer/CHANGELOG.md +207 -0
  58. package/src/buffer/DataBuffer.ts +1 -0
  59. package/src/buffer/MessagePackBuffer.ts +79 -0
  60. package/src/buffer/OPTIMIZATION.md +258 -0
  61. package/src/buffer/README.md +147 -0
  62. package/src/buffer/ReadBaseBuffer.ts +20 -0
  63. package/src/buffer/ReadBuffer.ts +58 -0
  64. package/src/buffer/SerializationFormat.ts +81 -0
  65. package/src/buffer/WriteBaseBuffer.ts +20 -0
  66. package/src/buffer/WriteBuffer.ts +15 -0
  67. package/src/buffer/examples.ts +242 -0
  68. package/src/buffer/index.ts +15 -0
  69. package/src/common.ts +20 -0
  70. package/src/endpoint/ProxyRPCClient.ts +64 -0
  71. package/src/endpoint/RPCClientHost.ts +45 -0
  72. package/src/endpoint/RPCService.ts +54 -0
  73. package/src/endpoint/RPCServiceHost.ts +18 -0
  74. package/src/error.ts +98 -0
  75. package/src/index.ts +16 -0
  76. package/src/middlewares/buffer.ts +33 -0
  77. package/src/middlewares/handleDisconnectedRequest.ts +30 -0
  78. package/src/middlewares/handlePortRequest.ts +141 -0
  79. package/src/middlewares/handleRequest.ts +128 -0
  80. package/src/middlewares/handleRequestUtils.ts +43 -0
  81. package/src/middlewares/handleResponse.ts +36 -0
  82. package/src/middlewares/index.ts +11 -0
  83. package/src/middlewares/logger.ts +22 -0
  84. package/src/middlewares/normalize.ts +167 -0
  85. package/src/middlewares/prepareRequestData.ts +137 -0
  86. package/src/middlewares/sendRequest.ts +15 -0
  87. package/src/middlewares/updateSeqInfo.ts +34 -0
  88. package/src/middlewares/utils.ts +67 -0
  89. package/src/protocol/AbstractChannelProtocol.ts +343 -0
  90. package/src/protocol/MessageChannel.ts +80 -0
  91. package/src/protocol/WebSocketChannel.ts +179 -0
  92. package/src/protocol/WorkerChannel.ts +36 -0
  93. package/src/types/buffer.ts +5 -0
  94. package/src/types/channel.ts +50 -0
  95. package/src/types/index.ts +9 -0
  96. package/src/types/messageChannel.ts +133 -0
  97. package/src/types/middleware.ts +54 -0
  98. package/src/types/proxyChannel.ts +3 -0
  99. package/src/types/proxyService.ts +18 -0
  100. package/src/types/rpc.ts +61 -0
  101. package/src/types/rpcProtocol.ts +24 -0
  102. package/src/utils/constants.ts +17 -0
  103. package/src/utils/index.ts +5 -0
  104. package/src/utils/jsonrpc.ts +242 -0
@@ -0,0 +1,133 @@
1
+ import { IpcRenderer, UtilityProcess, WebContents } from 'electron';
2
+ import { AbstractChannelProtocolProps as BaseAbstractChannelProtocolProps } from './channel';
3
+
4
+ export interface ChannelPort {
5
+ on(event: 'message', listener: (messageEvent: MessageEvent) => void): this;
6
+ removeListener(
7
+ event: 'message',
8
+ listener: (messageEvent: MessageEvent) => void
9
+ ): this;
10
+ postMessage(message: any, transfer?: MainPort[]): void;
11
+ }
12
+
13
+ export type UtilityMessageChannelPortStoreProps = {
14
+ name: string;
15
+ port: ParentPort;
16
+ masterProcessName: string;
17
+ clientMiddlewares?: ClientMiddleware[];
18
+ senderMiddlewares?: SenderMiddleware[];
19
+ };
20
+
21
+ export type OnMessageEntry = {
22
+ data: any;
23
+ ports: any;
24
+ };
25
+ export type SenderEntry = any;
26
+
27
+ export type ClientMiddleware = (
28
+ channel?: MessageChannelProtocol
29
+ ) => (v: OnMessageEntry) => OnMessageEntry;
30
+
31
+ export type SenderMiddleware = (
32
+ channel?: MessageChannelProtocol
33
+ ) => (data: SenderEntry) => SenderEntry;
34
+
35
+ /**
36
+ * Extended AbstractChannelProtocolProps for message channel protocols
37
+ * Includes additional middleware configuration options
38
+ */
39
+ export type AbstractChannelProtocolProps = BaseAbstractChannelProtocolProps & {
40
+ clientMiddlewares?: ClientMiddleware[];
41
+ senderMiddlewares?: SenderMiddleware[];
42
+ };
43
+
44
+ export type DeferredMessageChannelProtocolProps =
45
+ AbstractChannelProtocolProps & {
46
+ port?: MainPort;
47
+ };
48
+
49
+ export type MessageChannelProtocolProps = AbstractChannelProtocolProps & {
50
+ port?: MainPort;
51
+ };
52
+
53
+ export type ProcessChannelProtocolProps = {
54
+ process: UtilityProcess;
55
+ } & AbstractChannelProtocolProps;
56
+
57
+ export type IPCMainGlobalChannelProtocolProps = AbstractChannelProtocolProps & {
58
+ channelName: string;
59
+ };
60
+
61
+ export type IPCMainChannelProtocolProps = {
62
+ channelName: string;
63
+ webContents: WebContents;
64
+ } & AbstractChannelProtocolProps;
65
+
66
+ export type IPCRendererChannelProtocolProps = {
67
+ channelName: string;
68
+ ipcRenderer: IpcRenderer;
69
+ projectName: string;
70
+ } & AbstractChannelProtocolProps;
71
+
72
+ export interface ParentPort extends NodeJS.EventEmitter {
73
+ on(event: 'message', listener: (messageEvent: MessageEvent) => void): this;
74
+ once(event: 'message', listener: (messageEvent: MessageEvent) => void): this;
75
+ addListener(
76
+ event: 'message',
77
+ listener: (messageEvent: MessageEvent) => void
78
+ ): this;
79
+ removeListener(
80
+ event: 'message',
81
+ listener: (messageEvent: MessageEvent) => void
82
+ ): this;
83
+ postMessage(message: any): void;
84
+ }
85
+
86
+ export type UtilityNodeJSProcess = NodeJS.Process;
87
+
88
+ export interface MainPort extends NodeJS.EventEmitter {
89
+ // Docs: https://electronjs.org/docs/api/message-port-main
90
+
91
+ /**
92
+ * Emitted when the remote end of a MessagePortMain object becomes disconnected.
93
+ */
94
+ /**
95
+ * Emitted when a MessagePortMain object receives a message.
96
+ */
97
+ on(event: 'close', listener: Function): this;
98
+ on(event: 'message', listener: (messageEvent: MessageEvent) => void): this;
99
+ off(event: 'close', listener: Function): this;
100
+ off(event: 'message', listener: (messageEvent: MessageEvent) => void): this;
101
+ once(event: 'close', listener: Function): this;
102
+ once(event: 'message', listener: (messageEvent: MessageEvent) => void): this;
103
+ addListener(event: 'close', listener: Function): this;
104
+ addListener(
105
+ event: 'message',
106
+ listener: (messageEvent: MessageEvent) => void
107
+ ): this;
108
+ removeListener(event: 'close', listener: Function): this;
109
+ removeListener(
110
+ event: 'message',
111
+ listener: (messageEvent: MessageEvent) => void
112
+ ): this;
113
+ /**
114
+ * Disconnects the port, so it is no longer active.
115
+ */
116
+ close(): void;
117
+ /**
118
+ * Sends a message from the port, and optionally, transfers ownership of objects to
119
+ * other browsing contexts.
120
+ */
121
+ postMessage(message: any, transfer?: MainPort[]): void;
122
+ /**
123
+ * Starts the sending of messages queued on the port. Messages will be queued until
124
+ * this method is called.
125
+ */
126
+ start(): void;
127
+ }
128
+
129
+ export function isUtilityProcess(
130
+ process: NodeJS.Process
131
+ ): process is UtilityNodeJSProcess {
132
+ return !!(process as UtilityNodeJSProcess).parentPort;
133
+ }
@@ -0,0 +1,54 @@
1
+ import { Deferred } from '@x-oasis/deferred';
2
+
3
+ import { HostRequestEntry, RequestEntry } from './rpc';
4
+
5
+ export type MiddlewareFunction = {
6
+ (...args: any[]): any;
7
+ displayName?: string;
8
+ lifecycle?: SendMiddlewareLifecycle;
9
+ };
10
+
11
+ export type MessageOutput = {
12
+ event: any;
13
+ ports: any;
14
+ };
15
+
16
+ export type NormalizedRawMessageOutput = MessageOutput & {
17
+ data: string;
18
+ };
19
+
20
+ export type DeserializedMessageOutput = MessageOutput & {
21
+ data: HostRequestEntry | RequestEntry;
22
+ };
23
+
24
+ export type SenderMiddlewareOutput = {
25
+ data: any;
26
+ transfer: any;
27
+ seqId: number;
28
+ returnValue: Deferred;
29
+ isOptionsRequest: boolean;
30
+ middlewareContext: MiddlewareContext;
31
+ };
32
+
33
+ export type PendingSendEntry = SenderMiddlewareOutput & {
34
+ // fnName: string;
35
+ methodName: string;
36
+ lifecycle: SendMiddlewareLifecycle;
37
+ middlewareContext: MiddlewareContext;
38
+ };
39
+
40
+ export type MiddlewareContext = {
41
+ isResumed?: boolean;
42
+ startLifecycle: SendMiddlewareLifecycle;
43
+ minLifecycle: SendMiddlewareLifecycle;
44
+ reserved: PendingSendEntry;
45
+ };
46
+
47
+ export enum SendMiddlewareLifecycle {
48
+ Initial = 0,
49
+ Prepare = 10,
50
+ Transform = 20,
51
+ DataOperation = 30,
52
+ Send = 40,
53
+ Aborted = 100,
54
+ }
@@ -0,0 +1,3 @@
1
+ export type ProxyChannelProps = {
2
+ name: string;
3
+ };
@@ -0,0 +1,18 @@
1
+ import AbstractChannelProtocol from '../protocol/AbstractChannelProtocol';
2
+
3
+ // export type IService = {
4
+ // [key: string]: Function
5
+ // }
6
+ export type IService = any;
7
+
8
+ export type ServiceHandlerPath = string;
9
+ export type ProxyRPCClientChannel =
10
+ | AbstractChannelProtocol
11
+ | { (): AbstractChannelProtocol };
12
+
13
+ export type ProxyRPCClientProps = {
14
+ requestPath: string;
15
+ channel: ProxyRPCClientChannel;
16
+ };
17
+
18
+ export type ServiceHandlers = Record<string, (...args: any[]) => any>;
@@ -0,0 +1,61 @@
1
+ export enum RequestType {
2
+ /**
3
+ * for normal request, wait for return value
4
+ */
5
+ PromiseRequest = 'pr',
6
+ PromiseAbort = 'pa',
7
+
8
+ /**
9
+ * send a command
10
+ */
11
+ SignalRequest = 'sr',
12
+ SignalAbort = 'sa',
13
+ }
14
+
15
+ export type RequestRawSequenceId = number;
16
+
17
+ export type RequestSequenceId = string;
18
+ export type RequestServicePath = string;
19
+ export type RequestFnName = string;
20
+
21
+ export type RequestEntryHeader = [
22
+ RequestType,
23
+ RequestSequenceId,
24
+ RequestServicePath,
25
+ RequestFnName
26
+ ];
27
+ export type RequestEntryBody = any;
28
+ export type RequestEntry = [RequestEntryHeader, RequestEntryBody];
29
+
30
+ export enum ResponseType {
31
+ ReturnSuccess = 'rs',
32
+ ReturnFail = 'rf',
33
+
34
+ PortSuccess = 'ps',
35
+ PortFail = 'pf',
36
+ }
37
+ export type ResponseEntryHeader = [ResponseType, RequestSequenceId];
38
+ export type ResponseEntryBody = any;
39
+
40
+ export type HostName = string;
41
+
42
+ /**
43
+ * 0 RequestType: PromiseRequest, PromiseAbort, SignalRequest, SignalAbort
44
+ * 1 RequestSequenceId: string
45
+ */
46
+ export type HostRequestEntryHeader = [
47
+ RequestType,
48
+ RequestSequenceId,
49
+ RequestServicePath,
50
+ RequestFnName,
51
+ HostName
52
+ ];
53
+ export type HostRequestEntryBody = any;
54
+ export type HostRequestEntry = [HostRequestEntryHeader, HostRequestEntryBody];
55
+
56
+ // export type SendingProps = {
57
+ // requestPath?: string;
58
+ // fnName?: string;
59
+ // args?: any;
60
+ // isOptionsRequest?: boolean;
61
+ // };
@@ -0,0 +1,24 @@
1
+ export type IRPCProtocolServer = {
2
+ source: any;
3
+ encoder: null;
4
+ decoder: null;
5
+ onRequest: () => {};
6
+ handleRequest: () => {};
7
+ sendReply: () => {};
8
+ };
9
+
10
+ export type IRPCProtocolClient = {
11
+ source: any;
12
+ encode: () => {};
13
+ decode: () => {};
14
+ sendRequest: () => {};
15
+ onReply: () => {};
16
+ handleReply: () => {};
17
+ };
18
+
19
+ export type IRPCProtocol = {
20
+ encode: Function;
21
+ decode: Function;
22
+ onMessage: Function;
23
+ send: Function;
24
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Constants used for JSONRPC implementation
3
+ */
4
+
5
+ export const ERROR = 'Error';
6
+
7
+ export const isArray = Array.isArray;
8
+
9
+ export const isObject = (val: unknown): val is object => {
10
+ return val !== null && typeof val === 'object' && !isArray(val);
11
+ };
12
+
13
+ export const isFunction = (val: unknown): val is Function => {
14
+ return typeof val === 'function';
15
+ };
16
+
17
+ export const UNDEFINED = void 0;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * JSONRPC utilities and types
3
+ */
4
+ export * from './jsonrpc';
5
+ export * from './constants';
@@ -0,0 +1,242 @@
1
+ import { ID, ErrorResponse } from '../error';
2
+ import { ERROR, isArray, isFunction, isObject, UNDEFINED } from './constants';
3
+
4
+ /**
5
+ * JSONRPC version constant
6
+ */
7
+ export const jsonrpc = '2.0' as const;
8
+
9
+ /**
10
+ * JSONRPC Request interface
11
+ */
12
+ export interface Request {
13
+ readonly jsonrpc: typeof jsonrpc;
14
+ readonly id?: ID;
15
+ readonly method: string;
16
+ readonly params: readonly unknown[] | object;
17
+ readonly remoteStack?: string;
18
+ }
19
+
20
+ /**
21
+ * JSONRPC Success Response interface
22
+ */
23
+ export interface SuccessResponse {
24
+ readonly jsonrpc: typeof jsonrpc;
25
+ readonly id?: ID;
26
+ readonly result: unknown;
27
+ readonly undef?: boolean; // Non-standard extension for undefined values
28
+ }
29
+
30
+ /**
31
+ * JSONRPC Response type (success or error)
32
+ */
33
+ export type Response = SuccessResponse | ErrorResponse<unknown>;
34
+
35
+ /**
36
+ * Error map function type for custom error mapping
37
+ */
38
+ export type ErrorMapFunction<T = unknown> = (
39
+ error: unknown,
40
+ request: Request
41
+ ) => {
42
+ code: number;
43
+ message: string;
44
+ data?: T;
45
+ };
46
+
47
+ /**
48
+ * AsyncCall error detail structure
49
+ */
50
+ export interface AsyncCallErrorDetail {
51
+ readonly stack?: string;
52
+ readonly type?: string;
53
+ }
54
+
55
+ /**
56
+ * Create a JSONRPC request object
57
+ */
58
+ export const makeRequest = (
59
+ id: ID,
60
+ method: string,
61
+ params: readonly unknown[] | object,
62
+ remoteStack?: string
63
+ ): Request => {
64
+ const x: Request = { jsonrpc, id, method, params, remoteStack };
65
+ deleteUndefined(x, 'id');
66
+ deleteFalsy(x, 'remoteStack');
67
+ return x;
68
+ };
69
+
70
+ /**
71
+ * Create a JSONRPC success response object
72
+ */
73
+ export const makeSuccessResponse = (
74
+ id: ID,
75
+ result: unknown
76
+ ): SuccessResponse => {
77
+ const x: SuccessResponse = { jsonrpc, id, result };
78
+ deleteUndefined(x, 'id');
79
+ return x;
80
+ };
81
+
82
+ /**
83
+ * Create a JSONRPC error response object
84
+ * Pre-defined errors from JSONRPC 2.0 specification section 5.1
85
+ */
86
+ export const makeErrorResponse = <T>(
87
+ id: ID,
88
+ code: number,
89
+ message: string,
90
+ data?: T
91
+ ): ErrorResponse<T> => {
92
+ if (id === UNDEFINED) id = null;
93
+ code = Math.floor(code);
94
+ if (Number.isNaN(code)) code = -1;
95
+ const x: ErrorResponse<T> = {
96
+ jsonrpc,
97
+ id,
98
+ error: { code, message, data },
99
+ };
100
+ deleteUndefined(x.error, 'data');
101
+ return x;
102
+ };
103
+
104
+ /**
105
+ * Parse error response (-32700)
106
+ * Pre-defined error in JSONRPC 2.0 specification section 5.1
107
+ */
108
+ export const ErrorResponseParseError = <T>(
109
+ e: unknown,
110
+ mapper: ErrorMapFunction<T>
111
+ ): ErrorResponse<T> => {
112
+ const obj = ErrorResponseMapped({} as any, e, mapper);
113
+ const o = obj.error as Mutable<ErrorResponse['error']>;
114
+ o.code = -32700;
115
+ o.message = 'Parse error';
116
+ return obj;
117
+ };
118
+
119
+ /**
120
+ * Invalid Request error response (-32600)
121
+ * Pre-defined error in JSONRPC 2.0 specification section 5.1
122
+ */
123
+ export const ErrorResponseInvalidRequest = (id: ID): ErrorResponse => {
124
+ return makeErrorResponse(id, -32600, 'Invalid Request');
125
+ };
126
+
127
+ /**
128
+ * Method not found error response (-32601)
129
+ * Pre-defined error in JSONRPC 2.0 specification section 5.1
130
+ */
131
+ export const ErrorResponseMethodNotFound = (id: ID): ErrorResponse => {
132
+ return makeErrorResponse(id, -32601, 'Method not found');
133
+ };
134
+
135
+ /**
136
+ * Invalid params error response (-32602)
137
+ * Pre-defined error in JSONRPC 2.0 specification section 5.1
138
+ */
139
+ export const ErrorResponseInvalidParams = (id: ID): ErrorResponse => {
140
+ return makeErrorResponse(id, -32602, 'Invalid params');
141
+ };
142
+
143
+ /**
144
+ * Internal error response (-32603)
145
+ * Pre-defined error in JSONRPC 2.0 specification section 5.1
146
+ */
147
+ export const ErrorResponseInternalError = (id: ID): ErrorResponse => {
148
+ return makeErrorResponse(id, -32603, 'Internal error');
149
+ };
150
+
151
+ /**
152
+ * Map an error to a JSONRPC error response using a custom mapper
153
+ */
154
+ export const ErrorResponseMapped = <T>(
155
+ request: Request,
156
+ e: unknown,
157
+ mapper: ErrorMapFunction<T>
158
+ ): ErrorResponse<T> => {
159
+ const { id } = request;
160
+ const { code, message, data } = mapper(e, request);
161
+ return makeErrorResponse(id, code, message, data);
162
+ };
163
+
164
+ /**
165
+ * Default error mapper that extracts error information
166
+ */
167
+ export const defaultErrorMapper =
168
+ (stack = '', code = -1): ErrorMapFunction<AsyncCallErrorDetail> =>
169
+ (e) => {
170
+ let message = toString('', () => (e as any).message);
171
+ let type = toString(
172
+ ERROR,
173
+ (ctor = (e as any).constructor) => isFunction(ctor) && ctor.name
174
+ );
175
+
176
+ // Check for DOMException
177
+ const E = globalThis.DOMException;
178
+ if (E && e instanceof E) {
179
+ type = `DOMException:${e.name}`;
180
+ }
181
+
182
+ const eType = typeof e;
183
+ if (
184
+ eType === 'string' ||
185
+ eType === 'number' ||
186
+ eType === 'boolean' ||
187
+ eType === 'bigint'
188
+ ) {
189
+ type = ERROR;
190
+ message = String(e);
191
+ }
192
+
193
+ const data: AsyncCallErrorDetail = stack ? { stack, type } : { type };
194
+ return { code, message, data };
195
+ };
196
+
197
+ /**
198
+ * Check if an object is a valid JSONRPC request or response
199
+ */
200
+ export const isJSONRPCObject = (data: any): data is Response | Request => {
201
+ if (!isObject(data)) return false;
202
+ if (!('jsonrpc' in data)) return false;
203
+ const obj = data as { jsonrpc?: string; params?: unknown };
204
+ if (obj.jsonrpc !== jsonrpc) return false;
205
+ if ('params' in obj) {
206
+ const params = obj.params;
207
+ if (!isArray(params) && !isObject(params)) return false;
208
+ }
209
+ return true;
210
+ };
211
+
212
+ /**
213
+ * Helper function to safely convert value to string
214
+ */
215
+ const toString = (_default: string, val: () => any): string => {
216
+ try {
217
+ const v = val();
218
+ if (v === UNDEFINED) return _default;
219
+ return String(v);
220
+ } catch {
221
+ return _default;
222
+ }
223
+ };
224
+
225
+ /**
226
+ * Delete undefined property from object
227
+ */
228
+ const deleteUndefined = <O>(x: O, key: keyof O): void => {
229
+ if (x[key] === UNDEFINED) delete x[key];
230
+ };
231
+
232
+ /**
233
+ * Delete falsy property from object
234
+ */
235
+ const deleteFalsy = <T>(x: T, key: keyof T): void => {
236
+ if (!x[key]) delete x[key];
237
+ };
238
+
239
+ /**
240
+ * Make a type mutable
241
+ */
242
+ type Mutable<T> = { -readonly [key in keyof T]: T[key] };