bare-worker 4.1.8 → 4.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/README.md +165 -1
- package/global.js +2 -1
- package/index.js +12 -0
- package/lib/broadcast-channel.js +103 -0
- package/lib/errors.js +4 -0
- package/lib/message-channel.js +0 -3
- package/lib/message-port.js +0 -3
- package/lib/worker-state.js +25 -28
- package/lib/worker-thread.js +2 -8
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# bare-worker
|
|
2
2
|
|
|
3
|
-
Higher-level worker threads for JavaScript.
|
|
3
|
+
Higher-level worker threads for JavaScript. Built on top of <https://github.com/holepunchto/bare-thread> and <https://github.com/holepunchto/bare-channel>, it provides a `Worker` class together with `MessageChannel` and `MessagePort` primitives for passing structured messages between threads.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
6
|
npm i bare-worker
|
|
@@ -22,6 +22,170 @@ if (Worker.isMainThread) {
|
|
|
22
22
|
}
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
## API
|
|
26
|
+
|
|
27
|
+
#### `const worker = new Worker(entry[, options])`
|
|
28
|
+
|
|
29
|
+
Spawn a new worker thread that loads and runs the module at `entry`. `Worker` extends `MessagePort`, so the returned instance can be used to exchange messages with the worker.
|
|
30
|
+
|
|
31
|
+
Options include:
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
options = {
|
|
35
|
+
workerData: null
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`workerData` is an arbitrary value cloned into the worker and made available there as `Worker.workerData`.
|
|
40
|
+
|
|
41
|
+
#### `worker.detached`
|
|
42
|
+
|
|
43
|
+
Whether the underlying message port has been detached for transfer. See `MessagePort`.
|
|
44
|
+
|
|
45
|
+
#### `await worker.terminate()`
|
|
46
|
+
|
|
47
|
+
Stop the worker as soon as possible. Returns a `Promise` that resolves with the worker's exit code once it has fully closed. Safe to call multiple times; subsequent calls resolve with the same exit code.
|
|
48
|
+
|
|
49
|
+
#### `event: 'online'`
|
|
50
|
+
|
|
51
|
+
Emitted when the worker thread has started executing.
|
|
52
|
+
|
|
53
|
+
#### `event: 'message'`
|
|
54
|
+
|
|
55
|
+
Emitted for each message sent from the worker with `parentPort.postMessage()`. The message value is passed to the listener.
|
|
56
|
+
|
|
57
|
+
#### `event: 'error'`
|
|
58
|
+
|
|
59
|
+
Emitted when an uncaught exception or unhandled rejection occurs in the worker. The error is passed to the listener and the worker exits with code `1`.
|
|
60
|
+
|
|
61
|
+
#### `event: 'exit'`
|
|
62
|
+
|
|
63
|
+
Emitted when the worker has stopped. The exit code is passed to the listener.
|
|
64
|
+
|
|
65
|
+
#### `Worker.isMainThread`
|
|
66
|
+
|
|
67
|
+
`true` when the current thread is the main thread, `false` when running inside a worker.
|
|
68
|
+
|
|
69
|
+
#### `Worker.parentPort`
|
|
70
|
+
|
|
71
|
+
Within a worker, the `MessagePort` connected to the parent thread. `null` on the main thread.
|
|
72
|
+
|
|
73
|
+
#### `Worker.workerData`
|
|
74
|
+
|
|
75
|
+
Within a worker, the `workerData` value passed when the worker was spawned. `null` on the main thread.
|
|
76
|
+
|
|
77
|
+
#### `Worker.MessageChannel`
|
|
78
|
+
|
|
79
|
+
The `MessageChannel` class. See below.
|
|
80
|
+
|
|
81
|
+
#### `Worker.MessagePort`
|
|
82
|
+
|
|
83
|
+
The `MessagePort` class. See below.
|
|
84
|
+
|
|
85
|
+
#### `Worker.BroadcastChannel`
|
|
86
|
+
|
|
87
|
+
The `BroadcastChannel` class. See below.
|
|
88
|
+
|
|
89
|
+
#### `Worker.preload(entry)`
|
|
90
|
+
|
|
91
|
+
Register the module at `entry` to be loaded in every worker before its own entry module runs. Preloads are inherited by nested workers.
|
|
92
|
+
|
|
93
|
+
#### `Worker.setEnvironmentData(key[, value])`
|
|
94
|
+
|
|
95
|
+
Set a value on the environment data shared with workers. The data is cloned into each worker as it is spawned. Omitting `value` removes `key`.
|
|
96
|
+
|
|
97
|
+
#### `Worker.getEnvironmentData(key)`
|
|
98
|
+
|
|
99
|
+
Get a value previously set with `Worker.setEnvironmentData()`.
|
|
100
|
+
|
|
101
|
+
### `MessageChannel`
|
|
102
|
+
|
|
103
|
+
#### `const channel = new MessageChannel()`
|
|
104
|
+
|
|
105
|
+
Create a pair of connected message ports for two-way communication. A port may be transferred to a worker by including it in the transfer list of a `postMessage()` call.
|
|
106
|
+
|
|
107
|
+
#### `channel.port1`
|
|
108
|
+
|
|
109
|
+
One end of the channel, a `MessagePort`.
|
|
110
|
+
|
|
111
|
+
#### `channel.port2`
|
|
112
|
+
|
|
113
|
+
The other end of the channel, a `MessagePort`.
|
|
114
|
+
|
|
115
|
+
### `MessagePort`
|
|
116
|
+
|
|
117
|
+
The endpoint of a message channel. `MessagePort` extends `EventEmitter`. A port begins receiving messages once it is started, which happens automatically when a `'message'` listener is added.
|
|
118
|
+
|
|
119
|
+
#### `port.detached`
|
|
120
|
+
|
|
121
|
+
Whether the port has been detached for transfer to another thread.
|
|
122
|
+
|
|
123
|
+
#### `port.postMessage(message[, transferList])`
|
|
124
|
+
|
|
125
|
+
Send `message` to the other end of the channel. `transferList` is an optional array of transferable objects, such as other `MessagePort` instances, whose ownership is moved to the receiving thread rather than cloned.
|
|
126
|
+
|
|
127
|
+
#### `port.start()`
|
|
128
|
+
|
|
129
|
+
Begin receiving messages on the port. Called automatically when a `'message'` listener is added, but may be called explicitly to start before any listener is attached.
|
|
130
|
+
|
|
131
|
+
#### `port.close()`
|
|
132
|
+
|
|
133
|
+
Close the port. No further messages will be sent or received.
|
|
134
|
+
|
|
135
|
+
#### `port.ref()`
|
|
136
|
+
|
|
137
|
+
Keep the underlying I/O resource referenced so it prevents the thread from exiting while the port is open.
|
|
138
|
+
|
|
139
|
+
#### `port.unref()`
|
|
140
|
+
|
|
141
|
+
Allow the thread to exit even while the port is open. Inflight writes are still awaited.
|
|
142
|
+
|
|
143
|
+
#### `port.hasRef()`
|
|
144
|
+
|
|
145
|
+
Whether the port is currently referenced.
|
|
146
|
+
|
|
147
|
+
#### `event: 'message'`
|
|
148
|
+
|
|
149
|
+
Emitted for each message received on the port. The message value is passed to the listener.
|
|
150
|
+
|
|
151
|
+
#### `event: 'close'`
|
|
152
|
+
|
|
153
|
+
Emitted when the port has closed.
|
|
154
|
+
|
|
155
|
+
### `BroadcastChannel`
|
|
156
|
+
|
|
157
|
+
A named channel for broadcasting messages to every other `BroadcastChannel` with the same name across the entire worker tree, including the main thread and nested workers. `BroadcastChannel` extends `EventEmitter` and follows the semantics of the Web `BroadcastChannel`: a channel never receives its own messages, but every other channel sharing its name does.
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
const Worker = require('bare-worker')
|
|
161
|
+
|
|
162
|
+
const channel = new Worker.BroadcastChannel('updates')
|
|
163
|
+
|
|
164
|
+
channel.on('message', console.log)
|
|
165
|
+
|
|
166
|
+
channel.postMessage('Hello everyone')
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### `const channel = new BroadcastChannel(name)`
|
|
170
|
+
|
|
171
|
+
Create a channel bound to `name`. All channels constructed with the same `name`, in any thread of the worker tree, are connected.
|
|
172
|
+
|
|
173
|
+
#### `channel.name`
|
|
174
|
+
|
|
175
|
+
The name the channel is bound to.
|
|
176
|
+
|
|
177
|
+
#### `channel.postMessage(message)`
|
|
178
|
+
|
|
179
|
+
Broadcast `message` to every other connected channel sharing this channel's name. The message is cloned; unlike `MessagePort.postMessage()`, transferring is not supported. Throws if the channel has been closed.
|
|
180
|
+
|
|
181
|
+
#### `channel.close()`
|
|
182
|
+
|
|
183
|
+
Close the channel, disconnecting it from the others. No further messages will be sent or received.
|
|
184
|
+
|
|
185
|
+
#### `event: 'message'`
|
|
186
|
+
|
|
187
|
+
Emitted for each message broadcast to the channel by another channel sharing its name. The message value is passed to the listener.
|
|
188
|
+
|
|
25
189
|
## License
|
|
26
190
|
|
|
27
191
|
Apache-2.0
|
package/global.js
CHANGED
package/index.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
const Thread = require('bare-thread')
|
|
2
2
|
const Channel = require('bare-channel')
|
|
3
|
+
const Broadcast = require('bare-broadcast-channel')
|
|
3
4
|
const WorkerState = require('./lib/worker-state')
|
|
4
5
|
const MessageChannel = require('./lib/message-channel')
|
|
5
6
|
const MessagePort = require('./lib/message-port')
|
|
7
|
+
const BroadcastChannel = require('./lib/broadcast-channel')
|
|
6
8
|
const constants = require('./lib/constants')
|
|
7
9
|
|
|
8
10
|
const preloads = new Map()
|
|
9
11
|
|
|
12
|
+
let broadcast = null
|
|
10
13
|
let environmentData = new Map()
|
|
11
14
|
let parentPort = null
|
|
12
15
|
let workerData = null
|
|
@@ -16,11 +19,18 @@ if (WorkerState.parent) {
|
|
|
16
19
|
preloads.set(entry, source)
|
|
17
20
|
}
|
|
18
21
|
|
|
22
|
+
broadcast = WorkerState.parent.broadcast
|
|
19
23
|
parentPort = WorkerState.parent.port
|
|
20
24
|
workerData = WorkerState.parent.data
|
|
21
25
|
environmentData = WorkerState.parent.environmentData
|
|
26
|
+
} else {
|
|
27
|
+
broadcast = new Broadcast()
|
|
22
28
|
}
|
|
23
29
|
|
|
30
|
+
// Every named broadcast channel on this thread connects to the same underlying
|
|
31
|
+
// channel, which is shared across the whole worker tree.
|
|
32
|
+
BroadcastChannel._channel = broadcast
|
|
33
|
+
|
|
24
34
|
const worker = Thread.prepare(require.resolve('./lib/worker-thread'), { shared: true })
|
|
25
35
|
|
|
26
36
|
module.exports = exports = class Worker extends MessagePort {
|
|
@@ -37,6 +47,7 @@ module.exports = exports = class Worker extends MessagePort {
|
|
|
37
47
|
data: {
|
|
38
48
|
source: Thread.prepare(entry, { shared: true }),
|
|
39
49
|
channel: channel.handle,
|
|
50
|
+
broadcast: broadcast.handle,
|
|
40
51
|
data: workerData,
|
|
41
52
|
preloads,
|
|
42
53
|
environmentData
|
|
@@ -97,6 +108,7 @@ exports.Worker = exports
|
|
|
97
108
|
|
|
98
109
|
exports.MessageChannel = MessageChannel
|
|
99
110
|
exports.MessagePort = MessagePort
|
|
111
|
+
exports.BroadcastChannel = BroadcastChannel
|
|
100
112
|
|
|
101
113
|
exports.parentPort = parentPort
|
|
102
114
|
exports.workerData = workerData
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const EventEmitter = require('bare-events')
|
|
2
|
+
const errors = require('./errors')
|
|
3
|
+
|
|
4
|
+
module.exports = exports = class BroadcastChannel extends EventEmitter {
|
|
5
|
+
constructor(name) {
|
|
6
|
+
super()
|
|
7
|
+
|
|
8
|
+
this.name = String(name)
|
|
9
|
+
|
|
10
|
+
this._port = exports._channel.connect()
|
|
11
|
+
this._inflight = 0
|
|
12
|
+
this._closed = false
|
|
13
|
+
this._reading = false
|
|
14
|
+
|
|
15
|
+
// The read loop terminates once the last peer leaves, so restart it when
|
|
16
|
+
// peers reappear to resume receiving broadcasts.
|
|
17
|
+
this._port.on('peers', this._onpeers.bind(this))
|
|
18
|
+
|
|
19
|
+
// An idle channel without listeners should not hold the thread alive.
|
|
20
|
+
this._port.unref()
|
|
21
|
+
|
|
22
|
+
this.on('newListener', this._onnewlistener).on('removeListener', this._onremovelistener)
|
|
23
|
+
|
|
24
|
+
this._read()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
postMessage(message) {
|
|
28
|
+
if (this._closed) {
|
|
29
|
+
throw errors.CHANNEL_CLOSED('BroadcastChannel is closed')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
this._write(message)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
close() {
|
|
36
|
+
if (this._closed) return
|
|
37
|
+
|
|
38
|
+
this._closed = true
|
|
39
|
+
|
|
40
|
+
this._port.close()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
[Symbol.for('bare.inspect')]() {
|
|
44
|
+
return {
|
|
45
|
+
__proto__: { constructor: BroadcastChannel },
|
|
46
|
+
|
|
47
|
+
name: this.name
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async _write(message) {
|
|
52
|
+
this._inflight++
|
|
53
|
+
this._port.ref()
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
await this._port.write({ name: this.name, message })
|
|
57
|
+
} finally {
|
|
58
|
+
this._inflight--
|
|
59
|
+
this._unref()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_unref() {
|
|
64
|
+
if (this._inflight === 0 && this.listenerCount('message') === 0) this._port.unref()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async _read() {
|
|
68
|
+
if (this._reading) return
|
|
69
|
+
|
|
70
|
+
this._reading = true
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
for await (const data of this._port) {
|
|
74
|
+
// The channel shares a single underlying broadcast channel across the
|
|
75
|
+
// whole worker tree, so messages are tagged with their channel name and
|
|
76
|
+
// filtered on read.
|
|
77
|
+
if (data.name === this.name) this.emit('message', data.message)
|
|
78
|
+
}
|
|
79
|
+
} finally {
|
|
80
|
+
this._reading = false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
_onpeers(peers) {
|
|
85
|
+
if (peers > 0 && !this._closed) this._read()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
_onnewlistener(name) {
|
|
89
|
+
if (name !== 'message') return
|
|
90
|
+
|
|
91
|
+
if (this.listenerCount('message') === 0) this._port.ref()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
_onremovelistener(name) {
|
|
95
|
+
if (name !== 'message') return
|
|
96
|
+
|
|
97
|
+
if (this.listenerCount('message') === 0) this._unref()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// The underlying broadcast channel shared by every named channel on this
|
|
101
|
+
// thread. Set when the worker module is loaded.
|
|
102
|
+
static _channel = null
|
|
103
|
+
}
|
package/lib/errors.js
CHANGED
package/lib/message-channel.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const Channel = require('bare-channel')
|
|
2
|
-
const WorkerState = require('./worker-state')
|
|
3
2
|
const MessagePort = require('./message-port')
|
|
4
3
|
|
|
5
4
|
module.exports = class MessageChannel {
|
|
@@ -10,5 +9,3 @@ module.exports = class MessageChannel {
|
|
|
10
9
|
this.port2 = new MessagePort(channel)
|
|
11
10
|
}
|
|
12
11
|
}
|
|
13
|
-
|
|
14
|
-
if (WorkerState.parent) module.exports = WorkerState.parent.MessageChannel
|
package/lib/message-port.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const EventEmitter = require('bare-events')
|
|
2
2
|
const Channel = require('bare-channel')
|
|
3
|
-
const WorkerState = require('./worker-state')
|
|
4
3
|
const constants = require('./constants')
|
|
5
4
|
const errors = require('./errors')
|
|
6
5
|
|
|
@@ -219,8 +218,6 @@ module.exports = exports = class MessagePort extends EventEmitter {
|
|
|
219
218
|
static _ports = new Set()
|
|
220
219
|
}
|
|
221
220
|
|
|
222
|
-
if (WorkerState.parent) module.exports = WorkerState.parent.MessagePort
|
|
223
|
-
|
|
224
221
|
Bare.on('beforeExit', (exitCode) => {
|
|
225
222
|
for (const port of exports._ports) port._beforeExit(exitCode)
|
|
226
223
|
})
|
package/lib/worker-state.js
CHANGED
|
@@ -1,27 +1,44 @@
|
|
|
1
1
|
const Channel = require('bare-channel')
|
|
2
|
+
const Broadcast = require('bare-broadcast-channel')
|
|
3
|
+
const MessagePort = require('./message-port')
|
|
2
4
|
|
|
3
5
|
const state = Symbol.for('bare.worker.state')
|
|
4
6
|
const kind = Symbol.for('bare.worker.state.kind')
|
|
5
7
|
|
|
6
|
-
class WorkerState {
|
|
8
|
+
module.exports = class WorkerState {
|
|
7
9
|
static get [kind]() {
|
|
8
|
-
return
|
|
10
|
+
return 1 // Compatibility version
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
static get parent() {
|
|
14
|
+
const parent = global[state]
|
|
15
|
+
|
|
16
|
+
if (typeof parent === 'object' && parent !== null && parent[kind] === WorkerState[kind]) {
|
|
17
|
+
return parent
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return null
|
|
21
|
+
}
|
|
14
22
|
|
|
15
|
-
|
|
23
|
+
constructor() {
|
|
24
|
+
const {
|
|
25
|
+
source,
|
|
26
|
+
preloads,
|
|
27
|
+
data,
|
|
28
|
+
environmentData,
|
|
29
|
+
channel: handle,
|
|
30
|
+
broadcast: broadcastHandle
|
|
31
|
+
} = Bare.Thread.self.data
|
|
16
32
|
|
|
17
33
|
this.source = source
|
|
18
34
|
this.preloads = preloads
|
|
19
35
|
this.data = Bare.Thread.self.data = data
|
|
20
36
|
this.environmentData = environmentData
|
|
37
|
+
this.broadcast = Broadcast.from(broadcastHandle)
|
|
21
38
|
|
|
22
|
-
const channel = Channel.from(handle, { interfaces: [
|
|
39
|
+
const channel = Channel.from(handle, { interfaces: [MessagePort] })
|
|
23
40
|
|
|
24
|
-
this.port = new
|
|
41
|
+
this.port = new MessagePort(channel)
|
|
25
42
|
this.port._online()
|
|
26
43
|
|
|
27
44
|
global[state] = this
|
|
@@ -30,24 +47,4 @@ class WorkerState {
|
|
|
30
47
|
get [kind]() {
|
|
31
48
|
return WorkerState[kind]
|
|
32
49
|
}
|
|
33
|
-
|
|
34
|
-
get MessageChannel() {
|
|
35
|
-
return this._MessageChannel
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
get MessagePort() {
|
|
39
|
-
return this._MessagePort
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
module.exports = exports = WorkerState
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
typeof global[state] === 'object' &&
|
|
47
|
-
global[state] !== null &&
|
|
48
|
-
global[state][kind] === WorkerState[kind]
|
|
49
|
-
) {
|
|
50
|
-
exports.parent = global[state]
|
|
51
|
-
} else {
|
|
52
|
-
exports.parent = null
|
|
53
50
|
}
|
package/lib/worker-thread.js
CHANGED
|
@@ -8,17 +8,11 @@ Bare.on('newListener', onnewlistener)
|
|
|
8
8
|
.on('uncaughtException', onerror)
|
|
9
9
|
.on('unhandledRejection', onerror)
|
|
10
10
|
|
|
11
|
-
const cache = Object.create(null)
|
|
12
|
-
|
|
13
11
|
for (const [, source] of state.preloads) {
|
|
14
|
-
Module.load(
|
|
15
|
-
new URL(`bare:/worker/preload-${Math.random().toString(16).slice(2)}.bundle`),
|
|
16
|
-
source,
|
|
17
|
-
{ cache }
|
|
18
|
-
)
|
|
12
|
+
Module.load(new URL(`bare:/worker/preload-${Math.random().toString(16).slice(2)}.bundle`), source)
|
|
19
13
|
}
|
|
20
14
|
|
|
21
|
-
Module.load(new URL('bare:/worker.bundle'), state.source
|
|
15
|
+
Module.load(new URL('bare:/worker.bundle'), state.source)
|
|
22
16
|
|
|
23
17
|
function onnewlistener(name, fn) {
|
|
24
18
|
if (fn === onremovelistener || fn === onerror) return
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bare-worker",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "Higher-level worker threads for JavaScript",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.js",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"./constants": "./lib/constants.js",
|
|
10
10
|
"./errors": "./lib/errors.js",
|
|
11
11
|
"./message-channel": "./lib/message-channel.js",
|
|
12
|
-
"./message-port": "./lib/message-port.js"
|
|
12
|
+
"./message-port": "./lib/message-port.js",
|
|
13
|
+
"./broadcast-channel": "./lib/broadcast-channel.js"
|
|
13
14
|
},
|
|
14
15
|
"files": [
|
|
15
16
|
"index.js",
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
},
|
|
33
34
|
"homepage": "https://github.com/holepunchto/bare-worker#readme",
|
|
34
35
|
"dependencies": {
|
|
36
|
+
"bare-broadcast-channel": "^0.2.0",
|
|
35
37
|
"bare-channel": "^5.1.5",
|
|
36
38
|
"bare-events": "^2.2.1",
|
|
37
39
|
"bare-module": "^6.0.1",
|