chia-agent 14.3.5 → 14.3.6-beta.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## [14.3.6]
4
+ ### Fixed
5
+ - Fixed logging issues where it crashes upon stringifying an object with circular references
6
+
3
7
  ## [14.3.5]
4
8
  ### Fixed
5
9
  - Fixed an issue where it exhausts heap when logging an object with circular references
@@ -1756,6 +1760,8 @@ daemon.sendMessage(destination, get_block_record_by_height_command, data);
1756
1760
  Initial release.
1757
1761
 
1758
1762
  <!-- [Unreleased]: https://github.com/Chia-Mine/chia-agent/compare/v0.0.1...v0.0.2 -->
1763
+ [14.3.6]: https://github.com/Chia-Mine/chia-agent/compare/v14.3.5...v14.3.6
1764
+ [14.3.5]: https://github.com/Chia-Mine/chia-agent/compare/v14.3.4...v14.3.5
1759
1765
  [14.3.4]: https://github.com/Chia-Mine/chia-agent/compare/v14.3.3...v14.3.4
1760
1766
  [14.3.3]: https://github.com/Chia-Mine/chia-agent/compare/v14.3.2...v14.3.3
1761
1767
  [14.3.2]: https://github.com/Chia-Mine/chia-agent/compare/v14.3.1...v14.3.2
