@tsplc/debug 0.2.17 → 0.2.18
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/format.js +36 -18
- package/dist/format.js.map +4 -4
- package/dist/index.js +768 -516
- package/dist/index.js.map +20 -17
- package/dist/manifest.js +6 -2
- package/dist/manifest.js.map +3 -3
- package/dist/paths.js +60 -28
- package/dist/paths.js.map +3 -3
- package/package.json +47 -46
- package/src/adapter.ts +43 -36
- package/src/api.ts +126 -123
- package/src/debugger.ts +584 -467
- package/src/errors.ts +3 -0
- package/src/hardware.ts +127 -96
- package/src/http.ts +147 -116
- package/src/index.ts +34 -4
- package/src/jobs.ts +102 -77
- package/src/login-value.ts +165 -0
- package/src/login.ts +233 -253
- package/src/manifest.ts +200 -194
- package/src/opcua.ts +55 -55
- package/src/paths.ts +230 -157
- package/src/requests.ts +86 -0
- package/src/telemetry.ts +104 -90
- package/src/timeline.ts +96 -61
- package/src/trace.ts +41 -31
- package/src/ui/bits.ts +45 -29
- package/src/ui/client.ts +33 -26
- package/src/ui/dom.ts +189 -108
- package/src/ui/format.ts +127 -72
- package/src/ui/hardware.ts +416 -343
- package/src/ui/header.ts +186 -164
- package/src/ui/index.ts +16 -9
- package/src/ui/jobs.ts +263 -214
- package/src/ui/main.ts +52 -48
- package/src/ui/overview.ts +152 -108
- package/src/ui/recent.ts +329 -276
- package/src/ui/requests.ts +69 -73
- package/src/ui/store.ts +236 -198
- package/src/ui/tree.ts +162 -130
- package/src/ui/variables.ts +557 -431
- package/src/watch.ts +22 -17
- package/src/ui/dev.ts +0 -579
- package/src/ui/fixture.ts +0 -548
package/src/ui/jobs.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import type { JobState, RequestTrace } from "../api.ts";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
type View,
|
|
4
|
+
empty,
|
|
5
|
+
h,
|
|
6
|
+
pathLink,
|
|
7
|
+
recordSourceLink,
|
|
8
|
+
sourceLink,
|
|
9
|
+
table,
|
|
10
|
+
} from "./dom.ts";
|
|
3
11
|
import { formatDuration } from "./format.ts";
|
|
4
12
|
import type { Store } from "./store.ts";
|
|
5
13
|
|
|
@@ -7,243 +15,284 @@ type JobFilter = "all" | "busy" | "failed";
|
|
|
7
15
|
|
|
8
16
|
/** Failed while idle since its last unhandled failure: a later start that ended well clears it. */
|
|
9
17
|
const failed = (job: JobState) =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
18
|
+
job.busy === false &&
|
|
19
|
+
job.failure !== undefined &&
|
|
20
|
+
(job.startedAt === undefined || job.failure.at >= job.startedAt);
|
|
13
21
|
|
|
14
22
|
const jobStatus = (job: JobState) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
if (job.cancelling) {
|
|
24
|
+
return "cancelling";
|
|
25
|
+
}
|
|
26
|
+
if (job.busy) {
|
|
27
|
+
return "busy";
|
|
28
|
+
}
|
|
29
|
+
if (job.busy === null) {
|
|
30
|
+
return "unknown";
|
|
31
|
+
}
|
|
32
|
+
if (failed(job)) {
|
|
33
|
+
return "failed";
|
|
34
|
+
}
|
|
35
|
+
return "idle";
|
|
20
36
|
};
|
|
21
37
|
|
|
22
38
|
export const isActive = (job: JobState) => job.busy === true || failed(job);
|
|
23
39
|
|
|
24
40
|
const failureName = (store: Store, failure: NonNullable<JobState["failure"]>) =>
|
|
25
|
-
|
|
41
|
+
failure.name ??
|
|
42
|
+
store.manifest.errors[String(failure.code)] ??
|
|
43
|
+
`error ${failure.code}`;
|
|
26
44
|
|
|
27
45
|
/**
|
|
28
46
|
* A traced request's job, as a link: the user's job that awaits a compiler-made service job, which
|
|
29
47
|
* is the one whose id the request carries, or that job itself.
|
|
30
48
|
*/
|
|
31
|
-
export const requestJobLink = (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
export const requestJobLink = (
|
|
50
|
+
store: Store,
|
|
51
|
+
id: number,
|
|
52
|
+
sequence: number | undefined,
|
|
53
|
+
) => {
|
|
54
|
+
const { jobs } = store.manifest;
|
|
55
|
+
const job =
|
|
56
|
+
jobs.find(
|
|
57
|
+
(job) => !job.generated && job.requests.some((call) => call.job === id),
|
|
58
|
+
) ?? jobs.find((job) => job.id === id);
|
|
59
|
+
return h(
|
|
60
|
+
"a",
|
|
61
|
+
{ href: `#/jobs?id=${job?.id ?? id}` },
|
|
62
|
+
job
|
|
63
|
+
? `${job.name}${job.instance ? ` · ${job.instance}` : ""}`
|
|
64
|
+
: `job ${id}`,
|
|
65
|
+
` #${sequence ?? "?"}`,
|
|
66
|
+
);
|
|
42
67
|
};
|
|
43
68
|
|
|
44
69
|
export const requestStatus = (trace: RequestTrace) =>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
70
|
+
h(
|
|
71
|
+
"span",
|
|
72
|
+
{ class: `st st-${trace.status}` },
|
|
73
|
+
trace.status,
|
|
74
|
+
trace.httpStatus === undefined ? "" : ` ${trace.httpStatus}`,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
/** The step a busy job waits at, when its details were read. */
|
|
78
|
+
const waitingOn = (store: Store, job: JobState) => {
|
|
79
|
+
if (job.unread) {
|
|
80
|
+
return h("span", { class: "dim" }, "details not read (too many busy jobs)");
|
|
81
|
+
}
|
|
82
|
+
if (!job.current) {
|
|
83
|
+
return h("span", { class: "dim" }, "—");
|
|
84
|
+
}
|
|
85
|
+
return h(
|
|
86
|
+
"span",
|
|
87
|
+
{},
|
|
88
|
+
h("span", { class: "tag" }, job.current.kind),
|
|
89
|
+
" ",
|
|
90
|
+
h("code", {}, job.current.text),
|
|
91
|
+
" ",
|
|
92
|
+
sourceLink(store, job.current.range),
|
|
93
|
+
);
|
|
94
|
+
};
|
|
51
95
|
|
|
52
96
|
/** One job: what it is, where it is waiting and what it asked the backend. */
|
|
53
97
|
export const jobRow = (store: Store, job: JobState, highlighted = false) => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
)
|
|
141
|
-
: h("span", { class: "dim" }, "—"),
|
|
142
|
-
),
|
|
143
|
-
);
|
|
98
|
+
const definition = store.manifest.jobs.find(
|
|
99
|
+
(candidate) => candidate.id === job.id,
|
|
100
|
+
);
|
|
101
|
+
const status = jobStatus(job);
|
|
102
|
+
const trace = job.request?.trace;
|
|
103
|
+
const quality = store.connected ? job.quality : "stale";
|
|
104
|
+
return h(
|
|
105
|
+
"tr",
|
|
106
|
+
{ class: highlighted ? "highlight" : undefined, id: `job-${job.id}` },
|
|
107
|
+
h(
|
|
108
|
+
"td",
|
|
109
|
+
{},
|
|
110
|
+
h("b", { class: "mono" }, definition?.name ?? `job ${job.id}`),
|
|
111
|
+
definition ? h("div", {}, sourceLink(store, definition.declaration)) : "",
|
|
112
|
+
),
|
|
113
|
+
h(
|
|
114
|
+
"td",
|
|
115
|
+
{},
|
|
116
|
+
definition?.instance
|
|
117
|
+
? pathLink(definition.instance)
|
|
118
|
+
: h("span", { class: "dim" }, "—"),
|
|
119
|
+
),
|
|
120
|
+
h(
|
|
121
|
+
"td",
|
|
122
|
+
{},
|
|
123
|
+
h(
|
|
124
|
+
"span",
|
|
125
|
+
{
|
|
126
|
+
class: `st st-${status}`,
|
|
127
|
+
title:
|
|
128
|
+
status === "unknown"
|
|
129
|
+
? "Not read: shown jobs are read through the login"
|
|
130
|
+
: undefined,
|
|
131
|
+
},
|
|
132
|
+
status,
|
|
133
|
+
),
|
|
134
|
+
quality === "good" || job.busy === null
|
|
135
|
+
? ""
|
|
136
|
+
: h("span", { class: `meta s-${quality}` }, ` ${quality}`),
|
|
137
|
+
),
|
|
138
|
+
h("td", { class: "await" }, waitingOn(store, job)),
|
|
139
|
+
h(
|
|
140
|
+
"td",
|
|
141
|
+
{ class: "mono num" },
|
|
142
|
+
job.elapsedMs === undefined ? "—" : formatDuration(job.elapsedMs),
|
|
143
|
+
),
|
|
144
|
+
h(
|
|
145
|
+
"td",
|
|
146
|
+
{ class: "err" },
|
|
147
|
+
job.failure
|
|
148
|
+
? h(
|
|
149
|
+
"span",
|
|
150
|
+
{ title: `Unhandled at ${job.failure.at}` },
|
|
151
|
+
failureName(store, job.failure),
|
|
152
|
+
" ",
|
|
153
|
+
recordSourceLink(store, job.failure.source),
|
|
154
|
+
)
|
|
155
|
+
: "",
|
|
156
|
+
),
|
|
157
|
+
h(
|
|
158
|
+
"td",
|
|
159
|
+
{ class: "nowrap" },
|
|
160
|
+
job.request
|
|
161
|
+
? h(
|
|
162
|
+
"span",
|
|
163
|
+
{},
|
|
164
|
+
h(
|
|
165
|
+
"a",
|
|
166
|
+
{ href: `#/requests?seq=${trace?.seq ?? ""}`, class: "mono" },
|
|
167
|
+
`${job.request.service} #${job.request.sequence}`,
|
|
168
|
+
),
|
|
169
|
+
" ",
|
|
170
|
+
trace
|
|
171
|
+
? requestStatus(trace)
|
|
172
|
+
: h("span", { class: "dim" }, "no trace"),
|
|
173
|
+
trace?.durationMs === undefined
|
|
174
|
+
? ""
|
|
175
|
+
: h(
|
|
176
|
+
"span",
|
|
177
|
+
{ class: "mono dim" },
|
|
178
|
+
` ${formatDuration(trace.durationMs)}`,
|
|
179
|
+
),
|
|
180
|
+
)
|
|
181
|
+
: h("span", { class: "dim" }, "—"),
|
|
182
|
+
),
|
|
183
|
+
);
|
|
144
184
|
};
|
|
145
185
|
|
|
146
186
|
export const jobTable = (rows: HTMLElement[]) =>
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
),
|
|
161
|
-
h("tbody", {}, ...rows),
|
|
162
|
-
);
|
|
187
|
+
table(
|
|
188
|
+
{ class: "grid jobs" },
|
|
189
|
+
[
|
|
190
|
+
"Function",
|
|
191
|
+
"Instance",
|
|
192
|
+
"State",
|
|
193
|
+
"Waiting on",
|
|
194
|
+
"Elapsed",
|
|
195
|
+
"Error",
|
|
196
|
+
"Request",
|
|
197
|
+
],
|
|
198
|
+
h("tbody", {}, ...rows),
|
|
199
|
+
);
|
|
163
200
|
|
|
164
201
|
const FILTERS: JobFilter[] = ["all", "busy", "failed"];
|
|
165
|
-
const filters: Record<
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
202
|
+
const filters: Record<
|
|
203
|
+
JobFilter,
|
|
204
|
+
{ label: string; keep: (job: JobState) => boolean }
|
|
205
|
+
> = {
|
|
206
|
+
all: { label: "All", keep: () => true },
|
|
207
|
+
busy: { label: "Busy", keep: (job) => job.busy === true },
|
|
208
|
+
failed: { label: "Failed", keep: failed },
|
|
169
209
|
};
|
|
170
210
|
|
|
171
211
|
export const jobsView = (store: Store, params: URLSearchParams): View => {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
212
|
+
let filter = FILTERS.find((key) => key === params.get("filter")) ?? "all";
|
|
213
|
+
const highlighted = Number(params.get("id") ?? Number.NaN);
|
|
214
|
+
// Compiler-made jobs, such as each service call's own, are hidden unless asked for or linked to.
|
|
215
|
+
const generated = new Set(
|
|
216
|
+
store.manifest.jobs.filter((job) => job.generated).map((job) => job.id),
|
|
217
|
+
);
|
|
218
|
+
const generatedToggle = h("input", {
|
|
219
|
+
type: "checkbox",
|
|
220
|
+
checked: generated.has(highlighted),
|
|
221
|
+
});
|
|
222
|
+
const shown = (job: { id: number }) =>
|
|
223
|
+
generatedToggle.checked || !generated.has(job.id);
|
|
224
|
+
const bar = h("div", { class: "tabs" });
|
|
225
|
+
const body = h("div");
|
|
226
|
+
const render = () => {
|
|
227
|
+
bar.replaceChildren(
|
|
228
|
+
...FILTERS.map((key) =>
|
|
229
|
+
h(
|
|
230
|
+
"button",
|
|
231
|
+
{
|
|
232
|
+
class: `tab${key === filter ? " on" : ""}`,
|
|
233
|
+
onclick: () => {
|
|
234
|
+
filter = key;
|
|
235
|
+
history.replaceState(null, "", `#/jobs?filter=${key}`);
|
|
236
|
+
render();
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
filters[key].label,
|
|
240
|
+
h(
|
|
241
|
+
"span",
|
|
242
|
+
{ class: "n" },
|
|
243
|
+
String(
|
|
244
|
+
[...store.jobs.values()].filter(
|
|
245
|
+
(job) => shown(job) && filters[key].keep(job),
|
|
246
|
+
).length,
|
|
247
|
+
),
|
|
248
|
+
),
|
|
249
|
+
),
|
|
250
|
+
),
|
|
251
|
+
h("label", { class: "check" }, generatedToggle, "show generated"),
|
|
252
|
+
);
|
|
253
|
+
const jobs = [...store.jobs.values()]
|
|
254
|
+
.filter((job) => shown(job) && filters[filter].keep(job))
|
|
255
|
+
.sort((a, b) => Number(isActive(b)) - Number(isActive(a)) || a.id - b.id);
|
|
256
|
+
body.replaceChildren(
|
|
257
|
+
jobs.length
|
|
258
|
+
? jobTable(
|
|
259
|
+
jobs.map((job) => jobRow(store, job, job.id === highlighted)),
|
|
260
|
+
)
|
|
261
|
+
: empty("No jobs match."),
|
|
262
|
+
);
|
|
263
|
+
};
|
|
264
|
+
const showJobs = () =>
|
|
265
|
+
store.show({
|
|
266
|
+
jobs: store.manifest.jobs.filter(shown).map(({ id }) => id),
|
|
267
|
+
});
|
|
268
|
+
generatedToggle.addEventListener("change", () => {
|
|
269
|
+
showJobs();
|
|
270
|
+
render();
|
|
271
|
+
});
|
|
272
|
+
render();
|
|
273
|
+
showJobs();
|
|
274
|
+
return {
|
|
275
|
+
element: h(
|
|
276
|
+
"div",
|
|
277
|
+
{ class: "page" },
|
|
278
|
+
h("h1", {}, "Jobs"),
|
|
279
|
+
h(
|
|
280
|
+
"p",
|
|
281
|
+
{ class: "hint" },
|
|
282
|
+
"Busy and idle come from the trace when it is on; otherwise each listed job's busy flag is read through the login, and the details of busy ones. Failures are the program's unhandled exceptions.",
|
|
283
|
+
),
|
|
284
|
+
bar,
|
|
285
|
+
body,
|
|
286
|
+
),
|
|
287
|
+
update(topics) {
|
|
288
|
+
if (
|
|
289
|
+
topics.has("jobs") ||
|
|
290
|
+
topics.has("stream") ||
|
|
291
|
+
topics.has("session") ||
|
|
292
|
+
topics.has("timeline")
|
|
293
|
+
) {
|
|
294
|
+
render();
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
};
|
|
249
298
|
};
|
package/src/ui/main.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { errorMessage } from "../errors.ts";
|
|
1
2
|
import { getJson } from "./client.ts";
|
|
2
3
|
import { type View, h } from "./dom.ts";
|
|
3
4
|
import { hardwareView } from "./hardware.ts";
|
|
@@ -10,65 +11,68 @@ import { Store } from "./store.ts";
|
|
|
10
11
|
import { variablesView } from "./variables.ts";
|
|
11
12
|
|
|
12
13
|
interface RouteMatch {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
route: Route;
|
|
15
|
+
rest: string[];
|
|
16
|
+
params: URLSearchParams;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
const parseHash = (): RouteMatch => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
const [path = "", query = ""] = location.hash.replace(/^#\/?/, "").split("?");
|
|
21
|
+
const [name, ...rest] = path.split("/");
|
|
22
|
+
return {
|
|
23
|
+
route: ROUTES.find((route) => route === name) ?? "overview",
|
|
24
|
+
rest: rest.map(decodeURIComponent),
|
|
25
|
+
params: new URLSearchParams(query),
|
|
26
|
+
};
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
const views: Record<Route, (store: Store, at: RouteMatch) => View> = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
overview: (store) => overviewView(store),
|
|
31
|
+
variables: (store, at) => variablesView(store, at.params),
|
|
32
|
+
hardware: (store, at) => hardwareView(store, at.rest[0]),
|
|
33
|
+
jobs: (store, at) => jobsView(store, at.params),
|
|
34
|
+
recent: (store) => recentView(store),
|
|
35
|
+
requests: (store, at) => requestsView(store, at.params),
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
const start = async () => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
const [manifest, session] = await Promise.all([
|
|
40
|
+
getJson("manifest"),
|
|
41
|
+
getJson("session"),
|
|
42
|
+
]);
|
|
43
|
+
let view: View | undefined;
|
|
44
|
+
const store = new Store(manifest, session, (topics) => {
|
|
45
|
+
top.update(topics);
|
|
46
|
+
view?.update(topics);
|
|
47
|
+
});
|
|
48
|
+
const top = header(store);
|
|
49
|
+
const main = h("main");
|
|
50
|
+
document.body.replaceChildren(top.element, main);
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
const show = () => {
|
|
53
|
+
const at = parseHash();
|
|
54
|
+
view?.dispose?.();
|
|
55
|
+
view = views[at.route](store, at);
|
|
56
|
+
top.setRoute(at.route);
|
|
57
|
+
main.replaceChildren(view.element);
|
|
58
|
+
main.scrollTop = 0;
|
|
59
|
+
};
|
|
60
|
+
addEventListener("hashchange", show);
|
|
61
|
+
show();
|
|
58
62
|
};
|
|
59
63
|
|
|
60
64
|
start().catch((error: unknown) =>
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
65
|
+
document.body.replaceChildren(
|
|
66
|
+
h(
|
|
67
|
+
"div",
|
|
68
|
+
{ class: "fatal" },
|
|
69
|
+
h("h1", {}, "The debugger could not start"),
|
|
70
|
+
h("p", {}, errorMessage(error)),
|
|
71
|
+
h(
|
|
72
|
+
"p",
|
|
73
|
+
{ class: "dim" },
|
|
74
|
+
"Check that the debugger host is running and, under the agent, that the URL carries a valid ?token=.",
|
|
75
|
+
),
|
|
76
|
+
),
|
|
77
|
+
),
|
|
74
78
|
);
|