New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

rotating-file-stream

Package Overview
Dependencies
Maintainers
2
Versions
68
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rotating-file-stream - npm Package Compare versions

Comparing version 2.1.6 to 3.0.0

3

CHANGELOG.md

@@ -0,1 +1,4 @@

- 2021-11-04 - v3.0.0
- Released v3 - please check the [README.md](https://www.npmjs.com/package/rotating-file-stream#upgrading-from-v2-to-v3)
- devDependencies update
- 2021-09-26 - v2.1.6

@@ -2,0 +5,0 @@ - Made the [package compliant with Node.js v14 and v16](https://github.com/iccicci/rotating-file-stream/issues/63)

53

index.d.ts
/// <reference types="node" />
import { Writable } from "stream";
import { Readable, Writable } from "stream";
export declare type Compressor = (source: string, dest: string) => string;
export declare type Generator = (time: number | Date, index?: number) => string;
interface RotatingFileStreamEvents {
close: () => void;
drain: () => void;
error: (err: Error) => void;
finish: () => void;
pipe: (src: Readable) => void;
unpipe: (src: Readable) => void;
external: (stdout: string, stderr: string) => void;
history: () => void;
open: (filename: string) => void;
removed: (filename: string, number: boolean) => void;
rotation: () => void;
rotated: (filename: string) => void;
warning: (error: Error) => void;
}
export declare interface RotatingFileStream extends Writable {
addListener<Event extends keyof RotatingFileStreamEvents>(event: Event, listener: RotatingFileStreamEvents[Event]): this;
emit<Event extends keyof RotatingFileStreamEvents>(event: Event, ...args: Parameters<RotatingFileStreamEvents[Event]>): boolean;
on<Event extends keyof RotatingFileStreamEvents>(event: Event, listener: RotatingFileStreamEvents[Event]): this;
once<Event extends keyof RotatingFileStreamEvents>(event: Event, listener: RotatingFileStreamEvents[Event]): this;
prependListener<Event extends keyof RotatingFileStreamEvents>(event: Event, listener: RotatingFileStreamEvents[Event]): this;
prependOnceListener<Event extends keyof RotatingFileStreamEvents>(event: Event, listener: RotatingFileStreamEvents[Event]): this;
removeListener<Event extends keyof RotatingFileStreamEvents>(event: Event, listener: RotatingFileStreamEvents[Event]): this;
}
export interface Options {

@@ -16,2 +40,3 @@ compress?: boolean | string | Compressor;

mode?: number;
omitExtension?: boolean;
path?: string;

@@ -36,2 +61,3 @@ rotate?: number;

mode?: number;
omitExtension?: boolean;
path?: string;

@@ -49,30 +75,23 @@ rotate?: number;

private createGzip;
private destroyer;
private error;
private exec;
private file;
private filename;
private finished;
private fsClose;
private fsCreateReadStream;
private fsCreateWriteStream;
private fsMkdir;
private fsOpen;
private fsReadFile;
private fsRename;
private fsStat;
private fsUnlink;
private fsWrite;
private fsWriteFile;
private generator;
private initPromise;
private last;
private maxTimeout;
private next;
private opened;
private options;
private prev;
private rotatedName;
private rotation;
private size;
private stream;
private timer;
private stdout;
private timeout;
private timeoutPromise;
constructor(generator: Generator, options: Opts);

@@ -83,4 +102,4 @@ _destroy(error: Error, callback: Callback): void;

_writev(chunks: Chunk[], callback: Callback): void;
private refinal;
private rewrite;
private writeToStdOut;
private init;

@@ -101,11 +120,5 @@ private makePath;

private compress;
private external;
private gzip;
private rotated;
private history;
private historyGather;
private historyRemove;
private historyCheckFiles;
private historyCheckSize;
private historyWrite;
private immutate;

@@ -112,0 +125,0 @@ }

@@ -8,5 +8,8 @@ "use strict";

const fs_1 = require("fs");
const promises_1 = require("fs/promises");
const path_1 = require("path");
const util_1 = require("util");
const timers_1 = require("timers");
async function exists(filename) {
return new Promise(resolve => (0, fs_1.access)(filename, fs_1.constants.F_OK, error => resolve(!error)));
}
class RotatingFileStreamError extends Error {

@@ -25,16 +28,11 @@ constructor() {

this.filename = path + generator(null);
this.fsClose = fs_1.close;
this.fsCreateReadStream = fs_1.createReadStream;
this.fsCreateWriteStream = fs_1.createWriteStream;
this.fsMkdir = fs_1.mkdir;
this.fsOpen = fs_1.open;
this.fsReadFile = fs_1.readFile;
this.fsRename = fs_1.rename;
this.fsStat = fs_1.stat;
this.fsUnlink = fs_1.unlink;
this.fsWrite = fs_1.write;
this.fsWriteFile = fs_1.writeFile;
this.fsOpen = promises_1.open;
this.fsReadFile = promises_1.readFile;
this.fsStat = promises_1.stat;
this.generator = generator;
this.maxTimeout = 2147483640;
this.options = options;
this.stdout = process.stdout;
if (maxFiles || maxSize)

@@ -44,25 +42,19 @@ options.history = path + (history ? history : this.generator(null) + ".txt");

this.on("finish", () => (this.finished = this.clear()));
process.nextTick(() => this.init(error => {
this.error = error;
if (this.opened)
this.opened();
else if (this.error)
this.emit("error", error);
}));
// In v15 was introduced the _constructor method to delay any _write(), _final() and _destroy() calls
// Untill v16 will be not deprecated we still need this.initPromise
// https://nodejs.org/api/stream.html#stream_writable_construct_callback
(async () => {
try {
this.initPromise = this.init();
await this.initPromise;
delete this.initPromise;
}
catch (e) { }
})();
}
_destroy(error, callback) {
const destroyer = () => {
this.clear();
this.reclose(() => { });
};
if (this.stream)
destroyer();
else
this.destroyer = destroyer;
callback(error);
this.refinal(error, callback);
}
_final(callback) {
if (this.stream)
return this.stream.end(callback);
callback();
this.refinal(undefined, callback);
}

@@ -75,121 +67,95 @@ _write(chunk, encoding, callback) {

}
rewrite(chunks, index, callback) {
const destroy = (error) => {
this.destroy(error);
return callback();
};
const rewrite = () => {
if (this.destroyed)
return callback(this.error);
if (this.error)
return destroy(this.error);
if (!this.stream) {
this.opened = rewrite;
return;
async refinal(error, callback) {
try {
this.clear();
if (this.initPromise)
await this.initPromise;
if (this.timeoutPromise)
await this.timeoutPromise;
await this.reclose();
}
catch (e) {
return callback(error || e);
}
callback(error);
}
async rewrite(chunks, index, callback) {
const { size, teeToStdout } = this.options;
try {
if (this.initPromise)
await this.initPromise;
if (this.timeoutPromise)
await this.timeoutPromise;
for (let i = 0; i < chunks.length; ++i) {
const { chunk } = chunks[i];
this.size += chunk.length;
await this.file.write(chunk);
if (teeToStdout && !this.stdout.destroyed)
this.stdout.write(chunk);
if (size && this.size >= size)
await this.rotate();
}
const done = (error) => {
if (error)
return destroy(error);
if (++index !== chunks.length)
return this.rewrite(chunks, index, callback);
callback();
};
this.size += chunks[index].chunk.length;
this.stream.write(chunks[index].chunk, chunks[index].encoding, (error) => {
if (error)
return done(error);
if (this.options.size && this.size >= this.options.size)
return this.rotate(done);
done();
});
if (this.options.teeToStdout && !process.stdout.destroyed)
this.writeToStdOut(chunks[index].chunk, chunks[index].encoding);
};
if (this.stream) {
return this.fsStat(this.filename, (error) => {
if (!error)
return rewrite();
if (error.code !== "ENOENT")
return destroy(error);
this.reclose(() => this.reopen(false, 0, () => rewrite()));
});
}
this.opened = rewrite;
catch (e) {
return callback(e);
}
callback();
}
writeToStdOut(buffer, encoding) {
process.stdout.write(buffer, encoding);
}
init(callback) {
async init() {
const { immutable, initialRotation, interval, size } = this.options;
// In v15 was introduced the _constructor method to delay any _write(), _final() and _destroy() calls
// Once v16 will be deprecated we can restore only following line
// if(immutable) return this.immutate(true);
if (immutable)
return this.immutate(true, callback);
this.fsStat(this.filename, (error, stats) => {
if (error)
return error.code === "ENOENT" ? this.reopen(false, 0, callback) : callback(error);
if (!stats.isFile())
return callback(new Error(`Can't write on: ${this.filename} (it is not a file)`));
if (initialRotation) {
this.intervalBounds(this.now());
const prev = this.prev;
this.intervalBounds(new Date(stats.mtime.getTime()));
if (prev !== this.prev)
return this.rotate(callback);
}
this.size = stats.size;
if (!size || stats.size < size)
return this.reopen(false, stats.size, callback);
if (interval)
this.intervalBounds(this.now());
this.rotate(callback);
});
return new Promise((resolve, reject) => process.nextTick(() => this.immutate(true).then(resolve).catch(reject)));
let stats;
try {
stats = await (0, promises_1.stat)(this.filename);
}
catch (e) {
if (e.code !== "ENOENT")
throw e;
return this.reopen(0);
}
if (!stats.isFile())
throw new Error(`Can't write on: ${this.filename} (it is not a file)`);
if (initialRotation) {
this.intervalBounds(this.now());
const prev = this.prev;
this.intervalBounds(new Date(stats.mtime.getTime()));
if (prev !== this.prev)
return this.rotate();
}
this.size = stats.size;
if (!size || stats.size < size)
return this.reopen(stats.size);
if (interval)
this.intervalBounds(this.now());
return this.rotate();
}
makePath(name, callback) {
const dir = (0, path_1.parse)(name).dir;
this.fsMkdir(dir, (error) => {
if (error) {
if (error.code === "ENOENT")
return this.makePath(dir, (error) => (error ? callback(error) : this.makePath(name, callback)));
if (error.code === "EEXIST")
return callback();
return callback(error);
}
callback();
});
async makePath(name) {
return (0, promises_1.mkdir)(name.split(path_1.sep).slice(0, -1).join(path_1.sep), { recursive: true });
}
reopen(retry, size, callback) {
const options = { flags: "a" };
if ("mode" in this.options)
options.mode = this.options.mode;
let called;
const stream = this.fsCreateWriteStream(this.filename, options);
const end = (error) => {
if (called) {
this.error = error;
return;
}
called = true;
this.stream = stream;
if (this.opened) {
process.nextTick(this.opened);
this.opened = null;
}
if (this.destroyer)
process.nextTick(this.destroyer);
callback(error);
};
stream.once("open", () => {
this.size = size;
end();
this.interval();
this.emit("open", this.filename);
});
stream.once("error", (error) => error.code !== "ENOENT" || retry ? end(error) : this.makePath(this.filename, (error) => (error ? end(error) : this.reopen(true, size, callback))));
async reopen(size) {
let file;
try {
file = await (0, promises_1.open)(this.filename, "a", this.options.mode);
}
catch (e) {
if (e.code !== "ENOENT")
throw e;
await this.makePath(this.filename);
file = await (0, promises_1.open)(this.filename, "a", this.options.mode);
}
this.file = file;
this.size = size;
this.interval();
this.emit("open", this.filename);
}
reclose(callback) {
const { stream } = this;
if (!stream)
return callback();
this.stream = null;
stream.once("finish", callback);
stream.end();
async reclose() {
const { file } = this;
if (!file)
return;
delete this.file;
return file.close();
}

@@ -199,3 +165,3 @@ now() {

}
rotate(callback) {
async rotate() {
const { immutable, rotate } = this.options;

@@ -205,118 +171,73 @@ this.size = 0;

this.clear();
this.reclose(() => (rotate ? this.classical(rotate, callback) : immutable ? this.immutate(false, callback) : this.move(callback)));
this.emit("rotation");
await this.reclose();
if (rotate)
return this.classical();
if (immutable)
return this.immutate(false);
return this.move();
}
findName(tmp, callback, index) {
if (!index)
index = 1;
async findName() {
const { interval, path, intervalBoundary } = this.options;
let filename = `${this.filename}.${index}.rfs.tmp`;
if (index >= 1000)
return callback(new RotatingFileStreamError());
if (!tmp) {
try {
filename = path + this.generator(interval && intervalBoundary ? new Date(this.prev) : this.rotation, index);
}
catch (e) {
return callback(e);
}
for (let index = 1; index < 1000; ++index) {
const filename = path + this.generator(interval && intervalBoundary ? new Date(this.prev) : this.rotation, index);
if (!(await exists(filename)))
return filename;
}
this.fsStat(filename, error => {
if (!error || error.code !== "ENOENT")
return this.findName(tmp, callback, index + 1);
callback(null, filename);
});
throw new RotatingFileStreamError();
}
move(callback) {
async move() {
const { compress } = this.options;
let filename;
const open = (error) => {
if (error)
return callback(error);
this.rotated(filename, callback);
};
this.findName(false, (error, found) => {
if (error)
return callback(error);
filename = found;
this.touch(filename, false, (error) => {
if (error)
return callback(error);
if (compress)
return this.compress(filename, open);
this.fsRename(this.filename, filename, open);
});
});
const filename = await this.findName();
await this.touch(filename);
if (compress)
await this.compress(filename);
else
await (0, promises_1.rename)(this.filename, filename);
return this.rotated(filename);
}
touch(filename, retry, callback) {
this.fsOpen(filename, "a", parseInt("666", 8), (error, fd) => {
if (error) {
if (error.code !== "ENOENT" || retry)
return callback(error);
return this.makePath(filename, error => {
if (error)
return callback(error);
this.touch(filename, true, callback);
});
}
return this.fsClose(fd, (error) => {
if (error)
return callback(error);
this.fsUnlink(filename, (error) => {
if (error)
this.emit("warning", error);
callback();
});
});
});
}
classical(count, callback) {
const { compress, path, rotate } = this.options;
let prevName;
let thisName;
if (rotate === count)
delete this.rotatedName;
const open = (error) => {
if (error)
return callback(error);
this.rotated(this.rotatedName, callback);
};
async touch(filename) {
let file;
try {
prevName = count === 1 ? this.filename : path + this.generator(count - 1);
thisName = path + this.generator(count);
file = await this.fsOpen(filename, "a");
}
catch (e) {
return callback(e);
if (e.code !== "ENOENT")
throw e;
await this.makePath(filename);
file = await (0, promises_1.open)(filename, "a");
}
const next = count === 1 ? open : () => this.classical(count - 1, callback);
const move = () => {
await file.close();
return (0, promises_1.unlink)(filename);
}
async classical() {
const { compress, path, rotate } = this.options;
let rotatedName = "";
for (let count = rotate; count > 0; --count) {
const currName = path + this.generator(count);
const prevName = count === 1 ? this.filename : path + this.generator(count - 1);
if (!(await exists(prevName)))
continue;
if (!rotatedName)
rotatedName = currName;
if (count === 1 && compress)
return this.compress(thisName, open);
this.fsRename(prevName, thisName, (error) => {
if (!error)
return next();
if (error.code !== "ENOENT")
return callback(error);
this.makePath(thisName, (error) => {
if (error)
return callback(error);
this.fsRename(prevName, thisName, (error) => (error ? callback(error) : next()));
});
});
};
this.fsStat(prevName, (error) => {
if (error) {
if (error.code !== "ENOENT")
return callback(error);
return next();
await this.compress(currName);
else {
try {
await (0, promises_1.rename)(prevName, currName);
}
catch (e) {
if (e.code !== "ENOENT")
throw e;
await this.makePath(currName);
await (0, promises_1.rename)(prevName, currName);
}
}
if (!this.rotatedName)
this.rotatedName = thisName;
move();
});
}
return this.rotated(rotatedName);
}
clear() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}

@@ -365,63 +286,36 @@ return true;

this.intervalBounds(this.now());
const set = () => {
const set = async () => {
const time = this.next - this.now().getTime();
if (time <= 0)
return this.rotate(error => (this.error = error));
this.timer = (0, timers_1.setTimeout)(set, time > this.maxTimeout ? this.maxTimeout : time);
this.timer.unref();
if (time <= 0) {
try {
this.timeoutPromise = this.rotate();
await this.timeoutPromise;
delete this.timeoutPromise;
}
catch (e) { }
}
else {
this.timeout = setTimeout(set, time > this.maxTimeout ? this.maxTimeout : time);
this.timeout.unref();
}
};
set();
}
compress(filename, callback) {
async compress(filename) {
const { compress } = this.options;
const done = (error) => {
if (error)
return callback(error);
this.fsUnlink(this.filename, callback);
};
if (typeof compress === "function")
this.external(filename, done);
else
this.gzip(filename, done);
}
external(filename, callback) {
const compress = this.options.compress;
let cont;
try {
cont = compress(this.filename, filename);
}
catch (e) {
return callback(e);
}
this.findName(true, (error, found) => {
if (error)
return callback(error);
this.fsOpen(found, "w", 0o777, (error, fd) => {
if (error)
return callback(error);
const unlink = (error) => {
this.fsUnlink(found, (error2) => {
if (error2)
this.emit("warning", error2);
callback(error);
});
};
this.fsWrite(fd, cont, (error) => {
this.fsClose(fd, (error2) => {
if (error) {
if (error2)
this.emit("warning", error2);
return unlink(error);
}
if (error2)
return unlink(error2);
if (found.indexOf(path_1.sep) === -1)
found = `.${path_1.sep}${found}`;
this.exec(`sh "${found}"`, unlink);
});
if (typeof compress === "function") {
await new Promise((resolve, reject) => {
this.exec(compress(this.filename, filename), (error, stdout, stderr) => {
if (error)
return reject(error);
this.emit("external", stdout, stderr);
resolve();
});
});
});
}
else
await this.gzip(filename);
return (0, promises_1.unlink)(this.filename);
}
gzip(filename, callback) {
async gzip(filename) {
const { mode } = this.options;

@@ -432,121 +326,95 @@ const options = mode ? { mode } : {};

const zip = this.createGzip();
[inp, out, zip].map(stream => stream.once("error", callback));
out.once("finish", callback);
inp.pipe(zip).pipe(out);
return new Promise((resolve, reject) => {
[inp, out, zip].map(stream => stream.once("error", reject));
out.once("finish", resolve);
inp.pipe(zip).pipe(out);
});
}
rotated(filename, callback) {
async rotated(filename) {
const { maxFiles, maxSize } = this.options;
const open = (error) => {
if (error)
return callback(error);
this.reopen(false, 0, callback);
this.emit("rotated", filename);
};
if (maxFiles || maxSize)
return this.history(filename, open);
open();
await this.history(filename);
this.emit("rotated", filename);
return this.reopen(0);
}
history(filename, callback) {
const { history } = this.options;
this.fsReadFile(history, "utf8", (error, data) => {
if (error) {
if (error.code !== "ENOENT")
return callback(error);
return this.historyGather([filename], 0, [], callback);
async history(filename) {
const { history, maxFiles, maxSize } = this.options;
const res = [];
let files = [filename];
try {
const content = await this.fsReadFile(history, "utf8");
files = [...content.toString().split("\n"), filename];
}
catch (e) {
if (e.code !== "ENOENT")
throw e;
}
for (const file of files) {
if (file) {
try {
const stats = await this.fsStat(file);
if (stats.isFile()) {
res.push({
name: file,
size: stats.size,
time: stats.ctime.getTime()
});
}
else
this.emit("warning", new Error(`File '${file}' contained in history is not a regular file`));
}
catch (e) {
if (e.code !== "ENOENT")
throw e;
}
}
const files = data.split("\n");
files.push(filename);
this.historyGather(files, 0, [], callback);
});
}
historyGather(files, index, res, callback) {
if (index === files.length)
return this.historyCheckFiles(res, callback);
this.fsStat(files[index], (error, stats) => {
if (error) {
if (error.code !== "ENOENT")
return callback(error);
}
res.sort((a, b) => a.time - b.time);
if (maxFiles) {
while (res.length > maxFiles) {
const file = res.shift();
await (0, promises_1.unlink)(file.name);
this.emit("removed", file.name, true);
}
else if (stats.isFile()) {
res.push({
name: files[index],
size: stats.size,
time: stats.ctime.getTime()
});
}
if (maxSize) {
while (res.reduce((size, file) => size + file.size, 0) > maxSize) {
const file = res.shift();
await (0, promises_1.unlink)(file.name);
this.emit("removed", file.name, false);
}
else
this.emit("warning", new Error(`File '${files[index]}' contained in history is not a regular file`));
this.historyGather(files, index + 1, res, callback);
});
}
await (0, promises_1.writeFile)(history, res.map(e => e.name).join("\n") + "\n", "utf-8");
this.emit("history");
}
historyRemove(files, size, callback) {
const file = files.shift();
this.fsUnlink(file.name, (error) => {
if (error)
return callback(error);
this.emit("removed", file.name, !size);
callback();
});
}
historyCheckFiles(files, callback) {
const { maxFiles } = this.options;
files.sort((a, b) => a.time - b.time);
if (!maxFiles || files.length <= maxFiles)
return this.historyCheckSize(files, callback);
this.historyRemove(files, false, (error) => (error ? callback(error) : this.historyCheckFiles(files, callback)));
}
historyCheckSize(files, callback) {
const { maxSize } = this.options;
let size = 0;
if (!maxSize)
return this.historyWrite(files, callback);
files.map(e => (size += e.size));
if (size <= maxSize)
return this.historyWrite(files, callback);
this.historyRemove(files, true, (error) => (error ? callback(error) : this.historyCheckSize(files, callback)));
}
historyWrite(files, callback) {
this.fsWriteFile(this.options.history, files.map(e => e.name).join("\n") + "\n", "utf8", (error) => {
if (error)
return callback(error);
this.emit("history");
callback();
});
}
immutate(first, callback, index, now) {
if (!index) {
index = 1;
now = this.now();
}
if (index >= 1001)
return callback(new RotatingFileStreamError());
try {
async immutate(first) {
const { size } = this.options;
const now = this.now();
for (let index = 1; index < 1000; ++index) {
let fileSize = 0;
let stats = undefined;
this.filename = this.options.path + this.generator(now, index);
}
catch (e) {
return callback(e);
}
const open = (size, callback) => {
try {
stats = await this.fsStat(this.filename);
}
catch (e) {
if (e.code !== "ENOENT")
throw e;
}
if (stats) {
fileSize = stats.size;
if (!stats.isFile())
throw new Error(`Can't write on: '${this.filename}' (it is not a file)`);
if (size && fileSize >= size)
continue;
}
if (first) {
this.last = this.filename;
return this.reopen(false, size, callback);
return this.reopen(fileSize);
}
this.rotated(this.last, (error) => {
this.last = this.filename;
callback(error);
});
};
this.fsStat(this.filename, (error, stats) => {
const { size } = this.options;
if (error) {
if (error.code === "ENOENT")
return open(0, callback);
return callback(error);
}
if (!stats.isFile())
return callback(new Error(`Can't write on: '${this.filename}' (it is not a file)`));
if (size && stats.size >= size)
return this.immutate(first, callback, index + 1, now);
open(stats.size, callback);
});
await this.rotated(this.last);
this.last = this.filename;
return;
}
throw new RotatingFileStreamError();
}

@@ -583,9 +451,3 @@ }

}
const intervalUnits = {
M: true,
d: true,
h: true,
m: true,
s: true
};
const intervalUnits = { M: true, d: true, h: true, m: true, s: true };
function checkIntervalUnit(ret, unit, amount) {

@@ -610,8 +472,3 @@ if (parseInt((amount / ret.num), 10) * ret.num !== amount)

}
const sizeUnits = {
B: true,
G: true,
K: true,
M: true
};
const sizeUnits = { B: true, G: true, K: true, M: true };
function checkSize(value) {

@@ -628,2 +485,14 @@ const ret = checkMeasure(value, "size", sizeUnits);

const checks = {
encoding: (type, options, value) => new util_1.TextDecoder(value),
immutable: () => { },
initialRotation: () => { },
interval: buildStringCheck("interval", checkInterval),
intervalBoundary: () => { },
maxFiles: buildNumberCheck("maxFiles"),
maxSize: buildStringCheck("maxSize", checkSize),
mode: () => { },
omitExtension: () => { },
rotate: buildNumberCheck("rotate"),
size: buildStringCheck("size", checkSize),
teeToStdout: () => { },
compress: (type, options, value) => {

@@ -641,3 +510,2 @@ if (!value)

},
encoding: (type, options, value) => new util_1.TextDecoder(value),
history: (type) => {

@@ -647,9 +515,2 @@ if (type !== "string")

},
immutable: () => { },
initialRotation: () => { },
interval: buildStringCheck("interval", checkInterval),
intervalBoundary: () => { },
maxFiles: buildNumberCheck("maxFiles"),
maxSize: buildStringCheck("maxSize", checkSize),
mode: () => { },
path: (type, options, value) => {

@@ -660,6 +521,3 @@ if (type !== "string")

options.path = value + path_1.sep;
},
rotate: buildNumberCheck("rotate"),
size: buildStringCheck("size", checkSize),
teeToStdout: () => { }
}
};

@@ -696,6 +554,6 @@ function checkOpts(options) {

}
function createClassical(filename) {
return (index) => (index ? `${filename}.${index}` : filename);
function createClassical(filename, compress, omitExtension) {
return (index) => (index ? `${filename}.${index}${compress && !omitExtension ? ".gz" : ""}` : filename);
}
function createGenerator(filename) {
function createGenerator(filename, compress, omitExtension) {
const pad = (num) => (num > 9 ? "" : "0") + num;

@@ -709,3 +567,3 @@ return (time, index) => {

const minute = pad(time.getMinutes());
return month + day + "-" + hour + minute + "-" + pad(index) + "-" + filename;
return month + day + "-" + hour + minute + "-" + pad(index) + "-" + filename + (compress && !omitExtension ? ".gz" : "");
};

@@ -719,5 +577,6 @@ }

const opts = checkOpts(options);
const { compress, omitExtension } = opts;
let generator;
if (typeof filename === "string")
generator = options.rotate ? createClassical(filename) : createGenerator(filename);
generator = options.rotate ? createClassical(filename, compress !== undefined, omitExtension) : createGenerator(filename, compress !== undefined, omitExtension);
else if (typeof filename === "function")

@@ -724,0 +583,0 @@ generator = filename;

{
"name": "rotating-file-stream",
"version": "2.1.6",
"description": "Opens a stream.Writable to a file rotated by interval and/or size. A logrotate alternative.",
"scripts": {
"all": "npm run eslint && npm run coverage",
"clean": "node -r ts-node/register utils.ts clean",
"coverage": "npm run clean && TZ=\"Europe/Rome\" nyc -r lcov -r text -r text-summary -r html mocha -r ts-node/register test/*ts",
"eslint": "eslint index.ts utils.ts test/*ts",
"ignore": "node -r ts-node/register utils.ts ignore",
"prepare": "npm run ignore && tsc",
"test": "npm run clean && mocha -r ts-node/register test/*ts"
},
"bugs": "https://github.com/iccicci/rotating-file-stream/issues",
"repository": "https://github.com/iccicci/rotating-file-stream",
"keywords": [
"log",
"rotate",
"logrotate"
],
"engines": {
"node": ">=10.0"
},
"author": "Daniele Ricci <daniele.icc@gmail.com> (https://github.com/iccicci)",
"contributors": [
"cicci (https://www.trinityteam.it/DanieleRicci#en)",
"allevo",
"kbirger",
"jvassev",
"wangao",
"rakshith-ravi",
"Jorge Silva <jorgemsrs@gmail.com>",
"Jan Christoph Bernack <jc.bernack@gmail.com>",
"cchare (https://github.com/cchare)"
],
"license": "MIT",
"funding": {
"url": "https://www.blockchain.com/btc/address/12p1p5q7sK75tPyuesZmssiMYr4TKzpSCN"
},
"readmeFilename": "README.md",
"types": "index.d.ts",
"devDependencies": {
"@types/mocha": "9.0.0",
"@types/node": "16.10.1",
"@typescript-eslint/eslint-plugin": "4.31.2",
"@typescript-eslint/parser": "4.31.2",
"eslint": "7.32.0",
"mocha": "9.1.2",
"nyc": "15.1.0",
"prettier": "2.4.1",
"ts-node": "10.2.1",
"typescript": "4.4.3"
},
"prettier": {
"arrowParens": "avoid",
"jsxBracketSameLine": true,
"printWidth": 200,
"trailingComma": "none",
"useTabs": true,
"overrides": [
{
"files": [
"*.md"
],
"options": {
"useTabs": false
}
}
]
}
"name": "rotating-file-stream",
"version": "3.0.0",
"description": "Opens a stream.Writable to a file rotated by interval and/or size. A logrotate alternative.",
"scripts": {
"all": "npm run eslint && npm run coverage",
"clean": "node -r ts-node/register utils.ts clean",
"coverage": "npm run clean && TZ=\"Europe/Rome\" nyc -r lcov -r text -r text-summary -r html mocha -r ts-node/register test/*ts",
"eslint": "eslint index.ts utils.ts test/*ts",
"ignore": "node -r ts-node/register utils.ts ignore",
"prepare": "npm run ignore && tsc",
"test": "npm run clean && mocha -r ts-node/register test/*ts"
},
"bugs": "https://github.com/iccicci/rotating-file-stream/issues",
"repository": "https://github.com/iccicci/rotating-file-stream",
"keywords": [
"log",
"rotate",
"logrotate"
],
"engines": {
"node": ">=10.0"
},
"author": "Daniele Ricci <daniele.icc@gmail.com> (https://github.com/iccicci)",
"contributors": [
"cicci (https://www.trinityteam.it/DanieleRicci#en)",
"allevo",
"kbirger",
"jvassev",
"wangao",
"rakshith-ravi",
"Jorge Silva <jorgemsrs@gmail.com>",
"Jan Christoph Bernack <jc.bernack@gmail.com>",
"cchare (https://github.com/cchare)"
],
"license": "MIT",
"funding": {
"url": "https://www.blockchain.com/btc/address/12p1p5q7sK75tPyuesZmssiMYr4TKzpSCN"
},
"readmeFilename": "README.md",
"types": "index.d.ts",
"devDependencies": {
"@types/mocha": "9.0.0",
"@types/node": "16.11.6",
"@typescript-eslint/eslint-plugin": "5.3.0",
"@typescript-eslint/parser": "5.3.0",
"eslint": "8.1.0",
"mocha": "9.1.3",
"nyc": "15.1.0",
"prettier": "2.4.1",
"ts-node": "10.4.0",
"typescript": "4.4.4"
},
"prettier": {
"arrowParens": "avoid",
"jsxBracketSameLine": true,
"printWidth": 200,
"trailingComma": "none"
}
}

@@ -6,12 +6,10 @@ # rotating-file-stream

[![Test Coverage][cover-badge]][code-url]
[![Donate][donate-badge]][donate-url]
[![NPM version][npm-badge]][npm-url]
[![Types][types-badge]][npm-url]
[![NPM downloads][npm-downloads-badge]][npm-url]
[![Stars][stars-badge]][github-url]
[![Dependencies][dep-badge]][dep-url]
[![Dev Dependencies][dev-dep-badge]][dev-dep-url]
[![Types][types-badge]][npm-url]
[![Dependents][deps-badge]][npm-url]
[![Donate][donate-badge]][donate-url]

@@ -21,7 +19,3 @@ [code-badge]: https://codeclimate.com/github/iccicci/rotating-file-stream/badges/gpa.svg

[cover-badge]: https://codeclimate.com/github/iccicci/rotating-file-stream/badges/coverage.svg
[dep-badge]: https://david-dm.org/iccicci/rotating-file-stream.svg
[dep-url]: https://david-dm.org/iccicci/rotating-file-stream
[deps-badge]: https://badgen.net/npm/dependents/rotating-file-stream?icon=npm
[dev-dep-badge]: https://david-dm.org/iccicci/rotating-file-stream/dev-status.svg
[dev-dep-url]: https://david-dm.org/iccicci/rotating-file-stream?type=dev
[donate-badge]: https://badgen.net/badge/donate/bitcoin?icon=bitcoin

@@ -64,3 +58,4 @@ [donate-url]: https://blockchain.info/address/12p1p5q7sK75tPyuesZmssiMYr4TKzpSCN

- [Upgrading from v1.x.x to v2.x.x](#upgrading-from-v1xx-to-v2xx)
- [Upgrading from v2 to v3](#upgrading-from-v2-to-v3)
- [Upgrading from v1 to v2](#upgrading-from-v1-to-v2)
- [API](#api)

@@ -72,2 +67,3 @@ - [rfs.createStream(filename[, options])](#rfscreatestreamfilename-options)

- [Class: RotatingFileStream](#class-rotatingfilestream)
- [Event: 'external'](#event-external)
- [Event: 'history'](#event-history)

@@ -90,2 +86,3 @@ - [Event: 'open'](#event-open)

- [mode](#mode)
- [omitExtension](#omitextension)
- [path](#path)

@@ -104,4 +101,21 @@ - [rotate](#rotate)

# Upgrading from v1.x.x to v2.x.x
# Upgrading from v2 to v3
In **v3** the package was completely refactored using **async / await**.
**TypeScript** types for events and the [external](#event-external) event were added.
**Breaking change**: by default the `.gz` extension is added to the rotated compressed files.
**Breaking change**: the way the _external compression command_ is executed was slightly changed; possible bracking
change.
To maintain back compatibility upgrading from **v2** to **v3**, just follow this rules:
- using a _file name generator_ or not using [`options.compress`](#compress): nothing to do
- using a _file name_ and using [`options.rotation`](#rotation): use [`options.omitExtension`](#omitextension) or check
how rotated files are treated.
# Upgrading from v1 to v2
There are two main changes in package interface.

@@ -122,4 +136,4 @@

In **v2** the `time` argument passed to the _filename generator_ function is always the time when _rotaion job_
started, unless [`options.intervalBoundary`](#intervalboundary) option is used. In a few words, to maintain back compatibility
upgrading from **v1** to **v2**, just follow this rules:
started, unless [`options.intervalBoundary`](#intervalboundary) option is used. In a few words, to maintain back
compatibility upgrading from **v1** to **v2**, just follow this rules:

@@ -216,2 +230,12 @@ - using [`options.rotation`](#rotation): nothing to do

### Event: 'external'
- `stdout` [&lt;string>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type) The
standard output of the external compression command.
- `stderr` [&lt;string>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type) The
standard error of the external compression command.
The `external` event is emitted once an _external compression command_ completes its execution to give access to the
command output streams.
### Event: 'history'

@@ -292,2 +316,5 @@

**Default:** `0o666`.
- [`omitExtension`](#omitextension):
[&lt;boolean>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type)
Omits the `.gz` extension from compressed rotated files. **Default:** `null`.
- [`path`](#path):

@@ -421,2 +448,7 @@ [&lt;string>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type)

### omitExtension
From **v3** the package adds by default the `.gz` extension to the rotated compressed files. Simultaneously this option
was added: set this option to `true` to not add the extension, i.e. to keep backward compatibility.
### initialRotation

@@ -523,5 +555,5 @@

Requires **Node.js v10.x**.
Requires **Node.js v12.x**.
The package is tested under [all Node.js versions](https://travis-ci.org/iccicci/rotating-file-stream)
The package is tested under [all Node.js versions](https://app.travis-ci.com/github/iccicci/rotating-file-stream)
currently supported accordingly to [Node.js Release](https://github.com/nodejs/Release#readme).

@@ -537,28 +569,4 @@

Exported in **TypeScript**.
**TypeScript** types are distibuted with the package itself.
```typescript
import { Writable } from "stream";
export declare type Compressor = (source: string, dest: string) => string;
export declare type Generator = (time: number | Date, index?: number) => string;
export interface Options {
compress?: boolean | string | Compressor;
encoding?: BufferEncoding;
history?: string;
immutable?: boolean;
initialRotation?: boolean;
interval?: string;
intervalBoundary?: boolean;
maxFiles?: number;
maxSize?: string;
mode?: number;
path?: string;
rotate?: number;
size?: string;
teeToStdout?: boolean;
}
export declare class RotatingFileStream extends Writable {}
export declare function createStream(filename: string | Generator, options?: Options): RotatingFileStream;
```
# Licence

@@ -565,0 +573,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc