@rstreamlabs/react 1.7.5 → 1.7.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/dist/{chunk-HTAKPV7E.mjs → chunk-5MUU3P7J.mjs} +84 -70
- package/dist/{chunk-CCMP5O6Z.mjs → chunk-DFYVJASW.mjs} +1 -1
- package/dist/{chunk-CTKB6CWW.mjs → chunk-G47GUMJ6.mjs} +64 -29
- package/dist/components/index.d.mts +0 -3
- package/dist/components/index.d.ts +0 -3
- package/dist/components/index.js +83 -67
- package/dist/components/index.mjs +1 -1
- package/dist/hooks/index.d.mts +2 -2
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +64 -29
- package/dist/hooks/index.mjs +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +147 -96
- package/dist/index.mjs +3 -3
- package/dist/providers/index.d.mts +2 -2
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.js +64 -29
- package/dist/providers/index.mjs +2 -2
- package/package.json +2 -2
|
@@ -5,62 +5,78 @@ import { Terminal } from "@xterm/xterm";
|
|
|
5
5
|
import { Unicode11Addon } from "@xterm/addon-unicode11";
|
|
6
6
|
import { WebglAddon } from "@xterm/addon-webgl";
|
|
7
7
|
import { WebLinksAddon } from "@xterm/addon-web-links";
|
|
8
|
-
import {
|
|
9
|
-
WebTTY
|
|
10
|
-
} from "@rstreamlabs/webtty";
|
|
8
|
+
import { WebTTY } from "@rstreamlabs/webtty";
|
|
11
9
|
import * as React from "react";
|
|
12
10
|
import { jsx } from "react/jsx-runtime";
|
|
13
11
|
function WebTTYTerminal(props) {
|
|
14
|
-
const {
|
|
15
|
-
// WebTTY client config
|
|
16
|
-
url,
|
|
17
|
-
sendHeartbeat,
|
|
18
|
-
heartbeatIntervalMs,
|
|
19
|
-
// Execution config
|
|
20
|
-
cmdArgs,
|
|
21
|
-
envVars,
|
|
22
|
-
allocateTty,
|
|
23
|
-
interactive,
|
|
24
|
-
username,
|
|
25
|
-
workdir,
|
|
26
|
-
// Events
|
|
27
|
-
onStdout,
|
|
28
|
-
onStdoutEos,
|
|
29
|
-
onStderr,
|
|
30
|
-
onStderrEos,
|
|
31
|
-
onConnect,
|
|
32
|
-
onComplete,
|
|
33
|
-
onError,
|
|
34
|
-
// xterm.js options
|
|
35
|
-
terminalOptions,
|
|
36
|
-
// Callbacks
|
|
37
|
-
onTerminalCreated,
|
|
38
|
-
onTitleChange
|
|
39
|
-
} = props;
|
|
40
12
|
const ref = React.useRef(null);
|
|
13
|
+
const initialPropsRef = React.useRef(props);
|
|
14
|
+
const onStdoutEvent = React.useEffectEvent((chunk) => {
|
|
15
|
+
props.onStdout?.(chunk);
|
|
16
|
+
});
|
|
17
|
+
const onStdoutEosEvent = React.useEffectEvent(() => {
|
|
18
|
+
props.onStdoutEos?.();
|
|
19
|
+
});
|
|
20
|
+
const onStderrEvent = React.useEffectEvent((chunk) => {
|
|
21
|
+
props.onStderr?.(chunk);
|
|
22
|
+
});
|
|
23
|
+
const onStderrEosEvent = React.useEffectEvent(() => {
|
|
24
|
+
props.onStderrEos?.();
|
|
25
|
+
});
|
|
26
|
+
const onConnectEvent = React.useEffectEvent(() => {
|
|
27
|
+
props.onConnect?.();
|
|
28
|
+
});
|
|
29
|
+
const onCompleteEvent = React.useEffectEvent((code) => {
|
|
30
|
+
props.onComplete?.(code);
|
|
31
|
+
});
|
|
32
|
+
const onErrorEvent = React.useEffectEvent((message) => {
|
|
33
|
+
props.onError?.(message);
|
|
34
|
+
});
|
|
35
|
+
const onTerminalCreatedEvent = React.useEffectEvent((terminal) => {
|
|
36
|
+
props.onTerminalCreated?.(terminal);
|
|
37
|
+
});
|
|
38
|
+
const onTitleChangeEvent = React.useEffectEvent((title) => {
|
|
39
|
+
props.onTitleChange?.(title);
|
|
40
|
+
});
|
|
41
41
|
React.useEffect(() => {
|
|
42
42
|
if (!ref.current) {
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
const {
|
|
46
|
+
allocateTty,
|
|
47
|
+
cmdArgs,
|
|
48
|
+
envVars,
|
|
49
|
+
heartbeatIntervalMs,
|
|
50
|
+
interactive,
|
|
51
|
+
sendHeartbeat,
|
|
52
|
+
terminalOptions,
|
|
53
|
+
url,
|
|
54
|
+
username,
|
|
55
|
+
workdir
|
|
56
|
+
} = initialPropsRef.current;
|
|
57
|
+
const runtime = {
|
|
58
|
+
connected: false,
|
|
59
|
+
disposeOnData: null,
|
|
60
|
+
disposeOnResize: null,
|
|
61
|
+
disposeOnTitleChange: null,
|
|
62
|
+
resizeObserver: null,
|
|
63
|
+
syncedCols: 0,
|
|
64
|
+
syncedRows: 0
|
|
65
|
+
};
|
|
66
|
+
const textDecoder = new TextDecoder();
|
|
67
|
+
const textEncoder = new TextEncoder();
|
|
52
68
|
const clear = () => {
|
|
53
|
-
disposeOnData?.dispose();
|
|
54
|
-
disposeOnData = null;
|
|
55
|
-
disposeOnResize?.dispose();
|
|
56
|
-
disposeOnResize = null;
|
|
57
|
-
disposeOnTitleChange?.dispose();
|
|
58
|
-
disposeOnTitleChange = null;
|
|
59
|
-
resizeObserver?.disconnect();
|
|
60
|
-
resizeObserver = null;
|
|
61
|
-
connected = false;
|
|
62
|
-
syncedRows = 0;
|
|
63
|
-
syncedCols = 0;
|
|
69
|
+
runtime.disposeOnData?.dispose();
|
|
70
|
+
runtime.disposeOnData = null;
|
|
71
|
+
runtime.disposeOnResize?.dispose();
|
|
72
|
+
runtime.disposeOnResize = null;
|
|
73
|
+
runtime.disposeOnTitleChange?.dispose();
|
|
74
|
+
runtime.disposeOnTitleChange = null;
|
|
75
|
+
runtime.resizeObserver?.disconnect();
|
|
76
|
+
runtime.resizeObserver = null;
|
|
77
|
+
runtime.connected = false;
|
|
78
|
+
runtime.syncedRows = 0;
|
|
79
|
+
runtime.syncedCols = 0;
|
|
64
80
|
};
|
|
65
81
|
const terminal = new Terminal({
|
|
66
82
|
allowProposedApi: true,
|
|
@@ -92,67 +108,65 @@ function WebTTYTerminal(props) {
|
|
|
92
108
|
return true;
|
|
93
109
|
};
|
|
94
110
|
const syncRemoteSize = (rows, cols) => {
|
|
95
|
-
if (!connected) return;
|
|
111
|
+
if (!runtime.connected) return;
|
|
96
112
|
if (rows < 1 || cols < 1) return;
|
|
97
|
-
if (rows === syncedRows && cols === syncedCols) return;
|
|
98
|
-
syncedRows = rows;
|
|
99
|
-
syncedCols = cols;
|
|
113
|
+
if (rows === runtime.syncedRows && cols === runtime.syncedCols) return;
|
|
114
|
+
runtime.syncedRows = rows;
|
|
115
|
+
runtime.syncedCols = cols;
|
|
100
116
|
try {
|
|
101
117
|
webtty.resize(rows, cols);
|
|
102
118
|
} catch (e) {
|
|
103
119
|
console.error("Cannot resize remote TTY:", e);
|
|
104
120
|
}
|
|
105
121
|
};
|
|
106
|
-
|
|
107
|
-
disposeOnTitleChange = terminal.onTitleChange((title) => {
|
|
108
|
-
|
|
122
|
+
onTerminalCreatedEvent(terminal);
|
|
123
|
+
runtime.disposeOnTitleChange = terminal.onTitleChange((title) => {
|
|
124
|
+
onTitleChangeEvent(title);
|
|
109
125
|
});
|
|
110
126
|
const webtty = new WebTTY(
|
|
111
127
|
{ url, sendHeartbeat, heartbeatIntervalMs },
|
|
112
128
|
{ cmdArgs, envVars, allocateTty, interactive, username, workdir },
|
|
113
129
|
{
|
|
114
130
|
onStdout: (chunk) => {
|
|
115
|
-
|
|
131
|
+
onStdoutEvent(chunk);
|
|
116
132
|
terminal.write(chunk);
|
|
117
133
|
},
|
|
118
134
|
onStdoutEos: () => {
|
|
119
|
-
|
|
135
|
+
onStdoutEosEvent();
|
|
120
136
|
},
|
|
121
137
|
onStderr: (chunk) => {
|
|
122
|
-
|
|
123
|
-
terminal.write(
|
|
124
|
-
"\x1B[31m" + new TextDecoder().decode(chunk) + "\x1B[0m"
|
|
125
|
-
);
|
|
138
|
+
onStderrEvent(chunk);
|
|
139
|
+
terminal.write("\x1B[31m" + textDecoder.decode(chunk) + "\x1B[0m");
|
|
126
140
|
},
|
|
127
141
|
onStderrEos: () => {
|
|
128
|
-
|
|
142
|
+
onStderrEosEvent();
|
|
129
143
|
},
|
|
130
144
|
onConnect: () => {
|
|
131
|
-
connected = true;
|
|
132
|
-
|
|
145
|
+
runtime.connected = true;
|
|
146
|
+
onConnectEvent();
|
|
133
147
|
terminal.focus();
|
|
134
148
|
fit();
|
|
135
149
|
syncRemoteSize(terminal.rows, terminal.cols);
|
|
136
|
-
disposeOnData = terminal.onData((data) => {
|
|
150
|
+
runtime.disposeOnData = terminal.onData((data) => {
|
|
137
151
|
try {
|
|
138
|
-
webtty.writeStdin(
|
|
152
|
+
webtty.writeStdin(textEncoder.encode(data));
|
|
139
153
|
} catch (e) {
|
|
140
154
|
console.error("Cannot writeStdin:", e);
|
|
141
155
|
}
|
|
142
156
|
});
|
|
143
|
-
disposeOnResize = terminal.onResize((size) => {
|
|
157
|
+
runtime.disposeOnResize = terminal.onResize((size) => {
|
|
144
158
|
syncRemoteSize(size.rows, size.cols);
|
|
145
159
|
});
|
|
146
160
|
},
|
|
147
161
|
onComplete: (code) => {
|
|
148
|
-
|
|
162
|
+
onCompleteEvent(code);
|
|
149
163
|
terminal.write(`\r
|
|
150
164
|
Process exited with code ${code}.`);
|
|
151
165
|
terminal.write("\x1B[?25l");
|
|
152
166
|
clear();
|
|
153
167
|
},
|
|
154
168
|
onError: (err) => {
|
|
155
|
-
|
|
169
|
+
onErrorEvent(err);
|
|
156
170
|
terminal.write(`\r
|
|
157
171
|
[ERROR] ${err}`);
|
|
158
172
|
terminal.write("\x1B[?25l");
|
|
@@ -161,7 +175,7 @@ Process exited with code ${code}.`);
|
|
|
161
175
|
}
|
|
162
176
|
);
|
|
163
177
|
webtty.connect();
|
|
164
|
-
resizeObserver = new ResizeObserver((entries) => {
|
|
178
|
+
runtime.resizeObserver = new ResizeObserver((entries) => {
|
|
165
179
|
for (const entry of entries) {
|
|
166
180
|
if (entry.target === ref.current) {
|
|
167
181
|
if (!fit()) return;
|
|
@@ -169,7 +183,7 @@ Process exited with code ${code}.`);
|
|
|
169
183
|
}
|
|
170
184
|
}
|
|
171
185
|
});
|
|
172
|
-
resizeObserver.observe(ref.current);
|
|
186
|
+
runtime.resizeObserver.observe(ref.current);
|
|
173
187
|
fit();
|
|
174
188
|
return () => {
|
|
175
189
|
clear();
|
|
@@ -1,36 +1,71 @@
|
|
|
1
1
|
// src/hooks/use-rstream.ts
|
|
2
|
-
import { Watch } from "@rstreamlabs/
|
|
2
|
+
import { Watch } from "@rstreamlabs/tunnels";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
function hasAuth(options) {
|
|
5
5
|
return !!options && !!options.auth;
|
|
6
6
|
}
|
|
7
|
+
function credentialsKey(credentials) {
|
|
8
|
+
if (!credentials) {
|
|
9
|
+
return "";
|
|
10
|
+
}
|
|
11
|
+
if ("token" in credentials) {
|
|
12
|
+
return `token:${credentials.token}`;
|
|
13
|
+
}
|
|
14
|
+
return `client:${credentials.clientId}:${credentials.clientSecret}`;
|
|
15
|
+
}
|
|
16
|
+
function watchConnectionKey(options) {
|
|
17
|
+
if (!hasAuth(options)) {
|
|
18
|
+
return "disabled";
|
|
19
|
+
}
|
|
20
|
+
return JSON.stringify({
|
|
21
|
+
apiUrl: options.apiUrl ?? null,
|
|
22
|
+
auth: typeof options.auth === "function" ? "function" : `token:${options.auth}`,
|
|
23
|
+
controlPlaneCredentials: credentialsKey(options.controlPlaneCredentials),
|
|
24
|
+
engine: options.engine ?? null,
|
|
25
|
+
projectEndpoint: options.projectEndpoint ?? null,
|
|
26
|
+
transport: options.transport ?? "sse"
|
|
27
|
+
});
|
|
28
|
+
}
|
|
7
29
|
function useRstream(options) {
|
|
8
30
|
const [state, setState] = React.useState("disconnected");
|
|
9
31
|
const [error, setError] = React.useState(null);
|
|
10
32
|
const { reconnectTimeout = 1e3, errorTimeout = 5e3 } = options || {};
|
|
11
33
|
const [clients, setClients] = React.useState([]);
|
|
12
34
|
const [tunnels, setTunnels] = React.useState([]);
|
|
35
|
+
const authEnabled = hasAuth(options);
|
|
36
|
+
const connectionKey = watchConnectionKey(options);
|
|
37
|
+
const optionsRef = React.useRef(options);
|
|
13
38
|
React.useEffect(() => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
39
|
+
optionsRef.current = options;
|
|
40
|
+
}, [options]);
|
|
41
|
+
const getWatchOptions = React.useEffectEvent(() => {
|
|
42
|
+
const current = optionsRef.current;
|
|
43
|
+
return hasAuth(current) ? current : void 0;
|
|
44
|
+
});
|
|
45
|
+
React.useEffect(() => {
|
|
46
|
+
if (!authEnabled) return;
|
|
47
|
+
const runtime = {
|
|
48
|
+
active: true,
|
|
49
|
+
timeout: null,
|
|
50
|
+
watch: null
|
|
51
|
+
};
|
|
18
52
|
const schedule = () => {
|
|
19
|
-
if (!active) return;
|
|
20
|
-
if (timeout) return;
|
|
53
|
+
if (!runtime.active) return;
|
|
54
|
+
if (runtime.timeout) return;
|
|
21
55
|
setState("connecting");
|
|
22
|
-
timeout = setTimeout(() => {
|
|
23
|
-
if (!active) return;
|
|
24
|
-
timeout = null;
|
|
56
|
+
runtime.timeout = setTimeout(() => {
|
|
57
|
+
if (!runtime.active) return;
|
|
58
|
+
runtime.timeout = null;
|
|
25
59
|
run();
|
|
26
60
|
}, reconnectTimeout);
|
|
27
61
|
};
|
|
28
62
|
const run = async () => {
|
|
29
|
-
|
|
63
|
+
const watchOptions = getWatchOptions();
|
|
64
|
+
if (!watchOptions) return;
|
|
30
65
|
setState("connecting");
|
|
31
|
-
watch = new Watch(
|
|
66
|
+
runtime.watch = new Watch(watchOptions, {
|
|
32
67
|
onEvent: (event) => {
|
|
33
|
-
if (!active) return;
|
|
68
|
+
if (!runtime.active) return;
|
|
34
69
|
if (event.type === "state.initial") {
|
|
35
70
|
setClients(event.object.clients);
|
|
36
71
|
setTunnels(event.object.tunnels);
|
|
@@ -73,36 +108,36 @@ function useRstream(options) {
|
|
|
73
108
|
}
|
|
74
109
|
},
|
|
75
110
|
onConnect: () => {
|
|
76
|
-
if (!active) return;
|
|
111
|
+
if (!runtime.active) return;
|
|
77
112
|
setState("connected");
|
|
78
113
|
},
|
|
79
114
|
onClose: () => {
|
|
80
|
-
if (!active) return;
|
|
81
|
-
watch = null;
|
|
115
|
+
if (!runtime.active) return;
|
|
116
|
+
runtime.watch = null;
|
|
82
117
|
schedule();
|
|
83
118
|
}
|
|
84
119
|
});
|
|
85
120
|
try {
|
|
86
|
-
await watch.connect();
|
|
121
|
+
await runtime.watch.connect();
|
|
87
122
|
} catch {
|
|
88
123
|
schedule();
|
|
89
124
|
}
|
|
90
125
|
};
|
|
91
126
|
run();
|
|
92
127
|
return () => {
|
|
93
|
-
active = false;
|
|
94
|
-
if (watch) {
|
|
95
|
-
watch.disconnect();
|
|
96
|
-
watch = null;
|
|
128
|
+
runtime.active = false;
|
|
129
|
+
if (runtime.watch) {
|
|
130
|
+
runtime.watch.disconnect();
|
|
131
|
+
runtime.watch = null;
|
|
97
132
|
}
|
|
98
|
-
if (timeout) {
|
|
99
|
-
clearTimeout(timeout);
|
|
100
|
-
timeout = null;
|
|
133
|
+
if (runtime.timeout) {
|
|
134
|
+
clearTimeout(runtime.timeout);
|
|
135
|
+
runtime.timeout = null;
|
|
101
136
|
}
|
|
102
137
|
};
|
|
103
|
-
}, [
|
|
138
|
+
}, [authEnabled, connectionKey, reconnectTimeout]);
|
|
104
139
|
React.useEffect(() => {
|
|
105
|
-
if (
|
|
140
|
+
if (authEnabled) {
|
|
106
141
|
if (error && error.type === "danger") return;
|
|
107
142
|
if (state !== "connected") {
|
|
108
143
|
const timeout = setTimeout(() => {
|
|
@@ -120,13 +155,13 @@ function useRstream(options) {
|
|
|
120
155
|
} else {
|
|
121
156
|
setError(null);
|
|
122
157
|
}
|
|
123
|
-
}, [
|
|
158
|
+
}, [authEnabled, state, error, errorTimeout]);
|
|
124
159
|
React.useEffect(() => {
|
|
125
|
-
if (
|
|
160
|
+
if (!authEnabled || state !== "connected") {
|
|
126
161
|
setClients([]);
|
|
127
162
|
setTunnels([]);
|
|
128
163
|
}
|
|
129
|
-
}, [
|
|
164
|
+
}, [authEnabled, connectionKey, state]);
|
|
130
165
|
return { state, error, tunnels, clients };
|
|
131
166
|
}
|
|
132
167
|
|
|
@@ -17,9 +17,6 @@ interface WebTTYTerminalProps extends WebTTYClientConfig, WebTTYExecutionConfig,
|
|
|
17
17
|
*/
|
|
18
18
|
onTitleChange?: (title: string) => void;
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
* A React component that binds a WebTTY session to an xterm.js instance.
|
|
22
|
-
*/
|
|
23
20
|
declare function WebTTYTerminal(props: WebTTYTerminalProps): react_jsx_runtime.JSX.Element;
|
|
24
21
|
|
|
25
22
|
export { WebTTYTerminal, type WebTTYTerminalProps };
|
|
@@ -17,9 +17,6 @@ interface WebTTYTerminalProps extends WebTTYClientConfig, WebTTYExecutionConfig,
|
|
|
17
17
|
*/
|
|
18
18
|
onTitleChange?: (title: string) => void;
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
* A React component that binds a WebTTY session to an xterm.js instance.
|
|
22
|
-
*/
|
|
23
20
|
declare function WebTTYTerminal(props: WebTTYTerminalProps): react_jsx_runtime.JSX.Element;
|
|
24
21
|
|
|
25
22
|
export { WebTTYTerminal, type WebTTYTerminalProps };
|
package/dist/components/index.js
CHANGED
|
@@ -45,56 +45,74 @@ var import_webtty = require("@rstreamlabs/webtty");
|
|
|
45
45
|
var React = __toESM(require("react"));
|
|
46
46
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
47
47
|
function WebTTYTerminal(props) {
|
|
48
|
-
const {
|
|
49
|
-
// WebTTY client config
|
|
50
|
-
url,
|
|
51
|
-
sendHeartbeat,
|
|
52
|
-
heartbeatIntervalMs,
|
|
53
|
-
// Execution config
|
|
54
|
-
cmdArgs,
|
|
55
|
-
envVars,
|
|
56
|
-
allocateTty,
|
|
57
|
-
interactive,
|
|
58
|
-
username,
|
|
59
|
-
workdir,
|
|
60
|
-
// Events
|
|
61
|
-
onStdout,
|
|
62
|
-
onStdoutEos,
|
|
63
|
-
onStderr,
|
|
64
|
-
onStderrEos,
|
|
65
|
-
onConnect,
|
|
66
|
-
onComplete,
|
|
67
|
-
onError,
|
|
68
|
-
// xterm.js options
|
|
69
|
-
terminalOptions,
|
|
70
|
-
// Callbacks
|
|
71
|
-
onTerminalCreated,
|
|
72
|
-
onTitleChange
|
|
73
|
-
} = props;
|
|
74
48
|
const ref = React.useRef(null);
|
|
49
|
+
const initialPropsRef = React.useRef(props);
|
|
50
|
+
const onStdoutEvent = React.useEffectEvent((chunk) => {
|
|
51
|
+
props.onStdout?.(chunk);
|
|
52
|
+
});
|
|
53
|
+
const onStdoutEosEvent = React.useEffectEvent(() => {
|
|
54
|
+
props.onStdoutEos?.();
|
|
55
|
+
});
|
|
56
|
+
const onStderrEvent = React.useEffectEvent((chunk) => {
|
|
57
|
+
props.onStderr?.(chunk);
|
|
58
|
+
});
|
|
59
|
+
const onStderrEosEvent = React.useEffectEvent(() => {
|
|
60
|
+
props.onStderrEos?.();
|
|
61
|
+
});
|
|
62
|
+
const onConnectEvent = React.useEffectEvent(() => {
|
|
63
|
+
props.onConnect?.();
|
|
64
|
+
});
|
|
65
|
+
const onCompleteEvent = React.useEffectEvent((code) => {
|
|
66
|
+
props.onComplete?.(code);
|
|
67
|
+
});
|
|
68
|
+
const onErrorEvent = React.useEffectEvent((message) => {
|
|
69
|
+
props.onError?.(message);
|
|
70
|
+
});
|
|
71
|
+
const onTerminalCreatedEvent = React.useEffectEvent((terminal) => {
|
|
72
|
+
props.onTerminalCreated?.(terminal);
|
|
73
|
+
});
|
|
74
|
+
const onTitleChangeEvent = React.useEffectEvent((title) => {
|
|
75
|
+
props.onTitleChange?.(title);
|
|
76
|
+
});
|
|
75
77
|
React.useEffect(() => {
|
|
76
78
|
if (!ref.current) {
|
|
77
79
|
return;
|
|
78
80
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
const {
|
|
82
|
+
allocateTty,
|
|
83
|
+
cmdArgs,
|
|
84
|
+
envVars,
|
|
85
|
+
heartbeatIntervalMs,
|
|
86
|
+
interactive,
|
|
87
|
+
sendHeartbeat,
|
|
88
|
+
terminalOptions,
|
|
89
|
+
url,
|
|
90
|
+
username,
|
|
91
|
+
workdir
|
|
92
|
+
} = initialPropsRef.current;
|
|
93
|
+
const runtime = {
|
|
94
|
+
connected: false,
|
|
95
|
+
disposeOnData: null,
|
|
96
|
+
disposeOnResize: null,
|
|
97
|
+
disposeOnTitleChange: null,
|
|
98
|
+
resizeObserver: null,
|
|
99
|
+
syncedCols: 0,
|
|
100
|
+
syncedRows: 0
|
|
101
|
+
};
|
|
102
|
+
const textDecoder = new TextDecoder();
|
|
103
|
+
const textEncoder = new TextEncoder();
|
|
86
104
|
const clear = () => {
|
|
87
|
-
disposeOnData?.dispose();
|
|
88
|
-
disposeOnData = null;
|
|
89
|
-
disposeOnResize?.dispose();
|
|
90
|
-
disposeOnResize = null;
|
|
91
|
-
disposeOnTitleChange?.dispose();
|
|
92
|
-
disposeOnTitleChange = null;
|
|
93
|
-
resizeObserver?.disconnect();
|
|
94
|
-
resizeObserver = null;
|
|
95
|
-
connected = false;
|
|
96
|
-
syncedRows = 0;
|
|
97
|
-
syncedCols = 0;
|
|
105
|
+
runtime.disposeOnData?.dispose();
|
|
106
|
+
runtime.disposeOnData = null;
|
|
107
|
+
runtime.disposeOnResize?.dispose();
|
|
108
|
+
runtime.disposeOnResize = null;
|
|
109
|
+
runtime.disposeOnTitleChange?.dispose();
|
|
110
|
+
runtime.disposeOnTitleChange = null;
|
|
111
|
+
runtime.resizeObserver?.disconnect();
|
|
112
|
+
runtime.resizeObserver = null;
|
|
113
|
+
runtime.connected = false;
|
|
114
|
+
runtime.syncedRows = 0;
|
|
115
|
+
runtime.syncedCols = 0;
|
|
98
116
|
};
|
|
99
117
|
const terminal = new import_xterm2.Terminal({
|
|
100
118
|
allowProposedApi: true,
|
|
@@ -126,67 +144,65 @@ function WebTTYTerminal(props) {
|
|
|
126
144
|
return true;
|
|
127
145
|
};
|
|
128
146
|
const syncRemoteSize = (rows, cols) => {
|
|
129
|
-
if (!connected) return;
|
|
147
|
+
if (!runtime.connected) return;
|
|
130
148
|
if (rows < 1 || cols < 1) return;
|
|
131
|
-
if (rows === syncedRows && cols === syncedCols) return;
|
|
132
|
-
syncedRows = rows;
|
|
133
|
-
syncedCols = cols;
|
|
149
|
+
if (rows === runtime.syncedRows && cols === runtime.syncedCols) return;
|
|
150
|
+
runtime.syncedRows = rows;
|
|
151
|
+
runtime.syncedCols = cols;
|
|
134
152
|
try {
|
|
135
153
|
webtty.resize(rows, cols);
|
|
136
154
|
} catch (e) {
|
|
137
155
|
console.error("Cannot resize remote TTY:", e);
|
|
138
156
|
}
|
|
139
157
|
};
|
|
140
|
-
|
|
141
|
-
disposeOnTitleChange = terminal.onTitleChange((title) => {
|
|
142
|
-
|
|
158
|
+
onTerminalCreatedEvent(terminal);
|
|
159
|
+
runtime.disposeOnTitleChange = terminal.onTitleChange((title) => {
|
|
160
|
+
onTitleChangeEvent(title);
|
|
143
161
|
});
|
|
144
162
|
const webtty = new import_webtty.WebTTY(
|
|
145
163
|
{ url, sendHeartbeat, heartbeatIntervalMs },
|
|
146
164
|
{ cmdArgs, envVars, allocateTty, interactive, username, workdir },
|
|
147
165
|
{
|
|
148
166
|
onStdout: (chunk) => {
|
|
149
|
-
|
|
167
|
+
onStdoutEvent(chunk);
|
|
150
168
|
terminal.write(chunk);
|
|
151
169
|
},
|
|
152
170
|
onStdoutEos: () => {
|
|
153
|
-
|
|
171
|
+
onStdoutEosEvent();
|
|
154
172
|
},
|
|
155
173
|
onStderr: (chunk) => {
|
|
156
|
-
|
|
157
|
-
terminal.write(
|
|
158
|
-
"\x1B[31m" + new TextDecoder().decode(chunk) + "\x1B[0m"
|
|
159
|
-
);
|
|
174
|
+
onStderrEvent(chunk);
|
|
175
|
+
terminal.write("\x1B[31m" + textDecoder.decode(chunk) + "\x1B[0m");
|
|
160
176
|
},
|
|
161
177
|
onStderrEos: () => {
|
|
162
|
-
|
|
178
|
+
onStderrEosEvent();
|
|
163
179
|
},
|
|
164
180
|
onConnect: () => {
|
|
165
|
-
connected = true;
|
|
166
|
-
|
|
181
|
+
runtime.connected = true;
|
|
182
|
+
onConnectEvent();
|
|
167
183
|
terminal.focus();
|
|
168
184
|
fit();
|
|
169
185
|
syncRemoteSize(terminal.rows, terminal.cols);
|
|
170
|
-
disposeOnData = terminal.onData((data) => {
|
|
186
|
+
runtime.disposeOnData = terminal.onData((data) => {
|
|
171
187
|
try {
|
|
172
|
-
webtty.writeStdin(
|
|
188
|
+
webtty.writeStdin(textEncoder.encode(data));
|
|
173
189
|
} catch (e) {
|
|
174
190
|
console.error("Cannot writeStdin:", e);
|
|
175
191
|
}
|
|
176
192
|
});
|
|
177
|
-
disposeOnResize = terminal.onResize((size) => {
|
|
193
|
+
runtime.disposeOnResize = terminal.onResize((size) => {
|
|
178
194
|
syncRemoteSize(size.rows, size.cols);
|
|
179
195
|
});
|
|
180
196
|
},
|
|
181
197
|
onComplete: (code) => {
|
|
182
|
-
|
|
198
|
+
onCompleteEvent(code);
|
|
183
199
|
terminal.write(`\r
|
|
184
200
|
Process exited with code ${code}.`);
|
|
185
201
|
terminal.write("\x1B[?25l");
|
|
186
202
|
clear();
|
|
187
203
|
},
|
|
188
204
|
onError: (err) => {
|
|
189
|
-
|
|
205
|
+
onErrorEvent(err);
|
|
190
206
|
terminal.write(`\r
|
|
191
207
|
[ERROR] ${err}`);
|
|
192
208
|
terminal.write("\x1B[?25l");
|
|
@@ -195,7 +211,7 @@ Process exited with code ${code}.`);
|
|
|
195
211
|
}
|
|
196
212
|
);
|
|
197
213
|
webtty.connect();
|
|
198
|
-
resizeObserver = new ResizeObserver((entries) => {
|
|
214
|
+
runtime.resizeObserver = new ResizeObserver((entries) => {
|
|
199
215
|
for (const entry of entries) {
|
|
200
216
|
if (entry.target === ref.current) {
|
|
201
217
|
if (!fit()) return;
|
|
@@ -203,7 +219,7 @@ Process exited with code ${code}.`);
|
|
|
203
219
|
}
|
|
204
220
|
}
|
|
205
221
|
});
|
|
206
|
-
resizeObserver.observe(ref.current);
|
|
222
|
+
runtime.resizeObserver.observe(ref.current);
|
|
207
223
|
fit();
|
|
208
224
|
return () => {
|
|
209
225
|
clear();
|
package/dist/hooks/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WatchConfig } from '@rstreamlabs/
|
|
1
|
+
import { WatchConfig } from '@rstreamlabs/tunnels';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Configuration for the UseRstream hook.
|
|
@@ -30,7 +30,7 @@ interface UseRstreamOptions extends Partial<WatchConfig> {
|
|
|
30
30
|
* @returns An object with the current error (if any), and arrays of tunnels and clients.
|
|
31
31
|
*/
|
|
32
32
|
declare function useRstream(options?: UseRstreamOptions): {
|
|
33
|
-
state: "
|
|
33
|
+
state: "connecting" | "connected" | "disconnected";
|
|
34
34
|
error: {
|
|
35
35
|
message: string;
|
|
36
36
|
type: "warning" | "danger";
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WatchConfig } from '@rstreamlabs/
|
|
1
|
+
import { WatchConfig } from '@rstreamlabs/tunnels';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Configuration for the UseRstream hook.
|
|
@@ -30,7 +30,7 @@ interface UseRstreamOptions extends Partial<WatchConfig> {
|
|
|
30
30
|
* @returns An object with the current error (if any), and arrays of tunnels and clients.
|
|
31
31
|
*/
|
|
32
32
|
declare function useRstream(options?: UseRstreamOptions): {
|
|
33
|
-
state: "
|
|
33
|
+
state: "connecting" | "connected" | "disconnected";
|
|
34
34
|
error: {
|
|
35
35
|
message: string;
|
|
36
36
|
type: "warning" | "danger";
|