@outray/core
Advanced tools
+1
-0
@@ -80,2 +80,3 @@ /** | ||
| remotePort?: number; | ||
| password?: string; | ||
| } | ||
@@ -82,0 +83,0 @@ interface TunnelResponseMessage { |
+1
-0
@@ -80,2 +80,3 @@ /** | ||
| remotePort?: number; | ||
| password?: string; | ||
| } | ||
@@ -82,0 +83,0 @@ interface TunnelResponseMessage { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/protocol.ts","../src/mdns.ts","../src/types.ts"],"sourcesContent":["// Core client\nexport { OutrayClient } from \"./client\";\n\n// mDNS / Local access\nexport {\n MDNSAdvertiser,\n LocalProxy,\n LocalHttpsProxy,\n LocalAccessManager,\n} from \"./mdns\";\nexport type { LocalAccessInfo } from \"./mdns\";\n\n// Protocol utilities\nexport { encodeMessage, decodeMessage } from \"./protocol\";\n\n// Types\nexport type {\n // Client options\n OutrayClientOptions,\n RequestInfo,\n TunnelProtocol,\n // Protocol messages\n ClientMessage,\n ServerMessage,\n OpenTunnelMessage,\n TunnelOpenedMessage,\n TunnelDataMessage,\n TunnelResponseMessage,\n TCPConnectionMessage,\n TCPDataMessage,\n TCPCloseMessage,\n TCPIncomingDataMessage,\n TCPIncomingCloseMessage,\n UDPDataMessage,\n UDPResponseMessage,\n ErrorMessage,\n ErrorCode,\n // WebSocket passthrough\n WSUpgradeMessage,\n WSUpgradeResponseMessage,\n WSFrameMessage,\n WSCloseMessage,\n} from \"./types\";\n\n// Error codes constant\nexport { ErrorCodes } from \"./types\";\n","import WebSocket from \"ws\";\nimport http from \"http\";\nimport { encodeMessage, decodeMessage } from \"./protocol\";\nimport type {\n OutrayClientOptions,\n TunnelDataMessage,\n TunnelResponseMessage,\n WSUpgradeMessage,\n WSFrameMessage,\n WSCloseMessage,\n ErrorCodes,\n} from \"./types\";\n\nconst DEFAULT_SERVER_URL = \"wss://api.outray.dev/\";\nconst PING_INTERVAL_MS = 25000;\nconst PONG_TIMEOUT_MS = 10000;\n\n/**\n * Core Outray tunnel client.\n *\n * Establishes a WebSocket connection to the Outray server and proxies\n * HTTP requests to a local server.\n *\n * @example\n * ```ts\n * const client = new OutrayClient({\n * localPort: 3000,\n * onTunnelReady: (url) => console.log(`Tunnel: ${url}`),\n * onError: (err) => console.error(err),\n * });\n *\n * client.start();\n *\n * // Later...\n * client.stop();\n * ```\n */\nexport class OutrayClient {\n private ws: WebSocket | null = null;\n private options: Required<\n Pick<OutrayClientOptions, \"localPort\" | \"serverUrl\">\n > &\n OutrayClientOptions;\n private reconnectTimeout: NodeJS.Timeout | null = null;\n private pingInterval: NodeJS.Timeout | null = null;\n private pongTimeout: NodeJS.Timeout | null = null;\n private shouldReconnect = true;\n private assignedUrl: string | null = null;\n private subdomain?: string;\n private forceTakeover = false;\n private reconnectAttempts = 0;\n private lastPongReceived = Date.now();\n private localWebSockets = new Map<string, WebSocket>();\n\n constructor(options: OutrayClientOptions) {\n this.options = {\n ...options,\n serverUrl: options.serverUrl ?? DEFAULT_SERVER_URL,\n };\n this.subdomain = options.subdomain;\n }\n\n /**\n * Start the tunnel connection\n */\n public start(): void {\n this.shouldReconnect = true;\n this.connect();\n }\n\n /**\n * Stop the tunnel connection\n */\n public stop(): void {\n this.shouldReconnect = false;\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n\n this.stopPing();\n this.stopPongTimeout();\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n // Clean up all local WebSocket connections\n for (const [id, localWs] of this.localWebSockets) {\n localWs.close();\n }\n this.localWebSockets.clear();\n }\n\n /**\n * Get the assigned tunnel URL (if connected)\n */\n public getUrl(): string | null {\n return this.assignedUrl;\n }\n\n /**\n * Check if the client is currently connected\n */\n public isConnected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n private connect(): void {\n this.ws = new WebSocket(this.options.serverUrl);\n\n this.ws.on(\"open\", () => this.handleOpen());\n this.ws.on(\"message\", (data) => this.handleMessage(data.toString()));\n this.ws.on(\"close\", (code, reason) => this.handleClose(code, reason));\n this.ws.on(\"error\", (error) => {\n this.options.onError?.(error);\n });\n this.ws.on(\"pong\", () => {\n this.lastPongReceived = Date.now();\n this.stopPongTimeout();\n });\n }\n\n private handleOpen(): void {\n this.startPing();\n\n const handshake = encodeMessage({\n type: \"open_tunnel\",\n apiKey: this.options.apiKey,\n subdomain: this.subdomain,\n customDomain: this.options.customDomain,\n forceTakeover: this.forceTakeover,\n protocol: this.options.protocol,\n remotePort: this.options.remotePort,\n });\n this.ws?.send(handshake);\n }\n\n private handleMessage(data: string): void {\n try {\n const message = decodeMessage(data);\n\n if (message.type === \"tunnel_opened\") {\n this.assignedUrl = message.url;\n const derivedSubdomain = this.extractSubdomain(message.url);\n if (derivedSubdomain) {\n this.subdomain = derivedSubdomain;\n }\n this.forceTakeover = false;\n this.reconnectAttempts = 0;\n this.options.onTunnelReady?.(message.url, message.port);\n } else if (message.type === \"error\") {\n this.handleError(message.code, message.message);\n } else if (message.type === \"request\") {\n this.handleTunnelData(message);\n } else if (message.type === \"ws_upgrade\") {\n this.handleWSUpgrade(message as WSUpgradeMessage);\n } else if (message.type === \"ws_frame\") {\n this.handleWSFrame(message as WSFrameMessage);\n } else if (message.type === \"ws_close\") {\n this.handleWSClose(message as WSCloseMessage);\n }\n } catch (error) {\n this.options.onError?.(\n error instanceof Error ? error : new Error(String(error))\n );\n }\n }\n\n private handleError(code: string, message: string): void {\n if (code === \"SUBDOMAIN_IN_USE\" && this.assignedUrl && !this.forceTakeover) {\n // Reconnecting and subdomain is in use - try to take over\n this.forceTakeover = true;\n this.connect();\n return;\n }\n\n this.options.onError?.(new Error(message), code);\n\n // Fatal errors - stop reconnecting\n if (code === \"AUTH_FAILED\" || code === \"LIMIT_EXCEEDED\") {\n this.shouldReconnect = false;\n this.stop();\n }\n }\n\n private handleTunnelData(message: TunnelDataMessage): void {\n const startTime = Date.now();\n\n const reqOptions = {\n hostname: \"localhost\",\n port: this.options.localPort,\n path: message.path,\n method: message.method,\n headers: message.headers,\n };\n\n const req = http.request(reqOptions, (res) => {\n const chunks: Buffer[] = [];\n\n res.on(\"data\", (chunk) => {\n chunks.push(Buffer.from(chunk));\n });\n\n res.on(\"end\", () => {\n const duration = Date.now() - startTime;\n const statusCode = res.statusCode || 200;\n\n this.options.onRequest?.({\n method: message.method,\n path: message.path,\n statusCode,\n duration,\n });\n\n const bodyBuffer = Buffer.concat(chunks);\n const bodyBase64 =\n bodyBuffer.length > 0 ? bodyBuffer.toString(\"base64\") : undefined;\n\n const response: TunnelResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n statusCode,\n headers: res.headers as Record<string, string | string[]>,\n body: bodyBase64,\n };\n\n this.ws?.send(encodeMessage(response));\n });\n });\n\n req.on(\"error\", (err) => {\n const duration = Date.now() - startTime;\n\n this.options.onRequest?.({\n method: message.method,\n path: message.path,\n statusCode: 502,\n duration,\n error: err.message,\n });\n\n const errorResponse: TunnelResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n statusCode: 502,\n headers: { \"content-type\": \"text/plain\" },\n body: Buffer.from(`Bad Gateway: ${err.message}`).toString(\"base64\"),\n };\n\n this.ws?.send(encodeMessage(errorResponse));\n });\n\n if (message.body) {\n const bodyBuffer = Buffer.from(message.body, \"base64\");\n req.write(bodyBuffer);\n }\n\n req.end();\n }\n\n private handleWSUpgrade(message: WSUpgradeMessage): void {\n const wsUrl = `ws://localhost:${this.options.localPort}${message.path}`;\n\n try {\n const headers: Record<string, string> = {};\n // Forward relevant headers (like subprotocols)\n if (message.protocol) {\n headers[\"Sec-WebSocket-Protocol\"] = message.protocol;\n }\n\n const localWs = new WebSocket(wsUrl, {\n headers,\n });\n\n localWs.on(\"open\", () => {\n this.localWebSockets.set(message.wsConnectionId, localWs);\n\n // Confirm the upgrade to the server\n this.ws?.send(\n encodeMessage({\n type: \"ws_upgrade_response\",\n wsConnectionId: message.wsConnectionId,\n success: true,\n })\n );\n });\n\n // Relay frames from local WS to server\n localWs.on(\"message\", (data: WebSocket.RawData, isBinary: boolean) => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n const buffer = Buffer.isBuffer(data)\n ? data\n : Buffer.from(data as ArrayBuffer);\n this.ws.send(\n encodeMessage({\n type: \"ws_frame\",\n wsConnectionId: message.wsConnectionId,\n data: buffer.toString(\"base64\"),\n isBinary,\n })\n );\n }\n });\n\n localWs.on(\"close\", (code, reason) => {\n this.localWebSockets.delete(message.wsConnectionId);\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(\n encodeMessage({\n type: \"ws_close\",\n wsConnectionId: message.wsConnectionId,\n code,\n reason: reason?.toString(),\n })\n );\n }\n });\n\n localWs.on(\"error\", (error) => {\n this.localWebSockets.delete(message.wsConnectionId);\n\n // If not yet open, send failure response\n if (localWs.readyState === WebSocket.CONNECTING) {\n this.ws?.send(\n encodeMessage({\n type: \"ws_upgrade_response\",\n wsConnectionId: message.wsConnectionId,\n success: false,\n error: `Failed to connect to local WebSocket: ${error.message}`,\n })\n );\n }\n });\n } catch (error) {\n this.ws?.send(\n encodeMessage({\n type: \"ws_upgrade_response\",\n wsConnectionId: message.wsConnectionId,\n success: false,\n error: `Failed to create WebSocket connection: ${error instanceof Error ? error.message : String(error)}`,\n })\n );\n }\n }\n\n private handleWSFrame(message: WSFrameMessage): void {\n const localWs = this.localWebSockets.get(message.wsConnectionId);\n if (!localWs || localWs.readyState !== WebSocket.OPEN) {\n return;\n }\n\n const data = Buffer.from(message.data, \"base64\");\n localWs.send(data, { binary: message.isBinary });\n }\n\n private handleWSClose(message: WSCloseMessage): void {\n const localWs = this.localWebSockets.get(message.wsConnectionId);\n if (!localWs) {\n return;\n }\n\n this.localWebSockets.delete(message.wsConnectionId);\n localWs.close(message.code || 1000, message.reason || \"\");\n }\n\n private extractSubdomain(url: string): string | null {\n try {\n const hostname = new URL(url).hostname;\n const [subdomain] = hostname.split(\".\");\n return subdomain || null;\n } catch {\n return null;\n }\n }\n\n private startPing(): void {\n this.stopPing();\n this.lastPongReceived = Date.now();\n\n this.pingInterval = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.ping();\n\n this.stopPongTimeout();\n this.pongTimeout = setTimeout(() => {\n // No pong received - connection is likely dead\n if (this.ws) {\n this.ws.terminate();\n }\n }, PONG_TIMEOUT_MS);\n }\n }, PING_INTERVAL_MS);\n }\n\n private stopPing(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n private stopPongTimeout(): void {\n if (this.pongTimeout) {\n clearTimeout(this.pongTimeout);\n this.pongTimeout = null;\n }\n }\n\n private handleClose(code?: number, reason?: Buffer): void {\n this.stopPing();\n this.stopPongTimeout();\n\n if (!this.shouldReconnect) return;\n\n const reasonStr = reason?.toString() || \"\";\n\n if (code === 1000 && reasonStr === \"Tunnel stopped by user\") {\n this.options.onClose?.(reasonStr);\n this.stop();\n return;\n }\n\n // If we previously had a tunnel URL, force takeover on reconnect\n if (this.assignedUrl) {\n this.forceTakeover = true;\n }\n\n const delay = Math.min(2000 * Math.pow(2, this.reconnectAttempts), 30000);\n this.reconnectAttempts += 1;\n\n this.options.onReconnecting?.(this.reconnectAttempts, delay);\n\n this.reconnectTimeout = setTimeout(() => {\n this.connect();\n }, delay);\n }\n}\n","import type { ClientMessage, ServerMessage } from \"./types\";\n\n/**\n * Encode a client message to send to the server\n */\nexport function encodeMessage(message: ClientMessage): string {\n return JSON.stringify(message);\n}\n\n/**\n * Decode a server message received from the server\n */\nexport function decodeMessage(data: string): ServerMessage {\n return JSON.parse(data) as ServerMessage;\n}\n","import dgram from \"dgram\";\nimport http from \"http\";\nimport https from \"https\";\nimport os from \"os\";\n\nconst MDNS_PORT = 5353;\nconst MDNS_ADDRESS = \"224.0.0.251\";\n\ninterface MDNSAdvertisement {\n hostname: string;\n port: number;\n ip: string;\n}\n\n/**\n * Simple mDNS responder that advertises a .local hostname\n * This allows other devices on the LAN to discover and access the tunnel\n */\nexport class MDNSAdvertiser {\n private socket: dgram.Socket | null = null;\n private hostname: string;\n private port: number;\n private ip: string;\n private running = false;\n private announceInterval: NodeJS.Timeout | null = null;\n\n constructor(hostname: string, port: number) {\n this.hostname = hostname.endsWith(\".local\")\n ? hostname\n : `${hostname}.local`;\n this.port = port;\n this.ip = this.getLocalIP();\n }\n\n private getLocalIP(): string {\n const interfaces = os.networkInterfaces();\n for (const name of Object.keys(interfaces)) {\n const netInterfaces = interfaces[name];\n if (!netInterfaces) continue;\n\n for (const net of netInterfaces) {\n if (!net.internal && net.family === \"IPv4\") {\n return net.address;\n }\n }\n }\n return \"127.0.0.1\";\n }\n\n start(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.running) {\n resolve();\n return;\n }\n\n this.socket = dgram.createSocket({ type: \"udp4\", reuseAddr: true });\n\n this.socket.on(\"error\", (err) => {\n this.stop();\n });\n\n this.socket.on(\"message\", (msg, rinfo) => {\n this.handleQuery(msg, rinfo);\n });\n\n this.socket.bind(MDNS_PORT, \"0.0.0.0\", () => {\n try {\n this.socket?.addMembership(MDNS_ADDRESS, this.ip);\n this.socket?.setMulticastTTL(255);\n this.socket?.setMulticastLoopback(true);\n this.running = true;\n this.announce();\n resolve();\n } catch (err) {\n reject(err);\n }\n });\n });\n }\n\n stop(): void {\n if (this.announceInterval) {\n clearInterval(this.announceInterval);\n this.announceInterval = null;\n }\n if (this.socket) {\n try {\n this.socket.dropMembership(MDNS_ADDRESS);\n } catch {\n // Ignore\n }\n this.socket.close();\n this.socket = null;\n }\n this.running = false;\n }\n\n private handleQuery(msg: Buffer, rinfo: dgram.RemoteInfo): void {\n try {\n const query = this.parseDNSQuery(msg);\n if (query && query.toLowerCase() === this.hostname.toLowerCase()) {\n this.sendResponse();\n }\n } catch {\n // Ignore malformed queries\n }\n }\n\n private parseDNSQuery(msg: Buffer): string | null {\n if (msg.length < 12) return null;\n\n const flags = msg.readUInt16BE(2);\n if ((flags & 0x8000) !== 0) return null;\n\n const qdcount = msg.readUInt16BE(4);\n if (qdcount < 1) return null;\n\n let offset = 12;\n const labels: string[] = [];\n\n while (offset < msg.length) {\n const len = msg[offset];\n if (len === 0) break;\n offset++;\n if (offset + len > msg.length) return null;\n labels.push(msg.slice(offset, offset + len).toString(\"ascii\"));\n offset += len;\n }\n\n return labels.join(\".\");\n }\n\n private sendResponse(): void {\n const response = this.buildDNSResponse();\n if (this.socket && this.running) {\n this.socket.send(response, 0, response.length, MDNS_PORT, MDNS_ADDRESS);\n }\n }\n\n private announce(): void {\n this.sendResponse();\n setTimeout(() => this.sendResponse(), 500);\n setTimeout(() => this.sendResponse(), 1000);\n setTimeout(() => this.sendResponse(), 2000);\n\n this.announceInterval = setInterval(() => {\n if (this.running) {\n this.sendResponse();\n }\n }, 30000);\n }\n\n private buildDNSResponse(): Buffer {\n const labels = this.hostname.split(\".\");\n const nameLength = labels.reduce((sum, l) => sum + 1 + l.length, 1);\n const bufferLength = 12 + nameLength + 2 + 2 + 4 + 2 + 4;\n const buffer = Buffer.alloc(bufferLength);\n\n let offset = 0;\n\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n buffer.writeUInt16BE(0x8400, offset);\n offset += 2;\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n buffer.writeUInt16BE(1, offset);\n offset += 2;\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n\n for (const label of labels) {\n buffer.writeUInt8(label.length, offset);\n offset++;\n buffer.write(label, offset, \"ascii\");\n offset += label.length;\n }\n buffer.writeUInt8(0, offset);\n offset++;\n\n buffer.writeUInt16BE(1, offset);\n offset += 2;\n buffer.writeUInt16BE(0x8001, offset);\n offset += 2;\n buffer.writeUInt32BE(120, offset);\n offset += 4;\n buffer.writeUInt16BE(4, offset);\n offset += 2;\n\n const ipParts = this.ip.split(\".\").map(Number);\n for (const part of ipParts) {\n buffer.writeUInt8(part, offset);\n offset++;\n }\n\n return buffer;\n }\n\n getInfo(): MDNSAdvertisement {\n return {\n hostname: this.hostname,\n port: this.port,\n ip: this.ip,\n };\n }\n}\n\n/**\n * Local HTTP proxy that listens on port 80 and forwards to the target port.\n */\nexport class LocalProxy {\n private server: http.Server | null = null;\n private targetPort: number;\n private running = false;\n\n constructor(targetPort: number) {\n this.targetPort = targetPort;\n }\n\n start(): Promise<boolean> {\n return new Promise((resolve) => {\n this.server = http.createServer((req, res) => {\n const options: http.RequestOptions = {\n hostname: \"localhost\",\n port: this.targetPort,\n path: req.url,\n method: req.method,\n headers: req.headers,\n };\n\n const proxyReq = http.request(options, (proxyRes) => {\n res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on(\"error\", (err) => {\n res.writeHead(502);\n res.end(`Proxy error: ${err.message}`);\n });\n\n req.pipe(proxyReq);\n });\n\n this.server.on(\"error\", () => {\n resolve(false);\n });\n\n this.server.listen(80, \"0.0.0.0\", () => {\n this.running = true;\n resolve(true);\n });\n });\n }\n\n stop(): void {\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n this.running = false;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n}\n\n/**\n * Local HTTPS proxy that listens on port 443 and forwards to the target port.\n */\nexport class LocalHttpsProxy {\n private server: https.Server | null = null;\n private targetPort: number;\n private hostname: string;\n private running = false;\n private _isTrusted = false;\n\n constructor(targetPort: number, hostname: string) {\n this.targetPort = targetPort;\n this.hostname = hostname;\n }\n\n get isTrusted(): boolean {\n return this._isTrusted;\n }\n\n async start(): Promise<boolean> {\n return new Promise((resolve) => {\n this.generateCert()\n .then((creds) => {\n if (!creds) {\n resolve(false);\n return;\n }\n\n this._isTrusted = creds.trusted;\n\n this.server = https.createServer(\n { key: creds.key, cert: creds.cert },\n (req, res) => {\n const options: http.RequestOptions = {\n hostname: \"localhost\",\n port: this.targetPort,\n path: req.url,\n method: req.method,\n headers: req.headers,\n };\n\n const proxyReq = http.request(options, (proxyRes) => {\n res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on(\"error\", (err) => {\n res.writeHead(502);\n res.end(`Proxy error: ${err.message}`);\n });\n\n req.pipe(proxyReq);\n },\n );\n\n this.server.on(\"error\", () => {\n resolve(false);\n });\n\n this.server.listen(443, \"0.0.0.0\", () => {\n this.running = true;\n resolve(true);\n });\n })\n .catch(() => resolve(false));\n });\n }\n\n private generateCert(): Promise<{\n key: string;\n cert: string;\n trusted: boolean;\n } | null> {\n return new Promise((resolve) => {\n const { execSync } = require(\"child_process\");\n const fs = require(\"fs\");\n const os = require(\"os\");\n const path = require(\"path\");\n\n const tmpDir = os.tmpdir();\n const keyFile = path.join(tmpDir, `outray-${Date.now()}-key.pem`);\n const certFile = path.join(tmpDir, `outray-${Date.now()}.pem`);\n\n // Try mkcert first\n try {\n execSync(\"which mkcert\", { stdio: \"pipe\" });\n execSync(\n `mkcert -key-file \"${keyFile}\" -cert-file \"${certFile}\" \"${this.hostname}\"`,\n { stdio: \"pipe\" },\n );\n\n const key = fs.readFileSync(keyFile, \"utf8\");\n const cert = fs.readFileSync(certFile, \"utf8\");\n fs.unlinkSync(keyFile);\n fs.unlinkSync(certFile);\n\n resolve({ key, cert, trusted: true });\n return;\n } catch {\n // Fall back to openssl\n }\n\n try {\n execSync(\n `openssl req -x509 -newkey rsa:2048 -keyout \"${keyFile}\" -out \"${certFile}\" -days 365 -nodes -subj \"/CN=${this.hostname}\" -addext \"subjectAltName=DNS:${this.hostname}\"`,\n { stdio: \"pipe\" },\n );\n\n const key = fs.readFileSync(keyFile, \"utf8\");\n const cert = fs.readFileSync(certFile, \"utf8\");\n fs.unlinkSync(keyFile);\n fs.unlinkSync(certFile);\n\n resolve({ key, cert, trusted: false });\n } catch {\n resolve(null);\n }\n });\n }\n\n stop(): void {\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n this.running = false;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n}\n\nexport interface LocalAccessInfo {\n hostname: string;\n ip: string;\n port: number;\n httpUrl?: string;\n httpsUrl?: string;\n httpsIsTrusted?: boolean;\n}\n\n/**\n * Manages local LAN access via mDNS and optional HTTP/HTTPS proxies\n */\nexport class LocalAccessManager {\n private mdnsAdvertiser: MDNSAdvertiser | null = null;\n private localProxy: LocalProxy | null = null;\n private localHttpsProxy: LocalHttpsProxy | null = null;\n private port: number;\n private subdomain: string;\n\n constructor(port: number, subdomain: string) {\n this.port = port;\n this.subdomain = subdomain;\n }\n\n async start(): Promise<LocalAccessInfo> {\n const hostname = `${this.subdomain}.local`;\n\n // Start mDNS\n this.mdnsAdvertiser = new MDNSAdvertiser(this.subdomain, this.port);\n await this.mdnsAdvertiser.start();\n const info = this.mdnsAdvertiser.getInfo();\n\n const result: LocalAccessInfo = {\n hostname: info.hostname,\n ip: info.ip,\n port: this.port,\n };\n\n // Try HTTPS proxy (port 443)\n this.localHttpsProxy = new LocalHttpsProxy(this.port, hostname);\n const httpsStarted = await this.localHttpsProxy.start();\n if (httpsStarted) {\n result.httpsUrl = `https://${hostname}`;\n result.httpsIsTrusted = this.localHttpsProxy.isTrusted;\n }\n\n // Try HTTP proxy (port 80)\n this.localProxy = new LocalProxy(this.port);\n const httpStarted = await this.localProxy.start();\n if (httpStarted) {\n result.httpUrl = `http://${hostname}`;\n }\n\n return result;\n }\n\n stop(): void {\n if (this.localHttpsProxy) {\n this.localHttpsProxy.stop();\n this.localHttpsProxy = null;\n }\n if (this.localProxy) {\n this.localProxy.stop();\n this.localProxy = null;\n }\n if (this.mdnsAdvertiser) {\n this.mdnsAdvertiser.stop();\n this.mdnsAdvertiser = null;\n }\n }\n}\n","/**\n * Tunnel protocol types - HTTP\n */\nexport type TunnelProtocol = \"http\" | \"tcp\" | \"udp\";\n\n// ============================================================================\n// Client Configuration\n// ============================================================================\n\n/**\n * Options for creating an OutrayClient instance\n */\nexport interface OutrayClientOptions {\n /**\n * Local port to proxy requests to\n */\n localPort: number;\n\n /**\n * Outray server WebSocket URL\n * @default 'wss://api.outray.dev/'\n */\n serverUrl?: string;\n\n /**\n * API key for authentication\n */\n apiKey?: string;\n\n /**\n * Subdomain to use for the tunnel URL\n * Requires authentication\n */\n subdomain?: string;\n\n /**\n * Custom domain for the tunnel\n * Must be configured in the Outray dashboard first\n */\n customDomain?: string;\n\n /**\n * Tunnel protocol type\n * @default 'http'\n */\n protocol?: TunnelProtocol;\n\n /**\n * Remote port for TCP/UDP tunnels\n */\n remotePort?: number;\n\n /**\n * Callback fired when tunnel is successfully established\n */\n onTunnelReady?: (url: string, port?: number) => void;\n\n /**\n * Callback fired when tunnel encounters an error\n */\n onError?: (error: Error, code?: string) => void;\n\n /**\n * Callback fired when tunnel is closed\n */\n onClose?: (reason?: string) => void;\n\n /**\n * Callback fired when attempting to reconnect\n */\n onReconnecting?: (attempt: number, delay: number) => void;\n\n /**\n * Callback fired for each proxied request (HTTP only)\n */\n onRequest?: (info: RequestInfo) => void;\n}\n\n/**\n * Information about a proxied request\n */\nexport interface RequestInfo {\n method: string;\n path: string;\n statusCode: number;\n duration: number;\n error?: string;\n}\n\n// ============================================================================\n// Protocol Messages - Client to Server\n// ============================================================================\n\nexport interface OpenTunnelMessage {\n type: \"open_tunnel\";\n subdomain?: string | null;\n customDomain?: string | null;\n apiKey?: string;\n forceTakeover?: boolean;\n protocol?: TunnelProtocol;\n remotePort?: number;\n}\n\nexport interface TunnelResponseMessage {\n type: \"response\";\n requestId: string;\n statusCode: number;\n headers: Record<string, string | string[]>;\n body?: string;\n}\n\nexport interface TCPDataMessage {\n type: \"tcp_data\";\n connectionId: string;\n data: string; // base64 encoded\n}\n\nexport interface TCPCloseMessage {\n type: \"tcp_close\";\n connectionId: string;\n}\n\nexport interface UDPResponseMessage {\n type: \"udp_response\";\n packetId: string;\n targetAddress: string;\n targetPort: number;\n data: string; // base64 encoded\n}\n\n// WebSocket passthrough messages (client → server)\nexport interface WSUpgradeResponseMessage {\n type: \"ws_upgrade_response\";\n wsConnectionId: string;\n success: boolean;\n error?: string;\n}\n\nexport interface WSFrameMessage {\n type: \"ws_frame\";\n wsConnectionId: string;\n data: string; // base64 encoded\n isBinary: boolean;\n}\n\nexport interface WSCloseMessage {\n type: \"ws_close\";\n wsConnectionId: string;\n code?: number;\n reason?: string;\n}\n\nexport type ClientMessage =\n | OpenTunnelMessage\n | TunnelResponseMessage\n | TCPDataMessage\n | TCPCloseMessage\n | UDPResponseMessage\n | WSUpgradeResponseMessage\n | WSFrameMessage\n | WSCloseMessage;\n\n// ============================================================================\n// Protocol Messages - Server to Client\n// ============================================================================\n\nexport interface TunnelOpenedMessage {\n type: \"tunnel_opened\";\n url: string;\n protocol?: TunnelProtocol;\n port?: number;\n}\n\nexport interface TunnelDataMessage {\n type: \"request\";\n requestId: string;\n method: string;\n path: string;\n headers: Record<string, string | string[]>;\n body?: string;\n}\n\nexport interface TCPConnectionMessage {\n type: \"tcp_connection\";\n connectionId: string;\n}\n\nexport interface TCPIncomingDataMessage {\n type: \"tcp_data\";\n connectionId: string;\n data: string; // base64 encoded\n}\n\nexport interface TCPIncomingCloseMessage {\n type: \"tcp_close\";\n connectionId: string;\n}\n\nexport interface UDPDataMessage {\n type: \"udp_data\";\n packetId: string;\n sourceAddress: string;\n sourcePort: number;\n data: string; // base64 encoded\n}\n\nexport interface ErrorMessage {\n type: \"error\";\n code: string;\n message: string;\n}\n\n// WebSocket passthrough messages (server → client)\nexport interface WSUpgradeMessage {\n type: \"ws_upgrade\";\n wsConnectionId: string;\n path: string;\n headers: Record<string, string | string[]>;\n protocol?: string;\n}\n\nexport type ServerMessage =\n | TunnelOpenedMessage\n | TunnelDataMessage\n | TCPConnectionMessage\n | TCPIncomingDataMessage\n | TCPIncomingCloseMessage\n | UDPDataMessage\n | ErrorMessage\n | WSUpgradeMessage\n | WSFrameMessage\n | WSCloseMessage;\n\n// ============================================================================\n// Error Codes\n// ============================================================================\n\nexport const ErrorCodes = {\n SUBDOMAIN_IN_USE: \"SUBDOMAIN_IN_USE\",\n AUTH_FAILED: \"AUTH_FAILED\",\n LIMIT_EXCEEDED: \"LIMIT_EXCEEDED\",\n INVALID_SUBDOMAIN: \"INVALID_SUBDOMAIN\",\n CUSTOM_DOMAIN_NOT_CONFIGURED: \"CUSTOM_DOMAIN_NOT_CONFIGURED\",\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n"],"mappings":"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","names":["WebSocket","http","import_http","os","dgram","http","https"]} | ||
| {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/protocol.ts","../src/mdns.ts","../src/types.ts"],"sourcesContent":["// Core client\nexport { OutrayClient } from \"./client\";\n\n// mDNS / Local access\nexport {\n MDNSAdvertiser,\n LocalProxy,\n LocalHttpsProxy,\n LocalAccessManager,\n} from \"./mdns\";\nexport type { LocalAccessInfo } from \"./mdns\";\n\n// Protocol utilities\nexport { encodeMessage, decodeMessage } from \"./protocol\";\n\n// Types\nexport type {\n // Client options\n OutrayClientOptions,\n RequestInfo,\n TunnelProtocol,\n // Protocol messages\n ClientMessage,\n ServerMessage,\n OpenTunnelMessage,\n TunnelOpenedMessage,\n TunnelDataMessage,\n TunnelResponseMessage,\n TCPConnectionMessage,\n TCPDataMessage,\n TCPCloseMessage,\n TCPIncomingDataMessage,\n TCPIncomingCloseMessage,\n UDPDataMessage,\n UDPResponseMessage,\n ErrorMessage,\n ErrorCode,\n // WebSocket passthrough\n WSUpgradeMessage,\n WSUpgradeResponseMessage,\n WSFrameMessage,\n WSCloseMessage,\n} from \"./types\";\n\n// Error codes constant\nexport { ErrorCodes } from \"./types\";\n","import WebSocket from \"ws\";\nimport http from \"http\";\nimport { encodeMessage, decodeMessage } from \"./protocol\";\nimport type {\n OutrayClientOptions,\n TunnelDataMessage,\n TunnelResponseMessage,\n WSUpgradeMessage,\n WSFrameMessage,\n WSCloseMessage,\n ErrorCodes,\n} from \"./types\";\n\nconst DEFAULT_SERVER_URL = \"wss://api.outray.dev/\";\nconst PING_INTERVAL_MS = 25000;\nconst PONG_TIMEOUT_MS = 10000;\n\n/**\n * Core Outray tunnel client.\n *\n * Establishes a WebSocket connection to the Outray server and proxies\n * HTTP requests to a local server.\n *\n * @example\n * ```ts\n * const client = new OutrayClient({\n * localPort: 3000,\n * onTunnelReady: (url) => console.log(`Tunnel: ${url}`),\n * onError: (err) => console.error(err),\n * });\n *\n * client.start();\n *\n * // Later...\n * client.stop();\n * ```\n */\nexport class OutrayClient {\n private ws: WebSocket | null = null;\n private options: Required<\n Pick<OutrayClientOptions, \"localPort\" | \"serverUrl\">\n > &\n OutrayClientOptions;\n private reconnectTimeout: NodeJS.Timeout | null = null;\n private pingInterval: NodeJS.Timeout | null = null;\n private pongTimeout: NodeJS.Timeout | null = null;\n private shouldReconnect = true;\n private assignedUrl: string | null = null;\n private subdomain?: string;\n private forceTakeover = false;\n private reconnectAttempts = 0;\n private lastPongReceived = Date.now();\n private localWebSockets = new Map<string, WebSocket>();\n\n constructor(options: OutrayClientOptions) {\n this.options = {\n ...options,\n serverUrl: options.serverUrl ?? DEFAULT_SERVER_URL,\n };\n this.subdomain = options.subdomain;\n }\n\n /**\n * Start the tunnel connection\n */\n public start(): void {\n this.shouldReconnect = true;\n this.connect();\n }\n\n /**\n * Stop the tunnel connection\n */\n public stop(): void {\n this.shouldReconnect = false;\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n\n this.stopPing();\n this.stopPongTimeout();\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n // Clean up all local WebSocket connections\n for (const [id, localWs] of this.localWebSockets) {\n localWs.close();\n }\n this.localWebSockets.clear();\n }\n\n /**\n * Get the assigned tunnel URL (if connected)\n */\n public getUrl(): string | null {\n return this.assignedUrl;\n }\n\n /**\n * Check if the client is currently connected\n */\n public isConnected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n private connect(): void {\n this.ws = new WebSocket(this.options.serverUrl);\n\n this.ws.on(\"open\", () => this.handleOpen());\n this.ws.on(\"message\", (data) => this.handleMessage(data.toString()));\n this.ws.on(\"close\", (code, reason) => this.handleClose(code, reason));\n this.ws.on(\"error\", (error) => {\n this.options.onError?.(error);\n });\n this.ws.on(\"pong\", () => {\n this.lastPongReceived = Date.now();\n this.stopPongTimeout();\n });\n }\n\n private handleOpen(): void {\n this.startPing();\n\n const handshake = encodeMessage({\n type: \"open_tunnel\",\n apiKey: this.options.apiKey,\n subdomain: this.subdomain,\n customDomain: this.options.customDomain,\n forceTakeover: this.forceTakeover,\n protocol: this.options.protocol,\n remotePort: this.options.remotePort,\n });\n this.ws?.send(handshake);\n }\n\n private handleMessage(data: string): void {\n try {\n const message = decodeMessage(data);\n\n if (message.type === \"tunnel_opened\") {\n this.assignedUrl = message.url;\n const derivedSubdomain = this.extractSubdomain(message.url);\n if (derivedSubdomain) {\n this.subdomain = derivedSubdomain;\n }\n this.forceTakeover = false;\n this.reconnectAttempts = 0;\n this.options.onTunnelReady?.(message.url, message.port);\n } else if (message.type === \"error\") {\n this.handleError(message.code, message.message);\n } else if (message.type === \"request\") {\n this.handleTunnelData(message);\n } else if (message.type === \"ws_upgrade\") {\n this.handleWSUpgrade(message as WSUpgradeMessage);\n } else if (message.type === \"ws_frame\") {\n this.handleWSFrame(message as WSFrameMessage);\n } else if (message.type === \"ws_close\") {\n this.handleWSClose(message as WSCloseMessage);\n }\n } catch (error) {\n this.options.onError?.(\n error instanceof Error ? error : new Error(String(error))\n );\n }\n }\n\n private handleError(code: string, message: string): void {\n if (code === \"SUBDOMAIN_IN_USE\" && this.assignedUrl && !this.forceTakeover) {\n // Reconnecting and subdomain is in use - try to take over\n this.forceTakeover = true;\n this.connect();\n return;\n }\n\n this.options.onError?.(new Error(message), code);\n\n // Fatal errors - stop reconnecting\n if (code === \"AUTH_FAILED\" || code === \"LIMIT_EXCEEDED\") {\n this.shouldReconnect = false;\n this.stop();\n }\n }\n\n private handleTunnelData(message: TunnelDataMessage): void {\n const startTime = Date.now();\n\n const reqOptions = {\n hostname: \"localhost\",\n port: this.options.localPort,\n path: message.path,\n method: message.method,\n headers: message.headers,\n };\n\n const req = http.request(reqOptions, (res) => {\n const chunks: Buffer[] = [];\n\n res.on(\"data\", (chunk) => {\n chunks.push(Buffer.from(chunk));\n });\n\n res.on(\"end\", () => {\n const duration = Date.now() - startTime;\n const statusCode = res.statusCode || 200;\n\n this.options.onRequest?.({\n method: message.method,\n path: message.path,\n statusCode,\n duration,\n });\n\n const bodyBuffer = Buffer.concat(chunks);\n const bodyBase64 =\n bodyBuffer.length > 0 ? bodyBuffer.toString(\"base64\") : undefined;\n\n const response: TunnelResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n statusCode,\n headers: res.headers as Record<string, string | string[]>,\n body: bodyBase64,\n };\n\n this.ws?.send(encodeMessage(response));\n });\n });\n\n req.on(\"error\", (err) => {\n const duration = Date.now() - startTime;\n\n this.options.onRequest?.({\n method: message.method,\n path: message.path,\n statusCode: 502,\n duration,\n error: err.message,\n });\n\n const errorResponse: TunnelResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n statusCode: 502,\n headers: { \"content-type\": \"text/plain\" },\n body: Buffer.from(`Bad Gateway: ${err.message}`).toString(\"base64\"),\n };\n\n this.ws?.send(encodeMessage(errorResponse));\n });\n\n if (message.body) {\n const bodyBuffer = Buffer.from(message.body, \"base64\");\n req.write(bodyBuffer);\n }\n\n req.end();\n }\n\n private handleWSUpgrade(message: WSUpgradeMessage): void {\n const wsUrl = `ws://localhost:${this.options.localPort}${message.path}`;\n\n try {\n const headers: Record<string, string> = {};\n // Forward relevant headers (like subprotocols)\n if (message.protocol) {\n headers[\"Sec-WebSocket-Protocol\"] = message.protocol;\n }\n\n const localWs = new WebSocket(wsUrl, {\n headers,\n });\n\n localWs.on(\"open\", () => {\n this.localWebSockets.set(message.wsConnectionId, localWs);\n\n // Confirm the upgrade to the server\n this.ws?.send(\n encodeMessage({\n type: \"ws_upgrade_response\",\n wsConnectionId: message.wsConnectionId,\n success: true,\n })\n );\n });\n\n // Relay frames from local WS to server\n localWs.on(\"message\", (data: WebSocket.RawData, isBinary: boolean) => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n const buffer = Buffer.isBuffer(data)\n ? data\n : Buffer.from(data as ArrayBuffer);\n this.ws.send(\n encodeMessage({\n type: \"ws_frame\",\n wsConnectionId: message.wsConnectionId,\n data: buffer.toString(\"base64\"),\n isBinary,\n })\n );\n }\n });\n\n localWs.on(\"close\", (code, reason) => {\n this.localWebSockets.delete(message.wsConnectionId);\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(\n encodeMessage({\n type: \"ws_close\",\n wsConnectionId: message.wsConnectionId,\n code,\n reason: reason?.toString(),\n })\n );\n }\n });\n\n localWs.on(\"error\", (error) => {\n this.localWebSockets.delete(message.wsConnectionId);\n\n // If not yet open, send failure response\n if (localWs.readyState === WebSocket.CONNECTING) {\n this.ws?.send(\n encodeMessage({\n type: \"ws_upgrade_response\",\n wsConnectionId: message.wsConnectionId,\n success: false,\n error: `Failed to connect to local WebSocket: ${error.message}`,\n })\n );\n }\n });\n } catch (error) {\n this.ws?.send(\n encodeMessage({\n type: \"ws_upgrade_response\",\n wsConnectionId: message.wsConnectionId,\n success: false,\n error: `Failed to create WebSocket connection: ${error instanceof Error ? error.message : String(error)}`,\n })\n );\n }\n }\n\n private handleWSFrame(message: WSFrameMessage): void {\n const localWs = this.localWebSockets.get(message.wsConnectionId);\n if (!localWs || localWs.readyState !== WebSocket.OPEN) {\n return;\n }\n\n const data = Buffer.from(message.data, \"base64\");\n localWs.send(data, { binary: message.isBinary });\n }\n\n private handleWSClose(message: WSCloseMessage): void {\n const localWs = this.localWebSockets.get(message.wsConnectionId);\n if (!localWs) {\n return;\n }\n\n this.localWebSockets.delete(message.wsConnectionId);\n localWs.close(message.code || 1000, message.reason || \"\");\n }\n\n private extractSubdomain(url: string): string | null {\n try {\n const hostname = new URL(url).hostname;\n const [subdomain] = hostname.split(\".\");\n return subdomain || null;\n } catch {\n return null;\n }\n }\n\n private startPing(): void {\n this.stopPing();\n this.lastPongReceived = Date.now();\n\n this.pingInterval = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.ping();\n\n this.stopPongTimeout();\n this.pongTimeout = setTimeout(() => {\n // No pong received - connection is likely dead\n if (this.ws) {\n this.ws.terminate();\n }\n }, PONG_TIMEOUT_MS);\n }\n }, PING_INTERVAL_MS);\n }\n\n private stopPing(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n private stopPongTimeout(): void {\n if (this.pongTimeout) {\n clearTimeout(this.pongTimeout);\n this.pongTimeout = null;\n }\n }\n\n private handleClose(code?: number, reason?: Buffer): void {\n this.stopPing();\n this.stopPongTimeout();\n\n if (!this.shouldReconnect) return;\n\n const reasonStr = reason?.toString() || \"\";\n\n if (code === 1000 && reasonStr === \"Tunnel stopped by user\") {\n this.options.onClose?.(reasonStr);\n this.stop();\n return;\n }\n\n // If we previously had a tunnel URL, force takeover on reconnect\n if (this.assignedUrl) {\n this.forceTakeover = true;\n }\n\n const delay = Math.min(2000 * Math.pow(2, this.reconnectAttempts), 30000);\n this.reconnectAttempts += 1;\n\n this.options.onReconnecting?.(this.reconnectAttempts, delay);\n\n this.reconnectTimeout = setTimeout(() => {\n this.connect();\n }, delay);\n }\n}\n","import type { ClientMessage, ServerMessage } from \"./types\";\n\n/**\n * Encode a client message to send to the server\n */\nexport function encodeMessage(message: ClientMessage): string {\n return JSON.stringify(message);\n}\n\n/**\n * Decode a server message received from the server\n */\nexport function decodeMessage(data: string): ServerMessage {\n return JSON.parse(data) as ServerMessage;\n}\n","import dgram from \"dgram\";\nimport http from \"http\";\nimport https from \"https\";\nimport os from \"os\";\n\nconst MDNS_PORT = 5353;\nconst MDNS_ADDRESS = \"224.0.0.251\";\n\ninterface MDNSAdvertisement {\n hostname: string;\n port: number;\n ip: string;\n}\n\n/**\n * Simple mDNS responder that advertises a .local hostname\n * This allows other devices on the LAN to discover and access the tunnel\n */\nexport class MDNSAdvertiser {\n private socket: dgram.Socket | null = null;\n private hostname: string;\n private port: number;\n private ip: string;\n private running = false;\n private announceInterval: NodeJS.Timeout | null = null;\n\n constructor(hostname: string, port: number) {\n this.hostname = hostname.endsWith(\".local\")\n ? hostname\n : `${hostname}.local`;\n this.port = port;\n this.ip = this.getLocalIP();\n }\n\n private getLocalIP(): string {\n const interfaces = os.networkInterfaces();\n for (const name of Object.keys(interfaces)) {\n const netInterfaces = interfaces[name];\n if (!netInterfaces) continue;\n\n for (const net of netInterfaces) {\n if (!net.internal && net.family === \"IPv4\") {\n return net.address;\n }\n }\n }\n return \"127.0.0.1\";\n }\n\n start(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.running) {\n resolve();\n return;\n }\n\n this.socket = dgram.createSocket({ type: \"udp4\", reuseAddr: true });\n\n this.socket.on(\"error\", (err) => {\n this.stop();\n });\n\n this.socket.on(\"message\", (msg, rinfo) => {\n this.handleQuery(msg, rinfo);\n });\n\n this.socket.bind(MDNS_PORT, \"0.0.0.0\", () => {\n try {\n this.socket?.addMembership(MDNS_ADDRESS, this.ip);\n this.socket?.setMulticastTTL(255);\n this.socket?.setMulticastLoopback(true);\n this.running = true;\n this.announce();\n resolve();\n } catch (err) {\n reject(err);\n }\n });\n });\n }\n\n stop(): void {\n if (this.announceInterval) {\n clearInterval(this.announceInterval);\n this.announceInterval = null;\n }\n if (this.socket) {\n try {\n this.socket.dropMembership(MDNS_ADDRESS);\n } catch {\n // Ignore\n }\n this.socket.close();\n this.socket = null;\n }\n this.running = false;\n }\n\n private handleQuery(msg: Buffer, rinfo: dgram.RemoteInfo): void {\n try {\n const query = this.parseDNSQuery(msg);\n if (query && query.toLowerCase() === this.hostname.toLowerCase()) {\n this.sendResponse();\n }\n } catch {\n // Ignore malformed queries\n }\n }\n\n private parseDNSQuery(msg: Buffer): string | null {\n if (msg.length < 12) return null;\n\n const flags = msg.readUInt16BE(2);\n if ((flags & 0x8000) !== 0) return null;\n\n const qdcount = msg.readUInt16BE(4);\n if (qdcount < 1) return null;\n\n let offset = 12;\n const labels: string[] = [];\n\n while (offset < msg.length) {\n const len = msg[offset];\n if (len === 0) break;\n offset++;\n if (offset + len > msg.length) return null;\n labels.push(msg.slice(offset, offset + len).toString(\"ascii\"));\n offset += len;\n }\n\n return labels.join(\".\");\n }\n\n private sendResponse(): void {\n const response = this.buildDNSResponse();\n if (this.socket && this.running) {\n this.socket.send(response, 0, response.length, MDNS_PORT, MDNS_ADDRESS);\n }\n }\n\n private announce(): void {\n this.sendResponse();\n setTimeout(() => this.sendResponse(), 500);\n setTimeout(() => this.sendResponse(), 1000);\n setTimeout(() => this.sendResponse(), 2000);\n\n this.announceInterval = setInterval(() => {\n if (this.running) {\n this.sendResponse();\n }\n }, 30000);\n }\n\n private buildDNSResponse(): Buffer {\n const labels = this.hostname.split(\".\");\n const nameLength = labels.reduce((sum, l) => sum + 1 + l.length, 1);\n const bufferLength = 12 + nameLength + 2 + 2 + 4 + 2 + 4;\n const buffer = Buffer.alloc(bufferLength);\n\n let offset = 0;\n\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n buffer.writeUInt16BE(0x8400, offset);\n offset += 2;\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n buffer.writeUInt16BE(1, offset);\n offset += 2;\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n\n for (const label of labels) {\n buffer.writeUInt8(label.length, offset);\n offset++;\n buffer.write(label, offset, \"ascii\");\n offset += label.length;\n }\n buffer.writeUInt8(0, offset);\n offset++;\n\n buffer.writeUInt16BE(1, offset);\n offset += 2;\n buffer.writeUInt16BE(0x8001, offset);\n offset += 2;\n buffer.writeUInt32BE(120, offset);\n offset += 4;\n buffer.writeUInt16BE(4, offset);\n offset += 2;\n\n const ipParts = this.ip.split(\".\").map(Number);\n for (const part of ipParts) {\n buffer.writeUInt8(part, offset);\n offset++;\n }\n\n return buffer;\n }\n\n getInfo(): MDNSAdvertisement {\n return {\n hostname: this.hostname,\n port: this.port,\n ip: this.ip,\n };\n }\n}\n\n/**\n * Local HTTP proxy that listens on port 80 and forwards to the target port.\n */\nexport class LocalProxy {\n private server: http.Server | null = null;\n private targetPort: number;\n private running = false;\n\n constructor(targetPort: number) {\n this.targetPort = targetPort;\n }\n\n start(): Promise<boolean> {\n return new Promise((resolve) => {\n this.server = http.createServer((req, res) => {\n const options: http.RequestOptions = {\n hostname: \"localhost\",\n port: this.targetPort,\n path: req.url,\n method: req.method,\n headers: req.headers,\n };\n\n const proxyReq = http.request(options, (proxyRes) => {\n res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on(\"error\", (err) => {\n res.writeHead(502);\n res.end(`Proxy error: ${err.message}`);\n });\n\n req.pipe(proxyReq);\n });\n\n this.server.on(\"error\", () => {\n resolve(false);\n });\n\n this.server.listen(80, \"0.0.0.0\", () => {\n this.running = true;\n resolve(true);\n });\n });\n }\n\n stop(): void {\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n this.running = false;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n}\n\n/**\n * Local HTTPS proxy that listens on port 443 and forwards to the target port.\n */\nexport class LocalHttpsProxy {\n private server: https.Server | null = null;\n private targetPort: number;\n private hostname: string;\n private running = false;\n private _isTrusted = false;\n\n constructor(targetPort: number, hostname: string) {\n this.targetPort = targetPort;\n this.hostname = hostname;\n }\n\n get isTrusted(): boolean {\n return this._isTrusted;\n }\n\n async start(): Promise<boolean> {\n return new Promise((resolve) => {\n this.generateCert()\n .then((creds) => {\n if (!creds) {\n resolve(false);\n return;\n }\n\n this._isTrusted = creds.trusted;\n\n this.server = https.createServer(\n { key: creds.key, cert: creds.cert },\n (req, res) => {\n const options: http.RequestOptions = {\n hostname: \"localhost\",\n port: this.targetPort,\n path: req.url,\n method: req.method,\n headers: req.headers,\n };\n\n const proxyReq = http.request(options, (proxyRes) => {\n res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on(\"error\", (err) => {\n res.writeHead(502);\n res.end(`Proxy error: ${err.message}`);\n });\n\n req.pipe(proxyReq);\n },\n );\n\n this.server.on(\"error\", () => {\n resolve(false);\n });\n\n this.server.listen(443, \"0.0.0.0\", () => {\n this.running = true;\n resolve(true);\n });\n })\n .catch(() => resolve(false));\n });\n }\n\n private generateCert(): Promise<{\n key: string;\n cert: string;\n trusted: boolean;\n } | null> {\n return new Promise((resolve) => {\n const { execSync } = require(\"child_process\");\n const fs = require(\"fs\");\n const os = require(\"os\");\n const path = require(\"path\");\n\n const tmpDir = os.tmpdir();\n const keyFile = path.join(tmpDir, `outray-${Date.now()}-key.pem`);\n const certFile = path.join(tmpDir, `outray-${Date.now()}.pem`);\n\n // Try mkcert first\n try {\n execSync(\"which mkcert\", { stdio: \"pipe\" });\n execSync(\n `mkcert -key-file \"${keyFile}\" -cert-file \"${certFile}\" \"${this.hostname}\"`,\n { stdio: \"pipe\" },\n );\n\n const key = fs.readFileSync(keyFile, \"utf8\");\n const cert = fs.readFileSync(certFile, \"utf8\");\n fs.unlinkSync(keyFile);\n fs.unlinkSync(certFile);\n\n resolve({ key, cert, trusted: true });\n return;\n } catch {\n // Fall back to openssl\n }\n\n try {\n execSync(\n `openssl req -x509 -newkey rsa:2048 -keyout \"${keyFile}\" -out \"${certFile}\" -days 365 -nodes -subj \"/CN=${this.hostname}\" -addext \"subjectAltName=DNS:${this.hostname}\"`,\n { stdio: \"pipe\" },\n );\n\n const key = fs.readFileSync(keyFile, \"utf8\");\n const cert = fs.readFileSync(certFile, \"utf8\");\n fs.unlinkSync(keyFile);\n fs.unlinkSync(certFile);\n\n resolve({ key, cert, trusted: false });\n } catch {\n resolve(null);\n }\n });\n }\n\n stop(): void {\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n this.running = false;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n}\n\nexport interface LocalAccessInfo {\n hostname: string;\n ip: string;\n port: number;\n httpUrl?: string;\n httpsUrl?: string;\n httpsIsTrusted?: boolean;\n}\n\n/**\n * Manages local LAN access via mDNS and optional HTTP/HTTPS proxies\n */\nexport class LocalAccessManager {\n private mdnsAdvertiser: MDNSAdvertiser | null = null;\n private localProxy: LocalProxy | null = null;\n private localHttpsProxy: LocalHttpsProxy | null = null;\n private port: number;\n private subdomain: string;\n\n constructor(port: number, subdomain: string) {\n this.port = port;\n this.subdomain = subdomain;\n }\n\n async start(): Promise<LocalAccessInfo> {\n const hostname = `${this.subdomain}.local`;\n\n // Start mDNS\n this.mdnsAdvertiser = new MDNSAdvertiser(this.subdomain, this.port);\n await this.mdnsAdvertiser.start();\n const info = this.mdnsAdvertiser.getInfo();\n\n const result: LocalAccessInfo = {\n hostname: info.hostname,\n ip: info.ip,\n port: this.port,\n };\n\n // Try HTTPS proxy (port 443)\n this.localHttpsProxy = new LocalHttpsProxy(this.port, hostname);\n const httpsStarted = await this.localHttpsProxy.start();\n if (httpsStarted) {\n result.httpsUrl = `https://${hostname}`;\n result.httpsIsTrusted = this.localHttpsProxy.isTrusted;\n }\n\n // Try HTTP proxy (port 80)\n this.localProxy = new LocalProxy(this.port);\n const httpStarted = await this.localProxy.start();\n if (httpStarted) {\n result.httpUrl = `http://${hostname}`;\n }\n\n return result;\n }\n\n stop(): void {\n if (this.localHttpsProxy) {\n this.localHttpsProxy.stop();\n this.localHttpsProxy = null;\n }\n if (this.localProxy) {\n this.localProxy.stop();\n this.localProxy = null;\n }\n if (this.mdnsAdvertiser) {\n this.mdnsAdvertiser.stop();\n this.mdnsAdvertiser = null;\n }\n }\n}\n","/**\n * Tunnel protocol types - HTTP\n */\nexport type TunnelProtocol = \"http\" | \"tcp\" | \"udp\";\n\n// ============================================================================\n// Client Configuration\n// ============================================================================\n\n/**\n * Options for creating an OutrayClient instance\n */\nexport interface OutrayClientOptions {\n /**\n * Local port to proxy requests to\n */\n localPort: number;\n\n /**\n * Outray server WebSocket URL\n * @default 'wss://api.outray.dev/'\n */\n serverUrl?: string;\n\n /**\n * API key for authentication\n */\n apiKey?: string;\n\n /**\n * Subdomain to use for the tunnel URL\n * Requires authentication\n */\n subdomain?: string;\n\n /**\n * Custom domain for the tunnel\n * Must be configured in the Outray dashboard first\n */\n customDomain?: string;\n\n /**\n * Tunnel protocol type\n * @default 'http'\n */\n protocol?: TunnelProtocol;\n\n /**\n * Remote port for TCP/UDP tunnels\n */\n remotePort?: number;\n\n /**\n * Callback fired when tunnel is successfully established\n */\n onTunnelReady?: (url: string, port?: number) => void;\n\n /**\n * Callback fired when tunnel encounters an error\n */\n onError?: (error: Error, code?: string) => void;\n\n /**\n * Callback fired when tunnel is closed\n */\n onClose?: (reason?: string) => void;\n\n /**\n * Callback fired when attempting to reconnect\n */\n onReconnecting?: (attempt: number, delay: number) => void;\n\n /**\n * Callback fired for each proxied request (HTTP only)\n */\n onRequest?: (info: RequestInfo) => void;\n}\n\n/**\n * Information about a proxied request\n */\nexport interface RequestInfo {\n method: string;\n path: string;\n statusCode: number;\n duration: number;\n error?: string;\n}\n\n// ============================================================================\n// Protocol Messages - Client to Server\n// ============================================================================\n\nexport interface OpenTunnelMessage {\n type: \"open_tunnel\";\n subdomain?: string | null;\n customDomain?: string | null;\n apiKey?: string;\n forceTakeover?: boolean;\n protocol?: TunnelProtocol;\n remotePort?: number;\n password?: string;\n}\n\nexport interface TunnelResponseMessage {\n type: \"response\";\n requestId: string;\n statusCode: number;\n headers: Record<string, string | string[]>;\n body?: string;\n}\n\nexport interface TCPDataMessage {\n type: \"tcp_data\";\n connectionId: string;\n data: string; // base64 encoded\n}\n\nexport interface TCPCloseMessage {\n type: \"tcp_close\";\n connectionId: string;\n}\n\nexport interface UDPResponseMessage {\n type: \"udp_response\";\n packetId: string;\n targetAddress: string;\n targetPort: number;\n data: string; // base64 encoded\n}\n\n// WebSocket passthrough messages (client → server)\nexport interface WSUpgradeResponseMessage {\n type: \"ws_upgrade_response\";\n wsConnectionId: string;\n success: boolean;\n error?: string;\n}\n\nexport interface WSFrameMessage {\n type: \"ws_frame\";\n wsConnectionId: string;\n data: string; // base64 encoded\n isBinary: boolean;\n}\n\nexport interface WSCloseMessage {\n type: \"ws_close\";\n wsConnectionId: string;\n code?: number;\n reason?: string;\n}\n\nexport type ClientMessage =\n | OpenTunnelMessage\n | TunnelResponseMessage\n | TCPDataMessage\n | TCPCloseMessage\n | UDPResponseMessage\n | WSUpgradeResponseMessage\n | WSFrameMessage\n | WSCloseMessage;\n\n// ============================================================================\n// Protocol Messages - Server to Client\n// ============================================================================\n\nexport interface TunnelOpenedMessage {\n type: \"tunnel_opened\";\n url: string;\n protocol?: TunnelProtocol;\n port?: number;\n}\n\nexport interface TunnelDataMessage {\n type: \"request\";\n requestId: string;\n method: string;\n path: string;\n headers: Record<string, string | string[]>;\n body?: string;\n}\n\nexport interface TCPConnectionMessage {\n type: \"tcp_connection\";\n connectionId: string;\n}\n\nexport interface TCPIncomingDataMessage {\n type: \"tcp_data\";\n connectionId: string;\n data: string; // base64 encoded\n}\n\nexport interface TCPIncomingCloseMessage {\n type: \"tcp_close\";\n connectionId: string;\n}\n\nexport interface UDPDataMessage {\n type: \"udp_data\";\n packetId: string;\n sourceAddress: string;\n sourcePort: number;\n data: string; // base64 encoded\n}\n\nexport interface ErrorMessage {\n type: \"error\";\n code: string;\n message: string;\n}\n\n// WebSocket passthrough messages (server → client)\nexport interface WSUpgradeMessage {\n type: \"ws_upgrade\";\n wsConnectionId: string;\n path: string;\n headers: Record<string, string | string[]>;\n protocol?: string;\n}\n\nexport type ServerMessage =\n | TunnelOpenedMessage\n | TunnelDataMessage\n | TCPConnectionMessage\n | TCPIncomingDataMessage\n | TCPIncomingCloseMessage\n | UDPDataMessage\n | ErrorMessage\n | WSUpgradeMessage\n | WSFrameMessage\n | WSCloseMessage;\n\n// ============================================================================\n// Error Codes\n// ============================================================================\n\nexport const ErrorCodes = {\n SUBDOMAIN_IN_USE: \"SUBDOMAIN_IN_USE\",\n AUTH_FAILED: \"AUTH_FAILED\",\n LIMIT_EXCEEDED: \"LIMIT_EXCEEDED\",\n INVALID_SUBDOMAIN: \"INVALID_SUBDOMAIN\",\n CUSTOM_DOMAIN_NOT_CONFIGURED: \"CUSTOM_DOMAIN_NOT_CONFIGURED\",\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n"],"mappings":"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","names":["WebSocket","http","import_http","os","dgram","http","https"]} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/client.ts","../src/protocol.ts","../src/mdns.ts","../src/types.ts"],"sourcesContent":["import WebSocket from \"ws\";\nimport http from \"http\";\nimport { encodeMessage, decodeMessage } from \"./protocol\";\nimport type {\n OutrayClientOptions,\n TunnelDataMessage,\n TunnelResponseMessage,\n WSUpgradeMessage,\n WSFrameMessage,\n WSCloseMessage,\n ErrorCodes,\n} from \"./types\";\n\nconst DEFAULT_SERVER_URL = \"wss://api.outray.dev/\";\nconst PING_INTERVAL_MS = 25000;\nconst PONG_TIMEOUT_MS = 10000;\n\n/**\n * Core Outray tunnel client.\n *\n * Establishes a WebSocket connection to the Outray server and proxies\n * HTTP requests to a local server.\n *\n * @example\n * ```ts\n * const client = new OutrayClient({\n * localPort: 3000,\n * onTunnelReady: (url) => console.log(`Tunnel: ${url}`),\n * onError: (err) => console.error(err),\n * });\n *\n * client.start();\n *\n * // Later...\n * client.stop();\n * ```\n */\nexport class OutrayClient {\n private ws: WebSocket | null = null;\n private options: Required<\n Pick<OutrayClientOptions, \"localPort\" | \"serverUrl\">\n > &\n OutrayClientOptions;\n private reconnectTimeout: NodeJS.Timeout | null = null;\n private pingInterval: NodeJS.Timeout | null = null;\n private pongTimeout: NodeJS.Timeout | null = null;\n private shouldReconnect = true;\n private assignedUrl: string | null = null;\n private subdomain?: string;\n private forceTakeover = false;\n private reconnectAttempts = 0;\n private lastPongReceived = Date.now();\n private localWebSockets = new Map<string, WebSocket>();\n\n constructor(options: OutrayClientOptions) {\n this.options = {\n ...options,\n serverUrl: options.serverUrl ?? DEFAULT_SERVER_URL,\n };\n this.subdomain = options.subdomain;\n }\n\n /**\n * Start the tunnel connection\n */\n public start(): void {\n this.shouldReconnect = true;\n this.connect();\n }\n\n /**\n * Stop the tunnel connection\n */\n public stop(): void {\n this.shouldReconnect = false;\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n\n this.stopPing();\n this.stopPongTimeout();\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n // Clean up all local WebSocket connections\n for (const [id, localWs] of this.localWebSockets) {\n localWs.close();\n }\n this.localWebSockets.clear();\n }\n\n /**\n * Get the assigned tunnel URL (if connected)\n */\n public getUrl(): string | null {\n return this.assignedUrl;\n }\n\n /**\n * Check if the client is currently connected\n */\n public isConnected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n private connect(): void {\n this.ws = new WebSocket(this.options.serverUrl);\n\n this.ws.on(\"open\", () => this.handleOpen());\n this.ws.on(\"message\", (data) => this.handleMessage(data.toString()));\n this.ws.on(\"close\", (code, reason) => this.handleClose(code, reason));\n this.ws.on(\"error\", (error) => {\n this.options.onError?.(error);\n });\n this.ws.on(\"pong\", () => {\n this.lastPongReceived = Date.now();\n this.stopPongTimeout();\n });\n }\n\n private handleOpen(): void {\n this.startPing();\n\n const handshake = encodeMessage({\n type: \"open_tunnel\",\n apiKey: this.options.apiKey,\n subdomain: this.subdomain,\n customDomain: this.options.customDomain,\n forceTakeover: this.forceTakeover,\n protocol: this.options.protocol,\n remotePort: this.options.remotePort,\n });\n this.ws?.send(handshake);\n }\n\n private handleMessage(data: string): void {\n try {\n const message = decodeMessage(data);\n\n if (message.type === \"tunnel_opened\") {\n this.assignedUrl = message.url;\n const derivedSubdomain = this.extractSubdomain(message.url);\n if (derivedSubdomain) {\n this.subdomain = derivedSubdomain;\n }\n this.forceTakeover = false;\n this.reconnectAttempts = 0;\n this.options.onTunnelReady?.(message.url, message.port);\n } else if (message.type === \"error\") {\n this.handleError(message.code, message.message);\n } else if (message.type === \"request\") {\n this.handleTunnelData(message);\n } else if (message.type === \"ws_upgrade\") {\n this.handleWSUpgrade(message as WSUpgradeMessage);\n } else if (message.type === \"ws_frame\") {\n this.handleWSFrame(message as WSFrameMessage);\n } else if (message.type === \"ws_close\") {\n this.handleWSClose(message as WSCloseMessage);\n }\n } catch (error) {\n this.options.onError?.(\n error instanceof Error ? error : new Error(String(error))\n );\n }\n }\n\n private handleError(code: string, message: string): void {\n if (code === \"SUBDOMAIN_IN_USE\" && this.assignedUrl && !this.forceTakeover) {\n // Reconnecting and subdomain is in use - try to take over\n this.forceTakeover = true;\n this.connect();\n return;\n }\n\n this.options.onError?.(new Error(message), code);\n\n // Fatal errors - stop reconnecting\n if (code === \"AUTH_FAILED\" || code === \"LIMIT_EXCEEDED\") {\n this.shouldReconnect = false;\n this.stop();\n }\n }\n\n private handleTunnelData(message: TunnelDataMessage): void {\n const startTime = Date.now();\n\n const reqOptions = {\n hostname: \"localhost\",\n port: this.options.localPort,\n path: message.path,\n method: message.method,\n headers: message.headers,\n };\n\n const req = http.request(reqOptions, (res) => {\n const chunks: Buffer[] = [];\n\n res.on(\"data\", (chunk) => {\n chunks.push(Buffer.from(chunk));\n });\n\n res.on(\"end\", () => {\n const duration = Date.now() - startTime;\n const statusCode = res.statusCode || 200;\n\n this.options.onRequest?.({\n method: message.method,\n path: message.path,\n statusCode,\n duration,\n });\n\n const bodyBuffer = Buffer.concat(chunks);\n const bodyBase64 =\n bodyBuffer.length > 0 ? bodyBuffer.toString(\"base64\") : undefined;\n\n const response: TunnelResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n statusCode,\n headers: res.headers as Record<string, string | string[]>,\n body: bodyBase64,\n };\n\n this.ws?.send(encodeMessage(response));\n });\n });\n\n req.on(\"error\", (err) => {\n const duration = Date.now() - startTime;\n\n this.options.onRequest?.({\n method: message.method,\n path: message.path,\n statusCode: 502,\n duration,\n error: err.message,\n });\n\n const errorResponse: TunnelResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n statusCode: 502,\n headers: { \"content-type\": \"text/plain\" },\n body: Buffer.from(`Bad Gateway: ${err.message}`).toString(\"base64\"),\n };\n\n this.ws?.send(encodeMessage(errorResponse));\n });\n\n if (message.body) {\n const bodyBuffer = Buffer.from(message.body, \"base64\");\n req.write(bodyBuffer);\n }\n\n req.end();\n }\n\n private handleWSUpgrade(message: WSUpgradeMessage): void {\n const wsUrl = `ws://localhost:${this.options.localPort}${message.path}`;\n\n try {\n const headers: Record<string, string> = {};\n // Forward relevant headers (like subprotocols)\n if (message.protocol) {\n headers[\"Sec-WebSocket-Protocol\"] = message.protocol;\n }\n\n const localWs = new WebSocket(wsUrl, {\n headers,\n });\n\n localWs.on(\"open\", () => {\n this.localWebSockets.set(message.wsConnectionId, localWs);\n\n // Confirm the upgrade to the server\n this.ws?.send(\n encodeMessage({\n type: \"ws_upgrade_response\",\n wsConnectionId: message.wsConnectionId,\n success: true,\n })\n );\n });\n\n // Relay frames from local WS to server\n localWs.on(\"message\", (data: WebSocket.RawData, isBinary: boolean) => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n const buffer = Buffer.isBuffer(data)\n ? data\n : Buffer.from(data as ArrayBuffer);\n this.ws.send(\n encodeMessage({\n type: \"ws_frame\",\n wsConnectionId: message.wsConnectionId,\n data: buffer.toString(\"base64\"),\n isBinary,\n })\n );\n }\n });\n\n localWs.on(\"close\", (code, reason) => {\n this.localWebSockets.delete(message.wsConnectionId);\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(\n encodeMessage({\n type: \"ws_close\",\n wsConnectionId: message.wsConnectionId,\n code,\n reason: reason?.toString(),\n })\n );\n }\n });\n\n localWs.on(\"error\", (error) => {\n this.localWebSockets.delete(message.wsConnectionId);\n\n // If not yet open, send failure response\n if (localWs.readyState === WebSocket.CONNECTING) {\n this.ws?.send(\n encodeMessage({\n type: \"ws_upgrade_response\",\n wsConnectionId: message.wsConnectionId,\n success: false,\n error: `Failed to connect to local WebSocket: ${error.message}`,\n })\n );\n }\n });\n } catch (error) {\n this.ws?.send(\n encodeMessage({\n type: \"ws_upgrade_response\",\n wsConnectionId: message.wsConnectionId,\n success: false,\n error: `Failed to create WebSocket connection: ${error instanceof Error ? error.message : String(error)}`,\n })\n );\n }\n }\n\n private handleWSFrame(message: WSFrameMessage): void {\n const localWs = this.localWebSockets.get(message.wsConnectionId);\n if (!localWs || localWs.readyState !== WebSocket.OPEN) {\n return;\n }\n\n const data = Buffer.from(message.data, \"base64\");\n localWs.send(data, { binary: message.isBinary });\n }\n\n private handleWSClose(message: WSCloseMessage): void {\n const localWs = this.localWebSockets.get(message.wsConnectionId);\n if (!localWs) {\n return;\n }\n\n this.localWebSockets.delete(message.wsConnectionId);\n localWs.close(message.code || 1000, message.reason || \"\");\n }\n\n private extractSubdomain(url: string): string | null {\n try {\n const hostname = new URL(url).hostname;\n const [subdomain] = hostname.split(\".\");\n return subdomain || null;\n } catch {\n return null;\n }\n }\n\n private startPing(): void {\n this.stopPing();\n this.lastPongReceived = Date.now();\n\n this.pingInterval = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.ping();\n\n this.stopPongTimeout();\n this.pongTimeout = setTimeout(() => {\n // No pong received - connection is likely dead\n if (this.ws) {\n this.ws.terminate();\n }\n }, PONG_TIMEOUT_MS);\n }\n }, PING_INTERVAL_MS);\n }\n\n private stopPing(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n private stopPongTimeout(): void {\n if (this.pongTimeout) {\n clearTimeout(this.pongTimeout);\n this.pongTimeout = null;\n }\n }\n\n private handleClose(code?: number, reason?: Buffer): void {\n this.stopPing();\n this.stopPongTimeout();\n\n if (!this.shouldReconnect) return;\n\n const reasonStr = reason?.toString() || \"\";\n\n if (code === 1000 && reasonStr === \"Tunnel stopped by user\") {\n this.options.onClose?.(reasonStr);\n this.stop();\n return;\n }\n\n // If we previously had a tunnel URL, force takeover on reconnect\n if (this.assignedUrl) {\n this.forceTakeover = true;\n }\n\n const delay = Math.min(2000 * Math.pow(2, this.reconnectAttempts), 30000);\n this.reconnectAttempts += 1;\n\n this.options.onReconnecting?.(this.reconnectAttempts, delay);\n\n this.reconnectTimeout = setTimeout(() => {\n this.connect();\n }, delay);\n }\n}\n","import type { ClientMessage, ServerMessage } from \"./types\";\n\n/**\n * Encode a client message to send to the server\n */\nexport function encodeMessage(message: ClientMessage): string {\n return JSON.stringify(message);\n}\n\n/**\n * Decode a server message received from the server\n */\nexport function decodeMessage(data: string): ServerMessage {\n return JSON.parse(data) as ServerMessage;\n}\n","import dgram from \"dgram\";\nimport http from \"http\";\nimport https from \"https\";\nimport os from \"os\";\n\nconst MDNS_PORT = 5353;\nconst MDNS_ADDRESS = \"224.0.0.251\";\n\ninterface MDNSAdvertisement {\n hostname: string;\n port: number;\n ip: string;\n}\n\n/**\n * Simple mDNS responder that advertises a .local hostname\n * This allows other devices on the LAN to discover and access the tunnel\n */\nexport class MDNSAdvertiser {\n private socket: dgram.Socket | null = null;\n private hostname: string;\n private port: number;\n private ip: string;\n private running = false;\n private announceInterval: NodeJS.Timeout | null = null;\n\n constructor(hostname: string, port: number) {\n this.hostname = hostname.endsWith(\".local\")\n ? hostname\n : `${hostname}.local`;\n this.port = port;\n this.ip = this.getLocalIP();\n }\n\n private getLocalIP(): string {\n const interfaces = os.networkInterfaces();\n for (const name of Object.keys(interfaces)) {\n const netInterfaces = interfaces[name];\n if (!netInterfaces) continue;\n\n for (const net of netInterfaces) {\n if (!net.internal && net.family === \"IPv4\") {\n return net.address;\n }\n }\n }\n return \"127.0.0.1\";\n }\n\n start(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.running) {\n resolve();\n return;\n }\n\n this.socket = dgram.createSocket({ type: \"udp4\", reuseAddr: true });\n\n this.socket.on(\"error\", (err) => {\n this.stop();\n });\n\n this.socket.on(\"message\", (msg, rinfo) => {\n this.handleQuery(msg, rinfo);\n });\n\n this.socket.bind(MDNS_PORT, \"0.0.0.0\", () => {\n try {\n this.socket?.addMembership(MDNS_ADDRESS, this.ip);\n this.socket?.setMulticastTTL(255);\n this.socket?.setMulticastLoopback(true);\n this.running = true;\n this.announce();\n resolve();\n } catch (err) {\n reject(err);\n }\n });\n });\n }\n\n stop(): void {\n if (this.announceInterval) {\n clearInterval(this.announceInterval);\n this.announceInterval = null;\n }\n if (this.socket) {\n try {\n this.socket.dropMembership(MDNS_ADDRESS);\n } catch {\n // Ignore\n }\n this.socket.close();\n this.socket = null;\n }\n this.running = false;\n }\n\n private handleQuery(msg: Buffer, rinfo: dgram.RemoteInfo): void {\n try {\n const query = this.parseDNSQuery(msg);\n if (query && query.toLowerCase() === this.hostname.toLowerCase()) {\n this.sendResponse();\n }\n } catch {\n // Ignore malformed queries\n }\n }\n\n private parseDNSQuery(msg: Buffer): string | null {\n if (msg.length < 12) return null;\n\n const flags = msg.readUInt16BE(2);\n if ((flags & 0x8000) !== 0) return null;\n\n const qdcount = msg.readUInt16BE(4);\n if (qdcount < 1) return null;\n\n let offset = 12;\n const labels: string[] = [];\n\n while (offset < msg.length) {\n const len = msg[offset];\n if (len === 0) break;\n offset++;\n if (offset + len > msg.length) return null;\n labels.push(msg.slice(offset, offset + len).toString(\"ascii\"));\n offset += len;\n }\n\n return labels.join(\".\");\n }\n\n private sendResponse(): void {\n const response = this.buildDNSResponse();\n if (this.socket && this.running) {\n this.socket.send(response, 0, response.length, MDNS_PORT, MDNS_ADDRESS);\n }\n }\n\n private announce(): void {\n this.sendResponse();\n setTimeout(() => this.sendResponse(), 500);\n setTimeout(() => this.sendResponse(), 1000);\n setTimeout(() => this.sendResponse(), 2000);\n\n this.announceInterval = setInterval(() => {\n if (this.running) {\n this.sendResponse();\n }\n }, 30000);\n }\n\n private buildDNSResponse(): Buffer {\n const labels = this.hostname.split(\".\");\n const nameLength = labels.reduce((sum, l) => sum + 1 + l.length, 1);\n const bufferLength = 12 + nameLength + 2 + 2 + 4 + 2 + 4;\n const buffer = Buffer.alloc(bufferLength);\n\n let offset = 0;\n\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n buffer.writeUInt16BE(0x8400, offset);\n offset += 2;\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n buffer.writeUInt16BE(1, offset);\n offset += 2;\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n\n for (const label of labels) {\n buffer.writeUInt8(label.length, offset);\n offset++;\n buffer.write(label, offset, \"ascii\");\n offset += label.length;\n }\n buffer.writeUInt8(0, offset);\n offset++;\n\n buffer.writeUInt16BE(1, offset);\n offset += 2;\n buffer.writeUInt16BE(0x8001, offset);\n offset += 2;\n buffer.writeUInt32BE(120, offset);\n offset += 4;\n buffer.writeUInt16BE(4, offset);\n offset += 2;\n\n const ipParts = this.ip.split(\".\").map(Number);\n for (const part of ipParts) {\n buffer.writeUInt8(part, offset);\n offset++;\n }\n\n return buffer;\n }\n\n getInfo(): MDNSAdvertisement {\n return {\n hostname: this.hostname,\n port: this.port,\n ip: this.ip,\n };\n }\n}\n\n/**\n * Local HTTP proxy that listens on port 80 and forwards to the target port.\n */\nexport class LocalProxy {\n private server: http.Server | null = null;\n private targetPort: number;\n private running = false;\n\n constructor(targetPort: number) {\n this.targetPort = targetPort;\n }\n\n start(): Promise<boolean> {\n return new Promise((resolve) => {\n this.server = http.createServer((req, res) => {\n const options: http.RequestOptions = {\n hostname: \"localhost\",\n port: this.targetPort,\n path: req.url,\n method: req.method,\n headers: req.headers,\n };\n\n const proxyReq = http.request(options, (proxyRes) => {\n res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on(\"error\", (err) => {\n res.writeHead(502);\n res.end(`Proxy error: ${err.message}`);\n });\n\n req.pipe(proxyReq);\n });\n\n this.server.on(\"error\", () => {\n resolve(false);\n });\n\n this.server.listen(80, \"0.0.0.0\", () => {\n this.running = true;\n resolve(true);\n });\n });\n }\n\n stop(): void {\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n this.running = false;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n}\n\n/**\n * Local HTTPS proxy that listens on port 443 and forwards to the target port.\n */\nexport class LocalHttpsProxy {\n private server: https.Server | null = null;\n private targetPort: number;\n private hostname: string;\n private running = false;\n private _isTrusted = false;\n\n constructor(targetPort: number, hostname: string) {\n this.targetPort = targetPort;\n this.hostname = hostname;\n }\n\n get isTrusted(): boolean {\n return this._isTrusted;\n }\n\n async start(): Promise<boolean> {\n return new Promise((resolve) => {\n this.generateCert()\n .then((creds) => {\n if (!creds) {\n resolve(false);\n return;\n }\n\n this._isTrusted = creds.trusted;\n\n this.server = https.createServer(\n { key: creds.key, cert: creds.cert },\n (req, res) => {\n const options: http.RequestOptions = {\n hostname: \"localhost\",\n port: this.targetPort,\n path: req.url,\n method: req.method,\n headers: req.headers,\n };\n\n const proxyReq = http.request(options, (proxyRes) => {\n res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on(\"error\", (err) => {\n res.writeHead(502);\n res.end(`Proxy error: ${err.message}`);\n });\n\n req.pipe(proxyReq);\n },\n );\n\n this.server.on(\"error\", () => {\n resolve(false);\n });\n\n this.server.listen(443, \"0.0.0.0\", () => {\n this.running = true;\n resolve(true);\n });\n })\n .catch(() => resolve(false));\n });\n }\n\n private generateCert(): Promise<{\n key: string;\n cert: string;\n trusted: boolean;\n } | null> {\n return new Promise((resolve) => {\n const { execSync } = require(\"child_process\");\n const fs = require(\"fs\");\n const os = require(\"os\");\n const path = require(\"path\");\n\n const tmpDir = os.tmpdir();\n const keyFile = path.join(tmpDir, `outray-${Date.now()}-key.pem`);\n const certFile = path.join(tmpDir, `outray-${Date.now()}.pem`);\n\n // Try mkcert first\n try {\n execSync(\"which mkcert\", { stdio: \"pipe\" });\n execSync(\n `mkcert -key-file \"${keyFile}\" -cert-file \"${certFile}\" \"${this.hostname}\"`,\n { stdio: \"pipe\" },\n );\n\n const key = fs.readFileSync(keyFile, \"utf8\");\n const cert = fs.readFileSync(certFile, \"utf8\");\n fs.unlinkSync(keyFile);\n fs.unlinkSync(certFile);\n\n resolve({ key, cert, trusted: true });\n return;\n } catch {\n // Fall back to openssl\n }\n\n try {\n execSync(\n `openssl req -x509 -newkey rsa:2048 -keyout \"${keyFile}\" -out \"${certFile}\" -days 365 -nodes -subj \"/CN=${this.hostname}\" -addext \"subjectAltName=DNS:${this.hostname}\"`,\n { stdio: \"pipe\" },\n );\n\n const key = fs.readFileSync(keyFile, \"utf8\");\n const cert = fs.readFileSync(certFile, \"utf8\");\n fs.unlinkSync(keyFile);\n fs.unlinkSync(certFile);\n\n resolve({ key, cert, trusted: false });\n } catch {\n resolve(null);\n }\n });\n }\n\n stop(): void {\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n this.running = false;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n}\n\nexport interface LocalAccessInfo {\n hostname: string;\n ip: string;\n port: number;\n httpUrl?: string;\n httpsUrl?: string;\n httpsIsTrusted?: boolean;\n}\n\n/**\n * Manages local LAN access via mDNS and optional HTTP/HTTPS proxies\n */\nexport class LocalAccessManager {\n private mdnsAdvertiser: MDNSAdvertiser | null = null;\n private localProxy: LocalProxy | null = null;\n private localHttpsProxy: LocalHttpsProxy | null = null;\n private port: number;\n private subdomain: string;\n\n constructor(port: number, subdomain: string) {\n this.port = port;\n this.subdomain = subdomain;\n }\n\n async start(): Promise<LocalAccessInfo> {\n const hostname = `${this.subdomain}.local`;\n\n // Start mDNS\n this.mdnsAdvertiser = new MDNSAdvertiser(this.subdomain, this.port);\n await this.mdnsAdvertiser.start();\n const info = this.mdnsAdvertiser.getInfo();\n\n const result: LocalAccessInfo = {\n hostname: info.hostname,\n ip: info.ip,\n port: this.port,\n };\n\n // Try HTTPS proxy (port 443)\n this.localHttpsProxy = new LocalHttpsProxy(this.port, hostname);\n const httpsStarted = await this.localHttpsProxy.start();\n if (httpsStarted) {\n result.httpsUrl = `https://${hostname}`;\n result.httpsIsTrusted = this.localHttpsProxy.isTrusted;\n }\n\n // Try HTTP proxy (port 80)\n this.localProxy = new LocalProxy(this.port);\n const httpStarted = await this.localProxy.start();\n if (httpStarted) {\n result.httpUrl = `http://${hostname}`;\n }\n\n return result;\n }\n\n stop(): void {\n if (this.localHttpsProxy) {\n this.localHttpsProxy.stop();\n this.localHttpsProxy = null;\n }\n if (this.localProxy) {\n this.localProxy.stop();\n this.localProxy = null;\n }\n if (this.mdnsAdvertiser) {\n this.mdnsAdvertiser.stop();\n this.mdnsAdvertiser = null;\n }\n }\n}\n","/**\n * Tunnel protocol types - HTTP\n */\nexport type TunnelProtocol = \"http\" | \"tcp\" | \"udp\";\n\n// ============================================================================\n// Client Configuration\n// ============================================================================\n\n/**\n * Options for creating an OutrayClient instance\n */\nexport interface OutrayClientOptions {\n /**\n * Local port to proxy requests to\n */\n localPort: number;\n\n /**\n * Outray server WebSocket URL\n * @default 'wss://api.outray.dev/'\n */\n serverUrl?: string;\n\n /**\n * API key for authentication\n */\n apiKey?: string;\n\n /**\n * Subdomain to use for the tunnel URL\n * Requires authentication\n */\n subdomain?: string;\n\n /**\n * Custom domain for the tunnel\n * Must be configured in the Outray dashboard first\n */\n customDomain?: string;\n\n /**\n * Tunnel protocol type\n * @default 'http'\n */\n protocol?: TunnelProtocol;\n\n /**\n * Remote port for TCP/UDP tunnels\n */\n remotePort?: number;\n\n /**\n * Callback fired when tunnel is successfully established\n */\n onTunnelReady?: (url: string, port?: number) => void;\n\n /**\n * Callback fired when tunnel encounters an error\n */\n onError?: (error: Error, code?: string) => void;\n\n /**\n * Callback fired when tunnel is closed\n */\n onClose?: (reason?: string) => void;\n\n /**\n * Callback fired when attempting to reconnect\n */\n onReconnecting?: (attempt: number, delay: number) => void;\n\n /**\n * Callback fired for each proxied request (HTTP only)\n */\n onRequest?: (info: RequestInfo) => void;\n}\n\n/**\n * Information about a proxied request\n */\nexport interface RequestInfo {\n method: string;\n path: string;\n statusCode: number;\n duration: number;\n error?: string;\n}\n\n// ============================================================================\n// Protocol Messages - Client to Server\n// ============================================================================\n\nexport interface OpenTunnelMessage {\n type: \"open_tunnel\";\n subdomain?: string | null;\n customDomain?: string | null;\n apiKey?: string;\n forceTakeover?: boolean;\n protocol?: TunnelProtocol;\n remotePort?: number;\n}\n\nexport interface TunnelResponseMessage {\n type: \"response\";\n requestId: string;\n statusCode: number;\n headers: Record<string, string | string[]>;\n body?: string;\n}\n\nexport interface TCPDataMessage {\n type: \"tcp_data\";\n connectionId: string;\n data: string; // base64 encoded\n}\n\nexport interface TCPCloseMessage {\n type: \"tcp_close\";\n connectionId: string;\n}\n\nexport interface UDPResponseMessage {\n type: \"udp_response\";\n packetId: string;\n targetAddress: string;\n targetPort: number;\n data: string; // base64 encoded\n}\n\n// WebSocket passthrough messages (client → server)\nexport interface WSUpgradeResponseMessage {\n type: \"ws_upgrade_response\";\n wsConnectionId: string;\n success: boolean;\n error?: string;\n}\n\nexport interface WSFrameMessage {\n type: \"ws_frame\";\n wsConnectionId: string;\n data: string; // base64 encoded\n isBinary: boolean;\n}\n\nexport interface WSCloseMessage {\n type: \"ws_close\";\n wsConnectionId: string;\n code?: number;\n reason?: string;\n}\n\nexport type ClientMessage =\n | OpenTunnelMessage\n | TunnelResponseMessage\n | TCPDataMessage\n | TCPCloseMessage\n | UDPResponseMessage\n | WSUpgradeResponseMessage\n | WSFrameMessage\n | WSCloseMessage;\n\n// ============================================================================\n// Protocol Messages - Server to Client\n// ============================================================================\n\nexport interface TunnelOpenedMessage {\n type: \"tunnel_opened\";\n url: string;\n protocol?: TunnelProtocol;\n port?: number;\n}\n\nexport interface TunnelDataMessage {\n type: \"request\";\n requestId: string;\n method: string;\n path: string;\n headers: Record<string, string | string[]>;\n body?: string;\n}\n\nexport interface TCPConnectionMessage {\n type: \"tcp_connection\";\n connectionId: string;\n}\n\nexport interface TCPIncomingDataMessage {\n type: \"tcp_data\";\n connectionId: string;\n data: string; // base64 encoded\n}\n\nexport interface TCPIncomingCloseMessage {\n type: \"tcp_close\";\n connectionId: string;\n}\n\nexport interface UDPDataMessage {\n type: \"udp_data\";\n packetId: string;\n sourceAddress: string;\n sourcePort: number;\n data: string; // base64 encoded\n}\n\nexport interface ErrorMessage {\n type: \"error\";\n code: string;\n message: string;\n}\n\n// WebSocket passthrough messages (server → client)\nexport interface WSUpgradeMessage {\n type: \"ws_upgrade\";\n wsConnectionId: string;\n path: string;\n headers: Record<string, string | string[]>;\n protocol?: string;\n}\n\nexport type ServerMessage =\n | TunnelOpenedMessage\n | TunnelDataMessage\n | TCPConnectionMessage\n | TCPIncomingDataMessage\n | TCPIncomingCloseMessage\n | UDPDataMessage\n | ErrorMessage\n | WSUpgradeMessage\n | WSFrameMessage\n | WSCloseMessage;\n\n// ============================================================================\n// Error Codes\n// ============================================================================\n\nexport const ErrorCodes = {\n SUBDOMAIN_IN_USE: \"SUBDOMAIN_IN_USE\",\n AUTH_FAILED: \"AUTH_FAILED\",\n LIMIT_EXCEEDED: \"LIMIT_EXCEEDED\",\n INVALID_SUBDOMAIN: \"INVALID_SUBDOMAIN\",\n CUSTOM_DOMAIN_NOT_CONFIGURED: \"CUSTOM_DOMAIN_NOT_CONFIGURED\",\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n"],"mappings":";;;;;;;;AAAA,OAAO,eAAe;AACtB,OAAO,UAAU;;;ACIV,SAAS,cAAc,SAAgC;AAC5D,SAAO,KAAK,UAAU,OAAO;AAC/B;AAKO,SAAS,cAAc,MAA6B;AACzD,SAAO,KAAK,MAAM,IAAI;AACxB;;;ADDA,IAAM,qBAAqB;AAC3B,IAAM,mBAAmB;AACzB,IAAM,kBAAkB;AAsBjB,IAAM,eAAN,MAAmB;AAAA,EAiBxB,YAAY,SAA8B;AAhB1C,SAAQ,KAAuB;AAK/B,SAAQ,mBAA0C;AAClD,SAAQ,eAAsC;AAC9C,SAAQ,cAAqC;AAC7C,SAAQ,kBAAkB;AAC1B,SAAQ,cAA6B;AAErC,SAAQ,gBAAgB;AACxB,SAAQ,oBAAoB;AAC5B,SAAQ,mBAAmB,KAAK,IAAI;AACpC,SAAQ,kBAAkB,oBAAI,IAAuB;AAGnD,SAAK,UAAU;AAAA,MACb,GAAG;AAAA,MACH,WAAW,QAAQ,aAAa;AAAA,IAClC;AACA,SAAK,YAAY,QAAQ;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKO,QAAc;AACnB,SAAK,kBAAkB;AACvB,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKO,OAAa;AAClB,SAAK,kBAAkB;AAEvB,QAAI,KAAK,kBAAkB;AACzB,mBAAa,KAAK,gBAAgB;AAClC,WAAK,mBAAmB;AAAA,IAC1B;AAEA,SAAK,SAAS;AACd,SAAK,gBAAgB;AAErB,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,MAAM;AACd,WAAK,KAAK;AAAA,IACZ;AAGA,eAAW,CAAC,IAAI,OAAO,KAAK,KAAK,iBAAiB;AAChD,cAAQ,MAAM;AAAA,IAChB;AACA,SAAK,gBAAgB,MAAM;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKO,SAAwB;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKO,cAAuB;AAC5B,WAAO,KAAK,IAAI,eAAe,UAAU;AAAA,EAC3C;AAAA,EAEQ,UAAgB;AACtB,SAAK,KAAK,IAAI,UAAU,KAAK,QAAQ,SAAS;AAE9C,SAAK,GAAG,GAAG,QAAQ,MAAM,KAAK,WAAW,CAAC;AAC1C,SAAK,GAAG,GAAG,WAAW,CAAC,SAAS,KAAK,cAAc,KAAK,SAAS,CAAC,CAAC;AACnE,SAAK,GAAG,GAAG,SAAS,CAAC,MAAM,WAAW,KAAK,YAAY,MAAM,MAAM,CAAC;AACpE,SAAK,GAAG,GAAG,SAAS,CAAC,UAAU;AAC7B,WAAK,QAAQ,UAAU,KAAK;AAAA,IAC9B,CAAC;AACD,SAAK,GAAG,GAAG,QAAQ,MAAM;AACvB,WAAK,mBAAmB,KAAK,IAAI;AACjC,WAAK,gBAAgB;AAAA,IACvB,CAAC;AAAA,EACH;AAAA,EAEQ,aAAmB;AACzB,SAAK,UAAU;AAEf,UAAM,YAAY,cAAc;AAAA,MAC9B,MAAM;AAAA,MACN,QAAQ,KAAK,QAAQ;AAAA,MACrB,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK,QAAQ;AAAA,MAC3B,eAAe,KAAK;AAAA,MACpB,UAAU,KAAK,QAAQ;AAAA,MACvB,YAAY,KAAK,QAAQ;AAAA,IAC3B,CAAC;AACD,SAAK,IAAI,KAAK,SAAS;AAAA,EACzB;AAAA,EAEQ,cAAc,MAAoB;AACxC,QAAI;AACF,YAAM,UAAU,cAAc,IAAI;AAElC,UAAI,QAAQ,SAAS,iBAAiB;AACpC,aAAK,cAAc,QAAQ;AAC3B,cAAM,mBAAmB,KAAK,iBAAiB,QAAQ,GAAG;AAC1D,YAAI,kBAAkB;AACpB,eAAK,YAAY;AAAA,QACnB;AACA,aAAK,gBAAgB;AACrB,aAAK,oBAAoB;AACzB,aAAK,QAAQ,gBAAgB,QAAQ,KAAK,QAAQ,IAAI;AAAA,MACxD,WAAW,QAAQ,SAAS,SAAS;AACnC,aAAK,YAAY,QAAQ,MAAM,QAAQ,OAAO;AAAA,MAChD,WAAW,QAAQ,SAAS,WAAW;AACrC,aAAK,iBAAiB,OAAO;AAAA,MAC/B,WAAW,QAAQ,SAAS,cAAc;AACxC,aAAK,gBAAgB,OAA2B;AAAA,MAClD,WAAW,QAAQ,SAAS,YAAY;AACtC,aAAK,cAAc,OAAyB;AAAA,MAC9C,WAAW,QAAQ,SAAS,YAAY;AACtC,aAAK,cAAc,OAAyB;AAAA,MAC9C;AAAA,IACF,SAAS,OAAO;AACd,WAAK,QAAQ;AAAA,QACX,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,YAAY,MAAc,SAAuB;AACvD,QAAI,SAAS,sBAAsB,KAAK,eAAe,CAAC,KAAK,eAAe;AAE1E,WAAK,gBAAgB;AACrB,WAAK,QAAQ;AACb;AAAA,IACF;AAEA,SAAK,QAAQ,UAAU,IAAI,MAAM,OAAO,GAAG,IAAI;AAG/C,QAAI,SAAS,iBAAiB,SAAS,kBAAkB;AACvD,WAAK,kBAAkB;AACvB,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AAAA,EAEQ,iBAAiB,SAAkC;AACzD,UAAM,YAAY,KAAK,IAAI;AAE3B,UAAM,aAAa;AAAA,MACjB,UAAU;AAAA,MACV,MAAM,KAAK,QAAQ;AAAA,MACnB,MAAM,QAAQ;AAAA,MACd,QAAQ,QAAQ;AAAA,MAChB,SAAS,QAAQ;AAAA,IACnB;AAEA,UAAM,MAAM,KAAK,QAAQ,YAAY,CAAC,QAAQ;AAC5C,YAAM,SAAmB,CAAC;AAE1B,UAAI,GAAG,QAAQ,CAAC,UAAU;AACxB,eAAO,KAAK,OAAO,KAAK,KAAK,CAAC;AAAA,MAChC,CAAC;AAED,UAAI,GAAG,OAAO,MAAM;AAClB,cAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,cAAM,aAAa,IAAI,cAAc;AAErC,aAAK,QAAQ,YAAY;AAAA,UACvB,QAAQ,QAAQ;AAAA,UAChB,MAAM,QAAQ;AAAA,UACd;AAAA,UACA;AAAA,QACF,CAAC;AAED,cAAM,aAAa,OAAO,OAAO,MAAM;AACvC,cAAM,aACJ,WAAW,SAAS,IAAI,WAAW,SAAS,QAAQ,IAAI;AAE1D,cAAM,WAAkC;AAAA,UACtC,MAAM;AAAA,UACN,WAAW,QAAQ;AAAA,UACnB;AAAA,UACA,SAAS,IAAI;AAAA,UACb,MAAM;AAAA,QACR;AAEA,aAAK,IAAI,KAAK,cAAc,QAAQ,CAAC;AAAA,MACvC,CAAC;AAAA,IACH,CAAC;AAED,QAAI,GAAG,SAAS,CAAC,QAAQ;AACvB,YAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,WAAK,QAAQ,YAAY;AAAA,QACvB,QAAQ,QAAQ;AAAA,QAChB,MAAM,QAAQ;AAAA,QACd,YAAY;AAAA,QACZ;AAAA,QACA,OAAO,IAAI;AAAA,MACb,CAAC;AAED,YAAM,gBAAuC;AAAA,QAC3C,MAAM;AAAA,QACN,WAAW,QAAQ;AAAA,QACnB,YAAY;AAAA,QACZ,SAAS,EAAE,gBAAgB,aAAa;AAAA,QACxC,MAAM,OAAO,KAAK,gBAAgB,IAAI,OAAO,EAAE,EAAE,SAAS,QAAQ;AAAA,MACpE;AAEA,WAAK,IAAI,KAAK,cAAc,aAAa,CAAC;AAAA,IAC5C,CAAC;AAED,QAAI,QAAQ,MAAM;AAChB,YAAM,aAAa,OAAO,KAAK,QAAQ,MAAM,QAAQ;AACrD,UAAI,MAAM,UAAU;AAAA,IACtB;AAEA,QAAI,IAAI;AAAA,EACV;AAAA,EAEQ,gBAAgB,SAAiC;AACvD,UAAM,QAAQ,kBAAkB,KAAK,QAAQ,SAAS,GAAG,QAAQ,IAAI;AAErE,QAAI;AACF,YAAM,UAAkC,CAAC;AAEzC,UAAI,QAAQ,UAAU;AACpB,gBAAQ,wBAAwB,IAAI,QAAQ;AAAA,MAC9C;AAEA,YAAM,UAAU,IAAI,UAAU,OAAO;AAAA,QACnC;AAAA,MACF,CAAC;AAED,cAAQ,GAAG,QAAQ,MAAM;AACvB,aAAK,gBAAgB,IAAI,QAAQ,gBAAgB,OAAO;AAGxD,aAAK,IAAI;AAAA,UACP,cAAc;AAAA,YACZ,MAAM;AAAA,YACN,gBAAgB,QAAQ;AAAA,YACxB,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAGD,cAAQ,GAAG,WAAW,CAAC,MAAyB,aAAsB;AACpE,YAAI,KAAK,IAAI,eAAe,UAAU,MAAM;AAC1C,gBAAM,SAAS,OAAO,SAAS,IAAI,IAC/B,OACA,OAAO,KAAK,IAAmB;AACnC,eAAK,GAAG;AAAA,YACN,cAAc;AAAA,cACZ,MAAM;AAAA,cACN,gBAAgB,QAAQ;AAAA,cACxB,MAAM,OAAO,SAAS,QAAQ;AAAA,cAC9B;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF,CAAC;AAED,cAAQ,GAAG,SAAS,CAAC,MAAM,WAAW;AACpC,aAAK,gBAAgB,OAAO,QAAQ,cAAc;AAClD,YAAI,KAAK,IAAI,eAAe,UAAU,MAAM;AAC1C,eAAK,GAAG;AAAA,YACN,cAAc;AAAA,cACZ,MAAM;AAAA,cACN,gBAAgB,QAAQ;AAAA,cACxB;AAAA,cACA,QAAQ,QAAQ,SAAS;AAAA,YAC3B,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF,CAAC;AAED,cAAQ,GAAG,SAAS,CAAC,UAAU;AAC7B,aAAK,gBAAgB,OAAO,QAAQ,cAAc;AAGlD,YAAI,QAAQ,eAAe,UAAU,YAAY;AAC/C,eAAK,IAAI;AAAA,YACP,cAAc;AAAA,cACZ,MAAM;AAAA,cACN,gBAAgB,QAAQ;AAAA,cACxB,SAAS;AAAA,cACT,OAAO,yCAAyC,MAAM,OAAO;AAAA,YAC/D,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,SAAS,OAAO;AACd,WAAK,IAAI;AAAA,QACP,cAAc;AAAA,UACZ,MAAM;AAAA,UACN,gBAAgB,QAAQ;AAAA,UACxB,SAAS;AAAA,UACT,OAAO,0CAA0C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,QACzG,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,cAAc,SAA+B;AACnD,UAAM,UAAU,KAAK,gBAAgB,IAAI,QAAQ,cAAc;AAC/D,QAAI,CAAC,WAAW,QAAQ,eAAe,UAAU,MAAM;AACrD;AAAA,IACF;AAEA,UAAM,OAAO,OAAO,KAAK,QAAQ,MAAM,QAAQ;AAC/C,YAAQ,KAAK,MAAM,EAAE,QAAQ,QAAQ,SAAS,CAAC;AAAA,EACjD;AAAA,EAEQ,cAAc,SAA+B;AACnD,UAAM,UAAU,KAAK,gBAAgB,IAAI,QAAQ,cAAc;AAC/D,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AAEA,SAAK,gBAAgB,OAAO,QAAQ,cAAc;AAClD,YAAQ,MAAM,QAAQ,QAAQ,KAAM,QAAQ,UAAU,EAAE;AAAA,EAC1D;AAAA,EAEQ,iBAAiB,KAA4B;AACnD,QAAI;AACF,YAAM,WAAW,IAAI,IAAI,GAAG,EAAE;AAC9B,YAAM,CAAC,SAAS,IAAI,SAAS,MAAM,GAAG;AACtC,aAAO,aAAa;AAAA,IACtB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,YAAkB;AACxB,SAAK,SAAS;AACd,SAAK,mBAAmB,KAAK,IAAI;AAEjC,SAAK,eAAe,YAAY,MAAM;AACpC,UAAI,KAAK,IAAI,eAAe,UAAU,MAAM;AAC1C,aAAK,GAAG,KAAK;AAEb,aAAK,gBAAgB;AACrB,aAAK,cAAc,WAAW,MAAM;AAElC,cAAI,KAAK,IAAI;AACX,iBAAK,GAAG,UAAU;AAAA,UACpB;AAAA,QACF,GAAG,eAAe;AAAA,MACpB;AAAA,IACF,GAAG,gBAAgB;AAAA,EACrB;AAAA,EAEQ,WAAiB;AACvB,QAAI,KAAK,cAAc;AACrB,oBAAc,KAAK,YAAY;AAC/B,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA,EAEQ,kBAAwB;AAC9B,QAAI,KAAK,aAAa;AACpB,mBAAa,KAAK,WAAW;AAC7B,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA,EAEQ,YAAY,MAAe,QAAuB;AACxD,SAAK,SAAS;AACd,SAAK,gBAAgB;AAErB,QAAI,CAAC,KAAK,gBAAiB;AAE3B,UAAM,YAAY,QAAQ,SAAS,KAAK;AAExC,QAAI,SAAS,OAAQ,cAAc,0BAA0B;AAC3D,WAAK,QAAQ,UAAU,SAAS;AAChC,WAAK,KAAK;AACV;AAAA,IACF;AAGA,QAAI,KAAK,aAAa;AACpB,WAAK,gBAAgB;AAAA,IACvB;AAEA,UAAM,QAAQ,KAAK,IAAI,MAAO,KAAK,IAAI,GAAG,KAAK,iBAAiB,GAAG,GAAK;AACxE,SAAK,qBAAqB;AAE1B,SAAK,QAAQ,iBAAiB,KAAK,mBAAmB,KAAK;AAE3D,SAAK,mBAAmB,WAAW,MAAM;AACvC,WAAK,QAAQ;AAAA,IACf,GAAG,KAAK;AAAA,EACV;AACF;;;AEvbA,OAAO,WAAW;AAClB,OAAOA,WAAU;AACjB,OAAO,WAAW;AAClB,OAAO,QAAQ;AAEf,IAAM,YAAY;AAClB,IAAM,eAAe;AAYd,IAAM,iBAAN,MAAqB;AAAA,EAQ1B,YAAY,UAAkB,MAAc;AAP5C,SAAQ,SAA8B;AAItC,SAAQ,UAAU;AAClB,SAAQ,mBAA0C;AAGhD,SAAK,WAAW,SAAS,SAAS,QAAQ,IACtC,WACA,GAAG,QAAQ;AACf,SAAK,OAAO;AACZ,SAAK,KAAK,KAAK,WAAW;AAAA,EAC5B;AAAA,EAEQ,aAAqB;AAC3B,UAAM,aAAa,GAAG,kBAAkB;AACxC,eAAW,QAAQ,OAAO,KAAK,UAAU,GAAG;AAC1C,YAAM,gBAAgB,WAAW,IAAI;AACrC,UAAI,CAAC,cAAe;AAEpB,iBAAW,OAAO,eAAe;AAC/B,YAAI,CAAC,IAAI,YAAY,IAAI,WAAW,QAAQ;AAC1C,iBAAO,IAAI;AAAA,QACb;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,QAAuB;AACrB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,KAAK,SAAS;AAChB,gBAAQ;AACR;AAAA,MACF;AAEA,WAAK,SAAS,MAAM,aAAa,EAAE,MAAM,QAAQ,WAAW,KAAK,CAAC;AAElE,WAAK,OAAO,GAAG,SAAS,CAAC,QAAQ;AAC/B,aAAK,KAAK;AAAA,MACZ,CAAC;AAED,WAAK,OAAO,GAAG,WAAW,CAAC,KAAK,UAAU;AACxC,aAAK,YAAY,KAAK,KAAK;AAAA,MAC7B,CAAC;AAED,WAAK,OAAO,KAAK,WAAW,WAAW,MAAM;AAC3C,YAAI;AACF,eAAK,QAAQ,cAAc,cAAc,KAAK,EAAE;AAChD,eAAK,QAAQ,gBAAgB,GAAG;AAChC,eAAK,QAAQ,qBAAqB,IAAI;AACtC,eAAK,UAAU;AACf,eAAK,SAAS;AACd,kBAAQ;AAAA,QACV,SAAS,KAAK;AACZ,iBAAO,GAAG;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,OAAa;AACX,QAAI,KAAK,kBAAkB;AACzB,oBAAc,KAAK,gBAAgB;AACnC,WAAK,mBAAmB;AAAA,IAC1B;AACA,QAAI,KAAK,QAAQ;AACf,UAAI;AACF,aAAK,OAAO,eAAe,YAAY;AAAA,MACzC,QAAQ;AAAA,MAER;AACA,WAAK,OAAO,MAAM;AAClB,WAAK,SAAS;AAAA,IAChB;AACA,SAAK,UAAU;AAAA,EACjB;AAAA,EAEQ,YAAY,KAAa,OAA+B;AAC9D,QAAI;AACF,YAAM,QAAQ,KAAK,cAAc,GAAG;AACpC,UAAI,SAAS,MAAM,YAAY,MAAM,KAAK,SAAS,YAAY,GAAG;AAChE,aAAK,aAAa;AAAA,MACpB;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEQ,cAAc,KAA4B;AAChD,QAAI,IAAI,SAAS,GAAI,QAAO;AAE5B,UAAM,QAAQ,IAAI,aAAa,CAAC;AAChC,SAAK,QAAQ,WAAY,EAAG,QAAO;AAEnC,UAAM,UAAU,IAAI,aAAa,CAAC;AAClC,QAAI,UAAU,EAAG,QAAO;AAExB,QAAI,SAAS;AACb,UAAM,SAAmB,CAAC;AAE1B,WAAO,SAAS,IAAI,QAAQ;AAC1B,YAAM,MAAM,IAAI,MAAM;AACtB,UAAI,QAAQ,EAAG;AACf;AACA,UAAI,SAAS,MAAM,IAAI,OAAQ,QAAO;AACtC,aAAO,KAAK,IAAI,MAAM,QAAQ,SAAS,GAAG,EAAE,SAAS,OAAO,CAAC;AAC7D,gBAAU;AAAA,IACZ;AAEA,WAAO,OAAO,KAAK,GAAG;AAAA,EACxB;AAAA,EAEQ,eAAqB;AAC3B,UAAM,WAAW,KAAK,iBAAiB;AACvC,QAAI,KAAK,UAAU,KAAK,SAAS;AAC/B,WAAK,OAAO,KAAK,UAAU,GAAG,SAAS,QAAQ,WAAW,YAAY;AAAA,IACxE;AAAA,EACF;AAAA,EAEQ,WAAiB;AACvB,SAAK,aAAa;AAClB,eAAW,MAAM,KAAK,aAAa,GAAG,GAAG;AACzC,eAAW,MAAM,KAAK,aAAa,GAAG,GAAI;AAC1C,eAAW,MAAM,KAAK,aAAa,GAAG,GAAI;AAE1C,SAAK,mBAAmB,YAAY,MAAM;AACxC,UAAI,KAAK,SAAS;AAChB,aAAK,aAAa;AAAA,MACpB;AAAA,IACF,GAAG,GAAK;AAAA,EACV;AAAA,EAEQ,mBAA2B;AACjC,UAAM,SAAS,KAAK,SAAS,MAAM,GAAG;AACtC,UAAM,aAAa,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,IAAI,EAAE,QAAQ,CAAC;AAClE,UAAM,eAAe,KAAK,aAAa,IAAI,IAAI,IAAI,IAAI;AACvD,UAAM,SAAS,OAAO,MAAM,YAAY;AAExC,QAAI,SAAS;AAEb,WAAO,cAAc,GAAG,MAAM;AAC9B,cAAU;AACV,WAAO,cAAc,OAAQ,MAAM;AACnC,cAAU;AACV,WAAO,cAAc,GAAG,MAAM;AAC9B,cAAU;AACV,WAAO,cAAc,GAAG,MAAM;AAC9B,cAAU;AACV,WAAO,cAAc,GAAG,MAAM;AAC9B,cAAU;AACV,WAAO,cAAc,GAAG,MAAM;AAC9B,cAAU;AAEV,eAAW,SAAS,QAAQ;AAC1B,aAAO,WAAW,MAAM,QAAQ,MAAM;AACtC;AACA,aAAO,MAAM,OAAO,QAAQ,OAAO;AACnC,gBAAU,MAAM;AAAA,IAClB;AACA,WAAO,WAAW,GAAG,MAAM;AAC3B;AAEA,WAAO,cAAc,GAAG,MAAM;AAC9B,cAAU;AACV,WAAO,cAAc,OAAQ,MAAM;AACnC,cAAU;AACV,WAAO,cAAc,KAAK,MAAM;AAChC,cAAU;AACV,WAAO,cAAc,GAAG,MAAM;AAC9B,cAAU;AAEV,UAAM,UAAU,KAAK,GAAG,MAAM,GAAG,EAAE,IAAI,MAAM;AAC7C,eAAW,QAAQ,SAAS;AAC1B,aAAO,WAAW,MAAM,MAAM;AAC9B;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,UAA6B;AAC3B,WAAO;AAAA,MACL,UAAU,KAAK;AAAA,MACf,MAAM,KAAK;AAAA,MACX,IAAI,KAAK;AAAA,IACX;AAAA,EACF;AACF;AAKO,IAAM,aAAN,MAAiB;AAAA,EAKtB,YAAY,YAAoB;AAJhC,SAAQ,SAA6B;AAErC,SAAQ,UAAU;AAGhB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,QAA0B;AACxB,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,WAAK,SAASA,MAAK,aAAa,CAAC,KAAK,QAAQ;AAC5C,cAAM,UAA+B;AAAA,UACnC,UAAU;AAAA,UACV,MAAM,KAAK;AAAA,UACX,MAAM,IAAI;AAAA,UACV,QAAQ,IAAI;AAAA,UACZ,SAAS,IAAI;AAAA,QACf;AAEA,cAAM,WAAWA,MAAK,QAAQ,SAAS,CAAC,aAAa;AACnD,cAAI,UAAU,SAAS,cAAc,KAAK,SAAS,OAAO;AAC1D,mBAAS,KAAK,GAAG;AAAA,QACnB,CAAC;AAED,iBAAS,GAAG,SAAS,CAAC,QAAQ;AAC5B,cAAI,UAAU,GAAG;AACjB,cAAI,IAAI,gBAAgB,IAAI,OAAO,EAAE;AAAA,QACvC,CAAC;AAED,YAAI,KAAK,QAAQ;AAAA,MACnB,CAAC;AAED,WAAK,OAAO,GAAG,SAAS,MAAM;AAC5B,gBAAQ,KAAK;AAAA,MACf,CAAC;AAED,WAAK,OAAO,OAAO,IAAI,WAAW,MAAM;AACtC,aAAK,UAAU;AACf,gBAAQ,IAAI;AAAA,MACd,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,OAAa;AACX,QAAI,KAAK,QAAQ;AACf,WAAK,OAAO,MAAM;AAClB,WAAK,SAAS;AAAA,IAChB;AACA,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,YAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AACF;AAKO,IAAM,kBAAN,MAAsB;AAAA,EAO3B,YAAY,YAAoB,UAAkB;AANlD,SAAQ,SAA8B;AAGtC,SAAQ,UAAU;AAClB,SAAQ,aAAa;AAGnB,SAAK,aAAa;AAClB,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,IAAI,YAAqB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,QAA0B;AAC9B,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,WAAK,aAAa,EACf,KAAK,CAAC,UAAU;AACf,YAAI,CAAC,OAAO;AACV,kBAAQ,KAAK;AACb;AAAA,QACF;AAEA,aAAK,aAAa,MAAM;AAExB,aAAK,SAAS,MAAM;AAAA,UAClB,EAAE,KAAK,MAAM,KAAK,MAAM,MAAM,KAAK;AAAA,UACnC,CAAC,KAAK,QAAQ;AACZ,kBAAM,UAA+B;AAAA,cACnC,UAAU;AAAA,cACV,MAAM,KAAK;AAAA,cACX,MAAM,IAAI;AAAA,cACV,QAAQ,IAAI;AAAA,cACZ,SAAS,IAAI;AAAA,YACf;AAEA,kBAAM,WAAWA,MAAK,QAAQ,SAAS,CAAC,aAAa;AACnD,kBAAI,UAAU,SAAS,cAAc,KAAK,SAAS,OAAO;AAC1D,uBAAS,KAAK,GAAG;AAAA,YACnB,CAAC;AAED,qBAAS,GAAG,SAAS,CAAC,QAAQ;AAC5B,kBAAI,UAAU,GAAG;AACjB,kBAAI,IAAI,gBAAgB,IAAI,OAAO,EAAE;AAAA,YACvC,CAAC;AAED,gBAAI,KAAK,QAAQ;AAAA,UACnB;AAAA,QACF;AAEA,aAAK,OAAO,GAAG,SAAS,MAAM;AAC5B,kBAAQ,KAAK;AAAA,QACf,CAAC;AAED,aAAK,OAAO,OAAO,KAAK,WAAW,MAAM;AACvC,eAAK,UAAU;AACf,kBAAQ,IAAI;AAAA,QACd,CAAC;AAAA,MACH,CAAC,EACA,MAAM,MAAM,QAAQ,KAAK,CAAC;AAAA,IAC/B,CAAC;AAAA,EACH;AAAA,EAEQ,eAIE;AACR,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,YAAM,EAAE,SAAS,IAAI,UAAQ,eAAe;AAC5C,YAAM,KAAK,UAAQ,IAAI;AACvB,YAAMC,MAAK,UAAQ,IAAI;AACvB,YAAM,OAAO,UAAQ,MAAM;AAE3B,YAAM,SAASA,IAAG,OAAO;AACzB,YAAM,UAAU,KAAK,KAAK,QAAQ,UAAU,KAAK,IAAI,CAAC,UAAU;AAChE,YAAM,WAAW,KAAK,KAAK,QAAQ,UAAU,KAAK,IAAI,CAAC,MAAM;AAG7D,UAAI;AACF,iBAAS,gBAAgB,EAAE,OAAO,OAAO,CAAC;AAC1C;AAAA,UACE,qBAAqB,OAAO,iBAAiB,QAAQ,MAAM,KAAK,QAAQ;AAAA,UACxE,EAAE,OAAO,OAAO;AAAA,QAClB;AAEA,cAAM,MAAM,GAAG,aAAa,SAAS,MAAM;AAC3C,cAAM,OAAO,GAAG,aAAa,UAAU,MAAM;AAC7C,WAAG,WAAW,OAAO;AACrB,WAAG,WAAW,QAAQ;AAEtB,gBAAQ,EAAE,KAAK,MAAM,SAAS,KAAK,CAAC;AACpC;AAAA,MACF,QAAQ;AAAA,MAER;AAEA,UAAI;AACF;AAAA,UACE,+CAA+C,OAAO,WAAW,QAAQ,iCAAiC,KAAK,QAAQ,iCAAiC,KAAK,QAAQ;AAAA,UACrK,EAAE,OAAO,OAAO;AAAA,QAClB;AAEA,cAAM,MAAM,GAAG,aAAa,SAAS,MAAM;AAC3C,cAAM,OAAO,GAAG,aAAa,UAAU,MAAM;AAC7C,WAAG,WAAW,OAAO;AACrB,WAAG,WAAW,QAAQ;AAEtB,gBAAQ,EAAE,KAAK,MAAM,SAAS,MAAM,CAAC;AAAA,MACvC,QAAQ;AACN,gBAAQ,IAAI;AAAA,MACd;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,OAAa;AACX,QAAI,KAAK,QAAQ;AACf,WAAK,OAAO,MAAM;AAClB,WAAK,SAAS;AAAA,IAChB;AACA,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,YAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AACF;AAcO,IAAM,qBAAN,MAAyB;AAAA,EAO9B,YAAY,MAAc,WAAmB;AAN7C,SAAQ,iBAAwC;AAChD,SAAQ,aAAgC;AACxC,SAAQ,kBAA0C;AAKhD,SAAK,OAAO;AACZ,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,QAAkC;AACtC,UAAM,WAAW,GAAG,KAAK,SAAS;AAGlC,SAAK,iBAAiB,IAAI,eAAe,KAAK,WAAW,KAAK,IAAI;AAClE,UAAM,KAAK,eAAe,MAAM;AAChC,UAAM,OAAO,KAAK,eAAe,QAAQ;AAEzC,UAAM,SAA0B;AAAA,MAC9B,UAAU,KAAK;AAAA,MACf,IAAI,KAAK;AAAA,MACT,MAAM,KAAK;AAAA,IACb;AAGA,SAAK,kBAAkB,IAAI,gBAAgB,KAAK,MAAM,QAAQ;AAC9D,UAAM,eAAe,MAAM,KAAK,gBAAgB,MAAM;AACtD,QAAI,cAAc;AAChB,aAAO,WAAW,WAAW,QAAQ;AACrC,aAAO,iBAAiB,KAAK,gBAAgB;AAAA,IAC/C;AAGA,SAAK,aAAa,IAAI,WAAW,KAAK,IAAI;AAC1C,UAAM,cAAc,MAAM,KAAK,WAAW,MAAM;AAChD,QAAI,aAAa;AACf,aAAO,UAAU,UAAU,QAAQ;AAAA,IACrC;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,OAAa;AACX,QAAI,KAAK,iBAAiB;AACxB,WAAK,gBAAgB,KAAK;AAC1B,WAAK,kBAAkB;AAAA,IACzB;AACA,QAAI,KAAK,YAAY;AACnB,WAAK,WAAW,KAAK;AACrB,WAAK,aAAa;AAAA,IACpB;AACA,QAAI,KAAK,gBAAgB;AACvB,WAAK,eAAe,KAAK;AACzB,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AACF;;;AC5OO,IAAM,aAAa;AAAA,EACxB,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,8BAA8B;AAChC;","names":["http","os"]} | ||
| {"version":3,"sources":["../src/client.ts","../src/protocol.ts","../src/mdns.ts","../src/types.ts"],"sourcesContent":["import WebSocket from \"ws\";\nimport http from \"http\";\nimport { encodeMessage, decodeMessage } from \"./protocol\";\nimport type {\n OutrayClientOptions,\n TunnelDataMessage,\n TunnelResponseMessage,\n WSUpgradeMessage,\n WSFrameMessage,\n WSCloseMessage,\n ErrorCodes,\n} from \"./types\";\n\nconst DEFAULT_SERVER_URL = \"wss://api.outray.dev/\";\nconst PING_INTERVAL_MS = 25000;\nconst PONG_TIMEOUT_MS = 10000;\n\n/**\n * Core Outray tunnel client.\n *\n * Establishes a WebSocket connection to the Outray server and proxies\n * HTTP requests to a local server.\n *\n * @example\n * ```ts\n * const client = new OutrayClient({\n * localPort: 3000,\n * onTunnelReady: (url) => console.log(`Tunnel: ${url}`),\n * onError: (err) => console.error(err),\n * });\n *\n * client.start();\n *\n * // Later...\n * client.stop();\n * ```\n */\nexport class OutrayClient {\n private ws: WebSocket | null = null;\n private options: Required<\n Pick<OutrayClientOptions, \"localPort\" | \"serverUrl\">\n > &\n OutrayClientOptions;\n private reconnectTimeout: NodeJS.Timeout | null = null;\n private pingInterval: NodeJS.Timeout | null = null;\n private pongTimeout: NodeJS.Timeout | null = null;\n private shouldReconnect = true;\n private assignedUrl: string | null = null;\n private subdomain?: string;\n private forceTakeover = false;\n private reconnectAttempts = 0;\n private lastPongReceived = Date.now();\n private localWebSockets = new Map<string, WebSocket>();\n\n constructor(options: OutrayClientOptions) {\n this.options = {\n ...options,\n serverUrl: options.serverUrl ?? DEFAULT_SERVER_URL,\n };\n this.subdomain = options.subdomain;\n }\n\n /**\n * Start the tunnel connection\n */\n public start(): void {\n this.shouldReconnect = true;\n this.connect();\n }\n\n /**\n * Stop the tunnel connection\n */\n public stop(): void {\n this.shouldReconnect = false;\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n\n this.stopPing();\n this.stopPongTimeout();\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n // Clean up all local WebSocket connections\n for (const [id, localWs] of this.localWebSockets) {\n localWs.close();\n }\n this.localWebSockets.clear();\n }\n\n /**\n * Get the assigned tunnel URL (if connected)\n */\n public getUrl(): string | null {\n return this.assignedUrl;\n }\n\n /**\n * Check if the client is currently connected\n */\n public isConnected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n private connect(): void {\n this.ws = new WebSocket(this.options.serverUrl);\n\n this.ws.on(\"open\", () => this.handleOpen());\n this.ws.on(\"message\", (data) => this.handleMessage(data.toString()));\n this.ws.on(\"close\", (code, reason) => this.handleClose(code, reason));\n this.ws.on(\"error\", (error) => {\n this.options.onError?.(error);\n });\n this.ws.on(\"pong\", () => {\n this.lastPongReceived = Date.now();\n this.stopPongTimeout();\n });\n }\n\n private handleOpen(): void {\n this.startPing();\n\n const handshake = encodeMessage({\n type: \"open_tunnel\",\n apiKey: this.options.apiKey,\n subdomain: this.subdomain,\n customDomain: this.options.customDomain,\n forceTakeover: this.forceTakeover,\n protocol: this.options.protocol,\n remotePort: this.options.remotePort,\n });\n this.ws?.send(handshake);\n }\n\n private handleMessage(data: string): void {\n try {\n const message = decodeMessage(data);\n\n if (message.type === \"tunnel_opened\") {\n this.assignedUrl = message.url;\n const derivedSubdomain = this.extractSubdomain(message.url);\n if (derivedSubdomain) {\n this.subdomain = derivedSubdomain;\n }\n this.forceTakeover = false;\n this.reconnectAttempts = 0;\n this.options.onTunnelReady?.(message.url, message.port);\n } else if (message.type === \"error\") {\n this.handleError(message.code, message.message);\n } else if (message.type === \"request\") {\n this.handleTunnelData(message);\n } else if (message.type === \"ws_upgrade\") {\n this.handleWSUpgrade(message as WSUpgradeMessage);\n } else if (message.type === \"ws_frame\") {\n this.handleWSFrame(message as WSFrameMessage);\n } else if (message.type === \"ws_close\") {\n this.handleWSClose(message as WSCloseMessage);\n }\n } catch (error) {\n this.options.onError?.(\n error instanceof Error ? error : new Error(String(error))\n );\n }\n }\n\n private handleError(code: string, message: string): void {\n if (code === \"SUBDOMAIN_IN_USE\" && this.assignedUrl && !this.forceTakeover) {\n // Reconnecting and subdomain is in use - try to take over\n this.forceTakeover = true;\n this.connect();\n return;\n }\n\n this.options.onError?.(new Error(message), code);\n\n // Fatal errors - stop reconnecting\n if (code === \"AUTH_FAILED\" || code === \"LIMIT_EXCEEDED\") {\n this.shouldReconnect = false;\n this.stop();\n }\n }\n\n private handleTunnelData(message: TunnelDataMessage): void {\n const startTime = Date.now();\n\n const reqOptions = {\n hostname: \"localhost\",\n port: this.options.localPort,\n path: message.path,\n method: message.method,\n headers: message.headers,\n };\n\n const req = http.request(reqOptions, (res) => {\n const chunks: Buffer[] = [];\n\n res.on(\"data\", (chunk) => {\n chunks.push(Buffer.from(chunk));\n });\n\n res.on(\"end\", () => {\n const duration = Date.now() - startTime;\n const statusCode = res.statusCode || 200;\n\n this.options.onRequest?.({\n method: message.method,\n path: message.path,\n statusCode,\n duration,\n });\n\n const bodyBuffer = Buffer.concat(chunks);\n const bodyBase64 =\n bodyBuffer.length > 0 ? bodyBuffer.toString(\"base64\") : undefined;\n\n const response: TunnelResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n statusCode,\n headers: res.headers as Record<string, string | string[]>,\n body: bodyBase64,\n };\n\n this.ws?.send(encodeMessage(response));\n });\n });\n\n req.on(\"error\", (err) => {\n const duration = Date.now() - startTime;\n\n this.options.onRequest?.({\n method: message.method,\n path: message.path,\n statusCode: 502,\n duration,\n error: err.message,\n });\n\n const errorResponse: TunnelResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n statusCode: 502,\n headers: { \"content-type\": \"text/plain\" },\n body: Buffer.from(`Bad Gateway: ${err.message}`).toString(\"base64\"),\n };\n\n this.ws?.send(encodeMessage(errorResponse));\n });\n\n if (message.body) {\n const bodyBuffer = Buffer.from(message.body, \"base64\");\n req.write(bodyBuffer);\n }\n\n req.end();\n }\n\n private handleWSUpgrade(message: WSUpgradeMessage): void {\n const wsUrl = `ws://localhost:${this.options.localPort}${message.path}`;\n\n try {\n const headers: Record<string, string> = {};\n // Forward relevant headers (like subprotocols)\n if (message.protocol) {\n headers[\"Sec-WebSocket-Protocol\"] = message.protocol;\n }\n\n const localWs = new WebSocket(wsUrl, {\n headers,\n });\n\n localWs.on(\"open\", () => {\n this.localWebSockets.set(message.wsConnectionId, localWs);\n\n // Confirm the upgrade to the server\n this.ws?.send(\n encodeMessage({\n type: \"ws_upgrade_response\",\n wsConnectionId: message.wsConnectionId,\n success: true,\n })\n );\n });\n\n // Relay frames from local WS to server\n localWs.on(\"message\", (data: WebSocket.RawData, isBinary: boolean) => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n const buffer = Buffer.isBuffer(data)\n ? data\n : Buffer.from(data as ArrayBuffer);\n this.ws.send(\n encodeMessage({\n type: \"ws_frame\",\n wsConnectionId: message.wsConnectionId,\n data: buffer.toString(\"base64\"),\n isBinary,\n })\n );\n }\n });\n\n localWs.on(\"close\", (code, reason) => {\n this.localWebSockets.delete(message.wsConnectionId);\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(\n encodeMessage({\n type: \"ws_close\",\n wsConnectionId: message.wsConnectionId,\n code,\n reason: reason?.toString(),\n })\n );\n }\n });\n\n localWs.on(\"error\", (error) => {\n this.localWebSockets.delete(message.wsConnectionId);\n\n // If not yet open, send failure response\n if (localWs.readyState === WebSocket.CONNECTING) {\n this.ws?.send(\n encodeMessage({\n type: \"ws_upgrade_response\",\n wsConnectionId: message.wsConnectionId,\n success: false,\n error: `Failed to connect to local WebSocket: ${error.message}`,\n })\n );\n }\n });\n } catch (error) {\n this.ws?.send(\n encodeMessage({\n type: \"ws_upgrade_response\",\n wsConnectionId: message.wsConnectionId,\n success: false,\n error: `Failed to create WebSocket connection: ${error instanceof Error ? error.message : String(error)}`,\n })\n );\n }\n }\n\n private handleWSFrame(message: WSFrameMessage): void {\n const localWs = this.localWebSockets.get(message.wsConnectionId);\n if (!localWs || localWs.readyState !== WebSocket.OPEN) {\n return;\n }\n\n const data = Buffer.from(message.data, \"base64\");\n localWs.send(data, { binary: message.isBinary });\n }\n\n private handleWSClose(message: WSCloseMessage): void {\n const localWs = this.localWebSockets.get(message.wsConnectionId);\n if (!localWs) {\n return;\n }\n\n this.localWebSockets.delete(message.wsConnectionId);\n localWs.close(message.code || 1000, message.reason || \"\");\n }\n\n private extractSubdomain(url: string): string | null {\n try {\n const hostname = new URL(url).hostname;\n const [subdomain] = hostname.split(\".\");\n return subdomain || null;\n } catch {\n return null;\n }\n }\n\n private startPing(): void {\n this.stopPing();\n this.lastPongReceived = Date.now();\n\n this.pingInterval = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.ping();\n\n this.stopPongTimeout();\n this.pongTimeout = setTimeout(() => {\n // No pong received - connection is likely dead\n if (this.ws) {\n this.ws.terminate();\n }\n }, PONG_TIMEOUT_MS);\n }\n }, PING_INTERVAL_MS);\n }\n\n private stopPing(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n private stopPongTimeout(): void {\n if (this.pongTimeout) {\n clearTimeout(this.pongTimeout);\n this.pongTimeout = null;\n }\n }\n\n private handleClose(code?: number, reason?: Buffer): void {\n this.stopPing();\n this.stopPongTimeout();\n\n if (!this.shouldReconnect) return;\n\n const reasonStr = reason?.toString() || \"\";\n\n if (code === 1000 && reasonStr === \"Tunnel stopped by user\") {\n this.options.onClose?.(reasonStr);\n this.stop();\n return;\n }\n\n // If we previously had a tunnel URL, force takeover on reconnect\n if (this.assignedUrl) {\n this.forceTakeover = true;\n }\n\n const delay = Math.min(2000 * Math.pow(2, this.reconnectAttempts), 30000);\n this.reconnectAttempts += 1;\n\n this.options.onReconnecting?.(this.reconnectAttempts, delay);\n\n this.reconnectTimeout = setTimeout(() => {\n this.connect();\n }, delay);\n }\n}\n","import type { ClientMessage, ServerMessage } from \"./types\";\n\n/**\n * Encode a client message to send to the server\n */\nexport function encodeMessage(message: ClientMessage): string {\n return JSON.stringify(message);\n}\n\n/**\n * Decode a server message received from the server\n */\nexport function decodeMessage(data: string): ServerMessage {\n return JSON.parse(data) as ServerMessage;\n}\n","import dgram from \"dgram\";\nimport http from \"http\";\nimport https from \"https\";\nimport os from \"os\";\n\nconst MDNS_PORT = 5353;\nconst MDNS_ADDRESS = \"224.0.0.251\";\n\ninterface MDNSAdvertisement {\n hostname: string;\n port: number;\n ip: string;\n}\n\n/**\n * Simple mDNS responder that advertises a .local hostname\n * This allows other devices on the LAN to discover and access the tunnel\n */\nexport class MDNSAdvertiser {\n private socket: dgram.Socket | null = null;\n private hostname: string;\n private port: number;\n private ip: string;\n private running = false;\n private announceInterval: NodeJS.Timeout | null = null;\n\n constructor(hostname: string, port: number) {\n this.hostname = hostname.endsWith(\".local\")\n ? hostname\n : `${hostname}.local`;\n this.port = port;\n this.ip = this.getLocalIP();\n }\n\n private getLocalIP(): string {\n const interfaces = os.networkInterfaces();\n for (const name of Object.keys(interfaces)) {\n const netInterfaces = interfaces[name];\n if (!netInterfaces) continue;\n\n for (const net of netInterfaces) {\n if (!net.internal && net.family === \"IPv4\") {\n return net.address;\n }\n }\n }\n return \"127.0.0.1\";\n }\n\n start(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.running) {\n resolve();\n return;\n }\n\n this.socket = dgram.createSocket({ type: \"udp4\", reuseAddr: true });\n\n this.socket.on(\"error\", (err) => {\n this.stop();\n });\n\n this.socket.on(\"message\", (msg, rinfo) => {\n this.handleQuery(msg, rinfo);\n });\n\n this.socket.bind(MDNS_PORT, \"0.0.0.0\", () => {\n try {\n this.socket?.addMembership(MDNS_ADDRESS, this.ip);\n this.socket?.setMulticastTTL(255);\n this.socket?.setMulticastLoopback(true);\n this.running = true;\n this.announce();\n resolve();\n } catch (err) {\n reject(err);\n }\n });\n });\n }\n\n stop(): void {\n if (this.announceInterval) {\n clearInterval(this.announceInterval);\n this.announceInterval = null;\n }\n if (this.socket) {\n try {\n this.socket.dropMembership(MDNS_ADDRESS);\n } catch {\n // Ignore\n }\n this.socket.close();\n this.socket = null;\n }\n this.running = false;\n }\n\n private handleQuery(msg: Buffer, rinfo: dgram.RemoteInfo): void {\n try {\n const query = this.parseDNSQuery(msg);\n if (query && query.toLowerCase() === this.hostname.toLowerCase()) {\n this.sendResponse();\n }\n } catch {\n // Ignore malformed queries\n }\n }\n\n private parseDNSQuery(msg: Buffer): string | null {\n if (msg.length < 12) return null;\n\n const flags = msg.readUInt16BE(2);\n if ((flags & 0x8000) !== 0) return null;\n\n const qdcount = msg.readUInt16BE(4);\n if (qdcount < 1) return null;\n\n let offset = 12;\n const labels: string[] = [];\n\n while (offset < msg.length) {\n const len = msg[offset];\n if (len === 0) break;\n offset++;\n if (offset + len > msg.length) return null;\n labels.push(msg.slice(offset, offset + len).toString(\"ascii\"));\n offset += len;\n }\n\n return labels.join(\".\");\n }\n\n private sendResponse(): void {\n const response = this.buildDNSResponse();\n if (this.socket && this.running) {\n this.socket.send(response, 0, response.length, MDNS_PORT, MDNS_ADDRESS);\n }\n }\n\n private announce(): void {\n this.sendResponse();\n setTimeout(() => this.sendResponse(), 500);\n setTimeout(() => this.sendResponse(), 1000);\n setTimeout(() => this.sendResponse(), 2000);\n\n this.announceInterval = setInterval(() => {\n if (this.running) {\n this.sendResponse();\n }\n }, 30000);\n }\n\n private buildDNSResponse(): Buffer {\n const labels = this.hostname.split(\".\");\n const nameLength = labels.reduce((sum, l) => sum + 1 + l.length, 1);\n const bufferLength = 12 + nameLength + 2 + 2 + 4 + 2 + 4;\n const buffer = Buffer.alloc(bufferLength);\n\n let offset = 0;\n\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n buffer.writeUInt16BE(0x8400, offset);\n offset += 2;\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n buffer.writeUInt16BE(1, offset);\n offset += 2;\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n buffer.writeUInt16BE(0, offset);\n offset += 2;\n\n for (const label of labels) {\n buffer.writeUInt8(label.length, offset);\n offset++;\n buffer.write(label, offset, \"ascii\");\n offset += label.length;\n }\n buffer.writeUInt8(0, offset);\n offset++;\n\n buffer.writeUInt16BE(1, offset);\n offset += 2;\n buffer.writeUInt16BE(0x8001, offset);\n offset += 2;\n buffer.writeUInt32BE(120, offset);\n offset += 4;\n buffer.writeUInt16BE(4, offset);\n offset += 2;\n\n const ipParts = this.ip.split(\".\").map(Number);\n for (const part of ipParts) {\n buffer.writeUInt8(part, offset);\n offset++;\n }\n\n return buffer;\n }\n\n getInfo(): MDNSAdvertisement {\n return {\n hostname: this.hostname,\n port: this.port,\n ip: this.ip,\n };\n }\n}\n\n/**\n * Local HTTP proxy that listens on port 80 and forwards to the target port.\n */\nexport class LocalProxy {\n private server: http.Server | null = null;\n private targetPort: number;\n private running = false;\n\n constructor(targetPort: number) {\n this.targetPort = targetPort;\n }\n\n start(): Promise<boolean> {\n return new Promise((resolve) => {\n this.server = http.createServer((req, res) => {\n const options: http.RequestOptions = {\n hostname: \"localhost\",\n port: this.targetPort,\n path: req.url,\n method: req.method,\n headers: req.headers,\n };\n\n const proxyReq = http.request(options, (proxyRes) => {\n res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on(\"error\", (err) => {\n res.writeHead(502);\n res.end(`Proxy error: ${err.message}`);\n });\n\n req.pipe(proxyReq);\n });\n\n this.server.on(\"error\", () => {\n resolve(false);\n });\n\n this.server.listen(80, \"0.0.0.0\", () => {\n this.running = true;\n resolve(true);\n });\n });\n }\n\n stop(): void {\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n this.running = false;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n}\n\n/**\n * Local HTTPS proxy that listens on port 443 and forwards to the target port.\n */\nexport class LocalHttpsProxy {\n private server: https.Server | null = null;\n private targetPort: number;\n private hostname: string;\n private running = false;\n private _isTrusted = false;\n\n constructor(targetPort: number, hostname: string) {\n this.targetPort = targetPort;\n this.hostname = hostname;\n }\n\n get isTrusted(): boolean {\n return this._isTrusted;\n }\n\n async start(): Promise<boolean> {\n return new Promise((resolve) => {\n this.generateCert()\n .then((creds) => {\n if (!creds) {\n resolve(false);\n return;\n }\n\n this._isTrusted = creds.trusted;\n\n this.server = https.createServer(\n { key: creds.key, cert: creds.cert },\n (req, res) => {\n const options: http.RequestOptions = {\n hostname: \"localhost\",\n port: this.targetPort,\n path: req.url,\n method: req.method,\n headers: req.headers,\n };\n\n const proxyReq = http.request(options, (proxyRes) => {\n res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on(\"error\", (err) => {\n res.writeHead(502);\n res.end(`Proxy error: ${err.message}`);\n });\n\n req.pipe(proxyReq);\n },\n );\n\n this.server.on(\"error\", () => {\n resolve(false);\n });\n\n this.server.listen(443, \"0.0.0.0\", () => {\n this.running = true;\n resolve(true);\n });\n })\n .catch(() => resolve(false));\n });\n }\n\n private generateCert(): Promise<{\n key: string;\n cert: string;\n trusted: boolean;\n } | null> {\n return new Promise((resolve) => {\n const { execSync } = require(\"child_process\");\n const fs = require(\"fs\");\n const os = require(\"os\");\n const path = require(\"path\");\n\n const tmpDir = os.tmpdir();\n const keyFile = path.join(tmpDir, `outray-${Date.now()}-key.pem`);\n const certFile = path.join(tmpDir, `outray-${Date.now()}.pem`);\n\n // Try mkcert first\n try {\n execSync(\"which mkcert\", { stdio: \"pipe\" });\n execSync(\n `mkcert -key-file \"${keyFile}\" -cert-file \"${certFile}\" \"${this.hostname}\"`,\n { stdio: \"pipe\" },\n );\n\n const key = fs.readFileSync(keyFile, \"utf8\");\n const cert = fs.readFileSync(certFile, \"utf8\");\n fs.unlinkSync(keyFile);\n fs.unlinkSync(certFile);\n\n resolve({ key, cert, trusted: true });\n return;\n } catch {\n // Fall back to openssl\n }\n\n try {\n execSync(\n `openssl req -x509 -newkey rsa:2048 -keyout \"${keyFile}\" -out \"${certFile}\" -days 365 -nodes -subj \"/CN=${this.hostname}\" -addext \"subjectAltName=DNS:${this.hostname}\"`,\n { stdio: \"pipe\" },\n );\n\n const key = fs.readFileSync(keyFile, \"utf8\");\n const cert = fs.readFileSync(certFile, \"utf8\");\n fs.unlinkSync(keyFile);\n fs.unlinkSync(certFile);\n\n resolve({ key, cert, trusted: false });\n } catch {\n resolve(null);\n }\n });\n }\n\n stop(): void {\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n this.running = false;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n}\n\nexport interface LocalAccessInfo {\n hostname: string;\n ip: string;\n port: number;\n httpUrl?: string;\n httpsUrl?: string;\n httpsIsTrusted?: boolean;\n}\n\n/**\n * Manages local LAN access via mDNS and optional HTTP/HTTPS proxies\n */\nexport class LocalAccessManager {\n private mdnsAdvertiser: MDNSAdvertiser | null = null;\n private localProxy: LocalProxy | null = null;\n private localHttpsProxy: LocalHttpsProxy | null = null;\n private port: number;\n private subdomain: string;\n\n constructor(port: number, subdomain: string) {\n this.port = port;\n this.subdomain = subdomain;\n }\n\n async start(): Promise<LocalAccessInfo> {\n const hostname = `${this.subdomain}.local`;\n\n // Start mDNS\n this.mdnsAdvertiser = new MDNSAdvertiser(this.subdomain, this.port);\n await this.mdnsAdvertiser.start();\n const info = this.mdnsAdvertiser.getInfo();\n\n const result: LocalAccessInfo = {\n hostname: info.hostname,\n ip: info.ip,\n port: this.port,\n };\n\n // Try HTTPS proxy (port 443)\n this.localHttpsProxy = new LocalHttpsProxy(this.port, hostname);\n const httpsStarted = await this.localHttpsProxy.start();\n if (httpsStarted) {\n result.httpsUrl = `https://${hostname}`;\n result.httpsIsTrusted = this.localHttpsProxy.isTrusted;\n }\n\n // Try HTTP proxy (port 80)\n this.localProxy = new LocalProxy(this.port);\n const httpStarted = await this.localProxy.start();\n if (httpStarted) {\n result.httpUrl = `http://${hostname}`;\n }\n\n return result;\n }\n\n stop(): void {\n if (this.localHttpsProxy) {\n this.localHttpsProxy.stop();\n this.localHttpsProxy = null;\n }\n if (this.localProxy) {\n this.localProxy.stop();\n this.localProxy = null;\n }\n if (this.mdnsAdvertiser) {\n this.mdnsAdvertiser.stop();\n this.mdnsAdvertiser = null;\n }\n }\n}\n","/**\n * Tunnel protocol types - HTTP\n */\nexport type TunnelProtocol = \"http\" | \"tcp\" | \"udp\";\n\n// ============================================================================\n// Client Configuration\n// ============================================================================\n\n/**\n * Options for creating an OutrayClient instance\n */\nexport interface OutrayClientOptions {\n /**\n * Local port to proxy requests to\n */\n localPort: number;\n\n /**\n * Outray server WebSocket URL\n * @default 'wss://api.outray.dev/'\n */\n serverUrl?: string;\n\n /**\n * API key for authentication\n */\n apiKey?: string;\n\n /**\n * Subdomain to use for the tunnel URL\n * Requires authentication\n */\n subdomain?: string;\n\n /**\n * Custom domain for the tunnel\n * Must be configured in the Outray dashboard first\n */\n customDomain?: string;\n\n /**\n * Tunnel protocol type\n * @default 'http'\n */\n protocol?: TunnelProtocol;\n\n /**\n * Remote port for TCP/UDP tunnels\n */\n remotePort?: number;\n\n /**\n * Callback fired when tunnel is successfully established\n */\n onTunnelReady?: (url: string, port?: number) => void;\n\n /**\n * Callback fired when tunnel encounters an error\n */\n onError?: (error: Error, code?: string) => void;\n\n /**\n * Callback fired when tunnel is closed\n */\n onClose?: (reason?: string) => void;\n\n /**\n * Callback fired when attempting to reconnect\n */\n onReconnecting?: (attempt: number, delay: number) => void;\n\n /**\n * Callback fired for each proxied request (HTTP only)\n */\n onRequest?: (info: RequestInfo) => void;\n}\n\n/**\n * Information about a proxied request\n */\nexport interface RequestInfo {\n method: string;\n path: string;\n statusCode: number;\n duration: number;\n error?: string;\n}\n\n// ============================================================================\n// Protocol Messages - Client to Server\n// ============================================================================\n\nexport interface OpenTunnelMessage {\n type: \"open_tunnel\";\n subdomain?: string | null;\n customDomain?: string | null;\n apiKey?: string;\n forceTakeover?: boolean;\n protocol?: TunnelProtocol;\n remotePort?: number;\n password?: string;\n}\n\nexport interface TunnelResponseMessage {\n type: \"response\";\n requestId: string;\n statusCode: number;\n headers: Record<string, string | string[]>;\n body?: string;\n}\n\nexport interface TCPDataMessage {\n type: \"tcp_data\";\n connectionId: string;\n data: string; // base64 encoded\n}\n\nexport interface TCPCloseMessage {\n type: \"tcp_close\";\n connectionId: string;\n}\n\nexport interface UDPResponseMessage {\n type: \"udp_response\";\n packetId: string;\n targetAddress: string;\n targetPort: number;\n data: string; // base64 encoded\n}\n\n// WebSocket passthrough messages (client → server)\nexport interface WSUpgradeResponseMessage {\n type: \"ws_upgrade_response\";\n wsConnectionId: string;\n success: boolean;\n error?: string;\n}\n\nexport interface WSFrameMessage {\n type: \"ws_frame\";\n wsConnectionId: string;\n data: string; // base64 encoded\n isBinary: boolean;\n}\n\nexport interface WSCloseMessage {\n type: \"ws_close\";\n wsConnectionId: string;\n code?: number;\n reason?: string;\n}\n\nexport type ClientMessage =\n | OpenTunnelMessage\n | TunnelResponseMessage\n | TCPDataMessage\n | TCPCloseMessage\n | UDPResponseMessage\n | WSUpgradeResponseMessage\n | WSFrameMessage\n | WSCloseMessage;\n\n// ============================================================================\n// Protocol Messages - Server to Client\n// ============================================================================\n\nexport interface TunnelOpenedMessage {\n type: \"tunnel_opened\";\n url: string;\n protocol?: TunnelProtocol;\n port?: number;\n}\n\nexport interface TunnelDataMessage {\n type: \"request\";\n requestId: string;\n method: string;\n path: string;\n headers: Record<string, string | string[]>;\n body?: string;\n}\n\nexport interface TCPConnectionMessage {\n type: \"tcp_connection\";\n connectionId: string;\n}\n\nexport interface TCPIncomingDataMessage {\n type: \"tcp_data\";\n connectionId: string;\n data: string; // base64 encoded\n}\n\nexport interface TCPIncomingCloseMessage {\n type: \"tcp_close\";\n connectionId: string;\n}\n\nexport interface UDPDataMessage {\n type: \"udp_data\";\n packetId: string;\n sourceAddress: string;\n sourcePort: number;\n data: string; // base64 encoded\n}\n\nexport interface ErrorMessage {\n type: \"error\";\n code: string;\n message: string;\n}\n\n// WebSocket passthrough messages (server → client)\nexport interface WSUpgradeMessage {\n type: \"ws_upgrade\";\n wsConnectionId: string;\n path: string;\n headers: Record<string, string | string[]>;\n protocol?: string;\n}\n\nexport type ServerMessage =\n | TunnelOpenedMessage\n | TunnelDataMessage\n | TCPConnectionMessage\n | TCPIncomingDataMessage\n | TCPIncomingCloseMessage\n | UDPDataMessage\n | ErrorMessage\n | WSUpgradeMessage\n | WSFrameMessage\n | WSCloseMessage;\n\n// ============================================================================\n// Error Codes\n// ============================================================================\n\nexport const ErrorCodes = {\n SUBDOMAIN_IN_USE: \"SUBDOMAIN_IN_USE\",\n AUTH_FAILED: \"AUTH_FAILED\",\n LIMIT_EXCEEDED: \"LIMIT_EXCEEDED\",\n INVALID_SUBDOMAIN: \"INVALID_SUBDOMAIN\",\n CUSTOM_DOMAIN_NOT_CONFIGURED: \"CUSTOM_DOMAIN_NOT_CONFIGURED\",\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n"],"mappings":"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","names":["http","os"]} |
+1
-1
| { | ||
| "name": "@outray/core", | ||
| "version": "0.0.3", | ||
| "version": "0.0.4", | ||
| "description": "Core tunnel client for Outray - shared between CLI and framework plugins", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
Network access
Supply chain riskThis module accesses the network.
Found 3 instances in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 3 instances in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
158823
0.06%1803
0.06%