This commit is contained in:
nik
2025-10-03 22:27:28 +03:00
parent 829fad0e17
commit 871cf7e792
16520 changed files with 2967597 additions and 3 deletions

2
node_modules/sonic-boom/.eslintignore generated vendored Normal file
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,151 @@
# sonic-boom
[![NPM Package Version](https://img.shields.io/npm/v/sonic-boom)](https://www.npmjs.com/package/sonic-boom)
[![Build Status](https://github.com/pinojs/sonic-boom/workflows/CI/badge.svg)](https://github.com/pinojs/sonic-boom/actions?query=workflow%3ACI)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,4 @@
import { SonicBoom } from '../../'
const sonic = new SonicBoom({ fd: process.stdout.fd })
sonic.write('hello sonic\n')