/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = "./test/test.ts");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./lib/jsonstream.ts":
/*!***************************!*\
  !*** ./lib/jsonstream.ts ***!
  \***************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

Object.defineProperty(exports, "__esModule", { value: true });
// Node libraries
const Stream = __webpack_require__(/*! stream */ "stream");
const Events = __webpack_require__(/*! events */ "events");
// Shared libraries
const Util = __webpack_require__(/*! @dra2020/util */ "./node_modules/@dra2020/util/dist/util.js");
// Acts like Stream.Readable, emits 'error', 'close', 'data', 'end'
class Readable extends Events.EventEmitter {
    constructor() {
        super();
    }
}
exports.Readable = Readable;
// Acts like Stream.Writable, emits 'error', 'close', 'drain', 'finish''
class Writable extends Events.EventEmitter {
    constructor() {
        super();
    }
    // override
    write(b, encoding, cb) {
        return true;
    }
    // override
    end() {
        this.emit('finish');
    }
}
exports.Writable = Writable;
// Useful class for having JSONStreamWriter generate a Buffer
class BufferWritable extends Writable {
    constructor() {
        super();
        this._bufs = [];
        this._buf = null;
    }
    write(b, encoding, cb) {
        this._bufs.push(b);
        return true;
    }
    end() {
        this._buf = Buffer.concat(this._bufs);
        this.emit('finish', this._buf);
    }
}
exports.BufferWritable = BufferWritable;
// Useful class for having JSONStreamReader read from an existing Buffer
class BufferReadable extends Stream.Readable {
    constructor(b) {
        super({});
        this._b = b;
    }
    _read() {
        if (this._b) {
            this.push(this._b);
            this.push(null);
            this._b = null;
        }
    }
}
exports.BufferReadable = BufferReadable;
var ReadState;
(function (ReadState) {
    ReadState[ReadState["Start"] = 0] = "Start";
    ReadState[ReadState["ObjectPropertyStart"] = 1] = "ObjectPropertyStart";
    ReadState[ReadState["ObjectPropertyEnd"] = 2] = "ObjectPropertyEnd";
    ReadState[ReadState["ValueStart"] = 3] = "ValueStart";
    ReadState[ReadState["ValueEnd"] = 4] = "ValueEnd";
    ReadState[ReadState["InName"] = 5] = "InName";
    ReadState[ReadState["InNameBackslash"] = 6] = "InNameBackslash";
    ReadState[ReadState["NeedColon"] = 7] = "NeedColon";
    ReadState[ReadState["InNumber"] = 8] = "InNumber";
    ReadState[ReadState["InString"] = 9] = "InString";
    ReadState[ReadState["InStringBackslash"] = 10] = "InStringBackslash";
    ReadState[ReadState["InTrue"] = 11] = "InTrue";
    ReadState[ReadState["InFalse"] = 12] = "InFalse";
    ReadState[ReadState["InNull"] = 13] = "InNull";
    ReadState[ReadState["InUndefined"] = 14] = "InUndefined";
    ReadState[ReadState["Error"] = 15] = "Error";
    ReadState[ReadState["Ended"] = 16] = "Ended";
})(ReadState || (ReadState = {}));
;
const CurlyOpen = 123;
const CurlyClose = 125;
const SquareOpen = 91;
const SquareClose = 93;
const Comma = 44;
const Colon = 58;
const Quote = 34;
const Backslash = 92;
const Forwardslash = 47;
const Zero = 48;
const Nine = 57;
const Minus = 45;
const Plus = 43;
const Period = 46;
const UpperA = 65;
const LowerA = 97;
const UpperB = 66;
const LowerB = 98;
const UpperE = 69;
const LowerE = 101;
const UpperF = 70;
const LowerF = 102;
const UpperN = 78;
const LowerN = 110;
const UpperR = 82;
const LowerR = 114;
const UpperT = 84;
const LowerT = 116;
const UpperU = 85;
const LowerU = 117;
const UpperZ = 90;
const LowerZ = 122;
const Space = 32;
const Backspace = 8;
const Tab = 9;
const Newline = 10;
const CR = 13;
function isWhite(c) {
    return c == Space || c == Newline || c == Tab || c == CR;
}
function isEscapable(c) {
    switch (c) {
        case Quote: return Quote;
        case Backslash: return Backslash;
        case Forwardslash: return Forwardslash;
        case UpperB:
        case LowerB: return Backspace;
        case UpperT:
        case LowerT: return Tab;
        case UpperN:
        case LowerN: return Newline;
        case UpperR:
        case LowerR: return CR;
        // TODO: Really should support unicode \uHHHH
        default:
            return -1;
    }
}
function isNumeric(c) {
    return (c >= Zero && c <= Nine) || c == Minus || c == Plus || c == UpperE || c == LowerE || c == Period;
}
function isAlpha(c) {
    return (c >= UpperA && c <= UpperZ) || (c >= LowerA && c <= LowerZ);
}
var TokType;
(function (TokType) {
    TokType[TokType["TTNone"] = 0] = "TTNone";
    TokType[TokType["TTArray"] = 1] = "TTArray";
    TokType[TokType["TTObject"] = 2] = "TTObject";
    TokType[TokType["TTName"] = 3] = "TTName";
    TokType[TokType["TTString"] = 4] = "TTString";
    TokType[TokType["TTNumber"] = 5] = "TTNumber";
    TokType[TokType["TTTrue"] = 6] = "TTTrue";
    TokType[TokType["TTFalse"] = 7] = "TTFalse";
    TokType[TokType["TTNull"] = 8] = "TTNull";
    TokType[TokType["TTUndefined"] = 9] = "TTUndefined";
})(TokType || (TokType = {}));
;
const DefaultStreamOptions = {
    maxTokenSize: 10000,
    outBufferSize: 10000,
    encoding: 'utf8',
    syncChunkSize: 10000,
};
class JSONStreamReader extends Events.EventEmitter {
    constructor(options) {
        super();
        this.onData = this.onData.bind(this);
        this.onClose = this.onClose.bind(this);
        this.onEnd = this.onEnd.bind(this);
        this.onError = this.onError.bind(this);
        this.nextChunk = this.nextChunk.bind(this);
        this._state = ReadState.Start;
        this._charCount = 1;
        this._lineCount = 1;
        this._stack = [];
        this._result = undefined;
        this._incr = {};
        this._options = Util.shallowAssignImmutable(DefaultStreamOptions, options);
        this._tok = Buffer.alloc(this._options.maxTokenSize);
        this._tokLen = 0;
        this._s = undefined;
        this._chunks = [];
        this._chunkCur = 0;
        this._chunkIndex = 0;
        this._chunkScheduled = false;
        this._ended = false;
        this._closed = false;
    }
    start(s) {
        this._s = s;
        this._s.on('close', this.onClose);
        this._s.on('end', this.onEnd);
        this._s.on('error', this.onError);
        this._s.on('data', this.onData);
    }
    finish() {
        this._s.off('close', this.onClose);
        this._s.off('end', this.onEnd);
        this._s.off('error', this.onError);
        this._s.off('data', this.onData);
        this._s = undefined;
    }
    emitIncremental(name) {
        this._incr[name] = true;
    }
    pushToken(tok) {
        this._stack.push(tok);
        if (tok.tt == TokType.TTArray || tok.tt == TokType.TTObject) {
            let prevTok = this._stack.length > 1 ? this._stack[this._stack.length - 2] : null;
            if (prevTok && prevTok.tt == TokType.TTName && this._incr[prevTok.v] == true)
                tok.emit = prevTok.v;
        }
    }
    badJSON(detail) {
        this._state = ReadState.Error;
        if (detail !== undefined)
            this.emit('error', `Invalid JSON (${detail}) at line ${this._lineCount}, character ${this._charCount}`);
        else
            this.emit('error', `Invalid JSON at line ${this._lineCount}, character ${this._charCount}`);
        this.finish();
    }
    startTok(s, c) {
        this._state = s;
        if (c !== undefined)
            this.addToTok(c);
    }
    addToTok(c) {
        if (this._tokLen == this._options.maxTokenSize)
            this.badJSON('token too long');
        else {
            this._tok.writeUInt8(c, this._tokLen);
            this._tokLen++;
        }
    }
    consumeTok() {
        let s = this._tok.toString(this._options.encoding, 0, this._tokLen);
        this._tokLen = 0;
        return s;
    }
    produceValue(tok) {
        if (this._stack.length == 0) {
            this._state = ReadState.Ended;
            this._result = tok.v;
        }
        else {
            this._state = ReadState.ValueEnd;
            let topTok = this._stack[this._stack.length - 1];
            if (topTok.tt == TokType.TTArray) {
                if (topTok.emit)
                    this.emit('array', topTok.emit, tok.v);
                else
                    topTok.v.push(tok.v);
            }
            else if (topTok.tt == TokType.TTName) {
                let p = topTok.v;
                this._stack.pop();
                topTok = this._stack[this._stack.length - 1];
                if (topTok.tt != TokType.TTObject)
                    this.badJSON();
                else {
                    if (topTok.emit)
                        this.emit('object', topTok.emit, p, tok.v);
                    else
                        topTok.v[p] = tok.v;
                }
            }
        }
    }
    produceValidatedValue(tok) {
        let s = tok.v;
        switch (tok.tt) {
            case TokType.TTNumber:
                // TODO: Constrained parsing: [-]{0 | [1-9][0-9]*}[.[0-9]+][{e|E}[+-][0-9]+]
                // Optional leading minus, either zero or set of digits not starting with zero.
                // Optional decimal point with required digit sequence
                // Optional exponent, e or E, optional sign, required digit sequence
                tok.v = parseFloat(s);
                break;
            case TokType.TTNull:
                if (s != 'null')
                    this.badJSON('bad value');
                else
                    tok.v = null;
                break;
            case TokType.TTUndefined:
                if (s != 'undefined')
                    this.badJSON('bad value');
                else
                    tok.v = undefined;
                break;
            case TokType.TTTrue:
                if (s != 'true')
                    this.badJSON('bad value');
                else
                    tok.v = true;
                break;
            case TokType.TTFalse:
                if (s != 'false')
                    this.badJSON('bad value');
                else
                    tok.v = false;
                break;
        }
        if (this._state != ReadState.Error)
            this.produceValue(tok);
    }
    onData(chunk) {
        this._chunks.push(chunk);
        this.nextChunk();
    }
    scheduleNext() {
        if (this._state != ReadState.Error && this._chunkCur < this._chunks.length) {
            if (!this._chunkScheduled) {
                this._chunkScheduled = true;
                setImmediate(this.nextChunk);
            }
        }
        else if (this._closed)
            this.doClose();
        else if (this._ended)
            this.doEnd();
    }
    nextChunk() {
        this._chunkScheduled = false;
        if (this._chunkCur >= this._chunks.length) {
            this.scheduleNext();
            return;
        }
        let chunk = this._chunks[this._chunkCur];
        let i = this._chunkIndex;
        let e = i + this._options.syncChunkSize;
        if (e > chunk.length)
            e = chunk.length;
        for (; this._state != ReadState.Error && i < e; i++) {
            let c = chunk[i];
            this._charCount++;
            if (c == 10) {
                this._lineCount++;
                this._charCount = 1;
            }
            switch (this._state) {
                case ReadState.Start:
                    if (!isWhite(c))
                        if (c === CurlyOpen) {
                            this.pushToken({ tt: TokType.TTObject, v: {} });
                            this._state = ReadState.ObjectPropertyStart;
                        }
                        else
                            this.badJSON(`Unexpected character: ${String.fromCharCode(c)} in chunk ${chunk.toString('utf8')}`);
                    break;
                case ReadState.ObjectPropertyStart:
                    if (!isWhite(c))
                        switch (c) {
                            case Quote:
                                this.startTok(ReadState.InName);
                                break;
                            case CurlyClose:
                                this.produceValue(this._stack.pop());
                                break;
                            default:
                                this.badJSON();
                        }
                    break;
                case ReadState.ValueEnd:
                    if (!isWhite(c)) {
                        let topTok = this._stack[this._stack.length - 1];
                        if (c == Comma)
                            this._state = topTok.tt == TokType.TTArray ? ReadState.ValueStart : ReadState.ObjectPropertyStart;
                        else if ((c == SquareClose && topTok.tt == TokType.TTArray) || (c == CurlyClose && topTok.tt == TokType.TTObject))
                            this.produceValue(this._stack.pop());
                        else
                            this.badJSON(`missing comma or ending ${topTok.tt == TokType.TTArray ? 'square' : 'curly'} bracket`);
                    }
                    break;
                case ReadState.ValueStart:
                    if (!isWhite(c)) {
                        if (c == CurlyOpen) {
                            this.pushToken({ tt: TokType.TTObject, v: {} });
                            this._state = ReadState.ObjectPropertyStart;
                        }
                        else if (c == SquareOpen) {
                            this.pushToken({ tt: TokType.TTArray, v: [] });
                            this._state = ReadState.ValueStart;
                        }
                        else if (c == SquareClose) {
                            // Could be in array (good) or expecting value of object property (bad)
                            let topTok = this._stack[this._stack.length - 1];
                            if (topTok.tt == TokType.TTArray)
                                this.produceValue(this._stack.pop());
                            else
                                this.badJSON(`Unexpected character: ${String.fromCharCode(c)} in chunk ${chunk.toString('utf8')}`);
                        }
                        else if (c == Quote)
                            this.startTok(ReadState.InString);
                        else if (c == UpperT || c == LowerT)
                            this.startTok(ReadState.InTrue, c);
                        else if (c == UpperF || c == LowerF)
                            this.startTok(ReadState.InFalse, c);
                        else if (c == UpperN || c == LowerN)
                            this.startTok(ReadState.InNull, c);
                        else if (c == UpperU || c == LowerU)
                            this.startTok(ReadState.InUndefined, c);
                        else if (isNumeric(c))
                            this.startTok(ReadState.InNumber, c);
                        else
                            this.badJSON();
                    }
                    break;
                case ReadState.InName:
                    if (c == Backslash)
                        this._state = ReadState.InNameBackslash;
                    else if (c != Quote)
                        this.addToTok(c);
                    else {
                        this.pushToken({ tt: TokType.TTName, v: this.consumeTok() });
                        this._state = ReadState.NeedColon;
                    }
                    break;
                case ReadState.InNameBackslash:
                    {
                        // TODO: add hex encoding support
                        let escC = isEscapable(c);
                        if (escC < 0)
                            this.badJSON(`Illegal escaped character: ${String.fromCharCode(c)}`);
                        this.addToTok(escC);
                        this._state = ReadState.InName;
                    }
                    break;
                case ReadState.NeedColon:
                    if (!isWhite(c))
                        if (c !== Colon)
                            this.badJSON('expected colon');
                        else
                            this._state = ReadState.ValueStart;
                    break;
                case ReadState.InNumber:
                    if (isNumeric(c))
                        this.addToTok(c);
                    else {
                        this.produceValidatedValue({ tt: TokType.TTNumber, v: this.consumeTok() });
                        i--;
                    }
                    break;
                case ReadState.InString:
                    if (c == Backslash)
                        this._state = ReadState.InStringBackslash;
                    else if (c != Quote)
                        this.addToTok(c);
                    else
                        this.produceValue({ tt: TokType.TTString, v: this.consumeTok() });
                    break;
                case ReadState.InStringBackslash:
                    {
                        // TODO: add hex encoding support
                        let escC = isEscapable(c);
                        if (escC < 0)
                            this.badJSON(`Illegal escaped character: ${String.fromCharCode(c)}`);
                        this.addToTok(escC);
                        this._state = ReadState.InString;
                    }
                    break;
                case ReadState.InTrue:
                    if (isAlpha(c))
                        this.addToTok(c);
                    else {
                        this.produceValidatedValue({ tt: TokType.TTTrue, v: this.consumeTok() });
                        i--;
                    }
                    break;
                case ReadState.InFalse:
                    if (isAlpha(c))
                        this.addToTok(c);
                    else {
                        this.produceValidatedValue({ tt: TokType.TTFalse, v: this.consumeTok() });
                        i--;
                    }
                    break;
                case ReadState.InNull:
                    if (isAlpha(c))
                        this.addToTok(c);
                    else {
                        this.produceValidatedValue({ tt: TokType.TTNull, v: this.consumeTok() });
                        i--;
                    }
                    break;
                case ReadState.InUndefined:
                    if (isAlpha(c))
                        this.addToTok(c);
                    else {
                        this.produceValidatedValue({ tt: TokType.TTUndefined, v: this.consumeTok() });
                        i--;
                    }
                    break;
                case ReadState.Ended:
                    if (!isWhite(c))
                        this.badJSON('unexpected character after end of object');
                    break;
            }
        }
        // Potentially advance to next chunk and schedule next processing slice
        if (this._state === ReadState.Error) {
            this._chunks = [];
            this._chunkCur = 0;
            this._chunkIndex = 0;
        }
        else {
            if (e === chunk.length) {
                this._chunkIndex = 0;
                if (this._chunkCur === this._chunks.length - 1) {
                    this._chunks = [];
                    this._chunkCur = 0;
                }
                else {
                    this._chunkCur++;
                    if (this._chunkCur >= 100) {
                        this._chunks.splice(0, this._chunkCur);
                        this._chunkCur = 0;
                    }
                }
            }
            else
                this._chunkIndex = e;
        }
        this.scheduleNext();
    }
    onClose() {
        this._closed = true;
        this.scheduleNext();
    }
    doClose() {
        this._closed = undefined;
        this.doEnd();
        this.emit('close');
    }
    onError(e) {
        this._state = ReadState.Error;
        let s = 'stream error';
        if (e && typeof e.message === 'string')
            s = e.message;
        console.log(`jsonreader: emitting error: ${s}`);
        this.emit('error', s);
        this.finish();
    }
    onEnd() {
        this._ended = true;
        this.scheduleNext();
    }
    doEnd() {
        this._ended = undefined;
        if (this._state == ReadState.Ended) {
            // Only emit once
            if (this._result !== undefined) {
                this.emit('end', this._result);
                this._result = undefined;
                this.finish();
            }
        }
        else if (this._state !== ReadState.Error)
            this.badJSON('Unexpected end of input');
    }
}
exports.JSONStreamReader = JSONStreamReader;
class JSONStreamWriter extends Events.EventEmitter {
    constructor(options) {
        super();
        this._bDone = false;
        this._s = null;
        this._stack = [];
        this.onDrain = this.onDrain.bind(this);
        this.onError = this.onError.bind(this);
        this.onFinish = this.onFinish.bind(this);
        this.onClose = this.onClose.bind(this);
        this._options = Util.shallowAssignImmutable(DefaultStreamOptions, options);
        this._chunk = Buffer.alloc(this._options.outBufferSize);
        this._chunkLen = 0;
    }
    start(o, s) {
        this._s = s;
        this._s.on('drain', this.onDrain);
        this._stack.push({ o: o, keys: Array.isArray(o) ? null : Object.keys(o), index: 0 });
        this.onDrain();
    }
    addToChunk(s) {
        let b = Buffer.from(s, this._options.encoding);
        let bContinue = true;
        if (b.length + this._chunkLen > this._options.outBufferSize)
            bContinue = this.drainChunk();
        if (b.length > this._options.outBufferSize)
            throw 'Internal error: atom larger than buffer size';
        b.copy(this._chunk, this._chunkLen);
        this._chunkLen += b.length;
        return bContinue;
    }
    drainChunk() {
        if (this._chunkLen > 0) {
            let b = this._chunk.slice(0, this._chunkLen);
            // false just indicates backpressure - write still succeeded.
            let bDraining = this._s.write(b);
            // Allocate new write buffer to prevent overwriting output buffer before output stream
            // gets chance to copy out the data.
            this._chunk = Buffer.alloc(this._options.outBufferSize);
            this._chunkLen = 0;
            return bDraining;
        }
        return true;
    }
    onDrain() {
        let bContinue;
        while (!this._bDone && this._stack.length > 0) {
            let top = this._stack[this._stack.length - 1];
            // Object
            if (top.keys) {
                if (top.keys.length == 0)
                    bContinue = this.addToChunk('{');
                let bRecurse = false;
                // Even index means propname not written
                while (!this._bDone && top.index < top.keys.length * 2) {
                    let ii = Math.floor(top.index / 2);
                    if ((top.index % 2) == 0) {
                        let s = `${top.index == 0 ? '{' : ','}"${top.keys[ii]}":`;
                        bContinue = this.addToChunk(s);
                        top.index++;
                        if (!bContinue)
                            return;
                    }
                    let oo = top.o[top.keys[ii]];
                    if (oo && typeof oo === 'object') {
                        bRecurse = true;
                        top.index++;
                        this._stack.push({ o: oo, keys: Array.isArray(oo) ? null : Object.keys(oo), index: 0 });
                        break;
                    }
                    else {
                        let s = JSON.stringify(oo === undefined ? null : oo);
                        bContinue = this.addToChunk(s);
                        top.index++;
                        if (!bContinue)
                            return;
                    }
                }
                if (!bRecurse) {
                    bContinue = this.addToChunk('}');
                    this._stack.pop();
                    if (!bContinue)
                        return;
                }
            }
            // Array
            else {
                if (top.o.length == 0)
                    bContinue = this.addToChunk('[');
                let bRecurse = false;
                // Even index means separator not written
                while (!this._bDone && top.index < top.o.length * 2) {
                    let ii = Math.floor(top.index / 2);
                    if ((top.index % 2) == 0) {
                        let s = top.index == 0 ? '[' : ',';
                        bContinue = this.addToChunk(s);
                        top.index++;
                        if (!bContinue)
                            return;
                    }
                    let oo = top.o[ii];
                    if (oo && typeof oo === 'object') {
                        bRecurse = true;
                        top.index++;
                        this._stack.push({ o: oo, keys: Array.isArray(oo) ? null : Object.keys(oo), index: 0 });
                        break;
                    }
                    else {
                        let s = JSON.stringify(oo === undefined ? null : oo);
                        bContinue = this.addToChunk(s);
                        top.index++;
                        if (!bContinue)
                            return;
                    }
                }
                if (!bRecurse) {
                    bContinue = this.addToChunk(']');
                    this._stack.pop();
                    if (!bContinue)
                        return;
                }
            }
        }
        this.drainChunk();
        this._s.end();
    }
    onError(e) {
        this._bDone = true;
        let s = 'stream error';
        if (e && typeof e.message === 'string')
            s = e.message;
        console.log(`jsonwriter: emitting error: ${s}`);
        this.emit('error', s);
    }
    onClose() {
        this._bDone = true;
        this.emit('close');
    }
    onFinish() {
        this._bDone = true;
        this.emit('finish');
    }
}
exports.JSONStreamWriter = JSONStreamWriter;


