UNPKG

react-native-devtools-sync

Version:

A tool for syncing React Query state to an external Dev Tools

459 lines 19.5 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { log } from './utils/logger'; /** * Helper function to send network request data to the dashboard * * This function should be called by your network interceptors when a request is made, * when a response is received, or when an error occurs. * * @param socket The socket.io client instance * @param request The network request data to send * @param persistentDeviceId The unique identifier for this device * @param enableLogs Whether to enable logging */ export function sendNetworkRequest(socket, request, persistentDeviceId, enableLogs = false) { if (!socket) { log('Cannot send network request - socket not connected', enableLogs, 'warn'); return; } if (!persistentDeviceId) { log('Cannot send network request - no persistent device ID', enableLogs, 'warn'); return; } const message = { type: 'network-request-sync', request, persistentDeviceId, }; log(`Sending network request: ${request.type} ${request.method || ''} ${request.url}`, enableLogs); socket.emit('network-request-sync', message); } /** * Example implementation of fetch interceptor * * This is an example of how you might implement a fetch interceptor * to capture network requests and send them to the dashboard. * * @param socket The socket.io client instance * @param persistentDeviceId The unique identifier for this device * @param enableLogs Whether to enable logging */ export function setupFetchInterceptor(socket, persistentDeviceId, enableLogs = false) { if (!socket) { log('Cannot set up fetch interceptor - socket not connected', enableLogs, 'warn'); return () => { }; } if (!persistentDeviceId) { log('Cannot set up fetch interceptor - no persistent device ID', enableLogs, 'warn'); return () => { }; } // Store the original fetch function const originalFetch = global.fetch; // Replace the global fetch with our interceptor global.fetch = (input, init) => __awaiter(this, void 0, void 0, function* () { // Generate a unique ID for this request const requestId = Math.random().toString(36).substring(2, 15); // Get the URL string const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url; // Get the request method const method = (init === null || init === void 0 ? void 0 : init.method) || 'GET'; // Create the initial request object const requestStart = Date.now(); const initialRequest = { id: requestId, url, method, type: 'fetch', status: 'pending', startTime: requestStart, persistentDeviceId, }; // Send the initial request to the dashboard sendNetworkRequest(socket, initialRequest, persistentDeviceId, enableLogs); try { // Make the actual fetch request const response = yield originalFetch(input, init); // Clone the response so we can read the body const clonedResponse = response.clone(); // Try to parse the response body let responseBody; const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { try { responseBody = yield clonedResponse.json(); } catch (e) { responseBody = 'Could not parse JSON response'; } } else if (contentType && contentType.includes('text/')) { try { responseBody = yield clonedResponse.text(); } catch (e) { responseBody = 'Could not parse text response'; } } // Create the response headers object const responseHeaders = {}; response.headers.forEach((value, key) => { responseHeaders[key] = value; }); // Create the request headers object if available const requestHeaders = {}; if (init === null || init === void 0 ? void 0 : init.headers) { if (init.headers instanceof Headers) { init.headers.forEach((value, key) => { requestHeaders[key] = value; }); } else if (Array.isArray(init.headers)) { init.headers.forEach(([key, value]) => { requestHeaders[key] = value; }); } else { Object.entries(init.headers).forEach(([key, value]) => { requestHeaders[key] = value; }); } } // Create the updated request object with response data const updatedRequest = Object.assign(Object.assign({}, initialRequest), { status: response.ok ? 'success' : 'error', endTime: Date.now(), duration: Date.now() - requestStart, responseStatus: response.status, responseBody, headers: requestHeaders, responseHeaders, contentType, contentLength: responseBody ? JSON.stringify(responseBody).length : undefined }); // Send the updated request to the dashboard sendNetworkRequest(socket, updatedRequest, persistentDeviceId, enableLogs); // Return the original response return response; } catch (error) { // Create the error request object const errorRequest = Object.assign(Object.assign({}, initialRequest), { status: 'error', endTime: Date.now(), duration: Date.now() - requestStart, responseBody: error instanceof Error ? error.message : String(error) }); // Send the error request to the dashboard sendNetworkRequest(socket, errorRequest, persistentDeviceId, enableLogs); // Re-throw the error throw error; } }); // Return a function to restore the original fetch return () => { global.fetch = originalFetch; }; } /** * Example implementation of XMLHttpRequest interceptor * * This is an example of how you might implement an XMLHttpRequest interceptor * to capture network requests and send them to the dashboard. * * @param socket The socket.io client instance * @param persistentDeviceId The unique identifier for this device * @param enableLogs Whether to enable logging */ export function setupXHRInterceptor(socket, persistentDeviceId, enableLogs = false) { if (!socket) { log('Cannot set up XHR interceptor - socket not connected', enableLogs, 'warn'); return () => { }; } if (!persistentDeviceId) { log('Cannot set up XHR interceptor - no persistent device ID', enableLogs, 'warn'); return () => { }; } // Store the original XMLHttpRequest const OriginalXHR = global.XMLHttpRequest; // Create a new XMLHttpRequest constructor that wraps the original function InterceptedXHR() { const xhr = new OriginalXHR(); const requestId = Math.random().toString(36).substring(2, 15); let method = ''; let url = ''; let requestStart = 0; let requestHeaders = {}; let requestBody; // Override the open method to capture the method and URL const originalOpen = xhr.open; xhr.open = function (method_, url_, ...args) { method = method_; url = url_; return originalOpen.apply(xhr, [method_, url_, ...args]); }; // Override the setRequestHeader method to capture request headers const originalSetRequestHeader = xhr.setRequestHeader; xhr.setRequestHeader = function (key, value) { requestHeaders[key] = value; return originalSetRequestHeader.apply(xhr, [key, value]); }; // Override the send method to capture the request body and start time const originalSend = xhr.send; xhr.send = function (body) { requestStart = Date.now(); requestBody = body; // Create the initial request object const initialRequest = { id: requestId, url, method, type: 'xhr', status: 'pending', startTime: requestStart, persistentDeviceId, headers: requestHeaders, requestBody, }; // Send the initial request to the dashboard sendNetworkRequest(socket, initialRequest, persistentDeviceId, enableLogs); return originalSend.apply(xhr, [body]); }; // Listen for the load event to capture the response xhr.addEventListener('load', function () { // Try to parse the response body let responseBody; const contentType = xhr.getResponseHeader('content-type'); if (contentType && contentType.includes('application/json')) { try { responseBody = JSON.parse(xhr.responseText); } catch (e) { responseBody = 'Could not parse JSON response'; } } else { console.log(xhr); responseBody = xhr.responseText; } // Create the response headers object const responseHeaders = {}; const allHeaders = xhr.getAllResponseHeaders(); const headerLines = allHeaders.split('\r\n'); headerLines.forEach(line => { const parts = line.split(': '); if (parts.length === 2) { responseHeaders[parts[0]] = parts[1]; } }); // Create the updated request object with response data const updatedRequest = { id: requestId, url, method, type: 'xhr', status: xhr.status >= 200 && xhr.status < 300 ? 'success' : 'error', startTime: requestStart, endTime: Date.now(), duration: Date.now() - requestStart, persistentDeviceId, headers: requestHeaders, requestBody, responseStatus: xhr.status, responseBody, responseHeaders, contentType, contentLength: responseBody ? JSON.stringify(responseBody).length : undefined, }; // Send the updated request to the dashboard sendNetworkRequest(socket, updatedRequest, persistentDeviceId, enableLogs); }); // Listen for the error event to capture errors xhr.addEventListener('error', function () { // Create the error request object const errorRequest = { id: requestId, url, method, type: 'xhr', status: 'error', startTime: requestStart, endTime: Date.now(), duration: Date.now() - requestStart, persistentDeviceId, headers: requestHeaders, requestBody, responseBody: 'Network error', }; // Send the error request to the dashboard sendNetworkRequest(socket, errorRequest, persistentDeviceId, enableLogs); }); return xhr; } // Replace the global XMLHttpRequest with our interceptor global.XMLHttpRequest = InterceptedXHR; // Return a function to restore the original XMLHttpRequest return () => { global.XMLHttpRequest = OriginalXHR; }; } /** * Example implementation of WebSocket interceptor * * This is an example of how you might implement a WebSocket interceptor * to capture WebSocket messages and send them to the dashboard. * * @param socket The socket.io client instance * @param persistentDeviceId The unique identifier for this device * @param enableLogs Whether to enable logging */ export function setupWebSocketInterceptor(socket, persistentDeviceId, enableLogs = false) { if (!socket) { log('Cannot set up WebSocket interceptor - socket not connected', enableLogs, 'warn'); return () => { }; } if (!persistentDeviceId) { log('Cannot set up WebSocket interceptor - no persistent device ID', enableLogs, 'warn'); return () => { }; } // Store the original WebSocket const OriginalWebSocket = global.WebSocket; // Create a new WebSocket constructor that wraps the original function InterceptedWebSocket(url, protocols) { const ws = new OriginalWebSocket(url, protocols); const connectionId = Math.random().toString(36).substring(2, 15); const connectionStart = Date.now(); // Create the initial connection request const initialRequest = { id: connectionId, url, type: 'websocket', status: 'pending', startTime: connectionStart, persistentDeviceId, direction: 'sent', }; // Send the initial connection request to the dashboard sendNetworkRequest(socket, initialRequest, persistentDeviceId, enableLogs); // Listen for the open event ws.addEventListener('open', function () { // Create the open connection request const openRequest = Object.assign(Object.assign({}, initialRequest), { status: 'success', endTime: Date.now(), duration: Date.now() - connectionStart }); // Send the open connection request to the dashboard sendNetworkRequest(socket, openRequest, persistentDeviceId, enableLogs); }); // Listen for the message event ws.addEventListener('message', function (event) { // Create a unique ID for this message const messageId = `${connectionId}-${Math.random().toString(36).substring(2, 15)}`; // Try to parse the message data let messageData; try { messageData = JSON.parse(event.data); } catch (e) { messageData = event.data; } // Create the message request const messageRequest = { id: messageId, url, type: 'websocket', status: 'success', startTime: Date.now(), persistentDeviceId, direction: 'received', data: messageData, }; // Send the message request to the dashboard sendNetworkRequest(socket, messageRequest, persistentDeviceId, enableLogs); }); // Override the send method to capture outgoing messages const originalSend = ws.send; ws.send = function (data) { // Create a unique ID for this message const messageId = `${connectionId}-${Math.random().toString(36).substring(2, 15)}`; // Try to parse the message data let messageData; if (typeof data === 'string') { try { messageData = JSON.parse(data); } catch (e) { messageData = data; } } else { messageData = data; } // Create the message request const messageRequest = { id: messageId, url, type: 'websocket', status: 'success', startTime: Date.now(), persistentDeviceId, direction: 'sent', data: messageData, }; // Send the message request to the dashboard sendNetworkRequest(socket, messageRequest, persistentDeviceId, enableLogs); return originalSend.apply(ws, [data]); }; // Listen for the close event ws.addEventListener('close', function () { // Create the close connection request const closeRequest = { id: `${connectionId}-close`, url, type: 'websocket', status: 'success', startTime: Date.now(), persistentDeviceId, direction: 'sent', data: { type: 'connection-closed' }, }; // Send the close connection request to the dashboard sendNetworkRequest(socket, closeRequest, persistentDeviceId, enableLogs); }); // Listen for the error event ws.addEventListener('error', function (event) { // Create the error connection request const errorRequest = { id: `${connectionId}-error`, url, type: 'websocket', status: 'error', startTime: Date.now(), persistentDeviceId, direction: 'received', data: { type: 'connection-error', error: 'WebSocket connection error' }, }; // Send the error connection request to the dashboard sendNetworkRequest(socket, errorRequest, persistentDeviceId, enableLogs); }); return ws; } // Replace the global WebSocket with our interceptor global.WebSocket = InterceptedWebSocket; // Return a function to restore the original WebSocket return () => { global.WebSocket = OriginalWebSocket; }; } /** * Set up all network interceptors * * This function sets up interceptors for fetch, XMLHttpRequest, and WebSocket * to capture network requests and send them to the dashboard. * * @param socket The socket.io client instance * @param persistentDeviceId The unique identifier for this device * @param enableLogs Whether to enable logging */ export function setupNetworkInterceptors(socket, persistentDeviceId, enableLogs = false) { const removeFetchInterceptor = setupFetchInterceptor(socket, persistentDeviceId, enableLogs); const removeXHRInterceptor = setupXHRInterceptor(socket, persistentDeviceId, enableLogs); const removeWebSocketInterceptor = setupWebSocketInterceptor(socket, persistentDeviceId, enableLogs); // Return a function to remove all interceptors return () => { removeFetchInterceptor(); removeXHRInterceptor(); removeWebSocketInterceptor(); }; } //# sourceMappingURL=sendNetworkRequest.js.map