cyrus-cloudflare-tunnel-client 0.2.73 → 0.2.75
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/CloudflareTunnelClient.d.ts +67 -7
- package/dist/CloudflareTunnelClient.d.ts.map +1 -1
- package/dist/CloudflareTunnelClient.js +293 -55
- package/dist/CloudflareTunnelClient.js.map +1 -1
- package/dist/ConfigApiClient.d.ts +2 -0
- package/dist/ConfigApiClient.d.ts.map +1 -1
- package/dist/ConfigApiClient.js +15 -2
- package/dist/ConfigApiClient.js.map +1 -1
- package/dist/ConfigApiClient.test.d.ts +2 -0
- package/dist/ConfigApiClient.test.d.ts.map +1 -0
- package/dist/ConfigApiClient.test.js +42 -0
- package/dist/ConfigApiClient.test.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1,29 +1,87 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
|
-
import
|
|
2
|
+
import { Tunnel } from "cloudflared";
|
|
3
|
+
import type { CloudflareTunnelClientEvents, CloudflareTunnelRecoveryOptions } from "./types.js";
|
|
3
4
|
export declare interface CloudflareTunnelClient {
|
|
4
5
|
on<K extends keyof CloudflareTunnelClientEvents>(event: K, listener: CloudflareTunnelClientEvents[K]): this;
|
|
5
6
|
emit<K extends keyof CloudflareTunnelClientEvents>(event: K, ...args: Parameters<CloudflareTunnelClientEvents[K]>): boolean;
|
|
6
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Default recovery policy for a cloudflared child that exits after it had
|
|
10
|
+
* connected. Delays double from 1s up to a 60s cap; after 10 consecutive
|
|
11
|
+
* failed restarts the client stops retrying and stays disconnected.
|
|
12
|
+
*/
|
|
13
|
+
export declare const DEFAULT_TUNNEL_RECOVERY_OPTIONS: Required<CloudflareTunnelRecoveryOptions>;
|
|
14
|
+
export interface CloudflareTunnelClientOptions extends CloudflareTunnelRecoveryOptions {
|
|
15
|
+
/** Creates the cloudflared Tunnel (defaults to Tunnel.withToken); test seam */
|
|
16
|
+
createTunnel?: (token: string) => Tunnel;
|
|
17
|
+
}
|
|
7
18
|
/**
|
|
8
19
|
* Cloudflare tunnel client for establishing tunnels to local services
|
|
9
20
|
* Handles ONLY tunnel establishment - HTTP handling is done by SharedApplicationServer
|
|
21
|
+
*
|
|
22
|
+
* The client is the single owner of the cloudflared child process. If a child
|
|
23
|
+
* that had connected exits unexpectedly while the client is still running, the
|
|
24
|
+
* client respawns it with bounded exponential backoff, never before the
|
|
25
|
+
* previous child's exit is confirmed. Startup failures and deliberate
|
|
26
|
+
* disconnects never trigger a respawn.
|
|
27
|
+
*
|
|
28
|
+
* Reentrancy: every state transition completes before any event is emitted,
|
|
29
|
+
* and nothing that could start work runs after an emit unless the client is
|
|
30
|
+
* still current, so listeners may call disconnect() from any event.
|
|
10
31
|
*/
|
|
11
32
|
export declare class CloudflareTunnelClient extends EventEmitter {
|
|
12
|
-
|
|
33
|
+
/** The owned child; held until its exit is confirmed (or it never spawned) */
|
|
34
|
+
private tunnel;
|
|
35
|
+
/** Incremented per spawned child and on loss/disconnect; fences stale callbacks */
|
|
36
|
+
private generation;
|
|
37
|
+
/** The single pending timer: connect timeout, kill wait, or restart backoff */
|
|
38
|
+
private timer;
|
|
39
|
+
/** A lost child was killed; the restart waits for its exit */
|
|
40
|
+
private awaitingExit;
|
|
41
|
+
private pendingRestartDelay;
|
|
42
|
+
private state;
|
|
43
|
+
private startup;
|
|
44
|
+
private restartAttempts;
|
|
45
|
+
private connectedAt;
|
|
13
46
|
private tunnelUrl;
|
|
14
47
|
private connected;
|
|
15
48
|
private connectionCount;
|
|
16
49
|
private cloudflareToken;
|
|
17
50
|
private localPort;
|
|
18
|
-
|
|
51
|
+
private options;
|
|
52
|
+
private createTunnel;
|
|
53
|
+
constructor(cloudflareToken: string, localPort: number, onReady?: (tunnelUrl: string) => void, options?: CloudflareTunnelClientOptions);
|
|
19
54
|
/**
|
|
20
|
-
* Start the Cloudflare tunnel
|
|
55
|
+
* Start the Cloudflare tunnel. Resolves once the first connection is
|
|
56
|
+
* established; rejects if the child exits, fails to spawn, the connect
|
|
57
|
+
* timeout elapses, or disconnect() is called first. Can only be called once.
|
|
21
58
|
*/
|
|
22
59
|
startTunnel(): Promise<void>;
|
|
23
60
|
/**
|
|
24
|
-
*
|
|
61
|
+
* Spawn a cloudflared child owned by this client and arm its connect
|
|
62
|
+
* timeout. Throws (owning nothing) if the tunnel cannot be created.
|
|
63
|
+
*/
|
|
64
|
+
private spawnChild;
|
|
65
|
+
/** First connection of the current child: update state and settle startup */
|
|
66
|
+
private markConnected;
|
|
67
|
+
/**
|
|
68
|
+
* The current child is gone (exited, failed to spawn, or timed out before
|
|
69
|
+
* connecting). During startup this fails startTunnel(); afterwards it
|
|
70
|
+
* schedules a bounded restart once the child's exit is confirmed.
|
|
71
|
+
*/
|
|
72
|
+
private handleChildLoss;
|
|
73
|
+
/** An owned child has actually exited (or never spawned) */
|
|
74
|
+
private releaseChild;
|
|
75
|
+
private scheduleRestart;
|
|
76
|
+
private handleKillWaitTimeout;
|
|
77
|
+
private setTimer;
|
|
78
|
+
private clearTimer;
|
|
79
|
+
/**
|
|
80
|
+
* Kill the owned child (if still alive) and release it. Only used when no
|
|
81
|
+
* further child will be spawned. Failed children are killed immediately;
|
|
82
|
+
* deliberate shutdowns get a graceful SIGINT first.
|
|
25
83
|
*/
|
|
26
|
-
private
|
|
84
|
+
private stopChild;
|
|
27
85
|
/**
|
|
28
86
|
* Get the tunnel URL
|
|
29
87
|
*/
|
|
@@ -33,7 +91,9 @@ export declare class CloudflareTunnelClient extends EventEmitter {
|
|
|
33
91
|
*/
|
|
34
92
|
isConnected(): boolean;
|
|
35
93
|
/**
|
|
36
|
-
* Disconnect and cleanup
|
|
94
|
+
* Disconnect and cleanup. Cancels any pending restart or startup and kills
|
|
95
|
+
* the owned child; the client will not respawn afterwards. Safe to call
|
|
96
|
+
* from any event listener.
|
|
37
97
|
*/
|
|
38
98
|
disconnect(): void;
|
|
39
99
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CloudflareTunnelClient.d.ts","sourceRoot":"","sources":["../src/CloudflareTunnelClient.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CloudflareTunnelClient.d.ts","sourceRoot":"","sources":["../src/CloudflareTunnelClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAgB,MAAM,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EACX,4BAA4B,EAC5B,+BAA+B,EAC/B,MAAM,YAAY,CAAC;AAEpB,MAAM,CAAC,OAAO,WAAW,sBAAsB;IAC9C,EAAE,CAAC,CAAC,SAAS,MAAM,4BAA4B,EAC9C,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,4BAA4B,CAAC,CAAC,CAAC,GACvC,IAAI,CAAC;IACR,IAAI,CAAC,CAAC,SAAS,MAAM,4BAA4B,EAChD,KAAK,EAAE,CAAC,EACR,GAAG,IAAI,EAAE,UAAU,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC,GAClD,OAAO,CAAC;CACX;AAED;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,EAAE,QAAQ,CAAC,+BAA+B,CAgBpF,CAAC;AAEH,MAAM,WAAW,6BAChB,SAAQ,+BAA+B;IACvC,+EAA+E;IAC/E,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACzC;AAeD;;;;;;;;;;;;;GAaG;AACH,qBAAa,sBAAuB,SAAQ,YAAY;IACvD,8EAA8E;IAC9E,OAAO,CAAC,MAAM,CAAuB;IACrC,mFAAmF;IACnF,OAAO,CAAC,UAAU,CAAK;IACvB,+EAA+E;IAC/E,OAAO,CAAC,KAAK,CAA+B;IAC5C,8DAA8D;IAC9D,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,OAAO,CAGC;IAChB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAA4C;IAC3D,OAAO,CAAC,YAAY,CAA4B;IAEhD,YACC,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,EACrC,OAAO,GAAE,6BAAkC,EAc3C;IAED;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAuCjC;IAED;;;OAGG;IACH,OAAO,CAAC,UAAU;IAgFlB,6EAA6E;IAC7E,OAAO,CAAC,aAAa;IAkBrB;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAgEvB,4DAA4D;IAC5D,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,qBAAqB;IAQ7B,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,UAAU;IAOlB;;;;OAIG;IACH,OAAO,CAAC,SAAS;IAoBjB;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5B;IAED;;OAEG;IACH,WAAW,IAAI,OAAO,CAErB;IAED;;;;OAIG;IACH,UAAU,IAAI,IAAI,CAiBjB;CACD"}
|
|
@@ -1,93 +1,322 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { bin, install, Tunnel } from "cloudflared";
|
|
4
|
+
/**
|
|
5
|
+
* Default recovery policy for a cloudflared child that exits after it had
|
|
6
|
+
* connected. Delays double from 1s up to a 60s cap; after 10 consecutive
|
|
7
|
+
* failed restarts the client stops retrying and stays disconnected.
|
|
8
|
+
*/
|
|
9
|
+
export const DEFAULT_TUNNEL_RECOVERY_OPTIONS = {
|
|
10
|
+
// 120s: slow DNS resolvers (e.g. dead IPv6 nameservers ahead of the
|
|
11
|
+
// working one) can delay the first connection past 30s
|
|
12
|
+
connectTimeoutMs: 120000,
|
|
13
|
+
initialRestartDelayMs: 1000,
|
|
14
|
+
maxRestartDelayMs: 60000,
|
|
15
|
+
maxRestartAttempts: 10,
|
|
16
|
+
// A child that stayed connected this long counts as healthy, so its exit
|
|
17
|
+
// starts a fresh retry budget instead of continuing the previous one
|
|
18
|
+
stableConnectionMs: 60000,
|
|
19
|
+
// Grace period after SIGINT before SIGKILL on deliberate shutdown
|
|
20
|
+
killGraceMs: 5000,
|
|
21
|
+
// Max wait for a killed child to exit before a restart; if its exit is
|
|
22
|
+
// never confirmed, recovery stops rather than run a second child
|
|
23
|
+
killWaitMs: 10000,
|
|
24
|
+
};
|
|
25
|
+
const isAlive = (tunnel) => tunnel.process.pid !== undefined &&
|
|
26
|
+
tunnel.process.exitCode === null &&
|
|
27
|
+
tunnel.process.signalCode === null;
|
|
4
28
|
/**
|
|
5
29
|
* Cloudflare tunnel client for establishing tunnels to local services
|
|
6
30
|
* Handles ONLY tunnel establishment - HTTP handling is done by SharedApplicationServer
|
|
31
|
+
*
|
|
32
|
+
* The client is the single owner of the cloudflared child process. If a child
|
|
33
|
+
* that had connected exits unexpectedly while the client is still running, the
|
|
34
|
+
* client respawns it with bounded exponential backoff, never before the
|
|
35
|
+
* previous child's exit is confirmed. Startup failures and deliberate
|
|
36
|
+
* disconnects never trigger a respawn.
|
|
37
|
+
*
|
|
38
|
+
* Reentrancy: every state transition completes before any event is emitted,
|
|
39
|
+
* and nothing that could start work runs after an emit unless the client is
|
|
40
|
+
* still current, so listeners may call disconnect() from any event.
|
|
7
41
|
*/
|
|
8
42
|
export class CloudflareTunnelClient extends EventEmitter {
|
|
9
|
-
|
|
43
|
+
/** The owned child; held until its exit is confirmed (or it never spawned) */
|
|
44
|
+
tunnel = null;
|
|
45
|
+
/** Incremented per spawned child and on loss/disconnect; fences stale callbacks */
|
|
46
|
+
generation = 0;
|
|
47
|
+
/** The single pending timer: connect timeout, kill wait, or restart backoff */
|
|
48
|
+
timer = null;
|
|
49
|
+
/** A lost child was killed; the restart waits for its exit */
|
|
50
|
+
awaitingExit = false;
|
|
51
|
+
pendingRestartDelay = 0;
|
|
52
|
+
state = "idle";
|
|
53
|
+
startup = null;
|
|
54
|
+
restartAttempts = 0;
|
|
55
|
+
connectedAt = 0;
|
|
10
56
|
tunnelUrl = null;
|
|
11
57
|
connected = false;
|
|
12
58
|
connectionCount = 0;
|
|
13
59
|
cloudflareToken;
|
|
14
60
|
localPort;
|
|
15
|
-
|
|
61
|
+
options;
|
|
62
|
+
createTunnel;
|
|
63
|
+
constructor(cloudflareToken, localPort, onReady, options = {}) {
|
|
16
64
|
super();
|
|
17
65
|
this.cloudflareToken = cloudflareToken;
|
|
18
66
|
this.localPort = localPort;
|
|
67
|
+
const { createTunnel, ...recovery } = options;
|
|
68
|
+
this.options = { ...DEFAULT_TUNNEL_RECOVERY_OPTIONS, ...recovery };
|
|
69
|
+
// Create tunnel with token-based authentication (no URL needed for remotely-managed tunnels)
|
|
70
|
+
this.createTunnel = createTunnel ?? ((token) => Tunnel.withToken(token));
|
|
19
71
|
// Set up onReady callback if provided
|
|
20
72
|
if (onReady) {
|
|
21
73
|
this.on("ready", onReady);
|
|
22
74
|
}
|
|
23
75
|
}
|
|
24
76
|
/**
|
|
25
|
-
* Start the Cloudflare tunnel
|
|
77
|
+
* Start the Cloudflare tunnel. Resolves once the first connection is
|
|
78
|
+
* established; rejects if the child exits, fails to spawn, the connect
|
|
79
|
+
* timeout elapses, or disconnect() is called first. Can only be called once.
|
|
26
80
|
*/
|
|
27
81
|
async startTunnel() {
|
|
82
|
+
if (this.state !== "idle") {
|
|
83
|
+
throw new Error(`Cloudflare tunnel already started (state: ${this.state})`);
|
|
84
|
+
}
|
|
85
|
+
this.state = "starting";
|
|
28
86
|
try {
|
|
29
87
|
// Ensure cloudflared binary is installed
|
|
30
88
|
if (!existsSync(bin)) {
|
|
31
89
|
await install(bin);
|
|
32
90
|
}
|
|
91
|
+
if (this.state !== "starting") {
|
|
92
|
+
throw new Error("Cloudflare tunnel disconnected during startup");
|
|
93
|
+
}
|
|
33
94
|
console.log(`Starting tunnel to localhost:${this.localPort}`);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
tunnel.on("url", (url) => {
|
|
38
|
-
// Ensure URL has protocol for token-based tunnels
|
|
39
|
-
if (!url.startsWith("http")) {
|
|
40
|
-
url = `https://${url}`;
|
|
41
|
-
}
|
|
42
|
-
if (!this.tunnelUrl) {
|
|
43
|
-
this.tunnelUrl = url;
|
|
44
|
-
this.emit("ready", this.tunnelUrl);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
// Listen for connection event (Cloudflare establishes 4 connections per tunnel)
|
|
48
|
-
tunnel.on("connected", (connection) => {
|
|
49
|
-
this.connectionCount++;
|
|
50
|
-
console.log(`Cloudflare tunnel connection ${this.connectionCount}/4 established:`, connection);
|
|
51
|
-
// Emit 'connected' event for each connection (for external listeners)
|
|
52
|
-
this.emit("connected", connection);
|
|
53
|
-
// Mark as connected on first connection, but log all 4
|
|
54
|
-
if (!this.connected) {
|
|
55
|
-
this.connected = true;
|
|
56
|
-
this.emit("connect");
|
|
57
|
-
}
|
|
95
|
+
await new Promise((resolve, reject) => {
|
|
96
|
+
this.startup = { resolve, reject };
|
|
97
|
+
this.spawnChild();
|
|
58
98
|
});
|
|
59
|
-
// Listen for error event
|
|
60
|
-
tunnel.on("error", (error) => {
|
|
61
|
-
this.emit("error", error);
|
|
62
|
-
});
|
|
63
|
-
// Listen for exit event
|
|
64
|
-
tunnel.on("exit", (code) => {
|
|
65
|
-
this.connected = false;
|
|
66
|
-
this.connectionCount = 0; // Reset count on disconnect for fresh reconnection logs
|
|
67
|
-
this.emit("disconnect", `Tunnel process exited with code ${code}`);
|
|
68
|
-
});
|
|
69
|
-
// Wait for tunnel URL to be available (with timeout)
|
|
70
|
-
// 120s: slow DNS resolvers (e.g. dead IPv6 nameservers ahead of the
|
|
71
|
-
// working one) can delay the first connection past 30s
|
|
72
|
-
await this.waitForTunnelToConnect(120000);
|
|
73
99
|
}
|
|
74
100
|
catch (error) {
|
|
75
|
-
this.
|
|
101
|
+
this.startup = null;
|
|
102
|
+
// Re-read state: disconnect() or a child loss may have changed it
|
|
103
|
+
const state = this.state;
|
|
104
|
+
if (state === "starting") {
|
|
105
|
+
this.state = "failed";
|
|
106
|
+
this.generation++;
|
|
107
|
+
this.clearTimer();
|
|
108
|
+
this.stopChild(true);
|
|
109
|
+
}
|
|
110
|
+
// A deliberate disconnect during startup is not an error condition
|
|
111
|
+
if (state !== "stopped") {
|
|
112
|
+
this.emit("error", error);
|
|
113
|
+
}
|
|
76
114
|
throw error;
|
|
77
115
|
}
|
|
78
116
|
}
|
|
79
117
|
/**
|
|
80
|
-
*
|
|
118
|
+
* Spawn a cloudflared child owned by this client and arm its connect
|
|
119
|
+
* timeout. Throws (owning nothing) if the tunnel cannot be created.
|
|
120
|
+
*/
|
|
121
|
+
spawnChild() {
|
|
122
|
+
const generation = ++this.generation;
|
|
123
|
+
const isCurrent = () => generation === this.generation &&
|
|
124
|
+
(this.state === "starting" ||
|
|
125
|
+
this.state === "recovering" ||
|
|
126
|
+
this.state === "running");
|
|
127
|
+
this.connectionCount = 0;
|
|
128
|
+
const tunnel = this.createTunnel(this.cloudflareToken);
|
|
129
|
+
this.tunnel = tunnel;
|
|
130
|
+
// Ownership release is not generation-gated: a killed child stays owned
|
|
131
|
+
// until it has actually exited
|
|
132
|
+
tunnel.on("exit", () => this.releaseChild(tunnel));
|
|
133
|
+
// Listen for exit event
|
|
134
|
+
tunnel.on("exit", (code, signal) => {
|
|
135
|
+
if (!isCurrent())
|
|
136
|
+
return;
|
|
137
|
+
this.handleChildLoss(`Tunnel process exited with code ${code}${signal ? ` (signal ${signal})` : ""}`);
|
|
138
|
+
});
|
|
139
|
+
// Listen for error event. Errors are informational unless the child
|
|
140
|
+
// never spawned (no pid), in which case no 'exit' will follow.
|
|
141
|
+
tunnel.on("error", (error) => {
|
|
142
|
+
const neverSpawned = tunnel.process.pid === undefined;
|
|
143
|
+
if (neverSpawned)
|
|
144
|
+
this.releaseChild(tunnel);
|
|
145
|
+
if (generation !== this.generation)
|
|
146
|
+
return;
|
|
147
|
+
if (neverSpawned && isCurrent()) {
|
|
148
|
+
this.handleChildLoss(`Tunnel process failed to start: ${error.message}`);
|
|
149
|
+
}
|
|
150
|
+
this.emit("error", error);
|
|
151
|
+
});
|
|
152
|
+
// Listen for URL event (from ConfigHandler for token-based tunnels)
|
|
153
|
+
tunnel.on("url", (url) => {
|
|
154
|
+
if (!isCurrent())
|
|
155
|
+
return;
|
|
156
|
+
// Ensure URL has protocol for token-based tunnels
|
|
157
|
+
if (!url.startsWith("http")) {
|
|
158
|
+
url = `https://${url}`;
|
|
159
|
+
}
|
|
160
|
+
if (!this.tunnelUrl) {
|
|
161
|
+
this.tunnelUrl = url;
|
|
162
|
+
this.emit("ready", this.tunnelUrl);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
// Listen for connection event (Cloudflare establishes 4 connections per tunnel)
|
|
166
|
+
tunnel.on("connected", (connection) => {
|
|
167
|
+
if (!isCurrent())
|
|
168
|
+
return;
|
|
169
|
+
this.connectionCount++;
|
|
170
|
+
console.log(`Cloudflare tunnel connection ${this.connectionCount}/4 established:`, connection);
|
|
171
|
+
// Mark as connected on first connection, but log all 4
|
|
172
|
+
const first = !this.connected;
|
|
173
|
+
if (first) {
|
|
174
|
+
this.markConnected();
|
|
175
|
+
}
|
|
176
|
+
// Emit 'connected' event for each connection (for external listeners)
|
|
177
|
+
this.emit("connected", connection);
|
|
178
|
+
if (first && isCurrent()) {
|
|
179
|
+
this.emit("connect");
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
this.setTimer(() => {
|
|
183
|
+
this.handleChildLoss(`Timeout waiting for tunnel to connect after ${this.options.connectTimeoutMs}ms`);
|
|
184
|
+
}, this.options.connectTimeoutMs);
|
|
185
|
+
}
|
|
186
|
+
/** First connection of the current child: update state and settle startup */
|
|
187
|
+
markConnected() {
|
|
188
|
+
this.clearTimer();
|
|
189
|
+
this.connected = true;
|
|
190
|
+
this.connectedAt = Date.now();
|
|
191
|
+
const recovered = this.state === "recovering";
|
|
192
|
+
this.state = "running";
|
|
193
|
+
if (recovered) {
|
|
194
|
+
console.log(`Cloudflare tunnel recovered after ${this.restartAttempts} restart attempt(s)`);
|
|
195
|
+
}
|
|
196
|
+
const startup = this.startup;
|
|
197
|
+
this.startup = null;
|
|
198
|
+
startup?.resolve();
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* The current child is gone (exited, failed to spawn, or timed out before
|
|
202
|
+
* connecting). During startup this fails startTunnel(); afterwards it
|
|
203
|
+
* schedules a bounded restart once the child's exit is confirmed.
|
|
81
204
|
*/
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
205
|
+
handleChildLoss(reason) {
|
|
206
|
+
this.clearTimer();
|
|
207
|
+
// Fence any remaining callbacks from the lost child
|
|
208
|
+
this.generation++;
|
|
209
|
+
const wasConnected = this.connected;
|
|
210
|
+
this.connected = false;
|
|
211
|
+
this.connectionCount = 0; // Reset count on disconnect for fresh reconnection logs
|
|
212
|
+
if (this.state === "starting") {
|
|
213
|
+
this.state = "failed";
|
|
214
|
+
this.stopChild(true);
|
|
215
|
+
const startup = this.startup;
|
|
216
|
+
this.startup = null;
|
|
217
|
+
// startTunnel() reports this rejection as an 'error' event
|
|
218
|
+
startup?.reject(new Error(`Cloudflare tunnel failed to start: ${reason}`));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (wasConnected &&
|
|
222
|
+
Date.now() - this.connectedAt >= this.options.stableConnectionMs) {
|
|
223
|
+
this.restartAttempts = 0;
|
|
224
|
+
}
|
|
225
|
+
if (this.restartAttempts >= this.options.maxRestartAttempts) {
|
|
226
|
+
this.state = "failed";
|
|
227
|
+
this.stopChild(true);
|
|
228
|
+
const message = `Cloudflare tunnel recovery gave up after ${this.restartAttempts} consecutive failed restart attempts (${reason}); restart Cyrus to restore the tunnel`;
|
|
229
|
+
console.error(message);
|
|
230
|
+
if (wasConnected)
|
|
231
|
+
this.emit("disconnect", reason);
|
|
232
|
+
if (this.state === "failed")
|
|
233
|
+
this.emit("error", new Error(message));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
this.state = "recovering";
|
|
237
|
+
this.restartAttempts++;
|
|
238
|
+
this.pendingRestartDelay = Math.min(this.options.initialRestartDelayMs * 2 ** (this.restartAttempts - 1), this.options.maxRestartDelayMs);
|
|
239
|
+
const owned = this.tunnel;
|
|
240
|
+
if (owned && isAlive(owned)) {
|
|
241
|
+
// Never run two children: restart only after this one has exited
|
|
242
|
+
this.awaitingExit = true;
|
|
243
|
+
owned.process.kill("SIGKILL");
|
|
244
|
+
this.setTimer(() => this.handleKillWaitTimeout(owned), this.options.killWaitMs);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
this.tunnel = null;
|
|
248
|
+
this.scheduleRestart();
|
|
249
|
+
}
|
|
250
|
+
console.warn(`${reason}; restarting Cloudflare tunnel in ${this.pendingRestartDelay}ms (attempt ${this.restartAttempts}/${this.options.maxRestartAttempts})`);
|
|
251
|
+
if (wasConnected)
|
|
252
|
+
this.emit("disconnect", reason);
|
|
253
|
+
}
|
|
254
|
+
/** An owned child has actually exited (or never spawned) */
|
|
255
|
+
releaseChild(tunnel) {
|
|
256
|
+
if (this.tunnel !== tunnel)
|
|
257
|
+
return;
|
|
258
|
+
this.tunnel = null;
|
|
259
|
+
if (this.awaitingExit && this.state === "recovering") {
|
|
260
|
+
this.awaitingExit = false;
|
|
261
|
+
this.scheduleRestart();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
scheduleRestart() {
|
|
265
|
+
this.setTimer(() => {
|
|
266
|
+
if (this.state !== "recovering" || this.tunnel)
|
|
267
|
+
return;
|
|
268
|
+
try {
|
|
269
|
+
this.spawnChild();
|
|
87
270
|
}
|
|
88
|
-
|
|
271
|
+
catch (error) {
|
|
272
|
+
this.handleChildLoss(`Tunnel process failed to spawn: ${error instanceof Error ? error.message : String(error)}`);
|
|
273
|
+
}
|
|
274
|
+
}, this.pendingRestartDelay);
|
|
275
|
+
}
|
|
276
|
+
handleKillWaitTimeout(tunnel) {
|
|
277
|
+
this.awaitingExit = false;
|
|
278
|
+
this.state = "failed";
|
|
279
|
+
const message = `Cloudflare tunnel recovery stopped: previous cloudflared process (pid ${tunnel.process.pid}) did not exit within ${this.options.killWaitMs}ms of SIGKILL; not starting another to avoid running two; restart Cyrus to restore the tunnel`;
|
|
280
|
+
console.error(message);
|
|
281
|
+
this.emit("error", new Error(message));
|
|
282
|
+
}
|
|
283
|
+
setTimer(callback, delay) {
|
|
284
|
+
this.clearTimer();
|
|
285
|
+
this.timer = setTimeout(() => {
|
|
286
|
+
this.timer = null;
|
|
287
|
+
callback();
|
|
288
|
+
}, delay);
|
|
289
|
+
}
|
|
290
|
+
clearTimer() {
|
|
291
|
+
if (this.timer) {
|
|
292
|
+
clearTimeout(this.timer);
|
|
293
|
+
this.timer = null;
|
|
89
294
|
}
|
|
90
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* Kill the owned child (if still alive) and release it. Only used when no
|
|
298
|
+
* further child will be spawned. Failed children are killed immediately;
|
|
299
|
+
* deliberate shutdowns get a graceful SIGINT first.
|
|
300
|
+
*/
|
|
301
|
+
stopChild(force) {
|
|
302
|
+
const tunnel = this.tunnel;
|
|
303
|
+
this.tunnel = null;
|
|
304
|
+
if (!tunnel || !isAlive(tunnel))
|
|
305
|
+
return;
|
|
306
|
+
const child = tunnel.process;
|
|
307
|
+
if (force) {
|
|
308
|
+
child.kill("SIGKILL");
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
tunnel.stop();
|
|
312
|
+
const escalation = setTimeout(() => {
|
|
313
|
+
if (isAlive(tunnel)) {
|
|
314
|
+
child.kill("SIGKILL");
|
|
315
|
+
}
|
|
316
|
+
}, this.options.killGraceMs);
|
|
317
|
+
escalation.unref();
|
|
318
|
+
child.once("exit", () => clearTimeout(escalation));
|
|
319
|
+
}
|
|
91
320
|
/**
|
|
92
321
|
* Get the tunnel URL
|
|
93
322
|
*/
|
|
@@ -101,16 +330,25 @@ export class CloudflareTunnelClient extends EventEmitter {
|
|
|
101
330
|
return this.connected;
|
|
102
331
|
}
|
|
103
332
|
/**
|
|
104
|
-
* Disconnect and cleanup
|
|
333
|
+
* Disconnect and cleanup. Cancels any pending restart or startup and kills
|
|
334
|
+
* the owned child; the client will not respawn afterwards. Safe to call
|
|
335
|
+
* from any event listener.
|
|
105
336
|
*/
|
|
106
337
|
disconnect() {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
338
|
+
const previousState = this.state;
|
|
339
|
+
this.state = "stopped";
|
|
340
|
+
this.generation++;
|
|
341
|
+
this.awaitingExit = false;
|
|
342
|
+
this.clearTimer();
|
|
343
|
+
this.stopChild(false);
|
|
111
344
|
this.connected = false;
|
|
112
345
|
this.connectionCount = 0; // Reset count on disconnect for fresh reconnection logs
|
|
113
|
-
this.
|
|
346
|
+
const startup = this.startup;
|
|
347
|
+
this.startup = null;
|
|
348
|
+
startup?.reject(new Error("Cloudflare tunnel disconnected during startup"));
|
|
349
|
+
if (previousState !== "stopped") {
|
|
350
|
+
this.emit("disconnect", "Client disconnected");
|
|
351
|
+
}
|
|
114
352
|
}
|
|
115
353
|
}
|
|
116
354
|
//# sourceMappingURL=CloudflareTunnelClient.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CloudflareTunnelClient.js","sourceRoot":"","sources":["../src/CloudflareTunnelClient.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CloudflareTunnelClient.js","sourceRoot":"","sources":["../src/CloudflareTunnelClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigApiClient.d.ts","sourceRoot":"","sources":["../src/ConfigApiClient.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,qBAAqB,4BAA4B,CAAC;AAE/D;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE;QACR,eAAe,EAAE,MAAM,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ConfigApiClient.d.ts","sourceRoot":"","sources":["../src/ConfigApiClient.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,qBAAqB,4BAA4B,CAAC;AAE/D;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE;QACR,eAAe,EAAE,MAAM,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,+EAA+E;QAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AAEH,qBAAa,eAAe;IAC3B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAI9B;;;;OAIG;IACH,OAAa,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAiElE;IAED;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAWnD;CACD"}
|
package/dist/ConfigApiClient.js
CHANGED
|
@@ -40,7 +40,9 @@ export class ConfigApiClient {
|
|
|
40
40
|
}
|
|
41
41
|
// Call config API with auth key
|
|
42
42
|
const url = `${ConfigApiClient.getConfigApiUrl()}?auth_key=${encodeURIComponent(authKey)}`;
|
|
43
|
-
const response = await fetch(url
|
|
43
|
+
const response = await fetch(url, {
|
|
44
|
+
headers: { "X-Cyrus-Config-Capabilities": "self-host-port-v1" },
|
|
45
|
+
});
|
|
44
46
|
if (!response.ok) {
|
|
45
47
|
const errorText = await response.text();
|
|
46
48
|
return {
|
|
@@ -63,6 +65,12 @@ export class ConfigApiClient {
|
|
|
63
65
|
error: "Config API response missing required fields",
|
|
64
66
|
};
|
|
65
67
|
}
|
|
68
|
+
if (!ConfigApiClient.isValid(data)) {
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
error: "Config API response contains an invalid listener port",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
66
74
|
return data;
|
|
67
75
|
}
|
|
68
76
|
catch (error) {
|
|
@@ -84,7 +92,12 @@ export class ConfigApiClient {
|
|
|
84
92
|
static isValid(response) {
|
|
85
93
|
return (response.success &&
|
|
86
94
|
!!response.config?.cloudflareToken &&
|
|
87
|
-
!!response.config?.apiKey
|
|
95
|
+
!!response.config?.apiKey &&
|
|
96
|
+
(response.config.serverPort === undefined ||
|
|
97
|
+
(typeof response.config.serverPort === "number" &&
|
|
98
|
+
Number.isInteger(response.config.serverPort) &&
|
|
99
|
+
response.config.serverPort >= 1 &&
|
|
100
|
+
response.config.serverPort <= 65535)));
|
|
88
101
|
}
|
|
89
102
|
}
|
|
90
103
|
//# sourceMappingURL=ConfigApiClient.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigApiClient.js","sourceRoot":"","sources":["../src/ConfigApiClient.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,yBAAyB,CAAC;AAE/D;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC7B,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,qBAAqB,CAAC;AAC3D,CAAC;
|
|
1
|
+
{"version":3,"file":"ConfigApiClient.js","sourceRoot":"","sources":["../src/ConfigApiClient.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,yBAAyB,CAAC;AAE/D;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC7B,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,qBAAqB,CAAC;AAC3D,CAAC;AAgBD;;;GAGG;AACH,yGAAyG;AACzG,MAAM,OAAO,eAAe;IAC3B;;OAEG;IACK,MAAM,CAAC,eAAe;QAC7B,OAAO,GAAG,cAAc,EAAE,aAAa,CAAC;IACzC,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,OAAe;QACrC,IAAI,CAAC;YACJ,oBAAoB;YACpB,IACC,CAAC,OAAO;gBACR,OAAO,OAAO,KAAK,QAAQ;gBAC3B,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAC1B,CAAC;gBACF,OAAO;oBACN,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,sBAAsB;iBAC7B,CAAC;YACH,CAAC;YAED,gCAAgC;YAChC,MAAM,GAAG,GAAG,GAAG,eAAe,CAAC,eAAe,EAAE,aAAa,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3F,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBACjC,OAAO,EAAE,EAAE,6BAA6B,EAAE,mBAAmB,EAAE;aAC/D,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAClB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACxC,OAAO;oBACN,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,8BAA8B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,MAAM,SAAS,EAAE;iBAC5F,CAAC;YACH,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAsB,CAAC;YAE1D,8BAA8B;YAC9B,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACnC,OAAO;oBACN,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,yCAAyC;iBAC9D,CAAC;YACH,CAAC;YAED,2BAA2B;YAC3B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACzD,OAAO;oBACN,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,6CAA6C;iBACpD,CAAC;YACH,CAAC;YACD,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpC,OAAO;oBACN,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,uDAAuD;iBAC9D,CAAC;YACH,CAAC;YAED,OAAO,IAAI,CAAC;QACb,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;gBAC5B,OAAO;oBACN,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,8BAA8B,KAAK,CAAC,OAAO,EAAE;iBACpD,CAAC;YACH,CAAC;YACD,OAAO;gBACN,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,0CAA0C;aACjD,CAAC;QACH,CAAC;IACF,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,QAA2B;QACzC,OAAO,CACN,QAAQ,CAAC,OAAO;YAChB,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe;YAClC,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM;YACzB,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS;gBACxC,CAAC,OAAO,QAAQ,CAAC,MAAM,CAAC,UAAU,KAAK,QAAQ;oBAC9C,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC;oBAC5C,QAAQ,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC;oBAC/B,QAAQ,CAAC,MAAM,CAAC,UAAU,IAAI,KAAK,CAAC,CAAC,CACvC,CAAC;IACH,CAAC;CACD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigApiClient.test.d.ts","sourceRoot":"","sources":["../src/ConfigApiClient.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { ConfigApiClient } from "./ConfigApiClient.js";
|
|
3
|
+
afterEach(() => vi.unstubAllGlobals());
|
|
4
|
+
describe("hosted listener port contract", () => {
|
|
5
|
+
it.each([
|
|
6
|
+
undefined,
|
|
7
|
+
3456,
|
|
8
|
+
19119,
|
|
9
|
+
1,
|
|
10
|
+
65535,
|
|
11
|
+
])("accepts compatible port %s", async (serverPort) => {
|
|
12
|
+
const response = {
|
|
13
|
+
success: true,
|
|
14
|
+
config: { cloudflareToken: "fixture", apiKey: "fixture", serverPort },
|
|
15
|
+
};
|
|
16
|
+
vi.stubGlobal("fetch", vi.fn().mockResolvedValue(Response.json(response)));
|
|
17
|
+
expect(ConfigApiClient.isValid(response)).toBe(true);
|
|
18
|
+
expect(await ConfigApiClient.getConfig("fixture-auth")).toEqual(response);
|
|
19
|
+
expect(fetch).toHaveBeenCalledWith(expect.any(String), {
|
|
20
|
+
headers: { "X-Cyrus-Config-Capabilities": "self-host-port-v1" },
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
it.each([
|
|
24
|
+
null,
|
|
25
|
+
0,
|
|
26
|
+
65536,
|
|
27
|
+
-1,
|
|
28
|
+
1.5,
|
|
29
|
+
"19119",
|
|
30
|
+
"19119\nOTHER=value",
|
|
31
|
+
{},
|
|
32
|
+
])("rejects malformed port %s before auth writes", async (serverPort) => {
|
|
33
|
+
const response = {
|
|
34
|
+
success: true,
|
|
35
|
+
config: { cloudflareToken: "fixture", apiKey: "fixture", serverPort },
|
|
36
|
+
};
|
|
37
|
+
vi.stubGlobal("fetch", vi.fn().mockResolvedValue(Response.json(response)));
|
|
38
|
+
expect(ConfigApiClient.isValid(response)).toBe(false);
|
|
39
|
+
expect((await ConfigApiClient.getConfig("fixture-auth")).success).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=ConfigApiClient.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigApiClient.test.js","sourceRoot":"","sources":["../src/ConfigApiClient.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,gBAAgB,EAAE,CAAC,CAAC;AAEvC,QAAQ,CAAC,+BAA+B,EAAE,GAAG,EAAE;IAC9C,EAAE,CAAC,IAAI,CAAC;QACP,SAAS;QACT,IAAI;QACJ,KAAK;QACL,CAAC;QACD,KAAK;KACL,CAAC,CAAC,4BAA4B,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;QACrD,MAAM,QAAQ,GAAG;YAChB,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE;SACrE,CAAC;QACF,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC3E,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrD,MAAM,CAAC,MAAM,eAAe,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC1E,MAAM,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;YACtD,OAAO,EAAE,EAAE,6BAA6B,EAAE,mBAAmB,EAAE;SAC/D,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,IAAI,CAAC;QACP,IAAI;QACJ,CAAC;QACD,KAAK;QACL,CAAC,CAAC;QACF,GAAG;QACH,OAAO;QACP,oBAAoB;QACpB,EAAE;KACF,CAAC,CAAC,8CAA8C,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;QACvE,MAAM,QAAQ,GAAG;YAChB,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE;SACrE,CAAC;QACF,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC3E,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,QAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/D,MAAM,CAAC,CAAC,MAAM,eAAe,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CACrE,KAAK,CACL,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { CloudflareTunnelClient } from "./CloudflareTunnelClient.js";
|
|
1
|
+
export { CloudflareTunnelClient, type CloudflareTunnelClientOptions, } from "./CloudflareTunnelClient.js";
|
|
2
2
|
export { ConfigApiClient, DEFAULT_CYRUS_APP_URL, getCyrusAppUrl, } from "./ConfigApiClient.js";
|
|
3
3
|
export * from "./types.js";
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,sBAAsB,EACtB,KAAK,6BAA6B,GAClC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACN,eAAe,EACf,qBAAqB,EACrB,cAAc,GACd,MAAM,sBAAsB,CAAC;AAC9B,cAAc,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { CloudflareTunnelClient } from "./CloudflareTunnelClient.js";
|
|
1
|
+
export { CloudflareTunnelClient, } from "./CloudflareTunnelClient.js";
|
|
2
2
|
export { ConfigApiClient, DEFAULT_CYRUS_APP_URL, getCyrusAppUrl, } from "./ConfigApiClient.js";
|
|
3
3
|
export * from "./types.js";
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,sBAAsB,GAEtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACN,eAAe,EACf,qBAAqB,EACrB,cAAc,GACd,MAAM,sBAAsB,CAAC;AAC9B,cAAc,YAAY,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -8,4 +8,23 @@ export interface CloudflareTunnelClientEvents {
|
|
|
8
8
|
error: (error: Error) => void;
|
|
9
9
|
ready: (tunnelUrl: string) => void;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Restart policy for a cloudflared child that exits after it had connected
|
|
13
|
+
*/
|
|
14
|
+
export interface CloudflareTunnelRecoveryOptions {
|
|
15
|
+
/** Max time for a spawned child to report its first connection */
|
|
16
|
+
connectTimeoutMs?: number;
|
|
17
|
+
/** Delay before the first restart attempt; doubles per consecutive failure */
|
|
18
|
+
initialRestartDelayMs?: number;
|
|
19
|
+
/** Upper bound for the restart delay */
|
|
20
|
+
maxRestartDelayMs?: number;
|
|
21
|
+
/** Consecutive failed restarts before giving up (client stays disconnected) */
|
|
22
|
+
maxRestartAttempts?: number;
|
|
23
|
+
/** Connected duration after which an exit starts a fresh retry budget */
|
|
24
|
+
stableConnectionMs?: number;
|
|
25
|
+
/** Grace period after SIGINT before SIGKILL on deliberate disconnect */
|
|
26
|
+
killGraceMs?: number;
|
|
27
|
+
/** Max wait for a killed child to exit before restarting; recovery stops if exceeded */
|
|
28
|
+
killWaitMs?: number;
|
|
29
|
+
}
|
|
11
30
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACrC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAC9B,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACrC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAC9B,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC/C,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,wCAAwC;IACxC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+EAA+E;IAC/E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yEAAyE;IACzE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cyrus-cloudflare-tunnel-client",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.75",
|
|
4
4
|
"description": "Cloudflare tunnel client for receiving config updates and webhooks from cyrus-hosted",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/node": "^20.12.7",
|
|
27
27
|
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
28
|
-
"vitest": "^4.1.
|
|
28
|
+
"vitest": "^4.1.12"
|
|
29
29
|
},
|
|
30
30
|
"keywords": [
|
|
31
31
|
"cloudflare",
|