mirror of
https://github.com/idanoo/GoScrobble
synced 2025-07-22 15:39:14 +00:00
0.2.0 - Mid migration
This commit is contained in:
parent
139e6a915e
commit
7e38fdbd7d
42393 changed files with 5358157 additions and 62 deletions
17
web/node_modules/bfj/src/datastream.js
generated
vendored
Normal file
17
web/node_modules/bfj/src/datastream.js
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
'use strict'
|
||||
|
||||
const check = require('check-types')
|
||||
const BfjStream = require('./stream')
|
||||
const util = require('util')
|
||||
|
||||
util.inherits(DataStream, BfjStream)
|
||||
|
||||
module.exports = DataStream
|
||||
|
||||
function DataStream (read, options) {
|
||||
if (check.not.instanceStrict(this, DataStream)) {
|
||||
return new DataStream(read, options)
|
||||
}
|
||||
|
||||
return BfjStream.call(this, read, { ...options, objectMode: true })
|
||||
}
|
22
web/node_modules/bfj/src/error.js
generated
vendored
Normal file
22
web/node_modules/bfj/src/error.js
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = { create }
|
||||
|
||||
function create (actual, expected, line, column) {
|
||||
const error = new Error(
|
||||
/* eslint-disable prefer-template */
|
||||
'JSON error: encountered `' + actual +
|
||||
'` at line ' + line +
|
||||
', column ' + column +
|
||||
' where `' + expected +
|
||||
'` was expected.'
|
||||
/* eslint-enable prefer-template */
|
||||
)
|
||||
|
||||
error.actual = actual
|
||||
error.expected = expected
|
||||
error.lineNumber = line
|
||||
error.columnNumber = column
|
||||
|
||||
return error
|
||||
}
|
309
web/node_modules/bfj/src/eventify.js
generated
vendored
Normal file
309
web/node_modules/bfj/src/eventify.js
generated
vendored
Normal file
|
@ -0,0 +1,309 @@
|
|||
'use strict'
|
||||
|
||||
const check = require('check-types')
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const events = require('./events')
|
||||
const promise = require('./promise')
|
||||
|
||||
const invalidTypes = {
|
||||
undefined: true, // eslint-disable-line no-undefined
|
||||
function: true,
|
||||
symbol: true
|
||||
}
|
||||
|
||||
module.exports = eventify
|
||||
|
||||
/**
|
||||
* Public function `eventify`.
|
||||
*
|
||||
* Returns an event emitter and asynchronously traverses a data structure
|
||||
* (depth-first), emitting events as it encounters items. Sanely handles
|
||||
* promises, buffers, maps and other iterables. The event emitter is
|
||||
* decorated with a `pause` method that can be called to pause processing.
|
||||
*
|
||||
* @param data: The data structure to traverse.
|
||||
*
|
||||
* @option promises: 'resolve' or 'ignore', default is 'resolve'.
|
||||
*
|
||||
* @option buffers: 'toString' or 'ignore', default is 'toString'.
|
||||
*
|
||||
* @option maps: 'object' or 'ignore', default is 'object'.
|
||||
*
|
||||
* @option iterables: 'array' or 'ignore', default is 'array'.
|
||||
*
|
||||
* @option circular: 'error' or 'ignore', default is 'error'.
|
||||
*
|
||||
* @option yieldRate: The number of data items to process per timeslice,
|
||||
* default is 16384.
|
||||
*
|
||||
* @option Promise: The promise constructor to use, defaults to bluebird.
|
||||
**/
|
||||
function eventify (data, options = {}) {
|
||||
const coercions = {}
|
||||
const emitter = new EventEmitter()
|
||||
const Promise = promise(options)
|
||||
const references = new Map()
|
||||
|
||||
let count = 0
|
||||
let disableCoercions = false
|
||||
let ignoreCircularReferences
|
||||
let ignoreItems
|
||||
let pause
|
||||
let yieldRate
|
||||
|
||||
emitter.pause = () => {
|
||||
let resolve
|
||||
pause = new Promise(res => resolve = res)
|
||||
return () => {
|
||||
pause = null
|
||||
count = 0
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
parseOptions()
|
||||
setImmediate(begin)
|
||||
|
||||
return emitter
|
||||
|
||||
function parseOptions () {
|
||||
parseCoercionOption('promises')
|
||||
parseCoercionOption('buffers')
|
||||
parseCoercionOption('maps')
|
||||
parseCoercionOption('iterables')
|
||||
|
||||
if (Object.keys(coercions).length === 0) {
|
||||
disableCoercions = true
|
||||
}
|
||||
|
||||
if (options.circular === 'ignore') {
|
||||
ignoreCircularReferences = true
|
||||
}
|
||||
|
||||
check.assert.maybe.positive(options.yieldRate)
|
||||
yieldRate = options.yieldRate || 16384
|
||||
}
|
||||
|
||||
function parseCoercionOption (key) {
|
||||
if (options[key] !== 'ignore') {
|
||||
coercions[key] = true
|
||||
}
|
||||
}
|
||||
|
||||
function begin () {
|
||||
return proceed(data)
|
||||
.catch(error => emit(events.error, error))
|
||||
.then(() => emit(events.end))
|
||||
}
|
||||
|
||||
function proceed (datum) {
|
||||
if (++count % yieldRate !== 0) {
|
||||
return coerce(datum).then(after)
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
setImmediate(() => {
|
||||
coerce(datum)
|
||||
.then(after)
|
||||
.then(resolve)
|
||||
.catch(reject)
|
||||
})
|
||||
})
|
||||
|
||||
function after (coerced) {
|
||||
if (isInvalid(coerced)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (coerced === false || coerced === true || coerced === null) {
|
||||
return literal(coerced)
|
||||
}
|
||||
|
||||
if (Array.isArray(coerced)) {
|
||||
return array(coerced)
|
||||
}
|
||||
|
||||
const type = typeof coerced
|
||||
|
||||
switch (type) {
|
||||
case 'number':
|
||||
return value(coerced, type)
|
||||
case 'string':
|
||||
return value(escapeString(coerced), type)
|
||||
default:
|
||||
return object(coerced)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function coerce (datum) {
|
||||
if (disableCoercions || check.primitive(datum)) {
|
||||
return Promise.resolve(datum)
|
||||
}
|
||||
|
||||
if (check.thenable(datum)) {
|
||||
return coerceThing(datum, 'promises', coercePromise).then(coerce)
|
||||
}
|
||||
|
||||
if (check.instanceStrict(datum, Buffer)) {
|
||||
return coerceThing(datum, 'buffers', coerceBuffer)
|
||||
}
|
||||
|
||||
if (check.instanceStrict(datum, Map)) {
|
||||
return coerceThing(datum, 'maps', coerceMap)
|
||||
}
|
||||
|
||||
if (
|
||||
check.iterable(datum) &&
|
||||
check.not.string(datum) &&
|
||||
check.not.array(datum)
|
||||
) {
|
||||
return coerceThing(datum, 'iterables', coerceIterable)
|
||||
}
|
||||
|
||||
if (check.function(datum.toJSON)) {
|
||||
return Promise.resolve(datum.toJSON())
|
||||
}
|
||||
|
||||
return Promise.resolve(datum)
|
||||
}
|
||||
|
||||
function coerceThing (datum, thing, fn) {
|
||||
if (coercions[thing]) {
|
||||
return fn(datum)
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
function coercePromise (p) {
|
||||
return p
|
||||
}
|
||||
|
||||
function coerceBuffer (buffer) {
|
||||
return Promise.resolve(buffer.toString())
|
||||
}
|
||||
|
||||
function coerceMap (map) {
|
||||
const result = {}
|
||||
|
||||
return coerceCollection(map, result, (item, key) => {
|
||||
result[key] = item
|
||||
})
|
||||
}
|
||||
|
||||
function coerceCollection (coll, target, push) {
|
||||
coll.forEach(push)
|
||||
|
||||
return Promise.resolve(target)
|
||||
}
|
||||
|
||||
function coerceIterable (iterable) {
|
||||
const result = []
|
||||
|
||||
return coerceCollection(iterable, result, item => {
|
||||
result.push(item)
|
||||
})
|
||||
}
|
||||
|
||||
function isInvalid (datum) {
|
||||
const type = typeof datum
|
||||
return !! invalidTypes[type] || (
|
||||
type === 'number' && ! isValidNumber(datum)
|
||||
)
|
||||
}
|
||||
|
||||
function isValidNumber (datum) {
|
||||
return datum > Number.NEGATIVE_INFINITY && datum < Number.POSITIVE_INFINITY
|
||||
}
|
||||
|
||||
function literal (datum) {
|
||||
return value(datum, 'literal')
|
||||
}
|
||||
|
||||
function value (datum, type) {
|
||||
return emit(events[type], datum)
|
||||
}
|
||||
|
||||
function emit (event, eventData) {
|
||||
return (pause || Promise.resolve())
|
||||
.then(() => emitter.emit(event, eventData))
|
||||
.catch(err => {
|
||||
try {
|
||||
emitter.emit(events.error, err)
|
||||
} catch (_) {
|
||||
// When calling user code, anything is possible
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function array (datum) {
|
||||
// For an array, collection:object and collection:array are the same.
|
||||
return collection(datum, datum, 'array', item => {
|
||||
if (isInvalid(item)) {
|
||||
return proceed(null)
|
||||
}
|
||||
|
||||
return proceed(item)
|
||||
})
|
||||
}
|
||||
|
||||
function collection (obj, arr, type, action) {
|
||||
let ignoreThisItem
|
||||
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
if (references.has(obj)) {
|
||||
ignoreThisItem = ignoreItems = true
|
||||
|
||||
if (! ignoreCircularReferences) {
|
||||
return emit(events.dataError, new Error('Circular reference.'))
|
||||
}
|
||||
} else {
|
||||
references.set(obj, true)
|
||||
}
|
||||
})
|
||||
.then(() => emit(events[type]))
|
||||
.then(() => item(0))
|
||||
|
||||
function item (index) {
|
||||
if (index >= arr.length) {
|
||||
if (ignoreThisItem) {
|
||||
ignoreItems = false
|
||||
}
|
||||
|
||||
if (ignoreItems) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
return emit(events.endPrefix + events[type])
|
||||
.then(() => references.delete(obj))
|
||||
}
|
||||
|
||||
if (ignoreItems) {
|
||||
return item(index + 1)
|
||||
}
|
||||
|
||||
return action(arr[index])
|
||||
.then(() => item(index + 1))
|
||||
}
|
||||
}
|
||||
|
||||
function object (datum) {
|
||||
// For an object, collection:object and collection:array are different.
|
||||
return collection(datum, Object.keys(datum), 'object', key => {
|
||||
const item = datum[key]
|
||||
|
||||
if (isInvalid(item)) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
return emit(events.property, escapeString(key))
|
||||
.then(() => proceed(item))
|
||||
})
|
||||
}
|
||||
|
||||
function escapeString (string) {
|
||||
string = JSON.stringify(string)
|
||||
return string.substring(1, string.length - 1)
|
||||
}
|
||||
}
|
18
web/node_modules/bfj/src/events.js
generated
vendored
Normal file
18
web/node_modules/bfj/src/events.js
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
array: 'arr',
|
||||
object: 'obj',
|
||||
property: 'pro',
|
||||
string: 'str',
|
||||
number: 'num',
|
||||
literal: 'lit',
|
||||
endPrefix: 'end-',
|
||||
end: 'end',
|
||||
error: 'err'
|
||||
}
|
||||
|
||||
module.exports.endArray = module.exports.endPrefix + module.exports.array
|
||||
module.exports.endObject = module.exports.endPrefix + module.exports.object
|
||||
module.exports.endLine = `${module.exports.endPrefix}line`
|
||||
module.exports.dataError = `${module.exports.error}-data`
|
14
web/node_modules/bfj/src/index.js
generated
vendored
Normal file
14
web/node_modules/bfj/src/index.js
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
walk: require('./walk'),
|
||||
match: require('./match'),
|
||||
parse: require('./parse'),
|
||||
unpipe: require('./unpipe'),
|
||||
read: require('./read'),
|
||||
eventify: require('./eventify'),
|
||||
streamify: require('./streamify'),
|
||||
stringify: require('./stringify'),
|
||||
write: require('./write'),
|
||||
events: require('./events')
|
||||
}
|
17
web/node_modules/bfj/src/jsonstream.js
generated
vendored
Normal file
17
web/node_modules/bfj/src/jsonstream.js
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
'use strict'
|
||||
|
||||
const check = require('check-types')
|
||||
const BfjStream = require('./stream')
|
||||
const util = require('util')
|
||||
|
||||
util.inherits(JsonStream, BfjStream)
|
||||
|
||||
module.exports = JsonStream
|
||||
|
||||
function JsonStream (read, options) {
|
||||
if (check.not.instanceStrict(this, JsonStream)) {
|
||||
return new JsonStream(read, options)
|
||||
}
|
||||
|
||||
return BfjStream.call(this, read, { ...options, encoding: 'utf8' })
|
||||
}
|
234
web/node_modules/bfj/src/match.js
generated
vendored
Normal file
234
web/node_modules/bfj/src/match.js
generated
vendored
Normal file
|
@ -0,0 +1,234 @@
|
|||
'use strict'
|
||||
|
||||
const check = require('check-types')
|
||||
const DataStream = require('./datastream')
|
||||
const events = require('./events')
|
||||
const Hoopy = require('hoopy')
|
||||
const walk = require('./walk')
|
||||
|
||||
const DEFAULT_BUFFER_LENGTH = 1024
|
||||
|
||||
module.exports = match
|
||||
|
||||
/**
|
||||
* Public function `match`.
|
||||
*
|
||||
* Asynchronously parses a stream of JSON data, returning a stream of items
|
||||
* that match the argument. Note that if a value is `null`, it won't be matched
|
||||
* because `null` is used to signify end-of-stream in node.
|
||||
*
|
||||
* @param stream: Readable instance representing the incoming JSON.
|
||||
*
|
||||
* @param selector: Regular expression, string or predicate function used to
|
||||
* identify matches. If a regular expression or string is
|
||||
* passed, only property keys are tested. If a predicate is
|
||||
* passed, both the key and the value are passed to it as
|
||||
* arguments.
|
||||
*
|
||||
* @option minDepth: Number indicating the minimum depth to apply the selector
|
||||
* to. The default is `0`, but setting it to a higher value
|
||||
* can improve performance and reduce memory usage by
|
||||
* eliminating the need to actualise top-level items.
|
||||
*
|
||||
* @option numbers: Boolean, indicating whether numerical keys (e.g. array
|
||||
* indices) should be coerced to strings before testing the
|
||||
* match. Only applies if the `selector` argument is a string
|
||||
* or regular expression.
|
||||
*
|
||||
* @option ndjson: Set this to true to parse newline-delimited JSON,
|
||||
* default is `false`.
|
||||
*
|
||||
* @option yieldRate: The number of data items to process per timeslice,
|
||||
* default is 16384.
|
||||
*
|
||||
* @option bufferLength: The length of the match buffer, default is 1024.
|
||||
*
|
||||
* @option highWaterMark: If set, will be passed to the readable stream constructor
|
||||
* as the value for the highWaterMark option.
|
||||
*
|
||||
* @option Promise: The promise constructor to use, defaults to bluebird.
|
||||
**/
|
||||
function match (stream, selector, options = {}) {
|
||||
const scopes = []
|
||||
const properties = []
|
||||
const emitter = walk(stream, options)
|
||||
const matches = new Hoopy(options.bufferLength || DEFAULT_BUFFER_LENGTH)
|
||||
let streamOptions
|
||||
const { highWaterMark } = options
|
||||
if (highWaterMark) {
|
||||
streamOptions = { highWaterMark }
|
||||
}
|
||||
const results = new DataStream(read, streamOptions)
|
||||
|
||||
let selectorFunction, selectorString, resume
|
||||
let coerceNumbers = false
|
||||
let awaitPush = true
|
||||
let isEnded = false
|
||||
let length = 0
|
||||
let index = 0
|
||||
|
||||
const minDepth = options.minDepth || 0
|
||||
check.assert.greaterOrEqual(minDepth, 0)
|
||||
|
||||
if (check.function(selector)) {
|
||||
selectorFunction = selector
|
||||
selector = null
|
||||
} else {
|
||||
coerceNumbers = !! options.numbers
|
||||
|
||||
if (check.string(selector)) {
|
||||
check.assert.nonEmptyString(selector)
|
||||
selectorString = selector
|
||||
selector = null
|
||||
} else {
|
||||
check.assert.instanceStrict(selector, RegExp)
|
||||
}
|
||||
}
|
||||
|
||||
emitter.on(events.array, array)
|
||||
emitter.on(events.object, object)
|
||||
emitter.on(events.property, property)
|
||||
emitter.on(events.endArray, endScope)
|
||||
emitter.on(events.endObject, endScope)
|
||||
emitter.on(events.string, value)
|
||||
emitter.on(events.number, value)
|
||||
emitter.on(events.literal, value)
|
||||
emitter.on(events.end, end)
|
||||
emitter.on(events.error, error)
|
||||
emitter.on(events.dataError, dataError)
|
||||
|
||||
return results
|
||||
|
||||
function read () {
|
||||
if (awaitPush) {
|
||||
awaitPush = false
|
||||
|
||||
if (isEnded) {
|
||||
if (length > 0) {
|
||||
after()
|
||||
}
|
||||
|
||||
return endResults()
|
||||
}
|
||||
}
|
||||
|
||||
if (resume) {
|
||||
const resumeCopy = resume
|
||||
resume = null
|
||||
resumeCopy()
|
||||
after()
|
||||
}
|
||||
}
|
||||
|
||||
function after () {
|
||||
if (awaitPush || resume) {
|
||||
return
|
||||
}
|
||||
|
||||
let i
|
||||
|
||||
for (i = 0; i < length && ! resume; ++i) {
|
||||
if (! results.push(matches[i + index])) {
|
||||
pause()
|
||||
}
|
||||
}
|
||||
|
||||
if (i === length) {
|
||||
index = length = 0
|
||||
} else {
|
||||
length -= i
|
||||
index += i
|
||||
}
|
||||
}
|
||||
|
||||
function pause () {
|
||||
resume = emitter.pause()
|
||||
}
|
||||
|
||||
function endResults () {
|
||||
if (! awaitPush) {
|
||||
results.push(null)
|
||||
}
|
||||
}
|
||||
|
||||
function array () {
|
||||
scopes.push([])
|
||||
}
|
||||
|
||||
function object () {
|
||||
scopes.push({})
|
||||
}
|
||||
|
||||
function property (name) {
|
||||
if (scopes.length < minDepth) {
|
||||
return
|
||||
}
|
||||
|
||||
properties.push(name)
|
||||
}
|
||||
|
||||
function endScope () {
|
||||
value(scopes.pop())
|
||||
}
|
||||
|
||||
function value (v) {
|
||||
let key
|
||||
|
||||
if (scopes.length < minDepth) {
|
||||
return
|
||||
}
|
||||
|
||||
if (scopes.length > 0) {
|
||||
const scope = scopes[scopes.length - 1]
|
||||
|
||||
if (Array.isArray(scope)) {
|
||||
key = scope.length
|
||||
} else {
|
||||
key = properties.pop()
|
||||
}
|
||||
|
||||
scope[key] = v
|
||||
}
|
||||
|
||||
if (v === null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (selectorFunction) {
|
||||
if (selectorFunction(key, v, scopes.length)) {
|
||||
push(v)
|
||||
}
|
||||
} else {
|
||||
if (coerceNumbers && typeof key === 'number') {
|
||||
key = key.toString()
|
||||
}
|
||||
|
||||
if ((selectorString && selectorString === key) || (selector && selector.test(key))) {
|
||||
push(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function push (v) {
|
||||
if (length + 1 === matches.length) {
|
||||
pause()
|
||||
}
|
||||
|
||||
matches[index + length++] = v
|
||||
|
||||
after()
|
||||
}
|
||||
|
||||
function end () {
|
||||
isEnded = true
|
||||
endResults()
|
||||
}
|
||||
|
||||
function error (e) {
|
||||
results.emit('error', e)
|
||||
}
|
||||
|
||||
function dataError (e) {
|
||||
results.emit('dataError', e)
|
||||
}
|
||||
}
|
45
web/node_modules/bfj/src/memory.js
generated
vendored
Normal file
45
web/node_modules/bfj/src/memory.js
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
'use strict'
|
||||
|
||||
const PROPERTIES = [ 'rss', 'heapTotal', 'heapUsed', 'external' ]
|
||||
|
||||
let memory
|
||||
|
||||
module.exports = {
|
||||
initialise,
|
||||
update,
|
||||
report
|
||||
}
|
||||
|
||||
function initialise () {
|
||||
memory = PROPERTIES.reduce((result, name) => {
|
||||
result[name] = {
|
||||
sum: 0,
|
||||
hwm: 0
|
||||
}
|
||||
return result
|
||||
}, { count: 0 })
|
||||
}
|
||||
|
||||
function update () {
|
||||
const currentMemory = process.memoryUsage()
|
||||
PROPERTIES.forEach(name => updateProperty(name, currentMemory))
|
||||
}
|
||||
|
||||
function updateProperty (name, currentMemory) {
|
||||
const m = memory[name]
|
||||
const c = currentMemory[name]
|
||||
m.sum += c
|
||||
if (c > m.hwm) {
|
||||
m.hwm = c
|
||||
}
|
||||
}
|
||||
|
||||
function report () {
|
||||
PROPERTIES.forEach(name => reportProperty(name))
|
||||
}
|
||||
|
||||
function reportProperty (name) {
|
||||
const m = memory[name]
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`mean ${name}: ${m.sum / memory.count}; hwm: ${m.hwm}`)
|
||||
}
|
181
web/node_modules/bfj/src/parse.js
generated
vendored
Normal file
181
web/node_modules/bfj/src/parse.js
generated
vendored
Normal file
|
@ -0,0 +1,181 @@
|
|||
'use strict'
|
||||
|
||||
const check = require('check-types')
|
||||
const events = require('./events')
|
||||
const promise = require('./promise')
|
||||
const walk = require('./walk')
|
||||
|
||||
module.exports = parse
|
||||
|
||||
const NDJSON_STATE = new Map()
|
||||
|
||||
/**
|
||||
* Public function `parse`.
|
||||
*
|
||||
* Returns a promise and asynchronously parses a stream of JSON data. If
|
||||
* there are no errors, the promise is resolved with the parsed data. If
|
||||
* errors occur, the promise is rejected with the first error.
|
||||
*
|
||||
* @param stream: Readable instance representing the incoming JSON.
|
||||
*
|
||||
* @option reviver: Transformation function, invoked depth-first.
|
||||
*
|
||||
* @option yieldRate: The number of data items to process per timeslice,
|
||||
* default is 16384.
|
||||
*
|
||||
* @option Promise: The promise constructor to use, defaults to bluebird.
|
||||
*
|
||||
* @option ndjson: Set this to true to parse newline-delimited JSON. In
|
||||
* this case, each call will be resolved with one value
|
||||
* from the stream. To parse the entire stream, calls
|
||||
* should be made sequentially one-at-a-time until the
|
||||
* returned promise resolves to `undefined`.
|
||||
**/
|
||||
function parse (stream, options = {}) {
|
||||
const Promise = promise(options)
|
||||
|
||||
try {
|
||||
check.assert.maybe.function(options.reviver, 'Invalid reviver option')
|
||||
} catch (err) {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
|
||||
const errors = []
|
||||
const scopes = []
|
||||
const reviver = options.reviver
|
||||
const shouldHandleNdjson = !! options.ndjson
|
||||
|
||||
let emitter, resolve, reject, scopeKey
|
||||
if (shouldHandleNdjson && NDJSON_STATE.has(stream)) {
|
||||
const state = NDJSON_STATE.get(stream)
|
||||
NDJSON_STATE.delete(stream)
|
||||
emitter = state.emitter
|
||||
setImmediate(state.resume)
|
||||
} else {
|
||||
emitter = walk(stream, options)
|
||||
}
|
||||
|
||||
emitter.on(events.array, array)
|
||||
emitter.on(events.object, object)
|
||||
emitter.on(events.property, property)
|
||||
emitter.on(events.string, value)
|
||||
emitter.on(events.number, value)
|
||||
emitter.on(events.literal, value)
|
||||
emitter.on(events.endArray, endScope)
|
||||
emitter.on(events.endObject, endScope)
|
||||
emitter.on(events.end, end)
|
||||
emitter.on(events.error, error)
|
||||
emitter.on(events.dataError, error)
|
||||
|
||||
if (shouldHandleNdjson) {
|
||||
emitter.on(events.endLine, endLine)
|
||||
}
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
resolve = res
|
||||
reject = rej
|
||||
})
|
||||
|
||||
function array () {
|
||||
if (errors.length > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
beginScope([])
|
||||
}
|
||||
|
||||
function beginScope (parsed) {
|
||||
if (errors.length > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
if (scopes.length > 0) {
|
||||
value(parsed)
|
||||
}
|
||||
|
||||
scopes.push(parsed)
|
||||
}
|
||||
|
||||
function value (v) {
|
||||
if (errors.length > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
if (scopes.length === 0) {
|
||||
return scopes.push(v)
|
||||
}
|
||||
|
||||
const scope = scopes[scopes.length - 1]
|
||||
|
||||
if (scopeKey) {
|
||||
scope[scopeKey] = v
|
||||
scopeKey = null
|
||||
} else {
|
||||
scope.push(v)
|
||||
}
|
||||
}
|
||||
|
||||
function object () {
|
||||
if (errors.length > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
beginScope({})
|
||||
}
|
||||
|
||||
function property (name) {
|
||||
if (errors.length > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
scopeKey = name
|
||||
}
|
||||
|
||||
function endScope () {
|
||||
if (errors.length > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
if (scopes.length > 1) {
|
||||
scopes.pop()
|
||||
}
|
||||
}
|
||||
|
||||
function end () {
|
||||
if (shouldHandleNdjson) {
|
||||
const resume = emitter.pause()
|
||||
emitter.removeAllListeners()
|
||||
NDJSON_STATE.set(stream, { emitter, resume })
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
return reject(errors[0])
|
||||
}
|
||||
|
||||
if (reviver) {
|
||||
scopes[0] = transform(scopes[0], '')
|
||||
}
|
||||
|
||||
resolve(scopes[0])
|
||||
}
|
||||
|
||||
function transform (obj, key) {
|
||||
if (obj && typeof obj === 'object') {
|
||||
Object.entries(obj).forEach(([ k, v ]) => {
|
||||
obj[k] = transform(v, k)
|
||||
})
|
||||
}
|
||||
|
||||
return reviver(key, obj)
|
||||
}
|
||||
|
||||
function error (e) {
|
||||
errors.push(e)
|
||||
}
|
||||
|
||||
function endLine () {
|
||||
if (scopes.length > 0) {
|
||||
end()
|
||||
}
|
||||
}
|
||||
}
|
3
web/node_modules/bfj/src/promise.js
generated
vendored
Normal file
3
web/node_modules/bfj/src/promise.js
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = (options = {}) => options.Promise || require('bluebird')
|
26
web/node_modules/bfj/src/read.js
generated
vendored
Normal file
26
web/node_modules/bfj/src/read.js
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const parse = require('./parse')
|
||||
|
||||
module.exports = read
|
||||
|
||||
/**
|
||||
* Public function `read`.
|
||||
*
|
||||
* Returns a promise and asynchronously parses a JSON file read from disk. If
|
||||
* there are no errors, the promise is resolved with the parsed data. If errors
|
||||
* occur, the promise is rejected with the first error.
|
||||
*
|
||||
* @param path: Path to the JSON file.
|
||||
*
|
||||
* @option reviver: Transformation function, invoked depth-first.
|
||||
*
|
||||
* @option yieldRate: The number of data items to process per timeslice,
|
||||
* default is 16384.
|
||||
*
|
||||
* @option Promise: The promise constructor to use, defaults to bluebird.
|
||||
**/
|
||||
function read (path, options) {
|
||||
return parse(fs.createReadStream(path, options), { ...options, ndjson: false })
|
||||
}
|
23
web/node_modules/bfj/src/stream.js
generated
vendored
Normal file
23
web/node_modules/bfj/src/stream.js
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
'use strict'
|
||||
|
||||
const util = require('util')
|
||||
const Readable = require('stream').Readable
|
||||
const check = require('check-types')
|
||||
|
||||
util.inherits(BfjStream, Readable)
|
||||
|
||||
module.exports = BfjStream
|
||||
|
||||
function BfjStream (read, options) {
|
||||
if (check.not.instanceStrict(this, BfjStream)) {
|
||||
return new BfjStream(read)
|
||||
}
|
||||
|
||||
check.assert.function(read, 'Invalid read implementation')
|
||||
|
||||
this._read = function () { // eslint-disable-line no-underscore-dangle
|
||||
read()
|
||||
}
|
||||
|
||||
return Readable.call(this, options)
|
||||
}
|
283
web/node_modules/bfj/src/streamify.js
generated
vendored
Normal file
283
web/node_modules/bfj/src/streamify.js
generated
vendored
Normal file
|
@ -0,0 +1,283 @@
|
|||
'use strict'
|
||||
|
||||
const check = require('check-types')
|
||||
const eventify = require('./eventify')
|
||||
const events = require('./events')
|
||||
const JsonStream = require('./jsonstream')
|
||||
const Hoopy = require('hoopy')
|
||||
const promise = require('./promise')
|
||||
const tryer = require('tryer')
|
||||
|
||||
const DEFAULT_BUFFER_LENGTH = 1024
|
||||
|
||||
module.exports = streamify
|
||||
|
||||
/**
|
||||
* Public function `streamify`.
|
||||
*
|
||||
* Asynchronously serialises a data structure to a stream of JSON
|
||||
* data. Sanely handles promises, buffers, maps and other iterables.
|
||||
*
|
||||
* @param data: The data to transform.
|
||||
*
|
||||
* @option space: Indentation string, or the number of spaces
|
||||
* to indent each nested level by.
|
||||
*
|
||||
* @option promises: 'resolve' or 'ignore', default is 'resolve'.
|
||||
*
|
||||
* @option buffers: 'toString' or 'ignore', default is 'toString'.
|
||||
*
|
||||
* @option maps: 'object' or 'ignore', default is 'object'.
|
||||
*
|
||||
* @option iterables: 'array' or 'ignore', default is 'array'.
|
||||
*
|
||||
* @option circular: 'error' or 'ignore', default is 'error'.
|
||||
*
|
||||
* @option yieldRate: The number of data items to process per timeslice,
|
||||
* default is 16384.
|
||||
*
|
||||
* @option bufferLength: The length of the buffer, default is 1024.
|
||||
*
|
||||
* @option highWaterMark: If set, will be passed to the readable stream constructor
|
||||
* as the value for the highWaterMark option.
|
||||
*
|
||||
* @option Promise: The promise constructor to use, defaults to bluebird.
|
||||
**/
|
||||
function streamify (data, options = {}) {
|
||||
const emitter = eventify(data, options)
|
||||
const json = new Hoopy(options.bufferLength || DEFAULT_BUFFER_LENGTH)
|
||||
const Promise = promise(options)
|
||||
const space = normaliseSpace(options)
|
||||
let streamOptions
|
||||
const { highWaterMark } = options
|
||||
if (highWaterMark) {
|
||||
streamOptions = { highWaterMark }
|
||||
}
|
||||
const stream = new JsonStream(read, streamOptions)
|
||||
|
||||
let awaitPush = true
|
||||
let index = 0
|
||||
let indentation = ''
|
||||
let isEnded
|
||||
let isPaused = false
|
||||
let isProperty
|
||||
let length = 0
|
||||
let mutex = Promise.resolve()
|
||||
let needsComma
|
||||
|
||||
emitter.on(events.array, noRacing(array))
|
||||
emitter.on(events.object, noRacing(object))
|
||||
emitter.on(events.property, noRacing(property))
|
||||
emitter.on(events.string, noRacing(string))
|
||||
emitter.on(events.number, noRacing(value))
|
||||
emitter.on(events.literal, noRacing(value))
|
||||
emitter.on(events.endArray, noRacing(endArray))
|
||||
emitter.on(events.endObject, noRacing(endObject))
|
||||
emitter.on(events.end, noRacing(end))
|
||||
emitter.on(events.error, noRacing(error))
|
||||
emitter.on(events.dataError, noRacing(dataError))
|
||||
|
||||
return stream
|
||||
|
||||
function read () {
|
||||
if (awaitPush) {
|
||||
awaitPush = false
|
||||
|
||||
if (isEnded) {
|
||||
if (length > 0) {
|
||||
after()
|
||||
}
|
||||
|
||||
return endStream()
|
||||
}
|
||||
}
|
||||
|
||||
if (isPaused) {
|
||||
after()
|
||||
}
|
||||
}
|
||||
|
||||
function after () {
|
||||
if (awaitPush) {
|
||||
return
|
||||
}
|
||||
|
||||
let i
|
||||
|
||||
for (i = 0; i < length && ! awaitPush; ++i) {
|
||||
if (! stream.push(json[i + index], 'utf8')) {
|
||||
awaitPush = true
|
||||
}
|
||||
}
|
||||
|
||||
if (i === length) {
|
||||
index = length = 0
|
||||
} else {
|
||||
length -= i
|
||||
index += i
|
||||
}
|
||||
}
|
||||
|
||||
function endStream () {
|
||||
if (! awaitPush) {
|
||||
stream.push(null)
|
||||
}
|
||||
}
|
||||
|
||||
function noRacing (handler) {
|
||||
return eventData => mutex = mutex.then(() => handler(eventData))
|
||||
}
|
||||
|
||||
function array () {
|
||||
return beforeScope()
|
||||
.then(() => addJson('['))
|
||||
.then(() => afterScope())
|
||||
}
|
||||
|
||||
function beforeScope () {
|
||||
return before(true)
|
||||
}
|
||||
|
||||
function before (isScope) {
|
||||
if (isProperty) {
|
||||
isProperty = false
|
||||
|
||||
if (space) {
|
||||
return addJson(' ')
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
if (needsComma) {
|
||||
if (isScope) {
|
||||
needsComma = false
|
||||
}
|
||||
|
||||
return addJson(',')
|
||||
}
|
||||
|
||||
if (! isScope) {
|
||||
needsComma = true
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
if (space && indentation) {
|
||||
return indent()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function addJson (chunk) {
|
||||
if (length + 1 <= json.length) {
|
||||
json[index + length++] = chunk
|
||||
after()
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
isPaused = true
|
||||
return new Promise(resolve => {
|
||||
const unpause = emitter.pause()
|
||||
tryer({
|
||||
interval: -10,
|
||||
until () {
|
||||
return length + 1 <= json.length
|
||||
},
|
||||
pass () {
|
||||
isPaused = false
|
||||
json[index + length++] = chunk
|
||||
resolve()
|
||||
setImmediate(unpause)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function indent () {
|
||||
return addJson(`\n${indentation}`)
|
||||
}
|
||||
|
||||
function afterScope () {
|
||||
needsComma = false
|
||||
|
||||
if (space) {
|
||||
indentation += space
|
||||
}
|
||||
}
|
||||
|
||||
function object () {
|
||||
return beforeScope()
|
||||
.then(() => addJson('{'))
|
||||
.then(() => afterScope())
|
||||
}
|
||||
|
||||
function property (name) {
|
||||
return before()
|
||||
.then(() => addJson(`"${name}":`))
|
||||
.then(() => {
|
||||
isProperty = true
|
||||
})
|
||||
}
|
||||
|
||||
function string (s) {
|
||||
return value(`"${s}"`)
|
||||
}
|
||||
|
||||
function value (v) {
|
||||
return before()
|
||||
.then(() => addJson(`${v}`))
|
||||
}
|
||||
|
||||
function endArray () {
|
||||
return beforeScopeEnd()
|
||||
.then(() => addJson(']'))
|
||||
.then(() => afterScopeEnd())
|
||||
}
|
||||
|
||||
function beforeScopeEnd () {
|
||||
if (space) {
|
||||
indentation = indentation.substr(space.length)
|
||||
|
||||
return indent()
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
function afterScopeEnd () {
|
||||
needsComma = true
|
||||
}
|
||||
|
||||
function endObject () {
|
||||
return beforeScopeEnd()
|
||||
.then(() => addJson('}'))
|
||||
.then(() => afterScopeEnd())
|
||||
}
|
||||
|
||||
function end () {
|
||||
after()
|
||||
|
||||
isEnded = true
|
||||
endStream()
|
||||
}
|
||||
|
||||
function error (err) {
|
||||
stream.emit('error', err)
|
||||
}
|
||||
|
||||
function dataError (err) {
|
||||
stream.emit('dataError', err)
|
||||
}
|
||||
}
|
||||
|
||||
function normaliseSpace (options) {
|
||||
if (check.positive(options.space)) {
|
||||
return new Array(options.space + 1).join(' ')
|
||||
}
|
||||
|
||||
if (check.nonEmptyString(options.space)) {
|
||||
return options.space
|
||||
}
|
||||
}
|
67
web/node_modules/bfj/src/stringify.js
generated
vendored
Normal file
67
web/node_modules/bfj/src/stringify.js
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
'use strict'
|
||||
|
||||
const promise = require('./promise')
|
||||
const streamify = require('./streamify')
|
||||
|
||||
module.exports = stringify
|
||||
|
||||
/**
|
||||
* Public function `stringify`.
|
||||
*
|
||||
* Returns a promise and asynchronously serialises a data structure to a
|
||||
* JSON string. Sanely handles promises, buffers, maps and other iterables.
|
||||
*
|
||||
* @param data: The data to transform
|
||||
*
|
||||
* @option space: Indentation string, or the number of spaces
|
||||
* to indent each nested level by.
|
||||
*
|
||||
* @option promises: 'resolve' or 'ignore', default is 'resolve'.
|
||||
*
|
||||
* @option buffers: 'toString' or 'ignore', default is 'toString'.
|
||||
*
|
||||
* @option maps: 'object' or 'ignore', default is 'object'.
|
||||
*
|
||||
* @option iterables: 'array' or 'ignore', default is 'array'.
|
||||
*
|
||||
* @option circular: 'error' or 'ignore', default is 'error'.
|
||||
*
|
||||
* @option yieldRate: The number of data items to process per timeslice,
|
||||
* default is 16384.
|
||||
*
|
||||
* @option bufferLength: The length of the buffer, default is 1024.
|
||||
*
|
||||
* @option highWaterMark: If set, will be passed to the readable stream constructor
|
||||
* as the value for the highWaterMark option.
|
||||
*
|
||||
* @option Promise: The promise constructor to use, defaults to bluebird.
|
||||
**/
|
||||
function stringify (data, options) {
|
||||
const json = []
|
||||
const Promise = promise(options)
|
||||
const stream = streamify(data, options)
|
||||
|
||||
let resolve, reject
|
||||
|
||||
stream.on('data', read)
|
||||
stream.on('end', end)
|
||||
stream.on('error', error)
|
||||
stream.on('dataError', error)
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
resolve = res
|
||||
reject = rej
|
||||
})
|
||||
|
||||
function read (chunk) {
|
||||
json.push(chunk)
|
||||
}
|
||||
|
||||
function end () {
|
||||
resolve(json.join(''))
|
||||
}
|
||||
|
||||
function error (e) {
|
||||
reject(e)
|
||||
}
|
||||
}
|
37
web/node_modules/bfj/src/unpipe.js
generated
vendored
Normal file
37
web/node_modules/bfj/src/unpipe.js
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
'use strict'
|
||||
|
||||
const stream = require('stream')
|
||||
const check = require('check-types')
|
||||
const parse = require('./parse')
|
||||
|
||||
module.exports = unpipe
|
||||
|
||||
/**
|
||||
* Public function `unpipe`.
|
||||
*
|
||||
* Returns a writeable stream that can be passed to stream.pipe, then parses JSON
|
||||
* data read from the stream. If there are no errors, the callback is invoked with
|
||||
* the result as the second argument. If errors occur, the first error is passed to
|
||||
* the callback as the first argument.
|
||||
*
|
||||
* @param callback: Function that will be called after parsing is complete.
|
||||
*
|
||||
* @option reviver: Transformation function, invoked depth-first.
|
||||
*
|
||||
* @option discard: The number of characters to process before discarding them
|
||||
* to save memory. The default value is `1048576`.
|
||||
*
|
||||
* @option yieldRate: The number of data items to process per timeslice,
|
||||
* default is 16384.
|
||||
**/
|
||||
function unpipe (callback, options) {
|
||||
check.assert.function(callback, 'Invalid callback argument')
|
||||
|
||||
const jsonstream = new stream.PassThrough()
|
||||
|
||||
parse(jsonstream, { ...options, ndjson: false })
|
||||
.then(data => callback(null, data))
|
||||
.catch(error => callback(error))
|
||||
|
||||
return jsonstream
|
||||
}
|
720
web/node_modules/bfj/src/walk.js
generated
vendored
Normal file
720
web/node_modules/bfj/src/walk.js
generated
vendored
Normal file
|
@ -0,0 +1,720 @@
|
|||
'use strict'
|
||||
|
||||
const check = require('check-types')
|
||||
const error = require('./error')
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const events = require('./events')
|
||||
const promise = require('./promise')
|
||||
|
||||
const terminators = {
|
||||
obj: '}',
|
||||
arr: ']'
|
||||
}
|
||||
|
||||
const escapes = {
|
||||
/* eslint-disable quote-props */
|
||||
'"': '"',
|
||||
'\\': '\\',
|
||||
'/': '/',
|
||||
'b': '\b',
|
||||
'f': '\f',
|
||||
'n': '\n',
|
||||
'r': '\r',
|
||||
't': '\t'
|
||||
/* eslint-enable quote-props */
|
||||
}
|
||||
|
||||
module.exports = initialise
|
||||
|
||||
/**
|
||||
* Public function `walk`.
|
||||
*
|
||||
* Returns an event emitter and asynchronously walks a stream of JSON data,
|
||||
* emitting events as it encounters tokens. The event emitter is decorated
|
||||
* with a `pause` method that can be called to pause processing.
|
||||
*
|
||||
* @param stream: Readable instance representing the incoming JSON.
|
||||
*
|
||||
* @option yieldRate: The number of data items to process per timeslice,
|
||||
* default is 16384.
|
||||
*
|
||||
* @option Promise: The promise constructor to use, defaults to bluebird.
|
||||
*
|
||||
* @option ndjson: Set this to true to parse newline-delimited JSON.
|
||||
**/
|
||||
function initialise (stream, options = {}) {
|
||||
check.assert.instanceStrict(stream, require('stream').Readable, 'Invalid stream argument')
|
||||
|
||||
const currentPosition = {
|
||||
line: 1,
|
||||
column: 1
|
||||
}
|
||||
const emitter = new EventEmitter()
|
||||
const handlers = {
|
||||
arr: value,
|
||||
obj: property
|
||||
}
|
||||
const json = []
|
||||
const lengths = []
|
||||
const previousPosition = {}
|
||||
const Promise = promise(options)
|
||||
const scopes = []
|
||||
const yieldRate = options.yieldRate || 16384
|
||||
const shouldHandleNdjson = !! options.ndjson
|
||||
|
||||
let index = 0
|
||||
let isStreamEnded = false
|
||||
let isWalkBegun = false
|
||||
let isWalkEnded = false
|
||||
let isWalkingString = false
|
||||
let hasEndedLine = true
|
||||
let count = 0
|
||||
let resumeFn
|
||||
let pause
|
||||
let cachedCharacter
|
||||
|
||||
stream.setEncoding('utf8')
|
||||
stream.on('data', readStream)
|
||||
stream.on('end', endStream)
|
||||
stream.on('error', err => {
|
||||
emitter.emit(events.error, err)
|
||||
endStream()
|
||||
})
|
||||
|
||||
emitter.pause = () => {
|
||||
let resolve
|
||||
pause = new Promise(res => resolve = res)
|
||||
return () => {
|
||||
pause = null
|
||||
count = 0
|
||||
|
||||
if (shouldHandleNdjson && isStreamEnded && isWalkEnded) {
|
||||
emit(events.end)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return emitter
|
||||
|
||||
function readStream (chunk) {
|
||||
addChunk(chunk)
|
||||
|
||||
if (isWalkBegun) {
|
||||
return resume()
|
||||
}
|
||||
|
||||
isWalkBegun = true
|
||||
value()
|
||||
}
|
||||
|
||||
function addChunk (chunk) {
|
||||
json.push(chunk)
|
||||
|
||||
const chunkLength = chunk.length
|
||||
lengths.push({
|
||||
item: chunkLength,
|
||||
aggregate: length() + chunkLength
|
||||
})
|
||||
}
|
||||
|
||||
function length () {
|
||||
const chunkCount = lengths.length
|
||||
|
||||
if (chunkCount === 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return lengths[chunkCount - 1].aggregate
|
||||
}
|
||||
|
||||
function value () {
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
if (++count % yieldRate !== 0) {
|
||||
return _do()
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
setImmediate(() => _do().then(resolve))
|
||||
})
|
||||
|
||||
function _do () {
|
||||
return awaitNonWhitespace()
|
||||
.then(next)
|
||||
.then(handleValue)
|
||||
.catch(() => {})
|
||||
}
|
||||
/* eslint-enable no-underscore-dangle */
|
||||
}
|
||||
|
||||
function awaitNonWhitespace () {
|
||||
return wait()
|
||||
|
||||
function wait () {
|
||||
return awaitCharacter()
|
||||
.then(step)
|
||||
}
|
||||
|
||||
function step () {
|
||||
if (isWhitespace(character())) {
|
||||
return next().then(wait)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function awaitCharacter () {
|
||||
let resolve, reject
|
||||
|
||||
if (index < length()) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
if (isStreamEnded) {
|
||||
setImmediate(endWalk)
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
resumeFn = after
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
resolve = res
|
||||
reject = rej
|
||||
})
|
||||
|
||||
function after () {
|
||||
if (index < length()) {
|
||||
return resolve()
|
||||
}
|
||||
|
||||
reject()
|
||||
|
||||
if (isStreamEnded) {
|
||||
setImmediate(endWalk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function character () {
|
||||
if (cachedCharacter) {
|
||||
return cachedCharacter
|
||||
}
|
||||
|
||||
if (lengths[0].item > index) {
|
||||
return cachedCharacter = json[0][index]
|
||||
}
|
||||
|
||||
const len = lengths.length
|
||||
for (let i = 1; i < len; ++i) {
|
||||
const { aggregate, item } = lengths[i]
|
||||
if (aggregate > index) {
|
||||
return cachedCharacter = json[i][index + item - aggregate]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isWhitespace (char) {
|
||||
switch (char) {
|
||||
case '\n':
|
||||
if (shouldHandleNdjson && scopes.length === 0) {
|
||||
return false
|
||||
}
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function next () {
|
||||
return awaitCharacter().then(after)
|
||||
|
||||
function after () {
|
||||
const result = character()
|
||||
|
||||
cachedCharacter = null
|
||||
index += 1
|
||||
previousPosition.line = currentPosition.line
|
||||
previousPosition.column = currentPosition.column
|
||||
|
||||
if (result === '\n') {
|
||||
currentPosition.line += 1
|
||||
currentPosition.column = 1
|
||||
} else {
|
||||
currentPosition.column += 1
|
||||
}
|
||||
|
||||
if (index > lengths[0].aggregate) {
|
||||
json.shift()
|
||||
|
||||
const difference = lengths.shift().item
|
||||
index -= difference
|
||||
|
||||
lengths.forEach(len => len.aggregate -= difference)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
function handleValue (char) {
|
||||
if (shouldHandleNdjson && scopes.length === 0) {
|
||||
if (char === '\n') {
|
||||
hasEndedLine = true
|
||||
return emit(events.endLine)
|
||||
.then(value)
|
||||
}
|
||||
|
||||
if (! hasEndedLine) {
|
||||
return fail(char, '\n', previousPosition)
|
||||
.then(value)
|
||||
}
|
||||
|
||||
hasEndedLine = false
|
||||
}
|
||||
|
||||
switch (char) {
|
||||
case '[':
|
||||
return array()
|
||||
case '{':
|
||||
return object()
|
||||
case '"':
|
||||
return string()
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '-':
|
||||
case '.':
|
||||
return number(char)
|
||||
case 'f':
|
||||
return literalFalse()
|
||||
case 'n':
|
||||
return literalNull()
|
||||
case 't':
|
||||
return literalTrue()
|
||||
default:
|
||||
return fail(char, 'value', previousPosition)
|
||||
.then(value)
|
||||
}
|
||||
}
|
||||
|
||||
function array () {
|
||||
return scope(events.array, value)
|
||||
}
|
||||
|
||||
function scope (event, contentHandler) {
|
||||
return emit(event)
|
||||
.then(() => {
|
||||
scopes.push(event)
|
||||
return endScope(event)
|
||||
})
|
||||
.then(contentHandler)
|
||||
}
|
||||
|
||||
function emit (...args) {
|
||||
return (pause || Promise.resolve())
|
||||
.then(() => {
|
||||
try {
|
||||
emitter.emit(...args)
|
||||
} catch (err) {
|
||||
try {
|
||||
emitter.emit(events.error, err)
|
||||
} catch (_) {
|
||||
// When calling user code, anything is possible
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function endScope (scp) {
|
||||
return awaitNonWhitespace()
|
||||
.then(() => {
|
||||
if (character() === terminators[scp]) {
|
||||
return emit(events.endPrefix + scp)
|
||||
.then(() => {
|
||||
scopes.pop()
|
||||
return next()
|
||||
})
|
||||
.then(endValue)
|
||||
}
|
||||
})
|
||||
.catch(endWalk)
|
||||
}
|
||||
|
||||
function endValue () {
|
||||
return awaitNonWhitespace()
|
||||
.then(after)
|
||||
.catch(endWalk)
|
||||
|
||||
function after () {
|
||||
if (scopes.length === 0) {
|
||||
if (shouldHandleNdjson) {
|
||||
return value()
|
||||
}
|
||||
|
||||
return fail(character(), 'EOF', currentPosition)
|
||||
.then(value)
|
||||
}
|
||||
|
||||
return checkScope()
|
||||
}
|
||||
|
||||
function checkScope () {
|
||||
const scp = scopes[scopes.length - 1]
|
||||
const handler = handlers[scp]
|
||||
|
||||
return endScope(scp)
|
||||
.then(() => {
|
||||
if (scopes.length > 0) {
|
||||
return checkCharacter(character(), ',', currentPosition)
|
||||
}
|
||||
})
|
||||
.then(result => {
|
||||
if (result) {
|
||||
return next()
|
||||
}
|
||||
})
|
||||
.then(handler)
|
||||
}
|
||||
}
|
||||
|
||||
function fail (actual, expected, position) {
|
||||
return emit(
|
||||
events.dataError,
|
||||
error.create(
|
||||
actual,
|
||||
expected,
|
||||
position.line,
|
||||
position.column
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
function checkCharacter (char, expected, position) {
|
||||
if (char === expected) {
|
||||
return Promise.resolve(true)
|
||||
}
|
||||
|
||||
return fail(char, expected, position)
|
||||
.then(false)
|
||||
}
|
||||
|
||||
function object () {
|
||||
return scope(events.object, property)
|
||||
}
|
||||
|
||||
function property () {
|
||||
return awaitNonWhitespace()
|
||||
.then(next)
|
||||
.then(propertyName)
|
||||
}
|
||||
|
||||
function propertyName (char) {
|
||||
return checkCharacter(char, '"', previousPosition)
|
||||
.then(() => walkString(events.property))
|
||||
.then(awaitNonWhitespace)
|
||||
.then(next)
|
||||
.then(propertyValue)
|
||||
}
|
||||
|
||||
function propertyValue (char) {
|
||||
return checkCharacter(char, ':', previousPosition)
|
||||
.then(value)
|
||||
}
|
||||
|
||||
function walkString (event) {
|
||||
let isEscaping = false
|
||||
const str = []
|
||||
|
||||
isWalkingString = true
|
||||
|
||||
return next().then(step)
|
||||
|
||||
function step (char) {
|
||||
if (isEscaping) {
|
||||
isEscaping = false
|
||||
|
||||
return escape(char).then(escaped => {
|
||||
str.push(escaped)
|
||||
return next().then(step)
|
||||
})
|
||||
}
|
||||
|
||||
if (char === '\\') {
|
||||
isEscaping = true
|
||||
return next().then(step)
|
||||
}
|
||||
|
||||
if (char !== '"') {
|
||||
str.push(char)
|
||||
return next().then(step)
|
||||
}
|
||||
|
||||
isWalkingString = false
|
||||
return emit(event, str.join(''))
|
||||
}
|
||||
}
|
||||
|
||||
function escape (char) {
|
||||
if (escapes[char]) {
|
||||
return Promise.resolve(escapes[char])
|
||||
}
|
||||
|
||||
if (char === 'u') {
|
||||
return escapeHex()
|
||||
}
|
||||
|
||||
return fail(char, 'escape character', previousPosition)
|
||||
.then(() => `\\${char}`)
|
||||
}
|
||||
|
||||
function escapeHex () {
|
||||
let hexits = []
|
||||
|
||||
return next().then(step.bind(null, 0))
|
||||
|
||||
function step (idx, char) {
|
||||
if (isHexit(char)) {
|
||||
hexits.push(char)
|
||||
}
|
||||
|
||||
if (idx < 3) {
|
||||
return next().then(step.bind(null, idx + 1))
|
||||
}
|
||||
|
||||
hexits = hexits.join('')
|
||||
|
||||
if (hexits.length === 4) {
|
||||
return String.fromCharCode(parseInt(hexits, 16))
|
||||
}
|
||||
|
||||
return fail(char, 'hex digit', previousPosition)
|
||||
.then(() => `\\u${hexits}${char}`)
|
||||
}
|
||||
}
|
||||
|
||||
function string () {
|
||||
return walkString(events.string).then(endValue)
|
||||
}
|
||||
|
||||
function number (firstCharacter) {
|
||||
let digits = [ firstCharacter ]
|
||||
|
||||
return walkDigits().then(addDigits.bind(null, checkDecimalPlace))
|
||||
|
||||
function addDigits (step, result) {
|
||||
digits = digits.concat(result.digits)
|
||||
|
||||
if (result.atEnd) {
|
||||
return endNumber()
|
||||
}
|
||||
|
||||
return step()
|
||||
}
|
||||
|
||||
function checkDecimalPlace () {
|
||||
if (character() === '.') {
|
||||
return next()
|
||||
.then(char => {
|
||||
digits.push(char)
|
||||
return walkDigits()
|
||||
})
|
||||
.then(addDigits.bind(null, checkExponent))
|
||||
}
|
||||
|
||||
return checkExponent()
|
||||
}
|
||||
|
||||
function checkExponent () {
|
||||
if (character() === 'e' || character() === 'E') {
|
||||
return next()
|
||||
.then(char => {
|
||||
digits.push(char)
|
||||
return awaitCharacter()
|
||||
})
|
||||
.then(checkSign)
|
||||
.catch(fail.bind(null, 'EOF', 'exponent', currentPosition))
|
||||
}
|
||||
|
||||
return endNumber()
|
||||
}
|
||||
|
||||
function checkSign () {
|
||||
if (character() === '+' || character() === '-') {
|
||||
return next().then(char => {
|
||||
digits.push(char)
|
||||
return readExponent()
|
||||
})
|
||||
}
|
||||
|
||||
return readExponent()
|
||||
}
|
||||
|
||||
function readExponent () {
|
||||
return walkDigits().then(addDigits.bind(null, endNumber))
|
||||
}
|
||||
|
||||
function endNumber () {
|
||||
return emit(events.number, parseFloat(digits.join('')))
|
||||
.then(endValue)
|
||||
}
|
||||
}
|
||||
|
||||
function walkDigits () {
|
||||
const digits = []
|
||||
|
||||
return wait()
|
||||
|
||||
function wait () {
|
||||
return awaitCharacter()
|
||||
.then(step)
|
||||
.catch(atEnd)
|
||||
}
|
||||
|
||||
function step () {
|
||||
if (isDigit(character())) {
|
||||
return next().then(char => {
|
||||
digits.push(char)
|
||||
return wait()
|
||||
})
|
||||
}
|
||||
|
||||
return { digits, atEnd: false }
|
||||
}
|
||||
|
||||
function atEnd () {
|
||||
return { digits, atEnd: true }
|
||||
}
|
||||
}
|
||||
|
||||
function literalFalse () {
|
||||
return literal([ 'a', 'l', 's', 'e' ], false)
|
||||
}
|
||||
|
||||
function literal (expectedCharacters, val) {
|
||||
let actual, expected, invalid
|
||||
|
||||
return wait()
|
||||
|
||||
function wait () {
|
||||
return awaitCharacter()
|
||||
.then(step)
|
||||
.catch(atEnd)
|
||||
}
|
||||
|
||||
function step () {
|
||||
if (invalid || expectedCharacters.length === 0) {
|
||||
return atEnd()
|
||||
}
|
||||
|
||||
return next().then(afterNext)
|
||||
}
|
||||
|
||||
function atEnd () {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
if (invalid) {
|
||||
return fail(actual, expected, previousPosition)
|
||||
}
|
||||
|
||||
if (expectedCharacters.length > 0) {
|
||||
return fail('EOF', expectedCharacters.shift(), currentPosition)
|
||||
}
|
||||
|
||||
return done()
|
||||
})
|
||||
.then(endValue)
|
||||
}
|
||||
|
||||
function afterNext (char) {
|
||||
actual = char
|
||||
expected = expectedCharacters.shift()
|
||||
|
||||
if (actual !== expected) {
|
||||
invalid = true
|
||||
}
|
||||
|
||||
return wait()
|
||||
}
|
||||
|
||||
function done () {
|
||||
return emit(events.literal, val)
|
||||
}
|
||||
}
|
||||
|
||||
function literalNull () {
|
||||
return literal([ 'u', 'l', 'l' ], null)
|
||||
}
|
||||
|
||||
function literalTrue () {
|
||||
return literal([ 'r', 'u', 'e' ], true)
|
||||
}
|
||||
|
||||
function endStream () {
|
||||
isStreamEnded = true
|
||||
|
||||
if (isWalkBegun) {
|
||||
return resume()
|
||||
}
|
||||
|
||||
endWalk()
|
||||
}
|
||||
|
||||
function resume () {
|
||||
if (resumeFn) {
|
||||
resumeFn()
|
||||
resumeFn = null
|
||||
}
|
||||
}
|
||||
|
||||
function endWalk () {
|
||||
if (isWalkEnded) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
isWalkEnded = true
|
||||
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
if (isWalkingString) {
|
||||
return fail('EOF', '"', currentPosition)
|
||||
}
|
||||
})
|
||||
.then(popScopes)
|
||||
.then(() => emit(events.end))
|
||||
}
|
||||
|
||||
function popScopes () {
|
||||
if (scopes.length === 0) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
return fail('EOF', terminators[scopes.pop()], currentPosition)
|
||||
.then(popScopes)
|
||||
}
|
||||
}
|
||||
|
||||
function isHexit (character) {
|
||||
return isDigit(character) ||
|
||||
isInRange(character, 'A', 'F') ||
|
||||
isInRange(character, 'a', 'f')
|
||||
}
|
||||
|
||||
function isDigit (character) {
|
||||
return isInRange(character, '0', '9')
|
||||
}
|
||||
|
||||
function isInRange (character, lower, upper) {
|
||||
const code = character.charCodeAt(0)
|
||||
|
||||
return code >= lower.charCodeAt(0) && code <= upper.charCodeAt(0)
|
||||
}
|
55
web/node_modules/bfj/src/write.js
generated
vendored
Normal file
55
web/node_modules/bfj/src/write.js
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const promise = require('./promise')
|
||||
const streamify = require('./streamify')
|
||||
|
||||
module.exports = write
|
||||
|
||||
/**
|
||||
* Public function `write`.
|
||||
*
|
||||
* Returns a promise and asynchronously serialises a data structure to a
|
||||
* JSON file on disk. Sanely handles promises, buffers, maps and other
|
||||
* iterables.
|
||||
*
|
||||
* @param path: Path to the JSON file.
|
||||
*
|
||||
* @param data: The data to transform.
|
||||
*
|
||||
* @option space: Indentation string, or the number of spaces
|
||||
* to indent each nested level by.
|
||||
*
|
||||
* @option promises: 'resolve' or 'ignore', default is 'resolve'.
|
||||
*
|
||||
* @option buffers: 'toString' or 'ignore', default is 'toString'.
|
||||
*
|
||||
* @option maps: 'object' or 'ignore', default is 'object'.
|
||||
*
|
||||
* @option iterables: 'array' or 'ignore', default is 'array'.
|
||||
*
|
||||
* @option circular: 'error' or 'ignore', default is 'error'.
|
||||
*
|
||||
* @option yieldRate: The number of data items to process per timeslice,
|
||||
* default is 16384.
|
||||
*
|
||||
* @option bufferLength: The length of the buffer, default is 1024.
|
||||
*
|
||||
* @option highWaterMark: If set, will be passed to the readable stream constructor
|
||||
* as the value for the highWaterMark option.
|
||||
*
|
||||
* @option Promise: The promise constructor to use, defaults to bluebird.
|
||||
**/
|
||||
function write (path, data, options) {
|
||||
const Promise = promise(options)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
streamify(data, options)
|
||||
.pipe(fs.createWriteStream(path, options))
|
||||
.on('finish', () => {
|
||||
resolve()
|
||||
})
|
||||
.on('error', reject)
|
||||
.on('dataError', reject)
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue