package/LICENSE000644 0000002437 3560116604 010273 0ustar00000000 000000 The MIT License Copyright (c) Isaac Z. Schlueter and Contributors Copyright (c) 2011 Dominic Tarr Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- This is a derivative work based on JSONStream by Dominic Tarr, modified and redistributed according to the terms of the MIT license above. https://github.com/dominictarr/JSONStream package/index.js000644 0000013640 3560116604 010731 0ustar00000000 000000 // 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 package/package.json000644 0000001450 3560116604 011546 0ustar00000000 000000 { "name": "minipass-json-stream", "version": "1.0.1", "description": "Like JSONStream, but using Minipass streams", "author": "Isaac Z. Schlueter (https://izs.me)", "license": "MIT", "scripts": { "test": "tap", "snap": "tap", "preversion": "npm test", "postversion": "npm publish", "postpublish": "git push origin --follow-tags" }, "tap": { "check-coverage": true }, "devDependencies": { "JSONStream": "^1.3.5", "tap": "^14.6.9" }, "dependencies": { "jsonparse": "^1.3.1", "minipass": "^3.0.0" }, "repository": { "type": "git", "url": "git+https://github.com/npm/minipass-json-stream.git" }, "keywords": [ "stream", "json", "parse", "minipass", "JSONStream" ], "files": [ "index.js" ] } package/README.md000644 0000011356 3560116604 010545 0ustar00000000 000000 # minipass-json-stream Like [JSONStream](http://npm.im/JSONStream), but using Minipass streams ## install ``` npm install minipass-json-stream ``` ## example ```js const request = require('request') const JSONStream = require('minipass-json-stream') const es = require('event-stream') request({url: 'http://isaacs.couchone.com/registry/_all_docs'}) .pipe(JSONStream.parse('rows.*')) .pipe(es.mapSync(function (data) { console.error(data) return data })) ``` ## new JSONStream(options) Create a new stream. This is a [minipass](http://npm.im/minipass) stream that is always set in `objectMode`. It emits objects parsed out of string/buffer JSON input that match the supplied `path` option. ## JSONStream.parse(path) Return a new JSONStream object to stream values that match a path. (Equivalent to `new JSONStream({path})`.) ``` js JSONStream.parse('rows.*.doc') ``` The `..` operator is the recursive descent operator from [JSONPath](http://goessner.net/articles/JsonPath/), which will match a child at any depth (see examples below). If your keys have keys that include `.` or `*` etc, use an array instead. `['row', true, /^doc/]`. If you use an array, `RegExp`s, booleans, and/or functions. The `..` operator is also available in array representation, using `{recurse: true}`. any object that matches the path will be emitted as 'data' (and `pipe`d down stream) If `path` is empty or null, no 'data' events are emitted. If you want to have keys emitted, you can prefix your `*` operator with `$`: `obj.$*` - in this case the data passed to the stream is an object with a `key` holding the key and a `value` property holding the data. ### Examples query a couchdb view: ``` bash curl -sS localhost:5984/tests/_all_docs&include_docs=true ``` you will get something like this: ``` js {"total_rows":129,"offset":0,"rows":[ { "id":"change1_0.6995461115147918" , "key":"change1_0.6995461115147918" , "value":{"rev":"1-e240bae28c7bb3667f02760f6398d508"} , "doc":{ "_id": "change1_0.6995461115147918" , "_rev": "1-e240bae28c7bb3667f02760f6398d508","hello":1} }, { "id":"change2_0.6995461115147918" , "key":"change2_0.6995461115147918" , "value":{"rev":"1-13677d36b98c0c075145bb8975105153"} , "doc":{ "_id":"change2_0.6995461115147918" , "_rev":"1-13677d36b98c0c075145bb8975105153" , "hello":2 } }, ]} ``` we are probably most interested in the `rows.*.doc` create a `JSONStream` that parses the documents from the feed like this: ``` js var stream = JSONStream.parse(['rows', true, 'doc']) //rows, ANYTHING, doc stream.on('data', function(data) { console.log('received:', data); }); //emits anything from _before_ the first match stream.on('header', function (data) { console.log('header:', data) // => {"total_rows":129,"offset":0} }) ``` awesome! In case you wanted the contents the doc emitted: ``` js // equivalent to: 'rows.*.doc.$*' var stream = JSONStream.parse([ 'rows', true, 'doc', {emitKey: true} ]) //rows, ANYTHING, doc, items in docs with keys stream.on('data', function(data) { console.log('key:', data.key); console.log('value:', data.value); }); ``` You can also emit the path: ``` js var stream = JSONStream.parse([ 'rows', true, 'doc', {emitPath: true} ]) //rows, ANYTHING, doc, items in docs with keys stream.on('data', function(data) { console.log('path:', data.path); console.log('value:', data.value); }); ``` ### recursive patterns (..) `JSONStream.parse('docs..value')` (or `JSONStream.parse(['docs', {recurse: true}, 'value'])` using an array) will emit every `value` object that is a child, grand-child, etc. of the `docs` object. In this example, it will match exactly 5 times at various depth levels, emitting 0, 1, 2, 3 and 4 as results. ```js { "total": 5, "docs": [ { "key": { "value": 0, "some": "property" } }, {"value": 1}, {"value": 2}, {"blbl": [{}, {"a":0, "b":1, "value":3}, 10]}, {"value": 4} ] } ``` ## JSONStream.parse(pattern, map) (Equivalent to `new JSONStream({ pattern, map })`) provide a function that can be used to map or filter the json output. `map` is passed the value at that node of the pattern, if `map` return non-nullish (anything but `null` or `undefined`) that value will be emitted in the stream. If it returns a nullish value, nothing will be emitted. `JSONStream` also emits `'header'` and `'footer'` events, the `'header'` event contains anything in the output that was before the first match, and the `'footer'`, is anything after the last match. ## Acknowlegements This module is a fork of [JSONStream](http://npm.im/JSONStream) by Dominic Tarr, modified and redistributed under the terms of the MIT license. this module depends on https://github.com/creationix/jsonparse by Tim Caswell