@rstreamlabs/react 1.3.0 → 1.5.0
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-2JFL7TS5.mjs +0 -0
- package/dist/chunk-2WDUPTYY.mjs +132 -0
- package/dist/chunk-JW275FK7.mjs +151 -0
- package/dist/chunk-TAYPWKHF.mjs +29 -0
- package/dist/components/index.d.mts +21 -0
- package/dist/components/index.d.ts +21 -0
- package/dist/components/index.js +185 -0
- package/dist/components/index.mjs +6 -0
- package/dist/hooks/index.d.mts +80 -0
- package/dist/hooks/index.d.ts +80 -0
- package/dist/hooks/index.js +168 -0
- package/dist/hooks/index.mjs +7 -0
- package/dist/index.d.mts +8 -103
- package/dist/index.d.ts +8 -103
- package/dist/index.js +105 -69
- package/dist/index.mjs +13 -265
- package/dist/providers/index.d.mts +59 -0
- package/dist/providers/index.d.ts +59 -0
- package/dist/providers/index.js +191 -0
- package/dist/providers/index.mjs +9 -0
- package/package.json +33 -10
|
File without changes
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// src/hooks/use-rstream.ts
|
|
2
|
+
import { Watch } from "@rstreamlabs/rstream";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
function hasAuth(options) {
|
|
5
|
+
return !!options && !!options.auth;
|
|
6
|
+
}
|
|
7
|
+
function useRstream(options) {
|
|
8
|
+
const [state, setState] = React.useState("disconnected");
|
|
9
|
+
const [error, setError] = React.useState(null);
|
|
10
|
+
const { reconnectTimeout = 1e3, errorTimeout = 5e3 } = options || {};
|
|
11
|
+
const [clients, setClients] = React.useState([]);
|
|
12
|
+
const [tunnels, setTunnels] = React.useState([]);
|
|
13
|
+
React.useEffect(() => {
|
|
14
|
+
if (!hasAuth(options)) return;
|
|
15
|
+
let active = true;
|
|
16
|
+
let watch = null;
|
|
17
|
+
let timeout = null;
|
|
18
|
+
const schedule = () => {
|
|
19
|
+
if (!active) return;
|
|
20
|
+
if (timeout) return;
|
|
21
|
+
setState("connecting");
|
|
22
|
+
timeout = setTimeout(() => {
|
|
23
|
+
if (!active) return;
|
|
24
|
+
timeout = null;
|
|
25
|
+
run();
|
|
26
|
+
}, reconnectTimeout);
|
|
27
|
+
};
|
|
28
|
+
const run = async () => {
|
|
29
|
+
if (!hasAuth(options)) return;
|
|
30
|
+
setState("connecting");
|
|
31
|
+
watch = new Watch(options, {
|
|
32
|
+
onEvent: (event) => {
|
|
33
|
+
if (!active) return;
|
|
34
|
+
if (event.type.startsWith("client")) {
|
|
35
|
+
setClients((previous) => {
|
|
36
|
+
if (event.type === "client.created") {
|
|
37
|
+
return [...previous, event.object];
|
|
38
|
+
} else if (event.type === "client.updated") {
|
|
39
|
+
return previous.map((client) => {
|
|
40
|
+
if (client.id === event.object.id) {
|
|
41
|
+
return event.object;
|
|
42
|
+
}
|
|
43
|
+
return client;
|
|
44
|
+
});
|
|
45
|
+
} else if (event.type === "client.deleted") {
|
|
46
|
+
return previous.filter(
|
|
47
|
+
(client) => client.id !== event.object.id
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return previous;
|
|
51
|
+
});
|
|
52
|
+
} else if (event.type.startsWith("tunnel")) {
|
|
53
|
+
setTunnels((previous) => {
|
|
54
|
+
if (event.type === "tunnel.created") {
|
|
55
|
+
return [...previous, event.object];
|
|
56
|
+
} else if (event.type === "tunnel.updated") {
|
|
57
|
+
return previous.map((tunnel) => {
|
|
58
|
+
if (tunnel.id === event.object.id) {
|
|
59
|
+
return event.object;
|
|
60
|
+
}
|
|
61
|
+
return tunnel;
|
|
62
|
+
});
|
|
63
|
+
} else if (event.type === "tunnel.deleted") {
|
|
64
|
+
return previous.filter(
|
|
65
|
+
(tunnel) => tunnel.id !== event.object.id
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return previous;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
onConnect: () => {
|
|
73
|
+
if (!active) return;
|
|
74
|
+
setState("connected");
|
|
75
|
+
},
|
|
76
|
+
onClose: () => {
|
|
77
|
+
if (!active) return;
|
|
78
|
+
watch = null;
|
|
79
|
+
schedule();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
try {
|
|
83
|
+
await watch.connect();
|
|
84
|
+
} catch {
|
|
85
|
+
schedule();
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
run();
|
|
89
|
+
return () => {
|
|
90
|
+
active = false;
|
|
91
|
+
if (watch) {
|
|
92
|
+
watch.disconnect();
|
|
93
|
+
watch = null;
|
|
94
|
+
}
|
|
95
|
+
if (timeout) {
|
|
96
|
+
clearTimeout(timeout);
|
|
97
|
+
timeout = null;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}, [options, reconnectTimeout]);
|
|
101
|
+
React.useEffect(() => {
|
|
102
|
+
if (options?.auth) {
|
|
103
|
+
if (error && error.type === "danger") return;
|
|
104
|
+
if (state !== "connected") {
|
|
105
|
+
const timeout = setTimeout(() => {
|
|
106
|
+
setError({
|
|
107
|
+
message: "Failed to fetch rstream ressources. Retrying...",
|
|
108
|
+
type: "warning"
|
|
109
|
+
});
|
|
110
|
+
}, errorTimeout);
|
|
111
|
+
return () => {
|
|
112
|
+
clearTimeout(timeout);
|
|
113
|
+
};
|
|
114
|
+
} else if (state === "connected") {
|
|
115
|
+
setError(null);
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
setError(null);
|
|
119
|
+
}
|
|
120
|
+
}, [options, state, error, errorTimeout]);
|
|
121
|
+
React.useEffect(() => {
|
|
122
|
+
if (options?.auth === void 0 || state !== "connected") {
|
|
123
|
+
setClients([]);
|
|
124
|
+
setTunnels([]);
|
|
125
|
+
}
|
|
126
|
+
}, [options, state]);
|
|
127
|
+
return { error, tunnels, clients };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export {
|
|
131
|
+
useRstream
|
|
132
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// src/components/webtty.tsx
|
|
2
|
+
import "@xterm/xterm/css/xterm.css";
|
|
3
|
+
import { FitAddon } from "@xterm/addon-fit";
|
|
4
|
+
import { Terminal } from "@xterm/xterm";
|
|
5
|
+
import { Unicode11Addon } from "@xterm/addon-unicode11";
|
|
6
|
+
import { WebglAddon } from "@xterm/addon-webgl";
|
|
7
|
+
import { WebLinksAddon } from "@xterm/addon-web-links";
|
|
8
|
+
import {
|
|
9
|
+
WebTTY
|
|
10
|
+
} from "@rstreamlabs/webtty";
|
|
11
|
+
import * as React from "react";
|
|
12
|
+
import { jsx } from "react/jsx-runtime";
|
|
13
|
+
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
|
+
} = props;
|
|
39
|
+
const ref = React.useRef(null);
|
|
40
|
+
React.useEffect(() => {
|
|
41
|
+
if (!ref.current) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
let disposeOnData = null;
|
|
45
|
+
let disposeOnResize = null;
|
|
46
|
+
let resizeObserver = null;
|
|
47
|
+
const clear = () => {
|
|
48
|
+
disposeOnData?.dispose();
|
|
49
|
+
disposeOnData = null;
|
|
50
|
+
disposeOnResize?.dispose();
|
|
51
|
+
disposeOnResize = null;
|
|
52
|
+
resizeObserver?.disconnect();
|
|
53
|
+
resizeObserver = null;
|
|
54
|
+
};
|
|
55
|
+
const terminal = new Terminal({
|
|
56
|
+
allowProposedApi: true,
|
|
57
|
+
cursorBlink: false,
|
|
58
|
+
scrollback: 1e4,
|
|
59
|
+
...terminalOptions
|
|
60
|
+
});
|
|
61
|
+
const fitAddon = new FitAddon();
|
|
62
|
+
terminal.loadAddon(fitAddon);
|
|
63
|
+
terminal.loadAddon(new WebLinksAddon());
|
|
64
|
+
terminal.loadAddon(new Unicode11Addon());
|
|
65
|
+
try {
|
|
66
|
+
terminal.loadAddon(new WebglAddon());
|
|
67
|
+
} catch (err) {
|
|
68
|
+
console.warn("WebGL addon could not be loaded:", err);
|
|
69
|
+
}
|
|
70
|
+
terminal.open(ref.current);
|
|
71
|
+
onTerminalCreated?.(terminal);
|
|
72
|
+
const webtty = new WebTTY(
|
|
73
|
+
{ url, sendHeartbeat, heartbeatIntervalMs },
|
|
74
|
+
{ cmdArgs, envVars, allocateTty, interactive, username, workdir },
|
|
75
|
+
{
|
|
76
|
+
onStdout: (chunk) => {
|
|
77
|
+
onStdout?.(chunk);
|
|
78
|
+
terminal.write(chunk);
|
|
79
|
+
},
|
|
80
|
+
onStdoutEos: () => {
|
|
81
|
+
onStdoutEos?.();
|
|
82
|
+
},
|
|
83
|
+
onStderr: (chunk) => {
|
|
84
|
+
onStderr?.(chunk);
|
|
85
|
+
terminal.write(
|
|
86
|
+
"\x1B[31m" + new TextDecoder().decode(chunk) + "\x1B[0m"
|
|
87
|
+
);
|
|
88
|
+
},
|
|
89
|
+
onStderrEos: () => {
|
|
90
|
+
onStderrEos?.();
|
|
91
|
+
},
|
|
92
|
+
onConnect: () => {
|
|
93
|
+
onConnect?.();
|
|
94
|
+
terminal.focus();
|
|
95
|
+
try {
|
|
96
|
+
webtty.resize(terminal.rows, terminal.cols);
|
|
97
|
+
} catch (e) {
|
|
98
|
+
console.error("Cannot resize remote TTY:", e);
|
|
99
|
+
}
|
|
100
|
+
disposeOnData = terminal.onData((data) => {
|
|
101
|
+
try {
|
|
102
|
+
webtty.writeStdin(new TextEncoder().encode(data));
|
|
103
|
+
} catch (e) {
|
|
104
|
+
console.error("Cannot writeStdin:", e);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
disposeOnResize = terminal.onResize((size) => {
|
|
108
|
+
try {
|
|
109
|
+
webtty.resize(size.rows, size.cols);
|
|
110
|
+
} catch (e) {
|
|
111
|
+
console.error("Cannot resize remote TTY:", e);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
onComplete: (code) => {
|
|
116
|
+
onComplete?.(code);
|
|
117
|
+
terminal.write(`\r
|
|
118
|
+
Process exited with code ${code}.`);
|
|
119
|
+
terminal.write("\x1B[?25l");
|
|
120
|
+
clear();
|
|
121
|
+
},
|
|
122
|
+
onError: (err) => {
|
|
123
|
+
onError?.(err);
|
|
124
|
+
terminal.write(`\r
|
|
125
|
+
[ERROR] ${err}`);
|
|
126
|
+
terminal.write("\x1B[?25l");
|
|
127
|
+
clear();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
webtty.connect();
|
|
132
|
+
resizeObserver = new ResizeObserver((entries) => {
|
|
133
|
+
for (const entry of entries) {
|
|
134
|
+
if (entry.target === ref.current) {
|
|
135
|
+
fitAddon.fit();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
resizeObserver.observe(ref.current);
|
|
140
|
+
return () => {
|
|
141
|
+
clear();
|
|
142
|
+
webtty.disconnect();
|
|
143
|
+
terminal.dispose();
|
|
144
|
+
};
|
|
145
|
+
}, []);
|
|
146
|
+
return /* @__PURE__ */ jsx("div", { ref, style: { height: "100%", width: "100%" } });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export {
|
|
150
|
+
WebTTYTerminal
|
|
151
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useRstream
|
|
3
|
+
} from "./chunk-2WDUPTYY.mjs";
|
|
4
|
+
|
|
5
|
+
// src/providers/rstream.tsx
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
var RstreamContext = React.createContext(
|
|
9
|
+
void 0
|
|
10
|
+
);
|
|
11
|
+
function useRstreamContext() {
|
|
12
|
+
const ctx = React.useContext(RstreamContext);
|
|
13
|
+
if (!ctx)
|
|
14
|
+
throw new Error("useRstreamContext must be used within a RstreamProvider");
|
|
15
|
+
return ctx;
|
|
16
|
+
}
|
|
17
|
+
function RstreamProvider({ options, children }) {
|
|
18
|
+
const { error, tunnels, clients } = useRstream(options);
|
|
19
|
+
const value = React.useMemo(
|
|
20
|
+
() => ({ error, tunnels, clients }),
|
|
21
|
+
[error, tunnels, clients]
|
|
22
|
+
);
|
|
23
|
+
return /* @__PURE__ */ jsx(RstreamContext.Provider, { value, children });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
useRstreamContext,
|
|
28
|
+
RstreamProvider
|
|
29
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ITerminalOptions, Terminal } from '@xterm/xterm';
|
|
3
|
+
import { WebTTYClientConfig, WebTTYExecutionConfig, WebTTYEvents } from '@rstreamlabs/webtty';
|
|
4
|
+
|
|
5
|
+
interface WebTTYTerminalProps extends WebTTYClientConfig, WebTTYExecutionConfig, WebTTYEvents {
|
|
6
|
+
/**
|
|
7
|
+
* xterm.js TerminalOptions override
|
|
8
|
+
*/
|
|
9
|
+
terminalOptions?: ITerminalOptions;
|
|
10
|
+
/**
|
|
11
|
+
* Called once the xterm Terminal is created (before connect).
|
|
12
|
+
* Can be used to add your own add-ons or manipulate the Terminal instance.
|
|
13
|
+
*/
|
|
14
|
+
onTerminalCreated?: (terminal: Terminal) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A React component that binds a WebTTY session to an xterm.js instance.
|
|
18
|
+
*/
|
|
19
|
+
declare function WebTTYTerminal(props: WebTTYTerminalProps): react_jsx_runtime.JSX.Element;
|
|
20
|
+
|
|
21
|
+
export { WebTTYTerminal, type WebTTYTerminalProps };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ITerminalOptions, Terminal } from '@xterm/xterm';
|
|
3
|
+
import { WebTTYClientConfig, WebTTYExecutionConfig, WebTTYEvents } from '@rstreamlabs/webtty';
|
|
4
|
+
|
|
5
|
+
interface WebTTYTerminalProps extends WebTTYClientConfig, WebTTYExecutionConfig, WebTTYEvents {
|
|
6
|
+
/**
|
|
7
|
+
* xterm.js TerminalOptions override
|
|
8
|
+
*/
|
|
9
|
+
terminalOptions?: ITerminalOptions;
|
|
10
|
+
/**
|
|
11
|
+
* Called once the xterm Terminal is created (before connect).
|
|
12
|
+
* Can be used to add your own add-ons or manipulate the Terminal instance.
|
|
13
|
+
*/
|
|
14
|
+
onTerminalCreated?: (terminal: Terminal) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A React component that binds a WebTTY session to an xterm.js instance.
|
|
18
|
+
*/
|
|
19
|
+
declare function WebTTYTerminal(props: WebTTYTerminalProps): react_jsx_runtime.JSX.Element;
|
|
20
|
+
|
|
21
|
+
export { WebTTYTerminal, type WebTTYTerminalProps };
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/components/index.ts
|
|
31
|
+
var components_exports = {};
|
|
32
|
+
__export(components_exports, {
|
|
33
|
+
WebTTYTerminal: () => WebTTYTerminal
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(components_exports);
|
|
36
|
+
|
|
37
|
+
// src/components/webtty.tsx
|
|
38
|
+
var import_xterm = require("@xterm/xterm/css/xterm.css");
|
|
39
|
+
var import_addon_fit = require("@xterm/addon-fit");
|
|
40
|
+
var import_xterm2 = require("@xterm/xterm");
|
|
41
|
+
var import_addon_unicode11 = require("@xterm/addon-unicode11");
|
|
42
|
+
var import_addon_webgl = require("@xterm/addon-webgl");
|
|
43
|
+
var import_addon_web_links = require("@xterm/addon-web-links");
|
|
44
|
+
var import_webtty = require("@rstreamlabs/webtty");
|
|
45
|
+
var React = __toESM(require("react"));
|
|
46
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
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
|
+
} = props;
|
|
73
|
+
const ref = React.useRef(null);
|
|
74
|
+
React.useEffect(() => {
|
|
75
|
+
if (!ref.current) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
let disposeOnData = null;
|
|
79
|
+
let disposeOnResize = null;
|
|
80
|
+
let resizeObserver = null;
|
|
81
|
+
const clear = () => {
|
|
82
|
+
disposeOnData?.dispose();
|
|
83
|
+
disposeOnData = null;
|
|
84
|
+
disposeOnResize?.dispose();
|
|
85
|
+
disposeOnResize = null;
|
|
86
|
+
resizeObserver?.disconnect();
|
|
87
|
+
resizeObserver = null;
|
|
88
|
+
};
|
|
89
|
+
const terminal = new import_xterm2.Terminal({
|
|
90
|
+
allowProposedApi: true,
|
|
91
|
+
cursorBlink: false,
|
|
92
|
+
scrollback: 1e4,
|
|
93
|
+
...terminalOptions
|
|
94
|
+
});
|
|
95
|
+
const fitAddon = new import_addon_fit.FitAddon();
|
|
96
|
+
terminal.loadAddon(fitAddon);
|
|
97
|
+
terminal.loadAddon(new import_addon_web_links.WebLinksAddon());
|
|
98
|
+
terminal.loadAddon(new import_addon_unicode11.Unicode11Addon());
|
|
99
|
+
try {
|
|
100
|
+
terminal.loadAddon(new import_addon_webgl.WebglAddon());
|
|
101
|
+
} catch (err) {
|
|
102
|
+
console.warn("WebGL addon could not be loaded:", err);
|
|
103
|
+
}
|
|
104
|
+
terminal.open(ref.current);
|
|
105
|
+
onTerminalCreated?.(terminal);
|
|
106
|
+
const webtty = new import_webtty.WebTTY(
|
|
107
|
+
{ url, sendHeartbeat, heartbeatIntervalMs },
|
|
108
|
+
{ cmdArgs, envVars, allocateTty, interactive, username, workdir },
|
|
109
|
+
{
|
|
110
|
+
onStdout: (chunk) => {
|
|
111
|
+
onStdout?.(chunk);
|
|
112
|
+
terminal.write(chunk);
|
|
113
|
+
},
|
|
114
|
+
onStdoutEos: () => {
|
|
115
|
+
onStdoutEos?.();
|
|
116
|
+
},
|
|
117
|
+
onStderr: (chunk) => {
|
|
118
|
+
onStderr?.(chunk);
|
|
119
|
+
terminal.write(
|
|
120
|
+
"\x1B[31m" + new TextDecoder().decode(chunk) + "\x1B[0m"
|
|
121
|
+
);
|
|
122
|
+
},
|
|
123
|
+
onStderrEos: () => {
|
|
124
|
+
onStderrEos?.();
|
|
125
|
+
},
|
|
126
|
+
onConnect: () => {
|
|
127
|
+
onConnect?.();
|
|
128
|
+
terminal.focus();
|
|
129
|
+
try {
|
|
130
|
+
webtty.resize(terminal.rows, terminal.cols);
|
|
131
|
+
} catch (e) {
|
|
132
|
+
console.error("Cannot resize remote TTY:", e);
|
|
133
|
+
}
|
|
134
|
+
disposeOnData = terminal.onData((data) => {
|
|
135
|
+
try {
|
|
136
|
+
webtty.writeStdin(new TextEncoder().encode(data));
|
|
137
|
+
} catch (e) {
|
|
138
|
+
console.error("Cannot writeStdin:", e);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
disposeOnResize = terminal.onResize((size) => {
|
|
142
|
+
try {
|
|
143
|
+
webtty.resize(size.rows, size.cols);
|
|
144
|
+
} catch (e) {
|
|
145
|
+
console.error("Cannot resize remote TTY:", e);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
onComplete: (code) => {
|
|
150
|
+
onComplete?.(code);
|
|
151
|
+
terminal.write(`\r
|
|
152
|
+
Process exited with code ${code}.`);
|
|
153
|
+
terminal.write("\x1B[?25l");
|
|
154
|
+
clear();
|
|
155
|
+
},
|
|
156
|
+
onError: (err) => {
|
|
157
|
+
onError?.(err);
|
|
158
|
+
terminal.write(`\r
|
|
159
|
+
[ERROR] ${err}`);
|
|
160
|
+
terminal.write("\x1B[?25l");
|
|
161
|
+
clear();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
);
|
|
165
|
+
webtty.connect();
|
|
166
|
+
resizeObserver = new ResizeObserver((entries) => {
|
|
167
|
+
for (const entry of entries) {
|
|
168
|
+
if (entry.target === ref.current) {
|
|
169
|
+
fitAddon.fit();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
resizeObserver.observe(ref.current);
|
|
174
|
+
return () => {
|
|
175
|
+
clear();
|
|
176
|
+
webtty.disconnect();
|
|
177
|
+
terminal.dispose();
|
|
178
|
+
};
|
|
179
|
+
}, []);
|
|
180
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref, style: { height: "100%", width: "100%" } });
|
|
181
|
+
}
|
|
182
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
183
|
+
0 && (module.exports = {
|
|
184
|
+
WebTTYTerminal
|
|
185
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { WatchConfig } from '@rstreamlabs/rstream';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for the UseRstream hook.
|
|
5
|
+
*/
|
|
6
|
+
interface UseRstreamOptions extends Partial<WatchConfig> {
|
|
7
|
+
/**
|
|
8
|
+
* Timeout (in milliseconds) to wait before attempting to reconnect
|
|
9
|
+
* after the SSE connection is closed.
|
|
10
|
+
*
|
|
11
|
+
* @default 1000
|
|
12
|
+
*/
|
|
13
|
+
reconnectTimeout?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Timeout (in milliseconds) to wait before showing an error message
|
|
16
|
+
* when the connection is not yet established.
|
|
17
|
+
*
|
|
18
|
+
* @default 5000
|
|
19
|
+
*/
|
|
20
|
+
errorTimeout?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A React hook to subscribe to rstream resources.
|
|
24
|
+
*
|
|
25
|
+
* This hook fetches the list of clients and tunnels from the rstream API.
|
|
26
|
+
* It handles automatic reconnection and displays a warning if the connection
|
|
27
|
+
* is not established within the timeout.
|
|
28
|
+
*
|
|
29
|
+
* @param options - The configuration options for the hook.
|
|
30
|
+
* @returns An object with the current error (if any), and arrays of tunnels and clients.
|
|
31
|
+
*/
|
|
32
|
+
declare function useRstream(options?: UseRstreamOptions): {
|
|
33
|
+
error: {
|
|
34
|
+
message: string;
|
|
35
|
+
type: "warning" | "danger";
|
|
36
|
+
} | null;
|
|
37
|
+
tunnels: {
|
|
38
|
+
status: "online" | "offline";
|
|
39
|
+
client_id: string;
|
|
40
|
+
user_id: string;
|
|
41
|
+
id: string;
|
|
42
|
+
type?: "bytestream" | "datagram" | undefined;
|
|
43
|
+
name?: string | undefined;
|
|
44
|
+
creation_date?: Date | undefined;
|
|
45
|
+
publish?: boolean | undefined;
|
|
46
|
+
protocol?: "tls" | "dtls" | "quic" | "http" | undefined;
|
|
47
|
+
labels?: Record<string, string | undefined> | undefined;
|
|
48
|
+
geo_ip?: string[] | undefined;
|
|
49
|
+
trusted_ips?: string[] | undefined;
|
|
50
|
+
host?: string | undefined;
|
|
51
|
+
tls_mode?: "passthrough" | "terminated" | undefined;
|
|
52
|
+
tls_alpns?: string[] | undefined;
|
|
53
|
+
tls_min_version?: string | undefined;
|
|
54
|
+
tls_ciphers?: string[] | undefined;
|
|
55
|
+
mtls?: boolean | undefined;
|
|
56
|
+
mtls_ca_cert_pem?: string | undefined;
|
|
57
|
+
http_version?: "http/1.1" | "h2c" | "h3" | undefined;
|
|
58
|
+
http_use_tls?: boolean | undefined;
|
|
59
|
+
token_auth?: boolean | undefined;
|
|
60
|
+
sso?: boolean | undefined;
|
|
61
|
+
sso_providers?: string[] | undefined;
|
|
62
|
+
email_whitelist?: string[] | undefined;
|
|
63
|
+
email_blacklist?: string[] | undefined;
|
|
64
|
+
challenge?: boolean | undefined;
|
|
65
|
+
}[];
|
|
66
|
+
clients: {
|
|
67
|
+
status: "online" | "offline";
|
|
68
|
+
user_id: string;
|
|
69
|
+
id: string;
|
|
70
|
+
labels?: Record<string, string | undefined> | undefined;
|
|
71
|
+
details?: {
|
|
72
|
+
agent?: string | undefined;
|
|
73
|
+
os?: string | undefined;
|
|
74
|
+
version?: string | undefined;
|
|
75
|
+
protocol_version?: string | undefined;
|
|
76
|
+
} | undefined;
|
|
77
|
+
}[];
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export { type UseRstreamOptions, useRstream };
|