diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..375d784 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +*.bundle.js \ No newline at end of file diff --git a/README.md b/README.md index c08aff0..287df40 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,13 @@ npm install npm run pod npm run start npm run macos -# for release builds +# for release builds (automatically bundles the server) npm run macos-release ``` -### Windows Development +**Note:** The Reactotron server is now embedded in the macOS app and starts automatically on port 9292. No need to run a separate server process! + +### Windows Development #### System Requirements @@ -69,14 +71,26 @@ Both platforms use unified commands for native module development: See [Making a TurboModule](./docs/Making-a-TurboModule.md) for detailed native development instructions. +### Server Bundle + +If you modify the standalone server code (`standalone-server.js`), rebuild the bundle: + +```sh +npm run bundle-server +``` + +The bundle is automatically generated during release builds (`npm run macos-release`). + ## Enabling Reactotron in your app > [!NOTE] > We don't have a simple way to integrate the new Reactotron-macOS into your app yet, but that will be coming at some point. This assumes you've cloned down Reactotron-macOS. -1. From the root of Reactotron-macOS, start the standalone relay server: - `node -e "require('./standalone-server').startReactotronServer({ port: 9292 })"` -2. In your app, add the following to your app.tsx: +The Reactotron server is now embedded in the macOS app and starts automatically when you launch it. Simply: + +1. Run the Reactotron macOS app (via `npm run macos` or the built .app) +2. The server will automatically start on port 9292 +3. In your app, add the following to your app.tsx: ```tsx if (__DEV__) { @@ -92,7 +106,26 @@ if (__DEV__) { } ``` -3. Start your app and Reactotron-macOS. You should see logs appear. +4. Start your app and you should see logs appear in Reactotron. + +### Running the Server Standalone (Optional) + +If you need to run the server without the GUI (for CI/CD or headless environments), you can still run: + +```sh +node -e "require('./standalone-server').startReactotronServer({ port: 9292 })" +``` + +### Server Implementation Details + +The embedded server: + +- Starts automatically when the app launches +- Stops automatically when the app quits +- Runs on port 9292 by default (configurable in `AppDelegate.mm`) +- Is bundled as a single file with all dependencies +- Requires Node.js to be installed on the system +- Supports nvm, asdf, fnm, and other Node version managers ## Get Help diff --git a/app/app.tsx b/app/app.tsx index cc54478..c3a0041 100644 --- a/app/app.tsx +++ b/app/app.tsx @@ -20,6 +20,7 @@ import { TimelineItem } from "./types" import { PortalHost } from "./components/Portal" import { StateScreen } from "./screens/StateScreen" import { AboutModal } from "./components/AboutModal" +import { useReactotronServer } from "./state/useReactotronServer" if (__DEV__) { // This is for debugging Reactotron with ... Reactotron! @@ -107,6 +108,9 @@ function App(): React.JSX.Element { useMenuItem(menuConfig) + // Start the bundled Reactotron server + useReactotronServer() + setTimeout(() => { fetch("https://www.google.com") .then((res) => res.text()) diff --git a/app/components/Sidebar/SidebarMenu.tsx b/app/components/Sidebar/SidebarMenu.tsx index 97e4a7e..8f45523 100644 --- a/app/components/Sidebar/SidebarMenu.tsx +++ b/app/components/Sidebar/SidebarMenu.tsx @@ -1,6 +1,7 @@ import { Animated, View, ViewStyle, Pressable, TextStyle, Text } from "react-native" import { themed, useTheme, useThemeName } from "../../theme/theme" import { useGlobal } from "../../state/useGlobal" +import { manualReconnect } from "../../state/connectToServer" import { Icon } from "../Icon" const MENU_ITEMS = [ @@ -24,6 +25,8 @@ export const SidebarMenu = ({ progress, mounted, collapsedWidth }: SidebarMenuPr const theme = useTheme() const [themeName, setTheme] = useThemeName() const [isConnected] = useGlobal("isConnected", false) + const [connectionStatus] = useGlobal("connectionStatus", "Disconnected") + const [clientIds] = useGlobal("clientIds", []) const [error] = useGlobal("error", null) const arch = (global as any)?.nativeFabricUIManager ? "Fabric" : "Paper" @@ -92,7 +95,12 @@ export const SidebarMenu = ({ progress, mounted, collapsedWidth }: SidebarMenuPr })} - + [$menuItem(), $statusItemContainer, pressed && $menuItemPressed]} + onPress={manualReconnect} + accessibilityRole="button" + accessibilityLabel={`Connection status: ${connectionStatus}. Tap to retry.`} + > {mounted && ( - - Connection - + + + {connectionStatus} + + {isConnected && clientIds.length === 0 && ( + Port 9292 + )} + )} - + (({ colors }) => ({ fontWeight: "600", marginLeft: -4, })) -const $statusItemContainer: ViewStyle = { cursor: "default", height: 32 } +const $connectionStatusText = themed(() => ({ + fontSize: 11, + lineHeight: 14, +})) +const $helpText = themed(({ colors }) => ({ + fontSize: 10, + lineHeight: 12, + color: colors.neutral, + marginTop: 2, +})) +const $connectionContainer: ViewStyle = { flex: 1 } +const $statusItemContainer: ViewStyle = { cursor: "pointer", minHeight: 32 } diff --git a/app/native/IRReactotronServer/IRReactotronServer.h b/app/native/IRReactotronServer/IRReactotronServer.h new file mode 100644 index 0000000..53c314f --- /dev/null +++ b/app/native/IRReactotronServer/IRReactotronServer.h @@ -0,0 +1,8 @@ +// Generally, this file can be left alone as-is. +// Make your changes in the IRReactotronServer.mm file. +#import +#import + +@interface IRReactotronServer : NativeIRReactotronServerSpecBase +@end + diff --git a/app/native/IRReactotronServer/IRReactotronServer.mm b/app/native/IRReactotronServer/IRReactotronServer.mm new file mode 100644 index 0000000..138c859 --- /dev/null +++ b/app/native/IRReactotronServer/IRReactotronServer.mm @@ -0,0 +1,25 @@ +#import "IRReactotronServer.h" + +@implementation IRReactotronServer RCT_EXPORT_MODULE() + +/** + * Returns the path to the bundled Reactotron server script. + */ +- (NSString *)getBundlePath { + NSURL *bundleURL = [[NSBundle mainBundle] URLForResource:@"standalone-server.bundle" withExtension:@"js"]; + if (bundleURL) { + NSLog(@"✓ Found Reactotron server bundle at: %@", bundleURL.path); + return bundleURL.path; + } + + NSLog(@"⚠️ Reactotron server bundle not found in main bundle"); + return nil; +} + +// Required by TurboModules. +- (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { + return std::make_shared(params); +} + +@end + diff --git a/app/native/IRReactotronServer/NativeIRReactotronServer.ts b/app/native/IRReactotronServer/NativeIRReactotronServer.ts new file mode 100644 index 0000000..624e9ac --- /dev/null +++ b/app/native/IRReactotronServer/NativeIRReactotronServer.ts @@ -0,0 +1,8 @@ +import type { TurboModule } from "react-native" +import { TurboModuleRegistry } from "react-native" + +export interface Spec extends TurboModule { + getBundlePath(): string | null +} + +export default TurboModuleRegistry.getEnforcing("IRReactotronServer") diff --git a/app/native/IRRunShellCommand/IRRunShellCommand.mm b/app/native/IRRunShellCommand/IRRunShellCommand.mm index 923e6c9..d1590e1 100644 --- a/app/native/IRRunShellCommand/IRRunShellCommand.mm +++ b/app/native/IRRunShellCommand/IRRunShellCommand.mm @@ -7,6 +7,8 @@ #import "IRRunShellCommand.h" #import +#import +#import @interface IRRunShellCommand () @@ -44,6 +46,23 @@ - (NSNumber *)appPID { return [NSNumber numberWithInteger:[[NSProcessInfo processInfo] processIdentifier]]; } +/** + * Returns the user's default shell from system. + */ +- (NSString *)getUserShell { + // Get user's default shell from system + struct passwd *pw = getpwuid(getuid()); + if (pw && pw->pw_shell) { + NSString *detectedShell = [NSString stringWithUTF8String:pw->pw_shell]; + NSLog(@"✓ Detected user shell from passwd: %@", detectedShell); + return detectedShell; + } + + // Fallback to zsh (macOS default) + NSLog(@"⚠️ Failed to detect user shell, falling back to /bin/zsh"); + return @"/bin/zsh"; +} + /** * This method runs a command and returns the output as a string. * It's async, so use it for long-running commands. @@ -67,17 +86,65 @@ - (NSString *)runSync:(NSString *)command { /* * Executes a shell command asynchronously on a background queue. * Captures both stdout and stderr streams, and emits events with their output and completion status. + * + * If options.shell is provided, wraps the command in a shell with optional shellArgs. */ - (void)runTaskWithCommand:(NSString *)command args:(NSArray *)args - taskId:(NSString *)taskId { + options:(JS::NativeIRRunShellCommand::SpecRunTaskWithCommandOptions &)options { + NSString *taskId = options.taskId(); + NSString *shell = options.shell(); + NSArray *shellArgs = nil; + auto optionalShellArgs = options.shellArgs(); + if (optionalShellArgs.has_value()) { + auto lazyVector = optionalShellArgs.value(); + NSMutableArray *tempArray = [NSMutableArray array]; + for (size_t i = 0; i < lazyVector.size(); i++) { + [tempArray addObject:lazyVector[i]]; + } + shellArgs = [tempArray copy]; + } + dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @autoreleasepool { NSTask *task = [NSTask new]; - task.executableURL = [NSURL fileURLWithPath:command]; - task.arguments = args; + + // If shell is provided, wrap the command execution + if (shell && shell.length > 0) { + // Build the command string by joining command with args + NSMutableArray *commandParts = [NSMutableArray arrayWithObject:command]; + [commandParts addObjectsFromArray:args]; + + // Properly escape and quote arguments + NSMutableArray *quotedParts = [NSMutableArray array]; + for (NSString *part in commandParts) { + // Escape single quotes and wrap in single quotes + NSString *escaped = [part stringByReplacingOccurrencesOfString:@"'" withString:@"'\\''"]; + [quotedParts addObject:[NSString stringWithFormat:@"'%@'", escaped]]; + } + NSString *commandString = [quotedParts componentsJoinedByString:@" "]; + + // Set the shell as the executable + task.executableURL = [NSURL fileURLWithPath:shell]; + + // Combine shellArgs with -c and the command string + NSMutableArray *finalArgs = [NSMutableArray array]; + if (shellArgs) { + [finalArgs addObjectsFromArray:shellArgs]; + } + [finalArgs addObject:@"-c"]; + [finalArgs addObject:commandString]; + + task.arguments = finalArgs; + + NSLog(@"Running command with shell (%@): %@ %@", shell, [shellArgs componentsJoinedByString:@" "], commandString); + } else { + // Direct execution without shell + task.executableURL = [NSURL fileURLWithPath:command]; + task.arguments = args; + } [self.tasksLock lock]; self.runningTasks[taskId] = task; diff --git a/app/native/IRRunShellCommand/NativeIRRunShellCommand.ts b/app/native/IRRunShellCommand/NativeIRRunShellCommand.ts index 8695e4e..edb0950 100644 --- a/app/native/IRRunShellCommand/NativeIRRunShellCommand.ts +++ b/app/native/IRRunShellCommand/NativeIRRunShellCommand.ts @@ -16,10 +16,22 @@ export interface ShellCommandCompleteEvent { export interface Spec extends TurboModule { appPath(): string appPID(): number + getUserShell(): string runAsync(command: string): Promise runSync(command: string): string runCommandOnShutdown(command: string): void - runTaskWithCommand(command: string, args: string[], taskId: string): void + runTaskWithCommand( + command: string, + args: string[], + options: { + /** Shell to execute the command with. */ + shell?: string + /** Arguments to pass to the shell like -l and -i. */ + shellArgs?: string[] + /** ID of the task. */ + taskId: string + }, + ): void getRunningTaskIds(): ReadonlyArray killTaskWithId(taskId: string): boolean killAllTasks(): void diff --git a/app/state/connectToServer.ts b/app/state/connectToServer.ts index 1c03b2b..530dae1 100644 --- a/app/state/connectToServer.ts +++ b/app/state/connectToServer.ts @@ -16,11 +16,17 @@ export const getReactotronAppId = () => { return reactotronAppId } +let reconnectAttempts = 0 +let reconnectTimeout: NodeJS.Timeout | null = null +const MAX_RECONNECT_ATTEMPTS = 10 +const INITIAL_RECONNECT_DELAY = 1000 // 1 second + /** * Connects to the reactotron-core-server via websocket. * * Populates the following global state: * - isConnected: boolean + * - connectionStatus: string (Connecting, Connected, Disconnected, Retrying...) * - error: Error | null * - clientIds: string[] * - timelineItems: TimelineItem[] @@ -30,6 +36,7 @@ export const getReactotronAppId = () => { export function connectToServer(props: { port: number } = { port: 9292 }): UnsubscribeFn { const reactotronAppId = getReactotronAppId() const [_c, setIsConnected] = withGlobal("isConnected", false) + const [_cs, setConnectionStatus] = withGlobal("connectionStatus", "Connecting...") const [_e, setError] = withGlobal("error", null) const [clientIds, setClientIds] = withGlobal("clientIds", []) const [, setActiveClientId] = withGlobal("activeClientId", "") @@ -38,11 +45,36 @@ export function connectToServer(props: { port: number } = { port: 9292 }): Unsub [clientId: string]: StateSubscription[] }>("stateSubscriptionsByClientId", {}) - ws.socket = new WebSocket(`ws://localhost:${props.port}`) - if (!ws.socket) throw new Error("Failed to connect to Reactotron server") + // Clear any existing reconnect timeout + if (reconnectTimeout) { + clearTimeout(reconnectTimeout) + reconnectTimeout = null + } + + setConnectionStatus("Connecting...") + setError(null) + + try { + ws.socket = new WebSocket(`ws://localhost:${props.port}`) + } catch (error) { + setError(error as Error) + setConnectionStatus("Failed to connect") + scheduleReconnect(props) + return () => {} + } + + if (!ws.socket) { + setError(new Error("Failed to create WebSocket")) + setConnectionStatus("Failed to connect") + scheduleReconnect(props) + return () => {} + } // Tell the server we are a Reactotron app, not a React client. ws.socket.onopen = () => { + reconnectAttempts = 0 // Reset on successful connection + setConnectionStatus("Connected") + setError(null) ws.socket?.send( JSON.stringify({ type: "reactotron.subscribe", @@ -54,16 +86,25 @@ export function connectToServer(props: { port: number } = { port: 9292 }): Unsub } // Handle errors - ws.socket.onerror = (event) => setError(new Error(`WebSocket error: ${event.message}`)) + ws.socket.onerror = (event) => { + const errorMsg = event.message || "Connection failed" + setError(new Error(`WebSocket error: ${errorMsg}`)) + setConnectionStatus(`Error: ${errorMsg}`) + } // Handle messages coming from the server, intended to be sent to the client or Reactotron app. ws.socket.onmessage = (event) => { const data = JSON.parse(event.data) + console.tron.log("Received message from server:", data.type) - if (data.type === "reactotron.connected") setIsConnected(true) + if (data.type === "reactotron.connected") { + console.tron.log("Reactotron app marked as connected") + setIsConnected(true) + } if (data.type === "connectionEstablished") { const clientId = data?.conn?.clientId + console.tron.log("connectionEstablished event for client:", clientId) if (!clientIds.includes(clientId)) { setClientIds((prev) => [...prev, clientId]) setActiveClientId(clientId) @@ -75,6 +116,12 @@ export function connectToServer(props: { port: number } = { port: 9292 }): Unsub } if (data.type === "connectedClients") { + console.tron.log( + "connectedClients event. Count:", + data.clients?.length, + "Clients:", + data.clients, + ) let newestClientId = "" data.clients.forEach((client: any) => { // Store the client data in global state @@ -89,6 +136,7 @@ export function connectToServer(props: { port: number } = { port: 9292 }): Unsub if (newestClientId) { // Set the active client to the newest client + console.tron.log("Setting active client to:", newestClientId) setActiveClientId(newestClientId) } } @@ -159,8 +207,9 @@ export function connectToServer(props: { port: number } = { port: 9292 }): Unsub } // Clean up after disconnect - ws.socket.onclose = () => { - console.tron.log("Reactotron server disconnected") + ws.socket.onclose = (event) => { + console.tron.log("Reactotron server disconnected", event.code, event.reason) + // Clear individual client data clientIds.forEach((clientId) => { const [_, setClientData] = withGlobal(`client-${clientId}`, {}) @@ -172,6 +221,14 @@ export function connectToServer(props: { port: number } = { port: 9292 }): Unsub setActiveClientId("") setTimelineItems([]) setStateSubscriptionsByClientId({}) + + // Only attempt reconnect if it wasn't a normal close + if (event.code !== 1000) { + setConnectionStatus("Disconnected") + scheduleReconnect(props) + } else { + setConnectionStatus("Disconnected") + } } // Send a message to the server (which will be forwarded to the client) @@ -181,9 +238,47 @@ export function connectToServer(props: { port: number } = { port: 9292 }): Unsub } return () => { - ws.socket?.close() + if (reconnectTimeout) { + clearTimeout(reconnectTimeout) + reconnectTimeout = null + } + reconnectAttempts = 0 + ws.socket?.close(1000) // Normal closure + ws.socket = null + } +} + +function scheduleReconnect(props: { port: number }) { + if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) { + const [_, setConnectionStatus] = withGlobal("connectionStatus", "") + setConnectionStatus(`Failed after ${MAX_RECONNECT_ATTEMPTS} attempts`) + return + } + + reconnectAttempts++ + const delay = Math.min(INITIAL_RECONNECT_DELAY * Math.pow(2, reconnectAttempts - 1), 30000) + const [_, setConnectionStatus] = withGlobal("connectionStatus", "") + setConnectionStatus( + `Retrying in ${Math.round(delay / 1000)}s... (${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`, + ) + + reconnectTimeout = setTimeout(() => { + console.tron.log(`Reconnecting... attempt ${reconnectAttempts}`) + connectToServer(props) + }, delay) +} + +export function manualReconnect() { + reconnectAttempts = 0 + if (reconnectTimeout) { + clearTimeout(reconnectTimeout) + reconnectTimeout = null + } + if (ws.socket) { + ws.socket.close(1000) ws.socket = null } + connectToServer() } export function sendToClient(message: string | object, payload?: object, clientId?: string) { diff --git a/app/state/useGlobal.ts b/app/state/useGlobal.ts index c2b20e4..1badd6a 100644 --- a/app/state/useGlobal.ts +++ b/app/state/useGlobal.ts @@ -113,7 +113,7 @@ function buildSetValue(id: string, persist: boolean) { } export function deleteGlobal(id: string): void { - delete globals[id] + delete _globals[id] } /** diff --git a/app/state/useReactotronServer.ts b/app/state/useReactotronServer.ts new file mode 100644 index 0000000..31ee94d --- /dev/null +++ b/app/state/useReactotronServer.ts @@ -0,0 +1,89 @@ +import { useEffect, useRef } from "react" +import IRRunShellCommand, { + type ShellCommandOutputEvent, + type ShellCommandCompleteEvent, +} from "../native/IRRunShellCommand/NativeIRRunShellCommand" +import IRReactotronServer from "../native/IRReactotronServer/NativeIRReactotronServer" + +const SERVER_TASK_ID = "reactotron-server" +const DEFAULT_PORT = "9292" + +/** + * Hook to manage the Reactotron server lifecycle. + * Starts the server when the component mounts and stops it when it unmounts. + */ +export function useReactotronServer(port: string = DEFAULT_PORT) { + const serverStartedRef = useRef(false) + + useEffect(() => { + // Prevent double-start in development mode (React strict mode) + if (serverStartedRef.current) { + return + } + + const startServer = () => { + try { + // Get the bundled server path + const bundlePath = IRReactotronServer.getBundlePath() + if (!bundlePath) { + console.error( + "❌ Reactotron server bundle not found. Run 'npm run bundle-server' to generate it.", + ) + return + } + + // Get the user's shell for proper PATH resolution + const shellPath = IRRunShellCommand.getUserShell() + console.log(`Starting Reactotron server with shell: ${shellPath}`) + + // Start the server using runTaskWithCommand with shell support + IRRunShellCommand.runTaskWithCommand("node", [bundlePath, "--port", port], { + shell: shellPath, + shellArgs: ["-l", "-i"], // Login shell flags to load PATH + taskId: SERVER_TASK_ID, + }) + + serverStartedRef.current = true + console.log(`✅ Reactotron server starting on port ${port}`) + } catch (error) { + console.error("❌ Failed to start Reactotron server:", error) + } + } + + // Subscribe to server output events + const outputSubscription = IRRunShellCommand.onShellCommandOutput( + (event: ShellCommandOutputEvent) => { + if (event.taskId === SERVER_TASK_ID) { + const prefix = event.type === "stderr" ? "[Server Error]" : "[Server]" + console.log(`${prefix} ${event.output.trim()}`) + } + }, + ) + + // Subscribe to server completion events + const completeSubscription = IRRunShellCommand.onShellCommandComplete( + (event: ShellCommandCompleteEvent) => { + if (event.taskId === SERVER_TASK_ID) { + console.log(`Reactotron server exited with code: ${event.exitCode}`) + serverStartedRef.current = false + } + }, + ) + + // Start the server + startServer() + + // Cleanup function + return () => { + outputSubscription.remove() + completeSubscription.remove() + + // Kill the server task when the component unmounts + if (serverStartedRef.current) { + console.log("Stopping Reactotron server...") + IRRunShellCommand.killTaskWithId(SERVER_TASK_ID) + serverStartedRef.current = false + } + } + }, [port]) +} diff --git a/macos/Podfile.lock b/macos/Podfile.lock index cac1465..2badff4 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1836,7 +1836,7 @@ SPEC CHECKSUMS: React-timing: 756815d960e39ff0d09ff9c7cb7159299edf0169 React-utils: 0ed154e9b14a89a2d5c962ec00cdbad9f02ae0b8 ReactAppDependencyProvider: 3a460ab76f1149df99ed324bb1c764b76de7a730 - ReactCodegen: 4aab124636ad46be1546ce579d11e864dd1a8bc5 + ReactCodegen: 2df3b1973fbf3802d345e9028b2f2a763d5e9bfb ReactCommon: 15c98590279f1b5ab3436f4bf14c9f0a123df289 SocketRocket: 03f7111df1a343b162bf5b06ead333be808e1e0a Yoga: 187db0b2a37012c01dac36afb886a29b92bfd943 diff --git a/macos/Reactotron-macOS/AppDelegate.mm b/macos/Reactotron-macOS/AppDelegate.mm index ba9fc15..4b95b89 100644 --- a/macos/Reactotron-macOS/AppDelegate.mm +++ b/macos/Reactotron-macOS/AppDelegate.mm @@ -7,6 +7,8 @@ @implementation AppDelegate +#pragma mark - Application Lifecycle + - (void)applicationDidFinishLaunching:(NSNotification *)notification { self.moduleName = @"Reactotron"; diff --git a/macos/Reactotron-macOS/Reactotron.entitlements b/macos/Reactotron-macOS/Reactotron.entitlements index 625af03..40b639e 100644 --- a/macos/Reactotron-macOS/Reactotron.entitlements +++ b/macos/Reactotron-macOS/Reactotron.entitlements @@ -8,5 +8,7 @@ com.apple.security.network.client + com.apple.security.network.server + diff --git a/macos/Reactotron-macOS/standalone-server.bundle.js b/macos/Reactotron-macOS/standalone-server.bundle.js new file mode 100755 index 0000000..64926d8 --- /dev/null +++ b/macos/Reactotron-macOS/standalone-server.bundle.js @@ -0,0 +1,4508 @@ +#!/usr/bin/env node +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __esm = (fn, res) => function __init() { + return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; +}; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// node_modules/reactotron-core-server/node_modules/ws/lib/constants.js +var require_constants = __commonJS({ + "node_modules/reactotron-core-server/node_modules/ws/lib/constants.js"(exports2, module2) { + "use strict"; + var BINARY_TYPES = ["nodebuffer", "arraybuffer", "fragments"]; + var hasBlob = typeof Blob !== "undefined"; + if (hasBlob) BINARY_TYPES.push("blob"); + module2.exports = { + BINARY_TYPES, + EMPTY_BUFFER: Buffer.alloc(0), + GUID: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", + hasBlob, + kForOnEventAttribute: Symbol("kIsForOnEventAttribute"), + kListener: Symbol("kListener"), + kStatusCode: Symbol("status-code"), + kWebSocket: Symbol("websocket"), + NOOP: () => { + } + }; + } +}); + +// node_modules/reactotron-core-server/node_modules/ws/lib/buffer-util.js +var require_buffer_util = __commonJS({ + "node_modules/reactotron-core-server/node_modules/ws/lib/buffer-util.js"(exports2, module2) { + "use strict"; + var { EMPTY_BUFFER } = require_constants(); + var FastBuffer = Buffer[Symbol.species]; + function concat(list, totalLength) { + if (list.length === 0) return EMPTY_BUFFER; + if (list.length === 1) return list[0]; + const target = Buffer.allocUnsafe(totalLength); + let offset = 0; + for (let i = 0; i < list.length; i++) { + const buf = list[i]; + target.set(buf, offset); + offset += buf.length; + } + if (offset < totalLength) { + return new FastBuffer(target.buffer, target.byteOffset, offset); + } + return target; + } + function _mask(source, mask, output, offset, length) { + for (let i = 0; i < length; i++) { + output[offset + i] = source[i] ^ mask[i & 3]; + } + } + function _unmask(buffer, mask) { + for (let i = 0; i < buffer.length; i++) { + buffer[i] ^= mask[i & 3]; + } + } + function toArrayBuffer(buf) { + if (buf.length === buf.buffer.byteLength) { + return buf.buffer; + } + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length); + } + function toBuffer(data) { + toBuffer.readOnly = true; + if (Buffer.isBuffer(data)) return data; + let buf; + if (data instanceof ArrayBuffer) { + buf = new FastBuffer(data); + } else if (ArrayBuffer.isView(data)) { + buf = new FastBuffer(data.buffer, data.byteOffset, data.byteLength); + } else { + buf = Buffer.from(data); + toBuffer.readOnly = false; + } + return buf; + } + module2.exports = { + concat, + mask: _mask, + toArrayBuffer, + toBuffer, + unmask: _unmask + }; + if (!process.env.WS_NO_BUFFER_UTIL) { + try { + const bufferUtil = require("bufferutil"); + module2.exports.mask = function(source, mask, output, offset, length) { + if (length < 48) _mask(source, mask, output, offset, length); + else bufferUtil.mask(source, mask, output, offset, length); + }; + module2.exports.unmask = function(buffer, mask) { + if (buffer.length < 32) _unmask(buffer, mask); + else bufferUtil.unmask(buffer, mask); + }; + } catch (e) { + } + } + } +}); + +// node_modules/reactotron-core-server/node_modules/ws/lib/limiter.js +var require_limiter = __commonJS({ + "node_modules/reactotron-core-server/node_modules/ws/lib/limiter.js"(exports2, module2) { + "use strict"; + var kDone = Symbol("kDone"); + var kRun = Symbol("kRun"); + var Limiter = class { + /** + * Creates a new `Limiter`. + * + * @param {Number} [concurrency=Infinity] The maximum number of jobs allowed + * to run concurrently + */ + constructor(concurrency) { + this[kDone] = () => { + this.pending--; + this[kRun](); + }; + this.concurrency = concurrency || Infinity; + this.jobs = []; + this.pending = 0; + } + /** + * Adds a job to the queue. + * + * @param {Function} job The job to run + * @public + */ + add(job) { + this.jobs.push(job); + this[kRun](); + } + /** + * Removes a job from the queue and runs it if possible. + * + * @private + */ + [kRun]() { + if (this.pending === this.concurrency) return; + if (this.jobs.length) { + const job = this.jobs.shift(); + this.pending++; + job(this[kDone]); + } + } + }; + module2.exports = Limiter; + } +}); + +// node_modules/reactotron-core-server/node_modules/ws/lib/permessage-deflate.js +var require_permessage_deflate = __commonJS({ + "node_modules/reactotron-core-server/node_modules/ws/lib/permessage-deflate.js"(exports2, module2) { + "use strict"; + var zlib = require("zlib"); + var bufferUtil = require_buffer_util(); + var Limiter = require_limiter(); + var { kStatusCode } = require_constants(); + var FastBuffer = Buffer[Symbol.species]; + var TRAILER = Buffer.from([0, 0, 255, 255]); + var kPerMessageDeflate = Symbol("permessage-deflate"); + var kTotalLength = Symbol("total-length"); + var kCallback = Symbol("callback"); + var kBuffers = Symbol("buffers"); + var kError = Symbol("error"); + var zlibLimiter; + var PerMessageDeflate = class { + /** + * Creates a PerMessageDeflate instance. + * + * @param {Object} [options] Configuration options + * @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support + * for, or request, a custom client window size + * @param {Boolean} [options.clientNoContextTakeover=false] Advertise/ + * acknowledge disabling of client context takeover + * @param {Number} [options.concurrencyLimit=10] The number of concurrent + * calls to zlib + * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the + * use of a custom server window size + * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept + * disabling of server context takeover + * @param {Number} [options.threshold=1024] Size (in bytes) below which + * messages should not be compressed if context takeover is disabled + * @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on + * deflate + * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on + * inflate + * @param {Boolean} [isServer=false] Create the instance in either server or + * client mode + * @param {Number} [maxPayload=0] The maximum allowed message length + */ + constructor(options, isServer, maxPayload) { + this._maxPayload = maxPayload | 0; + this._options = options || {}; + this._threshold = this._options.threshold !== void 0 ? this._options.threshold : 1024; + this._isServer = !!isServer; + this._deflate = null; + this._inflate = null; + this.params = null; + if (!zlibLimiter) { + const concurrency = this._options.concurrencyLimit !== void 0 ? this._options.concurrencyLimit : 10; + zlibLimiter = new Limiter(concurrency); + } + } + /** + * @type {String} + */ + static get extensionName() { + return "permessage-deflate"; + } + /** + * Create an extension negotiation offer. + * + * @return {Object} Extension parameters + * @public + */ + offer() { + const params = {}; + if (this._options.serverNoContextTakeover) { + params.server_no_context_takeover = true; + } + if (this._options.clientNoContextTakeover) { + params.client_no_context_takeover = true; + } + if (this._options.serverMaxWindowBits) { + params.server_max_window_bits = this._options.serverMaxWindowBits; + } + if (this._options.clientMaxWindowBits) { + params.client_max_window_bits = this._options.clientMaxWindowBits; + } else if (this._options.clientMaxWindowBits == null) { + params.client_max_window_bits = true; + } + return params; + } + /** + * Accept an extension negotiation offer/response. + * + * @param {Array} configurations The extension negotiation offers/reponse + * @return {Object} Accepted configuration + * @public + */ + accept(configurations) { + configurations = this.normalizeParams(configurations); + this.params = this._isServer ? this.acceptAsServer(configurations) : this.acceptAsClient(configurations); + return this.params; + } + /** + * Releases all resources used by the extension. + * + * @public + */ + cleanup() { + if (this._inflate) { + this._inflate.close(); + this._inflate = null; + } + if (this._deflate) { + const callback = this._deflate[kCallback]; + this._deflate.close(); + this._deflate = null; + if (callback) { + callback( + new Error( + "The deflate stream was closed while data was being processed" + ) + ); + } + } + } + /** + * Accept an extension negotiation offer. + * + * @param {Array} offers The extension negotiation offers + * @return {Object} Accepted configuration + * @private + */ + acceptAsServer(offers) { + const opts = this._options; + const accepted = offers.find((params) => { + if (opts.serverNoContextTakeover === false && params.server_no_context_takeover || params.server_max_window_bits && (opts.serverMaxWindowBits === false || typeof opts.serverMaxWindowBits === "number" && opts.serverMaxWindowBits > params.server_max_window_bits) || typeof opts.clientMaxWindowBits === "number" && !params.client_max_window_bits) { + return false; + } + return true; + }); + if (!accepted) { + throw new Error("None of the extension offers can be accepted"); + } + if (opts.serverNoContextTakeover) { + accepted.server_no_context_takeover = true; + } + if (opts.clientNoContextTakeover) { + accepted.client_no_context_takeover = true; + } + if (typeof opts.serverMaxWindowBits === "number") { + accepted.server_max_window_bits = opts.serverMaxWindowBits; + } + if (typeof opts.clientMaxWindowBits === "number") { + accepted.client_max_window_bits = opts.clientMaxWindowBits; + } else if (accepted.client_max_window_bits === true || opts.clientMaxWindowBits === false) { + delete accepted.client_max_window_bits; + } + return accepted; + } + /** + * Accept the extension negotiation response. + * + * @param {Array} response The extension negotiation response + * @return {Object} Accepted configuration + * @private + */ + acceptAsClient(response) { + const params = response[0]; + if (this._options.clientNoContextTakeover === false && params.client_no_context_takeover) { + throw new Error('Unexpected parameter "client_no_context_takeover"'); + } + if (!params.client_max_window_bits) { + if (typeof this._options.clientMaxWindowBits === "number") { + params.client_max_window_bits = this._options.clientMaxWindowBits; + } + } else if (this._options.clientMaxWindowBits === false || typeof this._options.clientMaxWindowBits === "number" && params.client_max_window_bits > this._options.clientMaxWindowBits) { + throw new Error( + 'Unexpected or invalid parameter "client_max_window_bits"' + ); + } + return params; + } + /** + * Normalize parameters. + * + * @param {Array} configurations The extension negotiation offers/reponse + * @return {Array} The offers/response with normalized parameters + * @private + */ + normalizeParams(configurations) { + configurations.forEach((params) => { + Object.keys(params).forEach((key) => { + let value = params[key]; + if (value.length > 1) { + throw new Error(`Parameter "${key}" must have only a single value`); + } + value = value[0]; + if (key === "client_max_window_bits") { + if (value !== true) { + const num = +value; + if (!Number.isInteger(num) || num < 8 || num > 15) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + value = num; + } else if (!this._isServer) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + } else if (key === "server_max_window_bits") { + const num = +value; + if (!Number.isInteger(num) || num < 8 || num > 15) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + value = num; + } else if (key === "client_no_context_takeover" || key === "server_no_context_takeover") { + if (value !== true) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + } else { + throw new Error(`Unknown parameter "${key}"`); + } + params[key] = value; + }); + }); + return configurations; + } + /** + * Decompress data. Concurrency limited. + * + * @param {Buffer} data Compressed data + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @public + */ + decompress(data, fin, callback) { + zlibLimiter.add((done) => { + this._decompress(data, fin, (err, result) => { + done(); + callback(err, result); + }); + }); + } + /** + * Compress data. Concurrency limited. + * + * @param {(Buffer|String)} data Data to compress + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @public + */ + compress(data, fin, callback) { + zlibLimiter.add((done) => { + this._compress(data, fin, (err, result) => { + done(); + callback(err, result); + }); + }); + } + /** + * Decompress data. + * + * @param {Buffer} data Compressed data + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @private + */ + _decompress(data, fin, callback) { + const endpoint = this._isServer ? "client" : "server"; + if (!this._inflate) { + const key = `${endpoint}_max_window_bits`; + const windowBits = typeof this.params[key] !== "number" ? zlib.Z_DEFAULT_WINDOWBITS : this.params[key]; + this._inflate = zlib.createInflateRaw({ + ...this._options.zlibInflateOptions, + windowBits + }); + this._inflate[kPerMessageDeflate] = this; + this._inflate[kTotalLength] = 0; + this._inflate[kBuffers] = []; + this._inflate.on("error", inflateOnError); + this._inflate.on("data", inflateOnData); + } + this._inflate[kCallback] = callback; + this._inflate.write(data); + if (fin) this._inflate.write(TRAILER); + this._inflate.flush(() => { + const err = this._inflate[kError]; + if (err) { + this._inflate.close(); + this._inflate = null; + callback(err); + return; + } + const data2 = bufferUtil.concat( + this._inflate[kBuffers], + this._inflate[kTotalLength] + ); + if (this._inflate._readableState.endEmitted) { + this._inflate.close(); + this._inflate = null; + } else { + this._inflate[kTotalLength] = 0; + this._inflate[kBuffers] = []; + if (fin && this.params[`${endpoint}_no_context_takeover`]) { + this._inflate.reset(); + } + } + callback(null, data2); + }); + } + /** + * Compress data. + * + * @param {(Buffer|String)} data Data to compress + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @private + */ + _compress(data, fin, callback) { + const endpoint = this._isServer ? "server" : "client"; + if (!this._deflate) { + const key = `${endpoint}_max_window_bits`; + const windowBits = typeof this.params[key] !== "number" ? zlib.Z_DEFAULT_WINDOWBITS : this.params[key]; + this._deflate = zlib.createDeflateRaw({ + ...this._options.zlibDeflateOptions, + windowBits + }); + this._deflate[kTotalLength] = 0; + this._deflate[kBuffers] = []; + this._deflate.on("data", deflateOnData); + } + this._deflate[kCallback] = callback; + this._deflate.write(data); + this._deflate.flush(zlib.Z_SYNC_FLUSH, () => { + if (!this._deflate) { + return; + } + let data2 = bufferUtil.concat( + this._deflate[kBuffers], + this._deflate[kTotalLength] + ); + if (fin) { + data2 = new FastBuffer(data2.buffer, data2.byteOffset, data2.length - 4); + } + this._deflate[kCallback] = null; + this._deflate[kTotalLength] = 0; + this._deflate[kBuffers] = []; + if (fin && this.params[`${endpoint}_no_context_takeover`]) { + this._deflate.reset(); + } + callback(null, data2); + }); + } + }; + module2.exports = PerMessageDeflate; + function deflateOnData(chunk) { + this[kBuffers].push(chunk); + this[kTotalLength] += chunk.length; + } + function inflateOnData(chunk) { + this[kTotalLength] += chunk.length; + if (this[kPerMessageDeflate]._maxPayload < 1 || this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload) { + this[kBuffers].push(chunk); + return; + } + this[kError] = new RangeError("Max payload size exceeded"); + this[kError].code = "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"; + this[kError][kStatusCode] = 1009; + this.removeListener("data", inflateOnData); + this.reset(); + } + function inflateOnError(err) { + this[kPerMessageDeflate]._inflate = null; + if (this[kError]) { + this[kCallback](this[kError]); + return; + } + err[kStatusCode] = 1007; + this[kCallback](err); + } + } +}); + +// node_modules/reactotron-core-server/node_modules/ws/lib/validation.js +var require_validation = __commonJS({ + "node_modules/reactotron-core-server/node_modules/ws/lib/validation.js"(exports2, module2) { + "use strict"; + var { isUtf8 } = require("buffer"); + var { hasBlob } = require_constants(); + var tokenChars = [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + // 0 - 15 + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + // 16 - 31 + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + // 32 - 47 + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + // 48 - 63 + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + // 64 - 79 + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + // 80 - 95 + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + // 96 - 111 + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 1, + 0 + // 112 - 127 + ]; + function isValidStatusCode(code) { + return code >= 1e3 && code <= 1014 && code !== 1004 && code !== 1005 && code !== 1006 || code >= 3e3 && code <= 4999; + } + function _isValidUTF8(buf) { + const len = buf.length; + let i = 0; + while (i < len) { + if ((buf[i] & 128) === 0) { + i++; + } else if ((buf[i] & 224) === 192) { + if (i + 1 === len || (buf[i + 1] & 192) !== 128 || (buf[i] & 254) === 192) { + return false; + } + i += 2; + } else if ((buf[i] & 240) === 224) { + if (i + 2 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || buf[i] === 224 && (buf[i + 1] & 224) === 128 || // Overlong + buf[i] === 237 && (buf[i + 1] & 224) === 160) { + return false; + } + i += 3; + } else if ((buf[i] & 248) === 240) { + if (i + 3 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || (buf[i + 3] & 192) !== 128 || buf[i] === 240 && (buf[i + 1] & 240) === 128 || // Overlong + buf[i] === 244 && buf[i + 1] > 143 || buf[i] > 244) { + return false; + } + i += 4; + } else { + return false; + } + } + return true; + } + function isBlob(value) { + return hasBlob && typeof value === "object" && typeof value.arrayBuffer === "function" && typeof value.type === "string" && typeof value.stream === "function" && (value[Symbol.toStringTag] === "Blob" || value[Symbol.toStringTag] === "File"); + } + module2.exports = { + isBlob, + isValidStatusCode, + isValidUTF8: _isValidUTF8, + tokenChars + }; + if (isUtf8) { + module2.exports.isValidUTF8 = function(buf) { + return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf); + }; + } else if (!process.env.WS_NO_UTF_8_VALIDATE) { + try { + const isValidUTF8 = require("utf-8-validate"); + module2.exports.isValidUTF8 = function(buf) { + return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf); + }; + } catch (e) { + } + } + } +}); + +// node_modules/reactotron-core-server/node_modules/ws/lib/receiver.js +var require_receiver = __commonJS({ + "node_modules/reactotron-core-server/node_modules/ws/lib/receiver.js"(exports2, module2) { + "use strict"; + var { Writable } = require("stream"); + var PerMessageDeflate = require_permessage_deflate(); + var { + BINARY_TYPES, + EMPTY_BUFFER, + kStatusCode, + kWebSocket + } = require_constants(); + var { concat, toArrayBuffer, unmask } = require_buffer_util(); + var { isValidStatusCode, isValidUTF8 } = require_validation(); + var FastBuffer = Buffer[Symbol.species]; + var GET_INFO = 0; + var GET_PAYLOAD_LENGTH_16 = 1; + var GET_PAYLOAD_LENGTH_64 = 2; + var GET_MASK = 3; + var GET_DATA = 4; + var INFLATING = 5; + var DEFER_EVENT = 6; + var Receiver = class extends Writable { + /** + * Creates a Receiver instance. + * + * @param {Object} [options] Options object + * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether + * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted + * multiple times in the same tick + * @param {String} [options.binaryType=nodebuffer] The type for binary data + * @param {Object} [options.extensions] An object containing the negotiated + * extensions + * @param {Boolean} [options.isServer=false] Specifies whether to operate in + * client or server mode + * @param {Number} [options.maxPayload=0] The maximum allowed message length + * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or + * not to skip UTF-8 validation for text and close messages + */ + constructor(options = {}) { + super(); + this._allowSynchronousEvents = options.allowSynchronousEvents !== void 0 ? options.allowSynchronousEvents : true; + this._binaryType = options.binaryType || BINARY_TYPES[0]; + this._extensions = options.extensions || {}; + this._isServer = !!options.isServer; + this._maxPayload = options.maxPayload | 0; + this._skipUTF8Validation = !!options.skipUTF8Validation; + this[kWebSocket] = void 0; + this._bufferedBytes = 0; + this._buffers = []; + this._compressed = false; + this._payloadLength = 0; + this._mask = void 0; + this._fragmented = 0; + this._masked = false; + this._fin = false; + this._opcode = 0; + this._totalPayloadLength = 0; + this._messageLength = 0; + this._fragments = []; + this._errored = false; + this._loop = false; + this._state = GET_INFO; + } + /** + * Implements `Writable.prototype._write()`. + * + * @param {Buffer} chunk The chunk of data to write + * @param {String} encoding The character encoding of `chunk` + * @param {Function} cb Callback + * @private + */ + _write(chunk, encoding, cb) { + if (this._opcode === 8 && this._state == GET_INFO) return cb(); + this._bufferedBytes += chunk.length; + this._buffers.push(chunk); + this.startLoop(cb); + } + /** + * Consumes `n` bytes from the buffered data. + * + * @param {Number} n The number of bytes to consume + * @return {Buffer} The consumed bytes + * @private + */ + consume(n) { + this._bufferedBytes -= n; + if (n === this._buffers[0].length) return this._buffers.shift(); + if (n < this._buffers[0].length) { + const buf = this._buffers[0]; + this._buffers[0] = new FastBuffer( + buf.buffer, + buf.byteOffset + n, + buf.length - n + ); + return new FastBuffer(buf.buffer, buf.byteOffset, n); + } + const dst = Buffer.allocUnsafe(n); + do { + const buf = this._buffers[0]; + const offset = dst.length - n; + if (n >= buf.length) { + dst.set(this._buffers.shift(), offset); + } else { + dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset); + this._buffers[0] = new FastBuffer( + buf.buffer, + buf.byteOffset + n, + buf.length - n + ); + } + n -= buf.length; + } while (n > 0); + return dst; + } + /** + * Starts the parsing loop. + * + * @param {Function} cb Callback + * @private + */ + startLoop(cb) { + this._loop = true; + do { + switch (this._state) { + case GET_INFO: + this.getInfo(cb); + break; + case GET_PAYLOAD_LENGTH_16: + this.getPayloadLength16(cb); + break; + case GET_PAYLOAD_LENGTH_64: + this.getPayloadLength64(cb); + break; + case GET_MASK: + this.getMask(); + break; + case GET_DATA: + this.getData(cb); + break; + case INFLATING: + case DEFER_EVENT: + this._loop = false; + return; + } + } while (this._loop); + if (!this._errored) cb(); + } + /** + * Reads the first two bytes of a frame. + * + * @param {Function} cb Callback + * @private + */ + getInfo(cb) { + if (this._bufferedBytes < 2) { + this._loop = false; + return; + } + const buf = this.consume(2); + if ((buf[0] & 48) !== 0) { + const error = this.createError( + RangeError, + "RSV2 and RSV3 must be clear", + true, + 1002, + "WS_ERR_UNEXPECTED_RSV_2_3" + ); + cb(error); + return; + } + const compressed = (buf[0] & 64) === 64; + if (compressed && !this._extensions[PerMessageDeflate.extensionName]) { + const error = this.createError( + RangeError, + "RSV1 must be clear", + true, + 1002, + "WS_ERR_UNEXPECTED_RSV_1" + ); + cb(error); + return; + } + this._fin = (buf[0] & 128) === 128; + this._opcode = buf[0] & 15; + this._payloadLength = buf[1] & 127; + if (this._opcode === 0) { + if (compressed) { + const error = this.createError( + RangeError, + "RSV1 must be clear", + true, + 1002, + "WS_ERR_UNEXPECTED_RSV_1" + ); + cb(error); + return; + } + if (!this._fragmented) { + const error = this.createError( + RangeError, + "invalid opcode 0", + true, + 1002, + "WS_ERR_INVALID_OPCODE" + ); + cb(error); + return; + } + this._opcode = this._fragmented; + } else if (this._opcode === 1 || this._opcode === 2) { + if (this._fragmented) { + const error = this.createError( + RangeError, + `invalid opcode ${this._opcode}`, + true, + 1002, + "WS_ERR_INVALID_OPCODE" + ); + cb(error); + return; + } + this._compressed = compressed; + } else if (this._opcode > 7 && this._opcode < 11) { + if (!this._fin) { + const error = this.createError( + RangeError, + "FIN must be set", + true, + 1002, + "WS_ERR_EXPECTED_FIN" + ); + cb(error); + return; + } + if (compressed) { + const error = this.createError( + RangeError, + "RSV1 must be clear", + true, + 1002, + "WS_ERR_UNEXPECTED_RSV_1" + ); + cb(error); + return; + } + if (this._payloadLength > 125 || this._opcode === 8 && this._payloadLength === 1) { + const error = this.createError( + RangeError, + `invalid payload length ${this._payloadLength}`, + true, + 1002, + "WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH" + ); + cb(error); + return; + } + } else { + const error = this.createError( + RangeError, + `invalid opcode ${this._opcode}`, + true, + 1002, + "WS_ERR_INVALID_OPCODE" + ); + cb(error); + return; + } + if (!this._fin && !this._fragmented) this._fragmented = this._opcode; + this._masked = (buf[1] & 128) === 128; + if (this._isServer) { + if (!this._masked) { + const error = this.createError( + RangeError, + "MASK must be set", + true, + 1002, + "WS_ERR_EXPECTED_MASK" + ); + cb(error); + return; + } + } else if (this._masked) { + const error = this.createError( + RangeError, + "MASK must be clear", + true, + 1002, + "WS_ERR_UNEXPECTED_MASK" + ); + cb(error); + return; + } + if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16; + else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64; + else this.haveLength(cb); + } + /** + * Gets extended payload length (7+16). + * + * @param {Function} cb Callback + * @private + */ + getPayloadLength16(cb) { + if (this._bufferedBytes < 2) { + this._loop = false; + return; + } + this._payloadLength = this.consume(2).readUInt16BE(0); + this.haveLength(cb); + } + /** + * Gets extended payload length (7+64). + * + * @param {Function} cb Callback + * @private + */ + getPayloadLength64(cb) { + if (this._bufferedBytes < 8) { + this._loop = false; + return; + } + const buf = this.consume(8); + const num = buf.readUInt32BE(0); + if (num > Math.pow(2, 53 - 32) - 1) { + const error = this.createError( + RangeError, + "Unsupported WebSocket frame: payload length > 2^53 - 1", + false, + 1009, + "WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH" + ); + cb(error); + return; + } + this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4); + this.haveLength(cb); + } + /** + * Payload length has been read. + * + * @param {Function} cb Callback + * @private + */ + haveLength(cb) { + if (this._payloadLength && this._opcode < 8) { + this._totalPayloadLength += this._payloadLength; + if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) { + const error = this.createError( + RangeError, + "Max payload size exceeded", + false, + 1009, + "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH" + ); + cb(error); + return; + } + } + if (this._masked) this._state = GET_MASK; + else this._state = GET_DATA; + } + /** + * Reads mask bytes. + * + * @private + */ + getMask() { + if (this._bufferedBytes < 4) { + this._loop = false; + return; + } + this._mask = this.consume(4); + this._state = GET_DATA; + } + /** + * Reads data bytes. + * + * @param {Function} cb Callback + * @private + */ + getData(cb) { + let data = EMPTY_BUFFER; + if (this._payloadLength) { + if (this._bufferedBytes < this._payloadLength) { + this._loop = false; + return; + } + data = this.consume(this._payloadLength); + if (this._masked && (this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0) { + unmask(data, this._mask); + } + } + if (this._opcode > 7) { + this.controlMessage(data, cb); + return; + } + if (this._compressed) { + this._state = INFLATING; + this.decompress(data, cb); + return; + } + if (data.length) { + this._messageLength = this._totalPayloadLength; + this._fragments.push(data); + } + this.dataMessage(cb); + } + /** + * Decompresses data. + * + * @param {Buffer} data Compressed data + * @param {Function} cb Callback + * @private + */ + decompress(data, cb) { + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + perMessageDeflate.decompress(data, this._fin, (err, buf) => { + if (err) return cb(err); + if (buf.length) { + this._messageLength += buf.length; + if (this._messageLength > this._maxPayload && this._maxPayload > 0) { + const error = this.createError( + RangeError, + "Max payload size exceeded", + false, + 1009, + "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH" + ); + cb(error); + return; + } + this._fragments.push(buf); + } + this.dataMessage(cb); + if (this._state === GET_INFO) this.startLoop(cb); + }); + } + /** + * Handles a data message. + * + * @param {Function} cb Callback + * @private + */ + dataMessage(cb) { + if (!this._fin) { + this._state = GET_INFO; + return; + } + const messageLength = this._messageLength; + const fragments = this._fragments; + this._totalPayloadLength = 0; + this._messageLength = 0; + this._fragmented = 0; + this._fragments = []; + if (this._opcode === 2) { + let data; + if (this._binaryType === "nodebuffer") { + data = concat(fragments, messageLength); + } else if (this._binaryType === "arraybuffer") { + data = toArrayBuffer(concat(fragments, messageLength)); + } else if (this._binaryType === "blob") { + data = new Blob(fragments); + } else { + data = fragments; + } + if (this._allowSynchronousEvents) { + this.emit("message", data, true); + this._state = GET_INFO; + } else { + this._state = DEFER_EVENT; + setImmediate(() => { + this.emit("message", data, true); + this._state = GET_INFO; + this.startLoop(cb); + }); + } + } else { + const buf = concat(fragments, messageLength); + if (!this._skipUTF8Validation && !isValidUTF8(buf)) { + const error = this.createError( + Error, + "invalid UTF-8 sequence", + true, + 1007, + "WS_ERR_INVALID_UTF8" + ); + cb(error); + return; + } + if (this._state === INFLATING || this._allowSynchronousEvents) { + this.emit("message", buf, false); + this._state = GET_INFO; + } else { + this._state = DEFER_EVENT; + setImmediate(() => { + this.emit("message", buf, false); + this._state = GET_INFO; + this.startLoop(cb); + }); + } + } + } + /** + * Handles a control message. + * + * @param {Buffer} data Data to handle + * @return {(Error|RangeError|undefined)} A possible error + * @private + */ + controlMessage(data, cb) { + if (this._opcode === 8) { + if (data.length === 0) { + this._loop = false; + this.emit("conclude", 1005, EMPTY_BUFFER); + this.end(); + } else { + const code = data.readUInt16BE(0); + if (!isValidStatusCode(code)) { + const error = this.createError( + RangeError, + `invalid status code ${code}`, + true, + 1002, + "WS_ERR_INVALID_CLOSE_CODE" + ); + cb(error); + return; + } + const buf = new FastBuffer( + data.buffer, + data.byteOffset + 2, + data.length - 2 + ); + if (!this._skipUTF8Validation && !isValidUTF8(buf)) { + const error = this.createError( + Error, + "invalid UTF-8 sequence", + true, + 1007, + "WS_ERR_INVALID_UTF8" + ); + cb(error); + return; + } + this._loop = false; + this.emit("conclude", code, buf); + this.end(); + } + this._state = GET_INFO; + return; + } + if (this._allowSynchronousEvents) { + this.emit(this._opcode === 9 ? "ping" : "pong", data); + this._state = GET_INFO; + } else { + this._state = DEFER_EVENT; + setImmediate(() => { + this.emit(this._opcode === 9 ? "ping" : "pong", data); + this._state = GET_INFO; + this.startLoop(cb); + }); + } + } + /** + * Builds an error object. + * + * @param {function(new:Error|RangeError)} ErrorCtor The error constructor + * @param {String} message The error message + * @param {Boolean} prefix Specifies whether or not to add a default prefix to + * `message` + * @param {Number} statusCode The status code + * @param {String} errorCode The exposed error code + * @return {(Error|RangeError)} The error + * @private + */ + createError(ErrorCtor, message, prefix, statusCode, errorCode) { + this._loop = false; + this._errored = true; + const err = new ErrorCtor( + prefix ? `Invalid WebSocket frame: ${message}` : message + ); + Error.captureStackTrace(err, this.createError); + err.code = errorCode; + err[kStatusCode] = statusCode; + return err; + } + }; + module2.exports = Receiver; + } +}); + +// node_modules/reactotron-core-server/node_modules/ws/lib/sender.js +var require_sender = __commonJS({ + "node_modules/reactotron-core-server/node_modules/ws/lib/sender.js"(exports2, module2) { + "use strict"; + var { Duplex } = require("stream"); + var { randomFillSync } = require("crypto"); + var PerMessageDeflate = require_permessage_deflate(); + var { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants(); + var { isBlob, isValidStatusCode } = require_validation(); + var { mask: applyMask, toBuffer } = require_buffer_util(); + var kByteLength = Symbol("kByteLength"); + var maskBuffer = Buffer.alloc(4); + var RANDOM_POOL_SIZE = 8 * 1024; + var randomPool; + var randomPoolPointer = RANDOM_POOL_SIZE; + var DEFAULT = 0; + var DEFLATING = 1; + var GET_BLOB_DATA = 2; + var Sender = class _Sender { + /** + * Creates a Sender instance. + * + * @param {Duplex} socket The connection socket + * @param {Object} [extensions] An object containing the negotiated extensions + * @param {Function} [generateMask] The function used to generate the masking + * key + */ + constructor(socket, extensions, generateMask) { + this._extensions = extensions || {}; + if (generateMask) { + this._generateMask = generateMask; + this._maskBuffer = Buffer.alloc(4); + } + this._socket = socket; + this._firstFragment = true; + this._compress = false; + this._bufferedBytes = 0; + this._queue = []; + this._state = DEFAULT; + this.onerror = NOOP; + this[kWebSocket] = void 0; + } + /** + * Frames a piece of data according to the HyBi WebSocket protocol. + * + * @param {(Buffer|String)} data The data to frame + * @param {Object} options Options object + * @param {Boolean} [options.fin=false] Specifies whether or not to set the + * FIN bit + * @param {Function} [options.generateMask] The function used to generate the + * masking key + * @param {Boolean} [options.mask=false] Specifies whether or not to mask + * `data` + * @param {Buffer} [options.maskBuffer] The buffer used to store the masking + * key + * @param {Number} options.opcode The opcode + * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be + * modified + * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the + * RSV1 bit + * @return {(Buffer|String)[]} The framed data + * @public + */ + static frame(data, options) { + let mask; + let merge = false; + let offset = 2; + let skipMasking = false; + if (options.mask) { + mask = options.maskBuffer || maskBuffer; + if (options.generateMask) { + options.generateMask(mask); + } else { + if (randomPoolPointer === RANDOM_POOL_SIZE) { + if (randomPool === void 0) { + randomPool = Buffer.alloc(RANDOM_POOL_SIZE); + } + randomFillSync(randomPool, 0, RANDOM_POOL_SIZE); + randomPoolPointer = 0; + } + mask[0] = randomPool[randomPoolPointer++]; + mask[1] = randomPool[randomPoolPointer++]; + mask[2] = randomPool[randomPoolPointer++]; + mask[3] = randomPool[randomPoolPointer++]; + } + skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0; + offset = 6; + } + let dataLength; + if (typeof data === "string") { + if ((!options.mask || skipMasking) && options[kByteLength] !== void 0) { + dataLength = options[kByteLength]; + } else { + data = Buffer.from(data); + dataLength = data.length; + } + } else { + dataLength = data.length; + merge = options.mask && options.readOnly && !skipMasking; + } + let payloadLength = dataLength; + if (dataLength >= 65536) { + offset += 8; + payloadLength = 127; + } else if (dataLength > 125) { + offset += 2; + payloadLength = 126; + } + const target = Buffer.allocUnsafe(merge ? dataLength + offset : offset); + target[0] = options.fin ? options.opcode | 128 : options.opcode; + if (options.rsv1) target[0] |= 64; + target[1] = payloadLength; + if (payloadLength === 126) { + target.writeUInt16BE(dataLength, 2); + } else if (payloadLength === 127) { + target[2] = target[3] = 0; + target.writeUIntBE(dataLength, 4, 6); + } + if (!options.mask) return [target, data]; + target[1] |= 128; + target[offset - 4] = mask[0]; + target[offset - 3] = mask[1]; + target[offset - 2] = mask[2]; + target[offset - 1] = mask[3]; + if (skipMasking) return [target, data]; + if (merge) { + applyMask(data, mask, target, offset, dataLength); + return [target]; + } + applyMask(data, mask, data, 0, dataLength); + return [target, data]; + } + /** + * Sends a close message to the other peer. + * + * @param {Number} [code] The status code component of the body + * @param {(String|Buffer)} [data] The message component of the body + * @param {Boolean} [mask=false] Specifies whether or not to mask the message + * @param {Function} [cb] Callback + * @public + */ + close(code, data, mask, cb) { + let buf; + if (code === void 0) { + buf = EMPTY_BUFFER; + } else if (typeof code !== "number" || !isValidStatusCode(code)) { + throw new TypeError("First argument must be a valid error code number"); + } else if (data === void 0 || !data.length) { + buf = Buffer.allocUnsafe(2); + buf.writeUInt16BE(code, 0); + } else { + const length = Buffer.byteLength(data); + if (length > 123) { + throw new RangeError("The message must not be greater than 123 bytes"); + } + buf = Buffer.allocUnsafe(2 + length); + buf.writeUInt16BE(code, 0); + if (typeof data === "string") { + buf.write(data, 2); + } else { + buf.set(data, 2); + } + } + const options = { + [kByteLength]: buf.length, + fin: true, + generateMask: this._generateMask, + mask, + maskBuffer: this._maskBuffer, + opcode: 8, + readOnly: false, + rsv1: false + }; + if (this._state !== DEFAULT) { + this.enqueue([this.dispatch, buf, false, options, cb]); + } else { + this.sendFrame(_Sender.frame(buf, options), cb); + } + } + /** + * Sends a ping message to the other peer. + * + * @param {*} data The message to send + * @param {Boolean} [mask=false] Specifies whether or not to mask `data` + * @param {Function} [cb] Callback + * @public + */ + ping(data, mask, cb) { + let byteLength; + let readOnly; + if (typeof data === "string") { + byteLength = Buffer.byteLength(data); + readOnly = false; + } else if (isBlob(data)) { + byteLength = data.size; + readOnly = false; + } else { + data = toBuffer(data); + byteLength = data.length; + readOnly = toBuffer.readOnly; + } + if (byteLength > 125) { + throw new RangeError("The data size must not be greater than 125 bytes"); + } + const options = { + [kByteLength]: byteLength, + fin: true, + generateMask: this._generateMask, + mask, + maskBuffer: this._maskBuffer, + opcode: 9, + readOnly, + rsv1: false + }; + if (isBlob(data)) { + if (this._state !== DEFAULT) { + this.enqueue([this.getBlobData, data, false, options, cb]); + } else { + this.getBlobData(data, false, options, cb); + } + } else if (this._state !== DEFAULT) { + this.enqueue([this.dispatch, data, false, options, cb]); + } else { + this.sendFrame(_Sender.frame(data, options), cb); + } + } + /** + * Sends a pong message to the other peer. + * + * @param {*} data The message to send + * @param {Boolean} [mask=false] Specifies whether or not to mask `data` + * @param {Function} [cb] Callback + * @public + */ + pong(data, mask, cb) { + let byteLength; + let readOnly; + if (typeof data === "string") { + byteLength = Buffer.byteLength(data); + readOnly = false; + } else if (isBlob(data)) { + byteLength = data.size; + readOnly = false; + } else { + data = toBuffer(data); + byteLength = data.length; + readOnly = toBuffer.readOnly; + } + if (byteLength > 125) { + throw new RangeError("The data size must not be greater than 125 bytes"); + } + const options = { + [kByteLength]: byteLength, + fin: true, + generateMask: this._generateMask, + mask, + maskBuffer: this._maskBuffer, + opcode: 10, + readOnly, + rsv1: false + }; + if (isBlob(data)) { + if (this._state !== DEFAULT) { + this.enqueue([this.getBlobData, data, false, options, cb]); + } else { + this.getBlobData(data, false, options, cb); + } + } else if (this._state !== DEFAULT) { + this.enqueue([this.dispatch, data, false, options, cb]); + } else { + this.sendFrame(_Sender.frame(data, options), cb); + } + } + /** + * Sends a data message to the other peer. + * + * @param {*} data The message to send + * @param {Object} options Options object + * @param {Boolean} [options.binary=false] Specifies whether `data` is binary + * or text + * @param {Boolean} [options.compress=false] Specifies whether or not to + * compress `data` + * @param {Boolean} [options.fin=false] Specifies whether the fragment is the + * last one + * @param {Boolean} [options.mask=false] Specifies whether or not to mask + * `data` + * @param {Function} [cb] Callback + * @public + */ + send(data, options, cb) { + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + let opcode = options.binary ? 2 : 1; + let rsv1 = options.compress; + let byteLength; + let readOnly; + if (typeof data === "string") { + byteLength = Buffer.byteLength(data); + readOnly = false; + } else if (isBlob(data)) { + byteLength = data.size; + readOnly = false; + } else { + data = toBuffer(data); + byteLength = data.length; + readOnly = toBuffer.readOnly; + } + if (this._firstFragment) { + this._firstFragment = false; + if (rsv1 && perMessageDeflate && perMessageDeflate.params[perMessageDeflate._isServer ? "server_no_context_takeover" : "client_no_context_takeover"]) { + rsv1 = byteLength >= perMessageDeflate._threshold; + } + this._compress = rsv1; + } else { + rsv1 = false; + opcode = 0; + } + if (options.fin) this._firstFragment = true; + const opts = { + [kByteLength]: byteLength, + fin: options.fin, + generateMask: this._generateMask, + mask: options.mask, + maskBuffer: this._maskBuffer, + opcode, + readOnly, + rsv1 + }; + if (isBlob(data)) { + if (this._state !== DEFAULT) { + this.enqueue([this.getBlobData, data, this._compress, opts, cb]); + } else { + this.getBlobData(data, this._compress, opts, cb); + } + } else if (this._state !== DEFAULT) { + this.enqueue([this.dispatch, data, this._compress, opts, cb]); + } else { + this.dispatch(data, this._compress, opts, cb); + } + } + /** + * Gets the contents of a blob as binary data. + * + * @param {Blob} blob The blob + * @param {Boolean} [compress=false] Specifies whether or not to compress + * the data + * @param {Object} options Options object + * @param {Boolean} [options.fin=false] Specifies whether or not to set the + * FIN bit + * @param {Function} [options.generateMask] The function used to generate the + * masking key + * @param {Boolean} [options.mask=false] Specifies whether or not to mask + * `data` + * @param {Buffer} [options.maskBuffer] The buffer used to store the masking + * key + * @param {Number} options.opcode The opcode + * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be + * modified + * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the + * RSV1 bit + * @param {Function} [cb] Callback + * @private + */ + getBlobData(blob, compress, options, cb) { + this._bufferedBytes += options[kByteLength]; + this._state = GET_BLOB_DATA; + blob.arrayBuffer().then((arrayBuffer) => { + if (this._socket.destroyed) { + const err = new Error( + "The socket was closed while the blob was being read" + ); + process.nextTick(callCallbacks, this, err, cb); + return; + } + this._bufferedBytes -= options[kByteLength]; + const data = toBuffer(arrayBuffer); + if (!compress) { + this._state = DEFAULT; + this.sendFrame(_Sender.frame(data, options), cb); + this.dequeue(); + } else { + this.dispatch(data, compress, options, cb); + } + }).catch((err) => { + process.nextTick(onError, this, err, cb); + }); + } + /** + * Dispatches a message. + * + * @param {(Buffer|String)} data The message to send + * @param {Boolean} [compress=false] Specifies whether or not to compress + * `data` + * @param {Object} options Options object + * @param {Boolean} [options.fin=false] Specifies whether or not to set the + * FIN bit + * @param {Function} [options.generateMask] The function used to generate the + * masking key + * @param {Boolean} [options.mask=false] Specifies whether or not to mask + * `data` + * @param {Buffer} [options.maskBuffer] The buffer used to store the masking + * key + * @param {Number} options.opcode The opcode + * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be + * modified + * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the + * RSV1 bit + * @param {Function} [cb] Callback + * @private + */ + dispatch(data, compress, options, cb) { + if (!compress) { + this.sendFrame(_Sender.frame(data, options), cb); + return; + } + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + this._bufferedBytes += options[kByteLength]; + this._state = DEFLATING; + perMessageDeflate.compress(data, options.fin, (_, buf) => { + if (this._socket.destroyed) { + const err = new Error( + "The socket was closed while data was being compressed" + ); + callCallbacks(this, err, cb); + return; + } + this._bufferedBytes -= options[kByteLength]; + this._state = DEFAULT; + options.readOnly = false; + this.sendFrame(_Sender.frame(buf, options), cb); + this.dequeue(); + }); + } + /** + * Executes queued send operations. + * + * @private + */ + dequeue() { + while (this._state === DEFAULT && this._queue.length) { + const params = this._queue.shift(); + this._bufferedBytes -= params[3][kByteLength]; + Reflect.apply(params[0], this, params.slice(1)); + } + } + /** + * Enqueues a send operation. + * + * @param {Array} params Send operation parameters. + * @private + */ + enqueue(params) { + this._bufferedBytes += params[3][kByteLength]; + this._queue.push(params); + } + /** + * Sends a frame. + * + * @param {(Buffer | String)[]} list The frame to send + * @param {Function} [cb] Callback + * @private + */ + sendFrame(list, cb) { + if (list.length === 2) { + this._socket.cork(); + this._socket.write(list[0]); + this._socket.write(list[1], cb); + this._socket.uncork(); + } else { + this._socket.write(list[0], cb); + } + } + }; + module2.exports = Sender; + function callCallbacks(sender, err, cb) { + if (typeof cb === "function") cb(err); + for (let i = 0; i < sender._queue.length; i++) { + const params = sender._queue[i]; + const callback = params[params.length - 1]; + if (typeof callback === "function") callback(err); + } + } + function onError(sender, err, cb) { + callCallbacks(sender, err, cb); + sender.onerror(err); + } + } +}); + +// node_modules/reactotron-core-server/node_modules/ws/lib/event-target.js +var require_event_target = __commonJS({ + "node_modules/reactotron-core-server/node_modules/ws/lib/event-target.js"(exports2, module2) { + "use strict"; + var { kForOnEventAttribute, kListener } = require_constants(); + var kCode = Symbol("kCode"); + var kData = Symbol("kData"); + var kError = Symbol("kError"); + var kMessage = Symbol("kMessage"); + var kReason = Symbol("kReason"); + var kTarget = Symbol("kTarget"); + var kType = Symbol("kType"); + var kWasClean = Symbol("kWasClean"); + var Event = class { + /** + * Create a new `Event`. + * + * @param {String} type The name of the event + * @throws {TypeError} If the `type` argument is not specified + */ + constructor(type) { + this[kTarget] = null; + this[kType] = type; + } + /** + * @type {*} + */ + get target() { + return this[kTarget]; + } + /** + * @type {String} + */ + get type() { + return this[kType]; + } + }; + Object.defineProperty(Event.prototype, "target", { enumerable: true }); + Object.defineProperty(Event.prototype, "type", { enumerable: true }); + var CloseEvent = class extends Event { + /** + * Create a new `CloseEvent`. + * + * @param {String} type The name of the event + * @param {Object} [options] A dictionary object that allows for setting + * attributes via object members of the same name + * @param {Number} [options.code=0] The status code explaining why the + * connection was closed + * @param {String} [options.reason=''] A human-readable string explaining why + * the connection was closed + * @param {Boolean} [options.wasClean=false] Indicates whether or not the + * connection was cleanly closed + */ + constructor(type, options = {}) { + super(type); + this[kCode] = options.code === void 0 ? 0 : options.code; + this[kReason] = options.reason === void 0 ? "" : options.reason; + this[kWasClean] = options.wasClean === void 0 ? false : options.wasClean; + } + /** + * @type {Number} + */ + get code() { + return this[kCode]; + } + /** + * @type {String} + */ + get reason() { + return this[kReason]; + } + /** + * @type {Boolean} + */ + get wasClean() { + return this[kWasClean]; + } + }; + Object.defineProperty(CloseEvent.prototype, "code", { enumerable: true }); + Object.defineProperty(CloseEvent.prototype, "reason", { enumerable: true }); + Object.defineProperty(CloseEvent.prototype, "wasClean", { enumerable: true }); + var ErrorEvent = class extends Event { + /** + * Create a new `ErrorEvent`. + * + * @param {String} type The name of the event + * @param {Object} [options] A dictionary object that allows for setting + * attributes via object members of the same name + * @param {*} [options.error=null] The error that generated this event + * @param {String} [options.message=''] The error message + */ + constructor(type, options = {}) { + super(type); + this[kError] = options.error === void 0 ? null : options.error; + this[kMessage] = options.message === void 0 ? "" : options.message; + } + /** + * @type {*} + */ + get error() { + return this[kError]; + } + /** + * @type {String} + */ + get message() { + return this[kMessage]; + } + }; + Object.defineProperty(ErrorEvent.prototype, "error", { enumerable: true }); + Object.defineProperty(ErrorEvent.prototype, "message", { enumerable: true }); + var MessageEvent = class extends Event { + /** + * Create a new `MessageEvent`. + * + * @param {String} type The name of the event + * @param {Object} [options] A dictionary object that allows for setting + * attributes via object members of the same name + * @param {*} [options.data=null] The message content + */ + constructor(type, options = {}) { + super(type); + this[kData] = options.data === void 0 ? null : options.data; + } + /** + * @type {*} + */ + get data() { + return this[kData]; + } + }; + Object.defineProperty(MessageEvent.prototype, "data", { enumerable: true }); + var EventTarget = { + /** + * Register an event listener. + * + * @param {String} type A string representing the event type to listen for + * @param {(Function|Object)} handler The listener to add + * @param {Object} [options] An options object specifies characteristics about + * the event listener + * @param {Boolean} [options.once=false] A `Boolean` indicating that the + * listener should be invoked at most once after being added. If `true`, + * the listener would be automatically removed when invoked. + * @public + */ + addEventListener(type, handler, options = {}) { + for (const listener of this.listeners(type)) { + if (!options[kForOnEventAttribute] && listener[kListener] === handler && !listener[kForOnEventAttribute]) { + return; + } + } + let wrapper; + if (type === "message") { + wrapper = function onMessage(data, isBinary) { + const event = new MessageEvent("message", { + data: isBinary ? data : data.toString() + }); + event[kTarget] = this; + callListener(handler, this, event); + }; + } else if (type === "close") { + wrapper = function onClose(code, message) { + const event = new CloseEvent("close", { + code, + reason: message.toString(), + wasClean: this._closeFrameReceived && this._closeFrameSent + }); + event[kTarget] = this; + callListener(handler, this, event); + }; + } else if (type === "error") { + wrapper = function onError(error) { + const event = new ErrorEvent("error", { + error, + message: error.message + }); + event[kTarget] = this; + callListener(handler, this, event); + }; + } else if (type === "open") { + wrapper = function onOpen() { + const event = new Event("open"); + event[kTarget] = this; + callListener(handler, this, event); + }; + } else { + return; + } + wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute]; + wrapper[kListener] = handler; + if (options.once) { + this.once(type, wrapper); + } else { + this.on(type, wrapper); + } + }, + /** + * Remove an event listener. + * + * @param {String} type A string representing the event type to remove + * @param {(Function|Object)} handler The listener to remove + * @public + */ + removeEventListener(type, handler) { + for (const listener of this.listeners(type)) { + if (listener[kListener] === handler && !listener[kForOnEventAttribute]) { + this.removeListener(type, listener); + break; + } + } + } + }; + module2.exports = { + CloseEvent, + ErrorEvent, + Event, + EventTarget, + MessageEvent + }; + function callListener(listener, thisArg, event) { + if (typeof listener === "object" && listener.handleEvent) { + listener.handleEvent.call(listener, event); + } else { + listener.call(thisArg, event); + } + } + } +}); + +// node_modules/reactotron-core-server/node_modules/ws/lib/extension.js +var require_extension = __commonJS({ + "node_modules/reactotron-core-server/node_modules/ws/lib/extension.js"(exports2, module2) { + "use strict"; + var { tokenChars } = require_validation(); + function push(dest, name, elem) { + if (dest[name] === void 0) dest[name] = [elem]; + else dest[name].push(elem); + } + function parse(header) { + const offers = /* @__PURE__ */ Object.create(null); + let params = /* @__PURE__ */ Object.create(null); + let mustUnescape = false; + let isEscaping = false; + let inQuotes = false; + let extensionName; + let paramName; + let start = -1; + let code = -1; + let end = -1; + let i = 0; + for (; i < header.length; i++) { + code = header.charCodeAt(i); + if (extensionName === void 0) { + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (i !== 0 && (code === 32 || code === 9)) { + if (end === -1 && start !== -1) end = i; + } else if (code === 59 || code === 44) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + if (end === -1) end = i; + const name = header.slice(start, end); + if (code === 44) { + push(offers, name, params); + params = /* @__PURE__ */ Object.create(null); + } else { + extensionName = name; + } + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else if (paramName === void 0) { + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 32 || code === 9) { + if (end === -1 && start !== -1) end = i; + } else if (code === 59 || code === 44) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + if (end === -1) end = i; + push(params, header.slice(start, end), true); + if (code === 44) { + push(offers, extensionName, params); + params = /* @__PURE__ */ Object.create(null); + extensionName = void 0; + } + start = end = -1; + } else if (code === 61 && start !== -1 && end === -1) { + paramName = header.slice(start, i); + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else { + if (isEscaping) { + if (tokenChars[code] !== 1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + if (start === -1) start = i; + else if (!mustUnescape) mustUnescape = true; + isEscaping = false; + } else if (inQuotes) { + if (tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 34 && start !== -1) { + inQuotes = false; + end = i; + } else if (code === 92) { + isEscaping = true; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else if (code === 34 && header.charCodeAt(i - 1) === 61) { + inQuotes = true; + } else if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (start !== -1 && (code === 32 || code === 9)) { + if (end === -1) end = i; + } else if (code === 59 || code === 44) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + if (end === -1) end = i; + let value = header.slice(start, end); + if (mustUnescape) { + value = value.replace(/\\/g, ""); + mustUnescape = false; + } + push(params, paramName, value); + if (code === 44) { + push(offers, extensionName, params); + params = /* @__PURE__ */ Object.create(null); + extensionName = void 0; + } + paramName = void 0; + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } + } + if (start === -1 || inQuotes || code === 32 || code === 9) { + throw new SyntaxError("Unexpected end of input"); + } + if (end === -1) end = i; + const token = header.slice(start, end); + if (extensionName === void 0) { + push(offers, token, params); + } else { + if (paramName === void 0) { + push(params, token, true); + } else if (mustUnescape) { + push(params, paramName, token.replace(/\\/g, "")); + } else { + push(params, paramName, token); + } + push(offers, extensionName, params); + } + return offers; + } + function format(extensions) { + return Object.keys(extensions).map((extension) => { + let configurations = extensions[extension]; + if (!Array.isArray(configurations)) configurations = [configurations]; + return configurations.map((params) => { + return [extension].concat( + Object.keys(params).map((k) => { + let values = params[k]; + if (!Array.isArray(values)) values = [values]; + return values.map((v) => v === true ? k : `${k}=${v}`).join("; "); + }) + ).join("; "); + }).join(", "); + }).join(", "); + } + module2.exports = { format, parse }; + } +}); + +// node_modules/reactotron-core-server/node_modules/ws/lib/websocket.js +var require_websocket = __commonJS({ + "node_modules/reactotron-core-server/node_modules/ws/lib/websocket.js"(exports2, module2) { + "use strict"; + var EventEmitter = require("events"); + var https = require("https"); + var http = require("http"); + var net = require("net"); + var tls = require("tls"); + var { randomBytes, createHash } = require("crypto"); + var { Duplex, Readable } = require("stream"); + var { URL } = require("url"); + var PerMessageDeflate = require_permessage_deflate(); + var Receiver = require_receiver(); + var Sender = require_sender(); + var { isBlob } = require_validation(); + var { + BINARY_TYPES, + EMPTY_BUFFER, + GUID, + kForOnEventAttribute, + kListener, + kStatusCode, + kWebSocket, + NOOP + } = require_constants(); + var { + EventTarget: { addEventListener, removeEventListener } + } = require_event_target(); + var { format, parse } = require_extension(); + var { toBuffer } = require_buffer_util(); + var closeTimeout = 30 * 1e3; + var kAborted = Symbol("kAborted"); + var protocolVersions = [8, 13]; + var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"]; + var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/; + var WebSocket = class _WebSocket extends EventEmitter { + /** + * Create a new `WebSocket`. + * + * @param {(String|URL)} address The URL to which to connect + * @param {(String|String[])} [protocols] The subprotocols + * @param {Object} [options] Connection options + */ + constructor(address, protocols, options) { + super(); + this._binaryType = BINARY_TYPES[0]; + this._closeCode = 1006; + this._closeFrameReceived = false; + this._closeFrameSent = false; + this._closeMessage = EMPTY_BUFFER; + this._closeTimer = null; + this._errorEmitted = false; + this._extensions = {}; + this._paused = false; + this._protocol = ""; + this._readyState = _WebSocket.CONNECTING; + this._receiver = null; + this._sender = null; + this._socket = null; + if (address !== null) { + this._bufferedAmount = 0; + this._isServer = false; + this._redirects = 0; + if (protocols === void 0) { + protocols = []; + } else if (!Array.isArray(protocols)) { + if (typeof protocols === "object" && protocols !== null) { + options = protocols; + protocols = []; + } else { + protocols = [protocols]; + } + } + initAsClient(this, address, protocols, options); + } else { + this._autoPong = options.autoPong; + this._isServer = true; + } + } + /** + * For historical reasons, the custom "nodebuffer" type is used by the default + * instead of "blob". + * + * @type {String} + */ + get binaryType() { + return this._binaryType; + } + set binaryType(type) { + if (!BINARY_TYPES.includes(type)) return; + this._binaryType = type; + if (this._receiver) this._receiver._binaryType = type; + } + /** + * @type {Number} + */ + get bufferedAmount() { + if (!this._socket) return this._bufferedAmount; + return this._socket._writableState.length + this._sender._bufferedBytes; + } + /** + * @type {String} + */ + get extensions() { + return Object.keys(this._extensions).join(); + } + /** + * @type {Boolean} + */ + get isPaused() { + return this._paused; + } + /** + * @type {Function} + */ + /* istanbul ignore next */ + get onclose() { + return null; + } + /** + * @type {Function} + */ + /* istanbul ignore next */ + get onerror() { + return null; + } + /** + * @type {Function} + */ + /* istanbul ignore next */ + get onopen() { + return null; + } + /** + * @type {Function} + */ + /* istanbul ignore next */ + get onmessage() { + return null; + } + /** + * @type {String} + */ + get protocol() { + return this._protocol; + } + /** + * @type {Number} + */ + get readyState() { + return this._readyState; + } + /** + * @type {String} + */ + get url() { + return this._url; + } + /** + * Set up the socket and the internal resources. + * + * @param {Duplex} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Object} options Options object + * @param {Boolean} [options.allowSynchronousEvents=false] Specifies whether + * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted + * multiple times in the same tick + * @param {Function} [options.generateMask] The function used to generate the + * masking key + * @param {Number} [options.maxPayload=0] The maximum allowed message size + * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or + * not to skip UTF-8 validation for text and close messages + * @private + */ + setSocket(socket, head, options) { + const receiver = new Receiver({ + allowSynchronousEvents: options.allowSynchronousEvents, + binaryType: this.binaryType, + extensions: this._extensions, + isServer: this._isServer, + maxPayload: options.maxPayload, + skipUTF8Validation: options.skipUTF8Validation + }); + const sender = new Sender(socket, this._extensions, options.generateMask); + this._receiver = receiver; + this._sender = sender; + this._socket = socket; + receiver[kWebSocket] = this; + sender[kWebSocket] = this; + socket[kWebSocket] = this; + receiver.on("conclude", receiverOnConclude); + receiver.on("drain", receiverOnDrain); + receiver.on("error", receiverOnError); + receiver.on("message", receiverOnMessage); + receiver.on("ping", receiverOnPing); + receiver.on("pong", receiverOnPong); + sender.onerror = senderOnError; + if (socket.setTimeout) socket.setTimeout(0); + if (socket.setNoDelay) socket.setNoDelay(); + if (head.length > 0) socket.unshift(head); + socket.on("close", socketOnClose); + socket.on("data", socketOnData); + socket.on("end", socketOnEnd); + socket.on("error", socketOnError); + this._readyState = _WebSocket.OPEN; + this.emit("open"); + } + /** + * Emit the `'close'` event. + * + * @private + */ + emitClose() { + if (!this._socket) { + this._readyState = _WebSocket.CLOSED; + this.emit("close", this._closeCode, this._closeMessage); + return; + } + if (this._extensions[PerMessageDeflate.extensionName]) { + this._extensions[PerMessageDeflate.extensionName].cleanup(); + } + this._receiver.removeAllListeners(); + this._readyState = _WebSocket.CLOSED; + this.emit("close", this._closeCode, this._closeMessage); + } + /** + * Start a closing handshake. + * + * +----------+ +-----------+ +----------+ + * - - -|ws.close()|-->|close frame|-->|ws.close()|- - - + * | +----------+ +-----------+ +----------+ | + * +----------+ +-----------+ | + * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING + * +----------+ +-----------+ | + * | | | +---+ | + * +------------------------+-->|fin| - - - - + * | +---+ | +---+ + * - - - - -|fin|<---------------------+ + * +---+ + * + * @param {Number} [code] Status code explaining why the connection is closing + * @param {(String|Buffer)} [data] The reason why the connection is + * closing + * @public + */ + close(code, data) { + if (this.readyState === _WebSocket.CLOSED) return; + if (this.readyState === _WebSocket.CONNECTING) { + const msg = "WebSocket was closed before the connection was established"; + abortHandshake(this, this._req, msg); + return; + } + if (this.readyState === _WebSocket.CLOSING) { + if (this._closeFrameSent && (this._closeFrameReceived || this._receiver._writableState.errorEmitted)) { + this._socket.end(); + } + return; + } + this._readyState = _WebSocket.CLOSING; + this._sender.close(code, data, !this._isServer, (err) => { + if (err) return; + this._closeFrameSent = true; + if (this._closeFrameReceived || this._receiver._writableState.errorEmitted) { + this._socket.end(); + } + }); + setCloseTimer(this); + } + /** + * Pause the socket. + * + * @public + */ + pause() { + if (this.readyState === _WebSocket.CONNECTING || this.readyState === _WebSocket.CLOSED) { + return; + } + this._paused = true; + this._socket.pause(); + } + /** + * Send a ping. + * + * @param {*} [data] The data to send + * @param {Boolean} [mask] Indicates whether or not to mask `data` + * @param {Function} [cb] Callback which is executed when the ping is sent + * @public + */ + ping(data, mask, cb) { + if (this.readyState === _WebSocket.CONNECTING) { + throw new Error("WebSocket is not open: readyState 0 (CONNECTING)"); + } + if (typeof data === "function") { + cb = data; + data = mask = void 0; + } else if (typeof mask === "function") { + cb = mask; + mask = void 0; + } + if (typeof data === "number") data = data.toString(); + if (this.readyState !== _WebSocket.OPEN) { + sendAfterClose(this, data, cb); + return; + } + if (mask === void 0) mask = !this._isServer; + this._sender.ping(data || EMPTY_BUFFER, mask, cb); + } + /** + * Send a pong. + * + * @param {*} [data] The data to send + * @param {Boolean} [mask] Indicates whether or not to mask `data` + * @param {Function} [cb] Callback which is executed when the pong is sent + * @public + */ + pong(data, mask, cb) { + if (this.readyState === _WebSocket.CONNECTING) { + throw new Error("WebSocket is not open: readyState 0 (CONNECTING)"); + } + if (typeof data === "function") { + cb = data; + data = mask = void 0; + } else if (typeof mask === "function") { + cb = mask; + mask = void 0; + } + if (typeof data === "number") data = data.toString(); + if (this.readyState !== _WebSocket.OPEN) { + sendAfterClose(this, data, cb); + return; + } + if (mask === void 0) mask = !this._isServer; + this._sender.pong(data || EMPTY_BUFFER, mask, cb); + } + /** + * Resume the socket. + * + * @public + */ + resume() { + if (this.readyState === _WebSocket.CONNECTING || this.readyState === _WebSocket.CLOSED) { + return; + } + this._paused = false; + if (!this._receiver._writableState.needDrain) this._socket.resume(); + } + /** + * Send a data message. + * + * @param {*} data The message to send + * @param {Object} [options] Options object + * @param {Boolean} [options.binary] Specifies whether `data` is binary or + * text + * @param {Boolean} [options.compress] Specifies whether or not to compress + * `data` + * @param {Boolean} [options.fin=true] Specifies whether the fragment is the + * last one + * @param {Boolean} [options.mask] Specifies whether or not to mask `data` + * @param {Function} [cb] Callback which is executed when data is written out + * @public + */ + send(data, options, cb) { + if (this.readyState === _WebSocket.CONNECTING) { + throw new Error("WebSocket is not open: readyState 0 (CONNECTING)"); + } + if (typeof options === "function") { + cb = options; + options = {}; + } + if (typeof data === "number") data = data.toString(); + if (this.readyState !== _WebSocket.OPEN) { + sendAfterClose(this, data, cb); + return; + } + const opts = { + binary: typeof data !== "string", + mask: !this._isServer, + compress: true, + fin: true, + ...options + }; + if (!this._extensions[PerMessageDeflate.extensionName]) { + opts.compress = false; + } + this._sender.send(data || EMPTY_BUFFER, opts, cb); + } + /** + * Forcibly close the connection. + * + * @public + */ + terminate() { + if (this.readyState === _WebSocket.CLOSED) return; + if (this.readyState === _WebSocket.CONNECTING) { + const msg = "WebSocket was closed before the connection was established"; + abortHandshake(this, this._req, msg); + return; + } + if (this._socket) { + this._readyState = _WebSocket.CLOSING; + this._socket.destroy(); + } + } + }; + Object.defineProperty(WebSocket, "CONNECTING", { + enumerable: true, + value: readyStates.indexOf("CONNECTING") + }); + Object.defineProperty(WebSocket.prototype, "CONNECTING", { + enumerable: true, + value: readyStates.indexOf("CONNECTING") + }); + Object.defineProperty(WebSocket, "OPEN", { + enumerable: true, + value: readyStates.indexOf("OPEN") + }); + Object.defineProperty(WebSocket.prototype, "OPEN", { + enumerable: true, + value: readyStates.indexOf("OPEN") + }); + Object.defineProperty(WebSocket, "CLOSING", { + enumerable: true, + value: readyStates.indexOf("CLOSING") + }); + Object.defineProperty(WebSocket.prototype, "CLOSING", { + enumerable: true, + value: readyStates.indexOf("CLOSING") + }); + Object.defineProperty(WebSocket, "CLOSED", { + enumerable: true, + value: readyStates.indexOf("CLOSED") + }); + Object.defineProperty(WebSocket.prototype, "CLOSED", { + enumerable: true, + value: readyStates.indexOf("CLOSED") + }); + [ + "binaryType", + "bufferedAmount", + "extensions", + "isPaused", + "protocol", + "readyState", + "url" + ].forEach((property) => { + Object.defineProperty(WebSocket.prototype, property, { enumerable: true }); + }); + ["open", "error", "close", "message"].forEach((method) => { + Object.defineProperty(WebSocket.prototype, `on${method}`, { + enumerable: true, + get() { + for (const listener of this.listeners(method)) { + if (listener[kForOnEventAttribute]) return listener[kListener]; + } + return null; + }, + set(handler) { + for (const listener of this.listeners(method)) { + if (listener[kForOnEventAttribute]) { + this.removeListener(method, listener); + break; + } + } + if (typeof handler !== "function") return; + this.addEventListener(method, handler, { + [kForOnEventAttribute]: true + }); + } + }); + }); + WebSocket.prototype.addEventListener = addEventListener; + WebSocket.prototype.removeEventListener = removeEventListener; + module2.exports = WebSocket; + function initAsClient(websocket, address, protocols, options) { + const opts = { + allowSynchronousEvents: true, + autoPong: true, + protocolVersion: protocolVersions[1], + maxPayload: 100 * 1024 * 1024, + skipUTF8Validation: false, + perMessageDeflate: true, + followRedirects: false, + maxRedirects: 10, + ...options, + socketPath: void 0, + hostname: void 0, + protocol: void 0, + timeout: void 0, + method: "GET", + host: void 0, + path: void 0, + port: void 0 + }; + websocket._autoPong = opts.autoPong; + if (!protocolVersions.includes(opts.protocolVersion)) { + throw new RangeError( + `Unsupported protocol version: ${opts.protocolVersion} (supported versions: ${protocolVersions.join(", ")})` + ); + } + let parsedUrl; + if (address instanceof URL) { + parsedUrl = address; + } else { + try { + parsedUrl = new URL(address); + } catch (e) { + throw new SyntaxError(`Invalid URL: ${address}`); + } + } + if (parsedUrl.protocol === "http:") { + parsedUrl.protocol = "ws:"; + } else if (parsedUrl.protocol === "https:") { + parsedUrl.protocol = "wss:"; + } + websocket._url = parsedUrl.href; + const isSecure = parsedUrl.protocol === "wss:"; + const isIpcUrl = parsedUrl.protocol === "ws+unix:"; + let invalidUrlMessage; + if (parsedUrl.protocol !== "ws:" && !isSecure && !isIpcUrl) { + invalidUrlMessage = `The URL's protocol must be one of "ws:", "wss:", "http:", "https:", or "ws+unix:"`; + } else if (isIpcUrl && !parsedUrl.pathname) { + invalidUrlMessage = "The URL's pathname is empty"; + } else if (parsedUrl.hash) { + invalidUrlMessage = "The URL contains a fragment identifier"; + } + if (invalidUrlMessage) { + const err = new SyntaxError(invalidUrlMessage); + if (websocket._redirects === 0) { + throw err; + } else { + emitErrorAndClose(websocket, err); + return; + } + } + const defaultPort = isSecure ? 443 : 80; + const key = randomBytes(16).toString("base64"); + const request = isSecure ? https.request : http.request; + const protocolSet = /* @__PURE__ */ new Set(); + let perMessageDeflate; + opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect); + opts.defaultPort = opts.defaultPort || defaultPort; + opts.port = parsedUrl.port || defaultPort; + opts.host = parsedUrl.hostname.startsWith("[") ? parsedUrl.hostname.slice(1, -1) : parsedUrl.hostname; + opts.headers = { + ...opts.headers, + "Sec-WebSocket-Version": opts.protocolVersion, + "Sec-WebSocket-Key": key, + Connection: "Upgrade", + Upgrade: "websocket" + }; + opts.path = parsedUrl.pathname + parsedUrl.search; + opts.timeout = opts.handshakeTimeout; + if (opts.perMessageDeflate) { + perMessageDeflate = new PerMessageDeflate( + opts.perMessageDeflate !== true ? opts.perMessageDeflate : {}, + false, + opts.maxPayload + ); + opts.headers["Sec-WebSocket-Extensions"] = format({ + [PerMessageDeflate.extensionName]: perMessageDeflate.offer() + }); + } + if (protocols.length) { + for (const protocol of protocols) { + if (typeof protocol !== "string" || !subprotocolRegex.test(protocol) || protocolSet.has(protocol)) { + throw new SyntaxError( + "An invalid or duplicated subprotocol was specified" + ); + } + protocolSet.add(protocol); + } + opts.headers["Sec-WebSocket-Protocol"] = protocols.join(","); + } + if (opts.origin) { + if (opts.protocolVersion < 13) { + opts.headers["Sec-WebSocket-Origin"] = opts.origin; + } else { + opts.headers.Origin = opts.origin; + } + } + if (parsedUrl.username || parsedUrl.password) { + opts.auth = `${parsedUrl.username}:${parsedUrl.password}`; + } + if (isIpcUrl) { + const parts = opts.path.split(":"); + opts.socketPath = parts[0]; + opts.path = parts[1]; + } + let req; + if (opts.followRedirects) { + if (websocket._redirects === 0) { + websocket._originalIpc = isIpcUrl; + websocket._originalSecure = isSecure; + websocket._originalHostOrSocketPath = isIpcUrl ? opts.socketPath : parsedUrl.host; + const headers = options && options.headers; + options = { ...options, headers: {} }; + if (headers) { + for (const [key2, value] of Object.entries(headers)) { + options.headers[key2.toLowerCase()] = value; + } + } + } else if (websocket.listenerCount("redirect") === 0) { + const isSameHost = isIpcUrl ? websocket._originalIpc ? opts.socketPath === websocket._originalHostOrSocketPath : false : websocket._originalIpc ? false : parsedUrl.host === websocket._originalHostOrSocketPath; + if (!isSameHost || websocket._originalSecure && !isSecure) { + delete opts.headers.authorization; + delete opts.headers.cookie; + if (!isSameHost) delete opts.headers.host; + opts.auth = void 0; + } + } + if (opts.auth && !options.headers.authorization) { + options.headers.authorization = "Basic " + Buffer.from(opts.auth).toString("base64"); + } + req = websocket._req = request(opts); + if (websocket._redirects) { + websocket.emit("redirect", websocket.url, req); + } + } else { + req = websocket._req = request(opts); + } + if (opts.timeout) { + req.on("timeout", () => { + abortHandshake(websocket, req, "Opening handshake has timed out"); + }); + } + req.on("error", (err) => { + if (req === null || req[kAborted]) return; + req = websocket._req = null; + emitErrorAndClose(websocket, err); + }); + req.on("response", (res) => { + const location = res.headers.location; + const statusCode = res.statusCode; + if (location && opts.followRedirects && statusCode >= 300 && statusCode < 400) { + if (++websocket._redirects > opts.maxRedirects) { + abortHandshake(websocket, req, "Maximum redirects exceeded"); + return; + } + req.abort(); + let addr; + try { + addr = new URL(location, address); + } catch (e) { + const err = new SyntaxError(`Invalid URL: ${location}`); + emitErrorAndClose(websocket, err); + return; + } + initAsClient(websocket, addr, protocols, options); + } else if (!websocket.emit("unexpected-response", req, res)) { + abortHandshake( + websocket, + req, + `Unexpected server response: ${res.statusCode}` + ); + } + }); + req.on("upgrade", (res, socket, head) => { + websocket.emit("upgrade", res); + if (websocket.readyState !== WebSocket.CONNECTING) return; + req = websocket._req = null; + const upgrade = res.headers.upgrade; + if (upgrade === void 0 || upgrade.toLowerCase() !== "websocket") { + abortHandshake(websocket, socket, "Invalid Upgrade header"); + return; + } + const digest = createHash("sha1").update(key + GUID).digest("base64"); + if (res.headers["sec-websocket-accept"] !== digest) { + abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header"); + return; + } + const serverProt = res.headers["sec-websocket-protocol"]; + let protError; + if (serverProt !== void 0) { + if (!protocolSet.size) { + protError = "Server sent a subprotocol but none was requested"; + } else if (!protocolSet.has(serverProt)) { + protError = "Server sent an invalid subprotocol"; + } + } else if (protocolSet.size) { + protError = "Server sent no subprotocol"; + } + if (protError) { + abortHandshake(websocket, socket, protError); + return; + } + if (serverProt) websocket._protocol = serverProt; + const secWebSocketExtensions = res.headers["sec-websocket-extensions"]; + if (secWebSocketExtensions !== void 0) { + if (!perMessageDeflate) { + const message = "Server sent a Sec-WebSocket-Extensions header but no extension was requested"; + abortHandshake(websocket, socket, message); + return; + } + let extensions; + try { + extensions = parse(secWebSocketExtensions); + } catch (err) { + const message = "Invalid Sec-WebSocket-Extensions header"; + abortHandshake(websocket, socket, message); + return; + } + const extensionNames = Object.keys(extensions); + if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate.extensionName) { + const message = "Server indicated an extension that was not requested"; + abortHandshake(websocket, socket, message); + return; + } + try { + perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]); + } catch (err) { + const message = "Invalid Sec-WebSocket-Extensions header"; + abortHandshake(websocket, socket, message); + return; + } + websocket._extensions[PerMessageDeflate.extensionName] = perMessageDeflate; + } + websocket.setSocket(socket, head, { + allowSynchronousEvents: opts.allowSynchronousEvents, + generateMask: opts.generateMask, + maxPayload: opts.maxPayload, + skipUTF8Validation: opts.skipUTF8Validation + }); + }); + if (opts.finishRequest) { + opts.finishRequest(req, websocket); + } else { + req.end(); + } + } + function emitErrorAndClose(websocket, err) { + websocket._readyState = WebSocket.CLOSING; + websocket._errorEmitted = true; + websocket.emit("error", err); + websocket.emitClose(); + } + function netConnect(options) { + options.path = options.socketPath; + return net.connect(options); + } + function tlsConnect(options) { + options.path = void 0; + if (!options.servername && options.servername !== "") { + options.servername = net.isIP(options.host) ? "" : options.host; + } + return tls.connect(options); + } + function abortHandshake(websocket, stream, message) { + websocket._readyState = WebSocket.CLOSING; + const err = new Error(message); + Error.captureStackTrace(err, abortHandshake); + if (stream.setHeader) { + stream[kAborted] = true; + stream.abort(); + if (stream.socket && !stream.socket.destroyed) { + stream.socket.destroy(); + } + process.nextTick(emitErrorAndClose, websocket, err); + } else { + stream.destroy(err); + stream.once("error", websocket.emit.bind(websocket, "error")); + stream.once("close", websocket.emitClose.bind(websocket)); + } + } + function sendAfterClose(websocket, data, cb) { + if (data) { + const length = isBlob(data) ? data.size : toBuffer(data).length; + if (websocket._socket) websocket._sender._bufferedBytes += length; + else websocket._bufferedAmount += length; + } + if (cb) { + const err = new Error( + `WebSocket is not open: readyState ${websocket.readyState} (${readyStates[websocket.readyState]})` + ); + process.nextTick(cb, err); + } + } + function receiverOnConclude(code, reason) { + const websocket = this[kWebSocket]; + websocket._closeFrameReceived = true; + websocket._closeMessage = reason; + websocket._closeCode = code; + if (websocket._socket[kWebSocket] === void 0) return; + websocket._socket.removeListener("data", socketOnData); + process.nextTick(resume, websocket._socket); + if (code === 1005) websocket.close(); + else websocket.close(code, reason); + } + function receiverOnDrain() { + const websocket = this[kWebSocket]; + if (!websocket.isPaused) websocket._socket.resume(); + } + function receiverOnError(err) { + const websocket = this[kWebSocket]; + if (websocket._socket[kWebSocket] !== void 0) { + websocket._socket.removeListener("data", socketOnData); + process.nextTick(resume, websocket._socket); + websocket.close(err[kStatusCode]); + } + if (!websocket._errorEmitted) { + websocket._errorEmitted = true; + websocket.emit("error", err); + } + } + function receiverOnFinish() { + this[kWebSocket].emitClose(); + } + function receiverOnMessage(data, isBinary) { + this[kWebSocket].emit("message", data, isBinary); + } + function receiverOnPing(data) { + const websocket = this[kWebSocket]; + if (websocket._autoPong) websocket.pong(data, !this._isServer, NOOP); + websocket.emit("ping", data); + } + function receiverOnPong(data) { + this[kWebSocket].emit("pong", data); + } + function resume(stream) { + stream.resume(); + } + function senderOnError(err) { + const websocket = this[kWebSocket]; + if (websocket.readyState === WebSocket.CLOSED) return; + if (websocket.readyState === WebSocket.OPEN) { + websocket._readyState = WebSocket.CLOSING; + setCloseTimer(websocket); + } + this._socket.end(); + if (!websocket._errorEmitted) { + websocket._errorEmitted = true; + websocket.emit("error", err); + } + } + function setCloseTimer(websocket) { + websocket._closeTimer = setTimeout( + websocket._socket.destroy.bind(websocket._socket), + closeTimeout + ); + } + function socketOnClose() { + const websocket = this[kWebSocket]; + this.removeListener("close", socketOnClose); + this.removeListener("data", socketOnData); + this.removeListener("end", socketOnEnd); + websocket._readyState = WebSocket.CLOSING; + let chunk; + if (!this._readableState.endEmitted && !websocket._closeFrameReceived && !websocket._receiver._writableState.errorEmitted && (chunk = websocket._socket.read()) !== null) { + websocket._receiver.write(chunk); + } + websocket._receiver.end(); + this[kWebSocket] = void 0; + clearTimeout(websocket._closeTimer); + if (websocket._receiver._writableState.finished || websocket._receiver._writableState.errorEmitted) { + websocket.emitClose(); + } else { + websocket._receiver.on("error", receiverOnFinish); + websocket._receiver.on("finish", receiverOnFinish); + } + } + function socketOnData(chunk) { + if (!this[kWebSocket]._receiver.write(chunk)) { + this.pause(); + } + } + function socketOnEnd() { + const websocket = this[kWebSocket]; + websocket._readyState = WebSocket.CLOSING; + websocket._receiver.end(); + this.end(); + } + function socketOnError() { + const websocket = this[kWebSocket]; + this.removeListener("error", socketOnError); + this.on("error", NOOP); + if (websocket) { + websocket._readyState = WebSocket.CLOSING; + this.destroy(); + } + } + } +}); + +// node_modules/reactotron-core-server/node_modules/ws/lib/stream.js +var require_stream = __commonJS({ + "node_modules/reactotron-core-server/node_modules/ws/lib/stream.js"(exports2, module2) { + "use strict"; + var WebSocket = require_websocket(); + var { Duplex } = require("stream"); + function emitClose(stream) { + stream.emit("close"); + } + function duplexOnEnd() { + if (!this.destroyed && this._writableState.finished) { + this.destroy(); + } + } + function duplexOnError(err) { + this.removeListener("error", duplexOnError); + this.destroy(); + if (this.listenerCount("error") === 0) { + this.emit("error", err); + } + } + function createWebSocketStream(ws, options) { + let terminateOnDestroy = true; + const duplex = new Duplex({ + ...options, + autoDestroy: false, + emitClose: false, + objectMode: false, + writableObjectMode: false + }); + ws.on("message", function message(msg, isBinary) { + const data = !isBinary && duplex._readableState.objectMode ? msg.toString() : msg; + if (!duplex.push(data)) ws.pause(); + }); + ws.once("error", function error(err) { + if (duplex.destroyed) return; + terminateOnDestroy = false; + duplex.destroy(err); + }); + ws.once("close", function close() { + if (duplex.destroyed) return; + duplex.push(null); + }); + duplex._destroy = function(err, callback) { + if (ws.readyState === ws.CLOSED) { + callback(err); + process.nextTick(emitClose, duplex); + return; + } + let called = false; + ws.once("error", function error(err2) { + called = true; + callback(err2); + }); + ws.once("close", function close() { + if (!called) callback(err); + process.nextTick(emitClose, duplex); + }); + if (terminateOnDestroy) ws.terminate(); + }; + duplex._final = function(callback) { + if (ws.readyState === ws.CONNECTING) { + ws.once("open", function open() { + duplex._final(callback); + }); + return; + } + if (ws._socket === null) return; + if (ws._socket._writableState.finished) { + callback(); + if (duplex._readableState.endEmitted) duplex.destroy(); + } else { + ws._socket.once("finish", function finish() { + callback(); + }); + ws.close(); + } + }; + duplex._read = function() { + if (ws.isPaused) ws.resume(); + }; + duplex._write = function(chunk, encoding, callback) { + if (ws.readyState === ws.CONNECTING) { + ws.once("open", function open() { + duplex._write(chunk, encoding, callback); + }); + return; + } + ws.send(chunk, callback); + }; + duplex.on("end", duplexOnEnd); + duplex.on("error", duplexOnError); + return duplex; + } + module2.exports = createWebSocketStream; + } +}); + +// node_modules/reactotron-core-server/node_modules/ws/lib/subprotocol.js +var require_subprotocol = __commonJS({ + "node_modules/reactotron-core-server/node_modules/ws/lib/subprotocol.js"(exports2, module2) { + "use strict"; + var { tokenChars } = require_validation(); + function parse(header) { + const protocols = /* @__PURE__ */ new Set(); + let start = -1; + let end = -1; + let i = 0; + for (i; i < header.length; i++) { + const code = header.charCodeAt(i); + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (i !== 0 && (code === 32 || code === 9)) { + if (end === -1 && start !== -1) end = i; + } else if (code === 44) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + if (end === -1) end = i; + const protocol2 = header.slice(start, end); + if (protocols.has(protocol2)) { + throw new SyntaxError(`The "${protocol2}" subprotocol is duplicated`); + } + protocols.add(protocol2); + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } + if (start === -1 || end !== -1) { + throw new SyntaxError("Unexpected end of input"); + } + const protocol = header.slice(start, i); + if (protocols.has(protocol)) { + throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`); + } + protocols.add(protocol); + return protocols; + } + module2.exports = { parse }; + } +}); + +// node_modules/reactotron-core-server/node_modules/ws/lib/websocket-server.js +var require_websocket_server = __commonJS({ + "node_modules/reactotron-core-server/node_modules/ws/lib/websocket-server.js"(exports2, module2) { + "use strict"; + var EventEmitter = require("events"); + var http = require("http"); + var { Duplex } = require("stream"); + var { createHash } = require("crypto"); + var extension = require_extension(); + var PerMessageDeflate = require_permessage_deflate(); + var subprotocol = require_subprotocol(); + var WebSocket = require_websocket(); + var { GUID, kWebSocket } = require_constants(); + var keyRegex = /^[+/0-9A-Za-z]{22}==$/; + var RUNNING = 0; + var CLOSING = 1; + var CLOSED = 2; + var WebSocketServer = class extends EventEmitter { + /** + * Create a `WebSocketServer` instance. + * + * @param {Object} options Configuration options + * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether + * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted + * multiple times in the same tick + * @param {Boolean} [options.autoPong=true] Specifies whether or not to + * automatically send a pong in response to a ping + * @param {Number} [options.backlog=511] The maximum length of the queue of + * pending connections + * @param {Boolean} [options.clientTracking=true] Specifies whether or not to + * track clients + * @param {Function} [options.handleProtocols] A hook to handle protocols + * @param {String} [options.host] The hostname where to bind the server + * @param {Number} [options.maxPayload=104857600] The maximum allowed message + * size + * @param {Boolean} [options.noServer=false] Enable no server mode + * @param {String} [options.path] Accept only connections matching this path + * @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable + * permessage-deflate + * @param {Number} [options.port] The port where to bind the server + * @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S + * server to use + * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or + * not to skip UTF-8 validation for text and close messages + * @param {Function} [options.verifyClient] A hook to reject connections + * @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket` + * class to use. It must be the `WebSocket` class or class that extends it + * @param {Function} [callback] A listener for the `listening` event + */ + constructor(options, callback) { + super(); + options = { + allowSynchronousEvents: true, + autoPong: true, + maxPayload: 100 * 1024 * 1024, + skipUTF8Validation: false, + perMessageDeflate: false, + handleProtocols: null, + clientTracking: true, + verifyClient: null, + noServer: false, + backlog: null, + // use default (511 as implemented in net.js) + server: null, + host: null, + path: null, + port: null, + WebSocket, + ...options + }; + if (options.port == null && !options.server && !options.noServer || options.port != null && (options.server || options.noServer) || options.server && options.noServer) { + throw new TypeError( + 'One and only one of the "port", "server", or "noServer" options must be specified' + ); + } + if (options.port != null) { + this._server = http.createServer((req, res) => { + const body = http.STATUS_CODES[426]; + res.writeHead(426, { + "Content-Length": body.length, + "Content-Type": "text/plain" + }); + res.end(body); + }); + this._server.listen( + options.port, + options.host, + options.backlog, + callback + ); + } else if (options.server) { + this._server = options.server; + } + if (this._server) { + const emitConnection = this.emit.bind(this, "connection"); + this._removeListeners = addListeners(this._server, { + listening: this.emit.bind(this, "listening"), + error: this.emit.bind(this, "error"), + upgrade: (req, socket, head) => { + this.handleUpgrade(req, socket, head, emitConnection); + } + }); + } + if (options.perMessageDeflate === true) options.perMessageDeflate = {}; + if (options.clientTracking) { + this.clients = /* @__PURE__ */ new Set(); + this._shouldEmitClose = false; + } + this.options = options; + this._state = RUNNING; + } + /** + * Returns the bound address, the address family name, and port of the server + * as reported by the operating system if listening on an IP socket. + * If the server is listening on a pipe or UNIX domain socket, the name is + * returned as a string. + * + * @return {(Object|String|null)} The address of the server + * @public + */ + address() { + if (this.options.noServer) { + throw new Error('The server is operating in "noServer" mode'); + } + if (!this._server) return null; + return this._server.address(); + } + /** + * Stop the server from accepting new connections and emit the `'close'` event + * when all existing connections are closed. + * + * @param {Function} [cb] A one-time listener for the `'close'` event + * @public + */ + close(cb) { + if (this._state === CLOSED) { + if (cb) { + this.once("close", () => { + cb(new Error("The server is not running")); + }); + } + process.nextTick(emitClose, this); + return; + } + if (cb) this.once("close", cb); + if (this._state === CLOSING) return; + this._state = CLOSING; + if (this.options.noServer || this.options.server) { + if (this._server) { + this._removeListeners(); + this._removeListeners = this._server = null; + } + if (this.clients) { + if (!this.clients.size) { + process.nextTick(emitClose, this); + } else { + this._shouldEmitClose = true; + } + } else { + process.nextTick(emitClose, this); + } + } else { + const server = this._server; + this._removeListeners(); + this._removeListeners = this._server = null; + server.close(() => { + emitClose(this); + }); + } + } + /** + * See if a given request should be handled by this server instance. + * + * @param {http.IncomingMessage} req Request object to inspect + * @return {Boolean} `true` if the request is valid, else `false` + * @public + */ + shouldHandle(req) { + if (this.options.path) { + const index = req.url.indexOf("?"); + const pathname = index !== -1 ? req.url.slice(0, index) : req.url; + if (pathname !== this.options.path) return false; + } + return true; + } + /** + * Handle a HTTP Upgrade request. + * + * @param {http.IncomingMessage} req The request object + * @param {Duplex} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Function} cb Callback + * @public + */ + handleUpgrade(req, socket, head, cb) { + socket.on("error", socketOnError); + const key = req.headers["sec-websocket-key"]; + const upgrade = req.headers.upgrade; + const version = +req.headers["sec-websocket-version"]; + if (req.method !== "GET") { + const message = "Invalid HTTP method"; + abortHandshakeOrEmitwsClientError(this, req, socket, 405, message); + return; + } + if (upgrade === void 0 || upgrade.toLowerCase() !== "websocket") { + const message = "Invalid Upgrade header"; + abortHandshakeOrEmitwsClientError(this, req, socket, 400, message); + return; + } + if (key === void 0 || !keyRegex.test(key)) { + const message = "Missing or invalid Sec-WebSocket-Key header"; + abortHandshakeOrEmitwsClientError(this, req, socket, 400, message); + return; + } + if (version !== 13 && version !== 8) { + const message = "Missing or invalid Sec-WebSocket-Version header"; + abortHandshakeOrEmitwsClientError(this, req, socket, 400, message, { + "Sec-WebSocket-Version": "13, 8" + }); + return; + } + if (!this.shouldHandle(req)) { + abortHandshake(socket, 400); + return; + } + const secWebSocketProtocol = req.headers["sec-websocket-protocol"]; + let protocols = /* @__PURE__ */ new Set(); + if (secWebSocketProtocol !== void 0) { + try { + protocols = subprotocol.parse(secWebSocketProtocol); + } catch (err) { + const message = "Invalid Sec-WebSocket-Protocol header"; + abortHandshakeOrEmitwsClientError(this, req, socket, 400, message); + return; + } + } + const secWebSocketExtensions = req.headers["sec-websocket-extensions"]; + const extensions = {}; + if (this.options.perMessageDeflate && secWebSocketExtensions !== void 0) { + const perMessageDeflate = new PerMessageDeflate( + this.options.perMessageDeflate, + true, + this.options.maxPayload + ); + try { + const offers = extension.parse(secWebSocketExtensions); + if (offers[PerMessageDeflate.extensionName]) { + perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]); + extensions[PerMessageDeflate.extensionName] = perMessageDeflate; + } + } catch (err) { + const message = "Invalid or unacceptable Sec-WebSocket-Extensions header"; + abortHandshakeOrEmitwsClientError(this, req, socket, 400, message); + return; + } + } + if (this.options.verifyClient) { + const info = { + origin: req.headers[`${version === 8 ? "sec-websocket-origin" : "origin"}`], + secure: !!(req.socket.authorized || req.socket.encrypted), + req + }; + if (this.options.verifyClient.length === 2) { + this.options.verifyClient(info, (verified, code, message, headers) => { + if (!verified) { + return abortHandshake(socket, code || 401, message, headers); + } + this.completeUpgrade( + extensions, + key, + protocols, + req, + socket, + head, + cb + ); + }); + return; + } + if (!this.options.verifyClient(info)) return abortHandshake(socket, 401); + } + this.completeUpgrade(extensions, key, protocols, req, socket, head, cb); + } + /** + * Upgrade the connection to WebSocket. + * + * @param {Object} extensions The accepted extensions + * @param {String} key The value of the `Sec-WebSocket-Key` header + * @param {Set} protocols The subprotocols + * @param {http.IncomingMessage} req The request object + * @param {Duplex} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Function} cb Callback + * @throws {Error} If called more than once with the same socket + * @private + */ + completeUpgrade(extensions, key, protocols, req, socket, head, cb) { + if (!socket.readable || !socket.writable) return socket.destroy(); + if (socket[kWebSocket]) { + throw new Error( + "server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration" + ); + } + if (this._state > RUNNING) return abortHandshake(socket, 503); + const digest = createHash("sha1").update(key + GUID).digest("base64"); + const headers = [ + "HTTP/1.1 101 Switching Protocols", + "Upgrade: websocket", + "Connection: Upgrade", + `Sec-WebSocket-Accept: ${digest}` + ]; + const ws = new this.options.WebSocket(null, void 0, this.options); + if (protocols.size) { + const protocol = this.options.handleProtocols ? this.options.handleProtocols(protocols, req) : protocols.values().next().value; + if (protocol) { + headers.push(`Sec-WebSocket-Protocol: ${protocol}`); + ws._protocol = protocol; + } + } + if (extensions[PerMessageDeflate.extensionName]) { + const params = extensions[PerMessageDeflate.extensionName].params; + const value = extension.format({ + [PerMessageDeflate.extensionName]: [params] + }); + headers.push(`Sec-WebSocket-Extensions: ${value}`); + ws._extensions = extensions; + } + this.emit("headers", headers, req); + socket.write(headers.concat("\r\n").join("\r\n")); + socket.removeListener("error", socketOnError); + ws.setSocket(socket, head, { + allowSynchronousEvents: this.options.allowSynchronousEvents, + maxPayload: this.options.maxPayload, + skipUTF8Validation: this.options.skipUTF8Validation + }); + if (this.clients) { + this.clients.add(ws); + ws.on("close", () => { + this.clients.delete(ws); + if (this._shouldEmitClose && !this.clients.size) { + process.nextTick(emitClose, this); + } + }); + } + cb(ws, req); + } + }; + module2.exports = WebSocketServer; + function addListeners(server, map) { + for (const event of Object.keys(map)) server.on(event, map[event]); + return function removeListeners() { + for (const event of Object.keys(map)) { + server.removeListener(event, map[event]); + } + }; + } + function emitClose(server) { + server._state = CLOSED; + server.emit("close"); + } + function socketOnError() { + this.destroy(); + } + function abortHandshake(socket, code, message, headers) { + message = message || http.STATUS_CODES[code]; + headers = { + Connection: "close", + "Content-Type": "text/html", + "Content-Length": Buffer.byteLength(message), + ...headers + }; + socket.once("finish", socket.destroy); + socket.end( + `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r +` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message + ); + } + function abortHandshakeOrEmitwsClientError(server, req, socket, code, message, headers) { + if (server.listenerCount("wsClientError")) { + const err = new Error(message); + Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError); + server.emit("wsClientError", err, socket, req); + } else { + abortHandshake(socket, code, message, headers); + } + } + } +}); + +// node_modules/reactotron-core-server/node_modules/ws/index.js +var require_ws = __commonJS({ + "node_modules/reactotron-core-server/node_modules/ws/index.js"(exports2, module2) { + "use strict"; + var WebSocket = require_websocket(); + WebSocket.createWebSocketStream = require_stream(); + WebSocket.Server = require_websocket_server(); + WebSocket.Receiver = require_receiver(); + WebSocket.Sender = require_sender(); + WebSocket.WebSocket = WebSocket; + WebSocket.WebSocketServer = WebSocket.Server; + module2.exports = WebSocket; + } +}); + +// node_modules/mitt/dist/mitt.mjs +var mitt_exports = {}; +__export(mitt_exports, { + default: () => mitt_default +}); +function mitt_default(n) { + return { all: n = n || /* @__PURE__ */ new Map(), on: function(t, e) { + var i = n.get(t); + i ? i.push(e) : n.set(t, [e]); + }, off: function(t, e) { + var i = n.get(t); + i && (e ? i.splice(i.indexOf(e) >>> 0, 1) : n.set(t, [])); + }, emit: function(t, e) { + var i = n.get(t); + i && i.slice().map(function(n2) { + n2(e); + }), (i = n.get("*")) && i.slice().map(function(n2) { + n2(t, e); + }); + } }; +} +var init_mitt = __esm({ + "node_modules/mitt/dist/mitt.mjs"() { + } +}); + +// node_modules/reactotron-core-server/dist/index.js +var require_dist = __commonJS({ + "node_modules/reactotron-core-server/dist/index.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + var https = require("https"); + var ws = require_ws(); + var fs = require("fs"); + function _toPrimitive(a, b) { + if ("object" != typeof a || !a) return a; + var c = a[Symbol.toPrimitive]; + if (void 0 !== c) { + var d = c.call(a, b || "default"); + if ("object" != typeof d) return d; + throw new TypeError("@@toPrimitive must return a primitive value."); + } + return ("string" === b ? String : Number)(a); + } + function _toPropertyKey(a) { + var b = _toPrimitive(a, "string"); + return "symbol" == typeof b ? b : b + ""; + } + function _typeof(a) { + "@babel/helpers - typeof"; + return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(a2) { + return typeof a2; + } : function(a2) { + return a2 && "function" == typeof Symbol && a2.constructor === Symbol && a2 !== Symbol.prototype ? "symbol" : typeof a2; + }, _typeof(a); + } + function _classCallCheck(a, b) { + if (!(a instanceof b)) throw new TypeError("Cannot call a class as a function"); + } + function _defineProperties(a, b) { + for (var c, d = 0; d < b.length; d++) c = b[d], c.enumerable = c.enumerable || false, c.configurable = true, "value" in c && (c.writable = true), Object.defineProperty(a, _toPropertyKey(c.key), c); + } + function _createClass(a, b, c) { + return b && _defineProperties(a.prototype, b), c && _defineProperties(a, c), Object.defineProperty(a, "prototype", { writable: false }), a; + } + function _defineProperty(a, b, c) { + return b = _toPropertyKey(b), b in a ? Object.defineProperty(a, b, { value: c, enumerable: true, configurable: true, writable: true }) : a[b] = c, a; + } + function _isPlaceholder(b) { + return null != b && "object" == typeof b && true === b["@@functional/placeholder"]; + } + function _curry1(b) { + return function a(c) { + return 0 === arguments.length || _isPlaceholder(c) ? a : b.apply(this, arguments); + }; + } + function _curry2(c) { + return function b(d, a) { + switch (arguments.length) { + case 0: + return b; + case 1: + return _isPlaceholder(d) ? b : _curry1(function(a2) { + return c(d, a2); + }); + default: + return _isPlaceholder(d) && _isPlaceholder(a) ? b : _isPlaceholder(d) ? _curry1(function(b2) { + return c(b2, a); + }) : _isPlaceholder(a) ? _curry1(function(a2) { + return c(d, a2); + }) : c(d, a); + } + }; + } + function _concat(a, b) { + a = a || [], b = b || []; + var c, d = a.length, e = b.length, f = []; + for (c = 0; c < d; ) f[f.length] = a[c], c += 1; + for (c = 0; c < e; ) f[f.length] = b[c], c += 1; + return f; + } + function _arity(a, b) { + switch (a) { + case 0: + return function() { + return b.apply(this, arguments); + }; + case 1: + return function() { + return b.apply(this, arguments); + }; + case 2: + return function() { + return b.apply(this, arguments); + }; + case 3: + return function() { + return b.apply(this, arguments); + }; + case 4: + return function() { + return b.apply(this, arguments); + }; + case 5: + return function() { + return b.apply(this, arguments); + }; + case 6: + return function() { + return b.apply(this, arguments); + }; + case 7: + return function() { + return b.apply(this, arguments); + }; + case 8: + return function() { + return b.apply(this, arguments); + }; + case 9: + return function() { + return b.apply(this, arguments); + }; + case 10: + return function() { + return b.apply(this, arguments); + }; + default: + throw new Error("First argument to _arity must be a non-negative integer no greater than ten"); + } + } + function _curryN(a, b, c) { + return function() { + for (var d = [], e = 0, f = a, g = 0; g < b.length || e < arguments.length; ) { + var h; + g < b.length && (!_isPlaceholder(b[g]) || e >= arguments.length) ? h = b[g] : (h = arguments[e], e += 1), d[g] = h, _isPlaceholder(h) || (f -= 1), g += 1; + } + return 0 >= f ? c.apply(this, d) : _arity(f, _curryN(a, d, c)); + }; + } + var curryN = _curry2(function(a, b) { + return 1 === a ? _curry1(b) : _arity(a, _curryN(a, [], b)); + }); + function _curry3(d) { + return function c(e, a, b) { + switch (arguments.length) { + case 0: + return c; + case 1: + return _isPlaceholder(e) ? c : _curry2(function(a2, b2) { + return d(e, a2, b2); + }); + case 2: + return _isPlaceholder(e) && _isPlaceholder(a) ? c : _isPlaceholder(e) ? _curry2(function(b2, c2) { + return d(b2, a, c2); + }) : _isPlaceholder(a) ? _curry2(function(a2, b2) { + return d(e, a2, b2); + }) : _curry1(function(b2) { + return d(e, a, b2); + }); + default: + return _isPlaceholder(e) && _isPlaceholder(a) && _isPlaceholder(b) ? c : _isPlaceholder(e) && _isPlaceholder(a) ? _curry2(function(a2, c2) { + return d(a2, c2, b); + }) : _isPlaceholder(e) && _isPlaceholder(b) ? _curry2(function(b2, c2) { + return d(b2, a, c2); + }) : _isPlaceholder(a) && _isPlaceholder(b) ? _curry2(function(a2, b2) { + return d(e, a2, b2); + }) : _isPlaceholder(e) ? _curry1(function(c2) { + return d(c2, a, b); + }) : _isPlaceholder(a) ? _curry1(function(a2) { + return d(e, a2, b); + }) : _isPlaceholder(b) ? _curry1(function(b2) { + return d(e, a, b2); + }) : d(e, a, b); + } + }; + } + var _isArray = Array.isArray || function(a) { + return null != a && 0 <= a.length && "[object Array]" === Object.prototype.toString.call(a); + }; + function _isTransformer(a) { + return null != a && "function" == typeof a["@@transducer/step"]; + } + function _dispatchable(a, b, c) { + return function() { + if (0 === arguments.length) return c(); + var d = arguments[arguments.length - 1]; + if (!_isArray(d)) { + for (var e = 0; e < a.length; ) { + if ("function" == typeof d[a[e]]) return d[a[e]].apply(d, Array.prototype.slice.call(arguments, 0, -1)); + e += 1; + } + if (_isTransformer(d)) { + var f = b.apply(null, Array.prototype.slice.call(arguments, 0, -1)); + return f(d); + } + } + return c.apply(this, arguments); + }; + } + function _reduced(a) { + return a && a["@@transducer/reduced"] ? a : { "@@transducer/value": a, "@@transducer/reduced": true }; + } + var _xfBase = { init: function() { + return this.xf["@@transducer/init"](); + }, result: function(a) { + return this.xf["@@transducer/result"](a); + } }; + var max = _curry2(function(c, a) { + return a > c ? a : c; + }); + function _map(a, b) { + for (var c = 0, d = b.length, e = Array(d); c < d; ) e[c] = a(b[c]), c += 1; + return e; + } + function _isString(a) { + return "[object String]" === Object.prototype.toString.call(a); + } + var _isArrayLike = _curry1(function(a) { + return !!_isArray(a) || !!a && !("object" != typeof a) && !_isString(a) && (!(0 !== a.length) || !!(0 < a.length) && a.hasOwnProperty(0) && a.hasOwnProperty(a.length - 1)); + }); + var XWrap = function() { + function a(a2) { + this.f = a2; + } + return a.prototype["@@transducer/init"] = function() { + throw new Error("init not implemented on XWrap"); + }, a.prototype["@@transducer/result"] = function(a2) { + return a2; + }, a.prototype["@@transducer/step"] = function(a2, b) { + return this.f(a2, b); + }, a; + }(); + function _xwrap(a) { + return new XWrap(a); + } + var bind = _curry2(function(a, b) { + return _arity(a.length, function() { + return a.apply(b, arguments); + }); + }); + function _arrayReduce(a, b, c) { + for (var d = 0, e = c.length; d < e; ) { + if (b = a["@@transducer/step"](b, c[d]), b && b["@@transducer/reduced"]) { + b = b["@@transducer/value"]; + break; + } + d += 1; + } + return a["@@transducer/result"](b); + } + function _iterableReduce(a, b, c) { + for (var d = c.next(); !d.done; ) { + if (b = a["@@transducer/step"](b, d.value), b && b["@@transducer/reduced"]) { + b = b["@@transducer/value"]; + break; + } + d = c.next(); + } + return a["@@transducer/result"](b); + } + function _methodReduce(a, b, c, d) { + return a["@@transducer/result"](c[d](bind(a["@@transducer/step"], a), b)); + } + var symIterator = "undefined" == typeof Symbol ? "@@iterator" : Symbol.iterator; + function _reduce(a, b, c) { + if ("function" == typeof a && (a = _xwrap(a)), _isArrayLike(c)) return _arrayReduce(a, b, c); + if ("function" == typeof c["fantasy-land/reduce"]) return _methodReduce(a, b, c, "fantasy-land/reduce"); + if (null != c[symIterator]) return _iterableReduce(a, b, c[symIterator]()); + if ("function" == typeof c.next) return _iterableReduce(a, b, c); + if ("function" == typeof c.reduce) return _methodReduce(a, b, c, "reduce"); + throw new TypeError("reduce: list must be array or iterable"); + } + var XMap = function() { + function a(a2, b) { + this.xf = b, this.f = a2; + } + return a.prototype["@@transducer/init"] = _xfBase.init, a.prototype["@@transducer/result"] = _xfBase.result, a.prototype["@@transducer/step"] = function(a2, b) { + return this.xf["@@transducer/step"](a2, this.f(b)); + }, a; + }(); + var _xmap = _curry2(function(a, b) { + return new XMap(a, b); + }); + function _has(a, b) { + return Object.prototype.hasOwnProperty.call(b, a); + } + var toString = Object.prototype.toString; + var _isArguments = function() { + return "[object Arguments]" === toString.call(arguments) ? function(a) { + return "[object Arguments]" === toString.call(a); + } : function(a) { + return _has("callee", a); + }; + }(); + var hasEnumBug = !{ toString: null }.propertyIsEnumerable("toString"); + var nonEnumerableProps = ["constructor", "valueOf", "isPrototypeOf", "toString", "propertyIsEnumerable", "hasOwnProperty", "toLocaleString"]; + var hasArgsEnumBug = function() { + return arguments.propertyIsEnumerable("length"); + }(); + var contains = function(a, b) { + for (var c = 0; c < a.length; ) { + if (a[c] === b) return true; + c += 1; + } + return false; + }; + var keys = "function" != typeof Object.keys || hasArgsEnumBug ? _curry1(function(a) { + if (Object(a) !== a) return []; + var b, c, d = [], e = hasArgsEnumBug && _isArguments(a); + for (b in a) _has(b, a) && (!e || "length" !== b) && (d[d.length] = b); + if (hasEnumBug) for (c = nonEnumerableProps.length - 1; 0 <= c; ) b = nonEnumerableProps[c], _has(b, a) && !contains(d, b) && (d[d.length] = b), c -= 1; + return d; + }) : _curry1(function(a) { + return Object(a) === a ? Object.keys(a) : []; + }); + var map = _curry2(_dispatchable(["fantasy-land/map", "map"], _xmap, function(a, b) { + switch (Object.prototype.toString.call(b)) { + case "[object Function]": + return curryN(b.length, function() { + return a.call(this, b.apply(this, arguments)); + }); + case "[object Object]": + return _reduce(function(c, d) { + return c[d] = a(b[d]), c; + }, {}, keys(b)); + default: + return _map(a, b); + } + })); + var _isInteger = Number.isInteger || function(a) { + return a << 0 === a; + }; + var nth = _curry2(function(a, b) { + var c = 0 > a ? b.length + a : a; + return _isString(b) ? b.charAt(c) : b[c]; + }); + var prop = _curry2(function(a, b) { + return null == b ? void 0 : _isInteger(a) ? nth(a, b) : b[a]; + }); + var pluck = _curry2(function(a, b) { + return map(prop(a), b); + }); + var reduce = _curry3(_reduce); + var allPass = _curry1(function(a) { + return curryN(reduce(max, 0, pluck("length", a)), function() { + for (var b = 0, c = a.length; b < c; ) { + if (!a[b].apply(this, arguments)) return false; + b += 1; + } + return true; + }); + }); + var ap = _curry2(function(a, b) { + return "function" == typeof b["fantasy-land/ap"] ? b["fantasy-land/ap"](a) : "function" == typeof a.ap ? a.ap(b) : "function" == typeof a ? function(c) { + return a(c)(b(c)); + } : _reduce(function(a2, c) { + return _concat(a2, map(c, b)); + }, [], a); + }); + var isNil = _curry1(function(a) { + return null == a; + }); + var liftN = _curry2(function(a, b) { + var c = curryN(a, b); + return curryN(a, function() { + return _reduce(ap, map(c, arguments[0]), Array.prototype.slice.call(arguments, 1)); + }); + }); + var lift = _curry1(function(a) { + return liftN(a.length, a); + }); + var type = _curry1(function(a) { + return null === a ? "Null" : a === void 0 ? "Undefined" : Object.prototype.toString.call(a).slice(8, -1); + }); + var not = _curry1(function(b) { + return !b; + }); + var complement = lift(not); + function _checkForMethod(a, b) { + return function() { + var c = arguments.length; + if (0 === c) return b(); + var d = arguments[c - 1]; + return _isArray(d) || "function" != typeof d[a] ? b.apply(this, arguments) : d[a].apply(d, Array.prototype.slice.call(arguments, 0, c - 1)); + }; + } + function _arrayFromIterator(a) { + for (var b, c = []; !(b = a.next()).done; ) c.push(b.value); + return c; + } + function _includesWith(a, b, c) { + for (var d = 0, e = c.length; d < e; ) { + if (a(b, c[d])) return true; + d += 1; + } + return false; + } + function _functionName(a) { + var b = (a + "").match(/^function (\w*)/); + return null == b ? "" : b[1]; + } + function _objectIs(c, a) { + return c === a ? 0 !== c || 1 / c == 1 / a : c !== c && a !== a; + } + var _objectIs$1 = "function" == typeof Object.is ? Object.is : _objectIs; + function _uniqContentEquals(c, d, e, f) { + function g(a2, b) { + return _equals(a2, b, e.slice(), f.slice()); + } + var h = _arrayFromIterator(c), a = _arrayFromIterator(d); + return !_includesWith(function(a2, b) { + return !_includesWith(g, b, a2); + }, a, h); + } + function _equals(c, a, b, d) { + if (_objectIs$1(c, a)) return true; + var e = type(c); + if (e !== type(a)) return false; + if ("function" == typeof c["fantasy-land/equals"] || "function" == typeof a["fantasy-land/equals"]) return "function" == typeof c["fantasy-land/equals"] && c["fantasy-land/equals"](a) && "function" == typeof a["fantasy-land/equals"] && a["fantasy-land/equals"](c); + if ("function" == typeof c.equals || "function" == typeof a.equals) return "function" == typeof c.equals && c.equals(a) && "function" == typeof a.equals && a.equals(c); + switch (e) { + case "Arguments": + case "Array": + case "Object": + if ("function" == typeof c.constructor && "Promise" === _functionName(c.constructor)) return c === a; + break; + case "Boolean": + case "Number": + case "String": + if (!(typeof c == typeof a && _objectIs$1(c.valueOf(), a.valueOf()))) return false; + break; + case "Date": + if (!_objectIs$1(c.valueOf(), a.valueOf())) return false; + break; + case "Error": + return c.name === a.name && c.message === a.message; + case "RegExp": + if (c.source !== a.source || c.global !== a.global || c.ignoreCase !== a.ignoreCase || c.multiline !== a.multiline || c.sticky !== a.sticky || c.unicode !== a.unicode) return false; + } + for (var f = b.length - 1; 0 <= f; ) { + if (b[f] === c) return d[f] === a; + f -= 1; + } + switch (e) { + case "Map": + return !(c.size !== a.size) && _uniqContentEquals(c.entries(), a.entries(), b.concat([c]), d.concat([a])); + case "Set": + return !(c.size !== a.size) && _uniqContentEquals(c.values(), a.values(), b.concat([c]), d.concat([a])); + case "Arguments": + case "Array": + case "Object": + case "Boolean": + case "Number": + case "String": + case "Date": + case "Error": + case "RegExp": + case "Int8Array": + case "Uint8Array": + case "Uint8ClampedArray": + case "Int16Array": + case "Uint16Array": + case "Int32Array": + case "Uint32Array": + case "Float32Array": + case "Float64Array": + case "ArrayBuffer": + break; + default: + return false; + } + var g = keys(c); + if (g.length !== keys(a).length) return false; + var h = b.concat([c]), i = d.concat([a]); + for (f = g.length - 1; 0 <= f; ) { + var j = g[f]; + if (!(_has(j, a) && _equals(a[j], c[j], h, i))) return false; + f -= 1; + } + return true; + } + var equals = _curry2(function(c, a) { + return _equals(c, a, [], []); + }); + function _indexOf(b, c, a) { + var d, e; + if ("function" == typeof b.indexOf) switch (typeof c) { + case "number": + if (0 === c) { + for (d = 1 / c; a < b.length; ) { + if (e = b[a], 0 === e && 1 / e === d) return a; + a += 1; + } + return -1; + } + if (c !== c) { + for (; a < b.length; ) { + if (e = b[a], "number" == typeof e && e !== e) return a; + a += 1; + } + return -1; + } + return b.indexOf(c, a); + case "string": + case "boolean": + case "function": + case "undefined": + return b.indexOf(c, a); + case "object": + if (null === c) return b.indexOf(c, a); + } + for (; a < b.length; ) { + if (equals(b[a], c)) return a; + a += 1; + } + return -1; + } + function _includes(b, a) { + return 0 <= _indexOf(a, b, 0); + } + function _complement(a) { + return function() { + return !a.apply(this, arguments); + }; + } + function _filter(a, b) { + for (var c = 0, d = b.length, e = []; c < d; ) a(b[c]) && (e[e.length] = b[c]), c += 1; + return e; + } + function _isObject(a) { + return "[object Object]" === Object.prototype.toString.call(a); + } + var XFilter = function() { + function a(a2, b) { + this.xf = b, this.f = a2; + } + return a.prototype["@@transducer/init"] = _xfBase.init, a.prototype["@@transducer/result"] = _xfBase.result, a.prototype["@@transducer/step"] = function(a2, b) { + return this.f(b) ? this.xf["@@transducer/step"](a2, b) : a2; + }, a; + }(); + var _xfilter = _curry2(function(a, b) { + return new XFilter(a, b); + }); + var filter = _curry2(_dispatchable(["fantasy-land/filter", "filter"], _xfilter, function(a, b) { + return _isObject(b) ? _reduce(function(c, d) { + return a(b[d]) && (c[d] = b[d]), c; + }, {}, keys(b)) : _filter(a, b); + })); + var reject = _curry2(function(a, b) { + return filter(_complement(a), b); + }); + var XFind = function() { + function a(a2, b) { + this.xf = b, this.f = a2, this.found = false; + } + return a.prototype["@@transducer/init"] = _xfBase.init, a.prototype["@@transducer/result"] = function(a2) { + return this.found || (a2 = this.xf["@@transducer/step"](a2, void 0)), this.xf["@@transducer/result"](a2); + }, a.prototype["@@transducer/step"] = function(a2, b) { + return this.f(b) && (this.found = true, a2 = _reduced(this.xf["@@transducer/step"](a2, b))), a2; + }, a; + }(); + var _xfind = _curry2(function(a, b) { + return new XFind(a, b); + }); + var find = _curry2(_dispatchable(["find"], _xfind, function(a, b) { + for (var c = 0, d = b.length; c < d; ) { + if (a(b[c])) return b[c]; + c += 1; + } + })); + var flip = _curry1(function(c) { + return curryN(c.length, function(d, a) { + var b = Array.prototype.slice.call(arguments, 0); + return b[0] = a, b[1] = d, c.apply(this, b); + }); + }); + var forEach = _curry2(_checkForMethod("forEach", function(a, b) { + for (var c = b.length, d = 0; d < c; ) a(b[d]), d += 1; + return b; + })); + var includes = _curry2(_includes); + function _objectAssign(a) { + if (null == a) throw new TypeError("Cannot convert undefined or null to object"); + for (var b = Object(a), c = 1, d = arguments.length; c < d; ) { + var e = arguments[c]; + if (null != e) for (var f in e) _has(f, e) && (b[f] = e[f]); + c += 1; + } + return b; + } + var _objectAssign$1 = "function" == typeof Object.assign ? Object.assign : _objectAssign; + var is = _curry2(function(a, b) { + return b instanceof a || null != b && (b.constructor === a || "Object" === a.name && "object" == typeof b); + }); + var mergeRight = _curry2(function(a, b) { + return _objectAssign$1({}, a, b); + }); + var propEq = _curry3(function(a, b, c) { + return equals(b, prop(a, c)); + }); + var without = _curry2(function(a, b) { + return reject(flip(_includes)(a), b); + }); + var isPortValid = allPass([complement(isNil), is(Number), function(a) { + return 1 <= a && 65535 >= a; + }]); + var validate = function(a) { + var b = a.port; + if (!isPortValid(b)) throw new Error("invalid port"); + }; + var replacements = /* @__PURE__ */ Object.create(null); + replacements["~~~ undefined ~~~"] = void 0, replacements["~~~ null ~~~"] = null, replacements["~~~ false ~~~"] = false, replacements["~~~ zero ~~~"] = 0, replacements["~~~ empty string ~~~"] = "", replacements["~~~ anonymous function ~~~"] = "fn()", replacements["~~~ NaN ~~~"] = NaN, replacements["~~~ Infinity ~~~"] = 1 / 0, replacements["~~~ -Infinity ~~~"] = -Infinity; + function repair(a) { + function b(a2) { + var c, d = Object.prototype.hasOwnProperty.bind(a2); + for (c in a2) if (d(c)) switch (_typeof(a2[c])) { + case "object": + b(a2[c]); + break; + case "string": + a2[c].toLowerCase() in replacements ? a2[c] = replacements[a2[c].toLowerCase()] : 9 < a2[c].length && a2[c].startsWith("~~~ ") && a2[c].endsWith(" ~~~") && (a2[c] = a2[c].replace(/~~~/g, "")); + } + } + return "object" === _typeof(a) ? void b(a) : a; + } + var mitt = (init_mitt(), __toCommonJS(mitt_exports)); + mitt = mitt.default || mitt; + var DEFAULTS = { port: 9090 }; + function createGuid() { + function a() { + return Math.floor(65536 * (1 + Math.random())).toString(16).substring(1); + } + return a() + a() + "-" + a() + "-" + a() + "-" + a() + "-" + a() + a() + a(); + } + function isPfxServerOptions(a) { + return !!a.pathToPfx; + } + function buildHttpsServerOptions(a) { + return a ? isPfxServerOptions(a) ? { pfx: fs.readFileSync(a.pathToPfx), passphrase: a.passphrase } : a.pathToCert ? { cert: fs.readFileSync(a.pathToCert), key: a.pathToKey ? fs.readFileSync(a.pathToKey) : void 0, passphrase: a.passphrase } : void 0 : void 0; + } + var Server = function() { + function a() { + var b = this; + _classCallCheck(this, a), _defineProperty(this, "emitter", mitt()), _defineProperty(this, "options", mergeRight({}, DEFAULTS)), _defineProperty(this, "messageId", 0), _defineProperty(this, "connectionId", 0), _defineProperty(this, "subscriptions", []), _defineProperty(this, "partialConnections", []), _defineProperty(this, "wss", void 0), _defineProperty(this, "connections", []), _defineProperty(this, "started", false), _defineProperty(this, "keepAlive", void 0), _defineProperty(this, "start", function() { + var a2 = b.options.port, c = buildHttpsServerOptions(b.options.wss); + if (!c) b.wss = new ws.Server({ port: a2 }), b.wss.on("error", function(c2) { + c2.message.includes("EADDRINUSE") ? b.emitter.emit("portUnavailable", a2) : console.error(c2); + }); + else { + var d = https.createServer(c); + b.wss = new ws.Server({ server: d }), d.listen(a2); + } + return b.keepAlive && clearInterval(b.keepAlive), b.keepAlive = setInterval(function() { + b.wss.clients.forEach(function(a3) { + a3.ping(function() { + }); + }); + }, 3e4), b.wss.on("connection", function(a3, c2) { + var d2 = b.connectionId++, e = { id: d2, address: c2.socket.remoteAddress, socket: a3 }; + b.partialConnections.push(e), b.emitter.emit("connect", e), a3.on("error", function(a4) { + return console.log("ERR", a4); + }), a3.on("close", function() { + b.partialConnections = reject(propEq("id", d2), b.partialConnections); + var a4 = find(propEq("id", d2), b.connections); + a4 && (b.connections = reject(propEq("id", a4.id), b.connections), b.emitter.emit("disconnect", a4)); + }); + var f = function(a4) { + if (!a4) return /* @__PURE__ */ new Date(); + try { + return new Date(Date.parse(a4)); + } catch (a5) { + return /* @__PURE__ */ new Date(); + } + }; + a3.on("message", function(c3) { + var e2 = JSON.parse(c3.toString()); + repair(e2); + var g = e2.type, h = e2.important, j = e2.payload, k = e2.deltaTime, l = void 0 === k ? 0 : k, m = e2.diff; + b.messageId++; + var n = { type: g, important: h, payload: j, diff: m, connectionId: d2, messageId: b.messageId, date: f(e2.date), deltaTime: l, clientId: a3.clientId }; + if ("client.intro" === g) { + var o = find(propEq("id", d2), b.partialConnections); + n.payload.address = o.address, b.partialConnections = reject(propEq("id", d2), b.partialConnections); + var p = e2.payload.clientId; + if (!p) p = createGuid(), a3.send(JSON.stringify({ type: "setClientId", payload: p })); + else for (var q = Array.from(b.wss.clients), r = q.filter(function(a4) { + return a4.clientId === p; + }), s = 0; s < r.length; s++) { + setTimeout(r[s].close, 500); + var t = find(propEq("clientId", p), b.connections); + t && (b.connections = reject(propEq("clientId", t.clientId), b.connections)); + } + a3.clientId = p, n.clientId = p; + var u = mergeRight(j, { id: d2, address: o.address, clientId: n.clientId, diff: m }); + b.connections.push(u), b.emitter.emit("connectionEstablished", u); + } + "state.values.change" === g && (b.subscriptions = pluck("path", j.changes || [])), "state.backup.response" === g && (n.payload.name = null), b.emitter.emit("command", n); + }), b.stateValuesSendSubscriptions(); + }), b.emitter.emit("start"), b.started = true, b; + }), _defineProperty(this, "send", function(a2, c, d) { + b.wss.clients.forEach(function(b2) { + b2.readyState !== ws.OPEN || d && b2.clientId !== d || b2.send(JSON.stringify({ type: a2, payload: c })); + }); + }); + } + return _createClass(a, [{ key: "configure", value: function() { + var a2 = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : DEFAULTS, b = mergeRight(this.options, a2); + return validate(b), this.options = b, this; + } }, { key: "on", value: function(a2, b) { + this.emitter.on(a2, b); + } }, { key: "off", value: function(a2, b) { + this.emitter.off(a2, b); + } }, { key: "stop", value: function() { + return forEach(function(a2) { + return a2 && a2.connected && a2.disconnect(); + }, pluck("socket", this.connections)), this.keepAlive && clearInterval(this.keepAlive), this.wss.close(), this.emitter.emit("stop"), this.started = false, this; + } }, { key: "stateValuesSendSubscriptions", value: function() { + this.send("state.values.subscribe", { paths: this.subscriptions }); + } }, { key: "stateValuesSubscribe", value: function(a2) { + includes(a2, this.subscriptions) || (equals(a2, "*") ? this.subscriptions.push("") : this.subscriptions.push(a2), this.stateValuesSendSubscriptions()); + } }, { key: "stateValuesUnsubscribe", value: function(a2) { + includes(a2, this.subscriptions) && (this.subscriptions = without([a2], this.subscriptions), this.stateValuesSendSubscriptions()); + } }, { key: "stateValuesClearSubscriptions", value: function() { + this.subscriptions = [], this.stateValuesSendSubscriptions(); + } }, { key: "sendCustomMessage", value: function(a2, b) { + this.send("custom", a2, b); + } }]), a; + }(); + var createServer = function(a) { + var b = new Server(); + return b.configure(a), b; + }; + exports2.createServer = createServer, exports2.default = Server; + } +}); + +// standalone-server.js +var require_standalone_server = __commonJS({ + "standalone-server.js"(exports2, module2) { + "use strict"; + var connectedReactotrons = []; + var connectedClients = []; + function addReactotronApp(socket) { + connectedReactotrons.push(socket); + socket.send(JSON.stringify({ type: "reactotron.connected" })); + console.log("Reactotron app connected: ", socket.id); + const clients = connectedClients.map((c) => ({ clientId: c.clientId, name: c.name })); + connectedReactotrons.forEach((reactotronApp) => { + reactotronApp.send(JSON.stringify({ type: "connectedClients", clients })); + }); + } + function forwardMessage(message, server) { + server.send(message.type, message.payload, message.clientId); + } + function interceptMessage(incoming, socket, server) { + const message = JSON.parse(incoming.toString()); + if (message.type === "reactotron.sendToCore") { + const { payload } = message; + const { type, ...actualPayload } = payload; + server.wss.clients.forEach((wssClient) => { + if (wssClient.clientId === payload.clientId) { + wssClient.send( + JSON.stringify({ + type, + payload: actualPayload + }) + ); + } + }); + return; + } + if (connectedReactotrons.includes(socket)) forwardMessage(message, server); + if (message.type === "reactotron.subscribe") addReactotronApp(socket); + } + function startReactotronServer2(opts = {}) { + const { createServer } = require_dist(); + const server = createServer({ + port: opts.port || 9292, + // default + ...opts + }); + server.start(); + server.wss.on("connection", (socket, _request) => { + socket.on("message", (m) => interceptMessage(m, socket, server)); + }); + server.on("start", () => console.log("Reactotron started")); + server.on("connectionEstablished", (conn) => { + if (!connectedClients.find((c) => c.clientId === conn.clientId)) connectedClients.push(conn); + const clients = connectedClients; + connectedReactotrons.forEach((reactotronApp) => { + reactotronApp.send(JSON.stringify({ type: "connectedClients", clients })); + }); + }); + server.on("command", (cmd) => { + connectedReactotrons.forEach((reactotronApp) => { + console.log("Sending command to Reactotron app: ", cmd.type, reactotronApp.id); + reactotronApp.send(JSON.stringify({ type: "command", cmd })); + }); + }); + server.on("disconnect", (conn) => { + console.log("Disconnected", conn); + connectedReactotrons.forEach((reactotronApp) => { + reactotronApp.send(JSON.stringify({ type: "disconnect", conn })); + }); + const delIndex = connectedClients.findIndex((c) => c.clientId === conn.clientId); + if (delIndex !== -1) connectedClients.splice(delIndex, 1); + }); + server.on("stop", () => console.log("Reactotron stopped")); + server.on("portUnavailable", () => console.log(`Port ${opts.port} unavailable`)); + if (!server.started) { + console.log("Server failed to start"); + return; + } + process.on("SIGINT", () => { + server.stop(); + process.exit(0); + }); + } + module2.exports = { startReactotronServer: startReactotronServer2 }; + } +}); + +// standalone-server-entry.js +var { startReactotronServer } = require_standalone_server(); +var args = process.argv.slice(2); +var port = 9292; +for (let i = 0; i < args.length; i++) { + if (args[i] === "--port" && args[i + 1]) { + port = parseInt(args[i + 1], 10); + if (isNaN(port)) { + console.error(`Invalid port: ${args[i + 1]}`); + process.exit(1); + } + break; + } +} +console.log(`Starting Reactotron server on port ${port}...`); +startReactotronServer({ port }); diff --git a/macos/Reactotron.xcodeproj/project.pbxproj b/macos/Reactotron.xcodeproj/project.pbxproj index 9e207d0..6f6fc0d 100644 --- a/macos/Reactotron.xcodeproj/project.pbxproj +++ b/macos/Reactotron.xcodeproj/project.pbxproj @@ -14,16 +14,17 @@ 514201522437B4B40078DB4F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 514201512437B4B40078DB4F /* Assets.xcassets */; }; 514201552437B4B40078DB4F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 514201532437B4B40078DB4F /* Main.storyboard */; }; 514201582437B4B40078DB4F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 514201572437B4B40078DB4F /* main.m */; }; - 781C0A510276619A3FA0F1B5 /* libPods-Reactotron-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AF5D9503835923B7F2C9630F /* libPods-Reactotron-macOS.a */; }; 831983FBC55C0EF9C65DB4A6 /* ProcessUtils.c in Sources */ = {isa = PBXBuildFile; fileRef = 4F3799E74F7D2A242D38CF99 /* ProcessUtils.c */; }; + A16BBB38FBD733FC371D90DF /* libPods-Reactotron-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FAF9DE22431FDACC74ADF95 /* libPods-Reactotron-macOS.a */; }; E94E8A1F2DA73754008B52A6 /* SpaceGrotesk.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E94E8A1E2DA73754008B52A6 /* SpaceGrotesk.ttf */; }; + E94E8A202DA73755008B52A7 /* standalone-server.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = E94E8A1F2DA73755008B52A7 /* standalone-server.bundle.js */; }; EBA23295D3FDE3328174ADF1 /* BuildFile in Headers */ = {isa = PBXBuildFile; }; FF4C4719434907CDFF48ECE7 /* ProcessUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B174B7FD8393A730D51E178 /* ProcessUtils.h */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 2AC32FEAB6C823D7FAA18BFE /* Pods-Reactotron-macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Reactotron-macOS.release.xcconfig"; path = "Target Support Files/Pods-Reactotron-macOS/Pods-Reactotron-macOS.release.xcconfig"; sourceTree = ""; }; - 417D988DC189149043CB9C6B /* Pods-Reactotron-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Reactotron-macOS.debug.xcconfig"; path = "Target Support Files/Pods-Reactotron-macOS/Pods-Reactotron-macOS.debug.xcconfig"; sourceTree = ""; }; + 1FAF9DE22431FDACC74ADF95 /* libPods-Reactotron-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Reactotron-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 47A42CF6633046900B11F32D /* Pods-Reactotron-macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Reactotron-macOS.release.xcconfig"; path = "Target Support Files/Pods-Reactotron-macOS/Pods-Reactotron-macOS.release.xcconfig"; sourceTree = ""; }; 4F3799E74F7D2A242D38CF99 /* ProcessUtils.c */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.c; name = ProcessUtils.c; path = ../../app/native/ProcessUtils/ProcessUtils.c; sourceTree = ""; }; 5030E1330EE236E4CA991AFD /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 514201492437B4B30078DB4F /* Reactotron.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Reactotron.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -34,9 +35,10 @@ 514201562437B4B40078DB4F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 514201572437B4B40078DB4F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 514201592437B4B40078DB4F /* Reactotron.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Reactotron.entitlements; sourceTree = ""; }; + 705D90618A898C40370E48F0 /* Pods-Reactotron-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Reactotron-macOS.debug.xcconfig"; path = "Target Support Files/Pods-Reactotron-macOS/Pods-Reactotron-macOS.debug.xcconfig"; sourceTree = ""; }; 8B174B7FD8393A730D51E178 /* ProcessUtils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ProcessUtils.h; path = ../../app/native/ProcessUtils/ProcessUtils.h; sourceTree = ""; }; - AF5D9503835923B7F2C9630F /* libPods-Reactotron-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Reactotron-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; E94E8A1E2DA73754008B52A6 /* SpaceGrotesk.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = SpaceGrotesk.ttf; path = ../assets/fonts/SpaceGrotesk.ttf; sourceTree = SOURCE_ROOT; }; + E94E8A1F2DA73755008B52A7 /* standalone-server.bundle.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "standalone-server.bundle.js"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -45,7 +47,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 781C0A510276619A3FA0F1B5 /* libPods-Reactotron-macOS.a in Frameworks */, + A16BBB38FBD733FC371D90DF /* libPods-Reactotron-macOS.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -55,8 +57,8 @@ 01D19913288DA74FEB538384 /* Pods */ = { isa = PBXGroup; children = ( - 417D988DC189149043CB9C6B /* Pods-Reactotron-macOS.debug.xcconfig */, - 2AC32FEAB6C823D7FAA18BFE /* Pods-Reactotron-macOS.release.xcconfig */, + 705D90618A898C40370E48F0 /* Pods-Reactotron-macOS.debug.xcconfig */, + 47A42CF6633046900B11F32D /* Pods-Reactotron-macOS.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -65,7 +67,7 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - AF5D9503835923B7F2C9630F /* libPods-Reactotron-macOS.a */, + 1FAF9DE22431FDACC74ADF95 /* libPods-Reactotron-macOS.a */, ); name = Frameworks; sourceTree = ""; @@ -80,6 +82,7 @@ 514201562437B4B40078DB4F /* Info.plist */, 514201572437B4B40078DB4F /* main.m */, 514201592437B4B40078DB4F /* Reactotron.entitlements */, + E94E8A1F2DA73755008B52A7 /* standalone-server.bundle.js */, 9E90342DBE1B396F592DF34B /* Colocated */, ); path = "Reactotron-macOS"; @@ -144,14 +147,14 @@ isa = PBXNativeTarget; buildConfigurationList = 5142015A2437B4B40078DB4F /* Build configuration list for PBXNativeTarget "Reactotron-macOS" */; buildPhases = ( - D379D3EECB71C1F9751C49E8 /* [CP] Check Pods Manifest.lock */, + 2B1BC0B6349015C268719145 /* [CP] Check Pods Manifest.lock */, 514201452437B4B30078DB4F /* Sources */, 514201462437B4B30078DB4F /* Frameworks */, 514201472437B4B30078DB4F /* Resources */, 381D8A6E24576A4E00465D17 /* Bundle React Native code and images */, 614A44CF57166CA8B6AD60B6 /* Headers */, - 83FF77371CEFDD8B5ECB691C /* [CP] Embed Pods Frameworks */, - A107699AF4EC552473C5FA0B /* [CP] Copy Pods Resources */, + 04D27436C2E4759745F6B86C /* [CP] Embed Pods Frameworks */, + 1F47EDCEA18AFDD32CA79889 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -204,31 +207,14 @@ 514201552437B4B40078DB4F /* Main.storyboard in Resources */, 1BCAFE1EAF4275451E5A2CA2 /* PrivacyInfo.xcprivacy in Resources */, E94E8A1F2DA73754008B52A6 /* SpaceGrotesk.ttf in Resources */, + E94E8A202DA73755008B52A7 /* standalone-server.bundle.js in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 381D8A6E24576A4E00465D17 /* Bundle React Native code and images */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Bundle React Native code and images"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n../node_modules/react-native-macos/scripts/react-native-xcode.sh\n"; - }; - 83FF77371CEFDD8B5ECB691C /* [CP] Embed Pods Frameworks */ = { + 04D27436C2E4759745F6B86C /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -246,7 +232,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Reactotron-macOS/Pods-Reactotron-macOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - A107699AF4EC552473C5FA0B /* [CP] Copy Pods Resources */ = { + 1F47EDCEA18AFDD32CA79889 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -272,7 +258,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Reactotron-macOS/Pods-Reactotron-macOS-resources.sh\"\n"; showEnvVarsInLog = 0; }; - D379D3EECB71C1F9751C49E8 /* [CP] Check Pods Manifest.lock */ = { + 2B1BC0B6349015C268719145 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -294,6 +280,24 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 381D8A6E24576A4E00465D17 /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Bundle React Native code and images"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native-macos/scripts/react-native-xcode.sh\n"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -325,7 +329,7 @@ /* Begin XCBuildConfiguration section */ 5142015B2437B4B40078DB4F /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 417D988DC189149043CB9C6B /* Pods-Reactotron-macOS.debug.xcconfig */; + baseConfigurationReference = 705D90618A898C40370E48F0 /* Pods-Reactotron-macOS.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -352,7 +356,7 @@ }; 5142015C2437B4B40078DB4F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2AC32FEAB6C823D7FAA18BFE /* Pods-Reactotron-macOS.release.xcconfig */; + baseConfigurationReference = 47A42CF6633046900B11F32D /* Pods-Reactotron-macOS.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; diff --git a/metro.config.js b/metro.config.js index 7c92cbc..471ff29 100644 --- a/metro.config.js +++ b/metro.config.js @@ -40,6 +40,4 @@ const config = { }, } -require("./standalone-server").startReactotronServer({ port: 9292 }) - module.exports = mergeConfig(getDefaultConfig(__dirname), config) diff --git a/package-lock.json b/package-lock.json index 253ae42..88ffb14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "@rnx-kit/jest-preset": "^0.1.17", "@types/jest": "^29.5.14", "@types/react": "^19.1.0", + "esbuild": "^0.24.2", "eslint": "^8.57.1", "eslint-config-prettier": "^10.1.1", "eslint-plugin-prettier": "^4.2.1", @@ -2018,6 +2019,406 @@ "dev": true, "license": "MIT" }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -6333,6 +6734,46 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", diff --git a/package.json b/package.json index b36a5eb..f7e45ca 100644 --- a/package.json +++ b/package.json @@ -6,27 +6,29 @@ "all": "npm run pod && npm run macos", "all-windows": "npm run windows-link && npm run windows", "macos": "REACT_NATIVE_PATH=./node_modules/react-native-macos RCT_SCRIPT_RN_DIR=$REACT_NATIVE_PATH RCT_NEW_ARCH_ENABLED=1 node ./node_modules/react-native-macos/local-cli/cli.js run-macos", + "premacos-release": "npm run bundle-server", "macos-release": "REACT_NATIVE_PATH=./node_modules/react-native-macos RCT_SCRIPT_RN_DIR=$REACT_NATIVE_PATH RCT_NEW_ARCH_ENABLED=1 node ./node_modules/react-native-macos/local-cli/cli.js run-macos --configuration Release", "pod": "./bin/pod-install", "pod-clean": "./bin/pod-install clean", "windows-link": "./bin/windows-link", "windows-link-clean": "./bin/windows-link clean", - "lint": "eslint .", + "lint": "eslint . --ignore-pattern=*.bundle.js", "windows": "npx @react-native-community/cli run-windows", "test:windows": "jest --config jest.config.windows.js", "ci": "npm run lint", "start": "REACT_NATIVE_PATH=./node_modules/react-native-macos RCT_SCRIPT_RN_DIR=$REACT_NATIVE_PATH RCT_NEW_ARCH_ENABLED=1 ./node_modules/react-native-macos/scripts/packager.sh start", "test": "jest", "postinstall": "ln -sf $(pwd)/node_modules/react-native-macos $(pwd)/node_modules/react-native && patch-package", - "node-process": "node -e \"require('./standalone-server').startReactotronServer({ port: 9292 })\"" + "node-process": "node -e \"require('./standalone-server').startReactotronServer({ port: 9292 })\"", + "bundle-server": "npx esbuild standalone-server-entry.js --bundle --platform=node --target=node18 --format=cjs --packages=bundle --outfile=macos/Reactotron-macOS/standalone-server.bundle.js" }, "dependencies": { "@expo-google-fonts/space-grotesk": "^0.3.0", "@legendapp/list": "^1.1.4", "react": "19.0.0", "react-native-macos": "^0.78.2", - "react-native-windows": "^0.78.5", "react-native-mmkv": "^3.3.0", + "react-native-windows": "^0.78.5", "reactotron-core-contract": "^0.3.2" }, "overrides": { @@ -42,8 +44,10 @@ "@react-native/eslint-config": "0.78.2", "@react-native/metro-config": "0.78.2", "@react-native/typescript-config": "0.78.2", + "@rnx-kit/jest-preset": "^0.1.17", "@types/jest": "^29.5.14", "@types/react": "^19.1.0", + "esbuild": "^0.24.2", "eslint": "^8.57.1", "eslint-config-prettier": "^10.1.1", "eslint-plugin-prettier": "^4.2.1", @@ -53,7 +57,6 @@ "patch-package": "^8.0.0", "prettier": "2.8.8", "reactotron-core-client": "^2.9.9", - "@rnx-kit/jest-preset": "^0.1.17", "reactotron-core-server": "^3.2.0", "reactotron-react-native": "^5.1.14", "typescript": "5.0.4" diff --git a/patches/reactotron-core-server+3.2.0.patch b/patches/reactotron-core-server+3.2.0.patch new file mode 100644 index 0000000..07ed15e --- /dev/null +++ b/patches/reactotron-core-server+3.2.0.patch @@ -0,0 +1,7 @@ +diff --git a/node_modules/reactotron-core-server/dist/index.js b/node_modules/reactotron-core-server/dist/index.js +index 848508e..4608864 100644 +--- a/node_modules/reactotron-core-server/dist/index.js ++++ b/node_modules/reactotron-core-server/dist/index.js +@@ -1 +1 @@ +-"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var https=require("https"),ws=require("ws"),fs=require("fs");function _toPrimitive(a,b){if("object"!=typeof a||!a)return a;var c=a[Symbol.toPrimitive];if(void 0!==c){var d=c.call(a,b||"default");if("object"!=typeof d)return d;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===b?String:Number)(a)}function _toPropertyKey(a){var b=_toPrimitive(a,"string");return"symbol"==typeof b?b:b+""}function _typeof(a){"@babel/helpers - typeof";return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a},_typeof(a)}function _classCallCheck(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function _defineProperties(a,b){for(var c,d=0;d=arguments.length)?h=b[g]:(h=arguments[e],e+=1),d[g]=h,_isPlaceholder(h)||(f-=1),g+=1}return 0>=f?c.apply(this,d):_arity(f,_curryN(a,d,c))}}var curryN=_curry2(function(a,b){return 1===a?_curry1(b):_arity(a,_curryN(a,[],b))});function _curry3(d){return function c(e,a,b){switch(arguments.length){case 0:return c;case 1:return _isPlaceholder(e)?c:_curry2(function(a,b){return d(e,a,b)});case 2:return _isPlaceholder(e)&&_isPlaceholder(a)?c:_isPlaceholder(e)?_curry2(function(b,c){return d(b,a,c)}):_isPlaceholder(a)?_curry2(function(a,b){return d(e,a,b)}):_curry1(function(b){return d(e,a,b)});default:return _isPlaceholder(e)&&_isPlaceholder(a)&&_isPlaceholder(b)?c:_isPlaceholder(e)&&_isPlaceholder(a)?_curry2(function(a,c){return d(a,c,b)}):_isPlaceholder(e)&&_isPlaceholder(b)?_curry2(function(b,c){return d(b,a,c)}):_isPlaceholder(a)&&_isPlaceholder(b)?_curry2(function(a,b){return d(e,a,b)}):_isPlaceholder(e)?_curry1(function(c){return d(c,a,b)}):_isPlaceholder(a)?_curry1(function(a){return d(e,a,b)}):_isPlaceholder(b)?_curry1(function(b){return d(e,a,b)}):d(e,a,b)}}}var _isArray=Array.isArray||function(a){return null!=a&&0<=a.length&&"[object Array]"===Object.prototype.toString.call(a)};function _isTransformer(a){return null!=a&&"function"==typeof a["@@transducer/step"]}function _dispatchable(a,b,c){return function(){if(0===arguments.length)return c();var d=arguments[arguments.length-1];if(!_isArray(d)){for(var e=0;ec?a:c});function _map(a,b){for(var c=0,d=b.length,e=Array(d);ca?b.length+a:a;return _isString(b)?b.charAt(c):b[c]}),prop=_curry2(function(a,b){return null==b?void 0:_isInteger(a)?nth(a,b):b[a]}),pluck=_curry2(function(a,b){return map(prop(a),b)}),reduce=_curry3(_reduce),allPass=_curry1(function(a){return curryN(reduce(max,0,pluck("length",a)),function(){for(var b=0,c=a.length;b=a}]),validate=function(a){var b=a.port;if(!isPortValid(b))throw new Error("invalid port")},replacements=Object.create(null);replacements["~~~ undefined ~~~"]=void 0,replacements["~~~ null ~~~"]=null,replacements["~~~ false ~~~"]=!1,replacements["~~~ zero ~~~"]=0,replacements["~~~ empty string ~~~"]="",replacements["~~~ anonymous function ~~~"]="fn()",replacements["~~~ NaN ~~~"]=NaN,replacements["~~~ Infinity ~~~"]=1/0,replacements["~~~ -Infinity ~~~"]=-Infinity;function repair(a){function b(a){var c,d=Object.prototype.hasOwnProperty.bind(a);for(c in a)if(d(c))switch(_typeof(a[c])){case"object":b(a[c]);break;case"string":a[c].toLowerCase()in replacements?a[c]=replacements[a[c].toLowerCase()]:9=arguments.length)?h=b[g]:(h=arguments[e],e+=1),d[g]=h,_isPlaceholder(h)||(f-=1),g+=1}return 0>=f?c.apply(this,d):_arity(f,_curryN(a,d,c))}}var curryN=_curry2(function(a,b){return 1===a?_curry1(b):_arity(a,_curryN(a,[],b))});function _curry3(d){return function c(e,a,b){switch(arguments.length){case 0:return c;case 1:return _isPlaceholder(e)?c:_curry2(function(a,b){return d(e,a,b)});case 2:return _isPlaceholder(e)&&_isPlaceholder(a)?c:_isPlaceholder(e)?_curry2(function(b,c){return d(b,a,c)}):_isPlaceholder(a)?_curry2(function(a,b){return d(e,a,b)}):_curry1(function(b){return d(e,a,b)});default:return _isPlaceholder(e)&&_isPlaceholder(a)&&_isPlaceholder(b)?c:_isPlaceholder(e)&&_isPlaceholder(a)?_curry2(function(a,c){return d(a,c,b)}):_isPlaceholder(e)&&_isPlaceholder(b)?_curry2(function(b,c){return d(b,a,c)}):_isPlaceholder(a)&&_isPlaceholder(b)?_curry2(function(a,b){return d(e,a,b)}):_isPlaceholder(e)?_curry1(function(c){return d(c,a,b)}):_isPlaceholder(a)?_curry1(function(a){return d(e,a,b)}):_isPlaceholder(b)?_curry1(function(b){return d(e,a,b)}):d(e,a,b)}}}var _isArray=Array.isArray||function(a){return null!=a&&0<=a.length&&"[object Array]"===Object.prototype.toString.call(a)};function _isTransformer(a){return null!=a&&"function"==typeof a["@@transducer/step"]}function _dispatchable(a,b,c){return function(){if(0===arguments.length)return c();var d=arguments[arguments.length-1];if(!_isArray(d)){for(var e=0;ec?a:c});function _map(a,b){for(var c=0,d=b.length,e=Array(d);ca?b.length+a:a;return _isString(b)?b.charAt(c):b[c]}),prop=_curry2(function(a,b){return null==b?void 0:_isInteger(a)?nth(a,b):b[a]}),pluck=_curry2(function(a,b){return map(prop(a),b)}),reduce=_curry3(_reduce),allPass=_curry1(function(a){return curryN(reduce(max,0,pluck("length",a)),function(){for(var b=0,c=a.length;b=a}]),validate=function(a){var b=a.port;if(!isPortValid(b))throw new Error("invalid port")},replacements=Object.create(null);replacements["~~~ undefined ~~~"]=void 0,replacements["~~~ null ~~~"]=null,replacements["~~~ false ~~~"]=!1,replacements["~~~ zero ~~~"]=0,replacements["~~~ empty string ~~~"]="",replacements["~~~ anonymous function ~~~"]="fn()",replacements["~~~ NaN ~~~"]=NaN,replacements["~~~ Infinity ~~~"]=1/0,replacements["~~~ -Infinity ~~~"]=-Infinity;function repair(a){function b(a){var c,d=Object.prototype.hasOwnProperty.bind(a);for(c in a)if(d(c))switch(_typeof(a[c])){case"object":b(a[c]);break;case"string":a[c].toLowerCase()in replacements?a[c]=replacements[a[c].toLowerCase()]:9 ({ clientId: c.clientId, name: c.name })) + console.log("Sending initial client list to new Reactotron app:", clients.length, "clients") connectedReactotrons.forEach((reactotronApp) => { reactotronApp.send(JSON.stringify({ type: "connectedClients", clients })) }) @@ -61,7 +67,9 @@ function startReactotronServer(opts = {}) { server.start() - server.wss.on("connection", (socket, _request) => { + server.wss.on("connection", (socket, request) => { + const clientIp = request.socket.remoteAddress + console.log("WebSocket connection from:", clientIp) // Intercept messages sent to this socket to check for Reactotron apps socket.on("message", (m) => interceptMessage(m, socket, server)) }) @@ -70,14 +78,24 @@ function startReactotronServer(opts = {}) { server.on("start", () => console.log("Reactotron started")) // A client has connected, but we don't know who it is yet. - // server.on("connect", (conn) => console.log("Connected", conn)) + server.on("connect", (conn) => console.log("Client attempting connection:", conn.id)) // A client has connected and provided us the initial detail we want. server.on("connectionEstablished", (conn) => { + console.log("Client fully connected:", conn.clientId, conn.name) // Add the client to the list of connected clients if it's not already in the list - if (!connectedClients.find((c) => c.clientId === conn.clientId)) connectedClients.push(conn) + if (!connectedClients.find((c) => c.clientId === conn.clientId)) { + connectedClients.push(conn) + console.log("Added to connectedClients. Total clients:", connectedClients.length) + } const clients = connectedClients + console.log( + "Forwarding to", + connectedReactotrons.length, + "Reactotron apps. Client list:", + clients.map((c) => c.clientId), + ) connectedReactotrons.forEach((reactotronApp) => { // conn here is a ReactotronConnection object // We will forward this to all connected Reactotron apps.