@peers-app/peers-sdk 0.7.21 → 0.7.23

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.
@@ -1,9 +1,10 @@
1
+ type ConsoleLevel = 'debug' | 'info' | 'log' | 'warn' | 'error';
1
2
  /**
2
3
  * Setup console proxy to capture all console output and write to ConsoleLogs table.
3
4
  * Also subscribes to dataChanged events to output logs from other process instances.
4
5
  * @param processName - The name of the process (e.g., 'main', 'renderer')
5
6
  */
6
- export declare function setupConsoleProxy(processName: string): Promise<void>;
7
+ export declare function setupConsoleLogsProxy(processName: string): void;
7
8
  /**
8
9
  * Restore original console methods
9
10
  */
@@ -14,15 +15,6 @@ export declare function restoreConsole(): void;
14
15
  * @internal Exported for testing
15
16
  */
16
17
  export declare function extractCoreMessage(message: string): string;
17
- /**
18
- * Mark the logging system as initialized and flush any pending logs
19
- */
20
- export declare function markLoggingInitialized(): void;
21
- /**
22
- * Reset logging initialization state (primarily for testing)
23
- * @internal
24
- */
25
- export declare function resetLoggingState(): void;
26
18
  /**
27
19
  * Logger class for creating source-specific loggers
28
20
  *
@@ -35,6 +27,8 @@ export declare function resetLoggingState(): void;
35
27
  */
36
28
  export declare class Logger {
37
29
  private source;
30
+ private pendingLogs;
31
+ private waitingForConsoleLogsTable;
38
32
  constructor(source?: string);
39
33
  /**
40
34
  * Log a debug message
@@ -59,5 +53,7 @@ export declare class Logger {
59
53
  /**
60
54
  * Internal method to write log to database
61
55
  */
62
- private writeLog;
56
+ writeLog(level: ConsoleLevel, args: any[]): void;
57
+ flushPendingLogs(): Promise<void>;
63
58
  }
59
+ export {};
@@ -1,14 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Logger = void 0;
4
- exports.setupConsoleProxy = setupConsoleProxy;
4
+ exports.setupConsoleLogsProxy = setupConsoleLogsProxy;
5
5
  exports.restoreConsole = restoreConsole;
6
6
  exports.extractCoreMessage = extractCoreMessage;
7
- exports.markLoggingInitialized = markLoggingInitialized;
8
- exports.resetLoggingState = resetLoggingState;
7
+ const lodash_1 = require("lodash");
8
+ const serial_json_1 = require("../serial-json");
9
9
  const utils_1 = require("../utils");
10
10
  const console_logs_table_1 = require("./console-logs.table");
11
- const startupDelayForWritingLogsToDB = Date.now() + 2_000;
12
11
  // Store original console methods
