UNPKG

trystero

Version:

Serverless WebRTC matchmaking for painless P2P

112 lines (79 loc) 2.61 kB
const {floor, random, sin} = Math export const libName = 'Trystero' export const alloc = (n, f) => Array(n).fill().map(f) const charSet = '0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz' export const genId = n => alloc(n, () => charSet[floor(random() * charSet.length)]).join('') export const selfId = genId(20) export const all = Promise.all.bind(Promise) export const isBrowser = typeof window !== 'undefined' export const {entries, fromEntries, keys} = Object export const noOp = () => {} export const mkErr = msg => new Error(`${libName}: ${msg}`) const encoder = new TextEncoder() const decoder = new TextDecoder() export const encodeBytes = txt => encoder.encode(txt) export const decodeBytes = buffer => decoder.decode(buffer) export const toHex = buffer => buffer.reduce((a, c) => a + c.toString(16).padStart(2, '0'), '') export const topicPath = (...parts) => parts.join('@') const shuffle = (xs, seed) => { const a = [...xs] const rand = () => { const x = sin(seed++) * 10_000 return x - floor(x) } let i = a.length while (i) { const j = floor(rand() * i--) ;[a[i], a[j]] = [a[j], a[i]] } return a } export const getRelays = (config, defaults, defaultN, deriveFromAppId) => { const relayUrls = config.relayUrls || (deriveFromAppId ? shuffle(defaults, strToNum(config.appId)) : defaults) return relayUrls.slice( 0, config.relayUrls ? config.relayUrls.length : config.relayRedundancy || defaultN ) } export const toJson = JSON.stringify export const fromJson = JSON.parse export const strToNum = (str, limit = Number.MAX_SAFE_INTEGER) => str.split('').reduce((a, c) => a + c.charCodeAt(0), 0) % limit const defaultRetryMs = 3333 const socketRetryPeriods = {} export const makeSocket = (url, onMessage) => { const client = {} const init = () => { const socket = new WebSocket(url) socket.onclose = () => { socketRetryPeriods[url] ??= defaultRetryMs setTimeout(init, socketRetryPeriods[url]) socketRetryPeriods[url] *= 2 } socket.onmessage = e => onMessage(e.data) client.socket = socket client.url = socket.url client.ready = new Promise( res => (socket.onopen = () => { res(client) socketRetryPeriods[url] = defaultRetryMs }) ) client.send = data => { if (socket.readyState === 1) { socket.send(data) } } } init() return client } export const socketGetter = clientMap => () => fromEntries(entries(clientMap).map(([url, client]) => [url, client.socket]))