diff --git a/forward_engineering/helpers/validateAvroScript.js b/forward_engineering/helpers/validateAvroScript.js index cd6b2127..c5deb5cd 100644 --- a/forward_engineering/helpers/validateAvroScript.js +++ b/forward_engineering/helpers/validateAvroScript.js @@ -1,5 +1,5 @@ const _ = require('lodash'); -const avsc = require('../modules/avsc'); +const avsc = require('avsc'); const { parseJson } = require('./generalHelper'); const { SCRIPT_TYPES } = require('../../shared/constants'); diff --git a/forward_engineering/modules/avsc/LICENSE b/forward_engineering/modules/avsc/LICENSE deleted file mode 100644 index 4bf56157..00000000 --- a/forward_engineering/modules/avsc/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2015-2017, Matthieu Monsch. - -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. diff --git a/forward_engineering/modules/avsc/README.md b/forward_engineering/modules/avsc/README.md deleted file mode 100644 index 879a6522..00000000 --- a/forward_engineering/modules/avsc/README.md +++ /dev/null @@ -1,131 +0,0 @@ -# Avsc [![NPM version](https://img.shields.io/npm/v/avsc.svg)](https://www.npmjs.com/package/avsc) [![Download count](https://img.shields.io/npm/dm/avsc.svg)](https://www.npmjs.com/package/avsc) [![Build status](https://travis-ci.org/mtth/avsc.svg?branch=master)](https://travis-ci.org/mtth/avsc) [![Coverage status](https://coveralls.io/repos/mtth/avsc/badge.svg?branch=master&service=github)](https://coveralls.io/github/mtth/avsc?branch=master) - -Pure JavaScript implementation of the [Avro -specification](https://avro.apache.org/docs/current/spec.html). - - -## Features - -+ Blazingly [fast and compact][benchmarks] serialization! Typically faster than - JSON with much smaller encodings. -+ All the Avro goodness and more: [type inference][type-inference], [schema - evolution][schema-evolution], and [remote procedure calls][rpc]. -+ Support for [serializing arbitrary JavaScript objects][logical-types]. -+ Unopinionated [64-bit integer compatibility][custom-long]. - - -## Installation - -```bash -$ npm install avsc -``` - -`avsc` is compatible with all versions of [node.js][] since `0.11` and major -browsers via [browserify][]. For convenience, you can also find compiled -distributions with the [releases][] (but please host your own copy). - - -## Documentation - -+ [Home][home] -+ [API](https://github.com/mtth/avsc/wiki/API) -+ [Quickstart](https://github.com/mtth/avsc/wiki/Quickstart) -+ [Advanced usage](https://github.com/mtth/avsc/wiki/Advanced-usage) -+ [Benchmarks][benchmarks] - - -## Examples - -Inside a node.js module, or using browserify: - -```javascript -const avro = require('avsc'); -``` - -+ Encode and decode values from a known schema: - - ```javascript - const type = avro.Type.forSchema({ - type: 'record', - fields: [ - {name: 'kind', type: {type: 'enum', symbols: ['CAT', 'DOG']}}, - {name: 'name', type: 'string'} - ] - }); - - const buf = type.toBuffer({kind: 'CAT', name: 'Albert'}); // Encoded buffer. - const val = type.fromBuffer(buf); // = {kind: 'CAT', name: 'Albert'} - ``` - -+ Infer a value's schema and encode similar values: - - ```javascript - const type = avro.Type.forValue({ - city: 'Cambridge', - zipCodes: ['02138', '02139'], - visits: 2 - }); - - // We can use `type` to encode any values with the same structure: - const bufs = [ - type.toBuffer({city: 'Seattle', zipCodes: ['98101'], visits: 3}), - type.toBuffer({city: 'NYC', zipCodes: [], visits: 0}) - ]; - ``` - -+ Get a [readable stream][readable-stream] of decoded values from an Avro - container file compressed using [Snappy][snappy] (see the [`BlockDecoder` - API][decoder-api] for an example including checksum validation): - - ```javascript - const snappy = require('snappy'); // Or your favorite Snappy library. - const codecs = { - snappy: function (buf, cb) { - // Avro appends checksums to compressed blocks, which we skip here. - return snappy.uncompress(buf.slice(0, buf.length - 4), cb); - } - }; - - avro.createFileDecoder('./values.avro', {codecs}) - .on('metadata', function (type) { /* `type` is the writer's type. */ }) - .on('data', function (val) { /* Do something with the decoded value. */ }); - ``` - -+ Implement a TCP server for an [IDL-defined][idl] protocol: - - ```javascript - // We first generate a protocol from its IDL specification. - const protocol = avro.readProtocol(` - protocol LengthService { - /** Endpoint which returns the length of the input string. */ - int stringLength(string str); - } - `); - - // We then create a corresponding server, implementing our endpoint. - const server = avro.Service.forProtocol(protocol) - .createServer() - .onStringLength(function (str, cb) { cb(null, str.length); }); - - // Finally, we use our server to respond to incoming TCP connections! - require('net').createServer() - .on('connection', (con) => { server.createChannel(con); }) - .listen(24950); - ``` - - -[benchmarks]: https://github.com/mtth/avsc/wiki/Benchmarks -[browser-support]: https://github.com/mtth/avsc/wiki#browser-support -[browserify]: http://browserify.org/ -[custom-long]: https://github.com/mtth/avsc/wiki/Advanced-usage#custom-long-types -[decoder-api]: https://github.com/mtth/avsc/wiki/API#class-blockdecoderopts -[home]: https://github.com/mtth/avsc/wiki -[idl]: https://avro.apache.org/docs/current/idl.html -[logical-types]: https://github.com/mtth/avsc/wiki/Advanced-usage#logical-types -[node.js]: https://nodejs.org/en/ -[readable-stream]: https://nodejs.org/api/stream.html#stream_class_stream_readable -[releases]: https://github.com/mtth/avsc/releases -[rpc]: https://github.com/mtth/avsc/wiki/Quickstart#services -[schema-evolution]: https://github.com/mtth/avsc/wiki/Advanced-usage#schema-evolution -[snappy]: https://avro.apache.org/docs/current/spec.html#snappy -[type-inference]: https://github.com/mtth/avsc/wiki/Advanced-usage#type-inference diff --git a/forward_engineering/modules/avsc/etc/browser/avsc-protocols.js b/forward_engineering/modules/avsc/etc/browser/avsc-protocols.js deleted file mode 100644 index ca3c8047..00000000 --- a/forward_engineering/modules/avsc/etc/browser/avsc-protocols.js +++ /dev/null @@ -1,20 +0,0 @@ -/* jshint browserify: true */ - -'use strict'; - -/** - * (Deprecated, in favor of `avsc-services`) optional entry point for browser - * builds. - * - * To use it: `require('avsc/etc/browser/avsc-protocols')`. - */ - -var avroServices = require('./avsc-services'), - utils = require('../../lib/utils'); - -module.exports = { - Protocol: avroServices.Service, - assemble: avroServices.assembleProtocol, -}; - -utils.copyOwnProperties(avroServices, module.exports); diff --git a/forward_engineering/modules/avsc/etc/browser/avsc-services.js b/forward_engineering/modules/avsc/etc/browser/avsc-services.js deleted file mode 100644 index 8a4b79dc..00000000 --- a/forward_engineering/modules/avsc/etc/browser/avsc-services.js +++ /dev/null @@ -1,33 +0,0 @@ -/* jshint browserify: true */ - -'use strict'; - -/** - * Optional entry point for browser builds. - * - * To use it: `require('avsc/etc/browser/avsc-services')`. - */ - -var avroTypes = require('./avsc-types'), - services = require('../../lib/services'), - specs = require('../../lib/specs'), - utils = require('../../lib/utils'); - -/** Slightly enhanced parsing, supporting IDL declarations. */ -function parse(any, opts) { - var schemaOrProtocol = specs.read(any); - return schemaOrProtocol.protocol - ? services.Service.forProtocol(schemaOrProtocol, opts) - : avroTypes.Type.forSchema(schemaOrProtocol, opts); -} - -module.exports = { - Service: services.Service, - assembleProtocol: specs.assembleProtocol, - discoverProtocol: services.discoverProtocol, - parse: parse, - readProtocol: specs.readProtocol, - readSchema: specs.readSchema, -}; - -utils.copyOwnProperties(avroTypes, module.exports); diff --git a/forward_engineering/modules/avsc/etc/browser/avsc-types.js b/forward_engineering/modules/avsc/etc/browser/avsc-types.js deleted file mode 100644 index 217081c4..00000000 --- a/forward_engineering/modules/avsc/etc/browser/avsc-types.js +++ /dev/null @@ -1,36 +0,0 @@ -/* jshint browserify: true */ - -'use strict'; - -/** - * Optional entry point for browser builds. - * - * To use it: `require('avsc/etc/browser/avsc-types')`. - */ - -var types = require('../../lib/types'); - -/** Basic parse method, only supporting JSON parsing. */ -function parse(any, opts) { - var schema; - if (typeof any == 'string') { - try { - schema = JSON.parse(any); - } catch (err) { - schema = any; - } - } else { - schema = any; - } - return types.Type.forSchema(schema, opts); -} - -module.exports = { - Type: types.Type, - parse: parse, - types: types.builtins, - // Deprecated exports (not using `util.deprecate` since it causes stack - // overflow errors in the browser). - combine: types.Type.forTypes, - infer: types.Type.forValue, -}; diff --git a/forward_engineering/modules/avsc/etc/browser/avsc.js b/forward_engineering/modules/avsc/etc/browser/avsc.js deleted file mode 100644 index 92d6c0a2..00000000 --- a/forward_engineering/modules/avsc/etc/browser/avsc.js +++ /dev/null @@ -1,118 +0,0 @@ -/* jshint browser: true, node: true */ - -'use strict'; - -/** - * Main browserify entry point. - * - * This version of the entry point adds a couple browser-specific utilities to - * read and write blobs. - */ - -var avroServices = require('./avsc-services'), - containers = require('../../lib/containers'), - utils = require('../../lib/utils'), - stream = require('stream'), - util = require('util'); - -/** Transform stream which lazily reads a blob's contents. */ -function BlobReader(blob, opts) { - stream.Readable.call(this); - opts = opts || {}; - - this._batchSize = opts.batchSize || 65536; - this._blob = blob; - this._pos = 0; -} -util.inherits(BlobReader, stream.Readable); - -BlobReader.prototype._read = function () { - var pos = this._pos; - if (pos >= this._blob.size) { - this.push(null); - return; - } - - this._pos += this._batchSize; - var blob = this._blob.slice(pos, this._pos, this._blob.type); - var reader = new FileReader(); - var self = this; - reader.addEventListener( - 'loadend', - function cb(evt) { - reader.removeEventListener('loadend', cb, false); - if (evt.error) { - self.emit('error', evt.error); - } else { - self.push(utils.bufferFrom(reader.result)); - } - }, - false, - ); - reader.readAsArrayBuffer(blob); -}; - -/** Transform stream which builds a blob from all data written to it. */ -function BlobWriter() { - stream.Transform.call(this, { readableObjectMode: true }); - this._bufs = []; -} -util.inherits(BlobWriter, stream.Transform); - -BlobWriter.prototype._transform = function (buf, encoding, cb) { - this._bufs.push(buf); - cb(); -}; - -BlobWriter.prototype._flush = function (cb) { - this.push(new Blob(this._bufs, { type: 'application/octet-binary' })); - cb(); -}; - -/** Read an Avro-container stored as a blob. */ -function createBlobDecoder(blob, opts) { - return new BlobReader(blob).pipe(new containers.streams.BlockDecoder(opts)); -} - -/** - * Store Avro values into an Avro-container blob. - * - * The returned stream will emit a single value, the blob, when ended. - */ -function createBlobEncoder(schema, opts) { - var encoder = new containers.streams.BlockEncoder(schema, opts); - var builder = new BlobWriter(); - encoder.pipe(builder); - return new stream.Duplex({ - objectMode: true, - read: function () { - // Not the fastest implementation, but it will only be called at most - // once (since the builder only ever emits a single value) so it'll do. - // It's also likely impractical to create very large blobs. - var val = builder.read(); - if (val) { - done(val); - } else { - builder.once('readable', done); - } - var self = this; - function done(val) { - self.push(val || builder.read()); - self.push(null); - } - }, - write: function (val, encoding, cb) { - return encoder.write(val, encoding, cb); - }, - }).on('finish', function () { - encoder.end(); - }); -} - -module.exports = { - createBlobDecoder: createBlobDecoder, - createBlobEncoder: createBlobEncoder, - streams: containers.streams, -}; - -utils.copyOwnProperties(avroServices, module.exports); diff --git a/forward_engineering/modules/avsc/etc/browser/lib/crypto.js b/forward_engineering/modules/avsc/etc/browser/lib/crypto.js deleted file mode 100644 index d3f81f48..00000000 --- a/forward_engineering/modules/avsc/etc/browser/lib/crypto.js +++ /dev/null @@ -1,184 +0,0 @@ -/* jshint browserify: true */ - -'use strict'; - -/** - * Shim to enable schema fingerprint computation. - * - * MD5 implementation originally from [1], used with permission from the - * author, and lightly edited. - * - * [1] http://www.myersdaily.org/joseph/javascript/md5-text.html - * - */ - -function createHash(algorithm) { - if (algorithm !== 'md5') { - throw new Error('only md5 is supported in the browser'); - } - return new Hash(); -} - -function Hash() { - this.data = undefined; -} -Hash.prototype.end = function (data) { - this.data = data; -}; -Hash.prototype.read = function () { - return md5(this.data); -}; - -function md5cycle(x, k) { - var a = x[0], - b = x[1], - c = x[2], - d = x[3]; - - a = ff(a, b, c, d, k[0], 7, -680876936); - d = ff(d, a, b, c, k[1], 12, -389564586); - c = ff(c, d, a, b, k[2], 17, 606105819); - b = ff(b, c, d, a, k[3], 22, -1044525330); - a = ff(a, b, c, d, k[4], 7, -176418897); - d = ff(d, a, b, c, k[5], 12, 1200080426); - c = ff(c, d, a, b, k[6], 17, -1473231341); - b = ff(b, c, d, a, k[7], 22, -45705983); - a = ff(a, b, c, d, k[8], 7, 1770035416); - d = ff(d, a, b, c, k[9], 12, -1958414417); - c = ff(c, d, a, b, k[10], 17, -42063); - b = ff(b, c, d, a, k[11], 22, -1990404162); - a = ff(a, b, c, d, k[12], 7, 1804603682); - d = ff(d, a, b, c, k[13], 12, -40341101); - c = ff(c, d, a, b, k[14], 17, -1502002290); - b = ff(b, c, d, a, k[15], 22, 1236535329); - - a = gg(a, b, c, d, k[1], 5, -165796510); - d = gg(d, a, b, c, k[6], 9, -1069501632); - c = gg(c, d, a, b, k[11], 14, 643717713); - b = gg(b, c, d, a, k[0], 20, -373897302); - a = gg(a, b, c, d, k[5], 5, -701558691); - d = gg(d, a, b, c, k[10], 9, 38016083); - c = gg(c, d, a, b, k[15], 14, -660478335); - b = gg(b, c, d, a, k[4], 20, -405537848); - a = gg(a, b, c, d, k[9], 5, 568446438); - d = gg(d, a, b, c, k[14], 9, -1019803690); - c = gg(c, d, a, b, k[3], 14, -187363961); - b = gg(b, c, d, a, k[8], 20, 1163531501); - a = gg(a, b, c, d, k[13], 5, -1444681467); - d = gg(d, a, b, c, k[2], 9, -51403784); - c = gg(c, d, a, b, k[7], 14, 1735328473); - b = gg(b, c, d, a, k[12], 20, -1926607734); - - a = hh(a, b, c, d, k[5], 4, -378558); - d = hh(d, a, b, c, k[8], 11, -2022574463); - c = hh(c, d, a, b, k[11], 16, 1839030562); - b = hh(b, c, d, a, k[14], 23, -35309556); - a = hh(a, b, c, d, k[1], 4, -1530992060); - d = hh(d, a, b, c, k[4], 11, 1272893353); - c = hh(c, d, a, b, k[7], 16, -155497632); - b = hh(b, c, d, a, k[10], 23, -1094730640); - a = hh(a, b, c, d, k[13], 4, 681279174); - d = hh(d, a, b, c, k[0], 11, -358537222); - c = hh(c, d, a, b, k[3], 16, -722521979); - b = hh(b, c, d, a, k[6], 23, 76029189); - a = hh(a, b, c, d, k[9], 4, -640364487); - d = hh(d, a, b, c, k[12], 11, -421815835); - c = hh(c, d, a, b, k[15], 16, 530742520); - b = hh(b, c, d, a, k[2], 23, -995338651); - - a = ii(a, b, c, d, k[0], 6, -198630844); - d = ii(d, a, b, c, k[7], 10, 1126891415); - c = ii(c, d, a, b, k[14], 15, -1416354905); - b = ii(b, c, d, a, k[5], 21, -57434055); - a = ii(a, b, c, d, k[12], 6, 1700485571); - d = ii(d, a, b, c, k[3], 10, -1894986606); - c = ii(c, d, a, b, k[10], 15, -1051523); - b = ii(b, c, d, a, k[1], 21, -2054922799); - a = ii(a, b, c, d, k[8], 6, 1873313359); - d = ii(d, a, b, c, k[15], 10, -30611744); - c = ii(c, d, a, b, k[6], 15, -1560198380); - b = ii(b, c, d, a, k[13], 21, 1309151649); - a = ii(a, b, c, d, k[4], 6, -145523070); - d = ii(d, a, b, c, k[11], 10, -1120210379); - c = ii(c, d, a, b, k[2], 15, 718787259); - b = ii(b, c, d, a, k[9], 21, -343485551); - - x[0] = add32(a, x[0]); - x[1] = add32(b, x[1]); - x[2] = add32(c, x[2]); - x[3] = add32(d, x[3]); -} - -function cmn(q, a, b, x, s, t) { - a = add32(add32(a, q), add32(x, t)); - return add32((a << s) | (a >>> (32 - s)), b); -} - -function ff(a, b, c, d, x, s, t) { - return cmn((b & c) | (~b & d), a, b, x, s, t); -} - -function gg(a, b, c, d, x, s, t) { - return cmn((b & d) | (c & ~d), a, b, x, s, t); -} - -function hh(a, b, c, d, x, s, t) { - return cmn(b ^ c ^ d, a, b, x, s, t); -} - -function ii(a, b, c, d, x, s, t) { - return cmn(c ^ (b | ~d), a, b, x, s, t); -} - -function md51(s) { - var n = s.length, - state = [1732584193, -271733879, -1732584194, 271733878], - i; - for (i = 64; i <= s.length; i += 64) { - md5cycle(state, md5blk(s.substring(i - 64, i))); - } - - s = s.substring(i - 64); - var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - for (i = 0; i < s.length; i++) { - tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3); - } - tail[i >> 2] |= 0x80 << (i % 4 << 3); - if (i > 55) { - md5cycle(state, tail); - for (i = 0; i < 16; i++) { - tail[i] = 0; - } - } - tail[14] = n * 8; - md5cycle(state, tail); - return state; -} - -function md5blk(s) { - var md5blks = [], - i; - for (i = 0; i < 64; i += 4) { - md5blks[i >> 2] = - s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24); - } - return md5blks; -} - -function md5(s) { - var arr = md51(s); - var buf = Buffer.alloc ? Buffer.alloc(16) : new Buffer(16); - var i; - for (i = 0; i < 4; i++) { - buf.writeIntLE(arr[i], i * 4, 4); - } - return buf; -} - -function add32(a, b) { - return (a + b) & 0xffffffff; -} - -module.exports = { - createHash: createHash, -}; diff --git a/forward_engineering/modules/avsc/etc/browser/lib/files.js b/forward_engineering/modules/avsc/etc/browser/lib/files.js deleted file mode 100644 index 8292b490..00000000 --- a/forward_engineering/modules/avsc/etc/browser/lib/files.js +++ /dev/null @@ -1,32 +0,0 @@ -/* jshint node: true */ - -'use strict'; - -/** Shim without file-system operations. */ - -function createError() { - return new Error('unsupported in the browser'); -} - -function createImportHook() { - return function (fpath, kind, cb) { - cb(createError()); - }; -} - -function createSyncImportHook() { - return function () { - throw createError(); - }; -} - -module.exports = { - createImportHook: createImportHook, - createSyncImportHook: createSyncImportHook, - existsSync: function () { - return false; - }, - readFileSync: function () { - throw createError(); - }, -}; diff --git a/forward_engineering/modules/avsc/lib/containers.js b/forward_engineering/modules/avsc/lib/containers.js deleted file mode 100644 index 83791592..00000000 --- a/forward_engineering/modules/avsc/lib/containers.js +++ /dev/null @@ -1,616 +0,0 @@ -/* jshint node: true */ - -// TODO: Add streams which prefix each record with its length. - -'use strict'; - -/** - * This module defines custom streams to write and read Avro files. - * - * In particular, the `Block{En,De}coder` streams are able to deal with Avro - * container files. None of the streams below depend on the filesystem however, - * this way they can also be used in the browser (for example to parse HTTP - * responses). - */ - -var types = require('./types'), - utils = require('./utils'), - stream = require('stream'), - util = require('util'), - zlib = require('zlib'); - -var OPTS = { namespace: 'org.apache.avro.file' }; - -var LONG_TYPE = types.Type.forSchema('long', OPTS); - -var MAP_BYTES_TYPE = types.Type.forSchema({ type: 'map', values: 'bytes' }, OPTS); - -var HEADER_TYPE = types.Type.forSchema( - { - name: 'Header', - type: 'record', - fields: [ - { name: 'magic', type: { type: 'fixed', name: 'Magic', size: 4 } }, - { name: 'meta', type: MAP_BYTES_TYPE }, - { name: 'sync', type: { type: 'fixed', name: 'Sync', size: 16 } }, - ], - }, - OPTS, -); - -var BLOCK_TYPE = types.Type.forSchema( - { - name: 'Block', - type: 'record', - fields: [ - { name: 'count', type: 'long' }, - { name: 'data', type: 'bytes' }, - { name: 'sync', type: 'Sync' }, - ], - }, - OPTS, -); - -// First 4 bytes of an Avro object container file. -var MAGIC_BYTES = utils.bufferFrom('Obj\x01'); - -// Convenience. -var f = util.format; -var Tap = utils.Tap; - -/** Duplex stream for decoding fragments. */ -function RawDecoder(schema, opts) { - opts = opts || {}; - - var noDecode = !!opts.noDecode; - stream.Duplex.call(this, { - readableObjectMode: !noDecode, - allowHalfOpen: false, - }); - - this._type = types.Type.forSchema(schema); - this._tap = new Tap(utils.newBuffer(0)); - this._writeCb = null; - this._needPush = false; - this._readValue = createReader(noDecode, this._type); - this._finished = false; - - this.on('finish', function () { - this._finished = true; - this._read(); - }); -} -util.inherits(RawDecoder, stream.Duplex); - -RawDecoder.prototype._write = function (chunk, encoding, cb) { - // Store the write callback and call it when we are done decoding all records - // in this chunk. If we call it right away, we risk loading the entire input - // in memory. We only need to store the latest callback since the stream API - // guarantees that `_write` won't be called again until we call the previous. - this._writeCb = cb; - - var tap = this._tap; - tap.buf = Buffer.concat([tap.buf.slice(tap.pos), chunk]); - tap.pos = 0; - if (this._needPush) { - this._needPush = false; - this._read(); - } -}; - -RawDecoder.prototype._read = function () { - this._needPush = false; - - var tap = this._tap; - var pos = tap.pos; - var val = this._readValue(tap); - if (tap.isValid()) { - this.push(val); - } else if (!this._finished) { - tap.pos = pos; - this._needPush = true; - if (this._writeCb) { - // This should only ever be false on the first read, and only if it - // happens before the first write. - this._writeCb(); - } - } else { - this.push(null); - } -}; - -/** Duplex stream for decoding object container files. */ -function BlockDecoder(opts) { - opts = opts || {}; - - var noDecode = !!opts.noDecode; - stream.Duplex.call(this, { - allowHalfOpen: true, // For async decompressors. - readableObjectMode: !noDecode, - }); - - this._rType = opts.readerSchema !== undefined ? types.Type.forSchema(opts.readerSchema) : undefined; - this._wType = null; - this._codecs = opts.codecs; - this._codec = undefined; - this._parseHook = opts.parseHook; - this._tap = new Tap(utils.newBuffer(0)); - this._blockTap = new Tap(utils.newBuffer(0)); - this._syncMarker = null; - this._readValue = null; - this._noDecode = noDecode; - this._queue = new utils.OrderedQueue(); - this._decompress = null; // Decompression function. - this._index = 0; // Next block index. - this._remaining = undefined; // In the current block. - this._needPush = false; - this._finished = false; - - this.on('finish', function () { - this._finished = true; - if (this._needPush) { - this._read(); - } - }); -} -util.inherits(BlockDecoder, stream.Duplex); - -BlockDecoder.defaultCodecs = function () { - return { - 'null': function (buf, cb) { - cb(null, buf); - }, - 'deflate': zlib.inflateRaw, - }; -}; - -BlockDecoder.getDefaultCodecs = BlockDecoder.defaultCodecs; - -BlockDecoder.prototype._decodeHeader = function () { - var tap = this._tap; - if (tap.buf.length < MAGIC_BYTES.length) { - // Wait until more data arrives. - return false; - } - - if (!MAGIC_BYTES.equals(tap.buf.slice(0, MAGIC_BYTES.length))) { - this.emit('error', new Error('invalid magic bytes')); - return false; - } - - var header = HEADER_TYPE._read(tap); - if (!tap.isValid()) { - return false; - } - - this._codec = (header.meta['avro.codec'] || 'null').toString(); - var codecs = this._codecs || BlockDecoder.getDefaultCodecs(); - this._decompress = codecs[this._codec]; - if (!this._decompress) { - this.emit('error', new Error(f('unknown codec: %s', this._codec))); - return; - } - - try { - var schema = JSON.parse(header.meta['avro.schema'].toString()); - if (this._parseHook) { - schema = this._parseHook(schema); - } - this._wType = types.Type.forSchema(schema); - } catch (err) { - this.emit('error', err); - return; - } - - this._readValue = createReader(this._noDecode, this._wType, this._rType); - this._syncMarker = header.sync; - this.emit('metadata', this._wType, this._codec, header); - return true; -}; - -BlockDecoder.prototype._write = function (chunk, encoding, cb) { - var tap = this._tap; - tap.buf = Buffer.concat([tap.buf, chunk]); - tap.pos = 0; - - if (!this._decodeHeader()) { - process.nextTick(cb); - return; - } - - // We got the header, switch to block decoding mode. Also, call it directly - // in case we already have all the data (in which case `_write` wouldn't get - // called anymore). - this._write = this._writeChunk; - this._write(utils.newBuffer(0), encoding, cb); -}; - -BlockDecoder.prototype._writeChunk = function (chunk, encoding, cb) { - var tap = this._tap; - tap.buf = Buffer.concat([tap.buf.slice(tap.pos), chunk]); - tap.pos = 0; - - var nBlocks = 1; - var block; - while ((block = tryReadBlock(tap))) { - if (!this._syncMarker.equals(block.sync)) { - this.emit('error', new Error('invalid sync marker')); - return; - } - nBlocks++; - this._decompress(block.data, this._createBlockCallback(block.count, chunkCb)); - } - chunkCb(); - - function chunkCb() { - if (!--nBlocks) { - cb(); - } - } -}; - -BlockDecoder.prototype._createBlockCallback = function (count, cb) { - var self = this; - var index = this._index++; - - return function (cause, data) { - if (cause) { - var err = new Error(f('%s codec decompression error', self._codec)); - err.cause = cause; - self.emit('error', err); - cb(); - } else { - self._queue.push(new BlockData(index, data, cb, count)); - if (self._needPush) { - self._read(); - } - } - }; -}; - -BlockDecoder.prototype._read = function () { - this._needPush = false; - - var tap = this._blockTap; - if (!this._remaining) { - var data = this._queue.pop(); - if (!data || !data.count) { - if (this._finished) { - this.push(null); - } else { - this._needPush = true; - } - if (data) { - data.cb(); - } - return; // Wait for more data. - } - data.cb(); - this._remaining = data.count; - tap.buf = data.buf; - tap.pos = 0; - } - - this._remaining--; - this.push(this._readValue(tap)); // The read is guaranteed valid. -}; - -/** Duplex stream for encoding. */ -function RawEncoder(schema, opts) { - opts = opts || {}; - - stream.Transform.call(this, { - writableObjectMode: true, - allowHalfOpen: false, - }); - - this._type = types.Type.forSchema(schema); - this._writeValue = function (tap, val) { - try { - this._type._write(tap, val); - } catch (err) { - this.emit('error', err); - } - }; - this._tap = new Tap(utils.newBuffer(opts.batchSize || 65536)); -} -util.inherits(RawEncoder, stream.Transform); - -RawEncoder.prototype._transform = function (val, encoding, cb) { - var tap = this._tap; - var buf = tap.buf; - var pos = tap.pos; - - this._writeValue(tap, val); - if (!tap.isValid()) { - if (pos) { - // Emit any valid data. - this.push(copyBuffer(tap.buf, 0, pos)); - } - var len = tap.pos - pos; - if (len > buf.length) { - // Not enough space for last written object, need to resize. - tap.buf = utils.newBuffer(2 * len); - } - tap.pos = 0; - this._writeValue(tap, val); // Rewrite last failed write. - } - - cb(); -}; - -RawEncoder.prototype._flush = function (cb) { - var tap = this._tap; - var pos = tap.pos; - if (pos) { - // This should only ever be false if nothing is written to the stream. - this.push(tap.buf.slice(0, pos)); - } - cb(); -}; - -/** - * Duplex stream to write object container files. - * - * @param schema - * @param opts {Object} - * - * + `blockSize`, uncompressed. - * + `codec` - * + `codecs` - * + `metadata`` - * + `noCheck` - * + `omitHeader`, useful to append to an existing block file. - */ -function BlockEncoder(schema, opts) { - opts = opts || {}; - - stream.Duplex.call(this, { - allowHalfOpen: true, // To support async compressors. - writableObjectMode: true, - }); - - var type; - if (types.Type.isType(schema)) { - type = schema; - schema = undefined; - } else { - // Keep full schema to be able to write it to the header later. - type = types.Type.forSchema(schema); - } - - this._schema = schema; - this._type = type; - this._writeValue = function (tap, val) { - try { - this._type._write(tap, val); - } catch (err) { - this.emit('error', err); - } - }; - this._blockSize = opts.blockSize || 65536; - this._tap = new Tap(utils.newBuffer(this._blockSize)); - this._codecs = opts.codecs; - this._codec = opts.codec || 'null'; - this._blockCount = 0; - this._syncMarker = opts.syncMarker || new utils.Lcg().nextBuffer(16); - this._queue = new utils.OrderedQueue(); - this._pending = 0; - this._finished = false; - this._needHeader = false; - this._needPush = false; - - this._metadata = opts.metadata || {}; - if (!MAP_BYTES_TYPE.isValid(this._metadata)) { - throw new Error('invalid metadata'); - } - - var codec = this._codec; - this._compress = (this._codecs || BlockEncoder.getDefaultCodecs())[codec]; - if (!this._compress) { - throw new Error(f('unsupported codec: %s', codec)); - } - - if (opts.omitHeader !== undefined) { - // Legacy option. - opts.writeHeader = opts.omitHeader ? 'never' : 'auto'; - } - switch (opts.writeHeader) { - case false: - case 'never': - break; - case undefined: // Backwards-compatibility (eager default would be better). - case 'auto': - this._needHeader = true; - break; - default: - this._writeHeader(); - } - - this.on('finish', function () { - this._finished = true; - if (this._blockCount) { - this._flushChunk(); - } else if (this._finished && this._needPush) { - // We don't need to check `_isPending` since `_blockCount` is always - // positive after the first flush. - this.push(null); - } - }); -} -util.inherits(BlockEncoder, stream.Duplex); - -BlockEncoder.defaultCodecs = function () { - return { - 'null': function (buf, cb) { - cb(null, buf); - }, - 'deflate': zlib.deflateRaw, - }; -}; - -BlockEncoder.getDefaultCodecs = BlockEncoder.defaultCodecs; - -BlockEncoder.prototype._writeHeader = function () { - var schema = JSON.stringify(this._schema ? this._schema : this._type.getSchema({ exportAttrs: true })); - var meta = utils.copyOwnProperties( - this._metadata, - { 'avro.schema': utils.bufferFrom(schema), 'avro.codec': utils.bufferFrom(this._codec) }, - true, // Overwrite. - ); - var Header = HEADER_TYPE.getRecordConstructor(); - var header = new Header(MAGIC_BYTES, meta, this._syncMarker); - this.push(header.toBuffer()); -}; - -BlockEncoder.prototype._write = function (val, encoding, cb) { - if (this._needHeader) { - this._writeHeader(); - this._needHeader = false; - } - - var tap = this._tap; - var pos = tap.pos; - var flushing = false; - - this._writeValue(tap, val); - if (!tap.isValid()) { - if (pos) { - this._flushChunk(pos, cb); - flushing = true; - } - var len = tap.pos - pos; - if (len > this._blockSize) { - // Not enough space for last written object, need to resize. - this._blockSize = len * 2; - } - tap.buf = utils.newBuffer(this._blockSize); - tap.pos = 0; - this._writeValue(tap, val); // Rewrite last failed write. - } - this._blockCount++; - - if (!flushing) { - cb(); - } -}; - -BlockEncoder.prototype._flushChunk = function (pos, cb) { - var tap = this._tap; - pos = pos || tap.pos; - this._compress(tap.buf.slice(0, pos), this._createBlockCallback(cb)); - this._blockCount = 0; -}; - -BlockEncoder.prototype._read = function () { - var self = this; - var data = this._queue.pop(); - if (!data) { - if (this._finished && !this._pending) { - process.nextTick(function () { - self.push(null); - }); - } else { - this._needPush = true; - } - return; - } - - this.push(LONG_TYPE.toBuffer(data.count, true)); - this.push(LONG_TYPE.toBuffer(data.buf.length, true)); - this.push(data.buf); - this.push(this._syncMarker); - - if (!this._finished) { - data.cb(); - } -}; - -BlockEncoder.prototype._createBlockCallback = function (cb) { - var self = this; - var index = this._index++; - var count = this._blockCount; - this._pending++; - - return function (cause, data) { - if (cause) { - var err = new Error(f('%s codec compression error', self._codec)); - err.cause = cause; - self.emit('error', err); - return; - } - self._pending--; - self._queue.push(new BlockData(index, data, cb, count)); - if (self._needPush) { - self._needPush = false; - self._read(); - } - }; -}; - -// Helpers. - -/** - * An indexed block. - * - * This can be used to preserve block order since compression and decompression - * can cause some some blocks to be returned out of order. - */ -function BlockData(index, buf, cb, count) { - this.index = index; - this.buf = buf; - this.cb = cb; - this.count = count | 0; -} - -/** Maybe get a block. */ -function tryReadBlock(tap) { - var pos = tap.pos; - var block = BLOCK_TYPE._read(tap); - if (!tap.isValid()) { - tap.pos = pos; - return null; - } - return block; -} - -/** Create bytes consumer, either reading or skipping records. */ -function createReader(noDecode, writerType, readerType) { - if (noDecode) { - return (function (skipper) { - return function (tap) { - var pos = tap.pos; - skipper(tap); - return tap.buf.slice(pos, tap.pos); - }; - })(writerType._skip); - } else if (readerType) { - var resolver = readerType.createResolver(writerType); - return function (tap) { - return resolver._read(tap); - }; - } else { - return function (tap) { - return writerType._read(tap); - }; - } -} - -/** Copy a buffer. This avoids creating a slice of the original buffer. */ -function copyBuffer(buf, pos, len) { - var copy = utils.newBuffer(len); - buf.copy(copy, 0, pos, pos + len); - return copy; -} - -module.exports = { - BLOCK_TYPE: BLOCK_TYPE, // For tests. - HEADER_TYPE: HEADER_TYPE, // Idem. - MAGIC_BYTES: MAGIC_BYTES, // Idem. - streams: { - BlockDecoder: BlockDecoder, - BlockEncoder: BlockEncoder, - RawDecoder: RawDecoder, - RawEncoder: RawEncoder, - }, -}; diff --git a/forward_engineering/modules/avsc/lib/files.js b/forward_engineering/modules/avsc/lib/files.js deleted file mode 100644 index 08bf1f56..00000000 --- a/forward_engineering/modules/avsc/lib/files.js +++ /dev/null @@ -1,57 +0,0 @@ -/* jshint node: true */ - -'use strict'; - -/** - * Filesystem specifics. - * - * This module contains functions only used by node.js. It is shimmed by - * another module when `avsc` is required from `browserify`. - */ - -var fs = require('fs'), - path = require('path'); - -/** Default (asynchronous) file loading function for assembling IDLs. */ -function createImportHook() { - var imports = {}; - return function (fpath, kind, cb) { - fpath = path.resolve(fpath); - if (imports[fpath]) { - // Already imported, return nothing to avoid duplicating attributes. - process.nextTick(cb); - return; - } - imports[fpath] = true; - fs.readFile(fpath, { encoding: 'utf8' }, cb); - }; -} - -/** - * Synchronous file loading function for assembling IDLs. - * - * This is only for internal use (inside `specs.parse`). The returned - * hook should only be called on paths that are guaranteed to exist (where - * `fs.readFileSync` will not throw, otherwise the calling `assemble` call will - * throw rather than return the error to the callback). - */ -function createSyncImportHook() { - var imports = {}; - return function (fpath, kind, cb) { - fpath = path.resolve(fpath); - if (imports[fpath]) { - cb(); - } else { - imports[fpath] = true; - cb(null, fs.readFileSync(fpath, { encoding: 'utf8' })); - } - }; -} - -module.exports = { - createImportHook: createImportHook, - createSyncImportHook: createSyncImportHook, - // Proxy a few methods to better shim them for browserify. - existsSync: fs.existsSync, - readFileSync: fs.readFileSync, -}; diff --git a/forward_engineering/modules/avsc/lib/index.js b/forward_engineering/modules/avsc/lib/index.js deleted file mode 100644 index 03d4ba16..00000000 --- a/forward_engineering/modules/avsc/lib/index.js +++ /dev/null @@ -1,103 +0,0 @@ -/* jshint node: true */ - -'use strict'; - -/** - * Node.js entry point (see `etc/browser/` for browserify's entry points). - * - * It also adds Node.js specific functionality (for example a few convenience - * functions to read Avro files from the local filesystem). - */ - -var containers = require('./containers'), - services = require('./services'), - specs = require('./specs'), - types = require('./types'), - utils = require('./utils'), - fs = require('fs'), - util = require('util'); - -/** Parse a schema and return the corresponding type or service. */ -function parse(any, opts) { - var schemaOrProtocol = specs.read(any); - return schemaOrProtocol.protocol - ? services.Service.forProtocol(schemaOrProtocol, opts) - : types.Type.forSchema(schemaOrProtocol, opts); -} - -/** Extract a container file's header synchronously. */ -function extractFileHeader(path, opts) { - opts = opts || {}; - - var decode = opts.decode === undefined ? true : !!opts.decode; - var size = Math.max(opts.size || 4096, 4); - var fd = fs.openSync(path, 'r'); - var buf = utils.newBuffer(size); - var pos = 0; - var tap = new utils.Tap(buf); - var header = null; - - while (pos < 4) { - // Make sure we have enough to check the magic bytes. - pos += fs.readSync(fd, buf, pos, size - pos); - } - if (containers.MAGIC_BYTES.equals(buf.slice(0, 4))) { - do { - header = containers.HEADER_TYPE._read(tap); - } while (!isValid()); - if (decode !== false) { - var meta = header.meta; - meta['avro.schema'] = JSON.parse(meta['avro.schema'].toString()); - if (meta['avro.codec'] !== undefined) { - meta['avro.codec'] = meta['avro.codec'].toString(); - } - } - } - fs.closeSync(fd); - return header; - - function isValid() { - if (tap.isValid()) { - return true; - } - var len = 2 * tap.buf.length; - var buf = utils.newBuffer(len); - len = fs.readSync(fd, buf, 0, len); - tap.buf = Buffer.concat([tap.buf, buf]); - tap.pos = 0; - return false; - } -} - -/** Readable stream of records from a local Avro file. */ -function createFileDecoder(path, opts) { - return fs.createReadStream(path).pipe(new containers.streams.BlockDecoder(opts)); -} - -/** Writable stream of records to a local Avro file. */ -function createFileEncoder(path, schema, opts) { - var encoder = new containers.streams.BlockEncoder(schema, opts); - encoder.pipe(fs.createWriteStream(path, { defaultEncoding: 'binary' })); - return encoder; -} - -module.exports = { - Service: services.Service, - Type: types.Type, - assembleProtocol: specs.assembleProtocol, - createFileDecoder: createFileDecoder, - createFileEncoder: createFileEncoder, - discoverProtocol: services.discoverProtocol, - extractFileHeader: extractFileHeader, - parse: parse, - readProtocol: specs.readProtocol, - readSchema: specs.readSchema, - streams: containers.streams, - types: types.builtins, - // Deprecated exports. - Protocol: services.Service, - assemble: util.deprecate(specs.assembleProtocol, 'use `assembleProtocol` instead'), - combine: util.deprecate(types.Type.forTypes, 'use `Type.forTypes` intead'), - infer: util.deprecate(types.Type.forValue, 'use `Type.forValue` instead'), - errorsCollector: types.errorsCollector, -}; diff --git a/forward_engineering/modules/avsc/lib/services.js b/forward_engineering/modules/avsc/lib/services.js deleted file mode 100644 index dfc04b2b..00000000 --- a/forward_engineering/modules/avsc/lib/services.js +++ /dev/null @@ -1,2400 +0,0 @@ -/* jshint node: true */ - -// TODO: Add broadcast option to client `_emitMessage`, accessible for one-way -// messages. -// TODO: Add `server.mount` method to allow combining servers. The API is as -// follows: a mounted server's (i.e. the method's argument) handlers have lower -// precedence than the original server (i.e. `this`); the mounted server's -// middlewares are only invoked for its handlers. -// TODO: Change `objectMode` client and server channel option to `encoding` -// (accepting `'netty'`, `'standard'`, and `null` or `undefined`). Perhaps also -// expose encoders (API TBD). - -'use strict'; - -/** This module implements Avro's IPC/RPC logic. */ - -var types = require('./types'), - utils = require('./utils'), - events = require('events'), - stream = require('stream'), - util = require('util'); - -// A few convenience imports. -var Tap = utils.Tap; -var Type = types.Type; -var debug = util.debuglog('avsc:services'); -var f = util.format; - -// Various useful types. We instantiate options once, to share the registry. -var OPTS = { namespace: 'org.apache.avro.ipc' }; - -var BOOLEAN_TYPE = Type.forSchema('boolean', OPTS); - -var MAP_BYTES_TYPE = Type.forSchema({ type: 'map', values: 'bytes' }, OPTS); - -var STRING_TYPE = Type.forSchema('string', OPTS); - -var HANDSHAKE_REQUEST_TYPE = Type.forSchema( - { - name: 'HandshakeRequest', - type: 'record', - fields: [ - { name: 'clientHash', type: { name: 'MD5', type: 'fixed', size: 16 } }, - { name: 'clientProtocol', type: ['null', 'string'], 'default': null }, - { name: 'serverHash', type: 'MD5' }, - { name: 'meta', type: ['null', MAP_BYTES_TYPE], 'default': null }, - ], - }, - OPTS, -); - -var HANDSHAKE_RESPONSE_TYPE = Type.forSchema( - { - name: 'HandshakeResponse', - type: 'record', - fields: [ - { - name: 'match', - type: { - name: 'HandshakeMatch', - type: 'enum', - symbols: ['BOTH', 'CLIENT', 'NONE'], - }, - }, - { name: 'serverProtocol', type: ['null', 'string'], 'default': null }, - { name: 'serverHash', type: ['null', 'MD5'], 'default': null }, - { name: 'meta', type: ['null', MAP_BYTES_TYPE], 'default': null }, - ], - }, - OPTS, -); - -// Prefix used to differentiate between messages when sharing a stream. This -// length should be smaller than 16. The remainder is used for disambiguating -// between concurrent messages (the current value, 16, therefore supports ~64k -// concurrent messages). -var PREFIX_LENGTH = 16; - -// Internal message, used to check protocol compatibility. -var PING_MESSAGE = new Message( - '', // Empty name (invalid for other "normal" messages). - Type.forSchema({ name: 'PingRequest', type: 'record', fields: [] }, OPTS), - Type.forSchema(['string'], OPTS), - Type.forSchema('null', OPTS), -); - -/** An Avro message, containing its request, response, etc. */ -function Message(name, reqType, errType, resType, oneWay, doc) { - this.name = name; - if (!Type.isType(reqType, 'record')) { - throw new Error('invalid request type'); - } - this.requestType = reqType; - if (!Type.isType(errType, 'union') || !Type.isType(errType.getTypes()[0], 'string')) { - throw new Error('invalid error type'); - } - this.errorType = errType; - if (oneWay) { - if (!Type.isType(resType, 'null') || errType.getTypes().length > 1) { - throw new Error('inapplicable one-way parameter'); - } - } - this.responseType = resType; - this.oneWay = !!oneWay; - this.doc = doc !== undefined ? '' + doc : undefined; - Object.freeze(this); -} - -Message.forSchema = function (name, schema, opts) { - opts = opts || {}; - if (!types.isValidName(name)) { - throw new Error(f('invalid message name: %s', name)); - } - // We use a record with a placeholder name here (the user might have set - // `noAnonymousTypes`, so we can't use an anonymous one). We remove it from - // the registry afterwards to avoid exposing it outside. - if (!Array.isArray(schema.request)) { - throw new Error(f('invalid message request: %s', name)); - } - var recordName = f('%s.%sRequest', OPTS.namespace, utils.capitalize(name)); - var reqType = Type.forSchema( - { - name: recordName, - type: 'record', - namespace: opts.namespace || '', // Don't leak request namespace. - fields: schema.request, - }, - opts, - ); - delete opts.registry[recordName]; - if (!schema.response) { - throw new Error(f('invalid message response: %s', name)); - } - var resType = Type.forSchema(schema.response, opts); - if (schema.errors !== undefined && !Array.isArray(schema.errors)) { - throw new Error(f('invalid message errors: %s', name)); - } - var errType = Type.forSchema(['string'].concat(schema.errors || []), opts); - var oneWay = !!schema['one-way']; - return new Message(name, reqType, errType, resType, oneWay, schema.doc); -}; - -Message.prototype.schema = Type.prototype.getSchema; - -Message.prototype._attrs = function (opts) { - var reqSchema = this.requestType._attrs(opts); - var schema = { - request: reqSchema.fields, - response: this.responseType._attrs(opts), - }; - var msgDoc = this.doc; - if (msgDoc !== undefined) { - schema.doc = msgDoc; - } - var errSchema = this.errorType._attrs(opts); - if (errSchema.length > 1) { - schema.errors = errSchema.slice(1); - } - if (this.oneWay) { - schema['one-way'] = true; - } - return schema; -}; - -// Deprecated. - -utils.addDeprecatedGetters(Message, ['name', 'errorType', 'requestType', 'responseType']); - -Message.prototype.isOneWay = util.deprecate(function () { - return this.oneWay; -}, 'use `.oneWay` directly instead of `.isOneWay()`'); - -/** - * An Avro RPC service. - * - * This constructor shouldn't be called directly, but via the - * `Service.forProtocol` method. This function performs little logic to better - * support efficient copy. - */ -function Service(name, messages, types, ptcl, server) { - if (typeof name != 'string') { - // Let's be helpful in case this class is instantiated directly. - return Service.forProtocol(name, messages); - } - - this.name = name; - this._messagesByName = messages || {}; - this.messages = Object.freeze(utils.objectValues(this._messagesByName)); - - this._typesByName = types || {}; - this.types = Object.freeze(utils.objectValues(this._typesByName)); - - this.protocol = ptcl; - // We cache a string rather than a buffer to not retain an entire slab. - this._hashStr = utils.getHash(JSON.stringify(ptcl)).toString('binary'); - this.doc = ptcl.doc ? '' + ptcl.doc : undefined; - - // We add a server to each protocol for backwards-compatibility (to allow the - // use of `protocol.on`). This covers all cases except the use of the - // `strictErrors` option, which requires moving to the new API. - this._server = server || this.createServer({ silent: true }); - Object.freeze(this); -} - -Service.Client = Client; - -Service.Server = Server; - -Service.compatible = function (clientSvc, serverSvc) { - try { - createReaders(clientSvc, serverSvc); - } catch (err) { - return false; - } - return true; -}; - -Service.forProtocol = function (ptcl, opts) { - opts = opts || {}; - - var name = ptcl.protocol; - if (!name) { - throw new Error('missing protocol name'); - } - if (ptcl.namespace !== undefined) { - opts.namespace = ptcl.namespace; - } else { - var match = /^(.*)\.[^.]+$/.exec(name); - if (match) { - opts.namespace = match[1]; - } - } - name = types.qualify(name, opts.namespace); - - if (ptcl.types) { - ptcl.types.forEach(function (obj) { - Type.forSchema(obj, opts); - }); - } - var msgs; - if (ptcl.messages) { - msgs = {}; - Object.keys(ptcl.messages).forEach(function (key) { - msgs[key] = Message.forSchema(key, ptcl.messages[key], opts); - }); - } - - return new Service(name, msgs, opts.registry, ptcl); -}; - -Service.isService = function (any) { - // Not fool-proof but likely sufficient. - return !!any && any.hasOwnProperty('_hashStr'); -}; - -Service.prototype.createClient = function (opts) { - var client = new Client(this, opts); - process.nextTick(function () { - // We delay this processing such that we can attach handlers to the client - // before any channels get created. - if (opts && opts.server) { - // Convenience in-memory client. This can be useful to make requests - // relatively efficiently to an in-process server. Note that it is still - // is less efficient than direct method calls (because of the - // serialization, which does provide "type-safety" though). - var obj = { objectMode: true }; - var pts = [new stream.PassThrough(obj), new stream.PassThrough(obj)]; - opts.server.createChannel({ readable: pts[0], writable: pts[1] }, obj); - client.createChannel({ readable: pts[1], writable: pts[0] }, obj); - } else if (opts && opts.transport) { - // Convenience functionality for the common single channel use-case: we - // add a single channel using default options to the client. - client.createChannel(opts.transport); - } - }); - return client; -}; - -Service.prototype.createServer = function (opts) { - return new Server(this, opts); -}; - -Object.defineProperty(Service.prototype, 'hash', { - enumerable: true, - get: function () { - return utils.bufferFrom(this._hashStr, 'binary'); - }, -}); - -Service.prototype.message = function (name) { - return this._messagesByName[name]; -}; - -Service.prototype.type = function (name) { - return this._typesByName[name]; -}; - -Service.prototype.inspect = function () { - return f('', this.name); -}; - -// Deprecated methods. - -utils.addDeprecatedGetters(Service, ['message', 'messages', 'name', 'type', 'types']); - -Service.prototype.createEmitter = util.deprecate(function (transport, opts) { - opts = opts || {}; - var client = this.createClient({ - cache: opts.cache, - buffering: false, - strictTypes: opts.strictErrors, - timeout: opts.timeout, - }); - var channel = client.createChannel(transport, opts); - forwardErrors(client, channel); - return channel; -}, 'use `.createClient()` instead of `.createEmitter()`'); - -Service.prototype.createListener = util.deprecate(function (transport, opts) { - if (opts && opts.strictErrors) { - throw new Error('use `.createServer()` to support strict errors'); - } - return this._server.createChannel(transport, opts); -}, 'use `.createServer().createChannel()` instead of `.createListener()`'); - -Service.prototype.emit = util.deprecate(function (name, req, channel, cb) { - if (!channel || !this.equals(channel.client._svc$)) { - throw new Error('invalid emitter'); - } - - var client = channel.client; - // In case the method is overridden. - Client.prototype.emitMessage.call(client, name, req, cb && cb.bind(this)); - return channel.getPending(); -}, 'create a client via `.createClient()` to emit messages instead of `.emit()`'); - -Service.prototype.equals = util.deprecate(function (any) { - return Service.isService(any) && this.getFingerprint().equals(any.getFingerprint()); -}, 'equality testing is deprecated, compare the `.protocol`s instead'); - -Service.prototype.getFingerprint = util.deprecate(function (algorithm) { - return utils.getHash(JSON.stringify(this.protocol), algorithm); -}, 'use `.hash` instead of `.getFingerprint()`'); - -Service.prototype.getSchema = util.deprecate(Type.prototype.getSchema, 'use `.protocol` instead of `.getSchema()`'); - -Service.prototype.on = util.deprecate(function (name, handler) { - var self = this; // This protocol. - this._server.onMessage(name, function (req, cb) { - return handler.call(self, req, this.channel, cb); - }); - return this; -}, 'use `.createServer().onMessage()` instead of `.on()`'); - -Service.prototype.subprotocol = util.deprecate(function () { - var parent = this._server; - var opts = { strictTypes: parent._strict, cache: parent._cache }; - var server = new Server(parent.service, opts); - server._handlers = Object.create(parent._handlers); - return new Service(this.name, this._messagesByName, this._typesByName, this.protocol, server); -}, '`.subprotocol()` will be removed in 5.1'); - -Service.prototype._attrs = function (opts) { - var ptcl = { protocol: this.name }; - - var types = []; - this.types.forEach(function (t) { - if (t.getName() === undefined) { - // Don't include any unnamed types (e.g. primitives). - return; - } - var typeSchema = t._attrs(opts); - if (typeof typeSchema != 'string') { - // Some of the named types might already have been defined in a - // previous type, in this case we don't include its reference. - types.push(typeSchema); - } - }); - if (types.length) { - ptcl.types = types; - } - - var msgNames = Object.keys(this._messagesByName); - if (msgNames.length) { - ptcl.messages = {}; - msgNames.forEach(function (name) { - ptcl.messages[name] = this._messagesByName[name]._attrs(opts); - }, this); - } - - if (opts && opts.exportAttrs && this.doc !== undefined) { - ptcl.doc = this.doc; - } - return ptcl; -}; - -/** Function to retrieve a remote service's protocol. */ -function discoverProtocol(transport, opts, cb) { - if (cb === undefined && typeof opts == 'function') { - cb = opts; - opts = undefined; - } - - var svc = new Service({ protocol: 'Empty' }, OPTS); - var ptclStr; - svc.createClient({ timeout: opts && opts.timeout }) - .createChannel(transport, { - scope: opts && opts.scope, - endWritable: typeof transport == 'function', // Stateless transports only. - }) - .once('handshake', function (hreq, hres) { - ptclStr = hres.serverProtocol; - this.destroy(true); - }) - .once('eot', function (pending, err) { - // Stateless transports will throw an interrupted error when the - // channel is destroyed, we ignore it here. - if (err && !/interrupted/.test(err)) { - cb(err); // Likely timeout. - } else { - cb(null, JSON.parse(ptclStr)); - } - }); -} - -/** Load-balanced message sender. */ -function Client(svc, opts) { - opts = opts || {}; - events.EventEmitter.call(this); - - // We have to suffix all client properties to be safe, since the message - // names aren't prefixed with clients (unlike servers). - this._svc$ = svc; - this._channels$ = []; // Active channels. - this._fns$ = []; // Middleware functions. - - this._buffering$ = !!opts.buffering; - this._cache$ = opts.cache || {}; // For backwards compatibility. - this._policy$ = opts.channelPolicy; - this._strict$ = !!opts.strictTypes; - this._timeout$ = utils.getOption(opts, 'timeout', 10000); - - if (opts.remoteProtocols) { - insertRemoteProtocols(this._cache$, opts.remoteProtocols, svc, true); - } - - this._svc$.messages.forEach(function (msg) { - this[msg.name] = this._createMessageHandler$(msg); - }, this); -} -util.inherits(Client, events.EventEmitter); - -Client.prototype.activeChannels = function () { - return this._channels$.slice(); -}; - -Client.prototype.createChannel = function (transport, opts) { - var objectMode = opts && opts.objectMode; - var channel; - if (typeof transport == 'function') { - var writableFactory; - if (objectMode) { - writableFactory = transport; - } else { - // We provide a default standard-compliant codec. This should support - // most use-cases (for example when speaking to the official Java and - // Python implementations over HTTP, or when this library is used for - // both the emitting and listening sides). - writableFactory = function (cb) { - var encoder = new FrameEncoder(); - var writable = transport(function (err, readable) { - if (err) { - cb(err); - return; - } - // Since the decoder isn't exposed (so can't have an error handler - // attached, we forward any errors to the client). Since errors would - // only get thrown when the decoder flushes (if there is trailing - // data), at which point the source will have ended, there is no need - // to add re-piping logic (destination errors trigger an unpipe). - var decoder = new FrameDecoder().once('error', function (err) { - channel.destroy(err); - }); - cb(null, readable.pipe(decoder)); - }); - if (writable) { - encoder.pipe(writable); - return encoder; - } - }; - } - channel = new StatelessClientChannel(this, writableFactory, opts); - } else { - var readable, writable; - if (isStream(transport)) { - readable = writable = transport; - } else { - readable = transport.readable; - writable = transport.writable; - } - if (!objectMode) { - // To ease communication with Java servers, we provide a default codec - // compatible with Java servers' `NettyTransportCodec`'s implementation. - var decoder = new NettyDecoder(); - readable = readable.pipe(decoder); - var encoder = new NettyEncoder(); - encoder.pipe(writable); - writable = encoder; - } - channel = new StatefulClientChannel(this, readable, writable, opts); - if (!objectMode) { - // Since we never expose the automatically created encoder and decoder, - // we release them ourselves here when the channel ends. (Unlike for - // stateless channels, it is conceivable for the underlying stream to be - // reused afterwards). - channel.once('eot', function () { - readable.unpipe(decoder); - encoder.unpipe(writable); - }); - // We also forward any (trailing data) error. - decoder.once('error', function (err) { - channel.destroy(err); - }); - } - } - var channels = this._channels$; - channels.push(channel); - channel.once('_drain', function () { - // Remove the channel from the list of active ones. - channels.splice(channels.indexOf(this), 1); - }); - // We restrict buffering to startup, otherwise we risk silently hiding errors - // (especially since channel timeouts don't apply yet). - this._buffering$ = false; - this.emit('channel', channel); - return channel; -}; - -Client.prototype.destroyChannels = function (opts) { - this._channels$.forEach(function (channel) { - channel.destroy(opts && opts.noWait); - }); -}; - -Client.prototype.emitMessage = function (name, req, opts, cb) { - var msg = getExistingMessage(this._svc$, name); - var wreq = new WrappedRequest(msg, {}, req); - this._emitMessage$(wreq, opts, cb); -}; - -Client.prototype.remoteProtocols = function () { - return getRemoteProtocols(this._cache$, true); -}; - -Object.defineProperty(Client.prototype, 'service', { - enumerable: true, - get: function () { - return this._svc$; - }, -}); - -Client.prototype.use = function (/* fn ... */) { - var i, l, fn; - for (i = 0, l = arguments.length; i < l; i++) { - fn = arguments[i]; - this._fns$.push(fn.length < 3 ? fn(this) : fn); - } - return this; -}; - -Client.prototype._emitMessage$ = function (wreq, opts, cb) { - // Common logic between `client.emitMessage` and the "named" message methods. - if (!cb && typeof opts === 'function') { - cb = opts; - opts = undefined; - } - var self = this; - var channels = this._channels$; - var numChannels = channels.length; - if (!numChannels) { - if (this._buffering$) { - debug('no active client channels, buffering call'); - this.once('channel', function () { - this._emitMessage$(wreq, opts, cb); - }); - } else { - var err = new Error('no active channels'); - process.nextTick(function () { - if (cb) { - cb.call(new CallContext(wreq._msg), err); - } else { - self.emit('error', err); - } - }); - } - return; - } - - opts = opts || {}; - if (opts.timeout === undefined) { - opts.timeout = this._timeout$; - } - - var channel; - if (numChannels === 1) { - // Common case, optimized away. - channel = channels[0]; - } else if (this._policy$) { - channel = this._policy$(this._channels$.slice()); - if (!channel) { - debug('policy returned no channel, skipping call'); - return; - } - } else { - // Random selection, cheap and likely good enough for most use-cases. - channel = channels[Math.floor(Math.random() * numChannels)]; - } - - channel._emit(wreq, opts, function (err, wres) { - var ctx = this; // Call context. - var errType = ctx.message.errorType; - if (err) { - // System error, likely the message wasn't sent (or an error occurred - // while decoding the response). - if (self._strict$) { - err = errType.clone(err.message, { wrapUnions: true }); - } - done(err); - return; - } - if (!wres) { - // This is a one way message. - done(); - return; - } - // Message transmission succeeded, we transmit the message data; massaging - // any error strings into actual `Error` objects in non-strict mode. - err = wres.error; - if (!self._strict$) { - // Try to coerce an eventual error into more idiomatic JavaScript types: - // `undefined` becomes `null` and a remote string "system" error is - // wrapped inside an actual `Error` object. - if (err === undefined) { - err = null; - } else { - if (Type.isType(errType, 'union:unwrapped')) { - if (typeof err == 'string') { - err = new Error(err); - } - } else if (err && err.string && typeof err.string == 'string') { - err = new Error(err.string); - } - } - } - done(err, wres.response); - - function done(err, res) { - if (cb) { - cb.call(ctx, err, res); - } else if (err) { - self.emit('error', err); - } - } - }); -}; - -Client.prototype._createMessageHandler$ = function (msg) { - // jshint -W054 - var fields = msg.requestType.getFields(); - var names = fields.map(function (f) { - return f.getName(); - }); - var body = 'return function ' + msg.name + '('; - if (names.length) { - body += names.join(', ') + ', '; - } - body += 'opts, cb) {\n'; - body += ' var req = {'; - body += names - .map(function (n) { - return n + ': ' + n; - }) - .join(', '); - body += '};\n'; - body += " return this.emitMessage('" + msg.name + "', req, opts, cb);\n"; - body += '};'; - return new Function(body)(); -}; - -/** Message receiver. */ -function Server(svc, opts) { - opts = opts || {}; - events.EventEmitter.call(this); - - this.service = svc; - this._handlers = {}; - this._fns = []; // Middleware functions. - this._channels = {}; // Active channels. - this._nextChannelId = 1; - - this._cache = opts.cache || {}; // Deprecated. - this._defaultHandler = opts.defaultHandler; - this._sysErrFormatter = opts.systemErrorFormatter; - this._silent = !!opts.silent; - this._strict = !!opts.strictTypes; - - if (opts.remoteProtocols) { - insertRemoteProtocols(this._cache, opts.remoteProtocols, svc, false); - } - - svc.messages.forEach(function (msg) { - var name = msg.name; - if (!opts.noCapitalize) { - name = utils.capitalize(name); - } - this['on' + name] = this._createMessageHandler(msg); - }, this); -} -util.inherits(Server, events.EventEmitter); - -Server.prototype.activeChannels = function () { - return utils.objectValues(this._channels); -}; - -Server.prototype.createChannel = function (transport, opts) { - var objectMode = opts && opts.objectMode; - var channel; - if (typeof transport == 'function') { - var readableFactory; - if (objectMode) { - readableFactory = transport; - } else { - readableFactory = function (cb) { - var decoder = new FrameDecoder().once('error', function (err) { - channel.destroy(err); - }); - return transport(function (err, writable) { - if (err) { - cb(err); - return; - } - var encoder = new FrameEncoder(); - encoder.pipe(writable); - cb(null, encoder); - }).pipe(decoder); - }; - } - channel = new StatelessServerChannel(this, readableFactory, opts); - } else { - var readable, writable; - if (isStream(transport)) { - readable = writable = transport; - } else { - readable = transport.readable; - writable = transport.writable; - } - if (!objectMode) { - var decoder = new NettyDecoder(); - readable = readable.pipe(decoder); - var encoder = new NettyEncoder(); - encoder.pipe(writable); - writable = encoder; - } - channel = new StatefulServerChannel(this, readable, writable, opts); - if (!objectMode) { - // Similar to client channels, since we never expose the encoder and - // decoder, we must release them ourselves here. - channel.once('eot', function () { - readable.unpipe(decoder); - encoder.unpipe(writable); - }); - decoder.once('error', function (err) { - channel.destroy(err); - }); - } - } - - if (!this.listeners('error').length) { - this.on('error', this._onError); - } - var channelId = this._nextChannelId++; - var channels = this._channels; - channels[channelId] = channel.once('eot', function () { - delete channels[channelId]; - }); - this.emit('channel', channel); - return channel; -}; - -Server.prototype.onMessage = function (name, handler) { - getExistingMessage(this.service, name); // Check message existence. - this._handlers[name] = handler; - return this; -}; - -Server.prototype.remoteProtocols = function () { - return getRemoteProtocols(this._cache, false); -}; - -Server.prototype.use = function (/* fn ... */) { - var i, l, fn; - for (i = 0, l = arguments.length; i < l; i++) { - fn = arguments[i]; - this._fns.push(fn.length < 3 ? fn(this) : fn); - } - return this; -}; - -Server.prototype._createMessageHandler = function (msg) { - // jshint -W054 - var name = msg.name; - var fields = msg.requestType.fields; - var numArgs = fields.length; - var args = fields.length - ? ', ' + - fields - .map(function (f) { - return 'req.' + f.name; - }) - .join(', ') - : ''; - // We are careful to not lose the initial handler's number of arguments (or - // more specifically whether it would have access to the callback or not). - // This is useful to implement "smart promisification" logic downstream. - var body = 'return function (handler) {\n'; - body += ' if (handler.length > ' + numArgs + ') {\n'; - body += " return this.onMessage('" + name + "', function (req, cb) {\n"; - body += ' return handler.call(this' + args + ', cb);\n'; - body += ' });\n'; - body += ' } else {\n'; - body += " return this.onMessage('" + name + "', function (req) {\n"; - body += ' return handler.call(this' + args + ');\n'; - body += ' });\n'; - body += ' }\n'; - body += '};\n'; - return new Function(body)(); -}; - -Server.prototype._onError = function (err) { - /* istanbul ignore if */ - if (!this._silent && err.rpcCode !== 'UNKNOWN_PROTOCOL') { - console.error(); - if (err.rpcCode) { - console.error(err.rpcCode); - console.error(err.cause); - } else { - console.error('INTERNAL_SERVER_ERROR'); - console.error(err); - } - } -}; - -/** Base message emitter class. See below for the two available variants. */ -function ClientChannel(client, opts) { - opts = opts || {}; - events.EventEmitter.call(this); - - this.client = client; - this.timeout = utils.getOption(opts, 'timeout', client._timeout$); - this._endWritable = !!utils.getOption(opts, 'endWritable', true); - this._prefix = normalizedPrefix(opts.scope); - - var cache = client._cache$; - var clientSvc = client._svc$; - var hash = opts.serverHash; - if (!hash) { - hash = clientSvc.hash; - } - var adapter = cache[hash]; - if (!adapter) { - // This might happen even if the server hash option was set if the cache - // doesn't contain the corresponding adapter. In this case we fall back to - // the client's protocol (as mandated by the spec). - hash = clientSvc.hash; - adapter = cache[hash] = new Adapter(clientSvc, clientSvc, hash); - } - this._adapter = adapter; - - this._registry = new Registry(this, PREFIX_LENGTH); - this.pending = 0; - this.destroyed = false; - this.draining = false; - this.once('_eot', function (pending, err) { - // Since this listener is only run once, we will only forward an error if - // it is present during the initial `destroy` call, which is OK. - debug('client channel EOT'); - this.destroyed = true; - this.emit('eot', pending, err); - }); -} -util.inherits(ClientChannel, events.EventEmitter); - -ClientChannel.prototype.destroy = function (noWait) { - debug('destroying client channel'); - if (!this.draining) { - this.draining = true; - this.emit('_drain'); - } - var registry = this._registry; - var pending = this.pending; - if (noWait) { - registry.clear(); - } - if (noWait || !pending) { - if (isError(noWait)) { - debug('fatal client channel error: %s', noWait); - this.emit('_eot', pending, noWait); - } else { - this.emit('_eot', pending); - } - } else { - debug('client channel entering drain mode (%s pending)', pending); - } -}; - -ClientChannel.prototype.ping = function (timeout, cb) { - if (!cb && typeof timeout == 'function') { - cb = timeout; - timeout = undefined; - } - var self = this; - var wreq = new WrappedRequest(PING_MESSAGE); - this._emit(wreq, { timeout: timeout }, function (err) { - if (cb) { - cb.call(self, err); - } else if (err) { - self.destroy(err); - } - }); -}; - -ClientChannel.prototype._createHandshakeRequest = function (adapter, noSvc) { - var svc = this.client._svc$; - return { - clientHash: svc.hash, - clientProtocol: noSvc ? null : JSON.stringify(svc.protocol), - serverHash: adapter._hash, - }; -}; - -ClientChannel.prototype._emit = function (wreq, opts, cb) { - var msg = wreq._msg; - var wres = msg.oneWay ? undefined : new WrappedResponse(msg, {}); - var ctx = new CallContext(msg, this); - var self = this; - this.pending++; - process.nextTick(function () { - if (!msg.name) { - // Ping request, bypass middleware. - onTransition(wreq, wres, onCompletion); - } else { - self.emit('outgoingCall', ctx, opts); - var fns = self.client._fns$; - debug('starting client middleware chain (%s middleware)', fns.length); - chainMiddleware({ - fns: fns, - ctx: ctx, - wreq: wreq, - wres: wres, - onTransition: onTransition, - onCompletion: onCompletion, - onError: onError, - }); - } - }); - - function onTransition(wreq, wres, prev) { - // Serialize the message. - var err, reqBuf; - if (self.destroyed) { - err = new Error('channel destroyed'); - } else { - try { - reqBuf = wreq.toBuffer(); - } catch (cause) { - err = serializationError(f('invalid %j request', msg.name), wreq, [ - { name: 'headers', type: MAP_BYTES_TYPE }, - { name: 'request', type: msg.requestType }, - ]); - } - } - if (err) { - prev(err); - return; - } - - // Generate the response callback. - var timeout = opts && opts.timeout !== undefined ? opts.timeout : self.timeout; - var id = self._registry.add(timeout, function (err, resBuf, adapter) { - if (!err && !msg.oneWay) { - try { - adapter._decodeResponse(resBuf, wres, msg); - } catch (cause) { - err = cause; - } - } - prev(err); - }); - id |= self._prefix; - - debug('sending message %s', id); - self._send(id, reqBuf, !!msg && msg.oneWay); - } - - function onCompletion(err) { - self.pending--; - cb.call(ctx, err, wres); - if (self.draining && !self.destroyed && !self.pending) { - self.destroy(); - } - } - - function onError(err) { - // This will happen if a middleware callback is called multiple times. We - // forward the error to the client rather than emit it on the channel since - // middleware are a client-level abstraction, so better handled there. - self.client.emit('error', err, self); - } -}; - -ClientChannel.prototype._getAdapter = function (hres) { - var hash = hres.serverHash; - var cache = this.client._cache$; - var adapter = cache[hash]; - if (adapter) { - return adapter; - } - var ptcl = JSON.parse(hres.serverProtocol); - var serverSvc = Service.forProtocol(ptcl); - adapter = new Adapter(this.client._svc$, serverSvc, hash, true); - return (cache[hash] = adapter); -}; - -ClientChannel.prototype._matchesPrefix = function (id) { - return matchesPrefix(id, this._prefix); -}; - -ClientChannel.prototype._send = utils.abstractFunction; - -// Deprecated. - -utils.addDeprecatedGetters(ClientChannel, ['pending', 'timeout']); - -ClientChannel.prototype.getCache = util.deprecate(function () { - return this.client._cache$; -}, 'use `.remoteProtocols()` instead of `.getCache()`'); - -ClientChannel.prototype.getProtocol = util.deprecate(function () { - return this.client._svc$; -}, 'use `.service` instead or `.getProtocol()`'); - -ClientChannel.prototype.isDestroyed = util.deprecate(function () { - return this.destroyed; -}, 'use `.destroyed` instead of `.isDestroyed`'); - -/** - * Factory-based client channel. - * - * This channel doesn't keep a persistent connection to the server and requires - * prepending a handshake to each message emitted. Usage examples include - * talking to an HTTP server (where the factory returns an HTTP request). - * - * Since each message will use its own writable/readable stream pair, the - * advantage of this channel is that it is able to keep track of which response - * corresponds to each request without relying on transport ordering. In - * particular, this means these channels are compatible with any server - * implementation. - */ -function StatelessClientChannel(client, writableFactory, opts) { - ClientChannel.call(this, client, opts); - this._writableFactory = writableFactory; - - if (!opts || !opts.noPing) { - // Ping the server to check whether the remote protocol is compatible. - // If not, this will throw an error on the channel. - debug('emitting ping request'); - this.ping(); - } -} -util.inherits(StatelessClientChannel, ClientChannel); - -StatelessClientChannel.prototype._send = function (id, reqBuf) { - var cb = this._registry.get(id); - var adapter = this._adapter; - var self = this; - process.nextTick(emit); - return true; - - function emit(retry) { - if (self.destroyed) { - // The request's callback will already have been called. - return; - } - - var hreq = self._createHandshakeRequest(adapter, !retry); - - var writable = self._writableFactory.call(self, function (err, readable) { - if (err) { - cb(err); - return; - } - readable.on('data', function (obj) { - debug('received response %s', obj.id); - // We don't check that the prefix matches since the ID likely hasn't - // been propagated to the response (see default stateless codec). - var buf = Buffer.concat(obj.payload); - try { - var parts = readHead(HANDSHAKE_RESPONSE_TYPE, buf); - var hres = parts.head; - if (hres.serverHash) { - adapter = self._getAdapter(hres); - } - } catch (cause) { - cb(cause); - return; - } - var match = hres.match; - debug('handshake match: %s', match); - self.emit('handshake', hreq, hres); - if (match === 'NONE') { - // Try again, including the full protocol this time. - process.nextTick(function () { - emit(true); - }); - } else { - // Change the default adapter. - self._adapter = adapter; - cb(null, parts.tail, adapter); - } - }); - }); - if (!writable) { - cb(new Error('invalid writable stream')); - return; - } - writable.write({ - id: id, - payload: [HANDSHAKE_REQUEST_TYPE.toBuffer(hreq), reqBuf], - }); - if (self._endWritable) { - writable.end(); - } - } -}; - -/** - * Multiplexing client channel. - * - * These channels reuse the same streams (both readable and writable) for all - * messages. This avoids a lot of overhead (e.g. creating new connections, - * re-issuing handshakes) but requires the underlying transport to support - * forwarding message IDs. - */ -function StatefulClientChannel(client, readable, writable, opts) { - ClientChannel.call(this, client, opts); - this._readable = readable; - this._writable = writable; - this._connected = !!(opts && opts.noPing); - this._readable.on('end', onEnd); - this._writable.on('finish', onFinish); - - var self = this; - var timer = null; - this.once('eot', function () { - if (timer) { - clearTimeout(timer); - timer = null; - } - if (!self._connected) { - // Clear any buffered calls (they are guaranteed to error out when - // reaching the transition phase). - self.emit('_ready'); - } - // Remove references to this channel to avoid potential memory leaks. - this._writable.removeListener('finish', onFinish); - if (this._endWritable) { - debug('ending transport'); - this._writable.end(); - } - this._readable.removeListener('data', onPing).removeListener('data', onMessage).removeListener('end', onEnd); - }); - - var hreq; // For handshake events. - if (this._connected) { - this._readable.on('data', onMessage); - } else { - this._readable.on('data', onPing); - process.nextTick(ping); - if (self.timeout) { - timer = setTimeout(function () { - self.destroy(new Error('timeout')); - }, self.timeout); - } - } - - function ping(retry) { - if (self.destroyed) { - return; - } - hreq = self._createHandshakeRequest(self._adapter, !retry); - var payload = [ - HANDSHAKE_REQUEST_TYPE.toBuffer(hreq), - utils.bufferFrom([0, 0]), // No header, no data (empty message name). - ]; - // We can use a static ID here since we are guaranteed that this message is - // the only one on the channel (for this scope at least). - self._writable.write({ id: self._prefix, payload: payload }); - } - - function onPing(obj) { - if (!self._matchesPrefix(obj.id)) { - debug('discarding unscoped response %s (still connecting)', obj.id); - return; - } - var buf = Buffer.concat(obj.payload); - try { - var hres = readHead(HANDSHAKE_RESPONSE_TYPE, buf).head; - if (hres.serverHash) { - self._adapter = self._getAdapter(hres); - } - } catch (cause) { - // This isn't a recoverable error. - self.destroy(cause); - return; - } - var match = hres.match; - debug('handshake match: %s', match); - self.emit('handshake', hreq, hres); - if (match === 'NONE') { - process.nextTick(function () { - ping(true); - }); - } else { - debug('successfully connected'); - if (timer) { - clearTimeout(timer); - timer = null; - } - self._readable.removeListener('data', onPing).on('data', onMessage); - self._connected = true; - self.emit('_ready'); - hreq = null; // Release reference. - } - } - - // Callback used after a connection has been established. - function onMessage(obj) { - var id = obj.id; - if (!self._matchesPrefix(id)) { - debug('discarding unscoped message %s', id); - return; - } - var cb = self._registry.get(id); - if (cb) { - process.nextTick(function () { - debug('received message %s', id); - // Ensure that the initial callback gets called asynchronously, even - // for completely synchronous transports (otherwise the number of - // pending requests will sometimes be inconsistent between stateful and - // stateless transports). - cb(null, Buffer.concat(obj.payload), self._adapter); - }); - } - } - - function onEnd() { - self.destroy(true); - } - function onFinish() { - self.destroy(); - } -} -util.inherits(StatefulClientChannel, ClientChannel); - -StatefulClientChannel.prototype._emit = function () { - // Override this method to allow calling `_emit` even before the channel is - // connected. Note that we don't perform this logic in `_send` since we want - // to guarantee that `'handshake'` events are emitted before any - // `'outgoingCall'` events. - if (this._connected || this.draining) { - ClientChannel.prototype._emit.apply(this, arguments); - } else { - debug('queuing request'); - var args = []; - var i, l; - for (i = 0, l = arguments.length; i < l; i++) { - args.push(arguments[i]); - } - this.once('_ready', function () { - this._emit.apply(this, args); - }); - } -}; - -StatefulClientChannel.prototype._send = function (id, reqBuf, oneWay) { - if (oneWay) { - var self = this; - // Clear the callback, passing in an empty header. - process.nextTick(function () { - self._registry.get(id)(null, utils.bufferFrom([0, 0, 0]), self._adapter); - }); - } - return this._writable.write({ id: id, payload: [reqBuf] }); -}; - -/** The server-side emitter equivalent. */ -function ServerChannel(server, opts) { - opts = opts || {}; - events.EventEmitter.call(this); - - this.server = server; - this._endWritable = !!utils.getOption(opts, 'endWritable', true); - this._prefix = normalizedPrefix(opts.scope); - - var cache = server._cache; - var svc = server.service; - var hash = svc.hash; - if (!cache[hash]) { - // Add the channel's protocol to the cache if it isn't already there. This - // will save a handshake the first time on channels with the same protocol. - cache[hash] = new Adapter(svc, svc, hash); - } - this._adapter = null; - - this.destroyed = false; - this.draining = false; - this.pending = 0; - this.once('_eot', function (pending, err) { - debug('server channel EOT'); - this.emit('eot', pending, err); - }); -} -util.inherits(ServerChannel, events.EventEmitter); - -ServerChannel.prototype.destroy = function (noWait) { - if (!this.draining) { - this.draining = true; - this.emit('_drain'); - } - if (noWait || !this.pending) { - this.destroyed = true; - if (isError(noWait)) { - debug('fatal server channel error: %s', noWait); - this.emit('_eot', this.pending, noWait); - } else { - this.emit('_eot', this.pending); - } - } -}; - -ServerChannel.prototype._createHandshakeResponse = function (err, hreq) { - var svc = this.server.service; - var buf = svc.hash; - var serverMatch = hreq && hreq.serverHash.equals(buf); - return { - match: err ? 'NONE' : serverMatch ? 'BOTH' : 'CLIENT', - serverProtocol: serverMatch ? null : JSON.stringify(svc.protocol), - serverHash: serverMatch ? null : buf, - }; -}; - -ServerChannel.prototype._getAdapter = function (hreq) { - var hash = hreq.clientHash; - var adapter = this.server._cache[hash]; - if (adapter) { - return adapter; - } - if (!hreq.clientProtocol) { - throw toRpcError('UNKNOWN_PROTOCOL'); - } - var ptcl = JSON.parse(hreq.clientProtocol); - var clientSvc = Service.forProtocol(ptcl); - adapter = new Adapter(clientSvc, this.server.service, hash, true); - return (this.server._cache[hash] = adapter); -}; - -ServerChannel.prototype._matchesPrefix = function (id) { - return matchesPrefix(id, this._prefix); -}; - -ServerChannel.prototype._receive = function (reqBuf, adapter, cb) { - var self = this; - var wreq; - try { - wreq = adapter._decodeRequest(reqBuf); - } catch (cause) { - cb(self._encodeSystemError(toRpcError('INVALID_REQUEST', cause))); - return; - } - - var msg = wreq._msg; - var wres = new WrappedResponse(msg, {}); - if (!msg.name) { - // Ping message, we don't invoke middleware logic in this case. - wres.response = null; - cb(wres.toBuffer(), false); - return; - } - - var ctx = new CallContext(msg, this); - self.emit('incomingCall', ctx); - var fns = this.server._fns; - debug('starting server middleware chain (%s middleware)', fns.length); - self.pending++; - chainMiddleware({ - fns: fns, - ctx: ctx, - wreq: wreq, - wres: wres, - onTransition: onTransition, - onCompletion: onCompletion, - onError: onError, - }); - - function onTransition(wreq, wres, prev) { - var handler = self.server._handlers[msg.name]; - if (!handler) { - // The underlying service hasn't implemented a handler. - var defaultHandler = self.server._defaultHandler; - if (defaultHandler) { - // We call the default handler with arguments similar (slightly - // simpler, there are no phases here) to middleware such that it can - // easily access the message name (useful to implement proxies). - defaultHandler.call(ctx, wreq, wres, prev); - } else { - var cause = new Error(f('no handler for %s', msg.name)); - prev(toRpcError('NOT_IMPLEMENTED', cause)); - } - } else { - var pending = !msg.oneWay; - try { - if (pending) { - handler.call(ctx, wreq.request, function (err, res) { - pending = false; - wres.error = err; - wres.response = res; - prev(); - }); - } else { - handler.call(ctx, wreq.request); - prev(); - } - } catch (err) { - // We catch synchronous failures (same as express) and return the - // failure. Note that the server process can still crash if an error - // is thrown after the handler returns but before the response is - // sent (again, same as express). We are careful to only trigger the - // response callback once, emitting the errors afterwards instead. - if (pending) { - pending = false; - prev(err); - } else { - onError(err); - } - } - } - } - - function onCompletion(err) { - self.pending--; - var server = self.server; - var resBuf; - if (!err) { - var resErr = wres.error; - var isStrict = server._strict; - if (!isStrict) { - if (isError(resErr)) { - // If the error type is wrapped, we must wrap the error too. - wres.error = msg.errorType.clone(resErr.message, { wrapUnions: true }); - } else if (resErr === null) { - // We also allow `null`'s as error in this mode, converting them to - // the Avro-compatible `undefined`. - resErr = wres.error = undefined; - } - if (resErr === undefined && wres.response === undefined && msg.responseType.isValid(null)) { - // Finally, for messages with `null` as acceptable response type, we - // allow `undefined`; converting them to `null`. This allows users to - // write a more natural `cb()` instead of `cb(null, null)`. - wres.response = null; - } - } - try { - resBuf = wres.toBuffer(); - } catch (cause) { - // Note that we don't add an RPC code here such that the client - // receives the default `INTERNAL_SERVER_ERROR` one. - if (wres.error !== undefined) { - err = serializationError( - f('invalid %j error', msg.name), // Sic. - wres, - [ - { name: 'headers', type: MAP_BYTES_TYPE }, - { name: 'error', type: msg.errorType }, - ], - ); - } else { - err = serializationError(f('invalid %j response', msg.name), wres, [ - { name: 'headers', type: MAP_BYTES_TYPE }, - { name: 'response', type: msg.responseType }, - ]); - } - } - } - if (!resBuf) { - // The headers are only available if the message isn't one-way. - resBuf = self._encodeSystemError(err, wres.headers); - } else if (resErr !== undefined) { - server.emit('error', toRpcError('APPLICATION_ERROR', resErr)); - } - cb(resBuf, msg.oneWay); - if (self.draining && !self.pending) { - self.destroy(); - } - } - - function onError(err) { - // Similar to the client equivalent, we redirect this error to the server - // since middleware are defined at server-level. - self.server.emit('error', err, self); - } -}; - -// Deprecated. - -utils.addDeprecatedGetters(ServerChannel, ['pending']); - -ServerChannel.prototype.getCache = util.deprecate(function () { - return this.server._cache; -}, 'use `.remoteProtocols()` instead of `.getCache()`'); - -ServerChannel.prototype.getProtocol = util.deprecate(function () { - return this.server.service; -}, 'use `.service` instead of `.getProtocol()`'); - -ServerChannel.prototype.isDestroyed = util.deprecate(function () { - return this.destroyed; -}, 'use `.destroyed` instead of `.isDestroyed`'); - -/** - * Encode an error and optional header into a valid Avro response. - * - * @param err {Error} Error to encode. - * @param header {Object} Optional response header. - */ -ServerChannel.prototype._encodeSystemError = function (err, header) { - var server = this.server; - server.emit('error', err, this); - var errStr; - if (server._sysErrFormatter) { - // Format the error into a string to send over the wire. - errStr = server._sysErrFormatter.call(this, err); - } else if (err.rpcCode) { - // By default, only forward the error's message when the RPC code is set - // (i.e. when this isn't an internal server error). - errStr = err.message; - } - var hdrBuf; - if (header) { - try { - // Propagate the header if possible. - hdrBuf = MAP_BYTES_TYPE.toBuffer(header); - } catch (cause) { - server.emit('error', cause, this); - } - } - return Buffer.concat([ - hdrBuf || utils.bufferFrom([0]), - utils.bufferFrom([1, 0]), // Error flag and first union index. - STRING_TYPE.toBuffer(errStr || 'internal server error'), - ]); -}; - -/** - * Server channel for stateless transport. - * - * This channel expect a handshake to precede each message. - */ -function StatelessServerChannel(server, readableFactory, opts) { - ServerChannel.call(this, server, opts); - - this._writable = undefined; - var self = this; - var readable; - - process.nextTick(function () { - // Delay listening to allow handlers to be attached even if the factory is - // purely synchronous. - readable = readableFactory - .call(self, function (err, writable) { - process.nextTick(function () { - // We delay once more here in case this call is synchronous, to allow - // the readable to always be populated first. - if (err) { - onFinish(err); - return; - } - self._writable = writable.on('finish', onFinish); - self.emit('_writable'); - }); - }) - .on('data', onRequest) - .on('end', onEnd); - }); - - function onRequest(obj) { - var id = obj.id; - var buf = Buffer.concat(obj.payload); - var err; - try { - var parts = readHead(HANDSHAKE_REQUEST_TYPE, buf); - var hreq = parts.head; - var adapter = self._getAdapter(hreq); - } catch (cause) { - err = toRpcError('INVALID_HANDSHAKE_REQUEST', cause); - } - - var hres = self._createHandshakeResponse(err, hreq); - self.emit('handshake', hreq, hres); - if (err) { - done(self._encodeSystemError(err)); - } else { - self._receive(parts.tail, adapter, done); - } - - function done(resBuf) { - if (!self.destroyed) { - if (!self._writable) { - self.once('_writable', function () { - done(resBuf); - }); - return; - } - self._writable.write({ - id: id, - payload: [HANDSHAKE_RESPONSE_TYPE.toBuffer(hres), resBuf], - }); - } - if (self._writable && self._endWritable) { - self._writable.end(); - } - } - } - - function onEnd() { - self.destroy(); - } - - function onFinish(err) { - readable.removeListener('data', onRequest).removeListener('end', onEnd); - self.destroy(err || true); - } -} -util.inherits(StatelessServerChannel, ServerChannel); - -/** - * Stateful transport listener. - * - * A handshake is done when the channel first receives a message, then all - * messages are sent without. - */ -function StatefulServerChannel(server, readable, writable, opts) { - ServerChannel.call(this, server, opts); - this._adapter = undefined; - this._writable = writable.on('finish', onFinish); - this._readable = readable.on('data', onHandshake).on('end', onEnd); - - this.once('_drain', function () { - // Stop listening to incoming events. - this._readable - .removeListener('data', onHandshake) - .removeListener('data', onRequest) - .removeListener('end', onEnd); - }).once('eot', function () { - // Clean up any references to the channel on the underlying streams. - this._writable.removeListener('finish', onFinish); - if (this._endWritable) { - this._writable.end(); - } - }); - - var self = this; - - function onHandshake(obj) { - var id = obj.id; - if (!self._matchesPrefix(id)) { - return; - } - var buf = Buffer.concat(obj.payload); - var err; - try { - var parts = readHead(HANDSHAKE_REQUEST_TYPE, buf); - var hreq = parts.head; - self._adapter = self._getAdapter(hreq); - } catch (cause) { - err = toRpcError('INVALID_HANDSHAKE_REQUEST', cause); - } - var hres = self._createHandshakeResponse(err, hreq); - self.emit('handshake', hreq, hres); - if (err) { - // Either the client's protocol was unknown or it isn't compatible. - done(self._encodeSystemError(err)); - } else { - self._readable.removeListener('data', onHandshake).on('data', onRequest); - self._receive(parts.tail, self._adapter, done); - } - - function done(resBuf) { - if (self.destroyed) { - return; - } - self._writable.write({ - id: id, - payload: [HANDSHAKE_RESPONSE_TYPE.toBuffer(hres), resBuf], - }); - } - } - - function onRequest(obj) { - // These requests are not prefixed with handshakes. - var id = obj.id; - if (!self._matchesPrefix(id)) { - return; - } - var reqBuf = Buffer.concat(obj.payload); - self._receive(reqBuf, self._adapter, function (resBuf, oneWay) { - if (self.destroyed || oneWay) { - return; - } - self._writable.write({ id: id, payload: [resBuf] }); - }); - } - - function onEnd() { - self.destroy(); - } - - function onFinish() { - self.destroy(true); - } -} -util.inherits(StatefulServerChannel, ServerChannel); - -// Helpers. - -/** Enhanced request, used inside forward middleware functions. */ -function WrappedRequest(msg, hdrs, req) { - this._msg = msg; - this.headers = hdrs || {}; - this.request = req || {}; -} - -WrappedRequest.prototype.toBuffer = function () { - var msg = this._msg; - return Buffer.concat([ - MAP_BYTES_TYPE.toBuffer(this.headers), - STRING_TYPE.toBuffer(msg.name), - msg.requestType.toBuffer(this.request), - ]); -}; - -/** Enhanced response, used inside forward middleware functions. */ -function WrappedResponse(msg, hdr, err, res) { - this._msg = msg; - this.headers = hdr; - this.error = err; - this.response = res; -} - -WrappedResponse.prototype.toBuffer = function () { - var hdr = MAP_BYTES_TYPE.toBuffer(this.headers); - var hasError = this.error !== undefined; - return Buffer.concat([ - hdr, - BOOLEAN_TYPE.toBuffer(hasError), - hasError ? this._msg.errorType.toBuffer(this.error) : this._msg.responseType.toBuffer(this.response), - ]); -}; - -/** - * Context for all middleware and handlers. - * - * It exposes a `locals` object which can be used to pass information between - * each other during a given call. - */ -function CallContext(msg, channel) { - this.channel = channel; - this.locals = {}; - this.message = msg; - Object.freeze(this); -} - -/** - * Callback registry. - * - * Callbacks added must accept an error as first argument. This is used by - * client channels to store pending calls. This class isn't exposed by the - * public API. - */ -function Registry(ctx, prefixLength) { - this._ctx = ctx; // Context for all callbacks. - this._mask = ~0 >>> (prefixLength | 0); // 16 bits by default. - this._id = 0; // Unique integer ID for each call. - this._n = 0; // Number of pending calls. - this._cbs = {}; -} - -Registry.prototype.get = function (id) { - return this._cbs[id & this._mask]; -}; - -Registry.prototype.add = function (timeout, fn) { - this._id = (this._id + 1) & this._mask; - - var self = this; - var id = this._id; - var timer; - if (timeout > 0) { - timer = setTimeout(function () { - cb(new Error('timeout')); - }, timeout); - } - - this._cbs[id] = cb; - this._n++; - return id; - - function cb() { - if (!self._cbs[id]) { - // The callback has already run. - return; - } - delete self._cbs[id]; - self._n--; - if (timer) { - clearTimeout(timer); - } - fn.apply(self._ctx, arguments); - } -}; - -Registry.prototype.clear = function () { - Object.keys(this._cbs).forEach(function (id) { - this._cbs[id](new Error('interrupted')); - }, this); -}; - -/** - * Service resolution helper. - * - * It is used both by client and server channels, to respectively decode errors - * and responses, or requests. - */ -function Adapter(clientSvc, serverSvc, hash, isRemote) { - this._clientSvc = clientSvc; - this._serverSvc = serverSvc; - this._hash = hash; // Convenience to access it when creating handshakes. - this._isRemote = !!isRemote; - this._readers = createReaders(clientSvc, serverSvc); -} - -Adapter.prototype._decodeRequest = function (buf) { - var tap = new Tap(buf); - var hdr = MAP_BYTES_TYPE._read(tap); - var name = STRING_TYPE._read(tap); - var msg, req; - if (name) { - msg = this._serverSvc.message(name); - req = this._readers[name + '?']._read(tap); - } else { - msg = PING_MESSAGE; - } - if (!tap.isValid()) { - throw new Error(f('truncated %s request', name || 'ping$')); - } - return new WrappedRequest(msg, hdr, req); -}; - -Adapter.prototype._decodeResponse = function (buf, wres, msg) { - var tap = new Tap(buf); - utils.copyOwnProperties(MAP_BYTES_TYPE._read(tap), wres.headers, true); - var isError = BOOLEAN_TYPE._read(tap); - var name = msg.name; - if (name) { - var reader = this._readers[name + (isError ? '*' : '!')]; - msg = this._clientSvc.message(name); - if (isError) { - wres.error = reader._read(tap); - } else { - wres.response = reader._read(tap); - } - if (!tap.isValid()) { - throw new Error(f('truncated %s response', name)); - } - } else { - msg = PING_MESSAGE; - } -}; - -/** Standard "un-framing" stream. */ -function FrameDecoder() { - stream.Transform.call(this, { readableObjectMode: true }); - this._id = undefined; - this._buf = utils.newBuffer(0); - this._bufs = []; - - this.on('finish', function () { - this.push(null); - }); -} -util.inherits(FrameDecoder, stream.Transform); - -FrameDecoder.prototype._transform = function (buf, encoding, cb) { - buf = Buffer.concat([this._buf, buf]); - var frameLength; - while (buf.length >= 4 && buf.length >= (frameLength = buf.readInt32BE(0)) + 4) { - if (frameLength) { - this._bufs.push(buf.slice(4, frameLength + 4)); - } else { - var bufs = this._bufs; - this._bufs = []; - this.push({ id: null, payload: bufs }); - } - buf = buf.slice(frameLength + 4); - } - this._buf = buf; - cb(); -}; - -FrameDecoder.prototype._flush = function () { - if (this._buf.length || this._bufs.length) { - var bufs = this._bufs.slice(); - bufs.unshift(this._buf); - var err = toRpcError('TRAILING_DATA'); - // Attach the data to help debugging (e.g. if the encoded bytes contain a - // human-readable protocol like HTTP). - err.trailingData = Buffer.concat(bufs).toString(); - this.emit('error', err); - } -}; - -/** Standard framing stream. */ -function FrameEncoder() { - stream.Transform.call(this, { writableObjectMode: true }); - this.on('finish', function () { - this.push(null); - }); -} -util.inherits(FrameEncoder, stream.Transform); - -FrameEncoder.prototype._transform = function (obj, encoding, cb) { - var bufs = obj.payload; - var i, l, buf; - for (i = 0, l = bufs.length; i < l; i++) { - buf = bufs[i]; - this.push(intBuffer(buf.length)); - this.push(buf); - } - this.push(intBuffer(0)); - cb(); -}; - -/** Netty-compatible decoding stream. */ -function NettyDecoder() { - stream.Transform.call(this, { readableObjectMode: true }); - this._id = undefined; - this._frameCount = 0; - this._buf = utils.newBuffer(0); - this._bufs = []; - - this.on('finish', function () { - this.push(null); - }); -} -util.inherits(NettyDecoder, stream.Transform); - -NettyDecoder.prototype._transform = function (buf, encoding, cb) { - buf = Buffer.concat([this._buf, buf]); - - while (true) { - if (this._id === undefined) { - if (buf.length < 8) { - this._buf = buf; - cb(); - return; - } - this._id = buf.readInt32BE(0); - this._frameCount = buf.readInt32BE(4); - buf = buf.slice(8); - } - - var frameLength; - while (this._frameCount && buf.length >= 4 && buf.length >= (frameLength = buf.readInt32BE(0)) + 4) { - this._frameCount--; - this._bufs.push(buf.slice(4, frameLength + 4)); - buf = buf.slice(frameLength + 4); - } - - if (this._frameCount) { - this._buf = buf; - cb(); - return; - } else { - var obj = { id: this._id, payload: this._bufs }; - this._bufs = []; - this._id = undefined; - this.push(obj); - } - } -}; - -NettyDecoder.prototype._flush = FrameDecoder.prototype._flush; - -/** Netty-compatible encoding stream. */ -function NettyEncoder() { - stream.Transform.call(this, { writableObjectMode: true }); - this.on('finish', function () { - this.push(null); - }); -} -util.inherits(NettyEncoder, stream.Transform); - -NettyEncoder.prototype._transform = function (obj, encoding, cb) { - var bufs = obj.payload; - var l = bufs.length; - var buf; - // Header: [ ID, number of frames ] - buf = utils.newBuffer(8); - buf.writeInt32BE(obj.id, 0); - buf.writeInt32BE(l, 4); - this.push(buf); - // Frames, each: [ length, bytes ] - var i; - for (i = 0; i < l; i++) { - buf = bufs[i]; - this.push(intBuffer(buf.length)); - this.push(buf); - } - cb(); -}; - -/** - * Returns a buffer containing an integer's big-endian representation. - * - * @param n {Number} Integer. - */ -function intBuffer(n) { - var buf = utils.newBuffer(4); - buf.writeInt32BE(n); - return buf; -} - -/** - * Decode a type used as prefix inside a buffer. - * - * @param type {Type} The type of the prefix. - * @param buf {Buffer} Encoded bytes. - * - * This function will return an object `{head, tail}` where head contains the - * decoded value and tail the rest of the buffer. An error will be thrown if - * the prefix cannot be decoded. - */ -function readHead(type, buf) { - var tap = new Tap(buf); - var head = type._read(tap); - if (!tap.isValid()) { - throw new Error(f('truncated %j', type.schema())); - } - return { head: head, tail: tap.buf.slice(tap.pos) }; -} - -/** - * Generate a decoder, optimizing the case where reader and writer are equal. - * - * @param rtype {Type} Reader's type. - * @param wtype {Type} Writer's type. - */ -function createReader(rtype, wtype) { - return rtype.equals(wtype) ? rtype : rtype.createResolver(wtype); -} - -/** - * Generate all readers for a given protocol combination. - * - * @param clientSvc {Service} Client service. - * @param serverSvc {Service} Client service. - */ -function createReaders(clientSvc, serverSvc) { - var obj = {}; - clientSvc.messages.forEach(function (c) { - var n = c.name; - var s = serverSvc.message(n); - try { - if (!s) { - throw new Error(f('missing server message: %s', n)); - } - if (s.oneWay !== c.oneWay) { - throw new Error(f('inconsistent one-way message: %s', n)); - } - obj[n + '?'] = createReader(s.requestType, c.requestType); - obj[n + '*'] = createReader(c.errorType, s.errorType); - obj[n + '!'] = createReader(c.responseType, s.responseType); - } catch (cause) { - throw toRpcError('INCOMPATIBLE_PROTOCOL', cause); - } - }); - return obj; -} - -/** - * Populate a cache from a list of protocols. - * - * @param cache {Object} Cache of adapters. - * @param svc {Service} The local service (either client or server). - * @param ptcls {Array} Array of protocols to insert. - * @param isClient {Boolean} Whether the local service is a client's or - * server's. - */ -function insertRemoteProtocols(cache, ptcls, svc, isClient) { - Object.keys(ptcls).forEach(function (hash) { - var ptcl = ptcls[hash]; - var clientSvc, serverSvc; - if (isClient) { - clientSvc = svc; - serverSvc = Service.forProtocol(ptcl); - } else { - clientSvc = Service.forProtocol(ptcl); - serverSvc = svc; - } - cache[hash] = new Adapter(clientSvc, serverSvc, hash, true); - }); -} - -/** - * Extract remote protocols from a cache - * - * @param cache {Object} Cache of adapters. - * @param isClient {Boolean} Whether the remote protocols extracted should be - * the servers' or clients'. - */ -function getRemoteProtocols(cache, isClient) { - var ptcls = {}; - Object.keys(cache).forEach(function (hs) { - var adapter = cache[hs]; - if (adapter._isRemote) { - var svc = isClient ? adapter._serverSvc : adapter._clientSvc; - ptcls[hs] = svc.protocol; - } - }); - return ptcls; -} - -/** - * Check whether something is an `Error`. - * - * @param any {Object} Any object. - */ -function isError(any) { - // Also not ideal, but avoids brittle `instanceof` checks. - return !!any && Object.prototype.toString.call(any) === '[object Error]'; -} - -/** - * Forward any errors emitted on the source to the destination. - * - * @param src {EventEmitter} The initial source of error events. - * @param dst {EventEmitter} The new target of the source's error events. The - * original source will be provided as second argument (the error being the - * first). - * - * As a convenience, the source will be returned. - */ -function forwardErrors(src, dst) { - return src.on('error', function (err) { - dst.emit('error', err, src); - }); -} - -/** - * Create an error. - * - * @param msg {String} Error message. - * @param cause {Error} The cause of the error. It is available as `cause` - * field on the outer error. - */ -function toError(msg, cause) { - var err = new Error(msg); - err.cause = cause; - return err; -} - -/** - * Mark an error. - * - * @param rpcCode {String} Code representing the failure. - * @param cause {Error} The cause of the error. It is available as `cause` - * field on the outer error. - * - * This is used to keep the argument of channels' `'error'` event errors. - */ -function toRpcError(rpcCode, cause) { - var err = toError(rpcCode.toLowerCase().replace(/_/g, ' '), cause); - err.rpcCode = cause && cause.rpcCode ? cause.rpcCode : rpcCode; - return err; -} - -/** - * Provide a helpful error to identify why serialization failed. - * - * @param err {Error} The error to decorate. - * @param obj {...} The object containing fields to validated. - * @param fields {Array} Information about the fields to validate. - */ -function serializationError(msg, obj, fields) { - var details = []; - var i, l, field; - for (i = 0, l = fields.length; i < l; i++) { - field = fields[i]; - field.type.isValid(obj[field.name], { errorHook: errorHook }); - } - var detailsStr = details - .map(function (obj) { - return f('%s = %j but expected %s', obj.path, obj.value, obj.type); - }) - .join(', '); - var err = new Error(f('%s (%s)', msg, detailsStr)); - err.details = details; - return err; - - function errorHook(parts, any, type) { - var strs = []; - var i, l, part; - for (i = 0, l = parts.length; i < l; i++) { - part = parts[i]; - if (isNaN(part)) { - strs.push('.' + part); - } else { - strs.push('[' + part + ']'); - } - } - details.push({ - path: field.name + strs.join(''), - value: any, - type: type, - }); - } -} - -/** - * Compute a prefix of fixed length from a string. - * - * @param scope {String} Namespace to be hashed. - */ -function normalizedPrefix(scope) { - return scope ? utils.getHash(scope).readInt16BE(0) << (32 - PREFIX_LENGTH) : 0; -} - -/** - * Check whether an ID matches the prefix. - * - * @param id {Integer} Number to check. - * @param prefix {Integer} Already shifted prefix. - */ -function matchesPrefix(id, prefix) { - return (id ^ prefix) >> (32 - PREFIX_LENGTH) === 0; -} - -/** - * Check whether something is a stream. - * - * @param any {Object} Any object. - */ -function isStream(any) { - // This is a hacky way of checking that the transport is a stream-like - // object. We unfortunately can't use `instanceof Stream` checks since - // some libraries (e.g. websocket-stream) return streams which don't - // inherit from it. - return !!(any && any.pipe); -} - -/** - * Get a message, asserting that it exists. - * - * @param svc {Service} The protocol to look into. - * @param name {String} The message's name. - */ -function getExistingMessage(svc, name) { - var msg = svc.message(name); - if (!msg) { - throw new Error(f('unknown message: %s', name)); - } - return msg; -} - -/** - * Middleware logic. - * - * This is used both in clients and servers to intercept call handling (e.g. to - * populate headers, do access control). - * - * @param params {Object} The following parameters: - * + fns {Array} Array of middleware functions. - * + ctx {Object} Context used to call the middleware functions, onTransition, - * and onCompletion. - * + wreq {WrappedRequest} - * + wres {WrappedResponse} - * + onTransition {Function} End of forward phase callback. It accepts an - * eventual error as single argument. This will be used for the backward - * phase. This function is guaranteed to be called at most once. - * + onCompletion {Function} Final handler, it takes an error as unique - * argument. This function is guaranteed to be only at most once. - * + onError {Function} Error handler, called if an intermediate callback is - * called multiple times. - */ -function chainMiddleware(params) { - var args = [params.wreq, params.wres]; - var cbs = []; - var cause; // Backpropagated error. - forward(0); - - function forward(pos) { - var isDone = false; - if (pos < params.fns.length) { - params.fns[pos].apply( - params.ctx, - args.concat(function (err, cb) { - if (isDone) { - params.onError(toError('duplicate forward middleware call', err)); - return; - } - isDone = true; - if ( - err || - (params.wres && // Non one-way messages. - (params.wres.error !== undefined || params.wres.response !== undefined)) - ) { - // Stop the forward phase, bypass the handler, and start the backward - // phase. Note that we ignore any callback argument in this case. - cause = err; - backward(); - return; - } - if (cb) { - cbs.push(cb); - } - forward(++pos); - }), - ); - } else { - // Done with the middleware forward functions, call the handler. - params.onTransition.apply( - params.ctx, - args.concat(function (err) { - if (isDone) { - params.onError(toError('duplicate handler call', err)); - return; - } - isDone = true; - cause = err; - process.nextTick(backward); - }), - ); - } - } - - function backward() { - var cb = cbs.pop(); - if (cb) { - var isDone = false; - cb.call(params.ctx, cause, function (err) { - if (isDone) { - params.onError(toError('duplicate backward middleware call', err)); - return; - } - // Substitute the error. - cause = err; - isDone = true; - backward(); - }); - } else { - // Done with all middleware calls. - params.onCompletion.call(params.ctx, cause); - } - } -} - -module.exports = { - Adapter: Adapter, - HANDSHAKE_REQUEST_TYPE: HANDSHAKE_REQUEST_TYPE, - HANDSHAKE_RESPONSE_TYPE: HANDSHAKE_RESPONSE_TYPE, - Message: Message, - Registry: Registry, - Service: Service, - discoverProtocol: discoverProtocol, - streams: { - FrameDecoder: FrameDecoder, - FrameEncoder: FrameEncoder, - NettyDecoder: NettyDecoder, - NettyEncoder: NettyEncoder, - }, -}; diff --git a/forward_engineering/modules/avsc/lib/specs.js b/forward_engineering/modules/avsc/lib/specs.js deleted file mode 100644 index 2ab8cce5..00000000 --- a/forward_engineering/modules/avsc/lib/specs.js +++ /dev/null @@ -1,727 +0,0 @@ -/* jshint node: true */ - -// TODO: Add minimal templating. -// TODO: Add option to prefix nested type declarations with the outer types' -// names. - -'use strict'; - -/** IDL to protocol (services) and schema (types) parsing logic. */ - -var files = require('./files'), - utils = require('./utils'), - path = require('path'), - util = require('util'); - -var f = util.format; - -// Default type references defined by Avro. -var TYPE_REFS = { - date: { type: 'int', logicalType: 'date' }, - decimal: { type: 'bytes', logicalType: 'decimal' }, - time_ms: { type: 'long', logicalType: 'time-millis' }, - timestamp_ms: { type: 'long', logicalType: 'timestamp-millis' }, -}; - -/** Assemble an IDL file into a decoded protocol. */ -function assembleProtocol(fpath, opts, cb) { - if (!cb && typeof opts == 'function') { - cb = opts; - opts = undefined; - } - opts = opts || {}; - if (!opts.importHook) { - opts.importHook = files.createImportHook(); - } - - // Types found in imports. We store them separately to be able to insert them - // in the correct order in the final attributes. - var importedTypes = []; - var protocol, imports; - opts.importHook(fpath, 'idl', function (err, str) { - if (err) { - cb(err); - return; - } - if (str === undefined) { - // Skipped import (likely already imported). - cb(null, {}); - return; - } - try { - var reader = new Reader(str, opts); - var obj = reader._readProtocol(str, opts); - } catch (err) { - err.path = fpath; // To help debug which file caused the error. - cb(err); - return; - } - protocol = obj.protocol; - imports = obj.imports; - fetchImports(); - }); - - function fetchImports() { - var info = imports.shift(); - if (!info) { - // We are done with this file. We prepend all imported types to this - // file's and we can return the final result. - if (importedTypes.length) { - protocol.types = protocol.types ? importedTypes.concat(protocol.types) : importedTypes; - } - cb(null, protocol); - } else { - var importPath = path.join(path.dirname(fpath), info.name); - if (info.kind === 'idl') { - assembleProtocol(importPath, opts, mergeImportedSchema); - } else { - // We are importing a protocol or schema file. - opts.importHook(importPath, info.kind, function (err, str) { - if (err) { - cb(err); - return; - } - switch (info.kind) { - case 'protocol': - case 'schema': - if (str === undefined) { - // Flag used to signal an already imported file by the default - // import hooks. Implementors who wish to disallow duplicate - // imports should provide a custom hook which throws an error - // when a duplicate import is detected. - mergeImportedSchema(null, {}); - return; - } - try { - var obj = JSON.parse(str); - } catch (err) { - err.path = importPath; - cb(err); - return; - } - var schema = info.kind === 'schema' ? { types: [obj] } : obj; - mergeImportedSchema(null, schema); - break; - default: - cb(new Error(f('invalid import kind: %s', info.kind))); - } - }); - } - } - } - - function mergeImportedSchema(err, importedSchema) { - if (err) { - cb(err); - return; - } - // Merge first the types (where we don't need to check for duplicates - // since instantiating the service will take care of it), then the messages - // (where we need to, as duplicates will overwrite each other). - (importedSchema.types || []).forEach(function (typeSchema) { - // Ensure the imported protocol's namespace is inherited correctly (it - // might be different from the current one). - if (typeSchema.namespace === undefined) { - var namespace = importedSchema.namespace; - if (!namespace) { - var match = /^(.*)\.[^.]+$/.exec(importedSchema.protocol); - if (match) { - namespace = match[1]; - } - } - typeSchema.namespace = namespace || ''; - } - importedTypes.push(typeSchema); - }); - try { - Object.keys(importedSchema.messages || {}).forEach(function (name) { - if (!protocol.messages) { - protocol.messages = {}; - } - if (protocol.messages[name]) { - throw new Error(f('duplicate message: %s', name)); - } - protocol.messages[name] = importedSchema.messages[name]; - }); - } catch (err) { - cb(err); - return; - } - fetchImports(); // Continue importing any remaining imports. - } -} - -// Parsing functions. - -/** - * Convenience function to parse multiple inputs into protocols and schemas. - * - * It should cover most basic use-cases but has a few limitations: - * - * + It doesn't allow passing options to the parsing step. - * + The protocol/type inference logic can be deceived. - * - * The parsing logic is as follows: - * - * + If `str` contains `path.sep` (on windows `\`, otherwise `/`) and is a path - * to an existing file, it will first be read as JSON, then as an IDL - * specification if JSON parsing failed. If either succeeds, the result is - * returned, otherwise the next steps are run using the file's content - * instead of the input path. - * + If `str` is a valid JSON string, it is parsed then returned. - * + If `str` is a valid IDL protocol specification, it is parsed and returned - * if no imports are present (and an error is thrown if there are any - * imports). - * + If `str` is a valid IDL type specification, it is parsed and returned. - * + If neither of the above cases apply, `str` is returned. - */ -function read(str) { - var schema; - if (typeof str == 'string' && ~str.indexOf(path.sep) && files.existsSync(str)) { - // Try interpreting `str` as path to a file contain a JSON schema or an IDL - // protocol. Note that we add the second check to skip primitive references - // (e.g. `"int"`, the most common use-case for `avro.parse`). - var contents = files.readFileSync(str, { encoding: 'utf8' }); - try { - return JSON.parse(contents); - } catch (err) { - var opts = { importHook: files.createSyncImportHook() }; - assembleProtocol(str, opts, function (err, protocolSchema) { - schema = err ? contents : protocolSchema; - }); - } - } else { - schema = str; - } - if (typeof schema != 'string' || schema === 'null') { - // This last predicate is to allow `read('null')` to work similarly to - // `read('int')` and other primitives (null needs to be handled separately - // since it is also a valid JSON identifier). - return schema; - } - try { - return JSON.parse(schema); - } catch (err) { - try { - return Reader.readProtocol(schema); - } catch (err) { - try { - return Reader.readSchema(schema); - } catch (err) { - return schema; - } - } - } -} - -function Reader(str, opts) { - opts = opts || {}; - - this._tk = new Tokenizer(str); - this._ackVoidMessages = !!opts.ackVoidMessages; - this._implicitTags = !opts.delimitedCollections; - this._typeRefs = opts.typeRefs || TYPE_REFS; -} - -Reader.readProtocol = function (str, opts) { - var reader = new Reader(str, opts); - var protocol = reader._readProtocol(); - if (protocol.imports.length) { - // Imports can only be resolved when the IDL file is provided via its - // path, we fail rather than silently ignore imports. - throw new Error('unresolvable import'); - } - return protocol.protocol; -}; - -Reader.readSchema = function (str, opts) { - var reader = new Reader(str, opts); - var javadoc = reader._readJavadoc(); - var schema = reader._readType(javadoc === undefined ? {} : { doc: javadoc }); - reader._tk.next({ id: '(eof)' }); // Check that we have read everything. - return schema; -}; - -Reader.prototype._readProtocol = function () { - var tk = this._tk; - var imports = []; - var types = []; - var messages = {}; - var pos; - - // Outer declarations (outside of the protocol block). - this._readImports(imports); - var protocolSchema = {}; - var protocolJavadoc = this._readJavadoc(); - if (protocolJavadoc !== undefined) { - protocolSchema.doc = protocolJavadoc; - } - this._readAnnotations(protocolSchema); - tk.next({ val: 'protocol' }); - if (!tk.next({ val: '{', silent: true })) { - // Named protocol. - protocolSchema.protocol = tk.next({ id: 'name' }).val; - tk.next({ val: '{' }); - } - - // Inner declarations. - while (!tk.next({ val: '}', silent: true })) { - if (!this._readImports(imports)) { - var javadoc = this._readJavadoc(); - var typeSchema = this._readType(); - var numImports = this._readImports(imports, true); - var message = undefined; - // We mark our position and try to parse a message from here. - pos = tk.pos; - if (!numImports && (message = this._readMessage(typeSchema))) { - // Note that if any imports were found, we cannot be parsing a message. - if (javadoc !== undefined && message.schema.doc === undefined) { - message.schema.doc = javadoc; - } - var oneWay = false; - if (message.schema.response === 'void' || message.schema.response.type === 'void') { - oneWay = !this._ackVoidMessages && !message.schema.errors; - if (message.schema.response === 'void') { - message.schema.response = 'null'; - } else { - message.schema.response.type = 'null'; - } - } - if (oneWay) { - message.schema['one-way'] = true; - } - if (messages[message.name]) { - // We have to do this check here otherwise the duplicate will be - // overwritten (and service instantiation won't be able to catch it). - throw new Error(f('duplicate message: %s', message.name)); - } - messages[message.name] = message.schema; - } else { - // This was a standalone type definition. - if (javadoc) { - if (typeof typeSchema == 'string') { - typeSchema = { doc: javadoc, type: typeSchema }; - } else if (typeSchema.doc === undefined) { - typeSchema.doc = javadoc; - } - } - types.push(typeSchema); - // We backtrack until just before the type's type name and swallow an - // eventual semi-colon (to make type declarations more consistent). - tk.pos = pos; - tk.next({ val: ';', silent: true }); - } - javadoc = undefined; - } - } - tk.next({ id: '(eof)' }); - if (types.length) { - protocolSchema.types = types; - } - if (Object.keys(messages).length) { - protocolSchema.messages = messages; - } - return { protocol: protocolSchema, imports: imports }; -}; - -Reader.prototype._readAnnotations = function (schema) { - var tk = this._tk; - while (tk.next({ val: '@', silent: true })) { - // Annotations are allowed to have names which aren't valid Avro names, - // we must advance until we hit the first left parenthesis. - var parts = []; - while (!tk.next({ val: '(', silent: true })) { - parts.push(tk.next().val); - } - schema[parts.join('')] = tk.next({ id: 'json' }).val; - tk.next({ val: ')' }); - } -}; - -Reader.prototype._readMessage = function (responseSchema) { - var tk = this._tk; - var schema = { request: [], response: responseSchema }; - this._readAnnotations(schema); - var name = tk.next().val; - if (tk.next().val !== '(') { - // This isn't a message. - return; - } - if (!tk.next({ val: ')', silent: true })) { - do { - schema.request.push(this._readField()); - } while (!tk.next({ val: ')', silent: true }) && tk.next({ val: ',' })); - } - var token = tk.next(); - switch (token.val) { - case 'throws': - // It doesn't seem like the IDL is explicit about which syntax to used - // for multiple errors. We will assume a comma-separated list. - schema.errors = []; - do { - schema.errors.push(this._readType()); - } while (!tk.next({ val: ';', silent: true }) && tk.next({ val: ',' })); - break; - case 'oneway': - schema['one-way'] = true; - tk.next({ val: ';' }); - break; - case ';': - break; - default: - throw tk.error('invalid message suffix', token); - } - return { name: name, schema: schema }; -}; - -Reader.prototype._readJavadoc = function () { - var token = this._tk.next({ id: 'javadoc', emitJavadoc: true, silent: true }); - if (token) { - return token.val; - } -}; - -Reader.prototype._readField = function () { - var tk = this._tk; - var javadoc = this._readJavadoc(); - var schema = { type: this._readType() }; - if (javadoc !== undefined && schema.doc === undefined) { - schema.doc = javadoc; - } - this._readAnnotations(schema); - schema.name = tk.next({ id: 'name' }).val; - if (tk.next({ val: '=', silent: true })) { - schema['default'] = tk.next({ id: 'json' }).val; - } - return schema; -}; - -Reader.prototype._readType = function (schema) { - schema = schema || {}; - this._readAnnotations(schema); - schema.type = this._tk.next({ id: 'name' }).val; - switch (schema.type) { - case 'record': - case 'error': - return this._readRecord(schema); - case 'fixed': - return this._readFixed(schema); - case 'enum': - return this._readEnum(schema); - case 'map': - return this._readMap(schema); - case 'array': - return this._readArray(schema); - case 'union': - if (Object.keys(schema).length > 1) { - throw new Error('union annotations are not supported'); - } - return this._readUnion(); - default: - // Reference. - var ref = this._typeRefs[schema.type]; - if (ref) { - delete schema.type; // Always overwrite the type. - utils.copyOwnProperties(ref, schema); - } - return Object.keys(schema).length > 1 ? schema : schema.type; - } -}; - -Reader.prototype._readFixed = function (schema) { - var tk = this._tk; - if (!tk.next({ val: '(', silent: true })) { - schema.name = tk.next({ id: 'name' }).val; - tk.next({ val: '(' }); - } - schema.size = parseInt(tk.next({ id: 'number' }).val); - tk.next({ val: ')' }); - return schema; -}; - -Reader.prototype._readMap = function (schema) { - var tk = this._tk; - // Brackets are unwieldy when declaring inline types. We allow for them to be - // omitted (but we keep the consistency that if the entry bracket is present, - // the exit one must be as well). Note that this is non-standard. - var silent = this._implicitTags; - var implicitTags = tk.next({ val: '<', silent: silent }) === undefined; - schema.values = this._readType(); - tk.next({ val: '>', silent: implicitTags }); - return schema; -}; - -Reader.prototype._readArray = function (schema) { - var tk = this._tk; - var silent = this._implicitTags; - var implicitTags = tk.next({ val: '<', silent: silent }) === undefined; - schema.items = this._readType(); - tk.next({ val: '>', silent: implicitTags }); - return schema; -}; - -Reader.prototype._readEnum = function (schema) { - var tk = this._tk; - if (!tk.next({ val: '{', silent: true })) { - schema.name = tk.next({ id: 'name' }).val; - tk.next({ val: '{' }); - } - schema.symbols = []; - do { - schema.symbols.push(tk.next().val); - } while (!tk.next({ val: '}', silent: true }) && tk.next({ val: ',' })); - return schema; -}; - -Reader.prototype._readUnion = function () { - var tk = this._tk; - var arr = []; - tk.next({ val: '{' }); - do { - arr.push(this._readType()); - } while (!tk.next({ val: '}', silent: true }) && tk.next({ val: ',' })); - return arr; -}; - -Reader.prototype._readRecord = function (schema) { - var tk = this._tk; - if (!tk.next({ val: '{', silent: true })) { - schema.name = tk.next({ id: 'name' }).val; - tk.next({ val: '{' }); - } - schema.fields = []; - while (!tk.next({ val: '}', silent: true })) { - schema.fields.push(this._readField()); - tk.next({ val: ';' }); - } - return schema; -}; - -Reader.prototype._readImports = function (imports, maybeMessage) { - var tk = this._tk; - var numImports = 0; - var pos = tk.pos; - while (tk.next({ val: 'import', silent: true })) { - if (!numImports && maybeMessage && tk.next({ val: '(', silent: true })) { - // This will happen if a message is named import. - tk.pos = pos; - return; - } - var kind = tk.next({ id: 'name' }).val; - var fname = JSON.parse(tk.next({ id: 'string' }).val); - tk.next({ val: ';' }); - imports.push({ kind: kind, name: fname }); - numImports++; - } - return numImports; -}; - -// Helpers. - -/** - * Simple class to split an input string into tokens. - * - * There are different types of tokens, characterized by their `id`: - * - * + `number` numbers. - * + `name` references. - * + `string` double-quoted. - * + `operator`, anything else, always single character. - * + `javadoc`, only emitted when `next` is called with `emitJavadoc` set. - * + `json`, only emitted when `next` is called with `'json'` as `id` (the - * tokenizer doesn't have enough context to predict these). - */ -function Tokenizer(str) { - this._str = str; - this.pos = 0; -} - -Tokenizer.prototype.next = function (opts) { - var token = { pos: this.pos, id: undefined, val: undefined }; - var javadoc = this._skip(opts && opts.emitJavadoc); - if (javadoc) { - token.id = 'javadoc'; - token.val = javadoc; - } else { - var pos = this.pos; - var str = this._str; - var c = str.charAt(pos); - if (!c) { - token.id = '(eof)'; - } else { - if (opts && opts.id === 'json') { - token.id = 'json'; - this.pos = this._endOfJson(); - } else if (c === '"') { - token.id = 'string'; - this.pos = this._endOfString(); - } else if (/[0-9]/.test(c)) { - token.id = 'number'; - this.pos = this._endOf(/[0-9]/); - } else if (/[`A-Za-z_.]/.test(c)) { - token.id = 'name'; - this.pos = this._endOf(/[`A-Za-z0-9_.]/); - } else { - token.id = 'operator'; - this.pos = pos + 1; - } - token.val = str.slice(pos, this.pos); - if (token.id === 'json') { - // Let's be nice and give a more helpful error message when this occurs - // (JSON parsing errors wouldn't let us find the location otherwise). - try { - token.val = JSON.parse(token.val); - } catch (err) { - throw this.error('invalid JSON', token); - } - } else if (token.id === 'name') { - // Unescape names (our parser doesn't need them). - token.val = token.val.replace(/`/g, ''); - } - } - } - - var err; - if (opts && opts.id && opts.id !== token.id) { - err = this.error(f('expected ID %s', opts.id), token); - } else if (opts && opts.val && opts.val !== token.val) { - err = this.error(f('expected value %s', opts.val), token); - } - if (!err) { - return token; - } else if (opts && opts.silent) { - this.pos = token.pos; // Backtrack to start of token. - return undefined; - } else { - throw err; - } -}; - -Tokenizer.prototype.error = function (reason, context) { - // Context must be either a token or a position. - var isToken = typeof context != 'number'; - var pos = isToken ? context.pos : context; - var str = this._str; - var lineNum = 1; - var lineStart = 0; - var i; - for (i = 0; i < pos; i++) { - if (str.charAt(i) === '\n') { - lineNum++; - lineStart = i; - } - } - var msg = isToken ? f('invalid token %j: %s', context, reason) : reason; - var err = new Error(msg); - err.token = isToken ? context : undefined; - err.lineNum = lineNum; - err.colNum = pos - lineStart; - return err; -}; - -/** Skip whitespace and comments. */ -Tokenizer.prototype._skip = function (emitJavadoc) { - var str = this._str; - var isJavadoc = false; - var pos, c; - - while ((c = str.charAt(this.pos)) && /\s/.test(c)) { - this.pos++; - } - pos = this.pos; - if (c === '/') { - switch (str.charAt(this.pos + 1)) { - case '/': - this.pos += 2; - while ((c = str.charAt(this.pos)) && c !== '\n') { - this.pos++; - } - return this._skip(emitJavadoc); - case '*': - this.pos += 2; - if (str.charAt(this.pos) === '*') { - isJavadoc = true; - } - while ((c = str.charAt(this.pos++))) { - if (c === '*' && str.charAt(this.pos) === '/') { - this.pos++; - if (isJavadoc && emitJavadoc) { - return extractJavadoc(str.slice(pos + 3, this.pos - 2)); - } - return this._skip(emitJavadoc); - } - } - throw this.error('unterminated comment', pos); - } - } -}; - -/** Generic end of method. */ -Tokenizer.prototype._endOf = function (pat) { - var pos = this.pos; - var str = this._str; - while (pat.test(str.charAt(pos))) { - pos++; - } - return pos; -}; - -/** Find end of a string. */ -Tokenizer.prototype._endOfString = function () { - var pos = this.pos + 1; // Skip first double quote. - var str = this._str; - var c; - while ((c = str.charAt(pos))) { - if (c === '"') { - // The spec doesn't explicitly say so, but IDLs likely only - // allow double quotes for strings (C- and Java-style). - return pos + 1; - } - if (c === '\\') { - pos += 2; - } else { - pos++; - } - } - throw this.error('unterminated string', pos - 1); -}; - -/** Find end of JSON object, throwing an error if the end is reached first. */ -Tokenizer.prototype._endOfJson = function () { - var pos = utils.jsonEnd(this._str, this.pos); - if (pos < 0) { - throw this.error('invalid JSON', pos); - } - return pos; -}; - -/** - * Extract Javadoc contents from the comment. - * - * The parsing done is very simple and simply removes the line prefixes and - * leading / trailing empty lines. It's better to be conservative with - * formatting rather than risk losing information. - */ -function extractJavadoc(str) { - var lines = str - .replace(/^[ \t]+|[ \t]+$/g, '') // Trim whitespace. - .split('\n') - .map(function (line, i) { - return i ? line.replace(/^\s*\*\s?/, '') : line; - }); - while (!lines[0]) { - lines.shift(); - } - while (!lines[lines.length - 1]) { - lines.pop(); - } - return lines.join('\n'); -} - -module.exports = { - Tokenizer: Tokenizer, - assembleProtocol: assembleProtocol, - read: read, - readProtocol: Reader.readProtocol, - readSchema: Reader.readSchema, -}; diff --git a/forward_engineering/modules/avsc/lib/types.js b/forward_engineering/modules/avsc/lib/types.js deleted file mode 100644 index b0359e5a..00000000 --- a/forward_engineering/modules/avsc/lib/types.js +++ /dev/null @@ -1,3454 +0,0 @@ -/* jshint node: true */ - -// TODO: Make it easier to implement custom types. This will likely require -// exposing the `Tap` object, perhaps under another name. Probably worth a -// major release. -// TODO: Allow configuring when to write the size when writing arrays and maps, -// and customizing their block size. -// TODO: Code-generate `compare` and `clone` record and union methods. - -'use strict'; - -/** - * This module defines all Avro data types and their serialization logic. - * - */ - -var utils = require('./utils'), - buffer = require('buffer'), // For `SlowBuffer`. - util = require('util'); - -// Convenience imports. -var Tap = utils.Tap; -var debug = util.debuglog('avsc:types'); -var f = util.format; - -// All non-union concrete (i.e. non-logical) Avro types. -var TYPES = { - 'array': ArrayType, - 'boolean': BooleanType, - 'bytes': BytesType, - 'double': DoubleType, - 'enum': EnumType, - 'error': RecordType, - 'fixed': FixedType, - 'float': FloatType, - 'int': IntType, - 'long': LongType, - 'map': MapType, - 'null': NullType, - 'record': RecordType, - 'string': StringType, -}; - -// Valid (field, type, and symbol) name regex. -var NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/; - -// Random generator. -var RANDOM = new utils.Lcg(); - -// Encoding tap (shared for performance). -var TAP = new Tap(new buffer.SlowBuffer(1024)); - -// Currently active logical type, used for name redirection. -var LOGICAL_TYPE = null; - -// Underlying types of logical types currently being instantiated. This is used -// to be able to reference names (i.e. for branches) during instantiation. -var UNDERLYING_TYPES = []; - -/** - * "Abstract" base Avro type. - * - * This class' constructor will register any named types to support recursive - * schemas. All type values are represented in memory similarly to their JSON - * representation, except for: - * - * + `bytes` and `fixed` which are represented as `Buffer`s. - * + `union`s which will be "unwrapped" unless the `wrapUnions` option is set. - * - * See individual subclasses for details. - */ -function Type(schema, opts) { - var type; - if (LOGICAL_TYPE) { - type = LOGICAL_TYPE; - UNDERLYING_TYPES.push([LOGICAL_TYPE, this]); - LOGICAL_TYPE = null; - } else { - type = this; - } - - // Lazily instantiated hash string. It will be generated the first time the - // type's default fingerprint is computed (for example when using `equals`). - // We use a mutable object since types are frozen after instantiation. - this._hash = new Hash(); - this.name = undefined; - this.aliases = undefined; - this.doc = schema && schema.doc ? '' + schema.doc : undefined; - - if (schema) { - // This is a complex (i.e. non-primitive) type. - var name = schema.name; - var namespace = schema.namespace === undefined ? opts && opts.namespace : schema.namespace; - if (name !== undefined) { - // This isn't an anonymous type. - name = qualify(name, namespace); - if (isPrimitive(name)) { - // Avro doesn't allow redefining primitive names. - Type.addError(new Error(f('cannot rename primitive type: %j', name))); - } - var registry = opts && opts.registry; - if (registry) { - if (registry[name] !== undefined) { - Type.addError(new Error(f('duplicate type name: %s', name))); - } - registry[name] = type; - } - } else if (opts && opts.noAnonymousTypes) { - Type.addError(new Error(f('missing name property in schema: %j', schema))); - } - this.name = name; - this.aliases = schema.aliases - ? schema.aliases.map(function (s) { - return qualify(s, namespace); - }) - : []; - } -} - -Type.forSchema = function (schema, opts) { - opts = opts || {}; - opts.registry = opts.registry || {}; - - var UnionType = (function (wrapUnions) { - if (wrapUnions === true) { - wrapUnions = 'always'; - } else if (wrapUnions === false) { - wrapUnions = 'never'; - } else if (wrapUnions === undefined) { - wrapUnions = 'auto'; - } else if (typeof wrapUnions == 'string') { - wrapUnions = wrapUnions.toLowerCase(); - } - switch (wrapUnions) { - case 'always': - return WrappedUnionType; - case 'never': - return UnwrappedUnionType; - case 'auto': - return undefined; // Determined dynamically later on. - default: - Type.addError(new Error(f('invalid wrap unions option: %j', wrapUnions))); - } - })(opts.wrapUnions); - - if (schema === null) { - // Let's be helpful for this common error. - Type.addError(new Error('invalid type: null (did you mean "null"?)')); - } - - if (Type.isType(schema)) { - return schema; - } - - var type; - if (opts.typeHook && (type = opts.typeHook(schema, opts))) { - if (!Type.isType(type)) { - Type.addError(new Error(f('invalid typehook return value: %j', type))); - } - return type; - } - - if (typeof schema == 'string') { - // Type reference. - schema = qualify(schema, opts.namespace); - type = opts.registry[schema]; - if (type) { - // Type was already defined, return it. - return type; - } - if (isPrimitive(schema)) { - // Reference to a primitive type. These are also defined names by default - // so we create the appropriate type and it to the registry for future - // reference. - return (opts.registry[schema] = Type.forSchema({ type: schema }, opts)); - } - // Type.addError(new Error(f('undefined type name: %s', schema))); - - return schema; - } - - if (schema.logicalType && opts.logicalTypes && !LOGICAL_TYPE) { - var DerivedType = opts.logicalTypes[schema.logicalType]; - if (DerivedType) { - var namespace = opts.namespace; - var registry = {}; - Object.keys(opts.registry).forEach(function (key) { - registry[key] = opts.registry[key]; - }); - try { - debug('instantiating logical type for %s', schema.logicalType); - return new DerivedType(schema, opts); - } catch (err) { - debug('failed to instantiate logical type for %s', schema.logicalType); - if (opts.assertLogicalTypes) { - // The spec mandates that we fall through to the underlying type if - // the logical type is invalid. We provide this option to ease - // debugging. - throw err; - } - LOGICAL_TYPE = null; - opts.namespace = namespace; - opts.registry = registry; - } - } - } - - if (Array.isArray(schema)) { - // Union. - var types = schema.map(function (obj) { - return Type.forSchema(obj, opts); - }); - if (!UnionType) { - UnionType = isAmbiguous(types) ? WrappedUnionType : UnwrappedUnionType; - } - type = new UnionType(types, opts); - } else { - // New type definition. - type = (function (typeName) { - var Type = TYPES[typeName]; - if (Type === undefined) { - throw new Error(f('unknown type: %j', typeName)); - } - return new Type(schema, opts); - })(schema.type); - } - return type; -}; - -Type.forValue = function (val, opts) { - opts = opts || {}; - - // Sentinel used when inferring the types of empty arrays. - opts.emptyArrayType = - opts.emptyArrayType || - Type.forSchema({ - type: 'array', - items: 'null', - }); - - // Optional custom inference hook. - if (opts.valueHook) { - var type = opts.valueHook(val, opts); - if (type !== undefined) { - if (!Type.isType(type)) { - Type.addError(new Error(f('invalid value hook return value: %j', type))); - } - return type; - } - } - - // Default inference logic. - switch (typeof val) { - case 'string': - return Type.forSchema('string', opts); - case 'boolean': - return Type.forSchema('boolean', opts); - case 'number': - if ((val | 0) === val) { - return Type.forSchema('int', opts); - } else if (Math.abs(val) < 9007199254740991) { - return Type.forSchema('float', opts); - } - return Type.forSchema('double', opts); - case 'object': - if (val === null) { - return Type.forSchema('null', opts); - } else if (Array.isArray(val)) { - if (!val.length) { - return opts.emptyArrayType; - } - return Type.forSchema( - { - type: 'array', - items: Type.forTypes( - val.map(function (v) { - return Type.forValue(v, opts); - }), - opts, - ), - }, - opts, - ); - } else if (Buffer.isBuffer(val)) { - return Type.forSchema('bytes', opts); - } - var fieldNames = Object.keys(val); - if ( - fieldNames.some(function (s) { - return !isValidName(s); - }) - ) { - // We have to fall back to a map. - return Type.forSchema( - { - type: 'map', - values: Type.forTypes( - fieldNames.map(function (s) { - return Type.forValue(val[s], opts); - }), - opts, - ), - }, - opts, - ); - } - return Type.forSchema( - { - type: 'record', - fields: fieldNames.map(function (s) { - return { name: s, type: Type.forValue(val[s], opts) }; - }), - }, - opts, - ); - default: - Type.addError(new Error(f('cannot infer type from: %j', val))); - } -}; - -Type.forTypes = function (types, opts) { - if (!types.length) { - Type.addError(new Error('no types to combine')); - } - if (types.length === 1) { - return types[0]; // Nothing to do. - } - opts = opts || {}; - - // Extract any union types, with special care for wrapped unions (see below). - var expanded = []; - var numWrappedUnions = 0; - var isValidWrappedUnion = true; - types.forEach(function (type) { - switch (type.typeName) { - case 'union:unwrapped': - isValidWrappedUnion = false; - expanded = expanded.concat(type.types); - break; - case 'union:wrapped': - numWrappedUnions++; - expanded = expanded.concat(type.types); - break; - case 'null': - expanded.push(type); - break; - default: - isValidWrappedUnion = false; - expanded.push(type); - } - }); - if (numWrappedUnions) { - if (!isValidWrappedUnion) { - // It is only valid to combine wrapped unions when no other type is - // present other than wrapped unions and nulls (otherwise the values of - // others wouldn't be valid in the resulting union). - Type.addError(new Error('cannot combine wrapped union')); - } - var branchTypes = {}; - expanded.forEach(function (type) { - var name = type.branchName; - var branchType = branchTypes[name]; - if (!branchType) { - branchTypes[name] = type; - } else if (!type.equals(branchType)) { - Type.addError(new Error('inconsistent branch type')); - } - }); - var wrapUnions = opts.wrapUnions; - var unionType; - opts.wrapUnions = true; - try { - unionType = Type.forSchema( - Object.keys(branchTypes).map(function (name) { - return branchTypes[name]; - }), - opts, - ); - } catch (err) { - opts.wrapUnions = wrapUnions; - throw err; - } - opts.wrapUnions = wrapUnions; - return unionType; - } - - // Group types by category, similar to the logic for unwrapped unions. - var bucketized = {}; - expanded.forEach(function (type) { - var bucket = getTypeBucket(type); - var bucketTypes = bucketized[bucket]; - if (!bucketTypes) { - bucketized[bucket] = bucketTypes = []; - } - bucketTypes.push(type); - }); - - // Generate the "augmented" type for each group. - var buckets = Object.keys(bucketized); - var augmented = buckets.map(function (bucket) { - var bucketTypes = bucketized[bucket]; - if (bucketTypes.length === 1) { - return bucketTypes[0]; - } else { - switch (bucket) { - case 'null': - case 'boolean': - return bucketTypes[0]; - case 'number': - return combineNumbers(bucketTypes); - case 'string': - return combineStrings(bucketTypes, opts); - case 'buffer': - return combineBuffers(bucketTypes, opts); - case 'array': - // Remove any sentinel arrays (used when inferring from empty arrays) - // to avoid making things nullable when they shouldn't be. - bucketTypes = bucketTypes.filter(function (t) { - return t !== opts.emptyArrayType; - }); - if (!bucketTypes.length) { - // We still don't have a real type, just return the sentinel. - return opts.emptyArrayType; - } - return Type.forSchema( - { - type: 'array', - items: Type.forTypes( - bucketTypes.map(function (t) { - return t.itemsType; - }), - opts, - ), - }, - opts, - ); - default: - return combineObjects(bucketTypes, opts); - } - } - }); - - if (augmented.length === 1) { - return augmented[0]; - } else { - // We return an (unwrapped) union of all augmented types. - return Type.forSchema(augmented, opts); - } -}; - -Type.isType = function (/* any, [prefix] ... */) { - var l = arguments.length; - if (!l) { - return false; - } - - var any = arguments[0]; - if (!any || typeof any._update != 'function' || typeof any.fingerprint != 'function') { - // Not fool-proof, but most likely good enough. - return false; - } - - if (l === 1) { - // No type names specified, we are done. - return true; - } - - // We check if at least one of the prefixes matches. - var typeName = any.typeName; - var i; - for (i = 1; i < l; i++) { - if (typeName.indexOf(arguments[i]) === 0) { - return true; - } - } - return false; -}; - -Type.__reset = function (size) { - debug('resetting type buffer to %d', size); - TAP.buf = new buffer.SlowBuffer(size); -}; - -Object.defineProperty(Type.prototype, 'branchName', { - enumerable: true, - get: function () { - if (this.name) { - return this.name; - } - var type = Type.isType(this, 'logical') ? this.underlyingType : this; - if (Type.isType(type, 'abstract')) { - return type._concreteTypeName; - } - return Type.isType(type, 'union') ? undefined : type.typeName; - }, -}); - -Type.prototype.clone = function (val, opts) { - if (opts) { - opts = { - coerce: !!opts.coerceBuffers | 0, // Coerce JSON to Buffer. - fieldHook: opts.fieldHook, - qualifyNames: !!opts.qualifyNames, - skip: !!opts.skipMissingFields, - wrap: !!opts.wrapUnions | 0, // Wrap first match into union. - }; - return this._copy(val, opts); - } else { - // If no modifications are required, we can get by with a serialization - // roundtrip (generally much faster than a standard deep copy). - return this.fromBuffer(this.toBuffer(val)); - } -}; - -Type.prototype.compare = utils.abstractFunction; - -Type.prototype.compareBuffers = function (buf1, buf2) { - return this._match(new Tap(buf1), new Tap(buf2)); -}; - -Type.prototype.createResolver = function (type, opts) { - if (!Type.isType(type)) { - // More explicit error message than the "incompatible type" thrown - // otherwise (especially because of the overridden `toJSON` method). - Type.addError(new Error(f('not a type: %j', type))); - } - - if (!Type.isType(this, 'union', 'logical') && Type.isType(type, 'logical')) { - // Trying to read a logical type as a built-in: unwrap the logical type. - // Note that we exclude unions to support resolving into unions containing - // logical types. - return this.createResolver(type.underlyingType, opts); - } - - opts = opts || {}; - opts.registry = opts.registry || {}; - - var resolver, key; - if (Type.isType(this, 'record', 'error') && Type.isType(type, 'record', 'error')) { - // We allow conversions between records and errors. - key = this.name + ':' + type.name; // ':' is illegal in Avro type names. - resolver = opts.registry[key]; - if (resolver) { - return resolver; - } - } - - resolver = new Resolver(this); - if (key) { - // Register resolver early for recursive schemas. - opts.registry[key] = resolver; - } - - if (Type.isType(type, 'union')) { - var resolvers = type.types.map(function (t) { - return this.createResolver(t, opts); - }, this); - resolver._read = function (tap) { - var index = tap.readLong(); - var resolver = resolvers[index]; - if (resolver === undefined) { - Type.addError(new Error(f('invalid union index: %s', index))); - } - return resolvers[index]._read(tap); - }; - } else { - this._update(resolver, type, opts); - } - - if (!resolver._read) { - Type.addError(new Error(f('cannot read %s as %s', type, this))); - } - return Object.freeze(resolver); -}; - -Type.prototype.decode = function (buf, pos, resolver) { - var tap = new Tap(buf, pos); - var val = readValue(this, tap, resolver); - if (!tap.isValid()) { - return { value: undefined, offset: -1 }; - } - return { value: val, offset: tap.pos }; -}; - -Type.prototype.encode = function (val, buf, pos) { - var tap = new Tap(buf, pos); - this._write(tap, val); - if (!tap.isValid()) { - // Don't throw as there is no way to predict this. We also return the - // number of missing bytes to ease resizing. - return buf.length - tap.pos; - } - return tap.pos; -}; - -Type.prototype.equals = function (type) { - return Type.isType(type) && this.fingerprint().equals(type.fingerprint()); -}; - -Type.prototype.fingerprint = function (algorithm) { - if (!algorithm) { - if (!this._hash.str) { - var schemaStr = JSON.stringify(this.schema()); - this._hash.str = utils.getHash(schemaStr).toString('binary'); - } - return utils.bufferFrom(this._hash.str, 'binary'); - } else { - return utils.getHash(JSON.stringify(this.schema()), algorithm); - } -}; - -Type.prototype.fromBuffer = function (buf, resolver, noCheck) { - var tap = new Tap(buf); - var val = readValue(this, tap, resolver, noCheck); - if (!tap.isValid()) { - Type.addError(new Error('truncated buffer')); - } - if (!noCheck && tap.pos < buf.length) { - Type.addError(new Error('trailing data')); - } - return val; -}; - -Type.prototype.fromString = function (str) { - return this._copy(JSON.parse(str), { coerce: 2 }); -}; - -Type.prototype.inspect = function () { - var typeName = this.typeName; - var className = getClassName(typeName); - if (isPrimitive(typeName)) { - // The class name is sufficient to identify the type. - return f('<%s>', className); - } else { - // We add a little metadata for convenience. - var obj = this.schema({ exportAttrs: true, noDeref: true }); - if (typeof obj == 'object' && !Type.isType(this, 'logical')) { - obj.type = undefined; // Would be redundant with constructor name. - } - return f('<%s %j>', className, obj); - } -}; - -Type.prototype.isValid = function (val, opts) { - // We only have a single flag for now, so no need to complicate things. - var flags = (opts && opts.noUndeclaredFields) | 0; - var errorHook = opts && opts.errorHook; - var hook, path; - if (errorHook) { - path = []; - hook = function (any, type) { - errorHook.call(this, path.slice(), any, type, val); - }; - } - return this._check(val, flags, hook, path); -}; - -Type.prototype.random = utils.abstractFunction; - -Type.prototype.schema = function (opts) { - // Copy the options to avoid mutating the original options object when we add - // the registry of dereferenced types. - return this._attrs({ - exportAttrs: !!(opts && opts.exportAttrs), - noDeref: !!(opts && opts.noDeref), - }); -}; - -Type.prototype.toBuffer = function (val) { - TAP.pos = 0; - this._write(TAP, val); - var buf = utils.newBuffer(TAP.pos); - if (TAP.isValid()) { - TAP.buf.copy(buf, 0, 0, TAP.pos); - } else { - this._write(new Tap(buf), val); - } - return buf; -}; - -Type.prototype.toJSON = function () { - // Convenience to allow using `JSON.stringify(type)` to get a type's schema. - return this.schema({ exportAttrs: true }); -}; - -Type.prototype.toString = function (val) { - if (val === undefined) { - // Consistent behavior with standard `toString` expectations. - return JSON.stringify(this.schema({ noDeref: true })); - } - return JSON.stringify(this._copy(val, { coerce: 3 })); -}; - -Type.prototype.wrap = function (val) { - var Branch = this._branchConstructor; - return Branch === null ? null : new Branch(val); -}; - -Type.prototype._attrs = function (opts) { - // This function handles a lot of the common logic to schema generation - // across types, for example keeping track of which types have already been - // de-referenced (i.e. derefed). - opts.derefed = opts.derefed || {}; - var name = this.name; - if (name !== undefined) { - if (opts.noDeref || opts.derefed[name]) { - return name; - } - opts.derefed[name] = true; - } - var schema = {}; - // The order in which we add fields to the `schema` object matters here. - // Since JS objects are unordered, this implementation (unfortunately) relies - // on engines returning properties in the same order that they are inserted - // in. This is not in the JS spec, but can be "somewhat" safely assumed (see - // http://stackoverflow.com/q/5525795/1062617). - if (this.name !== undefined) { - schema.name = name; - } - schema.type = this.typeName; - var derefedSchema = this._deref(schema, opts); - if (derefedSchema !== undefined) { - // We allow the original schema to be overridden (this will happen for - // primitive types and logical types). - schema = derefedSchema; - } - if (opts.exportAttrs) { - if (this.aliases && this.aliases.length) { - schema.aliases = this.aliases; - } - if (this.doc !== undefined) { - schema.doc = this.doc; - } - } - return schema; -}; - -Type.prototype._createBranchConstructor = function () { - // jshint -W054 - var name = this.branchName; - if (name === 'null') { - return null; - } - var attr = ~name.indexOf('.') ? "this['" + name + "']" : 'this.' + name; - var body = 'return function Branch$(val) { ' + attr + ' = val; };'; - var Branch = new Function(body)(); - Branch.type = this; - Branch.prototype.unwrap = new Function('return ' + attr + ';'); - Branch.prototype.unwrapped = Branch.prototype.unwrap; // Deprecated. - return Branch; -}; - -Type.prototype._peek = function (tap) { - var pos = tap.pos; - var val = this._read(tap); - tap.pos = pos; - return val; -}; - -Type.prototype._check = utils.abstractFunction; -Type.prototype._copy = utils.abstractFunction; -Type.prototype._deref = utils.abstractFunction; -Type.prototype._match = utils.abstractFunction; -Type.prototype._read = utils.abstractFunction; -Type.prototype._skip = utils.abstractFunction; -Type.prototype._update = utils.abstractFunction; -Type.prototype._write = utils.abstractFunction; - -// "Deprecated" getters (will be explicitly deprecated in 5.1). - -Type.prototype.getAliases = function () { - return this.aliases; -}; - -Type.prototype.getFingerprint = Type.prototype.fingerprint; - -Type.prototype.getName = function (asBranch) { - return this.name || !asBranch ? this.name : this.branchName; -}; - -Type.prototype.getSchema = Type.prototype.schema; - -Type.prototype.getTypeName = function () { - return this.typeName; -}; - -// Implementations. - -/** - * Base primitive Avro type. - * - * Most of the primitive types share the same cloning and resolution - * mechanisms, provided by this class. This class also lets us conveniently - * check whether a type is a primitive using `instanceof`. - */ -function PrimitiveType(noFreeze) { - Type.call(this); - this._branchConstructor = this._createBranchConstructor(); - if (!noFreeze) { - // Abstract long types can't be frozen at this stage. - Object.freeze(this); - } -} -util.inherits(PrimitiveType, Type); - -PrimitiveType.prototype._update = function (resolver, type) { - if (type.typeName === this.typeName) { - resolver._read = this._read; - } -}; - -PrimitiveType.prototype._copy = function (val) { - this._check(val, undefined, throwInvalidError); - return val; -}; - -PrimitiveType.prototype._deref = function () { - return this.typeName; -}; - -PrimitiveType.prototype.compare = utils.compare; - -/** Nulls. */ -function NullType() { - PrimitiveType.call(this); -} -util.inherits(NullType, PrimitiveType); - -NullType.prototype._check = function (val, flags, hook) { - var b = val === null; - if (!b && hook) { - hook(val, this); - } - return b; -}; - -NullType.prototype._read = function () { - return null; -}; - -NullType.prototype._skip = function () {}; - -NullType.prototype._write = function (tap, val) { - if (val !== null) { - throwInvalidError(val, this); - } -}; - -NullType.prototype._match = function () { - return 0; -}; - -NullType.prototype.compare = NullType.prototype._match; - -NullType.prototype.typeName = 'null'; - -NullType.prototype.random = NullType.prototype._read; - -/** Booleans. */ -function BooleanType() { - PrimitiveType.call(this); -} -util.inherits(BooleanType, PrimitiveType); - -BooleanType.prototype._check = function (val, flags, hook) { - var b = typeof val == 'boolean'; - if (!b && hook) { - hook(val, this); - } - return b; -}; - -BooleanType.prototype._read = function (tap) { - return tap.readBoolean(); -}; - -BooleanType.prototype._skip = function (tap) { - tap.skipBoolean(); -}; - -BooleanType.prototype._write = function (tap, val) { - if (typeof val != 'boolean') { - throwInvalidError(val, this); - } - tap.writeBoolean(val); -}; - -BooleanType.prototype._match = function (tap1, tap2) { - return tap1.matchBoolean(tap2); -}; - -BooleanType.prototype.typeName = 'boolean'; - -BooleanType.prototype.random = function () { - return RANDOM.nextBoolean(); -}; - -/** Integers. */ -function IntType() { - PrimitiveType.call(this); -} -util.inherits(IntType, PrimitiveType); - -IntType.prototype._check = function (val, flags, hook) { - var b = val === (val | 0); - if (!b && hook) { - hook(val, this); - } - return b; -}; - -IntType.prototype._read = function (tap) { - return tap.readInt(); -}; - -IntType.prototype._skip = function (tap) { - tap.skipInt(); -}; - -IntType.prototype._write = function (tap, val) { - if (val !== (val | 0)) { - throwInvalidError(val, this); - } - tap.writeInt(val); -}; - -IntType.prototype._match = function (tap1, tap2) { - return tap1.matchInt(tap2); -}; - -IntType.prototype.typeName = 'int'; - -IntType.prototype.random = function () { - return RANDOM.nextInt(1000) | 0; -}; - -/** - * Longs. - * - * We can't capture all the range unfortunately since JavaScript represents all - * numbers internally as `double`s, so the default implementation plays safe - * and throws rather than potentially silently change the data. See `__with` or - * `AbstractLongType` below for a way to implement a custom long type. - */ -function LongType() { - PrimitiveType.call(this); -} -util.inherits(LongType, PrimitiveType); - -LongType.prototype._check = function (val, flags, hook) { - var b = typeof val == 'number' && val % 1 === 0 && isSafeLong(val); - if (!b && hook) { - hook(val, this); - } - return b; -}; - -LongType.prototype._read = function (tap) { - var n = tap.readLong(); - if (!isSafeLong(n)) { - Type.addError(new Error('potential precision loss')); - } - return n; -}; - -LongType.prototype._skip = function (tap) { - tap.skipLong(); -}; - -LongType.prototype._write = function (tap, val) { - if (typeof val != 'number' || val % 1 || !isSafeLong(val)) { - throwInvalidError(val, this); - } - tap.writeLong(val); -}; - -LongType.prototype._match = function (tap1, tap2) { - return tap1.matchLong(tap2); -}; - -LongType.prototype._update = function (resolver, type) { - switch (type.typeName) { - case 'int': - resolver._read = type._read; - break; - case 'abstract:long': - case 'long': - resolver._read = this._read; // In case `type` is an `AbstractLongType`. - } -}; - -LongType.prototype.typeName = 'long'; - -LongType.prototype.random = function () { - return RANDOM.nextInt(); -}; - -LongType.__with = function (methods, noUnpack) { - methods = methods || {}; // Will give a more helpful error message. - // We map some of the methods to a different name to be able to intercept - // their input and output (otherwise we wouldn't be able to perform any - // unpacking logic, and the type wouldn't work when nested). - var mapping = { - toBuffer: '_toBuffer', - fromBuffer: '_fromBuffer', - fromJSON: '_fromJSON', - toJSON: '_toJSON', - isValid: '_isValid', - compare: 'compare', - }; - var type = new AbstractLongType(noUnpack); - Object.keys(mapping).forEach(function (name) { - if (methods[name] === undefined) { - Type.addError(new Error(f('missing method implementation: %s', name))); - } - type[mapping[name]] = methods[name]; - }); - return Object.freeze(type); -}; - -/** Floats. */ -function FloatType() { - PrimitiveType.call(this); -} -util.inherits(FloatType, PrimitiveType); - -FloatType.prototype._check = function (val, flags, hook) { - var b = typeof val == 'number'; - if (!b && hook) { - hook(val, this); - } - return b; -}; - -FloatType.prototype._read = function (tap) { - return tap.readFloat(); -}; - -FloatType.prototype._skip = function (tap) { - tap.skipFloat(); -}; - -FloatType.prototype._write = function (tap, val) { - if (typeof val != 'number') { - throwInvalidError(val, this); - } - tap.writeFloat(val); -}; - -FloatType.prototype._match = function (tap1, tap2) { - return tap1.matchFloat(tap2); -}; - -FloatType.prototype._update = function (resolver, type) { - switch (type.typeName) { - case 'float': - case 'int': - resolver._read = type._read; - break; - case 'abstract:long': - case 'long': - // No need to worry about precision loss here since we're always rounding - // to float anyway. - resolver._read = function (tap) { - return tap.readLong(); - }; - } -}; - -FloatType.prototype.typeName = 'float'; - -FloatType.prototype.random = function () { - return RANDOM.nextFloat(1e3); -}; - -/** Doubles. */ -function DoubleType() { - PrimitiveType.call(this); -} -util.inherits(DoubleType, PrimitiveType); - -DoubleType.prototype._check = function (val, flags, hook) { - var b = typeof val == 'number'; - if (!b && hook) { - hook(val, this); - } - return b; -}; - -DoubleType.prototype._read = function (tap) { - return tap.readDouble(); -}; - -DoubleType.prototype._skip = function (tap) { - tap.skipDouble(); -}; - -DoubleType.prototype._write = function (tap, val) { - if (typeof val != 'number') { - throwInvalidError(val, this); - } - tap.writeDouble(val); -}; - -DoubleType.prototype._match = function (tap1, tap2) { - return tap1.matchDouble(tap2); -}; - -DoubleType.prototype._update = function (resolver, type) { - switch (type.typeName) { - case 'double': - case 'float': - case 'int': - resolver._read = type._read; - break; - case 'abstract:long': - case 'long': - // Similar to inside `FloatType`, no need to worry about precision loss - // here since we're always rounding to double anyway. - resolver._read = function (tap) { - return tap.readLong(); - }; - } -}; - -DoubleType.prototype.typeName = 'double'; - -DoubleType.prototype.random = function () { - return RANDOM.nextFloat(); -}; - -/** Strings. */ -function StringType() { - PrimitiveType.call(this); -} -util.inherits(StringType, PrimitiveType); - -StringType.prototype._check = function (val, flags, hook) { - var b = typeof val == 'string'; - if (!b && hook) { - hook(val, this); - } - return b; -}; - -StringType.prototype._read = function (tap) { - return tap.readString(); -}; - -StringType.prototype._skip = function (tap) { - tap.skipString(); -}; - -StringType.prototype._write = function (tap, val) { - if (typeof val != 'string') { - throwInvalidError(val, this); - } - tap.writeString(val); -}; - -StringType.prototype._match = function (tap1, tap2) { - return tap1.matchString(tap2); -}; - -StringType.prototype._update = function (resolver, type) { - switch (type.typeName) { - case 'bytes': - case 'string': - resolver._read = this._read; - } -}; - -StringType.prototype.typeName = 'string'; - -StringType.prototype.random = function () { - return RANDOM.nextString(RANDOM.nextInt(32)); -}; - -/** - * Bytes. - * - * These are represented in memory as `Buffer`s rather than binary-encoded - * strings. This is more efficient (when decoding/encoding from bytes, the - * common use-case), idiomatic, and convenient. - * - * Note the coercion in `_copy`. - */ -function BytesType() { - PrimitiveType.call(this); -} -util.inherits(BytesType, PrimitiveType); - -BytesType.prototype._check = function (val, flags, hook) { - var b = Buffer.isBuffer(val); - if (!b && hook) { - hook(val, this); - } - return b; -}; - -BytesType.prototype._read = function (tap) { - return tap.readBytes(); -}; - -BytesType.prototype._skip = function (tap) { - tap.skipBytes(); -}; - -BytesType.prototype._write = function (tap, val) { - if (!Buffer.isBuffer(val)) { - throwInvalidError(val, this); - } - tap.writeBytes(val); -}; - -BytesType.prototype._match = function (tap1, tap2) { - return tap1.matchBytes(tap2); -}; - -BytesType.prototype._update = StringType.prototype._update; - -BytesType.prototype._copy = function (obj, opts) { - var buf; - switch ((opts && opts.coerce) | 0) { - case 3: // Coerce buffers to strings. - this._check(obj, undefined, throwInvalidError); - return obj.toString('binary'); - case 2: // Coerce strings to buffers. - if (typeof obj != 'string') { - Type.addError(new Error(f('cannot coerce to buffer: %j', obj))); - } - buf = utils.bufferFrom(obj, 'binary'); - this._check(buf, undefined, throwInvalidError); - return buf; - case 1: // Coerce buffer JSON representation to buffers. - if (!isJsonBuffer(obj)) { - Type.addError(new Error(f('cannot coerce to buffer: %j', obj))); - } - buf = utils.bufferFrom(obj.data); - this._check(buf, undefined, throwInvalidError); - return buf; - default: // Copy buffer. - this._check(obj, undefined, throwInvalidError); - return utils.bufferFrom(obj); - } -}; - -BytesType.prototype.compare = Buffer.compare; - -BytesType.prototype.typeName = 'bytes'; - -BytesType.prototype.random = function () { - return RANDOM.nextBuffer(RANDOM.nextInt(32)); -}; - -/** Base "abstract" Avro union type. */ -function UnionType(schema, opts) { - Type.call(this); - - if (!Array.isArray(schema)) { - Type.addError(new Error(f('non-array union schema: %j', schema))); - } - if (!schema.length) { - Type.addError(new Error('empty union')); - } - this.types = Object.freeze( - schema.map(function (obj) { - return Type.forSchema(obj, opts); - }), - ); - - this._branchIndices = {}; - this.types.forEach(function (type, i) { - if (Type.isType(type, 'union')) { - Type.addError(new Error('unions cannot be directly nested')); - } - var branch = type.branchName; - if (branch && this._branchIndices[branch] !== undefined) { - Type.addError(new Error(f('duplicate union branch name: %j', branch))); - } - this._branchIndices[branch] = i; - }, this); -} -util.inherits(UnionType, Type); - -UnionType.prototype._branchConstructor = function () { - Type.addError(new Error('unions cannot be directly wrapped')); -}; - -UnionType.prototype._skip = function (tap) { - this.types[tap.readLong()]._skip(tap); -}; - -UnionType.prototype._match = function (tap1, tap2) { - var n1 = tap1.readLong(); - var n2 = tap2.readLong(); - if (n1 === n2) { - return this.types[n1]._match(tap1, tap2); - } else { - return n1 < n2 ? -1 : 1; - } -}; - -UnionType.prototype._deref = function (schema, opts) { - return this.types.map(function (t) { - return t._attrs(opts); - }); -}; - -UnionType.prototype.getTypes = function () { - return this.types; -}; - -/** - * "Natural" union type. - * - * This representation doesn't require a wrapping object and is therefore - * simpler and generally closer to what users expect. However it cannot be used - * to represent all Avro unions since some lead to ambiguities (e.g. if two - * number types are in the union). - * - * Currently, this union supports at most one type in each of the categories - * below: - * - * + `null` - * + `boolean` - * + `int`, `long`, `float`, `double` - * + `string`, `enum` - * + `bytes`, `fixed` - * + `array` - * + `map`, `record` - */ -function UnwrappedUnionType(schema, opts) { - UnionType.call(this, schema, opts); - - this._dynamicBranches = null; - this._bucketIndices = {}; - this.types.forEach(function (type, index) { - if (Type.isType(type, 'abstract', 'logical')) { - if (!this._dynamicBranches) { - this._dynamicBranches = []; - } - this._dynamicBranches.push({ index: index, type: type }); - } else { - var bucket = getTypeBucket(type); - if (this._bucketIndices[bucket] !== undefined) { - Type.addError(new Error(f('ambiguous unwrapped union: %j', this))); - } - this._bucketIndices[bucket] = index; - } - }, this); - - Object.freeze(this); -} -util.inherits(UnwrappedUnionType, UnionType); - -UnwrappedUnionType.prototype._getIndex = function (val) { - var index = this._bucketIndices[getValueBucket(val)]; - if (this._dynamicBranches) { - // Slower path, we must run the value through all branches. - index = this._getBranchIndex(val, index); - } - return index; -}; - -UnwrappedUnionType.prototype._getBranchIndex = function (any, index) { - var logicalBranches = this._dynamicBranches; - var i, l, branch; - for (i = 0, l = logicalBranches.length; i < l; i++) { - branch = logicalBranches[i]; - if (branch.type._check(any)) { - if (index === undefined) { - index = branch.index; - } else { - // More than one branch matches the value so we aren't guaranteed to - // infer the correct type. We throw rather than corrupt data. This can - // be fixed by "tightening" the logical types. - Type.addError(new Error('ambiguous conversion')); - } - } - } - return index; -}; - -UnwrappedUnionType.prototype._check = function (val, flags, hook, path) { - var index = this._getIndex(val); - var b = index !== undefined; - if (b) { - return this.types[index]._check(val, flags, hook, path); - } - if (hook) { - hook(val, this); - } - return b; -}; - -UnwrappedUnionType.prototype._read = function (tap) { - var index = tap.readLong(); - var branchType = this.types[index]; - if (branchType) { - return branchType._read(tap); - } else { - Type.addError(new Error(f('invalid union index: %s', index))); - } -}; - -UnwrappedUnionType.prototype._write = function (tap, val) { - var index = this._getIndex(val); - if (index === undefined) { - throwInvalidError(val, this); - } - tap.writeLong(index); - if (val !== null) { - this.types[index]._write(tap, val); - } -}; - -UnwrappedUnionType.prototype._update = function (resolver, type, opts) { - // jshint -W083 - // (The loop exits after the first function is created.) - var i, l, typeResolver; - for (i = 0, l = this.types.length; i < l; i++) { - try { - typeResolver = this.types[i].createResolver(type, opts); - } catch (err) { - continue; - } - resolver._read = function (tap) { - return typeResolver._read(tap); - }; - return; - } -}; - -UnwrappedUnionType.prototype._copy = function (val, opts) { - var coerce = opts && opts.coerce | 0; - var wrap = opts && opts.wrap | 0; - var index; - if (wrap === 2) { - // We are parsing a default, so always use the first branch's type. - index = 0; - } else { - switch (coerce) { - case 1: - // Using the `coerceBuffers` option can cause corruption and erroneous - // failures with unwrapped unions (in rare cases when the union also - // contains a record which matches a buffer's JSON representation). - if (isJsonBuffer(val) && this._bucketIndices.buffer !== undefined) { - index = this._bucketIndices.buffer; - } else { - index = this._getIndex(val); - } - break; - case 2: - // Decoding from JSON, we must unwrap the value. - if (val === null) { - index = this._bucketIndices['null']; - } else if (typeof val === 'object') { - var keys = Object.keys(val); - if (keys.length === 1) { - index = this._branchIndices[keys[0]]; - val = val[keys[0]]; - } - } - break; - default: - index = this._getIndex(val); - } - if (index === undefined) { - throwInvalidError(val, this); - return; - } - } - var type = this.types[index]; - if (val === null || wrap === 3) { - return type._copy(val, opts); - } else { - switch (coerce) { - case 3: - // Encoding to JSON, we wrap the value. - var obj = {}; - obj[type.branchName] = type._copy(val, opts); - return obj; - default: - return type._copy(val, opts); - } - } -}; - -UnwrappedUnionType.prototype.compare = function (val1, val2) { - var index1 = this._getIndex(val1); - var index2 = this._getIndex(val2); - if (index1 === undefined) { - throwInvalidError(val1, this); - } else if (index2 === undefined) { - throwInvalidError(val2, this); - } else if (index1 === index2) { - return this.types[index1].compare(val1, val2); - } else { - return utils.compare(index1, index2); - } -}; - -UnwrappedUnionType.prototype.typeName = 'union:unwrapped'; - -UnwrappedUnionType.prototype.random = function () { - var index = RANDOM.nextInt(this.types.length); - return this.types[index].random(); -}; - -/** - * Compatible union type. - * - * Values of this type are represented in memory similarly to their JSON - * representation (i.e. inside an object with single key the name of the - * contained type). - * - * This is not ideal, but is the most efficient way to unambiguously support - * all unions. Here are a few reasons why the wrapping object is necessary: - * - * + Unions with multiple number types would have undefined behavior, unless - * numbers are wrapped (either everywhere, leading to large performance and - * convenience costs; or only when necessary inside unions, making it hard to - * understand when numbers are wrapped or not). - * + Fixed types would have to be wrapped to be distinguished from bytes. - * + Using record's constructor names would work (after a slight change to use - * the fully qualified name), but would mean that generic objects could no - * longer be valid records (making it inconvenient to do simple things like - * creating new records). - */ -function WrappedUnionType(schema, opts) { - UnionType.call(this, schema, opts); - Object.freeze(this); -} -util.inherits(WrappedUnionType, UnionType); - -WrappedUnionType.prototype._check = function (val, flags, hook, path) { - var b = false; - if (val === null) { - // Shortcut type lookup in this case. - b = this._branchIndices['null'] !== undefined; - } else if (typeof val == 'object') { - var keys = Object.keys(val); - if (keys.length === 1) { - // We require a single key here to ensure that writes are correct and - // efficient as soon as a record passes this check. - var name = keys[0]; - var index = this._branchIndices[name]; - if (index !== undefined) { - if (hook) { - // Slow path. - path.push(name); - b = this.types[index]._check(val[name], flags, hook, path); - path.pop(); - return b; - } else { - return this.types[index]._check(val[name], flags); - } - } - } - } - if (!b && hook) { - hook(val, this); - } - return b; -}; - -WrappedUnionType.prototype._read = function (tap) { - var type = this.types[tap.readLong()]; - if (!type) { - Type.addError(new Error(f('invalid union index'))); - } - var Branch = type._branchConstructor; - if (Branch === null) { - return null; - } else { - return new Branch(type._read(tap)); - } -}; - -WrappedUnionType.prototype._write = function (tap, val) { - var index, keys, name; - if (val === null) { - index = this._branchIndices['null']; - if (index === undefined) { - throwInvalidError(val, this); - } - tap.writeLong(index); - } else { - keys = Object.keys(val); - if (keys.length === 1) { - name = keys[0]; - index = this._branchIndices[name]; - } - if (index === undefined) { - throwInvalidError(val, this); - } - tap.writeLong(index); - this.types[index]._write(tap, val[name]); - } -}; - -WrappedUnionType.prototype._update = function (resolver, type, opts) { - // jshint -W083 - // (The loop exits after the first function is created.) - var i, l, typeResolver, Branch; - for (i = 0, l = this.types.length; i < l; i++) { - try { - typeResolver = this.types[i].createResolver(type, opts); - } catch (err) { - continue; - } - Branch = this.types[i]._branchConstructor; - if (Branch) { - resolver._read = function (tap) { - return new Branch(typeResolver._read(tap)); - }; - } else { - resolver._read = function () { - return null; - }; - } - return; - } -}; - -WrappedUnionType.prototype._copy = function (val, opts) { - var wrap = opts && opts.wrap | 0; - if (wrap === 2) { - var firstType = this.types[0]; - // Promote into first type (used for schema defaults). - if (val === null && firstType.typeName === 'null') { - return null; - } - return new firstType._branchConstructor(firstType._copy(val, opts)); - } - if (val === null && this._branchIndices['null'] !== undefined) { - return null; - } - - var i, l, obj; - if (typeof val == 'object') { - var keys = Object.keys(val); - if (keys.length === 1) { - var name = keys[0]; - i = this._branchIndices[name]; - if (i === undefined && opts.qualifyNames) { - // We are a bit more flexible than in `_check` here since we have - // to deal with other serializers being less strict, so we fall - // back to looking up unqualified names. - var j, type; - for (j = 0, l = this.types.length; j < l; j++) { - type = this.types[j]; - if (type.name && name === unqualify(type.name)) { - i = j; - break; - } - } - } - if (i !== undefined) { - obj = this.types[i]._copy(val[name], opts); - } - } - } - if (wrap === 1 && obj === undefined) { - // Try promoting into first match (convenience, slow). - i = 0; - l = this.types.length; - while (i < l && obj === undefined) { - try { - obj = this.types[i]._copy(val, opts); - } catch (err) { - i++; - } - } - } - if (obj !== undefined) { - return wrap === 3 ? obj : new this.types[i]._branchConstructor(obj); - } - throwInvalidError(val, this); -}; - -WrappedUnionType.prototype.compare = function (val1, val2) { - var name1 = val1 === null ? 'null' : Object.keys(val1)[0]; - var name2 = val2 === null ? 'null' : Object.keys(val2)[0]; - var index = this._branchIndices[name1]; - if (name1 === name2) { - return name1 === 'null' ? 0 : this.types[index].compare(val1[name1], val2[name1]); - } else { - return utils.compare(index, this._branchIndices[name2]); - } -}; - -WrappedUnionType.prototype.typeName = 'union:wrapped'; - -WrappedUnionType.prototype.random = function () { - var index = RANDOM.nextInt(this.types.length); - var type = this.types[index]; - var Branch = type._branchConstructor; - if (!Branch) { - return null; - } - return new Branch(type.random()); -}; - -/** - * Avro enum type. - * - * Represented as strings (with allowed values from the set of symbols). Using - * integers would be a reasonable option, but the performance boost is arguably - * offset by the legibility cost and the extra deviation from the JSON encoding - * convention. - * - * An integer representation can still be used (e.g. for compatibility with - * TypeScript `enum`s) by overriding the `EnumType` with a `LongType` (e.g. via - * `parse`'s registry). - */ -function EnumType(schema, opts) { - Type.call(this, schema, opts); - if (!Array.isArray(schema.symbols) || !schema.symbols.length) { - Type.addError(new Error(f('invalid enum symbols: %j', schema.symbols))); - } - this.symbols = Object.freeze(schema.symbols.slice()); - this._indices = {}; - this.symbols.forEach(function (symbol, i) { - if (!isValidName(symbol)) { - Type.addError(new Error(f('invalid %s symbol: %j', this, symbol))); - } - if (this._indices[symbol] !== undefined) { - Type.addError(new Error(f('duplicate %s symbol: %j', this, symbol))); - } - this._indices[symbol] = i; - }, this); - this._branchConstructor = this._createBranchConstructor(); - Object.freeze(this); -} -util.inherits(EnumType, Type); - -EnumType.prototype._check = function (val, flags, hook) { - var b = this._indices[val] !== undefined; - if (!b && hook) { - hook(val, this); - } - return b; -}; - -EnumType.prototype._read = function (tap) { - var index = tap.readLong(); - var symbol = this.symbols[index]; - if (symbol === undefined) { - Type.addError(new Error(f('invalid %s enum index: %s', this.name, index))); - } - return symbol; -}; - -EnumType.prototype._skip = function (tap) { - tap.skipLong(); -}; - -EnumType.prototype._write = function (tap, val) { - var index = this._indices[val]; - if (index === undefined) { - throwInvalidError(val, this); - } - tap.writeLong(index); -}; - -EnumType.prototype._match = function (tap1, tap2) { - return tap1.matchLong(tap2); -}; - -EnumType.prototype.compare = function (val1, val2) { - return utils.compare(this._indices[val1], this._indices[val2]); -}; - -EnumType.prototype._update = function (resolver, type) { - var symbols = this.symbols; - if ( - type.typeName === 'enum' && - (!type.name || ~getAliases(this).indexOf(type.name)) && - type.symbols.every(function (s) { - return ~symbols.indexOf(s); - }) - ) { - resolver.symbols = type.symbols; - resolver._read = type._read; - } -}; - -EnumType.prototype._copy = function (val) { - this._check(val, undefined, throwInvalidError); - return val; -}; - -EnumType.prototype._deref = function (schema) { - schema.symbols = this.symbols; -}; - -EnumType.prototype.getSymbols = function () { - return this.symbols; -}; - -EnumType.prototype.typeName = 'enum'; - -EnumType.prototype.random = function () { - return RANDOM.choice(this.symbols); -}; - -/** Avro fixed type. Represented simply as a `Buffer`. */ -function FixedType(schema, opts) { - Type.call(this, schema, opts); - if (schema.size !== (schema.size | 0) || schema.size < 1) { - Type.addError(new Error(f('invalid %s size', this.branchName))); - } - this.size = schema.size | 0; - this._branchConstructor = this._createBranchConstructor(); - Object.freeze(this); -} -util.inherits(FixedType, Type); - -FixedType.prototype._check = function (val, flags, hook) { - var b = Buffer.isBuffer(val) && val.length === this.size; - if (!b && hook) { - hook(val, this); - } - return b; -}; - -FixedType.prototype._read = function (tap) { - return tap.readFixed(this.size); -}; - -FixedType.prototype._skip = function (tap) { - tap.skipFixed(this.size); -}; - -FixedType.prototype._write = function (tap, val) { - if (!Buffer.isBuffer(val) || val.length !== this.size) { - throwInvalidError(val, this); - } - tap.writeFixed(val, this.size); -}; - -FixedType.prototype._match = function (tap1, tap2) { - return tap1.matchFixed(tap2, this.size); -}; - -FixedType.prototype.compare = Buffer.compare; - -FixedType.prototype._update = function (resolver, type) { - if (type.typeName === 'fixed' && this.size === type.size && (!type.name || ~getAliases(this).indexOf(type.name))) { - resolver.size = this.size; - resolver._read = this._read; - } -}; - -FixedType.prototype._copy = BytesType.prototype._copy; - -FixedType.prototype._deref = function (schema) { - schema.size = this.size; -}; - -FixedType.prototype.getSize = function () { - return this.size; -}; - -FixedType.prototype.typeName = 'fixed'; - -FixedType.prototype.random = function () { - return RANDOM.nextBuffer(this.size); -}; - -/** Avro map. Represented as vanilla objects. */ -function MapType(schema, opts) { - Type.call(this); - if (!schema.values) { - Type.addError(new Error(f('missing map values: %j', schema))); - } - this.valuesType = Type.forSchema(schema.values, opts); - this._branchConstructor = this._createBranchConstructor(); - Object.freeze(this); -} -util.inherits(MapType, Type); - -MapType.prototype._check = function (val, flags, hook, path) { - if (!val || typeof val != 'object' || Array.isArray(val)) { - if (hook) { - hook(val, this); - } - return false; - } - - var keys = Object.keys(val); - var b = true; - var i, l, j, key; - if (hook) { - // Slow path. - j = path.length; - path.push(''); - for (i = 0, l = keys.length; i < l; i++) { - key = path[j] = keys[i]; - if (!this.valuesType._check(val[key], flags, hook, path)) { - b = false; - } - } - path.pop(); - } else { - for (i = 0, l = keys.length; i < l; i++) { - if (!this.valuesType._check(val[keys[i]], flags)) { - return false; - } - } - } - return b; -}; - -MapType.prototype._read = function (tap) { - var values = this.valuesType; - var val = {}; - var n; - while ((n = readArraySize(tap))) { - while (n--) { - var key = tap.readString(); - val[key] = values._read(tap); - } - } - return val; -}; - -MapType.prototype._skip = function (tap) { - var values = this.valuesType; - var len, n; - while ((n = tap.readLong())) { - if (n < 0) { - len = tap.readLong(); - tap.pos += len; - } else { - while (n--) { - tap.skipString(); - values._skip(tap); - } - } - } -}; - -MapType.prototype._write = function (tap, val) { - if (!val || typeof val != 'object' || Array.isArray(val)) { - throwInvalidError(val, this); - } - - var values = this.valuesType; - var keys = Object.keys(val); - var n = keys.length; - var i, key; - if (n) { - tap.writeLong(n); - for (i = 0; i < n; i++) { - key = keys[i]; - tap.writeString(key); - values._write(tap, val[key]); - } - } - tap.writeLong(0); -}; - -MapType.prototype._match = function () { - Type.addError(new Error('maps cannot be compared')); -}; - -MapType.prototype._update = function (rsv, type, opts) { - if (type.typeName === 'map') { - rsv.valuesType = this.valuesType.createResolver(type.valuesType, opts); - rsv._read = this._read; - } -}; - -MapType.prototype._copy = function (val, opts) { - if (val && typeof val == 'object' && !Array.isArray(val)) { - var values = this.valuesType; - var keys = Object.keys(val); - var i, l, key; - var copy = {}; - for (i = 0, l = keys.length; i < l; i++) { - key = keys[i]; - copy[key] = values._copy(val[key], opts); - } - return copy; - } - throwInvalidError(val, this); -}; - -MapType.prototype.compare = MapType.prototype._match; - -MapType.prototype.typeName = 'map'; - -MapType.prototype.getValuesType = function () { - return this.valuesType; -}; - -MapType.prototype.random = function () { - var val = {}; - var i, l; - for (i = 0, l = RANDOM.nextInt(10); i < l; i++) { - val[RANDOM.nextString(RANDOM.nextInt(20))] = this.valuesType.random(); - } - return val; -}; - -MapType.prototype._deref = function (schema, opts) { - schema.values = this.valuesType._attrs(opts); -}; - -/** Avro array. Represented as vanilla arrays. */ -function ArrayType(schema, opts) { - Type.call(this); - if (!schema.items) { - Type.addError(new Error(f('missing array items: %j', schema))); - } - this.itemsType = Type.forSchema(schema.items, opts); - this._branchConstructor = this._createBranchConstructor(); - Object.freeze(this); -} -util.inherits(ArrayType, Type); - -ArrayType.prototype._check = function (val, flags, hook, path) { - if (!Array.isArray(val)) { - if (hook) { - hook(val, this); - } - return false; - } - - var b = true; - var i, l, j; - if (hook) { - // Slow path. - j = path.length; - path.push(''); - for (i = 0, l = val.length; i < l; i++) { - path[j] = '' + i; - if (!this.itemsType._check(val[i], flags, hook, path)) { - b = false; - } - } - path.pop(); - } else { - for (i = 0, l = val.length; i < l; i++) { - if (!this.itemsType._check(val[i], flags)) { - return false; - } - } - } - return b; -}; - -ArrayType.prototype._read = function (tap) { - var items = this.itemsType; - var val = []; - var n; - while ((n = tap.readLong())) { - if (n < 0) { - n = -n; - tap.skipLong(); // Skip size. - } - while (n--) { - val.push(items._read(tap)); - } - } - return val; -}; - -ArrayType.prototype._skip = function (tap) { - var len, n; - while ((n = tap.readLong())) { - if (n < 0) { - len = tap.readLong(); - tap.pos += len; - } else { - while (n--) { - this.itemsType._skip(tap); - } - } - } -}; - -ArrayType.prototype._write = function (tap, val) { - if (!Array.isArray(val)) { - throwInvalidError(val, this); - } - - var n = val.length; - var i; - if (n) { - tap.writeLong(n); - for (i = 0; i < n; i++) { - this.itemsType._write(tap, val[i]); - } - } - tap.writeLong(0); -}; - -ArrayType.prototype._match = function (tap1, tap2) { - var n1 = tap1.readLong(); - var n2 = tap2.readLong(); - var f; - while (n1 && n2) { - f = this.itemsType._match(tap1, tap2); - if (f) { - return f; - } - if (!--n1) { - n1 = readArraySize(tap1); - } - if (!--n2) { - n2 = readArraySize(tap2); - } - } - return utils.compare(n1, n2); -}; - -ArrayType.prototype._update = function (resolver, type, opts) { - if (type.typeName === 'array') { - resolver.itemsType = this.itemsType.createResolver(type.itemsType, opts); - resolver._read = this._read; - } -}; - -ArrayType.prototype._copy = function (val, opts) { - if (!Array.isArray(val)) { - throwInvalidError(val, this); - } - var items = new Array(val.length); - var i, l; - for (i = 0, l = val.length; i < l; i++) { - items[i] = this.itemsType._copy(val[i], opts); - } - return items; -}; - -ArrayType.prototype._deref = function (schema, opts) { - schema.items = this.itemsType._attrs(opts); -}; - -ArrayType.prototype.compare = function (val1, val2) { - var n1 = val1.length; - var n2 = val2.length; - var i, l, f; - for (i = 0, l = Math.min(n1, n2); i < l; i++) { - if ((f = this.itemsType.compare(val1[i], val2[i]))) { - return f; - } - } - return utils.compare(n1, n2); -}; - -ArrayType.prototype.getItemsType = function () { - return this.itemsType; -}; - -ArrayType.prototype.typeName = 'array'; - -ArrayType.prototype.random = function () { - var arr = []; - var i, l; - for (i = 0, l = RANDOM.nextInt(10); i < l; i++) { - arr.push(this.itemsType.random()); - } - return arr; -}; - -/** - * Avro record. - * - * Values are represented as instances of a programmatically generated - * constructor (similar to a "specific record"), available via the - * `getRecordConstructor` method. This "specific record class" gives - * significant speedups over using generics objects. - * - * Note that vanilla objects are still accepted as valid as long as their - * fields match (this makes it much more convenient to do simple things like - * update nested records). - * - * This type is also used for errors (similar, except for the extra `Error` - * constructor call) and for messages (see comment below). - */ -function RecordType(schema, opts) { - // Force creation of the options object in case we need to register this - // record's name. - opts = opts || {}; - - // Save the namespace to restore it as we leave this record's scope. - var namespace = opts.namespace; - if (schema.namespace !== undefined) { - opts.namespace = schema.namespace; - } else if (schema.name) { - // Fully qualified names' namespaces are used when no explicit namespace - // attribute was specified. - var match = /^(.*)\.[^.]+$/.exec(schema.name); - if (match) { - opts.namespace = match[1]; - } - } - Type.call(this, schema, opts); - - if (!Array.isArray(schema.fields)) { - Type.addError(new Error(f('non-array record fields: %j', schema.fields ?? 'no `fields` in schema'))); - } - if ( - utils.hasDuplicates(schema.fields, function (f) { - return f.name; - }) - ) { - Type.addError(new Error(f('duplicate field name: %j', schema.fields))); - } - this._fieldsByName = {}; - this.fields = Object.freeze( - schema.fields.map(function (f) { - var field = new Field(f, opts); - this._fieldsByName[field.name] = field; - return field; - }, this), - ); - this._branchConstructor = this._createBranchConstructor(); - this._isError = schema.type === 'error'; - this.recordConstructor = this._createConstructor(opts.errorStackTraces); - this._read = this._createReader(); - this._skip = this._createSkipper(); - this._write = this._createWriter(); - this._check = this._createChecker(); - - opts.namespace = namespace; - Object.freeze(this); -} -util.inherits(RecordType, Type); - -RecordType.prototype._getConstructorName = function () { - return this.name ? unqualify(this.name) : this._isError ? 'Error$' : 'Record$'; -}; - -RecordType.prototype._createConstructor = function (errorStackTraces) { - // jshint -W054 - var outerArgs = []; - var innerArgs = []; - var ds = []; // Defaults. - var innerBody = ''; - var i, l, field, name, defaultValue, hasDefault, stackField; - for (i = 0, l = this.fields.length; i < l; i++) { - field = this.fields[i]; - defaultValue = field.defaultValue; - hasDefault = defaultValue() !== undefined; - name = field.name; - if (errorStackTraces && this._isError && name === 'stack' && Type.isType(field.type, 'string') && !hasDefault) { - // We keep track of whether we've encountered a valid stack field (in - // particular, without a default) to populate a stack trace below. - stackField = field; - } - innerArgs.push('v' + i); - innerBody += ' '; - if (!hasDefault) { - innerBody += 'this.' + name + ' = v' + i + ';\n'; - } else { - innerBody += 'if (v' + i + ' === undefined) { '; - innerBody += 'this.' + name + ' = d' + ds.length + '(); '; - innerBody += '} else { this.' + name + ' = v' + i + '; }\n'; - outerArgs.push('d' + ds.length); - ds.push(defaultValue); - } - } - if (stackField) { - // We should populate a stack trace. - innerBody += ' if (this.stack === undefined) { '; - /* istanbul ignore else */ - if (typeof Error.captureStackTrace == 'function') { - // v8 runtimes, the easy case. - innerBody += 'Error.captureStackTrace(this, this.constructor);'; - } else { - // A few other runtimes (e.g. SpiderMonkey), might not work everywhere. - innerBody += 'this.stack = Error().stack;'; - } - innerBody += ' }\n'; - } - var outerBody = 'return function ' + this._getConstructorName() + '('; - outerBody += innerArgs.join() + ') {\n' + innerBody + '};'; - var Record = new Function(outerArgs.join(), outerBody).apply(undefined, ds); - - var self = this; - Record.getType = function () { - return self; - }; - Record.type = self; - if (this._isError) { - util.inherits(Record, Error); - Record.prototype.name = this._getConstructorName(); - } - Record.prototype.clone = function (o) { - return self.clone(this, o); - }; - Record.prototype.compare = function (v) { - return self.compare(this, v); - }; - Record.prototype.isValid = function (o) { - return self.isValid(this, o); - }; - Record.prototype.toBuffer = function () { - return self.toBuffer(this); - }; - Record.prototype.toString = function () { - return self.toString(this); - }; - Record.prototype.wrap = function () { - return self.wrap(this); - }; - Record.prototype.wrapped = Record.prototype.wrap; // Deprecated. - return Record; -}; - -RecordType.prototype._createChecker = function () { - // jshint -W054 - var names = []; - var values = []; - var name = this._getConstructorName(); - var body = 'return function check' + name + '(v, f, h, p) {\n'; - body += ' if (\n'; - body += ' v === null ||\n'; - body += " typeof v != 'object' ||\n"; - body += ' (f && !this._checkFields(v))\n'; - body += ' ) {\n'; - body += ' if (h) { h(v, this); }\n'; - body += ' return false;\n'; - body += ' }\n'; - if (!this.fields.length) { - // Special case, empty record. We handle this directly. - body += ' return true;\n'; - } else { - for (i = 0, l = this.fields.length; i < l; i++) { - field = this.fields[i]; - names.push('t' + i); - values.push(field.type); - if (field.defaultValue() !== undefined) { - body += ' var v' + i + ' = v.' + field.name + ';\n'; - } - } - body += ' if (h) {\n'; - body += ' var b = 1;\n'; - body += ' var j = p.length;\n'; - body += " p.push('');\n"; - var i, l, field; - for (i = 0, l = this.fields.length; i < l; i++) { - field = this.fields[i]; - body += " p[j] = '" + field.name + "';\n"; - body += ' b &= '; - if (field.defaultValue() === undefined) { - body += 't' + i + '._check(v.' + field.name + ', f, h, p);\n'; - } else { - body += 'v' + i + ' === undefined || '; - body += 't' + i + '._check(v' + i + ', f, h, p);\n'; - } - } - body += ' p.pop();\n'; - body += ' return !!b;\n'; - body += ' } else {\n return (\n '; - body += this.fields - .map(function (field, i) { - return field.defaultValue() === undefined - ? 't' + i + '._check(v.' + field.name + ', f)' - : '(v' + i + ' === undefined || t' + i + '._check(v' + i + ', f))'; - }) - .join(' &&\n '); - body += '\n );\n }\n'; - } - body += '};'; - return new Function(names.join(), body).apply(undefined, values); -}; - -RecordType.prototype._createReader = function () { - // jshint -W054 - var names = []; - var values = [this.recordConstructor]; - var i, l; - for (i = 0, l = this.fields.length; i < l; i++) { - names.push('t' + i); - values.push(this.fields[i].type); - } - var name = this._getConstructorName(); - var body = 'return function read' + name + '(t) {\n'; - body += ' return new ' + name + '(\n '; - body += names - .map(function (s) { - return s + '._read(t)'; - }) - .join(',\n '); - body += '\n );\n};'; - names.unshift(name); - // We can do this since the JS spec guarantees that function arguments are - // evaluated from left to right. - return new Function(names.join(), body).apply(undefined, values); -}; - -RecordType.prototype._createSkipper = function () { - // jshint -W054 - var args = []; - var body = 'return function skip' + this._getConstructorName() + '(t) {\n'; - var values = []; - var i, l; - for (i = 0, l = this.fields.length; i < l; i++) { - args.push('t' + i); - values.push(this.fields[i].type); - body += ' t' + i + '._skip(t);\n'; - } - body += '}'; - return new Function(args.join(), body).apply(undefined, values); -}; - -RecordType.prototype._createWriter = function () { - // jshint -W054 - // We still do default handling here, in case a normal JS object is passed. - var args = []; - var name = this._getConstructorName(); - var body = 'return function write' + name + '(t, v) {\n'; - var values = []; - var i, l, field, value; - for (i = 0, l = this.fields.length; i < l; i++) { - field = this.fields[i]; - args.push('t' + i); - values.push(field.type); - body += ' '; - if (field.defaultValue() === undefined) { - body += 't' + i + '._write(t, v.' + field.name + ');\n'; - } else { - value = field.type.toBuffer(field.defaultValue()).toString('binary'); - // Convert the default value to a binary string ahead of time. We aren't - // converting it to a buffer to avoid retaining too much memory. If we - // had our own buffer pool, this could be an idea in the future. - args.push('d' + i); - values.push(value); - body += 'var v' + i + ' = v.' + field.name + ';\n'; - body += 'if (v' + i + ' === undefined) {\n'; - body += ' t.writeBinary(d' + i + ', ' + value.length + ');\n'; - body += ' } else {\n t' + i + '._write(t, v' + i + ');\n }\n'; - } - } - body += '}'; - return new Function(args.join(), body).apply(undefined, values); -}; - -RecordType.prototype._update = function (resolver, type, opts) { - // jshint -W054 - if (type.name && !~getAliases(this).indexOf(type.name)) { - Type.addError(new Error(f('no alias found for %s', type.name))); - } - - var rFields = this.fields; - var wFields = type.fields; - var wFieldsMap = utils.toMap(wFields, function (f) { - return f.name; - }); - - var innerArgs = []; // Arguments for reader constructor. - var resolvers = {}; // Resolvers keyed by writer field name. - var i, j, field, name, names, matches, fieldResolver; - for (i = 0; i < rFields.length; i++) { - field = rFields[i]; - names = getAliases(field); - matches = []; - for (j = 0; j < names.length; j++) { - name = names[j]; - if (wFieldsMap[name]) { - matches.push(name); - } - } - if (matches.length > 1) { - Type.addError(new Error())(f('ambiguous aliasing for %s.%s (%s)', type.name, field.name, matches)); - } - if (!matches.length) { - if (field.defaultValue() === undefined) { - Type.addError(new Error())(f('no matching field for default-less %s.%s', type.name, field.name)); - } - innerArgs.push('undefined'); - } else { - name = matches[0]; - fieldResolver = { - resolver: field.type.createResolver(wFieldsMap[name].type, opts), - name: '_' + field.name, // Reader field name. - }; - if (!resolvers[name]) { - resolvers[name] = [fieldResolver]; - } else { - resolvers[name].push(fieldResolver); - } - innerArgs.push(fieldResolver.name); - } - } - - // See if we can add a bypass for unused fields at the end of the record. - var lazyIndex = -1; - i = wFields.length; - while (i && resolvers[wFields[--i].name] === undefined) { - lazyIndex = i; - } - - var uname = this._getConstructorName(); - var args = [uname]; - var values = [this.recordConstructor]; - var body = ' return function read' + uname + '(t, b) {\n'; - for (i = 0; i < wFields.length; i++) { - if (i === lazyIndex) { - body += ' if (!b) {\n'; - } - field = type.fields[i]; - name = field.name; - if (resolvers[name] === undefined) { - body += ~lazyIndex && i >= lazyIndex ? ' ' : ' '; - args.push('r' + i); - values.push(field.type); - body += 'r' + i + '._skip(t);\n'; - } else { - j = resolvers[name].length; - while (j--) { - body += ~lazyIndex && i >= lazyIndex ? ' ' : ' '; - args.push('r' + i + 'f' + j); - fieldResolver = resolvers[name][j]; - values.push(fieldResolver.resolver); - body += 'var ' + fieldResolver.name + ' = '; - body += 'r' + i + 'f' + j + '._' + (j ? 'peek' : 'read') + '(t);\n'; - } - } - } - if (~lazyIndex) { - body += ' }\n'; - } - body += ' return new ' + uname + '(' + innerArgs.join() + ');\n};'; - - resolver._read = new Function(args.join(), body).apply(undefined, values); -}; - -RecordType.prototype._match = function (tap1, tap2) { - var fields = this.fields; - var i, l, field, order, type; - for (i = 0, l = fields.length; i < l; i++) { - field = fields[i]; - order = field._order; - type = field.type; - if (order) { - order *= type._match(tap1, tap2); - if (order) { - return order; - } - } else { - type._skip(tap1); - type._skip(tap2); - } - } - return 0; -}; - -RecordType.prototype._checkFields = function (obj) { - var keys = Object.keys(obj); - var i, l; - for (i = 0, l = keys.length; i < l; i++) { - if (!this._fieldsByName[keys[i]]) { - return false; - } - } - return true; -}; - -RecordType.prototype._copy = function (val, opts) { - // jshint -W058 - var hook = opts && opts.fieldHook; - var values = [undefined]; - var i, l, field, value; - for (i = 0, l = this.fields.length; i < l; i++) { - field = this.fields[i]; - value = val[field.name]; - if (value === undefined && field.hasOwnProperty('defaultValue')) { - value = field.defaultValue(); - } - if ((opts && !opts.skip) || value !== undefined) { - value = field.type._copy(value, opts); - } - if (hook) { - value = hook(field, value, this); - } - values.push(value); - } - var Record = this.recordConstructor; - return new (Record.bind.apply(Record, values))(); -}; - -RecordType.prototype._deref = function (schema, opts) { - schema.fields = this.fields.map(function (field) { - var fieldType = field.type; - var fieldSchema = { - name: field.name, - type: fieldType._attrs(opts), - }; - if (opts.exportAttrs) { - var val = field.defaultValue(); - if (val !== undefined) { - // We must both unwrap all unions and coerce buffers to strings. - fieldSchema['default'] = fieldType._copy(val, { coerce: 3, wrap: 3 }); - } - var fieldOrder = field.order; - if (fieldOrder !== 'ascending') { - fieldSchema.order = fieldOrder; - } - var fieldAliases = field.aliases; - if (fieldAliases.length) { - fieldSchema.aliases = fieldAliases; - } - var fieldDoc = field.doc; - if (fieldDoc !== undefined) { - fieldSchema.doc = fieldDoc; - } - } - return fieldSchema; - }); -}; - -RecordType.prototype.compare = function (val1, val2) { - var fields = this.fields; - var i, l, field, name, order, type; - for (i = 0, l = fields.length; i < l; i++) { - field = fields[i]; - name = field.name; - order = field._order; - type = field.type; - if (order) { - order *= type.compare(val1[name], val2[name]); - if (order) { - return order; - } - } - } - return 0; -}; - -RecordType.prototype.random = function () { - // jshint -W058 - var fields = this.fields.map(function (f) { - return f.type.random(); - }); - fields.unshift(undefined); - var Record = this.recordConstructor; - return new (Record.bind.apply(Record, fields))(); -}; - -RecordType.prototype.field = function (name) { - return this._fieldsByName[name]; -}; - -RecordType.prototype.getField = RecordType.prototype.field; - -RecordType.prototype.getFields = function () { - return this.fields; -}; - -RecordType.prototype.getRecordConstructor = function () { - return this.recordConstructor; -}; - -Object.defineProperty(RecordType.prototype, 'typeName', { - enumerable: true, - get: function () { - return this._isError ? 'error' : 'record'; - }, -}); - -/** Derived type abstract class. */ -function LogicalType(schema, opts) { - this._logicalTypeName = schema.logicalType; - Type.call(this); - LOGICAL_TYPE = this; - try { - this._underlyingType = Type.forSchema(schema, opts); - } finally { - LOGICAL_TYPE = null; - // Remove the underlying type now that we're done instantiating. Note that - // in some (rare) cases, it might not have been inserted; for example, if - // this constructor was manually called with an already instantiated type. - var l = UNDERLYING_TYPES.length; - if (l && UNDERLYING_TYPES[l - 1][0] === this) { - UNDERLYING_TYPES.pop(); - } - } - // We create a separate branch constructor for logical types to keep them - // monomorphic. - if (Type.isType(this.underlyingType, 'union')) { - this._branchConstructor = this.underlyingType._branchConstructor; - } else { - this._branchConstructor = this.underlyingType._createBranchConstructor(); - } - // We don't freeze derived types to allow arbitrary properties. Implementors - // can still do so in the subclass' constructor at their convenience. -} -util.inherits(LogicalType, Type); - -Object.defineProperty(LogicalType.prototype, 'typeName', { - enumerable: true, - get: function () { - return 'logical:' + this._logicalTypeName; - }, -}); - -Object.defineProperty(LogicalType.prototype, 'underlyingType', { - enumerable: true, - get: function () { - if (this._underlyingType) { - return this._underlyingType; - } - // If the field wasn't present, it means the logical type isn't complete - // yet: we're waiting on its underlying type to be fully instantiated. In - // this case, it will be present in the `UNDERLYING_TYPES` array. - var i, l, arr; - for (i = 0, l = UNDERLYING_TYPES.length; i < l; i++) { - arr = UNDERLYING_TYPES[i]; - if (arr[0] === this) { - return arr[1]; - } - } - }, -}); - -LogicalType.prototype.getUnderlyingType = function () { - return this.underlyingType; -}; - -LogicalType.prototype._read = function (tap) { - return this._fromValue(this.underlyingType._read(tap)); -}; - -LogicalType.prototype._write = function (tap, any) { - this.underlyingType._write(tap, this._toValue(any)); -}; - -LogicalType.prototype._check = function (any, flags, hook, path) { - try { - var val = this._toValue(any); - } catch (err) { - // Handled below. - } - if (val === undefined) { - if (hook) { - hook(any, this); - } - return false; - } - return this.underlyingType._check(val, flags, hook, path); -}; - -LogicalType.prototype._copy = function (any, opts) { - var type = this.underlyingType; - switch (opts && opts.coerce) { - case 3: // To string. - return type._copy(this._toValue(any), opts); - case 2: // From string. - return this._fromValue(type._copy(any, opts)); - default: // Normal copy. - return this._fromValue(type._copy(this._toValue(any), opts)); - } -}; - -LogicalType.prototype._update = function (resolver, type, opts) { - var _fromValue = this._resolve(type, opts); - if (_fromValue) { - resolver._read = function (tap) { - return _fromValue(type._read(tap)); - }; - } -}; - -LogicalType.prototype.compare = function (obj1, obj2) { - var val1 = this._toValue(obj1); - var val2 = this._toValue(obj2); - return this.underlyingType.compare(val1, val2); -}; - -LogicalType.prototype.random = function () { - return this._fromValue(this.underlyingType.random()); -}; - -LogicalType.prototype._deref = function (schema, opts) { - var type = this.underlyingType; - var isVisited = type.name !== undefined && opts.derefed[type.name]; - schema = type._attrs(opts); - if (!isVisited && opts.exportAttrs) { - if (typeof schema == 'string') { - schema = { type: schema }; - } - schema.logicalType = this._logicalTypeName; - this._export(schema); - } - return schema; -}; - -LogicalType.prototype._skip = function (tap) { - this.underlyingType._skip(tap); -}; - -// Unlike the other methods below, `_export` has a reasonable default which we -// can provide (not exporting anything). -LogicalType.prototype._export = function (/* schema */) {}; - -// Methods to be implemented. -LogicalType.prototype._fromValue = utils.abstractFunction; -LogicalType.prototype._toValue = utils.abstractFunction; -LogicalType.prototype._resolve = utils.abstractFunction; - -// General helpers. - -/** - * Customizable long. - * - * This allows support of arbitrarily large long (e.g. larger than - * `Number.MAX_SAFE_INTEGER`). See `LongType.__with` method above. Note that we - * can't use a logical type because we need a "lower-level" hook here: passing - * through through the standard long would cause a loss of precision. - */ -function AbstractLongType(noUnpack) { - this._concreteTypeName = 'long'; - PrimitiveType.call(this, true); - // Note that this type "inherits" `LongType` (i.e. gain its prototype - // methods) but only "subclasses" `PrimitiveType` to avoid being prematurely - // frozen. - this._noUnpack = !!noUnpack; -} -util.inherits(AbstractLongType, LongType); - -AbstractLongType.prototype.typeName = 'abstract:long'; - -AbstractLongType.prototype._check = function (val, flags, hook) { - var b = this._isValid(val); - if (!b && hook) { - hook(val, this); - } - return b; -}; - -AbstractLongType.prototype._read = function (tap) { - var buf, pos; - if (this._noUnpack) { - pos = tap.pos; - tap.skipLong(); - buf = tap.buf.slice(pos, tap.pos); - } else { - buf = tap.unpackLongBytes(tap); - } - if (tap.isValid()) { - return this._fromBuffer(buf); - } -}; - -AbstractLongType.prototype._write = function (tap, val) { - if (!this._isValid(val)) { - throwInvalidError(val, this); - } - var buf = this._toBuffer(val); - if (this._noUnpack) { - tap.writeFixed(buf); - } else { - tap.packLongBytes(buf); - } -}; - -AbstractLongType.prototype._copy = function (val, opts) { - switch (opts && opts.coerce) { - case 3: // To string. - return this._toJSON(val); - case 2: // From string. - return this._fromJSON(val); - default: // Normal copy. - // Slow but guarantees most consistent results. Faster alternatives would - // require assumptions on the long class used (e.g. immutability). - return this._fromJSON(this._toJSON(val)); - } -}; - -AbstractLongType.prototype._deref = function () { - return 'long'; -}; - -AbstractLongType.prototype._update = function (resolver, type) { - var self = this; - switch (type.typeName) { - case 'int': - resolver._read = function (tap) { - return self._fromJSON(type._read(tap)); - }; - break; - case 'abstract:long': - case 'long': - resolver._read = function (tap) { - return self._read(tap); - }; - } -}; - -AbstractLongType.prototype.random = function () { - return this._fromJSON(LongType.prototype.random()); -}; - -// Methods to be implemented by the user. -AbstractLongType.prototype._fromBuffer = utils.abstractFunction; -AbstractLongType.prototype._toBuffer = utils.abstractFunction; -AbstractLongType.prototype._fromJSON = utils.abstractFunction; -AbstractLongType.prototype._toJSON = utils.abstractFunction; -AbstractLongType.prototype._isValid = utils.abstractFunction; -AbstractLongType.prototype.compare = utils.abstractFunction; - -/** A record field. */ -function Field(schema, opts) { - const saveAddError = Type.addError; - Type.addError = err => { - err.fieldName = err.fieldName ? schema.name + '.' + err.fieldName : schema.name; - saveAddError(err); - }; - var name = schema.name; - if (typeof name != 'string' || !isValidName(name)) { - Type.addError(new Error(f('invalid field name: %s', name))); - } - - this.name = name; - this.type = Type.forSchema(schema.type, opts); - this.aliases = schema.aliases || []; - this.doc = schema.doc !== undefined ? '' + schema.doc : undefined; - - this._order = (function (order) { - switch (order) { - case 'ascending': - return 1; - case 'descending': - return -1; - case 'ignore': - return 0; - default: - Type.addError(new Error(f('invalid order: %j', order))); - } - })(schema.order === undefined ? 'ascending' : schema.order); - - var value = schema['default']; - - if (value === null && this.type.typeName === 'record') { - Type.addError(new Error(f('invalid default for field %s: null is not a %s', this.name, this.type.typeName))); - } else if (value !== undefined) { - // We need to convert defaults back to a valid format (unions are - // disallowed in default definitions, only the first type of each union is - // allowed instead). - // http://apache-avro.679487.n3.nabble.com/field-union-default-in-Java-td1175327.html - var type = this.type; - var val = type._copy(value, { coerce: 2, wrap: 2 }); - // The clone call above will throw an error if the default is invalid. - if (isPrimitive(type.typeName) && type.typeName !== 'bytes') { - // These are immutable. - this.defaultValue = function () { - return val; - }; - } else { - this.defaultValue = function () { - return type._copy(val); - }; - } - } - Type.addError = saveAddError; - - Object.freeze(this); -} - -Field.prototype.defaultValue = function () {}; // Undefined default. - -Object.defineProperty(Field.prototype, 'order', { - enumerable: true, - get: function () { - return ['descending', 'ignore', 'ascending'][this._order + 1]; - }, -}); - -Field.prototype.getAliases = function () { - return this.aliases; -}; - -Field.prototype.getDefault = Field.prototype.defaultValue; - -Field.prototype.getName = function () { - return this.name; -}; - -Field.prototype.getOrder = function () { - return this.order; -}; - -Field.prototype.getType = function () { - return this.type; -}; - -/** - * Resolver to read a writer's schema as a new schema. - * - * @param readerType {Type} The type to convert to. - */ -function Resolver(readerType) { - // Add all fields here so that all resolvers share the same hidden class. - this._readerType = readerType; - this._read = null; - this.itemsType = null; - this.size = 0; - this.symbols = null; - this.valuesType = null; -} - -Resolver.prototype._peek = Type.prototype._peek; - -Resolver.prototype.inspect = function () { - return ''; -}; - -/** Mutable hash container. */ -function Hash() { - this.str = undefined; -} - -/** - * Read a value from a tap. - * - * @param type {Type} The type to decode. - * @param tap {Tap} The tap to read from. No checks are performed here. - * @param resolver {Resolver} Optional resolver. It must match the input type. - * @param lazy {Boolean} Skip trailing fields when using a resolver. - */ -function readValue(type, tap, resolver, lazy) { - if (resolver) { - if (resolver._readerType !== type) { - Type.addError(new Error('invalid resolver')); - } - return resolver._read(tap, lazy); - } else { - return type._read(tap); - } -} - -/** - * Remove namespace from a name. - * - * @param name {String} Full or short name. - */ -function unqualify(name) { - var parts = name.split('.'); - return parts[parts.length - 1]; -} - -/** - * Verify and return fully qualified name. - * - * @param name {String} Full or short name. It can be prefixed with a dot to - * force global namespace. - * @param namespace {String} Optional namespace. - */ -function qualify(name, namespace) { - if (~name.indexOf('.')) { - name = name.replace(/^\./, ''); // Allow absolute referencing. - } else if (namespace) { - name = namespace + '.' + name; - } - name.split('.').forEach(function (part) { - if (!isValidName(part)) { - Type.addError(new Error(f('invalid name: %j', name))); - } - }); - var tail = unqualify(name); - // Primitives are always in the global namespace. - return isPrimitive(tail) ? tail : name; -} - -/** - * Get all aliases for a type (including its name). - * - * @param obj {Type|Object} Typically a type or a field. Its aliases property - * must exist and be an array. - */ -function getAliases(obj) { - var names = {}; - if (obj.name) { - names[obj.name] = true; - } - var aliases = obj.aliases; - var i, l; - for (i = 0, l = aliases.length; i < l; i++) { - names[aliases[i]] = true; - } - return Object.keys(names); -} - -/** - * Check whether a type's name is a primitive. - * - * @param name {String} Type name (e.g. `'string'`, `'array'`). - */ -function isPrimitive(typeName) { - // Since we use this module's own `TYPES` object, we can use `instanceof`. - var type = TYPES[typeName]; - return type && type.prototype instanceof PrimitiveType; -} - -/** - * Return a type's class name from its Avro type name. - * - * We can't simply use `constructor.name` since it isn't supported in all - * browsers. - * - * @param typeName {String} Type name. - */ -function getClassName(typeName) { - if (typeName === 'error') { - typeName = 'record'; - } else { - var match = /^([^:]+):(.*)$/.exec(typeName); - if (match) { - if (match[1] === 'union') { - typeName = match[2] + 'Union'; - } else { - // Logical type. - typeName = match[1]; - } - } - } - return utils.capitalize(typeName) + 'Type'; -} - -/** - * Get the number of elements in an array block. - * - * @param tap {Tap} A tap positioned at the beginning of an array block. - */ -function readArraySize(tap) { - var n = tap.readLong(); - if (n < 0) { - n = -n; - tap.skipLong(); // Skip size. - } - return n; -} - -/** - * Check whether a long can be represented without precision loss. - * - * @param n {Number} The number. - * - * Two things to note: - * - * + We are not using the `Number` constants for compatibility with older - * browsers. - * + We must remove one from each bound because of rounding errors. - */ -function isSafeLong(n) { - return n >= -9007199254740990 && n <= 9007199254740990; -} - -/** - * Check whether an object is the JSON representation of a buffer. - */ -function isJsonBuffer(obj) { - return obj && obj.type === 'Buffer' && Array.isArray(obj.data); -} - -/** - * Check whether a string is a valid Avro identifier. - */ -function isValidName(str) { - return NAME_PATTERN.test(str); -} - -/** - * Throw a somewhat helpful error on invalid object. - * - * @param path {Array} Passed from hook, but unused (because empty where this - * function is used, since we aren't keeping track of it for effiency). - * @param val {...} The object to reject. - * @param type {Type} The type to check against. - * - * This method is mostly used from `_write` to signal an invalid object for a - * given type. Note that this provides less information than calling `isValid` - * with a hook since the path is not propagated (for efficiency reasons). - */ -function throwInvalidError(val, type) { - Type.addError(new Error(f('invalid %j: %j', type.schema(), val))); -} - -/** - * Get a type's bucket when included inside an unwrapped union. - * - * @param type {Type} Any type. - */ -function getTypeBucket(type) { - var typeName = type.typeName; - switch (typeName) { - case 'double': - case 'float': - case 'int': - case 'long': - return 'number'; - case 'bytes': - case 'fixed': - return 'buffer'; - case 'enum': - return 'string'; - case 'map': - case 'error': - case 'record': - return 'object'; - default: - return typeName; - } -} - -/** - * Infer a value's bucket (see unwrapped unions for more details). - * - * @param val {...} Any value. - */ -function getValueBucket(val) { - if (val === null) { - return 'null'; - } - var bucket = typeof val; - if (bucket === 'object') { - // Could be bytes, fixed, array, map, or record. - if (Array.isArray(val)) { - return 'array'; - } else if (Buffer.isBuffer(val)) { - return 'buffer'; - } - } - return bucket; -} - -/** - * Check whether a collection of types leads to an ambiguous union. - * - * @param types {Array} Array of types. - */ -function isAmbiguous(types) { - var buckets = {}; - var i, l, bucket, type; - for (i = 0, l = types.length; i < l; i++) { - type = types[i]; - if (!Type.isType(type, 'logical')) { - bucket = getTypeBucket(type); - if (buckets[bucket]) { - return true; - } - buckets[bucket] = true; - } - } - return false; -} - -/** - * Combine number types. - * - * Note that never have to create a new type here, we are guaranteed to be able - * to reuse one of the input types as super-type. - */ -function combineNumbers(types) { - var typeNames = ['int', 'long', 'float', 'double']; - var superIndex = -1; - var superType = null; - var i, l, type, index; - for (i = 0, l = types.length; i < l; i++) { - type = types[i]; - index = typeNames.indexOf(type.typeName); - if (index > superIndex) { - superIndex = index; - superType = type; - } - } - return superType; -} - -/** - * Combine enums and strings. - * - * The order of the returned symbols is undefined and the returned enum is - * - */ -function combineStrings(types, opts) { - var symbols = {}; - var i, l, type, typeSymbols; - for (i = 0, l = types.length; i < l; i++) { - type = types[i]; - if (type.typeName === 'string') { - // If at least one of the types is a string, it will be the supertype. - return type; - } - typeSymbols = type.symbols; - var j, m; - for (j = 0, m = typeSymbols.length; j < m; j++) { - symbols[typeSymbols[j]] = true; - } - } - return Type.forSchema({ type: 'enum', symbols: Object.keys(symbols) }, opts); -} - -/** - * Combine bytes and fixed. - * - * This function is optimized to avoid creating new types when possible: in - * case of a size mismatch between fixed types, it will continue looking - * through the array to find an existing bytes type (rather than exit early by - * creating one eagerly). - */ -function combineBuffers(types, opts) { - var size = -1; - var i, l, type; - for (i = 0, l = types.length; i < l; i++) { - type = types[i]; - if (type.typeName === 'bytes') { - return type; - } - if (size === -1) { - size = type.size; - } else if (type.size !== size) { - // Don't create a bytes type right away, we might be able to reuse one - // later on in the types array. Just mark this for now. - size = -2; - } - } - return size < 0 ? Type.forSchema('bytes', opts) : types[0]; -} - -/** - * Combine maps and records. - * - * Field defaults are kept when possible (i.e. when no coercion to a map - * happens), with later definitions overriding previous ones. - */ -function combineObjects(types, opts) { - var allTypes = []; // Field and value types. - var fieldTypes = {}; // Record field types grouped by field name. - var fieldDefaults = {}; - var isValidRecord = true; - - // Check whether the final type will be a map or a record. - var i, l, type, fields; - for (i = 0, l = types.length; i < l; i++) { - type = types[i]; - if (type.typeName === 'map') { - isValidRecord = false; - allTypes.push(type.valuesType); - } else { - fields = type.fields; - var j, m, field, fieldDefault, fieldName, fieldType; - for (j = 0, m = fields.length; j < m; j++) { - field = fields[j]; - fieldName = field.name; - fieldType = field.type; - allTypes.push(fieldType); - if (isValidRecord) { - if (!fieldTypes[fieldName]) { - fieldTypes[fieldName] = []; - } - fieldTypes[fieldName].push(fieldType); - fieldDefault = field.defaultValue(); - if (fieldDefault !== undefined) { - // Later defaults will override any previous ones. - fieldDefaults[fieldName] = fieldDefault; - } - } - } - } - } - - if (isValidRecord) { - // Check that no fields are missing and that we have the approriate - // defaults for those which are. - var fieldNames = Object.keys(fieldTypes); - for (i = 0, l = fieldNames.length; i < l; i++) { - fieldName = fieldNames[i]; - if (fieldTypes[fieldName].length < types.length && fieldDefaults[fieldName] === undefined) { - // At least one of the records is missing a field with no default. - if (opts && opts.strictDefaults) { - isValidRecord = false; - } else { - fieldTypes[fieldName].unshift(Type.forSchema('null', opts)); - fieldDefaults[fieldName] = null; - } - } - } - } - - var schema; - if (isValidRecord) { - schema = { - type: 'record', - fields: fieldNames.map(function (s) { - var fieldType = Type.forTypes(fieldTypes[s], opts); - var fieldDefault = fieldDefaults[s]; - if (fieldDefault !== undefined && ~fieldType.typeName.indexOf('union')) { - // Ensure that the default's corresponding type is first. - var unionTypes = fieldType.types.slice(); - var i, l; - for (i = 0, l = unionTypes.length; i < l; i++) { - if (unionTypes[i].isValid(fieldDefault)) { - break; - } - } - if (i > 0) { - var unionType = unionTypes[0]; - unionTypes[0] = unionTypes[i]; - unionTypes[i] = unionType; - fieldType = Type.forSchema(unionTypes, opts); - } - } - return { - name: s, - type: fieldType, - 'default': fieldDefaults[s], - }; - }), - }; - } else { - schema = { - type: 'map', - values: Type.forTypes(allTypes, opts), - }; - } - return Type.forSchema(schema, opts); -} - -var errorsCollector = []; - -Type.addError = err => errorsCollector.push(err); - -module.exports = { - Type: Type, - getTypeBucket: getTypeBucket, - getValueBucket: getValueBucket, - isPrimitive: isPrimitive, - isValidName: isValidName, - qualify: qualify, - builtins: (function () { - var types = { - LogicalType: LogicalType, - UnwrappedUnionType: UnwrappedUnionType, - WrappedUnionType: WrappedUnionType, - }; - var typeNames = Object.keys(TYPES); - var i, l, typeName; - for (i = 0, l = typeNames.length; i < l; i++) { - typeName = typeNames[i]; - types[getClassName(typeName)] = TYPES[typeName]; - } - return types; - })(), - errorsCollector, -}; diff --git a/forward_engineering/modules/avsc/lib/utils.js b/forward_engineering/modules/avsc/lib/utils.js deleted file mode 100644 index adc8d358..00000000 --- a/forward_engineering/modules/avsc/lib/utils.js +++ /dev/null @@ -1,891 +0,0 @@ -/* jshint node: true */ - -// TODO: Make long comparison impervious to precision loss. -// TODO: Optimize binary comparison methods. - -'use strict'; - -/** Various utilities used across this library. */ - -var crypto = require('crypto'); -var util = require('util'); - -// Shared buffer pool for all taps. -var POOL = new BufferPool(4096); - -/** - * Create a new empty buffer. - * - * @param size {Number} The buffer's size. - */ -function newBuffer(size) { - if (typeof Buffer.alloc == 'function') { - return Buffer.alloc(size); - } else { - return new Buffer(size); - } -} - -/** - * Create a new buffer with the input contents. - * - * @param data {Array|String} The buffer's data. - * @param enc {String} Encoding, used if data is a string. - */ -function bufferFrom(data, enc) { - if (typeof Buffer.from == 'function') { - return Buffer.from(data, enc); - } else { - return new Buffer(data, enc); - } -} - -/** - * Uppercase the first letter of a string. - * - * @param s {String} The string. - */ -function capitalize(s) { - return s.charAt(0).toUpperCase() + s.slice(1); -} - -/** - * Compare two numbers. - * - * @param n1 {Number} The first one. - * @param n2 {Number} The second one. - */ -function compare(n1, n2) { - return n1 === n2 ? 0 : n1 < n2 ? -1 : 1; -} - -/** - * Get option or default if undefined. - * - * @param opts {Object} Options. - * @param key {String} Name of the option. - * @param def {...} Default value. - * - * This is useful mostly for true-ish defaults and false-ish values (where the - * usual `||` idiom breaks down). - */ -function getOption(opts, key, def) { - var value = opts[key]; - return value === undefined ? def : value; -} - -/** - * Compute a string's hash. - * - * @param str {String} The string to hash. - * @param algorithm {String} The algorithm used. Defaults to MD5. - */ -function getHash(str, algorithm) { - algorithm = algorithm || 'md5'; - var hash = crypto.createHash(algorithm); - hash.end(str); - return hash.read(); -} - -/** - * Find index of value in array. - * - * @param arr {Array} Can also be a false-ish value. - * @param v {Object} Value to find. - * - * Returns -1 if not found, -2 if found multiple times. - */ -function singleIndexOf(arr, v) { - var pos = -1; - var i, l; - if (!arr) { - return -1; - } - for (i = 0, l = arr.length; i < l; i++) { - if (arr[i] === v) { - if (pos >= 0) { - return -2; - } - pos = i; - } - } - return pos; -} - -/** - * Convert array to map. - * - * @param arr {Array} Elements. - * @param fn {Function} Function returning an element's key. - */ -function toMap(arr, fn) { - var obj = {}; - var i, elem; - for (i = 0; i < arr.length; i++) { - elem = arr[i]; - obj[fn(elem)] = elem; - } - return obj; -} - -/** - * Convert map to array of values (polyfill for `Object.values`). - * - * @param obj {Object} Map. - */ -function objectValues(obj) { - return Object.keys(obj).map(function (key) { - return obj[key]; - }); -} - -/** - * Check whether an array has duplicates. - * - * @param arr {Array} The array. - * @param fn {Function} Optional function to apply to each element. - */ -function hasDuplicates(arr, fn) { - var obj = {}; - var i, l, elem; - for (i = 0, l = arr.length; i < l; i++) { - elem = arr[i]; - if (fn) { - elem = fn(elem); - } - if (obj[elem]) { - return true; - } - obj[elem] = true; - } - return false; -} - -/** - * Copy properties from one object to another. - * - * @param src {Object} The source object. - * @param dst {Object} The destination object. - * @param overwrite {Boolean} Whether to overwrite existing destination - * properties. Defaults to false. - */ -function copyOwnProperties(src, dst, overwrite) { - var names = Object.getOwnPropertyNames(src); - var i, l, name; - for (i = 0, l = names.length; i < l; i++) { - name = names[i]; - if (!dst.hasOwnProperty(name) || overwrite) { - var descriptor = Object.getOwnPropertyDescriptor(src, name); - Object.defineProperty(dst, name, descriptor); - } - } - return dst; -} - -/** - * Returns offset in the string of the end of JSON object (-1 if past the end). - * - * To keep the implementation simple, this function isn't a JSON validator. It - * will gladly return a result for invalid JSON (which is OK since that will be - * promptly rejected by the JSON parser). What matters is that it is guaranteed - * to return the correct end when presented with valid JSON. - * - * @param str {String} Input string containing serialized JSON.. - * @param pos {Number} Starting position. - */ -function jsonEnd(str, pos) { - pos = pos | 0; - - // Handle the case of a simple literal separately. - var c = str.charAt(pos++); - if (/[\d-]/.test(c)) { - while (/[eE\d.+-]/.test(str.charAt(pos))) { - pos++; - } - return pos; - } else if (/true|null/.test(str.slice(pos - 1, pos + 3))) { - return pos + 3; - } else if (/false/.test(str.slice(pos - 1, pos + 4))) { - return pos + 4; - } - - // String, object, or array. - var depth = 0; - var literal = false; - do { - switch (c) { - case '{': - case '[': - if (!literal) { - depth++; - } - break; - case '}': - case ']': - if (!literal && !--depth) { - return pos; - } - break; - case '"': - literal = !literal; - if (!depth && !literal) { - return pos; - } - break; - case '\\': - pos++; // Skip the next character. - } - } while ((c = str.charAt(pos++))); - - return -1; -} - -/** "Abstract" function to help with "subclassing". */ -function abstractFunction() { - throw new Error('abstract'); -} - -/** Batch-deprecate "getters" from an object's prototype. */ -function addDeprecatedGetters(obj, props) { - var proto = obj.prototype; - var i, l, prop, getter; - for (i = 0, l = props.length; i < l; i++) { - prop = props[i]; - getter = 'get' + capitalize(prop); - proto[getter] = util.deprecate(createGetter(prop), 'use `.' + prop + '` instead of `.' + getter + '()`'); - } - - function createGetter(prop) { - return function () { - var delegate = this[prop]; - return typeof delegate == 'function' ? delegate.apply(this, arguments) : delegate; - }; - } -} - -/** - * Simple buffer pool to avoid allocating many small buffers. - * - * This provides significant speedups in recent versions of node (6+). - */ -function BufferPool(len) { - this._len = len | 0; - this._pos = 0; - this._slab = newBuffer(this._len); -} - -BufferPool.prototype.alloc = function (len) { - var maxLen = this._len; - if (len > maxLen) { - return newBuffer(len); - } - if (this._pos + len > maxLen) { - this._slab = newBuffer(maxLen); - this._pos = 0; - } - return this._slab.slice(this._pos, (this._pos += len)); -}; - -/** - * Generator of random things. - * - * Inspired by: http://stackoverflow.com/a/424445/1062617 - */ -function Lcg(seed) { - var a = 1103515245; - var c = 12345; - var m = Math.pow(2, 31); - var state = Math.floor(seed || Math.random() * (m - 1)); - - this._max = m; - this._nextInt = function () { - return (state = (a * state + c) % m); - }; -} - -Lcg.prototype.nextBoolean = function () { - // jshint -W018 - return !!(this._nextInt() % 2); -}; - -Lcg.prototype.nextInt = function (start, end) { - if (end === undefined) { - end = start; - start = 0; - } - end = end === undefined ? this._max : end; - return start + Math.floor(this.nextFloat() * (end - start)); -}; - -Lcg.prototype.nextFloat = function (start, end) { - if (end === undefined) { - end = start; - start = 0; - } - end = end === undefined ? 1 : end; - return start + ((end - start) * this._nextInt()) / this._max; -}; - -Lcg.prototype.nextString = function (len, flags) { - len |= 0; - flags = flags || 'aA'; - var mask = ''; - if (flags.indexOf('a') > -1) { - mask += 'abcdefghijklmnopqrstuvwxyz'; - } - if (flags.indexOf('A') > -1) { - mask += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - } - if (flags.indexOf('#') > -1) { - mask += '0123456789'; - } - if (flags.indexOf('!') > -1) { - mask += '~`!@#$%^&*()_+-={}[]:";\'<>?,./|\\'; - } - var result = []; - for (var i = 0; i < len; i++) { - result.push(this.choice(mask)); - } - return result.join(''); -}; - -Lcg.prototype.nextBuffer = function (len) { - var arr = []; - var i; - for (i = 0; i < len; i++) { - arr.push(this.nextInt(256)); - } - return bufferFrom(arr); -}; - -Lcg.prototype.choice = function (arr) { - var len = arr.length; - if (!len) { - throw new Error('choosing from empty array'); - } - return arr[this.nextInt(len)]; -}; - -/** - * Ordered queue which returns items consecutively. - * - * This is actually a heap by index, with the added requirements that elements - * can only be retrieved consecutively. - */ -function OrderedQueue() { - this._index = 0; - this._items = []; -} - -OrderedQueue.prototype.push = function (item) { - var items = this._items; - var i = items.length | 0; - var j; - items.push(item); - while (i > 0 && items[i].index < items[(j = (i - 1) >> 1)].index) { - item = items[i]; - items[i] = items[j]; - items[j] = item; - i = j; - } -}; - -OrderedQueue.prototype.pop = function () { - var items = this._items; - var len = (items.length - 1) | 0; - var first = items[0]; - if (!first || first.index > this._index) { - return null; - } - this._index++; - if (!len) { - items.pop(); - return first; - } - items[0] = items.pop(); - var mid = len >> 1; - var i = 0; - var i1, i2, j, item, c, c1, c2; - while (i < mid) { - item = items[i]; - i1 = (i << 1) + 1; - i2 = (i + 1) << 1; - c1 = items[i1]; - c2 = items[i2]; - if (!c2 || c1.index <= c2.index) { - c = c1; - j = i1; - } else { - c = c2; - j = i2; - } - if (c.index >= item.index) { - break; - } - items[j] = item; - items[i] = c; - i = j; - } - return first; -}; - -/** - * A tap is a buffer which remembers what has been already read. - * - * It is optimized for performance, at the cost of failing silently when - * overflowing the buffer. This is a purposeful trade-off given the expected - * rarity of this case and the large performance hit necessary to enforce - * validity. See `isValid` below for more information. - */ -function Tap(buf, pos) { - this.buf = buf; - this.pos = pos | 0; - if (this.pos < 0) { - throw new Error('negative offset'); - } -} - -/** - * Check that the tap is in a valid state. - * - * For efficiency reasons, none of the methods below will fail if an overflow - * occurs (either read, skip, or write). For this reason, it is up to the - * caller to always check that the read, skip, or write was valid by calling - * this method. - */ -Tap.prototype.isValid = function () { - return this.pos <= this.buf.length; -}; - -// Read, skip, write methods. -// -// These should fail silently when the buffer overflows. Note this is only -// required to be true when the functions are decoding valid objects. For -// example errors will still be thrown if a bad count is read, leading to a -// negative position offset (which will typically cause a failure in -// `readFixed`). - -Tap.prototype.readBoolean = function () { - return !!this.buf[this.pos++]; -}; - -Tap.prototype.skipBoolean = function () { - this.pos++; -}; - -Tap.prototype.writeBoolean = function (b) { - this.buf[this.pos++] = !!b; -}; - -Tap.prototype.readInt = Tap.prototype.readLong = function () { - var n = 0; - var k = 0; - var buf = this.buf; - var b, h, f, fk; - - do { - b = buf[this.pos++]; - h = b & 0x80; - n |= (b & 0x7f) << k; - k += 7; - } while (h && k < 28); - - if (h) { - // Switch to float arithmetic, otherwise we might overflow. - f = n; - fk = 268435456; // 2 ** 28. - do { - b = buf[this.pos++]; - f += (b & 0x7f) * fk; - fk *= 128; - } while (b & 0x80); - return (f % 2 ? -(f + 1) : f) / 2; - } - - return (n >> 1) ^ -(n & 1); -}; - -Tap.prototype.skipInt = Tap.prototype.skipLong = function () { - var buf = this.buf; - while (buf[this.pos++] & 0x80) {} -}; - -Tap.prototype.writeInt = Tap.prototype.writeLong = function (n) { - var buf = this.buf; - var f, m; - - if (n >= -1073741824 && n < 1073741824) { - // Won't overflow, we can use integer arithmetic. - m = n >= 0 ? n << 1 : (~n << 1) | 1; - do { - buf[this.pos] = m & 0x7f; - m >>= 7; - } while (m && (buf[this.pos++] |= 0x80)); - } else { - // We have to use slower floating arithmetic. - f = n >= 0 ? n * 2 : -n * 2 - 1; - do { - buf[this.pos] = f & 0x7f; - f /= 128; - } while (f >= 1 && (buf[this.pos++] |= 0x80)); - } - this.pos++; -}; - -Tap.prototype.readFloat = function () { - var buf = this.buf; - var pos = this.pos; - this.pos += 4; - if (this.pos > buf.length) { - return; - } - return this.buf.readFloatLE(pos); -}; - -Tap.prototype.skipFloat = function () { - this.pos += 4; -}; - -Tap.prototype.writeFloat = function (f) { - var buf = this.buf; - var pos = this.pos; - this.pos += 4; - if (this.pos > buf.length) { - return; - } - return this.buf.writeFloatLE(f, pos); -}; - -Tap.prototype.readDouble = function () { - var buf = this.buf; - var pos = this.pos; - this.pos += 8; - if (this.pos > buf.length) { - return; - } - return this.buf.readDoubleLE(pos); -}; - -Tap.prototype.skipDouble = function () { - this.pos += 8; -}; - -Tap.prototype.writeDouble = function (d) { - var buf = this.buf; - var pos = this.pos; - this.pos += 8; - if (this.pos > buf.length) { - return; - } - return this.buf.writeDoubleLE(d, pos); -}; - -Tap.prototype.readFixed = function (len) { - var pos = this.pos; - this.pos += len; - if (this.pos > this.buf.length) { - return; - } - var fixed = POOL.alloc(len); - this.buf.copy(fixed, 0, pos, pos + len); - return fixed; -}; - -Tap.prototype.skipFixed = function (len) { - this.pos += len; -}; - -Tap.prototype.writeFixed = function (buf, len) { - len = len || buf.length; - var pos = this.pos; - this.pos += len; - if (this.pos > this.buf.length) { - return; - } - buf.copy(this.buf, pos, 0, len); -}; - -Tap.prototype.readBytes = function () { - return this.readFixed(this.readLong()); -}; - -Tap.prototype.skipBytes = function () { - var len = this.readLong(); - this.pos += len; -}; - -Tap.prototype.writeBytes = function (buf) { - var len = buf.length; - this.writeLong(len); - this.writeFixed(buf, len); -}; - -/* istanbul ignore else */ -if (typeof Buffer.prototype.utf8Slice == 'function') { - // Use this optimized function when available. - Tap.prototype.readString = function () { - var len = this.readLong(); - var pos = this.pos; - var buf = this.buf; - this.pos += len; - if (this.pos > buf.length) { - return; - } - return this.buf.utf8Slice(pos, pos + len); - }; -} else { - Tap.prototype.readString = function () { - var len = this.readLong(); - var pos = this.pos; - var buf = this.buf; - this.pos += len; - if (this.pos > buf.length) { - return; - } - return this.buf.slice(pos, pos + len).toString(); - }; -} - -Tap.prototype.skipString = function () { - var len = this.readLong(); - this.pos += len; -}; - -Tap.prototype.writeString = function (s) { - var len = Buffer.byteLength(s); - var buf = this.buf; - this.writeLong(len); - var pos = this.pos; - this.pos += len; - if (this.pos > buf.length) { - return; - } - if (len > 64 && typeof Buffer.prototype.utf8Write == 'function') { - // This method is roughly 50% faster than the manual implementation below - // for long strings (which is itself faster than the generic `Buffer#write` - // at least in most browsers, where `utf8Write` is not available). - buf.utf8Write(s, pos, len); - } else { - var i, l, c1, c2; - for (i = 0, l = len; i < l; i++) { - c1 = s.charCodeAt(i); - if (c1 < 0x80) { - buf[pos++] = c1; - } else if (c1 < 0x800) { - buf[pos++] = (c1 >> 6) | 0xc0; - buf[pos++] = (c1 & 0x3f) | 0x80; - } else if ((c1 & 0xfc00) === 0xd800 && ((c2 = s.charCodeAt(i + 1)) & 0xfc00) === 0xdc00) { - c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff); - i++; - buf[pos++] = (c1 >> 18) | 0xf0; - buf[pos++] = ((c1 >> 12) & 0x3f) | 0x80; - buf[pos++] = ((c1 >> 6) & 0x3f) | 0x80; - buf[pos++] = (c1 & 0x3f) | 0x80; - } else { - buf[pos++] = (c1 >> 12) | 0xe0; - buf[pos++] = ((c1 >> 6) & 0x3f) | 0x80; - buf[pos++] = (c1 & 0x3f) | 0x80; - } - } - } -}; - -/* istanbul ignore else */ -if (typeof Buffer.prototype.latin1Write == 'function') { - // `binaryWrite` has been renamed to `latin1Write` in Node v6.4.0, see - // https://github.com/nodejs/node/pull/7111. Note that the `'binary'` - // encoding argument still works however. - Tap.prototype.writeBinary = function (str, len) { - var pos = this.pos; - this.pos += len; - if (this.pos > this.buf.length) { - return; - } - this.buf.latin1Write(str, pos, len); - }; -} else if (typeof Buffer.prototype.binaryWrite == 'function') { - Tap.prototype.writeBinary = function (str, len) { - var pos = this.pos; - this.pos += len; - if (this.pos > this.buf.length) { - return; - } - this.buf.binaryWrite(str, pos, len); - }; -} else { - // Slowest implementation. - Tap.prototype.writeBinary = function (s, len) { - var pos = this.pos; - this.pos += len; - if (this.pos > this.buf.length) { - return; - } - this.buf.write(s, pos, len, 'binary'); - }; -} - -// Binary comparison methods. -// -// These are not guaranteed to consume the objects they are comparing when -// returning a non-zero result (allowing for performance benefits), so no other -// operations should be done on either tap after a compare returns a non-zero -// value. Also, these methods do not have the same silent failure requirement -// as read, skip, and write since they are assumed to be called on valid -// buffers. - -Tap.prototype.matchBoolean = function (tap) { - return this.buf[this.pos++] - tap.buf[tap.pos++]; -}; - -Tap.prototype.matchInt = Tap.prototype.matchLong = function (tap) { - var n1 = this.readLong(); - var n2 = tap.readLong(); - return n1 === n2 ? 0 : n1 < n2 ? -1 : 1; -}; - -Tap.prototype.matchFloat = function (tap) { - var n1 = this.readFloat(); - var n2 = tap.readFloat(); - return n1 === n2 ? 0 : n1 < n2 ? -1 : 1; -}; - -Tap.prototype.matchDouble = function (tap) { - var n1 = this.readDouble(); - var n2 = tap.readDouble(); - return n1 === n2 ? 0 : n1 < n2 ? -1 : 1; -}; - -Tap.prototype.matchFixed = function (tap, len) { - return this.readFixed(len).compare(tap.readFixed(len)); -}; - -Tap.prototype.matchBytes = Tap.prototype.matchString = function (tap) { - var l1 = this.readLong(); - var p1 = this.pos; - this.pos += l1; - var l2 = tap.readLong(); - var p2 = tap.pos; - tap.pos += l2; - var b1 = this.buf.slice(p1, this.pos); - var b2 = tap.buf.slice(p2, tap.pos); - return b1.compare(b2); -}; - -// Functions for supporting custom long classes. -// -// The two following methods allow the long implementations to not have to -// worry about Avro's zigzag encoding, we directly expose longs as unpacked. - -Tap.prototype.unpackLongBytes = function () { - var res = newBuffer(8); - var n = 0; - var i = 0; // Byte index in target buffer. - var j = 6; // Bit offset in current target buffer byte. - var buf = this.buf; - var b, neg; - - b = buf[this.pos++]; - neg = b & 1; - res.fill(0); - - n |= (b & 0x7f) >> 1; - while (b & 0x80) { - b = buf[this.pos++]; - n |= (b & 0x7f) << j; - j += 7; - if (j >= 8) { - // Flush byte. - j -= 8; - res[i++] = n; - n >>= 8; - } - } - res[i] = n; - - if (neg) { - invert(res, 8); - } - - return res; -}; - -Tap.prototype.packLongBytes = function (buf) { - var neg = (buf[7] & 0x80) >> 7; - var res = this.buf; - var j = 1; - var k = 0; - var m = 3; - var n; - - if (neg) { - invert(buf, 8); - n = 1; - } else { - n = 0; - } - - var parts = [buf.readUIntLE(0, 3), buf.readUIntLE(3, 3), buf.readUIntLE(6, 2)]; - // Not reading more than 24 bits because we need to be able to combine the - // "carry" bits from the previous part and JavaScript only supports bitwise - // operations on 32 bit integers. - while (m && !parts[--m]) {} // Skip trailing 0s. - - // Leading parts (if any), we never bail early here since we need the - // continuation bit to be set. - while (k < m) { - n |= parts[k++] << j; - j += 24; - while (j > 7) { - res[this.pos++] = (n & 0x7f) | 0x80; - n >>= 7; - j -= 7; - } - } - - // Final part, similar to normal packing aside from the initial offset. - n |= parts[m] << j; - do { - res[this.pos] = n & 0x7f; - n >>= 7; - } while (n && (res[this.pos++] |= 0x80)); - this.pos++; - - // Restore original buffer (could make this optional?). - if (neg) { - invert(buf, 8); - } -}; - -// Helpers. - -/** - * Invert all bits in a buffer. - * - * @param buf {Buffer} Non-empty buffer to invert. - * @param len {Number} Buffer length (must be positive). - */ -function invert(buf, len) { - while (len--) { - buf[len] = ~buf[len]; - } -} - -module.exports = { - abstractFunction: abstractFunction, - addDeprecatedGetters: addDeprecatedGetters, - bufferFrom: bufferFrom, - capitalize: capitalize, - copyOwnProperties: copyOwnProperties, - getHash: getHash, - compare: compare, - getOption: getOption, - jsonEnd: jsonEnd, - newBuffer: newBuffer, - objectValues: objectValues, - toMap: toMap, - singleIndexOf: singleIndexOf, - hasDuplicates: hasDuplicates, - Lcg: Lcg, - OrderedQueue: OrderedQueue, - Tap: Tap, -}; diff --git a/forward_engineering/modules/avsc/package.json b/forward_engineering/modules/avsc/package.json deleted file mode 100644 index 08952302..00000000 --- a/forward_engineering/modules/avsc/package.json +++ /dev/null @@ -1,135 +0,0 @@ -{ - "_args": [ - [ - { - "raw": "avsc", - "scope": null, - "escapedName": "avsc", - "name": "avsc", - "rawSpec": "", - "spec": "latest", - "type": "tag" - }, - "/home/taras/Dev/Hackolade/Avro/forward_engineering" - ] - ], - "_from": "avsc@latest", - "_hasShrinkwrap": false, - "_id": "avsc@5.4.11", - "_inCache": true, - "_location": "/avsc", - "_nodeVersion": "10.15.3", - "_npmOperationalInternal": { - "host": "s3://npm-registry-packages", - "tmp": "tmp/avsc_5.4.11_1560301024505_0.02149323812597248" - }, - "_npmUser": { - "name": "mtth", - "email": "mtth@apache.org" - }, - "_npmVersion": "6.9.0", - "_phantomChildren": {}, - "_requested": { - "raw": "avsc", - "scope": null, - "escapedName": "avsc", - "name": "avsc", - "rawSpec": "", - "spec": "latest", - "type": "tag" - }, - "_requiredBy": [ - "#USER", - "/" - ], - "_resolved": "https://registry.npmjs.org/avsc/-/avsc-5.4.11.tgz", - "_shasum": "64dfd764182a3997cf7d5b9b388d9c3634527933", - "_shrinkwrap": null, - "_spec": "avsc", - "_where": "/home/taras/Dev/Hackolade/Avro/forward_engineering", - "author": { - "name": "Matthieu Monsch", - "email": "monsch@alum.mit.edu" - }, - "browser": { - "./lib": "./etc/browser/avsc.js", - "./lib/files": "./etc/browser/lib/files.js", - "crypto": "./etc/browser/lib/crypto.js" - }, - "bugs": { - "url": "https://github.com/mtth/avsc/issues" - }, - "dependencies": {}, - "description": "Avro for JavaScript", - "devDependencies": { - "benchmark": "~2.1.4", - "coveralls": "^3.0.4", - "mocha": "^5.2.0", - "nyc": "~14.1.1", - "tmp": "^0.0.33" - }, - "directories": {}, - "dist": { - "integrity": "sha512-8fE5XplaZwuKIvFHo15uEL6xStT+nuugD4orWem85WuovJeZw7G1dhY1HJVsYKT68OPbIjBjnuwFSruEUVLGrw==", - "shasum": "64dfd764182a3997cf7d5b9b388d9c3634527933", - "tarball": "https://registry.npmjs.org/avsc/-/avsc-5.4.11.tgz", - "fileCount": 17, - "unpackedSize": 258116, - "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdAE3hCRA9TVsSAnZWagAAdcsP/ihEHB8vRkpmjQ9Jyi6r\n+coKbEUjNQeGNJNqHKav1scuxGSO3X4+8KAniBk+99fCyaC007VvT5OYPrPN\nXvLQWfvvtkugVlT3WlhMKQM3hvvT3qzQMVSsXv9FWsR2/ZHj+1g2Fj0YcD2S\nCMvo95+Tj+xTmWnCRw5zxcMbH+Al2ZXL66DaW+9+kHXquei0AiFw+XtSFSXB\nEqOT3fFhjIzzM5Ur8Np7sWQkxq8MeeGjiBvhArP5E7YEf2XsFp7ONcX80bui\nc2DFzvBUHBvYII+VpfxZZfhlTpvMC8cNEKt7ltEVbQktJ4Xnrr6PPYYzRPAf\nCu/n7mm4RudDPTSnfIynDaBbMmTBUsPluJQJLVqvGLG1Zghgzo1HFw/f1I0C\n9pUZj+53QmEDZrsHJkTmrMtE3bSBlInNYOWTR+6kxvg5BHWIfIwj2NwOpz8t\nzAFkAzwSmnqWVvp9xDvzvqh2lGLxNvo/hwgjFGNVUGQKJ8X63fCvWDrF95vf\nhv8B6ND0MBATX02vKhps43KlwIxDxk+y9thO4uDOy3442HM5jgmeyCX5VIBp\npBrqLp0zkK9diQQ/fh4dqjdVnET0sW1kU7k6cBB5yd1H01uYGgcakDFXuirB\nV2NY1uxBWoDB9kSHRIA5SXbCtj+Ipx/XwwCqaFqAA987oB3ZAYsk3SB+xjJm\n1492\r\n=uWSS\r\n-----END PGP SIGNATURE-----\r\n" - }, - "engines": { - "node": ">=0.11" - }, - "gitHead": "3bd72eb6e68117c609217078389871ded2853fcb", - "homepage": "https://github.com/mtth/avsc", - "keywords": [ - "api", - "avdl", - "avpr", - "avro", - "avsc", - "binary", - "buffer", - "data", - "decoding", - "encoding", - "idl", - "interface", - "ipc", - "json", - "marshalling", - "message", - "protocol", - "rpc", - "schema", - "serde", - "serialization", - "type" - ], - "license": "MIT", - "main": "./lib", - "maintainers": [ - { - "name": "mtth", - "email": "monsch@alum.mit.edu" - } - ], - "name": "avsc", - "optionalDependencies": {}, - "readme": "ERROR: No README data found!", - "repository": { - "type": "git", - "url": "git://github.com/mtth/avsc.git" - }, - "scripts": { - "clean": "rm -rf coverage dist node_modules", - "cover": "nyc mocha", - "coverAndPublish": "nyc npm test && nyc report --reporter=text-lcov | coveralls", - "dist": "./etc/scripts/dist", - "perf": "./etc/scripts/perf etc/schemas/*", - "test": "mocha", - "zuul": "zuul --no-coverage -- test/*.js" - }, - "types": "./types", - "version": "5.4.11" -} diff --git a/forward_engineering/modules/avsc/types/index.d.ts b/forward_engineering/modules/avsc/types/index.d.ts deleted file mode 100644 index 55f2bbf7..00000000 --- a/forward_engineering/modules/avsc/types/index.d.ts +++ /dev/null @@ -1,405 +0,0 @@ -// Note: These typings are incomplete (https://github.com/mtth/avsc/pull/134). -// In particular, they do not contain entries for functions available in the -// browser (functions/methods in etc/browser). - -// TODO: Wherever the type is just `any`, it was probably generated -// automatically. Either finish documenting the type signature or document why -// `any` is appropriate. - -import * as stream from 'stream'; -import { EventEmitter } from 'events'; - -//"virtual" namespace (no JS, just types) for Avro Schema -declare namespace schema { - export type AvroSchema = DefinedType | DefinedType[]; - type DefinedType = PrimitiveType | ComplexType | LogicalType | string; - type PrimitiveType = 'null' | 'boolean' | 'int' | 'long' | 'float' | 'double' | 'bytes' | 'string'; - type ComplexType = NamedType | RecordType | EnumType | MapType | ArrayType | FixedType; - type LogicalType = ComplexType & LogicalTypeExtension; - - interface NamedType { - type: PrimitiveType - } - - interface RecordType { - type: "record"; - name: string; - namespace?: string; - doc?: string; - aliases?: string[]; - fields: { - name: string; - doc?: string; - type: Schema; - default?: any; - }[]; - order?: "ascending" | "descending" | "ignore"; - } - - interface EnumType { - type: "enum"; - name: string; - namespace?: string; - aliases?: string[]; - doc?: string; - symbols: string[]; - } - - interface ArrayType { - type: "array"; - items: Schema; - } - - interface MapType { - type: "map"; - values: Schema; - } - - interface FixedType { - type: "fixed"; - name: string; - aliases?: string[]; - size: number; - } - interface LogicalTypeExtension { - logicalType: string; - [param: string]: any; - } -} - -//Types of Options/arguments - -type Schema = Type | schema.AvroSchema; - -type Callback = (err: Err, value: V) => void; - - -type Codec = (buffer: Buffer, callback: Callback) => void; - -interface CodecOptions { - [name: string]: Codec; -} - -interface DecoderOptions { - noDecode: boolean; - readerSchema: string | object | Type; - codecs: CodecOptions; - parseHook: (schema: Schema) => Type -} - -interface EncoderOptions { - blockSize: number; - codec: string; - codecs: CodecOptions; - writeHeader: boolean | 'always' | 'never' | 'auto'; - syncMarker: Buffer; -} - -interface ForSchemaOptions { - assertLogicalTypes: boolean; - logicalTypes: { [type: string]: new (schema: Schema, opts?: any) => types.LogicalType; }; - namespace: string; - noAnonymousTypes: boolean; - registry: { [name: string]: Type }; - typeHook: (schema: Schema, opts: ForSchemaOptions) => Type; - wrapUnions: boolean | 'auto' | 'always' | 'never'; -} - -interface TypeOptions extends ForSchemaOptions { - strictDefaults: boolean; -} - -interface ForValueOptions extends TypeOptions { - emptyArrayType: Type; - valueHook: (val: any, opts: ForValueOptions) => Type; -} - -interface CloneOptions { - coerceBuffers: boolean; - fieldHook: (field: types.Field, value: any, type: Type) => any; - qualifyNames: boolean; - skipMissingFields: boolean; - wrapUnions: boolean; -} -interface IsValidOptions { - noUndeclaredFields: boolean; - errorHook: (path: string[], val: any, type: Type) => void -} -interface AssembleOptions { - importHook: (filePath: string, type: 'idl', callback: Callback) => void; -} - -interface SchemaOptions { - exportAttrs: boolean; - noDeref: boolean; -} - -declare class Resolver { - //no public methods -} - -//exported functions - -export function assembleProtocol(filePath: string, opts: Partial, callback: Callback): void; -export function assembleProtocol(filePath: string, callback: Callback): void; -export function createFileDecoder(fileName: string, opts?: Partial): streams.BlockDecoder; -export function createFileEncoder(filePath: string, schema: Schema, opts?: Partial): streams.BlockEncoder; -export function createBlobEncoder(schema: Schema, opts?: Partial): stream.Duplex; -export function createBlobDecoder(blob: Blob, opts?: Partial): streams.BlockDecoder; -export function discoverProtocol(transport: Service.Transport, options: any, callback: Callback): void; -export function discoverProtocol(transport: Service.Transport, callback: Callback): void; -export function extractFileHeader(filePath: string, options?: any): void; -export function parse(schemaOrProtocolIdl: string, options?: any): any; // TODO protocol literal or Type -export function readProtocol(protocolIdl: string, options?: Partial): any; -export function readSchema(schemaIdl: string, options?: Partial): Schema; - - -// TODO more specific types than `any` -export class Type { - clone(val: any, opts?: Partial): any; - compare(val1: any, val2: any): number; - compareBuffers(buf1: Buffer, buf2: Buffer): number; - createResolver(type: Type): Resolver; - decode(buf: Buffer, pos?: number, resolver?: Resolver): { value: any, offset: number}; - encode(val: any, buf: Buffer, pos?: number): number; - equals(type: Type): boolean; - fingerprint(algorithm?: string): Buffer; - fromBuffer(buffer: Buffer, resolver?: Resolver, noCheck?: boolean): any; - fromString(str: string): any; - inspect(): string; - isValid(val: any, opts?: Partial): boolean; - random(): Type; - schema(opts?: Partial): Schema; - toBuffer(value: any): Buffer; - toJSON(): object; - toString(val?: any): string; - wrap(val: any): any; - readonly aliases: string[] | undefined; - readonly doc: string | undefined; - readonly name: string | undefined; - readonly branchName: string | undefined; - readonly typeName: string; - static forSchema(schema: Schema, opts?: Partial): Type; - static forTypes(types: Type[], opts?: Partial): Type; - static forValue(value: object, opts?: Partial): Type; - static isType(arg: any, ...prefix: string[]): boolean; -} - -export class Service { - constructor(name: any, messages: any, types: any, ptcl: any, server: any); - createClient(options?: Partial): Service.Client; - createServer(options?: Partial): Service.Server; - equals(args: any): boolean; // deprecated - inspect(): string; - message(name: string): any; - type(name: string): Type | undefined; - - readonly doc: string | undefined; - readonly hash: Buffer; - readonly messages: any[]; - readonly name: string; - readonly protocol: any; - readonly types: Type[]; - - static compatible(client: Service.Client, server: Service.Server): boolean; - static forProtocol(protocol: any, options?: any): Service; - static isService(obj: any): boolean; -} - -export namespace Service { - interface ClientChannel extends EventEmitter { - readonly client: Client; - readonly destroyed: boolean; - readonly draining: boolean; - readonly pending: number; - readonly timeout: number; - ping(timeout?: number, cb?: any): void; - destroy(noWait?: boolean): void; - } - - interface ServerChannel extends EventEmitter { - readonly destroyed: boolean; - readonly draining: boolean; - readonly pending: number; - readonly server: Server; - destroy(noWait?: boolean): void; - } - - interface ClientOptions { - buffering: boolean; - channelPolicy: any; - strictTypes: boolean; - timeout: number; - remoteProtocols: boolean; - transport?: Transport; - } - - interface ServerOptions { - objectMode: boolean; - } - - type TransportFunction = () => void; // TODO - - type Transport = stream.Duplex | TransportFunction; - - interface ChannelCreateOptions { - objectMode: boolean; - } - - interface ChannelDestroyOptions { - noWait: boolean; - } - - class Server extends EventEmitter { - constructor(svc: any, opts: any); - - readonly service: Service; - // on() - - activeChannels(): ServerChannel[]; - createChannel(transport: Transport, options?: Partial): ServerChannel; - onMessage(name: string, handler: (arg1: any, callback: Callback) => void): this; - remoteProtocols(): any[]; - use(...args: any[]): this; - } - - class Client extends EventEmitter { - constructor(svc: any, opts: any); - activeChannels(): ClientChannel[]; - createChannel(transport: Transport, options?: Partial): ClientChannel; - destroyChannels(options?: Partial): void; - emitMessage(name: string, req: any, options?: any, callback?: Callback): void // TODO - remoteProtocols(): any[]; - use(...args: any[]): this; - } -} - -export namespace streams { - - class BlockDecoder extends stream.Duplex { - constructor(opts?: Partial); - static defaultCodecs(): CodecOptions; - - //should add meta-data listener, but regrettably that requires all other events to be repeated - //here, or else they won't show up in code-completion. To avoid clutter, the meta-data event - //is therefore omitted from this stream. - } - - class BlockEncoder extends stream.Duplex { - constructor(schema: Schema, opts?: Partial); - static defaultCodecs(): CodecOptions; - } - - class RawDecoder extends stream.Duplex { - constructor(schema: Schema, opts?: { decode?: boolean }); - } - - class RawEncoder extends stream.Duplex { - constructor(schema: Schema, opts?: { batchSize?: number }); - } -} - -export namespace types { - class ArrayType extends Type { - constructor(schema: Schema, opts: any); - readonly itemsType: Type; - random(): ArrayType; - } - - class BooleanType extends Type { // TODO: Document this on the wiki - constructor(); - random(): BooleanType; - } - - class BytesType extends Type { // TODO: Document this on the wiki - constructor(); - random(): BytesType; - } - - class DoubleType extends Type { // TODO: Document this on the wiki - constructor(); - random(): DoubleType; - } - - class EnumType extends Type { - constructor(schema: Schema, opts?: any); - readonly symbols: string[]; - random(): EnumType; - } - - class FixedType extends Type { - constructor(schema: Schema, opts?: any); - readonly size: number; - random(): FixedType; - } - - class FloatType extends Type { - constructor(); - random(): FloatType; - } - - class IntType extends Type { - constructor(); - random(): IntType; - } - - class LogicalType extends Type { - constructor(schema: Schema, opts?: any); - readonly underlyingType: Type; - protected _export(schema: Schema): void; - protected _fromValue(val: any): any; - protected _resolve(type: Type): any; - protected _toValue(any: any): any; - random(): LogicalType; - } - - class LongType extends Type { - constructor(); - random(): LongType; - static __with(methods: object, noUnpack?: boolean): void; - } - - class MapType extends Type { - constructor(schema: Schema, opts?: any); - readonly valuesType: any; - random(): MapType; - } - - class NullType extends Type { // TODO: Document this on the wiki - constructor(); - random(): NullType; - } - - class RecordType extends Type { - constructor(schema: Schema, opts?: any); - readonly fields: Field[]; - readonly recordConstructor: any; // TODO: typeof Record once Record interface/class exists - field(name: string): Field; - random(): RecordType; - } - - class Field { - aliases: string[]; - defaultValue(): any; - name: string; - order: string; - type: Type; - } - - class StringType extends Type { // TODO: Document this on the wiki - constructor(); - random(): StringType; - } - - class UnwrappedUnionType extends Type { - constructor(schema: Schema, opts: any); - random(): UnwrappedUnionType; - readonly types: Type[]; - } - - class WrappedUnionType extends Type { - constructor(schema: Schema, opts: any); - random(): WrappedUnionType; - readonly types: Type[]; - } -} diff --git a/forward_engineering/validationHelper.js b/forward_engineering/validationHelper.js index ad9026a0..7d152396 100644 --- a/forward_engineering/validationHelper.js +++ b/forward_engineering/validationHelper.js @@ -1,4 +1,4 @@ -const avsc = require('./modules/avsc'); +const avsc = require('avsc'); const toMessage = entityName => err => ({ type: 'error', diff --git a/package-lock.json b/package-lock.json index 4dc1bffd..d6264290 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,33 +1,33 @@ { "name": "Avro", - "version": "0.2.19", + "version": "0.2.20", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "Avro", - "version": "0.2.19", + "version": "0.2.20", "hasInstallScript": true, "dependencies": { - "avsc": "5.4.6", - "esbuild-plugin-copy": "2.1.1", + "avsc": "5.7.9", "lodash": "4.17.21", - "snappyjs": "0.6.0" + "snappyjs": "0.7.0" }, "devDependencies": { "@hackolade/hck-esbuild-plugins-pack": "0.0.1", "@typescript-eslint/eslint-plugin": "7.11.0", "@typescript-eslint/parser": "7.11.0", - "esbuild": "0.25.0", + "esbuild": "0.25.9", "esbuild-plugin-clean": "1.0.1", + "esbuild-plugin-copy": "2.1.1", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-formatter-teamcity": "^1.0.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-prettier": "5.1.3", "eslint-plugin-unused-imports": "3.2.0", - "lint-staged": "16.1.5", - "prettier": "3.2.5", + "lint-staged": "16.1.6", + "prettier": "3.6.2", "simple-git-hooks": "2.11.1" }, "engines": { @@ -36,12 +36,13 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", - "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -52,12 +53,13 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", - "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -68,12 +70,13 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", - "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -84,12 +87,13 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", - "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -100,12 +104,13 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", - "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -116,12 +121,13 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", - "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -132,12 +138,13 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", - "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -148,12 +155,13 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", - "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -164,12 +172,13 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", - "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -180,12 +189,13 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", - "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -196,12 +206,13 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", - "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -212,12 +223,13 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", - "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -228,12 +240,13 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", - "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -244,12 +257,13 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", - "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -260,12 +274,13 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", - "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -276,12 +291,13 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", - "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -292,12 +308,13 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", - "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -308,12 +325,13 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", - "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -324,12 +342,13 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", - "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -340,12 +359,13 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", - "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -356,12 +376,13 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", - "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -371,13 +392,31 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", - "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -388,12 +427,13 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", - "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -404,12 +444,13 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", - "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -420,12 +461,13 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", - "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -601,6 +643,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -614,6 +657,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -623,6 +667,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -943,6 +988,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -958,6 +1004,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -1018,6 +1065,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1132,9 +1180,9 @@ } }, "node_modules/avsc": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/avsc/-/avsc-5.4.6.tgz", - "integrity": "sha512-8bbABwEjGB2GoFI7INkMxPB9bro+Zh4O4NLtnOP+NIsElMY7ab91IrMIxlks3PUw0WfWWDknFXC/9GBlWLgD+A==", + "version": "5.7.9", + "resolved": "https://registry.npmjs.org/avsc/-/avsc-5.7.9.tgz", + "integrity": "sha512-yOA4wFeI7ET3v32Di/sUybQ+ttP20JHSW3mxLuNGeO0uD6PPcvLrIQXSvy/rhJOWU5JrYh7U4OHplWMmtAtjMg==", "license": "MIT", "engines": { "node": ">=0.11" @@ -1151,6 +1199,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1173,6 +1222,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -1245,6 +1295,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -1261,6 +1312,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -1328,6 +1380,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -1340,6 +1393,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/colorette": { @@ -1523,6 +1577,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, "license": "MIT", "dependencies": { "path-type": "^4.0.0" @@ -1729,9 +1784,10 @@ } }, "node_modules/esbuild": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", - "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -1741,31 +1797,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.0", - "@esbuild/android-arm": "0.25.0", - "@esbuild/android-arm64": "0.25.0", - "@esbuild/android-x64": "0.25.0", - "@esbuild/darwin-arm64": "0.25.0", - "@esbuild/darwin-x64": "0.25.0", - "@esbuild/freebsd-arm64": "0.25.0", - "@esbuild/freebsd-x64": "0.25.0", - "@esbuild/linux-arm": "0.25.0", - "@esbuild/linux-arm64": "0.25.0", - "@esbuild/linux-ia32": "0.25.0", - "@esbuild/linux-loong64": "0.25.0", - "@esbuild/linux-mips64el": "0.25.0", - "@esbuild/linux-ppc64": "0.25.0", - "@esbuild/linux-riscv64": "0.25.0", - "@esbuild/linux-s390x": "0.25.0", - "@esbuild/linux-x64": "0.25.0", - "@esbuild/netbsd-arm64": "0.25.0", - "@esbuild/netbsd-x64": "0.25.0", - "@esbuild/openbsd-arm64": "0.25.0", - "@esbuild/openbsd-x64": "0.25.0", - "@esbuild/sunos-x64": "0.25.0", - "@esbuild/win32-arm64": "0.25.0", - "@esbuild/win32-ia32": "0.25.0", - "@esbuild/win32-x64": "0.25.0" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" } }, "node_modules/esbuild-plugin-clean": { @@ -1786,6 +1843,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/esbuild-plugin-copy/-/esbuild-plugin-copy-2.1.1.tgz", "integrity": "sha512-Bk66jpevTcV8KMFzZI1P7MZKZ+uDcrZm2G2egZ2jNIvVnivDpodZI+/KnpL3Jnap0PBdIHU7HwFGB8r+vV5CVw==", + "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.2", @@ -1801,6 +1859,7 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -2279,6 +2338,7 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -2309,6 +2369,7 @@ "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -2331,6 +2392,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -2420,6 +2482,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -2472,9 +2535,9 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.1.tgz", + "integrity": "sha512-R1QfovbPsKmosqTnPoRFiJ7CF9MLRgb53ChvMZm+r4p76/+8yKDy17qLL2PKInORy2RkZZekuK0efYgmzTkXyQ==", "dev": true, "license": "MIT", "engines": { @@ -2567,6 +2630,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -2636,6 +2700,7 @@ "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, "license": "MIT", "dependencies": { "array-union": "^2.1.0", @@ -2669,6 +2734,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -2695,6 +2761,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2775,6 +2842,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -2909,6 +2977,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -3002,6 +3071,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3059,6 +3129,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -3097,6 +3168,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -3349,6 +3421,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -3395,17 +3468,17 @@ } }, "node_modules/lint-staged": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.1.5.tgz", - "integrity": "sha512-uAeQQwByI6dfV7wpt/gVqg+jAPaSp8WwOA8kKC/dv1qw14oGpnpAisY65ibGHUGDUv0rYaZ8CAJZ/1U8hUvC2A==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.1.6.tgz", + "integrity": "sha512-U4kuulU3CKIytlkLlaHcGgKscNfJPNTiDF2avIUGFCv7K95/DCYQ7Ra62ydeRWmgQGg9zJYw2dzdbztwJlqrow==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^5.5.0", + "chalk": "^5.6.0", "commander": "^14.0.0", "debug": "^4.4.1", "lilconfig": "^3.1.3", - "listr2": "^9.0.1", + "listr2": "^9.0.3", "micromatch": "^4.0.8", "nano-spawn": "^1.0.2", "pidtree": "^0.6.0", @@ -3436,9 +3509,9 @@ } }, "node_modules/listr2": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.2.tgz", - "integrity": "sha512-VVd7cS6W+vLJu2wmq4QmfVj14Iep7cz4r/OWNk36Aq5ZOY7G8/BfCrQFexcwB1OIxB3yERiePfE/REBjEFulag==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.3.tgz", + "integrity": "sha512-0aeh5HHHgmq1KRdMMDHfhMWQmIT/m7nRDTlxlFqni2Sp0had9baqsjJRvDGdlvgd6NmPE0nPloOipiQJGFtTHQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3529,13 +3602,13 @@ } }, "node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", - "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", "dev": true, "license": "MIT", "dependencies": { - "get-east-asian-width": "^1.0.0" + "get-east-asian-width": "^1.3.1" }, "engines": { "node": ">=18" @@ -3591,6 +3664,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -3600,6 +3674,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -3679,6 +3754,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3945,6 +4021,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3954,6 +4031,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -3996,9 +4074,9 @@ } }, "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", "bin": { @@ -4038,6 +4116,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -4058,6 +4137,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -4162,6 +4242,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -4196,6 +4277,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, "funding": [ { "type": "github", @@ -4459,6 +4541,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4495,9 +4578,9 @@ } }, "node_modules/snappyjs": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/snappyjs/-/snappyjs-0.6.0.tgz", - "integrity": "sha512-VTBBV4C9SHLuNEw8qrxw3EnkYmzwe8ml5VA8zS6XChr/yQ/CEDobofC6j4S25QJQR0htRGD68Oh4TZxv/mpf7w==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/snappyjs/-/snappyjs-0.7.0.tgz", + "integrity": "sha512-u5iEEXkMe2EInQio6Wv9LWHOQYRDbD2O9hzS27GpT/lwfIQhTCnHCTqedqHIHe9ZcvQo+9au6vngQayipz1NYw==", "license": "MIT" }, "node_modules/stop-iteration-iterator": { @@ -4670,6 +4753,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -4719,6 +4803,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -4902,6 +4987,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 10.0.0" diff --git a/package.json b/package.json index 87c49237..933dfbe7 100644 --- a/package.json +++ b/package.json @@ -118,10 +118,9 @@ "description": "Hackolade plugin for Apache Avro Schema", "disabled": false, "dependencies": { - "avsc": "5.4.6", - "esbuild-plugin-copy": "2.1.1", + "avsc": "5.7.9", "lodash": "4.17.21", - "snappyjs": "0.6.0" + "snappyjs": "0.7.0" }, "lint-staged": { "*.{js,json}": "prettier --write" @@ -133,22 +132,24 @@ "scripts": { "lint": "eslint . --max-warnings=0", "package": "node esbuild.package.js", + "install": "npx patch-package", "postinstall": "npx simple-git-hooks" }, "devDependencies": { "@hackolade/hck-esbuild-plugins-pack": "0.0.1", "@typescript-eslint/eslint-plugin": "7.11.0", "@typescript-eslint/parser": "7.11.0", - "esbuild": "0.25.0", + "esbuild": "0.25.9", "esbuild-plugin-clean": "1.0.1", + "esbuild-plugin-copy": "2.1.1", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-formatter-teamcity": "^1.0.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-prettier": "5.1.3", "eslint-plugin-unused-imports": "3.2.0", - "lint-staged": "16.1.5", - "prettier": "3.2.5", + "lint-staged": "16.1.6", + "prettier": "3.6.2", "simple-git-hooks": "2.11.1" } -} \ No newline at end of file +} diff --git a/patches/avsc+5.7.9.patch b/patches/avsc+5.7.9.patch new file mode 100644 index 00000000..4b5d6bc7 --- /dev/null +++ b/patches/avsc+5.7.9.patch @@ -0,0 +1,496 @@ +diff --git a/node_modules/avsc/lib/index.js b/node_modules/avsc/lib/index.js +index 003f7ce..4d509b3 100644 +--- a/node_modules/avsc/lib/index.js ++++ b/node_modules/avsc/lib/index.js +@@ -29,6 +29,20 @@ function parse(any, opts) { + types.Type.forSchema(schemaOrProtocol, opts); + } + ++// Align with studio patch of the avsc library ++function validateFileHeader(data) { ++ try { ++ const tap = new utils.Tap(data); ++ const header = containers.HEADER_TYPE._read(tap); ++ const schema = JSON.parse(header.meta['avro.schema'].toString()); ++ console.log("Validating Avro file header", schema && tap.isValid()); ++ return schema && tap.isValid(); ++ } catch(e) { ++ console.error("Error validating Avro file header", e); ++ return false; ++ } ++} ++ + /** Extract a container file's header synchronously. */ + function extractFileHeader(path, opts) { + opts = opts || {}; +@@ -96,6 +110,7 @@ module.exports = { + createFileEncoder: createFileEncoder, + discoverProtocol: services.discoverProtocol, + extractFileHeader: extractFileHeader, ++ validateFileHeader, + parse: parse, + readProtocol: specs.readProtocol, + readSchema: specs.readSchema, +@@ -114,5 +129,6 @@ module.exports = { + infer: util.deprecate( + types.Type.forValue, + 'use `Type.forValue` instead' +- ) ++ ), ++ errorsCollector: types.errorsCollector + }; +diff --git a/node_modules/avsc/lib/types.js b/node_modules/avsc/lib/types.js +index dc7c69c..e399f82 100644 +--- a/node_modules/avsc/lib/types.js ++++ b/node_modules/avsc/lib/types.js +@@ -97,17 +97,17 @@ function Type(schema, opts) { + name = maybeQualify(name, namespace); + if (isPrimitive(name)) { + // Avro doesn't allow redefining primitive names. +- throw new Error(f('cannot rename primitive type: %j', name)); ++ Type.addError(new Error(f('cannot rename primitive type: %j', name))); + } + var registry = opts && opts.registry; + if (registry) { + if (registry[name] !== undefined) { +- throw new Error(f('duplicate type name: %s', name)); ++ Type.addError(new Error(f('duplicate type name: %s', name))); + } + registry[name] = type; + } + } else if (opts && opts.noAnonymousTypes) { +- throw new Error(f('missing name property in schema: %j', schema)); ++ Type.addError(new Error(f('missing name property in schema: %j', schema))); + } + this.name = name; + this.aliases = schema.aliases ? +@@ -138,13 +138,13 @@ Type.forSchema = function (schema, opts) { + case 'auto': + return undefined; // Determined dynamically later on. + default: +- throw new Error(f('invalid wrap unions option: %j', wrapUnions)); ++ Type.addError(new Error(f('invalid wrap unions option: %j', wrapUnions))); + } + })(opts.wrapUnions); + + if (schema === null) { + // Let's be helpful for this common error. +- throw new Error('invalid type: null (did you mean "null"?)'); ++ Type.addError(new Error('invalid type: null (did you mean "null"?)')); + } + + if (Type.isType(schema)) { +@@ -154,7 +154,7 @@ Type.forSchema = function (schema, opts) { + var type; + if (opts.typeHook && (type = opts.typeHook(schema, opts))) { + if (!Type.isType(type)) { +- throw new Error(f('invalid typehook return value: %j', type)); ++ Type.addError(new Error(f('invalid typehook return value: %j', type))); + } + return type; + } +@@ -170,9 +170,9 @@ Type.forSchema = function (schema, opts) { + // Reference to a primitive type. These are also defined names by default + // so we create the appropriate type and it to the registry for future + // reference. +- return opts.registry[schema] = Type.forSchema({type: schema}, opts); +- } +- throw new Error(f('undefined type name: %s', schema)); ++ return (opts.registry[schema] = Type.forSchema({type: schema}, opts)); ++ }// Type.addError(new Error(f('undefined type name: %s', schema))); - return schema directly ++ return schema; + } + + if (schema.logicalType && opts.logicalTypes && !LOGICAL_TYPE) { +@@ -219,7 +219,7 @@ Type.forSchema = function (schema, opts) { + type = (function (typeName) { + var Type = TYPES[typeName]; + if (Type === undefined) { +- throw new Error(f('unknown type: %j', typeName)); ++ Type.addError(new Error(f('unknown type: %j', typeName))); + } + return new Type(schema, opts); + })(schema.type); +@@ -240,7 +240,7 @@ Type.forValue = function (val, opts) { + var type = opts.valueHook(val, opts); + if (type !== undefined) { + if (!Type.isType(type)) { +- throw new Error(f('invalid value hook return value: %j', type)); ++ Type.addError( new Error(f('invalid value hook return value: %j', type))); + } + return type; + } +@@ -293,13 +293,13 @@ Type.forValue = function (val, opts) { + }) + }, opts); + default: +- throw new Error(f('cannot infer type from: %j', val)); ++ Type.addError(new Error(f('cannot infer type from: %j', val))); + } + }; + + Type.forTypes = function (types, opts) { + if (!types.length) { +- throw new Error('no types to combine'); ++ Type.addError(new Error('no types to combine')); + } + if (types.length === 1) { + return types[0]; // Nothing to do. +@@ -333,7 +333,7 @@ Type.forTypes = function (types, opts) { + // It is only valid to combine wrapped unions when no other type is + // present other than wrapped unions and nulls (otherwise the values of + // others wouldn't be valid in the resulting union). +- throw new Error('cannot combine wrapped union'); ++ Type.addError(new Error('cannot combine wrapped union')); + } + var branchTypes = {}; + expanded.forEach(function (type) { +@@ -342,7 +342,7 @@ Type.forTypes = function (types, opts) { + if (!branchType) { + branchTypes[name] = type; + } else if (!type.equals(branchType)) { +- throw new Error('inconsistent branch type'); ++ Type.addError(new Error('inconsistent branch type')); + } + }); + var wrapUnions = opts.wrapUnions; +@@ -496,7 +496,7 @@ Type.prototype.createResolver = function (type, opts) { + if (!Type.isType(type)) { + // More explicit error message than the "incompatible type" thrown + // otherwise (especially because of the overridden `toJSON` method). +- throw new Error(f('not a type: %j', type)); ++ Type.addError(new Error(f('not a type: %j', type))); + } + + if (!Type.isType(this, 'union', 'logical') && Type.isType(type, 'logical')) { +@@ -599,10 +599,10 @@ Type.prototype.fromBuffer = function (buf, resolver, noCheck) { + var tap = new Tap(buf); + var val = readValue(this, tap, resolver, noCheck); + if (!tap.isValid()) { +- throw new Error('truncated buffer'); ++ Type.addError(new Error('truncated buffer')); + } + if (!noCheck && tap.pos < buf.length) { +- throw new Error('trailing data'); ++ Type.addError(new Error('trailing data')); + } + return val; + }; +@@ -914,7 +914,7 @@ LongType.prototype._check = function (val, flags, hook) { + LongType.prototype._read = function (tap) { + var n = tap.readLong(); + if (!isSafeLong(n)) { +- throw new Error('potential precision loss'); ++ Type.addError(new Error('potential precision loss')); + } + return n; + }; +@@ -963,7 +963,7 @@ LongType.__with = function (methods, noUnpack) { + var type = new AbstractLongType(noUnpack); + Object.keys(mapping).forEach(function (name) { + if (methods[name] === undefined) { +- throw new Error(f('missing method implementation: %s', name)); ++ Type.addError(new Error(f('missing method implementation: %s', name))); + } + type[mapping[name]] = methods[name]; + }); +@@ -1147,14 +1147,14 @@ BytesType.prototype._copy = function (obj, opts) { + return obj.toString('binary'); + case 2: // Coerce strings to buffers. + if (typeof obj != 'string') { +- throw new Error(f('cannot coerce to buffer: %j', obj)); ++ Type.addError(new Error(f('cannot coerce to buffer: %j', obj))); + } + buf = utils.bufferFrom(obj, 'binary'); + this._check(buf, undefined, throwInvalidError); + return buf; + case 1: // Coerce buffer JSON representation to buffers. + if (!isJsonBuffer(obj)) { +- throw new Error(f('cannot coerce to buffer: %j', obj)); ++ Type.addError(new Error(f('cannot coerce to buffer: %j', obj))); + } + buf = utils.bufferFrom(obj.data); + this._check(buf, undefined, throwInvalidError); +@@ -1178,10 +1178,10 @@ function UnionType(schema, opts) { + Type.call(this); + + if (!Array.isArray(schema)) { +- throw new Error(f('non-array union schema: %j', schema)); ++ Type.addError(new Error(f('non-array union schema: %j', schema))); + } + if (!schema.length) { +- throw new Error('empty union'); ++ Type.addError(new Error('empty union')); + } + this.types = Object.freeze(schema.map(function (obj) { + return Type.forSchema(obj, opts); +@@ -1190,11 +1190,11 @@ function UnionType(schema, opts) { + this._branchIndices = {}; + this.types.forEach(function (type, i) { + if (Type.isType(type, 'union')) { +- throw new Error('unions cannot be directly nested'); ++ Type.addError(new Error('unions cannot be directly nested')); + } + var branch = type.branchName; +- if (this._branchIndices[branch] !== undefined) { +- throw new Error(f('duplicate union branch name: %j', branch)); ++ if (branch && this._branchIndices[branch] !== undefined) { ++ Type.addError(new Error(f('duplicate union branch name: %j', branch))); + } + this._branchIndices[branch] = i; + }, this); +@@ -1202,7 +1202,7 @@ function UnionType(schema, opts) { + util.inherits(UnionType, Type); + + UnionType.prototype._branchConstructor = function () { +- throw new Error('unions cannot be directly wrapped'); ++ Type.addError(new Error('unions cannot be directly wrapped')); + }; + + UnionType.prototype._skip = function (tap) { +@@ -1258,7 +1258,7 @@ function UnwrappedUnionType(schema, opts) { + } else { + var bucket = getTypeBucket(type); + if (this._bucketIndices[bucket] !== undefined) { +- throw new Error(f('ambiguous unwrapped union: %j', this)); ++ Type.addError(new Error(f('ambiguous unwrapped union: %j', this))); + } + this._bucketIndices[bucket] = index; + } +@@ -1289,7 +1289,7 @@ UnwrappedUnionType.prototype._getBranchIndex = function (any, index) { + // More than one branch matches the value so we aren't guaranteed to + // infer the correct type. We throw rather than corrupt data. This can + // be fixed by "tightening" the logical types. +- throw new Error('ambiguous conversion'); ++ Type.addError(new Error('ambiguous conversion')); + } + } + } +@@ -1314,7 +1314,7 @@ UnwrappedUnionType.prototype._read = function (tap) { + if (branchType) { + return branchType._read(tap); + } else { +- throw new Error(f('invalid union index: %s', index)); ++ Type.addError(new Error(f('invalid union index: %s', index))); + } + }; + +@@ -1379,7 +1379,7 @@ UnwrappedUnionType.prototype._copy = function (val, opts) { + index = this._getIndex(val); + } + if (index === undefined) { +- throwInvalidError(val, this); ++ throwInvalidError(val, this); return; + } + } + var type = this.types[index]; +@@ -1479,7 +1479,7 @@ WrappedUnionType.prototype._check = function (val, flags, hook, path) { + WrappedUnionType.prototype._read = function (tap) { + var type = this.types[tap.readLong()]; + if (!type) { +- throw new Error(f('invalid union index')); ++ Type.addError(new Error(f('invalid union index'))); + } + var Branch = type._branchConstructor; + if (Branch === null) { +@@ -1629,22 +1629,22 @@ WrappedUnionType.prototype.random = function () { + function EnumType(schema, opts) { + Type.call(this, schema, opts); + if (!Array.isArray(schema.symbols) || !schema.symbols.length) { +- throw new Error(f('invalid enum symbols: %j', schema.symbols)); ++ Type.addError(new Error(f('invalid enum symbols: %j', schema.symbols))); + } + this.symbols = Object.freeze(schema.symbols.slice()); + this._indices = {}; + this.symbols.forEach(function (symbol, i) { + if (!utils.isValidName(symbol)) { +- throw new Error(f('invalid %s symbol: %j', this, symbol)); ++ Type.addError(new Error(f('invalid %s symbol: %j', this, symbol))); + } + if (this._indices[symbol] !== undefined) { +- throw new Error(f('duplicate %s symbol: %j', this, symbol)); ++ Type.addError(new Error(f('duplicate %s symbol: %j', this, symbol))); + } + this._indices[symbol] = i; + }, this); + this.default = schema.default; + if (this.default !== undefined && this._indices[this.default] === undefined) { +- throw new Error(f('invalid %s default: %j', this, this.default)); ++ Type.addError(new Error(f('invalid %s default: %j', this, this.default))); + } + this._branchConstructor = this._createBranchConstructor(); + Object.freeze(this); +@@ -1663,7 +1663,7 @@ EnumType.prototype._read = function (tap) { + var index = tap.readLong(); + var symbol = this.symbols[index]; + if (symbol === undefined) { +- throw new Error(f('invalid %s enum index: %s', this.name, index)); ++ Type.addError(new Error(f('invalid %s enum index: %s', this.name, index))); + } + return symbol; + }; +@@ -1723,8 +1723,8 @@ EnumType.prototype.random = function () { + /** Avro fixed type. Represented simply as a `Buffer`. */ + function FixedType(schema, opts) { + Type.call(this, schema, opts); +- if (schema.size !== (schema.size | 0) || schema.size < 0) { +- throw new Error(f('invalid %s size', this.branchName)); ++ if (schema.size !== (schema.size | 0) || schema.size < 1) { ++ Type.addError(new Error(f('invalid %s size', this.branchName))); + } + this.size = schema.size | 0; + this._branchConstructor = this._createBranchConstructor(); +@@ -1788,7 +1788,7 @@ FixedType.prototype.random = function () { + function MapType(schema, opts) { + Type.call(this); + if (!schema.values) { +- throw new Error(f('missing map values: %j', schema)); ++ Type.addError(new Error(f('missing map values: %j', schema))); + } + this.valuesType = Type.forSchema(schema.values, opts); + this._branchConstructor = this._createBranchConstructor(); +@@ -1878,7 +1878,7 @@ MapType.prototype._write = function (tap, val) { + }; + + MapType.prototype._match = function () { +- throw new Error('maps cannot be compared'); ++ Type.addError(new Error('maps cannot be compared')); + }; + + MapType.prototype._update = function (rsv, type, opts) { +@@ -1926,7 +1926,7 @@ MapType.prototype._deref = function (schema, opts) { + function ArrayType(schema, opts) { + Type.call(this); + if (!schema.items) { +- throw new Error(f('missing array items: %j', schema)); ++ Type.addError(new Error(f('missing array items: %j', schema))); + } + this.itemsType = Type.forSchema(schema.items, opts); + this._branchConstructor = this._createBranchConstructor(); +@@ -2117,10 +2117,10 @@ function RecordType(schema, opts) { + Type.call(this, schema, opts); + + if (!Array.isArray(schema.fields)) { +- throw new Error(f('non-array record fields: %j', schema.fields)); ++ Type.addError(new Error(f('non-array record fields: %j', schema.fields ?? 'no `fields` in schema'))); + } + if (utils.hasDuplicates(schema.fields, function (f) { return f.name; })) { +- throw new Error(f('duplicate field name: %j', schema.fields)); ++ Type.addError(new Error(f('duplicate field name: %j', schema.fields))); + } + this._fieldsByName = {}; + this.fields = Object.freeze(schema.fields.map(function (f) { +@@ -2344,8 +2344,8 @@ RecordType.prototype._createWriter = function () { + + RecordType.prototype._update = function (resolver, type, opts) { + // jshint -W054 +- if (!hasCompatibleName(this, type, !opts.ignoreNamespaces)) { +- throw new Error(f('no alias found for %s', type.name)); ++ if (type.name && !~getAliases(this).indexOf(type.name)) { ++ Type.addError(new Error(f('no alias found for %s', type.name))); + } + + var rFields = this.fields; +@@ -2366,14 +2366,14 @@ RecordType.prototype._update = function (resolver, type, opts) { + } + } + if (matches.length > 1) { +- throw new Error( +- f('ambiguous aliasing for %s.%s (%s)', type.name, field.name, matches) ++ Type.addError(new Error())( ++ f('ambiguous aliasing for %s.%s (%s)', type.name, field.name, matches), + ); + } + if (!matches.length) { + if (field.defaultValue() === undefined) { +- throw new Error( +- f('no matching field for default-less %s.%s', type.name, field.name) ++ Type.addError(new Error())( ++ f('no matching field for default-less %s.%s', type.name, field.name), + ); + } + innerArgs.push('undefined'); +@@ -2797,9 +2797,11 @@ AbstractLongType.prototype.compare = utils.abstractFunction; + + /** A record field. */ + function Field(schema, opts) { ++ const saveAddError = Type.addError; ++ Type.addError = (err) => { err.fieldName = err.fieldName ? schema.name + '.' + err.fieldName : schema.name; saveAddError(err); }; + var name = schema.name; + if (typeof name != 'string' || !utils.isValidName(name)) { +- throw new Error(f('invalid field name: %s', name)); ++ Type.addError(new Error(f('invalid field name: %s', name))); + } + + this.name = name; +@@ -2816,12 +2818,15 @@ function Field(schema, opts) { + case 'ignore': + return 0; + default: +- throw new Error(f('invalid order: %j', order)); ++ Type.addError(new Error(f('invalid order: %j', order))); + } + })(schema.order === undefined ? 'ascending' : schema.order); + + var value = schema['default']; +- if (value !== undefined) { ++ ++ if (value === null && this.type.typeName === 'record') { ++ Type.addError(new Error(f('invalid default for field %s: null is not a %s', this.name, this.type.typeName))); ++ } else if (value !== undefined) { + // We need to convert defaults back to a valid format (unions are + // disallowed in default definitions, only the first type of each union is + // allowed instead). +@@ -2838,7 +2843,7 @@ function Field(schema, opts) { + type.types[0] + ); + } +- throw new Error(msg); ++ Type.addError(new Error(msg)); + } + // The clone call above will throw an error if the default is invalid. + if (isPrimitive(type.typeName) && type.typeName !== 'bytes') { +@@ -2848,6 +2853,7 @@ function Field(schema, opts) { + this.defaultValue = function () { return type._copy(val); }; + } + } ++ Type.addError = saveAddError; + + Object.freeze(this); + } +@@ -2906,7 +2912,7 @@ function Hash() { + function readValue(type, tap, resolver, lazy) { + if (resolver) { + if (resolver._readerType !== type) { +- throw new Error('invalid resolver'); ++ Type.addError(new Error('invalid resolver')); + } + return resolver._read(tap, lazy); + } else { +@@ -3038,7 +3044,7 @@ function isJsonBuffer(obj) { + * with a hook since the path is not propagated (for efficiency reasons). + */ + function throwInvalidError(val, type) { +- throw new Error(f('invalid %j: %j', type.schema(), val)); ++ Type.addError(new Error(f('invalid %j: %j', type.schema(), val))); + } + + function maybeQualify(name, ns) { +@@ -3294,6 +3300,9 @@ function combineObjects(types, opts) { + return Type.forSchema(schema, opts); + } + ++const errorsCollector = []; ++ ++Type.addError = (err) => errorsCollector.push(err); + + module.exports = { + Type: Type, +@@ -3313,5 +3322,6 @@ module.exports = { + types[getClassName(typeName)] = TYPES[typeName]; + } + return types; +- })() ++ })(), ++ errorsCollector + };