New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

@outray/core

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@outray/core - npm Package Compare versions

Comparing version
0.0.2
to
0.0.3
+32
-3
dist/index.d.mts

@@ -104,3 +104,21 @@ /**

}
type ClientMessage = OpenTunnelMessage | TunnelResponseMessage | TCPDataMessage | TCPCloseMessage | UDPResponseMessage;
interface WSUpgradeResponseMessage {
type: "ws_upgrade_response";
wsConnectionId: string;
success: boolean;
error?: string;
}
interface WSFrameMessage {
type: "ws_frame";
wsConnectionId: string;
data: string;
isBinary: boolean;
}
interface WSCloseMessage {
type: "ws_close";
wsConnectionId: string;
code?: number;
reason?: string;
}
type ClientMessage = OpenTunnelMessage | TunnelResponseMessage | TCPDataMessage | TCPCloseMessage | UDPResponseMessage | WSUpgradeResponseMessage | WSFrameMessage | WSCloseMessage;
interface TunnelOpenedMessage {

@@ -145,3 +163,10 @@ type: "tunnel_opened";

}
type ServerMessage = TunnelOpenedMessage | TunnelDataMessage | TCPConnectionMessage | TCPIncomingDataMessage | TCPIncomingCloseMessage | UDPDataMessage | ErrorMessage;
interface WSUpgradeMessage {
type: "ws_upgrade";
wsConnectionId: string;
path: string;
headers: Record<string, string | string[]>;
protocol?: string;
}
type ServerMessage = TunnelOpenedMessage | TunnelDataMessage | TCPConnectionMessage | TCPIncomingDataMessage | TCPIncomingCloseMessage | UDPDataMessage | ErrorMessage | WSUpgradeMessage | WSFrameMessage | WSCloseMessage;
declare const ErrorCodes: {

@@ -188,2 +213,3 @@ readonly SUBDOMAIN_IN_USE: "SUBDOMAIN_IN_USE";

private lastPongReceived;
private localWebSockets;
constructor(options: OutrayClientOptions);

@@ -211,2 +237,5 @@ /**

private handleTunnelData;
private handleWSUpgrade;
private handleWSFrame;
private handleWSClose;
private extractSubdomain;

@@ -305,2 +334,2 @@ private startPing;

export { type ClientMessage, type ErrorCode, ErrorCodes, type ErrorMessage, type LocalAccessInfo, LocalAccessManager, LocalHttpsProxy, LocalProxy, MDNSAdvertiser, type OpenTunnelMessage, OutrayClient, type OutrayClientOptions, type RequestInfo, type ServerMessage, type TCPCloseMessage, type TCPConnectionMessage, type TCPDataMessage, type TCPIncomingCloseMessage, type TCPIncomingDataMessage, type TunnelDataMessage, type TunnelOpenedMessage, type TunnelProtocol, type TunnelResponseMessage, type UDPDataMessage, type UDPResponseMessage, decodeMessage, encodeMessage };
export { type ClientMessage, type ErrorCode, ErrorCodes, type ErrorMessage, type LocalAccessInfo, LocalAccessManager, LocalHttpsProxy, LocalProxy, MDNSAdvertiser, type OpenTunnelMessage, OutrayClient, type OutrayClientOptions, type RequestInfo, type ServerMessage, type TCPCloseMessage, type TCPConnectionMessage, type TCPDataMessage, type TCPIncomingCloseMessage, type TCPIncomingDataMessage, type TunnelDataMessage, type TunnelOpenedMessage, type TunnelProtocol, type TunnelResponseMessage, type UDPDataMessage, type UDPResponseMessage, type WSCloseMessage, type WSFrameMessage, type WSUpgradeMessage, type WSUpgradeResponseMessage, decodeMessage, encodeMessage };

@@ -104,3 +104,21 @@ /**

}
type ClientMessage = OpenTunnelMessage | TunnelResponseMessage | TCPDataMessage | TCPCloseMessage | UDPResponseMessage;
interface WSUpgradeResponseMessage {
type: "ws_upgrade_response";
wsConnectionId: string;
success: boolean;
error?: string;
}
interface WSFrameMessage {
type: "ws_frame";
wsConnectionId: string;
data: string;
isBinary: boolean;
}
interface WSCloseMessage {
type: "ws_close";
wsConnectionId: string;
code?: number;
reason?: string;
}
type ClientMessage = OpenTunnelMessage | TunnelResponseMessage | TCPDataMessage | TCPCloseMessage | UDPResponseMessage | WSUpgradeResponseMessage | WSFrameMessage | WSCloseMessage;
interface TunnelOpenedMessage {

@@ -145,3 +163,10 @@ type: "tunnel_opened";

}
type ServerMessage = TunnelOpenedMessage | TunnelDataMessage | TCPConnectionMessage | TCPIncomingDataMessage | TCPIncomingCloseMessage | UDPDataMessage | ErrorMessage;
interface WSUpgradeMessage {
type: "ws_upgrade";
wsConnectionId: string;
path: string;
headers: Record<string, string | string[]>;
protocol?: string;
}
type ServerMessage = TunnelOpenedMessage | TunnelDataMessage | TCPConnectionMessage | TCPIncomingDataMessage | TCPIncomingCloseMessage | UDPDataMessage | ErrorMessage | WSUpgradeMessage | WSFrameMessage | WSCloseMessage;
declare const ErrorCodes: {

@@ -188,2 +213,3 @@ readonly SUBDOMAIN_IN_USE: "SUBDOMAIN_IN_USE";

private lastPongReceived;
private localWebSockets;
constructor(options: OutrayClientOptions);

@@ -211,2 +237,5 @@ /**

private handleTunnelData;
private handleWSUpgrade;
private handleWSFrame;
private handleWSClose;
private extractSubdomain;

@@ -305,2 +334,2 @@ private startPing;

export { type ClientMessage, type ErrorCode, ErrorCodes, type ErrorMessage, type LocalAccessInfo, LocalAccessManager, LocalHttpsProxy, LocalProxy, MDNSAdvertiser, type OpenTunnelMessage, OutrayClient, type OutrayClientOptions, type RequestInfo, type ServerMessage, type TCPCloseMessage, type TCPConnectionMessage, type TCPDataMessage, type TCPIncomingCloseMessage, type TCPIncomingDataMessage, type TunnelDataMessage, type TunnelOpenedMessage, type TunnelProtocol, type TunnelResponseMessage, type UDPDataMessage, type UDPResponseMessage, decodeMessage, encodeMessage };
export { type ClientMessage, type ErrorCode, ErrorCodes, type ErrorMessage, type LocalAccessInfo, LocalAccessManager, LocalHttpsProxy, LocalProxy, MDNSAdvertiser, type OpenTunnelMessage, OutrayClient, type OutrayClientOptions, type RequestInfo, type ServerMessage, type TCPCloseMessage, type TCPConnectionMessage, type TCPDataMessage, type TCPIncomingCloseMessage, type TCPIncomingDataMessage, type TunnelDataMessage, type TunnelOpenedMessage, type TunnelProtocol, type TunnelResponseMessage, type UDPDataMessage, type UDPResponseMessage, type WSCloseMessage, type WSFrameMessage, type WSUpgradeMessage, type WSUpgradeResponseMessage, decodeMessage, encodeMessage };

@@ -71,2 +71,3 @@ "use strict";

this.lastPongReceived = Date.now();
this.localWebSockets = /* @__PURE__ */ new Map();
this.options = {

@@ -100,2 +101,6 @@ ...options,

}
for (const [id, localWs] of this.localWebSockets) {
localWs.close();
}
this.localWebSockets.clear();
}

