@aztec/foundation 0.22.0 → 0.24.0
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/dest/abi/buffer.js +2 -2
- package/dest/fields/fields.d.ts +1 -0
- package/dest/fields/fields.d.ts.map +1 -1
- package/dest/fields/fields.js +6 -3
- package/package.json +2 -2
- package/src/abi/abi.ts +337 -0
- package/src/abi/buffer.ts +36 -0
- package/src/abi/decoder.ts +176 -0
- package/src/abi/encoder.ts +143 -0
- package/src/abi/index.ts +6 -0
- package/src/abi/selector.ts +243 -0
- package/src/abi/utils.ts +50 -0
- package/src/array/array.ts +86 -0
- package/src/array/index.ts +1 -0
- package/src/async-map/index.ts +18 -0
- package/src/aztec-address/index.ts +36 -0
- package/src/bigint-buffer/index.ts +87 -0
- package/src/collection/array.ts +64 -0
- package/src/collection/index.ts +1 -0
- package/src/committable/committable.ts +46 -0
- package/src/committable/index.ts +1 -0
- package/src/crypto/index.ts +16 -0
- package/src/crypto/keccak/index.ts +33 -0
- package/src/crypto/pedersen/index.ts +1 -0
- package/src/crypto/pedersen/pedersen.elliptic.ts +584 -0
- package/src/crypto/pedersen/pedersen.noble.ts +573 -0
- package/src/crypto/pedersen/pedersen.wasm.ts +42 -0
- package/src/crypto/random/index.ts +42 -0
- package/src/crypto/sha256/index.ts +3 -0
- package/src/errors/index.ts +6 -0
- package/src/eth-address/index.ts +234 -0
- package/src/fields/coordinate.ts +104 -0
- package/src/fields/fields.ts +328 -0
- package/src/fields/index.ts +3 -0
- package/src/fields/point.ts +145 -0
- package/src/fifo/bounded_serial_queue.ts +100 -0
- package/src/fifo/index.ts +4 -0
- package/src/fifo/memory_fifo.ts +118 -0
- package/src/fifo/semaphore.ts +33 -0
- package/src/fifo/serial_queue.ts +81 -0
- package/src/index.ts +29 -0
- package/src/json-rpc/README.md +55 -0
- package/src/json-rpc/class_converter.ts +213 -0
- package/src/json-rpc/client/index.ts +1 -0
- package/src/json-rpc/client/json_rpc_client.ts +147 -0
- package/src/json-rpc/convert.ts +163 -0
- package/src/json-rpc/fixtures/class_a.ts +15 -0
- package/src/json-rpc/fixtures/class_b.ts +15 -0
- package/src/json-rpc/fixtures/test_state.ts +59 -0
- package/src/json-rpc/index.ts +8 -0
- package/src/json-rpc/js_utils.ts +20 -0
- package/src/json-rpc/server/index.ts +2 -0
- package/src/json-rpc/server/json_proxy.ts +60 -0
- package/src/json-rpc/server/json_rpc_server.ts +269 -0
- package/src/log/console.ts +39 -0
- package/src/log/debug.ts +83 -0
- package/src/log/index.ts +5 -0
- package/src/log/log_fn.ts +5 -0
- package/src/log/log_history.ts +44 -0
- package/src/log/logger.ts +137 -0
- package/src/mutex/index.ts +83 -0
- package/src/mutex/mutex_database.ts +12 -0
- package/src/noir/index.ts +1 -0
- package/src/noir/noir_package_config.ts +54 -0
- package/src/retry/index.ts +99 -0
- package/src/running-promise/index.ts +60 -0
- package/src/serialize/buffer_reader.ts +286 -0
- package/src/serialize/field_reader.ts +143 -0
- package/src/serialize/free_funcs.ts +147 -0
- package/src/serialize/index.ts +5 -0
- package/src/serialize/serialize.ts +303 -0
- package/src/serialize/types.ts +40 -0
- package/src/sleep/index.ts +71 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/test_data.ts +36 -0
- package/src/timer/elapsed.ts +23 -0
- package/src/timer/index.ts +3 -0
- package/src/timer/timeout.ts +64 -0
- package/src/timer/timer.ts +48 -0
- package/src/transport/browser/index.ts +4 -0
- package/src/transport/browser/message_port_socket.ts +48 -0
- package/src/transport/browser/shared_worker_connector.ts +21 -0
- package/src/transport/browser/shared_worker_listener.ts +53 -0
- package/src/transport/browser/worker_connector.ts +30 -0
- package/src/transport/browser/worker_listener.ts +54 -0
- package/src/transport/dispatch/create_dispatch_fn.ts +35 -0
- package/src/transport/dispatch/create_dispatch_proxy.ts +141 -0
- package/src/transport/dispatch/messages.ts +58 -0
- package/src/transport/index.ts +11 -0
- package/src/transport/interface/connector.ts +9 -0
- package/src/transport/interface/listener.ts +16 -0
- package/src/transport/interface/socket.ts +15 -0
- package/src/transport/interface/transferable.ts +125 -0
- package/src/transport/node/index.ts +2 -0
- package/src/transport/node/node_connector.ts +30 -0
- package/src/transport/node/node_connector_socket.ts +52 -0
- package/src/transport/node/node_listener.ts +34 -0
- package/src/transport/node/node_listener_socket.ts +48 -0
- package/src/transport/transport_client.ts +131 -0
- package/src/transport/transport_server.ts +108 -0
- package/src/trees/index.ts +54 -0
- package/src/types/index.ts +8 -0
- package/src/url/index.ts +73 -0
- package/src/wasm/README.md +6 -0
- package/src/wasm/empty_wasi_sdk.ts +166 -0
- package/src/wasm/fixtures/gcd.wasm +0 -0
- package/src/wasm/fixtures/gcd.wat +27 -0
- package/src/wasm/index.ts +1 -0
- package/src/wasm/wasm_module.ts +260 -0
- package/src/worker/browser/index.ts +2 -0
- package/src/worker/browser/start_web_module.ts +23 -0
- package/src/worker/browser/web_data_store.ts +38 -0
- package/src/worker/browser/web_worker.ts +24 -0
- package/src/worker/data_store.ts +19 -0
- package/src/worker/index.ts +2 -0
- package/src/worker/node/index.ts +2 -0
- package/src/worker/node/node_data_store.ts +27 -0
- package/src/worker/node/node_worker.ts +22 -0
- package/src/worker/node/start_node_module.ts +29 -0
- package/src/worker/wasm_worker.ts +7 -0
- package/src/worker/worker_pool.ts +73 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { assert } from './js_utils.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents a class compatible with our class conversion system.
|
|
5
|
+
* E.g. PublicKey here satisfies 'StringIOClass'.
|
|
6
|
+
* ```
|
|
7
|
+
* class PublicKey {
|
|
8
|
+
* toString() {
|
|
9
|
+
* return '...';
|
|
10
|
+
* }
|
|
11
|
+
* static fromString(str) {
|
|
12
|
+
* return new PublicKey(...);
|
|
13
|
+
* }
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
interface StringIOClass {
|
|
18
|
+
new (...args: any): any;
|
|
19
|
+
|
|
20
|
+
// TODO(#4254): Ensure that toString method is checked for as well.
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates an IOClass from a given string.
|
|
24
|
+
*/
|
|
25
|
+
fromString: (str: string) => any;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Represents a class compatible with our class conversion system.
|
|
30
|
+
* E.g. PublicKey here satisfies 'ObjIOClass'.
|
|
31
|
+
* ```
|
|
32
|
+
* class PublicKey {
|
|
33
|
+
* toJSON() {
|
|
34
|
+
* return {...};
|
|
35
|
+
* }
|
|
36
|
+
* static fromJSON(obj) {
|
|
37
|
+
* return new PublicKey({...});
|
|
38
|
+
* }
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
interface ObjIOClass {
|
|
43
|
+
new (...args: any): any;
|
|
44
|
+
|
|
45
|
+
// TODO(#4254): Ensure that toJSON method is checked for as well.
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Creates an IOClass from a given JSON object.
|
|
49
|
+
*/
|
|
50
|
+
fromJSON: (str: object) => any;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Either a StringIOClass or ObjIOClass
|
|
55
|
+
*/
|
|
56
|
+
type IOClass = ObjIOClass | StringIOClass;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Registered classes available for conversion.
|
|
60
|
+
*/
|
|
61
|
+
export interface StringClassConverterInput {
|
|
62
|
+
[className: string]: StringIOClass;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Registered classes available for conversion.
|
|
67
|
+
*/
|
|
68
|
+
export interface JsonClassConverterInput {
|
|
69
|
+
[className: string]: ObjIOClass;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Represents a class in a JSON-friendly encoding.
|
|
74
|
+
*/
|
|
75
|
+
export interface StringEncodedClass {
|
|
76
|
+
/**
|
|
77
|
+
* The class type.
|
|
78
|
+
*/
|
|
79
|
+
type: string;
|
|
80
|
+
/**
|
|
81
|
+
* The class data string.
|
|
82
|
+
*/
|
|
83
|
+
data: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Represents a class in a JSON-friendly encoding.
|
|
88
|
+
*/
|
|
89
|
+
export interface JsonEncodedClass {
|
|
90
|
+
/**
|
|
91
|
+
* The class type.
|
|
92
|
+
*/
|
|
93
|
+
type: string;
|
|
94
|
+
/**
|
|
95
|
+
* The class data string.
|
|
96
|
+
*/
|
|
97
|
+
data: object;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Whether a class is a complex object or simply represented by a string.
|
|
101
|
+
*/
|
|
102
|
+
export type ClassEncoding = 'string' | 'object';
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Handles mapping of classes to names, and calling toString and fromString to convert to and from JSON-friendly formats.
|
|
106
|
+
* Takes a class map as input.
|
|
107
|
+
*/
|
|
108
|
+
export class ClassConverter {
|
|
109
|
+
private toClass = new Map<string, [IOClass, ClassEncoding]>();
|
|
110
|
+
private toName = new Map<IOClass, [string, ClassEncoding]>();
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Create a class converter from a table of classes.
|
|
114
|
+
* @param stringClassMap - The class table of string encoded classes.
|
|
115
|
+
* @param objectClassMap - The class table of complex object classes
|
|
116
|
+
*/
|
|
117
|
+
constructor(stringClassMap?: StringClassConverterInput, objectClassMap?: JsonClassConverterInput) {
|
|
118
|
+
if (stringClassMap) {
|
|
119
|
+
for (const key of Object.keys(stringClassMap)) {
|
|
120
|
+
this.register(key, stringClassMap[key], 'string');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (objectClassMap) {
|
|
124
|
+
for (const key of Object.keys(objectClassMap)) {
|
|
125
|
+
this.register(key, objectClassMap[key], 'object');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Register a class with a certain name.
|
|
132
|
+
* This name is used for conversion from and to this class.
|
|
133
|
+
* @param type - The class name to use for serialization.
|
|
134
|
+
* @param class_ - The class object.
|
|
135
|
+
* @param encoding - Whether the class is a complex object or simply represented by a string.
|
|
136
|
+
*/
|
|
137
|
+
register(type: string, class_: IOClass, encoding: ClassEncoding) {
|
|
138
|
+
assert(type !== 'Buffer', "'Buffer' handling is hardcoded. Cannot use as name.");
|
|
139
|
+
assert(
|
|
140
|
+
class_.prototype['toString'] || class_.prototype['toJSON'],
|
|
141
|
+
`Class ${type} must define a toString() OR toJSON() method.`,
|
|
142
|
+
);
|
|
143
|
+
assert(
|
|
144
|
+
(class_ as StringIOClass)['fromString'] || (class_ as ObjIOClass)['fromJSON'],
|
|
145
|
+
`Class ${type} must define a fromString() OR fromJSON() static method.`,
|
|
146
|
+
);
|
|
147
|
+
this.toName.set(class_, [type, encoding]);
|
|
148
|
+
this.toClass.set(type, [class_, encoding]);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Does this type name have a registered class?
|
|
153
|
+
* @param type - The type name.
|
|
154
|
+
* @returns If there's a registered class.
|
|
155
|
+
*/
|
|
156
|
+
isRegisteredClassName(type: string) {
|
|
157
|
+
return this.toClass.has(type);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Is this class object registered?
|
|
161
|
+
* @param obj - The class object.
|
|
162
|
+
* @returns If it is a registered class.
|
|
163
|
+
*/
|
|
164
|
+
isRegisteredClass(obj: any) {
|
|
165
|
+
const name = obj.prototype.constructor.name;
|
|
166
|
+
return this.toName.has(obj) || this.isRegisteredClassName(name);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Convert a JSON-like object to a class object.
|
|
170
|
+
* @param jsonObj - An object encoding a class.
|
|
171
|
+
* @returns The class object.
|
|
172
|
+
*/
|
|
173
|
+
toClassObj(jsonObj: JsonEncodedClass | StringEncodedClass): any {
|
|
174
|
+
const result = this.toClass.get(jsonObj.type);
|
|
175
|
+
assert(result, `Could not find type in lookup.`);
|
|
176
|
+
|
|
177
|
+
const [class_, encoding] = result;
|
|
178
|
+
if (encoding === 'string' && typeof jsonObj.data === 'string') {
|
|
179
|
+
return (class_ as StringIOClass)!.fromString!(jsonObj.data);
|
|
180
|
+
} else {
|
|
181
|
+
return (class_ as ObjIOClass)!.fromJSON!(jsonObj.data as object);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Convert a class object to a JSON object.
|
|
186
|
+
* @param classObj - A JSON encoding a class.
|
|
187
|
+
* @returns The class object.
|
|
188
|
+
*/
|
|
189
|
+
toJsonObj(classObj: any): JsonEncodedClass | StringEncodedClass {
|
|
190
|
+
const { type, encoding } = this.lookupObject(classObj);
|
|
191
|
+
const data = encoding === 'string' ? classObj.toString() : classObj.toJSON();
|
|
192
|
+
return { type: type!, data };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Loads the corresponding type for this class based on constructor first and constructor name if not found.
|
|
197
|
+
* Constructor match works in the event of a minifier changing function names, and constructor name match
|
|
198
|
+
* works in the event of duplicated instances of node modules being loaded (see #1826).
|
|
199
|
+
* @param classObj - Object to lookup in the registered types.
|
|
200
|
+
* @returns Registered type name and encoding.
|
|
201
|
+
*/
|
|
202
|
+
private lookupObject(classObj: any) {
|
|
203
|
+
const nameResult = this.toName.get(classObj.constructor);
|
|
204
|
+
if (nameResult) {
|
|
205
|
+
return { type: nameResult[0], encoding: nameResult[1] };
|
|
206
|
+
}
|
|
207
|
+
const classResult = this.toClass.get(classObj.constructor.name);
|
|
208
|
+
if (classResult) {
|
|
209
|
+
return { type: classObj.constructor.name, encoding: classResult[1] };
|
|
210
|
+
}
|
|
211
|
+
throw new Error(`Could not find class ${classObj.constructor.name} in lookup.`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createJsonRpcClient, defaultFetch, makeFetch } from './json_rpc_client.js';
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// comlink:
|
|
2
|
+
// Dev dependency just for the somewhat complex RemoteObject type
|
|
3
|
+
// This takes a {foo(): T} and makes {foo(): Promise<T>}
|
|
4
|
+
// while avoiding Promise of Promise.
|
|
5
|
+
import { RemoteObject } from 'comlink';
|
|
6
|
+
import { format } from 'util';
|
|
7
|
+
|
|
8
|
+
import { DebugLogger, createDebugLogger } from '../../log/index.js';
|
|
9
|
+
import { NoRetryError, makeBackoff, retry } from '../../retry/index.js';
|
|
10
|
+
import { ClassConverter, JsonClassConverterInput, StringClassConverterInput } from '../class_converter.js';
|
|
11
|
+
import { JsonStringify, convertFromJsonObj, convertToJsonObj } from '../convert.js';
|
|
12
|
+
|
|
13
|
+
export { JsonStringify } from '../convert.js';
|
|
14
|
+
|
|
15
|
+
const debug = createDebugLogger('json-rpc:json_rpc_client');
|
|
16
|
+
/**
|
|
17
|
+
* A normal fetch function that does not retry.
|
|
18
|
+
* Alternatives are a fetch function with retries, or a mocked fetch.
|
|
19
|
+
* @param host - The host URL.
|
|
20
|
+
* @param method - The RPC method name.
|
|
21
|
+
* @param body - The RPC payload.
|
|
22
|
+
* @param noRetry - Whether to throw a `NoRetryError` in case the response is not ok and the body contains an error
|
|
23
|
+
* message (see `retry` function for more details).
|
|
24
|
+
* @returns The parsed JSON response, or throws an error.
|
|
25
|
+
*/
|
|
26
|
+
export async function defaultFetch(
|
|
27
|
+
host: string,
|
|
28
|
+
rpcMethod: string,
|
|
29
|
+
body: any,
|
|
30
|
+
useApiEndpoints: boolean,
|
|
31
|
+
noRetry = false,
|
|
32
|
+
) {
|
|
33
|
+
debug(format(`JsonRpcClient.fetch`, host, rpcMethod, '->', body));
|
|
34
|
+
let resp: Response;
|
|
35
|
+
if (useApiEndpoints) {
|
|
36
|
+
resp = await fetch(`${host}/${rpcMethod}`, {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
body: JsonStringify(body),
|
|
39
|
+
headers: { 'content-type': 'application/json' },
|
|
40
|
+
});
|
|
41
|
+
} else {
|
|
42
|
+
resp = await fetch(host, {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
body: JsonStringify({ ...body, method: rpcMethod }),
|
|
45
|
+
headers: { 'content-type': 'application/json' },
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let responseJson;
|
|
50
|
+
try {
|
|
51
|
+
responseJson = await resp.json();
|
|
52
|
+
} catch (err) {
|
|
53
|
+
if (!resp.ok) {
|
|
54
|
+
throw new Error(resp.statusText);
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`Failed to parse body as JSON: ${resp.text()}`);
|
|
57
|
+
}
|
|
58
|
+
if (!resp.ok) {
|
|
59
|
+
if (noRetry) {
|
|
60
|
+
throw new NoRetryError('(JSON-RPC PROPAGATED) ' + responseJson.error.message);
|
|
61
|
+
} else {
|
|
62
|
+
throw new Error('(JSON-RPC PROPAGATED) ' + responseJson.error.message);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return responseJson;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Makes a fetch function that retries based on the given attempts.
|
|
71
|
+
* @param retries - Sequence of intervals (in seconds) to retry.
|
|
72
|
+
* @param noRetry - Whether to stop retries on server errors.
|
|
73
|
+
* @param log - Optional logger for logging attempts.
|
|
74
|
+
* @returns A fetch function.
|
|
75
|
+
*/
|
|
76
|
+
export function makeFetch(retries: number[], noRetry: boolean, log?: DebugLogger) {
|
|
77
|
+
return async (host: string, rpcMethod: string, body: any, useApiEndpoints: boolean) => {
|
|
78
|
+
return await retry(
|
|
79
|
+
() => defaultFetch(host, rpcMethod, body, useApiEndpoints, noRetry),
|
|
80
|
+
`JsonRpcClient request to ${host}`,
|
|
81
|
+
makeBackoff(retries),
|
|
82
|
+
log,
|
|
83
|
+
true,
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Creates a Proxy object that delegates over RPC and satisfies RemoteObject<T>.
|
|
90
|
+
* The server should have ran new JsonRpcServer().
|
|
91
|
+
* @param host - The host URL.
|
|
92
|
+
* @param stringClassMap - A map of class names to string representations.
|
|
93
|
+
* @param objectClassMap - A map of class names to class constructors.
|
|
94
|
+
* @param useApiEndpoints - Whether to use the API endpoints or the default RPC endpoint.
|
|
95
|
+
* @param namespaceMethods - String value (or false/empty) to namespace all methods sent to the server. e.g. 'getInfo' -\> 'pxe_getInfo'
|
|
96
|
+
* @param fetch - The fetch implementation to use.
|
|
97
|
+
*/
|
|
98
|
+
export function createJsonRpcClient<T extends object>(
|
|
99
|
+
host: string,
|
|
100
|
+
stringClassMap: StringClassConverterInput,
|
|
101
|
+
objectClassMap: JsonClassConverterInput,
|
|
102
|
+
useApiEndpoints: boolean,
|
|
103
|
+
namespaceMethods?: string | false,
|
|
104
|
+
fetch = defaultFetch,
|
|
105
|
+
) {
|
|
106
|
+
const classConverter = new ClassConverter(stringClassMap, objectClassMap);
|
|
107
|
+
let id = 0;
|
|
108
|
+
const request = async (method: string, params: any[]): Promise<any> => {
|
|
109
|
+
const body = {
|
|
110
|
+
jsonrpc: '2.0',
|
|
111
|
+
id: id++,
|
|
112
|
+
method,
|
|
113
|
+
params: params.map(param => convertToJsonObj(classConverter, param)),
|
|
114
|
+
};
|
|
115
|
+
debug(format(`JsonRpcClient.request`, method, '<-', params));
|
|
116
|
+
const res = await fetch(host, method, body, useApiEndpoints);
|
|
117
|
+
debug(format(`JsonRpcClient.result`, method, '->', res));
|
|
118
|
+
if (res.error) {
|
|
119
|
+
throw res.error;
|
|
120
|
+
}
|
|
121
|
+
if ([null, undefined, 'null', 'undefined'].includes(res.result)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
return convertFromJsonObj(classConverter, res.result);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// Intercept any RPC methods with a proxy
|
|
128
|
+
// This wraps 'request' with a method-call syntax wrapper
|
|
129
|
+
return new Proxy(
|
|
130
|
+
{},
|
|
131
|
+
{
|
|
132
|
+
get: (target, method: string) => {
|
|
133
|
+
let rpcMethod = method;
|
|
134
|
+
if (namespaceMethods) {
|
|
135
|
+
rpcMethod = `${namespaceMethods}_${method}`;
|
|
136
|
+
}
|
|
137
|
+
if (['then', 'catch'].includes(method)) {
|
|
138
|
+
return Reflect.get(target, method);
|
|
139
|
+
}
|
|
140
|
+
return (...params: any[]) => {
|
|
141
|
+
debug(format(`JsonRpcClient.constructor`, 'proxy', rpcMethod, '<-', params));
|
|
142
|
+
return request(rpcMethod, params);
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
) as RemoteObject<T>;
|
|
147
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import cloneDeepWith from 'lodash.clonedeepwith';
|
|
3
|
+
|
|
4
|
+
import { ClassConverter } from './class_converter.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check prototype chain to determine if an object is 'plain' (not a class instance).
|
|
8
|
+
* @param obj - The object to check.
|
|
9
|
+
* @returns True if the object is 'plain'.
|
|
10
|
+
*/
|
|
11
|
+
function isPlainObject(obj: any) {
|
|
12
|
+
if (obj === null) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let proto = obj;
|
|
17
|
+
let counter = 0;
|
|
18
|
+
const MAX_PROTOTYPE_CHAIN_LENGTH = 1000; // Adjust as needed
|
|
19
|
+
while (Object.getPrototypeOf(proto) !== null) {
|
|
20
|
+
if (counter >= MAX_PROTOTYPE_CHAIN_LENGTH) {
|
|
21
|
+
// This is a failsafe in case circular prototype chain has been created. It should not be hit
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
proto = Object.getPrototypeOf(proto);
|
|
25
|
+
counter++;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return Object.getPrototypeOf(obj) === proto;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Recursively looks through an object for bigints and converts them to object format.
|
|
33
|
+
* @param obj - The object to convert.
|
|
34
|
+
* @returns The converted object with stringified bigints.
|
|
35
|
+
*/
|
|
36
|
+
export const convertBigintsInObj = (obj: any) => {
|
|
37
|
+
return cloneDeepWith(obj, (value: any) => {
|
|
38
|
+
if (typeof value === 'bigint') {
|
|
39
|
+
return { type: 'bigint', data: value.toString() };
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* JSON.stringify helper that handles bigints.
|
|
46
|
+
* @param obj - The object to be stringified.
|
|
47
|
+
* @returns The resulting string.
|
|
48
|
+
*/
|
|
49
|
+
export function JsonStringify(obj: object, prettify?: boolean): string {
|
|
50
|
+
return JSON.stringify(
|
|
51
|
+
obj,
|
|
52
|
+
(key, value) =>
|
|
53
|
+
typeof value === 'bigint'
|
|
54
|
+
? JSON.stringify({
|
|
55
|
+
type: 'bigint',
|
|
56
|
+
data: value.toString(),
|
|
57
|
+
})
|
|
58
|
+
: value,
|
|
59
|
+
prettify ? 2 : 0,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Convert a JSON-friendly object, which may encode a class object.
|
|
65
|
+
* @param cc - The class converter.
|
|
66
|
+
* @param obj - The encoded object.
|
|
67
|
+
* @returns The decoded object.
|
|
68
|
+
*/
|
|
69
|
+
export function convertFromJsonObj(cc: ClassConverter, obj: any): any {
|
|
70
|
+
if (obj === null) {
|
|
71
|
+
return undefined; // `null` doesn't work with default args.
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!obj) {
|
|
75
|
+
return obj; // Primitive type
|
|
76
|
+
}
|
|
77
|
+
// Is this a serialized Node buffer?
|
|
78
|
+
if (obj.type === 'Buffer' && typeof obj.data === 'string') {
|
|
79
|
+
return Buffer.from(obj.data, 'base64');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (obj.type === 'bigint' && typeof obj.data === 'string') {
|
|
83
|
+
return BigInt(obj.data);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Is this a convertible type?
|
|
87
|
+
if (typeof obj.type === 'string') {
|
|
88
|
+
if (cc.isRegisteredClassName(obj.type)) {
|
|
89
|
+
return cc.toClassObj(obj);
|
|
90
|
+
} else {
|
|
91
|
+
throw new Error(`Object ${obj.type} not registered for serialization FROM JSON`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Is this an array?
|
|
96
|
+
if (Array.isArray(obj)) {
|
|
97
|
+
return obj.map((x: any) => convertFromJsonObj(cc, x));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Is this a dictionary?
|
|
101
|
+
if (typeof obj === 'object') {
|
|
102
|
+
const newObj: any = {};
|
|
103
|
+
for (const key of Object.keys(obj)) {
|
|
104
|
+
newObj[key] = convertFromJsonObj(cc, obj[key]);
|
|
105
|
+
}
|
|
106
|
+
return newObj;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Leave alone, assume JSON primitive
|
|
110
|
+
return obj;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Convert objects or classes to a JSON-friendly object.
|
|
115
|
+
* @param cc - The class converter.
|
|
116
|
+
* @param obj - The object.
|
|
117
|
+
* @returns The encoded object.
|
|
118
|
+
*/
|
|
119
|
+
export function convertToJsonObj(cc: ClassConverter, obj: any): any {
|
|
120
|
+
// Bigint is a primitive type that needs special handling since it's not serializable
|
|
121
|
+
if (typeof obj === 'bigint') {
|
|
122
|
+
return {
|
|
123
|
+
type: 'bigint',
|
|
124
|
+
data: obj.toString(),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!obj) {
|
|
129
|
+
return obj; // Primitive type
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Is this a Node buffer?
|
|
133
|
+
if (obj instanceof Buffer) {
|
|
134
|
+
return { type: 'Buffer', data: obj.toString('base64') };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Is this a convertible type?
|
|
138
|
+
if (cc.isRegisteredClass(obj.constructor)) {
|
|
139
|
+
return cc.toJsonObj(obj);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Is this an array?
|
|
143
|
+
if (Array.isArray(obj)) {
|
|
144
|
+
return obj.map((x: any) => convertToJsonObj(cc, x));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (typeof obj === 'object') {
|
|
148
|
+
// Is this a dictionary?
|
|
149
|
+
if (isPlainObject(obj)) {
|
|
150
|
+
const newObj: any = {};
|
|
151
|
+
for (const key of Object.keys(obj)) {
|
|
152
|
+
newObj[key] = convertToJsonObj(cc, obj[key]);
|
|
153
|
+
}
|
|
154
|
+
return newObj;
|
|
155
|
+
} else {
|
|
156
|
+
// Throw if this is a non-primitive class that was not registered
|
|
157
|
+
throw new Error(`Object ${obj.constructor.name} not registered for serialization TO JSON`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Leave alone, assume JSON primitive
|
|
162
|
+
return obj;
|
|
163
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test class for testing string converter.
|
|
3
|
+
*/
|
|
4
|
+
export class ToStringClass {
|
|
5
|
+
constructor(/** A value */ public readonly x: string, /** Another value */ public readonly y: string) {}
|
|
6
|
+
|
|
7
|
+
toString(): string {
|
|
8
|
+
return [this.x, this.y].join('-');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static fromString(value: string) {
|
|
12
|
+
const [x, y] = value.split('-');
|
|
13
|
+
return new ToStringClass(x, y);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test class for testing string converter.
|
|
3
|
+
*/
|
|
4
|
+
export class ToStringClass {
|
|
5
|
+
constructor(/** A value */ public readonly x: string, /** Another value */ public readonly y: string) {}
|
|
6
|
+
|
|
7
|
+
toString(): string {
|
|
8
|
+
return [this.x, this.y].join('-');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static fromString(value: string) {
|
|
12
|
+
const [x, y] = value.split('-');
|
|
13
|
+
return new ToStringClass(x, y);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { sleep } from '../../sleep/index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Contrived example for JSON RPC tests.
|
|
5
|
+
*/
|
|
6
|
+
export class TestNote {
|
|
7
|
+
constructor(private data: string) {}
|
|
8
|
+
/**
|
|
9
|
+
* Create a string representation of this class.
|
|
10
|
+
* @returns The string representation.
|
|
11
|
+
*/
|
|
12
|
+
toString(): string {
|
|
13
|
+
return this.data;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Creates a string representation of this class.
|
|
17
|
+
* @param data - The data.
|
|
18
|
+
* @returns The string representation.
|
|
19
|
+
*/
|
|
20
|
+
static fromString(data: string): TestNote {
|
|
21
|
+
return new TestNote(data);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Represents a simple state management for TestNote instances.
|
|
27
|
+
* Provides functionality to get a note by index and add notes asynchronously.
|
|
28
|
+
* Primarily used for testing JSON RPC-related functionalities.
|
|
29
|
+
*/
|
|
30
|
+
export class TestState {
|
|
31
|
+
constructor(private notes: TestNote[]) {}
|
|
32
|
+
/**
|
|
33
|
+
* Retrieve the TestNote instance at the specified index from the notes array.
|
|
34
|
+
* This method allows getting a desired TestNote from the collection of notes
|
|
35
|
+
* maintained by the TestState instance using the provided index value.
|
|
36
|
+
*
|
|
37
|
+
* @param index - The index of the TestNote to be retrieved from the notes array.
|
|
38
|
+
* @returns The TestNote instance corresponding to the given index.
|
|
39
|
+
*/
|
|
40
|
+
getNote(index: number): TestNote {
|
|
41
|
+
return this.notes[index];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Add an array of TestNote instances to the current TestState's notes.
|
|
45
|
+
* This function simulates asynchronous behavior by waiting for a duration
|
|
46
|
+
* equal to the number of notes being added. It then returns the updated
|
|
47
|
+
* list of TestNote instances in the TestState.
|
|
48
|
+
*
|
|
49
|
+
* @param notes - An array of TestNote instances to be added.
|
|
50
|
+
* @returns A Promise that resolves to an array of TestNote instances, including the newly added notes.
|
|
51
|
+
*/
|
|
52
|
+
async addNotes(notes: TestNote[]): Promise<TestNote[]> {
|
|
53
|
+
for (const note of notes) {
|
|
54
|
+
this.notes.push(note);
|
|
55
|
+
}
|
|
56
|
+
await sleep(notes.length);
|
|
57
|
+
return this.notes;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Make sure this property was not inherited
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Does this own the property?
|
|
5
|
+
* @param obj - An object.
|
|
6
|
+
* @param method - A property name.
|
|
7
|
+
*/
|
|
8
|
+
export const hasOwnProperty = (obj: any, propertyName: string) =>
|
|
9
|
+
Object.prototype.hasOwnProperty.call(obj, propertyName);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Helper function to assert a condition is truthy
|
|
13
|
+
* @param x - A boolean condition to assert.
|
|
14
|
+
* @param err - Error message to throw if x isn't met.
|
|
15
|
+
*/
|
|
16
|
+
export function assert(x: any, err: string): asserts x {
|
|
17
|
+
if (!x) {
|
|
18
|
+
throw new Error(err);
|
|
19
|
+
}
|
|
20
|
+
}
|