add hw2
This commit is contained in:
2
node_modules/sonic-boom/.eslintignore
generated
vendored
Normal file
2
node_modules/sonic-boom/.eslintignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
types/index.d.ts
|
||||
types/index.test-d.ts
|
||||
4
node_modules/sonic-boom/.husky/pre-commit
generated
vendored
Executable file
4
node_modules/sonic-boom/.husky/pre-commit
generated
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npm test
|
||||
11
node_modules/sonic-boom/.taprc.yaml
generated
vendored
Normal file
11
node_modules/sonic-boom/.taprc.yaml
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
coverage: true
|
||||
flow: false
|
||||
ts: false
|
||||
jsx: false
|
||||
timeout: 240
|
||||
check-coverage: false
|
||||
|
||||
reporter: terse
|
||||
|
||||
files:
|
||||
- 'test/**/*.test.js'
|
||||
21
node_modules/sonic-boom/LICENSE
generated
vendored
Normal file
21
node_modules/sonic-boom/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Matteo Collina
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
151
node_modules/sonic-boom/README.md
generated
vendored
Normal file
151
node_modules/sonic-boom/README.md
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
# sonic-boom
|
||||
|
||||
[](https://www.npmjs.com/package/sonic-boom)
|
||||
[](https://github.com/pinojs/sonic-boom/actions?query=workflow%3ACI)
|
||||
[](https://standardjs.com/)
|
||||
|
||||
Extremely fast utf8-only stream implementation to write to files and
|
||||
file descriptors.
|
||||
|
||||
This implementation is partial, but support backpressure and `.pipe()` in is here.
|
||||
However, it is 2-3x faster than Node Core `fs.createWriteStream()`:
|
||||
|
||||
```
|
||||
benchSonic*1000: 1916.904ms
|
||||
benchSonicSync*1000: 8605.265ms
|
||||
benchSonic4k*1000: 1965.231ms
|
||||
benchSonicSync4k*1000: 1588.224ms
|
||||
benchCore*1000: 5851.959ms
|
||||
benchConsole*1000: 7605.713ms
|
||||
```
|
||||
|
||||
Note that sync mode without buffering is _slower_ than a Node Core WritableStream, however
|
||||
this mode matches the expected behavior of `console.log()`.
|
||||
|
||||
Note that if this is used to log to a windows terminal (`cmd.exe` or
|
||||
powershell), it is needed to run `chcp 65001` in the terminal to
|
||||
correctly display utf-8 characters, see
|
||||
[chcp](https://ss64.com/nt/chcp.html) for more details.
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
npm i sonic-boom
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
'use strict'
|
||||
|
||||
const SonicBoom = require('sonic-boom')
|
||||
const sonic = new SonicBoom({ fd: process.stdout.fd }) // or { dest: '/path/to/destination' }
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
sonic.write('hello sonic\n')
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### SonicBoom(opts)
|
||||
|
||||
Creates a new instance of SonicBoom.
|
||||
|
||||
The options are:
|
||||
|
||||
* `fd`: a file descriptor, something that is returned by `fs.open` or
|
||||
`fs.openSync`.
|
||||
* `dest`: a string that is a path to a file to be written to (mode controlled by the `append` option).
|
||||
* `minLength`: the minimum length of the internal buffer that is
|
||||
required to be full before flushing.
|
||||
* `maxLength`: the maximum length of the internal buffer. If a write operation would cause the buffer
|
||||
to exceed `maxLength`, the data written is dropped and a `drop` event is emitted with the dropped data
|
||||
* `maxWrite`: the maximum number of bytes that can be written; default: 16384
|
||||
* `sync`: perform writes synchronously (similar to `console.log`).
|
||||
* `fsync`: perform a [fsyncSync](https://nodejs.org/api/fs.html#fsfsyncsyncfd) every time a write is completed.
|
||||
* `append`: appends writes to dest file instead of truncating it (default `true`).
|
||||
* `mode`: specify the creating file `mode` (see [fs.open()](https://nodejs.org/api/fs.html#fsopenpath-flags-mode-callback) from Node.js core).
|
||||
* `mkdir`: ensure directory for dest file exists when `true` (default `false`).
|
||||
* `retryEAGAIN(err, writeBufferLen, remainingBufferLen)`: a function that will be called when sonic-boom
|
||||
write/writeSync/flushSync encounters a EAGAIN or EBUSY error. If the return value is
|
||||
true sonic-boom will retry the operation, otherwise it will bubble the
|
||||
error. `err` is the error that caused this function to be called,
|
||||
`writeBufferLen` is the length of the buffer sonic-boom tried to write, and
|
||||
`remainingBufferLen` is the length of the remaining buffer sonic-boom didn't try to write.
|
||||
|
||||
For `sync:false` a `SonicBoom` instance will emit the `'ready'` event when a file descriptor is available.
|
||||
For `sync:true` this is not relevant because the `'ready'` event will be fired when the `SonicBoom` instance is created, before it can be subscribed to.
|
||||
|
||||
|
||||
### SonicBoom#write(string)
|
||||
|
||||
Writes the string to the file.
|
||||
It will return false to signal the producer to slow down.
|
||||
|
||||
### SonicBoom#flush([cb])
|
||||
|
||||
Writes the current buffer to the file if a write was not in progress.
|
||||
Do nothing if `minLength` is zero or if it is already writing.
|
||||
|
||||
call the callback when the flush operation is completed. when failed the callback is called with an error.
|
||||
|
||||
### SonicBoom#reopen([file])
|
||||
|
||||
Reopen the file in place, useful for log rotation.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
const stream = new SonicBoom('./my.log')
|
||||
process.on('SIGUSR2', function () {
|
||||
stream.reopen()
|
||||
})
|
||||
```
|
||||
|
||||
### SonicBoom#flushSync()
|
||||
|
||||
Flushes the buffered data synchronously. This is a costly operation.
|
||||
|
||||
### SonicBoom#end()
|
||||
|
||||
Closes the stream, the data will be flushed down asynchronously
|
||||
|
||||
### SonicBoom#destroy()
|
||||
|
||||
Closes the stream immediately, the data is not flushed.
|
||||
|
||||
### Events
|
||||
|
||||
|
||||
#### SonicBoom#close
|
||||
|
||||
See [Stream#close](https://nodejs.org/api/stream.html#event-close). The `'close'` event when the instance has been closed.
|
||||
|
||||
#### SonicBoom#drain
|
||||
|
||||
See [Stream#drain](https://nodejs.org/api/stream.html#event-drain). The `'drain'` event is emitted when source can resume sending data.
|
||||
|
||||
#### SonicBoom#drop <any>
|
||||
|
||||
When destination file maximal length is reached, the `'drop'` event is emitted with data that could not be written.
|
||||
|
||||
#### SonicBoom#error <Error>
|
||||
|
||||
The `'error'` event is emitted when the destination file can not be opened, or written.
|
||||
|
||||
#### SonicBoom#finish
|
||||
|
||||
See [Stream#finish](https://nodejs.org/api/stream.html#event-finish). The `'finish'` event after calling `end()` method and when all data was written.
|
||||
|
||||
#### SonicBoom#ready
|
||||
|
||||
The `'ready'` event occurs when the created instance is ready to process input.
|
||||
|
||||
#### SonicBoom#write <number>
|
||||
|
||||
The `'write'` event occurs every time data is written to the underlying file. It emits the number of written bytes.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
98
node_modules/sonic-boom/bench.js
generated
vendored
Normal file
98
node_modules/sonic-boom/bench.js
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
'use strict'
|
||||
|
||||
const bench = require('fastbench')
|
||||
const SonicBoom = require('./')
|
||||
const Console = require('console').Console
|
||||
const fs = require('fs')
|
||||
|
||||
const core = fs.createWriteStream('/dev/null')
|
||||
const fd = fs.openSync('/dev/null', 'w')
|
||||
const sonic = new SonicBoom({ fd })
|
||||
const sonic4k = new SonicBoom({ fd, minLength: 4096 })
|
||||
const sonicSync = new SonicBoom({ fd, sync: true })
|
||||
const sonicSync4k = new SonicBoom({ fd, minLength: 4096, sync: true })
|
||||
const sonicBuffer = new SonicBoom({ fd, contentMode: 'buffer' })
|
||||
const sonic4kBuffer = new SonicBoom({ fd, contentMode: 'buffer', minLength: 4096 })
|
||||
const sonicSyncBuffer = new SonicBoom({ fd, contentMode: 'buffer', sync: true })
|
||||
const sonicSync4kBuffer = new SonicBoom({ fd, contentMode: 'buffer', minLength: 4096, sync: true })
|
||||
const dummyConsole = new Console(fs.createWriteStream('/dev/null'))
|
||||
|
||||
const MAX = 10000
|
||||
|
||||
const buf = Buffer.alloc(50, 'hello', 'utf8')
|
||||
const str = buf.toString()
|
||||
|
||||
setTimeout(doBench, 100)
|
||||
|
||||
const run = bench([
|
||||
function benchSonic (cb) {
|
||||
sonic.once('drain', cb)
|
||||
for (let i = 0; i < MAX; i++) {
|
||||
sonic.write(str)
|
||||
}
|
||||
},
|
||||
function benchSonicSync (cb) {
|
||||
sonicSync.once('drain', cb)
|
||||
for (let i = 0; i < MAX; i++) {
|
||||
sonicSync.write(str)
|
||||
}
|
||||
},
|
||||
function benchSonic4k (cb) {
|
||||
sonic4k.once('drain', cb)
|
||||
for (let i = 0; i < MAX; i++) {
|
||||
sonic4k.write(str)
|
||||
}
|
||||
},
|
||||
function benchSonicSync4k (cb) {
|
||||
sonicSync4k.once('drain', cb)
|
||||
for (let i = 0; i < MAX; i++) {
|
||||
sonicSync4k.write(str)
|
||||
}
|
||||
},
|
||||
function benchCore (cb) {
|
||||
core.once('drain', cb)
|
||||
for (let i = 0; i < MAX; i++) {
|
||||
core.write(str)
|
||||
}
|
||||
},
|
||||
function benchConsole (cb) {
|
||||
for (let i = 0; i < MAX; i++) {
|
||||
dummyConsole.log(str)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchSonicBuf (cb) {
|
||||
sonicBuffer.once('drain', cb)
|
||||
for (let i = 0; i < MAX; i++) {
|
||||
sonicBuffer.write(buf)
|
||||
}
|
||||
},
|
||||
function benchSonicSyncBuf (cb) {
|
||||
sonicSyncBuffer.once('drain', cb)
|
||||
for (let i = 0; i < MAX; i++) {
|
||||
sonicSyncBuffer.write(buf)
|
||||
}
|
||||
},
|
||||
function benchSonic4kBuf (cb) {
|
||||
sonic4kBuffer.once('drain', cb)
|
||||
for (let i = 0; i < MAX; i++) {
|
||||
sonic4kBuffer.write(buf)
|
||||
}
|
||||
},
|
||||
function benchSonicSync4kBuf (cb) {
|
||||
sonicSync4kBuffer.once('drain', cb)
|
||||
for (let i = 0; i < MAX; i++) {
|
||||
sonicSync4kBuffer.write(buf)
|
||||
}
|
||||
},
|
||||
function benchCoreBuf (cb) {
|
||||
core.once('drain', cb)
|
||||
for (let i = 0; i < MAX; i++) {
|
||||
core.write(buf)
|
||||
}
|
||||
}
|
||||
], 1000)
|
||||
|
||||
function doBench () {
|
||||
run(run)
|
||||
}
|
||||
18
node_modules/sonic-boom/check.js
generated
vendored
Normal file
18
node_modules/sonic-boom/check.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
'use strict'
|
||||
|
||||
const SonicBoom = require('.')
|
||||
const sonic = new SonicBoom({ fd: process.stdout.fd })
|
||||
|
||||
let count = 0
|
||||
function scheduleWrites () {
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
sonic.write('hello sonic\n')
|
||||
console.log('hello console')
|
||||
}
|
||||
|
||||
if (++count < 10) {
|
||||
setTimeout(scheduleWrites, 100)
|
||||
}
|
||||
}
|
||||
|
||||
scheduleWrites()
|
||||
8
node_modules/sonic-boom/example.js
generated
vendored
Normal file
8
node_modules/sonic-boom/example.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
const SonicBoom = require('.')
|
||||
const sonic = new SonicBoom({ fd: process.stdout.fd }) // or 'destination'
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
sonic.write('hello sonic\n')
|
||||
}
|
||||
22
node_modules/sonic-boom/fixtures/firehose.js
generated
vendored
Normal file
22
node_modules/sonic-boom/fixtures/firehose.js
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
'use strict'
|
||||
|
||||
const SonicBoom = require('..')
|
||||
|
||||
const out = new SonicBoom({ fd: process.stdout.fd })
|
||||
const str = Buffer.alloc(1000).fill('a').toString()
|
||||
|
||||
let i = 0
|
||||
|
||||
function write () {
|
||||
if (i++ === 10) {
|
||||
return
|
||||
}
|
||||
|
||||
if (out.write(str)) {
|
||||
write()
|
||||
} else {
|
||||
out.once('drain', write)
|
||||
}
|
||||
}
|
||||
|
||||
write()
|
||||
690
node_modules/sonic-boom/index.js
generated
vendored
Normal file
690
node_modules/sonic-boom/index.js
generated
vendored
Normal file
@@ -0,0 +1,690 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const EventEmitter = require('events')
|
||||
const inherits = require('util').inherits
|
||||
const path = require('path')
|
||||
const sleep = require('atomic-sleep')
|
||||
|
||||
const BUSY_WRITE_TIMEOUT = 100
|
||||
const kEmptyBuffer = Buffer.allocUnsafe(0)
|
||||
|
||||
// 16 KB. Don't write more than docker buffer size.
|
||||
// https://github.com/moby/moby/blob/513ec73831269947d38a644c278ce3cac36783b2/daemon/logger/copier.go#L13
|
||||
const MAX_WRITE = 16 * 1024
|
||||
|
||||
const kContentModeBuffer = 'buffer'
|
||||
const kContentModeUtf8 = 'utf8'
|
||||
|
||||
function openFile (file, sonic) {
|
||||
sonic._opening = true
|
||||
sonic._writing = true
|
||||
sonic._asyncDrainScheduled = false
|
||||
|
||||
// NOTE: 'error' and 'ready' events emitted below only relevant when sonic.sync===false
|
||||
// for sync mode, there is no way to add a listener that will receive these
|
||||
|
||||
function fileOpened (err, fd) {
|
||||
if (err) {
|
||||
sonic._reopening = false
|
||||
sonic._writing = false
|
||||
sonic._opening = false
|
||||
|
||||
if (sonic.sync) {
|
||||
process.nextTick(() => {
|
||||
if (sonic.listenerCount('error') > 0) {
|
||||
sonic.emit('error', err)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
sonic.emit('error', err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const reopening = sonic._reopening
|
||||
|
||||
sonic.fd = fd
|
||||
sonic.file = file
|
||||
sonic._reopening = false
|
||||
sonic._opening = false
|
||||
sonic._writing = false
|
||||
|
||||
if (sonic.sync) {
|
||||
process.nextTick(() => sonic.emit('ready'))
|
||||
} else {
|
||||
sonic.emit('ready')
|
||||
}
|
||||
|
||||
if (sonic.destroyed) {
|
||||
return
|
||||
}
|
||||
|
||||
// start
|
||||
if ((!sonic._writing && sonic._len > sonic.minLength) || sonic._flushPending) {
|
||||
sonic._actualWrite()
|
||||
} else if (reopening) {
|
||||
process.nextTick(() => sonic.emit('drain'))
|
||||
}
|
||||
}
|
||||
|
||||
const flags = sonic.append ? 'a' : 'w'
|
||||
const mode = sonic.mode
|
||||
|
||||
if (sonic.sync) {
|
||||
try {
|
||||
if (sonic.mkdir) fs.mkdirSync(path.dirname(file), { recursive: true })
|
||||
const fd = fs.openSync(file, flags, mode)
|
||||
fileOpened(null, fd)
|
||||
} catch (err) {
|
||||
fileOpened(err)
|
||||
throw err
|
||||
}
|
||||
} else if (sonic.mkdir) {
|
||||
fs.mkdir(path.dirname(file), { recursive: true }, (err) => {
|
||||
if (err) return fileOpened(err)
|
||||
fs.open(file, flags, mode, fileOpened)
|
||||
})
|
||||
} else {
|
||||
fs.open(file, flags, mode, fileOpened)
|
||||
}
|
||||
}
|
||||
|
||||
function SonicBoom (opts) {
|
||||
if (!(this instanceof SonicBoom)) {
|
||||
return new SonicBoom(opts)
|
||||
}
|
||||
|
||||
let { fd, dest, minLength, maxLength, maxWrite, sync, append = true, mkdir, retryEAGAIN, fsync, contentMode, mode } = opts || {}
|
||||
|
||||
fd = fd || dest
|
||||
|
||||
this._len = 0
|
||||
this.fd = -1
|
||||
this._bufs = []
|
||||
this._lens = []
|
||||
this._writing = false
|
||||
this._ending = false
|
||||
this._reopening = false
|
||||
this._asyncDrainScheduled = false
|
||||
this._flushPending = false
|
||||
this._hwm = Math.max(minLength || 0, 16387)
|
||||
this.file = null
|
||||
this.destroyed = false
|
||||
this.minLength = minLength || 0
|
||||
this.maxLength = maxLength || 0
|
||||
this.maxWrite = maxWrite || MAX_WRITE
|
||||
this.sync = sync || false
|
||||
this.writable = true
|
||||
this._fsync = fsync || false
|
||||
this.append = append || false
|
||||
this.mode = mode
|
||||
this.retryEAGAIN = retryEAGAIN || (() => true)
|
||||
this.mkdir = mkdir || false
|
||||
|
||||
let fsWriteSync
|
||||
let fsWrite
|
||||
if (contentMode === kContentModeBuffer) {
|
||||
this._writingBuf = kEmptyBuffer
|
||||
this.write = writeBuffer
|
||||
this.flush = flushBuffer
|
||||
this.flushSync = flushBufferSync
|
||||
this._actualWrite = actualWriteBuffer
|
||||
fsWriteSync = () => fs.writeSync(this.fd, this._writingBuf)
|
||||
fsWrite = () => fs.write(this.fd, this._writingBuf, this.release)
|
||||
} else if (contentMode === undefined || contentMode === kContentModeUtf8) {
|
||||
this._writingBuf = ''
|
||||
this.write = write
|
||||
this.flush = flush
|
||||
this.flushSync = flushSync
|
||||
this._actualWrite = actualWrite
|
||||
fsWriteSync = () => fs.writeSync(this.fd, this._writingBuf, 'utf8')
|
||||
fsWrite = () => fs.write(this.fd, this._writingBuf, 'utf8', this.release)
|
||||
} else {
|
||||
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`)
|
||||
}
|
||||
|
||||
if (typeof fd === 'number') {
|
||||
this.fd = fd
|
||||
process.nextTick(() => this.emit('ready'))
|
||||
} else if (typeof fd === 'string') {
|
||||
openFile(fd, this)
|
||||
} else {
|
||||
throw new Error('SonicBoom supports only file descriptors and files')
|
||||
}
|
||||
if (this.minLength >= this.maxWrite) {
|
||||
throw new Error(`minLength should be smaller than maxWrite (${this.maxWrite})`)
|
||||
}
|
||||
|
||||
this.release = (err, n) => {
|
||||
if (err) {
|
||||
if ((err.code === 'EAGAIN' || err.code === 'EBUSY') && this.retryEAGAIN(err, this._writingBuf.length, this._len - this._writingBuf.length)) {
|
||||
if (this.sync) {
|
||||
// This error code should not happen in sync mode, because it is
|
||||
// not using the underlining operating system asynchronous functions.
|
||||
// However it happens, and so we handle it.
|
||||
// Ref: https://github.com/pinojs/pino/issues/783
|
||||
try {
|
||||
sleep(BUSY_WRITE_TIMEOUT)
|
||||
this.release(undefined, 0)
|
||||
} catch (err) {
|
||||
this.release(err)
|
||||
}
|
||||
} else {
|
||||
// Let's give the destination some time to process the chunk.
|
||||
setTimeout(fsWrite, BUSY_WRITE_TIMEOUT)
|
||||
}
|
||||
} else {
|
||||
this._writing = false
|
||||
|
||||
this.emit('error', err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
this.emit('write', n)
|
||||
const releasedBufObj = releaseWritingBuf(this._writingBuf, this._len, n)
|
||||
this._len = releasedBufObj.len
|
||||
this._writingBuf = releasedBufObj.writingBuf
|
||||
|
||||
if (this._writingBuf.length) {
|
||||
if (!this.sync) {
|
||||
fsWrite()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
do {
|
||||
const n = fsWriteSync()
|
||||
const releasedBufObj = releaseWritingBuf(this._writingBuf, this._len, n)
|
||||
this._len = releasedBufObj.len
|
||||
this._writingBuf = releasedBufObj.writingBuf
|
||||
} while (this._writingBuf.length)
|
||||
} catch (err) {
|
||||
this.release(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (this._fsync) {
|
||||
fs.fsyncSync(this.fd)
|
||||
}
|
||||
|
||||
const len = this._len
|
||||
if (this._reopening) {
|
||||
this._writing = false
|
||||
this._reopening = false
|
||||
this.reopen()
|
||||
} else if (len > this.minLength) {
|
||||
this._actualWrite()
|
||||
} else if (this._ending) {
|
||||
if (len > 0) {
|
||||
this._actualWrite()
|
||||
} else {
|
||||
this._writing = false
|
||||
actualClose(this)
|
||||
}
|
||||
} else {
|
||||
this._writing = false
|
||||
if (this.sync) {
|
||||
if (!this._asyncDrainScheduled) {
|
||||
this._asyncDrainScheduled = true
|
||||
process.nextTick(emitDrain, this)
|
||||
}
|
||||
} else {
|
||||
this.emit('drain')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.on('newListener', function (name) {
|
||||
if (name === 'drain') {
|
||||
this._asyncDrainScheduled = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the writingBuf after fs.write n bytes data
|
||||
* @param {string | Buffer} writingBuf - currently writing buffer, usually be instance._writingBuf.
|
||||
* @param {number} len - currently buffer length, usually be instance._len.
|
||||
* @param {number} n - number of bytes fs already written
|
||||
* @returns {{writingBuf: string | Buffer, len: number}} released writingBuf and length
|
||||
*/
|
||||
function releaseWritingBuf (writingBuf, len, n) {
|
||||
// if Buffer.byteLength is equal to n, that means writingBuf contains no multi-byte character
|
||||
if (typeof writingBuf === 'string' && Buffer.byteLength(writingBuf) !== n) {
|
||||
// Since the fs.write callback parameter `n` means how many bytes the passed of string
|
||||
// We calculate the original string length for avoiding the multi-byte character issue
|
||||
n = Buffer.from(writingBuf).subarray(0, n).toString().length
|
||||
}
|
||||
len = Math.max(len - n, 0)
|
||||
writingBuf = writingBuf.slice(n)
|
||||
return { writingBuf, len }
|
||||
}
|
||||
|
||||
function emitDrain (sonic) {
|
||||
const hasListeners = sonic.listenerCount('drain') > 0
|
||||
if (!hasListeners) return
|
||||
sonic._asyncDrainScheduled = false
|
||||
sonic.emit('drain')
|
||||
}
|
||||
|
||||
inherits(SonicBoom, EventEmitter)
|
||||
|
||||
function mergeBuf (bufs, len) {
|
||||
if (bufs.length === 0) {
|
||||
return kEmptyBuffer
|
||||
}
|
||||
|
||||
if (bufs.length === 1) {
|
||||
return bufs[0]
|
||||
}
|
||||
|
||||
return Buffer.concat(bufs, len)
|
||||
}
|
||||
|
||||
function write (data) {
|
||||
if (this.destroyed) {
|
||||
throw new Error('SonicBoom destroyed')
|
||||
}
|
||||
|
||||
const len = this._len + data.length
|
||||
const bufs = this._bufs
|
||||
|
||||
if (this.maxLength && len > this.maxLength) {
|
||||
this.emit('drop', data)
|
||||
return this._len < this._hwm
|
||||
}
|
||||
|
||||
if (
|
||||
bufs.length === 0 ||
|
||||
bufs[bufs.length - 1].length + data.length > this.maxWrite
|
||||
) {
|
||||
bufs.push('' + data)
|
||||
} else {
|
||||
bufs[bufs.length - 1] += data
|
||||
}
|
||||
|
||||
this._len = len
|
||||
|
||||
if (!this._writing && this._len >= this.minLength) {
|
||||
this._actualWrite()
|
||||
}
|
||||
|
||||
return this._len < this._hwm
|
||||
}
|
||||
|
||||
function writeBuffer (data) {
|
||||
if (this.destroyed) {
|
||||
throw new Error('SonicBoom destroyed')
|
||||
}
|
||||
|
||||
const len = this._len + data.length
|
||||
const bufs = this._bufs
|
||||
const lens = this._lens
|
||||
|
||||
if (this.maxLength && len > this.maxLength) {
|
||||
this.emit('drop', data)
|
||||
return this._len < this._hwm
|
||||
}
|
||||
|
||||
if (
|
||||
bufs.length === 0 ||
|
||||
lens[lens.length - 1] + data.length > this.maxWrite
|
||||
) {
|
||||
bufs.push([data])
|
||||
lens.push(data.length)
|
||||
} else {
|
||||
bufs[bufs.length - 1].push(data)
|
||||
lens[lens.length - 1] += data.length
|
||||
}
|
||||
|
||||
this._len = len
|
||||
|
||||
if (!this._writing && this._len >= this.minLength) {
|
||||
this._actualWrite()
|
||||
}
|
||||
|
||||
return this._len < this._hwm
|
||||
}
|
||||
|
||||
function callFlushCallbackOnDrain (cb) {
|
||||
this._flushPending = true
|
||||
const onDrain = () => {
|
||||
// only if _fsync is false to avoid double fsync
|
||||
if (!this._fsync) {
|
||||
fs.fsync(this.fd, (err) => {
|
||||
this._flushPending = false
|
||||
cb(err)
|
||||
})
|
||||
} else {
|
||||
this._flushPending = false
|
||||
cb()
|
||||
}
|
||||
this.off('error', onError)
|
||||
}
|
||||
const onError = (err) => {
|
||||
this._flushPending = false
|
||||
cb(err)
|
||||
this.off('drain', onDrain)
|
||||
}
|
||||
|
||||
this.once('drain', onDrain)
|
||||
this.once('error', onError)
|
||||
}
|
||||
|
||||
function flush (cb) {
|
||||
if (cb != null && typeof cb !== 'function') {
|
||||
throw new Error('flush cb must be a function')
|
||||
}
|
||||
|
||||
if (this.destroyed) {
|
||||
const error = new Error('SonicBoom destroyed')
|
||||
if (cb) {
|
||||
cb(error)
|
||||
return
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
|
||||
if (this.minLength <= 0) {
|
||||
cb?.()
|
||||
return
|
||||
}
|
||||
|
||||
if (cb) {
|
||||
callFlushCallbackOnDrain.call(this, cb)
|
||||
}
|
||||
|
||||
if (this._writing) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._bufs.length === 0) {
|
||||
this._bufs.push('')
|
||||
}
|
||||
|
||||
this._actualWrite()
|
||||
}
|
||||
|
||||
function flushBuffer (cb) {
|
||||
if (cb != null && typeof cb !== 'function') {
|
||||
throw new Error('flush cb must be a function')
|
||||
}
|
||||
|
||||
if (this.destroyed) {
|
||||
const error = new Error('SonicBoom destroyed')
|
||||
if (cb) {
|
||||
cb(error)
|
||||
return
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
|
||||
if (this.minLength <= 0) {
|
||||
cb?.()
|
||||
return
|
||||
}
|
||||
|
||||
if (cb) {
|
||||
callFlushCallbackOnDrain.call(this, cb)
|
||||
}
|
||||
|
||||
if (this._writing) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._bufs.length === 0) {
|
||||
this._bufs.push([])
|
||||
this._lens.push(0)
|
||||
}
|
||||
|
||||
this._actualWrite()
|
||||
}
|
||||
|
||||
SonicBoom.prototype.reopen = function (file) {
|
||||
if (this.destroyed) {
|
||||
throw new Error('SonicBoom destroyed')
|
||||
}
|
||||
|
||||
if (this._opening) {
|
||||
this.once('ready', () => {
|
||||
this.reopen(file)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (this._ending) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.file) {
|
||||
throw new Error('Unable to reopen a file descriptor, you must pass a file to SonicBoom')
|
||||
}
|
||||
|
||||
if (file) {
|
||||
this.file = file
|
||||
}
|
||||
this._reopening = true
|
||||
|
||||
if (this._writing) {
|
||||
return
|
||||
}
|
||||
|
||||
const fd = this.fd
|
||||
this.once('ready', () => {
|
||||
if (fd !== this.fd) {
|
||||
fs.close(fd, (err) => {
|
||||
if (err) {
|
||||
return this.emit('error', err)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
openFile(this.file, this)
|
||||
}
|
||||
|
||||
SonicBoom.prototype.end = function () {
|
||||
if (this.destroyed) {
|
||||
throw new Error('SonicBoom destroyed')
|
||||
}
|
||||
|
||||
if (this._opening) {
|
||||
this.once('ready', () => {
|
||||
this.end()
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (this._ending) {
|
||||
return
|
||||
}
|
||||
|
||||
this._ending = true
|
||||
|
||||
if (this._writing) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._len > 0 && this.fd >= 0) {
|
||||
this._actualWrite()
|
||||
} else {
|
||||
actualClose(this)
|
||||
}
|
||||
}
|
||||
|
||||
function flushSync () {
|
||||
if (this.destroyed) {
|
||||
throw new Error('SonicBoom destroyed')
|
||||
}
|
||||
|
||||
if (this.fd < 0) {
|
||||
throw new Error('sonic boom is not ready yet')
|
||||
}
|
||||
|
||||
if (!this._writing && this._writingBuf.length > 0) {
|
||||
this._bufs.unshift(this._writingBuf)
|
||||
this._writingBuf = ''
|
||||
}
|
||||
|
||||
let buf = ''
|
||||
while (this._bufs.length || buf) {
|
||||
if (buf.length <= 0) {
|
||||
buf = this._bufs[0]
|
||||
}
|
||||
try {
|
||||
const n = fs.writeSync(this.fd, buf, 'utf8')
|
||||
const releasedBufObj = releaseWritingBuf(buf, this._len, n)
|
||||
buf = releasedBufObj.writingBuf
|
||||
this._len = releasedBufObj.len
|
||||
if (buf.length <= 0) {
|
||||
this._bufs.shift()
|
||||
}
|
||||
} catch (err) {
|
||||
const shouldRetry = err.code === 'EAGAIN' || err.code === 'EBUSY'
|
||||
if (shouldRetry && !this.retryEAGAIN(err, buf.length, this._len - buf.length)) {
|
||||
throw err
|
||||
}
|
||||
|
||||
sleep(BUSY_WRITE_TIMEOUT)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
fs.fsyncSync(this.fd)
|
||||
} catch {
|
||||
// Skip the error. The fd might not support fsync.
|
||||
}
|
||||
}
|
||||
|
||||
function flushBufferSync () {
|
||||
if (this.destroyed) {
|
||||
throw new Error('SonicBoom destroyed')
|
||||
}
|
||||
|
||||
if (this.fd < 0) {
|
||||
throw new Error('sonic boom is not ready yet')
|
||||
}
|
||||
|
||||
if (!this._writing && this._writingBuf.length > 0) {
|
||||
this._bufs.unshift([this._writingBuf])
|
||||
this._writingBuf = kEmptyBuffer
|
||||
}
|
||||
|
||||
let buf = kEmptyBuffer
|
||||
while (this._bufs.length || buf.length) {
|
||||
if (buf.length <= 0) {
|
||||
buf = mergeBuf(this._bufs[0], this._lens[0])
|
||||
}
|
||||
try {
|
||||
const n = fs.writeSync(this.fd, buf)
|
||||
buf = buf.subarray(n)
|
||||
this._len = Math.max(this._len - n, 0)
|
||||
if (buf.length <= 0) {
|
||||
this._bufs.shift()
|
||||
this._lens.shift()
|
||||
}
|
||||
} catch (err) {
|
||||
const shouldRetry = err.code === 'EAGAIN' || err.code === 'EBUSY'
|
||||
if (shouldRetry && !this.retryEAGAIN(err, buf.length, this._len - buf.length)) {
|
||||
throw err
|
||||
}
|
||||
|
||||
sleep(BUSY_WRITE_TIMEOUT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SonicBoom.prototype.destroy = function () {
|
||||
if (this.destroyed) {
|
||||
return
|
||||
}
|
||||
actualClose(this)
|
||||
}
|
||||
|
||||
function actualWrite () {
|
||||
const release = this.release
|
||||
this._writing = true
|
||||
this._writingBuf = this._writingBuf || this._bufs.shift() || ''
|
||||
|
||||
if (this.sync) {
|
||||
try {
|
||||
const written = fs.writeSync(this.fd, this._writingBuf, 'utf8')
|
||||
release(null, written)
|
||||
} catch (err) {
|
||||
release(err)
|
||||
}
|
||||
} else {
|
||||
fs.write(this.fd, this._writingBuf, 'utf8', release)
|
||||
}
|
||||
}
|
||||
|
||||
function actualWriteBuffer () {
|
||||
const release = this.release
|
||||
this._writing = true
|
||||
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift())
|
||||
|
||||
if (this.sync) {
|
||||
try {
|
||||
const written = fs.writeSync(this.fd, this._writingBuf)
|
||||
release(null, written)
|
||||
} catch (err) {
|
||||
release(err)
|
||||
}
|
||||
} else {
|
||||
fs.write(this.fd, this._writingBuf, release)
|
||||
}
|
||||
}
|
||||
|
||||
function actualClose (sonic) {
|
||||
if (sonic.fd === -1) {
|
||||
sonic.once('ready', actualClose.bind(null, sonic))
|
||||
return
|
||||
}
|
||||
|
||||
sonic.destroyed = true
|
||||
sonic._bufs = []
|
||||
sonic._lens = []
|
||||
|
||||
fs.fsync(sonic.fd, closeWrapped)
|
||||
|
||||
function closeWrapped () {
|
||||
// We skip errors in fsync
|
||||
|
||||
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
||||
fs.close(sonic.fd, done)
|
||||
} else {
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
function done (err) {
|
||||
if (err) {
|
||||
sonic.emit('error', err)
|
||||
return
|
||||
}
|
||||
|
||||
if (sonic._ending && !sonic._writing) {
|
||||
sonic.emit('finish')
|
||||
}
|
||||
sonic.emit('close')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* These export configurations enable JS and TS developers
|
||||
* to consumer SonicBoom in whatever way best suits their needs.
|
||||
* Some examples of supported import syntax includes:
|
||||
* - `const SonicBoom = require('SonicBoom')`
|
||||
* - `const { SonicBoom } = require('SonicBoom')`
|
||||
* - `import * as SonicBoom from 'SonicBoom'`
|
||||
* - `import { SonicBoom } from 'SonicBoom'`
|
||||
* - `import SonicBoom from 'SonicBoom'`
|
||||
*/
|
||||
SonicBoom.SonicBoom = SonicBoom
|
||||
SonicBoom.default = SonicBoom
|
||||
module.exports = SonicBoom
|
||||
50
node_modules/sonic-boom/package.json
generated
vendored
Normal file
50
node_modules/sonic-boom/package.json
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "sonic-boom",
|
||||
"version": "3.8.1",
|
||||
"description": "Extremely fast utf8 only stream implementation",
|
||||
"main": "index.js",
|
||||
"type": "commonjs",
|
||||
"types": "types/index.d.ts",
|
||||
"scripts": {
|
||||
"test": "npm run test:types && standard && npm run test:unit",
|
||||
"test:unit": "tap",
|
||||
"test:types": "tsc && tsd && ts-node types/tests/test.ts",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/pinojs/sonic-boom.git"
|
||||
},
|
||||
"keywords": [
|
||||
"stream",
|
||||
"fs",
|
||||
"net",
|
||||
"fd",
|
||||
"file",
|
||||
"descriptor",
|
||||
"fast"
|
||||
],
|
||||
"author": "Matteo Collina <hello@matteocollina.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pinojs/sonic-boom/issues"
|
||||
},
|
||||
"homepage": "https://github.com/pinojs/sonic-boom#readme",
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.1.0",
|
||||
"fastbench": "^1.0.1",
|
||||
"husky": "^9.0.6",
|
||||
"proxyquire": "^2.1.3",
|
||||
"standard": "^17.0.0",
|
||||
"tap": "^16.2.0",
|
||||
"tsd": "^0.31.0",
|
||||
"typescript": "^5.0.2",
|
||||
"ts-node": "^10.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"atomic-sleep": "^1.0.0"
|
||||
},
|
||||
"tsd": {
|
||||
"directory": "./types"
|
||||
}
|
||||
}
|
||||
49
node_modules/sonic-boom/test/destroy.test.js
generated
vendored
Normal file
49
node_modules/sonic-boom/test/destroy.test.js
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const SonicBoom = require('../')
|
||||
const { file, runTests } = require('./helper')
|
||||
|
||||
runTests(buildTests)
|
||||
|
||||
function buildTests (test, sync) {
|
||||
// Reset the umask for testing
|
||||
process.umask(0o000)
|
||||
|
||||
test('destroy', (t) => {
|
||||
t.plan(5)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, sync })
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
stream.destroy()
|
||||
t.throws(() => { stream.write('hello world\n') })
|
||||
|
||||
fs.readFile(dest, 'utf8', function (err, data) {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\n')
|
||||
})
|
||||
|
||||
stream.on('finish', () => {
|
||||
t.fail('finish emitted')
|
||||
})
|
||||
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('destroy while opening', (t) => {
|
||||
t.plan(1)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest })
|
||||
|
||||
stream.destroy()
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
}
|
||||
98
node_modules/sonic-boom/test/end.test.js
generated
vendored
Normal file
98
node_modules/sonic-boom/test/end.test.js
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
'use strict'
|
||||
|
||||
const { join } = require('path')
|
||||
const { fork } = require('child_process')
|
||||
const fs = require('fs')
|
||||
const SonicBoom = require('../')
|
||||
const { file, runTests } = require('./helper')
|
||||
|
||||
runTests(buildTests)
|
||||
|
||||
function buildTests (test, sync) {
|
||||
// Reset the umask for testing
|
||||
process.umask(0o000)
|
||||
|
||||
test('end after reopen', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, minLength: 4096, sync })
|
||||
|
||||
stream.once('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
const after = dest + '-moved'
|
||||
stream.reopen(after)
|
||||
stream.write('after reopen\n')
|
||||
stream.on('finish', () => {
|
||||
t.pass('finish emitted')
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
})
|
||||
})
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
|
||||
test('end after 2x reopen', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, minLength: 4096, sync })
|
||||
|
||||
stream.once('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
stream.reopen(dest + '-moved')
|
||||
const after = dest + '-moved-moved'
|
||||
stream.reopen(after)
|
||||
stream.write('after reopen\n')
|
||||
stream.on('finish', () => {
|
||||
t.pass('finish emitted')
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
})
|
||||
})
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
|
||||
test('end if not ready', (t) => {
|
||||
t.plan(3)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, minLength: 4096, sync })
|
||||
const after = dest + '-moved'
|
||||
stream.reopen(after)
|
||||
stream.write('after reopen\n')
|
||||
stream.on('finish', () => {
|
||||
t.pass('finish emitted')
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
})
|
||||
})
|
||||
stream.end()
|
||||
})
|
||||
|
||||
test('chunk data accordingly', (t) => {
|
||||
t.plan(2)
|
||||
|
||||
const child = fork(join(__dirname, '..', 'fixtures', 'firehose.js'), { silent: true })
|
||||
const str = Buffer.alloc(10000).fill('a').toString()
|
||||
|
||||
let data = ''
|
||||
|
||||
child.stdout.on('data', function (chunk) {
|
||||
data += chunk.toString()
|
||||
})
|
||||
|
||||
child.stdout.on('end', function () {
|
||||
t.equal(data, str)
|
||||
})
|
||||
|
||||
child.on('close', function (code) {
|
||||
t.equal(code, 0)
|
||||
})
|
||||
})
|
||||
}
|
||||
140
node_modules/sonic-boom/test/flush-sync.test.js
generated
vendored
Normal file
140
node_modules/sonic-boom/test/flush-sync.test.js
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('tap')
|
||||
const fs = require('fs')
|
||||
const proxyquire = require('proxyquire')
|
||||
const SonicBoom = require('../')
|
||||
const { file, runTests } = require('./helper')
|
||||
|
||||
runTests(buildTests)
|
||||
|
||||
function buildTests (test, sync) {
|
||||
// Reset the umask for testing
|
||||
process.umask(0o000)
|
||||
|
||||
test('flushSync', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 4096, sync })
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.flushSync()
|
||||
|
||||
// let the file system settle down things
|
||||
setImmediate(function () {
|
||||
stream.end()
|
||||
const data = fs.readFileSync(dest, 'utf8')
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
test('retry in flushSync on EAGAIN', (t) => {
|
||||
t.plan(7)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, sync: false, minLength: 0 })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
|
||||
fakeFs.writeSync = function (fd, buf, enc) {
|
||||
t.pass('fake fs.write called')
|
||||
fakeFs.writeSync = fs.writeSync
|
||||
const err = new Error('EAGAIN')
|
||||
err.code = 'EAGAIN'
|
||||
throw err
|
||||
}
|
||||
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.flushSync()
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('throw error in flushSync on EAGAIN', (t) => {
|
||||
t.plan(12)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({
|
||||
fd,
|
||||
sync: false,
|
||||
minLength: 1000,
|
||||
retryEAGAIN: (err, writeBufferLen, remainingBufferLen) => {
|
||||
t.equal(err.code, 'EAGAIN')
|
||||
t.equal(writeBufferLen, 12)
|
||||
t.equal(remainingBufferLen, 0)
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
const err = new Error('EAGAIN')
|
||||
err.code = 'EAGAIN'
|
||||
fakeFs.writeSync = function (fd, buf, enc) {
|
||||
Error.captureStackTrace(err)
|
||||
t.pass('fake fs.write called')
|
||||
fakeFs.writeSync = fs.writeSync
|
||||
throw err
|
||||
}
|
||||
|
||||
fakeFs.fsyncSync = function (...args) {
|
||||
t.pass('fake fs.fsyncSync called')
|
||||
fakeFs.fsyncSync = fs.fsyncSync
|
||||
return fs.fsyncSync.apply(null, args)
|
||||
}
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.throws(stream.flushSync.bind(stream), err, 'EAGAIN')
|
||||
|
||||
t.ok(stream.write('something else\n'))
|
||||
stream.flushSync()
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
419
node_modules/sonic-boom/test/flush.test.js
generated
vendored
Normal file
419
node_modules/sonic-boom/test/flush.test.js
generated
vendored
Normal file
@@ -0,0 +1,419 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const SonicBoom = require('../')
|
||||
const { file, runTests } = require('./helper')
|
||||
const proxyquire = require('proxyquire')
|
||||
|
||||
runTests(buildTests)
|
||||
|
||||
function buildTests (test, sync) {
|
||||
// Reset the unmask for testing
|
||||
process.umask(0o000)
|
||||
|
||||
test('append', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
fs.writeFileSync(dest, 'hello world\n')
|
||||
const stream = new SonicBoom({ dest, append: false, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.flush()
|
||||
|
||||
stream.on('drain', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'something else\n')
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('mkdir', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = path.join(file(), 'out.log')
|
||||
const stream = new SonicBoom({ dest, mkdir: true, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
|
||||
stream.flush()
|
||||
|
||||
stream.on('drain', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\n')
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('flush', (t) => {
|
||||
t.plan(5)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 4096, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.flush()
|
||||
|
||||
stream.on('drain', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('flush with no data', (t) => {
|
||||
t.plan(2)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 4096, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
stream.flush()
|
||||
|
||||
stream.on('drain', () => {
|
||||
t.pass('drain emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('call flush cb after flushed', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 4096, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.flush((err) => {
|
||||
if (err) t.fail(err)
|
||||
else t.pass('flush cb called')
|
||||
})
|
||||
})
|
||||
|
||||
test('only call fsyncSync and not fsync when fsync: true', (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({
|
||||
fd,
|
||||
sync,
|
||||
fsync: true,
|
||||
minLength: 4096
|
||||
})
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
fakeFs.fsync = function (fd, cb) {
|
||||
t.fail('fake fs.fsync called while should not')
|
||||
cb()
|
||||
}
|
||||
fakeFs.fsyncSync = function (fd) {
|
||||
t.pass('fake fsyncSync called')
|
||||
}
|
||||
|
||||
function successOnAsyncOrSyncFn (isSync, originalFn) {
|
||||
return function (...args) {
|
||||
t.pass(`fake fs.${originalFn.name} called`)
|
||||
fakeFs[originalFn.name] = originalFn
|
||||
return fakeFs[originalFn.name](...args)
|
||||
}
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
fakeFs.writeSync = successOnAsyncOrSyncFn(true, fs.writeSync)
|
||||
} else {
|
||||
fakeFs.write = successOnAsyncOrSyncFn(false, fs.write)
|
||||
}
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
stream.flush((err) => {
|
||||
if (err) t.fail(err)
|
||||
else t.pass('flush cb called')
|
||||
|
||||
process.nextTick(() => {
|
||||
// to make sure fsync is not called as well
|
||||
t.pass('nextTick after flush called')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('call flush cb with error when fsync failed', (t) => {
|
||||
t.plan(5)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({
|
||||
fd,
|
||||
sync,
|
||||
minLength: 4096
|
||||
})
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
const err = new Error('other')
|
||||
err.code = 'other'
|
||||
|
||||
function onFsyncOnFsyncSync (isSync, originalFn) {
|
||||
return function (...args) {
|
||||
Error.captureStackTrace(err)
|
||||
t.pass(`fake fs.${originalFn.name} called`)
|
||||
fakeFs[originalFn.name] = originalFn
|
||||
const cb = args[args.length - 1]
|
||||
|
||||
cb(err)
|
||||
}
|
||||
}
|
||||
|
||||
// only one is called depends on sync
|
||||
fakeFs.fsync = onFsyncOnFsyncSync(false, fs.fsync)
|
||||
|
||||
function successOnAsyncOrSyncFn (isSync, originalFn) {
|
||||
return function (...args) {
|
||||
t.pass(`fake fs.${originalFn.name} called`)
|
||||
fakeFs[originalFn.name] = originalFn
|
||||
return fakeFs[originalFn.name](...args)
|
||||
}
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
fakeFs.writeSync = successOnAsyncOrSyncFn(true, fs.writeSync)
|
||||
} else {
|
||||
fakeFs.write = successOnAsyncOrSyncFn(false, fs.write)
|
||||
}
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
stream.flush((err) => {
|
||||
if (err) t.equal(err.code, 'other')
|
||||
else t.fail('flush cb called without an error')
|
||||
})
|
||||
})
|
||||
|
||||
test('call flush cb even when have no data', (t) => {
|
||||
t.plan(2)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 4096, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
|
||||
stream.flush((err) => {
|
||||
if (err) t.fail(err)
|
||||
else t.pass('flush cb called')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('call flush cb even when minLength is 0', (t) => {
|
||||
t.plan(1)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 0, sync })
|
||||
|
||||
stream.flush((err) => {
|
||||
if (err) t.fail(err)
|
||||
else t.pass('flush cb called')
|
||||
})
|
||||
})
|
||||
|
||||
test('call flush cb with an error when trying to flush destroyed stream', (t) => {
|
||||
t.plan(1)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 4096, sync })
|
||||
stream.destroy()
|
||||
|
||||
stream.flush((err) => {
|
||||
if (err) t.pass(err)
|
||||
else t.fail('flush cb called without an error')
|
||||
})
|
||||
})
|
||||
|
||||
test('call flush cb with an error when failed to flush', (t) => {
|
||||
t.plan(5)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({
|
||||
fd,
|
||||
sync,
|
||||
minLength: 4096
|
||||
})
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
const err = new Error('other')
|
||||
err.code = 'other'
|
||||
|
||||
function onWriteOrWriteSync (isSync, originalFn) {
|
||||
return function (...args) {
|
||||
Error.captureStackTrace(err)
|
||||
t.pass(`fake fs.${originalFn.name} called`)
|
||||
fakeFs[originalFn.name] = originalFn
|
||||
|
||||
if (isSync) throw err
|
||||
const cb = args[args.length - 1]
|
||||
|
||||
cb(err)
|
||||
}
|
||||
}
|
||||
|
||||
// only one is called depends on sync
|
||||
fakeFs.write = onWriteOrWriteSync(false, fs.write)
|
||||
fakeFs.writeSync = onWriteOrWriteSync(true, fs.writeSync)
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
stream.flush((err) => {
|
||||
if (err) t.equal(err.code, 'other')
|
||||
else t.fail('flush cb called without an error')
|
||||
})
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('call flush cb when finish writing when currently in the middle', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({
|
||||
fd,
|
||||
sync,
|
||||
|
||||
// to trigger write without calling flush
|
||||
minLength: 1
|
||||
})
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
function onWriteOrWriteSync (originalFn) {
|
||||
return function (...args) {
|
||||
stream.flush((err) => {
|
||||
if (err) t.fail(err)
|
||||
else t.pass('flush cb called')
|
||||
})
|
||||
|
||||
t.pass(`fake fs.${originalFn.name} called`)
|
||||
fakeFs[originalFn.name] = originalFn
|
||||
return originalFn(...args)
|
||||
}
|
||||
}
|
||||
|
||||
// only one is called depends on sync
|
||||
fakeFs.write = onWriteOrWriteSync(fs.write)
|
||||
fakeFs.writeSync = onWriteOrWriteSync(fs.writeSync)
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
})
|
||||
|
||||
test('call flush cb when writing and trying to flush before ready (on async)', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
fakeFs.open = fsOpen
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({
|
||||
fd: dest,
|
||||
// only async as sync is part of the constructor so the user will not be able to call write/flush
|
||||
// before ready
|
||||
sync: false,
|
||||
|
||||
// to not trigger write without calling flush
|
||||
minLength: 4096
|
||||
})
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
function fsOpen (...args) {
|
||||
process.nextTick(() => {
|
||||
// try writing and flushing before ready and in the middle of opening
|
||||
t.pass('fake fs.open called')
|
||||
t.ok(stream.write('hello world\n'))
|
||||
|
||||
// calling flush
|
||||
stream.flush((err) => {
|
||||
if (err) t.fail(err)
|
||||
else t.pass('flush cb called')
|
||||
})
|
||||
|
||||
fakeFs.open = fs.open
|
||||
fs.open(...args)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
63
node_modules/sonic-boom/test/fsync.test.js
generated
vendored
Normal file
63
node_modules/sonic-boom/test/fsync.test.js
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('tap')
|
||||
const fs = require('fs')
|
||||
const proxyquire = require('proxyquire')
|
||||
const { file } = require('./helper')
|
||||
|
||||
test('fsync with sync', (t) => {
|
||||
t.plan(5)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.fsyncSync = function (fd) {
|
||||
t.pass('fake fs.fsyncSync called')
|
||||
return fs.fsyncSync(fd)
|
||||
}
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, sync: true, fsync: true })
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
const data = fs.readFileSync(dest, 'utf8')
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
|
||||
test('fsync with async', (t) => {
|
||||
t.plan(7)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.fsyncSync = function (fd) {
|
||||
t.pass('fake fs.fsyncSync called')
|
||||
return fs.fsyncSync(fd)
|
||||
}
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, fsync: true })
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
42
node_modules/sonic-boom/test/helper.js
generated
vendored
Normal file
42
node_modules/sonic-boom/test/helper.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
'use strict'
|
||||
|
||||
const { test, teardown } = require('tap')
|
||||
const fs = require('fs')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
|
||||
const files = []
|
||||
let count = 0
|
||||
|
||||
function file () {
|
||||
const file = path.join(os.tmpdir(), `sonic-boom-${process.pid}-${process.hrtime().toString()}-${count++}`)
|
||||
files.push(file)
|
||||
return file
|
||||
}
|
||||
|
||||
teardown(() => {
|
||||
const rmSync = fs.rmSync || fs.rmdirSync
|
||||
files.forEach((file) => {
|
||||
try {
|
||||
if (fs.existsSync(file)) {
|
||||
fs.statSync(file).isDirectory() ? rmSync(file, { recursive: true, maxRetries: 10 }) : fs.unlinkSync(file)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
function runTests (buildTests) {
|
||||
test('sync false', (t) => {
|
||||
buildTests(t.test, false)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('sync true', (t) => {
|
||||
buildTests(t.test, true)
|
||||
t.end()
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { file, runTests }
|
||||
35
node_modules/sonic-boom/test/minlength.test.js
generated
vendored
Normal file
35
node_modules/sonic-boom/test/minlength.test.js
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('tap')
|
||||
const fs = require('fs')
|
||||
const SonicBoom = require('../')
|
||||
const { file } = require('./helper')
|
||||
|
||||
const MAX_WRITE = 16 * 1024
|
||||
|
||||
test('drain deadlock', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, sync: false, minLength: 9999 })
|
||||
|
||||
t.ok(stream.write(Buffer.alloc(1500).fill('x').toString()))
|
||||
t.ok(stream.write(Buffer.alloc(1500).fill('x').toString()))
|
||||
t.ok(!stream.write(Buffer.alloc(MAX_WRITE).fill('x').toString()))
|
||||
stream.on('drain', () => {
|
||||
t.pass()
|
||||
})
|
||||
})
|
||||
|
||||
test('should throw if minLength >= maxWrite', (t) => {
|
||||
t.plan(1)
|
||||
t.throws(() => {
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
|
||||
SonicBoom({
|
||||
fd,
|
||||
minLength: MAX_WRITE
|
||||
})
|
||||
})
|
||||
})
|
||||
116
node_modules/sonic-boom/test/mode.test.js
generated
vendored
Normal file
116
node_modules/sonic-boom/test/mode.test.js
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const SonicBoom = require('../')
|
||||
const { file, runTests } = require('./helper')
|
||||
|
||||
const isWindows = process.platform === 'win32'
|
||||
|
||||
runTests(buildTests)
|
||||
|
||||
function buildTests (test, sync) {
|
||||
// Reset the umask for testing
|
||||
process.umask(0o000)
|
||||
|
||||
test('mode', { skip: isWindows }, (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const dest = file()
|
||||
const mode = 0o666
|
||||
const stream = new SonicBoom({ dest, sync, mode })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
t.equal(fs.statSync(dest).mode & 0o777, stream.mode)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('mode default', { skip: isWindows }, (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const dest = file()
|
||||
const defaultMode = 0o666
|
||||
const stream = new SonicBoom({ dest, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
t.equal(fs.statSync(dest).mode & 0o777, defaultMode)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('mode on mkdir', { skip: isWindows }, (t) => {
|
||||
t.plan(5)
|
||||
|
||||
const dest = path.join(file(), 'out.log')
|
||||
const mode = 0o666
|
||||
const stream = new SonicBoom({ dest, mkdir: true, mode, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
|
||||
stream.flush()
|
||||
|
||||
stream.on('drain', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\n')
|
||||
t.equal(fs.statSync(dest).mode & 0o777, stream.mode)
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('mode on append', { skip: isWindows }, (t) => {
|
||||
t.plan(5)
|
||||
|
||||
const dest = file()
|
||||
fs.writeFileSync(dest, 'hello world\n', 'utf8', 0o422)
|
||||
const mode = isWindows ? 0o444 : 0o666
|
||||
const stream = new SonicBoom({ dest, append: false, mode, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.flush()
|
||||
|
||||
stream.on('drain', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'something else\n')
|
||||
t.equal(fs.statSync(dest).mode & 0o777, stream.mode)
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
239
node_modules/sonic-boom/test/reopen.test.js
generated
vendored
Normal file
239
node_modules/sonic-boom/test/reopen.test.js
generated
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const proxyquire = require('proxyquire')
|
||||
const SonicBoom = require('../')
|
||||
const { file, runTests } = require('./helper')
|
||||
|
||||
runTests(buildTests)
|
||||
|
||||
function buildTests (test, sync) {
|
||||
// Reset the umask for testing
|
||||
process.umask(0o000)
|
||||
|
||||
test('reopen', (t) => {
|
||||
t.plan(9)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, sync })
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
const after = dest + '-moved'
|
||||
|
||||
stream.once('drain', () => {
|
||||
t.pass('drain emitted')
|
||||
|
||||
fs.renameSync(dest, after)
|
||||
stream.reopen()
|
||||
|
||||
stream.once('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
t.ok(stream.write('after reopen\n'))
|
||||
|
||||
stream.once('drain', () => {
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('reopen with buffer', (t) => {
|
||||
t.plan(9)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, minLength: 4096, sync })
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
const after = dest + '-moved'
|
||||
|
||||
stream.once('ready', () => {
|
||||
t.pass('drain emitted')
|
||||
|
||||
stream.flush()
|
||||
fs.renameSync(dest, after)
|
||||
stream.reopen()
|
||||
|
||||
stream.once('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
t.ok(stream.write('after reopen\n'))
|
||||
stream.flush()
|
||||
|
||||
stream.once('drain', () => {
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('reopen if not open', (t) => {
|
||||
t.plan(3)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, sync })
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.reopen()
|
||||
|
||||
stream.end()
|
||||
stream.on('close', function () {
|
||||
t.pass('ended')
|
||||
})
|
||||
})
|
||||
|
||||
test('reopen with file', (t) => {
|
||||
t.plan(10)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, minLength: 0, sync })
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
const after = dest + '-new'
|
||||
|
||||
stream.once('drain', () => {
|
||||
t.pass('drain emitted')
|
||||
|
||||
stream.reopen(after)
|
||||
t.equal(stream.file, after)
|
||||
|
||||
stream.once('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
t.ok(stream.write('after reopen\n'))
|
||||
|
||||
stream.once('drain', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('reopen throws an error', (t) => {
|
||||
t.plan(sync ? 10 : 9)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, sync })
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
const after = dest + '-moved'
|
||||
|
||||
stream.on('error', () => {
|
||||
t.pass('error emitted')
|
||||
})
|
||||
|
||||
stream.once('drain', () => {
|
||||
t.pass('drain emitted')
|
||||
|
||||
fs.renameSync(dest, after)
|
||||
if (sync) {
|
||||
fakeFs.openSync = function (file, flags) {
|
||||
t.pass('fake fs.openSync called')
|
||||
throw new Error('open error')
|
||||
}
|
||||
} else {
|
||||
fakeFs.open = function (file, flags, mode, cb) {
|
||||
t.pass('fake fs.open called')
|
||||
setTimeout(() => cb(new Error('open error')), 0)
|
||||
}
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
try {
|
||||
stream.reopen()
|
||||
} catch (err) {
|
||||
t.pass('reopen throwed')
|
||||
}
|
||||
} else {
|
||||
stream.reopen()
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
t.ok(stream.write('after reopen\n'))
|
||||
|
||||
stream.end()
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\nafter reopen\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
}, 0)
|
||||
})
|
||||
})
|
||||
|
||||
test('reopen emits drain', (t) => {
|
||||
t.plan(9)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, sync })
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
const after = dest + '-moved'
|
||||
|
||||
stream.once('drain', () => {
|
||||
t.pass('drain emitted')
|
||||
|
||||
fs.renameSync(dest, after)
|
||||
stream.reopen()
|
||||
|
||||
stream.once('drain', () => {
|
||||
t.pass('drain emitted')
|
||||
t.ok(stream.write('after reopen\n'))
|
||||
|
||||
stream.once('drain', () => {
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
414
node_modules/sonic-boom/test/retry.test.js
generated
vendored
Normal file
414
node_modules/sonic-boom/test/retry.test.js
generated
vendored
Normal file
@@ -0,0 +1,414 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('tap')
|
||||
const fs = require('fs')
|
||||
const proxyquire = require('proxyquire')
|
||||
const { file, runTests } = require('./helper')
|
||||
|
||||
const MAX_WRITE = 16 * 1024
|
||||
|
||||
runTests(buildTests)
|
||||
|
||||
function buildTests (test, sync) {
|
||||
// Reset the umask for testing
|
||||
process.umask(0o000)
|
||||
test('retry on EAGAIN', (t) => {
|
||||
t.plan(7)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.write = function (fd, buf, ...args) {
|
||||
t.pass('fake fs.write called')
|
||||
fakeFs.write = fs.write
|
||||
const err = new Error('EAGAIN')
|
||||
err.code = 'EAGAIN'
|
||||
process.nextTick(args.pop(), err)
|
||||
}
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, sync: false, minLength: 0 })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
test('emit error on async EAGAIN', (t) => {
|
||||
t.plan(11)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.write = function (fd, buf, ...args) {
|
||||
t.pass('fake fs.write called')
|
||||
fakeFs.write = fs.write
|
||||
const err = new Error('EAGAIN')
|
||||
err.code = 'EAGAIN'
|
||||
process.nextTick(args[args.length - 1], err)
|
||||
}
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({
|
||||
fd,
|
||||
sync: false,
|
||||
minLength: 12,
|
||||
retryEAGAIN: (err, writeBufferLen, remainingBufferLen) => {
|
||||
t.equal(err.code, 'EAGAIN')
|
||||
t.equal(writeBufferLen, 12)
|
||||
t.equal(remainingBufferLen, 0)
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
stream.once('error', err => {
|
||||
t.equal(err.code, 'EAGAIN')
|
||||
t.ok(stream.write('something else\n'))
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('retry on EAGAIN (sync)', (t) => {
|
||||
t.plan(7)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.writeSync = function (fd, buf, enc) {
|
||||
t.pass('fake fs.writeSync called')
|
||||
fakeFs.writeSync = fs.writeSync
|
||||
const err = new Error('EAGAIN')
|
||||
err.code = 'EAGAIN'
|
||||
throw err
|
||||
}
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 0, sync: true })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('emit error on EAGAIN (sync)', (t) => {
|
||||
t.plan(11)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.writeSync = function (fd, buf, enc) {
|
||||
t.pass('fake fs.writeSync called')
|
||||
fakeFs.writeSync = fs.writeSync
|
||||
const err = new Error('EAGAIN')
|
||||
err.code = 'EAGAIN'
|
||||
throw err
|
||||
}
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({
|
||||
fd,
|
||||
minLength: 0,
|
||||
sync: true,
|
||||
retryEAGAIN: (err, writeBufferLen, remainingBufferLen) => {
|
||||
t.equal(err.code, 'EAGAIN')
|
||||
t.equal(writeBufferLen, 12)
|
||||
t.equal(remainingBufferLen, 0)
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
stream.once('error', err => {
|
||||
t.equal(err.code, 'EAGAIN')
|
||||
t.ok(stream.write('something else\n'))
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('retryEAGAIN receives remaining buffer on async if write fails', (t) => {
|
||||
t.plan(12)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({
|
||||
fd,
|
||||
sync: false,
|
||||
minLength: 12,
|
||||
retryEAGAIN: (err, writeBufferLen, remainingBufferLen) => {
|
||||
t.equal(err.code, 'EAGAIN')
|
||||
t.equal(writeBufferLen, 12)
|
||||
t.equal(remainingBufferLen, 11)
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
stream.once('error', err => {
|
||||
t.equal(err.code, 'EAGAIN')
|
||||
t.ok(stream.write('done'))
|
||||
})
|
||||
|
||||
fakeFs.write = function (fd, buf, ...args) {
|
||||
t.pass('fake fs.write called')
|
||||
fakeFs.write = fs.write
|
||||
const err = new Error('EAGAIN')
|
||||
err.code = 'EAGAIN'
|
||||
t.ok(stream.write('sonic boom\n'))
|
||||
process.nextTick(args[args.length - 1], err)
|
||||
}
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsonic boom\ndone')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('retryEAGAIN receives remaining buffer if exceeds maxWrite', (t) => {
|
||||
t.plan(17)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const buf = Buffer.alloc(MAX_WRITE - 2).fill('x').toString() // 1 MB
|
||||
const stream = new SonicBoom({
|
||||
fd,
|
||||
sync: false,
|
||||
minLength: MAX_WRITE - 1,
|
||||
retryEAGAIN: (err, writeBufferLen, remainingBufferLen) => {
|
||||
t.equal(err.code, 'EAGAIN', 'retryEAGAIN received EAGAIN error')
|
||||
t.equal(writeBufferLen, buf.length, 'writeBufferLen === buf.length')
|
||||
t.equal(remainingBufferLen, 23, 'remainingBufferLen === 23')
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
fakeFs.write = function (fd, buf, ...args) {
|
||||
t.pass('fake fs.write called')
|
||||
const err = new Error('EAGAIN')
|
||||
err.code = 'EAGAIN'
|
||||
process.nextTick(args.pop(), err)
|
||||
}
|
||||
|
||||
fakeFs.writeSync = function (fd, buf, enc) {
|
||||
t.pass('fake fs.write called')
|
||||
const err = new Error('EAGAIN')
|
||||
err.code = 'EAGAIN'
|
||||
throw err
|
||||
}
|
||||
|
||||
t.ok(stream.write(buf), 'write buf')
|
||||
t.notOk(stream.write('hello world\nsonic boom\n'), 'write hello world sonic boom')
|
||||
|
||||
stream.once('error', err => {
|
||||
t.equal(err.code, 'EAGAIN', 'bubbled error should be EAGAIN')
|
||||
|
||||
try {
|
||||
stream.flushSync()
|
||||
} catch (err) {
|
||||
t.equal(err.code, 'EAGAIN', 'thrown error should be EAGAIN')
|
||||
fakeFs.write = fs.write
|
||||
fakeFs.writeSync = fs.writeSync
|
||||
stream.end()
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('finish', () => {
|
||||
t.pass('finish emitted')
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, `${buf}hello world\nsonic boom\n`, 'data on file should match written')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('retry on EBUSY', (t) => {
|
||||
t.plan(7)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.write = function (fd, buf, ...args) {
|
||||
t.pass('fake fs.write called')
|
||||
fakeFs.write = fs.write
|
||||
const err = new Error('EBUSY')
|
||||
err.code = 'EBUSY'
|
||||
process.nextTick(args.pop(), err)
|
||||
}
|
||||
const SonicBoom = proxyquire('..', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, sync: false, minLength: 0 })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('emit error on async EBUSY', (t) => {
|
||||
t.plan(11)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.write = function (fd, buf, ...args) {
|
||||
t.pass('fake fs.write called')
|
||||
fakeFs.write = fs.write
|
||||
const err = new Error('EBUSY')
|
||||
err.code = 'EBUSY'
|
||||
process.nextTick(args.pop(), err)
|
||||
}
|
||||
const SonicBoom = proxyquire('..', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({
|
||||
fd,
|
||||
sync: false,
|
||||
minLength: 12,
|
||||
retryEAGAIN: (err, writeBufferLen, remainingBufferLen) => {
|
||||
t.equal(err.code, 'EBUSY')
|
||||
t.equal(writeBufferLen, 12)
|
||||
t.equal(remainingBufferLen, 0)
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
stream.once('error', err => {
|
||||
t.equal(err.code, 'EBUSY')
|
||||
t.ok(stream.write('something else\n'))
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
261
node_modules/sonic-boom/test/sync.test.js
generated
vendored
Normal file
261
node_modules/sonic-boom/test/sync.test.js
generated
vendored
Normal file
@@ -0,0 +1,261 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('tap')
|
||||
const fs = require('fs')
|
||||
const proxyquire = require('proxyquire')
|
||||
const SonicBoom = require('../')
|
||||
const { file } = require('./helper')
|
||||
|
||||
test('write buffers that are not totally written with sync mode', (t) => {
|
||||
t.plan(9)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.writeSync = function (fd, buf, enc) {
|
||||
t.pass('fake fs.write called')
|
||||
fakeFs.writeSync = (fd, buf, enc) => {
|
||||
t.pass('calling real fs.writeSync, ' + buf)
|
||||
return fs.writeSync(fd, buf, enc)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 0, sync: true })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('write buffers that are not totally written with flush sync', (t) => {
|
||||
t.plan(7)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.writeSync = function (fd, buf, enc) {
|
||||
t.pass('fake fs.write called')
|
||||
fakeFs.writeSync = fs.writeSync
|
||||
return 0
|
||||
}
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 100, sync: false })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.flushSync()
|
||||
|
||||
stream.on('write', (n) => {
|
||||
if (n === 0) {
|
||||
t.fail('throwing to avoid infinite loop')
|
||||
throw Error('shouldn\'t call write handler after flushing with n === 0')
|
||||
}
|
||||
})
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('sync writing is fully sync', (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.writeSync = function (fd, buf, enc, cb) {
|
||||
t.pass('fake fs.write called')
|
||||
return fs.writeSync(fd, buf, enc)
|
||||
}
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 0, sync: true })
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
// 'drain' will be only emitted once,
|
||||
// the number of assertions at the top check this.
|
||||
stream.on('drain', () => {
|
||||
t.pass('drain emitted')
|
||||
})
|
||||
|
||||
const data = fs.readFileSync(dest, 'utf8')
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
|
||||
test('write enormously large buffers sync', (t) => {
|
||||
t.plan(3)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 0, sync: true })
|
||||
|
||||
const buf = Buffer.alloc(1024).fill('x').toString() // 1 MB
|
||||
let length = 0
|
||||
|
||||
for (let i = 0; i < 1024 * 512; i++) {
|
||||
length += buf.length
|
||||
stream.write(buf)
|
||||
}
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.stat(dest, (err, stat) => {
|
||||
t.error(err)
|
||||
t.equal(stat.size, length)
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('write enormously large buffers sync with utf8 multi-byte split', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 0, sync: true })
|
||||
|
||||
let buf = Buffer.alloc((1024 * 16) - 2).fill('x') // 16MB - 3B
|
||||
const length = buf.length + 4
|
||||
buf = buf.toString() + '🌲' // 16 MB + 1B
|
||||
|
||||
stream.write(buf)
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.stat(dest, (err, stat) => {
|
||||
t.error(err)
|
||||
t.equal(stat.size, length)
|
||||
const char = Buffer.alloc(4)
|
||||
const fd = fs.openSync(dest, 'r')
|
||||
fs.readSync(fd, char, 0, 4, length - 4)
|
||||
t.equal(char.toString(), '🌲')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
// for context see this issue https://github.com/pinojs/pino/issues/871
|
||||
test('file specified by dest path available immediately when options.sync is true', (t) => {
|
||||
t.plan(3)
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, sync: true })
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
stream.flushSync()
|
||||
t.pass('file opened and written to without error')
|
||||
})
|
||||
|
||||
test('sync error handling', (t) => {
|
||||
t.plan(1)
|
||||
try {
|
||||
/* eslint no-new: off */
|
||||
new SonicBoom({ dest: '/path/to/nowwhere', sync: true })
|
||||
t.fail('must throw synchronously')
|
||||
} catch (err) {
|
||||
t.pass('an error happened')
|
||||
}
|
||||
})
|
||||
|
||||
for (const fd of [1, 2]) {
|
||||
test(`fd ${fd}`, (t) => {
|
||||
t.plan(1)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const stream = new SonicBoom({ fd })
|
||||
|
||||
fakeFs.close = function (fd, cb) {
|
||||
t.fail(`should not close fd ${fd}`)
|
||||
}
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
test('._len must always be equal or greater than 0', (t) => {
|
||||
t.plan(3)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, sync: true })
|
||||
|
||||
t.ok(stream.write('hello world 👀\n'))
|
||||
t.ok(stream.write('another line 👀\n'))
|
||||
|
||||
t.equal(stream._len, 0)
|
||||
|
||||
stream.end()
|
||||
})
|
||||
|
||||
test('._len must always be equal or greater than 0', (t) => {
|
||||
const n = 20
|
||||
t.plan(n + 3)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, sync: true, minLength: 20 })
|
||||
|
||||
let str = ''
|
||||
for (let i = 0; i < 20; i++) {
|
||||
t.ok(stream.write('👀'))
|
||||
str += '👀'
|
||||
}
|
||||
|
||||
t.equal(stream._len, 0)
|
||||
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, str)
|
||||
})
|
||||
})
|
||||
465
node_modules/sonic-boom/test/write.test.js
generated
vendored
Normal file
465
node_modules/sonic-boom/test/write.test.js
generated
vendored
Normal file
@@ -0,0 +1,465 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('tap')
|
||||
const fs = require('fs')
|
||||
const proxyquire = require('proxyquire')
|
||||
const SonicBoom = require('../')
|
||||
const { file, runTests } = require('./helper')
|
||||
|
||||
runTests(buildTests)
|
||||
|
||||
function buildTests (test, sync) {
|
||||
// Reset the umask for testing
|
||||
process.umask(0o000)
|
||||
|
||||
test('write things to a file descriptor', (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('write things in a streaming fashion', (t) => {
|
||||
t.plan(8)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, sync })
|
||||
|
||||
stream.once('drain', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\n')
|
||||
t.ok(stream.write('something else\n'))
|
||||
})
|
||||
|
||||
stream.once('drain', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
|
||||
stream.on('finish', () => {
|
||||
t.pass('finish emitted')
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('can be piped into', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, sync })
|
||||
const source = fs.createReadStream(__filename, { encoding: 'utf8' })
|
||||
|
||||
source.pipe(stream)
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(__filename, 'utf8', (err, expected) => {
|
||||
t.error(err)
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('write things to a file', (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('minLength', (t) => {
|
||||
t.plan(8)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, minLength: 4096, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
const fail = t.fail
|
||||
stream.on('drain', fail)
|
||||
|
||||
// bad use of timer
|
||||
// TODO refactor
|
||||
setTimeout(function () {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, '')
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
})
|
||||
}, 100)
|
||||
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('write later on recoverable error', (t) => {
|
||||
t.plan(8)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 0, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
stream.on('error', () => {
|
||||
t.pass('error emitted')
|
||||
})
|
||||
|
||||
if (sync) {
|
||||
fakeFs.writeSync = function (fd, buf, enc) {
|
||||
t.pass('fake fs.writeSync called')
|
||||
throw new Error('recoverable error')
|
||||
}
|
||||
} else {
|
||||
fakeFs.write = function (fd, buf, ...args) {
|
||||
t.pass('fake fs.write called')
|
||||
setTimeout(() => args.pop()(new Error('recoverable error')), 0)
|
||||
}
|
||||
}
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
|
||||
setTimeout(() => {
|
||||
if (sync) {
|
||||
fakeFs.writeSync = fs.writeSync
|
||||
} else {
|
||||
fakeFs.write = fs.write
|
||||
}
|
||||
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
}, 0)
|
||||
})
|
||||
|
||||
test('emit write events', (t) => {
|
||||
t.plan(7)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
let length = 0
|
||||
stream.on('write', (bytes) => {
|
||||
length += bytes
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
t.equal(length, 27)
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('write multi-byte characters string over than maxWrite', (t) => {
|
||||
const fakeFs = Object.create(fs)
|
||||
const MAX_WRITE = 65535
|
||||
fakeFs.write = function (fd, buf, ...args) {
|
||||
// only write byteLength === MAX_WRITE
|
||||
const _buf = Buffer.from(buf).subarray(0, MAX_WRITE).toString()
|
||||
fs.write(fd, _buf, ...args)
|
||||
setImmediate(args[args.length - 1], null, MAX_WRITE)
|
||||
fakeFs.write = function (fd, buf, ...args) {
|
||||
fs.write(fd, buf, ...args)
|
||||
}
|
||||
}
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 0, sync, maxWrite: MAX_WRITE })
|
||||
let buf = Buffer.alloc(MAX_WRITE).fill('x')
|
||||
buf = '🌲' + buf.toString()
|
||||
stream.write(buf)
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, buf)
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
stream.on('error', () => {
|
||||
t.pass('error emitted')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
test('write buffers that are not totally written', (t) => {
|
||||
t.plan(9)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.write = function (fd, buf, ...args) {
|
||||
t.pass('fake fs.write called')
|
||||
fakeFs.write = function (fd, buf, ...args) {
|
||||
t.pass('calling real fs.write, ' + buf)
|
||||
fs.write(fd, buf, ...args)
|
||||
}
|
||||
process.nextTick(args[args.length - 1], null, 0)
|
||||
}
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 0, sync: false })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('write enormously large buffers async', (t) => {
|
||||
t.plan(3)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 0, sync: false })
|
||||
|
||||
const buf = Buffer.alloc(1024).fill('x').toString() // 1 MB
|
||||
let length = 0
|
||||
|
||||
for (let i = 0; i < 1024 * 512; i++) {
|
||||
length += buf.length
|
||||
stream.write(buf)
|
||||
}
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.stat(dest, (err, stat) => {
|
||||
t.error(err)
|
||||
t.equal(stat.size, length)
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('make sure `maxWrite` is passed', (t) => {
|
||||
t.plan(1)
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, maxLength: 65536 })
|
||||
t.equal(stream.maxLength, 65536)
|
||||
})
|
||||
|
||||
test('write enormously large buffers async atomicly', (t) => {
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 0, sync: false })
|
||||
|
||||
const buf = Buffer.alloc(1023).fill('x').toString()
|
||||
|
||||
fakeFs.write = function (fd, _buf, ...args) {
|
||||
if (_buf.length % buf.length !== 0) {
|
||||
t.fail('write called with wrong buffer size')
|
||||
}
|
||||
|
||||
setImmediate(args[args.length - 1], null, _buf.length)
|
||||
}
|
||||
|
||||
for (let i = 0; i < 1024 * 512; i++) {
|
||||
stream.write(buf)
|
||||
}
|
||||
|
||||
setImmediate(() => {
|
||||
for (let i = 0; i < 1024 * 512; i++) {
|
||||
stream.write(buf)
|
||||
}
|
||||
|
||||
stream.end()
|
||||
})
|
||||
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
|
||||
test('write should not drop new data if buffer is not full', (t) => {
|
||||
t.plan(2)
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 101, maxLength: 102, sync: false })
|
||||
|
||||
const buf = Buffer.alloc(100).fill('x').toString()
|
||||
|
||||
fakeFs.write = function (fd, _buf, ...args) {
|
||||
t.equal(_buf.length, buf.length + 2)
|
||||
setImmediate(args[args.length - 1], null, _buf.length)
|
||||
fakeFs.write = () => t.error('shouldnt call write again')
|
||||
stream.end()
|
||||
}
|
||||
|
||||
stream.on('drop', (data) => {
|
||||
t.error('should not drop')
|
||||
})
|
||||
|
||||
stream.write(buf)
|
||||
stream.write('aa')
|
||||
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('write should drop new data if buffer is full', (t) => {
|
||||
t.plan(3)
|
||||
const fakeFs = Object.create(fs)
|
||||
const SonicBoom = proxyquire('../', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom({ fd, minLength: 101, maxLength: 102, sync: false })
|
||||
|
||||
const buf = Buffer.alloc(100).fill('x').toString()
|
||||
|
||||
fakeFs.write = function (fd, _buf, ...args) {
|
||||
t.equal(_buf.length, buf.length)
|
||||
setImmediate(args[args.length - 1], null, _buf.length)
|
||||
fakeFs.write = () => t.error('shouldnt call write more than once')
|
||||
}
|
||||
|
||||
stream.on('drop', (data) => {
|
||||
t.equal(data.length, 3)
|
||||
stream.end()
|
||||
})
|
||||
|
||||
stream.write(buf)
|
||||
stream.write('aaa')
|
||||
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
62
node_modules/sonic-boom/types/index.d.ts
generated
vendored
Normal file
62
node_modules/sonic-boom/types/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
// Type definitions for sonic-boom 0.7
|
||||
// Definitions by: Alex Ferrando <https://github.com/alferpal>
|
||||
// Igor Savin <https://github.com/kibertoad>
|
||||
/// <reference types="node"/>
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
export default SonicBoom;
|
||||
export type SonicBoomOpts = {
|
||||
fd?: number | string | symbol
|
||||
dest?: string | number
|
||||
maxLength?: number
|
||||
minLength?: number
|
||||
maxWrite?: number
|
||||
sync?: boolean
|
||||
fsync?: boolean
|
||||
append?: boolean
|
||||
mode?: string | number
|
||||
mkdir?: boolean
|
||||
contentMode?: 'buffer' | 'utf8'
|
||||
retryEAGAIN?: (err: Error, writeBufferLen: number, remainingBufferLen: number) => boolean
|
||||
}
|
||||
|
||||
export class SonicBoom extends EventEmitter {
|
||||
/**
|
||||
* @param [fileDescriptor] File path or numerical file descriptor
|
||||
* relative protocol is enabled. Default: process.stdout
|
||||
* @returns a new sonic-boom instance
|
||||
*/
|
||||
constructor(opts: SonicBoomOpts)
|
||||
|
||||
/**
|
||||
* Writes the string to the file. It will return false to signal the producer to slow down.
|
||||
*/
|
||||
write(string: string): boolean;
|
||||
|
||||
/**
|
||||
* Writes the current buffer to the file if a write was not in progress.
|
||||
* Do nothing if minLength is zero or if it is already writing.
|
||||
*/
|
||||
flush(cb?: (err?: Error) => unknown): void;
|
||||
|
||||
/**
|
||||
* Reopen the file in place, useful for log rotation.
|
||||
*/
|
||||
reopen(fileDescriptor?: string | number): void;
|
||||
|
||||
/**
|
||||
* Flushes the buffered data synchronously. This is a costly operation.
|
||||
*/
|
||||
flushSync(): void;
|
||||
|
||||
/**
|
||||
* Closes the stream, the data will be flushed down asynchronously
|
||||
*/
|
||||
end(): void;
|
||||
|
||||
/**
|
||||
* Closes the stream immediately, the data is not flushed.
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
4
node_modules/sonic-boom/types/tests/test.ts
generated
vendored
Normal file
4
node_modules/sonic-boom/types/tests/test.ts
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import { SonicBoom } from '../../'
|
||||
|
||||
const sonic = new SonicBoom({ fd: process.stdout.fd })
|
||||
sonic.write('hello sonic\n')
|
||||
Reference in New Issue
Block a user