Simulator first commit

This commit is contained in:
2019-09-18 11:11:16 +03:00
commit 6e1686be67
5028 changed files with 985331 additions and 0 deletions

97
node_modules/mumble-client/README.md generated vendored Normal file
View File

@ -0,0 +1,97 @@
# mumble-client
This module implements the client side of the Mumble protocol for use in both Nodejs and the browser.
It does not enforce any particular transport protocol nor does it provide the audio encoder/decoder.
See [mumble-client-tcp] and [mumble-client-udp] and [mumble-client-codecs-node] for creating a Nodejs based application or
[mumble-client-websocket] and [mumble-client-codecs-browser] for a browser app.
### Usage
Node: All functions that take a Node-style callback as their last argument may also be used without that callback and will then instead return a [Promise].
Most transport modules will have their own MumbleClient factory functions.
Therefore this is only of importance if you do not wish to use any transport module or are implementing your one.
Note that encryption of the data stream has to be done by the transport module while the voice stream is encrypted by this module.
```javascript
var MumbleClient = require('mumble-client')
var client = new MumbleClient({
username: 'Test',
password: 'Pass123'
})
// someDuplexStream is used for data transmission and as a voice channel fallback
client.connectDataStream(someDuplexStream, function (err, client) {
if (err) throw err
// Connection established
console.log('Welcome message:', client.welcomeMessage)
console.log('Actual username:', client.self.username)
// Optionally connect a potentially lossy, udp-like voice channel
client.connectVoiceStream(someOtherDuplexStream)
var testChannel = client.getChannel('Test Channel')
if (testChannel) {
client.self.setChannel(testChannel)
}
client.users.forEach(function (user) {
console.log('User', user.username, 'is in channel', user.channel.name)
})
})
// You may then register listeners for the 'voice' event to receive a stream
// of raw PCM frames.
client.on('newUser', function (user) {
user.on('voice', function (stream) {
stream.on('data', function (data) {
// Interleaved IEEE754 32-bit linear PCM with nominal range between -1 and +1
// May be of zero length which is usually only the case when voice.end is true
console.log(data.pcm) // Float32Array
console.log(data.numberOfChannels)
// Target indicates who the user is talking to
// Can be one of: 'normal', 'whisper', 'shout'
console.log(data.target)
// Neither numberOfChannels nor target should normally change during one
// transmission however this cannot be guaranteed
}).on('end', function () {
// The current voice transmission has ended, stateful decoders have been reset
// Can be used to disable the 'talking' indicator of this use
// Because the last packet might get lost due to packet loss, a timer is
// used to always fire this event. As a result a single transmission might
// be split when packets are delayed.
console.log(user.username, 'stopped talking.')
})
})
})
// To send audio, pipe your raw PCM samples (as Float32Array or Buffer) at
// 48kHz into an outgoing stream created with Client#createVoiceStream.
// Any audio piped in the stream while muted, suppressed or not yet connected
// will be silently dropped.
// First argument is the target: 0 is normal talking, 1-31 are voice targets
var voiceStream = client.createVoiceStream(0)
myPcmSource.pipe(voiceStream)
// Make sure the stream is ended when the transmission should end
// For positional audio the Float32Array is wrapped in an object which
// also contains the position:
myPcmSource.on('data', function (chunk) {
voiceStream.write({
pcm: chunk,
x: 1,
y: 2.5,
z: 3
})
})
```
### License
MIT
[mumble-client-tcp]: https://github.com/johni0702/mumble-client-tcp
[mumble-client-udp]: https://github.com/johni0702/mumble-client-udp
[mumble-client-websocket]: https://github.com/johni0702/mumble-client-websocket
[mumble-client-codecs-node]: https://github.com/johni0702/mumble-client-codecs-node
[mumble-client-codecs-browser]: https://github.com/johni0702/mumble-client-codecs-browser
[Promise]: https://github.com/then/promise

1
node_modules/mumble-client/index.js generated vendored Normal file
View File

@ -0,0 +1 @@
module.exports = require('./lib/client.js').default

294
node_modules/mumble-client/lib/channel.js generated vendored Normal file
View File

