@logtape/file 1.0.0-dev.236 → 1.0.0-dev.237

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/filesink.test.ts CHANGED
@@ -2,8 +2,10 @@ import { getFileSink, getRotatingFileSink } from "#filesink";
2
2
  import { suite } from "@alinea/suite";
3
3
  import { isDeno } from "@david/which-runtime";
4
4
  import type { Sink } from "@logtape/logtape";
5
+ import { assert } from "@std/assert/assert";
5
6
  import { assertEquals } from "@std/assert/equals";
6
7
  import { assertThrows } from "@std/assert/throws";
8
+ import { delay } from "@std/async/delay";
7
9
  import { join } from "@std/path/join";
8
10
  import fs from "node:fs";
9
11
  import { tmpdir } from "node:os";
@@ -691,4 +693,122 @@ test("getBaseFileSink() with flushInterval disabled", () => {
691
693
  assertEquals(content.includes("Hello, 123 & 456!"), true);
692
694
  });
693
695
 
696
+ test("getFileSink() with nonBlocking mode", async () => {
697
+ const path = makeTempFileSync();
698
+ const sink = getFileSink(path, {
699
+ nonBlocking: true,
700
+ bufferSize: 50, // Small buffer to trigger flush by size
701
+ });
702
+
703
+ // Check that it returns AsyncDisposable
704
+ assert(typeof sink === "function");
705
+ assert(Symbol.asyncDispose in sink);
706
+
707
+ // Add enough records to trigger buffer flush
708
+ sink(debug);
709
+ sink(info);
710
+
711
+ // Wait for async flush to complete
712
+ await delay(50);
713
+ const content = fs.readFileSync(path, { encoding: "utf-8" });
714
+ assert(content.includes("Hello, 123 & 456!"));
715
+
716
+ await (sink as Sink & AsyncDisposable)[Symbol.asyncDispose]();
717
+ });
718
+
719
+ test("getRotatingFileSink() with nonBlocking mode", async () => {
720
+ const path = makeTempFileSync();
721
+ const sink = getRotatingFileSink(path, {
722
+ maxSize: 200,
723
+ nonBlocking: true,
724
+ bufferSize: 1000, // Large buffer to prevent immediate flush
725
+ flushInterval: 50, // Short interval for testing
726
+ });
727
+
728
+ // Check that it returns AsyncDisposable
729
+ assert(typeof sink === "function");
730
+ assert(Symbol.asyncDispose in sink);
731
+
732
+ // Add records with current timestamp
733
+ const record1 = { ...debug, timestamp: Date.now() };
734
+ const record2 = { ...info, timestamp: Date.now() };
735
+ sink(record1);
736
+ sink(record2);
737
+ assertEquals(fs.readFileSync(path, { encoding: "utf-8" }), ""); // Not written yet
738
+
739
+ // Wait for flush interval to pass
740
+ await delay(100);
741
+ const content = fs.readFileSync(path, { encoding: "utf-8" });
742
+ assert(content.includes("Hello, 123 & 456!"));
743
+
744
+ await (sink as Sink & AsyncDisposable)[Symbol.asyncDispose]();
745
+ });
746
+
747
+ test("getFileSink() with nonBlocking high-volume logging", async () => {
748
+ const path = makeTempFileSync();
749
+ const sink = getFileSink(path, {
750
+ nonBlocking: true,
751
+ bufferSize: 50, // Small buffer to trigger flush
752
+ flushInterval: 0, // Disable time-based flushing for this test
753
+ }) as unknown as Sink & AsyncDisposable;
754
+
755
+ // Add enough records to trigger buffer flush (50 chars per record roughly)
756
+ let totalChars = 0;
757
+ let recordCount = 0;
758
+ while (totalChars < 100) { // Exceed buffer size
759
+ sink(debug);
760
+ totalChars += 67; // Approximate length of each debug record
761
+ recordCount++;
762
+ }
763
+
764
+ // Wait for async flush to complete
765
+ await delay(50);
766
+ const content = fs.readFileSync(path, { encoding: "utf-8" });
767
+
768
+ // Should have some records written by now
769
+ const writtenCount = (content.match(/Hello, 123 & 456!/g) || []).length;
770
+ assert(
771
+ writtenCount > 0,
772
+ `Expected some records to be written, but got ${writtenCount}`,
773
+ );
774
+
775
+ await sink[Symbol.asyncDispose]();
776
+ });
777
+
778
+ test("getRotatingFileSink() with nonBlocking rotation", async () => {
779
+ const path = makeTempFileSync();
780
+ const sink = getRotatingFileSink(path, {
781
+ maxSize: 150, // Small size to trigger rotation
782
+ nonBlocking: true,
783
+ bufferSize: 100,
784
+ flushInterval: 10,
785
+ }) as unknown as Sink & AsyncDisposable;
786
+
787
+ // Add enough records to trigger rotation
788
+ sink(debug);
789
+ sink(info);
790
+ sink(warning);
791
+ sink(error);
792
+
793
+ // Wait for all flushes and rotation to complete
794
+ await delay(200);
795
+
796
+ // Check that rotation occurred
797
+ const mainContent = fs.readFileSync(path, { encoding: "utf-8" });
798
+ let rotatedContent = "";
799
+ try {
800
+ rotatedContent = fs.readFileSync(`${path}.1`, { encoding: "utf-8" });
801
+ } catch {
802
+ // No rotation occurred
803
+ }
804
+
805
+ const allContent = mainContent + rotatedContent;
806
+
807
+ // Should have all 4 records somewhere
808
+ const recordCount = (allContent.match(/Hello, 123 & 456!/g) || []).length;
809
+ assertEquals(recordCount, 4);
810
+
811
+ await sink[Symbol.asyncDispose]();
812
+ });
813
+
694
814
  // cSpell: ignore filesink
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logtape/file",
3
- "version": "1.0.0-dev.236+0c0f47bf",
3
+ "version": "1.0.0-dev.237+0615301b",
4
4
  "description": "File sink and rotating file sink for LogTape",
5
5
  "keywords": [
6
6
  "logging",
@@ -50,12 +50,13 @@
50
50
  }
51
51
  },
52
52
  "peerDependencies": {
53
- "@logtape/logtape": "1.0.0-dev.236+0c0f47bf"
53
+ "@logtape/logtape": "1.0.0-dev.237+0615301b"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@alinea/suite": "^0.6.3",
57
57
  "@david/which-runtime": "npm:@jsr/david__which-runtime@^0.2.1",
58
58
  "@std/assert": "npm:@jsr/std__assert@^1.0.13",
59
+ "@std/async": "npm:@jsr/std__async@^1.0.13",
59
60
  "@std/path": "npm:@jsr/std__path@^1.1.0",
60
61
  "tsdown": "^0.12.7",
61
62
  "typescript": "^5.8.3"
package/tsdown.config.ts CHANGED
@@ -12,7 +12,7 @@ export default defineConfig({
12
12
  onLog(level, log, defaultHandler) {
13
13
  if (
14
14
  level === "warn" && log.code === "UNRESOLVED_IMPORT" &&
15
- ["node:fs", "#filesink"].includes(log.exporter ?? "")
15
+ ["node:fs", "node:util", "#filesink"].includes(log.exporter ?? "")
16
16
  ) {
17
17
  return;
18
18
  }