chia-agent 16.0.1 → 16.0.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## [16.0.2]
4
+ ### Fixed
5
+ - Fixed logging issues where it crashes upon stringifying an object with circular references
6
+
3
7
  ## [16.0.1]
4
8
  ### Fixed
5
9
  - Fixed an issue where it exhausts heap when logging an object with circular references
@@ -1886,6 +1890,7 @@ daemon.sendMessage(destination, get_block_record_by_height_command, data);
1886
1890
  Initial release.
1887
1891
 
1888
1892
  <!-- [Unreleased]: https://github.com/Chia-Mine/chia-agent/compare/v0.0.1...v0.0.2 -->
1893
+ [16.0.2]: https://github.com/Chia-Mine/chia-agent/compare/v16.0.1...v16.0.2
1889
1894
  [16.0.1]: https://github.com/Chia-Mine/chia-agent/compare/v16.0.0...v16.0.1
1890
1895
  [16.0.0]: https://github.com/Chia-Mine/chia-agent/compare/v15.0.0...v16.0.0
1891
1896
  [15.0.0]: https://github.com/Chia-Mine/chia-agent/compare/v14.5.0...v15.0.0
package/daemon/index.js CHANGED
@@ -32,14 +32,14 @@ const onProcessExit = () => {
32
32
  setTimeout(async () => {
33
33
  try {
34
34
  if (daemon && daemon.connected && !daemon.closing) {
35
- (0, logger_1.getLogger)().debug("Detected Ctrl+C. Initiating shutdown...");
35
+ (0, logger_1.getLogger)().debug(() => "Detected Ctrl+C. Initiating shutdown...");
36
36
  await daemon.close();
37
37
  process.removeListener("SIGINT", onProcessExit);
38
38
  process.kill(process.pid, "SIGINT");
39
39
  }
40
40
  }
41
41
  catch (e) {
42
- process.stderr.write(JSON.stringify(e));
42
+ process.stderr.write((0, logger_1.stringify)(e));
43
43
  process.exit(1);
44
44
  }
45
45
  }, 67);
@@ -96,7 +96,7 @@ class Daemon {
96
96
  }
97
97
  else {
98
98
  try {
99
- (0, logger_1.getLogger)().error(`Error: ${JSON.stringify(e)}`);
99
+ (0, logger_1.getLogger)().error(() => `Error: ${(0, logger_1.stringify)(e)}`);
100
100
  }
101
101
  catch (_e) {
102
102
  (0, logger_1.getLogger)().error("Unknown error");
@@ -144,7 +144,7 @@ class Daemon {
144
144
  // Attempt connection with retry logic
145
145
  let lastError;
146
146
  for (let attempt = 1; attempt <= this._retryOptions.maxAttempts; attempt++) {
147
- (0, logger_1.getLogger)().debug(`Opening websocket connection to ${daemonServerURL} (attempt ${attempt}/${this._retryOptions.maxAttempts})`);
147
+ (0, logger_1.getLogger)().debug(() => `Opening websocket connection to ${daemonServerURL} (attempt ${attempt}/${this._retryOptions.maxAttempts})`);
148
148
  const result = await (0, connection_1.open)(daemonServerURL, timeoutMs).catch((error) => {
149
149
  lastError = error;
150
150
  return null;
@@ -186,7 +186,7 @@ class Daemon {
186
186
  // Disable reconnection for manual close
187
187
  this._autoReconnect = false;
188
188
  this._isReconnecting = false;
189
- (0, logger_1.getLogger)().debug("Closing web socket connection");
189
+ (0, logger_1.getLogger)().debug(() => "Closing web socket connection");
190
190
  this._socket.close();
191
191
  this._closing = true;
192
192
  this._connectedUrl = "";
@@ -215,12 +215,12 @@ class Daemon {
215
215
  rejecter: reject,
216
216
  timeout,
217
217
  };
218
- (0, logger_1.getLogger)().debug(`Sending Ws message. dest=${destination} command=${command} reqId=${reqId}`);
218
+ (0, logger_1.getLogger)().debug(() => `Sending Ws message. dest=${destination} command=${command} reqId=${reqId}`);
219
219
  const messageStr = JSON.stringify(message);
220
220
  this._socket.send(messageStr, (err) => {
221
221
  if (err) {
222
222
  (0, logger_1.getLogger)().error(`Error while sending message: ${messageStr}`);
223
- (0, logger_1.getLogger)().error(JSON.stringify(err));
223
+ (0, logger_1.getLogger)().error(() => (0, logger_1.stringify)(err));
224
224
  // Clean up on send error
225
225
  const entry = this._responseQueue[reqId];
226
226
  if (entry) {
@@ -268,7 +268,7 @@ class Daemon {
268
268
  (0, logger_1.getLogger)().error("Failed to register agent service to daemon");
269
269
  (0, logger_1.getLogger)().error(error instanceof Error
270
270
  ? `${error.name}: ${error.message}`
271
- : JSON.stringify(error));
271
+ : (0, logger_1.stringify)(error));
272
272
  throw new Error("Subscribe failed");
273
273
  }
274
274
  this._subscriptions.push(service);
@@ -366,7 +366,7 @@ class Daemon {
366
366
  ({ request_id, origin, command } = payload);
367
367
  }
368
368
  catch (err) {
369
- (0, logger_1.getLogger)().error(`Failed to parse ws message data: ${JSON.stringify(err)}`);
369
+ (0, logger_1.getLogger)().error(`Failed to parse ws message data: ${(0, logger_1.stringify)(err)}`);
370
370
  (0, logger_1.getLogger)().error(`ws payload: ${event.data}`);
371
371
  return;
372
372
  }
@@ -374,13 +374,13 @@ class Daemon {
374
374
  if (entry) {
375
375
  clearTimeout(entry.timeout);
376
376
  delete this._responseQueue[request_id];
377
- (0, logger_1.getLogger)().debug(`Ws response received. origin=${origin} command=${command} reqId=${request_id}`);
377
+ (0, logger_1.getLogger)().debug(() => `Ws response received. origin=${origin} command=${command} reqId=${request_id}`);
378
378
  entry.resolver(payload);
379
379
  }
380
380
  else {
381
- (0, logger_1.getLogger)().debug(`Ws message arrived. origin=${origin} command=${command} reqId=${request_id}`);
381
+ (0, logger_1.getLogger)().debug(() => `Ws message arrived. origin=${origin} command=${command} reqId=${request_id}`);
382
382
  }
383
- (0, logger_1.getLogger)().trace(`Ws message: ${JSON.stringify(payload)}`);
383
+ (0, logger_1.getLogger)().trace(() => `Ws message: ${(0, logger_1.stringify)(payload)}`);
384
384
  this._messageEventListeners.forEach((l) => l(event));
385
385
  for (const o in this._messageListeners) {
386
386
  if (!Object.prototype.hasOwnProperty.call(this._messageListeners, o)) {
@@ -424,10 +424,10 @@ class Daemon {
424
424
  }
425
425
  }
426
426
  onPing() {
427
- (0, logger_1.getLogger)().debug("Received ping");
427
+ (0, logger_1.getLogger)().debug(() => "Received ping");
428
428
  }
429
429
  onPong() {
430
- (0, logger_1.getLogger)().debug("Received pong");
430
+ (0, logger_1.getLogger)().debug(() => "Received pong");
431
431
  }
432
432
  _attemptReconnection(previousSubscriptions) {
433
433
  if (this._reconnectAttempts >= this._retryOptions.maxAttempts) {
@@ -464,7 +464,7 @@ class Daemon {
464
464
  for (const service of previousSubscriptions) {
465
465
  try {
466
466
  await this.subscribe(service);
467
- (0, logger_1.getLogger)().debug(`Re-subscribed to ${service}`);
467
+ (0, logger_1.getLogger)().debug(() => `Re-subscribed to ${service}`);
468
468
  }
469
469
  catch (e) {
470
470
  (0, logger_1.getLogger)().error(`Failed to re-subscribe to ${service}: ${e}`);
package/logger.d.ts CHANGED
@@ -24,6 +24,7 @@ export declare function getLogger(name?: string): Logger;
24
24
  export declare function clearLoggerCache(): void;
25
25
  export declare function createConsoleWriter(): Writer;
26
26
  export declare function createNullWriter(): Writer;
27
+ export declare function stringify(obj: any, indent?: number): string;
27
28
  export declare class Logger {
28
29
  private _name;
29
30
  private _logLevel;
@@ -39,11 +40,11 @@ export declare class Logger {
39
40
  getFormatter(): LogFormatter;
40
41
  private _shouldWrite;
41
42
  private _formatMessage;
42
- trace(msg: any): void;
43
- debug(msg: any): void;
44
- info(msg: any): void;
45
- warning(msg: any): void;
46
- error(msg: any): void;
43
+ trace(msg: any | (() => any)): void;
44
+ debug(msg: any | (() => any)): void;
45
+ info(msg: any | (() => any)): void;
46
+ warning(msg: any | (() => any)): void;
47
+ error(msg: any | (() => any)): void;
47
48
  }
48
49
  export declare function getLogLevel(): TLogLevel;
49
50
  export declare function setLogLevel(level: TLogLevel): void;
package/logger.js CHANGED
@@ -12,6 +12,7 @@ exports.getLogger = getLogger;
12
12
  exports.clearLoggerCache = clearLoggerCache;
13
13
  exports.createConsoleWriter = createConsoleWriter;
14
14
  exports.createNullWriter = createNullWriter;
15
+ exports.stringify = stringify;
15
16
  exports.getLogLevel = getLogLevel;
16
17
  exports.setLogLevel = setLogLevel;
17
18
  const LOG_LEVELS = [
@@ -165,7 +166,7 @@ function stringify(obj, indent) {
165
166
  return `${obj}`;
166
167
  }
167
168
  else if (typeof obj === "bigint") {
168
- return `${obj}`;
169
+ return `${obj}n`;
169
170
  }
170
171
  else if (typeof obj === "symbol") {
171
172
  return obj.toString();
@@ -180,17 +181,12 @@ function stringify(obj, indent) {
180
181
  return "null";
181
182
  }
182
183
  try {
183
- // Custom replacer for circular references
184
- const getCircularReplacer = () => {
185
- const seen = new WeakSet();
186
- return (_key, value) => {
187
- if (typeof value === "object" && value !== null) {
188
- if (seen.has(value)) {
189
- return "[Circular]";
190
- }
191
- seen.add(value);
192
- }
193
- else if (typeof value === "bigint") {
184
+ // Deep clone the object while removing circular references
185
+ const seen = new WeakSet();
186
+ const deepCloneWithoutCircular = (value, path = []) => {
187
+ // Handle primitives and null
188
+ if (value === null || typeof value !== "object") {
189
+ if (typeof value === "bigint") {
194
190
  return `${value}n`;
195
191
  }
196
192
  else if (typeof value === "function") {
@@ -200,9 +196,49 @@ function stringify(obj, indent) {
200
196
  return value.toString();
201
197
  }
202
198
  return value;
203
- };
199
+ }
200
+ // Check for circular reference
201
+ if (seen.has(value)) {
202
+ return undefined; // This will cause the property to be omitted
203
+ }
204
+ seen.add(value);
205
+ try {
206
+ // Handle arrays
207
+ if (Array.isArray(value)) {
208
+ const clonedArray = [];
209
+ for (let i = 0; i < value.length; i++) {
210
+ const clonedValue = deepCloneWithoutCircular(value[i], [
211
+ ...path,
212
+ String(i),
213
+ ]);
214
+ if (clonedValue !== undefined) {
215
+ clonedArray.push(clonedValue);
216
+ }
217
+ }
218
+ return clonedArray;
219
+ }
220
+ // Handle objects
221
+ const cloned = {};
222
+ for (const key in value) {
223
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
224
+ const clonedValue = deepCloneWithoutCircular(value[key], [
225
+ ...path,
226
+ key,
227
+ ]);
228
+ if (clonedValue !== undefined) {
229
+ cloned[key] = clonedValue;
230
+ }
231
+ }
232
+ }
233
+ return cloned;
234
+ }
235
+ finally {
236
+ // Remove from seen set when we're done processing this level
237
+ seen.delete(value);
238
+ }
204
239
  };
205
- return JSON.stringify(obj, getCircularReplacer(), indent);
240
+ const clonedObj = deepCloneWithoutCircular(obj);
241
+ return JSON.stringify(clonedObj, null, indent);
206
242
  }
207
243
  catch (error) {
208
244
  const msg = error && typeof error === "object" && "message" in error
@@ -265,27 +301,32 @@ class Logger {
265
301
  }
266
302
  trace(msg) {
267
303
  if (this._shouldWrite("trace")) {
268
- this._writer.write(this._formatMessage("trace", stringify(msg)), "trace");
304
+ const message = typeof msg === "function" ? msg() : msg;
305
+ this._writer.write(this._formatMessage("trace", stringify(message)), "trace");
269
306
  }
270
307
  }
271
308
  debug(msg) {
272
309
  if (this._shouldWrite("debug")) {
273
- this._writer.write(this._formatMessage("debug", stringify(msg)), "debug");
310
+ const message = typeof msg === "function" ? msg() : msg;
311
+ this._writer.write(this._formatMessage("debug", stringify(message)), "debug");
274
312
  }
275
313
  }
276
314
  info(msg) {
277
315
  if (this._shouldWrite("info")) {
278
- this._writer.write(this._formatMessage("info", stringify(msg)), "info");
316
+ const message = typeof msg === "function" ? msg() : msg;
317
+ this._writer.write(this._formatMessage("info", stringify(message)), "info");
279
318
  }
280
319
  }
281
320
  warning(msg) {
282
321
  if (this._shouldWrite("warning")) {
283
- this._writer.write(this._formatMessage("warning", stringify(msg)), "warning");
322
+ const message = typeof msg === "function" ? msg() : msg;
323
+ this._writer.write(this._formatMessage("warning", stringify(message)), "warning");
284
324
  }
285
325
  }
286
326
  error(msg) {
287
327
  if (this._shouldWrite("error")) {
288
- this._writer.write(this._formatMessage("error", stringify(msg)), "error");
328
+ const message = typeof msg === "function" ? msg() : msg;
329
+ this._writer.write(this._formatMessage("error", stringify(message)), "error");
289
330
  }
290
331
  }
291
332
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chia-agent",
3
- "version": "16.0.1",
3
+ "version": "16.0.2",
4
4
  "author": "ChiaMineJP <admin@chiamine.jp>",
5
5
  "description": "chia rpc/websocket client library",
6
6
  "license": "MIT",
package/rpc/index.js CHANGED
@@ -71,9 +71,9 @@ function loadCertFilesFromConfig(config) {
71
71
  const caCertPath = (0, index_1.resolveFromChiaRoot)([
72
72
  config["/private_ssl_ca/crt"],
73
73
  ]);
74
- (0, logger_1.getLogger)().debug(`Loading client cert file from ${clientCertPath}`);
75
- (0, logger_1.getLogger)().debug(`Loading client key file from ${clientKeyPath}`);
76
- (0, logger_1.getLogger)().debug(`Loading ca cert file from ${caCertPath}`);
74
+ (0, logger_1.getLogger)().debug(() => `Loading client cert file from ${clientCertPath}`);
75
+ (0, logger_1.getLogger)().debug(() => `Loading client key file from ${clientKeyPath}`);
76
+ (0, logger_1.getLogger)().debug(() => `Loading ca cert file from ${caCertPath}`);
77
77
  const getCertOrKey = (path) => {
78
78
  if (!(0, fs_1.existsSync)(path)) {
79
79
  (0, logger_1.getLogger)().error(`ssl crt/key does not exist at: ${path}`);
@@ -102,10 +102,10 @@ class RPCAgent {
102
102
  if (agent.options && agent.options.host && agent.options.port) {
103
103
  this._host = agent.options.host;
104
104
  this._port = agent.options.port;
105
- (0, logger_1.getLogger)().debug(`Constructing RPCAgent with httpsAgent: ${this._host}:${this._port}`);
105
+ (0, logger_1.getLogger)().debug(() => `Constructing RPCAgent with httpsAgent: ${this._host}:${this._port}`);
106
106
  }
107
107
  else {
108
- (0, logger_1.getLogger)().debug("Constructing RPCAgent with httpsAgent (host/port not available in agent options)");
108
+ (0, logger_1.getLogger)().debug(() => "Constructing RPCAgent with httpsAgent (host/port not available in agent options)");
109
109
  }
110
110
  }
111
111
  else if ("httpAgent" in props) {
@@ -114,7 +114,7 @@ class RPCAgent {
114
114
  this._host = props.host;
115
115
  this._port = props.port;
116
116
  this._skip_hostname_verification = Boolean(props.skip_hostname_verification);
117
- (0, logger_1.getLogger)().debug(`Constructing RPCAgent with httpAgent: ${this._host}:${this._port}`);
117
+ (0, logger_1.getLogger)().debug(() => `Constructing RPCAgent with httpAgent: ${this._host}:${this._port}`);
118
118
  }
119
119
  else if ("protocol" in props) {
120
120
  this._protocol = props.protocol;
@@ -163,7 +163,7 @@ class RPCAgent {
163
163
  maxSockets,
164
164
  timeout,
165
165
  });
166
- (0, logger_1.getLogger)().debug(`Constructed RPCAgent with httpsAgent: ${host}:${port}`);
166
+ (0, logger_1.getLogger)().debug(() => `Constructed RPCAgent with httpsAgent: ${host}:${port}`);
167
167
  }
168
168
  else {
169
169
  this._agent = new http_1.Agent({
@@ -197,7 +197,7 @@ class RPCAgent {
197
197
  host = props.host ? props.host : info.hostname;
198
198
  port = typeof props.port === "number" ? props.port : info.port;
199
199
  }
200
- (0, logger_1.getLogger)().debug(`Picked ${host}:${port} for ${props.service}`);
200
+ (0, logger_1.getLogger)().debug(() => `Picked ${host}:${port} for ${props.service}`);
201
201
  const certs = loadCertFilesFromConfig(config);
202
202
  const { clientCert, clientKey, caCert } = certs;
203
203
  this._skip_hostname_verification = Boolean(props.skip_hostname_verification);
@@ -219,7 +219,7 @@ class RPCAgent {
219
219
  }
220
220
  async sendMessage(destination, command, data) {
221
221
  // parameter `destination` is not used because target rpc server is determined by url.
222
- (0, logger_1.getLogger)().debug(`Sending RPC message. dest=${destination} command=${command}`);
222
+ (0, logger_1.getLogger)().debug(() => `Sending RPC message. dest=${destination} command=${command}`);
223
223
  return this.request("POST", command, data);
224
224
  }
225
225
  async request(method, path, data) {
@@ -276,9 +276,9 @@ class RPCAgent {
276
276
  }
277
277
  }
278
278
  const transporter = this._protocol === "https" ? https_1.request : http_1.request;
279
- (0, logger_1.getLogger)().debug(`Dispatching RPC ${METHOD} request to ${this._protocol}//${this._host}:${this._port}${options.path}`);
280
- (0, logger_1.getLogger)().trace(`Request options: ${JSON.stringify(options)}`);
281
- (0, logger_1.getLogger)().trace(`Request body: ${body}`);
279
+ (0, logger_1.getLogger)().debug(() => `Dispatching RPC ${METHOD} request to ${this._protocol}//${this._host}:${this._port}${options.path}`);
280
+ (0, logger_1.getLogger)().trace(() => `Request options: ${(0, logger_1.stringify)(options)}`);
281
+ (0, logger_1.getLogger)().trace(() => `Request body: ${body}`);
282
282
  const req = transporter(options, (res) => {
283
283
  if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {
284
284
  (0, logger_1.getLogger)().error(`Status not ok: ${res.statusCode}`);
@@ -294,9 +294,9 @@ class RPCAgent {
294
294
  res.on("data", (chunk) => {
295
295
  chunks.push(chunk);
296
296
  if (chunks.length === 0) {
297
- (0, logger_1.getLogger)().debug("The first response chunk data arrived");
297
+ (0, logger_1.getLogger)().debug(() => "The first response chunk data arrived");
298
298
  }
299
- (0, logger_1.getLogger)().trace(`Response chunk #${chunks.length} - ${chunk.length} bytes: ${chunk.toString()}`);
299
+ (0, logger_1.getLogger)().trace(() => `Response chunk #${chunks.length} - ${chunk.length} bytes: ${chunk.toString()}`);
300
300
  });
301
301
  res.on("end", () => {
302
302
  try {
@@ -316,8 +316,8 @@ class RPCAgent {
316
316
  (0, logger_1.getLogger)().info(`API failure: ${d.error}`);
317
317
  return reject(d);
318
318
  }
319
- (0, logger_1.getLogger)().debug(`RPC response received from ${this._protocol}//${this._host}:${this._port}${options.path}`);
320
- (0, logger_1.getLogger)().trace(`RPC response data: ${JSON.stringify(d)}`);
319
+ (0, logger_1.getLogger)().debug(() => `RPC response received from ${this._protocol}//${this._host}:${this._port}${options.path}`);
320
+ (0, logger_1.getLogger)().trace(() => `RPC response data: ${(0, logger_1.stringify)(d)}`);
321
321
  return resolve(d);
322
322
  }
323
323
  // RPC Server should return response like
@@ -327,7 +327,7 @@ class RPCAgent {
327
327
  reject(new Error("Server responded without expected data"));
328
328
  }
329
329
  catch (e) {
330
- (0, logger_1.getLogger)().error(`Failed to parse response data: ${JSON.stringify(e)}`);
330
+ (0, logger_1.getLogger)().error(`Failed to parse response data: ${(0, logger_1.stringify)(e)}`);
331
331
  try {
332
332
  (0, logger_1.getLogger)().error(Buffer.concat(chunks).toString());
333
333
  }
@@ -339,7 +339,7 @@ class RPCAgent {
339
339
  });
340
340
  });
341
341
  req.on("error", (error) => {
342
- (0, logger_1.getLogger)().error(JSON.stringify(error));
342
+ (0, logger_1.getLogger)().error(() => (0, logger_1.stringify)(error));
343
343
  reject(error);
344
344
  });
345
345
  if ((METHOD === "POST" || METHOD === "PUT" || METHOD === "DELETE") &&