@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/deno.json +1 -1
- package/dist/filesink.base.cjs +129 -51
- package/dist/filesink.base.d.cts +51 -3
- package/dist/filesink.base.d.cts.map +1 -1
- package/dist/filesink.base.d.ts +51 -3
- package/dist/filesink.base.d.ts.map +1 -1
- package/dist/filesink.base.js +129 -51
- package/dist/filesink.base.js.map +1 -1
- package/dist/filesink.deno.cjs +20 -23
- package/dist/filesink.deno.d.cts +17 -4
- package/dist/filesink.deno.d.cts.map +1 -1
- package/dist/filesink.deno.d.ts +17 -4
- package/dist/filesink.deno.d.ts.map +1 -1
- package/dist/filesink.deno.js +20 -24
- package/dist/filesink.deno.js.map +1 -1
- package/dist/filesink.node.cjs +17 -23
- package/dist/filesink.node.d.cts +17 -4
- package/dist/filesink.node.d.cts.map +1 -1
- package/dist/filesink.node.d.ts +17 -4
- package/dist/filesink.node.d.ts.map +1 -1
- package/dist/filesink.node.js +17 -24
- package/dist/filesink.node.js.map +1 -1
- package/filesink.base.ts +250 -30
- package/filesink.deno.ts +43 -4
- package/filesink.jsr.ts +32 -7
- package/filesink.node.ts +40 -4
- package/filesink.test.ts +120 -0
- package/package.json +3 -2
- package/tsdown.config.ts +1 -1
package/filesink.base.ts
CHANGED
|
@@ -31,6 +31,16 @@ export type FileSinkOptions = StreamSinkOptions & {
|
|
|
31
31
|
* @since 0.12.0
|
|
32
32
|
*/
|
|
33
33
|
flushInterval?: number;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Enable non-blocking mode with background flushing.
|
|
37
|
+
* When enabled, flush operations are performed asynchronously to prevent
|
|
38
|
+
* blocking the main thread during file I/O operations.
|
|
39
|
+
*
|
|
40
|
+
* @default `false`
|
|
41
|
+
* @since 1.0.0
|
|
42
|
+
*/
|
|
43
|
+
nonBlocking?: boolean;
|
|
34
44
|
};
|
|
35
45
|
|
|
36
46
|
/**
|
|
@@ -64,6 +74,25 @@ export interface FileSinkDriver<TFile> {
|
|
|
64
74
|
closeSync(fd: TFile): void;
|
|
65
75
|
}
|
|
66
76
|
|
|
77
|
+
/**
|
|
78
|
+
* A platform-specific async file sink driver.
|
|
79
|
+
* @typeParam TFile The type of the file descriptor.
|
|
80
|
+
* @since 1.0.0
|
|
81
|
+
*/
|
|
82
|
+
export interface AsyncFileSinkDriver<TFile> extends FileSinkDriver<TFile> {
|
|
83
|
+
/**
|
|
84
|
+
* Asynchronously flush the file to ensure that all data is written to the disk.
|
|
85
|
+
* @param fd The file descriptor.
|
|
86
|
+
*/
|
|
87
|
+
flush(fd: TFile): Promise<void>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Asynchronously close the file.
|
|
91
|
+
* @param fd The file descriptor.
|
|
92
|
+
*/
|
|
93
|
+
close(fd: TFile): Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
|
|
67
96
|
/**
|
|
68
97
|
* Get a platform-independent file sink.
|
|
69
98
|
*
|
|
@@ -71,12 +100,23 @@ export interface FileSinkDriver<TFile> {
|
|
|
71
100
|
* @param path A path to the file to write to.
|
|
72
101
|
* @param options The options for the sink and the file driver.
|
|
73
102
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
74
|
-
* object that closes the file when disposed.
|
|
103
|
+
* object that closes the file when disposed. If `nonBlocking` is enabled,
|
|
104
|
+
* returns a sink that also implements {@link AsyncDisposable}.
|
|
75
105
|
*/
|
|
76
106
|
export function getBaseFileSink<TFile>(
|
|
77
107
|
path: string,
|
|
78
108
|
options: FileSinkOptions & FileSinkDriver<TFile>,
|
|
79
|
-
): Sink & Disposable
|
|
109
|
+
): Sink & Disposable;
|
|
110
|
+
export function getBaseFileSink<TFile>(
|
|
111
|
+
path: string,
|
|
112
|
+
options: FileSinkOptions & AsyncFileSinkDriver<TFile>,
|
|
113
|
+
): Sink & AsyncDisposable;
|
|
114
|
+
export function getBaseFileSink<TFile>(
|
|
115
|
+
path: string,
|
|
116
|
+
options:
|
|
117
|
+
& FileSinkOptions
|
|
118
|
+
& (FileSinkDriver<TFile> | AsyncFileSinkDriver<TFile>),
|
|
119
|
+
): Sink & (Disposable | AsyncDisposable) {
|
|
80
120
|
const formatter = options.formatter ?? defaultTextFormatter;
|
|
81
121
|
const encoder = options.encoder ?? new TextEncoder();
|
|
82
122
|
const bufferSize = options.bufferSize ?? 1024 * 8; // Default buffer size of 8192 chars
|
|
@@ -85,18 +125,79 @@ export function getBaseFileSink<TFile>(
|
|
|
85
125
|
let buffer: string = "";
|
|
86
126
|
let lastFlushTimestamp: number = Date.now();
|
|
87
127
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
128
|
+
if (!options.nonBlocking) {
|
|
129
|
+
// Blocking mode implementation
|
|
130
|
+
// deno-lint-ignore no-inner-declarations
|
|
131
|
+
function flushBuffer(): void {
|
|
132
|
+
if (fd == null) return;
|
|
133
|
+
if (buffer.length > 0) {
|
|
134
|
+
options.writeSync(fd, encoder.encode(buffer));
|
|
135
|
+
buffer = "";
|
|
136
|
+
options.flushSync(fd);
|
|
137
|
+
lastFlushTimestamp = Date.now();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const sink: Sink & Disposable = (record: LogRecord) => {
|
|
142
|
+
if (fd == null) fd = options.openSync(path);
|
|
143
|
+
buffer += formatter(record);
|
|
144
|
+
|
|
145
|
+
const shouldFlushBySize = buffer.length >= bufferSize;
|
|
146
|
+
const shouldFlushByTime = flushInterval > 0 &&
|
|
147
|
+
(record.timestamp - lastFlushTimestamp) >= flushInterval;
|
|
148
|
+
|
|
149
|
+
if (shouldFlushBySize || shouldFlushByTime) {
|
|
150
|
+
flushBuffer();
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
sink[Symbol.dispose] = () => {
|
|
154
|
+
if (fd !== null) {
|
|
155
|
+
flushBuffer();
|
|
156
|
+
options.closeSync(fd);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
return sink;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Non-blocking mode implementation
|
|
163
|
+
const asyncOptions = options as AsyncFileSinkDriver<TFile>;
|
|
164
|
+
let disposed = false;
|
|
165
|
+
let activeFlush: Promise<void> | null = null;
|
|
166
|
+
let flushTimer: ReturnType<typeof setInterval> | null = null;
|
|
167
|
+
|
|
168
|
+
async function flushBuffer(): Promise<void> {
|
|
169
|
+
if (fd == null || buffer.length === 0) return;
|
|
170
|
+
|
|
171
|
+
const data = buffer;
|
|
172
|
+
buffer = "";
|
|
173
|
+
try {
|
|
174
|
+
asyncOptions.writeSync(fd, encoder.encode(data));
|
|
175
|
+
await asyncOptions.flush(fd);
|
|
94
176
|
lastFlushTimestamp = Date.now();
|
|
177
|
+
} catch {
|
|
178
|
+
// Silently ignore errors in non-blocking mode
|
|
95
179
|
}
|
|
96
180
|
}
|
|
97
181
|
|
|
98
|
-
|
|
99
|
-
if (
|
|
182
|
+
function scheduleFlush(): void {
|
|
183
|
+
if (activeFlush || disposed) return;
|
|
184
|
+
|
|
185
|
+
activeFlush = flushBuffer().finally(() => {
|
|
186
|
+
activeFlush = null;
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function startFlushTimer(): void {
|
|
191
|
+
if (flushTimer !== null || disposed) return;
|
|
192
|
+
|
|
193
|
+
flushTimer = setInterval(() => {
|
|
194
|
+
scheduleFlush();
|
|
195
|
+
}, flushInterval);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const nonBlockingSink: Sink & AsyncDisposable = (record: LogRecord) => {
|
|
199
|
+
if (disposed) return;
|
|
200
|
+
if (fd == null) fd = asyncOptions.openSync(path);
|
|
100
201
|
buffer += formatter(record);
|
|
101
202
|
|
|
102
203
|
const shouldFlushBySize = buffer.length >= bufferSize;
|
|
@@ -104,16 +205,29 @@ export function getBaseFileSink<TFile>(
|
|
|
104
205
|
(record.timestamp - lastFlushTimestamp) >= flushInterval;
|
|
105
206
|
|
|
106
207
|
if (shouldFlushBySize || shouldFlushByTime) {
|
|
107
|
-
|
|
208
|
+
scheduleFlush();
|
|
209
|
+
} else if (flushTimer === null && flushInterval > 0) {
|
|
210
|
+
startFlushTimer();
|
|
108
211
|
}
|
|
109
212
|
};
|
|
110
|
-
|
|
213
|
+
|
|
214
|
+
nonBlockingSink[Symbol.asyncDispose] = async () => {
|
|
215
|
+
disposed = true;
|
|
216
|
+
if (flushTimer !== null) {
|
|
217
|
+
clearInterval(flushTimer);
|
|
218
|
+
flushTimer = null;
|
|
219
|
+
}
|
|
220
|
+
await flushBuffer();
|
|
111
221
|
if (fd !== null) {
|
|
112
|
-
|
|
113
|
-
|
|
222
|
+
try {
|
|
223
|
+
await asyncOptions.close(fd);
|
|
224
|
+
} catch {
|
|
225
|
+
// Writer might already be closed or errored
|
|
226
|
+
}
|
|
114
227
|
}
|
|
115
228
|
};
|
|
116
|
-
|
|
229
|
+
|
|
230
|
+
return nonBlockingSink;
|
|
117
231
|
}
|
|
118
232
|
|
|
119
233
|
/**
|
|
@@ -150,6 +264,27 @@ export interface RotatingFileSinkDriver<TFile> extends FileSinkDriver<TFile> {
|
|
|
150
264
|
renameSync(oldPath: string, newPath: string): void;
|
|
151
265
|
}
|
|
152
266
|
|
|
267
|
+
/**
|
|
268
|
+
* A platform-specific async rotating file sink driver.
|
|
269
|
+
* @since 1.0.0
|
|
270
|
+
*/
|
|
271
|
+
export interface AsyncRotatingFileSinkDriver<TFile>
|
|
272
|
+
extends AsyncFileSinkDriver<TFile> {
|
|
273
|
+
/**
|
|
274
|
+
* Get the size of the file.
|
|
275
|
+
* @param path A path to the file.
|
|
276
|
+
* @returns The `size` of the file in bytes, in an object.
|
|
277
|
+
*/
|
|
278
|
+
statSync(path: string): { size: number };
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Rename a file.
|
|
282
|
+
* @param oldPath A path to the file to rename.
|
|
283
|
+
* @param newPath A path to be renamed to.
|
|
284
|
+
*/
|
|
285
|
+
renameSync(oldPath: string, newPath: string): void;
|
|
286
|
+
}
|
|
287
|
+
|
|
153
288
|
/**
|
|
154
289
|
* Get a platform-independent rotating file sink.
|
|
155
290
|
*
|
|
@@ -161,12 +296,23 @@ export interface RotatingFileSinkDriver<TFile> extends FileSinkDriver<TFile> {
|
|
|
161
296
|
* @param path A path to the file to write to.
|
|
162
297
|
* @param options The options for the sink and the file driver.
|
|
163
298
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
164
|
-
* object that closes the file when disposed.
|
|
299
|
+
* object that closes the file when disposed. If `nonBlocking` is enabled,
|
|
300
|
+
* returns a sink that also implements {@link AsyncDisposable}.
|
|
165
301
|
*/
|
|
166
302
|
export function getBaseRotatingFileSink<TFile>(
|
|
167
303
|
path: string,
|
|
168
304
|
options: RotatingFileSinkOptions & RotatingFileSinkDriver<TFile>,
|
|
169
|
-
): Sink & Disposable
|
|
305
|
+
): Sink & Disposable;
|
|
306
|
+
export function getBaseRotatingFileSink<TFile>(
|
|
307
|
+
path: string,
|
|
308
|
+
options: RotatingFileSinkOptions & AsyncRotatingFileSinkDriver<TFile>,
|
|
309
|
+
): Sink & AsyncDisposable;
|
|
310
|
+
export function getBaseRotatingFileSink<TFile>(
|
|
311
|
+
path: string,
|
|
312
|
+
options:
|
|
313
|
+
& RotatingFileSinkOptions
|
|
314
|
+
& (RotatingFileSinkDriver<TFile> | AsyncRotatingFileSinkDriver<TFile>),
|
|
315
|
+
): Sink & (Disposable | AsyncDisposable) {
|
|
170
316
|
const formatter = options.formatter ?? defaultTextFormatter;
|
|
171
317
|
const encoder = options.encoder ?? new TextEncoder();
|
|
172
318
|
const maxSize = options.maxSize ?? 1024 * 1024;
|
|
@@ -182,6 +328,7 @@ export function getBaseRotatingFileSink<TFile>(
|
|
|
182
328
|
}
|
|
183
329
|
let fd = options.openSync(path);
|
|
184
330
|
let lastFlushTimestamp: number = Date.now();
|
|
331
|
+
let buffer: string = "";
|
|
185
332
|
|
|
186
333
|
function shouldRollover(bytes: Uint8Array): boolean {
|
|
187
334
|
return offset + bytes.length > maxSize;
|
|
@@ -202,20 +349,80 @@ export function getBaseRotatingFileSink<TFile>(
|
|
|
202
349
|
fd = options.openSync(path);
|
|
203
350
|
}
|
|
204
351
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
352
|
+
if (!options.nonBlocking) {
|
|
353
|
+
// Blocking mode implementation
|
|
354
|
+
// deno-lint-ignore no-inner-declarations
|
|
355
|
+
function flushBuffer(): void {
|
|
356
|
+
if (buffer.length > 0) {
|
|
357
|
+
const bytes = encoder.encode(buffer);
|
|
358
|
+
buffer = "";
|
|
359
|
+
if (shouldRollover(bytes)) performRollover();
|
|
360
|
+
options.writeSync(fd, bytes);
|
|
361
|
+
options.flushSync(fd);
|
|
362
|
+
offset += bytes.length;
|
|
363
|
+
lastFlushTimestamp = Date.now();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const sink: Sink & Disposable = (record: LogRecord) => {
|
|
368
|
+
buffer += formatter(record);
|
|
369
|
+
|
|
370
|
+
const shouldFlushBySize = buffer.length >= bufferSize;
|
|
371
|
+
const shouldFlushByTime = flushInterval > 0 &&
|
|
372
|
+
(record.timestamp - lastFlushTimestamp) >= flushInterval;
|
|
373
|
+
|
|
374
|
+
if (shouldFlushBySize || shouldFlushByTime) {
|
|
375
|
+
flushBuffer();
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
sink[Symbol.dispose] = () => {
|
|
379
|
+
flushBuffer();
|
|
380
|
+
options.closeSync(fd);
|
|
381
|
+
};
|
|
382
|
+
return sink;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Non-blocking mode implementation
|
|
386
|
+
const asyncOptions = options as AsyncRotatingFileSinkDriver<TFile>;
|
|
387
|
+
let disposed = false;
|
|
388
|
+
let activeFlush: Promise<void> | null = null;
|
|
389
|
+
let flushTimer: ReturnType<typeof setInterval> | null = null;
|
|
390
|
+
|
|
391
|
+
async function flushBuffer(): Promise<void> {
|
|
392
|
+
if (buffer.length === 0) return;
|
|
393
|
+
|
|
394
|
+
const data = buffer;
|
|
395
|
+
buffer = "";
|
|
396
|
+
try {
|
|
397
|
+
const bytes = encoder.encode(data);
|
|
209
398
|
if (shouldRollover(bytes)) performRollover();
|
|
210
|
-
|
|
211
|
-
|
|
399
|
+
asyncOptions.writeSync(fd, bytes);
|
|
400
|
+
await asyncOptions.flush(fd);
|
|
212
401
|
offset += bytes.length;
|
|
213
402
|
lastFlushTimestamp = Date.now();
|
|
403
|
+
} catch {
|
|
404
|
+
// Silently ignore errors in non-blocking mode
|
|
214
405
|
}
|
|
215
406
|
}
|
|
216
407
|
|
|
217
|
-
|
|
218
|
-
|
|
408
|
+
function scheduleFlush(): void {
|
|
409
|
+
if (activeFlush || disposed) return;
|
|
410
|
+
|
|
411
|
+
activeFlush = flushBuffer().finally(() => {
|
|
412
|
+
activeFlush = null;
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function startFlushTimer(): void {
|
|
417
|
+
if (flushTimer !== null || disposed) return;
|
|
418
|
+
|
|
419
|
+
flushTimer = setInterval(() => {
|
|
420
|
+
scheduleFlush();
|
|
421
|
+
}, flushInterval);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const nonBlockingSink: Sink & AsyncDisposable = (record: LogRecord) => {
|
|
425
|
+
if (disposed) return;
|
|
219
426
|
buffer += formatter(record);
|
|
220
427
|
|
|
221
428
|
const shouldFlushBySize = buffer.length >= bufferSize;
|
|
@@ -223,12 +430,25 @@ export function getBaseRotatingFileSink<TFile>(
|
|
|
223
430
|
(record.timestamp - lastFlushTimestamp) >= flushInterval;
|
|
224
431
|
|
|
225
432
|
if (shouldFlushBySize || shouldFlushByTime) {
|
|
226
|
-
|
|
433
|
+
scheduleFlush();
|
|
434
|
+
} else if (flushTimer === null && flushInterval > 0) {
|
|
435
|
+
startFlushTimer();
|
|
227
436
|
}
|
|
228
437
|
};
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
438
|
+
|
|
439
|
+
nonBlockingSink[Symbol.asyncDispose] = async () => {
|
|
440
|
+
disposed = true;
|
|
441
|
+
if (flushTimer !== null) {
|
|
442
|
+
clearInterval(flushTimer);
|
|
443
|
+
flushTimer = null;
|
|
444
|
+
}
|
|
445
|
+
await flushBuffer();
|
|
446
|
+
try {
|
|
447
|
+
await asyncOptions.close(fd);
|
|
448
|
+
} catch {
|
|
449
|
+
// Writer might already be closed or errored
|
|
450
|
+
}
|
|
232
451
|
};
|
|
233
|
-
|
|
452
|
+
|
|
453
|
+
return nonBlockingSink;
|
|
234
454
|
}
|
package/filesink.deno.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Sink } from "@logtape/logtape";
|
|
2
2
|
import {
|
|
3
|
+
type AsyncRotatingFileSinkDriver,
|
|
3
4
|
type FileSinkOptions,
|
|
4
5
|
getBaseFileSink,
|
|
5
6
|
getBaseRotatingFileSink,
|
|
@@ -27,6 +28,20 @@ export const denoDriver: RotatingFileSinkDriver<Deno.FsFile> = {
|
|
|
27
28
|
renameSync: globalThis?.Deno.renameSync,
|
|
28
29
|
};
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* A Deno-specific async file sink driver.
|
|
33
|
+
* @since 1.0.0
|
|
34
|
+
*/
|
|
35
|
+
export const denoAsyncDriver: AsyncRotatingFileSinkDriver<Deno.FsFile> = {
|
|
36
|
+
...denoDriver,
|
|
37
|
+
async flush(fd) {
|
|
38
|
+
await fd.sync();
|
|
39
|
+
},
|
|
40
|
+
close(fd) {
|
|
41
|
+
return Promise.resolve(fd.close());
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
30
45
|
/**
|
|
31
46
|
* Get a file sink.
|
|
32
47
|
*
|
|
@@ -35,12 +50,24 @@ export const denoDriver: RotatingFileSinkDriver<Deno.FsFile> = {
|
|
|
35
50
|
* @param path A path to the file to write to.
|
|
36
51
|
* @param options The options for the sink.
|
|
37
52
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
38
|
-
* object that closes the file when disposed.
|
|
53
|
+
* object that closes the file when disposed. If `nonBlocking` is enabled,
|
|
54
|
+
* returns a sink that also implements {@link AsyncDisposable}.
|
|
39
55
|
*/
|
|
56
|
+
export function getFileSink(
|
|
57
|
+
path: string,
|
|
58
|
+
options?: FileSinkOptions,
|
|
59
|
+
): Sink & Disposable;
|
|
60
|
+
export function getFileSink(
|
|
61
|
+
path: string,
|
|
62
|
+
options: FileSinkOptions & { nonBlocking: true },
|
|
63
|
+
): Sink & AsyncDisposable;
|
|
40
64
|
export function getFileSink(
|
|
41
65
|
path: string,
|
|
42
66
|
options: FileSinkOptions = {},
|
|
43
|
-
): Sink & Disposable {
|
|
67
|
+
): Sink & (Disposable | AsyncDisposable) {
|
|
68
|
+
if (options.nonBlocking) {
|
|
69
|
+
return getBaseFileSink(path, { ...options, ...denoAsyncDriver });
|
|
70
|
+
}
|
|
44
71
|
return getBaseFileSink(path, { ...options, ...denoDriver });
|
|
45
72
|
}
|
|
46
73
|
|
|
@@ -57,12 +84,24 @@ export function getFileSink(
|
|
|
57
84
|
* @param path A path to the file to write to.
|
|
58
85
|
* @param options The options for the sink and the file driver.
|
|
59
86
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
60
|
-
* object that closes the file when disposed.
|
|
87
|
+
* object that closes the file when disposed. If `nonBlocking` is enabled,
|
|
88
|
+
* returns a sink that also implements {@link AsyncDisposable}.
|
|
61
89
|
*/
|
|
90
|
+
export function getRotatingFileSink(
|
|
91
|
+
path: string,
|
|
92
|
+
options?: RotatingFileSinkOptions,
|
|
93
|
+
): Sink & Disposable;
|
|
94
|
+
export function getRotatingFileSink(
|
|
95
|
+
path: string,
|
|
96
|
+
options: RotatingFileSinkOptions & { nonBlocking: true },
|
|
97
|
+
): Sink & AsyncDisposable;
|
|
62
98
|
export function getRotatingFileSink(
|
|
63
99
|
path: string,
|
|
64
100
|
options: RotatingFileSinkOptions = {},
|
|
65
|
-
): Sink & Disposable {
|
|
101
|
+
): Sink & (Disposable | AsyncDisposable) {
|
|
102
|
+
if (options.nonBlocking) {
|
|
103
|
+
return getBaseRotatingFileSink(path, { ...options, ...denoAsyncDriver });
|
|
104
|
+
}
|
|
66
105
|
return getBaseRotatingFileSink(path, { ...options, ...denoDriver });
|
|
67
106
|
}
|
|
68
107
|
|
package/filesink.jsr.ts
CHANGED
|
@@ -4,7 +4,10 @@ import type {
|
|
|
4
4
|
RotatingFileSinkOptions,
|
|
5
5
|
} from "./filesink.base.ts";
|
|
6
6
|
|
|
7
|
-
const filesink: Omit<
|
|
7
|
+
const filesink: Omit<
|
|
8
|
+
typeof import("./filesink.deno.ts"),
|
|
9
|
+
"denoDriver" | "denoAsyncDriver"
|
|
10
|
+
> =
|
|
8
11
|
// dnt-shim-ignore
|
|
9
12
|
await ("Deno" in globalThis
|
|
10
13
|
? import("./filesink.deno.ts")
|
|
@@ -18,13 +21,24 @@ const filesink: Omit<typeof import("./filesink.deno.ts"), "denoDriver"> =
|
|
|
18
21
|
* @param path A path to the file to write to.
|
|
19
22
|
* @param options The options for the sink.
|
|
20
23
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
21
|
-
* object that closes the file when disposed.
|
|
24
|
+
* object that closes the file when disposed. If `nonBlocking` is enabled,
|
|
25
|
+
* returns a sink that also implements {@link AsyncDisposable}.
|
|
22
26
|
*/
|
|
27
|
+
export function getFileSink(
|
|
28
|
+
path: string,
|
|
29
|
+
options?: FileSinkOptions,
|
|
30
|
+
): Sink & Disposable;
|
|
31
|
+
export function getFileSink(
|
|
32
|
+
path: string,
|
|
33
|
+
options: FileSinkOptions & { nonBlocking: true },
|
|
34
|
+
): Sink & AsyncDisposable;
|
|
23
35
|
export function getFileSink(
|
|
24
36
|
path: string,
|
|
25
37
|
options: FileSinkOptions = {},
|
|
26
|
-
): Sink & Disposable {
|
|
27
|
-
return filesink.getFileSink(path, options)
|
|
38
|
+
): Sink & (Disposable | AsyncDisposable) {
|
|
39
|
+
return filesink.getFileSink(path, options) as
|
|
40
|
+
& Sink
|
|
41
|
+
& (Disposable | AsyncDisposable);
|
|
28
42
|
}
|
|
29
43
|
|
|
30
44
|
/**
|
|
@@ -40,13 +54,24 @@ export function getFileSink(
|
|
|
40
54
|
* @param path A path to the file to write to.
|
|
41
55
|
* @param options The options for the sink and the file driver.
|
|
42
56
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
43
|
-
* object that closes the file when disposed.
|
|
57
|
+
* object that closes the file when disposed. If `nonBlocking` is enabled,
|
|
58
|
+
* returns a sink that also implements {@link AsyncDisposable}.
|
|
44
59
|
*/
|
|
60
|
+
export function getRotatingFileSink(
|
|
61
|
+
path: string,
|
|
62
|
+
options?: RotatingFileSinkOptions,
|
|
63
|
+
): Sink & Disposable;
|
|
64
|
+
export function getRotatingFileSink(
|
|
65
|
+
path: string,
|
|
66
|
+
options: RotatingFileSinkOptions & { nonBlocking: true },
|
|
67
|
+
): Sink & AsyncDisposable;
|
|
45
68
|
export function getRotatingFileSink(
|
|
46
69
|
path: string,
|
|
47
70
|
options: RotatingFileSinkOptions = {},
|
|
48
|
-
): Sink & Disposable {
|
|
49
|
-
return filesink.getRotatingFileSink(path, options)
|
|
71
|
+
): Sink & (Disposable | AsyncDisposable) {
|
|
72
|
+
return filesink.getRotatingFileSink(path, options) as
|
|
73
|
+
& Sink
|
|
74
|
+
& (Disposable | AsyncDisposable);
|
|
50
75
|
}
|
|
51
76
|
|
|
52
77
|
// cSpell: ignore filesink
|
package/filesink.node.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Sink } from "@logtape/logtape";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
+
import { promisify } from "node:util";
|
|
3
4
|
import {
|
|
5
|
+
type AsyncRotatingFileSinkDriver,
|
|
4
6
|
type FileSinkOptions,
|
|
5
7
|
getBaseFileSink,
|
|
6
8
|
getBaseRotatingFileSink,
|
|
@@ -22,6 +24,16 @@ export const nodeDriver: RotatingFileSinkDriver<number | void> = {
|
|
|
22
24
|
renameSync: fs.renameSync,
|
|
23
25
|
};
|
|
24
26
|
|
|
27
|
+
/**
|
|
28
|
+
* A Node.js-specific async file sink driver.
|
|
29
|
+
* @since 1.0.0
|
|
30
|
+
*/
|
|
31
|
+
export const nodeAsyncDriver: AsyncRotatingFileSinkDriver<number | void> = {
|
|
32
|
+
...nodeDriver,
|
|
33
|
+
flush: promisify(fs.fsync),
|
|
34
|
+
close: promisify(fs.close),
|
|
35
|
+
};
|
|
36
|
+
|
|
25
37
|
/**
|
|
26
38
|
* Get a file sink.
|
|
27
39
|
*
|
|
@@ -30,12 +42,24 @@ export const nodeDriver: RotatingFileSinkDriver<number | void> = {
|
|
|
30
42
|
* @param path A path to the file to write to.
|
|
31
43
|
* @param options The options for the sink.
|
|
32
44
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
33
|
-
* object that closes the file when disposed.
|
|
45
|
+
* object that closes the file when disposed. If `nonBlocking` is enabled,
|
|
46
|
+
* returns a sink that also implements {@link AsyncDisposable}.
|
|
34
47
|
*/
|
|
48
|
+
export function getFileSink(
|
|
49
|
+
path: string,
|
|
50
|
+
options?: FileSinkOptions,
|
|
51
|
+
): Sink & Disposable;
|
|
52
|
+
export function getFileSink(
|
|
53
|
+
path: string,
|
|
54
|
+
options: FileSinkOptions & { nonBlocking: true },
|
|
55
|
+
): Sink & AsyncDisposable;
|
|
35
56
|
export function getFileSink(
|
|
36
57
|
path: string,
|
|
37
58
|
options: FileSinkOptions = {},
|
|
38
|
-
): Sink & Disposable {
|
|
59
|
+
): Sink & (Disposable | AsyncDisposable) {
|
|
60
|
+
if (options.nonBlocking) {
|
|
61
|
+
return getBaseFileSink(path, { ...options, ...nodeAsyncDriver });
|
|
62
|
+
}
|
|
39
63
|
return getBaseFileSink(path, { ...options, ...nodeDriver });
|
|
40
64
|
}
|
|
41
65
|
|
|
@@ -52,12 +76,24 @@ export function getFileSink(
|
|
|
52
76
|
* @param path A path to the file to write to.
|
|
53
77
|
* @param options The options for the sink and the file driver.
|
|
54
78
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
55
|
-
* object that closes the file when disposed.
|
|
79
|
+
* object that closes the file when disposed. If `nonBlocking` is enabled,
|
|
80
|
+
* returns a sink that also implements {@link AsyncDisposable}.
|
|
56
81
|
*/
|
|
82
|
+
export function getRotatingFileSink(
|
|
83
|
+
path: string,
|
|
84
|
+
options?: RotatingFileSinkOptions,
|
|
85
|
+
): Sink & Disposable;
|
|
86
|
+
export function getRotatingFileSink(
|
|
87
|
+
path: string,
|
|
88
|
+
options: RotatingFileSinkOptions & { nonBlocking: true },
|
|
89
|
+
): Sink & AsyncDisposable;
|
|
57
90
|
export function getRotatingFileSink(
|
|
58
91
|
path: string,
|
|
59
92
|
options: RotatingFileSinkOptions = {},
|
|
60
|
-
): Sink & Disposable {
|
|
93
|
+
): Sink & (Disposable | AsyncDisposable) {
|
|
94
|
+
if (options.nonBlocking) {
|
|
95
|
+
return getBaseRotatingFileSink(path, { ...options, ...nodeAsyncDriver });
|
|
96
|
+
}
|
|
61
97
|
return getBaseRotatingFileSink(path, { ...options, ...nodeDriver });
|
|
62
98
|
}
|
|
63
99
|
|