@jsenv/core 27.6.1 → 27.8.1

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.
Files changed (47) hide show
  1. package/dist/js/autoreload.js +3 -6
  2. package/dist/js/html_supervisor_installer.js +36 -32
  3. package/dist/js/html_supervisor_setup.js +10 -2
  4. package/dist/js/server_events_client.js +249 -216
  5. package/dist/js/wrapper.mjs +4233 -0
  6. package/dist/main.js +21266 -21628
  7. package/package.json +3 -3
  8. package/src/build/build.js +19 -18
  9. package/src/dev/start_dev_server.js +7 -11
  10. package/src/execute/execute.js +2 -2
  11. package/src/execute/runtimes/browsers/chromium.js +1 -1
  12. package/src/execute/runtimes/browsers/firefox.js +1 -1
  13. package/src/execute/runtimes/browsers/webkit.js +1 -1
  14. package/src/omega/kitchen.js +9 -14
  15. package/src/omega/omega_server.js +13 -2
  16. package/src/omega/server/file_service.js +13 -29
  17. package/src/plugins/autoreload/client/autoreload.js +3 -4
  18. package/src/plugins/autoreload/jsenv_plugin_autoreload.js +0 -4
  19. package/src/plugins/autoreload/jsenv_plugin_autoreload_client.js +1 -1
  20. package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +1 -1
  21. package/src/plugins/autoreload/jsenv_plugin_hmr.js +1 -1
  22. package/src/plugins/bundling/jsenv_plugin_bundling.js +1 -3
  23. package/src/plugins/cache_control/jsenv_plugin_cache_control.js +2 -5
  24. package/src/plugins/commonjs_globals/jsenv_plugin_commonjs_globals.js +2 -2
  25. package/src/plugins/file_urls/jsenv_plugin_file_urls.js +4 -8
  26. package/src/plugins/html_supervisor/client/error_formatter.js +43 -37
  27. package/src/plugins/html_supervisor/client/html_supervisor_installer.js +1 -4
  28. package/src/plugins/html_supervisor/client/html_supervisor_setup.js +10 -2
  29. package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +4 -5
  30. package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +2 -2
  31. package/src/plugins/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +18 -24
  32. package/src/plugins/importmap/jsenv_plugin_importmap.js +1 -1
  33. package/src/plugins/minification/jsenv_plugin_minification.js +1 -3
  34. package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +9 -7
  35. package/src/plugins/plugin_controller.js +17 -6
  36. package/src/plugins/plugins.js +0 -2
  37. package/src/plugins/server_events/client/connection_manager.js +165 -0
  38. package/src/plugins/server_events/client/event_source_connection.js +50 -256
  39. package/src/plugins/server_events/client/events_manager.js +75 -0
  40. package/src/plugins/server_events/client/server_events_client.js +12 -11
  41. package/src/plugins/server_events/client/web_socket_connection.js +81 -0
  42. package/src/plugins/server_events/server_events_dispatcher.js +70 -54
  43. package/src/plugins/toolbar/jsenv_plugin_toolbar.js +1 -3
  44. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +1 -1
  45. package/src/plugins/url_analysis/html/html_urls.js +2 -2
  46. package/src/test/execute_plan.js +2 -2
  47. package/src/test/execute_test_plan.js +1 -1
@@ -1,227 +1,75 @@
1
- const STATUSES = {
2
- CONNECTING: "connecting",
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
- retryMaxAttempt = Infinity,
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 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)
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
- 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
- }
28
+ eventSource.onopen = () => {
29
+ eventSource.onopen = null
30
+ onOpen()
99
31
  }
100
- }
101
-
102
- const status = {
103
- value: "default",
104
- goTo: (value) => {
105
- if (value === status.value) {
106
- return
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
- eventSource = new EventSource(url, {
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
- let retryCount = 0
146
- let firstRetryMs = Date.now()
147
- eventSource.onerror = (errorEvent) => {
148
- if (errorEvent.target.readyState === EventSource.CONNECTING) {
149
- if (retryCount > retryMaxAttempt) {
150
- console.info(`could not connect after ${retryMaxAttempt} attempt`)
151
- _disconnect()
152
- return
153
- }
154
-
155
- if (retryCount === 0) {
156
- firstRetryMs = Date.now()
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
- eventSource.onopen = () => {
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
- status,
221
- connect: () => _connect(),
222
- addEventCallbacks,
223
- disconnect: () => _disconnect(),
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
- }
@@ -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
- import { createEventSourceConnection } from "./event_source_connection.js"
1
+ /* globals self */
2
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
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
- addEventCallbacks,
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 destroyCallbackList = createCallbackListNotifiedOnce()
6
- const rooms = []
7
- const sseRoomLimit = 100
2
+ const clients = []
3
+ const MAX_CLIENTS = 100
8
4
 
9
- destroyCallbackList.add(() => {
10
- rooms.forEach((room) => {
11
- room.close()
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
- addRoom: (request) => {
17
- const existingRoom = rooms.find(
18
- (roomCandidate) =>
19
- roomCandidate.request.ressource === request.ressource,
20
- )
21
- if (existingRoom) {
22
- return existingRoom
23
- }
24
- const room = createSSERoom({
25
- retryDuration: 2000,
26
- historyLength: 100,
27
- welcomeEventEnabled: true,
28
- effect: () => {
29
- rooms.push(room)
30
- if (rooms.length >= sseRoomLimit) {
31
- const firstRoom = rooms.shift()
32
- firstRoom.close()
33
- }
34
- return () => {
35
- // when the last client leaves the room it is closed and removed from the list
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
- room.request = request
45
- return room
46
+ destroy: () => {
47
+ websocket.terminate()
48
+ },
49
+ }
50
+ client.sendEvent({ type: "welcome" })
51
+ return addClient(client)
46
52
  },
47
- dispatch: ({ type, data }) => {
48
- rooms.forEach((room) =>
49
- room.sendEventToAllClients({
50
- type,
51
- data: JSON.stringify(data),
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
- dispatchToRoomsMatching: ({ type, data }, predicate) => {
56
- rooms.forEach((room) => {
57
- if (predicate(room)) {
58
- room.sendEventToAllClients({
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
- destroyCallbackList.notify()
80
+ clients.forEach((client) => {
81
+ client.destroy()
82
+ })
67
83
  },
68
84
  }
69
85
  }
@@ -17,9 +17,7 @@ export const jsenvPluginToolbar = ({ logs = false } = {}) => {
17
17
 
18
18
  return {
19
19
  name: "jsenv:toolbar",
20
- appliesDuring: {
21
- dev: true,
22
- },
20
+ appliesDuring: { dev: true, test: false },
23
21
  transformUrlContent: {
24
22
  html: ({ url, content }, { referenceUtils }) => {
25
23
  if (url === toolbarHtmlClientFileUrl) {
@@ -43,7 +43,7 @@ export const jsenvPluginImportAssertions = ({
43
43
  // We would have to tell rollup to ignore import with assertion
44
44
  // - means rollup can bundle more js file together
45
45
  // - means url versioning can work for css inlined in js
46
- if (context.scenario === "build") {
46
+ if (context.scenarios.build) {
47
47
  json = true
48
48
  css = true
49
49
  text = true