@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
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
setHtmlNodeText,
|
|
21
21
|
} from "@jsenv/ast"
|
|
22
22
|
import { generateInlineContentUrl, stringifyUrlSite } from "@jsenv/urls"
|
|
23
|
+
import { getOriginalPosition } from "@jsenv/sourcemap"
|
|
23
24
|
|
|
24
25
|
import { requireFromJsenv } from "@jsenv/core/src/require_from_jsenv.js"
|
|
25
26
|
|
|
@@ -46,44 +47,42 @@ export const jsenvPluginHtmlSupervisor = ({
|
|
|
46
47
|
dev: true,
|
|
47
48
|
test: true,
|
|
48
49
|
},
|
|
49
|
-
serve: (request, context) => {
|
|
50
|
-
if (request.ressource.startsWith("/__open_in_editor__/")) {
|
|
51
|
-
const file = request.ressource.slice("/__open_in_editor__/".length)
|
|
52
|
-
if (!file) {
|
|
53
|
-
return {
|
|
54
|
-
status: 400,
|
|
55
|
-
body: "Missing file in url",
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
const launch = requireFromJsenv("launch-editor")
|
|
59
|
-
launch(fileURLToPath(file), () => {
|
|
60
|
-
// ignore error for now
|
|
61
|
-
})
|
|
62
|
-
return {
|
|
63
|
-
status: 200,
|
|
64
|
-
headers: {
|
|
65
|
-
"cache-control": "no-store",
|
|
66
|
-
},
|
|
67
|
-
}
|
|
68
|
-
}
|
|
50
|
+
serve: async (request, context) => {
|
|
69
51
|
if (request.ressource.startsWith("/__get_code_frame__/")) {
|
|
70
|
-
const
|
|
71
|
-
const
|
|
52
|
+
const { pathname, searchParams } = new URL(request.url)
|
|
53
|
+
const urlWithLineAndColumn = pathname.slice(
|
|
54
|
+
"/__get_code_frame__/".length,
|
|
55
|
+
)
|
|
56
|
+
const match = urlWithLineAndColumn.match(/:([0-9]+):([0-9]+)$/)
|
|
72
57
|
if (!match) {
|
|
73
58
|
return {
|
|
74
59
|
status: 400,
|
|
75
60
|
body: "Missing line and column in url",
|
|
76
61
|
}
|
|
77
62
|
}
|
|
78
|
-
const file =
|
|
79
|
-
|
|
80
|
-
|
|
63
|
+
const file = urlWithLineAndColumn.slice(0, match.index)
|
|
64
|
+
let line = parseInt(match[1])
|
|
65
|
+
let column = parseInt(match[2])
|
|
81
66
|
const urlInfo = context.urlGraph.getUrlInfo(file)
|
|
82
67
|
if (!urlInfo) {
|
|
83
68
|
return {
|
|
84
69
|
status: 404,
|
|
85
70
|
}
|
|
86
71
|
}
|
|
72
|
+
const remap = searchParams.has("remap")
|
|
73
|
+
if (remap) {
|
|
74
|
+
const sourcemap = urlInfo.sourcemap
|
|
75
|
+
if (sourcemap) {
|
|
76
|
+
const original = await getOriginalPosition({
|
|
77
|
+
sourcemap,
|
|
78
|
+
url: file,
|
|
79
|
+
line,
|
|
80
|
+
column,
|
|
81
|
+
})
|
|
82
|
+
line = original.line
|
|
83
|
+
column = original.column
|
|
84
|
+
}
|
|
85
|
+
}
|
|
87
86
|
const codeFrame = stringifyUrlSite({
|
|
88
87
|
url: file,
|
|
89
88
|
line,
|
|
@@ -99,6 +98,78 @@ export const jsenvPluginHtmlSupervisor = ({
|
|
|
99
98
|
body: codeFrame,
|
|
100
99
|
}
|
|
101
100
|
}
|
|
101
|
+
if (request.ressource.startsWith("/__get_error_cause__/")) {
|
|
102
|
+
const file = request.ressource.slice("/__get_error_cause__/".length)
|
|
103
|
+
if (!file) {
|
|
104
|
+
return {
|
|
105
|
+
status: 400,
|
|
106
|
+
body: "Missing file in url",
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const getErrorCauseInfo = () => {
|
|
110
|
+
const urlInfo = context.urlGraph.getUrlInfo(file)
|
|
111
|
+
if (!urlInfo) {
|
|
112
|
+
return null
|
|
113
|
+
}
|
|
114
|
+
const { error } = urlInfo
|
|
115
|
+
if (error) {
|
|
116
|
+
return error
|
|
117
|
+
}
|
|
118
|
+
// search in direct dependencies (404 or 500)
|
|
119
|
+
const { dependencies } = urlInfo
|
|
120
|
+
for (const dependencyUrl of dependencies) {
|
|
121
|
+
const dependencyUrlInfo = context.urlGraph.getUrlInfo(dependencyUrl)
|
|
122
|
+
if (dependencyUrlInfo.error) {
|
|
123
|
+
return dependencyUrlInfo.error
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null
|
|
127
|
+
}
|
|
128
|
+
const causeInfo = getErrorCauseInfo()
|
|
129
|
+
const body = JSON.stringify(
|
|
130
|
+
causeInfo
|
|
131
|
+
? {
|
|
132
|
+
code: causeInfo.code,
|
|
133
|
+
message: causeInfo.message,
|
|
134
|
+
reason: causeInfo.reason,
|
|
135
|
+
stack: errorBaseUrl
|
|
136
|
+
? `stack mocked for snapshot`
|
|
137
|
+
: causeInfo.stack,
|
|
138
|
+
codeFrame: causeInfo.traceMessage,
|
|
139
|
+
}
|
|
140
|
+
: null,
|
|
141
|
+
null,
|
|
142
|
+
" ",
|
|
143
|
+
)
|
|
144
|
+
return {
|
|
145
|
+
status: 200,
|
|
146
|
+
headers: {
|
|
147
|
+
"cache-control": "no-cache",
|
|
148
|
+
"content-type": "application/json",
|
|
149
|
+
"content-length": Buffer.byteLength(body),
|
|
150
|
+
},
|
|
151
|
+
body,
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (request.ressource.startsWith("/__open_in_editor__/")) {
|
|
155
|
+
const file = request.ressource.slice("/__open_in_editor__/".length)
|
|
156
|
+
if (!file) {
|
|
157
|
+
return {
|
|
158
|
+
status: 400,
|
|
159
|
+
body: "Missing file in url",
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const launch = requireFromJsenv("launch-editor")
|
|
163
|
+
launch(fileURLToPath(file), () => {
|
|
164
|
+
// ignore error for now
|
|
165
|
+
})
|
|
166
|
+
return {
|
|
167
|
+
status: 200,
|
|
168
|
+
headers: {
|
|
169
|
+
"cache-control": "no-store",
|
|
170
|
+
},
|
|
171
|
+
}
|
|
172
|
+
}
|
|
102
173
|
return null
|
|
103
174
|
},
|
|
104
175
|
transformUrlContent: {
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
const READY_STATES = {
|
|
2
|
+
CONNECTING: "connecting",
|
|
3
|
+
OPEN: "open",
|
|
4
|
+
CLOSING: "closing",
|
|
5
|
+
CLOSED: "closed",
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const createConnectionManager = (
|
|
9
|
+
attemptConnection,
|
|
10
|
+
{ retry, retryAfter, retryMaxAttempt, retryAllocatedMs },
|
|
11
|
+
) => {
|
|
12
|
+
const readyState = {
|
|
13
|
+
value: READY_STATES.CLOSED,
|
|
14
|
+
goTo: (value) => {
|
|
15
|
+
if (value === readyState.value) {
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
readyState.value = value
|
|
19
|
+
readyState.onchange()
|
|
20
|
+
},
|
|
21
|
+
onchange: () => {},
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let _disconnect = () => {}
|
|
25
|
+
const connect = () => {
|
|
26
|
+
if (
|
|
27
|
+
readyState.value === READY_STATES.CONNECTING ||
|
|
28
|
+
readyState.value === READY_STATES.OPEN
|
|
29
|
+
) {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let retryCount = 0
|
|
34
|
+
let msSpent = 0
|
|
35
|
+
const attempt = () => {
|
|
36
|
+
readyState.goTo(READY_STATES.CONNECTING)
|
|
37
|
+
_disconnect = attemptConnection({
|
|
38
|
+
onClosed: () => {
|
|
39
|
+
if (!retry) {
|
|
40
|
+
readyState.goTo(READY_STATES.CLOSED)
|
|
41
|
+
console.info(`[jsenv] failed to connect to server`)
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
if (retryCount > retryMaxAttempt) {
|
|
45
|
+
readyState.goTo(READY_STATES.CLOSED)
|
|
46
|
+
console.info(
|
|
47
|
+
`[jsenv] could not connect to server after ${retryMaxAttempt} attempt`,
|
|
48
|
+
)
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
if (retryAllocatedMs && msSpent > retryAllocatedMs) {
|
|
52
|
+
readyState.goTo(READY_STATES.CLOSED)
|
|
53
|
+
console.info(
|
|
54
|
+
`[jsenv] could not connect to server in less than ${retryAllocatedMs}ms`,
|
|
55
|
+
)
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// if closed while open -> connection lost
|
|
60
|
+
// otherwise it's the attempt to connect for the first time
|
|
61
|
+
// or to reconnect
|
|
62
|
+
if (readyState.value === READY_STATES.OPEN) {
|
|
63
|
+
console.info(`[jsenv] server connection lost; retrying to connect`)
|
|
64
|
+
}
|
|
65
|
+
retryCount++
|
|
66
|
+
setTimeout(() => {
|
|
67
|
+
msSpent += retryAfter
|
|
68
|
+
attempt()
|
|
69
|
+
}, retryAfter)
|
|
70
|
+
},
|
|
71
|
+
onOpen: () => {
|
|
72
|
+
readyState.goTo(READY_STATES.OPEN)
|
|
73
|
+
// console.info(`[jsenv] connected to server`)
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
attempt()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const disconnect = () => {
|
|
81
|
+
if (
|
|
82
|
+
readyState.value !== READY_STATES.CONNECTING &&
|
|
83
|
+
readyState.value !== READY_STATES.OPEN
|
|
84
|
+
) {
|
|
85
|
+
console.warn(
|
|
86
|
+
`disconnect() ignored because connection is ${readyState.value}`,
|
|
87
|
+
)
|
|
88
|
+
return null
|
|
89
|
+
}
|
|
90
|
+
return _disconnect()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const removePageUnloadListener = listenPageUnload(() => {
|
|
94
|
+
if (
|
|
95
|
+
readyState.value === READY_STATES.CONNECTING ||
|
|
96
|
+
readyState.value === READY_STATES.OPEN
|
|
97
|
+
) {
|
|
98
|
+
_disconnect()
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
readyState,
|
|
104
|
+
connect,
|
|
105
|
+
disconnect,
|
|
106
|
+
destroy: () => {
|
|
107
|
+
removePageUnloadListener()
|
|
108
|
+
disconnect()
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// const listenPageMightFreeze = (callback) => {
|
|
114
|
+
// const removePageHideListener = listenEvent(window, "pagehide", (pageHideEvent) => {
|
|
115
|
+
// if (pageHideEvent.persisted === true) {
|
|
116
|
+
// callback(pageHideEvent)
|
|
117
|
+
// }
|
|
118
|
+
// })
|
|
119
|
+
// return removePageHideListener
|
|
120
|
+
// }
|
|
121
|
+
|
|
122
|
+
// const listenPageFreeze = (callback) => {
|
|
123
|
+
// const removeFreezeListener = listenEvent(document, "freeze", (freezeEvent) => {
|
|
124
|
+
// callback(freezeEvent)
|
|
125
|
+
// })
|
|
126
|
+
// return removeFreezeListener
|
|
127
|
+
// }
|
|
128
|
+
|
|
129
|
+
// const listenPageIsRestored = (callback) => {
|
|
130
|
+
// const removeResumeListener = listenEvent(document, "resume", (resumeEvent) => {
|
|
131
|
+
// removePageshowListener()
|
|
132
|
+
// callback(resumeEvent)
|
|
133
|
+
// })
|
|
134
|
+
// const removePageshowListener = listenEvent(window, "pageshow", (pageshowEvent) => {
|
|
135
|
+
// if (pageshowEvent.persisted === true) {
|
|
136
|
+
// removePageshowListener()
|
|
137
|
+
// removeResumeListener()
|
|
138
|
+
// callback(pageshowEvent)
|
|
139
|
+
// }
|
|
140
|
+
// })
|
|
141
|
+
// return () => {
|
|
142
|
+
// removeResumeListener()
|
|
143
|
+
// removePageshowListener()
|
|
144
|
+
// }
|
|
145
|
+
// }
|
|
146
|
+
|
|
147
|
+
const listenPageUnload = (callback) => {
|
|
148
|
+
const removePageHideListener = listenEvent(
|
|
149
|
+
window,
|
|
150
|
+
"pagehide",
|
|
151
|
+
(pageHideEvent) => {
|
|
152
|
+
if (pageHideEvent.persisted !== true) {
|
|
153
|
+
callback(pageHideEvent)
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
)
|
|
157
|
+
return removePageHideListener
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const listenEvent = (emitter, event, callback) => {
|
|
161
|
+
emitter.addEventListener(event, callback)
|
|
162
|
+
return () => {
|
|
163
|
+
emitter.removeEventListener(event, callback)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -1,227 +1,75 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
CONNECTED: "connected",
|
|
4
|
-
DISCONNECTED: "disconnected",
|
|
5
|
-
}
|
|
1
|
+
import { createConnectionManager } from "./connection_manager.js"
|
|
2
|
+
import { createEventsManager } from "./events_manager.js"
|
|
6
3
|
|
|
7
4
|
export const createEventSourceConnection = (
|
|
8
5
|
eventSourceUrl,
|
|
9
6
|
{
|
|
10
|
-
|
|
11
|
-
retryAllocatedMs = Infinity,
|
|
7
|
+
withCredentials = true,
|
|
12
8
|
lastEventId,
|
|
13
9
|
useEventsToManageConnection = true,
|
|
10
|
+
retry = false,
|
|
11
|
+
retryMaxAttempt = Infinity,
|
|
12
|
+
retryAllocatedMs = Infinity,
|
|
14
13
|
} = {},
|
|
15
14
|
) => {
|
|
16
|
-
const { EventSource } = window
|
|
17
|
-
if (typeof EventSource !== "function") {
|
|
18
|
-
return () => {}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
let eventSource
|
|
22
|
-
const listenersMap = new Map()
|
|
23
|
-
const callbacksMap = new Map()
|
|
24
15
|
const eventSourceOrigin = new URL(eventSourceUrl).origin
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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)
|
|
51
|
-
}
|
|
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()
|
|
16
|
+
const attemptConnection = ({ onOpen, onClosed }) => {
|
|
17
|
+
const url = lastEventId
|
|
18
|
+
? addLastEventIdIntoUrlSearchParams(eventSourceUrl, lastEventId)
|
|
19
|
+
: eventSourceUrl
|
|
20
|
+
let eventSource = new EventSource(url, { withCredentials })
|
|
21
|
+
eventSource.onerror = () => {
|
|
22
|
+
eventSource.onerror = null
|
|
23
|
+
eventSource.onopen = null
|
|
24
|
+
eventSource.onmessage = null
|
|
25
|
+
eventSource = null
|
|
26
|
+
onClosed()
|
|
63
27
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
}
|
|
28
|
+
eventSource.onopen = () => {
|
|
29
|
+
eventSource.onopen = null
|
|
30
|
+
onOpen()
|
|
99
31
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
32
|
+
eventSource.onmessage = (messageEvent) => {
|
|
33
|
+
if (messageEvent.origin === eventSourceOrigin) {
|
|
34
|
+
if (messageEvent.lastEventId) {
|
|
35
|
+
lastEventId = messageEvent.lastEventId
|
|
36
|
+
}
|
|
37
|
+
const event = JSON.parse(messageEvent.data)
|
|
38
|
+
eventsManager.triggerCallbacks(event)
|
|
107
39
|
}
|
|
108
|
-
status.value = value
|
|
109
|
-
status.onchange()
|
|
110
|
-
},
|
|
111
|
-
onchange: () => {},
|
|
112
|
-
}
|
|
113
|
-
let _disconnect = () => {}
|
|
114
|
-
|
|
115
|
-
const attemptConnection = (url) => {
|
|
116
|
-
if (
|
|
117
|
-
status.value === STATUSES.CONNECTING ||
|
|
118
|
-
status.value === STATUSES.CONNECTED
|
|
119
|
-
) {
|
|
120
|
-
return
|
|
121
40
|
}
|
|
122
|
-
|
|
123
|
-
withCredentials: true,
|
|
124
|
-
})
|
|
125
|
-
_disconnect = () => {
|
|
126
|
-
if (
|
|
127
|
-
status.value !== STATUSES.CONNECTING &&
|
|
128
|
-
status.value !== STATUSES.CONNECTED
|
|
129
|
-
) {
|
|
130
|
-
console.warn(
|
|
131
|
-
`disconnect() ignored because connection is ${status.value}`,
|
|
132
|
-
)
|
|
133
|
-
return
|
|
134
|
-
}
|
|
41
|
+
return () => {
|
|
135
42
|
if (eventSource) {
|
|
136
|
-
eventSource.onerror = undefined
|
|
137
43
|
eventSource.close()
|
|
138
|
-
listenersMap.forEach((listener, eventName) => {
|
|
139
|
-
eventSource.removeEventListener(eventName, listener)
|
|
140
|
-
})
|
|
141
44
|
}
|
|
142
|
-
eventSource = null
|
|
143
|
-
status.goTo(STATUSES.DISCONNECTED)
|
|
144
45
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
} else {
|
|
158
|
-
const allRetryDuration = Date.now() - firstRetryMs
|
|
159
|
-
if (retryAllocatedMs && allRetryDuration > retryAllocatedMs) {
|
|
160
|
-
console.info(
|
|
161
|
-
`could not connect in less than ${retryAllocatedMs} ms`,
|
|
162
|
-
)
|
|
163
|
-
_disconnect()
|
|
164
|
-
return
|
|
165
|
-
}
|
|
46
|
+
}
|
|
47
|
+
const connectionManager = createConnectionManager(attemptConnection, {
|
|
48
|
+
retry,
|
|
49
|
+
retryMaxAttempt,
|
|
50
|
+
retryAllocatedMs,
|
|
51
|
+
})
|
|
52
|
+
const eventsManager = createEventsManager({
|
|
53
|
+
effect: () => {
|
|
54
|
+
if (useEventsToManageConnection) {
|
|
55
|
+
connectionManager.connect()
|
|
56
|
+
return () => {
|
|
57
|
+
connectionManager.disconnect()
|
|
166
58
|
}
|
|
167
|
-
|
|
168
|
-
retryCount++
|
|
169
|
-
status.goTo(STATUSES.CONNECTING)
|
|
170
|
-
return
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (errorEvent.target.readyState === EventSource.CLOSED) {
|
|
174
|
-
_disconnect()
|
|
175
|
-
return
|
|
176
59
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
status.goTo(STATUSES.CONNECTED)
|
|
180
|
-
}
|
|
181
|
-
listenersMap.forEach((listener, eventName) => {
|
|
182
|
-
eventSource.addEventListener(eventName, listener)
|
|
183
|
-
})
|
|
184
|
-
if (!listenersMap.has("welcome")) {
|
|
185
|
-
addEventCallbacks({
|
|
186
|
-
welcome: () => {}, // to update lastEventId
|
|
187
|
-
})
|
|
188
|
-
}
|
|
189
|
-
status.goTo(STATUSES.CONNECTING)
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
let _connect = () => {
|
|
193
|
-
attemptConnection(eventSourceUrl)
|
|
194
|
-
_connect = () => {
|
|
195
|
-
attemptConnection(
|
|
196
|
-
lastEventId
|
|
197
|
-
? addLastEventIdIntoUrlSearchParams(eventSourceUrl, lastEventId)
|
|
198
|
-
: eventSourceUrl,
|
|
199
|
-
)
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const removePageUnloadListener = listenPageUnload(() => {
|
|
204
|
-
if (
|
|
205
|
-
status.value === STATUSES.CONNECTING ||
|
|
206
|
-
status.value === STATUSES.CONNECTED
|
|
207
|
-
) {
|
|
208
|
-
_disconnect()
|
|
209
|
-
}
|
|
60
|
+
return null
|
|
61
|
+
},
|
|
210
62
|
})
|
|
211
63
|
|
|
212
|
-
const destroy = () => {
|
|
213
|
-
removePageUnloadListener()
|
|
214
|
-
_disconnect()
|
|
215
|
-
listenersMap.clear()
|
|
216
|
-
callbacksMap.clear()
|
|
217
|
-
}
|
|
218
|
-
|
|
219
64
|
return {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
destroy
|
|
65
|
+
readyState: connectionManager.readyState,
|
|
66
|
+
listenEvents: (namedCallbacks) => {
|
|
67
|
+
return eventsManager.addCallbacks(namedCallbacks)
|
|
68
|
+
},
|
|
69
|
+
destroy: () => {
|
|
70
|
+
connectionManager.destroy()
|
|
71
|
+
eventsManager.destroy()
|
|
72
|
+
},
|
|
225
73
|
}
|
|
226
74
|
}
|
|
227
75
|
|
|
@@ -233,57 +81,3 @@ const addLastEventIdIntoUrlSearchParams = (url, lastEventId) => {
|
|
|
233
81
|
}
|
|
234
82
|
return `${url}last-event-id=${encodeURIComponent(lastEventId)}`
|
|
235
83
|
}
|
|
236
|
-
|
|
237
|
-
// const listenPageMightFreeze = (callback) => {
|
|
238
|
-
// const removePageHideListener = listenEvent(window, "pagehide", (pageHideEvent) => {
|
|
239
|
-
// if (pageHideEvent.persisted === true) {
|
|
240
|
-
// callback(pageHideEvent)
|
|
241
|
-
// }
|
|
242
|
-
// })
|
|
243
|
-
// return removePageHideListener
|
|
244
|
-
// }
|
|
245
|
-
|
|
246
|
-
// const listenPageFreeze = (callback) => {
|
|
247
|
-
// const removeFreezeListener = listenEvent(document, "freeze", (freezeEvent) => {
|
|
248
|
-
// callback(freezeEvent)
|
|
249
|
-
// })
|
|
250
|
-
// return removeFreezeListener
|
|
251
|
-
// }
|
|
252
|
-
|
|
253
|
-
// const listenPageIsRestored = (callback) => {
|
|
254
|
-
// const removeResumeListener = listenEvent(document, "resume", (resumeEvent) => {
|
|
255
|
-
// removePageshowListener()
|
|
256
|
-
// callback(resumeEvent)
|
|
257
|
-
// })
|
|
258
|
-
// const removePageshowListener = listenEvent(window, "pageshow", (pageshowEvent) => {
|
|
259
|
-
// if (pageshowEvent.persisted === true) {
|
|
260
|
-
// removePageshowListener()
|
|
261
|
-
// removeResumeListener()
|
|
262
|
-
// callback(pageshowEvent)
|
|
263
|
-
// }
|
|
264
|
-
// })
|
|
265
|
-
// return () => {
|
|
266
|
-
// removeResumeListener()
|
|
267
|
-
// removePageshowListener()
|
|
268
|
-
// }
|
|
269
|
-
// }
|
|
270
|
-
|
|
271
|
-
const listenPageUnload = (callback) => {
|
|
272
|
-
const removePageHideListener = listenEvent(
|
|
273
|
-
window,
|
|
274
|
-
"pagehide",
|
|
275
|
-
(pageHideEvent) => {
|
|
276
|
-
if (pageHideEvent.persisted !== true) {
|
|
277
|
-
callback(pageHideEvent)
|
|
278
|
-
}
|
|
279
|
-
},
|
|
280
|
-
)
|
|
281
|
-
return removePageHideListener
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const listenEvent = (emitter, event, callback) => {
|
|
285
|
-
emitter.addEventListener(event, callback)
|
|
286
|
-
return () => {
|
|
287
|
-
emitter.removeEventListener(event, callback)
|
|
288
|
-
}
|
|
289
|
-
}
|