{"version":3,"names":["PROTOCOL_VERSION","parseMessage","data","message","JSON","parse","version","logger","error","undefined","serializeMessage","toSerialize","Error","prettyFormat","escapeString","highlight","maxDepth","min","type","map","item","plugins","ReactElement","stringify","e","createEventsSocketEndpoint","broadcast","wss","WebSocketServer","noServer","verifyClient","origin","startsWith","clients","Map","nextClientId","broadCastEvent","size","serialized","ws","values","send","toString","on","clientWs","clientId","set","onclose","onerror","delete","onmessage","event","command","params","server","reportEvent"],"sources":["../../src/websocket/createEventsSocketEndpoint.ts"],"sourcesContent":["import {Server as WebSocketServer} from 'ws';\nimport {logger} from '@react-native-community/cli-tools';\nimport prettyFormat from 'pretty-format';\n\n/**\n * The eventsSocket websocket listens at the 'events/` for websocket\n * connections, on which all Metro reports will be emitted.\n *\n * This is mostly useful for developer tools (clients) that wants to monitor Metro,\n * and the apps connected to Metro.\n *\n * The eventsSocket provides the following features:\n * - it reports any Metro event (that is reported through a reporter) to all clients\n * - it reports any console.log's (and friends) from the connected app to all clients\n * (as client_log event)\n * - it allows connected clients to send commands through Metro to the connected app.\n * This reuses the generic command mechanism.\n * Two useful commands are 'reload' and 'devmenu'.\n */\n\ntype Command = {\n version: number;\n type: 'command';\n command: string;\n params?: any;\n};\n\n/**\n * This number is used to version the communication protocol between\n * Dev tooling like Flipper and Metro, so that in the future we can recognize\n * messages coming from old clients, so that it will be simpler to implement\n * backward compatibility.\n *\n * We start at 2 as the protocol is currently the same as used internally at FB,\n * which happens to be at version 2 as well.\n */\nconst PROTOCOL_VERSION = 2;\n\nfunction parseMessage(data: string): T | undefined {\n try {\n const message = JSON.parse(data);\n if (message.version === PROTOCOL_VERSION) {\n return message;\n }\n logger.error(\n 'Received message had wrong protocol version: ' + message.version,\n );\n } catch {\n logger.error('Failed to parse the message as JSON:\\n' + data);\n }\n return undefined;\n}\n\n/**\n * Two types of messages will arrive in this function,\n * 1) messages generated by Metro itself (through the reporter abstraction)\n * those are yet to be serialized, and can contain any kind of data structure\n * 2) a specific event generated by Metro is `client_log`, which describes\n * console.* calls in the app.\n * The arguments send to the console are pretty printed so that they can be\n * displayed in a nicer way in dev tools\n *\n * @param message\n */\nfunction serializeMessage(message: any) {\n // We do want to send Metro report messages, but their contents is not guaranteed to be serializable.\n // For some known types we will pretty print otherwise not serializable parts first:\n let toSerialize = message;\n if (message && message.error && message.error instanceof Error) {\n toSerialize = {\n ...message,\n error: prettyFormat(message.error, {\n escapeString: true,\n highlight: true,\n maxDepth: 3,\n min: true,\n }),\n };\n } else if (message && message.type === 'client_log') {\n toSerialize = {\n ...message,\n data: message.data.map((item: any) =>\n typeof item === 'string'\n ? item\n : prettyFormat(item, {\n escapeString: true,\n highlight: true,\n maxDepth: 3,\n min: true,\n plugins: [prettyFormat.plugins.ReactElement],\n }),\n ),\n };\n }\n try {\n return JSON.stringify(toSerialize);\n } catch (e) {\n logger.error('Failed to serialize: ' + e);\n return null;\n }\n}\n\n/**\n * Starts the eventsSocket at the given path\n *\n */\nexport default function createEventsSocketEndpoint(\n broadcast: (method: string, params?: Record) => void,\n): {\n server: WebSocketServer;\n reportEvent: (event: any) => void;\n} {\n const wss = new WebSocketServer({\n noServer: true,\n verifyClient({origin}: {origin: string}) {\n // This exposes the full JS logs and enables issuing commands like reload\n // so let's make sure only locally running stuff can connect to it\n // origin is only checked if it is set, e.g. when the request is made from a (CORS) browser\n // any 'back-end' connection isn't CORS at all, and has full control over the origin header,\n // so there is no point in checking it security wise\n return (\n !origin ||\n origin.startsWith('http://localhost:') ||\n origin.startsWith('file:')\n );\n },\n });\n\n const clients = new Map();\n let nextClientId = 0;\n\n /**\n * broadCastEvent is called by reportEvent (below), which is called by the\n * default reporter of this server, to make sure that all Metro events are\n * broadcasted to all connected clients\n * (that is, all devtools such as Flipper, _not_: connected apps)\n *\n * @param message\n */\n function broadCastEvent(message: any) {\n if (!clients.size) {\n return;\n }\n const serialized = serializeMessage(message);\n if (!serialized) {\n return;\n }\n for (const ws of clients.values()) {\n try {\n ws.send(serialized);\n } catch (e) {\n logger.error(\n `Failed to send broadcast to client due to:\\n ${(e as any).toString()}`,\n );\n }\n }\n }\n\n wss.on('connection', function (clientWs) {\n const clientId = `client#${nextClientId++}`;\n\n clients.set(clientId, clientWs);\n\n clientWs.onclose = clientWs.onerror = () => {\n clients.delete(clientId);\n };\n\n clientWs.onmessage = (event) => {\n const message: Command | undefined = parseMessage(event.data.toString());\n if (message == null) {\n return;\n }\n if (message.type === 'command') {\n try {\n /**\n * messageSocket.broadcast (not to be confused with our own broadcast above)\n * forwards a command to all connected React Native applications.\n */\n broadcast(message.command, message.params);\n } catch (e) {\n logger.error('Failed to forward message to clients: ', e as any);\n }\n } else {\n logger.error('Unknown message type: ', message.type);\n }\n };\n });\n\n return {\n server: wss,\n reportEvent: (event: any) => {\n broadCastEvent(event);\n },\n };\n}\n"],"mappings":"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"}