eventsSocketServer.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. function _ws() {
  7. const data = require("ws");
  8. _ws = function () {
  9. return data;
  10. };
  11. return data;
  12. }
  13. function _cliTools() {
  14. const data = require("@react-native-community/cli-tools");
  15. _cliTools = function () {
  16. return data;
  17. };
  18. return data;
  19. }
  20. function _prettyFormat() {
  21. const data = _interopRequireDefault(require("pretty-format"));
  22. _prettyFormat = function () {
  23. return data;
  24. };
  25. return data;
  26. }
  27. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  28. /**
  29. * This number is used to version the communication protocol between
  30. * Dev tooling like Flipper and Metro, so that in the future we can recognize
  31. * messages coming from old clients, so that it will be simpler to implement
  32. * backward compatibility.
  33. *
  34. * We start at 2 as the protocol is currently the same as used internally at FB,
  35. * which happens to be at version 2 as well.
  36. */
  37. const PROTOCOL_VERSION = 2;
  38. function parseMessage(data) {
  39. try {
  40. const message = JSON.parse(data);
  41. if (message.version === PROTOCOL_VERSION) {
  42. return message;
  43. }
  44. _cliTools().logger.error('Received message had wrong protocol version: ' + message.version);
  45. } catch (_unused) {
  46. _cliTools().logger.error('Failed to parse the message as JSON:\n' + data);
  47. }
  48. return undefined;
  49. }
  50. /**
  51. * Two types of messages will arrive in this function,
  52. * 1) messages generated by Metro itself (through the reporter abstraction)
  53. * those are yet to be serialized, and can contain any kind of data structure
  54. * 2) a specific event generated by Metro is `client_log`, which describes
  55. * console.* calls in the app.
  56. * The arguments send to the console are pretty printed so that they can be
  57. * displayed in a nicer way in dev tools
  58. *
  59. * @param message
  60. */
  61. function serializeMessage(message) {
  62. // We do want to send Metro report messages, but their contents is not guaranteed to be serializable.
  63. // For some known types we will pretty print otherwise not serializable parts first:
  64. let toSerialize = message;
  65. if (message && message.error && message.error instanceof Error) {
  66. toSerialize = { ...message,
  67. error: (0, _prettyFormat().default)(message.error, {
  68. escapeString: true,
  69. highlight: true,
  70. maxDepth: 3,
  71. min: true
  72. })
  73. };
  74. } else if (message && message.type === 'client_log') {
  75. toSerialize = { ...message,
  76. data: message.data.map(item => typeof item === 'string' ? item : (0, _prettyFormat().default)(item, {
  77. escapeString: true,
  78. highlight: true,
  79. maxDepth: 3,
  80. min: true,
  81. plugins: [_prettyFormat().default.plugins.ReactElement]
  82. }))
  83. };
  84. }
  85. try {
  86. return JSON.stringify(toSerialize);
  87. } catch (e) {
  88. _cliTools().logger.error('Failed to serialize: ' + e);
  89. return null;
  90. }
  91. }
  92. /**
  93. * Starts the eventsSocket at the given path
  94. *
  95. * @param server
  96. * @param path typically: 'events/'
  97. * @param messageSocket: webSocket to which all connected RN apps are listening
  98. */
  99. function attachToServer(server, path, messageSocket) {
  100. const wss = new (_ws().Server)({
  101. server: server,
  102. path: path,
  103. verifyClient({
  104. origin
  105. }) {
  106. // This exposes the full JS logs and enables issuing commands like reload
  107. // so let's make sure only locally running stuff can connect to it
  108. return origin.startsWith('http://localhost:') || origin.startsWith('file:');
  109. }
  110. });
  111. const clients = new Map();
  112. let nextClientId = 0;
  113. /**
  114. * broadCastEvent is called by reportEvent (below), which is called by the
  115. * default reporter of this server, to make sure that all Metro events are
  116. * broadcasted to all connected clients
  117. * (that is, all devtools such as Flipper, _not_: connected apps)
  118. *
  119. * @param message
  120. */
  121. function broadCastEvent(message) {
  122. if (!clients.size) {
  123. return;
  124. }
  125. const serialized = serializeMessage(message);
  126. if (!serialized) {
  127. return;
  128. }
  129. for (const ws of clients.values()) {
  130. try {
  131. ws.send(serialized);
  132. } catch (e) {
  133. _cliTools().logger.error(`Failed to send broadcast to client due to:\n ${e.toString()}`);
  134. }
  135. }
  136. }
  137. wss.on('connection', function (clientWs) {
  138. const clientId = `client#${nextClientId++}`;
  139. clients.set(clientId, clientWs);
  140. clientWs.onclose = clientWs.onerror = () => {
  141. clients.delete(clientId);
  142. };
  143. clientWs.onmessage = event => {
  144. const message = parseMessage(event.data.toString());
  145. if (message == null) {
  146. return;
  147. }
  148. if (message.type === 'command') {
  149. try {
  150. /**
  151. * messageSocket.broadcast (not to be confused with our own broadcast above)
  152. * forwards a command to all connected React Native applications.
  153. */
  154. messageSocket.broadcast(message.command, message.params);
  155. } catch (e) {
  156. _cliTools().logger.error('Failed to forward message to clients: ', e);
  157. }
  158. } else {
  159. _cliTools().logger.error('Unknown message type: ', message.type);
  160. }
  161. };
  162. });
  163. return {
  164. reportEvent: event => {
  165. broadCastEvent(event);
  166. }
  167. };
  168. }
  169. var _default = {
  170. attachToServer
  171. };
  172. exports.default = _default;
  173. //# sourceMappingURL=eventsSocketServer.js.map