ruud
Version:
the player who serves
82 lines (71 loc) • 1.85 kB
JavaScript
export default async (path, options = {}) => {
const url = /^wss?:\/\//.test(path)
? path
: `ws${location.protocol.substring(4)}//${options.host || location.host}${path.startsWith('/') ? path : '/' + path}`;
const config = {
autoReconnect: true,
reconnectInterval: 3000,
maxReconnects: 5,
...options
};
let ws, reconnects = 0, to;
const handlers = { open: [], message: [], close: [], error: [] };
const call = (type, arg) => handlers[type].forEach(fn => fn(arg));
const tryJson = data => {
try {
return JSON.parse(data);
} catch {
return data;
}
};
const socket = {
send(data) {
if (typeof data !== 'string') {
data = JSON.stringify(data || '');
}
if (ws.readyState !== WebSocket.OPEN) {
this.on('open', () => this.send(data));
return this;
}
ws.send(data);
return this;
},
on(type, fn) {
handlers[type]?.push(fn);
return this;
},
close() {
clearTimeout(to);
config.autoReconnect = false;
ws.close();
return this;
},
get raw() {
return ws;
}
};
return new Promise((resolve) => {
const connect = () => {
ws = new WebSocket(url, options.protocols || []);
if (options.headers) {
Object.assign(ws, { headers: options.headers });
}
ws.onopen = e => {
reconnects = 0;
call('open', e);
resolve(socket);
};
ws.onmessage = e => {
call('message', tryJson(e.data));
};
ws.onclose = e => {
call('close', e);
if (config.autoReconnect && reconnects++ < config.maxReconnects) {
to = setTimeout(connect, config.reconnectInterval);
}
};
ws.onerror = e => call('error', e);
};
connect();
});
};