cojson 0.9.10 → 0.9.11

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 (101) hide show
  1. package/.turbo/turbo-build.log +3 -3
  2. package/CHANGELOG.md +7 -0
  3. package/dist/native/PeerState.js +2 -1
  4. package/dist/native/PeerState.js.map +1 -1
  5. package/dist/native/PriorityBasedMessageQueue.js +54 -7
  6. package/dist/native/PriorityBasedMessageQueue.js.map +1 -1
  7. package/dist/native/base64url.js +0 -6
  8. package/dist/native/base64url.js.map +1 -1
  9. package/dist/native/coValueCore.js +4 -10
  10. package/dist/native/coValueCore.js.map +1 -1
  11. package/dist/native/coValueState.js +5 -4
  12. package/dist/native/coValueState.js.map +1 -1
  13. package/dist/native/coValues/account.js +2 -1
  14. package/dist/native/coValues/account.js.map +1 -1
  15. package/dist/native/coValues/coList.js +0 -7
  16. package/dist/native/coValues/coList.js.map +1 -1
  17. package/dist/native/coValues/coStream.js +3 -8
  18. package/dist/native/coValues/coStream.js.map +1 -1
  19. package/dist/native/coValues/group.js +3 -2
  20. package/dist/native/coValues/group.js.map +1 -1
  21. package/dist/native/crypto/PureJSCrypto.js +2 -1
  22. package/dist/native/crypto/PureJSCrypto.js.map +1 -1
  23. package/dist/native/crypto/WasmCrypto.js +2 -1
  24. package/dist/native/crypto/WasmCrypto.js.map +1 -1
  25. package/dist/native/crypto/crypto.js +2 -4
  26. package/dist/native/crypto/crypto.js.map +1 -1
  27. package/dist/native/exports.js +2 -1
  28. package/dist/native/exports.js.map +1 -1
  29. package/dist/native/localNode.js +5 -6
  30. package/dist/native/localNode.js.map +1 -1
  31. package/dist/native/logger.js +58 -0
  32. package/dist/native/logger.js.map +1 -0
  33. package/dist/native/permissions.js +3 -4
  34. package/dist/native/permissions.js.map +1 -1
  35. package/dist/native/storage/FileSystem.js +0 -10
  36. package/dist/native/storage/FileSystem.js.map +1 -1
  37. package/dist/native/storage/index.js +14 -23
  38. package/dist/native/storage/index.js.map +1 -1
  39. package/dist/native/sync.js +20 -28
  40. package/dist/native/sync.js.map +1 -1
  41. package/dist/web/PeerState.js +2 -1
  42. package/dist/web/PeerState.js.map +1 -1
  43. package/dist/web/PriorityBasedMessageQueue.js +54 -7
  44. package/dist/web/PriorityBasedMessageQueue.js.map +1 -1
  45. package/dist/web/base64url.js +0 -6
  46. package/dist/web/base64url.js.map +1 -1
  47. package/dist/web/coValueCore.js +4 -10
  48. package/dist/web/coValueCore.js.map +1 -1
  49. package/dist/web/coValueState.js +5 -4
  50. package/dist/web/coValueState.js.map +1 -1
  51. package/dist/web/coValues/account.js +2 -1
  52. package/dist/web/coValues/account.js.map +1 -1
  53. package/dist/web/coValues/coList.js +0 -7
  54. package/dist/web/coValues/coList.js.map +1 -1
  55. package/dist/web/coValues/coStream.js +3 -8
  56. package/dist/web/coValues/coStream.js.map +1 -1
  57. package/dist/web/coValues/group.js +3 -2
  58. package/dist/web/coValues/group.js.map +1 -1
  59. package/dist/web/crypto/PureJSCrypto.js +2 -1
  60. package/dist/web/crypto/PureJSCrypto.js.map +1 -1
  61. package/dist/web/crypto/WasmCrypto.js +2 -1
  62. package/dist/web/crypto/WasmCrypto.js.map +1 -1
  63. package/dist/web/crypto/crypto.js +2 -4
  64. package/dist/web/crypto/crypto.js.map +1 -1
  65. package/dist/web/exports.js +2 -1
  66. package/dist/web/exports.js.map +1 -1
  67. package/dist/web/localNode.js +5 -6
  68. package/dist/web/localNode.js.map +1 -1
  69. package/dist/web/logger.js +58 -0
  70. package/dist/web/logger.js.map +1 -0
  71. package/dist/web/permissions.js +3 -4
  72. package/dist/web/permissions.js.map +1 -1
  73. package/dist/web/storage/FileSystem.js +0 -10
  74. package/dist/web/storage/FileSystem.js.map +1 -1
  75. package/dist/web/storage/index.js +14 -23
  76. package/dist/web/storage/index.js.map +1 -1
  77. package/dist/web/sync.js +20 -28
  78. package/dist/web/sync.js.map +1 -1
  79. package/package.json +1 -1
  80. package/src/PeerState.ts +4 -3
  81. package/src/PriorityBasedMessageQueue.ts +67 -11
  82. package/src/base64url.ts +1 -6
  83. package/src/coValueCore.ts +4 -10
  84. package/src/coValueState.ts +5 -4
  85. package/src/coValues/account.ts +2 -1
  86. package/src/coValues/coList.ts +0 -7
  87. package/src/coValues/coStream.ts +3 -8
  88. package/src/coValues/group.ts +3 -2
  89. package/src/crypto/PureJSCrypto.ts +2 -1
  90. package/src/crypto/WasmCrypto.ts +2 -1
  91. package/src/crypto/crypto.ts +2 -4
  92. package/src/exports.ts +2 -0
  93. package/src/localNode.ts +5 -9
  94. package/src/logger.ts +78 -0
  95. package/src/permissions.ts +3 -5
  96. package/src/storage/FileSystem.ts +0 -12
  97. package/src/storage/index.ts +21 -37
  98. package/src/sync.ts +20 -29
  99. package/src/tests/PeerState.test.ts +0 -3
  100. package/src/tests/PriorityBasedMessageQueue.test.ts +1 -1
  101. package/src/tests/logger.test.ts +145 -0
