@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,60 @@
|
|
|
1
|
+
import { format } from 'util';
|
|
2
|
+
|
|
3
|
+
import { createDebugLogger } from '../../log/index.js';
|
|
4
|
+
import { ClassConverter, JsonClassConverterInput, StringClassConverterInput } from '../class_converter.js';
|
|
5
|
+
import { convertFromJsonObj, convertToJsonObj } from '../convert.js';
|
|
6
|
+
import { assert, hasOwnProperty } from '../js_utils.js';
|
|
7
|
+
|
|
8
|
+
const debug = createDebugLogger('json-rpc:json_proxy');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A map of class names to class constructors.
|
|
12
|
+
*/
|
|
13
|
+
export type ClassMaps = {
|
|
14
|
+
/** The String class map */
|
|
15
|
+
stringClassMap: StringClassConverterInput;
|
|
16
|
+
/** The object class map */
|
|
17
|
+
objectClassMap: JsonClassConverterInput;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Handles conversion of objects over the write.
|
|
22
|
+
* Delegates to a ClassConverter object.
|
|
23
|
+
*/
|
|
24
|
+
export class JsonProxy {
|
|
25
|
+
classConverter: ClassConverter;
|
|
26
|
+
constructor(
|
|
27
|
+
private handler: object,
|
|
28
|
+
private stringClassMap: StringClassConverterInput,
|
|
29
|
+
private objectClassMap: JsonClassConverterInput,
|
|
30
|
+
) {
|
|
31
|
+
this.classConverter = new ClassConverter(stringClassMap, objectClassMap);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Call an RPC method.
|
|
35
|
+
* @param methodName - The RPC method.
|
|
36
|
+
* @param jsonParams - The RPG parameters.
|
|
37
|
+
* @param skipConversion - Whether to skip conversion of the parameters.
|
|
38
|
+
* @returns The remote result.
|
|
39
|
+
*/
|
|
40
|
+
public async call(methodName: string, jsonParams: any[] = [], skipConversion = false) {
|
|
41
|
+
debug(format(`JsonProxy:call`, methodName, jsonParams));
|
|
42
|
+
// Get access to our class members
|
|
43
|
+
const proto = Object.getPrototypeOf(this.handler);
|
|
44
|
+
assert(hasOwnProperty(proto, methodName), `JsonProxy: Method ${methodName} not found!`);
|
|
45
|
+
assert(Array.isArray(jsonParams), `JsonProxy: ${methodName} params not an array: ${jsonParams}`);
|
|
46
|
+
// convert the params from json representation to classes
|
|
47
|
+
let convertedParams = jsonParams;
|
|
48
|
+
if (!skipConversion) {
|
|
49
|
+
convertedParams = jsonParams.map(param => convertFromJsonObj(this.classConverter, param));
|
|
50
|
+
}
|
|
51
|
+
debug(format('JsonProxy:call', methodName, '<-', convertedParams));
|
|
52
|
+
const rawRet = await (this.handler as any)[methodName](...convertedParams);
|
|
53
|
+
let ret = rawRet;
|
|
54
|
+
if (!skipConversion) {
|
|
55
|
+
ret = convertToJsonObj(this.classConverter, rawRet);
|
|
56
|
+
}
|
|
57
|
+
debug(format('JsonProxy:call', methodName, '->', ret));
|
|
58
|
+
return ret;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import cors from '@koa/cors';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
import Koa from 'koa';
|
|
4
|
+
import bodyParser from 'koa-bodyparser';
|
|
5
|
+
import compress from 'koa-compress';
|
|
6
|
+
import Router from 'koa-router';
|
|
7
|
+
|
|
8
|
+
import { createDebugLogger } from '../../log/index.js';
|
|
9
|
+
import { JsonClassConverterInput, StringClassConverterInput } from '../class_converter.js';
|
|
10
|
+
import { convertBigintsInObj } from '../convert.js';
|
|
11
|
+
import { ClassMaps, JsonProxy } from './json_proxy.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* JsonRpcServer.
|
|
15
|
+
* Minimal, dev-friendly mechanism to create a server from an object.
|
|
16
|
+
*/
|
|
17
|
+
export class JsonRpcServer {
|
|
18
|
+
/**
|
|
19
|
+
* The proxy object.
|
|
20
|
+
*/
|
|
21
|
+
public proxy: JsonProxy;
|
|
22
|
+
constructor(
|
|
23
|
+
private handler: object,
|
|
24
|
+
private stringClassMap: StringClassConverterInput,
|
|
25
|
+
private objectClassMap: JsonClassConverterInput,
|
|
26
|
+
/** List of methods to disallow from calling remotely */
|
|
27
|
+
public readonly disallowedMethods: string[] = [],
|
|
28
|
+
private log = createDebugLogger('aztec:foundation:json-rpc:server'),
|
|
29
|
+
) {
|
|
30
|
+
this.proxy = new JsonProxy(handler, stringClassMap, objectClassMap);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get an express app object.
|
|
35
|
+
* @param prefix - Our server prefix.
|
|
36
|
+
* @returns The app object.
|
|
37
|
+
*/
|
|
38
|
+
public getApp(prefix = '') {
|
|
39
|
+
const router = this.getRouter(prefix);
|
|
40
|
+
const exceptionHandler = async (ctx: Koa.Context, next: () => Promise<void>) => {
|
|
41
|
+
try {
|
|
42
|
+
await next();
|
|
43
|
+
} catch (err: any) {
|
|
44
|
+
this.log.error(err);
|
|
45
|
+
if (err instanceof SyntaxError) {
|
|
46
|
+
ctx.status = 400;
|
|
47
|
+
ctx.body = {
|
|
48
|
+
jsonrpc: '2.0',
|
|
49
|
+
id: null,
|
|
50
|
+
error: {
|
|
51
|
+
code: -32700,
|
|
52
|
+
message: 'Parse error',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
} else {
|
|
56
|
+
ctx.status = 500;
|
|
57
|
+
ctx.body = {
|
|
58
|
+
jsonrpc: '2.0',
|
|
59
|
+
id: null,
|
|
60
|
+
error: {
|
|
61
|
+
code: -32603,
|
|
62
|
+
message: 'Internal error',
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const app = new Koa();
|
|
69
|
+
app.on('error', error => {
|
|
70
|
+
this.log.error(`Error on API handler: ${error}`);
|
|
71
|
+
});
|
|
72
|
+
app.use(exceptionHandler);
|
|
73
|
+
app.use(compress({ br: false } as any));
|
|
74
|
+
app.use(
|
|
75
|
+
bodyParser({
|
|
76
|
+
jsonLimit: '10mb',
|
|
77
|
+
enableTypes: ['json'],
|
|
78
|
+
detectJSON: () => true,
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
app.use(cors());
|
|
82
|
+
app.use(router.routes());
|
|
83
|
+
app.use(router.allowedMethods());
|
|
84
|
+
|
|
85
|
+
return app;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get a router object wrapping our RPC class.
|
|
90
|
+
* @param prefix - The server prefix.
|
|
91
|
+
* @returns The router object.
|
|
92
|
+
*/
|
|
93
|
+
private getRouter(prefix: string) {
|
|
94
|
+
const router = new Router({ prefix });
|
|
95
|
+
const proto = Object.getPrototypeOf(this.handler);
|
|
96
|
+
// "JSON RPC mode" where a single endpoint is used and the method is given in the request body
|
|
97
|
+
router.post('/', async (ctx: Koa.Context) => {
|
|
98
|
+
const { params = [], jsonrpc, id, method } = ctx.request.body as any;
|
|
99
|
+
// Ignore if not a function
|
|
100
|
+
if (method === 'constructor' || typeof proto[method] !== 'function' || this.disallowedMethods.includes(method)) {
|
|
101
|
+
ctx.status = 400;
|
|
102
|
+
ctx.body = {
|
|
103
|
+
jsonrpc,
|
|
104
|
+
id,
|
|
105
|
+
error: {
|
|
106
|
+
code: -32601,
|
|
107
|
+
message: `Method not found: ${method}`,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
} else {
|
|
111
|
+
try {
|
|
112
|
+
const result = await this.proxy.call(method, params);
|
|
113
|
+
ctx.body = {
|
|
114
|
+
jsonrpc,
|
|
115
|
+
id,
|
|
116
|
+
result: convertBigintsInObj(result),
|
|
117
|
+
};
|
|
118
|
+
ctx.status = 200;
|
|
119
|
+
} catch (err: any) {
|
|
120
|
+
// Propagate the error message to the client. Plenty of the errors are expected to occur (e.g. adding
|
|
121
|
+
// a duplicate recipient) so this is necessary.
|
|
122
|
+
ctx.status = 400;
|
|
123
|
+
ctx.body = {
|
|
124
|
+
jsonrpc,
|
|
125
|
+
id,
|
|
126
|
+
error: {
|
|
127
|
+
// TODO assign error codes - https://github.com/AztecProtocol/aztec-packages/issues/2633
|
|
128
|
+
code: -32000,
|
|
129
|
+
message: err.message,
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
return router;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Start this server with koa.
|
|
141
|
+
* @param port - Port number.
|
|
142
|
+
* @param prefix - Prefix string.
|
|
143
|
+
*/
|
|
144
|
+
public start(port: number, prefix = '') {
|
|
145
|
+
const httpServer = http.createServer(this.getApp(prefix).callback());
|
|
146
|
+
httpServer.listen(port);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get a list of methods.
|
|
151
|
+
* @returns A list of methods.
|
|
152
|
+
*/
|
|
153
|
+
public getMethods(): string[] {
|
|
154
|
+
return Object.getOwnPropertyNames(Object.getPrototypeOf(this.handler));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Gets the class maps that were used to create the proxy.
|
|
159
|
+
* @returns The string & object class maps.
|
|
160
|
+
*/
|
|
161
|
+
public getClassMaps(): ClassMaps {
|
|
162
|
+
return { stringClassMap: this.stringClassMap, objectClassMap: this.objectClassMap };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Call an RPC method.
|
|
167
|
+
* @param methodName - The RPC method.
|
|
168
|
+
* @param jsonParams - The RPG parameters.
|
|
169
|
+
* @param skipConversion - Whether to skip conversion of the parameters.
|
|
170
|
+
* @returns The remote result.
|
|
171
|
+
*/
|
|
172
|
+
public async call(methodName: string, jsonParams: any[] = [], skipConversion: boolean) {
|
|
173
|
+
return await this.proxy.call(methodName, jsonParams, skipConversion);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Creates a router for handling a plain status request that will return 200 status when running.
|
|
179
|
+
* @param apiPrefix - The prefix to use for all api requests
|
|
180
|
+
* @returns - The router for handling status requests.
|
|
181
|
+
*/
|
|
182
|
+
export function createStatusRouter(apiPrefix = '') {
|
|
183
|
+
const router = new Router({ prefix: `${apiPrefix}` });
|
|
184
|
+
router.get('/status', (ctx: Koa.Context) => {
|
|
185
|
+
ctx.status = 200;
|
|
186
|
+
});
|
|
187
|
+
return router;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Creates an http server that forwards calls to the underlying instance and starts it on the given port.
|
|
192
|
+
* @param instance - Instance to wrap in a JSON-RPC server.
|
|
193
|
+
* @param jsonRpcFactoryFunc - Function that wraps the instance in a JSON-RPC server.
|
|
194
|
+
* @param port - Port to listen in.
|
|
195
|
+
* @returns A running http server.
|
|
196
|
+
*/
|
|
197
|
+
export function startHttpRpcServer<T>(
|
|
198
|
+
name: string,
|
|
199
|
+
instance: T,
|
|
200
|
+
jsonRpcFactoryFunc: (instance: T) => JsonRpcServer,
|
|
201
|
+
port: string | number,
|
|
202
|
+
): http.Server {
|
|
203
|
+
const rpcServer = jsonRpcFactoryFunc(instance);
|
|
204
|
+
|
|
205
|
+
const namespacedServer = createNamespacedJsonRpcServer([{ [name]: rpcServer }]);
|
|
206
|
+
|
|
207
|
+
const app = namespacedServer.getApp();
|
|
208
|
+
|
|
209
|
+
const httpServer = http.createServer(app.callback());
|
|
210
|
+
httpServer.listen(port);
|
|
211
|
+
|
|
212
|
+
return httpServer;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* List of namespace to server instance.
|
|
216
|
+
*/
|
|
217
|
+
export type ServerList = {
|
|
218
|
+
/** name of the service to be used for namespacing */
|
|
219
|
+
[name: string]: JsonRpcServer;
|
|
220
|
+
}[];
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Creates a single JsonRpcServer from multiple servers.
|
|
224
|
+
* @param servers - List of servers to be combined into a single server, passed as ServerList.
|
|
225
|
+
* @returns A single JsonRpcServer with namespaced methods.
|
|
226
|
+
*/
|
|
227
|
+
export function createNamespacedJsonRpcServer(
|
|
228
|
+
servers: ServerList,
|
|
229
|
+
log = createDebugLogger('aztec:foundation:json-rpc:multi-server'),
|
|
230
|
+
): JsonRpcServer {
|
|
231
|
+
const handler = {} as any;
|
|
232
|
+
const disallowedMethods: string[] = [];
|
|
233
|
+
const classMapsArr: ClassMaps[] = [];
|
|
234
|
+
|
|
235
|
+
for (const serverEntry of servers) {
|
|
236
|
+
const [namespace, server] = Object.entries(serverEntry)[0];
|
|
237
|
+
const serverMethods = server.getMethods();
|
|
238
|
+
|
|
239
|
+
for (const method of serverMethods) {
|
|
240
|
+
const namespacedMethod = `${namespace}_${method}`;
|
|
241
|
+
|
|
242
|
+
handler[namespacedMethod] = (...args: any[]) => {
|
|
243
|
+
return server.call(method, args, true);
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// get the combined disallowed methods from all servers.
|
|
248
|
+
disallowedMethods.push(...server.disallowedMethods.map(method => `${namespace}_${method}`));
|
|
249
|
+
// get the combined classmaps from all servers.
|
|
250
|
+
const classMap = server.getClassMaps();
|
|
251
|
+
classMapsArr.push({
|
|
252
|
+
stringClassMap: classMap.stringClassMap,
|
|
253
|
+
objectClassMap: classMap.objectClassMap,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Get the combined stringClassMap & objectClassMap from all servers
|
|
258
|
+
const classMaps = classMapsArr.reduce(
|
|
259
|
+
(acc, curr) => {
|
|
260
|
+
return {
|
|
261
|
+
stringClassMap: { ...acc.stringClassMap, ...curr.stringClassMap },
|
|
262
|
+
objectClassMap: { ...acc.objectClassMap, ...curr.objectClassMap },
|
|
263
|
+
};
|
|
264
|
+
},
|
|
265
|
+
{ stringClassMap: {}, objectClassMap: {} } as ClassMaps,
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
return new JsonRpcServer(Object.create(handler), classMaps.stringClassMap, classMaps.objectClassMap, [], log);
|
|
269
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { LogFn } from './log_fn.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ConsoleLogger is a utility class that provides customizable console logging functionality.
|
|
6
|
+
* It allows setting a custom prefix for log messages and an optional custom logger function,
|
|
7
|
+
* which can be useful for controlling the format of the output or redirecting logs to a different destination.
|
|
8
|
+
*/
|
|
9
|
+
class ConsoleLogger {
|
|
10
|
+
constructor(private prefix: string, private logger: (...args: any[]) => void = console.log) {}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Log messages with the specified prefix using the provided logger.
|
|
14
|
+
* By default, it uses 'console.log' as the logger but can be overridden
|
|
15
|
+
* during ConsoleLogger instantiation. This method allows for easy
|
|
16
|
+
* organization and readability of log messages in the console.
|
|
17
|
+
*
|
|
18
|
+
* @param args - The data to be logged, any number of arguments can be passed to this function.
|
|
19
|
+
*/
|
|
20
|
+
public log(...args: any[]) {
|
|
21
|
+
this.logger(`${this.prefix}:`, ...args);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates a Logger function with an optional prefix for log messages.
|
|
27
|
+
* If a prefix is provided, the created logger will prepend it to each log message.
|
|
28
|
+
* If no prefix is provided, the default console.log will be returned.
|
|
29
|
+
*
|
|
30
|
+
* @param prefix - The optional string to prepend to each log message.
|
|
31
|
+
* @returns A Logger function that accepts any number of arguments and logs them with the specified prefix.
|
|
32
|
+
*/
|
|
33
|
+
export function createConsoleLogger(prefix?: string): LogFn {
|
|
34
|
+
if (prefix) {
|
|
35
|
+
const logger = new ConsoleLogger(prefix, console.log);
|
|
36
|
+
return (...args: any[]) => logger.log(...args);
|
|
37
|
+
}
|
|
38
|
+
return console.log;
|
|
39
|
+
}
|
package/src/log/debug.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
|
|
3
|
+
import { LogFn } from './log_fn.js';
|
|
4
|
+
|
|
5
|
+
let preLogHook: ((...args: any[]) => void) | undefined;
|
|
6
|
+
let postLogHook: ((...args: any[]) => void) | undefined;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Process and handle the logging of messages through custom hooks and the given logger.
|
|
10
|
+
* This function checks if the logger's namespace is enabled, executes any preLogHook functions, logs the message using the provided logger, and then executes any postLogHook functions.
|
|
11
|
+
*
|
|
12
|
+
* @param logger - The debug logger instance to be used for logging.
|
|
13
|
+
* @param args - The arguments to be passed to the logger and any hook functions.
|
|
14
|
+
*/
|
|
15
|
+
function theFunctionThroughWhichAllLogsPass(logger: any, ...args: any[]) {
|
|
16
|
+
if (!debug.enabled(logger.namespace)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (preLogHook) {
|
|
20
|
+
preLogHook(logger.namespace, ...args);
|
|
21
|
+
}
|
|
22
|
+
logger(...args);
|
|
23
|
+
if (postLogHook) {
|
|
24
|
+
postLogHook(logger.namespace, ...args);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Return a logger, meant to be silent by default and verbose during debugging.
|
|
30
|
+
* @param name - The module name of the logger.
|
|
31
|
+
* @returns A callable log function.
|
|
32
|
+
*/
|
|
33
|
+
export function createDebugOnlyLogger(name: string): LogFn {
|
|
34
|
+
const logger = debug(name);
|
|
35
|
+
return (...args: any[]) => theFunctionThroughWhichAllLogsPass(logger, ...args);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Set a function to be called before each log message is handled by the debug logger.
|
|
40
|
+
* The hook function will receive the logger namespace and any arguments passed to the logger.
|
|
41
|
+
* This can be useful for adding additional context, filtering logs, or performing side-effects
|
|
42
|
+
* based on logged messages.
|
|
43
|
+
*
|
|
44
|
+
* @param fn - The function to be called before each log message.
|
|
45
|
+
*/
|
|
46
|
+
export function setPreDebugLogHook(fn: (...args: any[]) => void) {
|
|
47
|
+
preLogHook = fn;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Set a callback function to be executed after each log is written by the debug logger.
|
|
52
|
+
* This allows additional behavior or side effects to occur after a log has been written,
|
|
53
|
+
* such as sending logs to external services, formatting output, or triggering events.
|
|
54
|
+
*
|
|
55
|
+
* @param fn - The callback function to be executed after each log. It receives the same arguments as the original log function call.
|
|
56
|
+
*/
|
|
57
|
+
export function setPostDebugLogHook(fn: (...args: any[]) => void) {
|
|
58
|
+
postLogHook = fn;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Enable logs for the specified namespace(s) or wildcard pattern(s).
|
|
63
|
+
* This function activates the logging functionality for the given
|
|
64
|
+
* namespace(s) or pattern(s), allowing developers to selectively display
|
|
65
|
+
* debug logs that match the provided string(s).
|
|
66
|
+
*
|
|
67
|
+
* @param str - The namespace(s) or wildcard pattern(s) for which logs should be enabled.
|
|
68
|
+
*/
|
|
69
|
+
export function enableLogs(str: string) {
|
|
70
|
+
debug.enable(str);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check if the logging is enabled for a given namespace.
|
|
75
|
+
* The input 'str' represents the namespace for which the log status is being checked.
|
|
76
|
+
* Returns true if the logging is enabled, otherwise false.
|
|
77
|
+
*
|
|
78
|
+
* @param str - The namespace string used to determine if logging is enabled.
|
|
79
|
+
* @returns A boolean indicating whether logging is enabled for the given namespace.
|
|
80
|
+
*/
|
|
81
|
+
export function isLogEnabled(str: string) {
|
|
82
|
+
return debug.enabled(str);
|
|
83
|
+
}
|
package/src/log/index.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { setPreDebugLogHook } from './debug.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LogHistory is a utility class that provides the ability to store and manage debug logs.
|
|
5
|
+
* It can be enabled to record logs along with their timestamps, retrieve a specified number
|
|
6
|
+
* of recent logs, or clear stored logs based on a given count. This can be useful for debugging
|
|
7
|
+
* purposes, monitoring application activities, and maintaining log history.
|
|
8
|
+
*/
|
|
9
|
+
export class LogHistory {
|
|
10
|
+
private logs: any[][] = [];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Enables the logging of debug messages with timestamps.
|
|
14
|
+
* Hooks into the pre-debug log and stores each log entry along with its timestamp in the logs array.
|
|
15
|
+
*/
|
|
16
|
+
public enable() {
|
|
17
|
+
setPreDebugLogHook((...args: any[]) => {
|
|
18
|
+
this.logs.push([new Date().toISOString(), ...args]);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Retrieves a specified number of logs from the end of the log history or all logs if no argument is provided.
|
|
24
|
+
* The logs are ordered chronologically, with the oldest logs at the beginning of the array.
|
|
25
|
+
*
|
|
26
|
+
* @param last - Optional number representing the amount of recent logs to return. Defaults to 0, which returns all logs.
|
|
27
|
+
* @returns An array of log arrays, each containing a timestamp and log arguments.
|
|
28
|
+
*/
|
|
29
|
+
public getLogs(last = 0) {
|
|
30
|
+
return last ? this.logs.slice(-last) : this.logs;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Clear a specified number of logs from the beginning of the logs array.
|
|
35
|
+
* If no count is provided, it will clear all logs.
|
|
36
|
+
*
|
|
37
|
+
* @param count - The number of logs to be removed (default: total logs length).
|
|
38
|
+
*/
|
|
39
|
+
public clear(count = this.logs.length) {
|
|
40
|
+
this.logs = this.logs.slice(count);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const logHistory = new LogHistory();
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
import isNode from 'detect-node';
|
|
3
|
+
import { isatty } from 'tty';
|
|
4
|
+
|
|
5
|
+
import { LogData, LogFn } from './log_fn.js';
|
|
6
|
+
|
|
7
|
+
// Matches a subset of Winston log levels
|
|
8
|
+
const LogLevels = ['silent', 'error', 'warn', 'info', 'verbose', 'debug'] as const;
|
|
9
|
+
const DefaultLogLevel = process.env.NODE_ENV === 'test' ? ('silent' as const) : ('info' as const);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A valid log severity level.
|
|
13
|
+
*/
|
|
14
|
+
export type LogLevel = (typeof LogLevels)[number];
|
|
15
|
+
|
|
16
|
+
const envLogLevel = process.env.LOG_LEVEL?.toLowerCase() as LogLevel;
|
|
17
|
+
const currentLevel = LogLevels.includes(envLogLevel) ? envLogLevel : DefaultLogLevel;
|
|
18
|
+
|
|
19
|
+
/** Log function that accepts an exception object */
|
|
20
|
+
type ErrorLogFn = (msg: string, err?: Error | unknown, data?: LogData) => void;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Logger that supports multiple severity levels.
|
|
24
|
+
*/
|
|
25
|
+
export type Logger = { [K in LogLevel]: LogFn } & { /** Error log function */ error: ErrorLogFn };
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Logger that supports multiple severity levels and can be called directly to issue a debug statement.
|
|
29
|
+
* Intended as a drop-in replacement for the debug module.
|
|
30
|
+
*/
|
|
31
|
+
export type DebugLogger = LogFn & Logger;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Creates a new DebugLogger for the current module, defaulting to the LOG_LEVEL env var.
|
|
35
|
+
* If DEBUG="[module]" env is set, will enable debug logging if the module matches.
|
|
36
|
+
* Uses npm debug for debug level and console.error for other levels.
|
|
37
|
+
* @param name - Name of the module.
|
|
38
|
+
* @returns A debug logger.
|
|
39
|
+
*/
|
|
40
|
+
export function createDebugLogger(name: string): DebugLogger {
|
|
41
|
+
const debugLogger = debug(name);
|
|
42
|
+
if (currentLevel === 'debug') {
|
|
43
|
+
debugLogger.enabled = true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const logger = {
|
|
47
|
+
silent: () => {},
|
|
48
|
+
error: (msg: string, err?: unknown, data?: LogData) => logWithDebug(debugLogger, 'error', fmtErr(msg, err), data),
|
|
49
|
+
warn: (msg: string, data?: LogData) => logWithDebug(debugLogger, 'warn', msg, data),
|
|
50
|
+
info: (msg: string, data?: LogData) => logWithDebug(debugLogger, 'info', msg, data),
|
|
51
|
+
verbose: (msg: string, data?: LogData) => logWithDebug(debugLogger, 'verbose', msg, data),
|
|
52
|
+
debug: (msg: string, data?: LogData) => logWithDebug(debugLogger, 'debug', msg, data),
|
|
53
|
+
};
|
|
54
|
+
return Object.assign((msg: string, data?: LogData) => logWithDebug(debugLogger, 'debug', msg, data), logger);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** A callback to capture all logs. */
|
|
58
|
+
export type LogHandler = (level: LogLevel, namespace: string, msg: string, data?: LogData) => void;
|
|
59
|
+
|
|
60
|
+
const logHandlers: LogHandler[] = [];
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Registers a callback for all logs, whether they are emitted in the current log level or not.
|
|
64
|
+
* @param handler - Callback to be called on every log.
|
|
65
|
+
*/
|
|
66
|
+
export function onLog(handler: LogHandler) {
|
|
67
|
+
logHandlers.push(handler);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Logs args to npm debug if enabled or log level is debug, console.error otherwise.
|
|
72
|
+
* @param debug - Instance of npm debug.
|
|
73
|
+
* @param level - Intended log level.
|
|
74
|
+
* @param args - Args to log.
|
|
75
|
+
*/
|
|
76
|
+
function logWithDebug(debug: debug.Debugger, level: LogLevel, msg: string, data?: LogData) {
|
|
77
|
+
for (const handler of logHandlers) {
|
|
78
|
+
handler(level, debug.namespace, msg, data);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
msg = data ? `${msg} ${fmtLogData(data)}` : msg;
|
|
82
|
+
if (debug.enabled) {
|
|
83
|
+
if (level !== 'debug') {
|
|
84
|
+
msg = `${level.toUpperCase()} ${msg}`;
|
|
85
|
+
}
|
|
86
|
+
debug(msg);
|
|
87
|
+
} else if (LogLevels.indexOf(level) <= LogLevels.indexOf(currentLevel)) {
|
|
88
|
+
printLog(`${getPrefix(debug, level)} ${msg}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Returns a log prefix that emulates that of npm debug. Uses colors if in node and in a tty.
|
|
94
|
+
* @param debugLogger - Instance of npm debug logger.
|
|
95
|
+
* @param level - Intended log level (printed out if strictly above current log level).
|
|
96
|
+
* @returns Log prefix.
|
|
97
|
+
*/
|
|
98
|
+
function getPrefix(debugLogger: debug.Debugger, level: LogLevel) {
|
|
99
|
+
const levelLabel = currentLevel !== level ? ` ${level.toUpperCase()}` : '';
|
|
100
|
+
const prefix = `${debugLogger.namespace.replace(/^aztec:/, '')}${levelLabel}`;
|
|
101
|
+
if (!isNode || !isatty(process.stderr.fd)) {
|
|
102
|
+
return prefix;
|
|
103
|
+
}
|
|
104
|
+
const colorIndex = debug.selectColor(debugLogger.namespace) as number;
|
|
105
|
+
const colorCode = '\u001B[3' + (colorIndex < 8 ? colorIndex : '8;5;' + colorIndex);
|
|
106
|
+
return ` ${colorCode};1m${prefix}\u001B[0m`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Outputs to console error.
|
|
111
|
+
* @param msg - What to log.
|
|
112
|
+
*/
|
|
113
|
+
function printLog(msg: string) {
|
|
114
|
+
// eslint-disable-next-line no-console
|
|
115
|
+
console.error(msg);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Concatenates a log message and an exception.
|
|
120
|
+
* @param msg - Log message
|
|
121
|
+
* @param err - Error to log
|
|
122
|
+
* @returns A string with both the log message and the error message.
|
|
123
|
+
*/
|
|
124
|
+
function fmtErr(msg: string, err?: Error | unknown): string {
|
|
125
|
+
const errStr = err && [(err as Error).name, (err as Error).message].filter(x => !!x).join(' ');
|
|
126
|
+
return err ? `${msg}: ${errStr || err}` : msg;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Formats structured log data as a string for console output.
|
|
131
|
+
* @param data - Optional log data.
|
|
132
|
+
*/
|
|
133
|
+
function fmtLogData(data?: LogData): string {
|
|
134
|
+
return Object.entries(data ?? {})
|
|
135
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
136
|
+
.join(' ');
|
|
137
|
+
}
|