Local dev: Node http so Host is honored (undici fetch may override Host for http URLs).
(port, hostHeader)
| 93 | |
| 94 | /** Local dev: Node http so Host is honored (undici fetch may override Host for http URLs). */ |
| 95 | function fetchLocalOpenApi(port, hostHeader) { |
| 96 | return new Promise((resolve, reject) => { |
| 97 | const req = http.request( |
| 98 | { |
| 99 | hostname: '127.0.0.1', |
| 100 | port: Number(port), |
| 101 | path: '/2/openapi.json', |
| 102 | method: 'GET', |
| 103 | timeout: 30_000, |
| 104 | headers: { |
| 105 | 'Host': hostHeader, |
| 106 | 'User-Agent': 'FxEmbed-Docs-Builder/1.0' |
| 107 | } |
| 108 | }, |
| 109 | res => { |
| 110 | let data = ''; |
| 111 | res.setEncoding('utf8'); |
| 112 | res.on('data', chunk => { |
| 113 | data += chunk; |
| 114 | }); |
| 115 | res.on('end', () => { |
| 116 | if (res.statusCode !== undefined && res.statusCode >= 200 && res.statusCode < 300) { |
| 117 | try { |
| 118 | resolve(JSON.parse(data)); |
| 119 | } catch (e) { |
| 120 | const msg = e instanceof Error ? e.message : String(e); |
| 121 | reject(new Error(`Invalid JSON: ${msg}`)); |
| 122 | } |
| 123 | } else { |
| 124 | reject(new Error(`HTTP ${res.statusCode}: ${data.slice(0, 200)}`)); |
| 125 | } |
| 126 | }); |
| 127 | } |
| 128 | ); |
| 129 | req.on('timeout', () => { |
| 130 | req.destroy(); |
| 131 | reject(new Error('Request timed out')); |
| 132 | }); |
| 133 | req.on('error', reject); |
| 134 | req.end(); |
| 135 | }); |
| 136 | } |
| 137 | |
| 138 | async function fetchSpec(endpoint, localPort) { |
| 139 | if (localPort) { |