clivly 0.10.5 → 0.10.6
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/index.cjs +64 -8
- package/index.mjs +64 -8
- package/package.json +1 -1
package/index.cjs
CHANGED
|
@@ -480,23 +480,38 @@ function cloudflaredMissingMessage() {
|
|
|
480
480
|
* Warning someone away from a tunnel that was fine is worse than staying quiet,
|
|
481
481
|
* for the same reason the port probe defaults to assuming the port is alive.
|
|
482
482
|
*/
|
|
483
|
-
/** How long
|
|
483
|
+
/** How long any single probe attempt may take. */
|
|
484
484
|
const IDENTITY_PROBE_TIMEOUT_MS = 8e3;
|
|
485
485
|
/**
|
|
486
|
-
*
|
|
486
|
+
* How long to keep retrying before giving up.
|
|
487
487
|
*
|
|
488
|
-
*
|
|
488
|
+
* The probe fires the moment `cloudflared` publishes a URL, which on a
|
|
489
|
+
* Vite/Nuxt app is *before* the dev server has finished building its route
|
|
490
|
+
* table. The first attempt therefore reaches a server that is up but does not
|
|
491
|
+
* yet serve the tick route — a cold start, not a wrong port. The condition
|
|
492
|
+
* being detected (a tunnel aimed at the wrong process) does not resolve
|
|
493
|
+
* itself, so waiting costs nothing and a premature verdict costs a lot.
|
|
489
494
|
*/
|
|
490
|
-
|
|
495
|
+
const IDENTITY_RETRY_BUDGET_MS = 2e4;
|
|
496
|
+
const IDENTITY_RETRY_DELAY_MS = 2e3;
|
|
497
|
+
const sleep = (ms) => new Promise((resolve) => {
|
|
498
|
+
setTimeout(resolve, ms);
|
|
499
|
+
});
|
|
500
|
+
/** One attempt. Separated so the retry loop stays readable. */
|
|
501
|
+
async function attempt(triggerUrl, fetchImpl) {
|
|
491
502
|
const controller = new AbortController();
|
|
492
503
|
const timer = setTimeout(() => controller.abort(), IDENTITY_PROBE_TIMEOUT_MS);
|
|
493
504
|
try {
|
|
494
|
-
|
|
505
|
+
const response = await fetchImpl(triggerUrl, {
|
|
495
506
|
method: "POST",
|
|
496
507
|
headers: { "content-type": "application/json" },
|
|
497
508
|
body: "{}",
|
|
498
509
|
signal: controller.signal
|
|
499
|
-
})
|
|
510
|
+
});
|
|
511
|
+
if (response.status === 401) return "clivly";
|
|
512
|
+
if (response.status === 403) return "host-rejected";
|
|
513
|
+
if (response.status === 404 || response.status >= 500) return "unknown";
|
|
514
|
+
return "mismatch";
|
|
500
515
|
} catch {
|
|
501
516
|
return "unknown";
|
|
502
517
|
} finally {
|
|
@@ -504,6 +519,40 @@ async function probeTunnelIdentity(triggerUrl, fetchImpl = fetch) {
|
|
|
504
519
|
}
|
|
505
520
|
}
|
|
506
521
|
/**
|
|
522
|
+
* Probe the published tunnel URL and report who is on the other end.
|
|
523
|
+
*
|
|
524
|
+
* Retries while the answer is inconclusive, because the first attempt lands
|
|
525
|
+
* during the dev server's boot. Only a confident contradiction is reported.
|
|
526
|
+
*
|
|
527
|
+
* `fetchImpl` and `now` are injectable so tests need no network or clock.
|
|
528
|
+
*/
|
|
529
|
+
async function probeTunnelIdentity(triggerUrl, fetchImpl = fetch, now = Date.now) {
|
|
530
|
+
const deadline = now() + IDENTITY_RETRY_BUDGET_MS;
|
|
531
|
+
let last = "unknown";
|
|
532
|
+
do {
|
|
533
|
+
last = await attempt(triggerUrl, fetchImpl);
|
|
534
|
+
if (last !== "unknown") return last;
|
|
535
|
+
if (now() >= deadline) break;
|
|
536
|
+
await sleep(IDENTITY_RETRY_DELAY_MS);
|
|
537
|
+
} while (now() < deadline);
|
|
538
|
+
return last;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* What to tell someone whose dev server refused the tunnel's Host header.
|
|
542
|
+
*
|
|
543
|
+
* `clivly dev` already prints the allowedHosts hint for Vite frameworks before
|
|
544
|
+
* the tunnel opens; this fires when the tunnel proves it was not applied.
|
|
545
|
+
*/
|
|
546
|
+
function hostRejectedMessage() {
|
|
547
|
+
return [
|
|
548
|
+
"Warning: your dev server refused the tunnel's Host header (403).",
|
|
549
|
+
" Vite rejects hostnames it does not recognise, so Clivly's request never",
|
|
550
|
+
" reaches your tick route. Add the tunnel domain to your Vite config:",
|
|
551
|
+
" server: { allowedHosts: ['.trycloudflare.com'] }",
|
|
552
|
+
" (in nuxt.config.ts, nest it under `vite:`), then restart the dev server."
|
|
553
|
+
].join("\n");
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
507
556
|
* What to tell someone whose tunnel reaches the wrong process.
|
|
508
557
|
*
|
|
509
558
|
* Names the likely cause rather than only the symptom: the overwhelmingly
|
|
@@ -526,6 +575,12 @@ const UNREACHABLE_RESULT = {
|
|
|
526
575
|
ok: false,
|
|
527
576
|
kind: "unreachable"
|
|
528
577
|
};
|
|
578
|
+
/** The line to print for a probe verdict, or null when there is nothing to say. */
|
|
579
|
+
function tunnelWarning(identity, port) {
|
|
580
|
+
if (identity === "mismatch") return tunnelMismatchMessage(port);
|
|
581
|
+
if (identity === "host-rejected") return hostRejectedMessage();
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
529
584
|
function flagValue$1(argv, flag) {
|
|
530
585
|
const i = argv.indexOf(flag);
|
|
531
586
|
return i >= 0 ? argv[i + 1] : void 0;
|
|
@@ -570,9 +625,10 @@ async function runDev(options) {
|
|
|
570
625
|
if (!await deps.isPortListening(port).catch(() => true)) deps.out(deadPortMessage(port, framework));
|
|
571
626
|
}
|
|
572
627
|
const identity = deps.probeTunnel && !flagValue$1(options.argv, "--url") ? await deps.probeTunnel(triggerUrl).catch(() => "unknown") : "unknown";
|
|
573
|
-
|
|
628
|
+
const warning = tunnelWarning(identity, port);
|
|
629
|
+
if (warning) deps.out(warning);
|
|
574
630
|
const result = await deps.register(triggerUrl).catch(() => UNREACHABLE_RESULT);
|
|
575
|
-
if (result.ok) deps.out(identity === "mismatch" ? "Trigger URL registered, but see the warning above before relying on it." : "Dashboard-triggered sync now points at this local session.");
|
|
631
|
+
if (result.ok) deps.out(identity === "mismatch" || identity === "host-rejected" ? "Trigger URL registered, but see the warning above before relying on it." : "Dashboard-triggered sync now points at this local session.");
|
|
576
632
|
else deps.out(overrideFailureMessage(result.kind, result.status));
|
|
577
633
|
});
|
|
578
634
|
const stopWatching = deps.watchConfig?.(options.cwd, () => {
|
package/index.mjs
CHANGED
|
@@ -479,23 +479,38 @@ function cloudflaredMissingMessage() {
|
|
|
479
479
|
* Warning someone away from a tunnel that was fine is worse than staying quiet,
|
|
480
480
|
* for the same reason the port probe defaults to assuming the port is alive.
|
|
481
481
|
*/
|
|
482
|
-
/** How long
|
|
482
|
+
/** How long any single probe attempt may take. */
|
|
483
483
|
const IDENTITY_PROBE_TIMEOUT_MS = 8e3;
|
|
484
484
|
/**
|
|
485
|
-
*
|
|
485
|
+
* How long to keep retrying before giving up.
|
|
486
486
|
*
|
|
487
|
-
*
|
|
487
|
+
* The probe fires the moment `cloudflared` publishes a URL, which on a
|
|
488
|
+
* Vite/Nuxt app is *before* the dev server has finished building its route
|
|
489
|
+
* table. The first attempt therefore reaches a server that is up but does not
|
|
490
|
+
* yet serve the tick route — a cold start, not a wrong port. The condition
|
|
491
|
+
* being detected (a tunnel aimed at the wrong process) does not resolve
|
|
492
|
+
* itself, so waiting costs nothing and a premature verdict costs a lot.
|
|
488
493
|
*/
|
|
489
|
-
|
|
494
|
+
const IDENTITY_RETRY_BUDGET_MS = 2e4;
|
|
495
|
+
const IDENTITY_RETRY_DELAY_MS = 2e3;
|
|
496
|
+
const sleep = (ms) => new Promise((resolve) => {
|
|
497
|
+
setTimeout(resolve, ms);
|
|
498
|
+
});
|
|
499
|
+
/** One attempt. Separated so the retry loop stays readable. */
|
|
500
|
+
async function attempt(triggerUrl, fetchImpl) {
|
|
490
501
|
const controller = new AbortController();
|
|
491
502
|
const timer = setTimeout(() => controller.abort(), IDENTITY_PROBE_TIMEOUT_MS);
|
|
492
503
|
try {
|
|
493
|
-
|
|
504
|
+
const response = await fetchImpl(triggerUrl, {
|
|
494
505
|
method: "POST",
|
|
495
506
|
headers: { "content-type": "application/json" },
|
|
496
507
|
body: "{}",
|
|
497
508
|
signal: controller.signal
|
|
498
|
-
})
|
|
509
|
+
});
|
|
510
|
+
if (response.status === 401) return "clivly";
|
|
511
|
+
if (response.status === 403) return "host-rejected";
|
|
512
|
+
if (response.status === 404 || response.status >= 500) return "unknown";
|
|
513
|
+
return "mismatch";
|
|
499
514
|
} catch {
|
|
500
515
|
return "unknown";
|
|
501
516
|
} finally {
|
|
@@ -503,6 +518,40 @@ async function probeTunnelIdentity(triggerUrl, fetchImpl = fetch) {
|
|
|
503
518
|
}
|
|
504
519
|
}
|
|
505
520
|
/**
|
|
521
|
+
* Probe the published tunnel URL and report who is on the other end.
|
|
522
|
+
*
|
|
523
|
+
* Retries while the answer is inconclusive, because the first attempt lands
|
|
524
|
+
* during the dev server's boot. Only a confident contradiction is reported.
|
|
525
|
+
*
|
|
526
|
+
* `fetchImpl` and `now` are injectable so tests need no network or clock.
|
|
527
|
+
*/
|
|
528
|
+
async function probeTunnelIdentity(triggerUrl, fetchImpl = fetch, now = Date.now) {
|
|
529
|
+
const deadline = now() + IDENTITY_RETRY_BUDGET_MS;
|
|
530
|
+
let last = "unknown";
|
|
531
|
+
do {
|
|
532
|
+
last = await attempt(triggerUrl, fetchImpl);
|
|
533
|
+
if (last !== "unknown") return last;
|
|
534
|
+
if (now() >= deadline) break;
|
|
535
|
+
await sleep(IDENTITY_RETRY_DELAY_MS);
|
|
536
|
+
} while (now() < deadline);
|
|
537
|
+
return last;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* What to tell someone whose dev server refused the tunnel's Host header.
|
|
541
|
+
*
|
|
542
|
+
* `clivly dev` already prints the allowedHosts hint for Vite frameworks before
|
|
543
|
+
* the tunnel opens; this fires when the tunnel proves it was not applied.
|
|
544
|
+
*/
|
|
545
|
+
function hostRejectedMessage() {
|
|
546
|
+
return [
|
|
547
|
+
"Warning: your dev server refused the tunnel's Host header (403).",
|
|
548
|
+
" Vite rejects hostnames it does not recognise, so Clivly's request never",
|
|
549
|
+
" reaches your tick route. Add the tunnel domain to your Vite config:",
|
|
550
|
+
" server: { allowedHosts: ['.trycloudflare.com'] }",
|
|
551
|
+
" (in nuxt.config.ts, nest it under `vite:`), then restart the dev server."
|
|
552
|
+
].join("\n");
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
506
555
|
* What to tell someone whose tunnel reaches the wrong process.
|
|
507
556
|
*
|
|
508
557
|
* Names the likely cause rather than only the symptom: the overwhelmingly
|
|
@@ -525,6 +574,12 @@ const UNREACHABLE_RESULT = {
|
|
|
525
574
|
ok: false,
|
|
526
575
|
kind: "unreachable"
|
|
527
576
|
};
|
|
577
|
+
/** The line to print for a probe verdict, or null when there is nothing to say. */
|
|
578
|
+
function tunnelWarning(identity, port) {
|
|
579
|
+
if (identity === "mismatch") return tunnelMismatchMessage(port);
|
|
580
|
+
if (identity === "host-rejected") return hostRejectedMessage();
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
528
583
|
function flagValue$1(argv, flag) {
|
|
529
584
|
const i = argv.indexOf(flag);
|
|
530
585
|
return i >= 0 ? argv[i + 1] : void 0;
|
|
@@ -569,9 +624,10 @@ async function runDev(options) {
|
|
|
569
624
|
if (!await deps.isPortListening(port).catch(() => true)) deps.out(deadPortMessage(port, framework));
|
|
570
625
|
}
|
|
571
626
|
const identity = deps.probeTunnel && !flagValue$1(options.argv, "--url") ? await deps.probeTunnel(triggerUrl).catch(() => "unknown") : "unknown";
|
|
572
|
-
|
|
627
|
+
const warning = tunnelWarning(identity, port);
|
|
628
|
+
if (warning) deps.out(warning);
|
|
573
629
|
const result = await deps.register(triggerUrl).catch(() => UNREACHABLE_RESULT);
|
|
574
|
-
if (result.ok) deps.out(identity === "mismatch" ? "Trigger URL registered, but see the warning above before relying on it." : "Dashboard-triggered sync now points at this local session.");
|
|
630
|
+
if (result.ok) deps.out(identity === "mismatch" || identity === "host-rejected" ? "Trigger URL registered, but see the warning above before relying on it." : "Dashboard-triggered sync now points at this local session.");
|
|
575
631
|
else deps.out(overrideFailureMessage(result.kind, result.status));
|
|
576
632
|
});
|
|
577
633
|
const stopWatching = deps.watchConfig?.(options.cwd, () => {
|