/***/ }),

/***/ "./node_modules/@dra2020/util/dist/util.js":
/*!*************************************************!*\
  !*** ./node_modules/@dra2020/util/dist/util.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

(function webpackUniversalModuleDefinition(root, factory) {
	if(true)
		module.exports = factory();
	else {}
})(global, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = "./lib/all.ts");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./lib/all.ts":
/*!********************!*\
  !*** ./lib/all.ts ***!
  \********************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

function __export(m) {
    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(__webpack_require__(/*! ./util */ "./lib/util.ts"));
__export(__webpack_require__(/*! ./countedhash */ "./lib/countedhash.ts"));
__export(__webpack_require__(/*! ./indexedarray */ "./lib/indexedarray.ts"));


/***/ }),

/***/ "./lib/countedhash.ts":
/*!****************************!*\
  !*** ./lib/countedhash.ts ***!
  \****************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

Object.defineProperty(exports, "__esModule", { value: true });
class CountedHash {
    constructor() {
        this.n = 0;
        this.val = {};
    }
    get length() { return this.n; }
    test(id) {
        return id != '' && this.val[id] !== undefined;
    }
    set(id) {
        if (id != '' && !this.test(id)) {
            this.n++;
            this.val[id] = true;
        }
    }
    clear(id) {
        if (this.test(id)) {
            this.n--;
            delete this.val[id];
        }
    }
    empty() {
        this.n = 0;
        this.val = {};
    }
    asArray() {
        let a = [];
        this.forEach(id => { a.push(id); });
        return a;
    }
    asString() {
        for (var id in this.val)
            if (this.val.hasOwnProperty(id))
                return id;
        return '';
    }
    forEach(f) {
        for (var id in this.val)
            if (this.val.hasOwnProperty(id))
                f(id);
    }
}
exports.CountedHash = CountedHash;


/***/ }),

