code:
'use strict'

const crypto = require('crypto')
const MiniPass = require('minipass')

const SPEC_ALGORITHMS = ['sha256', 'sha384', 'sha512']

// TODO: this should really be a hardcoded list of algorithms we support,
// rather than [a-z0-9].
const BASE64_REGEX = /^[a-z0-9+/]+(?:=?=?)$/i
const SRI_REGEX = /^([a-z0-9]+)-([^?]+)([?\S*]*)$/
const STRICT_SRI_REGEX = /^([a-z0-9]+)-([A-Za-z0-9+/=]{44,88})(\?[\x21-\x7E]*)?$/
const VCHAR_REGEX = /^[\x21-\x7E]+$/

const defaultOpts = {
  algorithms: ['sha512'],
  error: false,
  options: [],
  pickAlgorithm: getPrioritizedHash,
  sep: ' ',
  single: false,
  strict: false
}

const ssriOpts = (opts = {}) => ({ ...defaultOpts, ...opts })

const getOptString = options => !options || !options.length
  ? ''
  : `?${options.join('?')}`

const _onEnd = Symbol('_onEnd')
const _getOptions = Symbol('_getOptions')
class IntegrityStream extends MiniPass {
  constructor (opts) {
    super()
    this.size = 0
    this.opts = opts

    // may be overridden later, but set now for class consistency
    this[_getOptions]()

    // options used for calculating stream.  can't be changed.
    const { algorithms = defaultOpts.algorithms } = opts
    this.algorithms = Array.from(
      new Set(algorithms.concat(this.algorithm ? [this.algorithm] : []))
    )
    this.hashes = this.algorithms.map(crypto.createHash)
  }

  [_getOptions] () {
    const {
      integrity,
      size,
      options
    } = { ...defaultOpts, ...this.opts }

    // For verification
    this.sri = integrity ? parse(integrity, this.opts) : null
    this.expectedSize = size
    this.goodSri = this.sri ? !!Object.keys(this.sri).length : false
    this.algorithm = this.goodSri ? this.sri.pickAlgorithm(this.opts) : null
    this.digests = this.goodSri ? this.sri[this.algorithm] : null
    this.optString = getOptString(options)
  }

  emit (ev, data) {
    if (ev === 'end') this[_onEnd]()
    return super.emit(ev, data)
  }

  write (data) {
    this.size += data.length
    this.hashes.forEach(h => h.update(data))
    return super.write(data)
  }

  [_onEnd] () {
    if (!this.goodSri) {
      this[_getOptions]()
    }
    const newSri = parse(this.hashes.map((h, i) => {
      return `${this.algorithms[i]}-${h.digest('base64')}${this.optString}`
    }).join(' '), this.opts)
    // Integrity verification mode
    const match = this.goodSri && newSri.match(this.sri, this.opts)
    if (typeof this.expectedSize === 'number' && this.size !== this.expectedSize) {
      const err = new Error(`stream size mismatch when checking ${this.sri}.\n  Wanted: ${this.expectedSize}\n  Found: ${this.size}`)
      err.code = 'EBADSIZE'
      err.found = this.size
      err.expected = this.expectedSize
      err.sri = this.sri
      this.emit('error', err)
    } else if (this.sri && !match) {
      const err = new Error(`${this.sri} integrity checksum failed when using ${this.algorithm}: wanted ${this.digests} but got ${newSri}. (${this.size} bytes)`)
      err.code = 'EINTEGRITY'
      err.found = newSri
      err.expected = this.digests
      err.algorithm = this.algorithm
      err.sri = this.sri
      this.emit('error', err)
    } else {
      this.emit('size', this.size)
      this.emit('integrity', newSri)
      match && this.emit('verified', match)
    }
  }
}

class Hash {
  get isHash () { return true }
  constructor (hash, opts) {
    opts = ssriOpts(opts)
    const strict = !!opts.strict
    this.source = hash.trim()

    // set default values so that we make V8 happy to
    // always see a familiar object template.
    this.digest = ''
    this.algorithm = ''
    this.options = []

    // 3.1. Integrity metadata (called "Hash" by ssri)
    // https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description
    const match = this.source.match(
      strict
        ? STRICT_SRI_REGEX
        : SRI_REGEX
    )
    if (!match) { return }
    if (strict && !SPEC_ALGORITHMS.some(a => a === match[1])) { return }
    this.algorithm = match[1]
    this.digest = match[2]

    const rawOpts = match[3]
    if (rawOpts) {
      this.options = rawOpts.slice(1).split('?')
    }
  }

  hexDigest () {
    return this.digest && Buffer.from(this.digest, 'base64').toString('hex')
  }

  toJSON () {
    return this.toString()
  }

  toString (opts) {
    opts = ssriOpts(opts)
    if (opts.strict) {
      // Strict mode enforces the standard as close to the foot of the
      // letter as it can.
      if (!(
        // The spec has very restricted productions for algorithms.
        // https://www.w3.org/TR/CSP2/#source-list-syntax
        SPEC_ALGORITHMS.some(x => x === this.algorithm) &&
        // Usually, if someone insists on using a "different" base64, we
        // leave it as-is, since there's multiple standards, and the
        // specified is not a URL-safe variant.
        // https://www.w3.org/TR/CSP2/#base64_value
        this.digest.match(BASE64_REGEX) &&
        // Option syntax is strictly visual chars.
        // https://w3c.github.io/webappsec-subresource-integrity/#grammardef-option-expression
        // https://tools.ietf.org/html/rfc5234#appendix-B.1
        this.options.every(opt => opt.match(VCHAR_REGEX))
      )) {
        return ''
      }
    }
    const options = this.options && this.options.length
      ? `?${this.options.join('?')}`
      : ''
    return `${this.algorithm}-${this.digest}${options}`
  }
}

class Integrity {
  get isIntegrity () { return true }
  toJSON () {
    return this.toString()
  }

  isEmpty () {
    return Object.keys(this).length === 0
  }

  toString (opts) {
    opts = ssriOpts(opts)
    let sep = opts.sep || ' '
    if (opts.strict) {
      // Entries must be separated by whitespace, according to spec.
      sep = sep.replace(/\S+/g, ' ')
    }
    return Object.keys(this).map(k => {
      return this[k].map(hash => {
        return Hash.prototype.toString.call(hash, opts)
      }).filter(x => x.length).join(sep)
    }).filter(x => x.length).join(sep)
  }

  concat (integrity, opts) {
    opts = ssriOpts(opts)
    const other = typeof integrity === 'string'
      ? integrity
      : stringify(integrity, opts)
    return parse(`${this.toString(opts)} ${other}`, opts)
  }

  hexDigest () {
    return parse(this, { single: true }).hexDigest()
  }

  // add additional hashes to an integrity value, but prevent
  // *changing* an existing integrity hash.
  merge (integrity, opts) {
    opts = ssriOpts(opts)
    const other = parse(integrity, opts)
    for (const algo in other) {
      if (this[algo]) {
        if (!this[algo].find(hash =>
          other[algo].find(otherhash =>
            hash.digest === otherhash.digest))) {
          throw new Error('hashes do not match, cannot update integrity')
        }
      } else {
        this[algo] = other[algo]
      }
    }
  }

  match (integrity, opts) {
    opts = ssriOpts(opts)
    const other = parse(integrity, opts)
    const algo = other.pickAlgorithm(opts)
    return (
      this[algo] &&
      other[algo] &&
      this[algo].find(hash =>
        other[algo].find(otherhash =>
          hash.digest === otherhash.digest
        )
      )
    ) || false
  }

  pickAlgorithm (opts) {
    opts = ssriOpts(opts)
    const pickAlgorithm = opts.pickAlgorithm
    const keys = Object.keys(this)
    return keys.reduce((acc, algo) => {
      return pickAlgorithm(acc, algo) || acc
    })
  }
}

module.exports.parse = parse
function parse (sri, opts) {
  if (!sri) return null
  opts = ssriOpts(opts)
  if (typeof sri === 'string') {
    return _parse(sri, opts)
  } else if (sri.algorithm && sri.digest) {
    const fullSri = new Integrity()
    fullSri[sri.algorithm] = [sri]
    return _parse(stringify(fullSri, opts), opts)
  } else {
    return _parse(stringify(sri, opts), opts)
  }
}

function _parse (integrity, opts) {
  // 3.4.3. Parse metadata
  // https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata
  if (opts.single) {
    return new Hash(integrity, opts)
  }
  const hashes = integrity.trim().split(/\s+/).reduce((acc, string) => {
    const hash = new Hash(string, opts)
    if (hash.algorithm && hash.digest) {
      const algo = hash.algorithm
      if (!acc[algo]) { acc[algo] = [] }
      acc[algo].push(hash)
    }
    return acc
  }, new Integrity())
  return hashes.isEmpty() ? null : hashes
}

module.exports.stringify = stringify
function stringify (obj, opts) {
  opts = ssriOpts(opts)
  if (obj.algorithm && obj.digest) {
    return Hash.prototype.toString.call(obj, opts)
  } else if (typeof obj === 'string') {
    return stringify(parse(obj, opts), opts)
  } else {
    return Integrity.prototype.toString.call(obj, opts)
  }
}

module.exports.fromHex = fromHex
function fromHex (hexDigest, algorithm, opts) {
  opts = ssriOpts(opts)
  const optString = getOptString(opts.options)
  return parse(
    `${algorithm}-${
      Buffer.from(hexDigest, 'hex').toString('base64')
    }${optString}`, opts
  )
}

module.exports.fromData = fromData
function fromData (data, opts) {
  opts = ssriOpts(opts)
  const algorithms = opts.algorithms
  const optString = getOptString(opts.options)
  return algorithms.reduce((acc, algo) => {
    const digest = crypto.createHash(algo).update(data).digest('base64')
    const hash = new Hash(
      `${algo}-${digest}${optString}`,
      opts
    )
    /* istanbul ignore else - it would be VERY strange if the string we
     * just calculated with an algo did not have an algo or digest.
     */
    if (hash.algorithm && hash.digest) {
      const algo = hash.algorithm
      if (!acc[algo]) { acc[algo] = [] }
      acc[algo].push(hash)
    }
    return acc
  }, new Integrity())
}

module.exports.fromStream = fromStream
function fromStream (stream, opts) {
  opts = ssriOpts(opts)
  const istream = integrityStream(opts)
  return new Promise((resolve, reject) => {
    stream.pipe(istream)
    stream.on('error', reject)
    istream.on('error', reject)
    let sri
    istream.on('integrity', s => { sri = s })
    istream.on('end', () => resolve(sri))
    istream.on('data', () => {})
  })
}

