@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.
@@ -35,38 +35,73 @@ __export(hooks_exports, {
35
35
  module.exports = __toCommonJS(hooks_exports);
36
36
 
37
37
  // src/hooks/use-rstream.ts
38
- var import_rstream = require("@rstreamlabs/rstream");
38
+ var import_tunnels = require("@rstreamlabs/tunnels");
39
39
  var React = __toESM(require("react"));
40
40
  function hasAuth(options) {
41
41
  return !!options && !!options.auth;
42
42
  }
43
+ function credentialsKey(credentials) {
44
+ if (!credentials) {
45
+ return "";
46
+ }
47
+ if ("token" in credentials) {
48
+ return `token:${credentials.token}`;
49
+ }
50
+ return `client:${credentials.clientId}:${credentials.clientSecret}`;
51
+ }
52
+ function watchConnectionKey(options) {
53
+ if (!hasAuth(options)) {
54
+ return "disabled";
55
+ }
56
+ return JSON.stringify({
57
+ apiUrl: options.apiUrl ?? null,
58
+ auth: typeof options.auth === "function" ? "function" : `token:${options.auth}`,
59
+ controlPlaneCredentials: credentialsKey(options.controlPlaneCredentials),
60
+ engine: options.engine ?? null,
61
+ projectEndpoint: options.projectEndpoint ?? null,
62
+ transport: options.transport ?? "sse"
63
+ });
64
+ }
43
65
  function useRstream(options) {
44
66
  const [state, setState] = React.useState("disconnected");
45
67
  const [error, setError] = React.useState(null);
46
68
  const { reconnectTimeout = 1e3, errorTimeout = 5e3 } = options || {};
47
69
  const [clients, setClients] = React.useState([]);
48
70
  const [tunnels, setTunnels] = React.useState([]);
71
+ const authEnabled = hasAuth(options);
72
+ const connectionKey = watchConnectionKey(options);
73
+ const optionsRef = React.useRef(options);
74
+ React.useEffect(() => {
75
+ optionsRef.current = options;
76
+ }, [options]);
77
+ const getWatchOptions = React.useEffectEvent(() => {
78
+ const current = optionsRef.current;
79
+ return hasAuth(current) ? current : void 0;
80
+ });
49
81
  React.useEffect(() => {
50
- if (!hasAuth(options)) return;
51
- let active = true;
52
- let watch = null;
53
- let timeout = null;
82
+ if (!authEnabled) return;
83
+ const runtime = {
84
+ active: true,
85
+ timeout: null,
86
+ watch: null
87
+ };
54
88
  const schedule = () => {
55
- if (!active) return;
56
- if (timeout) return;
89
+ if (!runtime.active) return;
90
+ if (runtime.timeout) return;
57
91
  setState("connecting");
58
- timeout = setTimeout(() => {
59
- if (!active) return;
60
- timeout = null;
92
+ runtime.timeout = setTimeout(() => {
93
+ if (!runtime.active) return;
94
+ runtime.timeout = null;
61
95
  run();
62
96
  }, reconnectTimeout);
63
97
  };
64
98
  const run = async () => {
65
- if (!hasAuth(options)) return;
99
+ const watchOptions = getWatchOptions();
100
+ if (!watchOptions) return;
66
101
  setState("connecting");
67
- watch = new import_rstream.Watch(options, {
102
+ runtime.watch = new import_tunnels.Watch(watchOptions, {
68
103
  onEvent: (event) => {
69
- if (!active) return;
104
+ if (!runtime.active) return;
70
105
  if (event.type === "state.initial") {
71
106
  setClients(event.object.clients);
72
107
  setTunnels(event.object.tunnels);
@@ -109,36 +144,36 @@ function useRstream(options) {
109
144
  }
110
145
  },
111
146
  onConnect: () => {
112
- if (!active) return;
147
+ if (!runtime.active) return;
113
148
  setState("connected");
114
149
  },
115
150
  onClose: () => {
116
- if (!active) return;
117
- watch = null;
151
+ if (!runtime.active) return;
152
+ runtime.watch = null;
118
153
  schedule();
119
154
  }
120
155
  });
121
156
  try {
122
- await watch.connect();
157
+ await runtime.watch.connect();
123
158
  } catch {
124
159
  schedule();
125
160
  }
126
161
  };
127
162
  run();
128
163
  return () => {
129
- active = false;
130
- if (watch) {
131
- watch.disconnect();
132
- watch = null;
164
+ runtime.active = false;
165
+ if (runtime.watch) {
166
+ runtime.watch.disconnect();
167
+ runtime.watch = null;
133
168
  }
134
- if (timeout) {
135
- clearTimeout(timeout);
136
- timeout = null;
169
+ if (runtime.timeout) {
170
+ clearTimeout(runtime.timeout);
171
+ runtime.timeout = null;
137
172
  }
138
173
  };
139
- }, [options, reconnectTimeout]);
174
+ }, [authEnabled, connectionKey, reconnectTimeout]);
140
175
  React.useEffect(() => {
141
- if (options?.auth) {
176
+ if (authEnabled) {
142
177
  if (error && error.type === "danger") return;
143
178
  if (state !== "connected") {
144
179
  const timeout = setTimeout(() => {
@@ -156,13 +191,13 @@ function useRstream(options) {
156
191
  } else {
157
192
  setError(null);
158
193
  }
159
- }, [options, state, error, errorTimeout]);
194
+ }, [authEnabled, state, error, errorTimeout]);
160
195
  React.useEffect(() => {
161
- if (options?.auth === void 0 || state !== "connected") {
196
+ if (!authEnabled || state !== "connected") {
162
197
  setClients([]);
163
198
  setTunnels([]);
164
199
  }
165
- }, [options, state]);
200
+ }, [authEnabled, connectionKey, state]);
166
201
  return { state, error, tunnels, clients };
167
202
  }
168
203
  // Annotate the CommonJS export names for ESM import in node:
@@ -1,7 +1,7 @@
1
1
  import "../chunk-2JFL7TS5.mjs";
2
2
  import {
3
3
  useRstream
4
- } from "../chunk-CTKB6CWW.mjs";
4
+ } from "../chunk-G47GUMJ6.mjs";
5
5
  export {
6
6
  useRstream
7
7
  };
package/dist/index.d.mts CHANGED
@@ -4,5 +4,5 @@ export { RstreamProvider, useRstreamContext } from './providers/index.mjs';
4
4
  import 'react/jsx-runtime';
5
5
  import '@xterm/xterm';
6
6
  import '@rstreamlabs/webtty';
7
- import '@rstreamlabs/rstream';
7
+ import '@rstreamlabs/tunnels';
8
8
  import 'react';
package/dist/index.d.ts CHANGED
@@ -4,5 +4,5 @@ export { RstreamProvider, useRstreamContext } from './providers/index.js';
4
4
  import 'react/jsx-runtime';
5
5
  import '@xterm/xterm';
6
6
  import '@rstreamlabs/webtty';
7
- import '@rstreamlabs/rstream';
7
+ import '@rstreamlabs/tunnels';
8
8
  import 'react';
package/dist/index.js CHANGED
@@ -48,56 +48,74 @@ var import_webtty = require("@rstreamlabs/webtty");
48
48
  var React = __toESM(require("react"));
49
49
  var import_jsx_runtime = require("react/jsx-runtime");
50
50
  function WebTTYTerminal(props) {
51
- const {
52
- // WebTTY client config
53
- url,
54
- sendHeartbeat,
55
- heartbeatIntervalMs,
56
- // Execution config
57
- cmdArgs,
58
- envVars,
59
- allocateTty,
60
- interactive,
61
- username,
62
- workdir,
63
- // Events
64
- onStdout,
65
- onStdoutEos,
66
- onStderr,
67
- onStderrEos,
68
- onConnect,
69
- onComplete,
70
- onError,
71
- // xterm.js options
72
- terminalOptions,
73
- // Callbacks
74
- onTerminalCreated,
75
- onTitleChange
76
- } = props;
77
51
  const ref = React.useRef(null);
52
+ const initialPropsRef = React.useRef(props);
53
+ const onStdoutEvent = React.useEffectEvent((chunk) => {
54
+ props.onStdout?.(chunk);
55
+ });
56
+ const onStdoutEosEvent = React.useEffectEvent(() => {
57
+ props.onStdoutEos?.();
58
+ });
59
+ const onStderrEvent = React.useEffectEvent((chunk) => {
60
+ props.onStderr?.(chunk);
61
+ });
62
+ const onStderrEosEvent = React.useEffectEvent(() => {
63
+ props.onStderrEos?.();
64
+ });
65
+ const onConnectEvent = React.useEffectEvent(() => {
66
+ props.onConnect?.();
67
+ });
68
+ const onCompleteEvent = React.useEffectEvent((code) => {
69
+ props.onComplete?.(code);
70
+ });
71
+ const onErrorEvent = React.useEffectEvent((message) => {
72
+ props.onError?.(message);
73
+ });
74
+ const onTerminalCreatedEvent = React.useEffectEvent((terminal) => {
75
+ props.onTerminalCreated?.(terminal);
76
+ });
77
+ const onTitleChangeEvent = React.useEffectEvent((title) => {
78
+ props.onTitleChange?.(title);
79
+ });
78
80
  React.useEffect(() => {
79
81
  if (!ref.current) {
80
82
  return;
81
83
  }
82
- let disposeOnData = null;
83
- let disposeOnResize = null;
84
- let disposeOnTitleChange = null;
85
- let resizeObserver = null;
86
- let connected = false;
87
- let syncedRows = 0;
88
- let syncedCols = 0;
84
+ const {
85
+ allocateTty,
86
+ cmdArgs,
87
+ envVars,
88
+ heartbeatIntervalMs,
89
+ interactive,
90
+ sendHeartbeat,
91
+ terminalOptions,
92
+ url,
93
+ username,
94
+ workdir
95
+ } = initialPropsRef.current;
96
+ const runtime = {
97
+ connected: false,
98
+ disposeOnData: null,
99
+ disposeOnResize: null,
100
+ disposeOnTitleChange: null,
101
+ resizeObserver: null,
102
+ syncedCols: 0,
103
+ syncedRows: 0
104
+ };
105
+ const textDecoder = new TextDecoder();
106
+ const textEncoder = new TextEncoder();
89
107
  const clear = () => {
90
- disposeOnData?.dispose();
91
- disposeOnData = null;
92
- disposeOnResize?.dispose();
93
- disposeOnResize = null;
94
- disposeOnTitleChange?.dispose();
95
- disposeOnTitleChange = null;
96
- resizeObserver?.disconnect();
97
- resizeObserver = null;
98
- connected = false;
99
- syncedRows = 0;
100
- syncedCols = 0;
108
+ runtime.disposeOnData?.dispose();
109
+ runtime.disposeOnData = null;
110
+ runtime.disposeOnResize?.dispose();
111
+ runtime.disposeOnResize = null;
112
+ runtime.disposeOnTitleChange?.dispose();
113
+ runtime.disposeOnTitleChange = null;
114
+ runtime.resizeObserver?.disconnect();
115
+ runtime.resizeObserver = null;
116
+ runtime.connected = false;
117
+ runtime.syncedRows = 0;
118
+ runtime.syncedCols = 0;
101
119
  };
102
120
  const terminal = new import_xterm2.Terminal({
103
121
  allowProposedApi: true,
@@ -129,67 +147,65 @@ function WebTTYTerminal(props) {
129
147
  return true;
130
148
  };
131
149
  const syncRemoteSize = (rows, cols) => {
132
- if (!connected) return;
150
+ if (!runtime.connected) return;
133
151
  if (rows < 1 || cols < 1) return;
134
- if (rows === syncedRows && cols === syncedCols) return;
135
- syncedRows = rows;
136
- syncedCols = cols;
152
+ if (rows === runtime.syncedRows && cols === runtime.syncedCols) return;
153
+ runtime.syncedRows = rows;
154
+ runtime.syncedCols = cols;
137
155
  try {
138
156
  webtty.resize(rows, cols);
139
157
  } catch (e) {
140
158
  console.error("Cannot resize remote TTY:", e);
141
159
  }
142
160
  };
143
- onTerminalCreated?.(terminal);
144
- disposeOnTitleChange = terminal.onTitleChange((title) => {
145
- onTitleChange?.(title);
161
+ onTerminalCreatedEvent(terminal);
162
+ runtime.disposeOnTitleChange = terminal.onTitleChange((title) => {
163
+ onTitleChangeEvent(title);
146
164
  });
147
165
  const webtty = new import_webtty.WebTTY(
148
166
  { url, sendHeartbeat, heartbeatIntervalMs },
149
167
  { cmdArgs, envVars, allocateTty, interactive, username, workdir },
150
168
  {
151
169
  onStdout: (chunk) => {
152
- onStdout?.(chunk);
170
+ onStdoutEvent(chunk);
153
171
  terminal.write(chunk);
154
172
  },
155
173
  onStdoutEos: () => {
156
- onStdoutEos?.();
174
+ onStdoutEosEvent();
157
175
  },
158
176
  onStderr: (chunk) => {
159
- onStderr?.(chunk);
160
- terminal.write(
161
- "\x1B[31m" + new TextDecoder().decode(chunk) + "\x1B[0m"
162
- );
177
+ onStderrEvent(chunk);
178
+ terminal.write("\x1B[31m" + textDecoder.decode(chunk) + "\x1B[0m");
163
179
  },
164
180
  onStderrEos: () => {
165
- onStderrEos?.();
181
+ onStderrEosEvent();
166
182
  },
167
183
  onConnect: () => {
168
- connected = true;
169
- onConnect?.();
184
+ runtime.connected = true;
185
+ onConnectEvent();
170
186
  terminal.focus();
171
187
  fit();
172
188
  syncRemoteSize(terminal.rows, terminal.cols);
173
- disposeOnData = terminal.onData((data) => {
189
+ runtime.disposeOnData = terminal.onData((data) => {
174
190
  try {
175
- webtty.writeStdin(new TextEncoder().encode(data));
191
+ webtty.writeStdin(textEncoder.encode(data));
176
192
  } catch (e) {
177
193
  console.error("Cannot writeStdin:", e);
178
194
  }
179
195
  });
180
- disposeOnResize = terminal.onResize((size) => {
196
+ runtime.disposeOnResize = terminal.onResize((size) => {
181
197
  syncRemoteSize(size.rows, size.cols);
182
198
  });
183
199
  },
184
200
  onComplete: (code) => {
185
- onComplete?.(code);
201
+ onCompleteEvent(code);
186
202
  terminal.write(`\r
187
203
  Process exited with code ${code}.`);
188
204
  terminal.write("\x1B[?25l");
189
205
  clear();
190
206
  },
191
207
  onError: (err) => {
192
- onError?.(err);
208
+ onErrorEvent(err);
193
209
  terminal.write(`\r
194
210
  [ERROR] ${err}`);
195
211
  terminal.write("\x1B[?25l");
@@ -198,7 +214,7 @@ Process exited with code ${code}.`);
198
214
  }
199
215
  );
200
216
  webtty.connect();
201
- resizeObserver = new ResizeObserver((entries) => {
217
+ runtime.resizeObserver = new ResizeObserver((entries) => {
202
218
  for (const entry of entries) {
203
219
  if (entry.target === ref.current) {
204
220
  if (!fit()) return;
@@ -206,7 +222,7 @@ Process exited with code ${code}.`);
206
222
  }
207
223
  }
208
224
  });
209
- resizeObserver.observe(ref.current);
225
+ runtime.resizeObserver.observe(ref.current);
210
226
  fit();
211
227
  return () => {
212
228
  clear();
@@ -218,38 +234,73 @@ Process exited with code ${code}.`);
218
234
  }
219
235
 
220
236
  // src/hooks/use-rstream.ts
221
- var import_rstream = require("@rstreamlabs/rstream");
237
+ var import_tunnels = require("@rstreamlabs/tunnels");
222
238
  var React2 = __toESM(require("react"));
223
239
  function hasAuth(options) {
224
240
  return !!options && !!options.auth;
225
241
  }
242
+ function credentialsKey(credentials) {
243
+ if (!credentials) {
244
+ return "";
245
+ }
246
+ if ("token" in credentials) {
247
+ return `token:${credentials.token}`;
248
+ }
249
+ return `client:${credentials.clientId}:${credentials.clientSecret}`;
250
+ }
251
+ function watchConnectionKey(options) {
252
+ if (!hasAuth(options)) {
253
+ return "disabled";
254
+ }
255
+ return JSON.stringify({
256
+ apiUrl: options.apiUrl ?? null,
257
+ auth: typeof options.auth === "function" ? "function" : `token:${options.auth}`,
258
+ controlPlaneCredentials: credentialsKey(options.controlPlaneCredentials),
259
+ engine: options.engine ?? null,
260
+ projectEndpoint: options.projectEndpoint ?? null,
261
+ transport: options.transport ?? "sse"
262
+ });
263
+ }
226
264
  function useRstream(options) {
227
265
  const [state, setState] = React2.useState("disconnected");
228
266
  const [error, setError] = React2.useState(null);
229
267
  const { reconnectTimeout = 1e3, errorTimeout = 5e3 } = options || {};
230
268
  const [clients, setClients] = React2.useState([]);
231
269
  const [tunnels, setTunnels] = React2.useState([]);
270
+ const authEnabled = hasAuth(options);
271
+ const connectionKey = watchConnectionKey(options);
272
+ const optionsRef = React2.useRef(options);
232
273
  React2.useEffect(() => {
233
- if (!hasAuth(options)) return;
234
- let active = true;
235
- let watch = null;
236
- let timeout = null;
274
+ optionsRef.current = options;
275
+ }, [options]);
276
+ const getWatchOptions = React2.useEffectEvent(() => {
277
+ const current = optionsRef.current;
278
+ return hasAuth(current) ? current : void 0;
279
+ });
280
+ React2.useEffect(() => {
281
+ if (!authEnabled) return;
282
+ const runtime = {
283
+ active: true,
284
+ timeout: null,
285
+ watch: null
286
+ };
237
287
  const schedule = () => {
238
- if (!active) return;
239
- if (timeout) return;
288
+ if (!runtime.active) return;
289
+ if (runtime.timeout) return;
240
290
  setState("connecting");
241
- timeout = setTimeout(() => {
242
- if (!active) return;
243
- timeout = null;
291
+ runtime.timeout = setTimeout(() => {
292
+ if (!runtime.active) return;
293
+ runtime.timeout = null;
244
294
  run();
245
295
  }, reconnectTimeout);
246
296
  };
247
297
  const run = async () => {
248
- if (!hasAuth(options)) return;
298
+ const watchOptions = getWatchOptions();
299
+ if (!watchOptions) return;
249
300
  setState("connecting");
250
- watch = new import_rstream.Watch(options, {
301
+ runtime.watch = new import_tunnels.Watch(watchOptions, {
251
302
  onEvent: (event) => {
252
- if (!active) return;
303
+ if (!runtime.active) return;
253
304
  if (event.type === "state.initial") {
254
305
  setClients(event.object.clients);
255
306
  setTunnels(event.object.tunnels);
@@ -292,36 +343,36 @@ function useRstream(options) {
292
343
  }
293
344
  },
294
345
  onConnect: () => {
295
- if (!active) return;
346
+ if (!runtime.active) return;
296
347
  setState("connected");
297
348
  },
298
349
  onClose: () => {
299
- if (!active) return;
300
- watch = null;
350
+ if (!runtime.active) return;
351
+ runtime.watch = null;
301
352
  schedule();
302
353
  }
303
354
  });
304
355
  try {
305
- await watch.connect();
356
+ await runtime.watch.connect();
306
357
  } catch {
307
358
  schedule();
308
359
  }
309
360
  };
310
361
  run();
311
362
  return () => {
312
- active = false;
313
- if (watch) {
314
- watch.disconnect();
315
- watch = null;
363
+ runtime.active = false;
364
+ if (runtime.watch) {
365
+ runtime.watch.disconnect();
366
+ runtime.watch = null;
316
367
  }
317
- if (timeout) {
318
- clearTimeout(timeout);
319
- timeout = null;
368
+ if (runtime.timeout) {
369
+ clearTimeout(runtime.timeout);
370
+ runtime.timeout = null;
320
371
  }
321
372
  };
322
- }, [options, reconnectTimeout]);
373
+ }, [authEnabled, connectionKey, reconnectTimeout]);
323
374
  React2.useEffect(() => {
324
- if (options?.auth) {
375
+ if (authEnabled) {
325
376
  if (error && error.type === "danger") return;
326
377
  if (state !== "connected") {
327
378
  const timeout = setTimeout(() => {
@@ -339,13 +390,13 @@ function useRstream(options) {
339
390
  } else {
340
391
  setError(null);
341
392
  }
342
- }, [options, state, error, errorTimeout]);
393
+ }, [authEnabled, state, error, errorTimeout]);
343
394
  React2.useEffect(() => {
344
- if (options?.auth === void 0 || state !== "connected") {
395
+ if (!authEnabled || state !== "connected") {
345
396
  setClients([]);
346
397
  setTunnels([]);
347
398
  }
348
- }, [options, state]);
399
+ }, [authEnabled, connectionKey, state]);
349
400
  return { state, error, tunnels, clients };
350
401
  }
351
402
 
package/dist/index.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  WebTTYTerminal
3
- } from "./chunk-HTAKPV7E.mjs";
3
+ } from "./chunk-5MUU3P7J.mjs";
4
4
  import "./chunk-2JFL7TS5.mjs";
5
5
  import {
6
6
  RstreamProvider,
7
7
  useRstreamContext
8
- } from "./chunk-CCMP5O6Z.mjs";
8
+ } from "./chunk-DFYVJASW.mjs";
9
9
  import {
10
10
  useRstream
11
- } from "./chunk-CTKB6CWW.mjs";
11
+ } from "./chunk-G47GUMJ6.mjs";
12
12
  export {
13
13
  RstreamProvider,
14
14
  WebTTYTerminal,
@@ -1,11 +1,11 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { UseRstreamOptions, useRstream } from '../hooks/index.mjs';
3
3
  import * as React from 'react';
4
- import '@rstreamlabs/rstream';
4
+ import '@rstreamlabs/tunnels';
5
5
 
6
6
  type RstreamContextValue = ReturnType<typeof useRstream>;
7
7
  declare function useRstreamContext(): {
8
- state: "disconnected" | "connecting" | "connected";
8
+ state: "connecting" | "connected" | "disconnected";
9
9
  error: {
10
10
  message: string;
11
11
  type: "warning" | "danger";
@@ -1,11 +1,11 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { UseRstreamOptions, useRstream } from '../hooks/index.js';
3
3
  import * as React from 'react';
4
- import '@rstreamlabs/rstream';
4
+ import '@rstreamlabs/tunnels';
5
5
 
6
6
  type RstreamContextValue = ReturnType<typeof useRstream>;
7
7
  declare function useRstreamContext(): {
8
- state: "disconnected" | "connecting" | "connected";
8
+ state: "connecting" | "connected" | "disconnected";
9
9
  error: {
10
10
  message: string;
11
11
  type: "warning" | "danger";