@wwa/socket.io-stream
Version:
stream for socket.io
1,968 lines (1,688 loc) • 233 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ss = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
module.exports = require('./lib');
},{"./lib":3}],2:[function(require,module,exports){
(function (Buffer){(function (){
const { Readable } = require('stream');
/**
* Readable stream for Blob and File on browser.
*/
class BlobReadStream extends Readable {
/**
* @param {Blob} blob - Blob or File object
* @param {Object} [options] - Stream options
* @param {boolean} [options.synchronous] - Use synchronous FileReader
* @api private
*/
constructor(blob, options = {}) {
// Extract stream-specific options from options object
const { synchronous, ...streamOptions } = options;
super(streamOptions);
this.blob = blob;
this.start = 0;
this.sync = synchronous || false;
// Initialize FileReader based on sync preference
if (this.sync && typeof window !== 'undefined' && 'FileReaderSync' in window && window.FileReaderSync) {
// @ts-expect-error - TypeScript does not recognize FileReaderSync as a valid property of the window object
this.fileReader = new window.FileReaderSync();
} else {
this.fileReader = new FileReader();
this.sync = false; // Force async if sync not available
this.fileReader.addEventListener('load', this._onload);
this.fileReader.addEventListener('error', this._onerror);
}
}
/**
* Read implementation for Readable stream
*
* @param {number} size - Size to read
* @api private
*/
_read(size) {
const start = this.start;
const end = this.start = this.start + size;
const chunk = this.blob.slice(start, end);
if (chunk.size) {
if (this.sync) {
const result = this.fileReader.readAsArrayBuffer(chunk);
const bufferChunk = Buffer.from(new Uint8Array(result));
this.push(bufferChunk);
} else {
this.fileReader.readAsArrayBuffer(chunk);
}
} else {
this.push(null);
}
}
/**
* Handle FileReader load event
*
* @param {ProgressEvent<FileReader>} e - Load event
* @api private
*/
_onload = (e) => {
if (e.target?.result instanceof ArrayBuffer) {
const chunk = Buffer.from(new Uint8Array(e.target.result));
this.push(chunk);
}
};
/**
* Handle FileReader error event
*
* @param {ProgressEvent<FileReader>} e - Error event
* @api private
*/
_onerror = (e) => {
if (e.target?.error) {
this.emit('error', e.target.error);
}
}
}
module.exports = BlobReadStream;
}).call(this)}).call(this,require("buffer").Buffer)
},{"buffer":10,"stream":18}],3:[function(require,module,exports){
(function (Buffer){(function (){
const Socket = require('./socket');
const IOStream = require('./iostream');
const BlobReadStream = require('./blob-read-stream');
/**
* Look up an existing Socket.
*
* @param {Object} sio - socket.io instance
* @param {Object} [options]
* @return {Socket} Socket instance
* @api public
*/
function lookup(sio, options) {
options = options || {};
if (null == options.forceBase64) {
options.forceBase64 = exports.forceBase64;
}
if (!sio._streamSocket) {
sio._streamSocket = new Socket(sio, options);
}
return sio._streamSocket;
}
exports = module.exports = lookup;
/**
* Expose Node Buffer for browser.
*
* @api public
*/
exports.Buffer = Buffer;
/**
* Expose Socket constructor.
*
* @api public
*/
exports.Socket = Socket;
/**
* Expose IOStream constructor.
*
* @api public
*/
exports.IOStream = IOStream;
/**
* Forces base 64 encoding when emitting. Must be set to true for Socket.IO v0.9 or lower.
*
* @api public
* @type {boolean}
*/
exports.forceBase64 = false;
/**
* Creates a new duplex stream.
*
* @param {Object} [options]
* @return {IOStream} duplex stream
* @api public
*/
exports.createStream = (options) => new IOStream(options);
/**
* Creates a new readable stream for Blob/File on browser.
*
* @param {Blob} blob
* @param {Object} options
* @return {BlobReadStream} stream
* @api public
*/
exports.createBlobReadStream = (blob, options) => new BlobReadStream(blob, options);
}).call(this)}).call(this,require("buffer").Buffer)
},{"./blob-read-stream":2,"./iostream":4,"./socket":6,"buffer":10}],4:[function(require,module,exports){
const { Duplex } = require('stream');
const uuid = require('./uuid');
const debug = require('debug')('socket.io-stream:iostream');
/**
* Duplex stream for socket.io-stream
*/
class IOStream extends Duplex {
/**
* @param {Object} [options] - Stream options
* @api private
*/
constructor(options) {
super(options);
this.options = options;
this.id = uuid();
this.socket = null;
// Buffers
this.pushBuffer = [];
this.writeBuffer = [];
// Op states
this._readable = false;
this._writable = false;
// default to *not* allowing half open sockets
this.allowHalfOpen = options?.allowHalfOpen || false;
this.on('finish', this._onfinish);
this.on('end', this._onend);
this.on('error', this._onerror);
}
/**
* Ensures that no more I/O activity happens on this stream.
* Not necessary in the usual case.
*
* @api public
*/
destroy() {
debug('destroy');
if (this.destroyed) {
debug('already destroyed');
return this;
}
super.destroy();
if (this.socket) {
debug('clean up');
this.socket.cleanup(this.id);
this.socket = null;
}
return this;
}
/**
* Local read
*
* @api private
*/
_read(size) {
// We cannot read from the socket if it's destroyed obviously ...
if (this.destroyed) return;
if (this.pushBuffer.length) {
// flush buffer and end if it exists.
let push;
while ((push = this.pushBuffer.shift())) {
if (!push()) break;
}
return;
}
this._readable = true;
// Go get data from remote stream
// Calls ._onread remotely then ._onwrite locally
this.socket._read(this.id, size);
}
/**
* Read from remote stream
*
* @api private
*/
_onread(size) {
const write = this.writeBuffer.shift();
if (write) return write();
this._writable = true;
}
/**
* Write local data to remote stream
* Calls remote ._onwrite
*
* @api private
*/
_write(chunk, encoding, callback) {
const write = () => {
// We cannot write to the socket if it's destroyed obviously ...
if (this.destroyed) return;
this._writable = false;
this.socket._write(this.id, chunk, encoding, callback);
};
if (this._writable) {
write();
} else {
this.writeBuffer.push(write);
}
}
/**
* Write the data fetched remotely
* so that we can now read locally
*
* @api private
*/
_onwrite(chunk, encoding, callback) {
const push = () => {
this._readable = false;
const ret = this.push(chunk || '', encoding);
callback();
return ret;
};
if (this._readable) {
push();
} else {
this.pushBuffer.push(push);
}
}
/**
* When ending send 'end' event to remote stream
*
* @api private
*/
_end() {
if (this.pushBuffer.length) {
// end after flushing buffer.
this.pushBuffer.push(() => this._done());
} else {
this._done();
}
}
/**
* Remote stream just ended
*
* @api private
*/
_done() {
this._readable = false;
// signal the end of the data.
return this.push(null);
}
/**
* the user has called .end(), and all the bytes have been
* sent out to the other side.
* If allowHalfOpen is false, or if the readable side has
* ended already, then destroy.
* If allowHalfOpen is true, then we need to set writable false,
* so that only the writable side will be cleaned up.
*
* @api private
*/
_onfinish() {
debug('_onfinish');
// Local socket just finished
// send 'end' event to remote
this.socket?._end(this.id);
if (!this.writableEnded) {
this.end();
}
if (!this.readable || this._readableState.ended) {
debug('_onfinish: ended, destroy %s', this._readableState);
return this.destroy();
}
debug('_onfinish: not ended');
if (!this.allowHalfOpen) {
this.push(null);
// just in case we're waiting for an EOF.
if (this.readable && !this.readableEnded) {
this.read(0);
}
}
}
/**
* the EOF has been received, and no more bytes are coming.
* if the writable side has ended already, then clean everything
* up.
*
* @api private
*/
_onend() {
debug('_onend');
if (!this.readableEnded) {
this.push(null);
}
if (!this.writable || this.writableFinished) {
debug('_onend: %s', this.writableFinished);
return this.destroy();
}
debug('_onend: not finished');
if (!this.allowHalfOpen) {
this.end();
}
}
/**
* When error in local stream
* notify remote
* if err.remote = true
* then error happened on remote stream
*
* @api private
*/
_onerror(err) {
// check if the error came from remote stream.
if (!err.remote && this.socket) {
// notify the error to the corresponding remote stream.
this.socket._error(this.id, err);
}
this.destroy();
}
}
module.exports = IOStream;
},{"./uuid":7,"debug":11,"stream":18}],5:[function(require,module,exports){
const { EventEmitter } = require('events');
const IOStream = require('./iostream');
class Encoder extends EventEmitter {
encode(v) {
if (v instanceof IOStream) {
return this.encodeStream(v);
}
if (Array.isArray(v)) {
return this.encodeArray(v);
}
if (v && 'object' == typeof v) {
return this.encodeObject(v);
}
return v;
}
/**
* @param {IOStream} stream
*/
encodeStream(stream) {
this.emit('stream', stream);
// represent a stream in an object.
const v = { $stream: stream.id };
if (stream.options) {
v.options = stream.options;
}
return v;
}
/**
* @param {Array} arr
* @return {Array}
*/
encodeArray(arr) {
return arr.map(item => this.encode(item));
}
/**
* @param {Object} obj
* @return {Object}
*/
encodeObject(obj) {
const v = {};
for (const k in obj) {
if (Object.hasOwn(obj, k)) {
v[k] = this.encode(obj[k]);
}
}
return v;
}
}
class Decoder extends EventEmitter {
/**
* @param {unknown} v
*/
decode(v) {
if (v && 'object' === typeof v && '$stream' in v && v.$stream) {
return this.decodeStream(v);
}
if (Array.isArray(v)) {
return this.decodeArray(v);
}
if (v && 'object' == typeof v) {
return this.decodeObject(v);
}
return v;
}
/**
* @param {Object} obj
* @return {IOStream}
*/
decodeStream(obj) {
const stream = new IOStream(obj.options);
stream.id = obj.$stream;
this.emit('stream', stream);
return stream;
}
/**
* @param {Array} arr
* @return {Array}
*/
decodeArray(arr) {
return arr.map(item => this.decode(item));
}
/**
* @param {Object} obj
* @return {Object}
*/
decodeObject(obj) {
const v = {};
for (const k in obj) {
if (Object.hasOwn(obj, k)) {
v[k] = this.decode(obj[k]);
}
}
return v;
}
}
exports.Encoder = Encoder;
exports.Decoder = Decoder;
},{"./iostream":4,"events":13}],6:[function(require,module,exports){
(function (global,Buffer){(function (){
const { EventEmitter } = require('events');
const parser = require('./parser');
const debug = require('debug')('socket.io-stream:socket');
/**
* Base event name for messaging.
*
* @api public
*/
const EVENT_NAME = '$stream';
const RESERVED_EVENTS = [
'error',
'newListener',
'removeListener'
];
/**
* Bidirectional stream socket which wraps Socket.IO.
*/
class Socket extends EventEmitter {
/**
* @param {import('socket.io').Socket} sio - Socket.IO socket instance
* @param {Object} [options] - Configuration options
* @param {boolean} [options.forceBase64] - Force base64 encoding
*/
constructor(sio, options = {}) {
super();
this.sio = sio;
this.forceBase64 = Boolean(options.forceBase64);
this.streams = {};
this.encoder = new parser.Encoder();
this.decoder = new parser.Decoder();
// Bind Socket.IO events
sio.on(EVENT_NAME, (...args) => super.emit(...args));
sio.on(`${EVENT_NAME}-read`, this._onread);
sio.on(`${EVENT_NAME}-write`, this._onwrite);
sio.on(`${EVENT_NAME}-end`, this._onend);
sio.on(`${EVENT_NAME}-error`, this._onerror);
sio.on('error', (err) => super.emit('error', err));
sio.on('disconnect', this._ondisconnect);
// Bind parser events
this.encoder.on('stream', this._onencode);
this.decoder.on('stream', this._ondecode);
}
$emit(type, ...args) {
return super.emit(type, ...args);
}
/**
* Emits streams to this corresponding server/client.
* Overrides EventEmitter.emit to handle stream events specially.
*
* @param {string} type - Event type
* @param {...any} args - Arguments to emit
* @return {this} self for chaining
* @api public
*/
emit(type, ...args) {
if (RESERVED_EVENTS.includes(type)) {
super.emit(type, ...args);
return this;
}
this._stream(type, ...args);
return this;
}
/**
* Add event listener with stream support.
*
* @param {string} type - Event type
* @param {(...args: any[]) => void} listener - Event listener
* @return {this} self
*/
on(type, listener) {
if (RESERVED_EVENTS.includes(type)) {
return super.on(type, listener);
}
this._onstream(type, listener);
return this;
}
/**
* Sends a new stream request.
*
* @param {string} type - Event type
* @param {...any} args - Arguments including streams
* @api private
*/
_stream(type, ...args) {
debug('sending new streams');
const ack = args[args.length - 1];
if (typeof ack === 'function') {
args[args.length - 1] = (...ackArgs) => {
const decodedArgs = this.decoder.decode(ackArgs);
ack.apply(this, decodedArgs);
};
}
const encodedArgs = this.encoder.encode(args);
this.sio.emit(EVENT_NAME, type, ...encodedArgs);
}
/**
* Notifies the read event.
*
* @param {string} id - Stream ID
* @param {number} size - Read size
* @api private
*/
_read(id, size) {
this.sio.emit(`${EVENT_NAME}-read`, id, size);
}
/**
* Requests to write a chunk.
*
* @param {string} id - Stream ID
* @param {Buffer|string|ArrayBuffer} chunk - Data chunk
* @param {string} encoding - Encoding type
* @param {Function} callback - Callback function
* @api private
*/
_write(id, chunk, encoding, callback) {
if (Buffer.isBuffer(chunk)) {
if (this.forceBase64) {
encoding = 'base64';
chunk = chunk.toString('base64');
} else if (!global.Buffer) {
// socket.io can't handle Buffer when using browserify.
if ('toArrayBuffer' in chunk && typeof chunk.toArrayBuffer === 'function') {
chunk = chunk.toArrayBuffer();
} else {
chunk = chunk.buffer;
}
}
}
this.sio.emit(`${EVENT_NAME}-write`, id, chunk, encoding, callback);
}
/**
* Sends end event to remote stream.
*
* @param {string} id - Stream ID
* @api private
*/
_end(id) {
this.sio.emit(`${EVENT_NAME}-end`, id);
}
/**
* Sends error event to remote stream.
*
* @param {string} id - Stream ID
* @param {Error|string} err - Error object or message
* @api private
*/
_error(id, err) {
const message = typeof err === 'string' ? err : err.message || String(err);
this.sio.emit(`${EVENT_NAME}-error`, id, message);
}
/**
* Handles a new stream request.
*
* @param {string} type - Event type
* @param {(...args: any[]) => void} listener - Event listener
* @api private
*/
_onstream(type, listener) {
if (typeof listener !== 'function') {
throw new TypeError('listener must be a function');
}
const onstream = (...args) => {
debug('new streams');
const ack = args[args.length - 1];
if (typeof ack === 'function') {
args[args.length - 1] = (...ackArgs) => {
const encodedArgs = this.encoder.encode(ackArgs);
ack.apply(this, encodedArgs);
};
}
const decodedArgs = this.decoder.decode(args);
listener.apply(this, decodedArgs);
};
// Store original listener for removeListener
onstream.listener = listener;
super.on(type, onstream);
}
/**
* Handles read request from remote stream.
*
* @param {string} id - Stream ID
* @param {number} size - Read size
* @api private
*/
_onread = (id, size) => {
debug('read: "%s"', id);
const stream = this.streams[id];
if (stream) {
stream._onread(size);
} else {
debug('ignore invalid stream id');
}
};
/**
* Handles write request from remote stream.
*
* @param {string} id - Stream ID
* @param {any} chunk - Data chunk
* @param {string} encoding - Encoding type
* @param {Function} callback - Callback function
* @api private
*/
_onwrite = (id, chunk, encoding, callback) => {
debug('write: "%s"', id);
const stream = this.streams[id];
if (!stream) {
callback(`invalid stream id: ${id}`);
return;
}
if (global.ArrayBuffer && chunk instanceof ArrayBuffer) {
// Make sure that chunk is a buffer for stream
chunk = Buffer.from(new Uint8Array(chunk));
}
stream._onwrite(chunk, encoding, callback);
};
/**
* Handles end event from remote stream.
*
* @param {string} id - Stream ID
* @api private
*/
_onend = (id) => {
debug('end: "%s"', id);
const stream = this.streams[id];
if (!stream) {
debug('ignore non-existent stream id: "%s"', id);
return;
}
stream._end();
};
/**
* Handles error event from remote stream.
*
* @param {string} id - Stream ID
* @param {string} message - Error message
* @api private
*/
_onerror = (id, message) => {
debug('error: "%s", "%s"', id, message);
const stream = this.streams[id];
if (!stream) {
debug('invalid stream id: "%s"', id);
return;
}
const err = new Error(message);
// @ts-ignore - Adding custom property for error source tracking
err.remote = true;
stream.emit('error', err);
};
/**
* Handles disconnect event from socket.io.
* Destroys all active streams.
*
* @api private
*/
_ondisconnect = () => {
for (const id in this.streams) {
const stream = this.streams[id];
stream.destroy();
// Close streams when the underlying
// socket.io connection is closed (regardless why)
stream.emit('close');
stream.emit('error', new Error('Connection aborted'));
}
};
/**
* Handles encoded stream from encoder.
*
* @param {import('./iostream')} stream - Stream instance
* @api private
*/
_onencode = (stream) => {
if (stream.socket || stream.destroyed) {
throw new Error('stream has already been sent.');
}
const { id } = stream;
if (this.streams[id]) {
throw new Error(`Encoded stream already exists: ${id}`);
}
this.streams[id] = stream;
stream.socket = this;
}
/**
* Handles decoded stream from decoder.
*
* @param {import('./iostream')} stream - Stream instance
* @api private
*/
_ondecode = (stream) => {
const { id } = stream;
if (this.streams[id]) {
this._error(id, new Error(`Decoded stream already exists: ${id}`));
return;
}
this.streams[id] = stream;
stream.socket = this;
}
/**
* Cleans up stream reference.
*
* @param {string} id - Stream ID
* @api public
*/
cleanup(id) {
delete this.streams[id];
}
}
// Export the Socket class and constants
module.exports = Socket;
module.exports.event = EVENT_NAME;
module.exports.events = RESERVED_EVENTS;
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer)
},{"./parser":5,"buffer":10,"debug":11,"events":13}],7:[function(require,module,exports){
// UUID function from https://gist.github.com/jed/982883
// More lightweight than node-uuid
/**
* Generate a UUID v4 string
* @param {*} [a] - placeholder for recursion
* @returns {string} UUID string
*/
function generateUUID(a) {
return a // if the placeholder was passed, return
? ( // a random number from 0 to 15
a ^ // unless b is 8,
Math.random() // in which case
* 16 // a random number from
>> a/4 // 8 to 11
).toString(16) // in hexadecimal
: '10000000-1000-4000-80000000-100000000000'.replace(/[018]/g, generateUUID);
}
module.exports = generateUUID;
},{}],8:[function(require,module,exports){
'use strict'
exports.byteLength = byteLength
exports.toByteArray = toByteArray
exports.fromByteArray = fromByteArray
var lookup = []
var revLookup = []
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for (var i = 0, len = code.length; i < len; ++i) {
lookup[i] = code[i]
revLookup[code.charCodeAt(i)] = i
}
// Support decoding URL-safe base64 strings, as Node.js does.
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
revLookup['-'.charCodeAt(0)] = 62
revLookup['_'.charCodeAt(0)] = 63
function getLens (b64) {
var len = b64.length
if (len % 4 > 0) {
throw new Error('Invalid string. Length must be a multiple of 4')
}
// Trim off extra bytes after placeholder bytes are found
// See: https://github.com/beatgammit/base64-js/issues/42
var validLen = b64.indexOf('=')
if (validLen === -1) validLen = len
var placeHoldersLen = validLen === len
? 0
: 4 - (validLen % 4)
return [validLen, placeHoldersLen]
}
// base64 is 4/3 + up to two characters of the original data
function byteLength (b64) {
var lens = getLens(b64)
var validLen = lens[0]
var placeHoldersLen = lens[1]
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}
function _byteLength (b64, validLen, placeHoldersLen) {
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}
function toByteArray (b64) {
var tmp
var lens = getLens(b64)
var validLen = lens[0]
var placeHoldersLen = lens[1]
var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))
var curByte = 0
// if there are placeholders, only get up to the last complete 4 chars
var len = placeHoldersLen > 0
? validLen - 4
: validLen
var i
for (i = 0; i < len; i += 4) {
tmp =
(revLookup[b64.charCodeAt(i)] << 18) |
(revLookup[b64.charCodeAt(i + 1)] << 12) |
(revLookup[b64.charCodeAt(i + 2)] << 6) |
revLookup[b64.charCodeAt(i + 3)]
arr[curByte++] = (tmp >> 16) & 0xFF
arr[curByte++] = (tmp >> 8) & 0xFF
arr[curByte++] = tmp & 0xFF
}
if (placeHoldersLen === 2) {
tmp =
(revLookup[b64.charCodeAt(i)] << 2) |
(revLookup[b64.charCodeAt(i + 1)] >> 4)
arr[curByte++] = tmp & 0xFF
}
if (placeHoldersLen === 1) {
tmp =
(revLookup[b64.charCodeAt(i)] << 10) |
(revLookup[b64.charCodeAt(i + 1)] << 4) |
(revLookup[b64.charCodeAt(i + 2)] >> 2)
arr[curByte++] = (tmp >> 8) & 0xFF
arr[curByte++] = tmp & 0xFF
}
return arr
}
function tripletToBase64 (num) {
return lookup[num >> 18 & 0x3F] +
lookup[num >> 12 & 0x3F] +
lookup[num >> 6 & 0x3F] +
lookup[num & 0x3F]
}
function encodeChunk (uint8, start, end) {
var tmp
var output = []
for (var i = start; i < end; i += 3) {
tmp =
((uint8[i] << 16) & 0xFF0000) +
((uint8[i + 1] << 8) & 0xFF00) +
(uint8[i + 2] & 0xFF)
output.push(tripletToBase64(tmp))
}
return output.join('')
}
function fromByteArray (uint8) {
var tmp
var len = uint8.length
var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
var parts = []
var maxChunkLength = 16383 // must be multiple of 3
// go through the array every three bytes, we'll deal with trailing stuff later
for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
}
// pad the end with zeros, but make sure to not forget the extra bytes
if (extraBytes === 1) {
tmp = uint8[len - 1]
parts.push(
lookup[tmp >> 2] +
lookup[(tmp << 4) & 0x3F] +
'=='
)
} else if (extraBytes === 2) {
tmp = (uint8[len - 2] << 8) + uint8[len - 1]
parts.push(
lookup[tmp >> 10] +
lookup[(tmp >> 4) & 0x3F] +
lookup[(tmp << 2) & 0x3F] +
'='
)
}
return parts.join('')
}
},{}],9:[function(require,module,exports){
},{}],10:[function(require,module,exports){
(function (Buffer){(function (){
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
/* eslint-disable no-proto */
'use strict'
var base64 = require('base64-js')
var ieee754 = require('ieee754')
exports.Buffer = Buffer
exports.SlowBuffer = SlowBuffer
exports.INSPECT_MAX_BYTES = 50
var K_MAX_LENGTH = 0x7fffffff
exports.kMaxLength = K_MAX_LENGTH
/**
* If `Buffer.TYPED_ARRAY_SUPPORT`:
* === true Use Uint8Array implementation (fastest)
* === false Print warning and recommend using `buffer` v4.x which has an Object
* implementation (most compatible, even IE6)
*
* Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
* Opera 11.6+, iOS 4.2+.
*
* We report that the browser does not support typed arrays if the are not subclassable
* using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array`
* (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support
* for __proto__ and has a buggy typed array implementation.
*/
Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport()
if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' &&
typeof console.error === 'function') {
console.error(
'This browser lacks typed array (Uint8Array) support which is required by ' +
'`buffer` v5.x. Use `buffer` v4.x if you require old browser support.'
)
}
function typedArraySupport () {
// Can typed array instances can be augmented?
try {
var arr = new Uint8Array(1)
arr.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } }
return arr.foo() === 42
} catch (e) {
return false
}
}
Object.defineProperty(Buffer.prototype, 'parent', {
enumerable: true,
get: function () {
if (!Buffer.isBuffer(this)) return undefined
return this.buffer
}
})
Object.defineProperty(Buffer.prototype, 'offset', {
enumerable: true,
get: function () {
if (!Buffer.isBuffer(this)) return undefined
return this.byteOffset
}
})
function createBuffer (length) {
if (length > K_MAX_LENGTH) {
throw new RangeError('The value "' + length + '" is invalid for option "size"')
}
// Return an augmented `Uint8Array` instance
var buf = new Uint8Array(length)
buf.__proto__ = Buffer.prototype
return buf
}
/**
* The Buffer constructor returns instances of `Uint8Array` that have their
* prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
* `Uint8Array`, so the returned instances will have all the node `Buffer` methods
* and the `Uint8Array` methods. Square bracket notation works as expected -- it
* returns a single octet.
*
* The `Uint8Array` prototype remains unmodified.
*/
function Buffer (arg, encodingOrOffset, length) {
// Common case.
if (typeof arg === 'number') {
if (typeof encodingOrOffset === 'string') {
throw new TypeError(
'The "string" argument must be of type string. Received type number'
)
}
return allocUnsafe(arg)
}
return from(arg, encodingOrOffset, length)
}
// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
if (typeof Symbol !== 'undefined' && Symbol.species != null &&
Buffer[Symbol.species] === Buffer) {
Object.defineProperty(Buffer, Symbol.species, {
value: null,
configurable: true,
enumerable: false,
writable: false
})
}
Buffer.poolSize = 8192 // not used by this implementation
function from (value, encodingOrOffset, length) {
if (typeof value === 'string') {
return fromString(value, encodingOrOffset)
}
if (ArrayBuffer.isView(value)) {
return fromArrayLike(value)
}
if (value == null) {
throw TypeError(
'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
'or Array-like Object. Received type ' + (typeof value)
)
}
if (isInstance(value, ArrayBuffer) ||
(value && isInstance(value.buffer, ArrayBuffer))) {
return fromArrayBuffer(value, encodingOrOffset, length)
}
if (typeof value === 'number') {
throw new TypeError(
'The "value" argument must not be of type number. Received type number'
)
}
var valueOf = value.valueOf && value.valueOf()
if (valueOf != null && valueOf !== value) {
return Buffer.from(valueOf, encodingOrOffset, length)
}
var b = fromObject(value)
if (b) return b
if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null &&
typeof value[Symbol.toPrimitive] === 'function') {
return Buffer.from(
value[Symbol.toPrimitive]('string'), encodingOrOffset, length
)
}
throw new TypeError(
'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
'or Array-like Object. Received type ' + (typeof value)
)
}
/**
* Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
* if value is a number.
* Buffer.from(str[, encoding])
* Buffer.from(array)
* Buffer.from(buffer)
* Buffer.from(arrayBuffer[, byteOffset[, length]])
**/
Buffer.from = function (value, encodingOrOffset, length) {
return from(value, encodingOrOffset, length)
}
// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug:
// https://github.com/feross/buffer/pull/148
Buffer.prototype.__proto__ = Uint8Array.prototype
Buffer.__proto__ = Uint8Array
function assertSize (size) {
if (typeof size !== 'number') {
throw new TypeError('"size" argument must be of type number')
} else if (size < 0) {
throw new RangeError('The value "' + size + '" is invalid for option "size"')
}
}
function alloc (size, fill, encoding) {
assertSize(size)
if (size <= 0) {
return createBuffer(size)
}
if (fill !== undefined) {
// Only pay attention to encoding if it's a string. This
// prevents accidentally sending in a number that would
// be interpretted as a start offset.
return typeof encoding === 'string'
? createBuffer(size).fill(fill, encoding)
: createBuffer(size).fill(fill)
}
return createBuffer(size)
}
/**
* Creates a new filled Buffer instance.
* alloc(size[, fill[, encoding]])
**/
Buffer.alloc = function (size, fill, encoding) {
return alloc(size, fill, encoding)
}
function allocUnsafe (size) {
assertSize(size)
return createBuffer(size < 0 ? 0 : checked(size) | 0)
}
/**
* Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
* */
Buffer.allocUnsafe = function (size) {
return allocUnsafe(size)
}
/**
* Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
*/
Buffer.allocUnsafeSlow = function (size) {
return allocUnsafe(size)
}
function fromString (string, encoding) {
if (typeof encoding !== 'string' || encoding === '') {
encoding = 'utf8'
}
if (!Buffer.isEncoding(encoding)) {
throw new TypeError('Unknown encoding: ' + encoding)
}
var length = byteLength(string, encoding) | 0
var buf = createBuffer(length)
var actual = buf.write(string, encoding)
if (actual !== length) {
// Writing a hex string, for example, that contains invalid characters will
// cause everything after the first invalid character to be ignored. (e.g.
// 'abxxcd' will be treated as 'ab')
buf = buf.slice(0, actual)
}
return buf
}
function fromArrayLike (array) {
var length = array.length < 0 ? 0 : checked(array.length) | 0
var buf = createBuffer(length)
for (var i = 0; i < length; i += 1) {
buf[i] = array[i] & 255
}
return buf
}
function fromArrayBuffer (array, byteOffset, length) {
if (byteOffset < 0 || array.byteLength < byteOffset) {
throw new RangeError('"offset" is outside of buffer bounds')
}
if (array.byteLength < byteOffset + (length || 0)) {
throw new RangeError('"length" is outside of buffer bounds')
}
var buf
if (byteOffset === undefined && length === undefined) {
buf = new Uint8Array(array)
} else if (length === undefined) {
buf = new Uint8Array(array, byteOffset)
} else {
buf = new Uint8Array(array, byteOffset, length)
}
// Return an augmented `Uint8Array` instance
buf.__proto__ = Buffer.prototype
return buf
}
function fromObject (obj) {
if (Buffer.isBuffer(obj)) {
var len = checked(obj.length) | 0
var buf = createBuffer(len)
if (buf.length === 0) {
return buf
}
obj.copy(buf, 0, 0, len)
return buf
}
if (obj.length !== undefined) {
if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) {
return createBuffer(0)
}
return fromArrayLike(obj)
}
if (obj.type === 'Buffer' && Array.isArray(obj.data)) {
return fromArrayLike(obj.data)
}
}
function checked (length) {
// Note: cannot use `length < K_MAX_LENGTH` here because that fails when
// length is NaN (which is otherwise coerced to zero.)
if (length >= K_MAX_LENGTH) {
throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes')
}
return length | 0
}
function SlowBuffer (length) {
if (+length != length) { // eslint-disable-line eqeqeq
length = 0
}
return Buffer.alloc(+length)
}
Buffer.isBuffer = function isBuffer (b) {
return b != null && b._isBuffer === true &&
b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false
}
Buffer.compare = function compare (a, b) {
if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength)
if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength)
if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
throw new TypeError(
'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array'
)
}
if (a === b) return 0
var x = a.length
var y = b.length
for (var i = 0, len = Math.min(x, y); i < len; ++i) {
if (a[i] !== b[i]) {
x = a[i]
y = b[i]
break
}
}
if (x < y) return -1
if (y < x) return 1
return 0
}
Buffer.isEncoding = function isEncoding (encoding) {
switch (String(encoding).toLowerCase()) {
case 'hex':
case 'utf8':
case 'utf-8':
case 'ascii':
case 'latin1':
case 'binary':
case 'base64':
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
return true
default:
return false
}
}
Buffer.concat = function concat (list, length) {
if (!Array.isArray(list)) {
throw new TypeError('"list" argument must be an Array of Buffers')
}
if (list.length === 0) {
return Buffer.alloc(0)
}
var i
if (length === undefined) {
length = 0
for (i = 0; i < list.length; ++i) {
length += list[i].length
}
}
var buffer = Buffer.allocUnsafe(length)
var pos = 0
for (i = 0; i < list.length; ++i) {
var buf = list[i]
if (isInstance(buf, Uint8Array)) {
buf = Buffer.from(buf)
}
if (!Buffer.isBuffer(buf)) {
throw new TypeError('"list" argument must be an Array of Buffers')
}
buf.copy(buffer, pos)
pos += buf.length
}
return buffer
}
function byteLength (string, encoding) {
if (Buffer.isBuffer(string)) {
return string.length
}
if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) {
return string.byteLength
}
if (typeof string !== 'string') {
throw new TypeError(
'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' +
'Received type ' + typeof string
)
}
var len = string.length
var mustMatch = (arguments.length > 2 && arguments[2] === true)
if (!mustMatch && len === 0) return 0
// Use a for loop to avoid recursion
var loweredCase = false
for (;;) {
switch (encoding) {
case 'ascii':
case 'latin1':
case 'binary':
return len
case 'utf8':
case 'utf-8':
return utf8ToBytes(string).length
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
return len * 2
case 'hex':
return len >>> 1
case 'base64':
return base64ToBytes(string).length
default:
if (loweredCase) {
return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8
}
encoding = ('' + encoding).toLowerCase()
loweredCase = true
}
}
}
Buffer.byteLength = byteLength
function slowToString (encoding, start, end) {
var loweredCase = false
// No need to verify that "this.length <= MAX_UINT32" since it's a read-only
// property of a typed array.
// This behaves neither like String nor Uint8Array in that we set start/end
// to their upper/lower bounds if the value passed is out of range.
// undefined is handled specially as per ECMA-262 6th Edition,
// Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
if (start === undefined || start < 0) {
start = 0
}
// Return early if start > this.length. Done here to prevent potential uint32
// coercion fail below.
if (start > this.length) {
return ''
}
if (end === undefined || end > this.length) {
end = this.length
}
if (end <= 0) {
return ''
}
// Force coersion to uint32. This will also coerce falsey/NaN values to 0.
end >>>= 0
start >>>= 0
if (end <= start) {
return ''
}
if (!encoding) encoding = 'utf8'
while (true) {
switch (encoding) {
case 'hex':
return hexSlice(this, start, end)
case 'utf8':
case 'utf-8':
return utf8Slice(this, start, end)
case 'ascii':
return asciiSlice(this, start, end)
case 'latin1':
case 'binary':
return latin1Slice(this, start, end)
case 'base64':
return base64Slice(this, start, end)
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
return utf16leSlice(this, start, end)
default:
if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
encoding = (encoding + '').toLowerCase()
loweredCase = true
}
}
}
// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package)
// to detect a Buffer instance. It's not possible to use `instanceof Buffer`
// reliably in a browserify context because there could be multiple different
// copies of the 'buffer' package in use. This method works even for Buffer
// instances that were created from another copy of the `buffer` package.
// See: https://github.com/feross/buffer/issues/154
Buffer.prototype._isBuffer = true
function swap (b, n, m) {
var i = b[n]
b[n] = b[m]
b[m] = i
}
Buffer.prototype.swap16 = function swap16 () {
var len = this.length
if (len % 2 !== 0) {
throw new RangeError('Buffer size must be a multiple of 16-bits')
}
for (var i = 0; i < len; i += 2) {
swap(this, i, i + 1)
}
return this
}
Buffer.prototype.swap32 = function swap32 () {
var len = this.length
if (len % 4 !== 0) {
throw new RangeError('Buffer size must be a multiple of 32-bits')
}
for (var i = 0; i < len; i += 4) {
swap(this, i, i + 3)
swap(this, i + 1, i + 2)
}
return this
}
Buffer.prototype.swap64 = function swap64 () {
var len = this.length
if (len % 8 !== 0) {
throw new RangeError('Buffer size must be a multiple of 64-bits')
}
for (var i = 0; i < len; i += 8) {
swap(this, i, i + 7)
swap(this, i + 1, i + 6)
swap(this, i + 2, i + 5)
swap(this, i + 3, i + 4)
}
return this
}
Buffer.prototype.toString = function toString () {
var length = this.length
if (length === 0) return ''
if (arguments.length === 0) return utf8Slice(this, 0, length)
return slowToString.apply(this, arguments)
}
Buffer.prototype.toLocaleString = Buffer.prototype.toString
Buffer.prototype.equals = function equals (b) {
if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
if (this === b) return true
return Buffer.compare(this, b) === 0
}
Buffer.prototype.inspect = function inspect () {
var str = ''
var max = exports.INSPECT_MAX_BYTES
str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim()
if (this.length > max) str += ' ... '
return '<Buffer ' + str + '>'
}
Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
if (isInstance(target, Uint8Array)) {
target = Buffer.from(target, target.offset, target.byteLength)
}
if (!Buffer.isBuffer(target)) {
throw new TypeError(
'The "target" argument must be one of type Buffer or Uint8Array. ' +
'Received type ' + (typeof target)
)
}
if (start === undefined) {
start = 0
}
if (end === undefined) {
end = target ? target.length : 0
}
if (thisStart === undefined) {
thisStart = 0
}
if (thisEnd === undefined) {
thisEnd = this.length
}
if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
throw new RangeError('out of range index')
}
if (thisStart >= thisEnd && start >= end) {
return 0
}
if (thisStart >= thisEnd) {
return -1
}
if (start >= end) {
return 1
}
start >>>= 0
end >>>= 0
thisStart >>>= 0
thisEnd >>>= 0
if (this === target) return 0
var x = thisEnd - thisStart
var y = end - start
var len = Math.min(x, y)
var thisCopy = this.slice(thisStart, thisEnd)
var targetCopy = target.slice(start, end)
for (var i = 0; i < len; ++i) {
if (thisCopy[i] !== targetCopy[i]) {
x = thisCopy[i]
y = targetCopy[i]
break
}
}
if (x < y) return -1
if (y < x) return 1
return 0
}
// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
// OR the last index of `val` in `buffer` at offset <= `byteOffset`.
//
// Arguments:
// - buffer - a Buffer to search
// - val - a string, Buffer, or number
// - byteOffset - an index into `buffer`; will be clamped to an int32
// - encoding - an optional encoding, relevant is val is a string
// - dir - true for indexOf, false for lastIndexOf
function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
// Empty buffer means no match
if (buffer.length === 0) return -1
// Normalize byteOffset
if (typeof byteOffset === 'string') {
encoding = byteOffset
byteOffset = 0
} else if (byteOffset > 0x7fffffff) {
byteOffset = 0x7fffffff
} else if (byteOffset < -0x80000000) {
byteOffset = -0x80000000
}
byteOffset = +byteOffset // Coerce to Number.
if (numberIsNaN(byteOffset)) {
// byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
byteOffset = dir ? 0 : (buffer.length - 1)
}
// Normalize byteOffset: negative offsets start from the end of the buffer
if (byteOffset < 0) byteOffset = buffer.length + byteOffset
if (byteOffset >= buffer.length) {
if (dir) return -1
else byteOffset = buffer.length - 1
} else if (byteOffset < 0) {
if (dir) byteOffset = 0
else return -1
}
// Normalize val
if (typeof val === 'string') {
val = Buffer.from(val, encoding)
}
// Finally, search either indexOf (if dir is true) or lastIndexOf
if (Buffer.isBuffer(val)) {
// Special case: looking for empty string/buffer always fails
if (val.length === 0) {
return -1
}
return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
} else if (typeof val === 'number') {
val = val & 0xFF // Search for a byte value [0-255]
if (typeof Uint8Array.prototype.indexOf === 'function') {
if (dir) {
return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
} else {
return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
}
}
return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)
}
throw new TypeError('val must be string, number or Buffer')
}
function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
var indexSize = 1
var arrLength = arr.length
var valLength = val.length
if (encoding !== undefined) {
encoding = String(encoding).toLowerCase()
if (encoding === 'ucs2' || encoding === 'ucs-2' ||
encoding === 'utf16le' || encoding === 'utf-16le') {
if (arr.length < 2 || val.length < 2) {
return -1
}
indexSize = 2
arrLength /= 2
valLength /= 2
byteOffset /= 2
}
}
function read (buf, i) {
if (indexSize === 1) {
return buf[i]
} else {
return buf.readUInt16BE(i * indexSize)
}
}
var i
if (dir) {
var foundIndex = -1
for (i = byteOffset; i < arrLength; i++) {
if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
if (foundIndex === -1) foundIndex = i
if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
} else {
if (foundIndex !== -1) i -= i - foundIndex
foundIndex = -1
}
}
} else {
if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
for (i = byteOffset; i >= 0; i--) {
var found = true
for (var j = 0; j < valLength; j++) {
if (read(arr, i + j) !== read(val, j)) {
found = false
break
}
}
if (found) return i
}
}
return -1
}
Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
return this.indexOf(val, byteOffset, encoding) !== -1
}
Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
}
Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
}
function hexWrite (buf, string, offset, length) {
offset = Number(offset) || 0
var remaining = buf.length - offset
if (!length) {
length = remaining
} else {
length = Number(length)
if (length > remaining) {
length = remaining
}
}
var strLen = string.length
if (length > strLen / 2) {
length = strLen / 2
}
for (var i = 0; i < length; ++i) {
var parsed = parseInt(string.substr(i * 2, 2), 16)
if (numberIsNaN(parsed)) return i
buf[offset + i] = parsed
}
return i
}
function utf8Write (buf, string, offset, length) {
return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
}
function asciiWrite (buf, string, offset, length) {
return blitBuffer(asciiToBytes(string), buf, offset, length)
}
function latin1Write (buf, string, offset, length) {
return asciiWrite(buf, string, offset, length)
}
function base64Write (buf, string, offset, length) {
return blitBuffer(base64ToBytes(string), buf, offset, length)
}
function ucs2Write (buf, string, offset, length) {
return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
}
Buffer.prototype.write = function write (string, offset, length, encoding) {
// Buffer#write(string)
if (offset === undefined) {
encoding = 'utf8'
length = this.length
offset = 0
// Buffer#write(string, encoding)
} else if (length === undefined && typeof offset === 'string') {
encoding = offset
length = this.length
offset = 0
// Buffer#write(string, offset[, length][, encoding])
} else if (isFinite(offset)) {
offset = offset >>> 0
if (isFinite(length)) {
length = length >>> 0
if (encoding === undefined) encoding = 'utf8'
} else {
encoding = length
length = undefined
}
} else {
throw new Error(
'Buffer.write(string, encoding, offset[, length]) is no longer supported'
)
}
var remaining = this.length - offset
if (length === undefined || length > remaining) length = remaining
if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
throw new RangeError('Attempt to write outside buffer bounds')
}
if (!encoding) encoding = 'utf8'
var loweredCase = false
for (;;) {
switch (encoding) {
case 'hex':
return hexWrite(this, string, offset, length)
case 'utf8':