@libp2p/mplex 11.0.46 → 11.0.47-0f07e3df5

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/src/mplex.ts CHANGED
@@ -1,27 +1,16 @@
1
- import { TooManyOutboundProtocolStreamsError, MuxerClosedError } from '@libp2p/interface'
2
- import { closeSource } from '@libp2p/utils/close-source'
3
- import { RateLimiter } from '@libp2p/utils/rate-limiter'
4
- import { pipe } from 'it-pipe'
5
- import { pushable } from 'it-pushable'
1
+ import { MuxerClosedError } from '@libp2p/interface'
2
+ import { RateLimiter, AbstractStreamMuxer } from '@libp2p/utils'
6
3
  import { toString as uint8ArrayToString } from 'uint8arrays'
7
- import { Decoder } from './decode.js'
8
- import { encode } from './encode.js'
9
- import { StreamInputBufferError } from './errors.js'
4
+ import { Decoder, MAX_MSG_QUEUE_SIZE, MAX_MSG_SIZE } from './decode.js'
10
5
  import { MessageTypes, MessageTypeNames } from './message-types.js'
11
6
  import { createStream } from './stream.js'
12
7
  import type { MplexInit } from './index.js'
13
8
  import type { Message } from './message-types.js'
14
9
  import type { MplexStream } from './stream.js'
15
- import type { AbortOptions, ComponentLogger, Logger, Stream, StreamMuxer, StreamMuxerInit } from '@libp2p/interface'
16
- import type { Pushable } from 'it-pushable'
17
- import type { Sink, Source } from 'it-stream-types'
10
+ import type { CreateStreamOptions, MultiaddrConnection, MessageStreamDirection } from '@libp2p/interface'
18
11
  import type { Uint8ArrayList } from 'uint8arraylist'
19
12
 
20
- const MAX_STREAMS_INBOUND_STREAMS_PER_CONNECTION = 1024
21
- const MAX_STREAMS_OUTBOUND_STREAMS_PER_CONNECTION = 1024
22
- const MAX_STREAM_BUFFER_SIZE = 1024 * 1024 * 4 // 4MB
23
13
  const DISCONNECT_THRESHOLD = 5
24
- const CLOSE_TIMEOUT = 500
25
14
 
