+3
-0
@@ -6,5 +6,8 @@ language: node_js | ||
| - 8 | ||
| - 10 | ||
| - 12 | ||
| after_success: | ||
| - if [ `node -p 'process.version.slice(0, 3)'` != "v8." ]; then exit; fi | ||
| - npm install -g nyc | ||
| - npm install -g codecov | ||
| - nyc --reporter lcov npm test && codecov |
+11
-2
@@ -7,3 +7,3 @@ /** | ||
| * | ||
| * Copyright (C) 2014-2015 Andras Radics | ||
| * Copyright (C) 2014-2015,2019 Andras Radics | ||
| * Licensed under the Apache License, Version 2.0 | ||
@@ -15,4 +15,13 @@ */ | ||
| var fs = require('fs'); | ||
| var fse = require('./fs-ext.js'); | ||
| var fse = _tryRequire('fs-ext'); | ||
| var aflow = require('aflow'); | ||
| if (!fse) { | ||
| console.warn("qfputs: fs-ext not installed, appending lines without flock"); | ||
| fse = { | ||
| // emulate the calls needed by FileWriter: flock and flockSync | ||
| flock: function(fd, flag, cb) { if (cb) cb(null); }, | ||
| flockSync: function(fd, flag) { }, | ||
| }; | ||
| }; | ||
| function _tryRequire(name) { return eval("try { require('" + name + "') } catch (e) { }") } | ||
@@ -19,0 +28,0 @@ var flock = fse.flock; |
+1
-1
@@ -1,2 +0,2 @@ | ||
| Copyright 2014 Andras Radics | ||
| Copyright 2014-2015,2019 Andras Radics | ||
| andras at andrasq dot com | ||
@@ -3,0 +3,0 @@ |
+17
-22
| { | ||
| "name": "qfputs", | ||
| "version": "1.7.2", | ||
| "version": "1.8.0", | ||
| "description": "very fast write-combining bufferd output", | ||
| "license": "Apache-2.0", | ||
| "main": "lib/fputs.js", | ||
| "repository": { | ||
@@ -10,4 +11,5 @@ "type": "git", | ||
| }, | ||
| "homepage": "https://github.com/andrasq/node-qfputs", | ||
| "main": "index.js", | ||
| "readme": "see Readme.md in the repo", | ||
| "author": { | ||
@@ -20,29 +22,22 @@ "name": "Andras", | ||
| }, | ||
| "scripts": { | ||
| "coverage": "nyc --include lib --reporter lcov --reporter text npm test", | ||
| "clean": "rm -rf .nyc_output/ coverage/ npm-debug.log", | ||
| "test": "qnit" | ||
| }, | ||
| "keywords": [ | ||
| "Andras", | ||
| "fputs", | ||
| "very fast", | ||
| "fast", | ||
| "buffered", | ||
| "line", | ||
| "binary", | ||
| "Buffer", | ||
| "output", | ||
| "text", | ||
| "newline", | ||
| "delimited", | ||
| "transport" | ||
| "Andras", "fputs", "very fast", "fast", "buffered", "line", "binary", | ||
| "Buffer", "output", "text", "newline", "delimited", "transport" | ||
| ], | ||
| "dependencies": { | ||
| "aflow": "0.10.0", | ||
| "fs-ext": "1.2.1" | ||
| "aflow": "0.10.0" | ||
| }, | ||
| "optionalDependencies": { | ||
| "fs-ext": "1.3.0" | ||
| }, | ||
| "devDependencies": { | ||
| "qnit": "0.25.1" | ||
| }, | ||
| "scripts": { | ||
| "coverage": "nyc --include lib --reporter lcov --reporter text npm test", | ||
| "clean": "rm -rf .nyc_output/ coverage/ npm-debug.log", | ||
| "test": "qnit" | ||
| } | ||
| } |
+3
-0
@@ -197,2 +197,5 @@ qfputs | ||
| ### 1.8.0 - deprecate index.js, make fs-ext an optional dependency | ||
| ### 1.7.2 - upgrade fs-ext to 1.2.1 for node-v10, upgrade qnit | ||
| ### 1.7.1 | ||
@@ -199,0 +202,0 @@ |
-1
| module.exports = require('./lib/fputs'); |
| /** | ||
| * Copyright (C) 2015 Andras Radics | ||
| * Licensed under the Apache License, Version 2.0 | ||
| */ | ||
| try { | ||
| module.exports = require('fs-ext'); | ||
| return; | ||
| } | ||
| catch (err) { | ||
| // fs-ext not installed, limp along without | ||
| console.log("qfputs: fs-ext not installed, putting append-only without flock"); | ||
| module.exports = emulateFse(); | ||
| return; | ||
| } | ||
| function emulateFse() { | ||
| fs = require('fs'); | ||
| return { | ||
| // emulate the calls needed by FileWriter, flock and flockSync | ||
| flock: function(fd, flag, cb) { if (cb) cb(null); }, | ||
| flockSync: function(fd, flag) { }, | ||
| }; | ||
| } |
| // qfputs test | ||
| // Copyright (C) 2014 Andras Radics | ||
| // Licensed under the Apache License, Version 2.0 | ||
| var fs = require('fs'); | ||
| var fse = require('fs-ext'); | ||
| var Fputs = require('../index.js'); | ||
| var FileWriter = require('../lib/filewriter.js'); | ||
| function uniqid( ) { | ||
| return Math.floor(Math.random() * 0x100000000).toString(16); | ||
| } | ||
| module.exports = { | ||
| setUp: function(cb) { | ||
| var tempfile = "/tmp/nodeunit-" + process.pid + ".tmp"; | ||
| var tempfile2 = "/tmp/nodeunit-" + process.pid + "-2.tmp"; | ||
| try { fs.unlinkSync(tempfile); } catch (e) {} | ||
| this.tempfile = tempfile; | ||
| this.tempfile2 = tempfile2; | ||
| this.mockWriter = { | ||
| written: [], | ||
| write: function(str, cb) { this.written.push("" + str); cb(); }, | ||
| fflush: function(cb) { cb(); }, | ||
| sync: function(cb) { cb(); }, | ||
| getContents: function() { return this.written.join(''); }, | ||
| }; | ||
| this.fileWriter = new Fputs.FileWriter(tempfile, "a"); | ||
| this.fileWriter.getContents = function() { return "" + fs.readFileSync(this.tempfile); } | ||
| this.writer = this.mockWriter; | ||
| this.fp = new Fputs(this.writer); | ||
| cb(); | ||
| }, | ||
| tearDown: function(cb) { | ||
| try { fs.unlinkSync(this.tempfile); } catch (e) {} | ||
| try { fs.unlinkSync(this.tempfile2); } catch (e) {} | ||
| cb(); | ||
| }, | ||
| 'package should be valid json': function(t) { | ||
| json = require('../package.json'); | ||
| t.done(); | ||
| }, | ||
| 'should expose FileWriter class': function(t) { | ||
| t.equal(Fputs.FileWriter, FileWriter); | ||
| t.done(); | ||
| }, | ||
| 'should expose FileWriter.renameFile class method': function(t) { | ||
| t.equal(Fputs.renameFile, FileWriter.renameFile); | ||
| t.done(); | ||
| }, | ||
| 'should expose FileWriter.renameFile as an instance method': function(t) { | ||
| t.equal(this.fileWriter.renameFile, FileWriter.renameFile); | ||
| t.done(); | ||
| }, | ||
| 'fputs should lazy create the logfile': function(t) { | ||
| var fp = new Fputs(this.tempfile); | ||
| t.expect(1); | ||
| try { | ||
| fs.statSync(this.tempfile); | ||
| t.ok(false, "logfile should not exist yet"); | ||
| } catch (err) { t.ok(true); } | ||
| t.done(); | ||
| }, | ||
| 'should create writable file with default write mode': function(t) { | ||
| var fp = new Fputs.FileWriter(this.tempfile); | ||
| var self = this; | ||
| fp.write("one\ntwo\n", function(err) { | ||
| t.ifError(err); | ||
| t.equals(fs.readFileSync(self.tempfile), "one\ntwo\n"); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'fputs should write contents soon': function(t) { | ||
| var line = "test line " + uniqid() + "\n"; | ||
| this.fp.fputs(line); | ||
| var self = this; | ||
| setTimeout(function(err) { | ||
| t.ifError(err); | ||
| t.equals(self.writer.getContents(), line); | ||
| t.done(); | ||
| }, 10); | ||
| }, | ||
| 'fputs should not clobber write-only files on reopen': function(t) { | ||
| var line1 = "test line " + uniqid() + "\n"; | ||
| var line2 = "test line " + uniqid() + "\n"; | ||
| var self = this; | ||
| fp = new Fputs(new Fputs.FileWriter(self.tempfile, "w")); | ||
| fp.fputs(line1); | ||
| setTimeout(function() { | ||
| fp.fputs(line2); | ||
| fp.fflush(function(err) { | ||
| t.ifError(err); | ||
| t.equal(fs.readFileSync(self.tempfile), line1 + line2); | ||
| t.done(); | ||
| }); | ||
| }, 25); | ||
| }, | ||
| 'fputs should get newline terminated': function(t) { | ||
| var line = "test line " + uniqid(); | ||
| this.fp.fputs(line); | ||
| var self = this; | ||
| setTimeout(function(err) { | ||
| t.ifError(err); | ||
| t.equals(self.writer.getContents(), line + "\n"); | ||
| t.done(); | ||
| }, 10); | ||
| }, | ||
| 'fflush should write pending data': function(t) { | ||
| var line = "test line"; | ||
| this.fp.fputs(line); | ||
| var self = this; | ||
| t.expect(2); | ||
| this.fp.fflush(function(err) { | ||
| t.ifError(err); | ||
| var contents = self.writer.getContents(); | ||
| t.equals(contents, line + "\n"); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'fflush should return write errors': function(t) { | ||
| writer = new Fputs.FileWriter("/nonesuch", "a"); | ||
| fp = new Fputs(writer); | ||
| fp.write("data"); | ||
| fp.fflush(function(err) { | ||
| t.ok(err instanceof Error); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'abort should discard unwritten data': function(t) { | ||
| t.expect(4); | ||
| this.fp.fputs("test 1\n"); | ||
| t.ok(this.fp._syncing); | ||
| t.ok(this.fp.datachunks[0]); | ||
| var self = this; | ||
| this.fp.abort(function(err) { | ||
| t.ifError(err); | ||
| var contents = self.writer.getContents(); | ||
| t.equals(contents, ""); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'setOnError handler should report errors before fflush callback runs': function(t) { | ||
| var writer = new Fputs.FileWriter("/nonesuch", "a"); | ||
| var fp = new Fputs(writer); | ||
| var error = null; | ||
| fp.setOnError(function(err) { error = err }); | ||
| fp.write("data"); | ||
| fp.fflush(function(err) { | ||
| t.ok(!err, "fflush callback should not return error"); | ||
| t.ok(error instanceof Error, "setOnError should have reported the error"); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'getUnwrittenLength': { | ||
| 'should return unwritten chars for strings': function(t) { | ||
| var self = this; | ||
| this.fp.write("test\x81\n"); | ||
| t.equal(this.fp.getUnwrittenLength(), 6); | ||
| this.fp.write("test\x82\n"); | ||
| t.equal(this.fp.getUnwrittenLength(), 12); | ||
| this.fp.fflush(function(err) { | ||
| t.equal(self.fp.getUnwrittenLength(), 0); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'should return unwritten bytes for string followed by buffer': function(t) { | ||
| var self = this; | ||
| this.fp.write("test\x81\n"); | ||
| this.fp.write("test\x82\n"); | ||
| this.fp.write(new Buffer("test3\n")); | ||
| t.equal(this.fp.getUnwrittenLength(), 20); | ||
| t.done(); | ||
| }, | ||
| }, | ||
| 'drain should write pending data': function(t) { | ||
| this.fp.fputs("test 1\n"); | ||
| var self = this; | ||
| this.fp.drain(0, function(err) { | ||
| t.ifError(err); | ||
| t.equal(self.writer.getContents(), "test 1\n"); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'drain should return write errors': function(t) { | ||
| writer = new Fputs.FileWriter("/nonesuch", "a"); | ||
| fp = new Fputs(writer); | ||
| fp.write("data"); | ||
| fp.drain(0, function(err) { | ||
| t.ok(err instanceof Error); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'constructor should accept a filename': function(t) { | ||
| var fp = new Fputs(this.tempfile); | ||
| fp.fputs("test"); | ||
| var self = this; | ||
| fp.fflush(function(err) { | ||
| t.ifError(err); | ||
| t.equals(fs.readFileSync(self.tempfile), "test\n"); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'constructor should accept a writable object': function(t) { | ||
| t.expect(1); | ||
| var self = this; | ||
| writer = fs.createWriteStream(this.tempfile, {flags: "a"}); | ||
| var fp = new Fputs(writer); | ||
| fp.write("data"); | ||
| fp.fflush(function() { | ||
| var contents = "" + fs.readFileSync(self.tempfile); | ||
| t.equals(contents, "data"); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'write': { | ||
| 'write should write contents, without newline': function(t) { | ||
| var line = "test line " + uniqid(); | ||
| this.fp.write(line); | ||
| var self = this; | ||
| this.fp.fflush(function() { | ||
| var contents = self.writer.getContents(); | ||
| t.equals(contents, line); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'write should take an optional callback': function(t) { | ||
| t.expect(1); | ||
| var self = this; | ||
| this.fp.write("test callback", function(err) { | ||
| t.ifError(err); | ||
| // wait for the write to finish so tempfile can be removed. | ||
| // Nodejs is async, the pending write can land in the next tests tempfile. | ||
| self.fp.fflush(function() { | ||
| t.done(); | ||
| }); | ||
| }); | ||
| }, | ||
| 'write should return true if buffer has room': function(t) { | ||
| var fp = this.fp; | ||
| var ok = fp.write("test"); | ||
| t.equal(ok, true); | ||
| t.done(); | ||
| }, | ||
| 'write should return false if buffer is full': function(t) { | ||
| var fp = this.fp; | ||
| var nleft = fp.highWaterMark; | ||
| while (nleft > 0) { | ||
| fp.write("xxxxxxxxxxxxxxxxxxxx"); | ||
| nleft -= 20; | ||
| } | ||
| var ok = fp.write("test"); | ||
| t.equal(ok, false); | ||
| t.done(); | ||
| }, | ||
| 'should write many lines of various sizes': function(t) { | ||
| var i, line = "", expect = ""; | ||
| for (i=0; i<2000; i++) { | ||
| line += "xxxx"; | ||
| expect += line + "\n"; | ||
| this.fp.fputs(line); | ||
| } | ||
| var tempfile = this.tempfile; | ||
| t.expect(2); | ||
| var self = this; | ||
| this.fp.fflush(function(err) { | ||
| t.ifError(err); | ||
| var contents = "" + self.writer.getContents(tempfile); | ||
| t.equals(contents, expect); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'should write buffers': function(t) { | ||
| var written = []; | ||
| var writer = { | ||
| write: function(s, cb) { written.push(s.toString()); if (cb) cb() } | ||
| } | ||
| var fp = new Fputs(writer, {writesize: 20}); | ||
| var expect = ""; | ||
| for (var i=0; i<100; i++) { | ||
| var line = "test " + i + "\n"; | ||
| fp.write(new Buffer(line)); | ||
| expect += line; | ||
| } | ||
| fp.fflush(function(err){ | ||
| t.ifError(err); | ||
| t.equal(written.join(''), expect); | ||
| t.ok(written.length > 25 && written.length < 50); | ||
| t.done(); | ||
| }) | ||
| }, | ||
| 'should write mix of strings and bufferse': function(t) { | ||
| var written = []; | ||
| var writer = { | ||
| write: function(s, cb) { written.push(s.toString()); if (cb) cb() } | ||
| } | ||
| var fp = new Fputs(writer, {writesize: 20}); | ||
| var expect = ""; | ||
| for (var i=0; i<100; i++) { | ||
| var line = "test " + i + "\n"; | ||
| fp.write(i % 2 ? new Buffer(line) : line); | ||
| expect += line; | ||
| } | ||
| fp.fflush(function(err){ | ||
| t.ifError(err); | ||
| t.equal(written.join(''), expect); | ||
| t.ok(written.length > 10 && written.length < 25); | ||
| t.done(); | ||
| }) | ||
| }, | ||
| }, | ||
| 'FileWriter': { | ||
| 'FileWriter.write should write entire string': function(t) { | ||
| var self = this; | ||
| this.fileWriter.write("test123", function(err) { | ||
| t.equal(fs.readFileSync(self.tempfile), "test123"); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'FileWriter.write should write buffers': function(t) { | ||
| var self = this; | ||
| this.fileWriter.write(new Buffer("test123"), function(err) { | ||
| t.equal(fs.readFileSync(self.tempfile), "test123"); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'FileWriter.renameFile should rename file': function(t) { | ||
| var self = this; | ||
| fs.writeFileSync(this.tempfile, "test"); | ||
| Fputs.FileWriter.renameFile(self.tempfile, self.tempfile2, function(err) { | ||
| t.equals(fs.readFileSync(self.tempfile2).toString(), "test"); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'FileWriter.renameFile should pause N ms': function(t) { | ||
| var self = this; | ||
| fs.writeFileSync(this.tempfile, "test2"); | ||
| var t1 = Date.now(); | ||
| t.expect(1); | ||
| Fputs.FileWriter.renameFile(self.tempfile, self.tempfile2, 66, function(err) { | ||
| t.ok(Date.now() >= t1 + 66); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'FileWriter.renameFile should return EEXIST error, not pause and not overwrite if target already exists': function(t) { | ||
| var self = this; | ||
| fs.writeFileSync(this.tempfile, "test3a"); | ||
| fs.writeFileSync(this.tempfile2, "test3b"); | ||
| var t1 = Date.now(); | ||
| t.expect(4); | ||
| Fputs.FileWriter.renameFile(self.tempfile, self.tempfile2, 66, function(err, ret) { | ||
| t.ok(Date.now() < t1 + 5); | ||
| t.ok(err instanceof Error); | ||
| t.ok(err.message.indexOf('EEXIST') === 0); | ||
| t.equal(fs.readFileSync(self.tempfile2), "test3b"); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'should expose FileWriter.renameFile on fputs instances': function(t) { | ||
| var fp = new Fputs(this.fileWriter); | ||
| t.equals(Fputs.FileWriter.renameFile, fp.renameFile); | ||
| t.done(); | ||
| }, | ||
| 'FileWriter.renameFile should wait for ongoing write to finish': function(t) { | ||
| fs.writeFileSync(this.tempfile, "test4"); | ||
| var fd = fs.openSync(this.tempfile, 'r'); | ||
| fse.flockSync(fd, 'ex') ; | ||
| var t1 = Date.now(); | ||
| setTimeout(function(){ fse.flockSync(fd, 'un'); fs.closeSync(fd) }, 125); | ||
| var self = this; | ||
| t.expect(3); | ||
| Fputs.FileWriter.renameFile(this.tempfile, this.tempfile2, function(err, ret) { | ||
| t.ifError(err); | ||
| t.ok(Date.now() >= t1 + 125); | ||
| t.equal(fs.readFileSync(self.tempfile2).toString(), "test4"); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| 'FileWriter.renameFile should time out after mutexTimeout': function(t) { | ||
| fs.writeFileSync(this.tempfile, "test4"); | ||
| var fd = fs.openSync(this.tempfile, 'r'); | ||
| fse.flockSync(fd, 'ex') ; | ||
| var t1 = Date.now(); | ||
| setTimeout(function(){ fse.flockSync(fd, 'un'); fs.closeSync(fd) }, 200); | ||
| var self = this; | ||
| t.expect(3); | ||
| Fputs.FileWriter.renameFile(this.tempfile, this.tempfile2, {mutexTimeout: 125, waitMs: 10}, function(err, ret) { | ||
| t.ok(err); | ||
| t.ok(Date.now() >= t1 + 125); | ||
| t.ok(Date.now() < t1 + 150); | ||
| // note: node does not exit while fd is locked | ||
| fse.flockSync(fd, 'un'); | ||
| t.done(); | ||
| }); | ||
| }, | ||
| } | ||
| } |
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
302
1%1
-66.67%28852
-33.87%6
-33.33%425
-48.92%1
Infinity%+ Added
- Removed
- Removed