/***/ "./lib/indexedarray.ts":
/*!*****************************!*\
  !*** ./lib/indexedarray.ts ***!
  \*****************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

Object.defineProperty(exports, "__esModule", { value: true });
class IndexedArray {
    constructor() {
        this.o = {};
        this.a = null;
    }
    ensure() {
        if (this.a === null) {
            this.a = [];
            for (let p in this.o)
                if (this.o.hasOwnProperty(p))
                    this.a.push(p);
            this.a.sort((a, b) => { a = a.toUpperCase(); b = b.toUpperCase(); return a < b ? -1 : (a > b ? 1 : 0); });
        }
    }
    get length() { this.ensure(); return this.a.length; }
    test(s) {
        return !!s && this.o[s] !== undefined;
    }
    set(s) {
        if (!!s && !this.test(s)) {
            this.o[s] = true;
            this.a = null;
        }
    }
    setAll(a) {
        if (a && a.length)
            for (let i = 0; i < a.length; i++)
                this.set(a[i]);
    }
    clear(s) {
        if (this.test(s)) {
            delete this.o[s];
            this.a = null;
        }
    }
    at(i) {
        this.ensure();
        if (i < 0 || i >= this.a.length)
            return undefined;
        return this.a[i];
    }
    empty() {
        this.o = {};
        this.a = null;
    }
    forEach(f) {
        for (var s in this.o)
            if (this.o.hasOwnProperty(s))
                f(s);
    }
}
exports.IndexedArray = IndexedArray;


/***/ }),

