@libp2p/interface-compliance-tests 6.5.0 → 7.0.0-049bfa0fa

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