@aztec/foundation 0.66.0 → 0.67.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dest/abi/abi.d.ts.map +1 -1
  2. package/dest/abi/abi.js +25 -10
  3. package/dest/abi/encoder.js +3 -3
  4. package/dest/abi/event_selector.js +2 -2
  5. package/dest/abi/function_selector.js +2 -2
  6. package/dest/collection/array.d.ts +2 -0
  7. package/dest/collection/array.d.ts.map +1 -1
  8. package/dest/collection/array.js +10 -1
  9. package/dest/collection/object.d.ts +6 -0
  10. package/dest/collection/object.d.ts.map +1 -1
  11. package/dest/collection/object.js +15 -1
  12. package/dest/config/env_var.d.ts +1 -1
  13. package/dest/config/env_var.d.ts.map +1 -1
  14. package/dest/crypto/random/randomness_singleton.js +3 -3
  15. package/dest/fields/fields.d.ts +20 -2
  16. package/dest/fields/fields.d.ts.map +1 -1
  17. package/dest/fields/fields.js +37 -3
  18. package/dest/fs/run_in_dir.js +2 -2
  19. package/dest/iterable/index.d.ts +1 -0
  20. package/dest/iterable/index.d.ts.map +1 -1
  21. package/dest/iterable/index.js +2 -1
  22. package/dest/iterable/toArray.d.ts +2 -0
  23. package/dest/iterable/toArray.d.ts.map +1 -0
  24. package/dest/iterable/toArray.js +8 -0
  25. package/dest/json-rpc/client/fetch.d.ts +2 -2
  26. package/dest/json-rpc/client/fetch.d.ts.map +1 -1
  27. package/dest/json-rpc/client/fetch.js +3 -3
  28. package/dest/json-rpc/client/safe_json_rpc_client.d.ts.map +1 -1
  29. package/dest/json-rpc/client/safe_json_rpc_client.js +7 -12
  30. package/dest/json-rpc/server/safe_json_rpc_server.d.ts +3 -2
  31. package/dest/json-rpc/server/safe_json_rpc_server.d.ts.map +1 -1
  32. package/dest/json-rpc/server/safe_json_rpc_server.js +5 -5
  33. package/dest/log/log-filters.d.ts.map +1 -1
  34. package/dest/log/log-filters.js +8 -2
  35. package/dest/log/log_fn.d.ts +1 -1
  36. package/dest/log/pino-logger.d.ts +18 -7
  37. package/dest/log/pino-logger.d.ts.map +1 -1
  38. package/dest/log/pino-logger.js +32 -18
  39. package/dest/queue/base_memory_queue.d.ts.map +1 -1
  40. package/dest/queue/base_memory_queue.js +3 -3
  41. package/dest/queue/bounded_serial_queue.d.ts.map +1 -1
  42. package/dest/queue/bounded_serial_queue.js +3 -3
  43. package/dest/queue/fifo_memory_queue.d.ts +2 -2
  44. package/dest/queue/fifo_memory_queue.d.ts.map +1 -1
  45. package/dest/queue/fifo_memory_queue.js +1 -1
  46. package/dest/retry/index.d.ts.map +1 -1
  47. package/dest/retry/index.js +3 -3
  48. package/dest/schemas/utils.d.ts +3 -1
  49. package/dest/schemas/utils.d.ts.map +1 -1
  50. package/dest/schemas/utils.js +8 -3
  51. package/dest/testing/test_data.d.ts +11 -0
  52. package/dest/testing/test_data.d.ts.map +1 -1
  53. package/dest/testing/test_data.js +24 -1
  54. package/dest/transport/dispatch/create_dispatch_fn.d.ts.map +1 -1
  55. package/dest/transport/dispatch/create_dispatch_fn.js +3 -3
  56. package/dest/transport/transport_client.js +3 -3
  57. package/dest/wasm/wasm_module.d.ts.map +1 -1
  58. package/dest/wasm/wasm_module.js +2 -2
  59. package/dest/worker/worker_pool.js +3 -3
  60. package/package.json +9 -3
  61. package/src/abi/abi.ts +30 -11
  62. package/src/abi/encoder.ts +2 -2
  63. package/src/abi/event_selector.ts +1 -1
  64. package/src/abi/function_selector.ts +1 -1
  65. package/src/collection/array.ts +10 -0
  66. package/src/collection/object.ts +22 -0
  67. package/src/config/env_var.ts +9 -2
  68. package/src/crypto/random/randomness_singleton.ts +2 -2
  69. package/src/fields/fields.ts +40 -2
  70. package/src/fs/run_in_dir.ts +1 -1
  71. package/src/iterable/index.ts +1 -0
  72. package/src/iterable/toArray.ts +7 -0
  73. package/src/jest/setup.mjs +9 -0
  74. package/src/json-rpc/client/fetch.ts +3 -3
  75. package/src/json-rpc/client/safe_json_rpc_client.ts +7 -15
  76. package/src/json-rpc/server/safe_json_rpc_server.ts +5 -5
  77. package/src/log/log-filters.ts +7 -1
  78. package/src/log/log_fn.ts +1 -1
  79. package/src/log/pino-logger.ts +48 -41
  80. package/src/queue/base_memory_queue.ts +2 -2
  81. package/src/queue/bounded_serial_queue.ts +2 -2
  82. package/src/queue/fifo_memory_queue.ts +2 -2
  83. package/src/retry/index.ts +2 -2
  84. package/src/schemas/utils.ts +10 -2
  85. package/src/testing/test_data.ts +25 -0
  86. package/src/transport/dispatch/create_dispatch_fn.ts +2 -2
  87. package/src/transport/transport_client.ts +2 -2
  88. package/src/wasm/wasm_module.ts +1 -1
  89. package/src/worker/worker_pool.ts +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/foundation",
