@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/index.js CHANGED
@@ -28,12 +28,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
30
  // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ RstreamProvider: () => RstreamProvider,
33
34
  WebTTYTerminal: () => WebTTYTerminal,
34
- useRstream: () => useRstream
35
+ useRstream: () => useRstream,
36
+ useRstreamContext: () => useRstreamContext
35
37
  });
36
- module.exports = __toCommonJS(index_exports);
38
+ module.exports = __toCommonJS(src_exports);
37
39
 
38
40
  // src/components/webtty.tsx
39
41
  var import_xterm = require("@xterm/xterm/css/xterm.css");
@@ -150,15 +152,15 @@ function WebTTYTerminal(props) {
150
152
  onComplete: (code) => {
151
153
  onComplete?.(code);
152
154
  terminal.write(`\r
153
- Process exited with code ${code}\r
154
- `);
155
+ Process exited with code ${code}.`);
156
+ terminal.write("\x1B[?25l");
155
157
  clear();
156
158
  },
157
159
  onError: (err) => {
158
160
  onError?.(err);
159
161
  terminal.write(`\r
160
- [ERROR] ${err}\r
161
- `);
162
+ [ERROR] ${err}`);
163
+ terminal.write("\x1B[?25l");
162
164
  clear();
163
165
  }
164
166
  }
@@ -174,15 +176,19 @@ Process exited with code ${code}\r
174
176
  resizeObserver.observe(ref.current);
175
177
  return () => {
176
178
  clear();
179
+ webtty.disconnect();
177
180
  terminal.dispose();
178
181
  };
179
- });
182
+ }, []);
180
183
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref, style: { height: "100%", width: "100%" } });
181
184
  }
182
185
 
183
186
  // src/hooks/use-rstream.ts
184
187
  var import_rstream = require("@rstreamlabs/rstream");
185
188
  var React2 = __toESM(require("react"));
