@jsenv/core 27.3.3 → 27.5.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/autoreload.js +359 -0
- package/dist/js/execute_using_dynamic_import.js +1 -1
- package/dist/js/html_supervisor_installer.js +524 -147
- package/dist/js/html_supervisor_setup.js +3 -4
- package/dist/js/new_stylesheet.js +26 -58
- package/dist/js/server_events_client.js +307 -0
- package/dist/main.js +7699 -7307
- package/package.json +15 -15
- package/{README.md → readme.md} +18 -7
- package/src/build/build.js +16 -18
- package/src/build/start_build_server.js +24 -28
- package/src/dev/start_dev_server.js +30 -94
- package/src/execute/execute.js +17 -35
- package/src/execute/run.js +2 -0
- package/src/omega/errors.js +43 -9
- package/src/omega/kitchen.js +42 -25
- package/src/omega/omega_server.js +96 -74
- package/src/omega/server/file_service.js +256 -28
- package/src/omega/url_graph.js +39 -20
- package/src/plugins/autoreload/client/autoreload.js +201 -0
- package/src/plugins/autoreload/{dev_sse/client → client}/autoreload_preference.js +0 -0
- package/src/plugins/autoreload/{dev_sse/client → client}/reload.js +29 -10
- package/src/plugins/autoreload/{dev_sse/client → client}/url_helpers.js +0 -0
- package/src/plugins/autoreload/jsenv_plugin_autoreload.js +4 -8
- package/src/plugins/autoreload/{dev_sse/jsenv_plugin_dev_sse_client.js → jsenv_plugin_autoreload_client.js} +8 -8
- package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +196 -0
- package/src/{dev/plugins → plugins}/explorer/client/explorer.html +0 -0
- package/src/{dev/plugins → plugins}/explorer/client/jsenv.png +0 -0
- package/src/{dev/plugins → plugins}/explorer/jsenv_plugin_explorer.js +1 -3
- package/src/plugins/html_supervisor/client/error_overlay.js +401 -0
- package/src/plugins/html_supervisor/client/html_supervisor_installer.js +138 -23
- package/src/plugins/html_supervisor/client/html_supervisor_setup.js +3 -4
- package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +55 -23
- package/src/plugins/inline/jsenv_plugin_html_inline_content.js +97 -117
- package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +66 -58
- package/src/plugins/plugin_controller.js +102 -67
- package/src/plugins/plugins.js +10 -10
- package/src/{helpers/event_source/event_source.js → plugins/server_events/client/event_source_connection.js} +125 -33
- package/src/plugins/server_events/client/server_events_client.js +17 -0
- package/src/plugins/server_events/jsenv_plugin_server_events_client_injection.js +48 -0
- package/src/plugins/server_events/server_events_dispatcher.js +69 -0
- package/src/{dev/plugins → plugins}/toolbar/client/animation/toolbar_animation.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/eventsource/eventsource.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/eventsource/toolbar_eventsource.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/execution/execution.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/execution/toolbar_execution.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/focus/focus.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/focus/toolbar_focus.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/jsenv_logo.svg +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/notification/toolbar_notification.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/responsive/overflow_menu.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/responsive/toolbar_responsive.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/settings/settings.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/settings/toolbar_settings.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/theme/jsenv_theme.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/theme/light_theme.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/theme/toolbar_theme.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/toolbar.html +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/toolbar_injector.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/toolbar_main.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/toolbar_main.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/tooltip/tooltip.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/tooltip/tooltip.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/animation.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/dom.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/fetch_using_xhr.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/fetching.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/iframe_to_parent_href.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/jsenv_logger.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/preferences.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/responsive.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/util.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/variant/variant.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/jsenv_plugin_toolbar.js +0 -0
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +4 -3
- package/src/plugins/transpilation/babel/new_stylesheet/client/new_stylesheet.js +25 -55
- package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +44 -24
- package/src/plugins/transpilation/jsenv_plugin_transpilation.js +6 -1
- package/src/plugins/url_analysis/html/html_urls.js +8 -8
- package/src/plugins/url_analysis/jsenv_plugin_url_analysis.js +3 -1
- package/src/test/execute_plan.js +36 -54
- package/src/test/execute_test_plan.js +2 -2
- package/src/test/logs_file_execution.js +60 -27
- package/src/test/logs_file_execution.test.mjs +41 -0
- package/dist/js/event_source_client.js +0 -528
- package/src/helpers/event_source/sse_service.js +0 -53
- package/src/plugins/autoreload/dev_sse/client/event_source_client.js +0 -193
- package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_server.js +0 -203
- package/src/plugins/html_supervisor/client/error_in_document.js +0 -198
|
@@ -1,44 +1,63 @@
|
|
|
1
1
|
import { timeStart } from "@jsenv/server"
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
3
|
+
const HOOK_NAMES = [
|
|
4
|
+
"init",
|
|
5
|
+
"serve", // is called only during dev/tests
|
|
6
|
+
"resolveUrl",
|
|
7
|
+
"redirectUrl",
|
|
8
|
+
"fetchUrlContent",
|
|
9
|
+
"transformUrlContent",
|
|
10
|
+
"transformUrlSearchParams",
|
|
11
|
+
"formatUrl",
|
|
12
|
+
"finalizeUrlContent",
|
|
13
|
+
"bundle", // is called only during build
|
|
14
|
+
"optimizeUrlContent", // is called only during build
|
|
15
|
+
"cooked",
|
|
16
|
+
"augmentResponse", // is called only during dev/tests
|
|
17
|
+
"destroy",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
export const createPluginController = ({ plugins, scenario }) => {
|
|
21
|
+
const flatPlugins = flattenAndFilterPlugins(plugins, { scenario })
|
|
22
|
+
// precompute a list of hooks per hookName for one major reason:
|
|
22
23
|
// - When debugging, there is less iteration
|
|
23
|
-
//
|
|
24
|
+
// also it should increase perf as there is less work to do
|
|
25
|
+
|
|
24
26
|
const hookGroups = {}
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
const addPlugin = (plugin, { position = "start" }) => {
|
|
28
|
+
Object.keys(plugin).forEach((key) => {
|
|
29
|
+
if (key === "name" || key === "appliesDuring" || key === "serverEvents") {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
const isHook = HOOK_NAMES.includes(key)
|
|
33
|
+
if (!isHook) {
|
|
34
|
+
console.warn(`Unexpected "${key}" property on "${plugin.name}" plugin`)
|
|
35
|
+
}
|
|
36
|
+
const hookName = key
|
|
37
|
+
const hookValue = plugin[hookName]
|
|
38
|
+
if (hookValue) {
|
|
39
|
+
const group = hookGroups[hookName] || (hookGroups[hookName] = [])
|
|
40
|
+
const hook = {
|
|
31
41
|
plugin,
|
|
32
|
-
hookName,
|
|
33
|
-
value:
|
|
34
|
-
}
|
|
42
|
+
name: hookName,
|
|
43
|
+
value: hookValue,
|
|
44
|
+
}
|
|
45
|
+
if (position === "start") {
|
|
46
|
+
group.push(hook)
|
|
47
|
+
} else {
|
|
48
|
+
group.unshift(hook)
|
|
49
|
+
}
|
|
35
50
|
}
|
|
36
51
|
})
|
|
37
|
-
hookGroups[hookName] = hooks
|
|
38
|
-
return hooks
|
|
39
52
|
}
|
|
40
|
-
|
|
41
|
-
|
|
53
|
+
const pushPlugin = (plugin) => {
|
|
54
|
+
addPlugin(plugin, { position: "start" })
|
|
55
|
+
}
|
|
56
|
+
const unshiftPlugin = (plugin) => {
|
|
57
|
+
addPlugin(plugin, { position: "end" })
|
|
58
|
+
}
|
|
59
|
+
flatPlugins.forEach((plugin) => {
|
|
60
|
+
pushPlugin(plugin)
|
|
42
61
|
})
|
|
43
62
|
|
|
44
63
|
let currentPlugin = null
|
|
@@ -49,15 +68,18 @@ export const createPluginController = ({
|
|
|
49
68
|
return null
|
|
50
69
|
}
|
|
51
70
|
currentPlugin = hook.plugin
|
|
52
|
-
currentHookName = hook.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
71
|
+
currentHookName = hook.name
|
|
72
|
+
let timeEnd
|
|
73
|
+
if (info.timing) {
|
|
74
|
+
timeEnd = timeStart(
|
|
75
|
+
`${currentHookName}-${currentPlugin.name.replace("jsenv:", "")}`,
|
|
76
|
+
)
|
|
77
|
+
}
|
|
56
78
|
let valueReturned = hookFn(info, context)
|
|
57
79
|
if (info.timing) {
|
|
58
80
|
Object.assign(info.timing, timeEnd())
|
|
59
81
|
}
|
|
60
|
-
valueReturned = assertAndNormalizeReturnValue(hook.
|
|
82
|
+
valueReturned = assertAndNormalizeReturnValue(hook.name, valueReturned)
|
|
61
83
|
currentPlugin = null
|
|
62
84
|
currentHookName = null
|
|
63
85
|
return valueReturned
|
|
@@ -68,15 +90,18 @@ export const createPluginController = ({
|
|
|
68
90
|
return null
|
|
69
91
|
}
|
|
70
92
|
currentPlugin = hook.plugin
|
|
71
|
-
currentHookName = hook.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
93
|
+
currentHookName = hook.name
|
|
94
|
+
let timeEnd
|
|
95
|
+
if (info.timing) {
|
|
96
|
+
timeEnd = timeStart(
|
|
97
|
+
`${currentHookName}-${currentPlugin.name.replace("jsenv:", "")}`,
|
|
98
|
+
)
|
|
99
|
+
}
|
|
75
100
|
let valueReturned = await hookFn(info, context)
|
|
76
101
|
if (info.timing) {
|
|
77
102
|
Object.assign(info.timing, timeEnd())
|
|
78
103
|
}
|
|
79
|
-
valueReturned = assertAndNormalizeReturnValue(hook.
|
|
104
|
+
valueReturned = assertAndNormalizeReturnValue(hook.name, valueReturned)
|
|
80
105
|
currentPlugin = null
|
|
81
106
|
currentHookName = null
|
|
82
107
|
return valueReturned
|
|
@@ -84,36 +109,45 @@ export const createPluginController = ({
|
|
|
84
109
|
|
|
85
110
|
const callHooks = (hookName, info, context, callback) => {
|
|
86
111
|
const hooks = hookGroups[hookName]
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
112
|
+
if (hooks) {
|
|
113
|
+
for (const hook of hooks) {
|
|
114
|
+
const returnValue = callHook(hook, info, context)
|
|
115
|
+
if (returnValue && callback) {
|
|
116
|
+
callback(returnValue)
|
|
117
|
+
}
|
|
91
118
|
}
|
|
92
119
|
}
|
|
93
120
|
}
|
|
94
121
|
const callAsyncHooks = async (hookName, info, context, callback) => {
|
|
95
122
|
const hooks = hookGroups[hookName]
|
|
96
|
-
|
|
97
|
-
await previous
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
123
|
+
if (hooks) {
|
|
124
|
+
await hooks.reduce(async (previous, hook) => {
|
|
125
|
+
await previous
|
|
126
|
+
const returnValue = await callAsyncHook(hook, info, context)
|
|
127
|
+
if (returnValue && callback) {
|
|
128
|
+
await callback(returnValue)
|
|
129
|
+
}
|
|
130
|
+
}, Promise.resolve())
|
|
131
|
+
}
|
|
103
132
|
}
|
|
104
133
|
|
|
105
134
|
const callHooksUntil = (hookName, info, context) => {
|
|
106
135
|
const hooks = hookGroups[hookName]
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
136
|
+
if (hooks) {
|
|
137
|
+
for (const hook of hooks) {
|
|
138
|
+
const returnValue = callHook(hook, info, context)
|
|
139
|
+
if (returnValue) {
|
|
140
|
+
return returnValue
|
|
141
|
+
}
|
|
111
142
|
}
|
|
112
143
|
}
|
|
113
144
|
return null
|
|
114
145
|
}
|
|
115
146
|
const callAsyncHooksUntil = (hookName, info, context) => {
|
|
116
147
|
const hooks = hookGroups[hookName]
|
|
148
|
+
if (!hooks) {
|
|
149
|
+
return null
|
|
150
|
+
}
|
|
117
151
|
if (hooks.length === 0) {
|
|
118
152
|
return null
|
|
119
153
|
}
|
|
@@ -136,8 +170,9 @@ export const createPluginController = ({
|
|
|
136
170
|
}
|
|
137
171
|
|
|
138
172
|
return {
|
|
139
|
-
plugins,
|
|
140
|
-
|
|
173
|
+
plugins: flatPlugins,
|
|
174
|
+
pushPlugin,
|
|
175
|
+
unshiftPlugin,
|
|
141
176
|
getHookFunction,
|
|
142
177
|
callHook,
|
|
143
178
|
callAsyncHook,
|
|
@@ -152,8 +187,8 @@ export const createPluginController = ({
|
|
|
152
187
|
}
|
|
153
188
|
}
|
|
154
189
|
|
|
155
|
-
const flattenAndFilterPlugins = (
|
|
156
|
-
const
|
|
190
|
+
const flattenAndFilterPlugins = (plugins, { scenario }) => {
|
|
191
|
+
const flatPlugins = []
|
|
157
192
|
const visitPluginEntry = (pluginEntry) => {
|
|
158
193
|
if (Array.isArray(pluginEntry)) {
|
|
159
194
|
pluginEntry.forEach((value) => visitPluginEntry(value))
|
|
@@ -169,7 +204,7 @@ const flattenAndFilterPlugins = (pluginsRaw, { scenario }) => {
|
|
|
169
204
|
return
|
|
170
205
|
}
|
|
171
206
|
if (appliesDuring === "*") {
|
|
172
|
-
|
|
207
|
+
flatPlugins.push(pluginEntry)
|
|
173
208
|
return
|
|
174
209
|
}
|
|
175
210
|
if (typeof appliesDuring === "string") {
|
|
@@ -179,7 +214,7 @@ const flattenAndFilterPlugins = (pluginsRaw, { scenario }) => {
|
|
|
179
214
|
)
|
|
180
215
|
}
|
|
181
216
|
if (appliesDuring === scenario) {
|
|
182
|
-
|
|
217
|
+
flatPlugins.push(pluginEntry)
|
|
183
218
|
}
|
|
184
219
|
return
|
|
185
220
|
}
|
|
@@ -189,7 +224,7 @@ const flattenAndFilterPlugins = (pluginsRaw, { scenario }) => {
|
|
|
189
224
|
)
|
|
190
225
|
}
|
|
191
226
|
if (appliesDuring[scenario]) {
|
|
192
|
-
|
|
227
|
+
flatPlugins.push(pluginEntry)
|
|
193
228
|
return
|
|
194
229
|
}
|
|
195
230
|
if (pluginEntry.destroy) {
|
|
@@ -199,8 +234,8 @@ const flattenAndFilterPlugins = (pluginsRaw, { scenario }) => {
|
|
|
199
234
|
}
|
|
200
235
|
throw new Error(`plugin must be objects, got ${pluginEntry}`)
|
|
201
236
|
}
|
|
202
|
-
|
|
203
|
-
return
|
|
237
|
+
plugins.forEach((plugin) => visitPluginEntry(plugin))
|
|
238
|
+
return flatPlugins
|
|
204
239
|
}
|
|
205
240
|
|
|
206
241
|
const getHookFunction = (
|
package/src/plugins/plugins.js
CHANGED
|
@@ -19,16 +19,17 @@ import { jsenvPluginMinification } from "./minification/jsenv_plugin_minificatio
|
|
|
19
19
|
import { jsenvPluginImportMetaHot } from "./import_meta_hot/jsenv_plugin_import_meta_hot.js"
|
|
20
20
|
import { jsenvPluginAutoreload } from "./autoreload/jsenv_plugin_autoreload.js"
|
|
21
21
|
import { jsenvPluginCacheControl } from "./cache_control/jsenv_plugin_cache_control.js"
|
|
22
|
+
// dev only
|
|
23
|
+
import { jsenvPluginExplorer } from "./explorer/jsenv_plugin_explorer.js"
|
|
22
24
|
|
|
23
25
|
export const getCorePlugins = ({
|
|
24
26
|
rootDirectoryUrl,
|
|
25
|
-
urlGraph,
|
|
26
27
|
scenario,
|
|
27
28
|
runtimeCompat,
|
|
28
29
|
|
|
29
30
|
urlAnalysis = {},
|
|
30
31
|
htmlSupervisor,
|
|
31
|
-
nodeEsmResolution,
|
|
32
|
+
nodeEsmResolution = true,
|
|
32
33
|
fileSystemMagicResolution,
|
|
33
34
|
directoryReferenceAllowed,
|
|
34
35
|
transpilation = true,
|
|
@@ -38,6 +39,7 @@ export const getCorePlugins = ({
|
|
|
38
39
|
clientAutoreload = false,
|
|
39
40
|
clientFileChangeCallbackList,
|
|
40
41
|
clientFilesPruneCallbackList,
|
|
42
|
+
explorer,
|
|
41
43
|
} = {}) => {
|
|
42
44
|
if (htmlSupervisor === true) {
|
|
43
45
|
htmlSupervisor = {}
|
|
@@ -45,9 +47,13 @@ export const getCorePlugins = ({
|
|
|
45
47
|
if (nodeEsmResolution === true) {
|
|
46
48
|
nodeEsmResolution = {}
|
|
47
49
|
}
|
|
50
|
+
if (fileSystemMagicResolution === true) {
|
|
51
|
+
fileSystemMagicResolution = {}
|
|
52
|
+
}
|
|
48
53
|
if (clientAutoreload === true) {
|
|
49
54
|
clientAutoreload = {}
|
|
50
55
|
}
|
|
56
|
+
|
|
51
57
|
return [
|
|
52
58
|
jsenvPluginUrlAnalysis({ rootDirectoryUrl, ...urlAnalysis }),
|
|
53
59
|
jsenvPluginTranspilation(transpilation),
|
|
@@ -63,12 +69,7 @@ export const getCorePlugins = ({
|
|
|
63
69
|
jsenvPluginHttpUrls(),
|
|
64
70
|
jsenvPluginLeadingSlash(),
|
|
65
71
|
// before url resolution to handle "js_import_export" resolution
|
|
66
|
-
jsenvPluginNodeEsmResolution(
|
|
67
|
-
rootDirectoryUrl,
|
|
68
|
-
urlGraph,
|
|
69
|
-
runtimeCompat,
|
|
70
|
-
...nodeEsmResolution,
|
|
71
|
-
}),
|
|
72
|
+
jsenvPluginNodeEsmResolution(nodeEsmResolution),
|
|
72
73
|
jsenvPluginUrlResolution(),
|
|
73
74
|
jsenvPluginUrlVersion(),
|
|
74
75
|
jsenvPluginCommonJsGlobals(),
|
|
@@ -83,8 +84,6 @@ export const getCorePlugins = ({
|
|
|
83
84
|
? [
|
|
84
85
|
jsenvPluginAutoreload({
|
|
85
86
|
...clientAutoreload,
|
|
86
|
-
rootDirectoryUrl,
|
|
87
|
-
urlGraph,
|
|
88
87
|
scenario,
|
|
89
88
|
clientFileChangeCallbackList,
|
|
90
89
|
clientFilesPruneCallbackList,
|
|
@@ -92,5 +91,6 @@ export const getCorePlugins = ({
|
|
|
92
91
|
]
|
|
93
92
|
: []),
|
|
94
93
|
jsenvPluginCacheControl(),
|
|
94
|
+
...(explorer ? [jsenvPluginExplorer(explorer)] : []),
|
|
95
95
|
]
|
|
96
96
|
}
|
|
@@ -1,27 +1,103 @@
|
|
|
1
|
-
|
|
1
|
+
const STATUSES = {
|
|
2
|
+
CONNECTING: "connecting",
|
|
3
|
+
CONNECTED: "connected",
|
|
4
|
+
DISCONNECTED: "disconnected",
|
|
5
|
+
}
|
|
2
6
|
|
|
3
7
|
export const createEventSourceConnection = (
|
|
4
8
|
eventSourceUrl,
|
|
5
|
-
|
|
6
|
-
|
|
9
|
+
{
|
|
10
|
+
retryMaxAttempt = Infinity,
|
|
11
|
+
retryAllocatedMs = Infinity,
|
|
12
|
+
lastEventId,
|
|
13
|
+
useEventsToManageConnection = true,
|
|
14
|
+
} = {},
|
|
7
15
|
) => {
|
|
8
16
|
const { EventSource } = window
|
|
9
17
|
if (typeof EventSource !== "function") {
|
|
10
18
|
return () => {}
|
|
11
19
|
}
|
|
12
20
|
|
|
21
|
+
let eventSource
|
|
22
|
+
const listenersMap = new Map()
|
|
23
|
+
const callbacksMap = new Map()
|
|
13
24
|
const eventSourceOrigin = new URL(eventSourceUrl).origin
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
const addEventCallbacks = (namedCallbacks) => {
|
|
26
|
+
let listenersMapSize = listenersMap.size
|
|
27
|
+
Object.keys(namedCallbacks).forEach((eventName) => {
|
|
28
|
+
const callback = namedCallbacks[eventName]
|
|
29
|
+
const existingCallbacks = callbacksMap.get(eventName)
|
|
30
|
+
let callbacks
|
|
31
|
+
if (existingCallbacks) {
|
|
32
|
+
callbacks = existingCallbacks
|
|
33
|
+
} else {
|
|
34
|
+
callbacks = []
|
|
35
|
+
callbacksMap.set(eventName, callbacks)
|
|
36
|
+
}
|
|
37
|
+
if (callbacks.length === 0) {
|
|
38
|
+
const eventListener = (e) => {
|
|
39
|
+
if (e.origin === eventSourceOrigin) {
|
|
40
|
+
if (e.lastEventId) {
|
|
41
|
+
lastEventId = e.lastEventId
|
|
42
|
+
}
|
|
43
|
+
callbacks.forEach((eventCallback) => {
|
|
44
|
+
eventCallback(e)
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
listenersMap.set(eventName, eventListener)
|
|
49
|
+
if (eventSource) {
|
|
50
|
+
eventSource.addEventListener(eventName, eventListener)
|
|
20
51
|
}
|
|
21
|
-
eventCallback(e)
|
|
22
52
|
}
|
|
53
|
+
callbacks.push(callback)
|
|
54
|
+
})
|
|
55
|
+
if (
|
|
56
|
+
useEventsToManageConnection &&
|
|
57
|
+
listenersMapSize === 0 &&
|
|
58
|
+
listenersMap.size > 0 &&
|
|
59
|
+
status.value !== STATUSES.CONNECTING &&
|
|
60
|
+
status.value !== STATUSES.CONNECTED
|
|
61
|
+
) {
|
|
62
|
+
_connect()
|
|
23
63
|
}
|
|
24
|
-
|
|
64
|
+
|
|
65
|
+
let removed = false
|
|
66
|
+
return () => {
|
|
67
|
+
if (removed) return
|
|
68
|
+
removed = true
|
|
69
|
+
listenersMapSize = listenersMap.size
|
|
70
|
+
Object.keys(namedCallbacks).forEach((eventName) => {
|
|
71
|
+
const callback = namedCallbacks[eventName]
|
|
72
|
+
const callbacks = callbacksMap.get(eventName)
|
|
73
|
+
if (callbacks) {
|
|
74
|
+
const index = callbacks.indexOf(callback)
|
|
75
|
+
if (index > -1) {
|
|
76
|
+
callbacks.splice(index, 1)
|
|
77
|
+
if (callbacks.length === 0) {
|
|
78
|
+
const listener = listenersMap.get(eventName)
|
|
79
|
+
if (listener) {
|
|
80
|
+
listenersMap.delete(listener)
|
|
81
|
+
if (eventSource) {
|
|
82
|
+
eventSource.removeEventListener(eventName, listener)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
namedCallbacks = null // allow garbage collect
|
|
90
|
+
if (
|
|
91
|
+
useEventsToManageConnection &&
|
|
92
|
+
listenersMapSize > 0 &&
|
|
93
|
+
listenersMap.size === 0 &&
|
|
94
|
+
(status.value === STATUSES.CONNECTING ||
|
|
95
|
+
status.value === STATUSES.CONNECTED)
|
|
96
|
+
) {
|
|
97
|
+
_disconnect()
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
25
101
|
|
|
26
102
|
const status = {
|
|
27
103
|
value: "default",
|
|
@@ -37,22 +113,34 @@ export const createEventSourceConnection = (
|
|
|
37
113
|
let _disconnect = () => {}
|
|
38
114
|
|
|
39
115
|
const attemptConnection = (url) => {
|
|
40
|
-
|
|
116
|
+
if (
|
|
117
|
+
status.value === STATUSES.CONNECTING ||
|
|
118
|
+
status.value === STATUSES.CONNECTED
|
|
119
|
+
) {
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
eventSource = new EventSource(url, {
|
|
41
123
|
withCredentials: true,
|
|
42
124
|
})
|
|
43
125
|
_disconnect = () => {
|
|
44
|
-
if (
|
|
126
|
+
if (
|
|
127
|
+
status.value !== STATUSES.CONNECTING &&
|
|
128
|
+
status.value !== STATUSES.CONNECTED
|
|
129
|
+
) {
|
|
45
130
|
console.warn(
|
|
46
131
|
`disconnect() ignored because connection is ${status.value}`,
|
|
47
132
|
)
|
|
48
133
|
return
|
|
49
134
|
}
|
|
50
|
-
eventSource
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
135
|
+
if (eventSource) {
|
|
136
|
+
eventSource.onerror = undefined
|
|
137
|
+
eventSource.close()
|
|
138
|
+
listenersMap.forEach((listener, eventName) => {
|
|
139
|
+
eventSource.removeEventListener(eventName, listener)
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
eventSource = null
|
|
143
|
+
status.goTo(STATUSES.DISCONNECTED)
|
|
56
144
|
}
|
|
57
145
|
let retryCount = 0
|
|
58
146
|
let firstRetryMs = Date.now()
|
|
@@ -78,7 +166,7 @@ export const createEventSourceConnection = (
|
|
|
78
166
|
}
|
|
79
167
|
|
|
80
168
|
retryCount++
|
|
81
|
-
status.goTo(
|
|
169
|
+
status.goTo(STATUSES.CONNECTING)
|
|
82
170
|
return
|
|
83
171
|
}
|
|
84
172
|
|
|
@@ -88,24 +176,22 @@ export const createEventSourceConnection = (
|
|
|
88
176
|
}
|
|
89
177
|
}
|
|
90
178
|
eventSource.onopen = () => {
|
|
91
|
-
status.goTo(
|
|
179
|
+
status.goTo(STATUSES.CONNECTED)
|
|
92
180
|
}
|
|
93
|
-
|
|
94
|
-
eventSource.addEventListener(eventName,
|
|
181
|
+
listenersMap.forEach((listener, eventName) => {
|
|
182
|
+
eventSource.addEventListener(eventName, listener)
|
|
95
183
|
})
|
|
96
|
-
if (!
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
lastEventId = e.lastEventId
|
|
100
|
-
}
|
|
184
|
+
if (!listenersMap.has("welcome")) {
|
|
185
|
+
addEventCallbacks({
|
|
186
|
+
welcome: () => {}, // to update lastEventId
|
|
101
187
|
})
|
|
102
188
|
}
|
|
103
|
-
status.goTo(
|
|
189
|
+
status.goTo(STATUSES.CONNECTING)
|
|
104
190
|
}
|
|
105
191
|
|
|
106
|
-
let
|
|
192
|
+
let _connect = () => {
|
|
107
193
|
attemptConnection(eventSourceUrl)
|
|
108
|
-
|
|
194
|
+
_connect = () => {
|
|
109
195
|
attemptConnection(
|
|
110
196
|
lastEventId
|
|
111
197
|
? addLastEventIdIntoUrlSearchParams(eventSourceUrl, lastEventId)
|
|
@@ -115,7 +201,10 @@ export const createEventSourceConnection = (
|
|
|
115
201
|
}
|
|
116
202
|
|
|
117
203
|
const removePageUnloadListener = listenPageUnload(() => {
|
|
118
|
-
if (
|
|
204
|
+
if (
|
|
205
|
+
status.value === STATUSES.CONNECTING ||
|
|
206
|
+
status.value === STATUSES.CONNECTED
|
|
207
|
+
) {
|
|
119
208
|
_disconnect()
|
|
120
209
|
}
|
|
121
210
|
})
|
|
@@ -123,11 +212,14 @@ export const createEventSourceConnection = (
|
|
|
123
212
|
const destroy = () => {
|
|
124
213
|
removePageUnloadListener()
|
|
125
214
|
_disconnect()
|
|
215
|
+
listenersMap.clear()
|
|
216
|
+
callbacksMap.clear()
|
|
126
217
|
}
|
|
127
218
|
|
|
128
219
|
return {
|
|
129
220
|
status,
|
|
130
|
-
connect,
|
|
221
|
+
connect: () => _connect(),
|
|
222
|
+
addEventCallbacks,
|
|
131
223
|
disconnect: () => _disconnect(),
|
|
132
224
|
destroy,
|
|
133
225
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createEventSourceConnection } from "./event_source_connection.js"
|
|
2
|
+
|
|
3
|
+
const eventsourceConnection = createEventSourceConnection(
|
|
4
|
+
document.location.href,
|
|
5
|
+
{
|
|
6
|
+
retryMaxAttempt: Infinity,
|
|
7
|
+
retryAllocatedMs: 20 * 1000,
|
|
8
|
+
},
|
|
9
|
+
)
|
|
10
|
+
const { status, connect, addEventCallbacks, disconnect } = eventsourceConnection
|
|
11
|
+
window.__server_events__ = {
|
|
12
|
+
addEventCallbacks,
|
|
13
|
+
status,
|
|
14
|
+
connect,
|
|
15
|
+
disconnect,
|
|
16
|
+
}
|
|
17
|
+
connect()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This plugin is very special because it is here
|
|
3
|
+
* to provide "serverEvents" used by other plugins
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
parseHtmlString,
|
|
8
|
+
stringifyHtmlAst,
|
|
9
|
+
injectScriptNodeAsEarlyAsPossible,
|
|
10
|
+
createHtmlNode,
|
|
11
|
+
} from "@jsenv/ast"
|
|
12
|
+
|
|
13
|
+
const serverEventsClientFileUrl = new URL(
|
|
14
|
+
"./client/server_events_client.js",
|
|
15
|
+
import.meta.url,
|
|
16
|
+
).href
|
|
17
|
+
|
|
18
|
+
export const jsenvPluginServerEventsClientInjection = () => {
|
|
19
|
+
return {
|
|
20
|
+
name: "jsenv:server_events_client_injection",
|
|
21
|
+
appliesDuring: "*",
|
|
22
|
+
transformUrlContent: {
|
|
23
|
+
html: (htmlUrlInfo, context) => {
|
|
24
|
+
const htmlAst = parseHtmlString(htmlUrlInfo.content)
|
|
25
|
+
const [serverEventsClientFileReference] = context.referenceUtils.inject(
|
|
26
|
+
{
|
|
27
|
+
type: "script_src",
|
|
28
|
+
expectedType: "js_module",
|
|
29
|
+
specifier: serverEventsClientFileUrl,
|
|
30
|
+
},
|
|
31
|
+
)
|
|
32
|
+
injectScriptNodeAsEarlyAsPossible(
|
|
33
|
+
htmlAst,
|
|
34
|
+
createHtmlNode({
|
|
35
|
+
"tagName": "script",
|
|
36
|
+
"type": "module",
|
|
37
|
+
"src": serverEventsClientFileReference.generatedSpecifier,
|
|
38
|
+
"injected-by": "jsenv:server_events",
|
|
39
|
+
}),
|
|
40
|
+
)
|
|
41
|
+
const htmlModified = stringifyHtmlAst(htmlAst)
|
|
42
|
+
return {
|
|
43
|
+
content: htmlModified,
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
}
|