@@ -2,6 +2,7 @@ import { CoID, RawCoValue } from "../coValue.js";
2
2
  import { CoValueHeader, Transaction } from "../coValueCore.js";
3
3
  import { Signature } from "../crypto/crypto.js";
4
4
  import { RawCoID } from "../ids.js";
5
+ import { logger } from "../logger.js";
5
6
  import { connectedPeers } from "../streamUtils.js";
6
7
  import {
7
8
  CoValueKnownState,
@@ -68,7 +69,6 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
68
69
 
69
70
  const processMessages = async () => {
70
71
  for await (const msg of fromLocalNode) {
71
- console.log("Storage msg start", nMsg);
72
72
  try {
73
73
  if (msg === "Disconnected" || msg === "PingTimeout") {
74
74
  throw new Error("Unexpected Disconnected message");
@@ -83,32 +83,29 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
83
83
  await this.sendNewContent(msg.id, msg, undefined);
84
84
  }
85
85
  } catch (e) {
86
- console.error(
87
- new Error(
88
- `Error reading from localNode, handling msg\n\n${JSON.stringify(
89
- msg,
90
- (k, v) =>
91
- k === "changes" || k === "encryptedChanges"
92
- ? v.slice(0, 20) + "..."
93
- : v,
94
- )}`,
95
- { cause: e },
96
- ),
86
+ logger.error(
87
+ `Error reading from localNode, handling msg\n\n${JSON.stringify(
88
+ msg,
89
+ (k, v) =>
90
+ k === "changes" || k === "encryptedChanges"
91
+ ? v.slice(0, 20) + "..."
92
+ : v,
93
+ )}`,
94
+ e,
97
95
  );
98
96
  }
99
- console.log("Storage msg end", nMsg);
100
97
  nMsg++;
101
98
  }
102
99
  };
103
100
 
104
101
  processMessages().catch((e) =>
105
- console.error("Error in processMessages in storage", e),
102
+ logger.error("Error in processMessages in storage", e),
106
103
  );
107
104
 
108
105
  setTimeout(
109
106
  () =>
110
107
  this.compact().catch((e) => {
111
- console.error("Error while compacting", e);
108
+ logger.error("Error while compacting", e);
112
109
  }),
113
110
  20000,
114
111
  );
@@ -134,7 +131,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
134
131
  sessions: {},
135
132
  asDependencyOf,
136
133
  })