189
+ function hasAuth(options) {
190
+ return !!options && !!options.auth;
191
+ }
186
192
  function useRstream(options) {
187
193
  const [state, setState] = React2.useState("disconnected");
188
194
  const [error, setError] = React2.useState(null);
@@ -190,72 +196,79 @@ function useRstream(options) {
190
196
  const [clients, setClients] = React2.useState([]);
191
197
  const [tunnels, setTunnels] = React2.useState([]);
192
198
  React2.useEffect(() => {
193
- if (!options?.auth) return;
199
+ if (!hasAuth(options)) return;
194
200
  let active = true;
195
201
  let watch = null;
196
202
  let timeout = null;
203
+ const schedule = () => {
204
+ if (!active) return;
205
+ if (timeout) return;
206
+ setState("connecting");
207
+ timeout = setTimeout(() => {
208
+ if (!active) return;
209
+ timeout = null;
210
+ run();
211
+ }, reconnectTimeout);
212
+ };
197
213
  const run = async () => {
198
- if (!options?.auth) return;
214
+ if (!hasAuth(options)) return;
199
215
  setState("connecting");
200
- watch = new import_rstream.Watch(
201
- { auth: options.auth },
202
- {
203
- onEvent: (event) => {
204
- if (!active) return;
205
- if (event.type.startsWith("client")) {
206
- setClients((previous) => {
207
- if (event.type === "client.created") {
208
- return [...previous, event.object];
209
- } else if (event.type === "client.updated") {
210
- return previous.map((client) => {
211
- if (client.id === event.object.id) {
212
- return event.object;
213
- }
214
- return client;
215
- });
216
- } else if (event.type === "client.deleted") {
217
- return previous.filter(
218
- (client) => client.id !== event.object.id
219
- );
220
- }
221
- return previous;
222
- });
223
- } else if (event.type.startsWith("tunnel")) {
224
- setTunnels((previous) => {
225
- if (event.type === "tunnel.created") {
226
- return [...previous, event.object];
227
- } else if (event.type === "tunnel.updated") {
228
- return previous.map((tunnel) => {
229
- if (tunnel.id === event.object.id) {
230
- return event.object;
231
- }
232
- return tunnel;
233
- });
234
- } else if (event.type === "tunnel.deleted") {
235
- return previous.filter(
236
- (tunnel) => tunnel.id !== event.object.id
237
- );
238
- }
239
- return previous;
240
- });
241
- }
242
- },
243
- onConnect: () => {
244
- if (!active) return;
245
- setState("connected");
246
- },
247
- onClose: () => {
248
- if (!active) return;
249
- watch = null;
250
- setState("connecting");
251
- timeout = setTimeout(() => {
252
- if (!active) return;
253
- timeout = null;
254
- run();
255
- }, reconnectTimeout);
216
+ watch = new import_rstream.Watch(options, {
217
+ onEvent: (event) => {
218
+ if (!active) return;
219
+ if (event.type.startsWith("client")) {
220
+ setClients((previous) => {
221
+ if (event.type === "client.created") {
222
+ return [...previous, event.object];
223
+ } else if (event.type === "client.updated") {
224
+ return previous.map((client) => {
225
+ if (client.id === event.object.id) {
226
+ return event.object;
227
+ }
228
+ return client;
229
+ });
230
+ } else if (event.type === "client.deleted") {
231
+ return previous.filter(
232
+ (client) => client.id !== event.object.id
233
+ );
234
+ }
235
+ return previous;
236
+ });
237
+ } else if (event.type.startsWith("tunnel")) {
238
+ setTunnels((previous) => {
239
+ if (event.type === "tunnel.created") {
240
+ return [...previous, event.object];
241
+ } else if (event.type === "tunnel.updated") {
242
+ return previous.map((tunnel) => {
243
+ if (tunnel.id === event.object.id) {
244
+ return event.object;
245
+ }
246
+ return tunnel;
247
+ });
248
+ } else if (event.type === "tunnel.deleted") {
249
+ return previous.filter(
250
+ (tunnel) => tunnel.id !== event.object.id
251
+ );
252
+ }
253
+ return previous;
254
+ });
256
255
  }
256
+ },
257
+ onConnect: () => {
258
+ if (!active) return;
259
+ setState("connected");
260
+ },
261
+ onClose: () => {
262
+ if (!active) return;
263
+ watch = null;
264
+ schedule();
257
265
  }
258
- );
266
+ });
267
+ try {
268
+ await watch.connect();
269
+ } catch {
270
+ schedule();
271
+ }
259
272
  };
260
273
  run();
261
274
  return () => {
@@ -298,8 +311,31 @@ function useRstream(options) {
298
311
  }, [options, state]);
299
312
  return { error, tunnels, clients };
300
313
  }
314
+
315
+ // src/providers/rstream.tsx
316
+ var React3 = __toESM(require("react"));
317
+ var import_jsx_runtime2 = require("react/jsx-runtime");
318
+ var RstreamContext = React3.createContext(
319
+ void 0
320
+ );
321
+ function useRstreamContext() {
322
+ const ctx = React3.useContext(RstreamContext);
323
+ if (!ctx)
324
+ throw new Error("useRstreamContext must be used within a RstreamProvider");
325
+ return ctx;
326
+ }
327
+ function RstreamProvider({ options, children }) {
328
+ const { error, tunnels, clients } = useRstream(options);
329
+ const value = React3.useMemo(
330
+ () => ({ error, tunnels, clients }),
331
+ [error, tunnels, clients]
332
+ );
333
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RstreamContext.Provider, { value, children });
334
+ }
301
335
  // Annotate the CommonJS export names for ESM import in node:
302
336
  0 && (module.exports = {
337
+ RstreamProvider,
303
338
  WebTTYTerminal,
304
- useRstream
339
+ useRstream,
340
+ useRstreamContext
305
341
  });
package/dist/index.mjs CHANGED
@@ -1,269 +1,17 @@
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
1
  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}\r
119
- `);
120
- clear();
121
- },
122
- onError: (err) => {
123
- onError?.(err);
124
- terminal.write(`\r
125
- [ERROR] ${err}\r
126
- `);
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
- terminal.dispose();
143
- };
144
- });
145
- return /* @__PURE__ */ jsx("div", { ref, style: { height: "100%", width: "100%" } });
146
- }
147
-
148
- // src/hooks/use-rstream.ts
149
- import { Watch } from "@rstreamlabs/rstream";
150
- import * as React2 from "react";
151
- function useRstream(options) {
152
- const [state, setState] = React2.useState("disconnected");
153
- const [error, setError] = React2.useState(null);
154
- const { reconnectTimeout = 1e3, errorTimeout = 5e3 } = options || {};
155
- const [clients, setClients] = React2.useState([]);
156
- const [tunnels, setTunnels] = React2.useState([]);
157
- React2.useEffect(() => {
158
- if (!options?.auth) return;
159
- let active = true;
160
- let watch = null;
161
- let timeout = null;
162
- const run = async () => {
163
- if (!options?.auth) return;
164
- setState("connecting");
165
- watch = new Watch(
166
- { auth: options.auth },
167
- {
168
- onEvent: (event) => {
169
- if (!active) return;
170
- if (event.type.startsWith("client")) {
171
- setClients((previous) => {
172
- if (event.type === "client.created") {
173
- return [...previous, event.object];
174
- } else if (event.type === "client.updated") {
175
- return previous.map((client) => {
176
- if (client.id === event.object.id) {
177
- return event.object;
178
- }
179
- return client;
180
- });
181
- } else if (event.type === "client.deleted") {
182
- return previous.filter(
183
- (client) => client.id !== event.object.id
184
- );
185
- }
186
- return previous;
187
- });
188
- } else if (event.type.startsWith("tunnel")) {
189
- setTunnels((previous) => {
190
- if (event.type === "tunnel.created") {
191
- return [...previous, event.object];
192
- } else if (event.type === "tunnel.updated") {
193
- return previous.map((tunnel) => {
194
- if (tunnel.id === event.object.id) {
195
- return event.object;
196
- }
197
- return tunnel;
198
- });
199
- } else if (event.type === "tunnel.deleted") {
200
- return previous.filter(
201
- (tunnel) => tunnel.id !== event.object.id
202
- );
203
- }
204
- return previous;
205
- });
206
- }
207
- },
208
- onConnect: () => {
209
- if (!active) return;
210
- setState("connected");
211
- },
212
- onClose: () => {
213
- if (!active) return;
214
- watch = null;
215
- setState("connecting");
216
- timeout = setTimeout(() => {
217
- if (!active) return;
218
- timeout = null;
219
- run();
220
- }, reconnectTimeout);
221
- }
222
- }
223
- );
224
- };
225
- run();
226
- return () => {
227
- active = false;
228
- if (watch) {
229
- watch.disconnect();
230
- watch = null;
231
- }
232
- if (timeout) {
233
- clearTimeout(timeout);
234
- timeout = null;
235
- }
236
- };
237
- }, [options, reconnectTimeout]);
238
- React2.useEffect(() => {
239
- if (options?.auth) {
240
- if (error && error.type === "danger") return;
241
- if (state !== "connected") {
242
- const timeout = setTimeout(() => {
243
- setError({
244
- message: "Failed to fetch rstream ressources. Retrying...",
245
- type: "warning"
246
- });
247
- }, errorTimeout);
248
- return () => {
249
- clearTimeout(timeout);
250
- };
251
- } else if (state === "connected") {
252
- setError(null);
253
- }
254
- } else {
255
- setError(null);
256
- }
257
- }, [options, state, error, errorTimeout]);
258
- React2.useEffect(() => {
259
- if (options?.auth === void 0 || state !== "connected") {
260
- setClients([]);
261
- setTunnels([]);
262
- }
263
- }, [options, state]);
264
- return { error, tunnels, clients };
265
- }
2
+ WebTTYTerminal
3
+ } from "./chunk-JW275FK7.mjs";
4
+ import "./chunk-2JFL7TS5.mjs";
5
+ import {
6
+ RstreamProvider,
7
+ useRstreamContext
8
+ } from "./chunk-TAYPWKHF.mjs";
9
+ import {
10
+ useRstream
11
+ } from "./chunk-2WDUPTYY.mjs";
266
12
  export {
13
+ RstreamProvider,
267
14
  WebTTYTerminal,
268
- useRstream
15
+ useRstream,
16
+ useRstreamContext
269
17
  };
@@ -0,0 +1,59 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+ import { UseRstreamOptions } from '../hooks/index.mjs';
4
+ import '@rstreamlabs/rstream';
5
+
6
+ declare function useRstreamContext(): {
7
+ error: {
8
+ message: string;
9
+ type: "warning" | "danger";
10
+ } | null;
11
+ tunnels: {
12
+ status: "online" | "offline";
13
+ client_id: string;
14
+ user_id: string;
15
+ id: string;
16
+ type?: "bytestream" | "datagram" | undefined;
17
+ name?: string | undefined;
18
+ creation_date?: Date | undefined;
19
+ publish?: boolean | undefined;
20
+ protocol?: "tls" | "dtls" | "quic" | "http" | undefined;
21
+ labels?: Record<string, string | undefined> | undefined;
22
+ geo_ip?: string[] | undefined;
23
+ trusted_ips?: string[] | undefined;
24
+ host?: string | undefined;
25
+ tls_mode?: "passthrough" | "terminated" | undefined;
26
+ tls_alpns?: string[] | undefined;
27
+ tls_min_version?: string | undefined;
28
+ tls_ciphers?: string[] | undefined;
29
+ mtls?: boolean | undefined;
30
+ mtls_ca_cert_pem?: string | undefined;
31
+ http_version?: "http/1.1" | "h2c" | "h3" | undefined;
32
+ http_use_tls?: boolean | undefined;
33
+ token_auth?: boolean | undefined;
34
+ sso?: boolean | undefined;
35
+ sso_providers?: string[] | undefined;
36
+ email_whitelist?: string[] | undefined;
37
+ email_blacklist?: string[] | undefined;
38
+ challenge?: boolean | undefined;
39
+ }[];
40
+ clients: {
41
+ status: "online" | "offline";
42
+ user_id: string;
43
+ id: string;
44
+ labels?: Record<string, string | undefined> | undefined;
45
+ details?: {
46
+ agent?: string | undefined;
47
+ os?: string | undefined;
48
+ version?: string | undefined;
49
+ protocol_version?: string | undefined;
50
+ } | undefined;
51
+ }[];
52
+ };
53
+ interface RstreamProviderProps {
54
+ options?: UseRstreamOptions;
55
+ children?: React.ReactNode;
56
+ }
57
+ declare function RstreamProvider({ options, children }: RstreamProviderProps): react_jsx_runtime.JSX.Element;
58
+
59
+ export { RstreamProvider, useRstreamContext };
@@ -0,0 +1,59 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+ import { UseRstreamOptions } from '../hooks/index.js';
4
+ import '@rstreamlabs/rstream';
5
+
6
+ declare function useRstreamContext(): {
7
+ error: {
8
+ message: string;
9
+ type: "warning" | "danger";
10
+ } | null;
11
+ tunnels: {
12
+ status: "online" | "offline";
13
+ client_id: string;
14
+ user_id: string;
15
+ id: string;
16
+ type?: "bytestream" | "datagram" | undefined;
17
+ name?: string | undefined;
18
+ creation_date?: Date | undefined;
19
+ publish?: boolean | undefined;
20
+ protocol?: "tls" | "dtls" | "quic" | "http" | undefined;
21
+ labels?: Record<string, string | undefined> | undefined;
22
+ geo_ip?: string[] | undefined;
23
+ trusted_ips?: string[] | undefined;
24
+ host?: string | undefined;
25
+ tls_mode?: "passthrough" | "terminated" | undefined;
26
+ tls_alpns?: string[] | undefined;
27
+ tls_min_version?: string | undefined;
28
+ tls_ciphers?: string[] | undefined;
29
+ mtls?: boolean | undefined;
30
+ mtls_ca_cert_pem?: string | undefined;
31
+ http_version?: "http/1.1" | "h2c" | "h3" | undefined;
32
+ http_use_tls?: boolean | undefined;
33
+ token_auth?: boolean | undefined;
34
+ sso?: boolean | undefined;
35
+ sso_providers?: string[] | undefined;
36
+ email_whitelist?: string[] | undefined;
37
+ email_blacklist?: string[] | undefined;
38
+ challenge?: boolean | undefined;
39
+ }[];
40
+ clients: {
41
+ status: "online" | "offline";
42
+ user_id: string;
43
+ id: string;
44
+ labels?: Record<string, string | undefined> | undefined;
45
+ details?: {
46
+ agent?: string | undefined;
47
+ os?: string | undefined;
48
+ version?: string | undefined;
49
+ protocol_version?: string | undefined;
50
+ } | undefined;
51
+ }[];
52
+ };
53
+ interface RstreamProviderProps {
54
+ options?: UseRstreamOptions;
55
+ children?: React.ReactNode;
56
+ }
57
+ declare function RstreamProvider({ options, children }: RstreamProviderProps): react_jsx_runtime.JSX.Element;
58
+
59
+ export { RstreamProvider, useRstreamContext };