@libp2p/utils 6.7.2 → 7.0.0-55b7e5fea

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 (160) hide show
  1. package/README.md +16 -1
  2. package/dist/index.min.js +7 -1
  3. package/dist/index.min.js.map +4 -4
  4. package/dist/src/abstract-message-stream.d.ts +129 -0
  5. package/dist/src/abstract-message-stream.d.ts.map +1 -0
  6. package/dist/src/abstract-message-stream.js +393 -0
  7. package/dist/src/abstract-message-stream.js.map +1 -0
  8. package/dist/src/abstract-multiaddr-connection.d.ts +26 -0
  9. package/dist/src/abstract-multiaddr-connection.d.ts.map +1 -0
  10. package/dist/src/abstract-multiaddr-connection.js +66 -0
  11. package/dist/src/abstract-multiaddr-connection.js.map +1 -0
  12. package/dist/src/abstract-stream-muxer.d.ts +53 -0
  13. package/dist/src/abstract-stream-muxer.d.ts.map +1 -0
  14. package/dist/src/abstract-stream-muxer.js +169 -0
  15. package/dist/src/abstract-stream-muxer.js.map +1 -0
  16. package/dist/src/abstract-stream.d.ts +14 -130
  17. package/dist/src/abstract-stream.d.ts.map +1 -1
  18. package/dist/src/abstract-stream.js +39 -321
  19. package/dist/src/abstract-stream.js.map +1 -1
  20. package/dist/src/errors.d.ts +8 -0
  21. package/dist/src/errors.d.ts.map +1 -1
  22. package/dist/src/errors.js +8 -0
  23. package/dist/src/errors.js.map +1 -1
  24. package/dist/src/get-thin-waist-addresses.browser.d.ts +1 -1
  25. package/dist/src/get-thin-waist-addresses.browser.d.ts.map +1 -1
  26. package/dist/src/get-thin-waist-addresses.browser.js +4 -3
  27. package/dist/src/get-thin-waist-addresses.browser.js.map +1 -1
  28. package/dist/src/get-thin-waist-addresses.d.ts +1 -1
  29. package/dist/src/get-thin-waist-addresses.d.ts.map +1 -1
  30. package/dist/src/get-thin-waist-addresses.js +7 -9
  31. package/dist/src/get-thin-waist-addresses.js.map +1 -1
  32. package/dist/src/index.d.ts +31 -1
  33. package/dist/src/index.d.ts.map +1 -1
  34. package/dist/src/index.js +31 -1
  35. package/dist/src/index.js.map +1 -1
  36. package/dist/src/length-prefixed-decoder.d.ts +37 -0
  37. package/dist/src/length-prefixed-decoder.d.ts.map +1 -0
  38. package/dist/src/length-prefixed-decoder.js +64 -0
  39. package/dist/src/length-prefixed-decoder.js.map +1 -0
  40. package/dist/src/message-queue.d.ts +61 -0
  41. package/dist/src/message-queue.d.ts.map +1 -0
  42. package/dist/src/message-queue.js +93 -0
  43. package/dist/src/message-queue.js.map +1 -0
  44. package/dist/src/mock-muxer.d.ts +57 -0
  45. package/dist/src/mock-muxer.d.ts.map +1 -0
  46. package/dist/src/mock-muxer.js +204 -0
  47. package/dist/src/mock-muxer.js.map +1 -0
  48. package/dist/src/mock-stream.d.ts +31 -0
  49. package/dist/src/mock-stream.d.ts.map +1 -0
  50. package/dist/src/mock-stream.js +69 -0
  51. package/dist/src/mock-stream.js.map +1 -0
  52. package/dist/src/multiaddr/get-net-config.d.ts +55 -0
  53. package/dist/src/multiaddr/get-net-config.d.ts.map +1 -0
  54. package/dist/src/multiaddr/get-net-config.js +54 -0
  55. package/dist/src/multiaddr/get-net-config.js.map +1 -0
  56. package/dist/src/multiaddr/index.d.ts +7 -0
  57. package/dist/src/multiaddr/index.d.ts.map +1 -0
  58. package/dist/src/multiaddr/index.js +7 -0
  59. package/dist/src/multiaddr/index.js.map +1 -0
  60. package/dist/src/multiaddr/is-global-unicast.d.ts.map +1 -1
  61. package/dist/src/multiaddr/is-global-unicast.js +8 -9
  62. package/dist/src/multiaddr/is-global-unicast.js.map +1 -1
  63. package/dist/src/multiaddr/is-link-local.d.ts.map +1 -1
  64. package/dist/src/multiaddr/is-link-local.js +11 -16
  65. package/dist/src/multiaddr/is-link-local.js.map +1 -1
  66. package/dist/src/multiaddr/is-loopback.d.ts.map +1 -1
  67. package/dist/src/multiaddr/is-loopback.js +12 -5
  68. package/dist/src/multiaddr/is-loopback.js.map +1 -1
  69. package/dist/src/multiaddr/is-network-address.d.ts.map +1 -1
  70. package/dist/src/multiaddr/is-network-address.js +4 -16
  71. package/dist/src/multiaddr/is-network-address.js.map +1 -1
  72. package/dist/src/multiaddr/is-private.d.ts.map +1 -1
  73. package/dist/src/multiaddr/is-private.js +9 -10
  74. package/dist/src/multiaddr/is-private.js.map +1 -1
  75. package/dist/src/multiaddr/utils.d.ts +5 -0
  76. package/dist/src/multiaddr/utils.d.ts.map +1 -0
  77. package/dist/src/multiaddr/utils.js +32 -0
  78. package/dist/src/multiaddr/utils.js.map +1 -0
  79. package/dist/src/multiaddr-connection-pair.d.ts +25 -0
  80. package/dist/src/multiaddr-connection-pair.d.ts.map +1 -0
  81. package/dist/src/multiaddr-connection-pair.js +103 -0
  82. package/dist/src/multiaddr-connection-pair.js.map +1 -0
  83. package/dist/src/queue/index.d.ts +3 -6
  84. package/dist/src/queue/index.d.ts.map +1 -1
  85. package/dist/src/queue/index.js +20 -4
  86. package/dist/src/queue/index.js.map +1 -1
  87. package/dist/src/rate-limiter.d.ts +1 -15
  88. package/dist/src/rate-limiter.d.ts.map +1 -1
  89. package/dist/src/rate-limiter.js +1 -14
  90. package/dist/src/rate-limiter.js.map +1 -1
  91. package/dist/src/stream-pair.d.ts +42 -0
  92. package/dist/src/stream-pair.d.ts.map +1 -0
  93. package/dist/src/stream-pair.js +40 -0
  94. package/dist/src/stream-pair.js.map +1 -0
  95. package/dist/src/stream-utils.d.ts +191 -0
  96. package/dist/src/stream-utils.d.ts.map +1 -0
  97. package/dist/src/stream-utils.js +371 -0
  98. package/dist/src/stream-utils.js.map +1 -0
  99. package/package.json +15 -162
  100. package/src/abstract-message-stream.ts +553 -0
  101. package/src/abstract-multiaddr-connection.ts +93 -0
  102. package/src/abstract-stream-muxer.ts +239 -0
  103. package/src/abstract-stream.ts +51 -464
  104. package/src/errors.ts +10 -0
  105. package/src/get-thin-waist-addresses.browser.ts +5 -4
  106. package/src/get-thin-waist-addresses.ts +8 -12
  107. package/src/index.ts +31 -1
  108. package/src/length-prefixed-decoder.ts +98 -0
  109. package/src/message-queue.ts +156 -0
  110. package/src/mock-muxer.ts +304 -0
  111. package/src/mock-stream.ts +101 -0
  112. package/src/multiaddr/get-net-config.ts +112 -0
  113. package/src/multiaddr/index.ts +6 -0
  114. package/src/multiaddr/is-global-unicast.ts +8 -11
  115. package/src/multiaddr/is-link-local.ts +11 -20
  116. package/src/multiaddr/is-loopback.ts +12 -7
  117. package/src/multiaddr/is-network-address.ts +4 -19
  118. package/src/multiaddr/is-private.ts +9 -14
  119. package/src/multiaddr/utils.ts +46 -0
  120. package/src/multiaddr-connection-pair.ts +147 -0
  121. package/src/queue/index.ts +24 -11
  122. package/src/rate-limiter.ts +3 -30
  123. package/src/stream-pair.ts +90 -0
  124. package/src/stream-utils.ts +866 -0
  125. package/dist/src/abort-options.d.ts +0 -7
  126. package/dist/src/abort-options.d.ts.map +0 -1
  127. package/dist/src/abort-options.js +0 -14
  128. package/dist/src/abort-options.js.map +0 -1
  129. package/dist/src/array-equals.d.ts +0 -24
  130. package/dist/src/array-equals.d.ts.map +0 -1
  131. package/dist/src/array-equals.js +0 -31
  132. package/dist/src/array-equals.js.map +0 -1
  133. package/dist/src/close-source.d.ts +0 -4
  134. package/dist/src/close-source.d.ts.map +0 -1
  135. package/dist/src/close-source.js +0 -11
  136. package/dist/src/close-source.js.map +0 -1
  137. package/dist/src/close.d.ts +0 -21
  138. package/dist/src/close.d.ts.map +0 -1
  139. package/dist/src/close.js +0 -49
  140. package/dist/src/close.js.map +0 -1
  141. package/dist/src/merge-options.d.ts +0 -7
  142. package/dist/src/merge-options.d.ts.map +0 -1
  143. package/dist/src/merge-options.js +0 -128
  144. package/dist/src/merge-options.js.map +0 -1
  145. package/dist/src/multiaddr/is-ip-based.d.ts +0 -6
  146. package/dist/src/multiaddr/is-ip-based.d.ts.map +0 -1
  147. package/dist/src/multiaddr/is-ip-based.js +0 -18
  148. package/dist/src/multiaddr/is-ip-based.js.map +0 -1
  149. package/dist/src/stream-to-ma-conn.d.ts +0 -23
  150. package/dist/src/stream-to-ma-conn.d.ts.map +0 -1
  151. package/dist/src/stream-to-ma-conn.js +0 -75
  152. package/dist/src/stream-to-ma-conn.js.map +0 -1
  153. package/dist/typedoc-urls.json +0 -147
  154. package/src/abort-options.ts +0 -20
  155. package/src/array-equals.ts +0 -34
  156. package/src/close-source.ts +0 -14
  157. package/src/close.ts +0 -65
  158. package/src/merge-options.ts +0 -161
  159. package/src/multiaddr/is-ip-based.ts +0 -21
  160. package/src/stream-to-ma-conn.ts +0 -106
