trouble-in-terror-town/node_modules/minipass-json-stream/index.js
Mikolaj 2bbacbea09 did some more work on networking and removed EOS in favor of LRM
did some more work on networking and removed EOS in favor of Light Reflective Mirror
2022-05-31 15:04:31 +02:00

227 lines
5.9 KiB
JavaScript

// put javascript in here
'use strict'
const Parser = require('jsonparse')
const Minipass = require('minipass')
class JSONStreamError extends Error {
constructor (err, caller) {
super(err.message)
Error.captureStackTrace(this, caller || this.constructor)
}
get name () {
return 'JSONStreamError'
}
set name (n) {}
}
const check = (x, y) =>
typeof x === 'string' ? String(y) === x
: x && typeof x.test === 'function' ? x.test(y)
: typeof x === 'boolean' || typeof x === 'object' ? x
: typeof x === 'function' ? x(y)
: false
const _parser = Symbol('_parser')
const _onValue = Symbol('_onValue')
const _onTokenOriginal = Symbol('_onTokenOriginal')
const _onToken = Symbol('_onToken')
const _onError = Symbol('_onError')
const _count = Symbol('_count')
const _path = Symbol('_path')
const _map = Symbol('_map')
const _root = Symbol('_root')
const _header = Symbol('_header')
const _footer = Symbol('_footer')
const _setHeaderFooter = Symbol('_setHeaderFooter')
const _ending = Symbol('_ending')
class JSONStream extends Minipass {
constructor (opts = {}) {
super({
...opts,
objectMode: true,
})
this[_ending] = false
const parser = this[_parser] = new Parser()
parser.onValue = value => this[_onValue](value)
this[_onTokenOriginal] = parser.onToken
parser.onToken = (token, value) => this[_onToken](token, value)
parser.onError = er => this[_onError](er)
this[_count] = 0
this[_path] = typeof opts.path === 'string'
? opts.path.split('.').map(e =>
e === '$*' ? { emitKey: true }
: e === '*' ? true
: e === '' ? { recurse: true }
: e)
: Array.isArray(opts.path) && opts.path.length ? opts.path
: null
this[_map] = typeof opts.map === 'function' ? opts.map : null
this[_root] = null
this[_header] = null
this[_footer] = null
this[_count] = 0
}
[_setHeaderFooter] (key, value) {
// header has not been emitted yet
if (this[_header] !== false) {
this[_header] = this[_header] || {}
this[_header][key] = value
}
// footer has not been emitted yet but header has
if (this[_footer] !== false && this[_header] === false) {
this[_footer] = this[_footer] || {}
this[_footer][key] = value
}
}
[_onError] (er) {
// error will always happen during a write() call.
const caller = this[_ending] ? this.end : this.write
this[_ending] = false
return this.emit('error', new JSONStreamError(er, caller))
}
[_onToken] (token, value) {
const parser = this[_parser]
this[_onTokenOriginal].call(parser, token, value)
if (parser.stack.length === 0) {
if (this[_root]) {
const root = this[_root]
if (!this[_path])
super.write(root)
this[_root] = null
this[_count] = 0
}
}
}
[_onValue] (value) {
const parser = this[_parser]
// the LAST onValue encountered is the root object.
// just overwrite it each time.
this[_root] = value
if(!this[_path]) return
let i = 0 // iterates on path
let j = 0 // iterates on stack
let emitKey = false
let emitPath = false
while (i < this[_path].length) {
const key = this[_path][i]
j++
if (key && !key.recurse) {
const c = (j === parser.stack.length) ? parser : parser.stack[j]
if (!c) return
if (!check(key, c.key)) {
this[_setHeaderFooter](c.key, value)
return
}
emitKey = !!key.emitKey;
emitPath = !!key.emitPath;
i++
} else {
i++
if (i >= this[_path].length)
return
const nextKey = this[_path][i]
if (!nextKey)
return
while (true) {
const c = (j === parser.stack.length) ? parser : parser.stack[j]
if (!c) return
if (check(nextKey, c.key)) {
i++
if (!Object.isFrozen(parser.stack[j]))
parser.stack[j].value = null
break
} else {
this[_setHeaderFooter](c.key, value)
}
j++
}
}
}
// emit header
if (this[_header]) {
const header = this[_header]
this[_header] = false
this.emit('header', header)
}
if (j !== parser.stack.length) return
this[_count] ++
const actualPath = parser.stack.slice(1)
.map(e => e.key).concat([parser.key])
if (value !== null && value !== undefined) {
const data = this[_map] ? this[_map](value, actualPath) : value
if (data !== null && data !== undefined) {
const emit = emitKey || emitPath ? { value: data } : data
if (emitKey)
emit.key = parser.key
if (emitPath)
emit.path = actualPath
super.write(emit)
}
}
if (parser.value)
delete parser.value[parser.key]
for (const k of parser.stack) {
k.value = null
}
}
write (chunk, encoding, cb) {
if (typeof encoding === 'function')
cb = encoding, encoding = null
if (typeof chunk === 'string')
chunk = Buffer.from(chunk, encoding)
else if (!Buffer.isBuffer(chunk))
return this.emit('error', new TypeError(
'Can only parse JSON from string or buffer input'))
this[_parser].write(chunk)
if (cb)
cb()
return this.flowing
}
end (chunk, encoding, cb) {
this[_ending] = true
if (typeof encoding === 'function')
cb = encoding, encoding = null
if (typeof chunk === 'function')
cb = chunk, chunk = null
if (chunk)
this.write(chunk, encoding)
if (cb)
this.once('end', cb)
const h = this[_header]
this[_header] = null
const f = this[_footer]
this[_footer] = null
if (h)
this.emit('header', h)
if (f)
this.emit('footer', f)
return super.end()
}
static get JSONStreamError () { return JSONStreamError }
static parse (path, map) {
return new JSONStream({path, map})
}
}
module.exports = JSONStream