3
- "version": "0.66.0",
3
+ "version": "0.67.1",
4
4
  "packageManager": "yarn@3.4.1",
5
5
  "type": "module",
6
6
  "main": "./dest/index.js",
@@ -96,10 +96,17 @@
96
96
  "summaryThreshold": 9999
97
97
  }
98
98
  ]
99
+ ],
100
+ "testTimeout": 30000,
101
+ "setupFilesAfterEnv": [
102
+ "../../foundation/src/jest/setup.mjs"
103
+ ],
104
+ "setupFiles": [
105
+ "../../foundation/src/jest/setup.mjs"
99
106
  ]
100
107
  },
101
108
  "dependencies": {
102
- "@aztec/bb.js": "0.66.0",
109
+ "@aztec/bb.js": "0.67.1",
103
110
  "@koa/cors": "^5.0.0",
104
111
  "@noble/curves": "^1.2.0",
105
112
  "bn.js": "^5.2.1",
@@ -133,7 +140,6 @@
133
140
  "@types/koa": "^2.13.5",
134
141
  "@types/koa-bodyparser": "^4.3.10",
135
142
  "@types/koa-compress": "^4.0.3",
136
- "@types/koa-cors": "^0.0.2",
137
143
  "@types/koa-router": "^7.4.4",
138
144
  "@types/koa__cors": "^4.0.0",
139
145
  "@types/leveldown": "^4.0.3",
package/src/abi/abi.ts CHANGED
@@ -3,6 +3,7 @@ import { inflate } from 'pako';
3
3
  import { z } from 'zod';
4
4
 
5
5
  import { type Fr } from '../fields/fields.js';
6
+ import { createLogger } from '../log/index.js';
6
7
  import { schemas } from '../schemas/schemas.js';
7
8
  import { type ZodFor } from '../schemas/types.js';
8
9
  import { type FunctionSelector } from './function_selector.js';
@@ -15,6 +16,8 @@ export interface BasicValue<T extends string, V> {
15
16
  value: V;
16
17
  }
17
18
 
19
+ const logger = createLogger('aztec:foundation:abi');
20
+
18
21
  /** An exported value. */
19
22
  export type AbiValue =
20
23
  | BasicValue<'boolean', boolean>
@@ -390,7 +393,9 @@ export function getFunctionArtifact(
390
393
  if (!functionArtifact) {
391
394
  throw new Error(`Unknown function ${functionNameOrSelector}`);
392
395
  }
396
+
393
397
  const debugMetadata = getFunctionDebugMetadata(artifact, functionArtifact);
398
+
394
399
  return { ...functionArtifact, debug: debugMetadata };
395
400
  }
396
401
 
@@ -404,18 +409,32 @@ export function getFunctionDebugMetadata(
404
409
  contractArtifact: ContractArtifact,
405
410
  functionArtifact: FunctionArtifact,
406
411
  ): FunctionDebugMetadata | undefined {
407
- if (functionArtifact.debugSymbols && contractArtifact.fileMap) {
408
- const programDebugSymbols = JSON.parse(
409
- inflate(Buffer.from(functionArtifact.debugSymbols, 'base64'), { to: 'string', raw: true }),
410
- );
411
- // TODO(https://github.com/AztecProtocol/aztec-packages/issues/5813)
412
- // We only support handling debug info for the contract function entry point.
413
- // So for now we simply index into the first debug info.
414
- return {
415
- debugSymbols: programDebugSymbols.debug_infos[0],
416
- files: contractArtifact.fileMap,
417
- };
412
+ try {
413
+ if (functionArtifact.debugSymbols && contractArtifact.fileMap) {
414
+ // TODO(https://github.com/AztecProtocol/aztec-packages/issues/10546) investigate why debugMetadata is so big for some tests.
415
+ const programDebugSymbols = JSON.parse(
416
+ inflate(Buffer.from(functionArtifact.debugSymbols, 'base64'), { to: 'string', raw: true }),
417
+ );
418
+ // TODO(https://github.com/AztecProtocol/aztec-packages/issues/5813)
419
+ // We only support handling debug info for the contract function entry point.
420
+ // So for now we simply index into the first debug info.
421
+ return {
422
+ debugSymbols: programDebugSymbols.debug_infos[0],
423
+ files: contractArtifact.fileMap,
424
+ };
425
+ }
426
+ } catch (err: any) {
427
+ if (err instanceof RangeError && err.message.includes('Invalid string length')) {
428
+ logger.warn(
429
+ `Caught RangeError: Invalid string length. This suggests the debug_symbols field of the contract ${contractArtifact.name} and function ${functionArtifact.name} is huge; too big to parse. We'll skip returning this info until this issue is resolved. Here's the error:\n${err.message}`,
430
+ );
431
+ // We'll return undefined.
432
+ } else {
433
+ // Rethrow unexpected errors
434
+ throw err;
435
+ }
418
436
  }
437
+
419
438
  return undefined;
420
439
  }
421
440
 
@@ -49,7 +49,7 @@ class ArgumentEncoder {
49
49
  } else if (typeof arg === 'bigint') {
50
50
  this.flattened.push(new Fr(arg));
51
51
  } else if (typeof arg === 'string') {
52
- this.flattened.push(Fr.fromString(arg));
52
+ this.flattened.push(Fr.fromHexString(arg));
53
53
  } else if (typeof arg === 'boolean') {
54
54
  this.flattened.push(new Fr(arg ? 1n : 0n));
55
55
  } else if (typeof arg === 'object') {
@@ -58,7 +58,7 @@ class ArgumentEncoder {
58
58
  } else if (typeof arg.toField === 'function') {
59
59
  this.flattened.push(arg.toField());
60
60
  } else if (typeof arg.value === 'string') {
61
- this.flattened.push(Fr.fromString(arg.value));
61
+ this.flattened.push(Fr.fromHexString(arg.value));
62
62
  } else {
63
63
  throw new Error(`Argument for ${name} cannot be serialized to a field`);
64
64
  }
@@ -62,7 +62,7 @@ export class EventSelector extends Selector {
62
62
  static fromString(selector: string) {
63
63
  const buf = fromHex(selector);
64
64
  if (buf.length !== Selector.SIZE) {
65
- throw new Error(`Invalid Selector length ${buf.length} (expected ${Selector.SIZE}).`);
65
+ throw new Error(`Invalid EventSelector length ${buf.length} (expected ${Selector.SIZE}).`);
66
66
  }
67
67
  return EventSelector.fromBuffer(buf);
68
68
  }
@@ -91,7 +91,7 @@ export class FunctionSelector extends Selector {
91
91
  static fromString(selector: string) {
92
92
  const buf = fromHex(selector);
93
93
  if (buf.length !== Selector.SIZE) {
94
- throw new Error(`Invalid Selector length ${buf.length} (expected ${Selector.SIZE}).`);
94
+ throw new Error(`Invalid FunctionSelector length ${buf.length} (expected ${Selector.SIZE}).`);
95
95
  }
96
96
  return FunctionSelector.fromBuffer(buf);
97
97
  }
@@ -145,3 +145,13 @@ export function areArraysEqual<T>(a: T[], b: T[], eq: (a: T, b: T) => boolean =
145
145
  export function maxBy<T>(arr: T[], fn: (x: T) => number): T | undefined {
146
146
  return arr.reduce((max, x) => (fn(x) > fn(max) ? x : max), arr[0]);
147
147
  }
148
+
149
+ /** Computes the median of a numeric array. Returns undefined if array is empty. */
150
+ export function median(arr: number[]) {
151
+ if (arr.length === 0) {
152
+ return undefined;
153
+ }
154
+ const sorted = [...arr].sort((a, b) => a - b);
155
+ const mid = Math.floor(sorted.length / 2);
156
+ return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
157
+ }
@@ -28,3 +28,25 @@ export function compact<T extends object>(obj: T): { [P in keyof T]+?: Exclude<T
28
28
  }
29
29
  return result;
30
30
  }
31
+
32
+ /** Returns a new object by picking the given keys. */
33
+ export function pick<T extends object, U extends keyof T>(object: T, ...props: U[]): Pick<T, U>;
34
+ export function pick<T extends object>(object: T, ...props: string[]): Partial<T>;
35
+ export function pick<T extends object>(object: T, ...props: string[]): Partial<T> {
36
+ const obj: any = {};
37
+ for (const prop of props) {
38
+ obj[prop] = (object as any)[prop];
39
+ }
40
+ return obj;
41
+ }
42
+
43
+ /** Returns a new object by omitting the given keys. */
44
+ export function omit<T extends object, K extends keyof T>(object: T, ...props: K[]): Omit<T, K>;
45
+ export function omit<T extends object>(object: T, ...props: string[]): Partial<T>;
46
+ export function omit<T extends object>(object: T, ...props: string[]): Partial<T> {
47
+ const obj: any = { ...object };
48
+ for (const prop of props) {
49
+ delete obj[prop];
50
+ }
51
+ return obj;
52
+ }
@@ -56,10 +56,12 @@ export type EnvVar =
56
56
  | 'L2_QUEUE_SIZE'
57
57
  | 'LOG_ELAPSED_TIME'
58
58
  | 'LOG_JSON'
59
+ | 'LOG_MULTILINE'
59
60
  | 'LOG_LEVEL'
60
61
  | 'MNEMONIC'
61
62
  | 'NETWORK_NAME'
62
63
  | 'NETWORK'
64
+ | 'NO_PXE'
63
65
  | 'COIN_ISSUER_CONTRACT_ADDRESS'
64
66
  | 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT'
65
67
  | 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT'
@@ -124,7 +126,6 @@ export type EnvVar =
124
126
  | 'PROVER_REQUIRED_CONFIRMATIONS'
125
127
  | 'PROVER_TEST_DELAY_MS'
126
128
  | 'PROVER_CACHE_DIR'
127
- | 'PXE_BLOCK_POLLING_INTERVAL_MS'
128
129
  | 'PXE_L2_STARTING_BLOCK'
129
130
  | 'PXE_PROVER_ENABLED'
130
131
  | 'QUOTE_PROVIDER_BASIS_POINT_FEE'
@@ -182,4 +183,10 @@ export type EnvVar =
182
183
  | 'L1_TX_MONITOR_MAX_ATTEMPTS'
183
184
  | 'L1_TX_MONITOR_CHECK_INTERVAL_MS'
184
185
  | 'L1_TX_MONITOR_STALL_TIME_MS'
185
- | 'L1_TX_MONITOR_TX_TIMEOUT_MS';
186
+ | 'L1_TX_MONITOR_TX_TIMEOUT_MS'
187
+ | 'FAUCET_MNEMONIC_ACCOUNT_INDEX'
188
+ | 'FAUCET_ETH_AMOUNT'
189
+ | 'FAUCET_INTERVAL_MS'
190
+ | 'FAUCET_L1_ASSETS'
191
+ | 'K8S_POD_NAME'
192
+ | 'K8S_POD_UID';
@@ -1,4 +1,4 @@
1
- import { createDebugLogger } from '../../log/pino-logger.js';
1
+ import { createLogger } from '../../log/pino-logger.js';
2
2
 
3
3
  /**
4
4
  * A number generator which is used as a source of randomness in the system. If the SEED env variable is set, the
@@ -15,7 +15,7 @@ export class RandomnessSingleton {
15
15
 
16
16
  private constructor(
17
17
  private readonly seed?: number,
18
- private readonly log = createDebugLogger('aztec:randomness_singleton'),
18
+ private readonly log = createLogger('foundation:randomness_singleton'),
19
19
  ) {
20
20
  if (seed !== undefined) {
21
21
  this.log.debug(`Using pseudo-randomness with seed: ${seed}`);
@@ -232,12 +232,31 @@ export class Fr extends BaseField {
232
232
  return fromBufferReduce(buffer, Fr);
233
233
  }
234
234
 
235
+ /**
236
+ * Creates a Fr instance from a string.
237
+ * @param buf - the string to create a Fr from.
238
+ * @returns the Fr instance
239
+ * @remarks if the string only consists of numbers, we assume we are parsing a bigint,
240
+ * otherwise we require the hex string to be prepended with "0x", to ensure there is no misunderstanding
241
+ * as to what is being parsed.
242
+ */
243
+ static fromString(buf: string) {
244
+ if (buf.match(/^\d+$/) !== null) {
245
+ return new Fr(toBufferBE(BigInt(buf), 32));
246
+ }
247
+ if (buf.match(/^0x/i) !== null) {
248
+ return fromHexString(buf, Fr);
249
+ }
250
+
251
+ throw new Error('Tried to create a Fr from an invalid string');
252
+ }
253
+
235
254
  /**
236
255
  * Creates a Fr instance from a hex string.
237
256
  * @param buf - a hex encoded string.
238
257
  * @returns the Fr instance
239
258
  */
240
- static fromString(buf: string) {
259
+ static fromHexString(buf: string) {
241
260
  return fromHexString(buf, Fr);
242
261
  }
243
262
 
@@ -368,12 +387,31 @@ export class Fq extends BaseField {
368
387
  return fromBufferReduce(buffer, Fq);
369
388
  }
370
389
 
390
+ /**
391
+ * Creates a Fq instance from a string.
392
+ * @param buf - the string to create a Fq from.
393
+ * @returns the Fq instance
394
+ * @remarks if the string only consists of numbers, we assume we are parsing a bigint,
395
+ * otherwise we require the hex string to be prepended with "0x", to ensure there is no misunderstanding
396
+ * as to what is being parsed.
397
+ */
398
+ static fromString(buf: string) {
399
+ if (buf.match(/^\d+$/) !== null) {
400
+ return new Fq(toBufferBE(BigInt(buf), 32));
401
+ }
402
+ if (buf.match(/^0x/i) !== null) {
403
+ return fromHexString(buf, Fq);
404
+ }
405
+
406
+ throw new Error('Tried to create a Fq from an invalid string');
407
+ }
408
+
371
409
  /**
372
410
  * Creates a Fq instance from a hex string.
373
411
  * @param buf - a hex encoded string.
374
412
  * @returns the Fq instance
375
413
  */
376
- static fromString(buf: string) {
414
+ static fromHexString(buf: string) {
377
415
  return fromHexString(buf, Fq);
378
416
  }
379
417
 
@@ -1,4 +1,4 @@
1
- import * as fs from 'fs/promises';
1
+ import { promises as fs } from 'fs';
2
2
  import * as path from 'path';
3
3
 
4
4
  // Create a random directory underneath a 'base' directory
@@ -4,3 +4,4 @@ export * from './sort.js';
4
4
  export * from './take.js';
5
5
  export * from './all.js';
6
6
  export * from './peek.js';
7
+ export * from './toArray.js';
@@ -0,0 +1,7 @@
1
+ export async function toArray<T>(iterator: AsyncIterableIterator<T> | IterableIterator<T>): Promise<T[]> {
2
+ const arr = [];
3
+ for await (const i of iterator) {
4
+ arr.push(i);
5
+ }
6
+ return arr;
7
+ }
@@ -0,0 +1,9 @@
1
+ import { overwriteLoggingStream, pinoPrettyOpts } from '@aztec/foundation/log';
2
+
3
+ import pretty from 'pino-pretty';
4
+
5
+ // Overwrite logging stream with pino-pretty. We define this as a separate
6
+ // file so we don't mess up with dependencies in non-testing environments,
7
+ // since pino-pretty messes up with browser bundles.
8
+ // See also https://www.npmjs.com/package/pino-pretty?activeTab=readme#user-content-usage-with-jest
9
+ overwriteLoggingStream(pretty(pinoPrettyOpts));
@@ -1,10 +1,10 @@
1
1
  import { format, inspect } from 'util';
2
2
 
3
- import { type DebugLogger, createDebugLogger } from '../../log/index.js';
3
+ import { type Logger, createLogger } from '../../log/index.js';
4
4
  import { NoRetryError, makeBackoff, retry } from '../../retry/index.js';
5
5
  import { jsonStringify } from '../convert.js';
6
6
 
7
- const log = createDebugLogger('json-rpc:json_rpc_client');
7
+ const log = createLogger('json-rpc:json_rpc_client');
8
8
 
9
9
  /**
10
10
  * A normal fetch function that does not retry.
@@ -73,7 +73,7 @@ export async function defaultFetch(
73
73
  * @param log - Optional logger for logging attempts.
74
74
  * @returns A fetch function.
75
75
  */
76
- export function makeFetch(retries: number[], defaultNoRetry: boolean, log?: DebugLogger) {
76
+ export function makeFetch(retries: number[], defaultNoRetry: boolean, log?: Logger) {
77
77
  return async (host: string, rpcMethod: string, body: any, useApiEndpoints: boolean, noRetry?: boolean) => {
78
78
  return await retry(
79
79
  () => defaultFetch(host, rpcMethod, body, useApiEndpoints, noRetry ?? defaultNoRetry),
@@ -1,6 +1,6 @@
1
1
  import { format } from 'util';
2
2
 
3
- import { createDebugLogger } from '../../log/pino-logger.js';
3
+ import { createLogger } from '../../log/pino-logger.js';
4
4
  import { type ApiSchema, type ApiSchemaFor, schemaHasMethod } from '../../schemas/api.js';
5
5
  import { defaultFetch } from './fetch.js';
6
6
 
@@ -19,7 +19,7 @@ export function createSafeJsonRpcClient<T extends object>(
19
19
  useApiEndpoints: boolean = false,
20
20
  namespaceMethods?: string | false,
21
21
  fetch = defaultFetch,
22
- log = createDebugLogger('json-rpc:client'),
22
+ log = createLogger('json-rpc:client'),
23
23
  ): T {
24
24
  let id = 0;
25
25
  const request = async (methodName: string, params: any[]): Promise<any> => {
@@ -44,18 +44,10 @@ export function createSafeJsonRpcClient<T extends object>(
44
44
  return (schema as ApiSchema)[methodName].returnType().parse(res.result);
45
45
  };
46
46
 
47
- // Intercept any RPC methods with a proxy
48
- const proxy = new Proxy(
49
- {},
50
- {
51
- get: (target, method: string) => {
52
- if (['then', 'catch'].includes(method)) {
53
- return Reflect.get(target, method);
54
- }
55
- return (...params: any[]) => request(method, params);
56
- },
57
- },
58
- ) as T;
47
+ const proxy: any = {};
48
+ for (const method of Object.keys(schema)) {
49
+ proxy[method] = (...params: any[]) => request(method, params);
50
+ }
59
51
 
60
- return proxy;
52
+ return proxy as T;
61
53
  }
@@ -8,7 +8,7 @@ import { type AddressInfo } from 'net';
8
8
  import { format, inspect } from 'util';
9
9
  import { ZodError } from 'zod';
10
10
 
11
- import { type DebugLogger, createDebugLogger } from '../../log/index.js';
11
+ import { type Logger, createLogger } from '../../log/index.js';
12
12
  import { promiseWithResolvers } from '../../promise/utils.js';
13
13
  import { type ApiSchema, type ApiSchemaFor, parseWithOptionals, schemaHasMethod } from '../../schemas/index.js';
14
14
  import { jsonStringify } from '../convert.js';
@@ -27,7 +27,7 @@ export class SafeJsonRpcServer {
27
27
  /** Health check function */
28
28
  private readonly healthCheck: StatusCheckFn = () => true,
29
29
  /** Logger */
30
- private log = createDebugLogger('json-rpc:server'),
30
+ private log = createLogger('json-rpc:server'),
31
31
  ) {}
32
32
 
33
33
  public isHealthy(): boolean | Promise<boolean> {
@@ -170,7 +170,7 @@ interface Proxy {
170
170
  * before forwarding calls, and then converts outputs into JSON using default conversions.
171
171
  */
172
172
  export class SafeJsonProxy<T extends object = any> implements Proxy {
173
- private log = createDebugLogger('json-rpc:proxy');
173
+ private log = createLogger('json-rpc:proxy');
174
174
  private schema: ApiSchema;
175
175
 
176
176
  constructor(private handler: T, schema: ApiSchemaFor<T>) {
@@ -233,7 +233,7 @@ export function makeHandler<T extends object>(handler: T, schema: ApiSchemaFor<T
233
233
  return [handler, schema];
234
234
  }
235
235
 
236
- function makeAggregateHealthcheck(namedHandlers: NamespacedApiHandlers, log?: DebugLogger): StatusCheckFn {
236
+ function makeAggregateHealthcheck(namedHandlers: NamespacedApiHandlers, log?: Logger): StatusCheckFn {
237
237
  return async () => {
238
238
  try {
239
239
  const results = await Promise.all(
@@ -259,7 +259,7 @@ function makeAggregateHealthcheck(namedHandlers: NamespacedApiHandlers, log?: De
259
259
  */
260
260
  export function createNamespacedSafeJsonRpcServer(
261
261
  handlers: NamespacedApiHandlers,
262
- log = createDebugLogger('json-rpc:server'),
262
+ log = createLogger('json-rpc:server'),
263
263
  ): SafeJsonRpcServer {
264
264
  const proxy = new NamespacedSafeJsonProxy(handlers);
265
265
  const healthCheck = makeAggregateHealthcheck(handlers, log);
@@ -42,7 +42,13 @@ export function parseFilters(definition: string | undefined): LogFilters {
42
42
  const sanitizedLevel = level.trim().toLowerCase();
43
43
  assertLogLevel(sanitizedLevel);
44
44
  for (const module of modules.split(',')) {
45
- filters.push([module.trim().toLowerCase(), sanitizedLevel as LogLevel | 'silent']);
45
+ filters.push([
46
+ module
47
+ .trim()
48
+ .toLowerCase()
49
+ .replace(/^aztec:/, ''),
50
+ sanitizedLevel as LogLevel | 'silent',
51
+ ]);
46
52
  }
47
53
  }
48
54
  return filters.reverse();
package/src/log/log_fn.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  export type LogData = Record<string, string | number | bigint | boolean | { toString(): string } | undefined>;
3
3
 
4
4
  /** A callable logger instance. */
5
- export type LogFn = (msg: string, data?: LogData) => void;
5
+ export type LogFn = (msg: string, data?: unknown) => void;
@@ -1,7 +1,6 @@
1
1
  import { createColors } from 'colorette';
2
2
  import isNode from 'detect-node';
3
3
  import { pino, symbols } from 'pino';
4
- import pretty from 'pino-pretty';
5
4
  import { type Writable } from 'stream';
6
5
  import { inspect } from 'util';
7
6
 
@@ -10,38 +9,37 @@ import { getLogLevelFromFilters, parseEnv } from './log-filters.js';
10
9
  import { type LogLevel } from './log-levels.js';
11
10
  import { type LogData, type LogFn } from './log_fn.js';
12
11
 
13
- // TODO(palla/log): Rename to createLogger
14
- export function createDebugLogger(module: string): DebugLogger {
15
- // TODO(palla/log): Rename all module names to remove the aztec prefix
16
- const pinoLogger = logger.child(
17
- { module: module.replace(/^aztec:/, '') },
18
- { level: getLogLevelFromFilters(logFilters, module) },
19
- );
12
+ export function createLogger(module: string): Logger {
13
+ module = module.replace(/^aztec:/, '');
14
+ const pinoLogger = logger.child({ module }, { level: getLogLevelFromFilters(logFilters, module) });
20
15
 
21
16
  // We check manually for isLevelEnabled to avoid calling processLogData unnecessarily.
22
17
  // Note that isLevelEnabled is missing from the browser version of pino.
23
- const logFn = (level: LogLevel, msg: string, data?: LogData) =>
24
- isLevelEnabled(pinoLogger, level) && pinoLogger[level](processLogData(data ?? {}), msg);
18
+ const logFn = (level: LogLevel, msg: string, data?: unknown) =>
19
+ isLevelEnabled(pinoLogger, level) && pinoLogger[level](processLogData((data as LogData) ?? {}), msg);
25
20
 
26
21
  return {
27
22
  silent: () => {},
28
23
  // TODO(palla/log): Should we move err to data instead of the text message?
29
24
  /** Log as fatal. Use when an error has brought down the system. */
30
- fatal: (msg: string, err?: unknown, data?: LogData) => logFn('fatal', formatErr(msg, err), data),
25
+ fatal: (msg: string, err?: unknown, data?: unknown) => logFn('fatal', formatErr(msg, err), data),
31
26
  /** Log as error. Use for errors in general. */
32
- error: (msg: string, err?: unknown, data?: LogData) => logFn('error', formatErr(msg, err), data),
27
+ error: (msg: string, err?: unknown, data?: unknown) => logFn('error', formatErr(msg, err), data),
33
28
  /** Log as warn. Use for when we stray from the happy path. */
34
- warn: (msg: string, data?: LogData) => logFn('warn', msg, data),
29
+ warn: (msg: string, data?: unknown) => logFn('warn', msg, data),
35
30
  /** Log as info. Use for providing an operator with info on what the system is doing. */
36
- info: (msg: string, data?: LogData) => logFn('info', msg, data),
31
+ info: (msg: string, data?: unknown) => logFn('info', msg, data),
37
32
  /** Log as verbose. Use for when we need additional insight on what a subsystem is doing. */
38
- verbose: (msg: string, data?: LogData) => logFn('verbose', msg, data),
33
+ verbose: (msg: string, data?: unknown) => logFn('verbose', msg, data),
39
34
  /** Log as debug. Use for when we need debugging info to troubleshoot an issue on a specific component. */
40
- debug: (msg: string, data?: LogData) => logFn('debug', msg, data),
35
+ debug: (msg: string, data?: unknown) => logFn('debug', msg, data),
41
36
  /** Log as trace. Use for when we want to denial-of-service any recipient of the logs. */
42
- trace: (msg: string, data?: LogData) => logFn('trace', msg, data),
37
+ trace: (msg: string, data?: unknown) => logFn('trace', msg, data),
43
38
  level: pinoLogger.level as LogLevel,
39
+ /** Whether the given level is enabled for this logger. */
44
40
  isLevelEnabled: (level: LogLevel) => isLevelEnabled(pinoLogger, level),
41
+ /** Module name for the logger. */
42
+ module,
45
43
  };
46
44
  }
47
45
 
@@ -69,10 +67,19 @@ function isLevelEnabled(logger: pino.Logger<'verbose', boolean>, level: LogLevel
69
67
  const defaultLogLevel = process.env.NODE_ENV === 'test' ? 'silent' : 'info';
70
68
  const [logLevel, logFilters] = parseEnv(process.env.LOG_LEVEL, defaultLogLevel);
71
69
 
70
+ // Define custom logging levels for pino.
71
+ const customLevels = { verbose: 25 };
72
+ const pinoOpts = { customLevels, useOnlyCustomLevels: false, level: logLevel };
73
+
74
+ export const levels = {
75
+ labels: { ...pino.levels.labels, ...Object.fromEntries(Object.entries(customLevels).map(e => e.reverse())) },
76
+ values: { ...pino.levels.values, ...customLevels },
77
+ };
78
+
72
79
  // Transport options for pretty logging to stderr via pino-pretty.
73
80
  const useColor = true;
74
81
  const { bold, reset } = createColors({ useColor });
75
- const pinoPrettyOpts = {
82
+ export const pinoPrettyOpts = {
76
83
  destination: 2,
77
84
  sync: true,
78
85
  colorize: useColor,
@@ -81,25 +88,20 @@ const pinoPrettyOpts = {
81
88
  customLevels: 'fatal:60,error:50,warn:40,info:30,verbose:25,debug:20,trace:10',
82
89
  customColors: 'fatal:bgRed,error:red,warn:yellow,info:green,verbose:magenta,debug:blue,trace:gray',
83
90
  minimumLevel: 'trace' as const,
91
+ singleLine: !['1', 'true'].includes(process.env.LOG_MULTILINE ?? ''),
84
92
  };
85
- const prettyTransport: pino.TransportSingleOptions = {
93
+
94
+ const prettyTransport: pino.TransportTargetOptions = {
86
95
  target: 'pino-pretty',
87
96
  options: pinoPrettyOpts,
97
+ level: 'trace',
88
98
  };
89
99
 
90
100
  // Transport for vanilla stdio logging as JSON.
91
- const stdioTransport: pino.TransportSingleOptions = {
101
+ const stdioTransport: pino.TransportTargetOptions = {
92
102
  target: 'pino/file',
93
103
  options: { destination: 2 },
94
- };
95
-
96
- // Define custom logging levels for pino.
97
- const customLevels = { verbose: 25 };
98
- const pinoOpts = { customLevels, useOnlyCustomLevels: false, level: logLevel };
99
-
100
- export const levels = {
101
- labels: { ...pino.levels.labels, ...Object.fromEntries(Object.entries(customLevels).map(e => e.reverse())) },
102
- values: { ...pino.levels.values, ...customLevels },
104
+ level: 'trace',
103
105
  };
104
106
 
105
107
  // Transport for OpenTelemetry logging. While defining this here is an abstraction leakage since this
@@ -110,18 +112,21 @@ export const levels = {
110
112
  // since pino will load this transport separately on a worker thread, to minimize disruption to the main loop.
111
113
  const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT;
112
114
  const otelOpts = { levels };
113
- const otelTransport: pino.TransportSingleOptions = {
115
+ const otelTransport: pino.TransportTargetOptions = {
114
116
  target: '@aztec/telemetry-client/otel-pino-stream',
115
117
  options: otelOpts,
118
+ level: 'trace',
116
119
  };
117
120
 
118
121
  function makeLogger() {
119
122
  if (!isNode) {
120
- // We are on the browser
123
+ // We are on the browser.
121
124
  return pino({ ...pinoOpts, browser: { asObject: false } });
122
125
  } else if (process.env.JEST_WORKER_ID) {
123
- // We are on jest, so we need sync logging. We stream to stderr with pretty.
124
- return pino(pinoOpts, pretty(pinoPrettyOpts));
126
+ // We are on jest, so we need sync logging and stream to stderr.
127
+ // We expect jest/setup.mjs to kick in later and replace set up a pretty logger,
128
+ // but if for some reason it doesn't, at least we're covered with a default logger.
129
+ return pino(pinoOpts, pino.destination(2));
125
130
  } else {
126
131
  // Regular nodejs with transports on worker thread, using pino-pretty for console logging if LOG_JSON
127
132
  // is not set, and an optional OTLP transport if the OTLP endpoint is provided.
@@ -129,7 +134,7 @@ function makeLogger() {
129
134
  ['1', 'true', 'TRUE'].includes(process.env.LOG_JSON ?? '') ? stdioTransport : prettyTransport,
130
135
  otlpEndpoint ? otelTransport : undefined,
131
136
  ]);
132
- return pino(pinoOpts, pino.transport({ targets }));
137
+ return pino(pinoOpts, pino.transport({ targets, levels: levels.values }));
133
138
  }
134
139
  }
135
140
 
@@ -146,6 +151,14 @@ logger.verbose(
146
151
  : `Browser console logger initialized with level ${logLevel}`,
147
152
  );
148
153
 
154
+ /**
155
+ * Overwrites the logging stream with a different destination.
156
+ * Used by jest/setup.mjs to set up a pretty logger.
157
+ */
158
+ export function overwriteLoggingStream(stream: Writable): void {
159
+ (logger as any)[symbols.streamSym] = stream;
160
+ }
161
+
149
162
  /**
150
163
  * Registers an additional destination to the pino logger.
151
164
  * Use only when working with destinations, not worker transports.
@@ -176,15 +189,9 @@ type ErrorLogFn = (msg: string, err?: Error | unknown, data?: LogData) => void;
176
189
  export type Logger = { [K in LogLevel]: LogFn } & { /** Error log function */ error: ErrorLogFn } & {
177
190
  level: LogLevel;
178
191
  isLevelEnabled: (level: LogLevel) => boolean;
192
+ module: string;
179
193
  };
180
194
 
181
- /**
182
- * Logger that supports multiple severity levels and can be called directly to issue a debug statement.
183
- * Intended as a drop-in replacement for the debug module.
184
- * TODO(palla/log): Remove this alias
185
- */
186
- export type DebugLogger = Logger;
187
-
188
195
  /**
189
196
  * Concatenates a log message and an exception.
190
197
  * @param msg - Log message