@@ -156,2 +161,8 @@ /**

this.handleTunnelData(message);
} else if (message.type === "ws_upgrade") {
this.handleWSUpgrade(message);
} else if (message.type === "ws_frame") {
this.handleWSFrame(message);
} else if (message.type === "ws_close") {
this.handleWSClose(message);
}

@@ -235,2 +246,88 @@ } catch (error) {

}
handleWSUpgrade(message) {
const wsUrl = `ws://localhost:${this.options.localPort}${message.path}`;
try {
const headers = {};
if (message.protocol) {
headers["Sec-WebSocket-Protocol"] = message.protocol;
}
const localWs = new import_ws.default(wsUrl, {
headers
});
localWs.on("open", () => {
this.localWebSockets.set(message.wsConnectionId, localWs);
this.ws?.send(
encodeMessage({
type: "ws_upgrade_response",
wsConnectionId: message.wsConnectionId,
success: true
})
);
});
localWs.on("message", (data, isBinary) => {
if (this.ws?.readyState === import_ws.default.OPEN) {
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
this.ws.send(
encodeMessage({
type: "ws_frame",
wsConnectionId: message.wsConnectionId,
data: buffer.toString("base64"),
isBinary
})
);
}
});
localWs.on("close", (code, reason) => {
this.localWebSockets.delete(message.wsConnectionId);
if (this.ws?.readyState === import_ws.default.OPEN) {
this.ws.send(
encodeMessage({
type: "ws_close",
wsConnectionId: message.wsConnectionId,
code,
reason: reason?.toString()
})
);
}
});
localWs.on("error", (error) => {
this.localWebSockets.delete(message.wsConnectionId);
if (localWs.readyState === import_ws.default.CONNECTING) {
this.ws?.send(
encodeMessage({
type: "ws_upgrade_response",
wsConnectionId: message.wsConnectionId,
success: false,
error: `Failed to connect to local WebSocket: ${error.message}`
})
);
}
});
} catch (error) {
this.ws?.send(
encodeMessage({
type: "ws_upgrade_response",
wsConnectionId: message.wsConnectionId,
success: false,
error: `Failed to create WebSocket connection: ${error instanceof Error ? error.message : String(error)}`
})
);
}
}
handleWSFrame(message) {
const localWs = this.localWebSockets.get(message.wsConnectionId);
if (!localWs || localWs.readyState !== import_ws.default.OPEN) {
return;
}
const data = Buffer.from(message.data, "base64");
localWs.send(data, { binary: message.isBinary });
}
handleWSClose(message) {
const localWs = this.localWebSockets.get(message.wsConnectionId);
if (!localWs) {
return;
}
this.localWebSockets.delete(message.wsConnectionId);
localWs.close(message.code || 1e3, message.reason || "");
}
extractSubdomain(url) {

@@ -237,0 +334,0 @@ try {

+1
-1

@@ -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} 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 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\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\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 }\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 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\nexport type ClientMessage =\n | OpenTunnelMessage\n | TunnelResponseMessage\n | TCPDataMessage\n | TCPCloseMessage\n | UDPResponseMessage;\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\nexport type ServerMessage =\n | TunnelOpenedMessage\n | TunnelDataMessage\n | TCPConnectionMessage\n | TCPIncomingDataMessage\n | TCPIncomingCloseMessage\n | UDPDataMessage\n | ErrorMessage;\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}\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"]}

@@ -35,2 +35,3 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {

this.lastPongReceived = Date.now();
this.localWebSockets = /* @__PURE__ */ new Map();
this.options = {

@@ -64,2 +65,6 @@ ...options,

}
for (const [id, localWs] of this.localWebSockets) {
localWs.close();
}
this.localWebSockets.clear();
}