module.exports.checkData = checkData
function checkData (data, sri, opts) {
  opts = ssriOpts(opts)
  sri = parse(sri, opts)
  if (!sri || !Object.keys(sri).length) {
    if (opts.error) {
      throw Object.assign(
        new Error('No valid integrity hashes to check against'), {
          code: 'EINTEGRITY'
        }
      )
    } else {
      return false
    }
  }
  const algorithm = sri.pickAlgorithm(opts)
  const digest = crypto.createHash(algorithm).update(data).digest('base64')
  const newSri = parse({ algorithm, digest })
  const match = newSri.match(sri, opts)
  if (match || !opts.error) {
    return match
  } else if (typeof opts.size === 'number' && (data.length !== opts.size)) {
    const err = new Error(`data size mismatch when checking ${sri}.\n  Wanted: ${opts.size}\n  Found: ${data.length}`)
    err.code = 'EBADSIZE'
    err.found = data.length
    err.expected = opts.size
    err.sri = sri
    throw err
  } else {
    const err = new Error(`Integrity checksum failed when using ${algorithm}: Wanted ${sri}, but got ${newSri}. (${data.length} bytes)`)
    err.code = 'EINTEGRITY'
    err.found = newSri
    err.expected = sri
    err.algorithm = algorithm
    err.sri = sri
    throw err
  }
}

module.exports.checkStream = checkStream
function checkStream (stream, sri, opts) {
  opts = ssriOpts(opts)
  opts.integrity = sri
  sri = parse(sri, opts)
  if (!sri || !Object.keys(sri).length) {
    return Promise.reject(Object.assign(
      new Error('No valid integrity hashes to check against'), {
        code: 'EINTEGRITY'
      }
    ))
  }
  const checker = integrityStream(opts)
  return new Promise((resolve, reject) => {
    stream.pipe(checker)
    stream.on('error', reject)
    checker.on('error', reject)
    let sri
    checker.on('verified', s => { sri = s })
    checker.on('end', () => resolve(sri))
    checker.on('data', () => {})
  })
}

module.exports.integrityStream = integrityStream
function integrityStream (opts = {}) {
  return new IntegrityStream(opts)
}

module.exports.create = createIntegrity
function createIntegrity (opts) {
  opts = ssriOpts(opts)
  const algorithms = opts.algorithms
  const optString = getOptString(opts.options)

  const hashes = algorithms.map(crypto.createHash)

  return {
    update: function (chunk, enc) {
      hashes.forEach(h => h.update(chunk, enc))
      return this
    },
    digest: function (enc) {
      const integrity = algorithms.reduce((acc, algo) => {
        const digest = hashes.shift().digest('base64')
        const hash = new Hash(
          `${algo}-${digest}${optString}`,
          opts
        )
        /* istanbul ignore else - it would be VERY strange if the hash we
         * just calculated with an algo did not have an algo or digest.
         */
        if (hash.algorithm && hash.digest) {
          const algo = hash.algorithm
          if (!acc[algo]) { acc[algo] = [] }
          acc[algo].push(hash)
        }
        return acc
      }, new Integrity())

      return integrity
    }
  }
}

const NODE_HASHES = new Set(crypto.getHashes())

// This is a Best Effort™ at a reasonable priority for hash algos
const DEFAULT_PRIORITY = [
  'md5', 'whirlpool', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512',
  // TODO - it's unclear _which_ of these Node will actually use as its name
  //        for the algorithm, so we guesswork it based on the OpenSSL names.
  'sha3',
  'sha3-256', 'sha3-384', 'sha3-512',
  'sha3_256', 'sha3_384', 'sha3_512'
].filter(algo => NODE_HASHES.has(algo))

function getPrioritizedHash (algo1, algo2) {
  return DEFAULT_PRIORITY.indexOf(algo1.toLowerCase()) >= DEFAULT_PRIORITY.indexOf(algo2.toLowerCase())
    ? algo1
    : algo2
}


ssri

description: Standard Subresource Integrity library -- parses, serializes, generates, and verifies integrity metadata according to the SRI spec.

prerelease: npm t

postrelease: npm publish

prepublishOnly: git push --follow-tags

posttest: npm run lint

release: standard-version -s

test: tap

coverage: tap

lint: standard

keywords:
w3c
web
security
integrity
checksum
hashing
subresource integrity
sri
sri hash
sri string
sri generator
html

Kat Marchán

twitter: maybekatz

license: ISC

license: mit


code:
## Stable

A stable array sort, because `Array#sort()` is not guaranteed stable.

MIT licensed.

[![Node.js CI](https://secure.travis-ci.org/Two-Screen/stable.png)](http://travis-ci.org/Two-Screen/stable)

[![Browser CI](http://ci.testling.com/Two-Screen/stable.png)](http://ci.testling.com/Two-Screen/stable)

#### From npm

Install with:

```sh
npm install stable
```

Then use it in Node.js or some other CommonJS environment as:

```js
const stable = require('stable')
```

#### From the browser

Include [`stable.js`] or the minified version [`stable.min.js`]
in your page, then call `stable()`.

 [`stable.js`]: https://raw.github.com/Two-Screen/stable/master/stable.js
 [`stable.min.js`]: https://raw.github.com/Two-Screen/stable/master/stable.min.js

#### Usage

The default sort is, as with `Array#sort`, lexicographical:

```js
stable(['foo', 'bar', 'baz'])  // => ['bar', 'baz', 'foo']
stable([10, 1, 5])             // => [1, 10, 5]
```

Unlike `Array#sort`, the default sort is **NOT** in-place. To do an in-place
sort, use `stable.inplace`, which otherwise works the same:

```js
const arr = [10, 1, 5]
stable(arr) === arr          // => false
stable.inplace(arr) === arr  // => true
```

A comparator function can be specified:

```js
// Regular sort() compatible comparator, that returns a number.
// This demonstrates the default behavior.
const lexCmp = (a, b) => String(a).localeCompare(b)
stable(['foo', 'bar', 'baz'], lexCmp)  // => ['bar', 'baz', 'foo']

// Boolean comparator. Sorts `b` before `a` if true.
// This demonstrates a simple way to sort numerically.
const greaterThan = (a, b) => a > b
stable([10, 1, 5], greaterThan)  // => [1, 5, 10]
```

#### License

Copyright (C) 2018 Angry Bytes and contributors.

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.


code:
export as namespace stable;
export = stable;

type Comparator<T> = ((a : T, b : T)=>boolean) | ((a: T, b : T)=>number);

declare function stable<T>(array : T[], comparator? : Comparator<T>) : T[];
declare namespace stable {
    export function inplace<T>(array: T[], comparator? : Comparator<T>) : T[];
}


stable

keywords: stable array sort

description: A stable array sort for JavaScript

type: git

test: standard src/ && node ./src/test.js

prepare: npm run build && npm run minify

build: rollup -c

minify: uglifyjs --comments "/^!/" -c -m -o ./stable.min.js ./stable.js

author: Angry Bytes <info@angrybytes.com>

license: MIT


code:
//! stable.js 0.1.8, https://github.com/Two-Screen/stable
//! © 2018 Angry Bytes and contributors. MIT licensed.

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global.stable = factory());
}(this, (function () { 'use strict';

  // A stable array sort, because `Array#sort()` is not guaranteed stable.
  // This is an implementation of merge sort, without recursion.

  var stable = function (arr, comp) {
    return exec(arr.slice(), comp)
  };

  stable.inplace = function (arr, comp) {
    var result = exec(arr, comp);

    // This simply copies back if the result isn't in the original array,
    // which happens on an odd number of passes.
    if (result !== arr) {
      pass(result, null, arr.length, arr);
    }

    return arr
  };

  // Execute the sort using the input array and a second buffer as work space.
  // Returns one of those two, containing the final result.
  function exec(arr, comp) {
    if (typeof(comp) !== 'function') {
      comp = function (a, b) {
        return String(a).localeCompare(b)
      };
    }

    // Short-circuit when there's nothing to sort.
    var len = arr.length;
    if (len <= 1) {
      return arr
    }

    // Rather than dividing input, simply iterate chunks of 1, 2, 4, 8, etc.
    // Chunks are the size of the left or right hand in merge sort.
    // Stop when the left-hand covers all of the array.
    var buffer = new Array(len);
    for (var chk = 1; chk < len; chk *= 2) {
      pass(arr, comp, chk, buffer);

      var tmp = arr;
      arr = buffer;
      buffer = tmp;
    }

    return arr
  }

  // Run a single pass with the given chunk size.
  var pass = function (arr, comp, chk, result) {
    var len = arr.length;
    var i = 0;
    // Step size / double chunk size.
    var dbl = chk * 2;
    // Bounds of the left and right chunks.
    var l, r, e;
    // Iterators over the left and right chunk.
    var li, ri;

    // Iterate over pairs of chunks.
    for (l = 0; l < len; l += dbl) {
      r = l + chk;
      e = r + chk;
      if (r > len) r = len;
      if (e > len) e = len;

      // Iterate both chunks in parallel.
      li = l;
      ri = r;
      while (true) {
        // Compare the chunks.
        if (li < r && ri < e) {
          // This works for a regular `sort()` compatible comparator,
          // but also for a simple comparator like: `a > b`
          if (comp(arr[li], arr[ri]) <= 0) {
            result[i++] = arr[li++];
          }
          else {
            result[i++] = arr[ri++];
          }
        }
        // Nothing to compare, just flush what's left.
        else if (li < r) {
          result[i++] = arr[li++];
        }
        else if (ri < e) {
          result[i++] = arr[ri++];
        }
        // Both iterators are at the chunk ends.
        else {
          break
        }
      }
    }
  };

  return stable;

})));


code:
//! stable.js 0.1.8, https://github.com/Two-Screen/stable
//! © 2018 Angry Bytes and contributors. MIT licensed.
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):e.stable=n()}(this,function(){"use strict";var e=function(e,n){return t(e.slice(),n)};function t(e,n){"function"!=typeof n&&(n=function(e,n){return String(e).localeCompare(n)});var r=e.length;if(r<=1)return e;for(var t=new Array(r),f=1;f<r;f*=2){i(e,n,f,t);var o=e;e=t,t=o}return e}e.inplace=function(e,n){var r=t(e,n);return r!==e&&i(r,null,e.length,e),e};var i=function(e,n,r,t){var f,o,i,u,a,c=e.length,l=0,s=2*r;for(f=0;f<c;f+=s)for(i=(o=f+r)+r,c<o&&(o=c),c<i&&(i=c),u=f,a=o;;)if(u<o&&a<i)n(e[u],e[a])<=0?t[l++]=e[u++]:t[l++]=e[a++];else if(u<o)t[l++]=e[u++];else{if(!(a<i))break;t[l++]=e[a++]}};return e});


code:
stackframe
==========
## JS Object representation of a stack frame