13
12
  const originalConsole = {
14
13
  debug: console.debug,
@@ -17,35 +16,32 @@ const originalConsole = {
17
16
  warn: console.warn,
18
17
  error: console.error,
19
18
  };
20
- let isProxySetup = false;
19
+ let isConsoleLogsProxySetup = false;
21
20
  let currentProcessName = 'unknown';
22
21
  let currentProcessInstanceId = '';
23
- // Guard against infinite recursion during log processing (single process)
24
- let isWritingLog = false;
25
- let recursionAttempts = 0;
26
- const MAX_RECURSION_ATTEMPTS = 3;
27
22
  // Guard against cross-process infinite loops
28
23
  // Track recent log signatures (hash of message + level + source)
29
24
  const recentLogSignatures = new Map();
30
25
  const LOG_SIGNATURE_WINDOW_MS = 5000; // 5 second window
31
26
  const MAX_IDENTICAL_LOGS_IN_WINDOW = 10; // Max same log in window
32
27
  // Track initialization state
33
- let isInitialized = false;
34
- const pendingLogs = [];
35
28
  const MAX_PENDING_LOGS = 1000; // Prevent memory buildup if initialization never happens
29
+ let defaultLogger = undefined;
30
+ let consoleLogsProxySubscription = undefined;
36
31
  /**
37
32
  * Setup console proxy to capture all console output and write to ConsoleLogs table.
38
33
  * Also subscribes to dataChanged events to output logs from other process instances.
39
34
  * @param processName - The name of the process (e.g., 'main', 'renderer')
40
35
  */
41
- async function setupConsoleProxy(processName) {
42
- if (isProxySetup) {
43
- console.warn('Console proxy already setup, skipping...');
36
+ function setupConsoleLogsProxy(processName) {
37
+ if (isConsoleLogsProxySetup) {
38
+ originalConsole.warn('Console logs proxy already setup, skipping...');
44
39
  return;
45
40
  }
46
- isProxySetup = true;
41
+ isConsoleLogsProxySetup = true;
47
42
  currentProcessName = processName;
48
43
  currentProcessInstanceId = (0, utils_1.newid)(); // Unique ID for this process instance
44
+ defaultLogger = new Logger(currentProcessName);
49
45
  const levels = ['debug', 'info', 'log', 'warn', 'error'];
50
46
  // Monkey-patch console methods to capture local logs
51
47
  levels.forEach((level) => {
@@ -53,130 +49,62 @@ async function setupConsoleProxy(processName) {
53
49
  console[level] = function (...args) {
54
50
  // Always call original console method first (preserve terminal output)
55
51
  original.apply(console, args);
56
- // Asynchronously write to database (don't block console output)
57
- writeLogToDatabase(level, args).catch((err) => {
58
- // Use original console to avoid infinite recursion
59
- originalConsole.error('Failed to write log to database:', err);
60
- });
52
+ defaultLogger.writeLog(level, args);
61
53
  };
62
54
  });
63
55
  // Subscribe to dataChanged events to output logs from OTHER process instances
64
- try {
65
- const consoleLogsTable = await (0, console_logs_table_1.ConsoleLogs)();
66
- consoleLogsTable.dataChanged.subscribe((evt) => {
67
- const log = evt.dataObject;
68
- // Skip if this is our own log
69
- if (log.processInstanceId === currentProcessInstanceId) {
70
- return;
71
- }
72
- // Output log from another process with prefix
73
- const colorCode = log.process === 'renderer' ? '\x1b[36m' : '\x1b[35m'; // cyan for renderer, magenta for main
74
- const resetCode = '\x1b[0m';
75
- const prefix = `${colorCode}[${log.process}]${resetCode}`;
76
- const logLevel = log.level;
77
- const logArgs = [prefix, log.message];
78
- if (log.context) {
79
- logArgs.push(log.context);
80
- }
81
- if (originalConsole[logLevel]) {
82
- originalConsole[logLevel](...logArgs);
83
- }
84
- else {
85
- originalConsole.log(...logArgs);
86
- }
87
- // Also output stack trace for errors
88
- if (log.stackTrace) {
89
- originalConsole.error(log.stackTrace);
90
- }
91
- });
92
- }
93
- catch (err) {
94
- originalConsole.error('Failed to subscribe to console logs dataChanged:', err);
95
- }
96
- console.log(`Console proxy initialized for process: ${processName} (instance: ${currentProcessInstanceId})`);
56
+ (0, console_logs_table_1.ConsoleLogs)().then(async (consoleLogsTable) => {
57
+ try {
58
+ consoleLogsProxySubscription = consoleLogsTable.dataChanged.subscribe((evt) => {
59
+ const log = evt.dataObject;
60
+ // Skip if this is our own log
61
+ if (log.processInstanceId === currentProcessInstanceId) {
62
+ return;
63
+ }
64
+ // Output log from another process with prefix
65
+ const colorCode = log.process === 'renderer' ? '\x1b[36m' : '\x1b[35m'; // cyan for renderer, magenta for main
66
+ const resetCode = '\x1b[0m';
67
+ const prefix = `${colorCode}[${log.process}]${resetCode}`;
68
+ const logLevel = log.level;
69
+ const logArgs = [prefix, log.message];
70
+ if (log.context) {
71
+ logArgs.push(log.context);
72
+ }
73
+ if (originalConsole[logLevel]) {
74
+ originalConsole[logLevel](...logArgs);
75
+ }
76
+ else {
77
+ originalConsole.log({ logLevel }, ...logArgs);
78
+ }
79
+ // Also output stack trace for errors
80
+ if (log.stackTrace) {
81
+ originalConsole.error(log.stackTrace);
82
+ }
83
+ });
84
+ }
85
+ catch (err) {
86
+ originalConsole.error('Failed to subscribe to console logs dataChanged:', err);
87
+ }
88
+ });
89
+ originalConsole.log(`Console proxy initialized for process: ${processName} (instance: ${currentProcessInstanceId})`);
97
90
  }
98
91
  /**
99
92
  * Restore original console methods
100
93
  */
101
94
  function restoreConsole() {
102
- if (!isProxySetup) {
95
+ if (!isConsoleLogsProxySetup) {
103
96
  return;
104
97
  }
98
+ consoleLogsProxySubscription?.unsubscribe();
99
+ consoleLogsProxySubscription = undefined;
105
100
  console.debug = originalConsole.debug;
106
101
  console.info = originalConsole.info;
107
102
  console.log = originalConsole.log;
108
103
  console.warn = originalConsole.warn;
109
104
  console.error = originalConsole.error;
110
- isProxySetup = false;
105
+ isConsoleLogsProxySetup = false;
111
106
  originalConsole.log('Console proxy removed, original console restored');
112
107
  }
113
- /**
114
- * Write a log entry to the ConsoleLogs table
115
- */
116
- async function writeLogToDatabase(level, args) {
117
- const timestamp = (0, utils_1.getTimestamp)();
118
- if (timestamp < startupDelayForWritingLogsToDB) {
119
- await (0, utils_1.sleep)(startupDelayForWritingLogsToDB - timestamp);
120
- }
121
- // Guard against infinite recursion (same process)
122
- if (isWritingLog) {
123
- recursionAttempts++;
124
- if (recursionAttempts >= MAX_RECURSION_ATTEMPTS) {
125
- // Break the loop by using original console
126
- originalConsole.error(`[console-logger] Infinite loop detected after ${recursionAttempts} attempts, dropping log write`, { level, args });
127
- // Reset counter after a delay to allow recovery
128
- setTimeout(() => {
129
- recursionAttempts = 0;
130
- }, 1000);
131
- return;
132
- }
133
- // Allow a few attempts in case of legitimate nested logging
134
- return;
135
- }
136
- isWritingLog = true;
137
- try {
138
- // Format message from arguments
139
- const message = formatLogMessage(args);
140
- // Extract context objects from arguments
141
- const context = extractContext(args);
142
- // Get stack trace for errors
143
- const stackTrace = level === 'error' ? getStackTrace() : undefined;
144
- // Extract source from stack trace
145
- const source = extractSource(stackTrace);
146
- // Guard against cross-process infinite loops
147
- // Extract core message to detect loops even when processes add prefixes
148
- const coreMessage = extractCoreMessage(message);
149
- // Create signature from level + source + core message (not the full message)
150
- const signature = `${level}:${source || 'unknown'}:${coreMessage.substring(0, 100)}`;
151
- if (!checkLogSignature(signature)) {
152
- // Too many identical logs in window, likely an infinite loop
153
- originalConsole.warn(`[console-logger] Cross-process infinite loop detected for log: ${signature.substring(0, 80)}...`);
154
- return;
155
- }
156
- const logRecord = {
157
- logId: (0, utils_1.newid)(),
158
- timestamp,
159
- level,
160
- process: currentProcessName,
161
- processInstanceId: currentProcessInstanceId,
162
- source,
163
- message,
164
- context,
165
- stackTrace,
166
- };
167
- const consoleLogsTable = await (0, console_logs_table_1.ConsoleLogs)();
168
- await consoleLogsTable.insert(logRecord);
169
- }
170
- catch (err) {
171
- originalConsole.error(`error while trying to write console log`, err);
172
- // Silently fail if table not available (e.g., during initialization)
173
- // Don't use console here to avoid infinite recursion
174
- }
175
- finally {
176
- isWritingLog = false;
177
- recursionAttempts = 0;
178
- }
179
- }
180
108
  /**
181
109
  * Extract core message by removing common prefixes that might be added by different processes
182
110
  * This helps detect cross-process loops where each process adds its own prefix
@@ -226,42 +154,6 @@ function checkLogSignature(signature) {
226
154
  }
227
155
  return true;
228
156
  }
229
- /**
230
- * Format log arguments into a single message string
231
- */
232
- function formatLogMessage(args) {
233
- return args.map((arg) => {
234
- if (typeof arg === 'string') {
235
- return arg;
236
- }
237
- if (arg instanceof Error) {
238
- return `${arg.name}: ${arg.message}`;
239
- }
240
- if (typeof arg === 'object') {
241
- try {
242
- return JSON.stringify(arg);
243
- }
244
- catch {
245
- return String(arg);
246
- }
247
- }
248
- return String(arg);
249
- }).join(' ');
250
- }
251
- /**
252
- * Extract structured context objects from log arguments
253
- */
254
- function extractContext(args) {
255
- const objects = args.filter((arg) => arg && typeof arg === 'object' && !(arg instanceof Error) && !(arg instanceof Date));
256
- if (objects.length === 0) {
257
- return undefined;
258
- }
259
- if (objects.length === 1) {
260
- return objects[0];
261
- }
262
- // Merge multiple objects
263
- return Object.assign({}, ...objects);
264
- }
265
157
  /**
266
158
  * Get current stack trace
267
159
  */
@@ -306,71 +198,37 @@ function extractSource(stackTrace) {
306
198
  }
307
199
  return undefined;
308
200
  }
309
- /**
310
- * Mark the logging system as initialized and flush any pending logs
311
- */
312
- function markLoggingInitialized() {
313
- isInitialized = true;
314
- // Flush pending logs sequentially with small delays to avoid recursion guard
315
- if (pendingLogs.length > 0) {
316
- originalConsole.log(`[console-logger] Flushing ${pendingLogs.length} pending logs...`);
317
- const logsToFlush = [...pendingLogs]; // Copy array
318
- pendingLogs.length = 0; // Clear the original array
319
- // Flush logs with small delays between them
320
- let delay = 0;
321
- for (const { level, source, args } of logsToFlush) {
322
- setTimeout(() => {
323
- writeLogToDatabaseWithSource(level, source, args).catch((err) => {
324
- originalConsole.error('[console-logger] Failed to flush pending log:', err);
325
- });
326
- }, delay);
327
- delay += 10; // 10ms between each log
328
- }
329
- }
330
- }
331
- /**
332
- * Reset logging initialization state (primarily for testing)
333
- * @internal
334
- */
335
- function resetLoggingState() {
336
- isInitialized = false;
337
- pendingLogs.length = 0;
338
- }
339
201
  /**
340
202
  * Write a log entry to the ConsoleLogs table with a specific source
341
203
  */
342
204
  async function writeLogToDatabaseWithSource(level, source, args) {
343
- const timestamp = (0, utils_1.getTimestamp)();
344
- if (timestamp < startupDelayForWritingLogsToDB) {
345
- await (0, utils_1.sleep)(startupDelayForWritingLogsToDB - timestamp);
346
- }
347
- // Guard against infinite recursion (same process)
348
- if (isWritingLog) {
349
- recursionAttempts++;
350
- if (recursionAttempts >= MAX_RECURSION_ATTEMPTS) {
351
- originalConsole.error(`[console-logger] Infinite loop detected after ${recursionAttempts} attempts, dropping log write`, { level, source, args });
352
- setTimeout(() => {
353
- recursionAttempts = 0;
354
- }, 1000);
355
- return;
356
- }
357
- return;
358
- }
359
- isWritingLog = true;
360
205
  try {
206
+ const timestamp = (0, utils_1.getTimestamp)();
361
207
  // Format message from arguments
362
- const message = formatLogMessage(args);
363
- // Extract context objects from arguments
364
- const context = extractContext(args);
365
- // Get stack trace for errors
366
- const stackTrace = level === 'error' ? getStackTrace() : undefined;
367
- // Guard against cross-process infinite loops
208
+ let message = typeof args[0] === 'string' ?
209
+ args.shift() :
210
+ (0, serial_json_1.toJSONString)(args[0]);
211
+ // Check for infinite loop before writing
212
+ // Extract core message to detect cross-process loops where prefixes are added
368
213
  const coreMessage = extractCoreMessage(message);
369
- const signature = `${level}:${source}:${coreMessage.substring(0, 100)}`;
214
+ const signature = `${level}:${coreMessage}:${source}`;
370
215
  if (!checkLogSignature(signature)) {
371
- originalConsole.warn(`[console-logger] Cross-process infinite loop detected for log: ${signature.substring(0, 80)}...`);
216
+ // This log is suspected to be part of an infinite loop, drop it
372
217
  return;
373
218
  }
219
+ // Extract context objects from arguments
220
+ for (const a of [...args]) {
221
+ if (typeof a === 'object' && !((0, lodash_1.isDate)(a) || a === null)) {
222
+ break;
223
+ }
224
+ message += ' ' + args.shift();
225
+ }
226
+ let context = args.length === 0 ?
227
+ undefined : args.length === 1 ?
228
+ args[0] :
229
+ args;
230
+ // Get stack trace for errors
231
+ const stackTrace = level === 'error' ? getStackTrace() : undefined;
374
232
  const logRecord = {
375
233
  logId: (0, utils_1.newid)(),
376
234
  timestamp,
@@ -386,11 +244,7 @@ async function writeLogToDatabaseWithSource(level, source, args) {
386
244
  await consoleLogsTable.insert(logRecord);
387
245
  }
388
246
  catch (err) {
389
- // Silently fail if table not available
390
- }
391
- finally {
392
- isWritingLog = false;
393
- recursionAttempts = 0;
247
+ originalConsole.error('[console-logger] Failed to write log to database:', err);
394
248
  }
395
249
  }
396
250
  /**
@@ -405,6 +259,8 @@ async function writeLogToDatabaseWithSource(level, source, args) {
405
259
  */
406
260
  class Logger {
407
261
  source;
262
+ pendingLogs = [];
263
+ waitingForConsoleLogsTable = true;
408
264
  constructor(source) {
409
265
  // If no source provided, try to extract from caller's location
410
266
  if (!source) {
@@ -412,9 +268,10 @@ class Logger {
412
268
  source = extractSource(stack) || 'unknown';
413
269
  }
414
270
  this.source = source;
415
- // ConsoleLogs().then(() => {
416
- // markLoggingInitialized();
417
- // })
271
+ (0, console_logs_table_1.ConsoleLogs)().then(() => {
272
+ this.waitingForConsoleLogsTable = false;
273
+ this.flushPendingLogs();
274
+ });
418
275
  }
419
276
  /**
420
277
  * Log a debug message
@@ -455,14 +312,14 @@ class Logger {
455
312
  * Internal method to write log to database
456
313
  */
457
314
  writeLog(level, args) {
458
- if (!isInitialized) {
315
+ if (this.waitingForConsoleLogsTable) {
459
316
  // Queue the log until system is initialized
460
- if (pendingLogs.length < MAX_PENDING_LOGS) {
461
- pendingLogs.push({ level, source: this.source, args });
317
+ if (this.pendingLogs.length < MAX_PENDING_LOGS) {
318
+ this.pendingLogs.push({ level, args });
462
319
  }
463
320
  else {
464
321
  // Drop logs if queue is full to prevent memory issues
465
- if (pendingLogs.length === MAX_PENDING_LOGS) {
322
+ if (this.pendingLogs.length === MAX_PENDING_LOGS) {
466
323
  originalConsole.warn('[console-logger] Pending log queue is full, dropping new logs until initialization');
467
324
  }
468
325
  }
@@ -473,5 +330,15 @@ class Logger {
473
330
  originalConsole.error('[console-logger] Failed to write log:', err);
474
331
  });
475
332
  }
333
+ async flushPendingLogs() {
334
+ const _pendingLogs = [...this.pendingLogs];
335
+ this.pendingLogs.length = 0;
336
+ for (const { level, args } of _pendingLogs) {
337
+ await (0, utils_1.sleep)(10); // small delay to avoid stampede
338
+ await writeLogToDatabaseWithSource(level, this.source, args).catch((err) => {
339
+ originalConsole.error('[console-logger] Failed to flush pending log:', err);
340
+ });
341
+ }
342
+ }
476
343
  }
477
344
  exports.Logger = Logger;
@@ -11,12 +11,16 @@ jest.mock('./console-logs.table', () => {
11
11
  };
12
12
  return {
13
13
  ConsoleLogs: jest.fn().mockResolvedValue(mockTable),
14
+ __mockTable: mockTable, // Export for test access
14
15
  };
15
16
  });
17
+ // Get reference to mock table
18
+ const { __mockTable: mockTable } = require('./console-logs.table');
16
19
  // Mock utils
17
20
  jest.mock('../utils', () => ({
18
21
  newid: () => 'test-id-123',
19
22
  getTimestamp: () => '2024-01-01T00:00:00.000Z',
23
+ sleep: jest.fn().mockResolvedValue(undefined),
20
24
  }));
21
25
  describe('console-logger', () => {
22
26
  let originalConsole;
@@ -62,6 +66,8 @@ describe('console-logger', () => {
62
66
  jest.clearAllMocks();
63
67
  // Restore console before each test
64
68
  (0, console_logger_1.restoreConsole)();
69
+ // Reset mock table
70
+ mockTable.insert.mockClear();
65
71
  });
66
72
  afterAll(() => {
67
73
  // Restore original console methods
@@ -73,8 +79,6 @@ describe('console-logger', () => {
73
79
  });
74
80
  describe('Single-process infinite loop detection', () => {
75
81
  it('should detect infinite loop when logging from within log handler', async () => {
76
- const { ConsoleLogs } = require('./console-logs.table');
77
- const mockTable = await ConsoleLogs();
78
82
  // Setup mock to throw error that triggers another log
79
83
  let callCount = 0;
80
84
  mockTable.insert.mockImplementation(() => {
@@ -85,23 +89,25 @@ describe('console-logger', () => {
85
89
  }
86
90
  return Promise.resolve();
87
91
  });
88
- await (0, console_logger_1.setupConsoleProxy)('test-process');
92
+ (0, console_logger_1.setupConsoleLogsProxy)('test-process');
93
+ // Wait for setup to complete
94
+ await new Promise(resolve => setTimeout(resolve, 100));
89
95
  // This should trigger the loop
90
96
  console.error('Initial error');
91
97
  // Wait for async operations
92
98
  await new Promise(resolve => setTimeout(resolve, 100));
93
99
  // Should have called insert but stopped early due to recursion guard
94
100
  expect(mockTable.insert).toHaveBeenCalled();
95
- // The recursion guard should stop it after just 1 attempt
96
- // (because isWritingLog prevents re-entry)
97
- expect(callCount).toBeLessThanOrEqual(2); // Setup log + 1 error attempt
101
+ // The calls should be limited
102
+ expect(callCount).toBeLessThanOrEqual(5);
98
103
  });
99
104
  });
100
105
  describe('Cross-process infinite loop detection', () => {
101
106
  it('should detect when same log is written too many times in window', async () => {
102
- const { ConsoleLogs } = require('./console-logs.table');
103
- const mockTable = await ConsoleLogs();
104
- await (0, console_logger_1.setupConsoleProxy)('test-process');
107
+ (0, console_logger_1.setupConsoleLogsProxy)('test-process');
108
+ // Wait for setup
109
+ await new Promise(resolve => setTimeout(resolve, 100));
110
+ mockTable.insert.mockClear();
105
111
  // Log the same message many times quickly (simulating cross-process loop)
106
112
  for (let i = 0; i < 15; i++) {
107
113
  console.error('Same error message');
@@ -111,14 +117,13 @@ describe('console-logger', () => {
111
117
  // Wait for async operations
112
118
  await new Promise(resolve => setTimeout(resolve, 100));
113
119
  // Should have stopped writing after threshold (10)
114
- // Plus 1 for the setup log
115
120
  expect(mockTable.insert.mock.calls.length).toBeLessThan(15);
116
121
  expect(mockTable.insert.mock.calls.length).toBeGreaterThan(0);
117
122
  });
118
123
  it('should NOT detect loop for different messages', async () => {
119
- const { ConsoleLogs } = require('./console-logs.table');
120
- const mockTable = await ConsoleLogs();
121
- await (0, console_logger_1.setupConsoleProxy)('test-process');
124
+ (0, console_logger_1.setupConsoleLogsProxy)('test-process');
125
+ // Wait for setup
126
+ await new Promise(resolve => setTimeout(resolve, 100));
122
127
  // Clear setup log
123
128
  mockTable.insert.mockClear();
124
129
  // Log different messages
@@ -133,9 +138,10 @@ describe('console-logger', () => {
133
138
  expect(mockTable.insert.mock.calls.length).toBeLessThanOrEqual(15);
134
139
  });
135
140
  it('should detect loop even with modified messages (prefix added)', async () => {
136
- const { ConsoleLogs } = require('./console-logs.table');
137
- const mockTable = await ConsoleLogs();
138
- await (0, console_logger_1.setupConsoleProxy)('test-process');
141
+ (0, console_logger_1.setupConsoleLogsProxy)('test-process');
142
+ // Wait for setup
143
+ await new Promise(resolve => setTimeout(resolve, 100));
144
+ mockTable.insert.mockClear();
139
145
  // Simulate cross-process loop where each process adds a prefix
140
146
  // This represents Process A logging "Error X"
141
147
  // Process B seeing it and logging "[main] Error X"
@@ -170,9 +176,10 @@ describe('console-logger', () => {
170
176
  });
171
177
  describe('Loop detection recovery', () => {
172
178
  it('should allow logging again after window expires', async () => {
173
- const { ConsoleLogs } = require('./console-logs.table');
174
- const mockTable = await ConsoleLogs();
175
- await (0, console_logger_1.setupConsoleProxy)('test-process');
179
+ (0, console_logger_1.setupConsoleLogsProxy)('test-process');
180
+ // Wait for setup
181
+ await new Promise(resolve => setTimeout(resolve, 100));
182
+ mockTable.insert.mockClear();
176
183
  // Trigger loop detection
177
184
  for (let i = 0; i < 12; i++) {
178
185
  console.error('Same error');
@@ -190,10 +197,9 @@ describe('console-logger', () => {
190
197
  });
191
198
  describe('Normal logging behavior', () => {
192
199
  it('should write logs to database for normal usage', async () => {
193
- const { ConsoleLogs } = require('./console-logs.table');
194
- await (0, console_logger_1.setupConsoleProxy)('test-process');
195
- // Get fresh mock reference after setup
196
- const mockTable = await ConsoleLogs();
200
+ (0, console_logger_1.setupConsoleLogsProxy)('test-process');
201
+ // Wait for setup to complete
202
+ await new Promise(resolve => setTimeout(resolve, 100));
197
203
  mockTable.insert.mockClear();
198
204
  console.log('Test message');
199
205
  await new Promise(resolve => setTimeout(resolve, 50));
@@ -208,15 +214,10 @@ describe('console-logger', () => {
208
214
  });
209
215
  });
210
216
  describe('Logger class', () => {
211
- beforeEach(() => {
212
- // Reset state before each Logger test
213
- (0, console_logger_1.resetLoggingState)();
214
- });
215
217
  it('should create logger with specified source', async () => {
216
- const { ConsoleLogs } = require('./console-logs.table');
217
- const mockTable = await ConsoleLogs();
218
- (0, console_logger_1.markLoggingInitialized)();
219
218
  const logger = new console_logger_1.Logger('MyModule');
219
+ // Wait for Logger to initialize
220
+ await new Promise(resolve => setTimeout(resolve, 100));
220
221
  mockTable.insert.mockClear();
221
222
  logger.log('Test message');
222
223
  await new Promise(resolve => setTimeout(resolve, 100));
@@ -226,10 +227,9 @@ describe('console-logger', () => {
226
227
  expect(call[0].message).toBe('Test message');
227
228
  });
228
229
  it('should support all log levels', async () => {
229
- const { ConsoleLogs } = require('./console-logs.table');
230
- const mockTable = await ConsoleLogs();
231
- (0, console_logger_1.markLoggingInitialized)();
232
230
  const logger = new console_logger_1.Logger('TestModule');
231
+ // Wait for Logger to initialize
232
+ await new Promise(resolve => setTimeout(resolve, 100));
233
233
  mockTable.insert.mockClear();
234
234
  logger.debug('Debug message');
235
235
  await new Promise(resolve => setTimeout(resolve, 20));
@@ -248,11 +248,14 @@ describe('console-logger', () => {
248
248
  const uniqueLevels = new Set(levels);
249
249
  expect(uniqueLevels.size).toBeGreaterThanOrEqual(4);
250
250
  });
251
- it('should queue logs before initialization', async () => {
251
+ it('should queue logs before initialization and flush after', async () => {
252
+ // Create a delayed promise for ConsoleLogs
253
+ let resolveConsoleLogs;
254
+ const delayedPromise = new Promise((resolve) => {
255
+ resolveConsoleLogs = resolve;
256
+ });
252
257
  const { ConsoleLogs } = require('./console-logs.table');
253
- const mockTable = await ConsoleLogs();
254
- // Ensure we're not initialized
255
- // (resetLoggingState was called in beforeEach)
258
+ ConsoleLogs.mockReturnValueOnce(delayedPromise);
256
259
  const logger = new console_logger_1.Logger('EarlyModule');
257
260
  // Log before initialization
258
261
  logger.log('Early message 1');
@@ -260,21 +263,19 @@ describe('console-logger', () => {
260
263
  logger.error('Early message 3');
261
264
  // Small delay to ensure logs are queued
262
265
  await new Promise(resolve => setTimeout(resolve, 50));
263
- mockTable.insert.mockClear();
264
266
  // Should not have written yet
265
267
  expect(mockTable.insert).not.toHaveBeenCalled();
266
- // Now initialize
267
- (0, console_logger_1.markLoggingInitialized)();
268
+ // Now resolve the ConsoleLogs promise
269
+ resolveConsoleLogs(mockTable);
268
270
  // Wait for flush
269
271
  await new Promise(resolve => setTimeout(resolve, 300));
270
272
  // Should have flushed pending logs
271
273
  expect(mockTable.insert.mock.calls.length).toBeGreaterThanOrEqual(2);
272
274
  });
273
275
  it('should write logs directly after initialization', async () => {
274
- const { ConsoleLogs } = require('./console-logs.table');
275
- const mockTable = await ConsoleLogs();
276
- (0, console_logger_1.markLoggingInitialized)();
277
276
  const logger = new console_logger_1.Logger('LateModule');
277
+ // Wait for initialization
278
+ await new Promise(resolve => setTimeout(resolve, 100));
278
279
  mockTable.insert.mockClear();
279
280
  logger.log('Direct message');
280
281
  await new Promise(resolve => setTimeout(resolve, 100));
@@ -282,11 +283,10 @@ describe('console-logger', () => {
282
283
  expect(mockTable.insert.mock.calls[0][0].message).toBe('Direct message');
283
284
  });
284
285
  it('should use reliable source from constructor', async () => {
285
- const { ConsoleLogs } = require('./console-logs.table');
286
- const mockTable = await ConsoleLogs();
287
- (0, console_logger_1.markLoggingInitialized)();
288
286
  const logger1 = new console_logger_1.Logger('SourceA');
289
287
  const logger2 = new console_logger_1.Logger('SourceB');
288
+ // Wait for initialization
289
+ await new Promise(resolve => setTimeout(resolve, 100));
290
290
  mockTable.insert.mockClear();
291
291
  logger1.log('From A');
292
292
  await new Promise(resolve => setTimeout(resolve, 50));
@@ -9,7 +9,7 @@ export declare const consoleLogSchema: z.ZodObject<{
9
9
  processInstanceId: z.ZodString;
10
10
  source: z.ZodOptional<z.ZodString>;
11
11
  message: z.ZodString;
12
- context: z.ZodOptional<z.ZodObject<{}, "strip", z.ZodAny, z.objectOutputType<{}, z.ZodAny, "strip">, z.objectInputType<{}, z.ZodAny, "strip">>>;
12
+ context: z.ZodOptional<z.ZodUnion<[z.ZodObject<{}, "strip", z.ZodAny, z.objectOutputType<{}, z.ZodAny, "strip">, z.objectInputType<{}, z.ZodAny, "strip">>, z.ZodArray<z.ZodAny, "many">]>>;
13
13
  stackTrace: z.ZodOptional<z.ZodString>;
14
14
  }, "strip", z.ZodTypeAny, {
15
15
  message: string;
@@ -19,7 +19,7 @@ export declare const consoleLogSchema: z.ZodObject<{
19
19
  process: string;
20
20
  processInstanceId: string;
21
21
  source?: string | undefined;
22
- context?: z.objectOutputType<{}, z.ZodAny, "strip"> | undefined;
22
+ context?: any[] | z.objectOutputType<{}, z.ZodAny, "strip"> | undefined;
23
23
  stackTrace?: string | undefined;
24
24
  }, {
25
25
  message: string;
@@ -29,7 +29,7 @@ export declare const consoleLogSchema: z.ZodObject<{
29
29
  processInstanceId: string;
30
30
  source?: string | undefined;
31
31
  timestamp?: number | undefined;
32
- context?: z.objectInputType<{}, z.ZodAny, "strip"> | undefined;
32
+ context?: any[] | z.objectInputType<{}, z.ZodAny, "strip"> | undefined;
33
33
  stackTrace?: string | undefined;
34
34
  }>;
35
35
  export type IConsoleLog = z.infer<typeof consoleLogSchema>;
@@ -51,7 +51,7 @@ exports.consoleLogSchema = zod_1.z.object({
51
51
  processInstanceId: zod_1.z.string().describe('Unique ID for this process instance to filter own logs'),
52
52
  source: zod_1.z.string().optional().describe('The source file or module that generated the log'),
53
53
  message: zod_1.z.string().describe('The log message'),
54
- context: zod_types_1.zodAnyObject.optional().describe('Additional structured context data'),
54
+ context: zod_types_1.zodAnyObjectOrArray.optional().describe('Additional structured context data'),
55
55
  stackTrace: zod_1.z.string().optional().describe('Stack trace for errors'),
56
56
  });
57
57
  const metaData = {
package/dist/rpc-types.js CHANGED
@@ -28,7 +28,8 @@ exports.rpcServerCalls = {
28
28
  };
29
29
  exports.rpcClientCalls = {
30
30
  ping: async (msg) => `pong: ${msg}`,
31
- emitEvent: rpcStub('emitEvent'),
31
+ // emitEvent: rpcStub('emitEvent') as ((event: IEventData) => Promise<boolean>),
32
+ emitEvent: ((...args) => Promise.resolve(false)),
32
33
  setClientPath: rpcStub('setClientPath'),
33
34
  openThread: rpcStub('openThread'),
34
35
  };
package/dist/utils.js CHANGED
@@ -235,6 +235,10 @@ async function retryOnErrorOrTimeout({ fn, connection, opts = {}, }) {
235
235
  throw finalError;
236
236
  }
237
237
  else {
238
+ const weightedTimeout = timeoutCount * 0.1;
239
+ const weightedInverse = 1 - weightedTimeout;
240
+ connection.errorRate = (connection.errorRate * weightedInverse) + weightedTimeout;
241
+ connection.latencyMs = (weightedTimeout * timeout) + (connection.latencyMs * weightedInverse);
238
242
  throw new Error(`Timeout after ${timeoutCount} retries waiting for ${timeout}ms`);
239
243
  }
240
244
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-sdk",
3
- "version": "0.7.21",
3
+ "version": "0.7.23",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/peers-app/peers-sdk.git"
@@ -1,41 +0,0 @@
1
- /**
2
- * Example usage of the Logger class
3
- *
4
- * This file demonstrates how to use the Logger class for reliable, source-specific logging
5
- */
6
- /**
7
- * Example function that uses the logger
8
- */
9
- export declare function processData(data: any): void;
10
- /**
11
- * Example initialization flow
12
- */
13
- export declare function initializeApp(): Promise<void>;
14
- /**
15
- * Benefits of using Logger class:
16
- *
17
- * 1. **Reliable Source Tracking**: Source is set once at construction time,
18
- * not extracted from stack traces which can be unreliable
19
- *
20
- * 2. **Initialization Safety**: Logs before system is ready are queued and
21
- * flushed once markLoggingInitialized() is called
22
- *
23
- * 3. **Infinite Loop Protection**: Inherits all the cross-process and
24
- * single-process infinite loop detection
25
- *
26
- * 4. **Type Safety**: Full TypeScript support with proper typing
27
- *
28
- * 5. **Console Passthrough**: All logs still appear in the console immediately,
29
- * database writes happen asynchronously
30
- *
31
- * Usage pattern:
32
- * ```typescript
33
- * // At the top of each file:
34
- * const logger = new Logger('FeatureName');
35
- *
36
- * // Throughout your code:
37
- * logger.log('Something happened');
38
- * logger.error('Something went wrong', error);
39
- * logger.debug('Detailed debugging info', { context });
40
- * ```
41
- */
@@ -1,74 +0,0 @@
1
- "use strict";
2
- /**
3
- * Example usage of the Logger class
4
- *
5
- * This file demonstrates how to use the Logger class for reliable, source-specific logging
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.processData = processData;
9
- exports.initializeApp = initializeApp;
10
- const console_logger_1 = require("./console-logger");
11
- // Create a logger at the top of your file with a descriptive source name
12
- const logger = new console_logger_1.Logger('MyModule');
13
- // You can also let it auto-detect the source from the filename
14
- // const logger = new Logger();
15
- /**
16
- * Example function that uses the logger
17
- */
18
- function processData(data) {
19
- logger.log('Processing data', { dataSize: data.length });
20
- try {
21
- // Do some work
22
- if (!data) {
23
- logger.warn('No data provided');
24
- return;
25
- }
26
- // More processing...
27
- logger.debug('Data processing in progress', { step: 'validation' });
28
- // Success
29
- logger.info('Data processed successfully');
30
- }
31
- catch (error) {
32
- logger.error('Failed to process data', error);
33
- throw error;
34
- }
35
- }
36
- /**
37
- * Example initialization flow
38
- */
39
- async function initializeApp() {
40
- // Early logging (before system is ready) - these get queued
41
- logger.log('Application starting...');
42
- // ... Initialize databases, connections, etc ...
43
- // Mark logging as initialized - this flushes all queued logs
44
- (0, console_logger_1.markLoggingInitialized)();
45
- logger.log('Application fully initialized');
46
- }
47
- /**
48
- * Benefits of using Logger class:
49
- *
50
- * 1. **Reliable Source Tracking**: Source is set once at construction time,
51
- * not extracted from stack traces which can be unreliable
52
- *
53
- * 2. **Initialization Safety**: Logs before system is ready are queued and
54
- * flushed once markLoggingInitialized() is called
55
- *
56
- * 3. **Infinite Loop Protection**: Inherits all the cross-process and
57
- * single-process infinite loop detection
58
- *
59
- * 4. **Type Safety**: Full TypeScript support with proper typing
60
- *
61
- * 5. **Console Passthrough**: All logs still appear in the console immediately,
62
- * database writes happen asynchronously
63
- *
64
- * Usage pattern:
65
- * ```typescript
66
- * // At the top of each file:
67
- * const logger = new Logger('FeatureName');
68
- *
69
- * // Throughout your code:
70
- * logger.log('Something happened');
71
- * logger.error('Something went wrong', error);
72
- * logger.debug('Detailed debugging info', { context });
73
- * ```
74
- */