/***/ "./lib/util.ts":
/*!*********************!*\
  !*** ./lib/util.ts ***!
  \*********************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

Object.defineProperty(exports, "__esModule", { value: true });
function Now() { return (new Date()).toJSON(); }
exports.Now = Now;
class Elapsed {
    constructor(bStart = true) {
        this.tStart = undefined;
        this.tDur = undefined;
        if (bStart)
            this.start();
    }
    start() {
        if (process)
            this.tStart = process.hrtime();
        else
            this.tStart = performance.now();
    }
    end() {
        if (this.tStart === undefined)
            this.start();
        if (process)
            this.tDur = process.hrtime(this.tStart);
        else
            this.tDur = performance.now() - this.tStart;
    }
    ms() {
        if (this.tDur === undefined)
            this.end();
        if (process)
            return Math.round((this.tDur[0] * 1000) + (this.tDur[1] / 1000000));
        else
            return this.tDur;
    }
    nano() {
        if (process)
            return (this.tDur[0] * 1000000000) + this.tDur[1];
        else
            return this.tDur * 1000000;
    }
}
exports.Elapsed = Elapsed;
class Deadline {
    constructor(msDelta) {
        this.msDelta = msDelta;
        this.elapsed = new Elapsed();
    }
    done() {
        this.elapsed.end();
        return this.elapsed.ms() > this.msDelta;
    }
}
exports.Deadline = Deadline;
function createGuid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}
exports.createGuid = createGuid;
function _sizeof(a, loops) {
    if (a === null || a === undefined)
        return 0;
    switch (typeof a) {
        default: return 0;
        case 'number': return 8;
        case 'boolean': return 4;
        case 'string': return a.length * 2;
        case 'object':
            {
                if (loops.has(a))
                    return 0;
                else
                    loops.set(a, true);
                let t = 0;
                if (Array.isArray(a)) {
                    for (let i = 0; i < a.length; i++)
                        t += _sizeof(a[i], loops);
                    t += 8; // length
                }
                else if (Buffer.isBuffer(a)) {
                    t = a.length;
                }
                else if (a.hasOwnProperty === undefined)
                    return t;
                else {
                    for (var key in a)
                        if (a.hasOwnProperty && a.hasOwnProperty(key)) {
                            t += _sizeof(key, loops); // this is a good estimate of download size, but poor estimate of internal size
                            // because of JS object templating vs. naive hashtables
                            t += _sizeof(a[key], loops);
                        }
                }
                return t;
            }
    }
}
function sizeof(a) {
    let loops = new WeakMap();
    let n = _sizeof(a, loops);
    return n;
}
exports.sizeof = sizeof;
function depthof(a) {
    if (a === null || a === undefined)
        return 1;
    switch (typeof a) {
        default: return 1;
        case 'number': return 1;
        case 'boolean': return 1;
        case 'string': return 1;
        case 'object':
            {
                let d = 0;
                if (Array.isArray(a))
                    return a.length > 0 ? (1 + depthof(a[0])) : 2; // still return 2 for empty array
                else if (Buffer.isBuffer(a))
                    return 2;
                else if (a.hasOwnProperty === undefined)
                    return 1;
                else {
                    for (var key in a)
                        if (a.hasOwnProperty(key))
                            return 1 + depthof(a[key]);
                    return 2; // or 2 for empty object
                }
            }
    }
}
exports.depthof = depthof;
function isEmpty(o) {
    if (o === null || o === undefined)
        return true;
    for (var p in o)
        if (o.hasOwnProperty(p))
            return false;
    return true;
}
exports.isEmpty = isEmpty;
function countKeys(o) {
    if (o === undefined || typeof o !== 'object')
        return -1;
    let count = 0;
    for (let p in o)
        if (o.hasOwnProperty(p))
            count++;
    return count;
}
exports.countKeys = countKeys;
function nthProperty(o, n = 0) {
    for (let p in o)
        if (o.hasOwnProperty(p)) {
            if (n <= 0)
                return o[p];
            n--;
        }
    return undefined;
}
exports.nthProperty = nthProperty;
function partialEqual(o, subset) {
    for (let p in subset)
        if (subset.hasOwnProperty(p))
            if (o[p] !== subset[p])
                return false;
    return true;
}
exports.partialEqual = partialEqual;
function deepEqual(o1, o2) {
    if (typeof o1 !== typeof o2)
        return false;
    if (typeof o1 !== 'object')
        return o1 === o2;
    // Special case array
    if (Array.isArray(o1)) {
        if (!Array.isArray(o2))
            return false;
        if (o1.length != o2.length)
            return false;
        for (let i = 0; i < o1.length; i++)
            if (!deepEqual(o1[i], o2[i]))
                return false;
        return true;
    }
    // Special case object
    if (o1.hasOwnProperty === undefined || o2.hasOwnProperty === undefined)
        return o1 === o2;
    for (let p in o1)
        if (o1.hasOwnProperty(p)) {
            if (o2[p] === undefined)
                return false;
            if (!deepEqual(o1[p], o2[p]))
                return false;
        }
    // If any properties in o2 aren't in o1, not equal
    for (let p in o2)
        if (o2.hasOwnProperty(p))
            if (o1[p] === undefined)
                return false;
    return true;
}
exports.deepEqual = deepEqual;
const Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
function prettyDate(d) {
    if (d == null)
        return 'unknown';
    let mmm = Months[d.getMonth()];
    let dd = d.getDate();
    let yyyy = d.getFullYear();
    let hh = d.getHours();
    let m = d.getMinutes();
    let mm = m < 10 ? `0${m}` : String(m);
    let ampm = hh >= 12 ? 'PM' : 'AM';
    if (hh > 12)
        hh -= 12;
    return `${mmm} ${dd}, ${yyyy} at ${hh}:${mm} ${ampm}`;
}
exports.prettyDate = prettyDate;
function textToHtml(sText) {
    let lines = sText.split('\n');
    let aHtml = [];
    let inTable = false;
    aHtml.push('<body>');
    for (let i = 0; i < lines.length; i++) {
        let line = lines[i];
        let isRow = line.indexOf('|') === 0;
        if (inTable && !isRow) {
            aHtml.push('</tbody></table>');
            inTable = false;
        }
        if (isRow && !inTable) {
            inTable = true;
            aHtml.push('<table border="1" cellspacing="0" cellpadding="2"><tbody>');
        }
        if (isRow) {
            let cells = line.split('|');
            if (cells.length > 2) {
                aHtml.push('<tr>');
                for (let j = 1; j < cells.length - 1; j++)
                    aHtml.push(`<td>${cells[j]}</td>`);
                aHtml.push('</tr>');
            }
        }
        else
            aHtml.push(`<div>${line}&nbsp;</div>`);
    }
    if (inTable)
        aHtml.push('</tbody></table>');
    aHtml.push('</body>');
    return aHtml.join('');
}
exports.textToHtml = textToHtml;
function shallowCopy(src) {
    if (src === null || src === undefined)
        return src;
    switch (typeof src) {
        case 'boolean':
        case 'number':
        case 'string':
        case 'symbol':
        case 'function':
        default:
            return src;
        case 'object':
            if (Array.isArray(src))
                return src.slice();
            else {
                let copy = {};
                for (var p in src)
                    if (src.hasOwnProperty(p))
                        copy[p] = src[p];
                return copy;
            }
    }
}
exports.shallowCopy = shallowCopy;
function shallowAssign(o1, o2) {
    if (o1 === null || o1 === undefined)
        o1 = {};
    if (o2 === null || o2 === undefined)
        return o1;
    if (typeof o2 !== 'object' || typeof o1 !== 'object')
        return o1;
    for (var p in o2)
        if (o2.hasOwnProperty(p))
            o1[p] = o2[p];
    return o1;
}
exports.shallowAssign = shallowAssign;
function shallowAssignImmutable(o1, o2) {
    if (o1 === null || o1 === undefined)
        o1 = {};
    if (o2 === null || o2 === undefined)
        return o1;
    if (typeof o2 !== 'object' || typeof o1 !== 'object')
        return o1;
    // First determine whether o2 changes any properties, if it has, make new instance
    let oNew = o1;
    for (let p in o2)
        if (o2.hasOwnProperty(p)) {
            if (o1[p] != o2[p]) {
                oNew = shallowCopy(o1);
                break;
            }
        }
    if (oNew !== o1)
        shallowAssign(oNew, o2);
    return oNew;
}
exports.shallowAssignImmutable = shallowAssignImmutable;
function shallowEqual(o1, o2) {
    if (o1 === undefined || o2 === undefined || typeof o1 !== 'object' || typeof o2 !== 'object')
        return o1 === o2;
    if (Array.isArray(o1) && Array.isArray(o2)) {
        if (o1.length != o2.length)
            return false;
        for (let i = 0; i < o1.length; i++)
            if (o1[i] !== o2[i])
                return false;
        return true;
    }
    else {
        let p;
        for (p in o1)
            if (o1.hasOwnProperty(p))
                if (o1[p] !== o2[p])
                    return false;
        for (p in o2)
            if (o2.hasOwnProperty(p))
                if (o1[p] === undefined)
                    return false;
        return true;
    }
}
exports.shallowEqual = shallowEqual;
function deepCopy(src) {
    // Beware typeof oddities
    if (src === null || src === undefined)
        return src;
    if (typeof src === 'object') {
        if (Array.isArray(src)) {
            let dst = [];
            for (let i = 0; i < src.length; i++)
                dst.push(deepCopy(src[i]));
            return dst;
        }
        else {
            if (src.hasOwnProperty === undefined)
                return src;
            let dst = {};
            for (var p in src)
                if (src.hasOwnProperty(p))
                    dst[p] = deepCopy(src[p]);
            return dst;
        }
    }
    else
        return src;
}
exports.deepCopy = deepCopy;
function precisionRound(n, p) {
    let f = Math.pow(10, p);
    return Math.round(n * f) / f;
}
exports.precisionRound = precisionRound;
function percentString(num, den, precision = 0) {
    if (den == 0)
        return '(-)';
    let p = precisionRound((num / den) * 100, precision);
    return String(p) + '%';
}
exports.percentString = percentString;
function hash(s) {
    let hash = 5381;
    let i = s.length;
    while (i)
        hash = (hash * 33) ^ s.charCodeAt(--i);
    /* JavaScript does bitwise operations (like XOR, above) on 32-bit signed
     * integers. Since we want the results to be always positive, convert the
     * signed int to an unsigned by doing an unsigned bitshift. */
    return hash >>> 0;
}
exports.hash = hash;
function hashObject(o) {
    return hash(o ? JSON.stringify(o) : '');
}
exports.hashObject = hashObject;
const HexTable = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
function toHex(n) {
    if (n < 0 || n > 255)
        throw ('only 0 to 255 supported now');
    n = Math.floor(n);
    return HexTable[n >> 4] + HexTable[n & 15];
}
exports.toHex = toHex;
function toRGBA(color, alpha) {
    let r;
    let g;
    let b;
    switch (color) {
        case 'white':
            r = 255;
            g = 255;
            b = 255;
            break;
        case 'black':
            r = 0;
            g = 0;
            b = 0;
            break;
        default:
            r = parseInt(color.substr(1, 2), 16);
            g = parseInt(color.substr(3, 2), 16);
            b = parseInt(color.substr(5, 2), 16);
            break;
    }
    return `rgba(${String(r)}, ${String(g)}, ${String(b)}, ${String(alpha)})`;
}
exports.toRGBA = toRGBA;
function toRGBAIntensity(color, intensity, alpha) {
    // for now assume color is black
    let g = precisionRound(255 * intensity, 0);
    return `rgba(${String(g)}, ${String(g)}, ${String(g)}, ${String(alpha)})`;
}
exports.toRGBAIntensity = toRGBAIntensity;
// Geo functions
function distance(x0, y0, x1, y1) {
    return Math.hypot(x0 - x1, y0 - y1);
}
exports.distance = distance;
function deg2rad(num) { return num * Math.PI / 180; }
exports.deg2rad = deg2rad;
function rad2deg(num) { return num / Math.PI * 180; }
exports.rad2deg = rad2deg;
// Restricts lon to range [-180..180]
function wrapLon(lon) {
    let worlds = Math.floor((lon + 180) / 360);
    return lon - (worlds * 360);
}
exports.wrapLon = wrapLon;


