socket.io-client-v2 2.5.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/LICENSE +22 -0
- package/README.md +67 -0
- package/dist/socket.io.dev.js +6667 -0
- package/dist/socket.io.dev.js.map +1 -0
- package/dist/socket.io.js +9 -0
- package/dist/socket.io.js.map +1 -0
- package/dist/socket.io.slim.dev.js +5901 -0
- package/dist/socket.io.slim.dev.js.map +1 -0
- package/dist/socket.io.slim.js +9 -0
- package/dist/socket.io.slim.js.map +1 -0
- package/lib/index.js +94 -0
- package/lib/manager.js +577 -0
- package/lib/on.js +24 -0
- package/lib/socket.js +438 -0
- package/lib/url.js +75 -0
- package/package.json +87 -0
package/lib/socket.js
ADDED
@@ -0,0 +1,438 @@
|
|
1
|
+
|
2
|
+
/**
|
3
|
+
* Module dependencies.
|
4
|
+
*/
|
5
|
+
|
6
|
+
var parser = require('socket.io-parser');
|
7
|
+
var Emitter = require('component-emitter');
|
8
|
+
var toArray = require('to-array');
|
9
|
+
var on = require('./on');
|
10
|
+
var bind = require('component-bind');
|
11
|
+
var debug = require('debug')('socket.io-client:socket');
|
12
|
+
var parseqs = require('parseqs');
|
13
|
+
var hasBin = require('has-binary2');
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Module exports.
|
17
|
+
*/
|
18
|
+
|
19
|
+
module.exports = exports = Socket;
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Internal events (blacklisted).
|
23
|
+
* These events can't be emitted by the user.
|
24
|
+
*
|
25
|
+
* @api private
|
26
|
+
*/
|
27
|
+
|
28
|
+
var events = {
|
29
|
+
connect: 1,
|
30
|
+
connect_error: 1,
|
31
|
+
connect_timeout: 1,
|
32
|
+
connecting: 1,
|
33
|
+
disconnect: 1,
|
34
|
+
error: 1,
|
35
|
+
reconnect: 1,
|
36
|
+
reconnect_attempt: 1,
|
37
|
+
reconnect_failed: 1,
|
38
|
+
reconnect_error: 1,
|
39
|
+
reconnecting: 1,
|
40
|
+
ping: 1,
|
41
|
+
pong: 1
|
42
|
+
};
|
43
|
+
|
44
|
+
/**
|
45
|
+
* Shortcut to `Emitter#emit`.
|
46
|
+
*/
|
47
|
+
|
48
|
+
var emit = Emitter.prototype.emit;
|
49
|
+
|
50
|
+
/**
|
51
|
+
* `Socket` constructor.
|
52
|
+
*
|
53
|
+
* @api public
|
54
|
+
*/
|
55
|
+
|
56
|
+
function Socket (io, nsp, opts) {
|
57
|
+
this.io = io;
|
58
|
+
this.nsp = nsp;
|
59
|
+
this.json = this; // compat
|
60
|
+
this.ids = 0;
|
61
|
+
this.acks = {};
|
62
|
+
this.receiveBuffer = [];
|
63
|
+
this.sendBuffer = [];
|
64
|
+
this.connected = false;
|
65
|
+
this.disconnected = true;
|
66
|
+
this.flags = {};
|
67
|
+
if (opts && opts.query) {
|
68
|
+
this.query = opts.query;
|
69
|
+
}
|
70
|
+
if (this.io.autoConnect) this.open();
|
71
|
+
}
|
72
|
+
|
73
|
+
/**
|
74
|
+
* Mix in `Emitter`.
|
75
|
+
*/
|
76
|
+
|
77
|
+
Emitter(Socket.prototype);
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Subscribe to open, close and packet events
|
81
|
+
*
|
82
|
+
* @api private
|
83
|
+
*/
|
84
|
+
|
85
|
+
Socket.prototype.subEvents = function () {
|
86
|
+
if (this.subs) return;
|
87
|
+
|
88
|
+
var io = this.io;
|
89
|
+
this.subs = [
|
90
|
+
on(io, 'open', bind(this, 'onopen')),
|
91
|
+
on(io, 'packet', bind(this, 'onpacket')),
|
92
|
+
on(io, 'close', bind(this, 'onclose'))
|
93
|
+
];
|
94
|
+
};
|
95
|
+
|
96
|
+
/**
|
97
|
+
* "Opens" the socket.
|
98
|
+
*
|
99
|
+
* @api public
|
100
|
+
*/
|
101
|
+
|
102
|
+
Socket.prototype.open =
|
103
|
+
Socket.prototype.connect = function () {
|
104
|
+
if (this.connected) return this;
|
105
|
+
|
106
|
+
this.subEvents();
|
107
|
+
if (!this.io.reconnecting) this.io.open(); // ensure open
|
108
|
+
if ('open' === this.io.readyState) this.onopen();
|
109
|
+
this.emit('connecting');
|
110
|
+
return this;
|
111
|
+
};
|
112
|
+
|
113
|
+
/**
|
114
|
+
* Sends a `message` event.
|
115
|
+
*
|
116
|
+
* @return {Socket} self
|
117
|
+
* @api public
|
118
|
+
*/
|
119
|
+
|
120
|
+
Socket.prototype.send = function () {
|
121
|
+
var args = toArray(arguments);
|
122
|
+
args.unshift('message');
|
123
|
+
this.emit.apply(this, args);
|
124
|
+
return this;
|
125
|
+
};
|
126
|
+
|
127
|
+
/**
|
128
|
+
* Override `emit`.
|
129
|
+
* If the event is in `events`, it's emitted normally.
|
130
|
+
*
|
131
|
+
* @param {String} event name
|
132
|
+
* @return {Socket} self
|
133
|
+
* @api public
|
134
|
+
*/
|
135
|
+
|
136
|
+
Socket.prototype.emit = function (ev) {
|
137
|
+
if (events.hasOwnProperty(ev)) {
|
138
|
+
emit.apply(this, arguments);
|
139
|
+
return this;
|
140
|
+
}
|
141
|
+
|
142
|
+
var args = toArray(arguments);
|
143
|
+
var packet = {
|
144
|
+
type: (this.flags.binary !== undefined ? this.flags.binary : hasBin(args)) ? parser.BINARY_EVENT : parser.EVENT,
|
145
|
+
data: args
|
146
|
+
};
|
147
|
+
|
148
|
+
packet.options = {};
|
149
|
+
packet.options.compress = !this.flags || false !== this.flags.compress;
|
150
|
+
|
151
|
+
// event ack callback
|
152
|
+
if ('function' === typeof args[args.length - 1]) {
|
153
|
+
debug('emitting packet with ack id %d', this.ids);
|
154
|
+
this.acks[this.ids] = args.pop();
|
155
|
+
packet.id = this.ids++;
|
156
|
+
}
|
157
|
+
|
158
|
+
if (this.connected) {
|
159
|
+
this.packet(packet);
|
160
|
+
} else {
|
161
|
+
this.sendBuffer.push(packet);
|
162
|
+
}
|
163
|
+
|
164
|
+
this.flags = {};
|
165
|
+
|
166
|
+
return this;
|
167
|
+
};
|
168
|
+
|
169
|
+
/**
|
170
|
+
* Sends a packet.
|
171
|
+
*
|
172
|
+
* @param {Object} packet
|
173
|
+
* @api private
|
174
|
+
*/
|
175
|
+
|
176
|
+
Socket.prototype.packet = function (packet) {
|
177
|
+
packet.nsp = this.nsp;
|
178
|
+
this.io.packet(packet);
|
179
|
+
};
|
180
|
+
|
181
|
+
/**
|
182
|
+
* Called upon engine `open`.
|
183
|
+
*
|
184
|
+
* @api private
|
185
|
+
*/
|
186
|
+
|
187
|
+
Socket.prototype.onopen = function () {
|
188
|
+
debug('transport is open - connecting');
|
189
|
+
|
190
|
+
// write connect packet if necessary
|
191
|
+
if ('/' !== this.nsp) {
|
192
|
+
if (this.query) {
|
193
|
+
var query = typeof this.query === 'object' ? parseqs.encode(this.query) : this.query;
|
194
|
+
debug('sending connect packet with query %s', query);
|
195
|
+
this.packet({type: parser.CONNECT, query: query});
|
196
|
+
} else {
|
197
|
+
this.packet({type: parser.CONNECT});
|
198
|
+
}
|
199
|
+
}
|
200
|
+
};
|
201
|
+
|
202
|
+
/**
|
203
|
+
* Called upon engine `close`.
|
204
|
+
*
|
205
|
+
* @param {String} reason
|
206
|
+
* @api private
|
207
|
+
*/
|
208
|
+
|
209
|
+
Socket.prototype.onclose = function (reason) {
|
210
|
+
debug('close (%s)', reason);
|
211
|
+
this.connected = false;
|
212
|
+
this.disconnected = true;
|
213
|
+
delete this.id;
|
214
|
+
this.emit('disconnect', reason);
|
215
|
+
};
|
216
|
+
|
217
|
+
/**
|
218
|
+
* Called with socket packet.
|
219
|
+
*
|
220
|
+
* @param {Object} packet
|
221
|
+
* @api private
|
222
|
+
*/
|
223
|
+
|
224
|
+
Socket.prototype.onpacket = function (packet) {
|
225
|
+
var sameNamespace = packet.nsp === this.nsp;
|
226
|
+
var rootNamespaceError = packet.type === parser.ERROR && packet.nsp === '/';
|
227
|
+
|
228
|
+
if (!sameNamespace && !rootNamespaceError) return;
|
229
|
+
|
230
|
+
switch (packet.type) {
|
231
|
+
case parser.CONNECT:
|
232
|
+
this.onconnect();
|
233
|
+
break;
|
234
|
+
|
235
|
+
case parser.EVENT:
|
236
|
+
this.onevent(packet);
|
237
|
+
break;
|
238
|
+
|
239
|
+
case parser.BINARY_EVENT:
|
240
|
+
this.onevent(packet);
|
241
|
+
break;
|
242
|
+
|
243
|
+
case parser.ACK:
|
244
|
+
this.onack(packet);
|
245
|
+
break;
|
246
|
+
|
247
|
+
case parser.BINARY_ACK:
|
248
|
+
this.onack(packet);
|
249
|
+
break;
|
250
|
+
|
251
|
+
case parser.DISCONNECT:
|
252
|
+
this.ondisconnect();
|
253
|
+
break;
|
254
|
+
|
255
|
+
case parser.ERROR:
|
256
|
+
this.emit('error', packet.data);
|
257
|
+
break;
|
258
|
+
}
|
259
|
+
};
|
260
|
+
|
261
|
+
/**
|
262
|
+
* Called upon a server event.
|
263
|
+
*
|
264
|
+
* @param {Object} packet
|
265
|
+
* @api private
|
266
|
+
*/
|
267
|
+
|
268
|
+
Socket.prototype.onevent = function (packet) {
|
269
|
+
var args = packet.data || [];
|
270
|
+
debug('emitting event %j', args);
|
271
|
+
|
272
|
+
if (null != packet.id) {
|
273
|
+
debug('attaching ack callback to event');
|
274
|
+
args.push(this.ack(packet.id));
|
275
|
+
}
|
276
|
+
|
277
|
+
if (this.connected) {
|
278
|
+
emit.apply(this, args);
|
279
|
+
} else {
|
280
|
+
this.receiveBuffer.push(args);
|
281
|
+
}
|
282
|
+
};
|
283
|
+
|
284
|
+
/**
|
285
|
+
* Produces an ack callback to emit with an event.
|
286
|
+
*
|
287
|
+
* @api private
|
288
|
+
*/
|
289
|
+
|
290
|
+
Socket.prototype.ack = function (id) {
|
291
|
+
var self = this;
|
292
|
+
var sent = false;
|
293
|
+
return function () {
|
294
|
+
// prevent double callbacks
|
295
|
+
if (sent) return;
|
296
|
+
sent = true;
|
297
|
+
var args = toArray(arguments);
|
298
|
+
debug('sending ack %j', args);
|
299
|
+
|
300
|
+
self.packet({
|
301
|
+
type: hasBin(args) ? parser.BINARY_ACK : parser.ACK,
|
302
|
+
id: id,
|
303
|
+
data: args
|
304
|
+
});
|
305
|
+
};
|
306
|
+
};
|
307
|
+
|
308
|
+
/**
|
309
|
+
* Called upon a server acknowlegement.
|
310
|
+
*
|
311
|
+
* @param {Object} packet
|
312
|
+
* @api private
|
313
|
+
*/
|
314
|
+
|
315
|
+
Socket.prototype.onack = function (packet) {
|
316
|
+
var ack = this.acks[packet.id];
|
317
|
+
if ('function' === typeof ack) {
|
318
|
+
debug('calling ack %s with %j', packet.id, packet.data);
|
319
|
+
ack.apply(this, packet.data);
|
320
|
+
delete this.acks[packet.id];
|
321
|
+
} else {
|
322
|
+
debug('bad ack %s', packet.id);
|
323
|
+
}
|
324
|
+
};
|
325
|
+
|
326
|
+
/**
|
327
|
+
* Called upon server connect.
|
328
|
+
*
|
329
|
+
* @api private
|
330
|
+
*/
|
331
|
+
|
332
|
+
Socket.prototype.onconnect = function () {
|
333
|
+
this.connected = true;
|
334
|
+
this.disconnected = false;
|
335
|
+
this.emitBuffered();
|
336
|
+
this.emit('connect');
|
337
|
+
};
|
338
|
+
|
339
|
+
/**
|
340
|
+
* Emit buffered events (received and emitted).
|
341
|
+
*
|
342
|
+
* @api private
|
343
|
+
*/
|
344
|
+
|
345
|
+
Socket.prototype.emitBuffered = function () {
|
346
|
+
var i;
|
347
|
+
for (i = 0; i < this.receiveBuffer.length; i++) {
|
348
|
+
emit.apply(this, this.receiveBuffer[i]);
|
349
|
+
}
|
350
|
+
this.receiveBuffer = [];
|
351
|
+
|
352
|
+
for (i = 0; i < this.sendBuffer.length; i++) {
|
353
|
+
this.packet(this.sendBuffer[i]);
|
354
|
+
}
|
355
|
+
this.sendBuffer = [];
|
356
|
+
};
|
357
|
+
|
358
|
+
/**
|
359
|
+
* Called upon server disconnect.
|
360
|
+
*
|
361
|
+
* @api private
|
362
|
+
*/
|
363
|
+
|
364
|
+
Socket.prototype.ondisconnect = function () {
|
365
|
+
debug('server disconnect (%s)', this.nsp);
|
366
|
+
this.destroy();
|
367
|
+
this.onclose('io server disconnect');
|
368
|
+
};
|
369
|
+
|
370
|
+
/**
|
371
|
+
* Called upon forced client/server side disconnections,
|
372
|
+
* this method ensures the manager stops tracking us and
|
373
|
+
* that reconnections don't get triggered for this.
|
374
|
+
*
|
375
|
+
* @api private.
|
376
|
+
*/
|
377
|
+
|
378
|
+
Socket.prototype.destroy = function () {
|
379
|
+
if (this.subs) {
|
380
|
+
// clean subscriptions to avoid reconnections
|
381
|
+
for (var i = 0; i < this.subs.length; i++) {
|
382
|
+
this.subs[i].destroy();
|
383
|
+
}
|
384
|
+
this.subs = null;
|
385
|
+
}
|
386
|
+
|
387
|
+
this.io.destroy(this);
|
388
|
+
};
|
389
|
+
|
390
|
+
/**
|
391
|
+
* Disconnects the socket manually.
|
392
|
+
*
|
393
|
+
* @return {Socket} self
|
394
|
+
* @api public
|
395
|
+
*/
|
396
|
+
|
397
|
+
Socket.prototype.close =
|
398
|
+
Socket.prototype.disconnect = function () {
|
399
|
+
if (this.connected) {
|
400
|
+
debug('performing disconnect (%s)', this.nsp);
|
401
|
+
this.packet({ type: parser.DISCONNECT });
|
402
|
+
}
|
403
|
+
|
404
|
+
// remove socket from pool
|
405
|
+
this.destroy();
|
406
|
+
|
407
|
+
if (this.connected) {
|
408
|
+
// fire events
|
409
|
+
this.onclose('io client disconnect');
|
410
|
+
}
|
411
|
+
return this;
|
412
|
+
};
|
413
|
+
|
414
|
+
/**
|
415
|
+
* Sets the compress flag.
|
416
|
+
*
|
417
|
+
* @param {Boolean} if `true`, compresses the sending data
|
418
|
+
* @return {Socket} self
|
419
|
+
* @api public
|
420
|
+
*/
|
421
|
+
|
422
|
+
Socket.prototype.compress = function (compress) {
|
423
|
+
this.flags.compress = compress;
|
424
|
+
return this;
|
425
|
+
};
|
426
|
+
|
427
|
+
/**
|
428
|
+
* Sets the binary flag
|
429
|
+
*
|
430
|
+
* @param {Boolean} whether the emitted data contains binary
|
431
|
+
* @return {Socket} self
|
432
|
+
* @api public
|
433
|
+
*/
|
434
|
+
|
435
|
+
Socket.prototype.binary = function (binary) {
|
436
|
+
this.flags.binary = binary;
|
437
|
+
return this;
|
438
|
+
};
|
package/lib/url.js
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
|
2
|
+
/**
|
3
|
+
* Module dependencies.
|
4
|
+
*/
|
5
|
+
|
6
|
+
var parseuri = require('parseuri');
|
7
|
+
var debug = require('debug')('socket.io-client:url');
|
8
|
+
|
9
|
+
/**
|
10
|
+
* Module exports.
|
11
|
+
*/
|
12
|
+
|
13
|
+
module.exports = url;
|
14
|
+
|
15
|
+
/**
|
16
|
+
* URL parser.
|
17
|
+
*
|
18
|
+
* @param {String} url
|
19
|
+
* @param {Object} An object meant to mimic window.location.
|
20
|
+
* Defaults to window.location.
|
21
|
+
* @api public
|
22
|
+
*/
|
23
|
+
|
24
|
+
function url (uri, loc) {
|
25
|
+
var obj = uri;
|
26
|
+
|
27
|
+
// default to window.location
|
28
|
+
loc = loc || (typeof location !== 'undefined' && location);
|
29
|
+
if (null == uri) uri = loc.protocol + '//' + loc.host;
|
30
|
+
|
31
|
+
// relative path support
|
32
|
+
if ('string' === typeof uri) {
|
33
|
+
if ('/' === uri.charAt(0)) {
|
34
|
+
if ('/' === uri.charAt(1)) {
|
35
|
+
uri = loc.protocol + uri;
|
36
|
+
} else {
|
37
|
+
uri = loc.host + uri;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
if (!/^(https?|wss?):\/\//.test(uri)) {
|
42
|
+
debug('protocol-less url %s', uri);
|
43
|
+
if ('undefined' !== typeof loc) {
|
44
|
+
uri = loc.protocol + '//' + uri;
|
45
|
+
} else {
|
46
|
+
uri = 'https://' + uri;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
// parse
|
51
|
+
debug('parse %s', uri);
|
52
|
+
obj = parseuri(uri);
|
53
|
+
}
|
54
|
+
|
55
|
+
// make sure we treat `localhost:80` and `localhost` equally
|
56
|
+
if (!obj.port) {
|
57
|
+
if (/^(http|ws)$/.test(obj.protocol)) {
|
58
|
+
obj.port = '80';
|
59
|
+
} else if (/^(http|ws)s$/.test(obj.protocol)) {
|
60
|
+
obj.port = '443';
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
obj.path = obj.path || '/';
|
65
|
+
|
66
|
+
var ipv6 = obj.host.indexOf(':') !== -1;
|
67
|
+
var host = ipv6 ? '[' + obj.host + ']' : obj.host;
|
68
|
+
|
69
|
+
// define unique id
|
70
|
+
obj.id = obj.protocol + '://' + host + ':' + obj.port;
|
71
|
+
// define href
|
72
|
+
obj.href = obj.protocol + '://' + host + (loc && loc.port === obj.port ? '' : (':' + obj.port));
|
73
|
+
|
74
|
+
return obj;
|
75
|
+
}
|
package/package.json
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
{
|
2
|
+
"name": "socket.io-client-v2",
|
3
|
+
"version": "2.5.0",
|
4
|
+
"keywords": [
|
5
|
+
"realtime",
|
6
|
+
"framework",
|
7
|
+
"websocket",
|
8
|
+
"tcp",
|
9
|
+
"events",
|
10
|
+
"client"
|
11
|
+
],
|
12
|
+
"main": "./lib/index",
|
13
|
+
"files": [
|
14
|
+
"lib/",
|
15
|
+
"dist/"
|
16
|
+
],
|
17
|
+
"dependencies": {
|
18
|
+
"backo2": "1.0.2",
|
19
|
+
"component-bind": "1.0.0",
|
20
|
+
"component-emitter": "~1.3.0",
|
21
|
+
"debug": "~3.1.0",
|
22
|
+
"engine.io-client": "~3.5.0",
|
23
|
+
"has-binary2": "~1.0.2",
|
24
|
+
"indexof": "0.0.1",
|
25
|
+
"parseqs": "0.0.6",
|
26
|
+
"parseuri": "0.0.6",
|
27
|
+
"socket.io-parser": "~3.3.0",
|
28
|
+
"to-array": "0.1.4"
|
29
|
+
},
|
30
|
+
"devDependencies": {
|
31
|
+
"babel-core": "^6.24.1",
|
32
|
+
"babel-eslint": "4.1.7",
|
33
|
+
"babel-loader": "7.0.0",
|
34
|
+
"babel-preset-es2015": "6.24.1",
|
35
|
+
"base64-arraybuffer": "^0.1.5",
|
36
|
+
"concat-stream": "^1.6.0",
|
37
|
+
"derequire": "^2.0.6",
|
38
|
+
"eslint-config-standard": "4.4.0",
|
39
|
+
"eslint-plugin-standard": "1.3.1",
|
40
|
+
"expect.js": "0.3.1",
|
41
|
+
"gulp": "^3.9.1",
|
42
|
+
"gulp-eslint": "1.1.1",
|
43
|
+
"gulp-file": "^0.3.0",
|
44
|
+
"gulp-istanbul": "^1.1.1",
|
45
|
+
"gulp-mocha": "^4.3.1",
|
46
|
+
"gulp-task-listing": "1.0.1",
|
47
|
+
"has-cors": "^1.1.0",
|
48
|
+
"imports-loader": "^0.7.1",
|
49
|
+
"istanbul": "^0.4.5",
|
50
|
+
"mocha": "^3.3.0",
|
51
|
+
"socket.io": "2.3.0",
|
52
|
+
"socket.io-browsers": "^1.0.0",
|
53
|
+
"strip-loader": "0.1.2",
|
54
|
+
"text-blob-builder": "0.0.1",
|
55
|
+
"webpack-merge": "4.1.2",
|
56
|
+
"webpack-stream": "3.2.0",
|
57
|
+
"zuul": "~3.11.1",
|
58
|
+
"zuul-builder-webpack": "^1.2.0",
|
59
|
+
"zuul-ngrok": "4.0.0"
|
60
|
+
},
|
61
|
+
"scripts": {
|
62
|
+
"test": "gulp test"
|
63
|
+
},
|
64
|
+
"contributors": [
|
65
|
+
{
|
66
|
+
"name": "Guillermo Rauch",
|
67
|
+
"email": "rauchg@gmail.com"
|
68
|
+
},
|
69
|
+
{
|
70
|
+
"name": "Arnout Kazemier",
|
71
|
+
"email": "info@3rd-eden.com"
|
72
|
+
},
|
73
|
+
{
|
74
|
+
"name": "Vladimir Dronnikov",
|
75
|
+
"email": "dronnikov@gmail.com"
|
76
|
+
},
|
77
|
+
{
|
78
|
+
"name": "Einar Otto Stangvik",
|
79
|
+
"email": "einaros@gmail.com"
|
80
|
+
}
|
81
|
+
],
|
82
|
+
"repository": {
|
83
|
+
"type": "git",
|
84
|
+
"url": "https://github.com/Automattic/socket.io-client.git"
|
85
|
+
},
|
86
|
+
"license": "MIT"
|
87
|
+
}
|