@iann29/synapse 1.8.9 → 1.8.10
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.
- package/lib/commands/https-setup.js +28 -0
- package/lib/https/hosts.js +59 -6
- package/lib/https/planner.js +26 -3
- package/package.json +1 -1
|
@@ -240,6 +240,34 @@ Examples:
|
|
|
240
240
|
process.exitCode = 1;
|
|
241
241
|
return;
|
|
242
242
|
}
|
|
243
|
+
|
|
244
|
+
// v1.8.10: post-execute verification. Even after every step
|
|
245
|
+
// reports ✓ the actual end-state can be wrong — most commonly on
|
|
246
|
+
// Windows where the DNS Client cache holds a negative response
|
|
247
|
+
// even after the hosts file is updated. Re-scan and verify the
|
|
248
|
+
// domain ACTUALLY resolves to 127.0.0.1 (when we wrote hosts).
|
|
249
|
+
// If it doesn't, surface a yellow warning with a concrete
|
|
250
|
+
// remediation instead of pretending the setup is healthy.
|
|
251
|
+
const wroteHosts = results.some(
|
|
252
|
+
(r) => r.id === "hosts" && r.kind === "ok",
|
|
253
|
+
);
|
|
254
|
+
const verifyOk = after.resolution.resolvesToLoopback;
|
|
255
|
+
if (wroteHosts && !verifyOk && !skipHosts) {
|
|
256
|
+
summary.verifyResolution = false;
|
|
257
|
+
summary.verifyHint =
|
|
258
|
+
detection.platform.id === "windows"
|
|
259
|
+
? "Windows DNS Client cache is still serving stale NXDOMAIN for this domain. Run `ipconfig /flushdns` from an Administrator PowerShell, then retry `npm run dev:https`. If it still fails, restart your machine."
|
|
260
|
+
: "Hosts file was written but the OS resolver still doesn't return 127.0.0.1. Try restarting nscd / systemd-resolved if you use one, or open a new shell.";
|
|
261
|
+
if (!ctx.out.json) {
|
|
262
|
+
ctx.out.stdout.write("\n");
|
|
263
|
+
ctx.out.warn(
|
|
264
|
+
`${domain} doesn't resolve to 127.0.0.1 yet — ${summary.verifyHint}`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
} else if (wroteHosts && verifyOk) {
|
|
268
|
+
summary.verifyResolution = true;
|
|
269
|
+
}
|
|
270
|
+
|
|
243
271
|
ctx.out.stdout.write(
|
|
244
272
|
`${colors.green("✓")} ${colors.bold(`HTTPS ready for ${domain}`)}\n`,
|
|
245
273
|
);
|
package/lib/https/hosts.js
CHANGED
|
@@ -178,6 +178,26 @@ function readHosts(hostsPath) {
|
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
// Best-effort DNS cache flush. On Windows the DNS Client (Dnscache)
|
|
182
|
+
// service caches lookups separately from the hosts file — editing
|
|
183
|
+
// /etc/hosts on Windows does NOT invalidate the cache, so `next dev`
|
|
184
|
+
// and Node's dns.lookup() can both return ENOTFOUND despite the
|
|
185
|
+
// entry being written correctly. This bit Matheus's Windows machine
|
|
186
|
+
// in real-world testing (v1.8.10 bug report).
|
|
187
|
+
//
|
|
188
|
+
// `ipconfig /flushdns` is safe to call without admin elevation on
|
|
189
|
+
// every Windows version since at least Windows 7, but we never let a
|
|
190
|
+
// failure here block the wider flow — it's a hint, not a guarantee.
|
|
191
|
+
function flushDnsCacheIfWindows({ execImpl = execFileSync, platform = process.platform } = {}) {
|
|
192
|
+
if (platform !== "win32") return { ran: false, reason: "non-windows platform" };
|
|
193
|
+
try {
|
|
194
|
+
execImpl("ipconfig", ["/flushdns"], { stdio: "ignore", timeout: 5000 });
|
|
195
|
+
return { ran: true };
|
|
196
|
+
} catch (err) {
|
|
197
|
+
return { ran: false, reason: err.message };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
181
201
|
// Writes content to the hosts file. Three strategies are tried in
|
|
182
202
|
// order based on the platform + the `elevation` knob:
|
|
183
203
|
//
|
|
@@ -191,6 +211,13 @@ function readHosts(hostsPath) {
|
|
|
191
211
|
// - "never": only try direct write; error if it fails
|
|
192
212
|
// - "always": skip the direct write attempt
|
|
193
213
|
//
|
|
214
|
+
// On Windows we additionally:
|
|
215
|
+
// - Normalise line endings to CRLF (the Windows hosts file
|
|
216
|
+
// traditionally uses CRLF; some Windows components are tolerant
|
|
217
|
+
// of LF but defensive normalisation costs nothing)
|
|
218
|
+
// - Run `ipconfig /flushdns` after a successful write so the
|
|
219
|
+
// DNS Client cache picks up the new entry immediately
|
|
220
|
+
//
|
|
194
221
|
// `writeImpl` is injected for tests.
|
|
195
222
|
function writeHosts(hostsPath, content, {
|
|
196
223
|
elevation = "auto",
|
|
@@ -198,11 +225,15 @@ function writeHosts(hostsPath, content, {
|
|
|
198
225
|
execImpl = execFileSync,
|
|
199
226
|
platform = process.platform,
|
|
200
227
|
} = {}) {
|
|
228
|
+
const onDiskContent =
|
|
229
|
+
platform === "win32" ? content.replace(/\r?\n/g, "\r\n") : content;
|
|
230
|
+
|
|
201
231
|
// Try direct write first (fast path).
|
|
232
|
+
let result;
|
|
202
233
|
if (elevation !== "always") {
|
|
203
234
|
try {
|
|
204
|
-
writeImpl(hostsPath,
|
|
205
|
-
|
|
235
|
+
writeImpl(hostsPath, onDiskContent);
|
|
236
|
+
result = { method: "direct", elevated: false };
|
|
206
237
|
} catch (err) {
|
|
207
238
|
if (elevation === "never") {
|
|
208
239
|
throw new HostsError(
|
|
@@ -214,10 +245,25 @@ function writeHosts(hostsPath, content, {
|
|
|
214
245
|
}
|
|
215
246
|
}
|
|
216
247
|
|
|
248
|
+
if (!result) {
|
|
249
|
+
if (platform === "win32") {
|
|
250
|
+
result = writeHostsViaRunAs(hostsPath, onDiskContent, { execImpl });
|
|
251
|
+
} else {
|
|
252
|
+
result = writeHostsViaSudo(hostsPath, onDiskContent, { execImpl });
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Post-write DNS cache flush (best-effort, Windows only). The
|
|
257
|
+
// elevated runas path ALREADY flushes inside its PowerShell script
|
|
258
|
+
// — see writeHostsViaRunAs. Calling here covers the direct-write
|
|
259
|
+
// case (operator already in an elevated shell). Doubling up is
|
|
260
|
+
// harmless: a second flush is a no-op.
|
|
217
261
|
if (platform === "win32") {
|
|
218
|
-
|
|
262
|
+
const flush = flushDnsCacheIfWindows({ execImpl, platform });
|
|
263
|
+
result.dnsFlushed = flush.ran;
|
|
264
|
+
if (!flush.ran) result.dnsFlushReason = flush.reason;
|
|
219
265
|
}
|
|
220
|
-
return
|
|
266
|
+
return result;
|
|
221
267
|
}
|
|
222
268
|
|
|
223
269
|
// Linux/macOS elevated write. We pipe the new content into `sudo
|
|
@@ -269,12 +315,18 @@ function writeHostsViaRunAs(hostsPath, content, { execImpl = execFileSync } = {}
|
|
|
269
315
|
const tmpFile = path.join(os.tmpdir(), `synapse-hosts-${Date.now()}.txt`);
|
|
270
316
|
fs.writeFileSync(tmpFile, content);
|
|
271
317
|
const backupPath = `${hostsPath}.synapse-bak.${Date.now()}`;
|
|
272
|
-
// PowerShell script
|
|
273
|
-
// Copy
|
|
318
|
+
// PowerShell script — runs ELEVATED via Start-Process RunAs below.
|
|
319
|
+
// 1. Copy current hosts to backup
|
|
320
|
+
// 2. Move new content over hosts
|
|
321
|
+
// 3. ipconfig /flushdns (v1.8.10): WITHOUT this the Windows DNS
|
|
322
|
+
// Client cache keeps returning NXDOMAIN for the freshly-added
|
|
323
|
+
// entry, breaking `next dev --hostname dev.foo.com` with
|
|
324
|
+
// ENOTFOUND despite the hosts file being correct.
|
|
274
325
|
const script = [
|
|
275
326
|
`try {`,
|
|
276
327
|
` Copy-Item -LiteralPath '${hostsPath}' -Destination '${backupPath}' -ErrorAction Stop;`,
|
|
277
328
|
` Move-Item -LiteralPath '${tmpFile}' -Destination '${hostsPath}' -Force -ErrorAction Stop;`,
|
|
329
|
+
` & ipconfig /flushdns | Out-Null;`,
|
|
278
330
|
` exit 0`,
|
|
279
331
|
`} catch {`,
|
|
280
332
|
` Write-Error $_.Exception.Message;`,
|
|
@@ -345,6 +397,7 @@ module.exports = {
|
|
|
345
397
|
MANAGED_BLOCK_START,
|
|
346
398
|
MANAGED_BLOCK_END,
|
|
347
399
|
hostsPathForOS,
|
|
400
|
+
flushDnsCacheIfWindows,
|
|
348
401
|
planAddEntry,
|
|
349
402
|
planRemoveEntry,
|
|
350
403
|
readHosts,
|
package/lib/https/planner.js
CHANGED
|
@@ -182,6 +182,16 @@ function plan(detection, {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
// ---- Step: hosts file ----------------------------------------------
|
|
185
|
+
// Decision tree:
|
|
186
|
+
// 1. --skip-hosts → skip
|
|
187
|
+
// 2. Resolves via public DNS → skip (any machine works)
|
|
188
|
+
// 3. Hosts has entry + resolution works → skip (idempotent)
|
|
189
|
+
// 4. Hosts has entry + resolution still fails → exec ("DNS cache
|
|
190
|
+
// stale" fix — re-writing triggers ipconfig /flushdns on Windows).
|
|
191
|
+
// This is the v1.8.10 fix for the bug Matheus hit: hosts file
|
|
192
|
+
// had the entry but Windows DNS Client kept returning ENOTFOUND.
|
|
193
|
+
// 5. No entry → exec (write + flush)
|
|
194
|
+
const hostsHasEntry = detection.hosts.matches.some((m) => m.address === "127.0.0.1");
|
|
185
195
|
if (skipHosts) {
|
|
186
196
|
steps.push({
|
|
187
197
|
id: "hosts",
|
|
@@ -202,13 +212,26 @@ function plan(detection, {
|
|
|
202
212
|
skipReason:
|
|
203
213
|
"no hosts edit needed — DNS A record points at loopback (any machine on any OS resolves correctly)",
|
|
204
214
|
});
|
|
205
|
-
} else if (
|
|
215
|
+
} else if (hostsHasEntry && detection.resolution.resolvesToLoopback) {
|
|
206
216
|
steps.push({
|
|
207
217
|
id: "hosts",
|
|
208
218
|
title: "Hosts file entry",
|
|
209
219
|
kind: "skip",
|
|
210
|
-
reason: `${detection.hosts.path} already maps ${detection.domain} to 127.0.0.1`,
|
|
211
|
-
skipReason: "entry present",
|
|
220
|
+
reason: `${detection.hosts.path} already maps ${detection.domain} to 127.0.0.1 and resolution works`,
|
|
221
|
+
skipReason: "entry present + DNS cache fresh",
|
|
222
|
+
});
|
|
223
|
+
} else if (hostsHasEntry && !detection.resolution.resolvesToLoopback) {
|
|
224
|
+
steps.push({
|
|
225
|
+
id: "hosts",
|
|
226
|
+
title: `Refresh DNS cache for ${detection.domain}`,
|
|
227
|
+
kind: "exec",
|
|
228
|
+
reason:
|
|
229
|
+
detection.platform.id === "windows"
|
|
230
|
+
? `${detection.hosts.path} has the entry but Windows DNS cache is stale — re-writing to trigger \`ipconfig /flushdns\``
|
|
231
|
+
: `${detection.hosts.path} has the entry but resolution still fails — re-writing to force a refresh`,
|
|
232
|
+
async run() {
|
|
233
|
+
return hostsMod.addEntry(detection.hosts.path, detection.domain);
|
|
234
|
+
},
|
|
212
235
|
});
|
|
213
236
|
} else {
|
|
214
237
|
const needsElevation = !detection.hosts.writable;
|