@libp2p/interface-compliance-tests 6.5.0-8484de8a2 → 6.5.0-87bc8d4fb

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 (131) hide show
  1. package/dist/src/connection-encryption/index.d.ts.map +1 -1
  2. package/dist/src/connection-encryption/index.js +15 -24
  3. package/dist/src/connection-encryption/index.js.map +1 -1
  4. package/dist/src/connection-encryption/utils/index.d.ts +3 -0
  5. package/dist/src/connection-encryption/utils/index.d.ts.map +1 -0
  6. package/dist/src/connection-encryption/utils/index.js +21 -0
  7. package/dist/src/connection-encryption/utils/index.js.map +1 -0
  8. package/dist/src/matchers.d.ts +12 -0
  9. package/dist/src/matchers.d.ts.map +1 -0
  10. package/dist/src/matchers.js +14 -0
  11. package/dist/src/matchers.js.map +1 -0
  12. package/dist/src/mocks/connection-manager.d.ts +27 -0
  13. package/dist/src/mocks/connection-manager.d.ts.map +1 -0
  14. package/dist/src/mocks/connection-manager.js +147 -0
  15. package/dist/src/mocks/connection-manager.js.map +1 -0
  16. package/dist/src/mocks/connection.d.ts +41 -0
  17. package/dist/src/mocks/connection.d.ts.map +1 -0
  18. package/dist/src/mocks/connection.js +236 -0
  19. package/dist/src/mocks/connection.js.map +1 -0
  20. package/dist/src/mocks/duplex.d.ts +4 -0
  21. package/dist/src/mocks/duplex.d.ts.map +1 -0
  22. package/dist/src/mocks/duplex.js +9 -0
  23. package/dist/src/mocks/duplex.js.map +1 -0
  24. package/dist/src/mocks/index.d.ts +12 -0
  25. package/dist/src/mocks/index.d.ts.map +1 -0
  26. package/dist/src/mocks/index.js +8 -0
  27. package/dist/src/mocks/index.js.map +1 -0
  28. package/dist/src/mocks/multiaddr-connection.d.ts +17 -0
  29. package/dist/src/mocks/multiaddr-connection.d.ts.map +1 -0
  30. package/dist/src/mocks/multiaddr-connection.js +64 -0
  31. package/dist/src/mocks/multiaddr-connection.js.map +1 -0
  32. package/dist/src/mocks/muxer.d.ts +36 -0
  33. package/dist/src/mocks/muxer.d.ts.map +1 -0
  34. package/dist/src/mocks/muxer.js +234 -0
  35. package/dist/src/mocks/muxer.js.map +1 -0
  36. package/dist/src/mocks/registrar.d.ts +16 -0
  37. package/dist/src/mocks/registrar.d.ts.map +1 -0
  38. package/dist/src/mocks/registrar.js +66 -0
  39. package/dist/src/mocks/registrar.js.map +1 -0
  40. package/dist/src/mocks/upgrader.d.ts +9 -0
  41. package/dist/src/mocks/upgrader.d.ts.map +1 -0
  42. package/dist/src/mocks/upgrader.js +46 -0
  43. package/dist/src/mocks/upgrader.js.map +1 -0
  44. package/dist/src/pubsub/api.d.ts +6 -0
  45. package/dist/src/pubsub/api.d.ts.map +1 -0
  46. package/dist/src/pubsub/api.js +88 -0
  47. package/dist/src/pubsub/api.js.map +1 -0
  48. package/dist/src/pubsub/connection-handlers.d.ts +6 -0
  49. package/dist/src/pubsub/connection-handlers.d.ts.map +1 -0
  50. package/dist/src/pubsub/connection-handlers.js +329 -0
  51. package/dist/src/pubsub/connection-handlers.js.map +1 -0
  52. package/dist/src/pubsub/emit-self.d.ts +6 -0
  53. package/dist/src/pubsub/emit-self.d.ts.map +1 -0
  54. package/dist/src/pubsub/emit-self.js +80 -0
  55. package/dist/src/pubsub/emit-self.js.map +1 -0
  56. package/dist/src/pubsub/index.d.ts +18 -0
  57. package/dist/src/pubsub/index.d.ts.map +1 -0
  58. package/dist/src/pubsub/index.js +17 -0
  59. package/dist/src/pubsub/index.js.map +1 -0
  60. package/dist/src/pubsub/messages.d.ts +6 -0
  61. package/dist/src/pubsub/messages.d.ts.map +1 -0
  62. package/dist/src/pubsub/messages.js +48 -0
  63. package/dist/src/pubsub/messages.js.map +1 -0
  64. package/dist/src/pubsub/multiple-nodes.d.ts +6 -0
  65. package/dist/src/pubsub/multiple-nodes.d.ts.map +1 -0
  66. package/dist/src/pubsub/multiple-nodes.js +350 -0
  67. package/dist/src/pubsub/multiple-nodes.js.map +1 -0
  68. package/dist/src/pubsub/two-nodes.d.ts +6 -0
  69. package/dist/src/pubsub/two-nodes.d.ts.map +1 -0
  70. package/dist/src/pubsub/two-nodes.js +216 -0
  71. package/dist/src/pubsub/two-nodes.js.map +1 -0
  72. package/dist/src/pubsub/utils.d.ts +5 -0
  73. package/dist/src/pubsub/utils.d.ts.map +1 -0
  74. package/dist/src/pubsub/utils.js +27 -0
  75. package/dist/src/pubsub/utils.js.map +1 -0
  76. package/dist/src/stream-muxer/base-test.d.ts.map +1 -1
  77. package/dist/src/stream-muxer/base-test.js +345 -62
  78. package/dist/src/stream-muxer/base-test.js.map +1 -1
  79. package/dist/src/stream-muxer/close-test.d.ts.map +1 -1
  80. package/dist/src/stream-muxer/close-test.js +320 -253
  81. package/dist/src/stream-muxer/close-test.js.map +1 -1
  82. package/dist/src/stream-muxer/index.js +2 -2
  83. package/dist/src/stream-muxer/index.js.map +1 -1
  84. package/dist/src/stream-muxer/{stream-test.d.ts → mega-stress-test.d.ts} +2 -2
  85. package/dist/src/stream-muxer/mega-stress-test.d.ts.map +1 -0
  86. package/dist/src/stream-muxer/mega-stress-test.js +11 -0
  87. package/dist/src/stream-muxer/mega-stress-test.js.map +1 -0
  88. package/dist/src/stream-muxer/spawner.d.ts +4 -0
  89. package/dist/src/stream-muxer/spawner.d.ts.map +1 -0
  90. package/dist/src/stream-muxer/spawner.js +39 -0
  91. package/dist/src/stream-muxer/spawner.js.map +1 -0
  92. package/dist/src/stream-muxer/stress-test.d.ts.map +1 -1
  93. package/dist/src/stream-muxer/stress-test.js +16 -70
  94. package/dist/src/stream-muxer/stress-test.js.map +1 -1
  95. package/dist/src/transport/index.d.ts.map +1 -1
  96. package/dist/src/transport/index.js +203 -232
  97. package/dist/src/transport/index.js.map +1 -1
  98. package/dist/src/transport/utils.js +2 -2
  99. package/dist/src/transport/utils.js.map +1 -1
  100. package/package.json +51 -23
  101. package/src/connection-encryption/index.ts +20 -27
  102. package/src/connection-encryption/utils/index.ts +27 -0
  103. package/src/matchers.ts +18 -0
  104. package/src/mocks/connection-manager.ts +216 -0
  105. package/src/mocks/connection.ts +309 -0
  106. package/src/mocks/duplex.ts +11 -0
  107. package/src/mocks/index.ts +11 -0
  108. package/src/mocks/multiaddr-connection.ts +80 -0
  109. package/src/mocks/muxer.ts +331 -0
  110. package/src/mocks/registrar.ts +86 -0
  111. package/src/mocks/upgrader.ts +65 -0
  112. package/src/pubsub/api.ts +116 -0
  113. package/src/pubsub/connection-handlers.ts +413 -0
  114. package/src/pubsub/emit-self.ts +99 -0
  115. package/src/pubsub/index.ts +34 -0
  116. package/src/pubsub/messages.ts +59 -0
  117. package/src/pubsub/multiple-nodes.ts +440 -0
  118. package/src/pubsub/two-nodes.ts +272 -0
  119. package/src/pubsub/utils.ts +34 -0
  120. package/src/stream-muxer/base-test.ts +413 -75
  121. package/src/stream-muxer/close-test.ts +343 -304
  122. package/src/stream-muxer/index.ts +2 -2
  123. package/src/stream-muxer/mega-stress-test.ts +14 -0
  124. package/src/stream-muxer/spawner.ts +57 -0
  125. package/src/stream-muxer/stress-test.ts +18 -92
  126. package/src/transport/index.ts +241 -280
  127. package/src/transport/utils.ts +2 -2
  128. package/dist/src/stream-muxer/stream-test.d.ts.map +0 -1
  129. package/dist/src/stream-muxer/stream-test.js +0 -289
  130. package/dist/src/stream-muxer/stream-test.js.map +0 -1
  131. package/src/stream-muxer/stream-test.ts +0 -380
