|
8 | 8 | * <script type="text/javascript" src="/_supervisor/frontend/main.js" charset="utf-8"></script> should be inserted to index.html as first body script,
|
9 | 9 | * all other IDE scripts should go afterwards, head element should not have scripts
|
10 | 10 | */
|
11 |
| -import { IDEMetricsServiceClient, MetricsName } from "./ide/ide-metrics-service-client"; |
12 |
| -IDEMetricsServiceClient.addCounter(MetricsName.SupervisorFrontendClientTotal).catch(() => {}); |
13 | 11 |
|
14 |
| -//#region supervisor frontend error capture |
15 |
| -function isElement(obj: any): obj is Element { |
16 |
| - return typeof obj.getAttribute === "function"; |
| 12 | +import { workspaceUrl } from "./shared/urls"; |
| 13 | +if (workspaceUrl.debugWorkspace) { |
| 14 | + require("./debug"); |
| 15 | +} else { |
| 16 | + require("./regular"); |
17 | 17 | }
|
18 |
| - |
19 |
| -window.addEventListener("error", (event) => { |
20 |
| - const labels: Record<string, string> = {}; |
21 |
| - let resourceSource: string | null | undefined; |
22 |
| - if (isElement(event.target)) { |
23 |
| - // We take a look at what is the resource that was attempted to load; |
24 |
| - resourceSource = event.target.getAttribute("src") || event.target.getAttribute("href"); |
25 |
| - // If the event has a `target`, it means that it wasn't a script error |
26 |
| - if (resourceSource) { |
27 |
| - if (resourceSource.match(new RegExp(/\/build\/ide\/code:.+\/__files__\//g))) { |
28 |
| - // TODO(ak) reconsider how to hide knowledge of VS Code from supervisor frontend, i.e instrument amd loader instead |
29 |
| - labels["resource"] = "vscode-web-workbench"; |
30 |
| - } |
31 |
| - labels["error"] = "LoadError"; |
32 |
| - } |
33 |
| - } |
34 |
| - if (event.error) { |
35 |
| - IDEMetricsServiceClient.reportError(event.error).catch(() => {}); |
36 |
| - } else if (labels["error"] == "LoadError") { |
37 |
| - let error = new Error("LoadError"); |
38 |
| - IDEMetricsServiceClient.reportError(error, { |
39 |
| - resource: labels["resource"], |
40 |
| - url: resourceSource ?? "", |
41 |
| - }).catch(() => {}); |
42 |
| - } |
43 |
| - IDEMetricsServiceClient.addCounter(MetricsName.SupervisorFrontendErrorTotal, labels).catch(() => {}); |
44 |
| -}); |
45 |
| -//#endregion |
46 |
| - |
47 |
| -require("../src/shared/index.css"); |
48 |
| - |
49 |
| -import { createGitpodService, WorkspaceInstancePhase } from "@gitpod/gitpod-protocol"; |
50 |
| -import { DisposableCollection } from "@gitpod/gitpod-protocol/lib/util/disposable"; |
51 |
| -import * as GitpodServiceClient from "./ide/gitpod-service-client"; |
52 |
| -import * as heartBeat from "./ide/heart-beat"; |
53 |
| -import * as IDEFrontendService from "./ide/ide-frontend-service-impl"; |
54 |
| -import * as IDEWorker from "./ide/ide-worker"; |
55 |
| -import * as IDEWebSocket from "./ide/ide-web-socket"; |
56 |
| -import { SupervisorServiceClient } from "./ide/supervisor-service-client"; |
57 |
| -import * as LoadingFrame from "./shared/loading-frame"; |
58 |
| -import { serverUrl, startUrl } from "./shared/urls"; |
59 |
| - |
60 |
| -window.gitpod = { |
61 |
| - service: createGitpodService(serverUrl.toString()), |
62 |
| -}; |
63 |
| -IDEWorker.install(); |
64 |
| -IDEWebSocket.install(); |
65 |
| -const ideService = IDEFrontendService.create(); |
66 |
| -const pendingGitpodServiceClient = GitpodServiceClient.create(); |
67 |
| -const loadingIDE = new Promise((resolve) => window.addEventListener("DOMContentLoaded", resolve, { once: true })); |
68 |
| -const toStop = new DisposableCollection(); |
69 |
| - |
70 |
| -(async () => { |
71 |
| - const gitpodServiceClient = await pendingGitpodServiceClient; |
72 |
| - IDEMetricsServiceClient.loadWorkspaceInfo(gitpodServiceClient); |
73 |
| - |
74 |
| - document.title = gitpodServiceClient.info.workspace.description; |
75 |
| - |
76 |
| - if (gitpodServiceClient.info.workspace.type !== "regular") { |
77 |
| - return; |
78 |
| - } |
79 |
| - |
80 |
| - //#region ide lifecycle |
81 |
| - function isWorkspaceInstancePhase(phase: WorkspaceInstancePhase): boolean { |
82 |
| - return gitpodServiceClient.info.latestInstance?.status.phase === phase; |
83 |
| - } |
84 |
| - if (!isWorkspaceInstancePhase("running")) { |
85 |
| - await new Promise<void>((resolve) => { |
86 |
| - const listener = gitpodServiceClient.onDidChangeInfo(() => { |
87 |
| - if (isWorkspaceInstancePhase("running")) { |
88 |
| - listener.dispose(); |
89 |
| - resolve(); |
90 |
| - } |
91 |
| - }); |
92 |
| - }); |
93 |
| - } |
94 |
| - const supervisorServiceClient = SupervisorServiceClient.get(gitpodServiceClient); |
95 |
| - const [ideStatus] = await Promise.all([ |
96 |
| - supervisorServiceClient.ideReady, |
97 |
| - supervisorServiceClient.contentReady, |
98 |
| - loadingIDE, |
99 |
| - ]); |
100 |
| - if (isWorkspaceInstancePhase("stopping") || isWorkspaceInstancePhase("stopped")) { |
101 |
| - return; |
102 |
| - } |
103 |
| - toStop.pushAll([ |
104 |
| - IDEWebSocket.connectWorkspace(), |
105 |
| - gitpodServiceClient.onDidChangeInfo(() => { |
106 |
| - if (isWorkspaceInstancePhase("stopping") || isWorkspaceInstancePhase("stopped")) { |
107 |
| - toStop.dispose(); |
108 |
| - } |
109 |
| - }), |
110 |
| - ]); |
111 |
| - const isDesktopIde = ideStatus && ideStatus.desktop && ideStatus.desktop.link; |
112 |
| - if (!isDesktopIde) { |
113 |
| - toStop.push(ideService.start()); |
114 |
| - } |
115 |
| - //#endregion |
116 |
| -})(); |
117 |
| - |
118 |
| -(async () => { |
119 |
| - document.body.style.visibility = "hidden"; |
120 |
| - const [loading, gitpodServiceClient] = await Promise.all([ |
121 |
| - LoadingFrame.load({ gitpodService: window.gitpod.service }), |
122 |
| - pendingGitpodServiceClient, |
123 |
| - ]); |
124 |
| - const sessionId = await loading.sessionId; |
125 |
| - |
126 |
| - if (gitpodServiceClient.info.workspace.type !== "regular") { |
127 |
| - return; |
128 |
| - } |
129 |
| - |
130 |
| - const supervisorServiceClient = SupervisorServiceClient.get(gitpodServiceClient); |
131 |
| - |
132 |
| - let hideDesktopIde = false; |
133 |
| - const serverOrigin = startUrl.url.origin; |
134 |
| - const hideDesktopIdeEventListener = (event: MessageEvent) => { |
135 |
| - if (event.origin === serverOrigin && event.data.type == "openBrowserIde") { |
136 |
| - window.removeEventListener("message", hideDesktopIdeEventListener); |
137 |
| - hideDesktopIde = true; |
138 |
| - toStop.push(ideService.start()); |
139 |
| - } |
140 |
| - }; |
141 |
| - window.addEventListener("message", hideDesktopIdeEventListener, false); |
142 |
| - toStop.push({ dispose: () => window.removeEventListener("message", hideDesktopIdeEventListener) }); |
143 |
| - |
144 |
| - //#region gitpod browser telemetry |
145 |
| - // TODO(ak) get rid of it |
146 |
| - // it is bad usage of window.postMessage |
147 |
| - // VS Code should use Segment directly here and publish to production/staging untrusted |
148 |
| - // supervisor frontend should not care about IDE specifics |
149 |
| - window.addEventListener("message", async (event) => { |
150 |
| - const type = event.data.type; |
151 |
| - if (type === "vscode_telemetry") { |
152 |
| - const { event: eventName, properties } = event.data; |
153 |
| - window.gitpod.service.server.trackEvent({ |
154 |
| - event: eventName, |
155 |
| - properties: { |
156 |
| - sessionId, |
157 |
| - instanceId: gitpodServiceClient.info.latestInstance?.id, |
158 |
| - workspaceId: gitpodServiceClient.info.workspace.id, |
159 |
| - type: gitpodServiceClient.info.workspace.type, |
160 |
| - ...properties, |
161 |
| - }, |
162 |
| - }); |
163 |
| - } |
164 |
| - }); |
165 |
| - //#endregion |
166 |
| - |
167 |
| - type DesktopIDEStatus = { link: string; label: string; clientID?: string; kind?: String }; |
168 |
| - let isDesktopIde: undefined | boolean = undefined; |
169 |
| - let ideStatus: undefined | { desktop: DesktopIDEStatus } = undefined; |
170 |
| - |
171 |
| - //#region current-frame |
172 |
| - let current: HTMLElement = loading.frame; |
173 |
| - let desktopRedirected = false; |
174 |
| - let currentInstanceId = ""; |
175 |
| - const nextFrame = () => { |
176 |
| - const instance = gitpodServiceClient.info.latestInstance; |
177 |
| - if (instance) { |
178 |
| - // refresh web page when instanceId changed |
179 |
| - if (currentInstanceId !== "") { |
180 |
| - if (instance.id !== currentInstanceId && instance.ideUrl !== "") { |
181 |
| - currentInstanceId = instance.id; |
182 |
| - window.location.href = instance.ideUrl; |
183 |
| - } |
184 |
| - } else { |
185 |
| - currentInstanceId = instance.id; |
186 |
| - } |
187 |
| - if (instance.status.phase === "running") { |
188 |
| - if (!hideDesktopIde) { |
189 |
| - if (isDesktopIde == undefined) { |
190 |
| - return loading.frame; |
191 |
| - } |
192 |
| - if (isDesktopIde && !!ideStatus) { |
193 |
| - trackDesktopIDEReady(ideStatus.desktop); |
194 |
| - loading.setState({ |
195 |
| - desktopIdeLink: ideStatus.desktop.link, |
196 |
| - desktopIdeLabel: ideStatus.desktop.label || "Open Desktop IDE", |
197 |
| - desktopIdeClientID: ideStatus.desktop.clientID, |
198 |
| - }); |
199 |
| - if (!desktopRedirected) { |
200 |
| - desktopRedirected = true; |
201 |
| - loading.openDesktopLink(ideStatus.desktop.link); |
202 |
| - } |
203 |
| - return loading.frame; |
204 |
| - } |
205 |
| - } |
206 |
| - if (ideService.state === "ready") { |
207 |
| - return document.body; |
208 |
| - } |
209 |
| - } |
210 |
| - } |
211 |
| - return loading.frame; |
212 |
| - }; |
213 |
| - const updateCurrentFrame = () => { |
214 |
| - const newCurrent = nextFrame(); |
215 |
| - if (current === newCurrent) { |
216 |
| - return; |
217 |
| - } |
218 |
| - current.style.visibility = "hidden"; |
219 |
| - newCurrent.style.visibility = "visible"; |
220 |
| - if (current === document.body) { |
221 |
| - while (document.body.firstChild && document.body.firstChild !== newCurrent) { |
222 |
| - document.body.removeChild(document.body.firstChild); |
223 |
| - } |
224 |
| - while (document.body.lastChild && document.body.lastChild !== newCurrent) { |
225 |
| - document.body.removeChild(document.body.lastChild); |
226 |
| - } |
227 |
| - } |
228 |
| - current = newCurrent; |
229 |
| - }; |
230 |
| - |
231 |
| - const updateLoadingState = () => { |
232 |
| - loading.setState({ |
233 |
| - ideFrontendFailureCause: ideService.failureCause?.message, |
234 |
| - }); |
235 |
| - }; |
236 |
| - const trackStatusRenderedEvent = ( |
237 |
| - phase: string, |
238 |
| - properties?: { |
239 |
| - [prop: string]: any; |
240 |
| - }, |
241 |
| - ) => { |
242 |
| - window.gitpod.service.server.trackEvent({ |
243 |
| - event: "status_rendered", |
244 |
| - properties: { |
245 |
| - sessionId, |
246 |
| - instanceId: gitpodServiceClient.info.latestInstance?.id, |
247 |
| - workspaceId: gitpodServiceClient.info.workspace.id, |
248 |
| - type: gitpodServiceClient.info.workspace.type, |
249 |
| - phase, |
250 |
| - ...properties, |
251 |
| - }, |
252 |
| - }); |
253 |
| - }; |
254 |
| - let trackedDesktopIDEReady = false; |
255 |
| - const trackDesktopIDEReady = ({ clientID, kind }: DesktopIDEStatus) => { |
256 |
| - if (trackedDesktopIDEReady) { |
257 |
| - return; |
258 |
| - } |
259 |
| - trackedDesktopIDEReady = true; |
260 |
| - trackStatusRenderedEvent("desktop-ide-ready", { clientID, kind }); |
261 |
| - }; |
262 |
| - const trackIDEStatusRenderedEvent = () => { |
263 |
| - let error: string | undefined; |
264 |
| - if (ideService.failureCause) { |
265 |
| - error = `${ideService.failureCause.message}\n${ideService.failureCause.stack}`; |
266 |
| - } |
267 |
| - trackStatusRenderedEvent(`ide-${ideService.state}`, { error }); |
268 |
| - }; |
269 |
| - |
270 |
| - updateCurrentFrame(); |
271 |
| - updateLoadingState(); |
272 |
| - trackIDEStatusRenderedEvent(); |
273 |
| - gitpodServiceClient.onDidChangeInfo(() => updateCurrentFrame()); |
274 |
| - ideService.onDidChange(() => { |
275 |
| - updateLoadingState(); |
276 |
| - updateCurrentFrame(); |
277 |
| - trackIDEStatusRenderedEvent(); |
278 |
| - }); |
279 |
| - supervisorServiceClient.ideReady |
280 |
| - .then((newIdeStatus) => { |
281 |
| - ideStatus = newIdeStatus; |
282 |
| - isDesktopIde = !!ideStatus && !!ideStatus.desktop && !!ideStatus.desktop.link; |
283 |
| - updateCurrentFrame(); |
284 |
| - }) |
285 |
| - .catch((error) => console.error(`Unexpected error from supervisorServiceClient.ideReady: ${error}`)); |
286 |
| - window.addEventListener("unload", () => trackStatusRenderedEvent("window-unload"), { capture: true }); |
287 |
| - //#endregion |
288 |
| - |
289 |
| - //#region heart-beat |
290 |
| - heartBeat.track(window); |
291 |
| - const updateHeartBeat = () => { |
292 |
| - if (gitpodServiceClient.info.latestInstance?.status.phase === "running") { |
293 |
| - heartBeat.schedule(gitpodServiceClient.info, sessionId); |
294 |
| - } else { |
295 |
| - heartBeat.cancel(); |
296 |
| - } |
297 |
| - }; |
298 |
| - updateHeartBeat(); |
299 |
| - gitpodServiceClient.onDidChangeInfo(() => updateHeartBeat()); |
300 |
| - //#endregion |
301 |
| -})(); |
0 commit comments