unplugin-cloudflare-tunnel 0.0.0-alpha-1 → 0.0.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.
- package/dist/api.d.mts +50 -0
- package/dist/api.mjs +59 -0
- package/dist/{esbuild.d.ts → esbuild.d.mts} +1 -1
- package/dist/{esbuild.js → esbuild.mjs} +6 -1
- package/dist/{farm.d.ts → farm.d.mts} +1 -1
- package/dist/{farm.js → farm.mjs} +6 -1
- package/dist/{index-BjNI6nQt.d.ts → index.d.mts} +45 -35
- package/dist/{src-BC4MyCER.js → index.mjs} +393 -104
- package/dist/{rolldown.d.ts → rolldown.d.mts} +1 -1
- package/dist/{rolldown.js → rolldown.mjs} +6 -1
- package/dist/{rollup.d.ts → rollup.d.mts} +1 -1
- package/dist/{rollup.js → rollup.mjs} +6 -1
- package/dist/{rspack.d.ts → rspack.d.mts} +1 -1
- package/dist/{rspack.js → rspack.mjs} +6 -1
- package/dist/schemas-CwcXCIyR.mjs +941 -0
- package/dist/{vite.d.ts → vite.d.mts} +1 -1
- package/dist/{vite.js → vite.mjs} +6 -1
- package/dist/{webpack.d.ts → webpack.d.mts} +1 -1
- package/dist/{webpack.js → webpack.mjs} +6 -1
- package/package.json +48 -44
- package/dist/api.d.ts +0 -1
- package/dist/api.js +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -3
- /package/dist/{astro.d.ts → astro.d.mts} +0 -0
- /package/dist/{astro.js → astro.mjs} +0 -0
- /package/dist/{virtual.d.ts → virtual.d.mts} +0 -0
|
@@ -1,56 +1,104 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
|
-
import { exec, spawn } from "node:child_process";
|
|
4
|
-
import { z } from "zod";
|
|
1
|
+
import { a as number, c as string, i as nullish, l as unknown, n as array, o as object, r as boolean, s as optional, t as any } from "./schemas-CwcXCIyR.mjs";
|
|
5
2
|
import { createUnplugin } from "unplugin";
|
|
3
|
+
import NodeFS from "node:fs/promises";
|
|
4
|
+
import { bin, install } from "cloudflared";
|
|
5
|
+
import * as NodeChildProcess from "node:child_process";
|
|
6
6
|
|
|
7
7
|
//#region src/index.ts
|
|
8
|
+
/**
|
|
9
|
+
* @fileoverview Cloudflare Tunnel Unplugin
|
|
10
|
+
*
|
|
11
|
+
* A cross-bundler plugin that automatically creates and manages
|
|
12
|
+
* Cloudflare tunnels for local development, providing instant HTTPS access
|
|
13
|
+
* to your local dev server from anywhere on the internet.
|
|
14
|
+
*
|
|
15
|
+
* @author Cloudflare Tunnel Plugin Contributors
|
|
16
|
+
* @version 1.0.0
|
|
17
|
+
* @license MIT
|
|
18
|
+
*/
|
|
19
|
+
const PLUGIN_NAME = "unplugin-cloudflare-tunnel";
|
|
8
20
|
const INFO_LOG_REGEX = /^.*Z INF .*/;
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
21
|
+
const LOG_LEVEL_RANK = {
|
|
22
|
+
debug: 10,
|
|
23
|
+
info: 20,
|
|
24
|
+
warn: 30,
|
|
25
|
+
error: 40,
|
|
26
|
+
fatal: 50
|
|
27
|
+
};
|
|
28
|
+
function shouldLog(threshold, level) {
|
|
29
|
+
return LOG_LEVEL_RANK[level] >= LOG_LEVEL_RANK[threshold];
|
|
30
|
+
}
|
|
31
|
+
function supportsColor() {
|
|
32
|
+
if (!process.stdout.isTTY) return false;
|
|
33
|
+
if (process.env.NO_COLOR !== void 0) return false;
|
|
34
|
+
if (process.env.TERM === "dumb") return false;
|
|
35
|
+
if (process.env.FORCE_COLOR === "0") return false;
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
const ANSI = {
|
|
39
|
+
reset: "\x1B[0m",
|
|
40
|
+
dim: "\x1B[2m",
|
|
41
|
+
bold: "\x1B[1m",
|
|
42
|
+
blue: "\x1B[34m",
|
|
43
|
+
yellow: "\x1B[33m"
|
|
44
|
+
};
|
|
45
|
+
const ANSI_ESCAPE = String.fromCharCode(27);
|
|
46
|
+
const ANSI_STYLE_SEQUENCE_REGEX = new RegExp(`${ANSI_ESCAPE}\\[[0-9;]*m`, "g");
|
|
47
|
+
function stripAnsi(text) {
|
|
48
|
+
return text.replace(ANSI_STYLE_SEQUENCE_REGEX, "");
|
|
49
|
+
}
|
|
50
|
+
function colorize(text, ansi) {
|
|
51
|
+
if (!supportsColor()) return text;
|
|
52
|
+
return `${ansi}${text}${ANSI.reset}`;
|
|
53
|
+
}
|
|
54
|
+
const CloudflareErrorSchema = object({
|
|
55
|
+
code: number(),
|
|
56
|
+
message: string()
|
|
12
57
|
});
|
|
13
|
-
const CloudflareApiResponseSchema =
|
|
14
|
-
success:
|
|
15
|
-
errors:
|
|
16
|
-
messages:
|
|
17
|
-
result:
|
|
58
|
+
const CloudflareApiResponseSchema = object({
|
|
59
|
+
success: boolean(),
|
|
60
|
+
errors: optional(array(CloudflareErrorSchema)),
|
|
61
|
+
messages: optional(array(string())),
|
|
62
|
+
result: unknown()
|
|
18
63
|
});
|
|
19
|
-
const AccountSchema =
|
|
20
|
-
id:
|
|
21
|
-
name:
|
|
64
|
+
const AccountSchema = object({
|
|
65
|
+
id: string(),
|
|
66
|
+
name: string()
|
|
22
67
|
});
|
|
23
|
-
const ZoneSchema =
|
|
24
|
-
id:
|
|
25
|
-
name:
|
|
68
|
+
const ZoneSchema = object({
|
|
69
|
+
id: string(),
|
|
70
|
+
name: string()
|
|
26
71
|
});
|
|
27
|
-
const TunnelSchema =
|
|
28
|
-
id:
|
|
29
|
-
name:
|
|
30
|
-
account_tag:
|
|
31
|
-
created_at:
|
|
32
|
-
connections:
|
|
72
|
+
const TunnelSchema = object({
|
|
73
|
+
id: string(),
|
|
74
|
+
name: string(),
|
|
75
|
+
account_tag: string(),
|
|
76
|
+
created_at: string(),
|
|
77
|
+
connections: optional(array(unknown()))
|
|
33
78
|
});
|
|
34
|
-
const DNSRecordSchema =
|
|
35
|
-
id:
|
|
36
|
-
type:
|
|
37
|
-
name:
|
|
38
|
-
content:
|
|
39
|
-
proxied:
|
|
40
|
-
comment:
|
|
79
|
+
const DNSRecordSchema = object({
|
|
80
|
+
id: string(),
|
|
81
|
+
type: string(),
|
|
82
|
+
name: string(),
|
|
83
|
+
content: string(),
|
|
84
|
+
proxied: boolean(),
|
|
85
|
+
comment: nullish(string())
|
|
41
86
|
});
|
|
42
87
|
const unpluginFactory = (options = {}) => {
|
|
43
88
|
const { enabled = true } = options;
|
|
44
89
|
if (enabled === false) {
|
|
45
90
|
const VIRTUAL_MODULE_ID_STUB = "virtual:unplugin-cloudflare-tunnel";
|
|
46
91
|
return {
|
|
47
|
-
name:
|
|
92
|
+
name: PLUGIN_NAME,
|
|
48
93
|
enforce: "pre",
|
|
49
94
|
resolveId(id) {
|
|
50
|
-
if (id === VIRTUAL_MODULE_ID_STUB) return
|
|
95
|
+
if (id === VIRTUAL_MODULE_ID_STUB) return id;
|
|
96
|
+
},
|
|
97
|
+
loadInclude(id) {
|
|
98
|
+
return id === VIRTUAL_MODULE_ID_STUB;
|
|
51
99
|
},
|
|
52
100
|
load(id) {
|
|
53
|
-
if (id ===
|
|
101
|
+
if (id === VIRTUAL_MODULE_ID_STUB) return "export function getTunnelUrl() { return \"\"; }";
|
|
54
102
|
}
|
|
55
103
|
};
|
|
56
104
|
}
|
|
@@ -95,8 +143,79 @@ const unpluginFactory = (options = {}) => {
|
|
|
95
143
|
cleanupConfig = namedOptions.cleanup || {};
|
|
96
144
|
}
|
|
97
145
|
const { port: userProvidedPort, logFile, logLevel, debug = false } = options;
|
|
98
|
-
const
|
|
99
|
-
|
|
146
|
+
const effectivePluginLogLevel = logLevel ?? (debug ? "debug" : "info");
|
|
147
|
+
const pluginLog = {
|
|
148
|
+
debug: (...args) => {
|
|
149
|
+
if (debug || effectivePluginLogLevel === "debug") console.log("[cloudflare-tunnel:debug]", ...args);
|
|
150
|
+
},
|
|
151
|
+
info: (message) => {
|
|
152
|
+
if (shouldLog(effectivePluginLogLevel, "info")) console.log(`[unplugin-cloudflare-tunnel] ${message}`);
|
|
153
|
+
},
|
|
154
|
+
warn: (message) => {
|
|
155
|
+
if (shouldLog(effectivePluginLogLevel, "warn")) console.warn(`[unplugin-cloudflare-tunnel] ${message}`);
|
|
156
|
+
},
|
|
157
|
+
error: (message) => {
|
|
158
|
+
if (shouldLog(effectivePluginLogLevel, "error")) console.error(`[unplugin-cloudflare-tunnel] ${message}`);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
const debugLog = pluginLog.debug;
|
|
162
|
+
const makeLocalDisplay = (localTarget) => {
|
|
163
|
+
if (!localTarget) return localTarget;
|
|
164
|
+
return localTarget.replace("http://[::1]:", "http://localhost:").replace("http://127.0.0.1:", "http://localhost:");
|
|
165
|
+
};
|
|
166
|
+
const announceConnecting = () => {
|
|
167
|
+
if (globalState.__tunnelConnectingAnnounced) return;
|
|
168
|
+
globalState.__tunnelConnectingAnnounced = true;
|
|
169
|
+
const message = isQuickMode ? "cf tunnel connecting…" : hostname ? `cf tunnel connecting… (${hostname})` : "cf tunnel connecting…";
|
|
170
|
+
console.log("");
|
|
171
|
+
console.log(colorize(message, ANSI.bold));
|
|
172
|
+
};
|
|
173
|
+
const announceTunnel = (params) => {
|
|
174
|
+
if (!params.url) return;
|
|
175
|
+
if (globalState.__lastAnnouncedTunnelKey === params.key) return;
|
|
176
|
+
globalState.__lastAnnouncedTunnelKey = params.key;
|
|
177
|
+
const cols = process.stdout.columns ?? 80;
|
|
178
|
+
const maxWidth = Math.max(10, cols - 2);
|
|
179
|
+
const headerText = "unplugin-cloudflare-tunnel";
|
|
180
|
+
const header = (() => {
|
|
181
|
+
return `${colorize("[", ANSI.yellow)}${headerText}${colorize("]", ANSI.yellow)}`;
|
|
182
|
+
})();
|
|
183
|
+
const urlLine = colorize(params.url, ANSI.blue + ANSI.bold);
|
|
184
|
+
const localLine = params.localTarget ? makeLocalDisplay(params.localTarget) : "";
|
|
185
|
+
const headerPlainLen = stripAnsi(header).length;
|
|
186
|
+
const contentPlainLen = Math.max(stripAnsi(urlLine).length, localLine.length, 10, 5);
|
|
187
|
+
const width = Math.min(90, maxWidth, Math.max(44, headerPlainLen, contentPlainLen + 4));
|
|
188
|
+
const rule = "─".repeat(width);
|
|
189
|
+
const center = (text) => {
|
|
190
|
+
const plainLen = stripAnsi(text).length;
|
|
191
|
+
const pad = Math.max(0, Math.floor((width - plainLen) / 2));
|
|
192
|
+
return `${" ".repeat(pad)}${text}`;
|
|
193
|
+
};
|
|
194
|
+
if (cols < 70) {
|
|
195
|
+
const out$1 = [];
|
|
196
|
+
out$1.push("");
|
|
197
|
+
out$1.push(`${header} ${colorize("Tunnel URL", ANSI.bold)} ${urlLine}`);
|
|
198
|
+
if (localLine) out$1.push(`${header} ${colorize("Local", ANSI.dim + ANSI.bold)} ${localLine}`);
|
|
199
|
+
out$1.push("");
|
|
200
|
+
console.log(out$1.join("\n"));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const out = [];
|
|
204
|
+
out.push("");
|
|
205
|
+
out.push(center(header));
|
|
206
|
+
out.push(rule);
|
|
207
|
+
out.push(center(colorize("Tunnel URL", ANSI.bold)));
|
|
208
|
+
out.push(center(urlLine));
|
|
209
|
+
out.push(center(urlLine));
|
|
210
|
+
out.push(center(urlLine));
|
|
211
|
+
if (localLine) {
|
|
212
|
+
out.push("");
|
|
213
|
+
out.push(center(colorize("Local", ANSI.dim + ANSI.bold)));
|
|
214
|
+
out.push(center(localLine));
|
|
215
|
+
}
|
|
216
|
+
out.push(rule);
|
|
217
|
+
out.push("");
|
|
218
|
+
console.log(out.join("\n"));
|
|
100
219
|
};
|
|
101
220
|
if (!isQuickMode && (!hostname || typeof hostname !== "string")) throw new Error("[unplugin-cloudflare-tunnel] hostname is required and must be a valid string in named tunnel mode");
|
|
102
221
|
let tunnelUrl = hostname ? `https://${hostname}` : "";
|
|
@@ -130,7 +249,7 @@ const unpluginFactory = (options = {}) => {
|
|
|
130
249
|
};
|
|
131
250
|
const findMismatchedSslCertificates = async (apiToken, zoneId, currentTunnelName, currentHostname) => {
|
|
132
251
|
try {
|
|
133
|
-
const certPacks = await cf(apiToken, "GET", `/zones/${zoneId}/ssl/certificate_packs?status=all`, void 0,
|
|
252
|
+
const certPacks = await cf(apiToken, "GET", `/zones/${zoneId}/ssl/certificate_packs?status=all`, void 0, any());
|
|
134
253
|
const currentTunnelCerts = (Array.isArray(certPacks) ? certPacks : certPacks.result || []).filter((cert) => {
|
|
135
254
|
return (cert.hostnames || cert.hosts || []).some((host) => host.startsWith(`cf-tunnel-plugin-${currentTunnelName}--`));
|
|
136
255
|
});
|
|
@@ -154,7 +273,7 @@ const unpluginFactory = (options = {}) => {
|
|
|
154
273
|
};
|
|
155
274
|
const cleanupMismatchedDnsRecords = async (apiToken, zoneId, dnsComment, currentHostname, tunnelId) => {
|
|
156
275
|
try {
|
|
157
|
-
const pluginDnsRecords = await cf(apiToken, "GET", `/zones/${zoneId}/dns_records?comment=${dnsComment}&match=all`, void 0,
|
|
276
|
+
const pluginDnsRecords = await cf(apiToken, "GET", `/zones/${zoneId}/dns_records?comment=${dnsComment}&match=all`, void 0, array(DNSRecordSchema));
|
|
158
277
|
debugLog(`Found ${pluginDnsRecords.length} DNS records for current tunnel: ${dnsComment}`);
|
|
159
278
|
const expectedCnameContent = `${tunnelId}.cfargotunnel.com`;
|
|
160
279
|
const mismatchedRecords = pluginDnsRecords.filter((record) => {
|
|
@@ -252,7 +371,7 @@ const unpluginFactory = (options = {}) => {
|
|
|
252
371
|
if (logFile) cloudflaredArgs.push("--logfile", logFile);
|
|
253
372
|
cloudflaredArgs.push("--url", localTarget);
|
|
254
373
|
debugLog("Spawning quick tunnel:", bin, cloudflaredArgs);
|
|
255
|
-
const child$1 = spawn(bin, cloudflaredArgs, {
|
|
374
|
+
const child$1 = NodeChildProcess.spawn(bin, cloudflaredArgs, {
|
|
256
375
|
stdio: [
|
|
257
376
|
"ignore",
|
|
258
377
|
"pipe",
|
|
@@ -262,7 +381,7 @@ const unpluginFactory = (options = {}) => {
|
|
|
262
381
|
windowsHide: true,
|
|
263
382
|
shell: process.platform === "win32"
|
|
264
383
|
});
|
|
265
|
-
|
|
384
|
+
debugLog(`[unplugin-cloudflare-tunnel] Quick tunnel process spawned with PID: ${child$1.pid}`);
|
|
266
385
|
return new Promise((resolve, reject) => {
|
|
267
386
|
let urlFound = false;
|
|
268
387
|
const timeout = setTimeout(() => {
|
|
@@ -310,17 +429,17 @@ const unpluginFactory = (options = {}) => {
|
|
|
310
429
|
globalState.shuttingDown = true;
|
|
311
430
|
globalState.tunnelUrl = void 0;
|
|
312
431
|
try {
|
|
313
|
-
|
|
314
|
-
if (!child.kill(signal) && process.platform === "win32") exec(`taskkill /pid ${child.pid} /T /F`, () => {});
|
|
432
|
+
debugLog(`[unplugin-cloudflare-tunnel] Terminating cloudflared process (PID: ${child.pid}) with ${signal}...`);
|
|
433
|
+
if (!child.kill(signal) && process.platform === "win32") NodeChildProcess.exec(`taskkill /pid ${child.pid} /T /F`, () => {});
|
|
315
434
|
if (signal === "SIGTERM") setTimeout(() => {
|
|
316
435
|
if (child && !child.killed) {
|
|
317
|
-
|
|
318
|
-
if (process.platform === "win32") exec(`taskkill /pid ${child.pid} /T /F`, () => {});
|
|
436
|
+
debugLog("[unplugin-cloudflare-tunnel] Force killing cloudflared process...");
|
|
437
|
+
if (process.platform === "win32") NodeChildProcess.exec(`taskkill /pid ${child.pid} /T /F`, () => {});
|
|
319
438
|
else child.kill("SIGKILL");
|
|
320
439
|
}
|
|
321
440
|
}, 2e3);
|
|
322
441
|
} catch (error) {
|
|
323
|
-
|
|
442
|
+
debugLog(`[unplugin-cloudflare-tunnel] Note: Error killing cloudflared: ${error}`);
|
|
324
443
|
}
|
|
325
444
|
};
|
|
326
445
|
let exitHandlersRegistered = globalState.exitHandlersRegistered ?? false;
|
|
@@ -359,9 +478,27 @@ const unpluginFactory = (options = {}) => {
|
|
|
359
478
|
const generateDnsComment = () => {
|
|
360
479
|
return `unplugin-cloudflare-tunnel:${tunnelName}`;
|
|
361
480
|
};
|
|
481
|
+
const registerListeningHandler = (handler) => {
|
|
482
|
+
const httpServer = server.httpServer;
|
|
483
|
+
if (!httpServer) return;
|
|
484
|
+
const invokeHandler = () => {
|
|
485
|
+
try {
|
|
486
|
+
const maybePromise = handler();
|
|
487
|
+
if (maybePromise && typeof maybePromise.then === "function") maybePromise.catch((error) => {
|
|
488
|
+
console.error(`[unplugin-cloudflare-tunnel] ❌ Dev server listening hook failed: ${error.message}`);
|
|
489
|
+
});
|
|
490
|
+
} catch (error) {
|
|
491
|
+
console.error(`[unplugin-cloudflare-tunnel] ❌ Dev server listening hook failed: ${error.message}`);
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
httpServer.on("listening", invokeHandler);
|
|
495
|
+
if (httpServer.listening) invokeHandler();
|
|
496
|
+
};
|
|
362
497
|
try {
|
|
363
498
|
const { host: serverHost, port: detectedPort } = normalizeAddress(server.httpServer?.address());
|
|
364
|
-
const
|
|
499
|
+
const configPortValue = server.config?.server?.port;
|
|
500
|
+
const resolvedConfigPort = typeof configPortValue === "string" ? Number.parseInt(configPortValue, 10) : configPortValue;
|
|
501
|
+
const port = userProvidedPort || detectedPort || (typeof resolvedConfigPort === "number" && !Number.isNaN(resolvedConfigPort) ? resolvedConfigPort : void 0) || 5173;
|
|
365
502
|
const newConfigHash = JSON.stringify({
|
|
366
503
|
isQuickMode,
|
|
367
504
|
hostname,
|
|
@@ -372,13 +509,13 @@ const unpluginFactory = (options = {}) => {
|
|
|
372
509
|
});
|
|
373
510
|
if (globalState.child && !globalState.child.killed && globalState.configHash === newConfigHash) {
|
|
374
511
|
tunnelUrl = await globalState.tunnelUrl ?? "";
|
|
375
|
-
|
|
512
|
+
debugLog("[unplugin-cloudflare-tunnel] Config unchanged – re-using existing tunnel");
|
|
376
513
|
globalState.shuttingDown = false;
|
|
377
514
|
registerExitHandler();
|
|
378
515
|
return;
|
|
379
516
|
}
|
|
380
517
|
if (globalState.child && !globalState.child.killed) {
|
|
381
|
-
|
|
518
|
+
debugLog("[unplugin-cloudflare-tunnel] Config changed – terminating previous tunnel...");
|
|
382
519
|
try {
|
|
383
520
|
globalState.child.kill("SIGTERM");
|
|
384
521
|
} catch (_) {}
|
|
@@ -387,7 +524,7 @@ const unpluginFactory = (options = {}) => {
|
|
|
387
524
|
delete globalState.configHash;
|
|
388
525
|
globalState.shuttingDown = false;
|
|
389
526
|
if (isQuickMode) {
|
|
390
|
-
|
|
527
|
+
debugLog("[unplugin-cloudflare-tunnel] Starting quick tunnel mode...");
|
|
391
528
|
debugLog("Quick tunnel mode - no API token or hostname required");
|
|
392
529
|
await ensureCloudflaredBinary(bin);
|
|
393
530
|
const localTarget$1 = getLocalTarget(serverHost, port);
|
|
@@ -399,20 +536,33 @@ const unpluginFactory = (options = {}) => {
|
|
|
399
536
|
globalState.child = child;
|
|
400
537
|
globalState.configHash = newConfigHash;
|
|
401
538
|
registerExitHandler();
|
|
402
|
-
|
|
403
|
-
|
|
539
|
+
registerListeningHandler(() => {
|
|
540
|
+
const { host: actualServerHost, port: actualPort } = normalizeAddress(server.httpServer?.address());
|
|
541
|
+
const actualLocalTarget = getLocalTarget(actualServerHost, actualPort ?? port);
|
|
542
|
+
announceTunnel({
|
|
543
|
+
key: `quick:${url}:${actualPort ?? port}`,
|
|
544
|
+
url,
|
|
545
|
+
localTarget: actualLocalTarget
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
registerListeningHandler(async () => {
|
|
404
549
|
try {
|
|
405
550
|
const { host: actualServerHost, port: actualPort } = normalizeAddress(server.httpServer?.address());
|
|
406
551
|
if (actualPort !== port) {
|
|
407
|
-
|
|
408
|
-
|
|
552
|
+
pluginLog.warn(`Port conflict detected - server is using port ${actualPort} instead of ${port}`);
|
|
553
|
+
pluginLog.info("Restarting quick tunnel for the new port...");
|
|
409
554
|
killCloudflared("SIGTERM");
|
|
410
555
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
411
|
-
const
|
|
556
|
+
const newLocalTarget = getLocalTarget(actualServerHost, actualPort ?? port);
|
|
557
|
+
const { child: newChild, url: newUrl } = await spawnQuickTunnel(newLocalTarget);
|
|
412
558
|
tunnelUrl = newUrl;
|
|
413
559
|
child = newChild;
|
|
414
560
|
globalState.child = child;
|
|
415
|
-
|
|
561
|
+
announceTunnel({
|
|
562
|
+
key: `quick:${newUrl}:${actualPort ?? port}`,
|
|
563
|
+
url: newUrl,
|
|
564
|
+
localTarget: newLocalTarget
|
|
565
|
+
});
|
|
416
566
|
globalState.configHash = JSON.stringify({
|
|
417
567
|
isQuickMode,
|
|
418
568
|
hostname,
|
|
@@ -435,12 +585,12 @@ const unpluginFactory = (options = {}) => {
|
|
|
435
585
|
throw error;
|
|
436
586
|
}
|
|
437
587
|
}
|
|
438
|
-
|
|
588
|
+
debugLog("[unplugin-cloudflare-tunnel] Starting named tunnel mode...");
|
|
439
589
|
const apiToken = providedApiToken || process.env.CLOUDFLARE_API_KEY;
|
|
440
590
|
if (!apiToken) throw new Error("[unplugin-cloudflare-tunnel] API token is required. Provide it via 'apiToken' option or set the CLOUDFLARE_API_KEY environment variable. Get your token at: https://dash.cloudflare.com/profile/api-tokens");
|
|
441
|
-
|
|
591
|
+
debugLog(`[unplugin-cloudflare-tunnel] Using port ${port}${userProvidedPort === port ? " (user-provided)" : " (from bundler config)"}`);
|
|
442
592
|
await ensureCloudflaredBinary(bin);
|
|
443
|
-
const accounts = await cf(apiToken, "GET", "/accounts", void 0,
|
|
593
|
+
const accounts = await cf(apiToken, "GET", "/accounts", void 0, array(AccountSchema));
|
|
444
594
|
const accountId = forcedAccount || accounts[0]?.id;
|
|
445
595
|
if (!accountId) throw new Error("Unable to determine Cloudflare account ID");
|
|
446
596
|
const apexDomain = hostname.split(".").slice(-2).join(".");
|
|
@@ -451,18 +601,18 @@ const unpluginFactory = (options = {}) => {
|
|
|
451
601
|
if (!zoneId) {
|
|
452
602
|
let zones = [];
|
|
453
603
|
try {
|
|
454
|
-
zones = await cf(apiToken, "GET", `/zones?name=${parentDomain}`, void 0,
|
|
604
|
+
zones = await cf(apiToken, "GET", `/zones?name=${parentDomain}`, void 0, array(ZoneSchema));
|
|
455
605
|
} catch (error) {
|
|
456
606
|
debugLog("← Error fetching zone for parent domain", error);
|
|
457
607
|
}
|
|
458
|
-
if (zones.length === 0) zones = await cf(apiToken, "GET", `/zones?name=${apexDomain}`, void 0,
|
|
608
|
+
if (zones.length === 0) zones = await cf(apiToken, "GET", `/zones?name=${apexDomain}`, void 0, array(ZoneSchema));
|
|
459
609
|
zoneId = zones[0]?.id;
|
|
460
610
|
}
|
|
461
611
|
if (!zoneId) throw new Error(`Zone ${apexDomain} not found in account ${accountId}`);
|
|
462
612
|
const { autoCleanup = true } = cleanupConfig;
|
|
463
|
-
let tunnel = (await cf(apiToken, "GET", `/accounts/${accountId}/cfd_tunnel?name=${tunnelName}`, void 0,
|
|
613
|
+
let tunnel = (await cf(apiToken, "GET", `/accounts/${accountId}/cfd_tunnel?name=${tunnelName}`, void 0, array(TunnelSchema)))[0];
|
|
464
614
|
if (!tunnel) {
|
|
465
|
-
|
|
615
|
+
pluginLog.info(`Creating tunnel '${tunnelName}'...`);
|
|
466
616
|
tunnel = await cf(apiToken, "POST", `/accounts/${accountId}/cfd_tunnel`, {
|
|
467
617
|
name: tunnelName,
|
|
468
618
|
config_src: "cloudflare"
|
|
@@ -470,13 +620,13 @@ const unpluginFactory = (options = {}) => {
|
|
|
470
620
|
}
|
|
471
621
|
const tunnelId = tunnel.id;
|
|
472
622
|
if (autoCleanup) {
|
|
473
|
-
|
|
623
|
+
debugLog(`[unplugin-cloudflare-tunnel] Running resource cleanup for tunnel '${tunnelName}'...`);
|
|
474
624
|
const dnsCleanup = await cleanupMismatchedDnsRecords(apiToken, zoneId, generateDnsComment(), hostname, tunnelId);
|
|
475
|
-
if (dnsCleanup.found.length > 0)
|
|
625
|
+
if (dnsCleanup.found.length > 0) pluginLog.warn(`DNS cleanup: ${dnsCleanup.found.length} mismatched, ${dnsCleanup.deleted.length} deleted`);
|
|
476
626
|
const mismatchedSslCerts = await findMismatchedSslCertificates(apiToken, zoneId, tunnelName, hostname);
|
|
477
627
|
if (mismatchedSslCerts.length > 0) {
|
|
478
628
|
for (const cert of mismatchedSslCerts) await cf(apiToken, "DELETE", `/zones/${zoneId}/ssl/certificate_packs/${cert.id}`);
|
|
479
|
-
|
|
629
|
+
pluginLog.warn(`SSL cleanup: ${mismatchedSslCerts.length} deleted`);
|
|
480
630
|
}
|
|
481
631
|
} else debugLog("← Cleanup skipped", cleanupConfig);
|
|
482
632
|
const localTarget = getLocalTarget(serverHost, port);
|
|
@@ -490,7 +640,7 @@ const unpluginFactory = (options = {}) => {
|
|
|
490
640
|
};
|
|
491
641
|
if (dnsOption) {
|
|
492
642
|
const ensureDnsRecord = async (type, content) => {
|
|
493
|
-
if ((await cf(apiToken, "GET", `/zones/${zoneId}/dns_records?type=${type}&name=${encodeURIComponent(dnsOption)}`, void 0,
|
|
643
|
+
if ((await cf(apiToken, "GET", `/zones/${zoneId}/dns_records?type=${type}&name=${encodeURIComponent(dnsOption)}`, void 0, array(DNSRecordSchema))).length === 0) {
|
|
494
644
|
console.log(`[unplugin-cloudflare-tunnel] Creating ${type} record for ${dnsOption}...`);
|
|
495
645
|
await cf(apiToken, "POST", `/zones/${zoneId}/dns_records`, {
|
|
496
646
|
type,
|
|
@@ -504,22 +654,34 @@ const unpluginFactory = (options = {}) => {
|
|
|
504
654
|
await ensureDnsRecord("CNAME", `${tunnelId}.cfargotunnel.com`);
|
|
505
655
|
} else {
|
|
506
656
|
const wildcardDns = `*.${parentDomain}`;
|
|
507
|
-
if ((await cf(apiToken, "GET", `/zones/${zoneId}/dns_records?type=CNAME&name=${wildcardDns}`, void 0,
|
|
508
|
-
|
|
657
|
+
if ((await cf(apiToken, "GET", `/zones/${zoneId}/dns_records?type=CNAME&name=${wildcardDns}`, void 0, array(DNSRecordSchema))).length === 0) {
|
|
658
|
+
const existingRecord = (await cf(apiToken, "GET", `/zones/${zoneId}/dns_records?type=CNAME&name=${hostname}`, void 0, array(DNSRecordSchema)))[0];
|
|
659
|
+
const expectedContent = `${tunnelId}.cfargotunnel.com`;
|
|
660
|
+
if (!existingRecord) {
|
|
509
661
|
console.log(`[unplugin-cloudflare-tunnel] Creating DNS record for ${hostname}...`);
|
|
510
662
|
await cf(apiToken, "POST", `/zones/${zoneId}/dns_records`, {
|
|
511
663
|
type: "CNAME",
|
|
512
664
|
name: hostname,
|
|
513
|
-
content:
|
|
665
|
+
content: expectedContent,
|
|
666
|
+
proxied: true,
|
|
667
|
+
comment: generateDnsComment()
|
|
668
|
+
}, DNSRecordSchema);
|
|
669
|
+
} else if (existingRecord.content !== expectedContent) {
|
|
670
|
+
debugLog(`← DNS record for ${hostname} points to different tunnel, updating...`);
|
|
671
|
+
pluginLog.info(`Updating DNS record for ${hostname} to point to tunnel '${tunnelName}'...`);
|
|
672
|
+
await cf(apiToken, "PUT", `/zones/${zoneId}/dns_records/${existingRecord.id}`, {
|
|
673
|
+
type: "CNAME",
|
|
674
|
+
name: hostname,
|
|
675
|
+
content: expectedContent,
|
|
514
676
|
proxied: true,
|
|
515
677
|
comment: generateDnsComment()
|
|
516
678
|
}, DNSRecordSchema);
|
|
517
679
|
}
|
|
518
680
|
}
|
|
519
681
|
}
|
|
520
|
-
const token = await cf(apiToken, "GET", `/accounts/${accountId}/cfd_tunnel/${tunnelId}/token`, void 0,
|
|
682
|
+
const token = await cf(apiToken, "GET", `/accounts/${accountId}/cfd_tunnel/${tunnelId}/token`, void 0, string());
|
|
521
683
|
try {
|
|
522
|
-
const certListRaw = await cf(apiToken, "GET", `/zones/${zoneId}/ssl/certificate_packs?status=all`, void 0,
|
|
684
|
+
const certListRaw = await cf(apiToken, "GET", `/zones/${zoneId}/ssl/certificate_packs?status=all`, void 0, any());
|
|
523
685
|
const certPacks = Array.isArray(certListRaw) ? certListRaw : certListRaw.result || [];
|
|
524
686
|
const certContainingHost = (host) => certPacks.filter((c) => (c.hostnames || c.hosts || []).includes(host))?.[0];
|
|
525
687
|
if (sslOption) {
|
|
@@ -545,11 +707,11 @@ const unpluginFactory = (options = {}) => {
|
|
|
545
707
|
const wildcardDomain = `*.${parentDomain}`;
|
|
546
708
|
const wildcardExists = certContainingHost(wildcardDomain);
|
|
547
709
|
if (!wildcardExists) {
|
|
548
|
-
const totalTls = await cf(apiToken, "GET", `/zones/${zoneId}/acm/total_tls`, void 0,
|
|
710
|
+
const totalTls = await cf(apiToken, "GET", `/zones/${zoneId}/acm/total_tls`, void 0, object({ status: string() }));
|
|
549
711
|
debugLog("← Total TLS", totalTls);
|
|
550
712
|
const existingHostnameCert = certContainingHost(hostname);
|
|
551
713
|
if (totalTls.status !== "on" && !existingHostnameCert) {
|
|
552
|
-
|
|
714
|
+
pluginLog.info(`Requesting edge certificate for ${hostname}...`);
|
|
553
715
|
const tagHostname = generateSslTagHostname();
|
|
554
716
|
const certificateHosts = [hostname, tagHostname];
|
|
555
717
|
debugLog(`Adding tag hostname to certificate: ${tagHostname}`);
|
|
@@ -574,7 +736,7 @@ const unpluginFactory = (options = {}) => {
|
|
|
574
736
|
if (logFile) cloudflaredArgs.push("--logfile", logFile);
|
|
575
737
|
debugLog("Spawning cloudflared", bin, cloudflaredArgs);
|
|
576
738
|
cloudflaredArgs.push("run", "--token", token);
|
|
577
|
-
child = spawn(bin, cloudflaredArgs, {
|
|
739
|
+
child = NodeChildProcess.spawn(bin, cloudflaredArgs, {
|
|
578
740
|
stdio: [
|
|
579
741
|
"ignore",
|
|
580
742
|
"pipe",
|
|
@@ -584,18 +746,37 @@ const unpluginFactory = (options = {}) => {
|
|
|
584
746
|
windowsHide: true,
|
|
585
747
|
shell: process.platform === "win32"
|
|
586
748
|
});
|
|
587
|
-
|
|
749
|
+
debugLog(`[unplugin-cloudflare-tunnel] Process spawned with PID: ${child.pid}`);
|
|
588
750
|
globalState.child = child;
|
|
589
751
|
globalState.configHash = newConfigHash;
|
|
590
752
|
registerExitHandler();
|
|
591
753
|
let tunnelReady = false;
|
|
754
|
+
const logCloudflaredLines = (kind, text) => {
|
|
755
|
+
if (globalState.shuttingDown && !debug) return;
|
|
756
|
+
const isVerbose = effectiveLogLevel === "debug" || effectiveLogLevel === "info";
|
|
757
|
+
const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
758
|
+
if (isVerbose) {
|
|
759
|
+
for (const line of lines) {
|
|
760
|
+
const prefix = kind === "stdout" ? "[cloudflared stdout]" : "[cloudflared stderr]";
|
|
761
|
+
if (kind === "stdout") console.log(`${prefix} ${line}`);
|
|
762
|
+
else console.error(`${prefix} ${line}`);
|
|
763
|
+
}
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
for (const line of lines) {
|
|
767
|
+
if (INFO_LOG_REGEX.test(line)) continue;
|
|
768
|
+
const prefix = kind === "stdout" ? "[cloudflared stdout]" : "[cloudflared stderr]";
|
|
769
|
+
if (kind === "stdout") console.log(`${prefix} ${line}`);
|
|
770
|
+
else console.error(`${prefix} ${line}`);
|
|
771
|
+
}
|
|
772
|
+
};
|
|
592
773
|
child.stdout?.on("data", (data) => {
|
|
593
774
|
const output = data.toString();
|
|
594
|
-
|
|
775
|
+
logCloudflaredLines("stdout", output);
|
|
595
776
|
if (output.includes("Connection") && output.includes("registered")) {
|
|
596
777
|
if (!tunnelReady) {
|
|
597
778
|
tunnelReady = true;
|
|
598
|
-
|
|
779
|
+
pluginLog.info(`Tunnel connected for https://${hostname}`);
|
|
599
780
|
}
|
|
600
781
|
}
|
|
601
782
|
});
|
|
@@ -605,10 +786,7 @@ const unpluginFactory = (options = {}) => {
|
|
|
605
786
|
if (logLevel === "debug") console.log(`[cloudflared debug] ${error}`);
|
|
606
787
|
return;
|
|
607
788
|
}
|
|
608
|
-
|
|
609
|
-
if (error.toLowerCase().includes("error") || error.toLowerCase().includes("failed") || error.toLowerCase().includes("fatal")) {
|
|
610
|
-
if (!globalState.shuttingDown || debug) console.error(`[unplugin-cloudflare-tunnel] ⚠️ ${error}`);
|
|
611
|
-
}
|
|
789
|
+
logCloudflaredLines("stderr", error);
|
|
612
790
|
});
|
|
613
791
|
child.on("error", (error) => {
|
|
614
792
|
console.error(`[unplugin-cloudflare-tunnel] ❌ Failed to start tunnel process: ${error.message}`);
|
|
@@ -620,25 +798,31 @@ const unpluginFactory = (options = {}) => {
|
|
|
620
798
|
if (signal) console.error(`[unplugin-cloudflare-tunnel] Process terminated by signal: ${signal}`);
|
|
621
799
|
} else if (code === 0) console.log(`[unplugin-cloudflare-tunnel] ✅ Tunnel process exited cleanly`);
|
|
622
800
|
});
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
801
|
+
registerListeningHandler(() => {
|
|
802
|
+
const { host: actualServerHost, port: actualPort } = normalizeAddress(server.httpServer?.address());
|
|
803
|
+
const actualLocalTarget = getLocalTarget(actualServerHost, actualPort ?? port);
|
|
804
|
+
announceTunnel({
|
|
805
|
+
key: `named:${hostname}:${actualPort ?? port}`,
|
|
806
|
+
url: `https://${hostname}`,
|
|
807
|
+
localTarget: actualLocalTarget
|
|
808
|
+
});
|
|
809
|
+
});
|
|
626
810
|
server.httpServer?.once("close", () => {
|
|
627
811
|
killCloudflared("SIGTERM");
|
|
628
812
|
});
|
|
629
|
-
|
|
813
|
+
registerListeningHandler(async () => {
|
|
630
814
|
try {
|
|
631
815
|
const { host: actualServerHost, port: actualPort } = normalizeAddress(server.httpServer?.address());
|
|
632
816
|
if (actualPort !== port) {
|
|
633
|
-
|
|
634
|
-
|
|
817
|
+
pluginLog.warn(`Port conflict detected - server is using port ${actualPort} instead of ${port}`);
|
|
818
|
+
pluginLog.info("Updating tunnel configuration...");
|
|
635
819
|
const newLocalTarget = getLocalTarget(actualServerHost, actualPort ?? port);
|
|
636
820
|
debugLog("← Updating local target to", newLocalTarget);
|
|
637
821
|
await cf(apiToken, "PUT", `/accounts/${accountId}/cfd_tunnel/${tunnelId}/configurations`, { config: { ingress: [{
|
|
638
822
|
hostname,
|
|
639
823
|
service: newLocalTarget
|
|
640
824
|
}, { service: "http_status:404" }] } });
|
|
641
|
-
|
|
825
|
+
pluginLog.info(`Tunnel configuration updated to use port ${actualPort}`);
|
|
642
826
|
globalState.configHash = JSON.stringify({
|
|
643
827
|
hostname,
|
|
644
828
|
port: actualPort,
|
|
@@ -652,27 +836,126 @@ const unpluginFactory = (options = {}) => {
|
|
|
652
836
|
}
|
|
653
837
|
});
|
|
654
838
|
} catch (error) {
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
839
|
+
if (error instanceof Error) {
|
|
840
|
+
console.error(`[unplugin-cloudflare-tunnel] ❌ Setup failed: ${error.message}`);
|
|
841
|
+
if (error.message.includes("API token")) {
|
|
842
|
+
console.error(`[unplugin-cloudflare-tunnel] 💡 Check your API token at: https://dash.cloudflare.com/profile/api-tokens`);
|
|
843
|
+
console.error(`[unplugin-cloudflare-tunnel] 💡 Required permissions: Zone:Zone:Read, Zone:DNS:Edit, Account:Cloudflare Tunnel:Edit`);
|
|
844
|
+
} else if (error.message.includes("Zone") && error.message.includes("not found")) console.error(`[unplugin-cloudflare-tunnel] 💡 Make sure '${hostname}' domain is added to your Cloudflare account`);
|
|
845
|
+
else if (error.message.includes("cloudflared")) console.error(`[unplugin-cloudflare-tunnel] 💡 Try deleting node_modules and reinstalling to get a fresh cloudflared binary`);
|
|
846
|
+
}
|
|
661
847
|
throw error;
|
|
662
848
|
}
|
|
663
849
|
};
|
|
850
|
+
const ensureWebpackAllowedHosts = (devServerOptions, bundler) => {
|
|
851
|
+
if (!devServerOptions) return;
|
|
852
|
+
const hostToAllow = isQuickMode ? ".trycloudflare.com" : hostname;
|
|
853
|
+
if (!hostToAllow) return;
|
|
854
|
+
const label = bundler === "webpack" ? "Webpack" : "Rspack";
|
|
855
|
+
const normalizeArray = (values) => {
|
|
856
|
+
const unique = Array.from(new Set(values.filter(Boolean)));
|
|
857
|
+
devServerOptions.allowedHosts = unique;
|
|
858
|
+
return unique;
|
|
859
|
+
};
|
|
860
|
+
let modified = false;
|
|
861
|
+
const current = devServerOptions.allowedHosts;
|
|
862
|
+
if (current === "all") return;
|
|
863
|
+
if (typeof current === "undefined" || current === "auto") {
|
|
864
|
+
normalizeArray(["localhost", hostToAllow]);
|
|
865
|
+
modified = true;
|
|
866
|
+
} else if (typeof current === "string") {
|
|
867
|
+
if (current !== hostToAllow) {
|
|
868
|
+
normalizeArray([current, hostToAllow]);
|
|
869
|
+
modified = true;
|
|
870
|
+
}
|
|
871
|
+
} else if (Array.isArray(current)) {
|
|
872
|
+
if (!current.includes(hostToAllow)) {
|
|
873
|
+
current.push(hostToAllow);
|
|
874
|
+
modified = true;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
if (modified) debugLog(`[unplugin-cloudflare-tunnel] Configured ${label} devServer.allowedHosts to include ${hostToAllow}`);
|
|
878
|
+
};
|
|
879
|
+
const setupWebpackLikeDevServerIntegration = (compiler, bundler) => {
|
|
880
|
+
if ((compiler?.options?.mode ?? process.env.NODE_ENV) === "production") return;
|
|
881
|
+
const optionsContainer = compiler.options;
|
|
882
|
+
if (!optionsContainer.devServer) optionsContainer.devServer = {};
|
|
883
|
+
const devServerOptions = optionsContainer.devServer;
|
|
884
|
+
ensureWebpackAllowedHosts(devServerOptions, bundler);
|
|
885
|
+
let lastHttpServer;
|
|
886
|
+
let missingServerWarned = false;
|
|
887
|
+
const runConfiguration = (devServerInstance) => {
|
|
888
|
+
if (!devServerInstance) {
|
|
889
|
+
if (!missingServerWarned) {
|
|
890
|
+
console.warn(`[unplugin-cloudflare-tunnel] ${bundler} dev server instance unavailable; skipping tunnel setup`);
|
|
891
|
+
missingServerWarned = true;
|
|
892
|
+
}
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
const httpServer = [
|
|
896
|
+
devServerInstance.server,
|
|
897
|
+
devServerInstance.httpServer,
|
|
898
|
+
devServerInstance.listeningApp,
|
|
899
|
+
devServerInstance.server?.server
|
|
900
|
+
].find((candidate) => candidate);
|
|
901
|
+
if (!httpServer) {
|
|
902
|
+
if (!missingServerWarned) {
|
|
903
|
+
console.warn(`[unplugin-cloudflare-tunnel] Unable to locate HTTP server from ${bundler} dev server; tunnel will not start`);
|
|
904
|
+
missingServerWarned = true;
|
|
905
|
+
}
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
if (lastHttpServer === httpServer) return;
|
|
909
|
+
lastHttpServer = httpServer;
|
|
910
|
+
httpServer.once("close", () => {
|
|
911
|
+
if (lastHttpServer === httpServer) lastHttpServer = void 0;
|
|
912
|
+
});
|
|
913
|
+
const configuredPromise = configureServer({
|
|
914
|
+
httpServer,
|
|
915
|
+
config: { server: { port: devServerInstance.options?.port ?? devServerOptions?.port } }
|
|
916
|
+
});
|
|
917
|
+
globalState.tunnelUrl = configuredPromise.then(() => tunnelUrl).catch(() => "");
|
|
918
|
+
configuredPromise.catch(() => {});
|
|
919
|
+
};
|
|
920
|
+
const scheduleConfiguration = (devServerInstance) => {
|
|
921
|
+
const httpServer = devServerInstance?.server || devServerInstance?.httpServer || devServerInstance?.listeningApp;
|
|
922
|
+
if (httpServer && typeof httpServer.once === "function") if (httpServer.listening) runConfiguration(devServerInstance);
|
|
923
|
+
else httpServer.once("listening", () => runConfiguration(devServerInstance));
|
|
924
|
+
else runConfiguration(devServerInstance);
|
|
925
|
+
};
|
|
926
|
+
const originalSetupMiddlewares = devServerOptions.setupMiddlewares;
|
|
927
|
+
devServerOptions.setupMiddlewares = function(middlewares, devServer) {
|
|
928
|
+
scheduleConfiguration(devServer);
|
|
929
|
+
if (typeof originalSetupMiddlewares === "function") return originalSetupMiddlewares.call(this, middlewares, devServer);
|
|
930
|
+
return middlewares;
|
|
931
|
+
};
|
|
932
|
+
const originalOnListening = devServerOptions.onListening;
|
|
933
|
+
devServerOptions.onListening = function(devServer) {
|
|
934
|
+
scheduleConfiguration(devServer);
|
|
935
|
+
if (typeof originalOnListening === "function") return originalOnListening.call(this, devServer);
|
|
936
|
+
};
|
|
937
|
+
};
|
|
664
938
|
return {
|
|
665
|
-
name:
|
|
939
|
+
name: PLUGIN_NAME,
|
|
666
940
|
enforce: "pre",
|
|
667
941
|
resolveId(id) {
|
|
668
|
-
if (id === VIRTUAL_MODULE_ID)
|
|
942
|
+
if (id === VIRTUAL_MODULE_ID) {
|
|
943
|
+
debugLog("resolveId called for", id);
|
|
944
|
+
return id;
|
|
945
|
+
}
|
|
946
|
+
},
|
|
947
|
+
loadInclude(id) {
|
|
948
|
+
return id === VIRTUAL_MODULE_ID;
|
|
669
949
|
},
|
|
670
950
|
async load(id) {
|
|
671
|
-
|
|
672
|
-
|
|
951
|
+
if (id === VIRTUAL_MODULE_ID) {
|
|
952
|
+
const url = await globalState.tunnelUrl;
|
|
953
|
+
return `export function getTunnelUrl() { return ${JSON.stringify(url || "")}; }`;
|
|
954
|
+
}
|
|
673
955
|
},
|
|
674
956
|
vite: {
|
|
675
|
-
config(config) {
|
|
957
|
+
config: (config) => {
|
|
958
|
+
announceConnecting();
|
|
676
959
|
if (!config.server) config.server = {};
|
|
677
960
|
if (isQuickMode) {
|
|
678
961
|
config.server.allowedHosts = [".trycloudflare.com"];
|
|
@@ -680,15 +963,15 @@ const unpluginFactory = (options = {}) => {
|
|
|
680
963
|
}
|
|
681
964
|
if (!config.server.allowedHosts) {
|
|
682
965
|
config.server.allowedHosts = [hostname];
|
|
683
|
-
|
|
966
|
+
debugLog(`[unplugin-cloudflare-tunnel] Configured Vite to allow requests from ${hostname}`);
|
|
684
967
|
} else if (Array.isArray(config.server.allowedHosts)) {
|
|
685
968
|
if (!config.server.allowedHosts.includes(hostname)) {
|
|
686
969
|
config.server.allowedHosts.push(hostname);
|
|
687
|
-
|
|
970
|
+
debugLog(`[unplugin-cloudflare-tunnel] Added ${hostname} to allowed hosts`);
|
|
688
971
|
}
|
|
689
972
|
}
|
|
690
973
|
},
|
|
691
|
-
configureServer(server) {
|
|
974
|
+
configureServer: (server) => {
|
|
692
975
|
const configuredPromise = configureServer(server);
|
|
693
976
|
globalState.tunnelUrl = configuredPromise.then(() => tunnelUrl).catch(() => "");
|
|
694
977
|
return async () => {
|
|
@@ -696,6 +979,12 @@ const unpluginFactory = (options = {}) => {
|
|
|
696
979
|
};
|
|
697
980
|
}
|
|
698
981
|
},
|
|
982
|
+
rspack: (compiler) => {
|
|
983
|
+
setupWebpackLikeDevServerIntegration(compiler, "rspack");
|
|
984
|
+
},
|
|
985
|
+
webpack: (compiler) => {
|
|
986
|
+
setupWebpackLikeDevServerIntegration(compiler, "webpack");
|
|
987
|
+
},
|
|
699
988
|
closeBundle() {
|
|
700
989
|
killCloudflared("SIGTERM");
|
|
701
990
|
delete globalState.child;
|
|
@@ -713,7 +1002,7 @@ function normalizeAddress(address) {
|
|
|
713
1002
|
}
|
|
714
1003
|
async function ensureCloudflaredBinary(binPath) {
|
|
715
1004
|
try {
|
|
716
|
-
await
|
|
1005
|
+
await NodeFS.access(binPath);
|
|
717
1006
|
} catch {
|
|
718
1007
|
console.log("[unplugin-cloudflare-tunnel] Installing cloudflared binary...");
|
|
719
1008
|
await install(binPath);
|
|
@@ -726,4 +1015,4 @@ const CloudflareTunnel = createUnplugin(unpluginFactory);
|
|
|
726
1015
|
var src_default = CloudflareTunnel;
|
|
727
1016
|
|
|
728
1017
|
//#endregion
|
|
729
|
-
export {
|
|
1018
|
+
export { CloudflareTunnel, src_default as default };
|