@@ -1,80 +1,104 @@
1
1
  /* eslint max-nested-callbacks: ["error", 8] */
2
- import { multiaddrConnectionPair, echo, pbStream } from '@libp2p/utils'
2
+ import { abortableSource } from 'abortable-iterator'
3
3
  import { expect } from 'aegir/chai'
4
4
  import delay from 'delay'
5
5
  import all from 'it-all'
6
- import map from 'it-map'
7
- import { pEvent } from 'p-event'
6
+ import drain from 'it-drain'
7
+ import { duplexPair } from 'it-pair/duplex'
8
+ import { pipe } from 'it-pipe'
9
+ import { pbStream } from 'it-protobuf-stream'
10
+ import toBuffer from 'it-to-buffer'
11
+ import pDefer from 'p-defer'
8
12
  import { Uint8ArrayList } from 'uint8arraylist'
9
13
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
10
14
  import { Message } from './fixtures/pb/message.js'
11
15
  import type { TestSetup } from '../index.js'
12
- import type { MultiaddrConnection, Stream, StreamMuxer, StreamMuxerFactory } from '@libp2p/interface'
16
+ import type { StreamMuxerFactory } from '@libp2p/interface'
13
17
 
14
18
  function randomBuffer (): Uint8Array {
15
19
  return uint8ArrayFromString(Math.random().toString())
16
20
  }