26
15
  function printMessage (msg: Message): any {
27
16
  const output: any = {
@@ -30,93 +19,34 @@ function printMessage (msg: Message): any {
30
19
  }
31
20
 
32
21
  if (msg.type === MessageTypes.NEW_STREAM) {
33
- output.data = uint8ArrayToString(msg.data instanceof Uint8Array ? msg.data : msg.data.subarray())
22
+ output.data = uint8ArrayToString(msg.data.subarray())
34
23
  }
35
24
 
36
25
  if (msg.type === MessageTypes.MESSAGE_INITIATOR || msg.type === MessageTypes.MESSAGE_RECEIVER) {
37
- output.data = uint8ArrayToString(msg.data instanceof Uint8Array ? msg.data : msg.data.subarray(), 'base16')
26
+ output.data = uint8ArrayToString(msg.data.subarray(), 'base16')
38
27
  }
39
28
 
40
29
  return output
41
30
  }
42
31
 
43
- export interface MplexComponents {
44
- logger: ComponentLogger
45
- }
46
-
47
- interface MplexStreamMuxerInit extends MplexInit, StreamMuxerInit {
48
- /**
49
- * The default timeout to use in ms when shutting down the muxer.
50
- */
51
- closeTimeout?: number
52
- }
53
-
54
- export class MplexStreamMuxer implements StreamMuxer {
55
- public protocol = '/mplex/6.7.0'
56
-
57
- public sink: Sink<Source<Uint8ArrayList | Uint8Array>, Promise<void>>
58
- public source: AsyncGenerator<Uint8ArrayList | Uint8Array>
59
-
60
- private readonly log: Logger
32
+ export class MplexStreamMuxer extends AbstractStreamMuxer<MplexStream> {
61
33
  private _streamId: number
62
- private readonly _streams: { initiators: Map<number, MplexStream>, receivers: Map<number, MplexStream> }
63
- private readonly _init: MplexStreamMuxerInit
64
- private readonly _source: Pushable<Message>
65
- private readonly closeController: AbortController
66
34
  private readonly rateLimiter: RateLimiter
67
- private readonly closeTimeout: number
68
- private readonly logger: ComponentLogger
69
-
70
- constructor (components: MplexComponents, init?: MplexStreamMuxerInit) {
71
- init = init ?? {}
72
-
73
- this.log = components.logger.forComponent('libp2p:mplex')
74
- this.logger = components.logger
75
- this._streamId = 0
76
- this._streams = {
77
- /**
78
- * Stream to ids map
79
- */
80
- initiators: new Map<number, MplexStream>(),
81
- /**
82
- * Stream to ids map
83
- */
84
- receivers: new Map<number, MplexStream>()
85
- }
86
- this._init = init
87
- this.closeTimeout = init.closeTimeout ?? CLOSE_TIMEOUT
88
-
89
- /**
90
- * An iterable sink
91
- */
92
- this.sink = this._createSink()
93
-
94
- /**
95
- * An iterable source
96
- */
97
- this._source = pushable<Message>({
98
- objectMode: true,
99
- onEnd: (): void => {
100
- // the source has ended, we can't write any more messages to gracefully
101
- // close streams so all we can do is destroy them
102
- for (const stream of this._streams.initiators.values()) {
103
- stream.destroy()
104
- }
105
-
106
- for (const stream of this._streams.receivers.values()) {
107
- stream.destroy()
108
- }
109
- }
35
+ private readonly maxMessageSize: number
36
+ private readonly maxUnprocessedMessageQueueSize: number
37
+ private readonly decoder: Decoder
38
+
39
+ constructor (maConn: MultiaddrConnection, init: MplexInit) {
40
+ super(maConn, {
41
+ ...init,
42
+ protocol: '/mplex/6.7.0',
43
+ name: 'mplex'
110
44
  })
111
- this.source = pipe(
112
- this._source,
113
- source => encode(source)
114
- )
115
45
 
116
- /**
117
- * Close controller
118
- */
119
- this.closeController = new AbortController()
46
+ this._streamId = 0
47
+ this.maxMessageSize = init.maxMessageSize ?? MAX_MSG_SIZE
48
+ this.maxUnprocessedMessageQueueSize = init.maxUnprocessedMessageQueueSize ?? MAX_MSG_QUEUE_SIZE
49
+ this.decoder = new Decoder(this.maxMessageSize, this.maxUnprocessedMessageQueueSize)
120
50
 
121
51
  this.rateLimiter = new RateLimiter({
122
52
  points: init.disconnectThreshold ?? DISCONNECT_THRESHOLD,
@@ -124,207 +54,75 @@ export class MplexStreamMuxer implements StreamMuxer {
124
54
  })
125
55
  }
126
56
 
127
- /**
128
- * Returns a Map of streams and their ids
129
- */
130
- get streams (): Stream[] {
131
- // Inbound and Outbound streams may have the same ids, so we need to make those unique
132
- const streams: Stream[] = []
133
- for (const stream of this._streams.initiators.values()) {
134
- streams.push(stream)
135
- }
136
-
137
- for (const stream of this._streams.receivers.values()) {
138
- streams.push(stream)
57
+ onData (data: Uint8Array | Uint8ArrayList): void {
58
+ for (const msg of this.decoder.write(data)) {
59
+ this.handleMessage(msg)
139
60
  }
140
- return streams
141
61
  }
142
62
 
143
63
  /**
144
64
  * Initiate a new stream with the given name. If no name is
145
65
  * provided, the id of the stream will be used.
146
66
  */
147
- newStream (name?: string): Stream {
148
- if (this.closeController.signal.aborted) {
67
+ onCreateStream (options: CreateStreamOptions): MplexStream {
68
+ if (this.status !== 'open') {
149
69
  throw new MuxerClosedError('Muxer already closed')
150
70
  }
151
- const id = this._streamId++
152
- name = name == null ? id.toString() : name.toString()
153
- const registry = this._streams.initiators
154
- return this._newStream({ id, name, type: 'initiator', registry })
155
- }
156
-
157
- /**
158
- * Close or abort all tracked streams and stop the muxer
159
- */
160
- async close (options?: AbortOptions): Promise<void> {
161
- if (this.closeController.signal.aborted) {
162
- return
163
- }
164
-
165
- const signal = options?.signal ?? AbortSignal.timeout(this.closeTimeout)
166
-
167
- try {
168
- // try to gracefully close all streams
169
- await Promise.all(
170
- this.streams.map(async s => s.close({
171
- signal
172
- }))
173
- )
174
71
 
175
- this._source.end()
176
-
177
- // try to gracefully close the muxer
178
- await this._source.onEmpty({
179
- signal
180
- })
181
-
182
- this.closeController.abort()
183
- } catch (err: any) {
184
- this.abort(err)
185
- }
186
- }
187
-
188
- abort (err: Error): void {
189
- if (this.closeController.signal.aborted) {
190
- return
191
- }
192
-
193
- this.streams.forEach(s => { s.abort(err) })
194
- this.closeController.abort(err)
195
- }
72
+ const id = this._streamId++
196
73
 
197
- /**
198
- * Called whenever an inbound stream is created
199
- */
200
- _newReceiverStream (options: { id: number, name: string }): MplexStream {
201
- const { id, name } = options
202
- const registry = this._streams.receivers
203
- return this._newStream({ id, name, type: 'receiver', registry })
74
+ return this._newStream(id, 'outbound', options)
204
75
  }
205
76
 
206
- _newStream (options: { id: number, name: string, type: 'initiator' | 'receiver', registry: Map<number, MplexStream> }): MplexStream {
207
- const { id, name, type, registry } = options
208
-
209
- this.log('new %s stream %s', type, id)
210
-
211
- if (type === 'initiator' && this._streams.initiators.size === (this._init.maxOutboundStreams ?? MAX_STREAMS_OUTBOUND_STREAMS_PER_CONNECTION)) {
212
- throw new TooManyOutboundProtocolStreamsError('Too many outbound streams open')
213
- }
77
+ _newStream (id: number, direction: MessageStreamDirection, options?: CreateStreamOptions): MplexStream {
78
+ this.log('new %s stream %s', direction, id)
214
79
 
215
- if (registry.has(id)) {
216
- throw new Error(`${type} stream ${id} already exists!`)
217
- }
218
-
219
- const send = async (msg: Message): Promise<void> => {
220
- if (this.log.enabled) {
221
- this.log.trace('%s stream %s send', type, id, printMessage(msg))
222
- }
223
-
224
- this._source.push(msg)
225
- }
226
-
227
- const onEnd = (): void => {
228
- this.log('%s stream with id %s and protocol %s ended', type, id, stream.protocol)
229
- registry.delete(id)
230
-
231
- if (this._init.onStreamEnd != null) {
232
- this._init.onStreamEnd(stream)
233
- }
234
- }
80
+ const stream = createStream({
81
+ ...options,
82
+ id,
83
+ direction,
84
+ maxMsgSize: this.maxMessageSize,
85
+ log: this.log,
86
+ muxer: this
87
+ })
235
88
 
236
- const stream = createStream({ id, name, send, type, onEnd, maxMsgSize: this._init.maxMsgSize, logger: this.logger })
237
- registry.set(id, stream)
238
89
  return stream
239
90
  }
240
91
 
241
- /**
242
- * Creates a sink with an abortable source. Incoming messages will
243
- * also have their size restricted. All messages will be varint decoded.
244
- */
245
- _createSink (): Sink<Source<Uint8ArrayList | Uint8Array>, Promise<void>> {
246
- const sink: Sink<Source<Uint8ArrayList | Uint8Array>, Promise<void>> = async source => {
247
- const abortListener = (): void => {
248
- closeSource(source, this.log)
249
- }
250
-
251
- this.closeController.signal.addEventListener('abort', abortListener)
252
-
253
- try {
254
- const decoder = new Decoder(this._init.maxMsgSize, this._init.maxUnprocessedMessageQueueSize)
255
-
256
- for await (const chunk of source) {
257
- for (const msg of decoder.write(chunk)) {
258
- await this._handleIncoming(msg)
259
- }
260
- }
261
-
262
- this._source.end()
263
- } catch (err: any) {
264
- this.log('error in sink', err)
265
- this._source.end(err) // End the source with an error
266
- } finally {
267
- this.closeController.signal.removeEventListener('abort', abortListener)
268
- }
269
- }
270
-
271
- return sink
272
- }
273
-
274
- async _handleIncoming (message: Message): Promise<void> {
275
- const { id, type } = message
276
-
92
+ handleMessage (message: Message): void {
277
93
  if (this.log.enabled) {
278
94
  this.log.trace('incoming message', printMessage(message))
279
95
  }
280
96
 
281
97
  // Create a new stream?
282
98
  if (message.type === MessageTypes.NEW_STREAM) {
283
- if (this._streams.receivers.size === (this._init.maxInboundStreams ?? MAX_STREAMS_INBOUND_STREAMS_PER_CONNECTION)) {
284
- this.log('too many inbound streams open')
285
-
286
- // not going to allow this stream, send the reset message manually
287
- // instead of setting it up just to tear it down
288
- this._source.push({
289
- id,
290
- type: MessageTypes.RESET_RECEIVER
291
- })
292
-
293
- // if we've hit our stream limit, and the remote keeps trying to open
294
- // more new streams, if they are doing this very quickly maybe they
295
- // are attacking us and we should close the connection
296
- try {
297
- await this.rateLimiter.consume('new-stream', 1)
298
- } catch {
299
- this.log('rate limit hit when opening too many new streams over the inbound stream limit - closing remote connection')
300
- // since there's no backpressure in mplex, the only thing we can really do to protect ourselves is close the connection
301
- this.abort(new Error('Too many open streams'))
302
- return
303
- }
304
-
99
+ // close the connection if the remote opens too many streams too quickly
100
+ try {
101
+ this.rateLimiter.consume('new-stream', 1)
102
+ } catch {
103
+ this.log('rate limit hit when opening too many new streams over the inbound stream limit - closing remote connection')
104
+ // since there's no backpressure in mplex, the only thing we can really do to protect ourselves is close the connection
105
+ this.abort(new Error('Too many open streams'))
305
106
  return
306
107
  }
307
108
 
308
- const stream = this._newReceiverStream({ id, name: uint8ArrayToString(message.data instanceof Uint8Array ? message.data : message.data.subarray()) })
309
-
310
- if (this._init.onIncomingStream != null) {
311
- this._init.onIncomingStream(stream)
312
- }
109
+ const stream = this._newStream(message.id, 'inbound', this.streamOptions)
110
+ this.onRemoteStream(stream)
313
111
 
314
112
  return
315
113
  }
316
114
 
317
- const list = (type & 1) === 1 ? this._streams.initiators : this._streams.receivers
318
- const stream = list.get(id)
115
+ const id = `${(message.type & 1) === 1 ? 'i' : 'r'}${message.id}`
116
+ const stream = this.streams.find(s => s.id === id)
319
117
 
320
118
  if (stream == null) {
321
- this.log('missing stream %s for message type %s', id, MessageTypeNames[type])
119
+ this.log('missing stream %s for message type %s', id, MessageTypeNames[message.type])
322
120
 
323
121
  // if the remote keeps sending us messages for streams that have been
324
122
  // closed or were never opened they may be attacking us so if they do
325
123
  // this very quickly all we can do is close the connection
326
124
  try {
327
- await this.rateLimiter.consume('missing-stream', 1)
125
+ this.rateLimiter.consume('missing-stream', 1)
328
126
  } catch {
329
127
  this.log('rate limit hit when receiving messages for streams that do not exist - closing remote connection')
330
128
  // since there's no backpressure in mplex, the only thing we can really do to protect ourselves is close the connection
@@ -335,41 +133,28 @@ export class MplexStreamMuxer implements StreamMuxer {
335
133
  return
336
134
  }
337
135
 
338
- const maxBufferSize = this._init.maxStreamBufferSize ?? MAX_STREAM_BUFFER_SIZE
339
-
340
136
  try {
341
- switch (type) {
137
+ switch (message.type) {
342
138
  case MessageTypes.MESSAGE_INITIATOR:
343
139
  case MessageTypes.MESSAGE_RECEIVER:
344
- if (stream.sourceReadableLength() > maxBufferSize) {
345
- // Stream buffer has got too large, reset the stream
346
- this._source.push({
347
- id: message.id,
348
- type: type === MessageTypes.MESSAGE_INITIATOR ? MessageTypes.RESET_RECEIVER : MessageTypes.RESET_INITIATOR
349
- })
350
-
351
- // Inform the stream consumer they are not fast enough
352
- throw new StreamInputBufferError('Input buffer full - increase Mplex maxBufferSize to accommodate slow consumers')
353
- }
354
-
355
140
  // We got data from the remote, push it into our local stream
356
- stream.sourcePush(message.data)
141
+ stream.onData(message.data)
357
142
  break
358
143
  case MessageTypes.CLOSE_INITIATOR:
359
144
  case MessageTypes.CLOSE_RECEIVER:
360
- // The remote has stopped writing, so we can stop reading
361
- stream.remoteCloseWrite()
145
+ // The remote has stopped writing
146
+ stream.onRemoteCloseWrite()
362
147
  break
363
148
  case MessageTypes.RESET_INITIATOR:
364
149
  case MessageTypes.RESET_RECEIVER:
365
150
  // The remote has errored, stop reading and writing to the stream immediately
366
- stream.reset()
151
+ stream.onRemoteReset()
367
152
  break
368
153
  default:
369
- this.log('unknown message type %s', type)
154
+ this.log('unknown message type')
370
155
  }
371
156
  } catch (err: any) {
372
- this.log.error('error while processing message', err)
157
+ this.log.error('error while processing message - %e', err)
373
158
  stream.abort(err)
374
159
  }
375
160
  }
package/src/stream.ts CHANGED
@@ -1,94 +1,127 @@
1
- import { AbstractStream } from '@libp2p/utils/abstract-stream'
1
+ import { AbstractStream } from '@libp2p/utils'
2
2
  import { Uint8ArrayList } from 'uint8arraylist'
3
3
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
4
4
  import { MAX_MSG_SIZE } from './decode.js'
5
+ import { encode } from './encode.ts'
5
6
  import { InitiatorMessageTypes, ReceiverMessageTypes } from './message-types.js'
6
- import type { Message } from './message-types.js'
7
- import type { ComponentLogger } from '@libp2p/interface'
8
- import type { AbstractStreamInit } from '@libp2p/utils/abstract-stream'
7
+ import type { MplexStreamMuxer } from './mplex.ts'
8
+ import type { Logger, MessageStreamDirection } from '@libp2p/interface'
9
+ import type { AbstractStreamInit, SendResult } from '@libp2p/utils'
10
+ import type { AbortOptions } from 'it-pushable'
9
11
 
10
12
  export interface Options {
11
13
  id: number
12
- send(msg: Message): Promise<void>
13
- name?: string
14
- onEnd?(err?: Error): void
15
- type?: 'initiator' | 'receiver'
14
+ log: Logger
15
+ direction: MessageStreamDirection
16
16
  maxMsgSize?: number
17
- logger: ComponentLogger
17
+ muxer: MplexStreamMuxer
18
18
  }
19
19
 
20
20
  interface MplexStreamInit extends AbstractStreamInit {
21
- streamId: number
22
- name: string
23
- send(msg: Message): Promise<void>
24
-
25
- /**
26
- * The maximum allowable data size, any data larger than this will be
27
- * chunked and sent in multiple data messages
28
- */
29
21
  maxDataSize: number
22
+ muxer: MplexStreamMuxer
23
+ direction: MessageStreamDirection
30
24
  }
31
25
 
32
26
  export class MplexStream extends AbstractStream {
33
- private readonly name: string
34
- private readonly streamId: number
35
- private readonly send: (msg: Message) => Promise<void>
27
+ public readonly streamId: number
36
28
  private readonly types: Record<string, number>
37
29
  private readonly maxDataSize: number
30
+ private readonly muxer: MplexStreamMuxer
38
31
 
39
32
  constructor (init: MplexStreamInit) {
40
33
  super(init)
41
34
 
42
35
  this.types = init.direction === 'outbound' ? InitiatorMessageTypes : ReceiverMessageTypes
43
- this.send = init.send
44
- this.name = init.name
45
- this.streamId = init.streamId
46
36
  this.maxDataSize = init.maxDataSize
47
- }
37
+ this.muxer = init.muxer
38
+ this.streamId = parseInt(this.id.substring(1))
48
39
 
49
- async sendNewStream (): Promise<void> {
50
- await this.send({ id: this.streamId, type: InitiatorMessageTypes.NEW_STREAM, data: new Uint8ArrayList(uint8ArrayFromString(this.name)) })
40
+ if (init.direction === 'outbound') {
41
+ // open the stream on the receiver end. do this in a microtask so the
42
+ // stream gets added to the streams array by the muxer superclass before
43
+ // we send the NEW_STREAM message, otherwise we create a race condition
44
+ // whereby we can receive the stream messages before the stream is added
45
+ // to the streams list
46
+ queueMicrotask(() => {
47
+ this.muxer.send(
48
+ encode({
49
+ id: this.streamId,
50
+ type: InitiatorMessageTypes.NEW_STREAM,
51
+ data: new Uint8ArrayList(uint8ArrayFromString(this.id))
52
+ })
53
+ )
54
+ })
55
+ }
51
56
  }
52
57
 
53
- async sendData (data: Uint8ArrayList): Promise<void> {
54
- data = data.sublist()
58
+ sendData (data: Uint8ArrayList): SendResult {
59
+ const list = new Uint8ArrayList()
60
+ const sentBytes = data.byteLength
55
61
 
56
62
  while (data.byteLength > 0) {
57
63
  const toSend = Math.min(data.byteLength, this.maxDataSize)
58
- await this.send({
59
- id: this.streamId,
60
- type: this.types.MESSAGE,
61
- data: data.sublist(0, toSend)
62
- })
64
+ const slice = data.sublist(0, toSend)
65
+ data = data.sublist(toSend)
66
+
67
+ list.append(
68
+ encode({
69
+ id: this.streamId,
70
+ type: this.types.MESSAGE,
71
+ data: slice
72
+ })
73
+ )
74
+ }
63
75
 
64
- data.consume(toSend)
76
+ return {
77
+ sentBytes,
78
+ canSendMore: this.muxer.send(list)
65
79
  }
66
80
  }
67
81
 
68
- async sendReset (): Promise<void> {
69
- await this.send({ id: this.streamId, type: this.types.RESET })
82
+ sendReset (): boolean {
83
+ return this.muxer.send(
84
+ encode({
85
+ id: this.streamId,
86
+ type: this.types.RESET
87
+ })
88
+ )
70
89
  }
71
90
 
72
- async sendCloseWrite (): Promise<void> {
73
- await this.send({ id: this.streamId, type: this.types.CLOSE })
91
+ async sendCloseWrite (options?: AbortOptions): Promise<void> {
92
+ this.muxer.send(
93
+ encode({
94
+ id: this.streamId,
95
+ type: this.types.CLOSE
96
+ })
97
+ )
98
+ options?.signal?.throwIfAborted()
74
99
  }
75
100
 
76
- async sendCloseRead (): Promise<void> {
101
+ async sendCloseRead (options?: AbortOptions): Promise<void> {
102
+ options?.signal?.throwIfAborted()
77
103
  // mplex does not support close read, only close write
78
104
  }
105
+
106
+ sendPause (): void {
107
+ // mplex does not support backpressure
108
+ }
109
+
110
+ sendResume (): void {
111
+ // mplex does not support backpressure
112
+ }
79
113
  }
80
114
 
81
115
  export function createStream (options: Options): MplexStream {
82
- const { id, name, send, onEnd, type = 'initiator', maxMsgSize = MAX_MSG_SIZE } = options
116
+ const { id, muxer, direction, maxMsgSize = MAX_MSG_SIZE } = options
83
117
 
84
118
  return new MplexStream({
85
- id: type === 'initiator' ? (`i${id}`) : `r${id}`,
86
- streamId: id,
87
- name: `${name ?? id}`,
88
- direction: type === 'initiator' ? 'outbound' : 'inbound',
119
+ ...options,
120
+ id: direction === 'outbound' ? (`i${id}`) : `r${id}`,
121
+ direction,
89
122
  maxDataSize: maxMsgSize,
90
- onEnd,
91
- send,
92
- log: options.logger.forComponent(`libp2p:mplex:stream:${type}:${id}`)
123
+ muxer,
124
+ log: options.log.newScope(`${direction}:${id}`),
125
+ protocol: ''
93
126
  })
94
127
  }
@@ -1,7 +0,0 @@
1
- {
2
- "MplexComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_mplex.MplexComponents.html",
3
- "MplexInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_mplex.MplexInit.html",
4
- ".:MplexInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_mplex.MplexInit.html",
5
- "mplex": "https://libp2p.github.io/js-libp2p/functions/_libp2p_mplex.mplex.html",
6
- ".:mplex": "https://libp2p.github.io/js-libp2p/functions/_libp2p_mplex.mplex.html"
7
- }