package/daemon/index.js CHANGED
@@ -42,14 +42,14 @@ const onProcessExit = () => {
42
42
  setTimeout(() => __awaiter(void 0, void 0, void 0, function* () {
43
43
  try {
44
44
  if (daemon && daemon.connected && !daemon.closing) {
45
- (0, logger_1.getLogger)().debug("Detected Ctrl+C. Initiating shutdown...");
45
+ (0, logger_1.getLogger)().debug(() => "Detected Ctrl+C. Initiating shutdown...");
46
46
  yield daemon.close();
47
47
  process.removeListener("SIGINT", onProcessExit);
48
48
  process.kill(process.pid, "SIGINT");
49
49
  }
50
50
  }
51
51
  catch (e) {
52
- process.stderr.write(JSON.stringify(e));
52
+ process.stderr.write((0, logger_1.stringify)(e));
53
53
  process.exit(1);
54
54
  }
55
55
  }), 67);
@@ -106,7 +106,7 @@ class Daemon {
106
106
  }
107
107
  else {
108
108
  try {
109
- (0, logger_1.getLogger)().error(`Error: ${JSON.stringify(e)}`);
109
+ (0, logger_1.getLogger)().error(() => `Error: ${(0, logger_1.stringify)(e)}`);
110
110
  }
111
111
  catch (_e) {
112
112
  (0, logger_1.getLogger)().error("Unknown error");
@@ -152,7 +152,7 @@ class Daemon {
152
152
  // Attempt connection with retry logic
153
153
  let lastError;
154
154
  for (let attempt = 1; attempt <= this._retryOptions.maxAttempts; attempt++) {
155
- (0, logger_1.getLogger)().debug(`Opening websocket connection to ${daemonServerURL} (attempt ${attempt}/${this._retryOptions.maxAttempts})`);
155
+ (0, logger_1.getLogger)().debug(() => `Opening websocket connection to ${daemonServerURL} (attempt ${attempt}/${this._retryOptions.maxAttempts})`);
156
156
  const result = yield (0, connection_1.open)(daemonServerURL, timeoutMs).catch((error) => {
157
157
  lastError = error;
158
158
  return null;
@@ -196,7 +196,7 @@ class Daemon {
196
196
  // Disable reconnection for manual close
197
197
  this._autoReconnect = false;
198
198
  this._isReconnecting = false;
199
- (0, logger_1.getLogger)().debug("Closing web socket connection");
199
+ (0, logger_1.getLogger)().debug(() => "Closing web socket connection");
200
200
  this._socket.close();
201
201
  this._closing = true;
202
202
  this._connectedUrl = "";
@@ -227,12 +227,12 @@ class Daemon {
227
227
  rejecter: reject,
228
228
  timeout,
229
229
  };
230
- (0, logger_1.getLogger)().debug(`Sending Ws message. dest=${destination} command=${command} reqId=${reqId}`);
231
- const messageStr = JSON.stringify(message);
230
+ (0, logger_1.getLogger)().debug(() => `Sending Ws message. dest=${destination} command=${command} reqId=${reqId}`);
231
+ const messageStr = (0, logger_1.stringify)(message);
232
232
  this._socket.send(messageStr, (err) => {
233
233
  if (err) {
234
234
  (0, logger_1.getLogger)().error(`Error while sending message: ${messageStr}`);
235
- (0, logger_1.getLogger)().error(JSON.stringify(err));
235
+ (0, logger_1.getLogger)().error(() => (0, logger_1.stringify)(err));
236
236
  // Clean up on send error
237
237
  const entry = this._responseQueue[reqId];
238
238
  if (entry) {
@@ -282,7 +282,7 @@ class Daemon {
282
282
  (0, logger_1.getLogger)().error("Failed to register agent service to daemon");
283
283
  (0, logger_1.getLogger)().error(error instanceof Error
284
284
  ? `${error.name}: ${error.message}`
285
- : JSON.stringify(error));
285
+ : (0, logger_1.stringify)(error));
286
286
  throw new Error("Subscribe failed");
287
287
  }
288
288
  this._subscriptions.push(service);
@@ -383,7 +383,7 @@ class Daemon {
383
383
  ({ request_id, origin, command } = payload);
384
384
  }
385
385
  catch (err) {
386
- (0, logger_1.getLogger)().error(`Failed to parse ws message data: ${JSON.stringify(err)}`);
386
+ (0, logger_1.getLogger)().error(`Failed to parse ws message data: ${(0, logger_1.stringify)(err)}`);
387
387
  (0, logger_1.getLogger)().error(`ws payload: ${event.data}`);
388
388
  return;
389
389
  }
@@ -391,13 +391,13 @@ class Daemon {
391
391
  if (entry) {
392
392
  clearTimeout(entry.timeout);
393
393
  delete this._responseQueue[request_id];
394
- (0, logger_1.getLogger)().debug(`Ws response received. origin=${origin} command=${command} reqId=${request_id}`);
394
+ (0, logger_1.getLogger)().debug(() => `Ws response received. origin=${origin} command=${command} reqId=${request_id}`);
395
395
  entry.resolver(payload);
396
396
  }
397
397
  else {
398
- (0, logger_1.getLogger)().debug(`Ws message arrived. origin=${origin} command=${command} reqId=${request_id}`);
398
+ (0, logger_1.getLogger)().debug(() => `Ws message arrived. origin=${origin} command=${command} reqId=${request_id}`);
399
399
  }
400
- (0, logger_1.getLogger)().trace(`Ws message: ${JSON.stringify(payload)}`);
400
+ (0, logger_1.getLogger)().trace(() => `Ws message: ${(0, logger_1.stringify)(payload)}`);
401
401
  this._messageEventListeners.forEach((l) => l(event));
402
402
  for (const o in this._messageListeners) {
403
403
  if (!Object.prototype.hasOwnProperty.call(this._messageListeners, o)) {
@@ -441,10 +441,10 @@ class Daemon {
441
441
  }
442
442
  }
443
443
  onPing() {
444
- (0, logger_1.getLogger)().debug("Received ping");
444
+ (0, logger_1.getLogger)().debug(() => "Received ping");
445
445
  }
446
446
  onPong() {
447
- (0, logger_1.getLogger)().debug("Received pong");
447
+ (0, logger_1.getLogger)().debug(() => "Received pong");
448
448
  }
449
449
  _attemptReconnection(previousSubscriptions) {
450
450
  if (this._reconnectAttempts >= this._retryOptions.maxAttempts) {
@@ -481,7 +481,7 @@ class Daemon {
481
481
  for (const service of previousSubscriptions) {
482
482
  try {
483
483
  yield this.subscribe(service);
484
- (0, logger_1.getLogger)().debug(`Re-subscribed to ${service}`);
484
+ (0, logger_1.getLogger)().debug(() => `Re-subscribed to ${service}`);
485
485
  }
486
486
  catch (e) {
487
487
  (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
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setLogLevel = exports.getLogLevel = exports.Logger = exports.createNullWriter = exports.createConsoleWriter = exports.clearLoggerCache = exports.getLogger = exports.jsonLogFormatter = exports.simpleLogFormatter = exports.defaultLogFormatter = exports.DEFAULT_LOGGER_NAME = exports.getDefaultFormatter = exports.setDefaultFormatter = exports.setDefaultWriter = exports.getDefaultLogLevel = exports.setDefaultLogLevel = exports.normalizeLogLevel = exports.isValidLogLevel = void 0;
3
+ exports.setLogLevel = exports.getLogLevel = exports.Logger = exports.stringify = exports.createNullWriter = exports.createConsoleWriter = exports.clearLoggerCache = exports.getLogger = exports.jsonLogFormatter = exports.simpleLogFormatter = exports.defaultLogFormatter = exports.DEFAULT_LOGGER_NAME = exports.getDefaultFormatter = exports.setDefaultFormatter = exports.setDefaultWriter = exports.getDefaultLogLevel = exports.setDefaultLogLevel = exports.normalizeLogLevel = exports.isValidLogLevel = void 0;
4
4
  const LOG_LEVELS = [
5
5
  "error",
6
6
  "warning",
@@ -163,7 +163,7 @@ function stringify(obj, indent) {
163
163
  return `${obj}`;
164
164
  }
165
165
  else if (typeof obj === "bigint") {
166
- return `${obj}`;
166
+ return `${obj}n`;
167
167
  }
168
168
  else if (typeof obj === "symbol") {
169
169
  return obj.toString();
@@ -178,17 +178,12 @@ function stringify(obj, indent) {
178
178
  return "null";
179
179
  }
180
180
  try {
181
- // Custom replacer for circular references
182
- const getCircularReplacer = () => {
183
- const seen = new WeakSet();
184
- return (_key, value) => {
185
- if (typeof value === "object" && value !== null) {
186
- if (seen.has(value)) {
187
- return "[Circular]";
188
- }
189
- seen.add(value);
190
- }
191
- else if (typeof value === "bigint") {
181
+ // Deep clone the object while removing circular references
182
+ const seen = new WeakSet();
183
+ const deepCloneWithoutCircular = (value, path = []) => {
184
+ // Handle primitives and null
185
+ if (value === null || typeof value !== "object") {
186
+ if (typeof value === "bigint") {
192
187
  return `${value}n`;
193
188
  }
194
189
  else if (typeof value === "function") {
@@ -198,15 +193,50 @@ function stringify(obj, indent) {
198
193
  return value.toString();
199
194
  }
200
195
  return value;
201
- };
196
+ }
197
+ // Check for circular reference
198
+ if (seen.has(value)) {
199
+ return undefined; // This will cause the property to be omitted
200
+ }
201
+ seen.add(value);
202
+ try {
203
+ // Handle arrays
204
+ if (Array.isArray(value)) {
205
+ const cloned = [];
206
+ for (let i = 0; i < value.length; i++) {
207
+ const clonedValue = deepCloneWithoutCircular(value[i], [...path, String(i)]);
208
+ if (clonedValue !== undefined) {
209
+ cloned.push(clonedValue);
210
+ }
211
+ }
212
+ return cloned;
213
+ }
214
+ // Handle objects
215
+ const cloned = {};
216
+ for (const key in value) {
217
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
218
+ const clonedValue = deepCloneWithoutCircular(value[key], [...path, key]);
219
+ if (clonedValue !== undefined) {
220
+ cloned[key] = clonedValue;
221
+ }
222
+ }
223
+ }
224
+ return cloned;
225
+ }
226
+ finally {
227
+ // Remove from seen set when we're done processing this level
228
+ seen.delete(value);
229
+ }
202
230
  };
203
- return JSON.stringify(obj, getCircularReplacer(), indent);
231
+ const clonedObj = deepCloneWithoutCircular(obj);
232
+ return JSON.stringify(clonedObj, null, indent);
204
233
  }
205
234
  catch (error) {
206
235
  const msg = error && typeof error === "object" && "message" in error ? error.message : "Unknown error";
207
236
  return `[Error stringifying object: ${msg}]`;
208
237
  }
209
238
  }
239
+ exports.stringify = stringify;
210
240
  class Logger {
211
241
  constructor(name, logLevel, writer, formatter) {
212
242
  // Normalize for JavaScript users who might pass uppercase
@@ -261,27 +291,32 @@ class Logger {
261
291
  }
262
292
  trace(msg) {
263
293
  if (this._shouldWrite("trace")) {
264
- this._writer.write(this._formatMessage("trace", stringify(msg)), "trace");
294
+ const message = typeof msg === "function" ? msg() : msg;
295
+ this._writer.write(this._formatMessage("trace", stringify(message)), "trace");
265
296
  }
266
297
  }
267
298
  debug(msg) {
268
299
  if (this._shouldWrite("debug")) {
269
- this._writer.write(this._formatMessage("debug", stringify(msg)), "debug");
300
+ const message = typeof msg === "function" ? msg() : msg;
301
+ this._writer.write(this._formatMessage("debug", stringify(message)), "debug");
270
302
  }
271
303
  }
272
304
  info(msg) {
273
305
  if (this._shouldWrite("info")) {
274
- this._writer.write(this._formatMessage("info", stringify(msg)), "info");
306
+ const message = typeof msg === "function" ? msg() : msg;
307
+ this._writer.write(this._formatMessage("info", stringify(message)), "info");
275
308
  }
276
309
  }
277
310
  warning(msg) {
278
311
  if (this._shouldWrite("warning")) {
279
- this._writer.write(this._formatMessage("warning", stringify(msg)), "warning");
312
+ const message = typeof msg === "function" ? msg() : msg;
313
+ this._writer.write(this._formatMessage("warning", stringify(message)), "warning");
280
314
  }
281
315
  }
282
316
  error(msg) {
283
317
  if (this._shouldWrite("error")) {
284
- this._writer.write(this._formatMessage("error", stringify(msg)), "error");
318
+ const message = typeof msg === "function" ? msg() : msg;
319
+ this._writer.write(this._formatMessage("error", stringify(message)), "error");
285
320
  }
286
321
  }
287
322
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chia-agent",
3
- "version": "14.3.5",
3
+ "version": "14.3.6-beta.1",
4
4
  "author": "ChiaMineJP <admin@chiamine.jp>",
5
5
  "description": "chia rpc/websocket client library",
6
6
  "license": "MIT",
package/rpc/index.js CHANGED
@@ -79,9 +79,9 @@ function loadCertFilesFromConfig(config) {
79
79
  const caCertPath = (0, index_1.resolveFromChiaRoot)([
80
80
  config["/private_ssl_ca/crt"],
81
81
  ]);
82
- (0, logger_1.getLogger)().debug(`Loading client cert file from ${clientCertPath}`);
83
- (0, logger_1.getLogger)().debug(`Loading client key file from ${clientKeyPath}`);
84
- (0, logger_1.getLogger)().debug(`Loading ca cert file from ${caCertPath}`);
82
+ (0, logger_1.getLogger)().debug(() => `Loading client cert file from ${clientCertPath}`);
83
+ (0, logger_1.getLogger)().debug(() => `Loading client key file from ${clientKeyPath}`);
84
+ (0, logger_1.getLogger)().debug(() => `Loading ca cert file from ${caCertPath}`);
85
85
  const getCertOrKey = (path) => {
86
86
  if (!(0, fs_1.existsSync)(path)) {
87
87
  (0, logger_1.getLogger)().error(`ssl crt/key does not exist at: ${path}`);
@@ -111,10 +111,10 @@ class RPCAgent {
111
111
  if (agent.options && agent.options.host && agent.options.port) {
112
112
  this._host = agent.options.host;
113
113
  this._port = agent.options.port;
114
- (0, logger_1.getLogger)().debug(`Constructing RPCAgent with httpsAgent: ${this._host}:${this._port}`);
114
+ (0, logger_1.getLogger)().debug(() => `Constructing RPCAgent with httpsAgent: ${this._host}:${this._port}`);
115
115
  }
116
116
  else {
117
- (0, logger_1.getLogger)().debug("Constructing RPCAgent with httpsAgent (host/port not available in agent options)");
117
+ (0, logger_1.getLogger)().debug(() => "Constructing RPCAgent with httpsAgent (host/port not available in agent options)");
118
118
  }
119
119
  }
120
120
  else if ("httpAgent" in props) {
@@ -123,7 +123,7 @@ class RPCAgent {
123
123
  this._host = props.host;
124
124
  this._port = props.port;
125
125
  this._skip_hostname_verification = Boolean(props.skip_hostname_verification);
126
- (0, logger_1.getLogger)().debug(`Constructing RPCAgent with httpAgent: ${this._host}:${this._port}`);
126
+ (0, logger_1.getLogger)().debug(() => `Constructing RPCAgent with httpAgent: ${this._host}:${this._port}`);
127
127
  }
128
128
  else if ("protocol" in props) {
129
129
  this._protocol = props.protocol;
@@ -172,7 +172,7 @@ class RPCAgent {
172
172
  maxSockets,
173
173
  timeout,
174
174
  });
175
- (0, logger_1.getLogger)().debug(`Constructed RPCAgent with httpsAgent: ${host}:${port}`);
175
+ (0, logger_1.getLogger)().debug(() => `Constructed RPCAgent with httpsAgent: ${host}:${port}`);
176
176
  }
177
177
  else {
178
178
  this._agent = new http_1.Agent({
@@ -206,7 +206,7 @@ class RPCAgent {
206
206
  host = props.host ? props.host : info.hostname;
207
207
  port = typeof props.port === "number" ? props.port : info.port;
208
208
  }
209
- (0, logger_1.getLogger)().debug(`Picked ${host}:${port} for ${props.service}`);
209
+ (0, logger_1.getLogger)().debug(() => `Picked ${host}:${port} for ${props.service}`);
210
210
  const certs = loadCertFilesFromConfig(config);
211
211
  const { clientCert, clientKey, caCert } = certs;
212
212
  this._skip_hostname_verification = Boolean(props.skip_hostname_verification);
@@ -229,7 +229,7 @@ class RPCAgent {
229
229
  sendMessage(destination, command, data) {
230
230
  return __awaiter(this, void 0, void 0, function* () {
231
231
  // parameter `destination` is not used because target rpc server is determined by url.
232
- (0, logger_1.getLogger)().debug(`Sending RPC message. dest=${destination} command=${command}`);
232
+ (0, logger_1.getLogger)().debug(() => `Sending RPC message. dest=${destination} command=${command}`);
233
233
  return this.request("POST", command, data);
234
234
  });
235
235
  }
@@ -284,9 +284,9 @@ class RPCAgent {
284
284
  }
285
285
  }
286
286
  const transporter = this._protocol === "https" ? https_1.request : http_1.request;
287
- (0, logger_1.getLogger)().debug(`Dispatching RPC ${METHOD} request to ${this._protocol}//${this._host}:${this._port}${options.path}`);
288
- (0, logger_1.getLogger)().trace(`Request options: ${JSON.stringify(options)}`);
289
- (0, logger_1.getLogger)().trace(`Request body: ${body}`);
287
+ (0, logger_1.getLogger)().debug(() => `Dispatching RPC ${METHOD} request to ${this._protocol}//${this._host}:${this._port}${options.path}`);
288
+ (0, logger_1.getLogger)().trace(() => `Request options: ${(0, logger_1.stringify)(options)}`);
289
+ (0, logger_1.getLogger)().trace(() => `Request body: ${body}`);
290
290
  const req = transporter(options, (res) => {
291
291
  if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {
292
292
  (0, logger_1.getLogger)().error(`Status not ok: ${res.statusCode}`);
@@ -302,9 +302,9 @@ class RPCAgent {
302
302
  res.on("data", (chunk) => {
303
303
  chunks.push(chunk);
304
304
  if (chunks.length === 0) {
305
- (0, logger_1.getLogger)().debug("The first response chunk data arrived");
305
+ (0, logger_1.getLogger)().debug(() => "The first response chunk data arrived");
306
306
  }
307
- (0, logger_1.getLogger)().trace(`Response chunk #${chunks.length} - ${chunk.length} bytes: ${chunk.toString()}`);
307
+ (0, logger_1.getLogger)().trace(() => `Response chunk #${chunks.length} - ${chunk.length} bytes: ${chunk.toString()}`);
308
308
  });
309
309
  res.on("end", () => {
310
310
  try {
@@ -324,8 +324,8 @@ class RPCAgent {
324
324
  (0, logger_1.getLogger)().info(`API failure: ${d.error}`);
325
325
  return reject(d);
326
326
  }
327
- (0, logger_1.getLogger)().debug(`RPC response received from ${this._protocol}//${this._host}:${this._port}${options.path}`);
328
- (0, logger_1.getLogger)().trace(`RPC response data: ${JSON.stringify(d)}`);
327
+ (0, logger_1.getLogger)().debug(() => `RPC response received from ${this._protocol}//${this._host}:${this._port}${options.path}`);
328
+ (0, logger_1.getLogger)().trace(() => `RPC response data: ${(0, logger_1.stringify)(d)}`);
329
329
  return resolve(d);
330
330
  }
331
331
  // RPC Server should return response like
@@ -335,7 +335,7 @@ class RPCAgent {
335
335
  reject(new Error("Server responded without expected data"));
336
336
  }
337
337
  catch (e) {
338
- (0, logger_1.getLogger)().error(`Failed to parse response data: ${JSON.stringify(e)}`);
338
+ (0, logger_1.getLogger)().error(`Failed to parse response data: ${(0, logger_1.stringify)(e)}`);
339
339
  try {
340
340
  (0, logger_1.getLogger)().error(Buffer.concat(chunks).toString());
341
341
  }
@@ -347,7 +347,7 @@ class RPCAgent {
347
347
  });
348
348
  });
349
349
  req.on("error", (error) => {
350
- (0, logger_1.getLogger)().error(JSON.stringify(error));
350
+ (0, logger_1.getLogger)().error(() => (0, logger_1.stringify)(error));
351
351
  reject(error);
352
352
  });
353
353
  if ((METHOD === "POST" || METHOD === "PUT" || METHOD === "DELETE") &&