Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
206 lines
4.8 KiB
JavaScript
Executable file
206 lines
4.8 KiB
JavaScript
Executable file
!((define)=>{define((require,exports,module)=>{
|
|
|
|
/**
|
|
* Mini Logger
|
|
*
|
|
* @type {Function}
|
|
*/
|
|
|
|
var debug = 0 ? function(arg1, ...args) {
|
|
var type = `[${self.constructor.name}][${location.pathname}]`;
|
|
console.log(`[ServiceStream]${type} - "${arg1}"`, ...args);
|
|
} : () => {};
|
|
|
|
module.exports = function(service) {
|
|
debug('attaching plugin');
|
|
|
|
service
|
|
.method('_stream', onStream)
|
|
.method('_streamcancel', onStreamCancel);
|
|
|
|
service._streamHandlers = {};
|
|
service._activeStreams = [];
|
|
|
|
service.stream = function(name, fn) {
|
|
debug('register stream', name);
|
|
this._streamHandlers[name] = fn;
|
|
return this;
|
|
};
|
|
|
|
function onStream(name, id, clientId, args) {
|
|
debug('on stream open', name);
|
|
var e = {
|
|
preventDefault: () => e.defaultPrevented = true,
|
|
arguments: arguments
|
|
};
|
|
|
|
// Allow other plugins to override default behaviour
|
|
service.emit('before-stream', e);
|
|
if (e.defaultPrevented) return;
|
|
|
|
var fn = service._streamHandlers[name];
|
|
if (!fn) throw error(1, name);
|
|
|
|
var stream = new ServiceStream(id, service, clientId);
|
|
service._activeStreams[id] = stream;
|
|
|
|
// Always pass stream object as first
|
|
// argument to simplify the process
|
|
fn.apply(service, [stream].concat(args));
|
|
}
|
|
|
|
function onStreamCancel(id, reason) {
|
|
debug('stream cancel');
|
|
var stream = service._activeStreams[id];
|
|
delete service._activeStreams[id];
|
|
return stream.cancel(reason);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Writable Stream instance passed to the
|
|
* `service.stream` implementation
|
|
*
|
|
* @param {Object} options
|
|
* @param {String} options.id Stream ID used to sync client and service streams
|
|
* @param {BroadcastChannel} options.channel Channel used to postMessage
|
|
* @param {String} options.serviceId ID of the service
|
|
* @param {String} options.clientId ID of client that should receive message
|
|
*/
|
|
|
|
function ServiceStream(streamId, service, clientId) {
|
|
this.id = streamId;
|
|
this.service = service;
|
|
this.clientId = clientId;
|
|
this.writable = true;
|
|
debug('initialized');
|
|
}
|
|
|
|
/**
|
|
* Prototype assigned to variable
|
|
* to improve compression.
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
|
|
var ServiceStreamPrototype = ServiceStream.prototype;
|
|
|
|
/**
|
|
* Services that allows clients to
|
|
* cancel the operation before it's
|
|
* complete should override the
|
|
* `stream.cancel` method.
|
|
*
|
|
* @param {*} [reason] Data sent from client about the cancellation
|
|
* @returns {(Promise|*)}
|
|
*/
|
|
|
|
ServiceStreamPrototype.cancel = function(reason) {
|
|
var err = new TypeError('service should implement stream.cancel()');
|
|
return Promise.reject(err);
|
|
};
|
|
|
|
/**
|
|
* Signal to client that action was
|
|
* aborted during the process, this
|
|
* should be used as a way to
|
|
* communicate errors.
|
|
*
|
|
* @param {*} [data] Reason of failure
|
|
* @returns {Promise}
|
|
* @public
|
|
*/
|
|
|
|
ServiceStreamPrototype.abort = function(data) {
|
|
debug('abort', data);
|
|
this._post('abort', data);
|
|
this.writable = false;
|
|
};
|
|
|
|
/**
|
|
* Sends a chunk of data to the client.
|
|
*
|
|
* @param {*} data Chunk of data to be sent to client.
|
|
* @returns {Promise}
|
|
* @public
|
|
*/
|
|
|
|
ServiceStreamPrototype.write = function(data) {
|
|
debug('write', data);
|
|
this._post('write', data);
|
|
};
|
|
|
|
/**
|
|
* Closes the stream, signals that
|
|
* action was completed with success.
|
|
*
|
|
* According to whatwg streams spec,
|
|
* WritableStream#close() doesn't send data.
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
|
|
ServiceStreamPrototype.close = function() {
|
|
debug('close');
|
|
this._post('close');
|
|
this.writable = false;
|
|
};
|
|
|
|
/**
|
|
* Validate the current state and
|
|
* call cancel on the target stream.
|
|
*
|
|
* Called by the Service when client
|
|
* sends a 'streamcancel' message.
|
|
*
|
|
* @param {*} [reason] Reason for cancelation sent by the client
|
|
* @returns {Promise}
|
|
* @private
|
|
*/
|
|
|
|
ServiceStreamPrototype._cancel = function(reason) {
|
|
debug('cancel', reason);
|
|
return this.cancel(reason)
|
|
.then(result => {
|
|
this.writable = false;
|
|
return result;
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Validate the current state and post message to client.
|
|
*
|
|
* @param {String} type 'write', 'abort' or 'close'
|
|
* @param {String} state 'writable', 'aborted' or 'closed'
|
|
* @param {*} [data] Data to be sent to the client
|
|
* @returns {Promise}
|
|
* @private
|
|
*/
|
|
|
|
ServiceStreamPrototype._post = function(type, data) {
|
|
debug('post', type, data, this.writable);
|
|
if (!this.writable) return Promise.reject();
|
|
this.service.broadcast('streamevent', {
|
|
streamId: this.id,
|
|
type: type,
|
|
data: data
|
|
}, [this.clientId]);
|
|
};
|
|
|
|
/**
|
|
* Create new `Error` from registery.
|
|
*
|
|
* @param {Number} id Error Id
|
|
* @return {Error}
|
|
* @private
|
|
*/
|
|
function error(id) {
|
|
var args = [].slice.call(arguments, 1);
|
|
return new Error({
|
|
1: 'stream "' + args[0] + '" doesn\'t exist'
|
|
}[id]);
|
|
}
|
|
|
|
});})((typeof define)[0]=='f'&&define.amd?define:((n,w)=>{return(typeof
|
|
module)[0]=='o'?c=>{c(require,exports,module);}:(c)=>{var m={exports:{}};
|
|
c(n=>w[n],m.exports,m);w[n]=m.exports;};})('streamService',this));
|