/***/ })

/******/ });
});


/***/ }),

/***/ "./test/test.ts":
/*!**********************!*\
  !*** ./test/test.ts ***!
  \**********************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

Object.defineProperty(exports, "__esModule", { value: true });
const fs = __webpack_require__(/*! fs */ "fs");
const JS = __webpack_require__(/*! ../lib/jsonstream */ "./lib/jsonstream.ts");
// Create a chunkier stream to test whether client deals with all chunk boundaries properly
class ChunkedStream extends JS.Readable {
    constructor(s, chunkSize = 100) {
        super();
        this.s = s;
        this.chunkSize = chunkSize;
        this.bDone = false;
        this.onEnd = this.onEnd.bind(this);
        this.onError = this.onError.bind(this);
        this.onClose = this.onClose.bind(this);
        this.onData = this.onData.bind(this);
        this.s.on('end', this.onEnd);
        this.s.on('error', this.onError);
        this.s.on('close', this.onClose);
        this.s.on('data', this.onData);
    }
    onData(chunk) {
        let offset = 0;
        let len = chunk.length;
        while (!this.bDone && offset < len) {
            let n = len - offset;
            if (n > this.chunkSize)
                n = this.chunkSize;
            let subChunk = chunk.slice(offset, offset + n);
            this.emit('data', subChunk);
            offset += n;
        }
    }
    onEnd() {
        this.bDone = true;
        this.emit('end');
    }
    onError(e) {
        this.bDone = true;
        this.emit('error', e);
    }
    onClose() {
        this.bDone = true;
        this.emit('close');
    }
}
// Create a stream with lots of back pressure to validate client deals well with it
class GlitchyStream extends JS.Writable {
    constructor(s, chunkSize = 10000) {
        super();
        this.s = s;
        this.chunkSize = chunkSize;
        this.buf = Buffer.alloc(chunkSize);
        this.bufLen = 0;
        this.bDone = false;
        this.bPaused = false;
        this.onFinish = this.onFinish.bind(this);
        this.onError = this.onError.bind(this);
        this.onClose = this.onClose.bind(this);
        this.onDrain = this.onDrain.bind(this);
        this.s.on('finish', this.onFinish);
        this.s.on('error', this.onError);
        this.s.on('close', this.onClose);
        this.s.on('drain', this.onDrain);
    }
    write(a, encoding, cb) {
        if (this.bDone)
            throw 'GlitchyStream: write called when stream done. Huh?';
        if (this.bPaused)
            throw 'GlitchyStream: write called while stream paused. Wait for drain event.';
        if (a.length > this.chunkSize)
            throw 'GlitchyStream: write called with chunk too big.';
        // If I can fit it in my small chunk, do so.
        if (a.length + this.bufLen <= this.chunkSize) {
            a.copy(this.buf, this.bufLen);
            this.bufLen += a.length;
            return true;
        }
        else {
            let bHere = this.chunkSize - this.bufLen;
            let bLeft = a.length - bHere;
            a.copy(this.buf, this.bufLen, 0, bHere);
            let bOK = this.s.write(this.buf);
            a.copy(this.buf, 0, bHere);
            this.bufLen = bLeft;
            // If underlying stream successfully wrote, it won't generate a drain event so I need to force that.
            if (bOK)
                process.nextTick(this.onDrain);
            // Create backpressure when chunk overflows
            return false;
        }
    }
    end() {
        if (!this.bDone && this.bufLen > 0) {
            let b = this.buf.slice(0, this.bufLen);
            this.s.write(b);
            this.bufLen = 0;
        }
        this.bDone = true;
    }
    onDrain() {
        // called by underlying stream as well as by my own implementation. Just forward.
        this.emit('drain');
    }
    onFinish() {
        this.bDone = true;
        this.emit('finish');
    }
    onError(e) {
        this.bDone = true;
        this.emit('error', e);
    }
    onClose() {
        this.bDone = true;
        this.emit('close');
    }
}
let file = process.argv[process.argv.length - 1];
let jsonReadStream = new JS.JSONStreamReader({ syncChunkSize: 10 });
let jsonWriteStream = new JS.JSONStreamWriter();
jsonReadStream.on('end', (o) => { jsonWriteStream.start(o, new GlitchyStream(process.stdout)); });
jsonReadStream.on('error', (err) => { console.log(err.message); });
//jsonReadStream.start(new ChunkedStream(fs.createReadStream(file)));
let bufferReadable = new JS.BufferReadable(fs.readFileSync(file));
jsonReadStream.start(bufferReadable);


/***/ }),

/***/ "events":
/*!*************************!*\
  !*** external "events" ***!
  \*************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = require("events");

/***/ }),

/***/ "fs":
/*!*********************!*\
  !*** external "fs" ***!
  \*********************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = require("fs");

/***/ }),

/***/ "stream":
/*!*************************!*\
  !*** external "stream" ***!
  \*************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = require("stream");

/***/ })

/******/ });
//# sourceMappingURL=test.js.map