17
21
 
18
- async function * infiniteRandom (): AsyncGenerator<Uint8ArrayList, void, unknown> {
19
- while (true) {
20
- await delay(10)
21
- yield new Uint8ArrayList(randomBuffer())
22
- }
23
- }
22
+ function infiniteRandom (): AsyncGenerator<Uint8ArrayList, void, unknown> {
23
+ let done: Error | boolean = false
24
24
 
25
- export default (common: TestSetup<StreamMuxerFactory>): void => {
26
- describe('close', () => {
27
- let outboundConnection: MultiaddrConnection
28
- let inboundConnection: MultiaddrConnection
29
- let dialer: StreamMuxer
30
- let listener: StreamMuxer
25
+ const generator: AsyncGenerator<Uint8ArrayList, void, unknown> = {
26
+ [Symbol.asyncIterator]: () => {
27
+ return generator
28
+ },
29
+ async next () {
30
+ await delay(10)
31
31
 
32
- beforeEach(async () => {
33
- [outboundConnection, inboundConnection] = multiaddrConnectionPair()
32
+ if (done instanceof Error) {
33
+ throw done
34
+ }
34
35
 
35
- const dialerFactory = await common.setup()
36
- dialer = dialerFactory.createStreamMuxer(outboundConnection)
36
+ if (done) {
37
+ return {
38
+ done: true,
39
+ value: undefined
40
+ }
41
+ }
37
42
 
38
- const listenerFactory = await common.setup()
39
- listener = listenerFactory.createStreamMuxer(inboundConnection)
40
- })
43
+ return {
44
+ done: false,
45
+ value: new Uint8ArrayList(randomBuffer())
46
+ }
47
+ },
48
+ async return (): Promise<IteratorReturnResult<void>> {
49
+ done = true
41
50
 
42
- afterEach(async () => {
43
- await dialer?.close()
44
- await listener?.close()
45
- })
51
+ return {
52
+ done: true,
53
+ value: undefined
54
+ }
55
+ },
56
+ async throw (err: Error): Promise<IteratorReturnResult<void>> {
57
+ done = err
58
+
59
+ return {
60
+ done: true,
61
+ value: undefined
62
+ }
63
+ }
64
+ }
46
65
 
47
- it('closing underlying MultiaddrConnection closes streams', async () => {
66
+ return generator
67
+ }
68
+
69
+ export default (common: TestSetup<StreamMuxerFactory>): void => {
70
+ describe('close', () => {
71
+ it('closing underlying socket closes streams', async () => {
48
72
  let openedStreams = 0
49
73
  const expectedStreams = 5
74
+ const dialerFactory = await common.setup()
75
+ const dialer = dialerFactory.createStreamMuxer({
76
+ direction: 'outbound'
77
+ })
50
78
 
51
- listener.addEventListener('stream', (evt) => {
52
- openedStreams++
53
-
54
- echo(evt.detail)
79
+ // Listener is echo server :)
80
+ const listenerFactory = await common.setup()
81
+ const listener = listenerFactory.createStreamMuxer({
82
+ direction: 'inbound',
83
+ onIncomingStream: (stream) => {
84
+ openedStreams++
85
+ void pipe(stream, stream)
86
+ }
55
87
  })
56
88
 
57
- const streams = await Promise.all(
58
- Array(expectedStreams).fill(0).map(async () => dialer.createStream())
59
- )
89
+ const p = duplexPair<Uint8Array | Uint8ArrayList>()
90
+ void pipe(p[0], dialer, p[0])
91
+ void pipe(p[1], listener, p[1])
92
+
93
+ const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.newStream()))
60
94
 
61
95
  void Promise.all(
62
96
  streams.map(async stream => {
63
- for await (const buf of infiniteRandom()) {
64
- if (stream.status !== 'open') {
65
- return
66
- }
67
-
68
- const sendMore = stream.send(buf)
69
-
70
- if (!sendMore) {
71
- await pEvent(stream, 'drain', {
72
- rejectionEvents: [
73
- 'close'
74
- ]
75
- })
76
- }
77
- }
97
+ await pipe(
98
+ infiniteRandom(),
99
+ stream,
100
+ drain
101
+ )
78
102
  })
79
103
  )
80
104
 
@@ -82,9 +106,7 @@ export default (common: TestSetup<StreamMuxerFactory>): void => {
82
106
 
83
107
  // Pause, and then close the dialer
84
108
  await delay(50)
85
- await inboundConnection.close()
86
- await outboundConnection.close()
87
- await delay(50)
109
+ await pipe(async function * () {}, dialer, drain)
88
110
 
89
111
  expect(openedStreams).to.have.equal(expectedStreams)
90
112
  expect(dialer.streams).to.have.lengthOf(0)
@@ -93,37 +115,36 @@ export default (common: TestSetup<StreamMuxerFactory>): void => {
93
115
  it('calling close closes streams', async () => {
94
116
  let openedStreams = 0
95
117
  const expectedStreams = 5
118
+ const dialerFactory = await common.setup()
119
+ const dialer = dialerFactory.createStreamMuxer({
120
+ direction: 'outbound'
121
+ })
96
122
 
97
- listener.addEventListener('stream', (evt) => {
98
- openedStreams++
99
-
100
- echo(evt.detail)
123
+ // Listener is echo server :)
124
+ const listenerFactory = await common.setup()
125
+ const listener = listenerFactory.createStreamMuxer({
126
+ direction: 'inbound',
127
+ onIncomingStream: (stream) => {
128
+ openedStreams++
129
+ void pipe(stream, stream).catch(() => {})
130
+ }
101
131
  })
102
132
 
103
- const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.createStream()))
133
+ const p = duplexPair<Uint8Array | Uint8ArrayList>()
134
+ void pipe(p[0], dialer, p[0])
135
+ void pipe(p[1], listener, p[1])
136
+
137
+ const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.newStream()))
104
138
 
105
139
  void Promise.all(
106
140
  streams.map(async stream => {
107
- for await (const buf of infiniteRandom()) {
108
- if (stream.status !== 'open') {
109
- return
110
- }
111
-
112
- const sendMore = stream.send(buf)
113
-
114
- if (!sendMore) {
115
- await pEvent(stream, 'drain', {
116
- rejectionEvents: [
117
- 'close'
118
- ]
119
- })
120
- }
121
- }
141
+ await pipe(
142
+ infiniteRandom(),
143
+ stream,
144
+ drain
145
+ )
122
146
  })
123
147
  )
124
- .catch(() => {
125
- // calling .send on a closed stream will throw so swallow any errors
126
- })
127
148
 
128
149
  expect(dialer.streams, 'dialer - number of opened streams should match number of calls to newStream').to.have.lengthOf(expectedStreams)
129
150
 
@@ -132,328 +153,346 @@ export default (common: TestSetup<StreamMuxerFactory>): void => {
132
153
 
133
154
  await dialer.close()
134
155
 
135
- await delay(50)
136
-
137
156
  expect(openedStreams, 'listener - number of opened streams should match number of calls to newStream').to.have.equal(expectedStreams)
138
157
  expect(dialer.streams, 'all tracked streams should be deleted after the muxer has called close').to.have.lengthOf(0)
139
158
  })
140
159
 
141
- it('calling abort aborts streams', async () => {
160
+ it('calling close with an error aborts streams', async () => {
142
161
  let openedStreams = 0
143
162
  const expectedStreams = 5
163
+ const dialerFactory = await common.setup()
164
+ const dialer = dialerFactory.createStreamMuxer({
165
+ direction: 'outbound'
166
+ })
144
167
 
145
- listener.addEventListener('stream', (evt) => {
146
- openedStreams++
147
-
148
- echo(evt.detail)
168
+ // Listener is echo server :)
169
+ const listenerFactory = await common.setup()
170
+ const listener = listenerFactory.createStreamMuxer({
171
+ direction: 'inbound',
172
+ onIncomingStream: (stream) => {
173
+ openedStreams++
174
+ void pipe(stream, stream).catch(() => {})
175
+ }
149
176
  })
150
177
 
151
- const streams = await Promise.all(
152
- Array(expectedStreams).fill(0).map(async () => dialer.createStream())
153
- )
178
+ const p = duplexPair<Uint8Array | Uint8ArrayList>()
179
+ void pipe(p[0], dialer, p[0])
180
+ void pipe(p[1], listener, p[1])
181
+
182
+ const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.newStream()))
154
183
 
155
184
  const streamPipes = streams.map(async stream => {
156
- for await (const buf of infiniteRandom()) {
157
- if (stream.writeStatus !== 'writable') {
158
- break
159
- }
185
+ await pipe(
186
+ infiniteRandom(),
187
+ stream,
188
+ drain
189
+ )
190
+ })
160
191
 
161
- const sendMore = stream.send(buf)
192
+ expect(dialer.streams, 'dialer - number of opened streams should match number of calls to newStream').to.have.lengthOf(expectedStreams)
162
193
 
163
- if (!sendMore) {
164
- await pEvent(stream, 'drain', {
165
- rejectionEvents: ['close']
166
- })
167
- }
168
- }
169
- })
194
+ // Pause, and then close the dialer
195
+ await delay(50)
170
196
 
171
- expect(dialer.streams).to.have.lengthOf(expectedStreams, 'dialer - number of opened streams should match number of calls to createStream')
197
+ // close _with an error_
198
+ dialer.abort(new Error('Oh no!'))
172
199
 
173
200
  const timeoutError = new Error('timeout')
174
-
175
- await Promise.all([
176
- // Pause, and then close the dialer
177
- delay(50).then(() => {
178
- // close _with an error_
179
- dialer.abort(new Error('Oh no!'))
180
- }),
181
- ...streamPipes.map(async pipe => {
182
- try {
183
- await Promise.race([
184
- pipe,
185
- new Promise((resolve, reject) => {
186
- setTimeout(() => {
187
- reject(timeoutError)
188
- }, 70)
189
- })
190
- ])
191
- expect.fail('stream pipe with infinite source should never return')
192
- } catch (e) {
193
- if (e === timeoutError) {
194
- expect.fail('expected stream pipe to throw an error after muxer closed with error')
195
- }
201
+ for (const pipe of streamPipes) {
202
+ try {
203
+ await Promise.race([
204
+ pipe,
205
+ new Promise((_resolve, reject) => setTimeout(() => { reject(timeoutError) }, 20))
206
+ ])
207
+ expect.fail('stream pipe with infinite source should never return')
208
+ } catch (e) {
209
+ if (e === timeoutError) {
210
+ expect.fail('expected stream pipe to throw an error after muxer closed with error')
196
211
  }
197
- })
198
- ])
212
+ }
213
+ }
199
214
 
200
- expect(openedStreams).to.equal(expectedStreams, 'listener - number of opened streams should match number of calls to createStream')
201
- expect(dialer.streams).to.have.lengthOf(0, 'all tracked streams should be deleted after the muxer has called close')
215
+ expect(openedStreams, 'listener - number of opened streams should match number of calls to newStream').to.have.equal(expectedStreams)
216
+ expect(dialer.streams, 'all tracked streams should be deleted after the muxer has called close').to.have.lengthOf(0)
202
217
  })
203
218
 
204
219
  it('calling newStream after close throws an error', async () => {
220
+ const dialerFactory = await common.setup()
221
+ const dialer = dialerFactory.createStreamMuxer({
222
+ direction: 'outbound'
223
+ })
224
+
205
225
  await dialer.close()
206
- await expect(dialer.createStream()).to.eventually.rejected.with.property('name', 'MuxerClosedError')
207
- expect(dialer.streams).to.have.lengthOf(0, 'closed muxer should have no streams')
226
+
227
+ try {
228
+ await dialer.newStream()
229
+ expect.fail('newStream should throw if called after close')
230
+ } catch (e) {
231
+ expect(dialer.streams, 'closed muxer should have no streams').to.have.lengthOf(0)
232
+ }
208
233
  })
209
234
 
210
235
  it('closing one of the muxed streams doesn\'t close others', async () => {
211
- const streamCount = 5
212
- const allStreamsOpen = Promise.withResolvers<void>()
213
-
214
- listener.addEventListener('stream', (evt) => {
215
- echo(evt.detail).catch(() => {})
236
+ const p = duplexPair<Uint8Array | Uint8ArrayList>()
237
+ const dialerFactory = await common.setup()
238
+ const dialer = dialerFactory.createStreamMuxer({
239
+ direction: 'outbound'
240
+ })
216
241
 
217
- if (listener.streams.length === streamCount) {
218
- allStreamsOpen.resolve()
242
+ // Listener is echo server :)
243
+ const listenerFactory = await common.setup()
244
+ const listener = listenerFactory.createStreamMuxer({
245
+ direction: 'inbound',
246
+ onIncomingStream: (stream) => {
247
+ void pipe(stream, stream).catch(() => {})
219
248
  }
220
249
  })
221
250
 
222
- const streams = await Promise.all(
223
- Array.from(Array(streamCount), async () => dialer.createStream())
224
- )
225
- await allStreamsOpen.promise
251
+ void pipe(p[0], dialer, p[0])
252
+ void pipe(p[1], listener, p[1])
226
253
 
227
- expect(dialer.streams).to.have.lengthOf(streamCount)
228
- expect(listener.streams).to.have.lengthOf(streamCount)
254
+ const stream = await dialer.newStream()
255
+ const streams = await Promise.all(Array.from(Array(5), async () => dialer.newStream()))
256
+ let closed = false
257
+ const controllers: AbortController[] = []
229
258
 
230
- expect(dialer.streams.map(s => s.status)).to.deep.equal(new Array(streamCount).fill('open'))
231
- expect(listener.streams.map(s => s.status)).to.deep.equal(new Array(streamCount).fill('open'))
259
+ const streamResults = streams.map(async stream => {
260
+ const controller = new AbortController()
261
+ controllers.push(controller)
232
262
 
233
- const localStream = streams[0]
234
- const remoteStream = listener.streams[0]
263
+ try {
264
+ const abortableRand = abortableSource(infiniteRandom(), controller.signal, {
265
+ abortName: 'TestAbortError'
266
+ })
267
+ await pipe(abortableRand, stream, drain)
268
+ } catch (err: any) {
269
+ if (err.name !== 'TestAbortError') { throw err }
270
+ }
271
+
272
+ if (!closed) { throw new Error('stream should not have ended yet!') }
273
+ })
235
274
 
236
- await Promise.all([
237
- pEvent(remoteStream, 'close'),
238
- pEvent(localStream, 'close'),
239
- localStream.close()
240
- ])
275
+ // Pause, and then send some data and close the first stream
276
+ await delay(50)
277
+ await pipe([new Uint8ArrayList(randomBuffer())], stream, drain)
278
+ closed = true
241
279
 
242
- expect(dialer.streams).to.have.lengthOf(streamCount - 1)
243
- expect(listener.streams).to.have.lengthOf(streamCount - 1)
280
+ // Abort all the other streams later
281
+ await delay(50)
282
+ controllers.forEach(c => { c.abort() })
244
283
 
245
- expect(dialer.streams.map(s => s.status)).to.deep.equal(new Array(streamCount - 1).fill('open'))
246
- expect(listener.streams.map(s => s.status)).to.deep.equal(new Array(streamCount - 1).fill('open'))
284
+ // These should now all resolve without error
285
+ await Promise.all(streamResults)
247
286
  })
248
287
 
249
288
  it('can close a stream for writing', async () => {
250
- const deferred = Promise.withResolvers<Error>()
251
- const data = [Uint8Array.from([0, 1, 2, 3, 4]), Uint8Array.from([5, 6, 7, 8, 9])]
289
+ const deferred = pDefer<Error>()
290
+
291
+ const p = duplexPair<Uint8Array | Uint8ArrayList>()
292
+ const dialerFactory = await common.setup()
293
+ const dialer = dialerFactory.createStreamMuxer({
294
+ direction: 'outbound'
295
+ })
296
+ const data = [randomBuffer(), randomBuffer()]
252
297
 
253
- listener.addEventListener('stream', (evt) => {
254
- void Promise.resolve().then(async () => {
255
- try {
298
+ const listenerFactory = await common.setup()
299
+ const listener = listenerFactory.createStreamMuxer({
300
+ direction: 'inbound',
301
+ onIncomingStream: (stream) => {
302
+ void Promise.resolve().then(async () => {
256
303
  // Immediate close for write
257
- await evt.detail.close({
258
- signal: AbortSignal.timeout(1_000)
304
+ await stream.closeWrite()
305
+
306
+ const results = await pipe(stream, async (source) => {
307
+ const data = []
308
+ for await (const chunk of source) {
309
+ data.push(chunk.slice())
310
+ }
311
+ return data
259
312
  })
260
-
261
- const results = await all(map(evt.detail, (buf) => {
262
- return buf.subarray()
263
- }))
264
-
265
- expect(results).to.deep.equal(data)
313
+ expect(results).to.eql(data)
266
314
 
267
315
  try {
268
- evt.detail.send(randomBuffer())
316
+ await stream.sink([new Uint8ArrayList(randomBuffer())])
269
317
  } catch (err: any) {
270
318
  deferred.resolve(err)
271
319
  }
272
320
 
273
- throw new Error('should not support writing to closed writer')
274
- } catch (err) {
275
- deferred.reject(err)
276
- }
277
- })
278
- })
279
-
280
- const stream = await dialer.createStream()
281
-
282
- for (const buf of data) {
283
- if (!stream.send(buf)) {
284
- await pEvent(stream, 'drain', {
285
- rejectionEvents: [
286
- 'close'
287
- ]
321
+ deferred.reject(new Error('should not support writing to closed writer'))
288
322
  })
289
323
  }
290
- }
291
-
292
- await stream.close({
293
- signal: AbortSignal.timeout(1_000)
294
324
  })
295
325
 
326
+ void pipe(p[0], dialer, p[0])
327
+ void pipe(p[1], listener, p[1])
328
+
329
+ const stream = await dialer.newStream()
330
+ await stream.sink(data)
331
+
296
332
  const err = await deferred.promise
297
333
  expect(err).to.have.property('name', 'StreamStateError')
298
334
  })
299
335
 
300
- it('should emit a close event for closed streams not previously written', async () => {
301
- listener.addEventListener('stream', async (evt) => {
302
- void evt.detail.close()
336
+ it('can close a stream for reading', async () => {
337
+ const deferred = pDefer<Uint8ArrayList[]>()
338
+ const p = duplexPair<Uint8Array | Uint8ArrayList>()
339
+ const dialerFactory = await common.setup()
340
+ const dialer = dialerFactory.createStreamMuxer({
341
+ direction: 'outbound'
342
+ })
343
+ const data = [randomBuffer(), randomBuffer()].map(d => new Uint8ArrayList(d))
344
+ const expected = toBuffer(data.map(d => d.subarray()))
345
+
346
+ const listenerFactory = await common.setup()
347
+ const listener = listenerFactory.createStreamMuxer({
348
+ direction: 'inbound',
349
+ onIncomingStream: (stream) => {
350
+ void all(stream.source).then(deferred.resolve, deferred.reject)
351
+ }
352
+ })
353
+
354
+ void pipe(p[0], dialer, p[0])
355
+ void pipe(p[1], listener, p[1])
356
+
357
+ const stream = await dialer.newStream()
358
+ await stream.closeRead()
359
+
360
+ // Source should be done
361
+ void Promise.resolve().then(async () => {
362
+ expect(await stream.source.next()).to.have.property('done', true)
363
+ await stream.sink(data)
303
364
  })
304
365
 
305
- const deferred = Promise.withResolvers<void>()
306
- const stream = await dialer.createStream()
307
- stream.addEventListener('close', () => {
308
- deferred.resolve()
366
+ const results = await deferred.promise
367
+ expect(toBuffer(results.map(b => b.subarray()))).to.equalBytes(expected)
368
+ })
369
+
370
+ it('calls onStreamEnd for closed streams not previously written', async () => {
371
+ const deferred = pDefer()
372
+
373
+ const onStreamEnd = (): void => { deferred.resolve() }
374
+ const dialerFactory = await common.setup()
375
+ const dialer = dialerFactory.createStreamMuxer({
376
+ direction: 'outbound',
377
+ onStreamEnd
309
378
  })
310
379
 
380
+ const stream = await dialer.newStream()
381
+
311
382
  await stream.close()
312
383
  await deferred.promise
313
384
  })
314
385
 
315
- it('should emit a close event for aborted streams not previously written', async () => {
316
- const deferred = Promise.withResolvers<void>()
317
- const stream = await dialer.createStream()
318
- stream.addEventListener('close', () => {
319
- deferred.resolve()
386
+ it('calls onStreamEnd for read and write closed streams not previously written', async () => {
387
+ const deferred = pDefer()
388
+
389
+ const onStreamEnd = (): void => { deferred.resolve() }
390
+ const dialerFactory = await common.setup()
391
+ const dialer = dialerFactory.createStreamMuxer({
392
+ direction: 'outbound',
393
+ onStreamEnd
320
394
  })
321
395
 
322
- stream.abort(new Error('Urk!'))
396
+ const stream = await dialer.newStream()
397
+
398
+ await stream.closeWrite()
399
+ await stream.closeRead()
323
400
  await deferred.promise
324
401
  })
325
402
 
326
403
  it('should wait for all data to be sent when closing streams', async () => {
327
- const deferred = Promise.withResolvers<Message>()
404
+ const deferred = pDefer<Message>()
328
405
 
329
- listener.addEventListener('stream', (evt) => {
330
- const pb = pbStream(evt.detail)
406
+ const p = duplexPair<Uint8Array | Uint8ArrayList>()
407
+ const dialerFactory = await common.setup()
408
+ const dialer = dialerFactory.createStreamMuxer({
409
+ direction: 'outbound'
410
+ })
331
411
 
332
- void pb.read(Message)
333
- .then(async message => {
334
- deferred.resolve(message)
335
- await evt.detail.close()
336
- })
337
- .catch(err => {
338
- deferred.reject(err)
339
- })
412
+ const listenerFactory = await common.setup()
413
+ const listener = listenerFactory.createStreamMuxer({
414
+ direction: 'inbound',
415
+ onIncomingStream: (stream) => {
416
+ const pb = pbStream(stream)
417
+
418
+ void pb.read(Message)
419
+ .then(async message => {
420
+ deferred.resolve(message)
421
+ await pb.unwrap().close()
422
+ })
423
+ .catch(err => {
424
+ deferred.reject(err)
425
+ })
426
+ }
340
427
  })
341
428
 
429
+ void pipe(p[0], dialer, p[0])
430
+ void pipe(p[1], listener, p[1])
431
+
342
432
  const message = {
343
433
  message: 'hello world',
344
434
  value: 5,
345
435
  flag: true
346
436
  }
347
437
 
348
- const stream = await dialer.createStream()
438
+ const stream = await dialer.newStream()
349
439
 
350
440
  const pb = pbStream(stream)
351
441
  await pb.write(message, Message)
352
- await stream.close()
442
+ await pb.unwrap().close()
353
443
 
354
444
  await expect(deferred.promise).to.eventually.deep.equal(message)
355
445
  })
446
+ /*
447
+ it('should abort closing a stream with outstanding data to read', async () => {
448
+ const deferred = pDefer<Message>()
356
449
 
357
- it('should remove a stream in the streams list after aborting', async () => {
358
- const [
359
- listenerStream,
360
- dialerStream
361
- ] = await Promise.all([
362
- pEvent<'stream', CustomEvent<Stream>>(listener, 'stream').then(evt => evt.detail),
363
- dialer.createStream()
364
- ])
365
-
366
- expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream')
367
- expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream')
368
-
369
- await Promise.all([
370
- pEvent(listenerStream, 'close'),
371
- dialerStream.abort(new Error('Urk!'))
372
- ])
373
-
374
- expect(dialer.streams).to.not.include(dialerStream, 'dialer did not remove outbound stream close')
375
- expect(listener.streams).to.not.include(listenerStream, 'listener did not remove inbound stream after close')
376
- })
377
-
378
- it('should remove a stream in the streams list after closing', async () => {
379
- const [
380
- listenerStream,
381
- dialerStream
382
- ] = await Promise.all([
383
- pEvent<'stream', CustomEvent<Stream>>(listener, 'stream').then(evt => evt.detail),
384
- dialer.createStream()
385
- ])
386
-
387
- expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream')
388
- expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream')
389
-
390
- await Promise.all([
391
- dialerStream.close(),
392
- listenerStream.close()
393
- ])
394
-
395
- await delay(10)
396
-
397
- expect(dialer.streams).to.not.include(dialerStream, 'dialer did not remove outbound stream close')
398
- expect(listener.streams).to.not.include(listenerStream, 'listener did not remove inbound stream after close')
399
- })
400
-
401
- it('should not remove a half-closed outbound stream', async () => {
402
- const [
403
- listenerStream,
404
- dialerStream
405
- ] = await Promise.all([
406
- pEvent<'stream', CustomEvent<Stream>>(listener, 'stream').then(evt => evt.detail),
407
- dialer.createStream()
408
- ])
409
-
410
- await dialerStream.close()
411
-
412
- expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream')
413
- expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream')
414
- })
415
-
416
- it('should not remove a half-closed inbound stream', async () => {
417
- const [
418
- listenerStream,
419
- dialerStream
420
- ] = await Promise.all([
421
- pEvent<'stream', CustomEvent<Stream>>(listener, 'stream').then(evt => evt.detail),
422
- dialer.createStream()
423
- ])
424
-
425
- await listenerStream.close()
450
+ const p = duplexPair<Uint8Array | Uint8ArrayList>()
451
+ const dialerFactory = await common.setup()
452
+ const dialer = dialerFactory.createStreamMuxer({ direction: 'outbound' })
426
453
 
427
- expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream')
428
- expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream')
429
- })
454
+ const listenerFactory = await common.setup()
455
+ const listener = listenerFactory.createStreamMuxer({
456
+ direction: 'inbound',
457
+ onIncomingStream: (stream) => {
458
+ const pb = pbStream(stream)
459
+
460
+ void pb.read(Message)
461
+ .then(async message => {
462
+ await pb.write(message, Message)
463
+ await pb.unwrap().close()
464
+ deferred.resolve(message)
465
+ })
466
+ .catch(err => {
467
+ deferred.reject(err)
468
+ })
469
+ }
470
+ })
430
471
 
431
- it('should remove a stream half closed from both ends', async () => {
432
- const [
433
- listenerStream,
434
- dialerStream
435
- ] = await Promise.all([
436
- pEvent<'stream', CustomEvent<Stream>>(listener, 'stream').then(evt => evt.detail),
437
- dialer.createStream()
438
- ])
472
+ void pipe(p[0], dialer, p[0])
473
+ void pipe(p[1], listener, p[1])
439
474
 
440
- expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream')
441
- expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream')
475
+ const message = {
476
+ message: 'hello world',
477
+ value: 5,
478
+ flag: true
479
+ }
442
480
 
443
- await listenerStream.close()
481
+ const stream = await dialer.newStream()
444
482
 
445
- expect(dialer.streams).to.include(dialerStream, 'dialer removed outbound stream before fully closing')
446
- expect(listener.streams).to.include(listenerStream, 'listener removed inbound stream before fully closing')
483
+ const pb = pbStream(stream)
484
+ await pb.write(message, Message)
447
485
 
448
- await Promise.all([
449
- pEvent(listenerStream, 'close'),
450
- dialerStream.close()
451
- ])
486
+ console.info('await write back')
487
+ await deferred.promise
452
488
 
453
- await delay(10)
489
+ // let message arrive
490
+ await delay(100)
454
491
 
455
- expect(dialer.streams).to.not.include(dialerStream, 'dialer did not remove outbound stream close')
456
- expect(listener.streams).to.not.include(listenerStream, 'listener did not remove inbound stream after close')
492
+ // close should time out as message is never read
493
+ await expect(pb.unwrap().close()).to.eventually.be.rejected
494
+ .with.property('name', 'TimeoutError')
457
495
  })
496
+ */
458
497
  })
459
498
  }