Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

tail

Package Overview
Dependencies
Maintainers
1
Versions
43
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tail - npm Package Compare versions

Comparing version 2.0.4 to 2.1.0

.mocharc.json

381

lib/tail.js

@@ -1,230 +0,207 @@

// Generated by CoffeeScript 2.4.1
var Tail, environment, events, fs, path,
boundMethodCheck = function(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new Error('Bound instance method accessed before binding'); } };
let events = require(`events`)
let fs = require('fs')
let path = require('path')
events = require("events");
// const environment = process.env['NODE_ENV'] || 'development'
fs = require('fs');
class devNull {
info() { };
error() { };
};
path = require('path');
class Tail extends events.EventEmitter {
constructor(filename, options = {}) {
super();
this.filename = filename;
this.absPath = path.dirname(this.filename);
this.separator = (options.separator !== undefined) ? options.separator : /[\r]{0,1}\n/;// null is a valid param
this.fsWatchOptions = options.fsWatchOptions || {};
this.follow = options.follow || true;
this.logger = options.logger || new devNull();
this.useWatchFile = options.useWatchFile || false;
this.flushAtEOF = options.flushAtEOF || false;
this.encoding = options.encoding || `utf-8`;
const fromBeginning = options.fromBeginning || false;
environment = process.env['NODE_ENV'] || 'development';
Tail = class Tail extends events.EventEmitter {
readBlock() {
var block, stream;
boundMethodCheck(this, Tail);
if (this.queue.length >= 1) {
block = this.queue[0];
if (block.end > block.start) {
stream = fs.createReadStream(this.filename, {
start: block.start,
end: block.end - 1,
encoding: this.encoding
});
stream.on('error', (error) => {
if (this.logger) {
this.logger.error(`Tail error: ${error}`);
}
return this.emit('error', error);
});
stream.on('end', () => {
var x;
x = this.queue.shift();
if (this.queue.length > 0) {
this.internalDispatcher.emit("next");
}
if (this.flushAtEOF && this.buffer.length > 0) {
this.emit("line", this.buffer);
return this.buffer = '';
}
});
return stream.on('data', (data) => {
var chunk, i, len, parts, results;
if (this.separator === null) {
return this.emit("line", data);
} else {
this.buffer += data;
parts = this.buffer.split(this.separator);
this.buffer = parts.pop();
results = [];
for (i = 0, len = parts.length; i < len; i++) {
chunk = parts[i];
results.push(this.emit("line", chunk));
this.logger.info(`Tail starting...`)
this.logger.info(`filename: ${this.filename}`);
this.logger.info(`encoding: ${this.encoding}`);
try {
fs.accessSync(this.filename, fs.constants.F_OK);
} catch (err) {
if (err.code == 'ENOENT') {
throw err
}
return results;
}
}
this.buffer = '';
this.internalDispatcher = new events.EventEmitter();
this.queue = [];
this.isWatching = false;
// this.internalDispatcher.on('next',this.readBlock);
this.internalDispatcher.on('next', () => {
this.readBlock();
});
}
this.logger.info(`fromBeginning: ${fromBeginning}`);
let startingPos = undefined;
if (fromBeginning) {
startingPos = 0;
//if fromBeginning triggers a check for content to flush the existing file
//without waiting for a new appended line
this.change(this.filename);
}
this.watch(startingPos);
}
}
constructor(filename, options = {}) {
var err, fromBeginning;
super(filename, options);
this.readBlock = this.readBlock.bind(this);
this.change = this.change.bind(this);
this.filename = filename;
this.absPath = path.dirname(this.filename);
({separator: this.separator = /[\r]{0,1}\n/, fsWatchOptions: this.fsWatchOptions = {}, follow: this.follow = true, logger: this.logger, useWatchFile: this.useWatchFile = false, flushAtEOF: this.flushAtEOF = false, encoding: this.encoding = "utf-8", fromBeginning = false} = options);
if (this.logger) {
this.logger.info("Tail starting...");
this.logger.info(`filename: ${this.filename}`);
this.logger.info(`encoding: ${this.encoding}`);
try {
fs.accessSync(this.filename, fs.constants.F_OK);
} catch (error1) {
err = error1;
if (err.code === 'ENOENT') {
throw err;
latestPosition() {
try {
return fs.statSync(this.filename).size;
} catch (err) {
this.logger.error(`size check for ${this.filename} failed: ${err}`);
this.emit("error", `size check for ${this.filename} failed: ${err}`);
throw err;
}
}
}
this.buffer = '';
this.internalDispatcher = new events.EventEmitter();
this.queue = [];
this.isWatching = false;
this.internalDispatcher.on('next', () => {
return this.readBlock();
});
this.watch(fromBeginning);
}
change(filename) {
var err, stats;
boundMethodCheck(this, Tail);
try {
stats = fs.statSync(filename);
} catch (error1) {
err = error1;
if (this.logger) {
this.logger.error(`change event for ${filename} failed: ${err}`);
}
this.emit("error", `change event for ${filename} failed: ${err}`);
return;
readBlock() {
if (this.queue.length >= 1) {
const block = this.queue[0];
if (block.end > block.start) {
let stream = fs.createReadStream(this.filename, { start: block.start, end: block.end - 1, encoding: this.encoding });
stream.on('error', (error) => {
this.logger.error(`Tail error: ${error}`);
this.emit('error', error);
});
stream.on('end', () => {
let _ = this.queue.shift();
if (this.queue.length > 0) {
this.internalDispatcher.emit('next');
}
if (this.flushAtEOF && this.buffer.length > 0) {
this.emit('line', this.buffer);
this.buffer = "";
}
});
stream.on('data', (d) => {
if (this.separator === null) {
this.emit("line", d);
} else {
this.buffer += d;
let parts = this.buffer.split(this.separator);
this.buffer = parts.pop();
for (const chunk of parts) {
this.emit("line", chunk);
}
}
});
}
}
}
if (stats.size < this.pos) { //scenario where texts is not appended but it's actually a w+
this.pos = stats.size;
}
if (stats.size > this.pos) {
this.queue.push({
start: this.pos,
end: stats.size
});
this.pos = stats.size;
if (this.queue.length === 1) {
return this.internalDispatcher.emit("next");
}
}
}
watch(fromBeginning) {
var err, stats;
if (this.isWatching) {
return;
change(filename) {
let p = this.latestPosition()
if (p < this.pos) {//scenario where text is not appended but it's actually a w+
this.pos = p
} else if (p > this.pos) {
this.queue.push({ start: this.pos, end: p});
this.pos = p
if (this.queue.length == 1) {
this.internalDispatcher.emit("next");
}
}
}
if (this.logger) {
this.logger.info(`filesystem.watch present? ${fs.watch !== void 0}`);
this.logger.info(`useWatchFile: ${this.useWatchFile}`);
this.logger.info(`fromBeginning: ${fromBeginning}`);
}
this.isWatching = true;
try {
stats = fs.statSync(this.filename);
} catch (error1) {
err = error1;
if (this.logger) {
this.logger.error(`watch for ${this.filename} failed: ${err}`);
}
this.emit("error", `watch for ${this.filename} failed: ${err}`);
return;
}
this.pos = fromBeginning ? 0 : stats.size;
if (this.pos === 0) {
this.change(this.filename);
}
if (!this.useWatchFile && fs.watch) {
if (this.logger) {
this.logger.info("watch strategy: watch");
}
return this.watcher = fs.watch(this.filename, this.fsWatchOptions, (e, filename) => {
return this.watchEvent(e, filename);
});
} else {
if (this.logger) {
this.logger.info("watch strategy: watchFile");
}
return fs.watchFile(this.filename, this.fsWatchOptions, (curr, prev) => {
return this.watchFileEvent(curr, prev);
});
}
}
rename(filename) {
//MacOS sometimes throws a rename event for no reason.
//Different platforms might behave differently.
//see https://nodejs.org/api/fs.html#fs_fs_watch_filename_options_listener
//filename might not be present.
//https://nodejs.org/api/fs.html#fs_filename_argument
//Better solution would be check inode but it will require a timeout and
// a sync file read.
if (filename === void 0 || filename !== this.filename) {
this.unwatch();
if (this.follow) {
this.filename = path.join(this.absPath, filename);
return this.rewatchId = setTimeout((() => {
return this.watch();
}), 1000);
} else {
if (this.logger) {
this.logger.error(`'rename' event for ${this.filename}. File not available.`);
watch(startingPos) {
if (this.isWatching) {
return
}
return this.emit("error", `'rename' event for ${this.filename}. File not available.`);
}
} else {
this.logger.info(`filesystem.watch present? ${fs.watch != undefined}`);
this.logger.info(`useWatchFile: ${this.useWatchFile}`);
this.isWatching = true;
this.pos = (startingPos === undefined) ? this.latestPosition() : startingPos;
try {
if (!this.useWatchFile && fs.watch) {
this.logger.info(`watch strategy: watch`);
this.watcher = fs.watch(this.filename, this.fsWatchOptions, (e, filename) => { this.watchEvent(e, filename); });
} else {
this.logger.info(`watch strategy: watchFile`);
fs.watchFile(this.filename, this.fsWatchOptions, (curr, prev) => { this.watchFileEvent(curr, prev) });
}
} catch (err) {
this.logger.error(`watch for ${this.filename} failed: ${err}`);
this.emit("error", `watch for ${this.filename} failed: ${err}`);
return
}
}
}
// @logger.info("rename event but same filename")
watchEvent(e, evtFilename) {
if (e === 'change') {
return this.change(this.filename);
} else if (e === 'rename') {
return this.rename(evtFilename);
rename(filename) {
//TODO
//MacOS sometimes throws a rename event for no reason.
//Different platforms might behave differently.
//see https://nodejs.org/api/fs.html#fs_fs_watch_filename_options_listener
//filename might not be present.
//https://nodejs.org/api/fs.html#fs_filename_argument
//Better solution would be check inode but it will require a timeout and
// a sync file read.
if (filename === undefined || filename !== this.filename) {
this.unwatch();
if (this.follow) {
this.filename = path.join(this.absPath, filename);
this.rewatchId = setTimeout((() => {
this.watch(this.pos);
}), 1000);
} else {
this.logger.error(`'rename' event for ${this.filename}. File not available anymore.`);
this.emit("error", `'rename' event for ${this.filename}. File not available anymore.`);
}
} else {
// this.logger.info("rename event but same filename")
}
}
}
watchFileEvent(curr, prev) {
if (curr.size > prev.size) {
this.pos = curr.size; // Update @pos so that a consumer can determine if entire file has been handled
this.queue.push({
start: prev.size,
end: curr.size
});
if (this.queue.length === 1) {
return this.internalDispatcher.emit("next");
}
watchEvent(e, evtFilename) {
if (e === 'change') {
this.change(this.filename);
} else if (e === 'rename') {
this.rename(evtFilename);
}
}
}
unwatch() {
if (this.watcher) {
this.watcher.close();
} else {
fs.unwatchFile(this.filename);
watchFileEvent(curr, prev) {
if (curr.size > prev.size) {
this.pos = curr.size; //Update this.pos so that a consumer can determine if entire file has been handled
this.queue.push({ start: prev.size, end: curr.size });
if (this.queue.length == 1) {
this.internalDispatcher.emit("next");
}
}
}
if (this.rewatchId) {
clearTimeout(this.rewatchId);
this.rewatchId = void 0;
unwatch() {
if (this.watcher) {
this.watcher.close();
} else {
fs.unwatchFile(this.filename);
}
if (this.rewatchId) {
clearTimeout(this.rewatchId);
this.rewatchId = undefined;
}
this.isWatching = false;
this.queue = [];// TODO: is this correct behaviour?
if (this.logger) {
this.logger.info(`Unwatch ${this.filename}`);
}
}
this.isWatching = false;
this.queue = [];
if (this.logger) {
return this.logger.info("Unwatch ", this.filename);
}
}
};
}
exports.Tail = Tail;
exports.Tail = Tail

@@ -17,3 +17,3 @@ {

],
"version": "2.0.4",
"version": "2.1.0",
"homepage": "https://www.lucagrulla.com/node-tail",

@@ -29,6 +29,7 @@ "repository": {

"scripts": {
"build": "cake build",
"build": "rm -f ./lib/** && cp src/tail.js ./lib/",
"prepare": "npm run build",
"prepublishOnly": "npm run test",
"test": "mocha"
"test": "mocha",
"coverage": "nyc npm run test"
},

@@ -38,6 +39,6 @@ "license": "MIT",

"devDependencies": {
"coffeescript": "2.5.1",
"chai": "4.x",
"mocha": "7.x"
"mocha": "8.x",
"nyc": "^15.1.0"
}
}

@@ -108,9 +108,8 @@ # Tail

Tail is written in [CoffeeScript](https://coffeescript.org/).
Tail is written in plain ES6.Pull Requests are welcome.
The Cakefile generates the javascript that is then published to npm.
## History
Tail was born as part of a data firehose. Read about it [here](https://www.lucagrulla.com/posts/building-a-firehose-with-nodejs/).
Tail original version was written in [CoffeeScript](https://coffeescript.org/). Since 2020 it's pure ES6.

@@ -117,0 +116,0 @@ ## License

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