it-ws
Version:
Simple async iterables for websocket client connections
86 lines • 3.27 kB
JavaScript
import { EventIterator } from 'event-iterator';
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
// copied from github.com/feross/buffer
// Some ArrayBuffers are not passing the instanceof check, so we need to do a bit more work :(
function isArrayBuffer(obj) {
return (obj instanceof ArrayBuffer) ||
(obj?.constructor?.name === 'ArrayBuffer' && typeof obj?.byteLength === 'number');
}
export default (socket) => {
socket.binaryType = 'arraybuffer';
const connected = async () => {
await new Promise((resolve, reject) => {
if (isConnected) {
resolve();
return;
}
if (connError != null) {
reject(connError);
return;
}
const cleanUp = (cont) => {
socket.removeEventListener('open', onOpen);
socket.removeEventListener('error', onError);
cont();
};
const onOpen = () => { cleanUp(resolve); };
const onError = (event) => {
cleanUp(() => { reject(event.error ?? new Error(`connect ECONNREFUSED ${socket.url}`)); });
};
socket.addEventListener('open', onOpen);
socket.addEventListener('error', onError);
});
};
const source = (async function* () {
const messages = new EventIterator(({ push, stop, fail }) => {
const onMessage = (event) => {
let data = null;
if (typeof event.data === 'string') {
data = uint8ArrayFromString(event.data);
}
if (isArrayBuffer(event.data)) {
data = new Uint8Array(event.data);
}
if (event.data instanceof Uint8Array) {
data = event.data;
}
if (data == null) {
return;
}
push(data);
};
const onError = (event) => { fail(event.error ?? new Error('Socket error')); };
socket.addEventListener('message', onMessage);
socket.addEventListener('error', onError);
socket.addEventListener('close', stop);
return () => {
socket.removeEventListener('message', onMessage);
socket.removeEventListener('error', onError);
socket.removeEventListener('close', stop);
};
}, { highWaterMark: Infinity });
await connected();
for await (const chunk of messages) {
yield isArrayBuffer(chunk) ? new Uint8Array(chunk) : chunk;
}
}());
let isConnected = socket.readyState === 1;
let connError;
socket.addEventListener('open', () => {
isConnected = true;
connError = null;
});
socket.addEventListener('close', () => {
isConnected = false;
connError = null;
});
socket.addEventListener('error', event => {
if (!isConnected) {
connError = event.error ?? new Error(`connect ECONNREFUSED ${socket.url}`);
}
});
return Object.assign(source, {
connected
});
};
//# sourceMappingURL=source.js.map