1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow strict-local
    
  8.  */
    
  9. 
    
  10. import type {LoggerEvent} from 'react-devtools-shared/src/Logger';
    
  11. 
    
  12. import {registerEventLogger} from 'react-devtools-shared/src/Logger';
    
  13. import {enableLogger} from 'react-devtools-feature-flags';
    
  14. 
    
  15. let currentLoggingIFrame = null;
    
  16. let currentSessionId = null;
    
  17. let missedEvents: Array<LoggerEvent> = [];
    
  18. 
    
  19. type LoggerContext = {
    
  20.   page_url: ?string,
    
  21. };
    
  22. 
    
  23. export function registerDevToolsEventLogger(
    
  24.   surface: string,
    
  25.   fetchAdditionalContext?:
    
  26.     | (() => LoggerContext)
    
  27.     | (() => Promise<LoggerContext>),
    
  28. ): void {
    
  29.   async function logEvent(event: LoggerEvent) {
    
  30.     if (enableLogger) {
    
  31.       if (currentLoggingIFrame != null && currentSessionId != null) {
    
  32.         const {metadata, ...eventWithoutMetadata} = event;
    
  33.         const additionalContext: LoggerContext | {} =
    
  34.           fetchAdditionalContext != null ? await fetchAdditionalContext() : {};
    
  35. 
    
  36.         currentLoggingIFrame?.contentWindow?.postMessage(
    
  37.           {
    
  38.             source: 'react-devtools-logging',
    
  39.             event: eventWithoutMetadata,
    
  40.             context: {
    
  41.               ...additionalContext,
    
  42.               metadata: metadata != null ? JSON.stringify(metadata) : '',
    
  43.               session_id: currentSessionId,
    
  44.               surface,
    
  45.               version: process.env.DEVTOOLS_VERSION,
    
  46.             },
    
  47.           },
    
  48.           '*',
    
  49.         );
    
  50.       } else {
    
  51.         missedEvents.push(event);
    
  52.       }
    
  53.     }
    
  54.   }
    
  55. 
    
  56.   function handleLoggingIFrameLoaded(iframe: HTMLIFrameElement) {
    
  57.     currentLoggingIFrame = iframe;
    
  58. 
    
  59.     if (missedEvents.length > 0) {
    
  60.       missedEvents.forEach(event => logEvent(event));
    
  61.       missedEvents = [];
    
  62.     }
    
  63.   }
    
  64. 
    
  65.   // If logger is enabled, register a logger that captures logged events
    
  66.   // and render iframe where the logged events will be reported to
    
  67.   if (enableLogger) {
    
  68.     const loggingUrl = process.env.LOGGING_URL;
    
  69.     const body = document.body;
    
  70. 
    
  71.     if (
    
  72.       typeof loggingUrl === 'string' &&
    
  73.       loggingUrl.length > 0 &&
    
  74.       body != null &&
    
  75.       currentLoggingIFrame == null
    
  76.     ) {
    
  77.       registerEventLogger(logEvent);
    
  78.       currentSessionId = window.crypto.randomUUID();
    
  79. 
    
  80.       const iframe = document.createElement('iframe');
    
  81. 
    
  82.       iframe.onload = () => handleLoggingIFrameLoaded(iframe);
    
  83.       iframe.src = loggingUrl;
    
  84. 
    
  85.       body.appendChild(iframe);
    
  86.     }
    
  87.   }
    
  88. }