@jsenv/core 28.1.1 → 28.2.0
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/js/script_type_module_supervisor.js +8 -13
- package/dist/js/supervisor.js +702 -534
- package/dist/main.js +13275 -13164
- package/package.json +5 -5
- package/readme.md +3 -3
- package/src/build/build.js +960 -712
- package/src/build/inject_global_version_mappings.js +5 -20
- package/src/build/start_build_server.js +2 -2
- package/src/dev/start_dev_server.js +3 -2
- package/src/execute/run.js +1 -1
- package/src/execute/runtimes/browsers/from_playwright.js +1 -1
- package/src/omega/compat/runtime_compat.js +9 -6
- package/src/omega/errors.js +3 -0
- package/src/omega/fetched_content_compliance.js +2 -2
- package/src/omega/kitchen.js +189 -145
- package/src/omega/server/file_service.js +104 -71
- package/src/omega/url_graph/url_graph_loader.js +77 -0
- package/src/omega/url_graph/url_info_transformations.js +12 -15
- package/src/omega/url_graph.js +115 -101
- package/src/plugins/autoreload/jsenv_plugin_autoreload_client.js +1 -0
- package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +34 -36
- package/src/plugins/autoreload/jsenv_plugin_hmr.js +3 -2
- package/src/plugins/bundling/js_module/{bundle_js_module.js → bundle_js_modules.js} +51 -14
- package/src/plugins/bundling/jsenv_plugin_bundling.js +2 -2
- package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +11 -0
- package/src/plugins/inline/jsenv_plugin_html_inline_content.js +73 -62
- package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +77 -89
- package/src/plugins/plugin_controller.js +26 -22
- package/src/plugins/server_events/jsenv_plugin_server_events_client_injection.js +1 -0
- package/src/plugins/supervisor/client/script_type_module_supervisor.js +7 -9
- package/src/plugins/supervisor/client/supervisor.js +125 -96
- package/src/plugins/supervisor/jsenv_plugin_supervisor.js +2 -4
- package/src/plugins/toolbar/client/execution/toolbar_execution.js +1 -1
- package/src/plugins/transpilation/as_js_classic/async-to-promises.js +16 -0
- package/src/plugins/transpilation/as_js_classic/convert_js_module_to_js_classic.js +85 -0
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +48 -190
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_conversion.js +102 -0
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +161 -240
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_library.js +84 -0
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_workers.js +19 -12
- package/src/plugins/transpilation/babel/jsenv_plugin_babel.js +1 -24
- package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +82 -52
- package/src/plugins/transpilation/jsenv_plugin_transpilation.js +12 -13
- package/src/plugins/url_analysis/html/html_urls.js +91 -34
- package/src/plugins/url_analysis/js/js_urls.js +5 -4
- package/src/plugins/url_resolution/jsenv_plugin_url_resolution.js +1 -0
- package/src/test/execute_plan.js +3 -8
- package/src/test/execute_test_plan.js +1 -1
- package/src/build/inject_service_worker_urls.js +0 -78
- package/src/build/resync_resource_hints.js +0 -112
- package/src/omega/url_graph/url_graph_load.js +0 -74
package/dist/js/supervisor.js
CHANGED
|
@@ -1,31 +1,22 @@
|
|
|
1
1
|
window.__supervisor__ = (() => {
|
|
2
2
|
const notImplemented = () => {
|
|
3
|
-
throw new Error(`window.__supervisor__.setup() not called`)
|
|
4
|
-
}
|
|
5
|
-
|
|
3
|
+
throw new Error(`window.__supervisor__.setup() not called`);
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
const executionResults = {};
|
|
6
7
|
const supervisor = {
|
|
7
8
|
reportError: notImplemented,
|
|
8
9
|
superviseScript: notImplemented,
|
|
9
10
|
reloadSupervisedScript: notImplemented,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
window.removeEventListener("load", loadCallback)
|
|
20
|
-
resolve()
|
|
21
|
-
}
|
|
22
|
-
window.addEventListener("load", loadCallback)
|
|
23
|
-
})
|
|
24
|
-
return htmlReadyPromise.then(() => {
|
|
25
|
-
return supervisor.collectScriptResults()
|
|
26
|
-
})
|
|
27
|
-
},
|
|
28
|
-
executionResults,
|
|
11
|
+
getDocumentExecutionResult: notImplemented,
|
|
12
|
+
executionResults
|
|
13
|
+
};
|
|
14
|
+
let navigationStartTime;
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
navigationStartTime = window.performance.timing.navigationStart;
|
|
18
|
+
} catch (e) {
|
|
19
|
+
navigationStartTime = Date.now();
|
|
29
20
|
}
|
|
30
21
|
|
|
31
22
|
supervisor.setupReportException = ({
|
|
@@ -33,23 +24,24 @@ window.__supervisor__ = (() => {
|
|
|
33
24
|
errorNotification,
|
|
34
25
|
errorOverlay,
|
|
35
26
|
errorBaseUrl,
|
|
36
|
-
openInEditor
|
|
27
|
+
openInEditor
|
|
37
28
|
}) => {
|
|
38
|
-
const DYNAMIC_IMPORT_FETCH_ERROR = "dynamic_import_fetch_error"
|
|
39
|
-
const DYNAMIC_IMPORT_EXPORT_MISSING = "dynamic_import_export_missing"
|
|
40
|
-
const DYNAMIC_IMPORT_SYNTAX_ERROR = "dynamic_import_syntax_error"
|
|
29
|
+
const DYNAMIC_IMPORT_FETCH_ERROR = "dynamic_import_fetch_error";
|
|
30
|
+
const DYNAMIC_IMPORT_EXPORT_MISSING = "dynamic_import_export_missing";
|
|
31
|
+
const DYNAMIC_IMPORT_SYNTAX_ERROR = "dynamic_import_syntax_error";
|
|
41
32
|
|
|
42
33
|
const createException = ({
|
|
43
34
|
reason,
|
|
44
35
|
reportedBy,
|
|
45
36
|
url,
|
|
46
37
|
line,
|
|
47
|
-
column
|
|
38
|
+
column
|
|
48
39
|
} = {}) => {
|
|
49
40
|
const exception = {
|
|
50
41
|
reason,
|
|
51
42
|
reportedBy,
|
|
52
|
-
isError: false,
|
|
43
|
+
isError: false,
|
|
44
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
|
|
53
45
|
code: null,
|
|
54
46
|
message: null,
|
|
55
47
|
stack: null,
|
|
@@ -62,462 +54,526 @@ window.__supervisor__ = (() => {
|
|
|
62
54
|
url: null,
|
|
63
55
|
line: null,
|
|
64
56
|
column: null,
|
|
65
|
-
originalUrl: null
|
|
66
|
-
}
|
|
67
|
-
}
|
|
57
|
+
originalUrl: null
|
|
58
|
+
}
|
|
59
|
+
};
|
|
68
60
|
|
|
69
61
|
const writeBasicProperties = () => {
|
|
70
62
|
if (reason === undefined) {
|
|
71
|
-
exception.message = "undefined"
|
|
72
|
-
return
|
|
63
|
+
exception.message = "undefined";
|
|
64
|
+
return;
|
|
73
65
|
}
|
|
66
|
+
|
|
74
67
|
if (reason === null) {
|
|
75
|
-
exception.message = "null"
|
|
76
|
-
return
|
|
68
|
+
exception.message = "null";
|
|
69
|
+
return;
|
|
77
70
|
}
|
|
71
|
+
|
|
78
72
|
if (typeof reason === "string") {
|
|
79
|
-
exception.message = reason
|
|
80
|
-
return
|
|
73
|
+
exception.message = reason;
|
|
74
|
+
return;
|
|
81
75
|
}
|
|
76
|
+
|
|
82
77
|
if (reason instanceof Error) {
|
|
83
|
-
const error = reason
|
|
84
|
-
let message = error.message
|
|
85
|
-
exception.isError = true
|
|
78
|
+
const error = reason;
|
|
79
|
+
let message = error.message;
|
|
80
|
+
exception.isError = true;
|
|
81
|
+
|
|
86
82
|
if (Error.captureStackTrace) {
|
|
87
83
|
// stackTrace formatted by V8
|
|
88
|
-
exception.message = message
|
|
89
|
-
exception.stack = getErrorStackWithoutErrorMessage(error)
|
|
90
|
-
exception.stackFormatIsV8 = true
|
|
91
|
-
exception.stackSourcemapped = true
|
|
84
|
+
exception.message = message;
|
|
85
|
+
exception.stack = getErrorStackWithoutErrorMessage(error);
|
|
86
|
+
exception.stackFormatIsV8 = true;
|
|
87
|
+
exception.stackSourcemapped = true;
|
|
92
88
|
} else {
|
|
93
|
-
exception.message = message
|
|
94
|
-
exception.stack = error.stack ? ` ${error.stack}` : null
|
|
95
|
-
exception.stackFormatIsV8 = false
|
|
96
|
-
exception.stackSourcemapped = false
|
|
89
|
+
exception.message = message;
|
|
90
|
+
exception.stack = error.stack ? ` ${error.stack}` : null;
|
|
91
|
+
exception.stackFormatIsV8 = false;
|
|
92
|
+
exception.stackSourcemapped = false;
|
|
97
93
|
}
|
|
94
|
+
|
|
98
95
|
if (error.reportedBy) {
|
|
99
|
-
exception.reportedBy = error.reportedBy
|
|
96
|
+
exception.reportedBy = error.reportedBy;
|
|
100
97
|
}
|
|
98
|
+
|
|
101
99
|
if (error.url) {
|
|
102
|
-
Object.assign(exception.site, resolveUrlSite({
|
|
100
|
+
Object.assign(exception.site, resolveUrlSite({
|
|
101
|
+
url: error.url
|
|
102
|
+
}));
|
|
103
103
|
}
|
|
104
|
+
|
|
104
105
|
if (error.needsReport) {
|
|
105
|
-
exception.needsReport = true
|
|
106
|
+
exception.needsReport = true;
|
|
106
107
|
}
|
|
107
|
-
|
|
108
|
+
|
|
109
|
+
{
|
|
108
110
|
// chrome
|
|
109
111
|
if (message.includes("does not provide an export named")) {
|
|
110
|
-
exception.code = DYNAMIC_IMPORT_EXPORT_MISSING
|
|
111
|
-
return
|
|
112
|
-
}
|
|
113
|
-
|
|
112
|
+
exception.code = DYNAMIC_IMPORT_EXPORT_MISSING;
|
|
113
|
+
return;
|
|
114
|
+
} // firefox
|
|
115
|
+
|
|
116
|
+
|
|
114
117
|
if (message.startsWith("import not found:")) {
|
|
115
|
-
exception.code = DYNAMIC_IMPORT_EXPORT_MISSING
|
|
116
|
-
return
|
|
117
|
-
}
|
|
118
|
-
|
|
118
|
+
exception.code = DYNAMIC_IMPORT_EXPORT_MISSING;
|
|
119
|
+
return;
|
|
120
|
+
} // safari
|
|
121
|
+
|
|
122
|
+
|
|
119
123
|
if (message.startsWith("import binding name")) {
|
|
120
|
-
exception.code = DYNAMIC_IMPORT_EXPORT_MISSING
|
|
121
|
-
return
|
|
124
|
+
exception.code = DYNAMIC_IMPORT_EXPORT_MISSING;
|
|
125
|
+
return;
|
|
122
126
|
}
|
|
123
127
|
}
|
|
124
|
-
|
|
128
|
+
|
|
129
|
+
{
|
|
125
130
|
if (error.name === "SyntaxError" && typeof line === "number") {
|
|
126
|
-
exception.code = DYNAMIC_IMPORT_SYNTAX_ERROR
|
|
127
|
-
return
|
|
131
|
+
exception.code = DYNAMIC_IMPORT_SYNTAX_ERROR;
|
|
132
|
+
return;
|
|
128
133
|
}
|
|
129
134
|
}
|
|
130
|
-
|
|
135
|
+
|
|
136
|
+
return;
|
|
131
137
|
}
|
|
138
|
+
|
|
132
139
|
if (typeof reason === "object") {
|
|
133
|
-
exception.code = reason.code
|
|
134
|
-
exception.message = reason.message
|
|
135
|
-
exception.stack = reason.stack
|
|
140
|
+
exception.code = reason.code;
|
|
141
|
+
exception.message = reason.message;
|
|
142
|
+
exception.stack = reason.stack;
|
|
143
|
+
|
|
136
144
|
if (reason.reportedBy) {
|
|
137
|
-
exception.reportedBy = reason.reportedBy
|
|
145
|
+
exception.reportedBy = reason.reportedBy;
|
|
138
146
|
}
|
|
147
|
+
|
|
139
148
|
if (reason.url) {
|
|
140
|
-
Object.assign(exception.site, resolveUrlSite({
|
|
149
|
+
Object.assign(exception.site, resolveUrlSite({
|
|
150
|
+
url: reason.url
|
|
151
|
+
}));
|
|
141
152
|
}
|
|
153
|
+
|
|
142
154
|
if (reason.needsReport) {
|
|
143
|
-
exception.needsReport = true
|
|
155
|
+
exception.needsReport = true;
|
|
144
156
|
}
|
|
145
|
-
|
|
157
|
+
|
|
158
|
+
return;
|
|
146
159
|
}
|
|
147
|
-
exception.message = JSON.stringify(reason)
|
|
148
|
-
}
|
|
149
|
-
writeBasicProperties()
|
|
150
160
|
|
|
151
|
-
|
|
161
|
+
exception.message = JSON.stringify(reason);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
writeBasicProperties(); // first create a version of the stack with file://
|
|
152
165
|
// (and use it to locate exception url+line+column)
|
|
166
|
+
|
|
153
167
|
if (exception.stack) {
|
|
154
|
-
exception.originalStack = exception.stack
|
|
155
|
-
exception.stack = replaceUrls(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
168
|
+
exception.originalStack = exception.stack;
|
|
169
|
+
exception.stack = replaceUrls(exception.originalStack, serverUrlSite => {
|
|
170
|
+
const fileUrlSite = resolveUrlSite(serverUrlSite);
|
|
171
|
+
|
|
172
|
+
if (exception.site.url === null) {
|
|
173
|
+
Object.assign(exception.site, fileUrlSite);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return stringifyUrlSite(fileUrlSite);
|
|
177
|
+
});
|
|
178
|
+
} // then if it fails, use url+line+column passed
|
|
179
|
+
|
|
180
|
+
|
|
167
181
|
if (exception.site.url === null && url) {
|
|
168
182
|
if (typeof line === "string") {
|
|
169
|
-
line = parseInt(line)
|
|
183
|
+
line = parseInt(line);
|
|
170
184
|
}
|
|
185
|
+
|
|
171
186
|
if (typeof column === "string") {
|
|
172
|
-
column = parseInt(column)
|
|
187
|
+
column = parseInt(column);
|
|
173
188
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
189
|
+
|
|
190
|
+
const fileUrlSite = resolveUrlSite({
|
|
191
|
+
url,
|
|
192
|
+
line,
|
|
193
|
+
column
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (fileUrlSite.isInline && exception.code === DYNAMIC_IMPORT_SYNTAX_ERROR) {
|
|
179
197
|
// syntax error on inline script need line-1 for some reason
|
|
180
198
|
if (Error.captureStackTrace) {
|
|
181
|
-
fileUrlSite.line
|
|
199
|
+
fileUrlSite.line--;
|
|
182
200
|
} else {
|
|
183
201
|
// firefox and safari need line-2
|
|
184
|
-
fileUrlSite.line -= 2
|
|
202
|
+
fileUrlSite.line -= 2;
|
|
185
203
|
}
|
|
186
204
|
}
|
|
187
|
-
|
|
205
|
+
|
|
206
|
+
Object.assign(exception.site, fileUrlSite);
|
|
188
207
|
}
|
|
189
|
-
exception.text = stringifyMessageAndStack(exception)
|
|
190
|
-
return exception
|
|
191
|
-
}
|
|
192
208
|
|
|
193
|
-
|
|
209
|
+
exception.text = stringifyMessageAndStack(exception);
|
|
210
|
+
return exception;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const stringifyMessageAndStack = ({
|
|
214
|
+
message,
|
|
215
|
+
stack
|
|
216
|
+
}) => {
|
|
194
217
|
if (message && stack) {
|
|
195
|
-
return `${message}\n${stack}
|
|
218
|
+
return `${message}\n${stack}`;
|
|
196
219
|
}
|
|
220
|
+
|
|
197
221
|
if (stack) {
|
|
198
|
-
return stack
|
|
222
|
+
return stack;
|
|
199
223
|
}
|
|
200
|
-
return message
|
|
201
|
-
}
|
|
202
224
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
225
|
+
return message;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const stringifyUrlSite = ({
|
|
229
|
+
url,
|
|
230
|
+
line,
|
|
231
|
+
column
|
|
232
|
+
}) => {
|
|
233
|
+
return typeof line === "number" && typeof column === "number" ? `${url}:${line}:${column}` : typeof line === "number" ? `${url}:${line}` : url;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const resolveUrlSite = ({
|
|
237
|
+
url,
|
|
238
|
+
line,
|
|
239
|
+
column
|
|
240
|
+
}) => {
|
|
241
|
+
const inlineUrlMatch = url.match(/@L([0-9]+)C([0-9]+)\-L([0-9]+)C([0-9]+)(\.[\w]+)$/);
|
|
210
242
|
|
|
211
|
-
const resolveUrlSite = ({ url, line, column }) => {
|
|
212
|
-
const inlineUrlMatch = url.match(
|
|
213
|
-
/@L([0-9]+)C([0-9]+)\-L([0-9]+)C([0-9]+)(\.[\w]+)$/,
|
|
214
|
-
)
|
|
215
243
|
if (inlineUrlMatch) {
|
|
216
|
-
const htmlUrl = url.slice(0, inlineUrlMatch.index)
|
|
217
|
-
const tagLineStart = parseInt(inlineUrlMatch[1])
|
|
218
|
-
const tagColumnStart = parseInt(inlineUrlMatch[2])
|
|
219
|
-
const tagLineEnd = parseInt(inlineUrlMatch[3])
|
|
220
|
-
const tagColumnEnd = parseInt(inlineUrlMatch[4])
|
|
221
|
-
const extension = inlineUrlMatch[5]
|
|
222
|
-
url = htmlUrl
|
|
223
|
-
line = tagLineStart + (typeof line === "number" ? line : 0)
|
|
224
|
-
|
|
244
|
+
const htmlUrl = url.slice(0, inlineUrlMatch.index);
|
|
245
|
+
const tagLineStart = parseInt(inlineUrlMatch[1]);
|
|
246
|
+
const tagColumnStart = parseInt(inlineUrlMatch[2]);
|
|
247
|
+
const tagLineEnd = parseInt(inlineUrlMatch[3]);
|
|
248
|
+
const tagColumnEnd = parseInt(inlineUrlMatch[4]);
|
|
249
|
+
const extension = inlineUrlMatch[5];
|
|
250
|
+
url = htmlUrl;
|
|
251
|
+
line = tagLineStart + (typeof line === "number" ? line : 0); // stackTrace formatted by V8 (chrome)
|
|
252
|
+
|
|
225
253
|
if (Error.captureStackTrace) {
|
|
226
|
-
line
|
|
254
|
+
line--;
|
|
227
255
|
}
|
|
228
|
-
|
|
229
|
-
|
|
256
|
+
|
|
257
|
+
column = tagColumnStart + (typeof column === "number" ? column : 0);
|
|
258
|
+
const fileUrl = resolveFileUrl(url);
|
|
230
259
|
return {
|
|
231
260
|
isInline: true,
|
|
232
261
|
serverUrl: url,
|
|
233
262
|
originalUrl: `${fileUrl}@L${tagLineStart}C${tagColumnStart}-L${tagLineEnd}C${tagColumnEnd}${extension}`,
|
|
234
263
|
url: fileUrl,
|
|
235
264
|
line,
|
|
236
|
-
column
|
|
237
|
-
}
|
|
265
|
+
column
|
|
266
|
+
};
|
|
238
267
|
}
|
|
268
|
+
|
|
239
269
|
return {
|
|
240
270
|
isInline: false,
|
|
241
271
|
serverUrl: url,
|
|
242
272
|
url: resolveFileUrl(url),
|
|
243
273
|
line,
|
|
244
|
-
column
|
|
245
|
-
}
|
|
246
|
-
}
|
|
274
|
+
column
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const getErrorStackWithoutErrorMessage = error => {
|
|
279
|
+
let stack = error.stack;
|
|
280
|
+
const messageInStack = `${error.name}: ${error.message}`;
|
|
247
281
|
|
|
248
|
-
const getErrorStackWithoutErrorMessage = (error) => {
|
|
249
|
-
let stack = error.stack
|
|
250
|
-
const messageInStack = `${error.name}: ${error.message}`
|
|
251
282
|
if (stack.startsWith(messageInStack)) {
|
|
252
|
-
stack = stack.slice(messageInStack.length)
|
|
283
|
+
stack = stack.slice(messageInStack.length);
|
|
253
284
|
}
|
|
254
|
-
|
|
285
|
+
|
|
286
|
+
const nextLineIndex = stack.indexOf("\n");
|
|
287
|
+
|
|
255
288
|
if (nextLineIndex > -1) {
|
|
256
|
-
stack = stack.slice(nextLineIndex + 1)
|
|
289
|
+
stack = stack.slice(nextLineIndex + 1);
|
|
257
290
|
}
|
|
258
|
-
return stack
|
|
259
|
-
}
|
|
260
291
|
|
|
261
|
-
|
|
262
|
-
|
|
292
|
+
return stack;
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const resolveFileUrl = url => {
|
|
296
|
+
let urlObject = new URL(url);
|
|
297
|
+
|
|
263
298
|
if (urlObject.origin === window.origin) {
|
|
264
|
-
urlObject = new URL(
|
|
265
|
-
`${urlObject.pathname.slice(1)}${urlObject.search}`,
|
|
266
|
-
rootDirectoryUrl,
|
|
267
|
-
)
|
|
299
|
+
urlObject = new URL(`${urlObject.pathname.slice(1)}${urlObject.search}`, rootDirectoryUrl);
|
|
268
300
|
}
|
|
301
|
+
|
|
269
302
|
if (urlObject.href.startsWith("file:")) {
|
|
270
|
-
const atFsIndex = urlObject.pathname.indexOf("/@fs/")
|
|
303
|
+
const atFsIndex = urlObject.pathname.indexOf("/@fs/");
|
|
304
|
+
|
|
271
305
|
if (atFsIndex > -1) {
|
|
272
|
-
const afterAtFs = urlObject.pathname.slice(atFsIndex + "/@fs/".length)
|
|
273
|
-
return new URL(afterAtFs, "file:///").href
|
|
306
|
+
const afterAtFs = urlObject.pathname.slice(atFsIndex + "/@fs/".length);
|
|
307
|
+
return new URL(afterAtFs, "file:///").href;
|
|
274
308
|
}
|
|
275
309
|
}
|
|
276
|
-
return urlObject.href
|
|
277
|
-
}
|
|
278
310
|
|
|
279
|
-
|
|
311
|
+
return urlObject.href;
|
|
312
|
+
}; // `Error: yo
|
|
280
313
|
// at Object.execute (http://127.0.0.1:57300/build/src/__test__/file-throw.js:9:13)
|
|
281
314
|
// at doExec (http://127.0.0.1:3000/src/__test__/file-throw.js:452:38)
|
|
282
315
|
// at postOrderExec (http://127.0.0.1:3000/src/__test__/file-throw.js:448:16)
|
|
283
316
|
// at http://127.0.0.1:3000/src/__test__/file-throw.js:399:18`.replace(/(?:https?|ftp|file):\/\/(.*+)$/gm, (...args) => {
|
|
284
317
|
// debugger
|
|
285
318
|
// })
|
|
319
|
+
|
|
320
|
+
|
|
286
321
|
const replaceUrls = (source, replace) => {
|
|
287
|
-
return source.replace(/(?:https?|ftp|file):\/\/\S+/gm,
|
|
288
|
-
let replacement = ""
|
|
289
|
-
const lastChar = match[match.length - 1]
|
|
322
|
+
return source.replace(/(?:https?|ftp|file):\/\/\S+/gm, match => {
|
|
323
|
+
let replacement = "";
|
|
324
|
+
const lastChar = match[match.length - 1]; // hotfix because our url regex sucks a bit
|
|
325
|
+
|
|
326
|
+
const endsWithSeparationChar = lastChar === ")" || lastChar === ":";
|
|
290
327
|
|
|
291
|
-
// hotfix because our url regex sucks a bit
|
|
292
|
-
const endsWithSeparationChar = lastChar === ")" || lastChar === ":"
|
|
293
328
|
if (endsWithSeparationChar) {
|
|
294
|
-
match = match.slice(0, -1)
|
|
329
|
+
match = match.slice(0, -1);
|
|
295
330
|
}
|
|
296
331
|
|
|
297
|
-
const lineAndColumnPattern = /:([0-9]+):([0-9]+)
|
|
298
|
-
const lineAndColumMatch = match.match(lineAndColumnPattern)
|
|
332
|
+
const lineAndColumnPattern = /:([0-9]+):([0-9]+)$/;
|
|
333
|
+
const lineAndColumMatch = match.match(lineAndColumnPattern);
|
|
334
|
+
|
|
299
335
|
if (lineAndColumMatch) {
|
|
300
|
-
const lineAndColumnString = lineAndColumMatch[0]
|
|
301
|
-
const lineString = lineAndColumMatch[1]
|
|
302
|
-
const columnString = lineAndColumMatch[2]
|
|
336
|
+
const lineAndColumnString = lineAndColumMatch[0];
|
|
337
|
+
const lineString = lineAndColumMatch[1];
|
|
338
|
+
const columnString = lineAndColumMatch[2];
|
|
303
339
|
replacement = replace({
|
|
304
340
|
url: match.slice(0, -lineAndColumnString.length),
|
|
305
341
|
line: lineString ? parseInt(lineString) : null,
|
|
306
|
-
column: columnString ? parseInt(columnString) : null
|
|
307
|
-
})
|
|
342
|
+
column: columnString ? parseInt(columnString) : null
|
|
343
|
+
});
|
|
308
344
|
} else {
|
|
309
|
-
const linePattern = /:([0-9]+)
|
|
310
|
-
const lineMatch = match.match(linePattern)
|
|
345
|
+
const linePattern = /:([0-9]+)$/;
|
|
346
|
+
const lineMatch = match.match(linePattern);
|
|
347
|
+
|
|
311
348
|
if (lineMatch) {
|
|
312
|
-
const lineString = lineMatch[0]
|
|
349
|
+
const lineString = lineMatch[0];
|
|
313
350
|
replacement = replace({
|
|
314
351
|
url: match.slice(0, -lineString.length),
|
|
315
|
-
line: lineString ? parseInt(lineString) : null
|
|
316
|
-
})
|
|
352
|
+
line: lineString ? parseInt(lineString) : null
|
|
353
|
+
});
|
|
317
354
|
} else {
|
|
318
355
|
replacement = replace({
|
|
319
|
-
url: match
|
|
320
|
-
})
|
|
356
|
+
url: match
|
|
357
|
+
});
|
|
321
358
|
}
|
|
322
359
|
}
|
|
360
|
+
|
|
323
361
|
if (endsWithSeparationChar) {
|
|
324
|
-
return `${replacement}${lastChar}
|
|
362
|
+
return `${replacement}${lastChar}`;
|
|
325
363
|
}
|
|
326
|
-
return replacement
|
|
327
|
-
})
|
|
328
|
-
}
|
|
329
364
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
365
|
+
return replacement;
|
|
366
|
+
});
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
let formatError;
|
|
370
|
+
|
|
371
|
+
{
|
|
372
|
+
formatError = exceptionInfo => {
|
|
333
373
|
const errorParts = {
|
|
334
374
|
theme: "dark",
|
|
335
375
|
title: "An error occured",
|
|
336
376
|
text: "",
|
|
337
377
|
tip: "",
|
|
338
|
-
errorDetailsPromise: null
|
|
339
|
-
}
|
|
340
|
-
const tips = []
|
|
341
|
-
tips.push("Click outside to close.")
|
|
342
|
-
errorParts.tip = tips.join(`\n <br />\n `)
|
|
378
|
+
errorDetailsPromise: null
|
|
379
|
+
};
|
|
380
|
+
const tips = [];
|
|
381
|
+
tips.push("Click outside to close.");
|
|
382
|
+
errorParts.tip = tips.join(`\n <br />\n `);
|
|
343
383
|
|
|
344
|
-
const generateClickableText =
|
|
384
|
+
const generateClickableText = text => {
|
|
345
385
|
const textWithHtmlLinks = makeLinksClickable(text, {
|
|
346
|
-
createLink: ({
|
|
347
|
-
|
|
386
|
+
createLink: ({
|
|
387
|
+
url,
|
|
388
|
+
line,
|
|
389
|
+
column
|
|
390
|
+
}) => {
|
|
391
|
+
const urlSite = resolveUrlSite({
|
|
392
|
+
url,
|
|
393
|
+
line,
|
|
394
|
+
column
|
|
395
|
+
});
|
|
396
|
+
|
|
348
397
|
if (errorBaseUrl) {
|
|
349
398
|
if (urlSite.url.startsWith(rootDirectoryUrl)) {
|
|
350
|
-
urlSite.url = `${errorBaseUrl}${urlSite.url.slice(
|
|
351
|
-
rootDirectoryUrl.length,
|
|
352
|
-
)}`
|
|
399
|
+
urlSite.url = `${errorBaseUrl}${urlSite.url.slice(rootDirectoryUrl.length)}`;
|
|
353
400
|
} else {
|
|
354
|
-
urlSite.url = "file:///mocked_for_snapshots"
|
|
401
|
+
urlSite.url = "file:///mocked_for_snapshots";
|
|
355
402
|
}
|
|
356
403
|
}
|
|
357
|
-
|
|
404
|
+
|
|
405
|
+
const urlWithLineAndColumn = stringifyUrlSite(urlSite);
|
|
358
406
|
return {
|
|
359
|
-
href:
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
367
|
-
},
|
|
368
|
-
})
|
|
369
|
-
return textWithHtmlLinks
|
|
370
|
-
}
|
|
407
|
+
href: urlSite.url.startsWith("file:") && openInEditor ? `javascript:window.fetch('/__open_in_editor__/${encodeURIComponent(urlWithLineAndColumn)}')` : urlSite.url,
|
|
408
|
+
text: urlWithLineAndColumn
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
return textWithHtmlLinks;
|
|
413
|
+
};
|
|
371
414
|
|
|
372
415
|
errorParts.text = stringifyMessageAndStack({
|
|
373
|
-
message: exceptionInfo.message
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
? generateClickableText(exceptionInfo.stack)
|
|
378
|
-
: "",
|
|
379
|
-
})
|
|
416
|
+
message: exceptionInfo.message ? generateClickableText(exceptionInfo.message) : "",
|
|
417
|
+
stack: exceptionInfo.stack ? generateClickableText(exceptionInfo.stack) : ""
|
|
418
|
+
});
|
|
419
|
+
|
|
380
420
|
if (exceptionInfo.site.url) {
|
|
381
421
|
errorParts.errorDetailsPromise = (async () => {
|
|
382
422
|
try {
|
|
383
|
-
if (
|
|
384
|
-
exceptionInfo.
|
|
385
|
-
|
|
386
|
-
) {
|
|
387
|
-
const response = await window.fetch(
|
|
388
|
-
`/__get_error_cause__/${encodeURIComponent(
|
|
389
|
-
exceptionInfo.site.isInline
|
|
390
|
-
? exceptionInfo.site.originalUrl
|
|
391
|
-
: exceptionInfo.site.url,
|
|
392
|
-
)}`,
|
|
393
|
-
)
|
|
423
|
+
if (exceptionInfo.code === DYNAMIC_IMPORT_FETCH_ERROR || exceptionInfo.reportedBy === "script_error_event") {
|
|
424
|
+
const response = await window.fetch(`/__get_error_cause__/${encodeURIComponent(exceptionInfo.site.isInline ? exceptionInfo.site.originalUrl : exceptionInfo.site.url)}`);
|
|
425
|
+
|
|
394
426
|
if (response.status !== 200) {
|
|
395
|
-
return null
|
|
427
|
+
return null;
|
|
396
428
|
}
|
|
397
|
-
|
|
429
|
+
|
|
430
|
+
const causeInfo = await response.json();
|
|
431
|
+
|
|
398
432
|
if (!causeInfo) {
|
|
399
|
-
return null
|
|
433
|
+
return null;
|
|
400
434
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
stack: generateClickableText(causeInfo.codeFrame),
|
|
410
|
-
})
|
|
435
|
+
|
|
436
|
+
const causeText = causeInfo.code === "NOT_FOUND" ? stringifyMessageAndStack({
|
|
437
|
+
message: generateClickableText(causeInfo.reason),
|
|
438
|
+
stack: generateClickableText(causeInfo.codeFrame)
|
|
439
|
+
}) : stringifyMessageAndStack({
|
|
440
|
+
message: generateClickableText(causeInfo.stack),
|
|
441
|
+
stack: generateClickableText(causeInfo.codeFrame)
|
|
442
|
+
});
|
|
411
443
|
return {
|
|
412
|
-
cause: causeText
|
|
413
|
-
}
|
|
444
|
+
cause: causeText
|
|
445
|
+
};
|
|
414
446
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
)
|
|
421
|
-
) {
|
|
422
|
-
const urlToFetch = new URL(
|
|
423
|
-
`/__get_code_frame__/${encodeURIComponent(
|
|
424
|
-
stringifyUrlSite(exceptionInfo.site),
|
|
425
|
-
)}`,
|
|
426
|
-
window.origin,
|
|
427
|
-
)
|
|
447
|
+
|
|
448
|
+
if (exceptionInfo.site.line !== undefined && // code frame showing internal window.reportError is pointless
|
|
449
|
+
!exceptionInfo.site.url.endsWith(`script_type_module_supervisor.js`)) {
|
|
450
|
+
const urlToFetch = new URL(`/__get_code_frame__/${encodeURIComponent(stringifyUrlSite(exceptionInfo.site))}`, window.origin);
|
|
451
|
+
|
|
428
452
|
if (!exceptionInfo.stackSourcemapped) {
|
|
429
|
-
urlToFetch.searchParams.set("remap", "")
|
|
453
|
+
urlToFetch.searchParams.set("remap", "");
|
|
430
454
|
}
|
|
431
|
-
|
|
455
|
+
|
|
456
|
+
const response = await window.fetch(urlToFetch);
|
|
457
|
+
|
|
432
458
|
if (response.status !== 200) {
|
|
433
|
-
return null
|
|
459
|
+
return null;
|
|
434
460
|
}
|
|
435
|
-
|
|
461
|
+
|
|
462
|
+
const codeFrame = await response.text();
|
|
436
463
|
return {
|
|
437
|
-
codeFrame: generateClickableText(codeFrame)
|
|
438
|
-
}
|
|
464
|
+
codeFrame: generateClickableText(codeFrame)
|
|
465
|
+
};
|
|
439
466
|
}
|
|
440
467
|
} catch (e) {
|
|
441
468
|
// happens if server is closed for instance
|
|
442
|
-
return null
|
|
469
|
+
return null;
|
|
443
470
|
}
|
|
444
|
-
|
|
445
|
-
|
|
471
|
+
|
|
472
|
+
return null;
|
|
473
|
+
})();
|
|
446
474
|
}
|
|
447
|
-
return errorParts
|
|
448
|
-
}
|
|
449
475
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
476
|
+
return errorParts;
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
const makeLinksClickable = (string, {
|
|
480
|
+
createLink = ({
|
|
481
|
+
url
|
|
482
|
+
}) => url
|
|
483
|
+
}) => {
|
|
454
484
|
// normalize line breaks
|
|
455
|
-
string = string.replace(/\n/g, "\n")
|
|
456
|
-
string = escapeHtml(string)
|
|
457
|
-
// render links
|
|
458
|
-
string = replaceUrls(string, ({ url, line, column }) => {
|
|
459
|
-
const { href, text } = createLink({ url, line, column })
|
|
460
|
-
return link({ href, text })
|
|
461
|
-
})
|
|
462
|
-
return string
|
|
463
|
-
}
|
|
485
|
+
string = string.replace(/\n/g, "\n");
|
|
486
|
+
string = escapeHtml(string); // render links
|
|
464
487
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
488
|
+
string = replaceUrls(string, ({
|
|
489
|
+
url,
|
|
490
|
+
line,
|
|
491
|
+
column
|
|
492
|
+
}) => {
|
|
493
|
+
const {
|
|
494
|
+
href,
|
|
495
|
+
text
|
|
496
|
+
} = createLink({
|
|
497
|
+
url,
|
|
498
|
+
line,
|
|
499
|
+
column
|
|
500
|
+
});
|
|
501
|
+
return link({
|
|
502
|
+
href,
|
|
503
|
+
text
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
return string;
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
const escapeHtml = string => {
|
|
510
|
+
return string.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
511
|
+
};
|
|
473
512
|
|
|
474
|
-
const link = ({
|
|
513
|
+
const link = ({
|
|
514
|
+
href,
|
|
515
|
+
text = href
|
|
516
|
+
}) => `<a href="${href}">${text}</a>`;
|
|
475
517
|
}
|
|
476
518
|
|
|
477
|
-
let displayErrorNotification
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
519
|
+
let displayErrorNotification;
|
|
520
|
+
|
|
521
|
+
{
|
|
522
|
+
const {
|
|
523
|
+
Notification
|
|
524
|
+
} = window;
|
|
525
|
+
displayErrorNotification = typeof Notification === "function" ? ({
|
|
526
|
+
title,
|
|
527
|
+
text,
|
|
528
|
+
icon
|
|
529
|
+
}) => {
|
|
530
|
+
if (Notification.permission === "granted") {
|
|
531
|
+
const notification = new Notification(title, {
|
|
532
|
+
lang: "en",
|
|
533
|
+
body: text,
|
|
534
|
+
icon
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
notification.onclick = () => {
|
|
538
|
+
window.focus();
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
} : () => {};
|
|
495
542
|
}
|
|
496
543
|
|
|
497
|
-
const JSENV_ERROR_OVERLAY_TAGNAME = "jsenv-error-overlay"
|
|
498
|
-
let displayJsenvErrorOverlay
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
.
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
544
|
+
const JSENV_ERROR_OVERLAY_TAGNAME = "jsenv-error-overlay";
|
|
545
|
+
let displayJsenvErrorOverlay;
|
|
546
|
+
|
|
547
|
+
{
|
|
548
|
+
displayJsenvErrorOverlay = params => {
|
|
549
|
+
let jsenvErrorOverlay = new JsenvErrorOverlay(params);
|
|
550
|
+
document.querySelectorAll(JSENV_ERROR_OVERLAY_TAGNAME).forEach(node => {
|
|
551
|
+
node.parentNode.removeChild(node);
|
|
552
|
+
});
|
|
553
|
+
document.body.appendChild(jsenvErrorOverlay);
|
|
554
|
+
|
|
508
555
|
const removeErrorOverlay = () => {
|
|
509
556
|
if (jsenvErrorOverlay && jsenvErrorOverlay.parentNode) {
|
|
510
|
-
document.body.removeChild(jsenvErrorOverlay)
|
|
511
|
-
jsenvErrorOverlay = null
|
|
557
|
+
document.body.removeChild(jsenvErrorOverlay);
|
|
558
|
+
jsenvErrorOverlay = null;
|
|
512
559
|
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
return removeErrorOverlay;
|
|
563
|
+
};
|
|
516
564
|
|
|
517
565
|
class JsenvErrorOverlay extends HTMLElement {
|
|
518
|
-
constructor({
|
|
519
|
-
|
|
520
|
-
|
|
566
|
+
constructor({
|
|
567
|
+
theme,
|
|
568
|
+
title,
|
|
569
|
+
text,
|
|
570
|
+
tip,
|
|
571
|
+
errorDetailsPromise
|
|
572
|
+
}) {
|
|
573
|
+
super();
|
|
574
|
+
this.root = this.attachShadow({
|
|
575
|
+
mode: "open"
|
|
576
|
+
});
|
|
521
577
|
this.root.innerHTML = `
|
|
522
578
|
<style>
|
|
523
579
|
${overlayCSS}
|
|
@@ -531,50 +587,60 @@ window.__supervisor__ = (() => {
|
|
|
531
587
|
<div class="tip">
|
|
532
588
|
${tip}
|
|
533
589
|
</div>
|
|
534
|
-
</div
|
|
590
|
+
</div>`;
|
|
591
|
+
|
|
535
592
|
this.root.querySelector(".backdrop").onclick = () => {
|
|
536
593
|
if (!this.parentNode) {
|
|
537
594
|
// not in document anymore
|
|
538
|
-
return
|
|
595
|
+
return;
|
|
539
596
|
}
|
|
540
|
-
|
|
541
|
-
this.
|
|
542
|
-
|
|
597
|
+
|
|
598
|
+
this.root.querySelector(".backdrop").onclick = null;
|
|
599
|
+
this.parentNode.removeChild(this);
|
|
600
|
+
};
|
|
601
|
+
|
|
543
602
|
if (errorDetailsPromise) {
|
|
544
|
-
errorDetailsPromise.then(
|
|
603
|
+
errorDetailsPromise.then(errorDetails => {
|
|
545
604
|
if (!errorDetails || !this.parentNode) {
|
|
546
|
-
return
|
|
605
|
+
return;
|
|
547
606
|
}
|
|
548
|
-
|
|
607
|
+
|
|
608
|
+
const {
|
|
609
|
+
codeFrame,
|
|
610
|
+
cause
|
|
611
|
+
} = errorDetails;
|
|
612
|
+
|
|
549
613
|
if (codeFrame) {
|
|
550
|
-
this.root.querySelector(".text").innerHTML += `\n\n${codeFrame}
|
|
614
|
+
this.root.querySelector(".text").innerHTML += `\n\n${codeFrame}`;
|
|
551
615
|
}
|
|
616
|
+
|
|
552
617
|
if (cause) {
|
|
553
|
-
const causeIndented = prefixRemainingLines(cause, " ")
|
|
554
|
-
this.root.querySelector(
|
|
555
|
-
".text",
|
|
556
|
-
).innerHTML += `\n [cause]: ${causeIndented}`
|
|
618
|
+
const causeIndented = prefixRemainingLines(cause, " ");
|
|
619
|
+
this.root.querySelector(".text").innerHTML += `\n [cause]: ${causeIndented}`;
|
|
557
620
|
}
|
|
558
|
-
})
|
|
621
|
+
});
|
|
559
622
|
}
|
|
560
623
|
}
|
|
624
|
+
|
|
561
625
|
}
|
|
562
626
|
|
|
563
627
|
const prefixRemainingLines = (text, prefix) => {
|
|
564
|
-
const lines = text.split(/\r?\n/)
|
|
565
|
-
const firstLine = lines.shift()
|
|
566
|
-
let result = firstLine
|
|
567
|
-
let i = 0
|
|
628
|
+
const lines = text.split(/\r?\n/);
|
|
629
|
+
const firstLine = lines.shift();
|
|
630
|
+
let result = firstLine;
|
|
631
|
+
let i = 0;
|
|
632
|
+
|
|
568
633
|
while (i < lines.length) {
|
|
569
|
-
const line = lines[i]
|
|
570
|
-
i
|
|
571
|
-
result += line.length ? `\n${prefix}${line}` : `\n
|
|
634
|
+
const line = lines[i];
|
|
635
|
+
i++;
|
|
636
|
+
result += line.length ? `\n${prefix}${line}` : `\n`;
|
|
572
637
|
}
|
|
573
|
-
|
|
574
|
-
|
|
638
|
+
|
|
639
|
+
return result;
|
|
640
|
+
};
|
|
575
641
|
|
|
576
642
|
if (customElements && !customElements.get(JSENV_ERROR_OVERLAY_TAGNAME)) {
|
|
577
|
-
customElements.define(JSENV_ERROR_OVERLAY_TAGNAME, JsenvErrorOverlay)
|
|
643
|
+
customElements.define(JSENV_ERROR_OVERLAY_TAGNAME, JsenvErrorOverlay);
|
|
578
644
|
}
|
|
579
645
|
|
|
580
646
|
const overlayCSS = `
|
|
@@ -651,13 +717,19 @@ window.__supervisor__ = (() => {
|
|
|
651
717
|
|
|
652
718
|
pre a {
|
|
653
719
|
color: inherit;
|
|
654
|
-
}
|
|
720
|
+
}`;
|
|
655
721
|
}
|
|
656
722
|
|
|
657
|
-
supervisor.createException = createException
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
723
|
+
supervisor.createException = createException;
|
|
724
|
+
|
|
725
|
+
supervisor.reportException = exception => {
|
|
726
|
+
const {
|
|
727
|
+
theme,
|
|
728
|
+
title,
|
|
729
|
+
text,
|
|
730
|
+
tip,
|
|
731
|
+
errorDetailsPromise
|
|
732
|
+
} = formatError(exception);
|
|
661
733
|
|
|
662
734
|
if (errorOverlay) {
|
|
663
735
|
const removeErrorOverlay = displayJsenvErrorOverlay({
|
|
@@ -665,30 +737,41 @@ window.__supervisor__ = (() => {
|
|
|
665
737
|
title,
|
|
666
738
|
text,
|
|
667
739
|
tip,
|
|
668
|
-
errorDetailsPromise
|
|
669
|
-
})
|
|
740
|
+
errorDetailsPromise
|
|
741
|
+
});
|
|
742
|
+
|
|
670
743
|
if (window.__reloader__) {
|
|
671
744
|
window.__reloader__.onstatuschange = () => {
|
|
672
745
|
if (window.__reloader__.status === "reloading") {
|
|
673
|
-
removeErrorOverlay()
|
|
746
|
+
removeErrorOverlay();
|
|
674
747
|
}
|
|
675
|
-
}
|
|
748
|
+
};
|
|
676
749
|
}
|
|
677
750
|
}
|
|
751
|
+
|
|
678
752
|
if (errorNotification) {
|
|
679
753
|
displayErrorNotification({
|
|
680
754
|
title,
|
|
681
|
-
text
|
|
682
|
-
})
|
|
755
|
+
text
|
|
756
|
+
});
|
|
683
757
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
758
|
+
|
|
759
|
+
return exception;
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
window.addEventListener("error", errorEvent => {
|
|
687
763
|
if (!errorEvent.isTrusted) {
|
|
688
764
|
// ignore custom error event (not sent by browser)
|
|
689
|
-
return
|
|
765
|
+
return;
|
|
690
766
|
}
|
|
691
|
-
|
|
767
|
+
|
|
768
|
+
const {
|
|
769
|
+
error,
|
|
770
|
+
message,
|
|
771
|
+
filename,
|
|
772
|
+
lineno,
|
|
773
|
+
colno
|
|
774
|
+
} = errorEvent;
|
|
692
775
|
const exception = supervisor.createException({
|
|
693
776
|
// when error is reported within a worker error is null
|
|
694
777
|
// but there is a message property on errorEvent
|
|
@@ -696,170 +779,249 @@ window.__supervisor__ = (() => {
|
|
|
696
779
|
reportedBy: "window_error_event",
|
|
697
780
|
url: filename,
|
|
698
781
|
line: lineno,
|
|
699
|
-
column: colno
|
|
700
|
-
})
|
|
701
|
-
supervisor.reportException(exception)
|
|
702
|
-
})
|
|
703
|
-
window.addEventListener("unhandledrejection",
|
|
782
|
+
column: colno
|
|
783
|
+
});
|
|
784
|
+
supervisor.reportException(exception);
|
|
785
|
+
});
|
|
786
|
+
window.addEventListener("unhandledrejection", event => {
|
|
704
787
|
if (event.defaultPrevented) {
|
|
705
|
-
return
|
|
788
|
+
return;
|
|
706
789
|
}
|
|
790
|
+
|
|
707
791
|
const exception = supervisor.createException({
|
|
708
792
|
reason: event.reason,
|
|
709
|
-
reportedBy: "window_unhandledrejection_event"
|
|
710
|
-
})
|
|
711
|
-
supervisor.reportException(exception)
|
|
712
|
-
})
|
|
713
|
-
}
|
|
793
|
+
reportedBy: "window_unhandledrejection_event"
|
|
794
|
+
});
|
|
795
|
+
supervisor.reportException(exception);
|
|
796
|
+
});
|
|
797
|
+
};
|
|
714
798
|
|
|
715
799
|
supervisor.setup = ({
|
|
716
800
|
rootDirectoryUrl,
|
|
717
801
|
logs,
|
|
718
|
-
measurePerf,
|
|
719
802
|
errorOverlay,
|
|
720
803
|
errorBaseUrl,
|
|
721
|
-
openInEditor
|
|
804
|
+
openInEditor
|
|
722
805
|
}) => {
|
|
723
806
|
supervisor.setupReportException({
|
|
724
807
|
rootDirectoryUrl,
|
|
725
808
|
errorOverlay,
|
|
726
809
|
errorBaseUrl,
|
|
727
|
-
openInEditor
|
|
728
|
-
})
|
|
810
|
+
openInEditor
|
|
811
|
+
});
|
|
812
|
+
const supervisedScripts = [];
|
|
813
|
+
const pendingPromises = []; // respect execution order
|
|
814
|
+
// - wait for classic scripts to be done (non async)
|
|
815
|
+
// - wait module script previous execution (non async)
|
|
816
|
+
// see https://gist.github.com/jakub-g/385ee6b41085303a53ad92c7c8afd7a6#typemodule-vs-non-module-typetextjavascript-vs-script-nomodule
|
|
817
|
+
|
|
818
|
+
const executionQueue = [];
|
|
819
|
+
let executing = false;
|
|
820
|
+
|
|
821
|
+
const addToExecutionQueue = async execution => {
|
|
822
|
+
if (execution.async) {
|
|
823
|
+
execution.start();
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
729
826
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
827
|
+
if (executing) {
|
|
828
|
+
executionQueue.push(execution);
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
startThenDequeue(execution);
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
const startThenDequeue = async execution => {
|
|
836
|
+
executing = true;
|
|
837
|
+
const promise = execution.start();
|
|
838
|
+
await promise;
|
|
839
|
+
executing = false;
|
|
840
|
+
|
|
841
|
+
if (executionQueue.length) {
|
|
842
|
+
const nextExecution = executionQueue.shift();
|
|
843
|
+
startThenDequeue(nextExecution);
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
supervisor.addExecution = ({
|
|
848
|
+
type,
|
|
849
|
+
src,
|
|
850
|
+
async,
|
|
851
|
+
execute
|
|
852
|
+
}) => {
|
|
733
853
|
const execution = {
|
|
734
854
|
type,
|
|
735
855
|
src,
|
|
736
856
|
async,
|
|
737
|
-
execute
|
|
738
|
-
}
|
|
857
|
+
execute
|
|
858
|
+
};
|
|
859
|
+
|
|
739
860
|
execution.start = () => {
|
|
740
|
-
return superviseExecution(execution, {
|
|
741
|
-
|
|
861
|
+
return superviseExecution(execution, {
|
|
862
|
+
isReload: false
|
|
863
|
+
});
|
|
864
|
+
};
|
|
865
|
+
|
|
742
866
|
execution.reload = () => {
|
|
743
|
-
return superviseExecution(execution, {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
867
|
+
return superviseExecution(execution, {
|
|
868
|
+
isReload: true
|
|
869
|
+
});
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
supervisedScripts.push(execution);
|
|
873
|
+
return addToExecutionQueue(execution);
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
const superviseExecution = async (execution, {
|
|
877
|
+
isReload
|
|
878
|
+
}) => {
|
|
749
879
|
if (logs) {
|
|
750
|
-
console.group(`[jsenv] loading ${execution.type} ${execution.src}`)
|
|
751
|
-
}
|
|
752
|
-
if (measurePerf) {
|
|
753
|
-
performance.mark(`execution_start`)
|
|
880
|
+
console.group(`[jsenv] loading ${execution.type} ${execution.src}`);
|
|
754
881
|
}
|
|
882
|
+
|
|
755
883
|
const executionResult = {
|
|
756
884
|
status: "pending",
|
|
885
|
+
loadDuration: null,
|
|
886
|
+
executionDuration: null,
|
|
887
|
+
duration: null,
|
|
757
888
|
exception: null,
|
|
758
889
|
namespace: null,
|
|
759
|
-
coverage: null
|
|
760
|
-
}
|
|
761
|
-
executionResults[execution.src] = executionResult
|
|
762
|
-
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
890
|
+
coverage: null
|
|
891
|
+
};
|
|
892
|
+
executionResults[execution.src] = executionResult;
|
|
893
|
+
|
|
894
|
+
const monitorScriptLoad = () => {
|
|
895
|
+
const loadStartTime = Date.now();
|
|
896
|
+
let resolveScriptLoadPromise;
|
|
897
|
+
const scriptLoadPromise = new Promise(resolve => {
|
|
898
|
+
resolveScriptLoadPromise = resolve;
|
|
899
|
+
});
|
|
900
|
+
pendingPromises.push(scriptLoadPromise);
|
|
901
|
+
return () => {
|
|
902
|
+
const loadEndTime = Date.now();
|
|
903
|
+
executionResult.loadDuration = loadEndTime - loadStartTime;
|
|
904
|
+
resolveScriptLoadPromise();
|
|
905
|
+
};
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
const monitorScriptExecution = () => {
|
|
909
|
+
const executionStartTime = Date.now();
|
|
910
|
+
let resolveExecutionPromise;
|
|
911
|
+
const executionPromise = new Promise(resolve => {
|
|
912
|
+
resolveExecutionPromise = resolve;
|
|
913
|
+
});
|
|
914
|
+
pendingPromises.push(executionPromise);
|
|
915
|
+
return () => {
|
|
916
|
+
executionResult.coverage = window.__coverage__;
|
|
917
|
+
executionResult.executionDuration = Date.now() - executionStartTime;
|
|
918
|
+
executionResult.duration = executionResult.loadDuration + executionResult.executionDuration;
|
|
919
|
+
resolveExecutionPromise();
|
|
920
|
+
};
|
|
921
|
+
};
|
|
922
|
+
|
|
923
|
+
const onError = e => {
|
|
924
|
+
executionResult.status = "errored";
|
|
925
|
+
const exception = supervisor.createException({
|
|
926
|
+
reason: e
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
if (exception.needsReport) {
|
|
930
|
+
supervisor.reportException(exception);
|
|
770
931
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
932
|
+
|
|
933
|
+
executionResult.exception = exception;
|
|
934
|
+
};
|
|
935
|
+
|
|
936
|
+
const scriptLoadDone = monitorScriptLoad();
|
|
937
|
+
|
|
774
938
|
try {
|
|
775
|
-
const result = await execution.execute({
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
executionResult.status = "completed"
|
|
780
|
-
executionResult.namespace = result
|
|
781
|
-
executionResult.coverage = window.__coverage__
|
|
939
|
+
const result = await execution.execute({
|
|
940
|
+
isReload
|
|
941
|
+
});
|
|
942
|
+
|
|
782
943
|
if (logs) {
|
|
783
|
-
console.log(`${execution.type} load ended`)
|
|
784
|
-
console.groupEnd()
|
|
785
|
-
}
|
|
786
|
-
resolveExecutionPromise()
|
|
787
|
-
} catch (e) {
|
|
788
|
-
if (measurePerf) {
|
|
789
|
-
performance.measure(`execution`, `execution_start`)
|
|
944
|
+
console.log(`${execution.type} load ended`);
|
|
945
|
+
console.groupEnd();
|
|
790
946
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
947
|
+
|
|
948
|
+
executionResult.status = "loaded";
|
|
949
|
+
scriptLoadDone();
|
|
950
|
+
const scriptExecutionDone = monitorScriptExecution();
|
|
951
|
+
|
|
952
|
+
if (execution.type === "js_classic") {
|
|
953
|
+
executionResult.status = "completed";
|
|
954
|
+
scriptExecutionDone();
|
|
955
|
+
} else if (execution.type === "js_module") {
|
|
956
|
+
result.executionPromise.then(namespace => {
|
|
957
|
+
executionResult.status = "completed";
|
|
958
|
+
executionResult.namespace = namespace;
|
|
959
|
+
scriptExecutionDone();
|
|
960
|
+
}, e => {
|
|
961
|
+
onError(e);
|
|
962
|
+
scriptExecutionDone();
|
|
963
|
+
});
|
|
797
964
|
}
|
|
798
|
-
|
|
799
|
-
executionResult.coverage = window.__coverage__
|
|
965
|
+
} catch (e) {
|
|
800
966
|
if (logs) {
|
|
801
|
-
console.groupEnd()
|
|
967
|
+
console.groupEnd();
|
|
802
968
|
}
|
|
803
|
-
resolveExecutionPromise()
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
969
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
// - wait module script previous execution (non async)
|
|
810
|
-
// see https://gist.github.com/jakub-g/385ee6b41085303a53ad92c7c8afd7a6#typemodule-vs-non-module-typetextjavascript-vs-script-nomodule
|
|
811
|
-
supervisor.getPreviousExecutionDonePromise = async () => {
|
|
812
|
-
const previousNonAsyncExecutions = executionPromises.filter(
|
|
813
|
-
(promise) => !promise.execution.async,
|
|
814
|
-
)
|
|
815
|
-
await Promise.all(previousNonAsyncExecutions)
|
|
816
|
-
}
|
|
817
|
-
supervisor.superviseScript = async ({ src, async }) => {
|
|
818
|
-
const { currentScript } = document
|
|
819
|
-
const parentNode = currentScript.parentNode
|
|
820
|
-
if (!async) {
|
|
821
|
-
await supervisor.getPreviousExecutionDonePromise()
|
|
970
|
+
onError(e);
|
|
971
|
+
scriptLoadDone();
|
|
822
972
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
973
|
+
};
|
|
974
|
+
|
|
975
|
+
supervisor.superviseScript = async ({
|
|
976
|
+
src,
|
|
977
|
+
async
|
|
978
|
+
}) => {
|
|
979
|
+
const {
|
|
980
|
+
currentScript
|
|
981
|
+
} = document;
|
|
982
|
+
const parentNode = currentScript.parentNode;
|
|
983
|
+
let nodeToReplace;
|
|
984
|
+
let currentScriptClone;
|
|
985
|
+
return supervisor.addExecution({
|
|
826
986
|
src,
|
|
827
987
|
type: "js_classic",
|
|
828
988
|
async,
|
|
829
|
-
execute: async ({
|
|
830
|
-
|
|
989
|
+
execute: async ({
|
|
990
|
+
isReload
|
|
991
|
+
}) => {
|
|
992
|
+
const urlObject = new URL(src, window.location);
|
|
831
993
|
const loadPromise = new Promise((resolve, reject) => {
|
|
832
994
|
// do not use script.cloneNode()
|
|
833
995
|
// bcause https://stackoverflow.com/questions/28771542/why-dont-clonenode-script-tags-execute
|
|
834
|
-
currentScriptClone = document.createElement("script")
|
|
835
|
-
// browsers set async by default when creating script(s)
|
|
996
|
+
currentScriptClone = document.createElement("script"); // browsers set async by default when creating script(s)
|
|
836
997
|
// we want an exact copy to preserves how code is executed
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
})
|
|
998
|
+
|
|
999
|
+
currentScriptClone.async = false;
|
|
1000
|
+
Array.from(currentScript.attributes).forEach(attribute => {
|
|
1001
|
+
currentScriptClone.setAttribute(attribute.nodeName, attribute.nodeValue);
|
|
1002
|
+
});
|
|
1003
|
+
|
|
844
1004
|
if (isReload) {
|
|
845
|
-
urlObject.searchParams.set("hmr", Date.now())
|
|
846
|
-
nodeToReplace = currentScriptClone
|
|
847
|
-
currentScriptClone.src = urlObject.href
|
|
1005
|
+
urlObject.searchParams.set("hmr", Date.now());
|
|
1006
|
+
nodeToReplace = currentScriptClone;
|
|
1007
|
+
currentScriptClone.src = urlObject.href;
|
|
848
1008
|
} else {
|
|
849
|
-
currentScriptClone.removeAttribute("jsenv-plugin-owner")
|
|
850
|
-
currentScriptClone.removeAttribute("jsenv-plugin-action")
|
|
851
|
-
currentScriptClone.removeAttribute("inlined-from-src")
|
|
852
|
-
currentScriptClone.removeAttribute("original-position")
|
|
853
|
-
currentScriptClone.removeAttribute("original-src-position")
|
|
854
|
-
nodeToReplace = currentScript
|
|
855
|
-
currentScriptClone.src = src
|
|
1009
|
+
currentScriptClone.removeAttribute("jsenv-plugin-owner");
|
|
1010
|
+
currentScriptClone.removeAttribute("jsenv-plugin-action");
|
|
1011
|
+
currentScriptClone.removeAttribute("inlined-from-src");
|
|
1012
|
+
currentScriptClone.removeAttribute("original-position");
|
|
1013
|
+
currentScriptClone.removeAttribute("original-src-position");
|
|
1014
|
+
nodeToReplace = currentScript;
|
|
1015
|
+
currentScriptClone.src = src;
|
|
856
1016
|
}
|
|
857
|
-
|
|
858
|
-
currentScriptClone.addEventListener("
|
|
859
|
-
|
|
860
|
-
|
|
1017
|
+
|
|
1018
|
+
currentScriptClone.addEventListener("error", reject);
|
|
1019
|
+
currentScriptClone.addEventListener("load", resolve);
|
|
1020
|
+
parentNode.replaceChild(currentScriptClone, nodeToReplace);
|
|
1021
|
+
});
|
|
1022
|
+
|
|
861
1023
|
try {
|
|
862
|
-
await loadPromise
|
|
1024
|
+
await loadPromise;
|
|
863
1025
|
} catch (e) {
|
|
864
1026
|
// eslint-disable-next-line no-throw-literal
|
|
865
1027
|
throw {
|
|
@@ -867,67 +1029,73 @@ window.__supervisor__ = (() => {
|
|
|
867
1029
|
reportedBy: "script_error_event",
|
|
868
1030
|
url: urlObject.href,
|
|
869
1031
|
// window.error won't be dispatched for this error
|
|
870
|
-
needsReport: true
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
},
|
|
874
|
-
})
|
|
875
|
-
return execution.start()
|
|
876
|
-
}
|
|
877
|
-
supervisor.reloadSupervisedScript = ({ type, src }) => {
|
|
878
|
-
const supervisedScript = supervisedScripts.find(
|
|
879
|
-
(supervisedScriptCandidate) => {
|
|
880
|
-
if (type && supervisedScriptCandidate.type !== type) {
|
|
881
|
-
return false
|
|
1032
|
+
needsReport: true
|
|
1033
|
+
};
|
|
882
1034
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
1035
|
+
}
|
|
1036
|
+
});
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
supervisor.reloadSupervisedScript = ({
|
|
1040
|
+
type,
|
|
1041
|
+
src
|
|
1042
|
+
}) => {
|
|
1043
|
+
const supervisedScript = supervisedScripts.find(supervisedScriptCandidate => {
|
|
1044
|
+
if (type && supervisedScriptCandidate.type !== type) {
|
|
1045
|
+
return false;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
if (supervisedScriptCandidate.src !== src) {
|
|
1049
|
+
return false;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
return true;
|
|
1053
|
+
});
|
|
1054
|
+
|
|
889
1055
|
if (supervisedScript) {
|
|
890
|
-
supervisedScript.reload()
|
|
1056
|
+
supervisedScript.reload();
|
|
891
1057
|
}
|
|
892
|
-
}
|
|
893
|
-
|
|
1058
|
+
};
|
|
1059
|
+
|
|
1060
|
+
supervisor.getDocumentExecutionResult = async () => {
|
|
894
1061
|
// just to be super safe and ensure any <script type="module"> got a chance to execute
|
|
895
|
-
const
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
"window.__supervisor__.scriptModuleCallback()"
|
|
900
|
-
window.__supervisor__.scriptModuleCallback = () => {
|
|
901
|
-
scriptTypeModule.parentNode.removeChild(scriptTypeModule)
|
|
902
|
-
resolve()
|
|
1062
|
+
const documentReadyPromise = new Promise(resolve => {
|
|
1063
|
+
if (document.readyState === "complete") {
|
|
1064
|
+
resolve();
|
|
1065
|
+
return;
|
|
903
1066
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
1067
|
+
|
|
1068
|
+
const loadCallback = () => {
|
|
1069
|
+
window.removeEventListener("load", loadCallback);
|
|
1070
|
+
resolve();
|
|
1071
|
+
};
|
|
1072
|
+
|
|
1073
|
+
window.addEventListener("load", loadCallback);
|
|
1074
|
+
});
|
|
1075
|
+
await documentReadyPromise;
|
|
1076
|
+
|
|
1077
|
+
const waitScriptExecutions = async () => {
|
|
1078
|
+
const numberOfPromises = pendingPromises.length;
|
|
1079
|
+
await Promise.all(pendingPromises); // new scripts added while the other where executing
|
|
1080
|
+
// (should happen only on webkit where
|
|
1081
|
+
// script might be added after window load event)
|
|
1082
|
+
|
|
1083
|
+
await new Promise(resolve => setTimeout(resolve));
|
|
1084
|
+
|
|
1085
|
+
if (pendingPromises.length > numberOfPromises) {
|
|
1086
|
+
await waitScriptExecutions();
|
|
912
1087
|
}
|
|
913
|
-
}
|
|
914
|
-
|
|
1088
|
+
};
|
|
1089
|
+
|
|
1090
|
+
await waitScriptExecutions();
|
|
915
1091
|
return {
|
|
916
1092
|
status: "completed",
|
|
917
1093
|
executionResults,
|
|
918
|
-
startTime:
|
|
919
|
-
endTime: Date.now()
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
const getNavigationStartTime = () => {
|
|
924
|
-
try {
|
|
925
|
-
return window.performance.timing.navigationStart
|
|
926
|
-
} catch (e) {
|
|
927
|
-
return Date.now()
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
}
|
|
1094
|
+
startTime: navigationStartTime,
|
|
1095
|
+
endTime: Date.now()
|
|
1096
|
+
};
|
|
1097
|
+
};
|
|
1098
|
+
};
|
|
931
1099
|
|
|
932
|
-
return supervisor
|
|
933
|
-
})()
|
|
1100
|
+
return supervisor;
|
|
1101
|
+
})();
|