angelia.io 3.4.83 → 4.0.85
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/client/index.js +134 -369
- package/client/worker.js +217 -0
- package/package.json +22 -16
- package/readme.md +81 -247
- package/server/Server.js +207 -389
- package/server/Socket.js +69 -81
- package/server/index.js +1 -3
- package/server/rooms/Manager.js +3 -3
- package/server/rooms/Room.js +8 -8
- package/server/utils.js +47 -0
- package/tsconfig.json +23 -0
- package/types/client/index.d.ts +48 -0
- package/types/client/worker.d.ts +1 -0
- package/types/index.d.ts +1 -0
- package/types/server/Server.d.ts +81 -0
- package/types/server/Socket.d.ts +49 -0
- package/types/server/index.d.ts +1 -0
- package/types/server/rooms/Manager.d.ts +9 -0
- package/types/server/rooms/Room.d.ts +17 -0
- package/types/server/rooms/Rooms.d.ts +11 -0
- package/types/server/rooms/constants.d.ts +5 -0
- package/types/server/utils.d.ts +26 -0
- package/publish.bat +0 -1
- package/server/Listeners.js +0 -199
package/client/index.js
CHANGED
|
@@ -1,423 +1,187 @@
|
|
|
1
|
-
|
|
1
|
+
import { ClientWebWorker } from './worker.js'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
constructor() {
|
|
5
|
-
Blob.prototype.toJSON = function () {
|
|
6
|
-
return {
|
|
7
|
-
b: new FileReaderSync().readAsDataURL(this),
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
ArrayBuffer.prototype.toJSON = function () {
|
|
11
|
-
return {
|
|
12
|
-
a: new FileReaderSync().readAsDataURL(this),
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
Object.assign(this, {
|
|
17
|
-
connid: this.generateId(),
|
|
18
|
-
|
|
19
|
-
connected: true,
|
|
20
|
-
reconnect: true,
|
|
21
|
-
isReconnect: false,
|
|
22
|
-
|
|
23
|
-
messages: [],
|
|
24
|
-
buffered: [],
|
|
25
|
-
|
|
26
|
-
onopen: this.onopen.bind(this),
|
|
27
|
-
onclose: this.onclose.bind(this),
|
|
28
|
-
onerror: this.onerror.bind(this),
|
|
29
|
-
onmessage: this.onmessage.bind(this),
|
|
30
|
-
|
|
31
|
-
postMessage: self.postMessage.bind(self),
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
self.onmessage = function (e) {
|
|
35
|
-
switch (e.data[0]) {
|
|
36
|
-
case 'emit': {
|
|
37
|
-
this.emit(e.data[1])
|
|
38
|
-
break
|
|
39
|
-
}
|
|
40
|
-
case 'connect': {
|
|
41
|
-
this.connect(e.data[1])
|
|
42
|
-
break
|
|
43
|
-
}
|
|
44
|
-
case 'disconnect': {
|
|
45
|
-
this.disconnect(e.data[1])
|
|
46
|
-
break
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}.bind(this)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
connect(options) {
|
|
53
|
-
if (
|
|
54
|
-
this.reconnect &&
|
|
55
|
-
(!this.io || this.io.readyState === WebSocket.CLOSED)
|
|
56
|
-
) {
|
|
57
|
-
let url = new URL(options.url)
|
|
58
|
-
|
|
59
|
-
// parms creation
|
|
60
|
-
let params = options.params
|
|
61
|
-
if (!params.connid) {
|
|
62
|
-
params.connid = this.connid
|
|
63
|
-
}
|
|
64
|
-
for (let [k, v] of url.searchParams.entries()) {
|
|
65
|
-
if (!params.hasOwnProperty(k)) {
|
|
66
|
-
params[k] = v
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
url.search = Object.entries(params)
|
|
70
|
-
.filter(([k, v]) => {
|
|
71
|
-
return !(
|
|
72
|
-
k === undefined ||
|
|
73
|
-
k === null ||
|
|
74
|
-
v === undefined ||
|
|
75
|
-
v === null
|
|
76
|
-
)
|
|
77
|
-
})
|
|
78
|
-
.map(
|
|
79
|
-
([k, v]) =>
|
|
80
|
-
encodeURIComponent(k) + '=' + encodeURIComponent(v),
|
|
81
|
-
)
|
|
82
|
-
.join('&')
|
|
3
|
+
const isFunction = v => typeof v === 'function'
|
|
83
4
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
// append buffered messages
|
|
87
|
-
let oURL = url
|
|
88
|
-
if (this.messages.length && !this.isReconnect) {
|
|
89
|
-
url =
|
|
90
|
-
(url.indexOf('?') === -1 ? url + '?' : url + '&') +
|
|
91
|
-
'angelia.io=' +
|
|
92
|
-
encodeURIComponent(JSON.stringify(this.messages))
|
|
93
|
-
if (url.length < 2048) {
|
|
94
|
-
this.messages = []
|
|
95
|
-
} else {
|
|
96
|
-
url = oURL
|
|
97
|
-
}
|
|
98
|
-
} else if (this.messages.length && this.isReconnect) {
|
|
99
|
-
for (let m of this.messages) {
|
|
100
|
-
// dont buffer callbacks, they are gone for new connections
|
|
101
|
-
if (m[0] !== '') this.buffered.push(m)
|
|
102
|
-
}
|
|
103
|
-
this.messages = []
|
|
104
|
-
}
|
|
105
|
-
this.io = new WebSocket(url)
|
|
106
|
-
Object.assign(this.io, {
|
|
107
|
-
onopen: this.onopen,
|
|
108
|
-
onclose: this.onclose,
|
|
109
|
-
onerror: this.onerror,
|
|
110
|
-
onmessage: this.onmessage,
|
|
111
|
-
})
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
disconnect(noReconnect) {
|
|
116
|
-
if (noReconnect) this.reconnect = false
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
this.io &&
|
|
120
|
-
this.io.readyState !== WebSocket.CLOSING &&
|
|
121
|
-
this.io.readyState !== WebSocket.CLOSED
|
|
122
|
-
) {
|
|
123
|
-
this.io.close()
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
onopen() {
|
|
128
|
-
this.connected = true
|
|
129
|
-
this.postMessage(['connected', true])
|
|
130
|
-
|
|
131
|
-
if (this.isReconnect) {
|
|
132
|
-
this.postMessage(['dispatch', 'reconnect'])
|
|
133
|
-
} else {
|
|
134
|
-
this.isReconnect = true
|
|
135
|
-
this.postMessage(['dispatch', 'connect'])
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
this.send()
|
|
139
|
-
}
|
|
140
|
-
onclose(event) {
|
|
141
|
-
switch (event.code) {
|
|
142
|
-
// normal close
|
|
143
|
-
case 1000:
|
|
144
|
-
/*console.log('ws - normal close', event.code, event.reason)*/
|
|
145
|
-
break
|
|
146
|
-
// closed by client
|
|
147
|
-
case 1005:
|
|
148
|
-
/*console.log(
|
|
149
|
-
'ws - we called socket.disconnect()',
|
|
150
|
-
event.code,
|
|
151
|
-
event.reason,
|
|
152
|
-
)*/
|
|
153
|
-
break
|
|
154
|
-
// closed by server or
|
|
155
|
-
// connection never opened and failed to connect
|
|
156
|
-
case 1006: {
|
|
157
|
-
/*console.log(
|
|
158
|
-
'ws - server killed the connection, or we failed to connect to server',
|
|
159
|
-
event.code,
|
|
160
|
-
event.reason,
|
|
161
|
-
)*/
|
|
162
|
-
break
|
|
163
|
-
}
|
|
164
|
-
default: {
|
|
165
|
-
console.log('ws - unknown close', event.code, event.reason)
|
|
166
|
-
break
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (this.connected) {
|
|
171
|
-
this.connected = false
|
|
172
|
-
this.postMessage(['connected', false])
|
|
173
|
-
this.postMessage(['dispatch', 'disconnect'])
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (this.reconnect) this.postMessage(['connect'])
|
|
177
|
-
}
|
|
178
|
-
// this happens when trying to connect while the server or
|
|
179
|
-
// the internet connection is down
|
|
180
|
-
onerror() {
|
|
181
|
-
if (this.connected) {
|
|
182
|
-
this.connected = false
|
|
183
|
-
this.postMessage(['connected', false])
|
|
184
|
-
this.postMessage(['dispatch', 'disconnect'])
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (this.reconnect) this.postMessage(['connect'])
|
|
188
|
-
}
|
|
189
|
-
onmessage(e) {
|
|
190
|
-
if (e.data === '') {
|
|
191
|
-
this.pong()
|
|
192
|
-
} else {
|
|
193
|
-
this.postMessage(['messages', JSON.parse(e.data)])
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
emit(messages) {
|
|
197
|
-
for (let m of messages) {
|
|
198
|
-
this.messages.push(m)
|
|
199
|
-
}
|
|
200
|
-
this.send()
|
|
201
|
-
}
|
|
202
|
-
send() {
|
|
203
|
-
if (this.io && this.io.readyState === WebSocket.OPEN) {
|
|
204
|
-
if (this.messages.length) {
|
|
205
|
-
let messages = this.messages
|
|
206
|
-
this.messages = []
|
|
207
|
-
this.io.send(JSON.stringify(messages))
|
|
208
|
-
}
|
|
209
|
-
if (this.buffered.length) {
|
|
210
|
-
let buffered = this.buffered
|
|
211
|
-
this.buffered = []
|
|
212
|
-
this.io.send(JSON.stringify(buffered))
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
pong() {
|
|
217
|
-
if (this.io && this.io.readyState === WebSocket.OPEN) {
|
|
218
|
-
this.io.send('')
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
generateId() {
|
|
222
|
-
var id = ''
|
|
223
|
-
while (!id) {
|
|
224
|
-
id = Math.random().toString(36).substr(2, 10)
|
|
225
|
-
}
|
|
226
|
-
return id
|
|
227
|
-
}
|
|
228
|
-
}
|
|
5
|
+
const error = console.error
|
|
229
6
|
|
|
230
7
|
export default class Client {
|
|
8
|
+
#callbacks
|
|
9
|
+
#listeners
|
|
10
|
+
#messages
|
|
11
|
+
#postMessage
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Creates a Socket Client Instance
|
|
15
|
+
*
|
|
16
|
+
* @param options {string | {url:string, params?:Function,
|
|
17
|
+
* noConnect?:boolean }}
|
|
18
|
+
*/
|
|
231
19
|
constructor(options) {
|
|
232
|
-
if (
|
|
20
|
+
if (typeof options === 'string') {
|
|
233
21
|
options = {
|
|
234
22
|
url: options,
|
|
235
23
|
}
|
|
236
24
|
}
|
|
237
25
|
|
|
238
|
-
|
|
239
|
-
if (!options.url) {
|
|
240
|
-
options.url = protocol + '://' + location.hostname + ':3001'
|
|
241
|
-
} else if (options.url.indexOf('://') === -1) {
|
|
242
|
-
options.url = protocol + '://' + options.url
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const io = new Worker(
|
|
246
|
-
URL.createObjectURL(
|
|
247
|
-
new Blob(
|
|
248
|
-
["'use strict';new (" + ClientWebWorker.toString() + ')'],
|
|
249
|
-
{
|
|
250
|
-
type: 'application/javascript; charset=utf-8',
|
|
251
|
-
},
|
|
252
|
-
),
|
|
253
|
-
),
|
|
254
|
-
)
|
|
255
|
-
let shouldConnect =
|
|
256
|
-
options.dontConnect !== undefined ? !options.dontConnect : true
|
|
257
|
-
|
|
258
|
-
Object.assign(this, {
|
|
259
|
-
url: options.url,
|
|
260
|
-
params: options.params,
|
|
261
|
-
longLiveFlash: options.longLiveFlash,
|
|
262
|
-
shouldConnect: shouldConnect,
|
|
263
|
-
connected: shouldConnect,
|
|
264
|
-
messages: [],
|
|
265
|
-
listeners: {
|
|
266
|
-
'': [this.oncallback.bind(this)],
|
|
267
|
-
},
|
|
268
|
-
callbacks: [() => {}],
|
|
269
|
-
|
|
270
|
-
onworkermessage: this.onworkermessage.bind(this),
|
|
271
|
-
postMessage: io.postMessage.bind(io),
|
|
272
|
-
disconnect: this.disconnect.bind(this),
|
|
26
|
+
this.options = options
|
|
273
27
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
28
|
+
this.#callbacks = [null]
|
|
29
|
+
this.#listeners = {
|
|
30
|
+
'': [this.#oncallback],
|
|
31
|
+
}
|
|
32
|
+
this.#messages = []
|
|
277
33
|
|
|
278
|
-
|
|
34
|
+
this.connected = false
|
|
279
35
|
|
|
280
36
|
// to force disconnect
|
|
281
37
|
this.on('disconnect', this.disconnect)
|
|
282
38
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
39
|
+
const workerURL = URL.createObjectURL(
|
|
40
|
+
new Blob(['(' + ClientWebWorker + ')()']),
|
|
41
|
+
)
|
|
42
|
+
const io = new Worker(workerURL)
|
|
43
|
+
URL.revokeObjectURL(workerURL)
|
|
44
|
+
|
|
45
|
+
io.onmessage = this.#onworkermessage
|
|
46
|
+
this.#postMessage = io.postMessage.bind(io)
|
|
286
47
|
|
|
287
|
-
|
|
48
|
+
options.noConnect === undefined && this.connect()
|
|
49
|
+
}
|
|
288
50
|
|
|
51
|
+
/** Connect to server */
|
|
289
52
|
connect() {
|
|
290
|
-
this
|
|
53
|
+
this.#postMessage([
|
|
291
54
|
'connect',
|
|
292
55
|
{
|
|
293
|
-
url: this.url,
|
|
56
|
+
url: this.options.url,
|
|
294
57
|
params:
|
|
295
|
-
|
|
296
|
-
? this.params()
|
|
297
|
-
: {},
|
|
58
|
+
(isFunction(this.options.params)
|
|
59
|
+
? this.options.params()
|
|
60
|
+
: this.options.params) || {},
|
|
298
61
|
},
|
|
299
62
|
])
|
|
300
63
|
}
|
|
301
64
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Disconnect from server
|
|
67
|
+
*
|
|
68
|
+
* @param {boolean} noReconnect - To prevent reconnections
|
|
69
|
+
*/
|
|
70
|
+
disconnect = noReconnect => {
|
|
71
|
+
this.#postMessage(['disconnect', noReconnect])
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Listen for data sent by the server
|
|
76
|
+
*
|
|
77
|
+
* @param {string} key
|
|
78
|
+
* @param {any | Function} [valueOrCallback]
|
|
79
|
+
* @param {Function} [callback]
|
|
80
|
+
* @returns {Promise<any> | undefined} When callback is provided it
|
|
81
|
+
* returns a promise
|
|
82
|
+
*/
|
|
83
|
+
emit(key, valueOrCallback, callback) {
|
|
84
|
+
// empty `key` is reserved
|
|
85
|
+
if (key == '') {
|
|
86
|
+
error('`key` cannot be empty')
|
|
316
87
|
} else {
|
|
317
|
-
this
|
|
88
|
+
if (!this.#messages.length) {
|
|
89
|
+
queueMicrotask(() => queueMicrotask(this.#send))
|
|
90
|
+
}
|
|
91
|
+
if (callback) {
|
|
92
|
+
const [i, promise] = this.#callback(callback)
|
|
93
|
+
this.#messages.push([key, valueOrCallback, i])
|
|
94
|
+
return promise
|
|
95
|
+
} else if (isFunction(valueOrCallback)) {
|
|
96
|
+
const [i, promise] = this.#callback(valueOrCallback)
|
|
97
|
+
this.#messages.push([key, 0, i])
|
|
98
|
+
return promise
|
|
99
|
+
} else if (valueOrCallback !== undefined) {
|
|
100
|
+
this.#messages.push([key, valueOrCallback])
|
|
101
|
+
} else {
|
|
102
|
+
this.#messages.push([key])
|
|
103
|
+
}
|
|
318
104
|
}
|
|
319
105
|
}
|
|
320
106
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Listen for data sent by the server
|
|
109
|
+
*
|
|
110
|
+
* @param {string} key
|
|
111
|
+
* @param {Function} value
|
|
112
|
+
* @returns () => void | undefined - `off` function to remove the
|
|
113
|
+
* listener
|
|
114
|
+
*/
|
|
115
|
+
on(key, value) {
|
|
116
|
+
// empty `key` is reserved
|
|
117
|
+
if (key == '') {
|
|
118
|
+
error('`key` cannot be empty')
|
|
326
119
|
} else {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
'", callback) key and callback cannot be empty',
|
|
331
|
-
)
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
off(k, v) {
|
|
335
|
-
if (!this.listeners[k]) {
|
|
336
|
-
console.error(
|
|
337
|
-
'ws - socket.off("' + k + '", callback)',
|
|
338
|
-
k,
|
|
339
|
-
'key not found',
|
|
340
|
-
)
|
|
341
|
-
} else {
|
|
342
|
-
let i = this.listeners[k].indexOf(v)
|
|
343
|
-
if (i === -1) {
|
|
344
|
-
console.error(
|
|
345
|
-
'ws - socket.off("' + k + '", callback)',
|
|
346
|
-
v,
|
|
347
|
-
'callback not found',
|
|
348
|
-
)
|
|
349
|
-
} else {
|
|
350
|
-
this.listeners[k].splice(i, 1)
|
|
351
|
-
}
|
|
120
|
+
this.#listeners[key] = this.#listeners[key] || []
|
|
121
|
+
this.#listeners[key].push(value)
|
|
122
|
+
return () => this.#off(key, value)
|
|
352
123
|
}
|
|
353
124
|
}
|
|
354
125
|
|
|
355
126
|
// PRIVATE API
|
|
356
127
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
e[1],
|
|
390
|
-
e[2]
|
|
391
|
-
? (...d) => {
|
|
392
|
-
this.emit('', [e[2], d])
|
|
393
|
-
}
|
|
394
|
-
: null,
|
|
395
|
-
)
|
|
396
|
-
}
|
|
128
|
+
#off(k, v) {
|
|
129
|
+
const i = this.#listeners[k].indexOf(v)
|
|
130
|
+
if (i !== -1) {
|
|
131
|
+
this.#listeners[k].splice(i, 1)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** @returns {[number, Promise<any>]} */
|
|
136
|
+
#callback(c) {
|
|
137
|
+
let resolve
|
|
138
|
+
const promise = new Promise(r => (resolve = r))
|
|
139
|
+
const i = this.#callbacks.length
|
|
140
|
+
this.#callbacks[i] = args => resolve(c(...args))
|
|
141
|
+
return [i, promise]
|
|
142
|
+
}
|
|
143
|
+
#oncallback = d => {
|
|
144
|
+
this.#callbacks[d[0]](d[1])
|
|
145
|
+
this.#callbacks[d[0]] = null
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#dispatch(d) {
|
|
149
|
+
for (const e of d) {
|
|
150
|
+
if (this.#listeners[e[0]]) {
|
|
151
|
+
for (const fn of this.#listeners[e[0]]) {
|
|
152
|
+
fn(
|
|
153
|
+
e[1],
|
|
154
|
+
e[2]
|
|
155
|
+
? (...d) => {
|
|
156
|
+
this.emit('', [e[2], d])
|
|
157
|
+
}
|
|
158
|
+
: undefined,
|
|
159
|
+
)
|
|
397
160
|
}
|
|
398
161
|
}
|
|
399
162
|
}
|
|
400
163
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
this
|
|
164
|
+
|
|
165
|
+
#send = () => {
|
|
166
|
+
if (this.#messages.length) {
|
|
167
|
+
const messages = this.#messages
|
|
168
|
+
this.#messages = []
|
|
169
|
+
this.#postMessage(['emit', messages])
|
|
406
170
|
}
|
|
407
171
|
}
|
|
408
172
|
|
|
409
|
-
onworkermessage
|
|
173
|
+
#onworkermessage = e => {
|
|
410
174
|
switch (e.data[0]) {
|
|
411
175
|
case 'messages': {
|
|
412
|
-
this
|
|
176
|
+
this.#dispatch(e.data[1])
|
|
413
177
|
break
|
|
414
178
|
}
|
|
415
179
|
case 'dispatch': {
|
|
416
|
-
this
|
|
180
|
+
this.#dispatch([[e.data[1], e.data[2]]])
|
|
417
181
|
break
|
|
418
182
|
}
|
|
419
183
|
case 'connect': {
|
|
420
|
-
|
|
184
|
+
this.connect()
|
|
421
185
|
break
|
|
422
186
|
}
|
|
423
187
|
case 'connected': {
|
|
@@ -426,6 +190,7 @@ export default class Client {
|
|
|
426
190
|
}
|
|
427
191
|
}
|
|
428
192
|
}
|
|
193
|
+
|
|
429
194
|
decode(s) {
|
|
430
195
|
if (s.b) {
|
|
431
196
|
return fetch(s.b).then(r => r.blob())
|