@@ -0,0 +1,98 @@
1
+ import { InvalidParametersError } from '@libp2p/interface'
2
+ import * as varint from 'uint8-varint'
3
+ import { Uint8ArrayList } from 'uint8arraylist'
4
+ import { InvalidMessageLengthError } from './stream-utils.ts'
5
+
6
+ const DEFAULT_MAX_BUFFER_SIZE = 1024 * 1024 * 4
7
+ const DEFAULT_MAX_DATA_LENGTH = 1024 * 1024 * 4
8
+
9
+ export interface LengthPrefixedDecoderInit {
10
+ /**
11
+ * How large the internal buffer is allowed to grow - attempting to store more
12
+ * data than this will throw
13
+ */
14
+ maxBufferSize?: number
15
+
16
+ /**
17
+ * Throw an error if the message that would be read from the buffer is larger
18
+ * than this value
19
+ */
20
+ maxDataLength?: number
21
+
22
+ /**
23
+ * Read a varint from the buffer
24
+ */
25
+ lengthDecoder?(data: Uint8ArrayList | Uint8Array): number
26
+
27
+ /**
28
+ * Return how many bytes it takes to encode the passed value
29
+ */
30
+ encodingLength?(length: number): number
31
+ }
32
+
33
+ /**
34
+ * Decode length-prefixed data from a buffer
35
+ */
36
+ export class LengthPrefixedDecoder {
37
+ private readonly buffer: Uint8ArrayList
38
+ private readonly maxBufferSize: number
39
+ private readonly lengthDecoder: (data: Uint8ArrayList | Uint8Array) => number
40
+ private readonly maxDataLength: number
41
+ private readonly encodingLength: (length: number) => number
42
+
43
+ constructor (init: LengthPrefixedDecoderInit = {}) {
44
+ this.buffer = new Uint8ArrayList()
45
+ this.maxBufferSize = init.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE
46
+ this.maxDataLength = init.maxDataLength ?? DEFAULT_MAX_DATA_LENGTH
47
+ this.lengthDecoder = init.lengthDecoder ?? varint.decode
48
+ this.encodingLength = init.encodingLength ?? varint.encodingLength
49
+ }
50
+
51
+ /**
52
+ * Decodes length-prefixed data
53
+ */
54
+ * decode (buf: Uint8Array | Uint8ArrayList): Generator<Uint8ArrayList> {
55
+ this.buffer.append(buf)
56
+
57
+ if (this.buffer.byteLength > this.maxBufferSize) {
58
+ throw new InvalidParametersError(`Buffer length limit exceeded - ${this.buffer.byteLength}/${this.maxBufferSize}`)
59
+ }
60
+
61
+ // Loop to consume as many bytes from the buffer as possible
62
+ // Eg: when a single chunk contains several frames
63
+ while (true) {
64
+ let dataLength: number
65
+
66
+ try {
67
+ dataLength = this.lengthDecoder(this.buffer)
68
+ } catch (err) {
69
+ if (err instanceof RangeError) {
70
+ // ignore errors where we don't have enough data to read the length
71
+ // prefix
72
+ break
73
+ }
74
+
75
+ throw err
76
+ }
77
+
78
+ if (dataLength < 0 || dataLength > this.maxDataLength) {
79
+ throw new InvalidMessageLengthError('Invalid message length')
80
+ }
81
+
82
+ const lengthLength = this.encodingLength(dataLength)
83
+ const chunkLength = lengthLength + dataLength
84
+
85
+ if (this.buffer.byteLength >= chunkLength) {
86
+ const buf = this.buffer.sublist(lengthLength, chunkLength)
87
+
88
+ this.buffer.consume(chunkLength)
89
+
90
+ if (buf.byteLength > 0) {
91
+ yield buf
92
+ }
93
+ } else {
94
+ break
95
+ }
96
+ }
97
+ }
98
+ }
@@ -0,0 +1,156 @@
1
+ import { StreamMessageEvent } from '@libp2p/interface'
2
+ import delay from 'delay'
3
+ import { TypedEventEmitter } from 'main-event'
4
+ import { raceSignal } from 'race-signal'
5
+ import { isUint8ArrayList, Uint8ArrayList } from 'uint8arraylist'
6
+ import { Queue } from './queue/index.js'
7
+ import type { AbortOptions, Logger } from '@libp2p/interface'
8
+
9
+ const DEFAULT_CHUNK_SIZE = 1024 * 64
10
+
11
+ export interface MessageQueueEvents {
12
+ /**
13
+ * Message data
14
+ */
15
+ message: StreamMessageEvent
16
+
17
+ /**
18
+ * Emitted when the queue is empty
19
+ */
20
+ drain: Event
21
+
22
+ /**
23
+ * The remote closed the connection abruptly
24
+ */
25
+ reset: Event
26
+ }
27
+
28
+ export interface MessageQueueInit {
29
+ /**
30
+ * How much delay there should be between each message send in ms (note that
31
+ * even 0 introduces a small delay)
32
+ *
33
+ * @default 0
34
+ */
35
+ delay?: number
36
+
37
+ /**
38
+ * How many messages to hold in the send queue before applying backpressure to
39
+ * the sender
40
+ */
41
+ capacity?: number
42
+
43
+ /**
44
+ * Data messages larger than this size will be chunked into smaller messages.
45
+ *
46
+ * Defaults to the maximum TCP package size.
47
+ *
48
+ * @default 65_536
49
+ */
50
+ chunkSize?: number
51
+ }
52
+
53
+ interface MessageQueueJobOptions extends AbortOptions {
54
+ evt: Event
55
+ }
56
+
57
+ /**
58
+ * Accepts events to emit after a short delay, and with a configurable maximum
59
+ * queue capacity after which the send method will return false to let us
60
+ * simulate write backpressure.
61
+ */
62
+ export class MessageQueue<Events> extends TypedEventEmitter<Events & MessageQueueEvents> {
63
+ public needsDrain: boolean
64
+
65
+ private queue: Queue<void, MessageQueueJobOptions>
66
+ private capacity: number
67
+ private delay: number
68
+ private log: Logger
69
+ private chunkSize: number
70
+
71
+ constructor (init: MessageQueueInit & { log: Logger }) {
72
+ super()
73
+
74
+ this.needsDrain = false
75
+ this.queue = new Queue({
76
+ concurrency: 1
77
+ })
78
+ this.capacity = init.capacity ?? 5
79
+ this.delay = init.delay ?? 0
80
+ this.log = init.log
81
+ this.chunkSize = init.chunkSize ?? DEFAULT_CHUNK_SIZE
82
+
83
+ this.queue.addEventListener('idle', () => {
84
+ if (this.needsDrain) {
85
+ this.log('network send queue drained')
86
+ this.needsDrain = false
87
+ this.safeDispatchEvent('drain')
88
+ } else {
89
+ this.log('network send queue idle')
90
+ }
91
+ })
92
+ }
93
+
94
+ send (evt: Event): boolean {
95
+ if (isMessageEvent(evt)) {
96
+ // chunk outgoing messages to match TCP packet sizes
97
+ const data = new Uint8ArrayList(evt.data)
98
+
99
+ while (data.byteLength > 0) {
100
+ const end = Math.min(this.chunkSize, data.byteLength)
101
+ const chunk = data.sublist(0, end)
102
+ data.consume(chunk.byteLength)
103
+
104
+ const chunkEvent = new StreamMessageEvent(chunk)
105
+
106
+ this.queue.add(async (opts) => {
107
+ if (this.delay > 0) {
108
+ await raceSignal(delay(this.delay), opts.signal)
109
+ }
110
+
111
+ this.dispatchEvent(opts.evt)
112
+ }, {
113
+ evt: chunkEvent
114
+ })
115
+ }
116
+ } else {
117
+ this.queue.add(async (opts) => {
118
+ if (this.delay > 0) {
119
+ await raceSignal(delay(this.delay), opts.signal)
120
+ }
121
+
122
+ this.dispatchEvent(opts.evt)
123
+ }, {
124
+ evt
125
+ })
126
+ }
127
+
128
+ if (this.queue.size >= this.capacity) {
129
+ this.log('network send queue full')
130
+ this.needsDrain = true
131
+ return false
132
+ }
133
+
134
+ return true
135
+ }
136
+
137
+ pause (): void {
138
+ this.queue.pause()
139
+ }
140
+
141
+ resume (): void {
142
+ this.queue.resume()
143
+ }
144
+
145
+ onIdle (): Promise<void> {
146
+ return this.queue.onIdle()
147
+ }
148
+
149
+ size (): number {
150
+ return this.queue.size
151
+ }
152
+ }
153
+
154
+ function isMessageEvent (evt?: any): evt is StreamMessageEvent {
155
+ return evt?.data instanceof Uint8Array || isUint8ArrayList(evt?.data)
156
+ }
@@ -0,0 +1,304 @@
1
+ import * as cborg from 'cborg'
2
+ import * as lp from 'it-length-prefixed'
3
+ import { pushable } from 'it-pushable'
4
+ import { Uint8ArrayList } from 'uint8arraylist'
5
+ import { AbstractStreamMuxer } from './abstract-stream-muxer.ts'
6
+ import { AbstractStream } from './abstract-stream.ts'
7
+ import { Queue } from './queue/index.js'
8
+ import type { SendResult } from './abstract-message-stream.ts'
9
+ import type { AbstractStreamInit } from './abstract-stream.ts'
10
+ import type { AbortOptions, MessageStreamDirection, CreateStreamOptions, StreamMuxerFactory, StreamMuxer, MultiaddrConnection, StreamMuxerOptions } from '@libp2p/interface'
11
+ import type { Pushable } from 'it-pushable'
12
+ import type { SupportedEncodings } from 'uint8arrays/from-string'
13
+
14
+ interface DataMessage {
15
+ id: string
16
+ type: 'data'
17
+ chunk: Uint8Array
18
+ }
19
+
20
+ interface ResetMessage {
21
+ id: string
22
+ type: 'reset'
23
+ }
24
+
25
+ interface CloseWriteMessage {
26
+ id: string
27
+ type: 'closeWrite'
28
+ }
29
+
30
+ interface CloseReadMessage {
31
+ id: string
32
+ type: 'closeRead'
33
+ }
34
+
35
+ interface CreateMessage {
36
+ id: string
37
+ type: 'create'
38
+ protocol?: string
39
+ }
40
+
41
+ interface PauseMessage {
42
+ id: string
43
+ type: 'pause'
44
+ }
45
+
46
+ interface ResumeMessage {
47
+ id: string
48
+ type: 'resume'
49
+ }
50
+
51
+ type StreamMessage = DataMessage | ResetMessage | CloseWriteMessage | CloseReadMessage | CreateMessage | PauseMessage | ResumeMessage
52
+
53
+ export interface MockMuxedStreamInit extends AbstractStreamInit {
54
+ sendMessage(message: StreamMessage): boolean
55
+ encoding: SupportedEncodings
56
+ }
57
+
58
+ class MockMuxedStream extends AbstractStream {
59
+ private readonly sendMessage: (message: StreamMessage) => boolean
60
+ private dataQueue: Queue
61
+ private encoding: SupportedEncodings
62
+
63
+ constructor (init: MockMuxedStreamInit) {
64
+ super(init)
65
+
66
+ this.sendMessage = init.sendMessage
67
+ this.encoding = init.encoding
68
+ this.dataQueue = new Queue({
69
+ concurrency: 1
70
+ })
71
+
72
+ if (this.direction === 'outbound') {
73
+ this.sendMessage({
74
+ id: this.id,
75
+ type: 'create',
76
+ protocol: this.protocol
77
+ })
78
+ }
79
+ }
80
+
81
+ sendData (data: Uint8ArrayList): SendResult {
82
+ const canSendMore = this.sendMessage({
83
+ id: this.id,
84
+ type: 'data',
85
+ chunk: data.subarray()
86
+ })
87
+
88
+ return {
89
+ sentBytes: data.byteLength,
90
+ canSendMore
91
+ }
92
+ }
93
+
94
+ sendReset (): void {
95
+ this.sendMessage({
96
+ id: this.id,
97
+ type: 'reset'
98
+ })
99
+ }
100
+
101
+ async sendCloseWrite (options?: AbortOptions): Promise<void> {
102
+ this.sendMessage({
103
+ id: this.id,
104
+ type: 'closeWrite'
105
+ })
106
+
107
+ options?.signal?.throwIfAborted()
108
+ }
109
+
110
+ async sendCloseRead (options?: AbortOptions): Promise<void> {
111
+ this.sendMessage({
112
+ id: this.id,
113
+ type: 'closeRead'
114
+ })
115
+
116
+ options?.signal?.throwIfAborted()
117
+ }
118
+
119
+ sendPause (): void {
120
+ this.sendMessage({
121
+ id: this.id,
122
+ type: 'pause'
123
+ })
124
+ }
125
+
126
+ sendResume (): void {
127
+ this.sendMessage({
128
+ id: this.id,
129
+ type: 'resume'
130
+ })
131
+ }
132
+
133
+ onRemotePaused (): void {
134
+ this.dataQueue.pause()
135
+ }
136
+
137
+ onRemoteResumed (): void {
138
+ this.dataQueue.resume()
139
+ }
140
+ }
141
+
142
+ interface MockMuxerInit extends StreamMuxerOptions {
143
+ /**
144
+ * How long the input queue can grow
145
+ */
146
+ maxInputQueueSize?: number
147
+
148
+ /**
149
+ * How to encode data message
150
+ *
151
+ * @default base64
152
+ */
153
+ encoding?: SupportedEncodings
154
+
155
+ /**
156
+ * How large muxer messages are allowed to be
157
+ */
158
+ maxMessageSize?: number
159
+ }
160
+
161
+ // CBOR encoding of non-message data fields
162
+ const MESSAGE_OVERHEAD = 30
163
+
164
+ class MockMuxer extends AbstractStreamMuxer<MockMuxedStream> {
165
+ private input: Pushable<Uint8Array | Uint8ArrayList>
166
+ private maxInputQueueSize: number
167
+ private encoding: SupportedEncodings
168
+ private maxMessageSize: number
169
+ private nextStreamId: number
170
+
171
+ constructor (maConn: MultiaddrConnection, init: MockMuxerInit) {
172
+ super(maConn, {
173
+ ...init,
174
+ protocol: '/mock-muxer/1.0.0',
175
+ name: 'mock-muxer'
176
+ })
177
+
178
+ this.maxInputQueueSize = init.maxInputQueueSize ?? 1024 * 1024 * 10
179
+ this.maxMessageSize = (init.maxMessageSize ?? 1024 * 1024 * 4) + MESSAGE_OVERHEAD
180
+ this.encoding = init.encoding ?? 'base64'
181
+ this.input = pushable()
182
+ this.sendMessage = this.sendMessage.bind(this)
183
+ this.nextStreamId = this.maConn.direction === 'outbound' ? 0 : 1
184
+
185
+ Promise.resolve()
186
+ .then(async () => {
187
+ for await (const buf of lp.decode(this.input, {
188
+ maxDataLength: this.maxMessageSize
189
+ })) {
190
+ this.onMessage(cborg.decode(buf.subarray()))
191
+ }
192
+ })
193
+ .catch(err => {
194
+ this.abort(err)
195
+ })
196
+ }
197
+
198
+ onData (data: Uint8Array | Uint8ArrayList): void {
199
+ if (this.input.readableLength >= this.maxInputQueueSize) {
200
+ this.abort(new Error(`Input queue exceeded maximum size ${this.input.readableLength} >= ${this.maxInputQueueSize}`))
201
+ return
202
+ }
203
+
204
+ this.input.push(data)
205
+ }
206
+
207
+ sendMessage (message: StreamMessage): boolean {
208
+ if (message.type === 'data') {
209
+ this.log.trace('send message %o', { ...message, chunk: `[ ${message.chunk.byteLength} bytes ]` })
210
+ } else {
211
+ this.log.trace('send message %o', message)
212
+ }
213
+
214
+ const buf = cborg.encode(message)
215
+ const encoded = lp.encode.single(buf, {
216
+ maxDataLength: this.maxMessageSize
217
+ })
218
+
219
+ return this.send(encoded)
220
+ }
221
+
222
+ onMessage (message: StreamMessage): void {
223
+ if (message.type === 'data') {
224
+ this.log.trace('incoming message %o', { ...message, chunk: `[ ${message.chunk.byteLength} bytes ]` })
225
+ } else {
226
+ this.log.trace('incoming message %o', message)
227
+ }
228
+
229
+ let stream: MockMuxedStream | undefined = this.streams.find(s => s.id === message.id)
230
+
231
+ if (message.type === 'create') {
232
+ if (stream != null) {
233
+ throw new Error(`Already had stream for ${message.id}`)
234
+ }
235
+
236
+ this.log.trace('create stream inbound %s', message.id)
237
+ stream = this._createStream(message.id, 'inbound', {
238
+ protocol: message.protocol
239
+ })
240
+
241
+ this.onRemoteStream(stream)
242
+ }
243
+
244
+ if (stream == null) {
245
+ this.log.error(`No stream found for ${message.id}`)
246
+ return
247
+ }
248
+
249
+ if (message.type === 'data') {
250
+ stream.onData(message.chunk)
251
+ } else if (message.type === 'reset') {
252
+ stream.onRemoteReset()
253
+ } else if (message.type === 'closeWrite') {
254
+ stream.onRemoteCloseWrite()
255
+ } else if (message.type === 'closeRead') {
256
+ stream.onRemoteCloseRead()
257
+ } else if (message.type === 'pause') {
258
+ stream.onRemotePaused()
259
+ } else if (message.type === 'resume') {
260
+ stream.onRemoteResumed()
261
+ }
262
+ }
263
+
264
+ async onCreateStream (options: CreateStreamOptions): Promise<MockMuxedStream> {
265
+ this.nextStreamId += 2
266
+
267
+ return this._createStream(`${this.nextStreamId}`, 'outbound', options)
268
+ }
269
+
270
+ _createStream (id: string, direction: MessageStreamDirection, options: CreateStreamOptions): MockMuxedStream {
271
+ this.log.trace('createStream %s %s', direction, id)
272
+
273
+ return new MockMuxedStream({
274
+ ...this.streamOptions,
275
+ ...options,
276
+ id,
277
+ direction,
278
+ log: this.log.newScope(`stream:${direction}:${id}`),
279
+ sendMessage: this.sendMessage,
280
+ encoding: this.encoding,
281
+ maxMessageSize: this.maxMessageSize - MESSAGE_OVERHEAD,
282
+ protocol: ''
283
+ })
284
+ }
285
+ }
286
+
287
+ class MockMuxerFactory implements StreamMuxerFactory {
288
+ public protocol: string = '/mock-muxer/1.0.0'
289
+ private init: MockMuxerInit
290
+
291
+ constructor (init: MockMuxerInit) {
292
+ this.init = init
293
+ }
294
+
295
+ createStreamMuxer (maConn: MultiaddrConnection): StreamMuxer {
296
+ return new MockMuxer(maConn, {
297
+ ...this.init
298
+ })
299
+ }
300
+ }
301
+
302
+ export function mockMuxer (init: MockMuxerInit = {}): StreamMuxerFactory {
303
+ return new MockMuxerFactory(init)
304
+ }
@@ -0,0 +1,101 @@
1
+ import { StreamMessageEvent } from '@libp2p/interface'
2
+ import { defaultLogger } from '@libp2p/logger'
3
+ import { raceSignal } from 'race-signal'
4
+ import { AbstractStream } from './abstract-stream.ts'
5
+ import type { SendResult } from './abstract-message-stream.ts'
6
+ import type { MessageQueue, MessageQueueEvents } from './message-queue.ts'
7
+ import type { AbortOptions, MessageStreamDirection, TypedEventTarget } from '@libp2p/interface'
8
+ import type { Uint8ArrayList } from 'uint8arraylist'
9
+
10
+ interface MockStreamMessages extends MessageQueueEvents {
11
+ closeWrite: Event
12
+ closeRead: Event
13
+ }
14
+
15
+ interface MockStreamInit {
16
+ delay?: number
17
+ direction: MessageStreamDirection
18
+ local: MessageQueue<MockStreamMessages>
19
+ remote: TypedEventTarget<MockStreamMessages>
20
+ }
21
+
22
+ let streamId = 0
23
+
24
+ export class MockStream extends AbstractStream {
25
+ private local: MessageQueue<MockStreamMessages>
26
+ private remote: TypedEventTarget<MockStreamMessages>
27
+
28
+ constructor (init: MockStreamInit) {
29
+ const id = `${streamId++}`
30
+
31
+ super({
32
+ ...init,
33
+ id,
34
+ log: defaultLogger().forComponent(`libp2p:stream-pair:${init.direction}:${id}`)
35
+ })
36
+
37
+ this.local = init.local
38
+ this.remote = init.remote
39
+
40
+ this.local.addEventListener('drain', () => {
41
+ this.safeDispatchEvent('drain')
42
+ })
43
+
44
+ this.remote.addEventListener('message', (evt) => {
45
+ this.onData(evt.data)
46
+ })
47
+ this.remote.addEventListener('reset', (evt) => {
48
+ this.onRemoteReset()
49
+ })
50
+ this.remote.addEventListener('closeWrite', (evt) => {
51
+ this.onRemoteCloseWrite()
52
+ })
53
+ }
54
+
55
+ sendData (data: Uint8ArrayList): SendResult {
56
+ const canSendMore = this.local.send(new StreamMessageEvent(data))
57
+
58
+ return {
59
+ sentBytes: data.byteLength,
60
+ canSendMore
61
+ }
62
+ }
63
+
64
+ sendReset (): void {
65
+ this.local.send(new Event('reset'))
66
+ }
67
+
68
+ async sendCloseWrite (options?: AbortOptions): Promise<void> {
69
+ return raceSignal(new Promise<void>((resolve, reject) => {
70
+ this.local.send(new Event('closeWrite'))
71
+ this.local.onIdle().then(resolve, reject)
72
+ }), options?.signal)
73
+ }
74
+
75
+ async sendCloseRead (options?: AbortOptions): Promise<void> {
76
+ return raceSignal(new Promise<void>((resolve, reject) => {
77
+ this.local.send(new Event('closeRead'))
78
+ this.local.onIdle().then(resolve, reject)
79
+ }), options?.signal)
80
+ }
81
+
82
+ sendPause (): void {
83
+ this.local.send(new Event('pause'))
84
+ }
85
+
86
+ sendResume (): void {
87
+ this.local.send(new Event('resume'))
88
+ }
89
+
90
+ onRemotePaused (): void {
91
+ this.local.pause()
92
+ }
93
+
94
+ onRemoteResumed (): void {
95
+ this.local.resume()
96
+ }
97
+
98
+ onMuxerDrain (): void {
99
+ this.local.resume()
100
+ }
101
+ }