1/* NOTE: 2 This file must be in /public App folder to use Socket with 3 a Shared Worker. 4 //https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker 5 */ 6 7/** 8 * Logs messages 9 * @param {String} level - Log level (uses consoles[level]) 10 * @param {...any} args - Arguments to log 11 */ 12const logger = (level, ...args) => { 13 /* eslint-disable-next-line no-console */ 14 const key = console[level] ? level : 'info'; 15 /* eslint-disable-next-line no-console */ 16 console[key]('%ctb-ui:SocketWorker', 'color: blue', ...args); 17}; 18 19/** 20 * Generates a random string 21 */ 22const randomString = () => Math.random().toString(36).substring(7); 23 24const defaultClientId = randomString(); 25 26/** 27 * Generates a new Socket IO 28 * @param {String} token - Auth token 29 * @param {String} url -Socket URL 30 * @param {Array.<String>} rooms - Socket client rooms 31 * @param {String?} clientId -Socket clientId 32 * @param {function} handler - Function to handle socket messages 33 */ 34const newSocket = (token, url, rooms = '', clientId = defaultClientId, handler) => { 35 if (!url) { 36 return null; 37 } 38 39 try { 40 logger('info', 'new-socket-rooms ', url, rooms); 41 // eslint-disable-next-line no-undef 42 importScripts(`${url}/socket.io/socket.io.js`); 43 const query = { 44 clientId, 45 ...(rooms ? { clientRooms: rooms } : {}), 46 ...(token ? { authToken: token } : {}), 47 }; 48 logger('info', 'query is', query); 49 50 // eslint-disable-next-line no-undef 51 return io(url, { 52 query, 53 transports: ['websocket'], 54 reconnection: true, 55 rejectUnauthorized: false, 56 }).on('message', handler); 57 } catch (e) { 58 logger('error', e); 59 return null; 60 } 61}; 62 63let socket; 64let token; 65let url; 66let clientId; 67let rooms; 68let socketPorts = []; // Connected tabs 69let socketTimeout; 70 71const end = () => { 72 if (socket) { 73 socket.close(); 74 } 75 this.close(); 76}; 77 78const closePort = (closingPortId) => { 79 logger('info', 'close-port', closingPortId); 80 socketPorts = socketPorts.filter(({ id }) => id !== closingPortId); 81 if (!socketPorts.length) { 82 end(); 83 } 84}; 85 86const onmessage = (e) => { 87 const { data: tabMessage } = e; 88 logger('info', 'on-message', tabMessage); 89 let updateSocket = false; 90 91 if (tabMessage.action === 'closing') { 92 const { data: { id: closingPortId } } = tabMessage; 93 closePort(closingPortId); 94 } 95 96 // Handle message actions 97 if (tabMessage.action === 'new-options' && token !== tabMessage.data.token) { 98 // eslint-disable-next-line prefer-destructuring 99 token = tabMessage.data.token; 100 updateSocket = true; 101 } 102 103 if (tabMessage.action === 'new-options' && url !== tabMessage.data.url) { 104 // eslint-disable-next-line prefer-destructuring 105 url = tabMessage.data.url; 106 updateSocket = true; 107 } 108 109 if (tabMessage.action === 'new-options' && rooms !== tabMessage.data.rooms) { 110 // eslint-disable-next-line prefer-destructuring 111 rooms = tabMessage.data.rooms; 112 updateSocket = true; 113 } 114 115 if (tabMessage.action === 'new-options' && rooms !== tabMessage.data.clientId) { 116 // eslint-disable-next-line prefer-destructuring 117 clientId = tabMessage.data.clientId; 118 updateSocket = true; 119 } 120 121 // If socket config has changed re-connect 122 if (updateSocket) { 123 // AWAIT 0.5 secs for new messages before create socket 124 if (socketTimeout) { 125 clearTimeout(socketTimeout); 126 } 127 128 socketTimeout = setTimeout(() => { 129 if (socket) { 130 logger('info', 'close-socket'); 131 socket.close(); 132 } 133 logger('info', 'create-socket'); 134 socket = newSocket(token, url, rooms, clientId, (message) => { 135 logger('info', 'receive-message', message); 136 socketPorts.forEach(({ port }) => port.postMessage({ action: 'message', data: message })); 137 }); 138 if (!socket) { 139 return; 140 } 141 142 socket.on('connect', () => logger('info', ('connected'))); 143 socket.on('error', err => logger('info', ('socket-error', err))); 144 }, 0.5 * 1000); 145 } 146}; 147 148// eslint-disable-next-line no-undef 149onconnect = ({ ports }) => { 150 ports.forEach((newPort) => { 151 const portId = randomString(); 152 logger('info', 'register-worker', portId); 153 logger('info', 'ports-length', socketPorts.length); 154 newPort.postMessage({ 155 action: 'connected', 156 data: { 157 id: portId, 158 }, 159 }); 160 161 socketPorts.push({ port: newPort, id: portId }); 162 }); 163 // eslint-disable-next-line no-param-reassign
164 ports.forEach((port) => { port.onmessage = onmessage; }); 165};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.