[![Build Status](https://img.shields.io/github/workflow/status/stacktracejs/stackframe/Continuous%20Integration/master?logo=github&style=flat-square)](https://github.com/stacktracejs/stackframe/actions?query=workflow%3AContinuous+Integration+branch%3Amaster)
[![Coverage Status](https://img.shields.io/coveralls/stacktracejs/stackframe.svg?style=flat-square)](https://coveralls.io/r/stacktracejs/stackframe?branch=master)
[![GitHub license](https://img.shields.io/github/license/stacktracejs/stackframe.svg?style=flat-square)](https://opensource.org/licenses/MIT)
[![dependencies](https://img.shields.io/badge/dependencies-0-green.svg?style=flat-square)](https://github.com/stacktracejs/stackframe/releases)
[![gzip size](https://img.shields.io/badge/gzipped-0.96k-green.svg?style=flat-square)](https://github.com/stacktracejs/stackframe/releases)
[![module format](https://img.shields.io/badge/module%20format-umd-lightgrey.svg?style=flat-square&colorB=ff69b4)](https://github.com/stacktracejs/stackframe/releases)
[![code of conduct](https://img.shields.io/badge/code%20of-conduct-lightgrey.svg?style=flat-square&colorB=ff69b4)](http://todogroup.org/opencodeofconduct/#stacktrace.js/me@eriwen.com)

Underlies functionality of other modules within [stacktrace.js](https://www.stacktracejs.com).

Written to closely resemble StackFrame representations in [Gecko](http://mxr.mozilla.org/mozilla-central/source/xpcom/base/nsIException.idl#14) and [V8](https://github.com/v8/v8/wiki/Stack%20Trace%20API)

## Usage
```js
// Create StackFrame and set properties
var stackFrame = new StackFrame({
    functionName: 'funName',
    args: ['args'],
    fileName: 'http://localhost:3000/file.js',
    lineNumber: 1,
    columnNumber: 3288,
    isEval: true,
    isNative: false,
    source: 'ORIGINAL_STACK_LINE'
    evalOrigin: new StackFrame({functionName: 'withinEval', lineNumber: 2, columnNumber: 43})
});

stackFrame.functionName      // => "funName"
stackFrame.setFunctionName('newName')
stackFrame.getFunctionName() // => "newName"

stackFrame.args              // => ["args"]
stackFrame.setArgs([])
stackFrame.getArgs()         // => []

stackFrame.fileName          // => 'http://localhost:3000/file.min.js'
stackFrame.setFileName('http://localhost:3000/file.js')
stackFrame.getFileName()     // => 'http://localhost:3000/file.js'

stackFrame.lineNumber        // => 1
stackFrame.setLineNumber(325)
stackFrame.getLineNumber()   // => 325

stackFrame.columnNumber      // => 3288
stackFrame.setColumnNumber(20)
stackFrame.getColumnNumber() // => 20

stackFrame.source            // => 'ORIGINAL_STACK_LINE'
stackFrame.setSource('NEW_SOURCE')
stackFrame.getSource()       // => 'NEW_SOURCE'

stackFrame.isEval            // => true
stackFrame.setIsEval(false)
stackFrame.getIsEval()       // => false

stackFrame.isNative          // => false
stackFrame.setIsNative(true)
stackFrame.getIsNative()     // => true

stackFrame.evalOrigin                         // => StackFrame({functionName: 'withinEval', lineNumber: ...})
stackFrame.setEvalOrigin({functionName: 'evalFn', fileName: 'anonymous'})
stackFrame.getEvalOrigin().getFunctionName()  // => 'evalFn'

stackFrame.toString() // => 'funName(args)@http://localhost:3000/file.js:325:20'
```

## Browser Support
[![Sauce Test Status](https://saucelabs.com/browser-matrix/stacktracejs.svg)](https://saucelabs.com/u/stacktracejs)

## Installation
```
npm install stackframe
bower install stackframe
https://raw.githubusercontent.com/stacktracejs/stackframe/master/dist/stackframe.min.js
```


code:
(function(root, factory) {
    'use strict';
    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers.

    /* istanbul ignore next */
    if (typeof define === 'function' && define.amd) {
        define('stackframe', [], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory();
    } else {
        root.StackFrame = factory();
    }
}(this, function() {
    'use strict';
    function _isNumber(n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    }

    function _capitalize(str) {
        return str.charAt(0).toUpperCase() + str.substring(1);
    }

    function _getter(p) {
        return function() {
            return this[p];
        };
    }

    var booleanProps = ['isConstructor', 'isEval', 'isNative', 'isToplevel'];
    var numericProps = ['columnNumber', 'lineNumber'];
    var stringProps = ['fileName', 'functionName', 'source'];
    var arrayProps = ['args'];
    var objectProps = ['evalOrigin'];

    var props = booleanProps.concat(numericProps, stringProps, arrayProps, objectProps);

    function StackFrame(obj) {
        if (!obj) return;
        for (var i = 0; i < props.length; i++) {
            if (obj[props[i]] !== undefined) {
                this['set' + _capitalize(props[i])](obj[props[i]]);
            }
        }
    }

    StackFrame.prototype = {
        getArgs: function() {
            return this.args;
        },
        setArgs: function(v) {
            if (Object.prototype.toString.call(v) !== '[object Array]') {
                throw new TypeError('Args must be an Array');
            }
            this.args = v;
        },

        getEvalOrigin: function() {
            return this.evalOrigin;
        },
        setEvalOrigin: function(v) {
            if (v instanceof StackFrame) {
                this.evalOrigin = v;
            } else if (v instanceof Object) {
                this.evalOrigin = new StackFrame(v);
            } else {
                throw new TypeError('Eval Origin must be an Object or StackFrame');
            }
        },

        toString: function() {
            var fileName = this.getFileName() || '';
            var lineNumber = this.getLineNumber() || '';
            var columnNumber = this.getColumnNumber() || '';
            var functionName = this.getFunctionName() || '';
            if (this.getIsEval()) {
                if (fileName) {
                    return '[eval] (' + fileName + ':' + lineNumber + ':' + columnNumber + ')';
                }
                return '[eval]:' + lineNumber + ':' + columnNumber;
            }
            if (functionName) {
                return functionName + ' (' + fileName + ':' + lineNumber + ':' + columnNumber + ')';
            }
            return fileName + ':' + lineNumber + ':' + columnNumber;
        }
    };

    StackFrame.fromString = function StackFrame$$fromString(str) {
        var argsStartIndex = str.indexOf('(');
        var argsEndIndex = str.lastIndexOf(')');

        var functionName = str.substring(0, argsStartIndex);
        var args = str.substring(argsStartIndex + 1, argsEndIndex).split(',');
        var locationString = str.substring(argsEndIndex + 1);

        if (locationString.indexOf('@') === 0) {
            var parts = /@(.+?)(?::(\d+))?(?::(\d+))?$/.exec(locationString, '');
            var fileName = parts[1];
            var lineNumber = parts[2];
            var columnNumber = parts[3];
        }

        return new StackFrame({
            functionName: functionName,
            args: args || undefined,
            fileName: fileName,
            lineNumber: lineNumber || undefined,
            columnNumber: columnNumber || undefined
        });
    };

    for (var i = 0; i < booleanProps.length; i++) {
        StackFrame.prototype['get' + _capitalize(booleanProps[i])] = _getter(booleanProps[i]);
        StackFrame.prototype['set' + _capitalize(booleanProps[i])] = (function(p) {
            return function(v) {
                this[p] = Boolean(v);
            };
        })(booleanProps[i]);
    }

    for (var j = 0; j < numericProps.length; j++) {
        StackFrame.prototype['get' + _capitalize(numericProps[j])] = _getter(numericProps[j]);
        StackFrame.prototype['set' + _capitalize(numericProps[j])] = (function(p) {
            return function(v) {
                if (!_isNumber(v)) {
                    throw new TypeError(p + ' must be a Number');
                }
                this[p] = Number(v);
            };
        })(numericProps[j]);
    }

    for (var k = 0; k < stringProps.length; k++) {
        StackFrame.prototype['get' + _capitalize(stringProps[k])] = _getter(stringProps[k]);
        StackFrame.prototype['set' + _capitalize(stringProps[k])] = (function(p) {
            return function(v) {
                this[p] = String(v);
            };
        })(stringProps[k]);
    }

    return StackFrame;
}));


stackframe

description: JS Object representation of a stack frame

maintainers: Eric Wendelin <me@eriwen.com> (https://www.eriwen.com)

maintainers: Victor Homyakov <vkhomyackov@gmail.com> (https://github.com/victor-homyakov)

keywords:
stacktrace
error
debugger
stack frame

lint: eslint

test: karma start karma.conf.js --single-run

test-pr: karma start karma.conf.js --single-run --browsers Firefox,Chrome_No_Sandbox

test-ci: karma start karma.conf.ci.js --single-run

prepare: cp stackframe.js dist/ && uglifyjs stackframe.js -o dist/stackframe.min.js --compress --mangle --source-map "url=stackframe.min.js.map"


code:
// Type definitions for StackFrame v1.3
// Project: https://github.com/stacktracejs/stackframe
// Definitions by: Eric Wendelin <https://www.eriwen.com>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped

export as namespace StackFrame;  // global for non-module UMD users

export = StackFrame;

declare namespace StackFrame {
    export interface StackFrameOptions {
        isConstructor?: boolean;
        isEval?: boolean;
        isNative?: boolean;
        isToplevel?: boolean;
        columnNumber?: number;
        lineNumber?: number;
        fileName?: string;
        functionName?: string;
        source?: string;
        args?: any[];
        evalOrigin?: StackFrame;
    }
}

declare class StackFrame {
    constructor(obj: StackFrame.StackFrameOptions);

    args?: any[];
    getArgs(): any[] | undefined;
    setArgs(args: any[]): void;

    evalOrigin?: StackFrame;
    getEvalOrigin(): StackFrame | undefined;
    setEvalOrigin(stackframe: StackFrame): void;

    isConstructor?: boolean;
    getIsConstructor(): boolean | undefined;
    setIsConstructor(isConstructor: boolean): void;

    isEval?: boolean;
    getIsEval(): boolean | undefined;
    setIsEval(isEval: boolean): void;

    isNative?: boolean;
    getIsNative(): boolean | undefined;
    setIsNative(isNative: boolean): void;

    isToplevel?: boolean;
    getIsToplevel(): boolean | undefined;
    setIsToplevel(isToplevel: boolean): void;

    columnNumber?: number;
    getColumnNumber(): number | undefined;
    setColumnNumber(columnNumber: number): void;

    lineNumber?: number;
    getLineNumber(): number | undefined;
    setLineNumber(lineNumber: number): void;

    fileName?: string;
    getFileName(): string | undefined;
    setFileName(fileName: string): void;

    functionName?: string;
    getFunctionName(): string | undefined;
    setFunctionName(functionName: string): void;

    source?: string;
    getSource(): string | undefined;
    setSource(source: string): void;

    toString(): string;
}


code:
2.0.1 / 2021-01-03
==================

  * Fix returning values from `Object.prototype`

2.0.0 / 2020-04-19
==================

  * Drop support for Node.js 0.6
  * Fix messaging casing of `418 I'm a Teapot`
  * Remove code 306
  * Remove `status[code]` exports; use `status.message[code]`
  * Remove `status[msg]` exports; use `status.code[msg]`
  * Rename `425 Unordered Collection` to standard `425 Too Early`
  * Rename `STATUS_CODES` export to `message`
  * Return status message for `statuses(code)` when given code

1.5.0 / 2018-03-27
==================

  * Add `103 Early Hints`

1.4.0 / 2017-10-20
==================

  * Add `STATUS_CODES` export

1.3.1 / 2016-11-11
==================

  * Fix return type in JSDoc

1.3.0 / 2016-05-17
==================

  * Add `421 Misdirected Request`
  * perf: enable strict mode

1.2.1 / 2015-02-01
==================

  * Fix message for status 451
    - `451 Unavailable For Legal Reasons`

1.2.0 / 2014-09-28
==================

  * Add `208 Already Repored`
  * Add `226 IM Used`
  * Add `306 (Unused)`
  * Add `415 Unable For Legal Reasons`
  * Add `508 Loop Detected`

1.1.1 / 2014-09-24
==================

  * Add missing 308 to `codes.json`

1.1.0 / 2014-09-21
==================

  * Add `codes.json` for universal support

1.0.4 / 2014-08-20
==================

  * Package cleanup

1.0.3 / 2014-06-08
==================

  * Add 308 to `.redirect` category

1.0.2 / 2014-03-13
==================

  * Add `.retry` category

1.0.1 / 2014-03-12
==================

  * Initial release


code:
# statuses

[![NPM Version][npm-version-image]][npm-url]
[![NPM Downloads][npm-downloads-image]][npm-url]
[![Node.js Version][node-version-image]][node-version-url]
[![Build Status][ci-image]][ci-url]
[![Test Coverage][coveralls-image]][coveralls-url]

HTTP status utility for node.

This module provides a list of status codes and messages sourced from
a few different projects:

  * The [IANA Status Code Registry](https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml)
  * The [Node.js project](https://nodejs.org/)
  * The [NGINX project](https://www.nginx.com/)
  * The [Apache HTTP Server project](https://httpd.apache.org/)

## Installation

This is a [Node.js](https://nodejs.org/en/) module available through the
[npm registry](https://www.npmjs.com/). Installation is done using the
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):

```sh
$ npm install statuses
```

## API

<!-- eslint-disable no-unused-vars -->

```js
var status = require('statuses')
```

### status(code)

Returns the status message string for a known HTTP status code. The code
may be a number or a string. An error is thrown for an unknown status code.

<!-- eslint-disable no-undef -->

```js
status(403) // => 'Forbidden'
status('403') // => 'Forbidden'
status(306) // throws
```

### status(msg)

Returns the numeric status code for a known HTTP status message. The message
is case-insensitive. An error is thrown for an unknown status message.

<!-- eslint-disable no-undef -->

```js
status('forbidden') // => 403
status('Forbidden') // => 403
status('foo') // throws
```

### status.codes

Returns an array of all the status codes as `Integer`s.

### status.code[msg]

Returns the numeric status code for a known status message (in lower-case),
otherwise `undefined`.

<!-- eslint-disable no-undef, no-unused-expressions -->

```js
status['not found'] // => 404
```

### status.empty[code]

Returns `true` if a status code expects an empty body.

<!-- eslint-disable no-undef, no-unused-expressions -->

```js
status.empty[200] // => undefined
status.empty[204] // => true
status.empty[304] // => true
```

### status.message[code]

Returns the string message for a known numeric status code, otherwise
`undefined`. This object is the same format as the
[Node.js http module `http.STATUS_CODES`](https://nodejs.org/dist/latest/docs/api/http.html#http_http_status_codes).

<!-- eslint-disable no-undef, no-unused-expressions -->

```js
status.message[404] // => 'Not Found'
```

### status.redirect[code]

Returns `true` if a status code is a valid redirect status.

<!-- eslint-disable no-undef, no-unused-expressions -->

```js
status.redirect[200] // => undefined
status.redirect[301] // => true
```

### status.retry[code]

Returns `true` if you should retry the rest.

<!-- eslint-disable no-undef, no-unused-expressions -->

```js
status.retry[501] // => undefined
status.retry[503] // => true
```

## License

[MIT](LICENSE)

[ci-image]: https://badgen.net/github/checks/jshttp/statuses/master?label=ci
[ci-url]: https://github.com/jshttp/statuses/actions?query=workflow%3Aci
[coveralls-image]: https://badgen.net/coveralls/c/github/jshttp/statuses/master
[coveralls-url]: https://coveralls.io/r/jshttp/statuses?branch=master
[node-version-image]: https://badgen.net/npm/node/statuses
[node-version-url]: https://nodejs.org/en/download
[npm-downloads-image]: https://badgen.net/npm/dm/statuses
[npm-url]: https://npmjs.org/package/statuses
[npm-version-image]: https://badgen.net/npm/v/statuses


Continue

Switching Protocols

Processing

Early Hints

OK

Created

Accepted

Non-Authoritative Information

No Content

Reset Content

Partial Content

Multi-Status

Already Reported

IM Used

Multiple Choices

Moved Permanently

Found

See Other

Not Modified

Use Proxy

Temporary Redirect

Permanent Redirect

Bad Request

Unauthorized

Payment Required

Forbidden

Not Found

Method Not Allowed

Not Acceptable

Proxy Authentication Required

Request Timeout

Conflict

Gone

Length Required

Precondition Failed

Payload Too Large

URI Too Long

Unsupported Media Type

Range Not Satisfiable

Expectation Failed

I'm a Teapot

Misdirected Request

Unprocessable Entity

Locked

Failed Dependency

Too Early

Upgrade Required

Precondition Required

Too Many Requests

Request Header Fields Too Large

Unavailable For Legal Reasons

Internal Server Error

Not Implemented

Bad Gateway

Service Unavailable

Gateway Timeout

HTTP Version Not Supported

Variant Also Negotiates

Insufficient Storage

Loop Detected

Bandwidth Limit Exceeded

Not Extended

Network Authentication Required


code:
/*!
 * statuses
 * Copyright(c) 2014 Jonathan Ong
 * Copyright(c) 2016 Douglas Christopher Wilson
 * MIT Licensed
 */

'use strict'

/**
 * Module dependencies.
 * @private
 */

var codes = require('./codes.json')

/**
 * Module exports.
 * @public
 */

module.exports = status

// status code to message map
status.message = codes

// status message (lower-case) to code map
status.code = createMessageToStatusCodeMap(codes)

// array of status codes
status.codes = createStatusCodeList(codes)

// status codes for redirects
status.redirect = {
  300: true,
  301: true,
  302: true,
  303: true,
  305: true,
  307: true,
  308: true
}

// status codes for empty bodies
status.empty = {
  204: true,
  205: true,
  304: true
}

// status codes for when you should retry the request
status.retry = {
  502: true,
  503: true,
  504: true
}

/**
 * Create a map of message to status code.
 * @private
 */

function createMessageToStatusCodeMap (codes) {
  var map = {}

  Object.keys(codes).forEach(function forEachCode (code) {
    var message = codes[code]
    var status = Number(code)

    // populate map
    map[message.toLowerCase()] = status
  })

  return map
}

/**
 * Create a list of all status codes.
 * @private
 */

function createStatusCodeList (codes) {
  return Object.keys(codes).map(function mapCode (code) {
    return Number(code)
  })
}

/**
 * Get the status code for given message.
 * @private
 */

function getStatusCode (message) {
  var msg = message.toLowerCase()

  if (!Object.prototype.hasOwnProperty.call(status.code, msg)) {
    throw new Error('invalid status message: "' + message + '"')
  }

  return status.code[msg]
}

/**
 * Get the status message for given code.
 * @private
 */

function getStatusMessage (code) {
  if (!Object.prototype.hasOwnProperty.call(status.message, code)) {
    throw new Error('invalid status code: ' + code)
  }

  return status.message[code]
}

/**
 * Get the status code.
 *
 * Given a number, this will throw if it is not a known status
 * code, otherwise the code will be returned. Given a string,
 * the string will be parsed for a number and return the code
 * if valid, otherwise will lookup the code assuming this is
 * the status message.
 *
 * @param {string|number} code
 * @returns {number}
 * @public
 */

function status (code) {
  if (typeof code === 'number') {
    return getStatusMessage(code)
  }

  if (typeof code !== 'string') {
    throw new TypeError('code must be a number or string')
  }

  // '403'
  var n = parseInt(code, 10)
  if (!isNaN(n)) {
    return getStatusMessage(n)
  }

  return getStatusCode(code)
}


statuses

description: HTTP status utility

contributors: Douglas Christopher Wilson <doug@somethingdoug.com>

contributors: Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)

repository: jshttp/statuses

keywords: http status code

build: node scripts/build.js

fetch: node scripts/fetch-apache.js && node scripts/fetch-iana.js && node scripts/fetch-nginx.js && node scripts/fetch-node.js

lint: eslint --plugin markdown --ext js,md .

test: mocha --reporter spec --check-leaks --bail test/

test-ci: nyc --reporter=lcov --reporter=text npm test

test-cov: nyc --reporter=html --reporter=text npm test

update: npm run fetch && npm run build

version: node scripts/version-history.js && git add HISTORY.md


code:
v0.1.1 (03 Apr 2015)
  Clean up.

v0.1.0 (21 Oct 2013)
	Performance slightly improved due to the simplification of the code. This is
		the most basic code that is needed.
	If you pass an object, the stream is converted automatically to the object
		mode.

v0.0.2 (19 Jul 2013)
	Bump version.

v0.0.1 (01 Apr 2013)
	First release.


code:
streamifier
===========

#### Converts a Buffer/String into a readable stream ####

[![npm][npm-image]][npm-url]

___module_.createReadStream(object[, options]) : Readable__

Returns a Readable stream.

The `object` can be of any data type. If it is a Buffer or a string, the available `options` are [`highWaterMark` and `encoding`](http://nodejs.org/api/stream.html#stream_new_stream_readable_options), otherwise the Readable stream is automatically set in [object mode](http://nodejs.org/api/stream.html#stream_object_mode) and the `options` parameter is ignored.

[npm-image]: https://img.shields.io/npm/v/streamifier.svg?style=flat
[npm-url]: https://npmjs.org/package/streamifier


code:
'use strict';

var util = require('util');
var stream = require('stream');

module.exports.createReadStream = function (object, options) {
  return new MultiStream (object, options);
};

var MultiStream = function (object, options) {
  if (object instanceof Buffer || typeof object === 'string') {
    options = options || {};
    stream.Readable.call(this, {
      highWaterMark: options.highWaterMark,
      encoding: options.encoding
    });
  } else {
    stream.Readable.call(this, { objectMode: true });
  }
  this._object = object;
};

util.inherits(MultiStream, stream.Readable);

MultiStream.prototype._read = function () {
  this.push(this._object);
  this._object = null;
};


streamifier

description: Converts a Buffer/String into a readable stream

keywords: string buffer readable stream

author: Gabriel Llamas <gagle@outlook.com>

main: lib


code:
# streamroller Changelog

## [3.1.5](https://github.com/log4js-node/streamroller/milestone/30)

- [fix: tilde expansion for windows](https://github.com/log4js-node/streamroller/pull/165) - thanks [@lamweili](https://github.com/lamweili)
- [chore(deps-dev): updated dependencies](https://github.com/log4js-node/streamroller/pull/166) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump @commitlint/cli from 17.3.0 to 17.4.3
  - chore(deps-dev): bump @commitlint/config-conventional from 17.3.0 to 17.4.3
  - chore(deps-dev): bump @types/node from 8.11.18 to 8.13.0
  - chore(deps-dev): bump eslint from 8.30.0 to 8.34.0
  - chore(deps-dev): bump husky from 8.0.2 to 8.0.3
  - chore(deps-dev): updated package-lock.json

## [3.1.4](https://github.com/log4js-node/streamroller/milestone/29)

- [fix: addressed unhandled promise rejection when a file gets deleted in midst of rolling](https://github.com/log4js-node/streamroller/pull/160) - thanks [@lamweili](https://github.com/lamweili)
- [docs: updated repository url](https://github.com/log4js-node/streamroller/pull/158) - thanks [@lamweili](https://github.com/lamweili)
- [ci: replaced deprecated github set-output](https://github.com/log4js-node/streamroller/pull/159) - thanks [@lamweili](https://github.com/lamweili)
- [ci: added quotes](https://github.com/log4js-node/streamroller/pull/157) - thanks [@lamweili](https://github.com/lamweili)
- [chore(deps-dev): updated dependencies](https://github.com/log4js-node/streamroller/pull/161) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump @types/node from 18.11.9 to 18.11.18
  - chore(deps-dev): bump eslint from 8.28.0 to 8.30.0
  - chore(deps-dev): bump mocha from 10.1.0 to 10.2.0
  - chore(deps-dev): updated package-lock.json
- [chore(deps-dev): updated dependencies](https://github.com/log4js-node/streamroller/pull/156) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump @commitlint/cli from 17.1.2 to 17.3.0
  - chore(deps-dev): bump @commitlint/config-conventional from 17.1.0 to 17.3.0
  - chore(deps-dev): bump @types/node from 18.7.23 to 18.11.9
  - chore(deps-dev): bump eslint from 8.24.0 to 8.28.0
  - chore(deps-dev): bump husky from 8.0.1 to 8.0.2
  - chore(deps-dev): bump mocha from 10.0.0 to 10.1.0
  - chore(deps-dev): updated package-lock.json

## [3.1.3](https://github.com/log4js-node/streamroller/milestone/28)

- [ci: manually downgrade dev dependencies for older versions](https://github.com/log4js-node/streamroller/pull/153) - thanks [@lamweili](https://github.com/lamweili)
- [ci: removed scheduled job from codeql and separated npm audit](https://github.com/log4js-node/streamroller/pull/152) - thanks [@lamweili](https://github.com/lamweili)
- [ci: updated codeql from v1 to v2](https://github.com/log4js-node/streamroller/pull/151) - thanks [@lamweili](https://github.com/lamweili)
- [chore(deps): updated dependencies](https://github.com/log4js-node/streamroller/pull/154) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump @commitlint/cli from 17.0.3 to 17.1.2
  - chore(deps-dev): bump @commitlint/config-conventional from 17.0.3 to 17.1.0
  - chore(deps-dev): bump @types/node from 18.0.6 to 18.7.23
  - chore(deps-dev): bump eslint from 6.8.0 to 8.24.0
  - chore(deps-dev): bump mocha from 7.2.0 to 10.0.0
  - chore(deps): bump date-format from 4.0.13 to 4.0.14
  - chore(deps): updated package-lock.json

## [3.1.2](https://github.com/log4js-node/streamroller/milestone/27)

- [refactor: support older Node.js versions](https://github.com/log4js-node/streamroller/pull/147) - thanks [@lamweili](https://github.com/lamweili)
- [docs: renamed peteriman to lamweili](https://github.com/log4js-node/streamroller/pull/144) - thanks [@lamweili](https://github.com/lamweili)
- [ci: added tests for Node.js 8.x, 10.x, 18.x](https://github.com/log4js-node/streamroller/pull/148) - thanks [@lamweili](https://github.com/lamweili)
- [chore(deps): bump date-format from 4.0.11 to 4.0.13](https://github.com/log4js-node/streamroller/pull/150) - thanks [@lamweili](https://github.com/lamweili)
- [chore(deps-dev): updated dependencies](https://github.com/log4js-node/streamroller/pull/146) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump @commitlint/cli from 17.0.1 to 17.0.3
  - chore(deps-dev): bump @commitlint/config-conventional from 17.0.2 to 17.0.3
  - chore(deps-dev): bump @types/node from 17.0.38 to 18.0.6
  - chore(deps-dev): bump eslint from 8.16.0 to 8.20.0
  - chore(deps-dev): updated package-lock.json
- [chore(deps-dev): updated dependencies](https://github.com/log4js-node/streamroller/pull/143) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump @commitlint/cli from 17.0.0 to 17.0.1
  - chore(deps-dev): bump @commitlint/config-conventional 17.0.0 to 17.0.2
  - chore(deps-dev): bump @types/node from 17.0.35 to 17.0.38
  - chore(deps): bump date-format 4.0.10 to 4.0.11
  - chore(deps): updated package-lock.json

## [3.1.1](https://github.com/log4js-node/streamroller/milestone/26)

- [fix: fs.appendFileSync should use flag instead of flags](https://github.com/log4js-node/streamroller/pull/141) - thanks [@lamweili](https://github.com/lamweili)

## [3.1.0](https://github.com/log4js-node/streamroller/milestone/25)

- [feat: tilde expansion for filename](https://github.com/log4js-node/streamroller/pull/135) - thanks [@lamweili](https://github.com/lamweili)
- [fix: better file validation](https://github.com/log4js-node/streamroller/pull/134) - thanks [@lamweili](https://github.com/lamweili)
- [chore(deps-dev): updated dependencies](https://github.com/log4js-node/streamroller/pull/140) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump @commitlint/cli from 16.3.0 to 17.0.0
  - chore(deps-dev): bump @commitlint/config-conventional from 16.2.4 to 17.0.0
  - chore(deps-dev): bump @types/node from 17.0.33 to 17.0.35
  - chore(deps-dev): bump eslint from 8.15.0 to 8.16.0
  - chore(deps): updated package-lock.json

## [3.0.9](https://github.com/log4js-node/streamroller/milestone/24)

- [fix: maxSize=0 means no rolling](https://github.com/log4js-node/streamroller/pull/131) - thanks [@lamweili](https://github.com/lamweili)
- [chore(deps): updated dependencies](https://github.com/log4js-node/streamroller/pull/132) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump @commitlint/cli from 16.2.3 to 16.3.0
  - chore(deps-dev): bump @commitlint/config-conventional from 16.2.1 to 16.2.4
  - chore(deps-dev): bump @types/node from 17.0.26 to 17.0.33
  - chore(deps-dev): bump eslint from 8.14.0 to 8.15.0
  - chore(deps-dev): bump husky from 7.0.4 to 8.0.1
  - chore(deps-dev): bump mocha from 9.2.2 to 10.0.0
  - chore(deps): bump date-format from 4.0.9 to 4.0.10
  - chore(deps): updated package-lock.json

## [3.0.8](https://github.com/log4js-node/streamroller/milestone/23)

- [fix: concurrency issues when forked processes trying to roll same file](https://github.com/log4js-node/streamroller/pull/124) - thanks [@lamweili](https://github.com/lamweili)
  - [refactor: use writeStream.destroy() instead](https://github.com/log4js-node/streamroller/pull/125)
  - [refactor: use isCreated variable instead of e.code='EEXIST'](https://github.com/log4js-node/streamroller/pull/126)
- [chore(lint): added .eslintrc and fixed linting issues](https://github.com/log4js-node/streamroller/pull/123) - thanks [@lamweili](https://github.com/lamweili)
- [chore(deps): updated dependencies](https://github.com/log4js-node/streamroller/pull/127) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump @types/node from 17.0.24 to 17.0.26
  - chore(deps-dev): bump eslint from 8.13.0 to 8.14.0
  - chore(deps): bump date-format from 4.0.7 to 4.0.9
  - chore(deps): updated package-lock.json
- [chore(deps): updated dependencies](https://github.com/log4js-node/streamroller/pull/119) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps): bump fs-extra from 10.0.1 to 10.1.0
  - chore(deps): updated package-lock.json
  - revert: "[chore(dep): temporary fix for fs-extra issue (to be reverted when fs-extra patches it)](https://github.com/log4js-node/streamroller/pull/116)"

## [3.0.7](https://github.com/log4js-node/streamroller/milestone/22)

- [chore(deps): temporary fix for fs-extra issue (to be reverted when fs-extra patches it)](https://github.com/log4js-node/streamroller/pull/116) - thanks [@lamweili](https://github.com/lamweili)
- [chore(deps): updated dependencies](https://github.com/log4js-node/streamroller/pull/117) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps): bump date-format from 4.0.6 to 4.0.7
  - chore(deps): updated package-lock.json
- [chore(deps-dev): updated dependencies](https://github.com/log4js-node/streamroller/pull/113) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump @types/node from 17.0.23 to 17.0.24
  - chore(deps-dev): updated package-lock.json
- [chore(deps-dev): updated dependencies](https://github.com/log4js-node/streamroller/pull/112) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump @types/node from 17.0.22 to 17.0.23
  - chore(deps-dev): bump eslint from 8.11.0 to 8.13.0
  - chore(deps-dev): updated package-lock.json

## [3.0.6](https://github.com/log4js-node/streamroller/milestone/21)

- [chore(deps): updated dependencies](https://github.com/log4js-node/streamroller/pull/110) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps): bump debug from 4.3.3 to 4.3.4
  - chore(deps): bump date-format from 4.0.5 to 4.0.6
  - chore(deps-dev): bump @types/node from 17.0.21 to 17.0.22
  - chore(deps-dev): bump @commitlint/cli from 16.2.1 to 16.2.3
  - chore(deps): updated package-lock.json

## [3.0.5](https://github.com/log4js-node/streamroller/milestone/20)

- [fix: added filename validation](https://github.com/log4js-node/streamroller/pull/101) - thanks [@lamweili](https://github.com/lamweili)
- [docs: updated README.md with badges](https://github.com/log4js-node/streamroller/pull/105) - thanks [@lamweili](https://github.com/lamweili)
- [docs: updated README.md for DateRollingFileStream](https://github.com/log4js-node/streamroller/pull/106) - thanks [@lamweili](https://github.com/lamweili)
- [docs: added docs for istanbul ignore](https://github.com/log4js-node/streamroller/pull/107) - thanks [@lamweili](https://github.com/lamweili)
- [chore(deps): updated dependencies](https://github.com/log4js-node/streamroller/pull/109) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): eslint from 8.10.0 to 8.11.0
  - chore(deps-dev): mocha from 9.2.1 to 9.2.2
  - chore(deps): date-format from 4.0.4 to 4.0.5
  - chore(deps): updated package-lock.json

## [3.0.4](https://github.com/log4js-node/streamroller/milestone/19)

- [test: remove test file/folder remnants](https://github.com/log4js-node/streamroller/pull/99) - thanks [@lamweili](https://github.com/lamweili)

## [3.0.3](https://github.com/log4js-node/streamroller/milestone/18)

- [fix: backward compatibility for RollingFileWriteStream to recursively create directory](https://github.com/log4js-node/streamroller/pull/96) - thanks [@lamweili](https://github.com/lamweili)
- [test: 100% test coverage](https://github.com/log4js-node/streamroller/pull/94) - thanks [@lamweili](https://github.com/lamweili)
- [chore(deps): updated dependencies](https://github.com/log4js-node/streamroller/pull/97) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump @commitlint/cli from 16.1.0 to 16.2.1
  - chore(deps-dev): bump @commitlint/config-conventional from 16.0.0 to 16.2.1
  - chore(deps-dev): bump @types/node from 17.0.16 to 17.0.21
  - chore(deps-dev): bump eslint from 8.8.0 to 8.10.0
  - chore(deps-dev): bump mocha from 9.2.0 to 9.2.1
  - chore(deps): bump date-format from 4.0.3 to 4.0.4
  - chore(deps): bump fs-extra from 10.0.0 to 10.0.1
- [chore(deps): updated dependencies](https://github.com/log4js-node/streamroller/pull/95) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump @commitlint/cli from 16.0.2 to 16.1.0
  - chore(deps-dev): bump @types/node from 17.0.9 to 17.0.16
  - chore(deps-dev): bump eslint from 8.7.0 to 8.8.0
  - chore(deps-dev): bump proxyquire from 2.1.1 to 2.1.3
  - chore(deps): bump debug from 4.1.1 to 4.3.3
- [chore(deps): updated dependencies](https://github.com/log4js-node/streamroller/pull/92) - thanks [@lamweili](https://github.com/lamweili)
  - updated package-lock.json
- [chore(deps): updated dependencies](https://github.com/log4js-node/streamroller/pull/91) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump mocha from 9.1.4 to 9.2.0

## [3.0.2](https://github.com/log4js-node/streamroller/milestone/17)

- [fix: changed default file modes from 0o644 to 0o600 for better security](https://github.com/log4js-node/streamroller/pull/87) - thanks [@lamweili](https://github.com/lamweili)
- [refactor: housekeeping for comments and .gitignore](https://github.com/log4js-node/streamroller/pull/89) - thanks [@lamweili](https://github.com/lamweili)
- [chore(deps): updated dependencies](https://github.com/log4js-node/streamroller/pull/88) - thanks [@lamweili](https://github.com/lamweili)
  - chore(deps-dev): bump caniuse-lite from 1.0.30001299 to 1.0.30001300
  - chore(deps-dev): bump electron-to-chromium from 1.4.45 to 1.4.47
  - chore(deps-dev): bump @types/node from 17.0.8 to 17.0.9
  - chore(deps-dev): bump eslint from 8.6.0 to 8.7.0
  - chore(deps-dev): bump mocha from 9.1.3 to 9.1.4
  - chore(deps): bump date-format from 4.0.2 to 4.0.3

## [3.0.1](https://github.com/log4js-node/streamroller/milestone/16)

- [build: not to publish misc files to NPM](https://github.com/log4js-node/streamroller/pull/82) - thanks [@lamweili](https://github.com/lamweili)
- chore(deps): updated dependencies - thanks [@lamweili](https://github.com/lamweili)
  - [chore(deps): bump date-format from 4.0.1 to 4.0.2](https://github.com/log4js-node/streamroller/pull/86)
  - [chore(deps-dev): bump electron-to-chromium from 1.4.44 to 1.4.45](https://github.com/log4js-node/streamroller/pull/81)

## [3.0.0](https://github.com/log4js-node/streamroller/milestone/15)

- [feat: allow for 0 backups (only hot file)](https://github.com/log4js-node/streamroller/pull/74) - thanks [@lamweili](https://github.com/lamweili)
- [feat: exposed fileNameSep to be configurable](https://github.com/log4js-node/streamroller/pull/67) - thanks [@laidaxian](https://github.com/laidaxian)
  - [fix: for fileNameSep affecting globally](https://github.com/log4js-node/streamroller/pull/79) - thanks [@lamweili](https://github.com/lamweili)
- [fix: for mode to apply to compressed file](https://github.com/log4js-node/streamroller/pull/65) - thanks [@rnd-debug](https://github.com/rnd-debug)
- [fix: for extra separator in filenames](https://github.com/log4js-node/streamroller/pull/75) - thanks [@lamweili](https://github.com/lamweili)
- [refactor: moved to options.numBackups instead of options.daysToKeep](https://github.com/log4js-node/streamroller/pull/78) - thanks [@lamweili](https://github.com/lamweili)
- [test: improved test case performance for fakeNow](https://github.com/log4js-node/streamroller/pull/76) - thanks [@lamweili](https://github.com/lamweili)
- chore(deps-dev): updated dependencies - thanks @lamweili
  - [chore(deps-dev): updated package.json](https://github.com/log4js-node/streamroller/pull/70)
    - chore(deps-dev): bump @commitlint/cli from 8.1.0 to 16.0.2
    - chore(deps-dev): bump @commitlint/config-conventional from 8.1.0 to 16.0.0
    - chore(deps-dev): bump @type/nodes 17.0.8
    - chore(deps-dev): bump eslint from 6.0.1 to 8.6.0
    - chore(deps-dev): bump mocha from 6.1.4 to 9.1.3
    - chore(deps-dev): bump nyc from 14.1.1 to 15.1.0
  - [chore(deps-dev): updated package-lock.json](https://github.com/log4js-node/streamroller/pull/71)
    - chore(deps-dev): bump @babel/compat-data from 7.16.4 to 7.16.8
    - chore(deps-dev): bump @babel/generator from 7.16.7 to 7.16.8
    - chore(deps-dev): bump @babel/parser from 7.16.7 to 7.16.8
    - chore(deps-dev): bump @babel/travers from 7.16.7 to 7.16.8
    - chore(deps-dev): bump @babel/types from 7.16.7 to 7.16.8
  - [chore(deps-dev): updated package-lock.json](https://github.com/log4js-node/streamroller/pull/77)
    - chore(deps-dev): bump caniuse-lite from 1.0.30001298 to 1.0.30001299
    - chore(deps-dev): bump electron-to-chromium from 1.4.39 to 1.4.44
  - [chore(deps-dev): updated package.json](https://github.com/log4js-node/streamroller/pull/80)
    - chore(deps): bump date-format from 3.0.0 to 4.0.1
    - chore(deps-dev): bump husky from 3.0.0 to 7.0.4
    - chore(deps): bump fs-extra from 8.1.0 to 10.0.0

## [2.2.4](https://github.com/log4js-node/streamroller/milestone/14)

- [Fix for incorrect filename matching](https://github.com/log4js-node/streamroller/pull/61) - thanks [@rnd-debug](https://github.com/rnd-debug)

## [2.2.3](https://github.com/log4js-node/streamroller/milestone/13)

- [Fix for unhandled promise rejection during cleanup](https://github.com/log4js-node/streamroller/pull/56)

## [2.2.2](https://github.com/log4js-node/streamroller/milestone/12)

- [Fix for overwriting current file when using date rotation](https://github.com/log4js-node/streamroller/pull/54)

## 2.2.1

- Fix for num to keep not working when date pattern is all digits (forgot to do a PR for this one)

## [2.2.0](https://github.com/log4js-node/streamroller/milestone/11)

- [Fallback to copy and truncate when file is busy](https://github.com/log4js-node/streamroller/pull/53)

## [2.1.0](https://github.com/log4js-node/streamroller/milestone/10)

- [Improve Windows support (closing streams)](https://github.com/log4js-node/streamroller/pull/52)

## [2.0.0](https://github.com/log4js-node/streamroller/milestone/9)

- [Remove support for node v6](https://github.com/log4js-node/streamroller/pull/44)
- [Replace lodash with native alternatives](https://github.com/log4js-node/streamroller/pull/45) - thanks [@devoto13](https://github.com/devoto13)
- [Simplify filename formatting and parsing](https://github.com/log4js-node/streamroller/pull/46)
- [Removed async lib from main code](https://github.com/log4js-node/streamroller/pull/47)
- [Fix timezone issues in tests](https://github.com/log4js-node/streamroller/pull/48) - thanks [@devoto13](https://github.com/devoto13)
- [Fix for flag values that need existing file size](https://github.com/log4js-node/streamroller/pull/49)
- [Refactor for better readability](https://github.com/log4js-node/streamroller/pull/50)
- [Removed async lib from test code](https://github.com/log4js-node/streamroller/pull/51)

## [1.0.6](https://github.com/log4js-node/streamroller/milestone/8)

- [Fix for overwriting old backup files](https://github.com/log4js-node/streamroller/pull/43)
- Updated lodash to 4.17.14

## [1.0.5](https://github.com/log4js-node/streamroller/milestone/7)

- [Updated dependencies](https://github.com/log4js-node/streamroller/pull/38)
- [Fix for initial file date when appending to existing file](https://github.com/log4js-node/streamroller/pull/40)

## [1.0.4](https://github.com/log4js-node/streamroller/milestone/6)

- [Fix for initial size when appending to existing file](https://github.com/log4js-node/streamroller/pull/35)

## [1.0.3](https://github.com/log4js-node/streamroller/milestone/5)

- [Fix for crash when pattern is all digits](https://github.com/log4js-node/streamroller/pull/33)

## 1.0.2

- is exactly the same as 1.0.1, due to me being an idiot and not pulling before I pushed

## Previous versions

Previous release details are available by browsing the [milestones](https://github.com/log4js-node/streamroller/milestones) in github.


code:
const RollingFileWriteStream = require('./RollingFileWriteStream');

// just to adapt the previous version
class DateRollingFileStream extends RollingFileWriteStream {
  constructor(filename, pattern, options) {
    if (pattern && typeof(pattern) === 'object') {
      options = pattern;
      pattern = null;
    }
    if (!options) {
      options = {};
    }
    if (!pattern) {
      pattern = 'yyyy-MM-dd';
    }
    options.pattern = pattern;
    if (!options.numBackups && options.numBackups !== 0) {
      if (!options.daysToKeep && options.daysToKeep !== 0) {
        options.daysToKeep = 1;
      } else {
        process.emitWarning(
          "options.daysToKeep is deprecated due to the confusion it causes when used " +
          "together with file size rolling. Please use options.numBackups instead.",
          "DeprecationWarning", "streamroller-DEP0001"
        );
      }
      options.numBackups = options.daysToKeep;
    } else {
      options.daysToKeep = options.numBackups;
    }
    super(filename, options);
    this.mode = this.options.mode;
  }

  get theStream() {
    return this.currentFileStream;
  }

}

module.exports = DateRollingFileStream;


code:
const RollingFileWriteStream = require('./RollingFileWriteStream');

// just to adapt the previous version
class RollingFileStream extends RollingFileWriteStream {
  constructor(filename, size, backups, options) {
    if (!options) {
      options = {};
    }
    if (size) {
      options.maxSize = size;
    }
    if (!options.numBackups && options.numBackups !== 0) {
      if (!backups && backups !== 0) {
        backups = 1;
      }
      options.numBackups = backups;
    }
    super(filename, options);
    this.backups = options.numBackups;
    this.size = this.options.maxSize;
  }

  get theStream() {
    return this.currentFileStream;
  }

}

module.exports = RollingFileStream;


code:
const debug = require("debug")("streamroller:RollingFileWriteStream");
const fs = require("fs-extra");
const path = require("path");
const os = require("os");
const newNow = require("./now");
const format = require("date-format");
const { Writable } = require("stream");
const fileNameFormatter = require("./fileNameFormatter");
const fileNameParser = require("./fileNameParser");
const moveAndMaybeCompressFile = require("./moveAndMaybeCompressFile");

const deleteFiles = fileNames => {
  debug(`deleteFiles: files to delete: ${fileNames}`);
  return Promise.all(fileNames.map(f => fs.unlink(f).catch((e) => {
    debug(`deleteFiles: error when unlinking ${f}, ignoring. Error was ${e}`);
  })));
};

/**
 * RollingFileWriteStream is mainly used when writing to a file rolling by date or size.
 * RollingFileWriteStream inherits from stream.Writable
 */
class RollingFileWriteStream extends Writable {
  /**
   * Create a RollingFileWriteStream
   * @constructor
   * @param {string} filePath - The file path to write.
   * @param {object} options - The extra options
   * @param {number} options.numToKeep - The max numbers of files to keep.
   * @param {number} options.maxSize - The maxSize one file can reach. Unit is Byte.
   *                                   This should be more than 1024. The default is 0.
   *                                   If not specified or 0, then no log rolling will happen.
   * @param {string} options.mode - The mode of the files. The default is '0600'. Refer to stream.writable for more.
   * @param {string} options.flags - The default is 'a'. Refer to stream.flags for more.
   * @param {boolean} options.compress - Whether to compress backup files.
   * @param {boolean} options.keepFileExt - Whether to keep the file extension.
   * @param {string} options.pattern - The date string pattern in the file name.
   * @param {boolean} options.alwaysIncludePattern - Whether to add date to the name of the first file.
   */
  constructor(filePath, options) {
    debug(`constructor: creating RollingFileWriteStream. path=${filePath}`);
    if (typeof filePath !== "string" || filePath.length === 0) {
      throw new Error(`Invalid filename: ${filePath}`);
    } else if (filePath.endsWith(path.sep)) {
      throw new Error(`Filename is a directory: ${filePath}`);
    } else if (filePath.indexOf(`~${path.sep}`) === 0) {
      // handle ~ expansion: https://github.com/nodejs/node/issues/684
      // exclude ~ and ~filename as these can be valid files
      filePath = filePath.replace("~", os.homedir());
    }
    super(options);
    this.options = this._parseOption(options);
    this.fileObject = path.parse(filePath);
    if (this.fileObject.dir === "") {
      this.fileObject = path.parse(path.join(process.cwd(), filePath));
    }
    this.fileFormatter = fileNameFormatter({
      file: this.fileObject,
      alwaysIncludeDate: this.options.alwaysIncludePattern,
      needsIndex: this.options.maxSize < Number.MAX_SAFE_INTEGER,
      compress: this.options.compress,
      keepFileExt: this.options.keepFileExt,
      fileNameSep: this.options.fileNameSep
    });

    this.fileNameParser = fileNameParser({
      file: this.fileObject,
      keepFileExt: this.options.keepFileExt,
      pattern: this.options.pattern,
      fileNameSep: this.options.fileNameSep
    });

    this.state = {
      currentSize: 0
    };

    if (this.options.pattern) {
      this.state.currentDate = format(this.options.pattern, newNow());
    }

    this.filename = this.fileFormatter({
      index: 0,
      date: this.state.currentDate
    });
    if (["a", "a+", "as", "as+"].includes(this.options.flags)) {
      this._setExistingSizeAndDate();
    }

    debug(
      `constructor: create new file ${this.filename}, state=${JSON.stringify(
        this.state
      )}`
    );
    this._renewWriteStream();
  }

  _setExistingSizeAndDate() {
    try {
      const stats = fs.statSync(this.filename);
      this.state.currentSize = stats.size;
      if (this.options.pattern) {
        this.state.currentDate = format(this.options.pattern, stats.mtime);
      }
    } catch (e) {
      //file does not exist, that's fine - move along
      return;
    }
  }

  _parseOption(rawOptions) {
    const defaultOptions = {
      maxSize: 0,
      numToKeep: Number.MAX_SAFE_INTEGER,
      encoding: "utf8",
      mode: parseInt("0600", 8),
      flags: "a",
      compress: false,
      keepFileExt: false,
      alwaysIncludePattern: false
    };
    const options = Object.assign({}, defaultOptions, rawOptions);
    if (!options.maxSize) {
      delete options.maxSize;
    } else if (options.maxSize <= 0) {
      throw new Error(`options.maxSize (${options.maxSize}) should be > 0`);
    }
    // options.numBackups will supercede options.numToKeep
    if (options.numBackups || options.numBackups === 0) {
      if (options.numBackups < 0) {
        throw new Error(`options.numBackups (${options.numBackups}) should be >= 0`);
      } else if (options.numBackups >= Number.MAX_SAFE_INTEGER) {
        // to cater for numToKeep (include the hot file) at Number.MAX_SAFE_INTEGER
        throw new Error(`options.numBackups (${options.numBackups}) should be < Number.MAX_SAFE_INTEGER`);
      } else {
        options.numToKeep = options.numBackups + 1;
      }
    } else if (options.numToKeep <= 0) {
      throw new Error(`options.numToKeep (${options.numToKeep}) should be > 0`);
    }
    debug(
      `_parseOption: creating stream with option=${JSON.stringify(options)}`
    );
    return options;
  }

  _final(callback) {
    this.currentFileStream.end("", this.options.encoding, callback);
  }

  _write(chunk, encoding, callback) {
    this._shouldRoll().then(() => {
      debug(
        `_write: writing chunk. ` +
          `file=${this.currentFileStream.path} ` +
          `state=${JSON.stringify(this.state)} ` +
          `chunk=${chunk}`
      );
      this.currentFileStream.write(chunk, encoding, e => {
        this.state.currentSize += chunk.length;
        callback(e);
      });
    });
  }

  async _shouldRoll() {
    if (this._dateChanged() || this._tooBig()) {
      debug(
        `_shouldRoll: rolling because dateChanged? ${this._dateChanged()} or tooBig? ${this._tooBig()}`
      );
      await this._roll();
    }
  }

  _dateChanged() {
    return (
      this.state.currentDate &&
      this.state.currentDate !== format(this.options.pattern, newNow())
    );
  }

  _tooBig() {
    return this.state.currentSize >= this.options.maxSize;
  }

  _roll() {
    debug(`_roll: closing the current stream`);
    return new Promise((resolve, reject) => {
      this.currentFileStream.end("", this.options.encoding, () => {
        this._moveOldFiles()
          .then(resolve)
          .catch(reject);
      });
    });
  }

  async _moveOldFiles() {
    const files = await this._getExistingFiles();
    const todaysFiles = this.state.currentDate
      ? files.filter(f => f.date === this.state.currentDate)
      : files;
    for (let i = todaysFiles.length; i >= 0; i--) {
      debug(`_moveOldFiles: i = ${i}`);
      const sourceFilePath = this.fileFormatter({
        date: this.state.currentDate,
        index: i
      });
      const targetFilePath = this.fileFormatter({
        date: this.state.currentDate,
        index: i + 1
      });

      const moveAndCompressOptions = {
        compress: this.options.compress && i === 0,
        mode: this.options.mode
      };
      await moveAndMaybeCompressFile(
        sourceFilePath,
        targetFilePath,
        moveAndCompressOptions
      );
    }

    this.state.currentSize = 0;
    this.state.currentDate = this.state.currentDate
      ? format(this.options.pattern, newNow())
      : null;
    debug(
      `_moveOldFiles: finished rolling files. state=${JSON.stringify(
        this.state
      )}`
    );
    this._renewWriteStream();
    // wait for the file to be open before cleaning up old ones,
    // otherwise the daysToKeep calculations can be off
    await new Promise((resolve, reject) => {
      this.currentFileStream.write("", "utf8", () => {
        this._clean()
          .then(resolve)
          .catch(reject);
      });
    });
  }

  // Sorted from the oldest to the latest
  async _getExistingFiles() {
    const files = await fs.readdir(this.fileObject.dir)
      .catch( /* istanbul ignore next: will not happen on windows */ () => []);

    debug(`_getExistingFiles: files=${files}`);
    const existingFileDetails = files
      .map(n => this.fileNameParser(n))
      .filter(n => n);

    const getKey = n =>
      (n.timestamp ? n.timestamp : newNow().getTime()) - n.index;
    existingFileDetails.sort((a, b) => getKey(a) - getKey(b));

    return existingFileDetails;
  }

  _renewWriteStream() {
    const filePath = this.fileFormatter({
      date: this.state.currentDate,
      index: 0
    });

    // attempt to create the directory
    const mkdir = (dir) => {
      try {
        return fs.mkdirSync(dir, { recursive: true });
      }
      // backward-compatible fs.mkdirSync for nodejs pre-10.12.0 (without recursive option)
      catch (e) {
        // recursive creation of parent first
        if (e.code === "ENOENT") {
          mkdir(path.dirname(dir));
          return mkdir(dir);
        }

        // throw error for all except EEXIST and EROFS (read-only filesystem)
        if (e.code !== "EEXIST" && e.code !== "EROFS") {
          throw e;
        }

        // EEXIST: throw if file and not directory
        // EROFS : throw if directory not found
        else {
          try {
            if (fs.statSync(dir).isDirectory()) {
              return dir;
            }
            throw e;
          } catch (err) {
            throw e;
          }
        }
      }
    };
    mkdir(this.fileObject.dir);

    const ops = {
      flags: this.options.flags,
      encoding: this.options.encoding,
      mode: this.options.mode
    };
    const renameKey = function(obj, oldKey, newKey) {
      obj[newKey] = obj[oldKey];
      delete obj[oldKey];
      return obj;
    };
    // try to throw EISDIR, EROFS, EACCES
    fs.appendFileSync(filePath, "", renameKey({ ...ops }, "flags", "flag"));
    this.currentFileStream = fs.createWriteStream(filePath, ops);
    this.currentFileStream.on("error", e => {
      this.emit("error", e);
    });
  }

  async _clean() {
    const existingFileDetails = await this._getExistingFiles();
    debug(
      `_clean: numToKeep = ${this.options.numToKeep}, existingFiles = ${existingFileDetails.length}`
    );
    debug("_clean: existing files are: ", existingFileDetails);
    if (this._tooManyFiles(existingFileDetails.length)) {
      const fileNamesToRemove = existingFileDetails
        .slice(0, existingFileDetails.length - this.options.numToKeep)
        .map(f => path.format({ dir: this.fileObject.dir, base: f.filename }));
      await deleteFiles(fileNamesToRemove);
    }
  }

  _tooManyFiles(numFiles) {
    return this.options.numToKeep > 0 && numFiles > this.options.numToKeep;
  }
}

module.exports = RollingFileWriteStream;


code:
const debug = require("debug")("streamroller:fileNameFormatter");
const path = require("path");
const ZIP_EXT = ".gz";
const DEFAULT_FILENAME_SEP = ".";

module.exports = ({
  file,
  keepFileExt,
  needsIndex,
  alwaysIncludeDate,
  compress,
  fileNameSep
}) => {
  let FILENAME_SEP = fileNameSep || DEFAULT_FILENAME_SEP;
  const dirAndName = path.join(file.dir, file.name);

  const ext = f => f + file.ext;

  const index = (f, i, d) =>
    (needsIndex || !d) && i ? f + FILENAME_SEP + i : f;

  const date = (f, i, d) => {
    return (i > 0 || alwaysIncludeDate) && d ? f + FILENAME_SEP + d : f;
  };

  const gzip = (f, i) => (i && compress ? f + ZIP_EXT : f);

  const parts = keepFileExt
    ? [date, index, ext, gzip]
    : [ext, date, index, gzip];

  return ({ date, index }) => {
    debug(`_formatFileName: date=${date}, index=${index}`);
    return parts.reduce(
      (filename, part) => part(filename, index, date),
      dirAndName
    );
  };
};


code:
const debug = require("debug")("streamroller:fileNameParser");
const ZIP_EXT = ".gz";
const format = require("date-format");
const DEFAULT_FILENAME_SEP = ".";

module.exports = ({ file, keepFileExt, pattern, fileNameSep }) => {
  let FILENAME_SEP = fileNameSep || DEFAULT_FILENAME_SEP;
  // All these functions take two arguments: f, the filename, and p, the result placeholder
  // They return the filename with any matching parts removed.
  // The "zip" function, for instance, removes the ".gz" part of the filename (if present)
  const zip = (f, p) => {
    if (f.endsWith(ZIP_EXT)) {
      debug("it is gzipped");
      p.isCompressed = true;
      return f.slice(0, -1 * ZIP_EXT.length);
    }
    return f;
  };

  const __NOT_MATCHING__ = "__NOT_MATCHING__";

  const extAtEnd = f => {
    if (f.startsWith(file.name) && f.endsWith(file.ext)) {
      debug("it starts and ends with the right things");
      return f.slice(file.name.length + 1, -1 * file.ext.length);
    }
    return __NOT_MATCHING__;
  };

  const extInMiddle = f => {
    if (f.startsWith(file.base)) {
      debug("it starts with the right things");
      return f.slice(file.base.length + 1);
    }
    return __NOT_MATCHING__;
  };

  const dateAndIndex = (f, p) => {
    const items = f.split(FILENAME_SEP);
    let indexStr = items[items.length - 1];
    debug("items: ", items, ", indexStr: ", indexStr);
    let dateStr = f;
    if (indexStr !== undefined && indexStr.match(/^\d+$/)) {
      dateStr = f.slice(0, -1 * (indexStr.length + 1));
      debug(`dateStr is ${dateStr}`);
      if (pattern && !dateStr) {
        dateStr = indexStr;
        indexStr = "0";
      }
    } else {
      indexStr = "0";
    }

    try {
      // Two arguments for new Date() are intentional. This will set other date
      // components to minimal values in the current timezone instead of UTC,
      // as new Date(0) will do.
      const date = format.parse(pattern, dateStr, new Date(0, 0));
      if (format.asString(pattern, date) !== dateStr) return f;
      p.index = parseInt(indexStr, 10);
      p.date = dateStr;
      p.timestamp = date.getTime();
      return "";
    } catch (e) {
      //not a valid date, don't panic.
      debug(`Problem parsing ${dateStr} as ${pattern}, error was: `, e);
      return f;
    }
  };

  const index = (f, p) => {
    if (f.match(/^\d+$/)) {
      debug("it has an index");
      p.index = parseInt(f, 10);
      return "";
    }
    return f;
  };

  let parts = [
    zip,
    keepFileExt ? extAtEnd : extInMiddle,
    pattern ? dateAndIndex : index
  ];

  return filename => {
    let result = { filename, index: 0, isCompressed: false };
    // pass the filename through each of the file part parsers
    let whatsLeftOver = parts.reduce(
      (remains, part) => part(remains, result),
      filename
    );
    // if there's anything left after parsing, then it wasn't a valid filename
    return whatsLeftOver ? null : result;
  };
};


code:
module.exports = {
  RollingFileWriteStream: require('./RollingFileWriteStream'),
  RollingFileStream: require('./RollingFileStream'),
  DateRollingFileStream: require('./DateRollingFileStream')
};


code:
const debug = require('debug')('streamroller:moveAndMaybeCompressFile');
const fs = require('fs-extra');
const zlib = require('zlib');

const _parseOption = function(rawOptions){
  const defaultOptions = {
    mode: parseInt("0600", 8),
    compress: false,
  };
  const options = Object.assign({}, defaultOptions, rawOptions);
  debug(`_parseOption: moveAndMaybeCompressFile called with option=${JSON.stringify(options)}`);
  return options;
};

const moveAndMaybeCompressFile = async (
  sourceFilePath,
  targetFilePath,
  options
) => {
  options = _parseOption(options);

  if (sourceFilePath === targetFilePath) {
    debug(`moveAndMaybeCompressFile: source and target are the same, not doing anything`);
    return;
  }

  if (await fs.pathExists(sourceFilePath)) {
    debug(
      `moveAndMaybeCompressFile: moving file from ${sourceFilePath} to ${targetFilePath} ${
        options.compress ? "with" : "without"
      } compress`
    );
    if (options.compress) {
      await new Promise((resolve, reject) => {
        let isCreated = false;
        // to avoid concurrency, the forked process which can create the file will proceed (using flags wx)
        const writeStream = fs.createWriteStream(targetFilePath, { mode: options.mode, flags: "wx" })
          // wait until writable stream is valid before proceeding to read
          .on("open", () => {
            isCreated = true;
            const readStream = fs.createReadStream(sourceFilePath)
              // wait until readable stream is valid before piping
              .on("open", () => {
                readStream.pipe(zlib.createGzip()).pipe(writeStream);
              })
              .on("error", (e) => {
                debug(`moveAndMaybeCompressFile: error reading ${sourceFilePath}`, e);
                // manually close writable: https://nodejs.org/api/stream.html#readablepipedestination-options
                writeStream.destroy(e);
              });
          })
          .on("finish", () => {
            debug(`moveAndMaybeCompressFile: finished compressing ${targetFilePath}, deleting ${sourceFilePath}`);
            // delete sourceFilePath
            fs.unlink(sourceFilePath)
              .then(resolve)
              .catch((e) => {
                debug(`moveAndMaybeCompressFile: error deleting ${sourceFilePath}, truncating instead`, e);
                // fallback to truncate
                fs.truncate(sourceFilePath)
                  .then(resolve)
                  .catch((e) => {
                    debug(`moveAndMaybeCompressFile: error truncating ${sourceFilePath}`, e);
                    reject(e);
                  });
              });
          })
          .on("error", (e) => {
            if (!isCreated) {
              debug(`moveAndMaybeCompressFile: error creating ${targetFilePath}`, e);
              // do not do anything if handled by another forked process
              reject(e);
            } else {
              debug(`moveAndMaybeCompressFile: error writing ${targetFilePath}, deleting`, e);
              // delete targetFilePath (taking as nothing happened)
              fs.unlink(targetFilePath)
                .then(() => { reject(e); })
                .catch((e) => {
                  debug(`moveAndMaybeCompressFile: error deleting ${targetFilePath}`, e);
                  reject(e);
                });
            }
          });
      }).catch(() => {});
    } else {
      debug(`moveAndMaybeCompressFile: renaming ${sourceFilePath} to ${targetFilePath}`);
      try {
        await fs.move(sourceFilePath, targetFilePath, { overwrite: true });
      } catch (e) {
        debug(`moveAndMaybeCompressFile: error renaming ${sourceFilePath} to ${targetFilePath}`, e);
        /* istanbul ignore else: no need to do anything if file does not exist */
        if (e.code !== "ENOENT") {
          debug(`moveAndMaybeCompressFile: trying copy+truncate instead`);
          try {
            await fs.copy(sourceFilePath, targetFilePath, { overwrite: true });
            await fs.truncate(sourceFilePath);
          } catch (e) {
            debug(`moveAndMaybeCompressFile: error copy+truncate`, e);
          }
        }
      }
    }
  }
};

module.exports = moveAndMaybeCompressFile;


code:
// allows us to inject a mock date in tests
module.exports = () => new Date();