file-timestamp-stream
Advanced tools
Comparing version 1.0.0 to 1.1.0
# Changelog | ||
## v1.1.0 2018-09-12 | ||
* Clean up streams more carefully after they are not needed anymore. | ||
## v1.0.0 2018-09-07 | ||
@@ -4,0 +8,0 @@ |
@@ -17,5 +17,6 @@ /// <reference types="node" /> | ||
newFilename: (fileTimestampStream: FileTimestampStream) => string; | ||
private streamErrorHandler; | ||
private streams; | ||
private streamCancelFinishers; | ||
private streamErrorHandlers; | ||
constructor(options?: FileTimestampStreamOptions); | ||
_rotate(): void; | ||
_write(chunk: any, encoding: string, callback: (error?: Error | null) => void): void; | ||
@@ -28,3 +29,4 @@ _writev(chunks: Array<{ | ||
_destroy(error: Error | null, callback: (error: Error | null) => void): void; | ||
private rotate; | ||
} | ||
export default FileTimestampStream; |
@@ -8,5 +8,12 @@ "use strict"; | ||
const ultra_strftime_1 = tslib_1.__importDefault(require("ultra-strftime")); | ||
// tslint:disable-next-line:no-var-requires | ||
const finished = require('stream.finished'); // TODO: wait for new typings for node | ||
// tslint:disable-next-line:strict-type-predicates | ||
const HAS_DESTROY = typeof stream_1.Writable.prototype.destroy === 'function'; | ||
class FileTimestampStream extends stream_1.Writable { | ||
constructor(options = {}) { | ||
super(options); | ||
this.streams = new Map(); | ||
this.streamCancelFinishers = new Map(); | ||
this.streamErrorHandlers = new Map(); | ||
this.newFilename = options.newFilename || defaultNewFilename; | ||
@@ -16,23 +23,6 @@ this.flags = options.flags || 'a'; | ||
this.path = options.path || 'out.log'; | ||
this.streamErrorHandler = (err) => { | ||
this.emit('error', err); | ||
}; | ||
} | ||
_rotate() { | ||
const newFilename = this.newFilename(this); | ||
if (newFilename !== this.currentFilename) { | ||
if (this.stream) { | ||
this.stream.end(); | ||
this.stream.removeListener('error', this.streamErrorHandler); | ||
} | ||
this.stream = this.fs.createWriteStream(newFilename, { | ||
flags: this.flags | ||
}); | ||
this.stream.on('error', this.streamErrorHandler); | ||
this.currentFilename = newFilename; | ||
} | ||
} | ||
_write(chunk, encoding, callback) { | ||
try { | ||
this._rotate(); | ||
this.rotate(); | ||
this.stream.write(chunk, encoding, callback); | ||
@@ -47,3 +37,3 @@ } | ||
try { | ||
this._rotate(); | ||
this.rotate(); | ||
corked = true; | ||
@@ -73,8 +63,27 @@ this.stream.cork(); | ||
_destroy(error, callback) { | ||
if (this.stream) { | ||
this.stream.destroy(); | ||
this.stream.removeListener('error', this.streamErrorHandler); | ||
delete this.streamErrorHandler; | ||
delete this.stream; | ||
if (this.streamErrorHandlers.size > 0) { | ||
this.streamErrorHandlers.forEach((handler, filename) => { | ||
const stream = this.streams.get(filename); | ||
if (stream) { | ||
stream.removeListener('error', handler); | ||
} | ||
}); | ||
this.streamErrorHandlers.clear(); | ||
} | ||
if (HAS_DESTROY) { | ||
if (this.streamCancelFinishers.size > 0) { | ||
this.streamCancelFinishers.forEach((cancel, filename) => { | ||
cancel(); | ||
this.streamCancelFinishers.delete(filename); | ||
}); | ||
this.streamCancelFinishers.clear(); | ||
} | ||
} | ||
if (this.streams.size > 0) { | ||
this.streams.forEach((stream) => { | ||
stream.destroy(); | ||
}); | ||
this.streams.clear(); | ||
} | ||
this.stream = undefined; | ||
this.newFilename = (_fileTimestampStream) => { | ||
@@ -85,2 +94,37 @@ throw new Error('write after destroy'); | ||
} | ||
rotate() { | ||
const newFilename = this.newFilename(this); | ||
if (newFilename !== this.currentFilename) { | ||
if (this.currentFilename && this.stream) { | ||
this.stream.end(); | ||
const streamErrorHandler = this.streamErrorHandlers.get(this.currentFilename); | ||
if (streamErrorHandler) { | ||
this.stream.removeListener('error', streamErrorHandler); | ||
this.streamErrorHandlers.delete(this.currentFilename); | ||
} | ||
if (!HAS_DESTROY) { | ||
this.streams.delete(this.currentFilename); | ||
} | ||
} | ||
const newStream = this.fs.createWriteStream(newFilename, { | ||
flags: this.flags | ||
}); | ||
this.stream = newStream; | ||
this.streams.set(newFilename, newStream); | ||
const newStreamErrorHandler = (err) => { | ||
this.emit('error', err); | ||
}; | ||
newStream.on('error', newStreamErrorHandler); | ||
this.streamErrorHandlers.set(newFilename, newStreamErrorHandler); | ||
if (HAS_DESTROY) { | ||
const newStreamCancelFinisher = finished(newStream, () => { | ||
newStream.destroy(); | ||
this.streamCancelFinishers.delete(newFilename); | ||
this.streams.delete(newFilename); | ||
}); | ||
this.streamCancelFinishers.set(newFilename, newStreamCancelFinisher); | ||
} | ||
this.currentFilename = newFilename; | ||
} | ||
} | ||
} | ||
@@ -87,0 +131,0 @@ exports.FileTimestampStream = FileTimestampStream; |
{ | ||
"name": "file-timestamp-stream", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Writing stream with file rotating based on timestamp", | ||
@@ -26,2 +26,3 @@ "main": "lib/file-timestamp-stream.js", | ||
"dependencies": { | ||
"stream.finished": "^1.0.1", | ||
"tslib": "^1.9.3", | ||
@@ -36,2 +37,3 @@ "ultra-strftime": "^1.0.2" | ||
"chai": "^4.1.2", | ||
"coveralls": "^3.0.2", | ||
"eslint": "^5.5.0", | ||
@@ -56,3 +58,3 @@ "eslint-config-standard": "^12.0.0", | ||
"prepublishOnly": "npm run build", | ||
"pretest": "npm run build && eslint . && tslint -t stylish -p . && markdownlint \"*.md\"", | ||
"pretest": "npm run build && tsc --pretty -p examples && tsc --pretty -p test && eslint . && tslint -t stylish -p . && tslint -t stylish -p examples && tslint -t stylish -p test && markdownlint \"*.md\"", | ||
"test": "npm run test:spec", | ||
@@ -68,2 +70,3 @@ "test:spec": "npm run ts-mocha -- \"test/*.ts\"", | ||
"exclude": [ | ||
".*.js", | ||
"**/*.d.ts" | ||
@@ -70,0 +73,0 @@ ] |
@@ -27,2 +27,14 @@ # file-timestamp-stream | ||
Transpiling this module with own settings in `tsconfig.json`: | ||
```json | ||
{ | ||
"compilerOptions": { | ||
"paths": { | ||
"file-timestamp-stream": ["node_modules/file-timestamp-stream/src/file-timestamp-stream"] | ||
} | ||
} | ||
} | ||
``` | ||
## Usage | ||
@@ -42,14 +54,2 @@ | ||
Transpiling this module with own settings in `tsconfig.json`: | ||
```json | ||
{ | ||
"compilerOptions": { | ||
"paths": { | ||
"file-timestamp-stream": ["node_modules/file-timestamp-stream/src/file-timestamp-stream"] | ||
} | ||
} | ||
} | ||
``` | ||
### Options | ||
@@ -56,0 +56,0 @@ |
@@ -7,2 +7,8 @@ /// <reference types="node" /> | ||
// tslint:disable-next-line:no-var-requires | ||
const finished = require('stream.finished') as (stream: NodeJS.ReadableStream | NodeJS.WritableStream | NodeJS.ReadWriteStream, callback?: (err: NodeJS.ErrnoException) => void) => () => void // TODO: wait for new typings for node | ||
// tslint:disable-next-line:strict-type-predicates | ||
const HAS_DESTROY = typeof Writable.prototype.destroy === 'function' | ||
export interface FileTimestampStreamOptions extends WritableOptions { | ||
@@ -24,3 +30,5 @@ flags?: string | null | ||
private streamErrorHandler: (err: Error) => void | ||
private streams: Map<string, Writable> = new Map() | ||
private streamCancelFinishers: Map<string, () => void> = new Map() | ||
private streamErrorHandlers: Map<string, (err: Error) => void> = new Map() | ||
@@ -35,30 +43,7 @@ constructor (options: FileTimestampStreamOptions = {}) { | ||
this.path = options.path || 'out.log' | ||
this.streamErrorHandler = (err) => { | ||
this.emit('error', err) | ||
} | ||
} | ||
_rotate (): void { | ||
const newFilename = this.newFilename(this) | ||
if (newFilename !== this.currentFilename) { | ||
if (this.stream) { | ||
this.stream.end() | ||
this.stream.removeListener('error', this.streamErrorHandler) | ||
} | ||
this.stream = this.fs.createWriteStream(newFilename, { | ||
flags: this.flags | ||
}) | ||
this.stream.on('error', this.streamErrorHandler) | ||
this.currentFilename = newFilename | ||
} | ||
} | ||
_write (chunk: any, encoding: string, callback: (error?: Error | null) => void): void { | ||
try { | ||
this._rotate() | ||
this.rotate() | ||
this.stream!.write(chunk, encoding, callback) | ||
@@ -73,3 +58,3 @@ } catch (e) { | ||
try { | ||
this._rotate() | ||
this.rotate() | ||
corked = true | ||
@@ -99,13 +84,76 @@ this.stream!.cork() | ||
_destroy (error: Error | null, callback: (error: Error | null) => void): void { | ||
if (this.stream) { | ||
this.stream.destroy() | ||
this.stream.removeListener('error', this.streamErrorHandler) | ||
delete this.streamErrorHandler | ||
delete this.stream | ||
if (this.streamErrorHandlers.size > 0) { | ||
this.streamErrorHandlers.forEach((handler, filename) => { | ||
const stream = this.streams.get(filename) | ||
if (stream) { | ||
stream.removeListener('error', handler) | ||
} | ||
}) | ||
this.streamErrorHandlers.clear() | ||
} | ||
if (HAS_DESTROY) { | ||
if (this.streamCancelFinishers.size > 0) { | ||
this.streamCancelFinishers.forEach((cancel, filename) => { | ||
cancel() | ||
this.streamCancelFinishers.delete(filename) | ||
}) | ||
this.streamCancelFinishers.clear() | ||
} | ||
} | ||
if (this.streams.size > 0) { | ||
this.streams.forEach((stream) => { | ||
stream.destroy() | ||
}) | ||
this.streams.clear() | ||
} | ||
this.stream = undefined | ||
this.newFilename = (_fileTimestampStream: any) => { | ||
throw new Error('write after destroy') | ||
} | ||
callback(error) | ||
} | ||
private rotate (): void { | ||
const newFilename = this.newFilename(this) | ||
if (newFilename !== this.currentFilename) { | ||
if (this.currentFilename && this.stream) { | ||
this.stream.end() | ||
const streamErrorHandler = this.streamErrorHandlers.get(this.currentFilename) | ||
if (streamErrorHandler) { | ||
this.stream.removeListener('error', streamErrorHandler) | ||
this.streamErrorHandlers.delete(this.currentFilename) | ||
} | ||
if (!HAS_DESTROY) { | ||
this.streams.delete(this.currentFilename) | ||
} | ||
} | ||
const newStream = this.fs.createWriteStream(newFilename, { | ||
flags: this.flags | ||
}) | ||
this.stream = newStream | ||
this.streams.set(newFilename, newStream) | ||
const newStreamErrorHandler = (err: Error) => { | ||
this.emit('error', err) | ||
} | ||
newStream.on('error', newStreamErrorHandler) | ||
this.streamErrorHandlers.set(newFilename, newStreamErrorHandler) | ||
if (HAS_DESTROY) { | ||
const newStreamCancelFinisher = finished(newStream, () => { | ||
newStream.destroy() | ||
this.streamCancelFinishers.delete(newFilename) | ||
this.streams.delete(newFilename) | ||
}) | ||
this.streamCancelFinishers.set(newFilename, newStreamCancelFinisher) | ||
} | ||
this.currentFilename = newFilename | ||
} | ||
} | ||
} | ||
@@ -112,0 +160,0 @@ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
18737
322
0
3
19
+ Addedstream.finished@^1.0.1
+ Addeddefine-data-property@1.1.4(transitive)
+ Addeddefine-properties@1.2.1(transitive)
+ Addedes-define-property@1.0.0(transitive)
+ Addedes-errors@1.3.0(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedget-intrinsic@1.2.4(transitive)
+ Addedgopd@1.0.1(transitive)
+ Addedhas-property-descriptors@1.0.2(transitive)
+ Addedhas-proto@1.0.3(transitive)
+ Addedhas-symbols@1.0.3(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedobject-keys@1.1.1(transitive)
+ Addedstream.finished@1.2.0(transitive)