137
- .catch((e) => console.error("Error while pushing known", e));
134
+ .catch((e) => logger.error("Error while pushing known", e));
138
135
 
139
136
  return;
140
137
  }
@@ -190,13 +187,13 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
190
187
  ...ourKnown,
191
188
  asDependencyOf,
192
189
  })
193
- .catch((e) => console.error("Error while pushing known", e));
190
+ .catch((e) => logger.error("Error while pushing known", e));
194
191
 
195
192
  for (const message of newContentMessages) {
196
193
  if (Object.keys(message.new).length === 0) continue;
197
194
  this.toLocalNode
198
195
  .push(message)
199
- .catch((e) => console.error("Error while pushing new content", e));
196
+ .catch((e) => logger.error("Error while pushing new content", e));
200
197
  }
201
198
 
202
199
  this.coValues[id] = coValue;
@@ -232,20 +229,19 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
232
229
 
233
230
  if (!coValue) {
234
231
  if (newContent.header) {
235
- // console.log("Creating in WAL", newContent.id);
236
232
  await this.withWAL((wal) =>
237
233
  writeToWal(wal, this.fs, newContent.id, newContentAsChunk),
238
234
  );
239
235
 
240
236
  this.coValues[newContent.id] = newContentAsChunk;
241
237
  } else {
242
- console.warn("Incontiguous incoming update for " + newContent.id);
238
+ logger.warn("Incontiguous incoming update for " + newContent.id);
243
239
  return;
244
240
  }
245
241
  } else {
246
242
  const merged = mergeChunks(coValue, newContentAsChunk);
247
243
  if (merged === "nonContigous") {
248
- console.warn(
244
+ logger.warn(
249
245
  "Non-contigous new content for " + newContent.id,
250
246
  Object.entries(coValue.sessionEntries).map(([session, entries]) =>
251
247
  entries.map((entry) => ({
@@ -264,7 +260,6 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
264
260
  ),
265
261
  );
266
262
  } else {
267
- // console.log("Appending to WAL", newContent.id);
268
263
  await this.withWAL((wal) =>
269
264
  writeToWal(wal, this.fs, newContent.id, newContentAsChunk),
270
265
  );
@@ -301,8 +296,6 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
301
296
 
302
297
  const { handle, size } = await this.getBlockHandle(blockFile, fs);
303
298
 
304
- // console.log("Attempting to load", id, blockFile);
305
-
306
299
  if (!cachedHeader) {
307
300
  cachedHeader = {};
308
301
  const header = await readHeader(blockFile, handle, size, fs);
@@ -317,15 +310,13 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
317
310
  }
318
311
  const headerEntry = cachedHeader[id];
319
312
 
320
- // console.log("Header entry", id, headerEntry);
321
-
322
313
  if (headerEntry) {
323
314
  const nextChunk = await readChunk(handle, headerEntry, fs);
324
315
  if (result) {
325
316
  const merged = mergeChunks(result, nextChunk);
326
317
 
327
318
  if (merged === "nonContigous") {
328
- console.warn(
319
+ logger.warn(
329
320
  "Non-contigous chunks while loading " + id,
330
321
  result,
331
322
  nextChunk,
@@ -354,7 +345,6 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
354
345
 
355
346
  const coValues = new Map<RawCoID, CoValueChunk>();
356
347
 
357
- console.log("Compacting WAL files", walFiles);
358
348
  if (walFiles.length === 0) return;
359
349
 
360
350
  const oldWal = this.currentWal;
@@ -385,7 +375,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
385
375
  if (existingChunk) {
386
376
  const merged = mergeChunks(existingChunk, chunk);
387
377
  if (merged === "nonContigous") {
388
- console.log(
378
+ logger.info(
389
379
  "Non-contigous chunks in " + chunk.id + ", " + fileName,
390
380
  existingChunk,
391
381
  chunk,
@@ -411,8 +401,6 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
411
401
  return acc;
412
402
  }, 0);
413
403
 
414
- console.log([...coValues.keys()], fileNames, highestBlockNumber);
415
-
416
404
  await writeBlock(coValues, MAX_N_LEVELS, highestBlockNumber + 1, this.fs);
417
405
 
418
406
  for (const walFile of walFiles) {
@@ -438,15 +426,11 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
438
426
  blockFilesByLevelInOrder[level]!.push(blockFile);
439
427
  }
440
428
 
441
- console.log(blockFilesByLevelInOrder);
442
-
443
429
  for (let level = MAX_N_LEVELS; level > 0; level--) {
444
430
  const nBlocksDesired = Math.pow(2, level);
445
431
  const blocksInLevel = blockFilesByLevelInOrder[level];
446
432
 
447
433
  if (blocksInLevel && blocksInLevel.length > nBlocksDesired) {
448
- console.log("Compacting blocks in level", level, blocksInLevel);
449
-
450
434
  const coValues = new Map<RawCoID, CoValueChunk>();
451
435
 
452
436
  for (const blockFile of blocksInLevel) {
@@ -465,7 +449,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
465
449
  if (existingChunk) {
466
450
  const merged = mergeChunks(existingChunk, chunk);
467
451
  if (merged === "nonContigous") {
468
- console.log(
452
+ logger.info(
469
453
  "Non-contigous chunks in " + entry.id + ", " + blockFile,
470
454
  existingChunk,
471
455
  chunk,
@@ -517,7 +501,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
517
501
  setTimeout(
518
502
  () =>
519
503
  this.compact().catch((e) => {
520
- console.error("Error while compacting", e);
504
+ logger.error("Error while compacting", e);
521
505
  }),
522
506
  5000,
523
507
  );
package/src/sync.ts CHANGED
@@ -6,6 +6,7 @@ import { CoValueCore } from "./coValueCore.js";
6
6
  import { Signature } from "./crypto/crypto.js";
7
7
  import { RawCoID, SessionID } from "./ids.js";
8
8
  import { LocalNode } from "./localNode.js";
9
+ import { logger } from "./logger.js";
9
10
  import { CoValuePriority } from "./priority.js";
10
11
 
11
12
  export type CoValueKnownState = {
@@ -150,7 +151,7 @@ export class SyncManager {
150
151
 
151
152
  async handleSyncMessage(msg: SyncMessage, peer: PeerState) {
152
153
  if (peer.erroredCoValues.has(msg.id)) {
153
- console.error(
154
+ logger.warn(
154
155
  `Skipping message ${msg.action} on errored coValue ${msg.id} from peer ${peer.id}`,
155
156
  );
156
157
  return;
@@ -182,7 +183,7 @@ export class SyncManager {
182
183
 
183
184
  if (entry.state.type !== "available") {
184
185
  entry.loadFromPeers([peer]).catch((e: unknown) => {
185
- console.error("Error sending load", e);
186
+ logger.error("Error sending load", e);
186
187
  });
187
188
  return;
188
189
  }
@@ -199,7 +200,7 @@ export class SyncManager {
199
200
  action: "load",
200
201
  ...coValue.knownState(),
201
202
  }).catch((e: unknown) => {
202
- console.error("Error sending load", e);
203
+ logger.error("Error sending load", e);
203
204
  });
204
205
  }
205
206
  }
@@ -229,7 +230,7 @@ export class SyncManager {
229
230
  asDependencyOf,
230
231
  ...coValue.knownState(),
231
232
  }).catch((e: unknown) => {
232
- console.error("Error sending known state", e);
233
+ logger.error("Error sending known state", e);
233
234
  });
234
235
 
235
236
  peer.toldKnownState.add(id);
@@ -256,15 +257,8 @@ export class SyncManager {
256
257
  const sendPieces = async () => {
257
258
  let lastYield = performance.now();
258
259
  for (const [_i, piece] of newContentPieces.entries()) {
259
- // console.log(
260
- // `${id} -> ${peer.id}: Sending content piece ${i + 1}/${
261
- // newContentPieces.length
262
- // } header: ${!!piece.header}`,
263
- // // Object.values(piece.new).map((s) => s.newTransactions)
264
- // );
265
-
266
260
  this.trySendToPeer(peer, piece).catch((e: unknown) => {
267
- console.error("Error sending content piece", e);
261
+ logger.error("Error sending content piece", e);
268
262
  });
269
263
 
270
264
  if (performance.now() - lastYield > 10) {
@@ -277,7 +271,7 @@ export class SyncManager {
277
271
  };
278
272
 
279
273
  sendPieces().catch((e) => {
280
- console.error("Error sending new content piece, retrying", e);
274
+ logger.error("Error sending new content piece, retrying", e);
281
275
  peer.optimisticKnownStates.dispatch({
282
276
  type: "SET",
283
277
  id,
@@ -337,7 +331,7 @@ export class SyncManager {
337
331
  return;
338
332
  }
339
333
  if (msg === "PingTimeout") {
340
- console.error("Ping timeout from peer", peer.id);
334
+ logger.error("Ping timeout from peer", peer.id);
341
335
  return;
342
336
  }
343
337
  try {
@@ -360,13 +354,13 @@ export class SyncManager {
360
354
  processMessages()
361
355
  .then(() => {
362
356
  if (peer.crashOnClose) {
363
- console.error("Unexepcted close from peer", peer.id);
357
+ logger.warn("Unexepcted close from peer", peer.id);
364
358
  this.local.crashed = new Error("Unexpected close from peer");
365
359
  throw new Error("Unexpected close from peer");
366
360
  }
367
361
  })
368
362
  .catch((e) => {
369
- console.error("Error processing messages from peer", peer.id, e);
363
+ logger.error("Error processing messages from peer", peer.id, e);
370
364
  if (peer.crashOnClose) {
371
365
  this.local.crashed = e;
372
366
  throw new Error(e);
@@ -406,13 +400,13 @@ export class SyncManager {
406
400
  // where we can get informations about the coValue
407
401
  if (msg.header || Object.keys(msg.sessions).length > 0) {
408
402
  entry.loadFromPeers([peer]).catch((e) => {
409
- console.error("Error loading coValue in handleLoad", e);
403
+ logger.error("Error loading coValue in handleLoad", e);
410
404
  });
411
405
  }
412
406
  return;
413
407
  } else {
414
408
  this.local.loadCoValueCore(msg.id, peer.id).catch((e) => {
415
- console.error("Error loading coValue in handleLoad", e);
409
+ logger.error("Error loading coValue in handleLoad", e);
416
410
  });
417
411
  }
418
412
  }
@@ -439,7 +433,7 @@ export class SyncManager {
439
433
  header: false,
440
434
  sessions: {},
441
435
  }).catch((e) => {
442
- console.error("Error sending known state back", e);
436
+ logger.error("Error sending known state back", e);
443
437
  });
444
438
 
445
439
  return;
@@ -449,7 +443,7 @@ export class SyncManager {
449
443
  await this.sendNewContentIncludingDependencies(msg.id, peer);
450
444
  })
451
445
  .catch((e) => {
452
- console.error("Error loading coValue in handleLoad loading state", e);
446
+ logger.error("Error loading coValue in handleLoad loading state", e);
453
447
  });
454
448
  }
455
449
 
@@ -484,7 +478,7 @@ export class SyncManager {
484
478
  peer.role === "storage" ? undefined : peer.id,
485
479
  )
486
480
  .catch((e) => {
487
- console.error(
481
+ logger.error(
488
482
  `Error loading coValue ${msg.id} to create loading state, as dependency of ${msg.asDependencyOf}`,
489
483
  e,
490
484
  );
@@ -521,7 +515,7 @@ export class SyncManager {
521
515
 
522
516
  if (entry.state.type !== "available") {
523
517
  if (!msg.header) {
524
- console.error("Expected header to be sent in first message");
518
+ logger.error("Expected header to be sent in first message");
525
519
  return;
526
520
  }
527
521
 
@@ -584,7 +578,7 @@ export class SyncManager {
584
578
  : t.changes.length,
585
579
  )
586
580
  .reduce((a, b) => a + b, 0);
587
- console.log(
581
+ logger.debug(
588
582
  `Adding incoming transactions took ${(after - before).toFixed(
589
583
  2,
590
584
  )}ms for ${totalTxLength} bytes = bandwidth: ${(
@@ -602,7 +596,7 @@ export class SyncManager {
602
596
  // );
603
597
 
604
598
  if (result.isErr()) {
605
- console.error(
599
+ logger.error(
606
600
  "Failed to add transactions from",
607
601
  peer.id,
608
602
  result.error,
@@ -633,7 +627,7 @@ export class SyncManager {
633
627
  isCorrection: true,
634
628
  ...coValue.knownState(),
635
629
  }).catch((e) => {
636
- console.error("Error sending known state correction", e);
630
+ logger.error("Error sending known state correction", e);
637
631
  });
638
632
  } else {
639
633
  /**
@@ -647,7 +641,7 @@ export class SyncManager {
647
641
  action: "known",
648
642
  ...coValue.knownState(),
649
643
  }).catch((e: unknown) => {
650
- console.error("Error sending known state", e);
644
+ logger.error("Error sending known state", e);
651
645
  });
652
646
  }
653
647
 
@@ -681,9 +675,6 @@ export class SyncManager {
681
675
  const done = new Promise<void>((resolve) => {
682
676
  queueMicrotask(async () => {
683
677
  delete this.requestedSyncs[coValue.id];
684
- // if (entry.nRequestsThisTick >= 2) {
685
- // console.log("Syncing", coValue.id, "for", entry.nRequestsThisTick, "requests");
686
- // }
687
678
  await this.actuallySyncCoValue(coValue);
688
679
  resolve();
689
680
  });
@@ -49,12 +49,9 @@ describe("PeerState", () => {
49
49
 
50
50
  test("should perform graceful shutdown", () => {
51
51
  const { mockPeer, peerState } = setup();
52
- const consoleSpy = vi.spyOn(console, "debug").mockImplementation(() => {});
53
52
  peerState.gracefulShutdown();
54
53
  expect(mockPeer.outgoing.close).toHaveBeenCalled();
55
54
  expect(peerState.closed).toBe(true);
56
- expect(consoleSpy).toHaveBeenCalledWith("Gracefully closing", "test-peer");
57
- consoleSpy.mockRestore();
58
55
  });
59
56
 
60
57
  test("should empty the queue when closing", async () => {
@@ -22,7 +22,7 @@ describe("PriorityBasedMessageQueue", () => {
22
22
  const { queue } = setup();
23
23
  expect(queue["defaultPriority"]).toBe(CO_VALUE_PRIORITY.MEDIUM);
24
24
  expect(queue["queues"].length).toBe(8);
25
- expect(queue["queues"].every((q) => q.length === 0)).toBe(true);
25
+ expect(queue["queues"].every((q) => !q.isNonEmpty())).toBe(true);
26
26
  });
27
27
 
28
28
  test("should push message with default priority", async () => {
@@ -0,0 +1,145 @@
1
+ import { describe, expect, test, vi } from "vitest";
2
+ import { LogLevel, Logger } from "../logger";
3
+
4
+ describe("Logger", () => {
5
+ describe("Log Level Filtering", () => {
6
+ test("should respect log level hierarchy", () => {
7
+ const mockLogSystem = {
8
+ debug: vi.fn(),
9
+ info: vi.fn(),
10
+ warn: vi.fn(),
11
+ error: vi.fn(),
12
+ };
13
+
14
+ const logger = new Logger(LogLevel.WARN, mockLogSystem);
15
+
16
+ logger.debug("Debug message");
17
+ logger.info("Info message");
18
+ logger.warn("Warning message");
19
+ logger.error("Error message");
20
+
21
+ expect(mockLogSystem.debug).not.toHaveBeenCalled();
22
+ expect(mockLogSystem.info).not.toHaveBeenCalled();
23
+ expect(mockLogSystem.warn).toHaveBeenCalledWith("Warning message");
24
+ expect(mockLogSystem.error).toHaveBeenCalledWith("Error message");
25
+ });
26
+
27
+ test("should pass additional arguments to log system", () => {
28
+ const mockLogSystem = {
29
+ debug: vi.fn(),
30
+ info: vi.fn(),
31
+ warn: vi.fn(),
32
+ error: vi.fn(),
33
+ };
34
+
35
+ const logger = new Logger(LogLevel.DEBUG, mockLogSystem);
36
+ const additionalArgs = [{ foo: "bar" }, 42, "extra"];
37
+
38
+ logger.debug("Debug message", ...additionalArgs);
39
+
40
+ expect(mockLogSystem.debug).toHaveBeenCalledWith(
41
+ "Debug message",
42
+ ...additionalArgs,
43
+ );
44
+ });
45
+ });
46
+
47
+ describe("Log System Configuration", () => {
48
+ test("should allow changing log level at runtime", () => {
49
+ const mockLogSystem = {
50
+ debug: vi.fn(),
51
+ info: vi.fn(),
52
+ warn: vi.fn(),
53
+ error: vi.fn(),
54
+ };
55
+
56
+ const logger = new Logger(LogLevel.ERROR, mockLogSystem);
57
+
58
+ logger.warn("Warning 1"); // Should not log
59
+ expect(mockLogSystem.warn).not.toHaveBeenCalled();
60
+
61
+ logger.setLevel(LogLevel.WARN);
62
+ logger.warn("Warning 2"); // Should log
63
+ expect(mockLogSystem.warn).toHaveBeenCalledWith("Warning 2");
64
+ });
65
+
66
+ test("should allow changing log system at runtime", () => {
67
+ const mockLogSystem1 = {
68
+ debug: vi.fn(),
69
+ info: vi.fn(),
70
+ warn: vi.fn(),
71
+ error: vi.fn(),
72
+ };
73
+
74
+ const mockLogSystem2 = {
75
+ debug: vi.fn(),
76
+ info: vi.fn(),
77
+ warn: vi.fn(),
78
+ error: vi.fn(),
79
+ };
80
+
81
+ const logger = new Logger(LogLevel.INFO, mockLogSystem1);
82
+
83
+ logger.info("Message 1");
84
+ expect(mockLogSystem1.info).toHaveBeenCalledWith("Message 1");
85
+ expect(mockLogSystem2.info).not.toHaveBeenCalled();
86
+
87
+ logger.setLogSystem(mockLogSystem2);
88
+ logger.info("Message 2");
89
+ expect(mockLogSystem2.info).toHaveBeenCalledWith("Message 2");
90
+ expect(mockLogSystem1.info).toHaveBeenCalledTimes(1);
91
+ });
92
+ });
93
+
94
+ describe("Default Console Log System", () => {
95
+ test("should use console methods by default", () => {
96
+ const consoleSpy = {
97
+ debug: vi.spyOn(console, "debug").mockImplementation(() => {}),
98
+ info: vi.spyOn(console, "info").mockImplementation(() => {}),
99
+ warn: vi.spyOn(console, "warn").mockImplementation(() => {}),
100
+ error: vi.spyOn(console, "error").mockImplementation(() => {}),
101
+ };
102
+
103
+ const logger = new Logger();
104
+ logger.setLevel(LogLevel.DEBUG);
105
+ const testMessage = "Test message";
106
+ const testArgs = [{ data: "test" }, 123];
107
+
108
+ logger.debug(testMessage, ...testArgs);
109
+ logger.info(testMessage, ...testArgs);
110
+ logger.warn(testMessage, ...testArgs);
111
+ logger.error(testMessage, ...testArgs);
112
+
113
+ expect(consoleSpy.debug).toHaveBeenCalledWith(testMessage, ...testArgs);
114
+ expect(consoleSpy.info).toHaveBeenCalledWith(testMessage, ...testArgs);
115
+ expect(consoleSpy.warn).toHaveBeenCalledWith(testMessage, ...testArgs);
116
+ expect(consoleSpy.error).toHaveBeenCalledWith(testMessage, ...testArgs);
117
+
118
+ // Cleanup
119
+ Object.values(consoleSpy).forEach((spy) => spy.mockRestore());
120
+ });
121
+ });
122
+
123
+ describe("Log Level NONE", () => {
124
+ test("should not log anything when level is NONE", () => {
125
+ const mockLogSystem = {
126
+ debug: vi.fn(),
127
+ info: vi.fn(),
128
+ warn: vi.fn(),
129
+ error: vi.fn(),
130
+ };
131
+
132
+ const logger = new Logger(LogLevel.NONE, mockLogSystem);
133
+
134
+ logger.debug("Debug message");
135
+ logger.info("Info message");
136
+ logger.warn("Warning message");
137
+ logger.error("Error message");
138
+
139
+ expect(mockLogSystem.debug).not.toHaveBeenCalled();
140
+ expect(mockLogSystem.info).not.toHaveBeenCalled();
141
+ expect(mockLogSystem.warn).not.toHaveBeenCalled();
142
+ expect(mockLogSystem.error).not.toHaveBeenCalled();
143
+ });
144
+ });
145
+ });