@@ -120,2 +125,8 @@ /**

this.handleTunnelData(message);
} else if (message.type === "ws_upgrade") {
this.handleWSUpgrade(message);
} else if (message.type === "ws_frame") {
this.handleWSFrame(message);
} else if (message.type === "ws_close") {
this.handleWSClose(message);
}

@@ -199,2 +210,88 @@ } catch (error) {

}
handleWSUpgrade(message) {
const wsUrl = `ws://localhost:${this.options.localPort}${message.path}`;
try {
const headers = {};
if (message.protocol) {
headers["Sec-WebSocket-Protocol"] = message.protocol;
}
const localWs = new WebSocket(wsUrl, {
headers
});
localWs.on("open", () => {
this.localWebSockets.set(message.wsConnectionId, localWs);
this.ws?.send(
encodeMessage({
type: "ws_upgrade_response",
wsConnectionId: message.wsConnectionId,
success: true
})
);
});
localWs.on("message", (data, isBinary) => {
if (this.ws?.readyState === WebSocket.OPEN) {
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
this.ws.send(
encodeMessage({
type: "ws_frame",
wsConnectionId: message.wsConnectionId,
data: buffer.toString("base64"),
isBinary
})
);
}
});
localWs.on("close", (code, reason) => {
this.localWebSockets.delete(message.wsConnectionId);
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(
encodeMessage({
type: "ws_close",
wsConnectionId: message.wsConnectionId,
code,
reason: reason?.toString()
})
);
}
});
localWs.on("error", (error) => {
this.localWebSockets.delete(message.wsConnectionId);
if (localWs.readyState === WebSocket.CONNECTING) {
this.ws?.send(
encodeMessage({
type: "ws_upgrade_response",
wsConnectionId: message.wsConnectionId,
success: false,
error: `Failed to connect to local WebSocket: ${error.message}`
})
);
}
});
} catch (error) {
this.ws?.send(
encodeMessage({
type: "ws_upgrade_response",
wsConnectionId: message.wsConnectionId,
success: false,
error: `Failed to create WebSocket connection: ${error instanceof Error ? error.message : String(error)}`
})
);
}
}
handleWSFrame(message) {
const localWs = this.localWebSockets.get(message.wsConnectionId);
if (!localWs || localWs.readyState !== WebSocket.OPEN) {
return;
}
const data = Buffer.from(message.data, "base64");
localWs.send(data, { binary: message.isBinary });
}
handleWSClose(message) {
const localWs = this.localWebSockets.get(message.wsConnectionId);
if (!localWs) {
return;
}
this.localWebSockets.delete(message.wsConnectionId);
localWs.close(message.code || 1e3, message.reason || "");
}
extractSubdomain(url) {

@@ -201,0 +298,0 @@ try {

@@ -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 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\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\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 }\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 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\nexport type ClientMessage =\n | OpenTunnelMessage\n | TunnelResponseMessage\n | TCPDataMessage\n | TCPCloseMessage\n | UDPResponseMessage;\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\nexport type ServerMessage =\n | TunnelOpenedMessage\n | TunnelDataMessage\n | TCPConnectionMessage\n | TCPIncomingDataMessage\n | TCPIncomingCloseMessage\n | UDPDataMessage\n | ErrorMessage;\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"]}
{"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":"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","names":["http","os"]}
{
"name": "@outray/core",
"version": "0.0.2",
"version": "0.0.3",
"description": "Core tunnel client for Outray - shared between CLI and framework plugins",

@@ -5,0 +5,0 @@ "main": "dist/index.js",