@ -0,0 +1,294 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _events = require('events');
var _removeValue = require('remove-value');
var _removeValue2 = _interopRequireDefault(_removeValue);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var Channel = function (_EventEmitter) {
_inherits(Channel, _EventEmitter);
function Channel(client, id) {
_classCallCheck(this, Channel);
var _this = _possibleConstructorReturn(this, (Channel.__proto__ || Object.getPrototypeOf(Channel)).call(this));
_this._client = client;
_this._id = id;
_this._links = [];
_this.users = [];
_this.children = [];
_this._haveRequestedDescription = false;
return _this;
}
_createClass(Channel, [{
key: '_remove',
value: function _remove() {
if (this.parent) {
(0, _removeValue2.default)(this.parent.children, this);
}
this.emit('remove');
}
}, {
key: '_update',
value: function _update(msg) {
var _this2 = this;
var changes = {};
if (msg.name != null) {
changes.name = this._name = msg.name;
}
if (msg.description != null) {
changes.description = this._description = msg.description;
}
if (msg.description_hash != null) {
changes.descriptionHash = this._descriptionHash = msg.description_hash;
this._haveRequestedDescription = false; // invalidate previous request
}
if (msg.temporary != null) {
changes.temporary = this._temporary = msg.temporary;
}
if (msg.position != null) {
changes.position = this._position = msg.position;
}
if (msg.max_users != null) {
changes.maxUsers = this._maxUsers = msg.max_users;
}
if (msg.links) {
this._links = msg.links;
changes.links = this.links;
}
if (msg.links_remove) {
this._links = this._links.filter(function (e) {
return !msg.links_remove.includes(e);
});
changes.links = this.links;
}
if (msg.links_add) {
msg.links_add.filter(function (e) {
return !_this2._links.includes(e);
}).forEach(function (e) {
return _this2._links.push(e);
});
changes.links = this.links;
}
if (msg.parent != null) {
if (this.parent) {
(0, _removeValue2.default)(this.parent.children, this);
}
this._parentId = msg.parent;
if (this.parent) {
this.parent.children.push(this);
}
changes.parent = this.parent;
}
this.emit('update', changes);
}
}, {
key: 'setName',
value: function setName(name) {
this._client._send({
name: 'ChannelState',
payload: {
channel_id: this._id,
name: name
}
});
}
}, {
key: 'setParent',
value: function setParent(parent) {
this._client._send({
name: 'ChannelState',
payload: {
channel_id: this._id,
parent: parent._id
}
});
}
}, {
key: 'setTemporary',
value: function setTemporary(temporary) {
this._client._send({
name: 'ChannelState',
payload: {
channel_id: this._id,
temporary: temporary
}
});
}
}, {
key: 'setDescription',
value: function setDescription(description) {
this._client._send({
name: 'ChannelState',
payload: {
channel_id: this._id,
description: description
}
});
}
}, {
key: 'setPosition',
value: function setPosition(position) {
this._client._send({
name: 'ChannelState',
payload: {
channel_id: this._id,
position: position
}
});
}
}, {
key: 'setLinks',
value: function setLinks(links) {
this._client._send({
name: 'ChannelState',
payload: {
channel_id: this._id,
links: links.map(function (c) {
return c._id;
})
}
});
}
}, {
key: 'setMaxUsers',
value: function setMaxUsers(maxUsers) {
this._client._send({
name: 'ChannelState',
payload: {
channel_id: this._id,
max_users: maxUsers
}
});
}
}, {
key: 'sendMessage',
value: function sendMessage(message) {
this._client._send({
name: 'TextMessage',
payload: {
channel_id: [this._id],
message: message
}
});
}
}, {
key: 'sendTreeMessage',
value: function sendTreeMessage(message) {
this._client._send({
name: 'TextMessage',
payload: {
tree_id: [this._id],
message: message
}
});
}
}, {
key: 'requestDescription',
value: function requestDescription() {
if (this._haveRequestedDescription) return;
this._client._send({
name: 'RequestBlob',
payload: {
channel_description: this._id
}
});
this._haveRequestedDescription = true;
}
}, {
key: 'id',
get: function get() {
return this._id;
}
}, {
key: 'name',
get: function get() {
return this._name;
},
set: function set(to) {
throw new Error('Cannot set name. Use #setName(name) instead.');
}
}, {
key: 'parent',
get: function get() {
return this._client._channelById[this._parentId];
},
set: function set(to) {
throw new Error('Cannot set parent. Use #setParent(channel) instead.');
}
}, {
key: 'description',
get: function get() {
return this._description;
},
set: function set(to) {
throw new Error('Cannot set description. Use #setDescription(desc) instead.');
}
}, {
key: 'descriptionHash',
get: function get() {
return this._descriptionHash;
},
set: function set(to) {
throw new Error('Cannot set descriptionHash.');
}
}, {
key: 'temporary',
get: function get() {
return this._temporary;
},
set: function set(to) {
throw new Error('Cannot set temporary. Use #setTemporary(tmp) instead.');
}
}, {
key: 'position',
get: function get() {
return this._position;
},
set: function set(to) {
throw new Error('Cannot set position.');
}
}, {
key: 'maxUsers',
get: function get() {
return this._maxUsers;
},
set: function set(to) {
throw new Error('Cannot set maxUsers.');
}
}, {
key: 'links',
get: function get() {
var _this3 = this;
return this._links.map(function (id) {
return _this3._client._channelById[id];
});
},
set: function set(to) {
throw new Error('Cannot set links. Use #setLinks(links) instead.');
}
}]);
return Channel;
}(_events.EventEmitter);
exports.default = Channel;

860
node_modules/mumble-client/lib/client.js generated vendored Normal file
View File

