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.2.4 to 2.2.5

146

lib/tail.js

@@ -51,35 +51,145 @@ let events = require(`events`)

let cursor;
this.logger.info(`fromBeginning: ${fromBeginning}`);
let startingCursor;
if (fromBeginning) {
startingCursor = 0;
cursor = 0;
} else if (this.nLines <= 0) {
cursor = 0;
} else if (this.nLines !== undefined) {
const data = fs.readFileSync(this.filename, {
flag: 'r',
encoding: this.encoding
});
const tokens = data.split(this.separator);
const dropLastToken = (tokens[tokens.length - 1] === '') ? 1 : 0;//if the file ends with empty line ignore line NL
if (tokens.length - this.nLines - dropLastToken <= 0) {
//nLines is bigger than avaiable tokens: tail from the begin
startingCursor = 0;
} else {
const match = data.match(new RegExp(`(?:[^\r\n]*[\r]{0,1}\n){${tokens.length - this.nLines - dropLastToken}}`));
startingCursor = (match && match.length) ? Buffer.byteLength(match[0], this.encoding) : this.latestPosition();
}
cursor = this.getPositionAtNthLine(this.nLines);
} else {
startingCursor = this.latestPosition();
cursor = this.latestPosition();
}
if (startingCursor === undefined) throw new Error("Tail can't initialize.");
if (cursor === undefined) throw new Error("Tail can't initialize.");
const flush = fromBeginning || (this.nLines != undefined);
try {
this.watch(startingCursor, flush);
this.watch(cursor, flush);
} catch (err) {
this.logger.error(`watch for ${this.filename} failed: ${err}`);
this.emit("error", `watch for ${this.filename} failed: ${err}`);
}
}
/**
* Grabs the index of the last line of text in the format /.*(\n)?/.
* Returns null if a full line can not be found.
* @param {string} text
* @returns {number | null}
*/
getIndexOfLastLine(text) {
/**
* Helper function get the last match as string
* @param {string} haystack
* @param {string | RegExp} needle
* @returns {string | undefined}
*/
const getLastMatch = (haystack, needle) => {
const matches = haystack.match(needle);
if (matches === null) {
return;
}
return matches[matches.length - 1];
};
const endSep = getLastMatch(text, this.separator);
if (!endSep) return null;
const endSepIndex = text.lastIndexOf(endSep);
let lastLine;
if (text.endsWith(endSep)) {
// If the text ends with a separator, look back further to find the next
// separator to complete the line
const trimmed = text.substring(0, endSepIndex);
const startSep = getLastMatch(trimmed, this.separator);
// If there isn't another separator, the line isn't complete so
// so return null to get more data
if (!startSep) {
return null;
}
const startSepIndex = trimmed.lastIndexOf(startSep);
// Exclude the starting separator, include the ending separator
lastLine = text.substring(
startSepIndex + startSep.length,
endSepIndex + endSep.length
);
} else {
// If the text does not end with a separator, grab everything after
// the last separator
lastLine = text.substring(endSepIndex + endSep.length);
}
return text.lastIndexOf(lastLine);
}
/**
* Returns the position of the start of the `nLines`th line from the bottom.
* Returns 0 if `nLines` is greater than the total number of lines in the file.
* @param {number} nLines
* @returns {number}
*/
getPositionAtNthLine(nLines) {
const { size } = fs.statSync(this.filename);
const fd = fs.openSync(this.filename, 'r');
// Start from the end of the file and work backwards in specific chunks
let currentReadPosition = size;
const chunkSizeBytes = Math.min(1024, size);
const lineBytes = [];
let remaining = '';
while (lineBytes.length < nLines) {
// Shift the current read position backward to the amount we're about to read
currentReadPosition -= chunkSizeBytes;
// If negative, we've reached the beginning of the file and we should stop and return 0, starting the
// stream at the beginning.
if (currentReadPosition < 0) {
return 0;
}
// Read a chunk of the file and prepend it to the working buffer
const buffer = Buffer.alloc(chunkSizeBytes);
const bytesRead = fs.readSync(fd, buffer,
0, // position in buffer to write to
chunkSizeBytes, // number of bytes to read
currentReadPosition // position in file to read from
);
// .subarray returns Uint8Array in node versions < 16.x and Buffer
// in versions >= 16.x. To support both, allocate a new buffer with
// Buffer.from which accepts both types
const readArray = buffer.subarray(0, bytesRead);
remaining = Buffer.from(readArray).toString(this.encoding) + remaining;
let index = this.getIndexOfLastLine(remaining);
while (index !== null && lineBytes.length < nLines) {
const line = remaining.substring(index);
lineBytes.push(Buffer.byteLength(line));
remaining = remaining.substring(0, index);
index = this.getIndexOfLastLine(remaining);
}
}
fs.closeSync(fd);
return size - lineBytes.reduce((acc, cur) => acc + cur, 0)
}
latestPosition() {

@@ -86,0 +196,0 @@ try {

4

package.json

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

],
"version": "2.2.4",
"version": "2.2.5",
"homepage": "https://www.lucagrulla.com/node-tail",

@@ -38,5 +38,5 @@ "repository": {

"chai": "4.x",
"mocha": "9.x",
"mocha": "10.x",
"nyc": "^15.1.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