@opengeni/react 0.52.1 → 1.0.1
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/README.md +51 -1
- package/dist/artifacts.d.ts +1 -1
- package/dist/artifacts.js +43 -14
- package/dist/artifacts.js.map +1 -1
- package/dist/{chunk-ROIRTM37.js → chunk-5UQPNLIO.js} +6 -6
- package/dist/{chunk-JR3QFY6B.js → chunk-6KLX2RH5.js} +49 -2
- package/dist/chunk-6KLX2RH5.js.map +1 -0
- package/dist/{chunk-FWS3KC3Z.js → chunk-B6WPZSTT.js} +104 -104
- package/dist/{chunk-FWS3KC3Z.js.map → chunk-B6WPZSTT.js.map} +1 -1
- package/dist/{chunk-HDBG7XGO.js → chunk-C5ZPSSS5.js} +50 -1
- package/dist/chunk-C5ZPSSS5.js.map +1 -0
- package/dist/{chunk-X4S56Y2H.js → chunk-HA46CYQK.js} +135 -11
- package/dist/chunk-HA46CYQK.js.map +1 -0
- package/dist/{chunk-724BW4TW.js → chunk-IGC6A2ZI.js} +2 -2
- package/dist/chunk-LLOLUHVG.js +5937 -0
- package/dist/chunk-LLOLUHVG.js.map +1 -0
- package/dist/{chunk-WYA65Y3F.js → chunk-MKPIAGVL.js} +13 -3
- package/dist/chunk-MKPIAGVL.js.map +1 -0
- package/dist/chunk-R3AZOAB5.js +933 -0
- package/dist/chunk-R3AZOAB5.js.map +1 -0
- package/dist/{chunk-23EJ676W.js → chunk-XBZWOM2K.js} +9 -4
- package/dist/chunk-XBZWOM2K.js.map +1 -0
- package/dist/{chunk-JAGDUCZQ.js → chunk-Y7OEDUJ5.js} +17 -13
- package/dist/chunk-Y7OEDUJ5.js.map +1 -0
- package/dist/client.d.ts +6 -4
- package/dist/components/artifacts/index.d.ts +1 -0
- package/dist/components/artifacts/published-html-artifact-frame.d.ts +14 -0
- package/dist/components/browser-input.d.ts +7 -0
- package/dist/components/browser-viewer.d.ts +5 -4
- package/dist/components/computer-viewer.d.ts +2 -1
- package/dist/components/desktop-viewer.d.ts +26 -2
- package/dist/components/interaction-intervention-banner.d.ts +11 -0
- package/dist/components/sandbox-terminal.d.ts +4 -1
- package/dist/components/sandbox-workspace.d.ts +3 -0
- package/dist/composer.js +6 -6
- package/dist/hooks/use-auth-runs.d.ts +32 -0
- package/dist/hooks/use-browser-downloads.d.ts +25 -0
- package/dist/hooks/use-browser-identities.d.ts +5 -1
- package/dist/hooks/use-browser-session.d.ts +2 -1
- package/dist/hooks/use-browser-sessions.d.ts +1 -1
- package/dist/hooks/use-channels.d.ts +26 -0
- package/dist/hooks/use-computer-frame-stream.d.ts +1 -1
- package/dist/hooks/use-computer-session.d.ts +2 -1
- package/dist/hooks/use-computer-sessions.d.ts +1 -1
- package/dist/hooks/use-desktop-stream.d.ts +4 -0
- package/dist/hooks/use-interaction-interventions.d.ts +27 -0
- package/dist/hooks/use-interaction-invalidation.d.ts +19 -0
- package/dist/hooks/use-network-routes.d.ts +25 -0
- package/dist/hooks/use-site-auth-connections.d.ts +25 -0
- package/dist/hooks/use-supergrok-accounts.d.ts +26 -0
- package/dist/hooks/use-terminal-stream.d.ts +3 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1220 -1545
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +13 -3
- package/dist/interaction.js +520 -6
- package/dist/interaction.js.map +1 -1
- package/dist/interactive-workbench-viewers-TOHBXJEE.js +15 -0
- package/dist/lib/host-platform.d.ts +5 -0
- package/dist/lib/relay-wire.d.ts +20 -1
- package/dist/machines.js +3 -3
- package/dist/model-policy.d.ts +1 -1
- package/dist/model-policy.js +1 -1
- package/dist/provider.d.ts +3 -2
- package/dist/realtime/realtime-control.d.ts +6 -2
- package/dist/realtime.js +138 -24
- package/dist/realtime.js.map +1 -1
- package/dist/session-context.d.ts +10 -2
- package/dist/session-ui.js +3 -3
- package/dist/session.js +11 -11
- package/dist/timeline/types.d.ts +5 -0
- package/package.json +2 -2
- package/src/artifacts.ts +3 -0
- package/src/client.ts +73 -3
- package/src/components/artifacts/index.ts +5 -0
- package/src/components/artifacts/published-html-artifact-frame.tsx +38 -0
- package/src/components/browser-input.ts +65 -0
- package/src/components/browser-viewer.tsx +1287 -177
- package/src/components/computer-viewer.tsx +611 -114
- package/src/components/desktop-viewer.tsx +114 -1
- package/src/components/interaction-intervention-banner.tsx +99 -0
- package/src/components/machines/machine-detail.tsx +1 -1
- package/src/components/model-policy-picker.tsx +15 -0
- package/src/components/sandbox-terminal.tsx +24 -7
- package/src/components/sandbox-workspace.tsx +42 -4
- package/src/components/workspace-dock.tsx +59 -5
- package/src/hooks/use-attached-browsers.ts +13 -11
- package/src/hooks/use-auth-runs.ts +278 -0
- package/src/hooks/use-browser-downloads.ts +218 -0
- package/src/hooks/use-browser-frame-stream.ts +168 -59
- package/src/hooks/use-browser-identities.ts +47 -6
- package/src/hooks/use-browser-session.ts +94 -27
- package/src/hooks/use-browser-sessions.ts +19 -14
- package/src/hooks/use-channels.ts +99 -0
- package/src/hooks/use-computer-frame-stream.ts +184 -61
- package/src/hooks/use-computer-session.ts +22 -2
- package/src/hooks/use-computer-sessions.ts +19 -13
- package/src/hooks/use-desktop-stream.ts +39 -7
- package/src/hooks/use-interaction-interventions.ts +242 -0
- package/src/hooks/use-interaction-invalidation.ts +66 -0
- package/src/hooks/use-machine-chip.ts +13 -3
- package/src/hooks/use-network-routes.ts +214 -0
- package/src/hooks/use-site-auth-connections.ts +226 -0
- package/src/hooks/use-supergrok-accounts.ts +81 -0
- package/src/hooks/use-terminal-stream.ts +288 -55
- package/src/index.ts +8 -0
- package/src/interaction.ts +30 -0
- package/src/lib/host-platform.ts +10 -0
- package/src/lib/relay-wire.ts +43 -4
- package/src/lib/terminal-capability.ts +1 -1
- package/src/model-policy.ts +19 -4
- package/src/provider.tsx +133 -1
- package/src/realtime/realtime-control.tsx +181 -26
- package/src/session-context.ts +66 -1
- package/src/timeline/activity-rail.tsx +6 -0
- package/src/timeline/projection.ts +58 -0
- package/src/timeline/types.ts +2 -0
- package/styles/compiled.css +64 -6
- package/dist/chunk-23EJ676W.js.map +0 -1
- package/dist/chunk-6BBWWOOU.js +0 -82
- package/dist/chunk-6BBWWOOU.js.map +0 -1
- package/dist/chunk-7I2KB3OK.js +0 -3895
- package/dist/chunk-7I2KB3OK.js.map +0 -1
- package/dist/chunk-HDBG7XGO.js.map +0 -1
- package/dist/chunk-JAGDUCZQ.js.map +0 -1
- package/dist/chunk-JR3QFY6B.js.map +0 -1
- package/dist/chunk-WYA65Y3F.js.map +0 -1
- package/dist/chunk-X4S56Y2H.js.map +0 -1
- package/dist/interactive-workbench-viewers-KHNOHWQE.js +0 -13
- /package/dist/{chunk-ROIRTM37.js.map → chunk-5UQPNLIO.js.map} +0 -0
- /package/dist/{chunk-724BW4TW.js.map → chunk-IGC6A2ZI.js.map} +0 -0
- /package/dist/{interactive-workbench-viewers-KHNOHWQE.js.map → interactive-workbench-viewers-TOHBXJEE.js.map} +0 -0
|
@@ -13,7 +13,9 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
decodeStreamFrame,
|
|
15
15
|
decodeStreamOpenAck,
|
|
16
|
+
encodeStreamClose,
|
|
16
17
|
encodeStreamOpen,
|
|
18
|
+
STREAM_CLOSE_REASON_NORMAL,
|
|
17
19
|
STREAM_KIND_BROWSER,
|
|
18
20
|
STREAM_ROLE_CLIENT,
|
|
19
21
|
} from "../lib/relay-wire";
|
|
@@ -39,6 +41,8 @@ export type BrowserFrameWebSocketFactory = (
|
|
|
39
41
|
const RELAY_TAG_OPEN = 1;
|
|
40
42
|
const RELAY_TAG_OPEN_ACK = 2;
|
|
41
43
|
const RELAY_TAG_FRAME = 3;
|
|
44
|
+
const RELAY_TAG_CLOSE = 4;
|
|
45
|
+
const MAX_AUTOMATIC_RECONNECTS_WITHOUT_A_FRAME = 2;
|
|
42
46
|
|
|
43
47
|
export type UseBrowserFrameStreamOptions = EmbeddedBrowserInteractionClientOverride & {
|
|
44
48
|
browserSessionId: string | null;
|
|
@@ -108,48 +112,81 @@ export function useBrowserFrameStream(
|
|
|
108
112
|
|
|
109
113
|
let disposed = false;
|
|
110
114
|
let socket: BrowserFrameWebSocket | null = null;
|
|
115
|
+
let socketHandlers: {
|
|
116
|
+
open: () => void;
|
|
117
|
+
message: (event: MessageEvent) => void;
|
|
118
|
+
error: () => void;
|
|
119
|
+
close: () => void;
|
|
120
|
+
} | null = null;
|
|
111
121
|
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
112
122
|
let expiryTimer: ReturnType<typeof setTimeout> | null = null;
|
|
113
123
|
let failures = 0;
|
|
114
124
|
let controllerGeneration: string | null = null;
|
|
125
|
+
let activeAttachment: BrowserSessionAttachment | null = null;
|
|
115
126
|
let activeStream: BrowserSessionAttachment["stream"] | null = null;
|
|
127
|
+
let attachmentExpiresAt = 0;
|
|
128
|
+
let lastRelaySequence: string | null = null;
|
|
116
129
|
let relayAccepted = false;
|
|
130
|
+
let pendingFrame: { bytes: Uint8Array; source: BrowserFrameWebSocket } | null = null;
|
|
131
|
+
let decodingFrame = false;
|
|
117
132
|
const attachmentAbort = new AbortController();
|
|
118
133
|
|
|
119
|
-
const clearSocket = () => {
|
|
134
|
+
const clearSocket = (terminateProducer = false) => {
|
|
120
135
|
if (!socket) return;
|
|
121
|
-
socket
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
136
|
+
const current = socket;
|
|
137
|
+
const handlers = socketHandlers;
|
|
138
|
+
if (terminateProducer && activeStream?.kind === "relay" && socket.readyState === 1) {
|
|
139
|
+
try {
|
|
140
|
+
socket.send(
|
|
141
|
+
relayDatagram(
|
|
142
|
+
RELAY_TAG_CLOSE,
|
|
143
|
+
encodeStreamClose({
|
|
144
|
+
channelId: activeStream.channel.channelId,
|
|
145
|
+
reason: STREAM_CLOSE_REASON_NORMAL,
|
|
146
|
+
message: "viewer detached",
|
|
147
|
+
}),
|
|
148
|
+
),
|
|
149
|
+
);
|
|
150
|
+
} catch {
|
|
151
|
+
// OPEN can race a transport close. Local teardown must still finish.
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (handlers) {
|
|
155
|
+
current.removeEventListener("open", handlers.open);
|
|
156
|
+
current.removeEventListener("message", handlers.message);
|
|
157
|
+
current.removeEventListener("error", handlers.error);
|
|
158
|
+
current.removeEventListener("close", handlers.close);
|
|
159
|
+
}
|
|
160
|
+
if (current.readyState === 0 || current.readyState === 1) {
|
|
161
|
+
current.close(1000, "viewer detached");
|
|
127
162
|
}
|
|
128
163
|
socket = null;
|
|
164
|
+
socketHandlers = null;
|
|
165
|
+
pendingFrame = null;
|
|
129
166
|
};
|
|
130
167
|
|
|
131
168
|
const scheduleReconnect = () => {
|
|
132
169
|
if (disposed || reconnectTimer) return;
|
|
170
|
+
if (failures >= MAX_AUTOMATIC_RECONNECTS_WITHOUT_A_FRAME) return;
|
|
133
171
|
const delay = Math.min(5_000, 250 * 2 ** Math.min(failures, 5));
|
|
134
172
|
failures += 1;
|
|
135
173
|
setResult((current) => ({ ...current, state: "reconnecting" }));
|
|
136
174
|
reconnectTimer = setTimeout(() => {
|
|
137
175
|
reconnectTimer = null;
|
|
138
|
-
|
|
176
|
+
connectSocket();
|
|
139
177
|
}, delay);
|
|
140
178
|
};
|
|
141
179
|
|
|
142
|
-
const fail = (cause: unknown) => {
|
|
180
|
+
const fail = (cause: unknown, reconnectAutomatically = true) => {
|
|
143
181
|
if (disposed) return;
|
|
144
182
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
145
183
|
setResult((current) => ({ ...current, state: "error", error }));
|
|
146
184
|
clearSocket();
|
|
147
|
-
scheduleReconnect();
|
|
185
|
+
if (reconnectAutomatically) scheduleReconnect();
|
|
148
186
|
};
|
|
149
187
|
|
|
150
|
-
const onOpen = () => {
|
|
151
|
-
if (disposed) return;
|
|
152
|
-
failures = 0;
|
|
188
|
+
const onOpen = (source: BrowserFrameWebSocket) => {
|
|
189
|
+
if (disposed || source !== socket) return;
|
|
153
190
|
if (activeStream?.kind === "relay") {
|
|
154
191
|
const body = encodeStreamOpen({
|
|
155
192
|
channel: {
|
|
@@ -158,19 +195,61 @@ export function useBrowserFrameStream(
|
|
|
158
195
|
},
|
|
159
196
|
token: activeStream.token,
|
|
160
197
|
role: STREAM_ROLE_CLIENT,
|
|
161
|
-
resumeFromSeq:
|
|
198
|
+
resumeFromSeq:
|
|
199
|
+
lastRelaySequence === null ? "0" : (BigInt(lastRelaySequence) + 1n).toString(),
|
|
162
200
|
});
|
|
163
|
-
|
|
201
|
+
try {
|
|
202
|
+
source.send(relayDatagram(RELAY_TAG_OPEN, body));
|
|
203
|
+
} catch (error) {
|
|
204
|
+
fail(error);
|
|
205
|
+
}
|
|
164
206
|
return;
|
|
165
207
|
}
|
|
166
208
|
setResult((current) => ({ ...current, state: "live", error: null }));
|
|
167
209
|
};
|
|
168
210
|
|
|
169
|
-
const
|
|
211
|
+
const drainLatestFrame = async () => {
|
|
212
|
+
if (decodingFrame) return;
|
|
213
|
+
decodingFrame = true;
|
|
214
|
+
try {
|
|
215
|
+
while (pendingFrame) {
|
|
216
|
+
if (disposed) break;
|
|
217
|
+
const pending = pendingFrame;
|
|
218
|
+
pendingFrame = null;
|
|
219
|
+
const { bytes, source } = pending;
|
|
220
|
+
const frame = decodeBrowserFrameMessage(bytes);
|
|
221
|
+
if (disposed || source !== socket) continue;
|
|
222
|
+
if (frame.browserSessionId !== browserSessionId || frame.targetId !== targetId) {
|
|
223
|
+
throw new Error("browser frame belongs to another resource");
|
|
224
|
+
}
|
|
225
|
+
if (!controllerGeneration || frame.controllerGeneration !== controllerGeneration) {
|
|
226
|
+
throw new Error("browser frame belongs to a stale controller");
|
|
227
|
+
}
|
|
228
|
+
const key = `${browserSessionId}:${targetId}:${frame.controllerGeneration}:${frame.targetGeneration}:${frame.documentGeneration}`;
|
|
229
|
+
if (latestRef.current.key === key && frame.sequence <= latestRef.current.sequence)
|
|
230
|
+
continue;
|
|
231
|
+
latestRef.current = { key, sequence: frame.sequence };
|
|
232
|
+
failures = 0;
|
|
233
|
+
setResult((current) => ({
|
|
234
|
+
...current,
|
|
235
|
+
state: "live",
|
|
236
|
+
frame,
|
|
237
|
+
error: null,
|
|
238
|
+
}));
|
|
239
|
+
}
|
|
240
|
+
} catch (cause) {
|
|
241
|
+
fail(cause);
|
|
242
|
+
} finally {
|
|
243
|
+
decodingFrame = false;
|
|
244
|
+
if (!disposed && pendingFrame) void drainLatestFrame();
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const onMessage = (source: BrowserFrameWebSocket, event: MessageEvent) => {
|
|
170
249
|
void (async () => {
|
|
171
250
|
try {
|
|
172
251
|
const bytes = await messageBytes(event.data);
|
|
173
|
-
if (disposed) return;
|
|
252
|
+
if (disposed || source !== socket) return;
|
|
174
253
|
let frameBytes = bytes;
|
|
175
254
|
if (activeStream?.kind === "relay") {
|
|
176
255
|
if (bytes.length < 1) throw new Error("browser relay returned an empty message");
|
|
@@ -179,45 +258,83 @@ export function useBrowserFrameStream(
|
|
|
179
258
|
if (tag === RELAY_TAG_OPEN_ACK) {
|
|
180
259
|
const ack = decodeStreamOpenAck(body);
|
|
181
260
|
if (!ack.accepted) {
|
|
261
|
+
activeAttachment = null;
|
|
262
|
+
activeStream = null;
|
|
263
|
+
attachmentExpiresAt = 0;
|
|
182
264
|
throw new Error(ack.error?.message ?? "browser stream was rejected by the relay");
|
|
183
265
|
}
|
|
184
266
|
relayAccepted = true;
|
|
185
267
|
return;
|
|
186
268
|
}
|
|
269
|
+
if (tag === RELAY_TAG_CLOSE) {
|
|
270
|
+
activeAttachment = null;
|
|
271
|
+
activeStream = null;
|
|
272
|
+
attachmentExpiresAt = 0;
|
|
273
|
+
lastRelaySequence = null;
|
|
274
|
+
relayAccepted = false;
|
|
275
|
+
fail(new Error("Browser frame source ended."), false);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
187
278
|
if (tag !== RELAY_TAG_FRAME || !relayAccepted) return;
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (frame.browserSessionId !== browserSessionId || frame.targetId !== targetId) {
|
|
192
|
-
throw new Error("browser frame belongs to another resource");
|
|
193
|
-
}
|
|
194
|
-
if (!controllerGeneration || frame.controllerGeneration !== controllerGeneration) {
|
|
195
|
-
throw new Error("browser frame belongs to a stale controller");
|
|
279
|
+
const relayFrame = decodeStreamFrame(body);
|
|
280
|
+
lastRelaySequence = relayFrame.seq;
|
|
281
|
+
frameBytes = relayFrame.data;
|
|
196
282
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
latestRef.current = { key, sequence: frame.sequence };
|
|
200
|
-
setResult((current) => ({
|
|
201
|
-
...current,
|
|
202
|
-
state: "live",
|
|
203
|
-
frame,
|
|
204
|
-
error: null,
|
|
205
|
-
}));
|
|
283
|
+
pendingFrame = { bytes: frameBytes, source };
|
|
284
|
+
void drainLatestFrame();
|
|
206
285
|
} catch (cause) {
|
|
207
286
|
fail(cause);
|
|
208
287
|
}
|
|
209
288
|
})();
|
|
210
289
|
};
|
|
211
290
|
|
|
212
|
-
const onError = () =>
|
|
291
|
+
const onError = (source: BrowserFrameWebSocket) => {
|
|
292
|
+
if (source !== socket) return;
|
|
293
|
+
fail(new Error("Browser view lost connection."));
|
|
294
|
+
};
|
|
213
295
|
|
|
214
|
-
const onClose = () => {
|
|
215
|
-
if (disposed) return;
|
|
296
|
+
const onClose = (source: BrowserFrameWebSocket) => {
|
|
297
|
+
if (disposed || source !== socket) return;
|
|
216
298
|
clearSocket();
|
|
217
299
|
scheduleReconnect();
|
|
218
300
|
};
|
|
219
301
|
|
|
220
|
-
const
|
|
302
|
+
const connectSocket = () => {
|
|
303
|
+
if (disposed) return;
|
|
304
|
+
clearSocket();
|
|
305
|
+
if (!activeAttachment || !activeStream || attachmentExpiresAt <= Date.now() + 1_000) {
|
|
306
|
+
void attach();
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
relayAccepted = false;
|
|
310
|
+
setResult((current) => ({
|
|
311
|
+
...current,
|
|
312
|
+
state: "connecting",
|
|
313
|
+
error: null,
|
|
314
|
+
}));
|
|
315
|
+
const createSocket =
|
|
316
|
+
factoryRef.current ?? ((url: string, protocols: string[]) => new WebSocket(url, protocols));
|
|
317
|
+
const openedSocket = createSocket(
|
|
318
|
+
activeStream.kind === "relay"
|
|
319
|
+
? activeStream.url
|
|
320
|
+
: browserFrameSocketUrl(activeAttachment, streamRef.current),
|
|
321
|
+
activeStream.kind === "direct_websocket" ? [...activeStream.protocols] : [],
|
|
322
|
+
);
|
|
323
|
+
socket = openedSocket;
|
|
324
|
+
socketHandlers = {
|
|
325
|
+
open: () => onOpen(openedSocket),
|
|
326
|
+
message: (event) => onMessage(openedSocket, event),
|
|
327
|
+
error: () => onError(openedSocket),
|
|
328
|
+
close: () => onClose(openedSocket),
|
|
329
|
+
};
|
|
330
|
+
openedSocket.binaryType = "arraybuffer";
|
|
331
|
+
openedSocket.addEventListener("open", socketHandlers.open);
|
|
332
|
+
openedSocket.addEventListener("message", socketHandlers.message);
|
|
333
|
+
openedSocket.addEventListener("error", socketHandlers.error);
|
|
334
|
+
openedSocket.addEventListener("close", socketHandlers.close);
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const attach = async () => {
|
|
221
338
|
if (disposed) return;
|
|
222
339
|
clearSocket();
|
|
223
340
|
if (expiryTimer) clearTimeout(expiryTimer);
|
|
@@ -242,7 +359,10 @@ export function useBrowserFrameStream(
|
|
|
242
359
|
throw new Error("browser attachment does not match the requested resource");
|
|
243
360
|
}
|
|
244
361
|
controllerGeneration = attachment.controllerGeneration;
|
|
362
|
+
activeAttachment = attachment;
|
|
245
363
|
activeStream = attachment.stream;
|
|
364
|
+
attachmentExpiresAt = Date.parse(attachment.expiresAt);
|
|
365
|
+
lastRelaySequence = null;
|
|
246
366
|
relayAccepted = false;
|
|
247
367
|
setResult((current) => ({
|
|
248
368
|
...current,
|
|
@@ -255,42 +375,31 @@ export function useBrowserFrameStream(
|
|
|
255
375
|
},
|
|
256
376
|
error: null,
|
|
257
377
|
}));
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
((url: string, protocols: string[]) => new WebSocket(url, protocols));
|
|
261
|
-
socket = createSocket(
|
|
262
|
-
attachment.stream.kind === "relay"
|
|
263
|
-
? attachment.stream.url
|
|
264
|
-
: browserFrameSocketUrl(attachment, streamRef.current),
|
|
265
|
-
attachment.stream.kind === "direct_websocket" ? [...attachment.stream.protocols] : [],
|
|
266
|
-
);
|
|
267
|
-
socket.binaryType = "arraybuffer";
|
|
268
|
-
socket.addEventListener("open", onOpen);
|
|
269
|
-
socket.addEventListener("message", onMessage);
|
|
270
|
-
socket.addEventListener("error", onError);
|
|
271
|
-
socket.addEventListener("close", onClose);
|
|
272
|
-
const expiresAt = Date.parse(attachment.expiresAt);
|
|
273
|
-
const refreshIn = Number.isFinite(expiresAt)
|
|
274
|
-
? Math.max(1_000, expiresAt - Date.now() - 5_000)
|
|
378
|
+
const refreshIn = Number.isFinite(attachmentExpiresAt)
|
|
379
|
+
? Math.max(1_000, attachmentExpiresAt - Date.now() - 5_000)
|
|
275
380
|
: 60_000;
|
|
276
381
|
expiryTimer = setTimeout(() => {
|
|
277
382
|
if (disposed) return;
|
|
278
|
-
clearSocket();
|
|
279
|
-
|
|
383
|
+
clearSocket(true);
|
|
384
|
+
activeAttachment = null;
|
|
385
|
+
activeStream = null;
|
|
386
|
+
attachmentExpiresAt = 0;
|
|
387
|
+
void attach();
|
|
280
388
|
}, refreshIn);
|
|
389
|
+
connectSocket();
|
|
281
390
|
} catch (cause) {
|
|
282
391
|
if (attachmentAbort.signal.aborted || disposed) return;
|
|
283
392
|
fail(cause);
|
|
284
393
|
}
|
|
285
394
|
};
|
|
286
395
|
|
|
287
|
-
void
|
|
396
|
+
void attach();
|
|
288
397
|
return () => {
|
|
289
398
|
disposed = true;
|
|
290
399
|
attachmentAbort.abort();
|
|
291
400
|
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
292
401
|
if (expiryTimer) clearTimeout(expiryTimer);
|
|
293
|
-
clearSocket();
|
|
402
|
+
clearSocket(true);
|
|
294
403
|
};
|
|
295
404
|
}, [client, enabled, nonce, options.browserSessionId, options.targetId, workspaceId]);
|
|
296
405
|
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
CreateBrowserIdentityRequest,
|
|
6
6
|
PublishBrowserRevisionRequest,
|
|
7
7
|
PublishBrowserRevisionResponse,
|
|
8
|
+
UpdateBrowserIdentityRequest,
|
|
8
9
|
} from "@opengeni/sdk/interaction";
|
|
9
10
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
10
11
|
import type { EmbeddedBrowserInteractionClientLike } from "../client";
|
|
@@ -12,9 +13,11 @@ import {
|
|
|
12
13
|
type EmbeddedBrowserInteractionClientOverride,
|
|
13
14
|
useEmbeddedBrowserInteraction,
|
|
14
15
|
} from "../session-context";
|
|
16
|
+
import { useInteractionInvalidation } from "./use-interaction-invalidation";
|
|
15
17
|
|
|
16
18
|
export type UseBrowserIdentitiesOptions = EmbeddedBrowserInteractionClientOverride & {
|
|
17
19
|
enabled?: boolean | undefined;
|
|
20
|
+
includeArchived?: boolean | undefined;
|
|
18
21
|
};
|
|
19
22
|
|
|
20
23
|
export type UseBrowserIdentitiesResult = {
|
|
@@ -30,6 +33,10 @@ export type UseBrowserIdentitiesResult = {
|
|
|
30
33
|
operationId?: string;
|
|
31
34
|
},
|
|
32
35
|
) => Promise<BrowserIdentityMutationResponse>;
|
|
36
|
+
update: (
|
|
37
|
+
identityId: string,
|
|
38
|
+
request: Omit<UpdateBrowserIdentityRequest, "operationId"> & { operationId?: string },
|
|
39
|
+
) => Promise<BrowserIdentityMutationResponse>;
|
|
33
40
|
revisions: (identityId: string) => Promise<BrowserRevisionListResponse>;
|
|
34
41
|
publish: (
|
|
35
42
|
browserSessionId: string,
|
|
@@ -43,8 +50,10 @@ export type UseBrowserIdentitiesResult = {
|
|
|
43
50
|
export function useBrowserIdentities(
|
|
44
51
|
options: UseBrowserIdentitiesOptions = {},
|
|
45
52
|
): UseBrowserIdentitiesResult {
|
|
46
|
-
const { client, workspaceId } =
|
|
53
|
+
const { client, workspaceId, workspaceInteractionEvent, workspaceInteractionConnectionState } =
|
|
54
|
+
useEmbeddedBrowserInteraction(options);
|
|
47
55
|
const enabled = options.enabled ?? true;
|
|
56
|
+
const includeArchived = options.includeArchived ?? false;
|
|
48
57
|
const [state, setState] = useState(() => emptyState(workspaceId, enabled));
|
|
49
58
|
const [mutationCount, setMutationCount] = useState(0);
|
|
50
59
|
const requestRef = useRef<{ id: number; controller: AbortController | null }>({
|
|
@@ -75,6 +84,7 @@ export function useBrowserIdentities(
|
|
|
75
84
|
}));
|
|
76
85
|
try {
|
|
77
86
|
const response = await client.listBrowserIdentities(workspaceId, {
|
|
87
|
+
includeArchived,
|
|
78
88
|
signal: controller.signal,
|
|
79
89
|
});
|
|
80
90
|
if (!mountedRef.current || requestRef.current.id !== id) return;
|
|
@@ -99,7 +109,7 @@ export function useBrowserIdentities(
|
|
|
99
109
|
if (requestRef.current.id === id) requestRef.current = { id, controller: null };
|
|
100
110
|
}
|
|
101
111
|
},
|
|
102
|
-
[client, enabled, workspaceId],
|
|
112
|
+
[client, enabled, includeArchived, workspaceId],
|
|
103
113
|
);
|
|
104
114
|
|
|
105
115
|
const refresh = useCallback(async () => await load(false), [load]);
|
|
@@ -118,6 +128,16 @@ export function useBrowserIdentities(
|
|
|
118
128
|
};
|
|
119
129
|
}, [cancelLoad, enabled, load, workspaceId]);
|
|
120
130
|
|
|
131
|
+
useInteractionInvalidation({
|
|
132
|
+
workspaceId,
|
|
133
|
+
key: workspaceId,
|
|
134
|
+
enabled,
|
|
135
|
+
event: workspaceInteractionEvent,
|
|
136
|
+
connectionState: workspaceInteractionConnectionState,
|
|
137
|
+
knownRevision: visibleState.revision,
|
|
138
|
+
refresh,
|
|
139
|
+
});
|
|
140
|
+
|
|
121
141
|
const mutate = useCallback(async <T>(operation: () => Promise<T>): Promise<T> => {
|
|
122
142
|
setMutationCount((count) => count + 1);
|
|
123
143
|
try {
|
|
@@ -150,6 +170,24 @@ export function useBrowserIdentities(
|
|
|
150
170
|
[client, workspaceId],
|
|
151
171
|
);
|
|
152
172
|
|
|
173
|
+
const update = useCallback(
|
|
174
|
+
async (
|
|
175
|
+
identityId: string,
|
|
176
|
+
request: Omit<UpdateBrowserIdentityRequest, "operationId"> & { operationId?: string },
|
|
177
|
+
): Promise<BrowserIdentityMutationResponse> =>
|
|
178
|
+
await mutate(async () => {
|
|
179
|
+
const response = await client.updateBrowserIdentity(workspaceId, identityId, {
|
|
180
|
+
...request,
|
|
181
|
+
operationId: request.operationId ?? crypto.randomUUID(),
|
|
182
|
+
});
|
|
183
|
+
setState((current) =>
|
|
184
|
+
mergeIdentity(current, workspaceId, response.identity, includeArchived),
|
|
185
|
+
);
|
|
186
|
+
return response;
|
|
187
|
+
}),
|
|
188
|
+
[client, includeArchived, mutate, workspaceId],
|
|
189
|
+
);
|
|
190
|
+
|
|
153
191
|
const publish = useCallback(
|
|
154
192
|
async (
|
|
155
193
|
browserSessionId: string,
|
|
@@ -177,6 +215,7 @@ export function useBrowserIdentities(
|
|
|
177
215
|
error: visibleState.error,
|
|
178
216
|
refresh,
|
|
179
217
|
create,
|
|
218
|
+
update,
|
|
180
219
|
revisions,
|
|
181
220
|
publish,
|
|
182
221
|
};
|
|
@@ -209,14 +248,16 @@ function mergeIdentity(
|
|
|
209
248
|
current: ReturnType<typeof emptyState>,
|
|
210
249
|
workspaceId: string,
|
|
211
250
|
identity: BrowserIdentity,
|
|
251
|
+
includeArchived = false,
|
|
212
252
|
): ReturnType<typeof emptyState> {
|
|
213
253
|
if (current.workspaceId !== workspaceId) return current;
|
|
214
254
|
return {
|
|
215
255
|
...current,
|
|
216
|
-
identities: sortIdentities(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
256
|
+
identities: sortIdentities(
|
|
257
|
+
identity.status === "archived" && !includeArchived
|
|
258
|
+
? current.identities.filter((candidate) => candidate.id !== identity.id)
|
|
259
|
+
: [...current.identities.filter((candidate) => candidate.id !== identity.id), identity],
|
|
260
|
+
),
|
|
220
261
|
error: null,
|
|
221
262
|
};
|
|
222
263
|
}
|