@workflow/utils 4.1.1 → 4.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"get-port.d.ts","sourceRoot":"","sources":["../src/get-port.ts"],"names":[],"mappings":"AAuMA;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAoBrD;AAED;;;GAGG;AACH,wBAAsB,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAG3D;AAMD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAiCD;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAqC7B"}
1
+ {"version":3,"file":"get-port.d.ts","sourceRoot":"","sources":["../src/get-port.ts"],"names":[],"mappings":"AAgPA;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAyBrD;AAED;;;GAGG;AACH,wBAAsB,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAG3D;AAMD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAiCD;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAqC7B"}
package/dist/get-port.js CHANGED
@@ -17,6 +17,28 @@ function parsePort(value, radix = 10) {
17
17
  // Since /proc is a virtual Linux filesystem, this causes build failures in @sveltejs/adapter-vercel.
18
18
  const join = (arr, sep) => arr.join(sep);
19
19
  const PROC_ROOT = join(['', 'proc'], '/');
20
+ function getReportedPorts() {
21
+ const report = process.report?.getReport?.();
22
+ const handles = report?.libuv;
23
+ if (!handles) {
24
+ return [];
25
+ }
26
+ const ports = [];
27
+ const seen = new Set();
28
+ for (const handle of handles) {
29
+ if (handle.type !== 'tcp' ||
30
+ handle.is_active !== true ||
31
+ handle.remoteEndpoint !== null) {
32
+ continue;
33
+ }
34
+ const port = parsePort(String(handle.localEndpoint?.port));
35
+ if (port !== undefined && !seen.has(port)) {
36
+ ports.push(port);
37
+ seen.add(port);
38
+ }
39
+ }
40
+ return ports;
41
+ }
20
42
  /**
21
43
  * Gets ALL listening ports for the current process on Linux by reading /proc filesystem.
22
44
  * Returns ports in order of file descriptor (deterministic ordering).
@@ -183,6 +205,10 @@ async function getWindowsPorts(pid) {
183
205
  export async function getAllPorts() {
184
206
  const { pid, platform } = process;
185
207
  try {
208
+ const reportedPorts = getReportedPorts();
209
+ if (reportedPorts.length > 0) {
210
+ return reportedPorts;
211
+ }
186
212
  switch (platform) {
187
213
  case 'linux':
188
214
  return await getLinuxPorts(pid);
@@ -1 +1 @@
1
- {"version":3,"file":"get-port.js","sourceRoot":"","sources":["../src/get-port.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile } from 'node:child_process';\nimport { readdir, readFile, readlink } from 'node:fs/promises';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Parses a port string and returns it if valid (0-65535), otherwise undefined.\n */\nfunction parsePort(value: string, radix = 10): number | undefined {\n const port = parseInt(value, radix);\n if (!Number.isNaN(port) && port >= 0 && port <= 65535) {\n return port;\n }\n return undefined;\n}\n\n// NOTE: We build /proc paths dynamically to prevent @vercel/nft from tracing them.\n// NFT's static analysis tries to bundle any file path literal it finds (e.g., '/proc/net/tcp').\n// Since /proc is a virtual Linux filesystem, this causes build failures in @sveltejs/adapter-vercel.\nconst join = (arr: string[], sep: string) => arr.join(sep);\nconst PROC_ROOT = join(['', 'proc'], '/');\n\n/**\n * Gets ALL listening ports for the current process on Linux by reading /proc filesystem.\n * Returns ports in order of file descriptor (deterministic ordering).\n */\nasync function getLinuxPorts(pid: number): Promise<number[]> {\n const listenState = '0A'; // TCP LISTEN state in /proc/net/tcp\n const tcpFiles = [`${PROC_ROOT}/net/tcp`, `${PROC_ROOT}/net/tcp6`] as const;\n\n // Step 1: Get socket inodes from /proc/<pid>/fd/ in order\n // We preserve order to maintain deterministic behavior\n // Use both array (for order) and Set (for O(1) lookup)\n const socketInodes: string[] = [];\n const socketInodesSet = new Set<string>();\n const fdPath = `${PROC_ROOT}/${pid}/fd`;\n\n try {\n const fds = await readdir(fdPath);\n // Sort FDs numerically to ensure deterministic order (FDs are always numeric strings)\n const sortedFds = fds.sort((a, b) => {\n const numA = Number.parseInt(a, 10);\n const numB = Number.parseInt(b, 10);\n return numA - numB;\n });\n\n const results = await Promise.allSettled(\n sortedFds.map(async (fd) => {\n const link = await readlink(`${fdPath}/${fd}`);\n // Socket links look like: socket:[12345]\n const match = link.match(/^socket:\\[(\\d+)\\]$/);\n return match?.[1] ?? null;\n })\n );\n\n for (const result of results) {\n if (result.status === 'fulfilled' && result.value) {\n socketInodes.push(result.value);\n socketInodesSet.add(result.value);\n }\n }\n } catch {\n // Process might not exist or no permission\n return [];\n }\n\n if (socketInodes.length === 0) {\n return [];\n }\n\n // Step 2: Read /proc/net/tcp and /proc/net/tcp6 to find listening sockets\n // Format: sl local_address rem_address st ... inode\n // local_address is hex IP:port, st=0A means LISTEN\n const inodeToPort = new Map<string, number>();\n\n for (const tcpFile of tcpFiles) {\n try {\n const content = await readFile(tcpFile, 'utf8');\n const lines = content.split('\\n').slice(1); // Skip header\n\n for (const line of lines) {\n if (!line.trim()) continue; // Skip empty lines\n\n const parts = line.trim().split(/\\s+/);\n if (parts.length < 10) continue;\n\n const localAddr = parts[1]; // e.g., \"00000000:0BB8\" (0.0.0.0:3000)\n const state = parts[3]; // \"0A\" = LISTEN\n const inode = parts[9];\n\n if (!localAddr || state !== listenState || !inode) continue;\n if (!socketInodesSet.has(inode)) continue;\n\n // Extract port from hex format (e.g., \"0BB8\" -> 3000)\n const colonIndex = localAddr.indexOf(':');\n if (colonIndex === -1) continue;\n\n const portHex = localAddr.slice(colonIndex + 1);\n if (!portHex) continue;\n\n const port = parsePort(portHex, 16);\n if (port !== undefined) {\n inodeToPort.set(inode, port);\n }\n }\n } catch {}\n }\n\n // Return all ports in socket inode order (deterministic)\n const ports: number[] = [];\n for (const inode of socketInodes) {\n const port = inodeToPort.get(inode);\n if (port !== undefined) {\n ports.push(port);\n }\n }\n\n return ports;\n}\n\n/**\n * Gets ALL listening ports for the current process on macOS using lsof.\n * Returns ports in the order they appear in lsof output.\n */\nasync function getDarwinPorts(pid: number): Promise<number[]> {\n try {\n const { stdout } = await execFileAsync('lsof', [\n '-a',\n '-i',\n '-P',\n '-n',\n '-p',\n pid.toString(),\n ]);\n\n const ports: number[] = [];\n const lines = stdout.split('\\n');\n\n for (const line of lines) {\n if (line.includes('LISTEN')) {\n // Column 9 (0-indexed: 8) contains the address like \"*:3000\" or \"127.0.0.1:3000\"\n const parts = line.trim().split(/\\s+/);\n const addr = parts[8];\n if (addr) {\n const colonIndex = addr.lastIndexOf(':');\n if (colonIndex !== -1) {\n const port = parsePort(addr.slice(colonIndex + 1));\n if (port !== undefined) {\n ports.push(port);\n }\n }\n }\n }\n }\n\n return ports;\n } catch {\n return [];\n }\n}\n\n/**\n * Gets ALL listening ports for the current process on Windows using netstat.\n * Returns ports in the order they appear in netstat output.\n */\nasync function getWindowsPorts(pid: number): Promise<number[]> {\n try {\n const { stdout } = await execFileAsync('cmd', [\n '/c',\n `netstat -ano | findstr ${pid} | findstr LISTENING`,\n ]);\n\n const ports: number[] = [];\n const trimmedOutput = stdout.trim();\n\n if (trimmedOutput) {\n const lines = trimmedOutput.split('\\n');\n for (const line of lines) {\n // Extract port from the local address column\n // Matches both IPv4 (e.g., \"127.0.0.1:3000\") and IPv6 bracket notation (e.g., \"[::1]:3000\")\n const match = line\n .trim()\n .match(/^\\s*TCP\\s+(?:\\[[\\da-f:]+\\]|[\\d.]+):(\\d+)\\s+/i);\n if (match) {\n const port = parsePort(match[1]);\n if (port !== undefined) {\n ports.push(port);\n }\n }\n }\n }\n\n return ports;\n } catch {\n return [];\n }\n}\n\n/**\n * Gets all listening ports for the current process.\n * @returns Array of port numbers the process is listening on, in deterministic order.\n */\nexport async function getAllPorts(): Promise<number[]> {\n const { pid, platform } = process;\n\n try {\n switch (platform) {\n case 'linux':\n return await getLinuxPorts(pid);\n case 'darwin':\n return await getDarwinPorts(pid);\n case 'win32':\n return await getWindowsPorts(pid);\n default:\n return [];\n }\n } catch (error) {\n if (process.env.NODE_ENV === 'development') {\n console.debug('[getAllPorts] Detection failed:', error);\n }\n return [];\n }\n}\n\n/**\n * Gets the port number that the process is listening on.\n * @returns The port number that the process is listening on, or undefined if the process is not listening on any port.\n */\nexport async function getPort(): Promise<number | undefined> {\n const ports = await getAllPorts();\n return ports[0];\n}\n\n// Configuration for HTTP probing\nconst PROBE_TIMEOUT_MS = 500;\nconst PROBE_ENDPOINT = '/.well-known/workflow/v1/flow?__health';\n\nexport interface ProbeOptions {\n endpoint?: string;\n timeout?: number;\n}\n\n/**\n * Probes a port to check if it's serving the workflow HTTP server.\n * Uses HEAD request to minimize overhead.\n *\n * @returns true if the port responds with a 200 status from the health check endpoint\n */\nasync function probePort(\n port: number,\n options: ProbeOptions = {}\n): Promise<boolean> {\n const { endpoint = PROBE_ENDPOINT, timeout = PROBE_TIMEOUT_MS } = options;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(`http://localhost:${port}${endpoint}`, {\n method: 'HEAD',\n signal: controller.signal,\n });\n\n // The workflow health endpoint returns 200 for healthy\n return response.status === 200;\n } catch {\n // Connection refused, timeout, or other error\n return false;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Gets the workflow server port by probing all listening ports.\n * This is more reliable than getPort() when other services (like Node.js inspector)\n * may also be listening on ports.\n *\n * @param options - Optional configuration for probing\n * @returns The port number of the workflow server, or undefined if not found\n */\nexport async function getWorkflowPort(\n options?: ProbeOptions\n): Promise<number | undefined> {\n const ports = await getAllPorts();\n\n if (ports.length === 0) {\n return undefined;\n }\n\n if (ports.length === 1) {\n // Only one port, no need to probe\n return ports[0];\n }\n\n // Probe all ports in parallel\n const probeResults = await Promise.all(\n ports.map(async (port) => ({\n port,\n isWorkflow: await probePort(port, options),\n }))\n );\n\n // Return first port that responded as workflow server\n const workflowPort = probeResults.find((r) => r.isWorkflow);\n if (workflowPort) {\n return workflowPort.port;\n }\n\n // Fallback to first port if probing doesn't identify workflow server\n // This handles cases where:\n // - Server hasn't started workflow routes yet\n // - Network issues during probing\n if (process.env.NODE_ENV === 'development') {\n console.debug(\n '[getWorkflowPort] Probing failed, falling back to first port:',\n ports[0]\n );\n }\n return ports[0];\n}\n"]}
1
+ {"version":3,"file":"get-port.js","sourceRoot":"","sources":["../src/get-port.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile } from 'node:child_process';\nimport { readdir, readFile, readlink } from 'node:fs/promises';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Parses a port string and returns it if valid (0-65535), otherwise undefined.\n */\nfunction parsePort(value: string, radix = 10): number | undefined {\n const port = parseInt(value, radix);\n if (!Number.isNaN(port) && port >= 0 && port <= 65535) {\n return port;\n }\n return undefined;\n}\n\n// NOTE: We build /proc paths dynamically to prevent @vercel/nft from tracing them.\n// NFT's static analysis tries to bundle any file path literal it finds (e.g., '/proc/net/tcp').\n// Since /proc is a virtual Linux filesystem, this causes build failures in @sveltejs/adapter-vercel.\nconst join = (arr: string[], sep: string) => arr.join(sep);\nconst PROC_ROOT = join(['', 'proc'], '/');\n\ninterface LibuvTcpHandle {\n type?: string;\n is_active?: boolean;\n localEndpoint?: {\n port?: number;\n };\n remoteEndpoint?: unknown;\n}\n\nfunction getReportedPorts(): number[] {\n const report = process.report?.getReport?.() as\n | { libuv?: LibuvTcpHandle[] }\n | undefined;\n const handles = report?.libuv;\n\n if (!handles) {\n return [];\n }\n\n const ports: number[] = [];\n const seen = new Set<number>();\n\n for (const handle of handles) {\n if (\n handle.type !== 'tcp' ||\n handle.is_active !== true ||\n handle.remoteEndpoint !== null\n ) {\n continue;\n }\n\n const port = parsePort(String(handle.localEndpoint?.port));\n if (port !== undefined && !seen.has(port)) {\n ports.push(port);\n seen.add(port);\n }\n }\n\n return ports;\n}\n\n/**\n * Gets ALL listening ports for the current process on Linux by reading /proc filesystem.\n * Returns ports in order of file descriptor (deterministic ordering).\n */\nasync function getLinuxPorts(pid: number): Promise<number[]> {\n const listenState = '0A'; // TCP LISTEN state in /proc/net/tcp\n const tcpFiles = [`${PROC_ROOT}/net/tcp`, `${PROC_ROOT}/net/tcp6`] as const;\n\n // Step 1: Get socket inodes from /proc/<pid>/fd/ in order\n // We preserve order to maintain deterministic behavior\n // Use both array (for order) and Set (for O(1) lookup)\n const socketInodes: string[] = [];\n const socketInodesSet = new Set<string>();\n const fdPath = `${PROC_ROOT}/${pid}/fd`;\n\n try {\n const fds = await readdir(fdPath);\n // Sort FDs numerically to ensure deterministic order (FDs are always numeric strings)\n const sortedFds = fds.sort((a, b) => {\n const numA = Number.parseInt(a, 10);\n const numB = Number.parseInt(b, 10);\n return numA - numB;\n });\n\n const results = await Promise.allSettled(\n sortedFds.map(async (fd) => {\n const link = await readlink(`${fdPath}/${fd}`);\n // Socket links look like: socket:[12345]\n const match = link.match(/^socket:\\[(\\d+)\\]$/);\n return match?.[1] ?? null;\n })\n );\n\n for (const result of results) {\n if (result.status === 'fulfilled' && result.value) {\n socketInodes.push(result.value);\n socketInodesSet.add(result.value);\n }\n }\n } catch {\n // Process might not exist or no permission\n return [];\n }\n\n if (socketInodes.length === 0) {\n return [];\n }\n\n // Step 2: Read /proc/net/tcp and /proc/net/tcp6 to find listening sockets\n // Format: sl local_address rem_address st ... inode\n // local_address is hex IP:port, st=0A means LISTEN\n const inodeToPort = new Map<string, number>();\n\n for (const tcpFile of tcpFiles) {\n try {\n const content = await readFile(tcpFile, 'utf8');\n const lines = content.split('\\n').slice(1); // Skip header\n\n for (const line of lines) {\n if (!line.trim()) continue; // Skip empty lines\n\n const parts = line.trim().split(/\\s+/);\n if (parts.length < 10) continue;\n\n const localAddr = parts[1]; // e.g., \"00000000:0BB8\" (0.0.0.0:3000)\n const state = parts[3]; // \"0A\" = LISTEN\n const inode = parts[9];\n\n if (!localAddr || state !== listenState || !inode) continue;\n if (!socketInodesSet.has(inode)) continue;\n\n // Extract port from hex format (e.g., \"0BB8\" -> 3000)\n const colonIndex = localAddr.indexOf(':');\n if (colonIndex === -1) continue;\n\n const portHex = localAddr.slice(colonIndex + 1);\n if (!portHex) continue;\n\n const port = parsePort(portHex, 16);\n if (port !== undefined) {\n inodeToPort.set(inode, port);\n }\n }\n } catch {}\n }\n\n // Return all ports in socket inode order (deterministic)\n const ports: number[] = [];\n for (const inode of socketInodes) {\n const port = inodeToPort.get(inode);\n if (port !== undefined) {\n ports.push(port);\n }\n }\n\n return ports;\n}\n\n/**\n * Gets ALL listening ports for the current process on macOS using lsof.\n * Returns ports in the order they appear in lsof output.\n */\nasync function getDarwinPorts(pid: number): Promise<number[]> {\n try {\n const { stdout } = await execFileAsync('lsof', [\n '-a',\n '-i',\n '-P',\n '-n',\n '-p',\n pid.toString(),\n ]);\n\n const ports: number[] = [];\n const lines = stdout.split('\\n');\n\n for (const line of lines) {\n if (line.includes('LISTEN')) {\n // Column 9 (0-indexed: 8) contains the address like \"*:3000\" or \"127.0.0.1:3000\"\n const parts = line.trim().split(/\\s+/);\n const addr = parts[8];\n if (addr) {\n const colonIndex = addr.lastIndexOf(':');\n if (colonIndex !== -1) {\n const port = parsePort(addr.slice(colonIndex + 1));\n if (port !== undefined) {\n ports.push(port);\n }\n }\n }\n }\n }\n\n return ports;\n } catch {\n return [];\n }\n}\n\n/**\n * Gets ALL listening ports for the current process on Windows using netstat.\n * Returns ports in the order they appear in netstat output.\n */\nasync function getWindowsPorts(pid: number): Promise<number[]> {\n try {\n const { stdout } = await execFileAsync('cmd', [\n '/c',\n `netstat -ano | findstr ${pid} | findstr LISTENING`,\n ]);\n\n const ports: number[] = [];\n const trimmedOutput = stdout.trim();\n\n if (trimmedOutput) {\n const lines = trimmedOutput.split('\\n');\n for (const line of lines) {\n // Extract port from the local address column\n // Matches both IPv4 (e.g., \"127.0.0.1:3000\") and IPv6 bracket notation (e.g., \"[::1]:3000\")\n const match = line\n .trim()\n .match(/^\\s*TCP\\s+(?:\\[[\\da-f:]+\\]|[\\d.]+):(\\d+)\\s+/i);\n if (match) {\n const port = parsePort(match[1]);\n if (port !== undefined) {\n ports.push(port);\n }\n }\n }\n }\n\n return ports;\n } catch {\n return [];\n }\n}\n\n/**\n * Gets all listening ports for the current process.\n * @returns Array of port numbers the process is listening on, in deterministic order.\n */\nexport async function getAllPorts(): Promise<number[]> {\n const { pid, platform } = process;\n\n try {\n const reportedPorts = getReportedPorts();\n if (reportedPorts.length > 0) {\n return reportedPorts;\n }\n\n switch (platform) {\n case 'linux':\n return await getLinuxPorts(pid);\n case 'darwin':\n return await getDarwinPorts(pid);\n case 'win32':\n return await getWindowsPorts(pid);\n default:\n return [];\n }\n } catch (error) {\n if (process.env.NODE_ENV === 'development') {\n console.debug('[getAllPorts] Detection failed:', error);\n }\n return [];\n }\n}\n\n/**\n * Gets the port number that the process is listening on.\n * @returns The port number that the process is listening on, or undefined if the process is not listening on any port.\n */\nexport async function getPort(): Promise<number | undefined> {\n const ports = await getAllPorts();\n return ports[0];\n}\n\n// Configuration for HTTP probing\nconst PROBE_TIMEOUT_MS = 500;\nconst PROBE_ENDPOINT = '/.well-known/workflow/v1/flow?__health';\n\nexport interface ProbeOptions {\n endpoint?: string;\n timeout?: number;\n}\n\n/**\n * Probes a port to check if it's serving the workflow HTTP server.\n * Uses HEAD request to minimize overhead.\n *\n * @returns true if the port responds with a 200 status from the health check endpoint\n */\nasync function probePort(\n port: number,\n options: ProbeOptions = {}\n): Promise<boolean> {\n const { endpoint = PROBE_ENDPOINT, timeout = PROBE_TIMEOUT_MS } = options;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(`http://localhost:${port}${endpoint}`, {\n method: 'HEAD',\n signal: controller.signal,\n });\n\n // The workflow health endpoint returns 200 for healthy\n return response.status === 200;\n } catch {\n // Connection refused, timeout, or other error\n return false;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Gets the workflow server port by probing all listening ports.\n * This is more reliable than getPort() when other services (like Node.js inspector)\n * may also be listening on ports.\n *\n * @param options - Optional configuration for probing\n * @returns The port number of the workflow server, or undefined if not found\n */\nexport async function getWorkflowPort(\n options?: ProbeOptions\n): Promise<number | undefined> {\n const ports = await getAllPorts();\n\n if (ports.length === 0) {\n return undefined;\n }\n\n if (ports.length === 1) {\n // Only one port, no need to probe\n return ports[0];\n }\n\n // Probe all ports in parallel\n const probeResults = await Promise.all(\n ports.map(async (port) => ({\n port,\n isWorkflow: await probePort(port, options),\n }))\n );\n\n // Return first port that responded as workflow server\n const workflowPort = probeResults.find((r) => r.isWorkflow);\n if (workflowPort) {\n return workflowPort.port;\n }\n\n // Fallback to first port if probing doesn't identify workflow server\n // This handles cases where:\n // - Server hasn't started workflow routes yet\n // - Network issues during probing\n if (process.env.NODE_ENV === 'development') {\n console.debug(\n '[getWorkflowPort] Probing failed, falling back to first port:',\n ports[0]\n );\n }\n return ports[0];\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@workflow/utils",
3
3
  "description": "Utility functions for Workflow SDK",
4
- "version": "4.1.1",
4
+ "version": "4.1.2",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "files": [