@logtape/file 0.12.0-dev.191 → 0.12.0-dev.194
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/deno.json +1 -1
- package/dist/filesink.base.cjs +29 -9
- package/dist/filesink.base.d.cts +8 -0
- package/dist/filesink.base.d.cts.map +1 -1
- package/dist/filesink.base.d.ts +8 -0
- package/dist/filesink.base.d.ts.map +1 -1
- package/dist/filesink.base.js +29 -9
- package/dist/filesink.base.js.map +1 -1
- package/filesink.base.ts +34 -9
- package/filesink.test.ts +329 -0
- package/package.json +2 -2
package/deno.json
CHANGED
package/dist/filesink.base.cjs
CHANGED
|
@@ -14,14 +14,24 @@ const __logtape_logtape = require_rolldown_runtime.__toESM(require("@logtape/log
|
|
|
14
14
|
function getBaseFileSink(path, options) {
|
|
15
15
|
const formatter = options.formatter ?? __logtape_logtape.defaultTextFormatter;
|
|
16
16
|
const encoder = options.encoder ?? new TextEncoder();
|
|
17
|
+
const bufferSize = options.bufferSize ?? 1024 * 8;
|
|
17
18
|
let fd = options.lazy ? null : options.openSync(path);
|
|
19
|
+
let buffer = "";
|
|
18
20
|
const sink = (record) => {
|
|
19
|
-
if (fd
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
if (fd == null) fd = options.openSync(path);
|
|
22
|
+
buffer += formatter(record);
|
|
23
|
+
if (buffer.length >= bufferSize) {
|
|
24
|
+
options.writeSync(fd, encoder.encode(buffer));
|
|
25
|
+
buffer = "";
|
|
26
|
+
options.flushSync(fd);
|
|
27
|
+
}
|
|
22
28
|
};
|
|
23
29
|
sink[Symbol.dispose] = () => {
|
|
24
|
-
if (fd !== null)
|
|
30
|
+
if (fd !== null) {
|
|
31
|
+
options.writeSync(fd, encoder.encode(buffer));
|
|
32
|
+
options.flushSync(fd);
|
|
33
|
+
options.closeSync(fd);
|
|
34
|
+
}
|
|
25
35
|
};
|
|
26
36
|
return sink;
|
|
27
37
|
}
|
|
@@ -43,6 +53,7 @@ function getBaseRotatingFileSink(path, options) {
|
|
|
43
53
|
const encoder = options.encoder ?? new TextEncoder();
|
|
44
54
|
const maxSize = options.maxSize ?? 1024 * 1024;
|
|
45
55
|
const maxFiles = options.maxFiles ?? 5;
|
|
56
|
+
const bufferSize = options.bufferSize ?? 1024 * 8;
|
|
46
57
|
let offset = 0;
|
|
47
58
|
try {
|
|
48
59
|
const stat = options.statSync(path);
|
|
@@ -65,14 +76,23 @@ function getBaseRotatingFileSink(path, options) {
|
|
|
65
76
|
offset = 0;
|
|
66
77
|
fd = options.openSync(path);
|
|
67
78
|
}
|
|
79
|
+
let buffer = "";
|
|
68
80
|
const sink = (record) => {
|
|
69
|
-
|
|
70
|
-
if (
|
|
71
|
-
|
|
81
|
+
buffer += formatter(record);
|
|
82
|
+
if (buffer.length >= bufferSize) {
|
|
83
|
+
const bytes = encoder.encode(buffer);
|
|
84
|
+
buffer = "";
|
|
85
|
+
if (shouldRollover(bytes)) performRollover();
|
|
86
|
+
options.writeSync(fd, bytes);
|
|
87
|
+
options.flushSync(fd);
|
|
88
|
+
offset += bytes.length;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
sink[Symbol.dispose] = () => {
|
|
92
|
+
options.writeSync(fd, encoder.encode(buffer));
|
|
72
93
|
options.flushSync(fd);
|
|
73
|
-
|
|
94
|
+
options.closeSync(fd);
|
|
74
95
|
};
|
|
75
|
-
sink[Symbol.dispose] = () => options.closeSync(fd);
|
|
76
96
|
return sink;
|
|
77
97
|
}
|
|
78
98
|
|
package/dist/filesink.base.d.cts
CHANGED
|
@@ -10,6 +10,14 @@ type FileSinkOptions = StreamSinkOptions & {
|
|
|
10
10
|
* If `true`, the file is not opened until the first write. Defaults to `false`.
|
|
11
11
|
*/
|
|
12
12
|
lazy?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* The size of the buffer to use when writing to the file. If not specified,
|
|
15
|
+
* a default buffer size will be used. If it is less or equal to 0,
|
|
16
|
+
* the file will be written directly without buffering.
|
|
17
|
+
* @default 8192
|
|
18
|
+
* @since 0.12.0
|
|
19
|
+
*/
|
|
20
|
+
bufferSize?: number;
|
|
13
21
|
};
|
|
14
22
|
/**
|
|
15
23
|
* A platform-specific file sink driver.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filesink.base.d.cts","names":[],"sources":["../filesink.base.ts"],"sourcesContent":[],"mappings":";;;;;;AAUA;
|
|
1
|
+
{"version":3,"file":"filesink.base.d.cts","names":[],"sources":["../filesink.base.ts"],"sourcesContent":[],"mappings":";;;;;;AAUA;AAoBiB,KApBL,eAAA,GAAkB,iBAoBC,GAAA;EAAA;;;EAYV,IAAS,CAAA,EAAA,OAAA;EAAU;;AAYnB;AA2CrB;;;;EAAqD,UAAA,CAAA,EAAA,MAAA;AAerD,CAAA;;;;AAAqE;UAlFpD;;;;;0BAKS;;;;;;gBAOV,cAAc;;;;;gBAMd;;;;;gBAMA;;;;;;;;;;;;;;;UA2CC,uBAAA,SAAgC,KAAK;;;;;;;;;;;;;UAerC,sCAAsC,eAAe"}
|
package/dist/filesink.base.d.ts
CHANGED
|
@@ -10,6 +10,14 @@ type FileSinkOptions = StreamSinkOptions & {
|
|
|
10
10
|
* If `true`, the file is not opened until the first write. Defaults to `false`.
|
|
11
11
|
*/
|
|
12
12
|
lazy?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* The size of the buffer to use when writing to the file. If not specified,
|
|
15
|
+
* a default buffer size will be used. If it is less or equal to 0,
|
|
16
|
+
* the file will be written directly without buffering.
|
|
17
|
+
* @default 8192
|
|
18
|
+
* @since 0.12.0
|
|
19
|
+
*/
|
|
20
|
+
bufferSize?: number;
|
|
13
21
|
};
|
|
14
22
|
/**
|
|
15
23
|
* A platform-specific file sink driver.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filesink.base.d.ts","names":[],"sources":["../filesink.base.ts"],"sourcesContent":[],"mappings":";;;;;;AAUA;
|
|
1
|
+
{"version":3,"file":"filesink.base.d.ts","names":[],"sources":["../filesink.base.ts"],"sourcesContent":[],"mappings":";;;;;;AAUA;AAoBiB,KApBL,eAAA,GAAkB,iBAoBC,GAAA;EAAA;;;EAYV,IAAS,CAAA,EAAA,OAAA;EAAU;;AAYnB;AA2CrB;;;;EAAqD,UAAA,CAAA,EAAA,MAAA;AAerD,CAAA;;;;AAAqE;UAlFpD;;;;;0BAKS;;;;;;gBAOV,cAAc;;;;;gBAMd;;;;;gBAMA;;;;;;;;;;;;;;;UA2CC,uBAAA,SAAgC,KAAK;;;;;;;;;;;;;UAerC,sCAAsC,eAAe"}
|
package/dist/filesink.base.js
CHANGED
|
@@ -13,14 +13,24 @@ import { defaultTextFormatter } from "@logtape/logtape";
|
|
|
13
13
|
function getBaseFileSink(path, options) {
|
|
14
14
|
const formatter = options.formatter ?? defaultTextFormatter;
|
|
15
15
|
const encoder = options.encoder ?? new TextEncoder();
|
|
16
|
+
const bufferSize = options.bufferSize ?? 1024 * 8;
|
|
16
17
|
let fd = options.lazy ? null : options.openSync(path);
|
|
18
|
+
let buffer = "";
|
|
17
19
|
const sink = (record) => {
|
|
18
|
-
if (fd
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
if (fd == null) fd = options.openSync(path);
|
|
21
|
+
buffer += formatter(record);
|
|
22
|
+
if (buffer.length >= bufferSize) {
|
|
23
|
+
options.writeSync(fd, encoder.encode(buffer));
|
|
24
|
+
buffer = "";
|
|
25
|
+
options.flushSync(fd);
|
|
26
|
+
}
|
|
21
27
|
};
|
|
22
28
|
sink[Symbol.dispose] = () => {
|
|
23
|
-
if (fd !== null)
|
|
29
|
+
if (fd !== null) {
|
|
30
|
+
options.writeSync(fd, encoder.encode(buffer));
|
|
31
|
+
options.flushSync(fd);
|
|
32
|
+
options.closeSync(fd);
|
|
33
|
+
}
|
|
24
34
|
};
|
|
25
35
|
return sink;
|
|
26
36
|
}
|
|
@@ -42,6 +52,7 @@ function getBaseRotatingFileSink(path, options) {
|
|
|
42
52
|
const encoder = options.encoder ?? new TextEncoder();
|
|
43
53
|
const maxSize = options.maxSize ?? 1024 * 1024;
|
|
44
54
|
const maxFiles = options.maxFiles ?? 5;
|
|
55
|
+
const bufferSize = options.bufferSize ?? 1024 * 8;
|
|
45
56
|
let offset = 0;
|
|
46
57
|
try {
|
|
47
58
|
const stat = options.statSync(path);
|
|
@@ -64,14 +75,23 @@ function getBaseRotatingFileSink(path, options) {
|
|
|
64
75
|
offset = 0;
|
|
65
76
|
fd = options.openSync(path);
|
|
66
77
|
}
|
|
78
|
+
let buffer = "";
|
|
67
79
|
const sink = (record) => {
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
|
|
80
|
+
buffer += formatter(record);
|
|
81
|
+
if (buffer.length >= bufferSize) {
|
|
82
|
+
const bytes = encoder.encode(buffer);
|
|
83
|
+
buffer = "";
|
|
84
|
+
if (shouldRollover(bytes)) performRollover();
|
|
85
|
+
options.writeSync(fd, bytes);
|
|
86
|
+
options.flushSync(fd);
|
|
87
|
+
offset += bytes.length;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
sink[Symbol.dispose] = () => {
|
|
91
|
+
options.writeSync(fd, encoder.encode(buffer));
|
|
71
92
|
options.flushSync(fd);
|
|
72
|
-
|
|
93
|
+
options.closeSync(fd);
|
|
73
94
|
};
|
|
74
|
-
sink[Symbol.dispose] = () => options.closeSync(fd);
|
|
75
95
|
return sink;
|
|
76
96
|
}
|
|
77
97
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filesink.base.js","names":["path: string","options: FileSinkOptions & FileSinkDriver<TFile>","sink: Sink & Disposable","record: LogRecord","options: RotatingFileSinkOptions & RotatingFileSinkDriver<TFile>","offset: number","bytes: Uint8Array"],"sources":["../filesink.base.ts"],"sourcesContent":["import {\n defaultTextFormatter,\n type LogRecord,\n type Sink,\n type StreamSinkOptions,\n} from \"@logtape/logtape\";\n\n/**\n * Options for the {@link getBaseFileSink} function.\n */\nexport type FileSinkOptions = StreamSinkOptions & {\n /**\n * If `true`, the file is not opened until the first write. Defaults to `false`.\n */\n lazy?: boolean;\n};\n\n/**\n * A platform-specific file sink driver.\n * @typeParam TFile The type of the file descriptor.\n */\nexport interface FileSinkDriver<TFile> {\n /**\n * Open a file for appending and return a file descriptor.\n * @param path A path to the file to open.\n */\n openSync(path: string): TFile;\n\n /**\n * Write a chunk of data to the file.\n * @param fd The file descriptor.\n * @param chunk The data to write.\n */\n writeSync(fd: TFile, chunk: Uint8Array): void;\n\n /**\n * Flush the file to ensure that all data is written to the disk.\n * @param fd The file descriptor.\n */\n flushSync(fd: TFile): void;\n\n /**\n * Close the file.\n * @param fd The file descriptor.\n */\n closeSync(fd: TFile): void;\n}\n\n/**\n * Get a platform-independent file sink.\n *\n * @typeParam TFile The type of the file descriptor.\n * @param path A path to the file to write to.\n * @param options The options for the sink and the file driver.\n * @returns A sink that writes to the file. The sink is also a disposable\n * object that closes the file when disposed.\n */\nexport function getBaseFileSink<TFile>(\n path: string,\n options: FileSinkOptions & FileSinkDriver<TFile>,\n): Sink & Disposable {\n const formatter = options.formatter ?? defaultTextFormatter;\n const encoder = options.encoder ?? new TextEncoder();\n let fd = options.lazy ? null : options.openSync(path);\n const sink: Sink & Disposable = (record: LogRecord) => {\n if (fd
|
|
1
|
+
{"version":3,"file":"filesink.base.js","names":["path: string","options: FileSinkOptions & FileSinkDriver<TFile>","buffer: string","sink: Sink & Disposable","record: LogRecord","options: RotatingFileSinkOptions & RotatingFileSinkDriver<TFile>","offset: number","bytes: Uint8Array"],"sources":["../filesink.base.ts"],"sourcesContent":["import {\n defaultTextFormatter,\n type LogRecord,\n type Sink,\n type StreamSinkOptions,\n} from \"@logtape/logtape\";\n\n/**\n * Options for the {@link getBaseFileSink} function.\n */\nexport type FileSinkOptions = StreamSinkOptions & {\n /**\n * If `true`, the file is not opened until the first write. Defaults to `false`.\n */\n lazy?: boolean;\n\n /**\n * The size of the buffer to use when writing to the file. If not specified,\n * a default buffer size will be used. If it is less or equal to 0,\n * the file will be written directly without buffering.\n * @default 8192\n * @since 0.12.0\n */\n bufferSize?: number;\n};\n\n/**\n * A platform-specific file sink driver.\n * @typeParam TFile The type of the file descriptor.\n */\nexport interface FileSinkDriver<TFile> {\n /**\n * Open a file for appending and return a file descriptor.\n * @param path A path to the file to open.\n */\n openSync(path: string): TFile;\n\n /**\n * Write a chunk of data to the file.\n * @param fd The file descriptor.\n * @param chunk The data to write.\n */\n writeSync(fd: TFile, chunk: Uint8Array): void;\n\n /**\n * Flush the file to ensure that all data is written to the disk.\n * @param fd The file descriptor.\n */\n flushSync(fd: TFile): void;\n\n /**\n * Close the file.\n * @param fd The file descriptor.\n */\n closeSync(fd: TFile): void;\n}\n\n/**\n * Get a platform-independent file sink.\n *\n * @typeParam TFile The type of the file descriptor.\n * @param path A path to the file to write to.\n * @param options The options for the sink and the file driver.\n * @returns A sink that writes to the file. The sink is also a disposable\n * object that closes the file when disposed.\n */\nexport function getBaseFileSink<TFile>(\n path: string,\n options: FileSinkOptions & FileSinkDriver<TFile>,\n): Sink & Disposable {\n const formatter = options.formatter ?? defaultTextFormatter;\n const encoder = options.encoder ?? new TextEncoder();\n const bufferSize = options.bufferSize ?? 1024 * 8; // Default buffer size of 8192 chars\n let fd = options.lazy ? null : options.openSync(path);\n let buffer: string = \"\";\n const sink: Sink & Disposable = (record: LogRecord) => {\n if (fd == null) fd = options.openSync(path);\n buffer += formatter(record);\n if (buffer.length >= bufferSize) {\n options.writeSync(fd, encoder.encode(buffer));\n buffer = \"\";\n options.flushSync(fd);\n }\n };\n sink[Symbol.dispose] = () => {\n if (fd !== null) {\n options.writeSync(fd, encoder.encode(buffer));\n options.flushSync(fd);\n options.closeSync(fd);\n }\n };\n return sink;\n}\n\n/**\n * Options for the {@link getBaseRotatingFileSink} function.\n */\nexport interface RotatingFileSinkOptions extends Omit<FileSinkOptions, \"lazy\"> {\n /**\n * The maximum bytes of the file before it is rotated. 1 MiB by default.\n */\n maxSize?: number;\n\n /**\n * The maximum number of files to keep. 5 by default.\n */\n maxFiles?: number;\n}\n\n/**\n * A platform-specific rotating file sink driver.\n */\nexport interface RotatingFileSinkDriver<TFile> extends FileSinkDriver<TFile> {\n /**\n * Get the size of the file.\n * @param path A path to the file.\n * @returns The `size` of the file in bytes, in an object.\n */\n statSync(path: string): { size: number };\n\n /**\n * Rename a file.\n * @param oldPath A path to the file to rename.\n * @param newPath A path to be renamed to.\n */\n renameSync(oldPath: string, newPath: string): void;\n}\n\n/**\n * Get a platform-independent rotating file sink.\n *\n * This sink writes log records to a file, and rotates the file when it reaches\n * the `maxSize`. The rotated files are named with the original file name\n * followed by a dot and a number, starting from 1. The number is incremented\n * for each rotation, and the maximum number of files to keep is `maxFiles`.\n *\n * @param path A path to the file to write to.\n * @param options The options for the sink and the file driver.\n * @returns A sink that writes to the file. The sink is also a disposable\n * object that closes the file when disposed.\n */\nexport function getBaseRotatingFileSink<TFile>(\n path: string,\n options: RotatingFileSinkOptions & RotatingFileSinkDriver<TFile>,\n): Sink & Disposable {\n const formatter = options.formatter ?? defaultTextFormatter;\n const encoder = options.encoder ?? new TextEncoder();\n const maxSize = options.maxSize ?? 1024 * 1024;\n const maxFiles = options.maxFiles ?? 5;\n const bufferSize = options.bufferSize ?? 1024 * 8; // Default buffer size of 8192 chars\n let offset: number = 0;\n try {\n const stat = options.statSync(path);\n offset = stat.size;\n } catch {\n // Continue as the offset is already 0.\n }\n let fd = options.openSync(path);\n function shouldRollover(bytes: Uint8Array): boolean {\n return offset + bytes.length > maxSize;\n }\n function performRollover(): void {\n options.closeSync(fd);\n for (let i = maxFiles - 1; i > 0; i--) {\n const oldPath = `${path}.${i}`;\n const newPath = `${path}.${i + 1}`;\n try {\n options.renameSync(oldPath, newPath);\n } catch (_) {\n // Continue if the file does not exist.\n }\n }\n options.renameSync(path, `${path}.1`);\n offset = 0;\n fd = options.openSync(path);\n }\n let buffer: string = \"\";\n const sink: Sink & Disposable = (record: LogRecord) => {\n buffer += formatter(record);\n if (buffer.length >= bufferSize) {\n const bytes = encoder.encode(buffer);\n buffer = \"\";\n if (shouldRollover(bytes)) performRollover();\n options.writeSync(fd, bytes);\n options.flushSync(fd);\n offset += bytes.length;\n }\n };\n sink[Symbol.dispose] = () => {\n options.writeSync(fd, encoder.encode(buffer));\n options.flushSync(fd);\n options.closeSync(fd);\n };\n return sink;\n}\n"],"mappings":";;;;;;;;;;;;AAkEA,SAAgB,gBACdA,MACAC,SACmB;CACnB,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,UAAU,QAAQ,WAAW,IAAI;CACvC,MAAM,aAAa,QAAQ,cAAc,OAAO;CAChD,IAAI,KAAK,QAAQ,OAAO,OAAO,QAAQ,SAAS,KAAK;CACrD,IAAIC,SAAiB;CACrB,MAAMC,OAA0B,CAACC,WAAsB;AACrD,MAAI,MAAM,KAAM,MAAK,QAAQ,SAAS,KAAK;AAC3C,YAAU,UAAU,OAAO;AAC3B,MAAI,OAAO,UAAU,YAAY;AAC/B,WAAQ,UAAU,IAAI,QAAQ,OAAO,OAAO,CAAC;AAC7C,YAAS;AACT,WAAQ,UAAU,GAAG;EACtB;CACF;AACD,MAAK,OAAO,WAAW,MAAM;AAC3B,MAAI,OAAO,MAAM;AACf,WAAQ,UAAU,IAAI,QAAQ,OAAO,OAAO,CAAC;AAC7C,WAAQ,UAAU,GAAG;AACrB,WAAQ,UAAU,GAAG;EACtB;CACF;AACD,QAAO;AACR;;;;;;;;;;;;;;AAiDD,SAAgB,wBACdJ,MACAK,SACmB;CACnB,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,UAAU,QAAQ,WAAW,IAAI;CACvC,MAAM,UAAU,QAAQ,WAAW,OAAO;CAC1C,MAAM,WAAW,QAAQ,YAAY;CACrC,MAAM,aAAa,QAAQ,cAAc,OAAO;CAChD,IAAIC,SAAiB;AACrB,KAAI;EACF,MAAM,OAAO,QAAQ,SAAS,KAAK;AACnC,WAAS,KAAK;CACf,QAAO,CAEP;CACD,IAAI,KAAK,QAAQ,SAAS,KAAK;CAC/B,SAAS,eAAeC,OAA4B;AAClD,SAAO,SAAS,MAAM,SAAS;CAChC;CACD,SAAS,kBAAwB;AAC/B,UAAQ,UAAU,GAAG;AACrB,OAAK,IAAI,IAAI,WAAW,GAAG,IAAI,GAAG,KAAK;GACrC,MAAM,WAAW,EAAE,KAAK,GAAG,EAAE;GAC7B,MAAM,WAAW,EAAE,KAAK,GAAG,IAAI,EAAE;AACjC,OAAI;AACF,YAAQ,WAAW,SAAS,QAAQ;GACrC,SAAQ,GAAG,CAEX;EACF;AACD,UAAQ,WAAW,OAAO,EAAE,KAAK,IAAI;AACrC,WAAS;AACT,OAAK,QAAQ,SAAS,KAAK;CAC5B;CACD,IAAIL,SAAiB;CACrB,MAAMC,OAA0B,CAACC,WAAsB;AACrD,YAAU,UAAU,OAAO;AAC3B,MAAI,OAAO,UAAU,YAAY;GAC/B,MAAM,QAAQ,QAAQ,OAAO,OAAO;AACpC,YAAS;AACT,OAAI,eAAe,MAAM,CAAE,kBAAiB;AAC5C,WAAQ,UAAU,IAAI,MAAM;AAC5B,WAAQ,UAAU,GAAG;AACrB,aAAU,MAAM;EACjB;CACF;AACD,MAAK,OAAO,WAAW,MAAM;AAC3B,UAAQ,UAAU,IAAI,QAAQ,OAAO,OAAO,CAAC;AAC7C,UAAQ,UAAU,GAAG;AACrB,UAAQ,UAAU,GAAG;CACtB;AACD,QAAO;AACR"}
|
package/filesink.base.ts
CHANGED
|
@@ -13,6 +13,15 @@ export type FileSinkOptions = StreamSinkOptions & {
|
|
|
13
13
|
* If `true`, the file is not opened until the first write. Defaults to `false`.
|
|
14
14
|
*/
|
|
15
15
|
lazy?: boolean;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The size of the buffer to use when writing to the file. If not specified,
|
|
19
|
+
* a default buffer size will be used. If it is less or equal to 0,
|
|
20
|
+
* the file will be written directly without buffering.
|
|
21
|
+
* @default 8192
|
|
22
|
+
* @since 0.12.0
|
|
23
|
+
*/
|
|
24
|
+
bufferSize?: number;
|
|
16
25
|
};
|
|
17
26
|
|
|
18
27
|
/**
|
|
@@ -61,16 +70,22 @@ export function getBaseFileSink<TFile>(
|
|
|
61
70
|
): Sink & Disposable {
|
|
62
71
|
const formatter = options.formatter ?? defaultTextFormatter;
|
|
63
72
|
const encoder = options.encoder ?? new TextEncoder();
|
|
73
|
+
const bufferSize = options.bufferSize ?? 1024 * 8; // Default buffer size of 8192 chars
|
|
64
74
|
let fd = options.lazy ? null : options.openSync(path);
|
|
75
|
+
let buffer: string = "";
|
|
65
76
|
const sink: Sink & Disposable = (record: LogRecord) => {
|
|
66
|
-
if (fd
|
|
67
|
-
|
|
77
|
+
if (fd == null) fd = options.openSync(path);
|
|
78
|
+
buffer += formatter(record);
|
|
79
|
+
if (buffer.length >= bufferSize) {
|
|
80
|
+
options.writeSync(fd, encoder.encode(buffer));
|
|
81
|
+
buffer = "";
|
|
82
|
+
options.flushSync(fd);
|
|
68
83
|
}
|
|
69
|
-
options.writeSync(fd, encoder.encode(formatter(record)));
|
|
70
|
-
options.flushSync(fd);
|
|
71
84
|
};
|
|
72
85
|
sink[Symbol.dispose] = () => {
|
|
73
86
|
if (fd !== null) {
|
|
87
|
+
options.writeSync(fd, encoder.encode(buffer));
|
|
88
|
+
options.flushSync(fd);
|
|
74
89
|
options.closeSync(fd);
|
|
75
90
|
}
|
|
76
91
|
};
|
|
@@ -132,6 +147,7 @@ export function getBaseRotatingFileSink<TFile>(
|
|
|
132
147
|
const encoder = options.encoder ?? new TextEncoder();
|
|
133
148
|
const maxSize = options.maxSize ?? 1024 * 1024;
|
|
134
149
|
const maxFiles = options.maxFiles ?? 5;
|
|
150
|
+
const bufferSize = options.bufferSize ?? 1024 * 8; // Default buffer size of 8192 chars
|
|
135
151
|
let offset: number = 0;
|
|
136
152
|
try {
|
|
137
153
|
const stat = options.statSync(path);
|
|
@@ -158,13 +174,22 @@ export function getBaseRotatingFileSink<TFile>(
|
|
|
158
174
|
offset = 0;
|
|
159
175
|
fd = options.openSync(path);
|
|
160
176
|
}
|
|
177
|
+
let buffer: string = "";
|
|
161
178
|
const sink: Sink & Disposable = (record: LogRecord) => {
|
|
162
|
-
|
|
163
|
-
if (
|
|
164
|
-
|
|
179
|
+
buffer += formatter(record);
|
|
180
|
+
if (buffer.length >= bufferSize) {
|
|
181
|
+
const bytes = encoder.encode(buffer);
|
|
182
|
+
buffer = "";
|
|
183
|
+
if (shouldRollover(bytes)) performRollover();
|
|
184
|
+
options.writeSync(fd, bytes);
|
|
185
|
+
options.flushSync(fd);
|
|
186
|
+
offset += bytes.length;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
sink[Symbol.dispose] = () => {
|
|
190
|
+
options.writeSync(fd, encoder.encode(buffer));
|
|
165
191
|
options.flushSync(fd);
|
|
166
|
-
|
|
192
|
+
options.closeSync(fd);
|
|
167
193
|
};
|
|
168
|
-
sink[Symbol.dispose] = () => options.closeSync(fd);
|
|
169
194
|
return sink;
|
|
170
195
|
}
|
package/filesink.test.ts
CHANGED
|
@@ -142,10 +142,209 @@ test("getFileSink()", () => {
|
|
|
142
142
|
);
|
|
143
143
|
});
|
|
144
144
|
|
|
145
|
+
test("getFileSink() with bufferSize: 0 (no buffering)", () => {
|
|
146
|
+
const path = makeTempFileSync();
|
|
147
|
+
const sink: Sink & Disposable = getFileSink(path, { bufferSize: 0 });
|
|
148
|
+
|
|
149
|
+
// Write first log entry
|
|
150
|
+
sink(debug);
|
|
151
|
+
// With no buffering, content should be immediately written to file
|
|
152
|
+
assertEquals(
|
|
153
|
+
fs.readFileSync(path, { encoding: "utf-8" }),
|
|
154
|
+
"2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!\n",
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// Write second log entry
|
|
158
|
+
sink(info);
|
|
159
|
+
assertEquals(
|
|
160
|
+
fs.readFileSync(path, { encoding: "utf-8" }),
|
|
161
|
+
`\
|
|
162
|
+
2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!
|
|
163
|
+
2023-11-14 22:13:20.000 +00:00 [INF] my-app·junk: Hello, 123 & 456!
|
|
164
|
+
`,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// Write remaining entries
|
|
168
|
+
sink(warning);
|
|
169
|
+
sink(error);
|
|
170
|
+
sink(fatal);
|
|
171
|
+
sink[Symbol.dispose]();
|
|
172
|
+
|
|
173
|
+
// Final verification
|
|
174
|
+
assertEquals(
|
|
175
|
+
fs.readFileSync(path, { encoding: "utf-8" }),
|
|
176
|
+
`\
|
|
177
|
+
2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!
|
|
178
|
+
2023-11-14 22:13:20.000 +00:00 [INF] my-app·junk: Hello, 123 & 456!
|
|
179
|
+
2023-11-14 22:13:20.000 +00:00 [WRN] my-app·junk: Hello, 123 & 456!
|
|
180
|
+
2023-11-14 22:13:20.000 +00:00 [ERR] my-app·junk: Hello, 123 & 456!
|
|
181
|
+
2023-11-14 22:13:20.000 +00:00 [FTL] my-app·junk: Hello, 123 & 456!
|
|
182
|
+
`,
|
|
183
|
+
);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("getFileSink() with small buffer size", () => {
|
|
187
|
+
const path = makeTempFileSync();
|
|
188
|
+
// Use a small buffer size (100 characters) to test buffering behavior
|
|
189
|
+
const sink: Sink & Disposable = getFileSink(path, { bufferSize: 100 });
|
|
190
|
+
|
|
191
|
+
// Write first log entry (about 65 characters)
|
|
192
|
+
sink(debug);
|
|
193
|
+
// Should be buffered, not yet written to file
|
|
194
|
+
assertEquals(fs.readFileSync(path, { encoding: "utf-8" }), "");
|
|
195
|
+
|
|
196
|
+
// Write second log entry - this should exceed buffer size and trigger flush
|
|
197
|
+
sink(info);
|
|
198
|
+
// Both entries should now be written to file
|
|
199
|
+
assertEquals(
|
|
200
|
+
fs.readFileSync(path, { encoding: "utf-8" }),
|
|
201
|
+
`\
|
|
202
|
+
2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!
|
|
203
|
+
2023-11-14 22:13:20.000 +00:00 [INF] my-app·junk: Hello, 123 & 456!
|
|
204
|
+
`,
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
// Write third log entry - should be buffered again
|
|
208
|
+
sink(warning);
|
|
209
|
+
// Should still only have the first two entries
|
|
210
|
+
assertEquals(
|
|
211
|
+
fs.readFileSync(path, { encoding: "utf-8" }),
|
|
212
|
+
`\
|
|
213
|
+
2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!
|
|
214
|
+
2023-11-14 22:13:20.000 +00:00 [INF] my-app·junk: Hello, 123 & 456!
|
|
215
|
+
`,
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
// Dispose should flush remaining buffer content
|
|
219
|
+
sink[Symbol.dispose]();
|
|
220
|
+
assertEquals(
|
|
221
|
+
fs.readFileSync(path, { encoding: "utf-8" }),
|
|
222
|
+
`\
|
|
223
|
+
2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!
|
|
224
|
+
2023-11-14 22:13:20.000 +00:00 [INF] my-app·junk: Hello, 123 & 456!
|
|
225
|
+
2023-11-14 22:13:20.000 +00:00 [WRN] my-app·junk: Hello, 123 & 456!
|
|
226
|
+
`,
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("getRotatingFileSink() with bufferSize: 0 (no buffering)", () => {
|
|
231
|
+
const path = makeTempFileSync();
|
|
232
|
+
const sink: Sink & Disposable = getRotatingFileSink(path, {
|
|
233
|
+
maxSize: 150,
|
|
234
|
+
bufferSize: 0, // No buffering - immediate writes
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Write first log entry - should be immediately written
|
|
238
|
+
sink(debug);
|
|
239
|
+
assertEquals(
|
|
240
|
+
fs.readFileSync(path, { encoding: "utf-8" }),
|
|
241
|
+
"2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!\n",
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
// Write second log entry - should be immediately written
|
|
245
|
+
sink(info);
|
|
246
|
+
assertEquals(
|
|
247
|
+
fs.readFileSync(path, { encoding: "utf-8" }),
|
|
248
|
+
`\
|
|
249
|
+
2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!
|
|
250
|
+
2023-11-14 22:13:20.000 +00:00 [INF] my-app·junk: Hello, 123 & 456!
|
|
251
|
+
`,
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
// Write third log entry - should trigger rotation
|
|
255
|
+
sink(warning);
|
|
256
|
+
assertEquals(
|
|
257
|
+
fs.readFileSync(path, { encoding: "utf-8" }),
|
|
258
|
+
"2023-11-14 22:13:20.000 +00:00 [WRN] my-app·junk: Hello, 123 & 456!\n",
|
|
259
|
+
);
|
|
260
|
+
// Check that rotation occurred
|
|
261
|
+
assertEquals(
|
|
262
|
+
fs.readFileSync(`${path}.1`, { encoding: "utf-8" }),
|
|
263
|
+
`\
|
|
264
|
+
2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!
|
|
265
|
+
2023-11-14 22:13:20.000 +00:00 [INF] my-app·junk: Hello, 123 & 456!
|
|
266
|
+
`,
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
sink[Symbol.dispose]();
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test("getRotatingFileSink() with small buffer size", () => {
|
|
273
|
+
const path = makeTempFileSync();
|
|
274
|
+
const sink: Sink & Disposable = getRotatingFileSink(path, {
|
|
275
|
+
maxSize: 200, // Larger maxSize to allow for buffering tests
|
|
276
|
+
bufferSize: 100, // Small buffer to test interaction with rotation
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Write first log entry - should be buffered
|
|
280
|
+
sink(debug);
|
|
281
|
+
assertEquals(fs.readFileSync(path, { encoding: "utf-8" }), "");
|
|
282
|
+
|
|
283
|
+
// Write second log entry - should trigger buffer flush
|
|
284
|
+
sink(info);
|
|
285
|
+
assertEquals(
|
|
286
|
+
fs.readFileSync(path, { encoding: "utf-8" }),
|
|
287
|
+
`\
|
|
288
|
+
2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!
|
|
289
|
+
2023-11-14 22:13:20.000 +00:00 [INF] my-app·junk: Hello, 123 & 456!
|
|
290
|
+
`,
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
// Write third log entry - should be buffered again
|
|
294
|
+
sink(warning);
|
|
295
|
+
assertEquals(
|
|
296
|
+
fs.readFileSync(path, { encoding: "utf-8" }),
|
|
297
|
+
`\
|
|
298
|
+
2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!
|
|
299
|
+
2023-11-14 22:13:20.000 +00:00 [INF] my-app·junk: Hello, 123 & 456!
|
|
300
|
+
`,
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
// Write fourth log entry - should flush buffer and likely trigger rotation
|
|
304
|
+
sink(error);
|
|
305
|
+
|
|
306
|
+
// Dispose should flush any remaining buffer content
|
|
307
|
+
sink[Symbol.dispose]();
|
|
308
|
+
|
|
309
|
+
// Verify final state - all entries should be written somewhere
|
|
310
|
+
const mainContent = fs.readFileSync(path, { encoding: "utf-8" });
|
|
311
|
+
let rotatedContent = "";
|
|
312
|
+
try {
|
|
313
|
+
rotatedContent = fs.readFileSync(`${path}.1`, { encoding: "utf-8" });
|
|
314
|
+
} catch {
|
|
315
|
+
// No rotation occurred
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const allContent = mainContent + rotatedContent;
|
|
319
|
+
// All four log entries should be present exactly once in either main or rotated file
|
|
320
|
+
const expectedEntries = [
|
|
321
|
+
"2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!\n",
|
|
322
|
+
"2023-11-14 22:13:20.000 +00:00 [INF] my-app·junk: Hello, 123 & 456!\n",
|
|
323
|
+
"2023-11-14 22:13:20.000 +00:00 [WRN] my-app·junk: Hello, 123 & 456!\n",
|
|
324
|
+
"2023-11-14 22:13:20.000 +00:00 [ERR] my-app·junk: Hello, 123 & 456!\n",
|
|
325
|
+
];
|
|
326
|
+
|
|
327
|
+
for (const entry of expectedEntries) {
|
|
328
|
+
assertEquals(
|
|
329
|
+
allContent.includes(entry),
|
|
330
|
+
true,
|
|
331
|
+
`Missing log entry: ${entry.trim()}`,
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Verify each entry appears exactly once
|
|
336
|
+
for (const entry of expectedEntries) {
|
|
337
|
+
const firstIndex = allContent.indexOf(entry);
|
|
338
|
+
const lastIndex = allContent.lastIndexOf(entry);
|
|
339
|
+
assertEquals(firstIndex, lastIndex, `Duplicate log entry: ${entry.trim()}`);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
145
343
|
test("getRotatingFileSink()", () => {
|
|
146
344
|
const path = makeTempFileSync();
|
|
147
345
|
const sink: Sink & Disposable = getRotatingFileSink(path, {
|
|
148
346
|
maxSize: 150,
|
|
347
|
+
bufferSize: 0, // Disable buffering for this test to maintain existing behavior
|
|
149
348
|
});
|
|
150
349
|
sink(debug);
|
|
151
350
|
assertEquals(
|
|
@@ -212,6 +411,7 @@ test("getRotatingFileSink()", () => {
|
|
|
212
411
|
const path2 = join(dirPath, "log");
|
|
213
412
|
const sink2: Sink & Disposable = getRotatingFileSink(path2, {
|
|
214
413
|
maxSize: 150,
|
|
414
|
+
bufferSize: 0, // Disable buffering for this test to maintain existing behavior
|
|
215
415
|
});
|
|
216
416
|
sink2(debug);
|
|
217
417
|
assertEquals(
|
|
@@ -221,4 +421,133 @@ test("getRotatingFileSink()", () => {
|
|
|
221
421
|
sink2[Symbol.dispose]();
|
|
222
422
|
});
|
|
223
423
|
|
|
424
|
+
test("getBaseFileSink() with buffer edge cases", () => {
|
|
425
|
+
// Test negative bufferSize (should behave like bufferSize: 0)
|
|
426
|
+
const path1 = makeTempFileSync();
|
|
427
|
+
let sink1: Sink & Disposable;
|
|
428
|
+
if (isDeno) {
|
|
429
|
+
const driver: FileSinkDriver<Deno.FsFile> = {
|
|
430
|
+
openSync(path: string) {
|
|
431
|
+
return Deno.openSync(path, { create: true, append: true });
|
|
432
|
+
},
|
|
433
|
+
writeSync(fd, chunk) {
|
|
434
|
+
fd.writeSync(chunk);
|
|
435
|
+
},
|
|
436
|
+
flushSync(fd) {
|
|
437
|
+
fd.syncSync();
|
|
438
|
+
},
|
|
439
|
+
closeSync(fd) {
|
|
440
|
+
fd.close();
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
sink1 = getBaseFileSink(path1, { ...driver, bufferSize: -10 });
|
|
444
|
+
} else {
|
|
445
|
+
const driver: FileSinkDriver<number> = {
|
|
446
|
+
openSync(path: string) {
|
|
447
|
+
return fs.openSync(path, "a");
|
|
448
|
+
},
|
|
449
|
+
writeSync: fs.writeSync,
|
|
450
|
+
flushSync: fs.fsyncSync,
|
|
451
|
+
closeSync: fs.closeSync,
|
|
452
|
+
};
|
|
453
|
+
sink1 = getBaseFileSink(path1, { ...driver, bufferSize: -10 });
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
sink1(debug);
|
|
457
|
+
// With negative bufferSize, should write immediately
|
|
458
|
+
assertEquals(
|
|
459
|
+
fs.readFileSync(path1, { encoding: "utf-8" }),
|
|
460
|
+
"2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!\n",
|
|
461
|
+
);
|
|
462
|
+
sink1[Symbol.dispose]();
|
|
463
|
+
|
|
464
|
+
// Test bufferSize of 1 (very small)
|
|
465
|
+
const path2 = makeTempFileSync();
|
|
466
|
+
let sink2: Sink & Disposable;
|
|
467
|
+
if (isDeno) {
|
|
468
|
+
const driver: FileSinkDriver<Deno.FsFile> = {
|
|
469
|
+
openSync(path: string) {
|
|
470
|
+
return Deno.openSync(path, { create: true, append: true });
|
|
471
|
+
},
|
|
472
|
+
writeSync(fd, chunk) {
|
|
473
|
+
fd.writeSync(chunk);
|
|
474
|
+
},
|
|
475
|
+
flushSync(fd) {
|
|
476
|
+
fd.syncSync();
|
|
477
|
+
},
|
|
478
|
+
closeSync(fd) {
|
|
479
|
+
fd.close();
|
|
480
|
+
},
|
|
481
|
+
};
|
|
482
|
+
sink2 = getBaseFileSink(path2, { ...driver, bufferSize: 1 });
|
|
483
|
+
} else {
|
|
484
|
+
const driver: FileSinkDriver<number> = {
|
|
485
|
+
openSync(path: string) {
|
|
486
|
+
return fs.openSync(path, "a");
|
|
487
|
+
},
|
|
488
|
+
writeSync: fs.writeSync,
|
|
489
|
+
flushSync: fs.fsyncSync,
|
|
490
|
+
closeSync: fs.closeSync,
|
|
491
|
+
};
|
|
492
|
+
sink2 = getBaseFileSink(path2, { ...driver, bufferSize: 1 });
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
sink2(debug);
|
|
496
|
+
// With bufferSize of 1, should write immediately since log entry > 1 char
|
|
497
|
+
assertEquals(
|
|
498
|
+
fs.readFileSync(path2, { encoding: "utf-8" }),
|
|
499
|
+
"2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!\n",
|
|
500
|
+
);
|
|
501
|
+
sink2[Symbol.dispose]();
|
|
502
|
+
|
|
503
|
+
// Test very large bufferSize
|
|
504
|
+
const path3 = makeTempFileSync();
|
|
505
|
+
let sink3: Sink & Disposable;
|
|
506
|
+
if (isDeno) {
|
|
507
|
+
const driver: FileSinkDriver<Deno.FsFile> = {
|
|
508
|
+
openSync(path: string) {
|
|
509
|
+
return Deno.openSync(path, { create: true, append: true });
|
|
510
|
+
},
|
|
511
|
+
writeSync(fd, chunk) {
|
|
512
|
+
fd.writeSync(chunk);
|
|
513
|
+
},
|
|
514
|
+
flushSync(fd) {
|
|
515
|
+
fd.syncSync();
|
|
516
|
+
},
|
|
517
|
+
closeSync(fd) {
|
|
518
|
+
fd.close();
|
|
519
|
+
},
|
|
520
|
+
};
|
|
521
|
+
sink3 = getBaseFileSink(path3, { ...driver, bufferSize: 10000 });
|
|
522
|
+
} else {
|
|
523
|
+
const driver: FileSinkDriver<number> = {
|
|
524
|
+
openSync(path: string) {
|
|
525
|
+
return fs.openSync(path, "a");
|
|
526
|
+
},
|
|
527
|
+
writeSync: fs.writeSync,
|
|
528
|
+
flushSync: fs.fsyncSync,
|
|
529
|
+
closeSync: fs.closeSync,
|
|
530
|
+
};
|
|
531
|
+
sink3 = getBaseFileSink(path3, { ...driver, bufferSize: 10000 });
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Write multiple entries that shouldn't exceed the large buffer
|
|
535
|
+
sink3(debug);
|
|
536
|
+
sink3(info);
|
|
537
|
+
sink3(warning);
|
|
538
|
+
// Should still be buffered (file empty)
|
|
539
|
+
assertEquals(fs.readFileSync(path3, { encoding: "utf-8" }), "");
|
|
540
|
+
|
|
541
|
+
// Dispose should flush all buffered content
|
|
542
|
+
sink3[Symbol.dispose]();
|
|
543
|
+
assertEquals(
|
|
544
|
+
fs.readFileSync(path3, { encoding: "utf-8" }),
|
|
545
|
+
`\
|
|
546
|
+
2023-11-14 22:13:20.000 +00:00 [DBG] my-app·junk: Hello, 123 & 456!
|
|
547
|
+
2023-11-14 22:13:20.000 +00:00 [INF] my-app·junk: Hello, 123 & 456!
|
|
548
|
+
2023-11-14 22:13:20.000 +00:00 [WRN] my-app·junk: Hello, 123 & 456!
|
|
549
|
+
`,
|
|
550
|
+
);
|
|
551
|
+
});
|
|
552
|
+
|
|
224
553
|
// cSpell: ignore filesink
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logtape/file",
|
|
3
|
-
"version": "0.12.0-dev.
|
|
3
|
+
"version": "0.12.0-dev.194+4b7c1127",
|
|
4
4
|
"description": "File sink and rotating file sink for LogTape",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"logging",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"@logtape/logtape": "0.12.0-dev.
|
|
53
|
+
"@logtape/logtape": "0.12.0-dev.194+4b7c1127"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@david/which-runtime": "npm:@jsr/david__which-runtime@^0.2.1",
|