@transcend-io/remote-web-streams
Version:
Web streams that work across web workers and iframes.
173 lines (166 loc) • 5.93 kB
JavaScript
import { WritableStream as WritableStream$1 } from 'web-streams-polyfill/ponyfill';
var NativeReadableStream = typeof ReadableStream === 'function' ? ReadableStream : undefined;
var NativeWritableStream = typeof WritableStream === 'function' ? WritableStream : undefined;
var SenderType;
(function (SenderType) {
SenderType[SenderType["WRITE"] = 0] = "WRITE";
SenderType[SenderType["ABORT"] = 1] = "ABORT";
SenderType[SenderType["CLOSE"] = 2] = "CLOSE";
})(SenderType || (SenderType = {}));
var ReceiverType;
(function (ReceiverType) {
ReceiverType[ReceiverType["PULL"] = 0] = "PULL";
ReceiverType[ReceiverType["ERROR"] = 1] = "ERROR";
})(ReceiverType || (ReceiverType = {}));
function fromReadablePort(port) {
return new NativeReadableStream(new MessagePortSource(port));
}
var MessagePortSource = /** @class */ (function () {
function MessagePortSource(_port) {
var _this = this;
this._port = _port;
this._port.onmessage = function (event) { return _this._onMessage(event.data); };
}
MessagePortSource.prototype.start = function (controller) {
this._controller = controller;
};
MessagePortSource.prototype.pull = function (controller) {
var message = {
type: ReceiverType.PULL
};
this._port.postMessage(message);
};
MessagePortSource.prototype.cancel = function (reason) {
var message = {
type: ReceiverType.ERROR,
reason: reason
};
this._port.postMessage(message);
this._port.close();
};
MessagePortSource.prototype._onMessage = function (message) {
switch (message.type) {
case SenderType.WRITE:
// enqueue() will call pull() if needed when there's no backpressure
this._controller.enqueue(message.chunk);
break;
case SenderType.ABORT:
this._controller.error(message.reason);
this._port.close();
break;
case SenderType.CLOSE:
this._controller.close();
this._port.close();
break;
}
};
return MessagePortSource;
}());
var WS = (NativeWritableStream ||
WritableStream$1);
function fromWritablePort(port, options) {
return new WS(new MessagePortSink(port, options));
}
var MessagePortSink = /** @class */ (function () {
function MessagePortSink(_port, options) {
var _this = this;
if (options === void 0) { options = {}; }
this._port = _port;
this._transferChunk = options.transferChunk;
this._resetReady();
this._port.onmessage = function (event) { return _this._onMessage(event.data); };
}
MessagePortSink.prototype.start = function (controller) {
this._controller = controller;
// Apply initial backpressure
return this._readyPromise;
};
MessagePortSink.prototype.write = function (chunk, controller) {
var message = {
type: SenderType.WRITE,
chunk: chunk,
};
// Send chunk, optionally transferring its contents
var transferList = this._transferChunk
? this._transferChunk(chunk)
: [];
if (transferList.length) {
this._port.postMessage(message, transferList);
}
else {
this._port.postMessage(message);
}
// Assume backpressure after every write, until sender pulls
this._resetReady();
// Apply backpressure
return this._readyPromise;
};
MessagePortSink.prototype.close = function () {
var message = {
type: SenderType.CLOSE,
};
this._port.postMessage(message);
this._port.close();
};
MessagePortSink.prototype.abort = function (reason) {
var message = {
type: SenderType.ABORT,
reason: reason,
};
this._port.postMessage(message);
this._port.close();
};
MessagePortSink.prototype._onMessage = function (message) {
switch (message.type) {
case ReceiverType.PULL:
this._resolveReady();
break;
case ReceiverType.ERROR:
this._onError(message.reason);
break;
}
};
MessagePortSink.prototype._onError = function (reason) {
this._controller.error(reason);
this._rejectReady(reason);
this._port.close();
};
MessagePortSink.prototype._resetReady = function () {
var _this = this;
this._readyPromise = new Promise(function (resolve, reject) {
_this._readyResolve = resolve;
_this._readyReject = reject;
});
this._readyPending = true;
};
MessagePortSink.prototype._resolveReady = function () {
this._readyResolve();
this._readyPending = false;
};
MessagePortSink.prototype._rejectReady = function (reason) {
if (!this._readyPending) {
this._resetReady();
}
this._readyPromise.catch(function () { });
this._readyReject(reason);
this._readyPending = false;
};
return MessagePortSink;
}());
var RemoteReadableStream = /** @class */ (function () {
function RemoteReadableStream() {
var channel = new MessageChannel();
this.writablePort = channel.port1;
this.readable = fromReadablePort(channel.port2);
}
return RemoteReadableStream;
}());
var RemoteWritableStream = /** @class */ (function () {
function RemoteWritableStream(options) {
var channel = new MessageChannel();
this.readablePort = channel.port1;
this.writable = fromWritablePort(channel.port2, options);
}
return RemoteWritableStream;
}());
export { RemoteReadableStream, RemoteWritableStream, fromReadablePort, fromWritablePort };