@ -0,0 +1,860 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _mumbleStreams = require('mumble-streams');
var _mumbleStreams2 = _interopRequireDefault(_mumbleStreams);
var _reduplexer = require('reduplexer');
var _reduplexer2 = _interopRequireDefault(_reduplexer);
var _events = require('events');
var _through = require('through2');
var _through2 = _interopRequireDefault(_through);
var _promise = require('promise');
var _promise2 = _interopRequireDefault(_promise);
var _dropStream = require('drop-stream');
var _dropStream2 = _interopRequireDefault(_dropStream);
var _utils = require('./utils.js');
var _user2 = require('./user');
var _user3 = _interopRequireDefault(_user2);
var _channel = require('./channel');
var _channel2 = _interopRequireDefault(_channel);
var _removeValue = require('remove-value');
var _removeValue2 = _interopRequireDefault(_removeValue);
var _statsIncremental = require('stats-incremental');
var _statsIncremental2 = _interopRequireDefault(_statsIncremental);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var DenyType = _mumbleStreams2.default.data.messages.PermissionDenied.DenyType;
/*
* @typedef {'Opus'} Codec
*/
/**
* Number of the voice target when outgoing (0 for normal talking, 1-31 for
* a voice target).
* String describing the source when incoming.
* @typedef {number|'normal'|'shout'|'whisper'} VoiceTarget
*/
/**
* @typedef {object} VoiceData
* @property {VoiceTarget} target - Target of the audio
* @property {Codec} codec - The codec of the audio packet
* @property {Buffer} frame - Encoded audio frame, null indicates a lost frame
* @property {?Position} position - Position of audio source
*/
/**
* Interleaved 32-bit float PCM frames in [-1; 1] range with sample rate of 48k.
* @typedef {object} PCMData
* @property {VoiceTarget} target - Target of the audio
* @property {Float32Array} pcm - The pcm data
* @property {number} numberOfChannels - Number of channels
* @property {?Position} position - Position of audio source
* @property {?number} bitrate - Target bitrate hint for encoder, see for default {@link MumbleClient#setAudioQuality}
*/
/**
* Transforms {@link VoiceData} to {@link PCMData}.
* Should ignore any unknown codecs.
*
* @interface DecoderStream
* @extends stream.Transform
*/
/**
* Transforms {@link PCMData} to {@link VoiceData}.
*
* @interface EncoderStream
* @extends stream.Transform
*/
/**
* @interface Codecs
* @property {number[]} celt - List of celt versions supported by this implementation
* @property {boolean} opus - Whether this implementation supports the Opus codec
*/
/**
* Returns the duration of encoded voice data without actually decoding it.
*
* @function Codecs#getDuration
* @param {Codec} codec - The codec
* @param {Buffer} buffer - The encoded data
* @return {number} The duration in milliseconds (has to be a multiple of 10)
*/
/**
* Creates a new decoder stream for a transmission of the specified user.
* This method is called for every single transmission (whenever a user starts
* speaking), as such it must not be expensive.
*
* @function Codecs#createDecoderStream
* @param {User} user - The user
* @return {DecoderStream} The decoder stream
*/
/**
* Creates a new encoder stream for a outgoing transmission.
* This method is called for every single transmission (whenever the user
* starts speaking), as such it must not be expensive.
*
* @function Codecs#createEncoderStream
* @param {Codec} codec - The codec
* @return {EncoderStream} The endecoder stream
*/
/**
* Single use Mumble client.
*/
var MumbleClient = function (_EventEmitter) {
_inherits(MumbleClient, _EventEmitter);
/**
* A mumble client.
* This object may only be connected to one server and cannot be reused.
*
* @param {object} options - Options
* @param {string} options.username - User name of the client
* @param {string} [options.password] - Server password to use
* @param {string[]} [options.tokens] - Array of access tokens to use
* @param {string} [options.clientSoftware] - Client software name/version
* @param {string} [options.osName] - Client operating system name
* @param {string} [options.osVersion] - Client operating system version
* @param {Codecs} [options.codecs] - Codecs used for voice
* @param {number} [options.userVoiceTimeout] - Milliseconds after which an
* inactive voice transmissions is timed out
* @param {number} [options.maxInFlightDataPings] - Amount of data pings without response
* after which the connection is considered timed out
* @param {number} [options.dataPingInterval] - Interval of data pings (in ms)
*/
function MumbleClient(options) {
_classCallCheck(this, MumbleClient);
var _this = _possibleConstructorReturn(this, (MumbleClient.__proto__ || Object.getPrototypeOf(MumbleClient)).call(this));
if (!options.username) {
throw new Error('No username given');
}
_this._options = options || {};
_this._username = options.username;
_this._password = options.password;
_this._tokens = options.tokens;
_this._codecs = options.codecs;
_this._dataPingInterval = options.dataPingInterval || 5000;
_this._maxInFlightDataPings = options.maxInFlightDataPings || 2;
_this._dataStats = new _statsIncremental2.default();
_this._voiceStats = new _statsIncremental2.default();
_this._userById = {};
_this._channelById = {};
_this.users = [];
_this.channels = [];
_this._dataEncoder = new _mumbleStreams2.default.data.Encoder();
_this._dataDecoder = new _mumbleStreams2.default.data.Decoder();
_this._voiceEncoder = new _mumbleStreams2.default.voice.Encoder('server');
_this._voiceDecoder = new _mumbleStreams2.default.voice.Decoder('server');
_this._data = (0, _reduplexer2.default)(_this._dataEncoder, _this._dataDecoder, { objectMode: true });
_this._voice = (0, _reduplexer2.default)(_this._voiceEncoder, _this._voiceDecoder, { objectMode: true });
_this._data.on('data', _this._onData.bind(_this));
_this._voice.on('data', _this._onVoice.bind(_this));
_this._voiceEncoder.on('data', function (data) {
// TODO This should only be the fallback option
_this._data.write({
name: 'UDPTunnel',
payload: data
});
});
_this._voiceDecoder.on('unknown_codec', function (codecId) {
return _this.emit('unknown_codec', codecId);
});
_this._data.on('end', _this.disconnect.bind(_this));
_this._registerErrorHandler(_this._data, _this._voice, _this._dataEncoder, _this._dataDecoder, _this._voiceEncoder, _this._voiceDecoder);
_this._disconnected = false;
return _this;
}
_createClass(MumbleClient, [{
key: '_registerErrorHandler',
value: function _registerErrorHandler() {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = arguments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var obj = _step.value;
obj.on('error', this._error.bind(this));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
}, {
key: '_error',
value: function _error(reason) {
this.emit('error', reason);
this.disconnect();
}
}, {
key: '_send',
value: function _send(msg) {
this._data.write(msg);
}
/**
* Connects this client to a duplex stream that is used for the data channel.
* The provided duplex stream is expected to be valid and usable.
* Calling this method will begin the initialization of the connection.
*
* @param stream - The stream used for the data channel.
* @param callback - Optional callback that is invoked when the connection has been established.
*/
}, {
key: 'connectDataStream',
value: function connectDataStream(stream, callback) {
var _this2 = this;
if (this._dataStream) throw Error('Already connected!');
this._dataStream = stream;
// Connect the supplied stream to the data channel encoder and decoder
this._registerErrorHandler(stream);
this._dataEncoder.pipe(stream).pipe(this._dataDecoder);
// Send the initial two packets
this._send({
name: 'Version',
payload: {
version: _mumbleStreams2.default.version.toUInt8(),
release: this._options.clientSoftware || 'Node.js mumble-client',
os: this._options.osName || (0, _utils.getOSName)(),
os_version: this._options.osVersion || (0, _utils.getOSVersion)()
}
});
this._send({
name: 'Authenticate',
payload: {
username: this._username,
password: this._password,
tokens: this._tokens,
celt_versions: (this._codecs || { celt: [] }).celt,
opus: (this._codecs || { opus: false }).opus
}
});
return new _promise2.default(function (resolve, reject) {
_this2.once('connected', function () {
return resolve(_this2);
});
_this2.once('reject', reject);
_this2.once('error', reject);
}).nodeify(callback);
}
/**
* Connects this client to a duplex stream that is used for the voice channel.
* The provided duplex stream is expected to be valid and usable.
* The stream may be unreliable. That is, it may lose packets or deliver them
* out of order.
* It must however gurantee that packets arrive unmodified and/or are dropped
* when corrupted.
* It is also responsible for any encryption that is necessary.
*
* Connecting a voice channel is entirely optional. If no voice channel
* is connected, all voice data is tunneled through the data channel.
*
* @param stream - The stream used for the data channel.
* @returns {undefined}
*/
}, {
key: 'connectVoiceStream',
value: function connectVoiceStream(stream) {
// Connect the stream to the voice channel encoder and decoder
this._registerErrorHandler(stream);
this._voiceEncoder.pipe(stream).pipe(this._voiceDecoder);
// TODO: Ping packet
}
}, {
key: 'createVoiceStream',
value: function createVoiceStream() {
var _this3 = this;
var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
var numberOfChannels = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
if (!this._codecs) {
return _dropStream2.default.obj();
}
var voiceStream = _through2.default.obj(function (chunk, encoding, callback) {
if (chunk instanceof Buffer) {
chunk = new Float32Array(chunk.buffer, chunk.byteOffset, chunk.byteLength / 4);
}
if (chunk instanceof Float32Array) {
chunk = {
target: target,
pcm: chunk,
numberOfChannels: numberOfChannels
};
} else {
chunk = {
target: target,
pcm: chunk.pcm,
numberOfChannels: numberOfChannels,
position: { x: chunk.x, y: chunk.y, z: chunk.z }
};
}
var samples = _this3._samplesPerPacket || chunk.pcm.length / numberOfChannels;
chunk.bitrate = _this3.getActualBitrate(samples, chunk.position != null);
callback(null, chunk);
});
var codec = 'Opus'; // TODO
var seqNum = 0;
voiceStream.pipe(this._codecs.createEncoderStream(codec)).on('data', function (data) {
var duration = _this3._codecs.getDuration(codec, data.frame) / 10;
_this3._voice.write({
seqNum: seqNum,
codec: codec,
mode: target,
frames: [data.frame],
position: data.position,
end: false
});
seqNum += duration;
}).on('end', function () {
_this3._voice.write({
seqNum: seqNum,
codec: codec,
mode: target,
frames: [],
end: true
});
});
return voiceStream;
}
/**
* Method called when new voice packets arrive.
* Forwards the packet to the source user.
*/
}, {
key: '_onVoice',
value: function _onVoice(chunk) {
var user = this._userById[chunk.source];
user._onVoice(chunk.seqNum, chunk.codec, chunk.target, chunk.frames, chunk.position, chunk.end);
}
/**
* Method called when new data packets arrive.
* If there is a method named '_onPacketName', the data is forwarded to
* that method, otherwise it is logged as unhandled.
*
* @param {object} chunk - The data packet
*/
}, {
key: '_onData',
value: function _onData(chunk) {
if (this['_on' + chunk.name]) {
this['_on' + chunk.name](chunk.payload);
} else {
console.log('Unhandled data packet:', chunk);
}
}
}, {
key: '_onUDPTunnel',
value: function _onUDPTunnel(payload) {
// Forward tunneled udp packets to the voice pipeline
this._voiceDecoder.write(payload);
}
}, {
key: '_onVersion',
value: function _onVersion(payload) {
this.serverVersion = {
major: payload.version >> 16,
minor: payload.version >> 8 & 0xff,
patch: payload.version >> 0 & 0xff,
release: payload.release,
os: payload.os,
osVersion: payload.os_version
};
}
}, {
key: '_onServerSync',
value: function _onServerSync(payload) {
var _this4 = this;
// This packet finishes the initialization phase
this.self = this._userById[payload.session];
this.maxBandwidth = payload.max_bandwidth;
this.welcomeMessage = payload.welcome_text;
// Make sure we send regular ping packets to not get disconnected
this._pinger = setInterval(function () {
if (_this4._inFlightDataPings >= _this4._maxInFlightDataPings) {
_this4._error('timeout');
return;
}
var dataStats = _this4._dataStats.getAll();
var voiceStats = _this4._voiceStats.getAll();
var timestamp = new Date().getTime();
var payload = {
timestamp: timestamp
};
if (dataStats) {
payload.tcp_packets = dataStats.n;
payload.tcp_ping_avg = dataStats.mean;
payload.tcp_ping_var = dataStats.variance;
}
if (voiceStats) {
payload.udp_packets = voiceStats.n;
payload.udp_ping_avg = voiceStats.mean;
payload.udp_ping_var = voiceStats.variance;
}
_this4._send({
name: 'Ping',
payload: payload
});
_this4._inFlightDataPings++;
}, this._dataPingInterval);
// We are now connected
this.emit('connected');
}
}, {
key: '_onPing',
value: function _onPing(payload) {
if (this._inFlightDataPings <= 0) {
console.warn('Got unexpected ping message:', payload);
return;
}
this._inFlightDataPings--;
var now = new Date().getTime();
var duration = now - payload.timestamp.toNumber();
this._dataStats.update(duration);
this.emit('dataPing', duration);
}
}, {
key: '_onReject',
value: function _onReject(payload) {
// We got rejected from the server for some reason.
this.emit('reject', payload);
this.disconnect();
}
}, {
key: '_onPermissionDenied',
value: function _onPermissionDenied(payload) {
if (payload.type === DenyType.Text) {
this.emit('denied', 'Text', null, null, payload.reason);
} else if (payload.type === DenyType.Permission) {
var user = this._userById[payload.session];
var channel = this._channelById[payload.channel_id];
this.emit('denied', 'Permission', user, channel, payload.permission);
} else if (payload.type === DenyType.SuperUser) {
this.emit('denied', 'SuperUser', null, null, null);
} else if (payload.type === DenyType.ChannelName) {
this.emit('denied', 'ChannelName', null, null, payload.name);
} else if (payload.type === DenyType.TextTooLong) {
this.emit('denied', 'TextTooLong', null, null, null);
} else if (payload.type === DenyType.TemporaryChannel) {
this.emit('denied', 'TemporaryChannel', null, null, null);
} else if (payload.type === DenyType.MissingCertificate) {
var _user = this._userById[payload.session];
this.emit('denied', 'MissingCertificate', _user, null, null);
} else if (payload.type === DenyType.UserName) {
this.emit('denied', 'UserName', null, null, payload.name);
} else if (payload.type === DenyType.ChannelFull) {
this.emit('denied', 'ChannelFull', null, null, null);
} else if (payload.type === DenyType.NestingLimit) {
this.emit('denied', 'NestingLimit', null, null, null);
} else {
throw Error('Invalid DenyType: ' + payload.type);
}
}
}, {
key: '_onTextMessage',
value: function _onTextMessage(payload) {
var _this5 = this;
this.emit('message', this._userById[payload.actor], payload.message, payload.session.map(function (id) {
return _this5._userById[id];
}), payload.channel_id.map(function (id) {
return _this5._channelById[id];
}), payload.tree_id.map(function (id) {
return _this5._channelById[id];
}));
}
}, {
key: '_onChannelState',
value: function _onChannelState(payload) {
var _this6 = this;
var channel = this._channelById[payload.channel_id];
if (!channel) {
channel = new _channel2.default(this, payload.channel_id);
this._channelById[channel._id] = channel;
this.channels.push(channel);
this.emit('newChannel', channel);
}
(payload.links_remove || []).forEach(function (otherId) {
var otherChannel = _this6._channelById[otherId];
if (otherChannel && otherChannel.links.indexOf(channel) !== -1) {
otherChannel._update({
links_remove: [payload.channel_id]
});
}
});
channel._update(payload);
}
}, {
key: '_onChannelRemove',
value: function _onChannelRemove(payload) {
var channel = this._channelById[payload.channel_id];
if (channel) {
channel._remove();
delete this._channelById[channel._id];
(0, _removeValue2.default)(this.channels, channel);
}
}
}, {
key: '_onUserState',
value: function _onUserState(payload) {
var user = this._userById[payload.session];
if (!user) {
user = new _user3.default(this, payload.session);
this._userById[user._id] = user;
this.users.push(user);
this.emit('newUser', user);
// For some reason, the mumble protocol does not send the initial
// channel of a client if it is the root channel
payload.channel_id = payload.channel_id || 0;
}
user._update(payload);
}
}, {
key: '_onUserRemove',
value: function _onUserRemove(payload) {
var user = this._userById[payload.session];
if (user) {
user._remove(this._userById[payload.actor], payload.reason, payload.ban);
delete this._userById[user._id];
(0, _removeValue2.default)(this.users, user);
}
}
/**
* Disconnect from the remote server.
* Once disconnected, this client may not be used again.
* Does nothing when not connected.
*/
}, {
key: 'disconnect',
value: function disconnect() {
if (this._disconnected) {
return;
}
this._disconnected = true;
this._voice.end();
this._data.end();
clearInterval(this._pinger);
this.emit('disconnected');
}
/**
* Set preferred audio bitrate and samples per packet.
*
* The {@link PCMData} passed to the stream returned by {@link createVoiceStream} must
* contain the appropriate amount of samples per channel for bandwidth control to
* function as expected.
*
* If this method is never called or false is passed as one of the values, then the
* samplesPerPacket are determined by inspecting the {@link PCMData} passed and the
* bitrate is calculated from the maximum bitrate advertised by the server.
*
* @param {number} bitrate - Preferred audio bitrate, sensible values are 8k to 96k
* @param {number} samplesPerPacket - Amount of samples per packet, valid values depend on the codec used but all should support 10ms (i.e. 480), 20ms, 40ms and 60ms
*/
}, {
key: 'setAudioQuality',
value: function setAudioQuality(bitrate, samplesPerPacket) {
this._preferredBitrate = bitrate;
this._samplesPerPacket = samplesPerPacket;
}
/**
* Calculate the actual bitrate taking into account maximum and preferred bitrate.
*/
}, {
key: 'getActualBitrate',
value: function getActualBitrate(samplesPerPacket, sendPosition) {
var bitrate = this.getPreferredBitrate(samplesPerPacket, sendPosition);
var bandwidth = MumbleClient.calcEnforcableBandwidth(bitrate, samplesPerPacket, sendPosition);
if (bandwidth <= this.maxBandwidth) {
return bitrate;
} else {
return this.getMaxBitrate(samplesPerPacket, sendPosition);
}
}
/**
* Returns the preferred bitrate set by {@link setAudioQuality} or
* {@link getMaxBitrate} if not set.
*/
}, {
key: 'getPreferredBitrate',
value: function getPreferredBitrate(samplesPerPacket, sendPosition) {
if (this._preferredBitrate) {
return this._preferredBitrate;
}
return this.getMaxBitrate(samplesPerPacket, sendPosition);
}
/**
* Calculate the maximum bitrate possible given the current server bandwidth limit.
*/
}, {
key: 'getMaxBitrate',
value: function getMaxBitrate(samplesPerPacket, sendPosition) {
var overhead = MumbleClient.calcEnforcableBandwidth(0, samplesPerPacket, sendPosition);
return this.maxBandwidth - overhead;
}
/**
* Calculate the bandwidth used if IP/UDP packets were used to transmit audio.
* This matches the value used by Mumble servers to enforce bandwidth limits.
* @returns {number} bits per second
*/
}, {
key: 'getChannel',
/**
* Find a channel by name.
* If no such channel exists, return null.
*
* @param {string} name - The full name of the channel
* @returns {?Channel}
*/
value: function getChannel(name) {
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = this.channels[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var channel = _step2.value;
if (channel.name === name) {
return channel;
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
return null;
}
}, {
key: 'setSelfMute',
value: function setSelfMute(mute) {
var message = {
name: 'UserState',
payload: {
session: this.self._id,
self_mute: mute
}
};
if (!mute) message.payload.self_deaf = false;
this._send(message);
}
}, {
key: 'setSelfDeaf',
value: function setSelfDeaf(deaf) {
var message = {
name: 'UserState',
payload: {
session: this.self._id,
self_deaf: deaf
}
};
if (deaf) message.payload.self_mute = true;
this._send(message);
}
}, {
key: 'setSelfTexture',
value: function setSelfTexture(texture) {
this._send({
name: 'UserState',
payload: {
session: this.self._id,
texture: texture
}
});
}
}, {
key: 'setSelfComment',
value: function setSelfComment(comment) {
this._send({
name: 'UserState',
payload: {
session: this.self._id,
comment: comment
}
});
}
}, {
key: 'setPluginContext',
value: function setPluginContext(context) {
this._send({
name: 'UserState',
payload: {
session: this.self._id,
plugin_context: context
}
});
}
}, {
key: 'setPluginIdentity',
value: function setPluginIdentity(identity) {
this._send({
name: 'UserState',
payload: {
session: this.self._id,
plugin_identity: identity
}
});
}
}, {
key: 'setRecording',
value: function setRecording(recording) {
this._send({
name: 'UserState',
payload: {
session: this.self._id,
recording: recording
}
});
}
}, {
key: 'getChannelById',
value: function getChannelById(id) {
return this._channelById[id];
}
}, {
key: 'getUserById',
value: function getUserById(id) {
return this._userById[id];
}
}, {
key: 'root',
get: function get() {
return this._channelById[0];
}
}, {
key: 'connected',
get: function get() {
return !this._disconnected && this._dataStream != null;
}
}, {
key: 'dataStats',
get: function get() {
return this._dataStats.getAll();
}
}, {
key: 'voiceStats',
get: function get() {
return this._voiceStats.getAll();
}
}], [{
key: 'calcEnforcableBandwidth',
value: function calcEnforcableBandwidth(bitrate, samplesPerPacket, sendPosition) {
// IP + UDP + Crypt + Header + SeqNum (VarInt) + Codec Header + Optional Position
// Codec Header depends on codec:
// - Opus is always 4 (just the length as VarInt)
// - CELT/Speex depends on frames (10ms) per packet (1 byte each)
var codecHeaderBytes = Math.max(4, samplesPerPacket / 480);
var packetBytes = 20 + 8 + 4 + 1 + 4 + codecHeaderBytes + (sendPosition ? 12 : 0);
var packetsPerSecond = 48000 / samplesPerPacket;
return Math.round(packetBytes * 8 * packetsPerSecond + bitrate);
}
}]);
return MumbleClient;
}(_events.EventEmitter);
exports.default = MumbleClient;

450
node_modules/mumble-client/lib/user.js generated vendored Normal file
View File

@ -0,0 +1,450 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _events = require('events');
var _dropStream = require('drop-stream');
var _dropStream2 = _interopRequireDefault(_dropStream);
var _removeValue = require('remove-value');
var _removeValue2 = _interopRequireDefault(_removeValue);
var _rtimer = require('rtimer');
var _rtimer2 = _interopRequireDefault(_rtimer);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var User = function (_EventEmitter) {
_inherits(User, _EventEmitter);
function User(client, id) {
_classCallCheck(this, User);
var _this = _possibleConstructorReturn(this, (User.__proto__ || Object.getPrototypeOf(User)).call(this));
_this._client = client;
_this._id = id;
_this._haveRequestedTexture = false;
_this._haveRequestedComment = false;
return _this;
}
_createClass(User, [{
key: '_update',
value: function _update(msg) {
var changes = {};
if (msg.name != null) {
changes.username = this._username = msg.name;
}
if (msg.user_id != null) {
changes.uniqueId = this._uniqueId = msg.user_id;
}
if (msg.mute != null) {
changes.mute = this._mute = msg.mute;
}
if (msg.deaf != null) {
changes.deaf = this._deaf = msg.deaf;
}
if (msg.suppress != null) {
changes.suppress = this._suppress = msg.suppress;
}
if (msg.self_mute != null) {
changes.selfMute = this._selfMute = msg.self_mute;
}
if (msg.self_deaf != null) {
changes.selfDeaf = this._selfDeaf = msg.self_deaf;
}
if (msg.texture != null) {
changes.texture = this._texture = msg.texture;
}
if (msg.texture_hash != null) {
changes.textureHash = this._textureHash = msg.texture_hash;
this._haveRequestedTexture = false; // invalidate previous request
}
if (msg.comment != null) {
changes.comment = this._comment = msg.comment;
}
if (msg.comment_hash != null) {
changes.commentHash = this._commentHash = msg.comment_hash;
this._haveRequestedComment = false; // invalidate previous request
}
if (msg.priority_speaker != null) {
changes.prioritySpeaker = this._prioritySpeaker = msg.priority_speaker;
}
if (msg.recording != null) {
changes.recording = this._recording = msg.recording;
}
if (msg.hash != null) {
changes.certHash = this._certHash = msg.hash;
}
if (msg.channel_id != null) {
if (this.channel) {
(0, _removeValue2.default)(this.channel.users, this);
}
this._channelId = msg.channel_id;
if (this.channel) {
this.channel.users.push(this);
}
changes.channel = this.channel;
}
this.emit('update', this._client._userById[msg.actor], changes);
}
}, {
key: '_remove',
value: function _remove(actor, reason, ban) {
if (this.channel) {
(0, _removeValue2.default)(this.channel.users, this);
}
this.emit('remove', actor, reason, ban);
}
}, {
key: '_getOrCreateVoiceStream',
value: function _getOrCreateVoiceStream() {
var _this2 = this;
if (!this._voice) {
// New transmission
if (!this._client._codecs) {
// No codecs available, cannot decode
this._voice = _dropStream2.default.obj();
} else {
this._voice = this._client._codecs.createDecoderStream(this);
}
this._voice.once('close', function () {
_this2._voice = null;
});
this._voiceTimeout = new _rtimer2.default(function () {
_this2._voice.end();
_this2._voice = null;
}, this._client._options.userVoiceTimeout || 200).set();
this.emit('voice', this._voice);
}
return this._voice;
}
}, {
key: '_getDuration',
value: function _getDuration(codec, frames) {
var _this3 = this;
if (this._client._codecs) {
var duration = 0;
frames.forEach(function (frame) {
duration += _this3._client._codecs.getDuration(codec, frame);
});
return duration;
} else {
return frames.length * 10;
}
}
/**
* This method filters and inserts empty frames as needed to accout
* for packet loss and then writes to the {@link #_voice} stream.
* If this is a new transmission it emits the 'voice' event and if
* the transmission has ended it closes the stream.
*/
}, {
key: '_onVoice',
value: function _onVoice(seqNum, codec, target, frames, position, end) {
var _this4 = this;
if (frames.length > 0) {
var duration = this._getDuration(codec, frames);
if (this._voice != null) {
// This is not the first packet in this transmission
// So drop it if it's late
if (this._lastVoiceSeqId > seqNum) {
return;
}
// And make up for lost packets
if (this._lastVoiceSeqId < seqNum - duration / 10) {
var lost = seqNum - this._lastVoiceSeqId - 1;
// Cap at 10 lost frames, the audio will sound broken at that point anyway
if (lost > 10) {
lost = 10;
}
for (var i = 0; i < lost; i++) {
this._getOrCreateVoiceStream().write({
target: target,
codec: codec,
frame: null,
position: position
});
}
}
}
frames.forEach(function (frame) {
_this4._getOrCreateVoiceStream().write({
target: target,
codec: codec,
frame: frame,
position: position
});
});
this._voiceTimeout.set();
this._lastVoiceSeqId = seqNum + duration / 10 - 1;
}
if (end && this._voice) {
this._voiceTimeout.clear();
this._voiceTimeout = null;
this._voice.end();
this._voice = null;
}
}
}, {
key: 'setMute',
value: function setMute(mute) {
var message = {
name: 'UserState',
payload: {
session: this._id,
mute: mute
}
};
if (!mute) message.payload.deaf = false;
this._client._send(message);
}
}, {
key: 'setDeaf',
value: function setDeaf(deaf) {
var message = {
name: 'UserState',
payload: {
session: this._id,
deaf: deaf
}
};
if (deaf) message.payload.mute = true;
this._client._send(message);
}
}, {
key: 'clearComment',
value: function clearComment() {
this._client._send({
name: 'UserState',
payload: {
session: this._id,
comment: ''
}
});
}
}, {
key: 'clearTexture',
value: function clearTexture() {
this._client._send({
name: 'UserState',
payload: {
session: this._id,
texture: ''
}
});
}
}, {
key: 'requestComment',
value: function requestComment() {
if (this._haveRequestedComment) return;
this._client._send({
name: 'RequestBlob',
payload: {
session_comment: this._id
}
});
this._haveRequestedComment = true;
}
}, {
key: 'requestTexture',
value: function requestTexture() {
if (this._haveRequestedTexture) return;
this._client._send({
name: 'RequestBlob',
payload: {
session_texture: this._id
}
});
this._haveRequestedTexture = true;
}
}, {
key: 'register',
value: function register() {
this._client._send({
name: 'UserState',
payload: {
session: this._id,
user_id: 0
}
});
}
}, {
key: 'sendMessage',
value: function sendMessage(message) {
this._client._send({
name: 'TextMessage',
payload: {
session: this._id,
message: message
}
});
}
}, {
key: 'setChannel',
value: function setChannel(channel) {
this._client._send({
name: 'UserState',
payload: {
session: this._id,
channel_id: channel._id
}
});
}
}, {
key: 'id',
get: function get() {
return this._id;
}
}, {
key: 'username',
get: function get() {
return this._username;
},
set: function set(to) {
throw new Error('Cannot set username.');
}
}, {
key: 'uniqueId',
get: function get() {
return this._uniqueId;
},
set: function set(to) {
throw new Error('Cannot set uniqueId. Maybe try #register()?');
}
}, {
key: 'mute',
get: function get() {
return this._mute;
},
set: function set(to) {
throw new Error('Cannot set mute. Use #setMute(mute) instead.');
}
}, {
key: 'deaf',
get: function get() {
return this._deaf;
},
set: function set(to) {
throw new Error('Cannot set deaf. Use #setDeaf(deaf) instead.');
}
}, {
key: 'selfMute',
get: function get() {
return this._selfMute;
},
set: function set(to) {
throw new Error('Cannot set selfMute. Use Client#setSelfMute(mute) instead.');
}
}, {
key: 'selfDeaf',
get: function get() {
return this._selfDeaf;
},
set: function set(to) {
throw new Error('Cannot set selfDeaf. Use Client#setSelfDeaf(deaf) instead.');
}
}, {
key: 'suppress',
get: function get() {
return this._suppress;
},
set: function set(to) {
throw new Error('Cannot set suppress.');
}
}, {
key: 'texture',
get: function get() {
return this._texture;
},
set: function set(to) {
throw new Error('Cannot set texture. Use Client#setSelfTexture(texture) or #clearTexture() instead.');
}
}, {
key: 'textureHash',
get: function get() {
return this._textureHash;
},
set: function set(to) {
throw new Error('Cannot set textureHash.');
}
}, {
key: 'comment',
get: function get() {
return this._comment;
},
set: function set(to) {
throw new Error('Cannot set comment. Use Client#setSelfTexture(texture) or #clearComment() instead.');
}
}, {
key: 'commentHash',
get: function get() {
return this._commentHash;
},
set: function set(to) {
throw new Error('Cannot set commentHash.');
}
}, {
key: 'prioritySpeaker',
get: function get() {
return this._prioritySpeaker;
},
set: function set(to) {
throw new Error('Cannot set prioritySpeaker. Use #setPrioritySpeaker(prioSpeaker) instead.');
}
}, {
key: 'recording',
get: function get() {
return this._recording;
},
set: function set(to) {
throw new Error('Cannot set recording. Use Client#setSelfRecording(recording) instead.');
}
}, {
key: 'certHash',
get: function get() {
return this._certHash;
},
set: function set(to) {
throw new Error('Cannot set certHash.');
}
}, {
key: 'channel',
get: function get() {
if (this._channelId != null) {
return this._client._channelById[this._channelId];
} else {
return null;
}
},
set: function set(to) {
throw new Error('Cannot set channel. Use #setChannel(channel) instead.');
}
}]);
return User;
}(_events.EventEmitter);
exports.default = User;

22
node_modules/mumble-client/lib/utils.js generated vendored Normal file
View File

@ -0,0 +1,22 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getOSName = getOSName;
exports.getOSVersion = getOSVersion;
function getOSName() {
if (process.browser) {
return 'Browser';
} else {
return 'Node.js';
}
}
function getOSVersion() {
if (process.browser) {
return navigator.userAgent;
} else {
return process.version;
}
}

72
node_modules/mumble-client/package.json generated vendored Normal file
View File

@ -0,0 +1,72 @@
{
"_from": "mumble-client@^1.1.1",
"_id": "mumble-client@1.3.0",
"_inBundle": false,
"_integrity": "sha512-4z/Frp+XwTsE0u+7g6BUQbYumV17iEaMBCZ5Oo5lQ5Jjq3sBnZYRH9pXDX1bU4/3HFU99/AVGcScH2R67olPPQ==",
"_location": "/mumble-client",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "mumble-client@^1.1.1",
"name": "mumble-client",
"escapedName": "mumble-client",
"rawSpec": "^1.1.1",
"saveSpec": null,
"fetchSpec": "^1.1.1"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/mumble-client/-/mumble-client-1.3.0.tgz",
"_shasum": "f045ad302260ed15b5e22d9c7fbc4683d5b4d1dd",
"_spec": "mumble-client@^1.1.1",
"_where": "/home/sergiu/linx-audio-simulator",
"author": {
"name": "Jonas Herzig",
"email": "me@johni0702.de"
},
"bugs": {
"url": "https://github.com/johni0702/mumble-client/issues"
},
"bundleDependencies": false,
"dependencies": {
"drop-stream": "^0.1.1",
"mumble-streams": "0.0.4",
"promise": "^7.1.1",
"reduplexer": "^1.1.0",
"remove-value": "^1.0.0",
"rtimer": "^0.1.0",
"stats-incremental": "^1.2.1",
"through2": "^2.0.2"
},
"deprecated": false,
"description": "Mumble protocol client library",
"devDependencies": {
"babel-cli": "^6.14.0",
"babel-preset-es2015": "^6.14.0",
"chai": "^3.5.0",
"mocha": "^3.0.2",
"mocha-standard": "^1.0.0",
"standard": "^8.0.0"
},
"files": [
"index.js",
"lib"
],
"homepage": "https://github.com/johni0702/mumble-client#readme",
"license": "MIT",
"main": "index.js",
"name": "mumble-client",
"repository": {
"type": "git",
"url": "git+https://github.com/johni0702/mumble-client.git"
},
"scripts": {
"compile": "babel -d lib/ src/",
"mocha": "mocha --compilers js:babel-core/register",
"prepublish": "npm run compile",
"test": "npm run compile && npm run mocha"
},
"version": "1.3.0"
}