@jsenv/core 27.5.3 → 27.7.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 +10 -6
- package/dist/js/html_supervisor_installer.js +272 -163
- package/dist/js/html_supervisor_setup.js +10 -2
- package/dist/js/server_events_client.js +249 -216
- package/dist/js/wrapper.mjs +4233 -0
- package/dist/main.js +21342 -21629
- package/package.json +4 -4
- package/src/build/build.js +15 -13
- package/src/dev/start_dev_server.js +10 -10
- package/src/execute/runtimes/browsers/chromium.js +1 -1
- package/src/execute/runtimes/browsers/firefox.js +1 -1
- package/src/execute/runtimes/browsers/webkit.js +1 -1
- package/src/omega/kitchen.js +13 -15
- package/src/omega/omega_server.js +15 -0
- package/src/omega/server/file_service.js +11 -84
- package/src/omega/url_graph/url_graph_load.js +0 -2
- package/src/omega/url_graph/url_info_transformations.js +27 -0
- package/src/omega/url_graph.js +1 -0
- package/src/plugins/autoreload/client/autoreload.js +10 -4
- package/src/plugins/html_supervisor/client/error_formatter.js +187 -66
- package/src/plugins/html_supervisor/client/error_overlay.js +29 -31
- package/src/plugins/html_supervisor/client/html_supervisor_installer.js +22 -67
- package/src/plugins/html_supervisor/client/html_supervisor_setup.js +10 -2
- package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +96 -25
- package/src/plugins/server_events/client/connection_manager.js +165 -0
- package/src/plugins/server_events/client/event_source_connection.js +50 -256
- package/src/plugins/server_events/client/events_manager.js +75 -0
- package/src/plugins/server_events/client/server_events_client.js +12 -11
- package/src/plugins/server_events/client/web_socket_connection.js +81 -0
- package/src/plugins/server_events/server_events_dispatcher.js +70 -54
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export const createEventsManager = ({ effect = () => {} } = {}) => {
|
|
2
|
+
const callbacksMap = new Map()
|
|
3
|
+
let cleanup
|
|
4
|
+
const addCallbacks = (namedCallbacks) => {
|
|
5
|
+
let callbacksMapSize = callbacksMap.size
|
|
6
|
+
Object.keys(namedCallbacks).forEach((eventName) => {
|
|
7
|
+
const callback = namedCallbacks[eventName]
|
|
8
|
+
const existingCallbacks = callbacksMap.get(eventName)
|
|
9
|
+
let callbacks
|
|
10
|
+
if (existingCallbacks) {
|
|
11
|
+
callbacks = existingCallbacks
|
|
12
|
+
} else {
|
|
13
|
+
callbacks = []
|
|
14
|
+
callbacksMap.set(eventName, callbacks)
|
|
15
|
+
}
|
|
16
|
+
callbacks.push(callback)
|
|
17
|
+
})
|
|
18
|
+
if (effect && callbacksMapSize === 0 && callbacksMapSize.size > 0) {
|
|
19
|
+
cleanup = effect()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let removed = false
|
|
23
|
+
return () => {
|
|
24
|
+
if (removed) return
|
|
25
|
+
removed = true
|
|
26
|
+
callbacksMapSize = callbacksMap.size
|
|
27
|
+
Object.keys(namedCallbacks).forEach((eventName) => {
|
|
28
|
+
const callback = namedCallbacks[eventName]
|
|
29
|
+
const callbacks = callbacksMap.get(eventName)
|
|
30
|
+
if (callbacks) {
|
|
31
|
+
const index = callbacks.indexOf(callback)
|
|
32
|
+
if (index > -1) {
|
|
33
|
+
callbacks.splice(index, 1)
|
|
34
|
+
if (callbacks.length === 0) {
|
|
35
|
+
callbacksMap.delete(eventName)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
namedCallbacks = null // allow garbage collect
|
|
41
|
+
if (
|
|
42
|
+
cleanup &&
|
|
43
|
+
typeof cleanup === "function" &&
|
|
44
|
+
callbacksMapSize > 0 &&
|
|
45
|
+
callbacksMapSize.size === 0
|
|
46
|
+
) {
|
|
47
|
+
cleanup()
|
|
48
|
+
cleanup = null
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const triggerCallbacks = (event) => {
|
|
54
|
+
const callbacks = callbacksMap.get(event.type)
|
|
55
|
+
if (callbacks) {
|
|
56
|
+
callbacks.forEach((callback) => {
|
|
57
|
+
callback(event)
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const destroy = () => {
|
|
63
|
+
callbacksMap.clear()
|
|
64
|
+
if (cleanup) {
|
|
65
|
+
cleanup()
|
|
66
|
+
cleanup = null
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
addCallbacks,
|
|
72
|
+
triggerCallbacks,
|
|
73
|
+
destroy,
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
/* globals self */
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
import { createWebSocketConnection } from "./web_socket_connection.js"
|
|
4
|
+
|
|
5
|
+
const websocketScheme = self.location.protocol === "https" ? "wss" : "ws"
|
|
6
|
+
const websocketUrl = `${websocketScheme}://${self.location.host}${self.location.pathname}${self.location.search}`
|
|
7
|
+
const websocketConnection = createWebSocketConnection(websocketUrl, {
|
|
8
|
+
retry: true,
|
|
9
|
+
retryAllocatedMs: 10_000,
|
|
10
|
+
})
|
|
11
|
+
const { readyState, connect, disconnect, listenEvents } = websocketConnection
|
|
11
12
|
window.__server_events__ = {
|
|
12
|
-
|
|
13
|
-
status,
|
|
13
|
+
readyState,
|
|
14
14
|
connect,
|
|
15
15
|
disconnect,
|
|
16
|
+
listenEvents,
|
|
16
17
|
}
|
|
17
18
|
connect()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { createConnectionManager } from "./connection_manager.js"
|
|
2
|
+
import { createEventsManager } from "./events_manager.js"
|
|
3
|
+
|
|
4
|
+
export const createWebSocketConnection = (
|
|
5
|
+
websocketUrl,
|
|
6
|
+
{
|
|
7
|
+
protocols = ["jsenv"],
|
|
8
|
+
useEventsToManageConnection = true,
|
|
9
|
+
retry = false,
|
|
10
|
+
retryAfter = 1000,
|
|
11
|
+
retryMaxAttempt = Infinity,
|
|
12
|
+
retryAllocatedMs = Infinity,
|
|
13
|
+
} = {},
|
|
14
|
+
) => {
|
|
15
|
+
const connectionManager = createConnectionManager(
|
|
16
|
+
({ onClosed, onOpen }) => {
|
|
17
|
+
let socket = new WebSocket(websocketUrl, protocols)
|
|
18
|
+
let interval
|
|
19
|
+
const cleanup = () => {
|
|
20
|
+
if (socket) {
|
|
21
|
+
socket.onerror = null
|
|
22
|
+
socket.onopen = null
|
|
23
|
+
socket.onclose = null
|
|
24
|
+
socket.onmessage = null
|
|
25
|
+
socket = null
|
|
26
|
+
clearInterval(interval)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
socket.onerror = () => {
|
|
30
|
+
cleanup()
|
|
31
|
+
onClosed()
|
|
32
|
+
}
|
|
33
|
+
socket.onopen = () => {
|
|
34
|
+
socket.onopen = null
|
|
35
|
+
onOpen()
|
|
36
|
+
interval = setInterval(() => {
|
|
37
|
+
socket.send('{"type":"ping"}')
|
|
38
|
+
}, 30_000)
|
|
39
|
+
}
|
|
40
|
+
socket.onclose = () => {
|
|
41
|
+
cleanup()
|
|
42
|
+
onClosed()
|
|
43
|
+
}
|
|
44
|
+
socket.onmessage = (messageEvent) => {
|
|
45
|
+
const event = JSON.parse(messageEvent.data)
|
|
46
|
+
eventsManager.triggerCallbacks(event)
|
|
47
|
+
}
|
|
48
|
+
return () => {
|
|
49
|
+
if (socket) {
|
|
50
|
+
socket.close()
|
|
51
|
+
cleanup()
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{ retry, retryAfter, retryMaxAttempt, retryAllocatedMs },
|
|
56
|
+
)
|
|
57
|
+
const eventsManager = createEventsManager({
|
|
58
|
+
effect: () => {
|
|
59
|
+
if (useEventsToManageConnection) {
|
|
60
|
+
connectionManager.connect()
|
|
61
|
+
return () => {
|
|
62
|
+
connectionManager.disconnect()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null
|
|
66
|
+
},
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
readyState: connectionManager.readyState,
|
|
71
|
+
connect: connectionManager.connect,
|
|
72
|
+
disconnect: connectionManager.disconnect,
|
|
73
|
+
listenEvents: (namedCallbacks) => {
|
|
74
|
+
return eventsManager.addCallbacks(namedCallbacks)
|
|
75
|
+
},
|
|
76
|
+
destroy: () => {
|
|
77
|
+
connectionManager.destroy()
|
|
78
|
+
eventsManager.destroy()
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -1,69 +1,85 @@
|
|
|
1
|
-
import { createSSERoom } from "@jsenv/server"
|
|
2
|
-
import { createCallbackListNotifiedOnce } from "@jsenv/abort"
|
|
3
|
-
|
|
4
1
|
export const createServerEventsDispatcher = () => {
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const sseRoomLimit = 100
|
|
2
|
+
const clients = []
|
|
3
|
+
const MAX_CLIENTS = 100
|
|
8
4
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
const addClient = (client) => {
|
|
6
|
+
clients.push(client)
|
|
7
|
+
if (clients.length >= MAX_CLIENTS) {
|
|
8
|
+
const firstClient = clients.shift()
|
|
9
|
+
firstClient.close()
|
|
10
|
+
}
|
|
11
|
+
return () => {
|
|
12
|
+
client.close()
|
|
13
|
+
const index = clients.indexOf(client)
|
|
14
|
+
if (index > -1) {
|
|
15
|
+
clients.splice(index, 1)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
14
19
|
|
|
15
20
|
return {
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
room.close()
|
|
37
|
-
const index = rooms.indexOf(room)
|
|
38
|
-
if (index > -1) {
|
|
39
|
-
rooms.splice(index, 1)
|
|
21
|
+
addWebsocket: (websocket, request) => {
|
|
22
|
+
const client = {
|
|
23
|
+
request,
|
|
24
|
+
getReadystate: () => {
|
|
25
|
+
return websocket.readyState
|
|
26
|
+
},
|
|
27
|
+
sendEvent: (event) => {
|
|
28
|
+
websocket.send(JSON.stringify(event))
|
|
29
|
+
},
|
|
30
|
+
close: (reason) => {
|
|
31
|
+
const closePromise = new Promise((resolve, reject) => {
|
|
32
|
+
websocket.onclose = () => {
|
|
33
|
+
websocket.onclose = null
|
|
34
|
+
websocket.onerror = null
|
|
35
|
+
resolve()
|
|
36
|
+
}
|
|
37
|
+
websocket.onerror = (e) => {
|
|
38
|
+
websocket.onclose = null
|
|
39
|
+
websocket.onerror = null
|
|
40
|
+
reject(e)
|
|
40
41
|
}
|
|
41
|
-
}
|
|
42
|
+
})
|
|
43
|
+
websocket.close(reason)
|
|
44
|
+
return closePromise
|
|
42
45
|
},
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
destroy: () => {
|
|
47
|
+
websocket.terminate()
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
client.sendEvent({ type: "welcome" })
|
|
51
|
+
return addClient(client)
|
|
46
52
|
},
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
)
|
|
53
|
+
// we could add "addEventSource" and let clients connect using
|
|
54
|
+
// new WebSocket or new EventSource
|
|
55
|
+
// in practice the new EventSource won't be used
|
|
56
|
+
// so "serverEventsDispatcher.addEventSource" is not implemented
|
|
57
|
+
// addEventSource: (request) => {},
|
|
58
|
+
dispatch: (event) => {
|
|
59
|
+
clients.forEach((client) => {
|
|
60
|
+
if (client.getReadystate() === 1) {
|
|
61
|
+
client.sendEvent(event)
|
|
62
|
+
}
|
|
63
|
+
})
|
|
54
64
|
},
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (predicate(
|
|
58
|
-
|
|
59
|
-
type,
|
|
60
|
-
data: JSON.stringify(data),
|
|
61
|
-
})
|
|
65
|
+
dispatchToClientsMatching: (event, predicate) => {
|
|
66
|
+
clients.forEach((client) => {
|
|
67
|
+
if (client.getReadystate() === 1 && predicate(client)) {
|
|
68
|
+
client.sendEvent(event)
|
|
62
69
|
}
|
|
63
70
|
})
|
|
64
71
|
},
|
|
72
|
+
close: async (reason) => {
|
|
73
|
+
await Promise.all(
|
|
74
|
+
clients.map(async (client) => {
|
|
75
|
+
await client.close(reason)
|
|
76
|
+
}),
|
|
77
|
+
)
|
|
78
|
+
},
|
|
65
79
|
destroy: () => {
|
|
66
|
-
|
|
80
|
+
clients.forEach((client) => {
|
|
81
|
+
client.destroy()
|
|
82
|
+
})
|
|
67
83
|
},
|
|
68
84
|
}
|
|
69
85
|
}
|