@opengeni/react 1.4.2 → 1.4.4
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/{browser-viewer-WGJ43YGI.js → browser-viewer-5EEAS3IU.js} +2 -2
- package/dist/{chunk-VCSERYXH.js → chunk-2OQA3CPB.js} +291 -158
- package/dist/{chunk-VCSERYXH.js.map → chunk-2OQA3CPB.js.map} +1 -1
- package/dist/{chunk-CREMJMVN.js → chunk-EAOSEIXB.js} +2 -2
- package/dist/{chunk-CREMJMVN.js.map → chunk-EAOSEIXB.js.map} +1 -1
- package/dist/{chunk-MDIXFKA3.js → chunk-I6OY3MWC.js} +302 -108
- package/dist/chunk-I6OY3MWC.js.map +1 -0
- package/dist/{computer-viewer-46UIN7F3.js → computer-viewer-ELNLZGHI.js} +2 -2
- package/dist/hooks/use-computer-session.d.ts +2 -0
- package/dist/index.js +3 -3
- package/dist/interaction.js +2 -2
- package/dist/session-ui.js +1 -1
- package/package.json +3 -3
- package/src/components/computer-viewer.tsx +139 -30
- package/src/components/session-list.tsx +1 -1
- package/src/hooks/use-browser-session.ts +445 -168
- package/src/hooks/use-computer-session.ts +334 -98
- package/styles/compiled.css +5 -0
- package/dist/chunk-MDIXFKA3.js.map +0 -1
- /package/dist/{browser-viewer-WGJ43YGI.js.map → browser-viewer-5EEAS3IU.js.map} +0 -0
- /package/dist/{computer-viewer-46UIN7F3.js.map → computer-viewer-ELNLZGHI.js.map} +0 -0
|
@@ -867,6 +867,11 @@ function useBrowserSession(options) {
|
|
|
867
867
|
const { client, workspaceId } = useEmbeddedBrowserInteraction(options);
|
|
868
868
|
const browserSessionId = options.browserSessionId;
|
|
869
869
|
const enabled = (options.enabled ?? true) && browserSessionId !== null;
|
|
870
|
+
const sourceRef = useRef5({ client, workspaceId, browserSessionId, enabled });
|
|
871
|
+
if (sourceRef.current.client !== client || sourceRef.current.workspaceId !== workspaceId || sourceRef.current.browserSessionId !== browserSessionId || sourceRef.current.enabled !== enabled) {
|
|
872
|
+
sourceRef.current = { client, workspaceId, browserSessionId, enabled };
|
|
873
|
+
}
|
|
874
|
+
const source = sourceRef.current;
|
|
870
875
|
const semanticObservationEnabled = options.semanticObservationEnabled ?? true;
|
|
871
876
|
const semanticObservationEnabledRef = useRef5(semanticObservationEnabled);
|
|
872
877
|
semanticObservationEnabledRef.current = semanticObservationEnabled;
|
|
@@ -876,13 +881,15 @@ function useBrowserSession(options) {
|
|
|
876
881
|
});
|
|
877
882
|
const pageLive = usePageLiveActivity();
|
|
878
883
|
const pollIntervalMs = Math.max(750, options.pollIntervalMs ?? 2e3);
|
|
879
|
-
const [state, setState] = useState5(() => emptyState4(browserSessionId, enabled));
|
|
880
|
-
const visible = state.browserSessionId === browserSessionId ? state : emptyState4(browserSessionId, enabled);
|
|
884
|
+
const [state, setState] = useState5(() => ({ ...emptyState4(browserSessionId, enabled), source }));
|
|
885
|
+
const visible = state.source === source && state.browserSessionId === browserSessionId ? state : { ...emptyState4(browserSessionId, enabled), source };
|
|
881
886
|
const refreshBlocked = isNonRetryableInteractionError(visible.error);
|
|
882
887
|
const inputFailureRef = useRef5(visible.inputFailure);
|
|
883
888
|
inputFailureRef.current = visible.inputFailure;
|
|
884
889
|
const selectedTargetIdRef = useRef5(visible.selectedTargetId);
|
|
885
890
|
selectedTargetIdRef.current = visible.selectedTargetId;
|
|
891
|
+
const selectionRevisionRef = useRef5(0);
|
|
892
|
+
const actionResultOrderRef = useRef5({ next: 0, settled: 0 });
|
|
886
893
|
const selectedTargetRef = useRef5(
|
|
887
894
|
visible.targets.find((target) => target.id === visible.selectedTargetId) ?? null
|
|
888
895
|
);
|
|
@@ -903,17 +910,23 @@ function useBrowserSession(options) {
|
|
|
903
910
|
});
|
|
904
911
|
const mutationRef = useRef5({
|
|
905
912
|
browserSessionId,
|
|
906
|
-
|
|
913
|
+
source,
|
|
914
|
+
count: 0,
|
|
915
|
+
needsInventoryReconcile: false
|
|
907
916
|
});
|
|
908
917
|
const mountedRef = useRef5(true);
|
|
918
|
+
const isCurrentSource = useCallback5(
|
|
919
|
+
() => mountedRef.current && sourceRef.current === source,
|
|
920
|
+
[source]
|
|
921
|
+
);
|
|
909
922
|
const invalidateRefresh = useCallback5(() => {
|
|
910
923
|
const id = requestRef.current.id + 1;
|
|
911
924
|
requestRef.current.controller?.abort();
|
|
912
925
|
requestRef.current = { id, controller: null };
|
|
913
926
|
}, []);
|
|
914
927
|
const refreshInventory = useCallback5(
|
|
915
|
-
async (checkInput = false) => {
|
|
916
|
-
if (!enabled || !browserSessionId) return;
|
|
928
|
+
async (checkInput = false, observeSelectedTarget = true) => {
|
|
929
|
+
if (!enabled || !browserSessionId || !isCurrentSource()) return;
|
|
917
930
|
const checkedInputFailure = inputFailureRef.current;
|
|
918
931
|
const id = requestRef.current.id + 1;
|
|
919
932
|
requestRef.current.controller?.abort();
|
|
@@ -928,14 +941,18 @@ function useBrowserSession(options) {
|
|
|
928
941
|
signal: controller.signal
|
|
929
942
|
})
|
|
930
943
|
]);
|
|
931
|
-
if (!
|
|
944
|
+
if (!isCurrentSource() || requestRef.current.id !== id) return;
|
|
932
945
|
const selected = chooseTarget(targetResponse.targets, selectedTargetIdRef.current);
|
|
933
946
|
selectedTargetIdRef.current = selected?.id ?? null;
|
|
947
|
+
selectedTargetRef.current = selected;
|
|
934
948
|
const previousObservation = observationRef.current.browserSessionId === browserSessionId ? observationRef.current.observation : null;
|
|
935
949
|
const retainedObservation = selected && previousObservation && sameObservationTarget(previousObservation, selected) ? previousObservation : null;
|
|
936
|
-
observationRef.current = {
|
|
950
|
+
observationRef.current = {
|
|
951
|
+
browserSessionId,
|
|
952
|
+
observation: retainedObservation
|
|
953
|
+
};
|
|
937
954
|
setState(
|
|
938
|
-
(current) => current.browserSessionId === browserSessionId ? {
|
|
955
|
+
(current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? {
|
|
939
956
|
...current,
|
|
940
957
|
session,
|
|
941
958
|
targets: targetResponse.targets,
|
|
@@ -948,7 +965,7 @@ function useBrowserSession(options) {
|
|
|
948
965
|
inputFailure: checkInput && session.id === browserSessionId && targetResponse.browserSessionId === browserSessionId && current.inputFailure === checkedInputFailure ? null : current.inputFailure
|
|
949
966
|
} : current
|
|
950
967
|
);
|
|
951
|
-
if (!selected || !semanticObservationEnabledRef.current) return;
|
|
968
|
+
if (!selected || !observeSelectedTarget || !semanticObservationEnabledRef.current) return;
|
|
952
969
|
try {
|
|
953
970
|
const observation = await client.observeBrowserTarget(
|
|
954
971
|
workspaceId,
|
|
@@ -956,22 +973,29 @@ function useBrowserSession(options) {
|
|
|
956
973
|
selected.id,
|
|
957
974
|
{ signal: controller.signal }
|
|
958
975
|
);
|
|
959
|
-
if (!
|
|
976
|
+
if (!isCurrentSource() || requestRef.current.id !== id) return;
|
|
977
|
+
const currentSelected = selectedTargetRef.current;
|
|
978
|
+
if (observationRef.current.browserSessionId !== browserSessionId || currentSelected?.id !== selected.id || observation.browserSessionId !== browserSessionId || observation.target.id !== selected.id || observation.target.controllerGeneration !== currentSelected.controllerGeneration || observation.target.targetGeneration !== currentSelected.targetGeneration)
|
|
979
|
+
return;
|
|
980
|
+
selectedTargetRef.current = observation.target;
|
|
960
981
|
observationRef.current = { browserSessionId, observation };
|
|
961
982
|
lastObservationAtRef.current = Date.now();
|
|
962
983
|
setState(
|
|
963
|
-
(current) => current.browserSessionId === browserSessionId && current.selectedTargetId === observation.target.id ? {
|
|
984
|
+
(current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId && current.selectedTargetId === observation.target.id ? {
|
|
985
|
+
...current,
|
|
986
|
+
observation,
|
|
987
|
+
targets: replaceTarget(current.targets, observation.target)
|
|
988
|
+
} : current
|
|
964
989
|
);
|
|
965
990
|
} catch (cause) {
|
|
966
|
-
if (controller.signal.aborted || !
|
|
991
|
+
if (controller.signal.aborted || !isCurrentSource() || requestRef.current.id !== id)
|
|
967
992
|
return;
|
|
968
993
|
void cause;
|
|
969
994
|
}
|
|
970
995
|
} catch (cause) {
|
|
971
|
-
if (controller.signal.aborted || !
|
|
972
|
-
return;
|
|
996
|
+
if (controller.signal.aborted || !isCurrentSource() || requestRef.current.id !== id) return;
|
|
973
997
|
setState(
|
|
974
|
-
(current) => current.browserSessionId === browserSessionId ? {
|
|
998
|
+
(current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? {
|
|
975
999
|
...current,
|
|
976
1000
|
loading: false,
|
|
977
1001
|
error: cause instanceof Error ? cause : new Error(String(cause))
|
|
@@ -983,11 +1007,11 @@ function useBrowserSession(options) {
|
|
|
983
1007
|
}
|
|
984
1008
|
}
|
|
985
1009
|
},
|
|
986
|
-
[browserSessionId, client, enabled, workspaceId]
|
|
1010
|
+
[browserSessionId, client, enabled, isCurrentSource, source, workspaceId]
|
|
987
1011
|
);
|
|
988
1012
|
const refresh = useCallback5(async () => await refreshInventory(true), [refreshInventory]);
|
|
989
1013
|
const refreshSemantic = useCallback5(async () => {
|
|
990
|
-
if (!enabled || !browserSessionId) return;
|
|
1014
|
+
if (!enabled || !browserSessionId || !isCurrentSource()) return;
|
|
991
1015
|
const target = selectedTargetRef.current;
|
|
992
1016
|
if (!target) return;
|
|
993
1017
|
const id = requestRef.current.id + 1;
|
|
@@ -1001,34 +1025,34 @@ function useBrowserSession(options) {
|
|
|
1001
1025
|
target.id,
|
|
1002
1026
|
{ signal: controller.signal }
|
|
1003
1027
|
);
|
|
1004
|
-
if (!
|
|
1028
|
+
if (!isCurrentSource() || requestRef.current.id !== id) return;
|
|
1005
1029
|
if (!sameObservationTarget(observation, selectedTargetRef.current)) return;
|
|
1006
1030
|
observationRef.current = { browserSessionId, observation };
|
|
1007
1031
|
lastObservationAtRef.current = Date.now();
|
|
1008
1032
|
setState(
|
|
1009
|
-
(current) => current.browserSessionId === browserSessionId && current.selectedTargetId === target.id ? { ...current, observation } : current
|
|
1033
|
+
(current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId && current.selectedTargetId === target.id ? { ...current, observation } : current
|
|
1010
1034
|
);
|
|
1011
1035
|
} catch {
|
|
1012
1036
|
} finally {
|
|
1013
1037
|
if (requestRef.current.id === id) requestRef.current = { id, controller: null };
|
|
1014
1038
|
}
|
|
1015
|
-
}, [browserSessionId, client, enabled, workspaceId]);
|
|
1039
|
+
}, [browserSessionId, client, enabled, isCurrentSource, source, workspaceId]);
|
|
1016
1040
|
useEffect5(() => {
|
|
1017
1041
|
mountedRef.current = true;
|
|
1018
1042
|
if (!enabled) {
|
|
1019
1043
|
invalidateRefresh();
|
|
1020
|
-
setState(emptyState4(browserSessionId, false));
|
|
1044
|
+
setState({ ...emptyState4(browserSessionId, false), source });
|
|
1021
1045
|
return;
|
|
1022
1046
|
}
|
|
1023
1047
|
setState(
|
|
1024
|
-
(current) => current.browserSessionId === browserSessionId ? { ...current, loading: true } : emptyState4(browserSessionId, true)
|
|
1048
|
+
(current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? { ...current, loading: true } : { ...emptyState4(browserSessionId, true), source }
|
|
1025
1049
|
);
|
|
1026
1050
|
void refreshInventory();
|
|
1027
1051
|
return () => {
|
|
1028
1052
|
mountedRef.current = false;
|
|
1029
1053
|
invalidateRefresh();
|
|
1030
1054
|
};
|
|
1031
|
-
}, [browserSessionId, enabled, invalidateRefresh, refreshInventory]);
|
|
1055
|
+
}, [browserSessionId, enabled, invalidateRefresh, isCurrentSource, refreshInventory, source]);
|
|
1032
1056
|
useEffect5(() => {
|
|
1033
1057
|
const previous = previousSemanticObservationRef.current;
|
|
1034
1058
|
previousSemanticObservationRef.current = {
|
|
@@ -1042,11 +1066,19 @@ function useBrowserSession(options) {
|
|
|
1042
1066
|
}
|
|
1043
1067
|
observationRef.current = { browserSessionId, observation: null };
|
|
1044
1068
|
setState(
|
|
1045
|
-
(current) => current.browserSessionId === browserSessionId ? { ...current, observation: null } : current
|
|
1069
|
+
(current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? { ...current, observation: null } : current
|
|
1046
1070
|
);
|
|
1047
1071
|
void refreshSemantic();
|
|
1048
1072
|
}
|
|
1049
|
-
}, [
|
|
1073
|
+
}, [
|
|
1074
|
+
browserSessionId,
|
|
1075
|
+
enabled,
|
|
1076
|
+
isCurrentSource,
|
|
1077
|
+
pollIntervalMs,
|
|
1078
|
+
refreshSemantic,
|
|
1079
|
+
semanticObservationEnabled,
|
|
1080
|
+
source
|
|
1081
|
+
]);
|
|
1050
1082
|
useEffect5(() => {
|
|
1051
1083
|
if (!enabled || !pageLive || visible.mutating || refreshBlocked) return;
|
|
1052
1084
|
const timer = setInterval(() => {
|
|
@@ -1058,6 +1090,7 @@ function useBrowserSession(options) {
|
|
|
1058
1090
|
if (!enabled || !browserSessionId || !pageLive || refreshBlocked) return;
|
|
1059
1091
|
let disposed = false;
|
|
1060
1092
|
const heartbeat = () => {
|
|
1093
|
+
if (!isCurrentSource()) return;
|
|
1061
1094
|
void client.heartbeatBrowserSession(workspaceId, browserSessionId).catch(() => {
|
|
1062
1095
|
if (!disposed) void refreshInventory();
|
|
1063
1096
|
});
|
|
@@ -1067,151 +1100,216 @@ function useBrowserSession(options) {
|
|
|
1067
1100
|
disposed = true;
|
|
1068
1101
|
clearInterval(timer);
|
|
1069
1102
|
};
|
|
1070
|
-
}, [
|
|
1103
|
+
}, [
|
|
1104
|
+
browserSessionId,
|
|
1105
|
+
client,
|
|
1106
|
+
enabled,
|
|
1107
|
+
isCurrentSource,
|
|
1108
|
+
pageLive,
|
|
1109
|
+
refreshInventory,
|
|
1110
|
+
refreshBlocked,
|
|
1111
|
+
workspaceId
|
|
1112
|
+
]);
|
|
1071
1113
|
const runMutation = useCallback5(
|
|
1072
|
-
async (scopeBrowserSessionId, operation) => {
|
|
1114
|
+
async (scopeBrowserSessionId, operation, selectionRead = false, changesInventory = false) => {
|
|
1115
|
+
if (!enabled || !isCurrentSource()) {
|
|
1116
|
+
throw new Error("The browser source is no longer selected.");
|
|
1117
|
+
}
|
|
1118
|
+
const selectionRevision = ++selectionRevisionRef.current;
|
|
1073
1119
|
invalidateRefresh();
|
|
1074
|
-
|
|
1120
|
+
const readRequestId = requestRef.current.id;
|
|
1121
|
+
if (mutationRef.current.browserSessionId !== scopeBrowserSessionId || mutationRef.current.source !== source) {
|
|
1075
1122
|
mutationRef.current = {
|
|
1076
1123
|
browserSessionId: scopeBrowserSessionId,
|
|
1077
|
-
|
|
1124
|
+
source,
|
|
1125
|
+
count: 0,
|
|
1126
|
+
needsInventoryReconcile: false
|
|
1078
1127
|
};
|
|
1079
1128
|
}
|
|
1080
1129
|
mutationRef.current.count += 1;
|
|
1081
1130
|
setState(
|
|
1082
|
-
(current) => current.browserSessionId === scopeBrowserSessionId ? { ...current, mutating: true, error: null } : current
|
|
1131
|
+
(current) => isCurrentSource() && current.source === source && current.browserSessionId === scopeBrowserSessionId ? { ...current, mutating: true, error: null } : current
|
|
1083
1132
|
);
|
|
1084
1133
|
try {
|
|
1085
|
-
return await operation();
|
|
1134
|
+
return await operation(selectionRevision, readRequestId);
|
|
1086
1135
|
} catch (cause) {
|
|
1087
1136
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
1088
|
-
|
|
1089
|
-
(
|
|
1090
|
-
|
|
1137
|
+
if (!selectionRead || requestRef.current.id === readRequestId) {
|
|
1138
|
+
setState(
|
|
1139
|
+
(current) => isCurrentSource() && selectionRevisionRef.current === selectionRevision && current.source === source && current.browserSessionId === scopeBrowserSessionId ? { ...current, error } : current
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1091
1142
|
throw error;
|
|
1092
1143
|
} finally {
|
|
1093
|
-
if (mutationRef.current.browserSessionId === scopeBrowserSessionId) {
|
|
1144
|
+
if (mutationRef.current.browserSessionId === scopeBrowserSessionId && mutationRef.current.source === source) {
|
|
1145
|
+
if (changesInventory && (selectionRevisionRef.current !== selectionRevision || requestRef.current.id !== readRequestId)) {
|
|
1146
|
+
mutationRef.current.needsInventoryReconcile = true;
|
|
1147
|
+
}
|
|
1094
1148
|
mutationRef.current.count = Math.max(0, mutationRef.current.count - 1);
|
|
1095
1149
|
const mutating = mutationRef.current.count > 0;
|
|
1096
1150
|
setState(
|
|
1097
|
-
(current) => current.browserSessionId === scopeBrowserSessionId ? { ...current, mutating } : current
|
|
1151
|
+
(current) => isCurrentSource() && current.source === source && current.browserSessionId === scopeBrowserSessionId ? { ...current, mutating } : current
|
|
1098
1152
|
);
|
|
1153
|
+
if (!mutating && mutationRef.current.needsInventoryReconcile && isCurrentSource()) {
|
|
1154
|
+
mutationRef.current.needsInventoryReconcile = false;
|
|
1155
|
+
void refreshInventory(false, false);
|
|
1156
|
+
}
|
|
1099
1157
|
}
|
|
1100
1158
|
}
|
|
1101
1159
|
},
|
|
1102
|
-
[invalidateRefresh]
|
|
1160
|
+
[enabled, invalidateRefresh, isCurrentSource, refreshInventory, source]
|
|
1103
1161
|
);
|
|
1104
1162
|
const selectTarget = useCallback5(
|
|
1105
1163
|
async (targetId) => {
|
|
1106
1164
|
if (!browserSessionId) throw new Error("No BrowserSession is selected.");
|
|
1107
|
-
return await runMutation(
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1165
|
+
return await runMutation(
|
|
1166
|
+
browserSessionId,
|
|
1167
|
+
async (selectionRevision, readRequestId) => {
|
|
1168
|
+
const isCurrentSelection = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision;
|
|
1169
|
+
const isCurrentSelectionRead = () => isCurrentSelection() && requestRef.current.id === readRequestId;
|
|
1170
|
+
let targets = visible.targets;
|
|
1171
|
+
let target;
|
|
1172
|
+
let observation;
|
|
1173
|
+
try {
|
|
1174
|
+
observation = await client.selectBrowserTarget(workspaceId, browserSessionId, targetId);
|
|
1175
|
+
target = observation.target;
|
|
1176
|
+
} catch (cause) {
|
|
1177
|
+
if (!isCurrentSelectionRead() || !isMissingBrowserTarget(cause)) throw cause;
|
|
1178
|
+
const response = await client.listBrowserTargets(workspaceId, browserSessionId);
|
|
1179
|
+
if (!isCurrentSelectionRead()) throw cause;
|
|
1180
|
+
targets = response.targets;
|
|
1181
|
+
const fallback = chooseTarget(targets, null);
|
|
1182
|
+
if (!fallback) {
|
|
1183
|
+
selectedTargetIdRef.current = null;
|
|
1184
|
+
selectedTargetRef.current = null;
|
|
1185
|
+
observationRef.current = { browserSessionId, observation: null };
|
|
1186
|
+
setState(
|
|
1187
|
+
(current) => isCurrentSelection() && current.source === source && current.browserSessionId === browserSessionId ? {
|
|
1188
|
+
...current,
|
|
1189
|
+
targets,
|
|
1190
|
+
selectedTargetId: null,
|
|
1191
|
+
observation: null
|
|
1192
|
+
} : current
|
|
1193
|
+
);
|
|
1194
|
+
throw cause;
|
|
1195
|
+
}
|
|
1196
|
+
observation = await client.selectBrowserTarget(
|
|
1197
|
+
workspaceId,
|
|
1198
|
+
browserSessionId,
|
|
1199
|
+
fallback.id
|
|
1129
1200
|
);
|
|
1130
|
-
|
|
1201
|
+
target = observation.target;
|
|
1131
1202
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1203
|
+
if (!isCurrentSelectionRead()) return target;
|
|
1204
|
+
selectedTargetIdRef.current = target.id;
|
|
1205
|
+
selectedTargetRef.current = target;
|
|
1206
|
+
observationRef.current = { browserSessionId, observation };
|
|
1207
|
+
lastObservationAtRef.current = Date.now();
|
|
1208
|
+
setState(
|
|
1209
|
+
(current) => isCurrentSelection() && current.source === source && current.browserSessionId === browserSessionId ? {
|
|
1210
|
+
...current,
|
|
1211
|
+
targets: replaceTarget(targets, target).map((candidate) => ({
|
|
1212
|
+
...candidate,
|
|
1213
|
+
selected: candidate.id === target.id
|
|
1214
|
+
})),
|
|
1215
|
+
selectedTargetId: target.id,
|
|
1216
|
+
observation
|
|
1217
|
+
} : current
|
|
1136
1218
|
);
|
|
1137
|
-
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
lastObservationAtRef.current = Date.now();
|
|
1142
|
-
setState(
|
|
1143
|
-
(current) => current.browserSessionId === browserSessionId ? {
|
|
1144
|
-
...current,
|
|
1145
|
-
targets: targets.map((candidate) => ({
|
|
1146
|
-
...candidate,
|
|
1147
|
-
selected: candidate.id === target.id
|
|
1148
|
-
})),
|
|
1149
|
-
selectedTargetId: target.id,
|
|
1150
|
-
observation
|
|
1151
|
-
} : current
|
|
1152
|
-
);
|
|
1153
|
-
return target;
|
|
1154
|
-
});
|
|
1219
|
+
return target;
|
|
1220
|
+
},
|
|
1221
|
+
true
|
|
1222
|
+
);
|
|
1155
1223
|
},
|
|
1156
|
-
[browserSessionId, client, runMutation, visible.targets, workspaceId]
|
|
1224
|
+
[browserSessionId, client, isCurrentSource, runMutation, source, visible.targets, workspaceId]
|
|
1157
1225
|
);
|
|
1158
1226
|
const openTarget = useCallback5(
|
|
1159
1227
|
async (url) => {
|
|
1160
1228
|
if (!browserSessionId) throw new Error("No BrowserSession is selected.");
|
|
1161
|
-
return await runMutation(
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
(
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1229
|
+
return await runMutation(
|
|
1230
|
+
browserSessionId,
|
|
1231
|
+
async (selectionRevision, readRequestId) => {
|
|
1232
|
+
const isCurrentSelection = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision;
|
|
1233
|
+
const isCurrentSelectionRead = () => isCurrentSelection() && requestRef.current.id === readRequestId;
|
|
1234
|
+
const observation = await client.openBrowserTarget(
|
|
1235
|
+
workspaceId,
|
|
1236
|
+
browserSessionId,
|
|
1237
|
+
url === void 0 ? {} : { url }
|
|
1238
|
+
);
|
|
1239
|
+
const target = observation.target;
|
|
1240
|
+
if (!isCurrentSource()) return target;
|
|
1241
|
+
if (!isCurrentSelectionRead()) return target;
|
|
1242
|
+
selectedTargetIdRef.current = target.id;
|
|
1243
|
+
selectedTargetRef.current = target;
|
|
1244
|
+
observationRef.current = { browserSessionId, observation };
|
|
1245
|
+
lastObservationAtRef.current = Date.now();
|
|
1246
|
+
setState(
|
|
1247
|
+
(current) => isCurrentSelection() && current.source === source && current.browserSessionId === browserSessionId ? {
|
|
1248
|
+
...current,
|
|
1249
|
+
targets: [
|
|
1250
|
+
...current.targets.map((candidate) => ({
|
|
1251
|
+
...candidate,
|
|
1252
|
+
selected: false
|
|
1253
|
+
})),
|
|
1254
|
+
target
|
|
1255
|
+
],
|
|
1256
|
+
selectedTargetId: target.id,
|
|
1257
|
+
observation
|
|
1258
|
+
} : current
|
|
1259
|
+
);
|
|
1260
|
+
return target;
|
|
1261
|
+
},
|
|
1262
|
+
false,
|
|
1263
|
+
true
|
|
1264
|
+
);
|
|
1187
1265
|
},
|
|
1188
|
-
[browserSessionId, client, runMutation, workspaceId]
|
|
1266
|
+
[browserSessionId, client, isCurrentSource, runMutation, source, workspaceId]
|
|
1189
1267
|
);
|
|
1190
1268
|
const closeTarget = useCallback5(
|
|
1191
1269
|
async (targetId) => {
|
|
1192
1270
|
if (!browserSessionId) throw new Error("No BrowserSession is selected.");
|
|
1193
|
-
await runMutation(
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1271
|
+
await runMutation(
|
|
1272
|
+
browserSessionId,
|
|
1273
|
+
async (selectionRevision, readRequestId) => {
|
|
1274
|
+
const isCurrentSelection = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision;
|
|
1275
|
+
const isCurrentSelectionRead = () => isCurrentSelection() && requestRef.current.id === readRequestId;
|
|
1276
|
+
const response = await client.closeBrowserTarget(workspaceId, browserSessionId, targetId);
|
|
1277
|
+
if (!isCurrentSource()) return;
|
|
1278
|
+
if (!isCurrentSelectionRead()) return;
|
|
1279
|
+
const selected = chooseTarget(response.targets, selectedTargetIdRef.current);
|
|
1280
|
+
const observed = selected ? await client.observeBrowserTarget(workspaceId, browserSessionId, selected.id) : null;
|
|
1281
|
+
if (!isCurrentSource()) return;
|
|
1282
|
+
if (!isCurrentSelectionRead()) return;
|
|
1283
|
+
const observation = observed && selected && observed.browserSessionId === browserSessionId && observed.target.id === selected.id && observed.target.controllerGeneration === selected.controllerGeneration && observed.target.targetGeneration === selected.targetGeneration ? observed : null;
|
|
1284
|
+
const currentTarget = observation?.target ?? selected;
|
|
1285
|
+
selectedTargetIdRef.current = currentTarget?.id ?? null;
|
|
1286
|
+
selectedTargetRef.current = currentTarget;
|
|
1287
|
+
observationRef.current = { browserSessionId, observation };
|
|
1288
|
+
if (observation) lastObservationAtRef.current = Date.now();
|
|
1289
|
+
setState(
|
|
1290
|
+
(current) => isCurrentSelection() && current.source === source && current.browserSessionId === browserSessionId ? {
|
|
1291
|
+
...current,
|
|
1292
|
+
targets: currentTarget ? replaceTarget(response.targets, currentTarget) : response.targets,
|
|
1293
|
+
selectedTargetId: currentTarget?.id ?? null,
|
|
1294
|
+
observation
|
|
1295
|
+
} : current
|
|
1296
|
+
);
|
|
1297
|
+
},
|
|
1298
|
+
false,
|
|
1299
|
+
true
|
|
1300
|
+
);
|
|
1209
1301
|
},
|
|
1210
|
-
[browserSessionId, client, runMutation, workspaceId]
|
|
1302
|
+
[browserSessionId, client, isCurrentSource, runMutation, source, workspaceId]
|
|
1211
1303
|
);
|
|
1212
1304
|
const dispatchAction = useCallback5(
|
|
1213
1305
|
async (action, operationId, frame, observationMode = "none") => {
|
|
1214
1306
|
if (!browserSessionId) throw new Error("No BrowserSession is selected.");
|
|
1307
|
+
if (!enabled || !isCurrentSource()) {
|
|
1308
|
+
throw new Error("The browser source is no longer selected.");
|
|
1309
|
+
}
|
|
1310
|
+
if (!selectedTargetRef.current) {
|
|
1311
|
+
throw new Error("The browser page is not ready for input.");
|
|
1312
|
+
}
|
|
1215
1313
|
if (frame && frame.browserSessionId !== browserSessionId) {
|
|
1216
1314
|
throw new Error("The displayed browser frame belongs to another BrowserSession.");
|
|
1217
1315
|
}
|
|
@@ -1221,7 +1319,7 @@ function useBrowserSession(options) {
|
|
|
1221
1319
|
expectedTargetGeneration: frame.targetGeneration,
|
|
1222
1320
|
expectedDocumentGeneration: frame.documentGeneration,
|
|
1223
1321
|
expectedFrameId: frame.frameId
|
|
1224
|
-
} : currentObservation ? {
|
|
1322
|
+
} : currentObservation && sameObservationTarget(currentObservation, selectedTargetRef.current) ? {
|
|
1225
1323
|
targetId: currentObservation.target.id,
|
|
1226
1324
|
expectedTargetGeneration: currentObservation.target.targetGeneration,
|
|
1227
1325
|
expectedDocumentGeneration: currentObservation.target.documentGeneration,
|
|
@@ -1230,6 +1328,21 @@ function useBrowserSession(options) {
|
|
|
1230
1328
|
if (!fence) {
|
|
1231
1329
|
throw new Error("The browser page is not ready for input.");
|
|
1232
1330
|
}
|
|
1331
|
+
const expectedControllerGeneration = frame?.controllerGeneration ?? currentObservation.target.controllerGeneration;
|
|
1332
|
+
const selectionRevision = selectionRevisionRef.current;
|
|
1333
|
+
const resultOrder = ++actionResultOrderRef.current.next;
|
|
1334
|
+
let resultDocumentGeneration = fence.expectedDocumentGeneration;
|
|
1335
|
+
const isCurrentResultView = () => {
|
|
1336
|
+
const selected = selectedTargetRef.current;
|
|
1337
|
+
return isCurrentSource() && selectionRevisionRef.current === selectionRevision && selected?.browserSessionId === browserSessionId && selected.id === fence.targetId && selected.controllerGeneration === expectedControllerGeneration && selected.targetGeneration === fence.expectedTargetGeneration && (selected.documentGeneration === fence.expectedDocumentGeneration || selected.documentGeneration === resultDocumentGeneration);
|
|
1338
|
+
};
|
|
1339
|
+
const canProjectResult = () => isCurrentResultView() && resultOrder >= actionResultOrderRef.current.settled;
|
|
1340
|
+
const settleResult = () => {
|
|
1341
|
+
if (!canProjectResult()) return false;
|
|
1342
|
+
actionResultOrderRef.current.settled = resultOrder;
|
|
1343
|
+
invalidateRefresh();
|
|
1344
|
+
return true;
|
|
1345
|
+
};
|
|
1233
1346
|
try {
|
|
1234
1347
|
const receipt = await client.actInBrowser(workspaceId, browserSessionId, {
|
|
1235
1348
|
operationId,
|
|
@@ -1237,40 +1350,49 @@ function useBrowserSession(options) {
|
|
|
1237
1350
|
observationMode,
|
|
1238
1351
|
action
|
|
1239
1352
|
});
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
);
|
|
1249
|
-
}
|
|
1250
|
-
if (receipt.observation) {
|
|
1353
|
+
resultDocumentGeneration = receipt.observation?.target.documentGeneration ?? fence.expectedDocumentGeneration;
|
|
1354
|
+
const settled = receipt.state === "completed" || receipt.state === "failed" || receipt.state === "outcome_unknown";
|
|
1355
|
+
if (!canProjectResult() || settled && !settleResult()) return receipt;
|
|
1356
|
+
const receivedObservation = receipt.observation;
|
|
1357
|
+
const selectedTarget = selectedTargetRef.current;
|
|
1358
|
+
let projectedObservation = null;
|
|
1359
|
+
if (settled && receivedObservation && canProjectResult() && receipt.browserSessionId === browserSessionId && receipt.controllerGeneration === expectedControllerGeneration && receipt.targetId === fence.targetId && observationRef.current.browserSessionId === browserSessionId && receivedObservation.browserSessionId === browserSessionId && selectedTarget?.browserSessionId === browserSessionId && selectedTarget.id === fence.targetId && receivedObservation.target.id === fence.targetId && selectedTarget.controllerGeneration === expectedControllerGeneration && receivedObservation.target.controllerGeneration === expectedControllerGeneration && selectedTarget.targetGeneration === fence.expectedTargetGeneration && receivedObservation.target.targetGeneration === fence.expectedTargetGeneration && (selectedTarget.documentGeneration === fence.expectedDocumentGeneration || selectedTarget.documentGeneration === receivedObservation.target.documentGeneration)) {
|
|
1360
|
+
projectedObservation = receivedObservation;
|
|
1251
1361
|
observationRef.current = {
|
|
1252
1362
|
browserSessionId,
|
|
1253
|
-
observation:
|
|
1363
|
+
observation: receivedObservation
|
|
1254
1364
|
};
|
|
1365
|
+
selectedTargetRef.current = receivedObservation.target;
|
|
1255
1366
|
lastObservationAtRef.current = Date.now();
|
|
1256
|
-
setState(
|
|
1257
|
-
(current) => current.browserSessionId === browserSessionId ? {
|
|
1258
|
-
...current,
|
|
1259
|
-
observation: receipt.observation,
|
|
1260
|
-
targets: replaceTarget(current.targets, receipt.observation.target)
|
|
1261
|
-
} : current
|
|
1262
|
-
);
|
|
1263
1367
|
}
|
|
1368
|
+
setState(
|
|
1369
|
+
(current) => isCurrentResultView() && current.source === source && current.browserSessionId === browserSessionId && current.actionResultOrder <= resultOrder ? {
|
|
1370
|
+
...current,
|
|
1371
|
+
actionResultOrder: settled ? resultOrder : current.actionResultOrder,
|
|
1372
|
+
error: receipt.state === "completed" ? null : current.error,
|
|
1373
|
+
inputFailure: receipt.state === "completed" ? current.inputFailure : {
|
|
1374
|
+
operationId: receipt.operationId,
|
|
1375
|
+
state: receipt.state,
|
|
1376
|
+
error: receipt.error
|
|
1377
|
+
},
|
|
1378
|
+
...projectedObservation ? {
|
|
1379
|
+
observation: projectedObservation,
|
|
1380
|
+
targets: replaceTarget(current.targets, projectedObservation.target)
|
|
1381
|
+
} : {}
|
|
1382
|
+
} : current
|
|
1383
|
+
);
|
|
1264
1384
|
return receipt;
|
|
1265
1385
|
} catch (cause) {
|
|
1266
1386
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
1267
|
-
|
|
1268
|
-
(
|
|
1269
|
-
|
|
1387
|
+
if (settleResult()) {
|
|
1388
|
+
setState(
|
|
1389
|
+
(current) => isCurrentResultView() && current.source === source && current.browserSessionId === browserSessionId && current.actionResultOrder <= resultOrder ? { ...current, error, actionResultOrder: resultOrder } : current
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1270
1392
|
throw error;
|
|
1271
1393
|
}
|
|
1272
1394
|
},
|
|
1273
|
-
[browserSessionId, client, workspaceId]
|
|
1395
|
+
[browserSessionId, client, enabled, invalidateRefresh, isCurrentSource, source, workspaceId]
|
|
1274
1396
|
);
|
|
1275
1397
|
const act = useCallback5(
|
|
1276
1398
|
async (action, operationId = crypto.randomUUID()) => await dispatchAction(action, operationId, null),
|
|
@@ -1282,13 +1404,16 @@ function useBrowserSession(options) {
|
|
|
1282
1404
|
);
|
|
1283
1405
|
const observeForInput = useCallback5(async () => {
|
|
1284
1406
|
const target = selectedTargetRef.current;
|
|
1407
|
+
if (!enabled || !isCurrentSource()) {
|
|
1408
|
+
throw new Error("The browser source is no longer selected.");
|
|
1409
|
+
}
|
|
1285
1410
|
if (!browserSessionId || !target) throw new Error("No browser tab is selected.");
|
|
1286
1411
|
const observation = await client.observeBrowserTarget(workspaceId, browserSessionId, target.id);
|
|
1287
|
-
if (!
|
|
1412
|
+
if (!isCurrentSource() || observation.browserSessionId !== browserSessionId || !sameObservationTarget(observation, selectedTargetRef.current)) {
|
|
1288
1413
|
throw new Error("The browser page changed. Open the options again.");
|
|
1289
1414
|
}
|
|
1290
1415
|
return observation;
|
|
1291
|
-
}, [browserSessionId, client, workspaceId]);
|
|
1416
|
+
}, [browserSessionId, client, enabled, isCurrentSource, workspaceId]);
|
|
1292
1417
|
const actFromObservation = useCallback5(
|
|
1293
1418
|
async (action, observation) => {
|
|
1294
1419
|
if (!sameObservationTarget(observation, selectedTargetRef.current)) {
|
|
@@ -1296,6 +1421,7 @@ function useBrowserSession(options) {
|
|
|
1296
1421
|
}
|
|
1297
1422
|
return await dispatchAction(action, crypto.randomUUID(), {
|
|
1298
1423
|
browserSessionId: observation.browserSessionId,
|
|
1424
|
+
controllerGeneration: observation.target.controllerGeneration,
|
|
1299
1425
|
targetId: observation.target.id,
|
|
1300
1426
|
targetGeneration: observation.target.targetGeneration,
|
|
1301
1427
|
documentGeneration: observation.target.documentGeneration,
|
|
@@ -1306,6 +1432,9 @@ function useBrowserSession(options) {
|
|
|
1306
1432
|
);
|
|
1307
1433
|
const diagnostics = useCallback5(
|
|
1308
1434
|
async (diagnosticOptions = {}) => {
|
|
1435
|
+
if (!enabled || !isCurrentSource()) {
|
|
1436
|
+
throw new Error("The browser source is no longer selected.");
|
|
1437
|
+
}
|
|
1309
1438
|
const targetId = selectedTargetIdRef.current;
|
|
1310
1439
|
if (!browserSessionId || !targetId) {
|
|
1311
1440
|
throw new Error("No browser tab is selected.");
|
|
@@ -1317,16 +1446,19 @@ function useBrowserSession(options) {
|
|
|
1317
1446
|
diagnosticOptions
|
|
1318
1447
|
);
|
|
1319
1448
|
},
|
|
1320
|
-
[browserSessionId, client, workspaceId]
|
|
1449
|
+
[browserSessionId, client, enabled, isCurrentSource, workspaceId]
|
|
1321
1450
|
);
|
|
1322
1451
|
const readClipboard = useCallback5(async () => {
|
|
1452
|
+
if (!enabled || !isCurrentSource()) {
|
|
1453
|
+
throw new Error("The browser source is no longer selected.");
|
|
1454
|
+
}
|
|
1323
1455
|
if (!browserSessionId) throw new Error("No BrowserSession is selected.");
|
|
1324
1456
|
const clipboard = await client.readBrowserClipboard(workspaceId, browserSessionId);
|
|
1325
|
-
if (clipboard.browserSessionId !== browserSessionId) {
|
|
1457
|
+
if (!isCurrentSource() || clipboard.browserSessionId !== browserSessionId) {
|
|
1326
1458
|
throw new Error("Browser clipboard belongs to another BrowserSession.");
|
|
1327
1459
|
}
|
|
1328
1460
|
return clipboard;
|
|
1329
|
-
}, [browserSessionId, client, workspaceId]);
|
|
1461
|
+
}, [browserSessionId, client, enabled, isCurrentSource, workspaceId]);
|
|
1330
1462
|
return {
|
|
1331
1463
|
session: visible.session,
|
|
1332
1464
|
targets: visible.targets,
|
|
@@ -1358,7 +1490,8 @@ function emptyState4(browserSessionId, loading) {
|
|
|
1358
1490
|
loading,
|
|
1359
1491
|
mutating: false,
|
|
1360
1492
|
error: null,
|
|
1361
|
-
inputFailure: null
|
|
1493
|
+
inputFailure: null,
|
|
1494
|
+
actionResultOrder: 0
|
|
1362
1495
|
};
|
|
1363
1496
|
}
|
|
1364
1497
|
function chooseTarget(targets, preferredId) {
|
|
@@ -4547,4 +4680,4 @@ export {
|
|
|
4547
4680
|
useBrowserFrameStream,
|
|
4548
4681
|
BrowserViewer
|
|
4549
4682
|
};
|
|
4550
|
-
//# sourceMappingURL=chunk-
|
|
4683
|
+
//# sourceMappingURL=chunk-2OQA3CPB.js.map
|