@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,866 @@
1
+ import { StreamMessageEvent, StreamCloseEvent, InvalidParametersError } from '@libp2p/interface'
2
+ import { pipe as itPipe } from 'it-pipe'
3
+ import { pushable } from 'it-pushable'
4
+ import { pEvent } from 'p-event'
5
+ import { raceSignal } from 'race-signal'
6
+ import * as varint from 'uint8-varint'
7
+ import { Uint8ArrayList } from 'uint8arraylist'
8
+ import { UnexpectedEOFError } from './errors.js'
9
+ import type { MessageStream, MultiaddrConnection, Stream, AbortOptions } from '@libp2p/interface'
10
+ import type { Duplex, Source, Transform, Sink } from 'it-stream-types'
11
+
12
+ const DEFAULT_MAX_BUFFER_SIZE = 4_194_304
13
+
14
+ export class UnwrappedError extends Error {
15
+ static name = 'UnwrappedError'
16
+ name = 'UnwrappedError'
17
+ }
18
+
19
+ /**
20
+ * The reported length of the next data message was not a positive integer
21
+ */
22
+ export class InvalidMessageLengthError extends Error {
23
+ name = 'InvalidMessageLengthError'
24
+ code = 'ERR_INVALID_MSG_LENGTH'
25
+ }
26
+
27
+ /**
28
+ * The reported length of the next data message was larger than the configured
29
+ * max allowable value
30
+ */
31
+ export class InvalidDataLengthError extends Error {
32
+ name = 'InvalidDataLengthError'
33
+ code = 'ERR_MSG_DATA_TOO_LONG'
34
+ }
35
+
36
+ /**
37
+ * The varint used to specify the length of the next data message contained more
38
+ * bytes than the configured max allowable value
39
+ */
40
+ export class InvalidDataLengthLengthError extends Error {
41
+ name = 'InvalidDataLengthLengthError'
42
+ code = 'ERR_MSG_LENGTH_TOO_LONG'
43
+ }
44
+
45
+ export interface ByteStreamOpts {
46
+ /**
47
+ * Incoming bytes are buffered until read, this setting limits how many bytes
48
+ * will be buffered.
49
+ *
50
+ * @default 4_194_304
51
+ */
52
+ maxBufferSize?: number
53
+ }
54
+
55
+ export interface ReadBytesOptions extends AbortOptions {
56
+ /**
57
+ * If specified, read this number of bytes from the stream
58
+ */
59
+ bytes: number
60
+ }
61
+
62
+ export interface ByteStream<S extends MessageStream> {
63
+ /**
64
+ * Read bytes from the stream.
65
+ *
66
+ * If a required number of bytes is passed as an option, this will wait for
67
+ * the underlying stream to supply that number of bytes, throwing an
68
+ * `UnexpectedEOFError` if the stream closes before this happens.
69
+ *
70
+ * If no required number of bytes is passed, this will return `null` if the
71
+ * underlying stream closes before supplying any bytes.
72
+ */
73
+ read(options: ReadBytesOptions): Promise<Uint8ArrayList>
74
+ read(options?: AbortOptions): Promise<Uint8ArrayList | null>
75
+
76
+ /**
77
+ * Write the passed bytes to the stream
78
+ */
79
+ write(data: Uint8Array | Uint8ArrayList, options?: AbortOptions): Promise<void>
80
+
81
+ /**
82
+ * After calling this method the stream can no longer be used. Any unread data
83
+ * will be emitted as a message event during the microtask queue of the
84
+ * current event loop tick.
85
+ */
86
+ unwrap(): S
87
+ }
88
+
89
+ function isStream (obj?: any): obj is Stream {
90
+ return typeof obj?.closeRead === 'function'
91
+ }
92
+
93
+ function isMultiaddrConnection (obj?: any): obj is MultiaddrConnection {
94
+ return typeof obj?.close === 'function'
95
+ }
96
+
97
+ function isEOF (obj?: any): boolean {
98
+ if (isStream(obj)) {
99
+ return obj.readStatus === 'closing' || obj.readStatus === 'closed'
100
+ }
101
+
102
+ if (isMultiaddrConnection(obj)) {
103
+ return obj.status !== 'open'
104
+ }
105
+
106
+ return false
107
+ }
108
+
109
+ type ByteStreamReadable = Pick<Stream & MultiaddrConnection, 'addEventListener' | 'removeEventListener' | 'send' | 'push' | 'log'>
110
+
111
+ function isValid (obj?: any): obj is ByteStreamReadable {
112
+ return obj?.addEventListener != null && obj?.removeEventListener != null && obj?.send != null && obj?.push != null && obj?.log != null
113
+ }
114
+
115
+ export function byteStream <T extends MessageStream> (stream: T, opts?: ByteStreamOpts): ByteStream<T> {
116
+ const maxBufferSize = opts?.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE
117
+ const readBuffer = new Uint8ArrayList()
118
+
119
+ let hasBytes: PromiseWithResolvers<void> | undefined
120
+ let unwrapped = false
121
+
122
+ if (!isValid(stream)) {
123
+ throw new InvalidParametersError('Argument should be a Stream or a Multiaddr')
124
+ }
125
+
126
+ const byteStreamOnMessageListener = (evt: StreamMessageEvent): void => {
127
+ readBuffer.append(evt.data)
128
+
129
+ if (readBuffer.byteLength > maxBufferSize) {
130
+ const readBufferSize = readBuffer.byteLength
131
+ readBuffer.consume(readBuffer.byteLength)
132
+ hasBytes?.reject(new Error(`Read buffer overflow - ${readBufferSize} > ${maxBufferSize}`))
133
+ }
134
+
135
+ hasBytes?.resolve()
136
+ }
137
+ stream.addEventListener('message', byteStreamOnMessageListener)
138
+
139
+ const byteStreamOnCloseListener = (evt: StreamCloseEvent): void => {
140
+ if (evt.error != null) {
141
+ hasBytes?.reject(evt.error)
142
+ } else {
143
+ hasBytes?.resolve()
144
+ }
145
+ }
146
+ stream.addEventListener('close', byteStreamOnCloseListener)
147
+
148
+ const byteStreamOnRemoteCloseWrite = (): void => {
149
+ hasBytes?.resolve()
150
+ }
151
+ stream.addEventListener('remoteCloseWrite', byteStreamOnRemoteCloseWrite)
152
+
153
+ const byteStream: ByteStream<T> = {
154
+ readBuffer,
155
+
156
+ // @ts-expect-error options type prevents type inference
157
+ async read (options?: ReadBytesOptions) {
158
+ if (unwrapped === true) {
159
+ throw new UnwrappedError('Stream was unwrapped')
160
+ }
161
+
162
+ if (isEOF(stream)) {
163
+ if (options?.bytes == null) {
164
+ return null
165
+ }
166
+
167
+ if (readBuffer.byteLength < options.bytes) {
168
+ stream.log.error('closed after reading %d/%d bytes', readBuffer.byteLength, options.bytes)
169
+ throw new UnexpectedEOFError(`Unexpected EOF - stream closed after reading ${readBuffer.byteLength}/${options.bytes} bytes`)
170
+ }
171
+ }
172
+
173
+ const bytesToRead = options?.bytes ?? 1
174
+ hasBytes = Promise.withResolvers<void>()
175
+
176
+ while (true) {
177
+ if (readBuffer.byteLength >= bytesToRead) {
178
+ // if we are about to exit the loop this promise will not be awaited
179
+ // so resolve it to prevent and unhandled promise rejections that may
180
+ // occur
181
+ hasBytes.resolve()
182
+
183
+ break
184
+ }
185
+
186
+ await raceSignal(hasBytes.promise, options?.signal)
187
+
188
+ if (isEOF(stream)) {
189
+ if (readBuffer.byteLength === 0 && options?.bytes == null) {
190
+ return null
191
+ }
192
+
193
+ break
194
+ }
195
+
196
+ hasBytes = Promise.withResolvers<void>()
197
+ }
198
+
199
+ const toRead = options?.bytes ?? readBuffer.byteLength
200
+
201
+ if (readBuffer.byteLength < toRead) {
202
+ if (isEOF(stream)) {
203
+ stream.log.error('closed while reading %d/%d bytes', readBuffer.byteLength, toRead)
204
+ throw new UnexpectedEOFError(`Unexpected EOF - stream closed while reading ${readBuffer.byteLength}/${toRead} bytes`)
205
+ }
206
+
207
+ return byteStream.read(options)
208
+ }
209
+
210
+ const output = readBuffer.sublist(0, toRead)
211
+ readBuffer.consume(toRead)
212
+
213
+ return output
214
+ },
215
+ async write (data: Uint8Array | Uint8ArrayList, options?: AbortOptions) {
216
+ if (unwrapped === true) {
217
+ throw new UnwrappedError('Stream was unwrapped')
218
+ }
219
+
220
+ if (!stream.send(data)) {
221
+ await pEvent(stream, 'drain', {
222
+ signal: options?.signal,
223
+ rejectionEvents: ['close']
224
+ })
225
+ }
226
+ },
227
+ unwrap () {
228
+ // already unwrapped, just return the original stream
229
+ if (unwrapped) {
230
+ return stream
231
+ }
232
+
233
+ // only unwrap once
234
+ unwrapped = true
235
+ stream.removeEventListener('message', byteStreamOnMessageListener)
236
+ stream.removeEventListener('close', byteStreamOnCloseListener)
237
+ stream.removeEventListener('remoteCloseWrite', byteStreamOnRemoteCloseWrite)
238
+
239
+ // emit any unread data
240
+ if (readBuffer.byteLength > 0) {
241
+ stream.log('stream unwrapped with %d unread bytes', readBuffer.byteLength)
242
+ stream.push(readBuffer)
243
+ }
244
+
245
+ return stream
246
+ }
247
+ }
248
+
249
+ return byteStream
250
+ }
251
+
252
+ export interface LengthPrefixedStream<S extends MessageStream = MessageStream> {
253
+ /**
254
+ * Read the next length-prefixed number of bytes from the stream
255
+ */
256
+ read(options?: AbortOptions): Promise<Uint8ArrayList>
257
+
258
+ /**
259
+ * Write the passed bytes to the stream prefixed by their length
260
+ */
261
+ write(data: Uint8Array | Uint8ArrayList, options?: AbortOptions): Promise<void>
262
+
263
+ /**
264
+ * Write passed list of bytes, prefix by their individual lengths to the stream as a single write
265
+ */
266
+ writeV(input: Array<Uint8Array | Uint8ArrayList>, options?: AbortOptions): Promise<void>
267
+
268
+ /**
269
+ * Returns the underlying stream
270
+ */
271
+ unwrap(): S
272
+ }
273
+
274
+ export interface LengthPrefixedStreamOpts extends ByteStreamOpts {
275
+ lengthEncoder (value: number): Uint8ArrayList | Uint8Array
276
+ lengthDecoder (data: Uint8ArrayList): number
277
+ maxLengthLength: number
278
+ maxDataLength: number
279
+ }
280
+
281
+ export function lpStream <T extends MessageStream> (stream: T, opts: Partial<LengthPrefixedStreamOpts> = {}): LengthPrefixedStream<T> {
282
+ const bytes = byteStream(stream, opts)
283
+
284
+ if (opts.maxDataLength != null && opts.maxLengthLength == null) {
285
+ // if max data length is set but max length length is not, calculate the
286
+ // max length length needed to encode max data length
287
+ opts.maxLengthLength = varint.encodingLength(opts.maxDataLength)
288
+ }
289
+
290
+ const decodeLength = opts?.lengthDecoder ?? varint.decode
291
+ const encodeLength = opts?.lengthEncoder ?? varint.encode
292
+
293
+ const lpStream: LengthPrefixedStream<any> = {
294
+ async read (options?: AbortOptions) {
295
+ let dataLength: number = -1
296
+ const lengthBuffer = new Uint8ArrayList()
297
+
298
+ while (true) {
299
+ // read one byte at a time until we can decode a varint
300
+ const buf = await bytes.read({
301
+ ...options,
302
+ bytes: 1
303
+ })
304
+
305
+ // the underlying resource closed gracefully
306
+ if (buf == null) {
307
+ break
308
+ }
309
+
310
+ // append byte and try to decode
311
+ lengthBuffer.append(buf)
312
+
313
+ try {
314
+ dataLength = decodeLength(lengthBuffer)
315
+ } catch (err) {
316
+ if (err instanceof RangeError) {
317
+ continue
318
+ }
319
+
320
+ throw err
321
+ }
322
+
323
+ if (dataLength < 0) {
324
+ throw new InvalidMessageLengthError('Invalid message length')
325
+ }
326
+
327
+ if (opts?.maxLengthLength != null && lengthBuffer.byteLength > opts.maxLengthLength) {
328
+ throw new InvalidDataLengthLengthError(`Message length length too long - ${lengthBuffer.byteLength} > ${opts.maxLengthLength}`)
329
+ }
330
+
331
+ if (dataLength > -1) {
332
+ break
333
+ }
334
+ }
335
+
336
+ if (opts?.maxDataLength != null && dataLength > opts.maxDataLength) {
337
+ throw new InvalidDataLengthError(`Message length too long - ${dataLength} > ${opts.maxDataLength}`)
338
+ }
339
+
340
+ const buf = await bytes.read({
341
+ ...options,
342
+ bytes: dataLength
343
+ })
344
+
345
+ if (buf == null) {
346
+ stream.log.error('tried to read %d bytes but the stream closed', dataLength)
347
+ throw new UnexpectedEOFError(`Unexpected EOF - tried to read ${dataLength} bytes but the stream closed`)
348
+ }
349
+
350
+ if (buf.byteLength !== dataLength) {
351
+ stream.log.error('read %d/%d bytes before the stream closed', buf.byteLength, dataLength)
352
+ throw new UnexpectedEOFError(`Unexpected EOF - read ${buf.byteLength}/${dataLength} bytes before the stream closed`)
353
+ }
354
+
355
+ return buf
356
+ },
357
+ async write (data, options?: AbortOptions) {
358
+ // encode, write
359
+ await bytes.write(new Uint8ArrayList(encodeLength(data.byteLength), data), options)
360
+ },
361
+ async writeV (data, options?: AbortOptions) {
362
+ const list = new Uint8ArrayList(
363
+ ...data.flatMap(buf => ([encodeLength(buf.byteLength), buf]))
364
+ )
365
+
366
+ // encode, write
367
+ await bytes.write(list, options)
368
+ },
369
+ unwrap () {
370
+ return bytes.unwrap()
371
+ }
372
+ }
373
+
374
+ return lpStream
375
+ }
376
+
377
+ /**
378
+ * A protobuf decoder - takes a byte array and returns an object
379
+ */
380
+ export interface ProtobufDecoder<T> {
381
+ (data: Uint8Array | Uint8ArrayList): T
382
+ }
383
+
384
+ /**
385
+ * A protobuf encoder - takes an object and returns a byte array
386
+ */
387
+ export interface ProtobufEncoder<T> {
388
+ (data: T): Uint8Array
389
+ }
390
+
391
+ /**
392
+ * Convenience methods for working with protobuf streams
393
+ */
394
+ export interface ProtobufStream<S extends MessageStream = MessageStream> {
395
+ /**
396
+ * Read the next length-prefixed byte array from the stream and decode it as the passed protobuf format
397
+ */
398
+ read<T>(proto: { decode: ProtobufDecoder<T> }, options?: AbortOptions): Promise<T>
399
+
400
+ /**
401
+ * Encode the passed object as a protobuf message and write it's length-prefixed bytes to the stream
402
+ */
403
+ write<T>(data: T, proto: { encode: ProtobufEncoder<T> }, options?: AbortOptions): Promise<void>
404
+
405
+ /**
406
+ * Encode the passed objects as protobuf messages and write their length-prefixed bytes to the stream as a single write
407
+ */
408
+ writeV<T>(input: T[], proto: { encode: ProtobufEncoder<T> }, options?: AbortOptions): Promise<void>
409
+
410
+ /**
411
+ * Returns an object with read/write methods for operating on one specific type of protobuf message
412
+ */
413
+ pb<T>(proto: { encode: ProtobufEncoder<T>, decode: ProtobufDecoder<T> }): ProtobufMessageStream<T, S>
414
+
415
+ /**
416
+ * Returns the underlying stream
417
+ */
418
+ unwrap(): S
419
+ }
420
+
421
+ /**
422
+ * A message reader/writer that only uses one type of message
423
+ */
424
+ export interface ProtobufMessageStream <T, S extends MessageStream = MessageStream> {
425
+ /**
426
+ * Read a message from the stream
427
+ */
428
+ read(options?: AbortOptions): Promise<T>
429
+
430
+ /**
431
+ * Write a message to the stream
432
+ */
433
+ write(d: T, options?: AbortOptions): Promise<void>
434
+
435
+ /**
436
+ * Write several messages to the stream
437
+ */
438
+ writeV(d: T[], options?: AbortOptions): Promise<void>
439
+
440
+ /**
441
+ * Unwrap the underlying protobuf stream
442
+ */
443
+ unwrap(): ProtobufStream<S>
444
+ }
445
+
446
+ export interface ProtobufStreamOpts extends LengthPrefixedStreamOpts {
447
+
448
+ }
449
+
450
+ export function pbStream <T extends MessageStream = Stream> (stream: T, opts?: Partial<ProtobufStreamOpts>): ProtobufStream<T> {
451
+ const lp = lpStream(stream, opts)
452
+
453
+ const pbStream: ProtobufStream<T> = {
454
+ read: async (proto, options?: AbortOptions) => {
455
+ // readLP, decode
456
+ const value = await lp.read(options)
457
+
458
+ return proto.decode(value)
459
+ },
460
+ write: async (message, proto, options?: AbortOptions) => {
461
+ // encode, writeLP
462
+ await lp.write(proto.encode(message), options)
463
+ },
464
+ writeV: async (messages, proto, options?: AbortOptions) => {
465
+ // encode, writeLP
466
+ await lp.writeV(messages.map(message => proto.encode(message)), options)
467
+ },
468
+ pb: (proto) => {
469
+ return {
470
+ read: async (options) => pbStream.read(proto, options),
471
+ write: async (d, options) => pbStream.write(d, proto, options),
472
+ writeV: async (d, options) => pbStream.writeV(d, proto, options),
473
+ unwrap: () => pbStream
474
+ }
475
+ },
476
+ unwrap: () => {
477
+ return lp.unwrap()
478
+ }
479
+ }
480
+
481
+ return pbStream
482
+ }
483
+
484
+ export async function echo (stream: MessageStream, options?: AbortOptions): Promise<void> {
485
+ const log = stream.log.newScope('echo')
486
+ const start = Date.now()
487
+
488
+ let bytes = 0
489
+
490
+ try {
491
+ for await (const buf of stream) {
492
+ bytes += buf.byteLength
493
+
494
+ if (!stream.send(buf)) {
495
+ stream.pause()
496
+
497
+ await pEvent(stream, 'drain', {
498
+ rejectionEvents: [
499
+ 'close'
500
+ ],
501
+ ...options
502
+ })
503
+
504
+ stream.resume()
505
+ }
506
+ }
507
+
508
+ log('echoed %d bytes in %dms', bytes, Date.now() - start)
509
+
510
+ await stream.close(options)
511
+ } catch (err: any) {
512
+ stream.abort(err)
513
+ }
514
+ }
515
+
516
+ export type PipeInput = Iterable<Uint8Array | Uint8ArrayList> | AsyncIterable<Uint8Array | Uint8ArrayList> | Stream
517
+
518
+ function isMessageStream (obj?: any): obj is Stream {
519
+ return obj?.addEventListener != null
520
+ }
521
+
522
+ export function messageStreamToDuplex (stream: Stream): Duplex<AsyncGenerator<Uint8ArrayList | Uint8Array>, Iterable<Uint8ArrayList | Uint8Array> | AsyncIterable<Uint8ArrayList | Uint8Array>, Promise<void>> {
523
+ const source = pushable<Uint8ArrayList | Uint8Array>()
524
+ let onError: PromiseWithResolvers<IteratorResult<Uint8ArrayList | Uint8Array>> | undefined
525
+
526
+ const onMessage = (evt: StreamMessageEvent): void => {
527
+ source.push(evt.data)
528
+ }
529
+
530
+ const onRemoteCloseWrite = (): void => {
531
+ source.end()
532
+
533
+ stream.removeEventListener('message', onMessage)
534
+ stream.removeEventListener('close', onClose)
535
+ stream.removeEventListener('remoteCloseWrite', onRemoteCloseWrite)
536
+ }
537
+
538
+ const onClose = (evt: StreamCloseEvent): void => {
539
+ source.end(evt.error)
540
+
541
+ if (evt.error != null) {
542
+ onError?.reject(evt.error)
543
+ }
544
+
545
+ stream.removeEventListener('message', onMessage)
546
+ stream.removeEventListener('close', onClose)
547
+ stream.removeEventListener('remoteCloseWrite', onRemoteCloseWrite)
548
+ }
549
+
550
+ stream.addEventListener('message', onMessage)
551
+ stream.addEventListener('close', onClose, {
552
+ once: true
553
+ })
554
+ stream.addEventListener('remoteCloseWrite', onRemoteCloseWrite, {
555
+ once: true
556
+ })
557
+
558
+ return {
559
+ source,
560
+ async sink (source: Source<Uint8Array | Uint8ArrayList>) {
561
+ async function * toGenerator (): AsyncGenerator<Uint8Array | Uint8ArrayList> {
562
+ yield * source
563
+ }
564
+
565
+ const gen = toGenerator()
566
+
567
+ while (true) {
568
+ onError = Promise.withResolvers<IteratorResult<Uint8ArrayList | Uint8Array>>()
569
+
570
+ const { done, value } = await Promise.race([
571
+ gen.next(),
572
+ onError.promise
573
+ ])
574
+
575
+ if (stream.writeStatus === 'closing' || stream.writeStatus === 'closed') {
576
+ break
577
+ }
578
+
579
+ if (value != null) {
580
+ if (!stream.send(value)) {
581
+ await Promise.race([
582
+ pEvent(stream, 'drain', {
583
+ rejectionEvents: [
584
+ 'close'
585
+ ]
586
+ })
587
+ ])
588
+ }
589
+ }
590
+
591
+ if (done === true) {
592
+ break
593
+ }
594
+ }
595
+
596
+ await stream.close()
597
+ }
598
+ }
599
+ }
600
+
601
+ interface SourceFn<A = any> { (): A }
602
+
603
+ type PipeSource<A = any> =
604
+ Iterable<A> |
605
+ AsyncIterable<A> |
606
+ SourceFn<A> |
607
+ Duplex<A, any, any> |
608
+ MessageStream
609
+
610
+ type PipeTransform<A = any, B = any> =
611
+ Transform<A, B> |
612
+ Duplex<B, A> |
613
+ MessageStream
614
+
615
+ type PipeSink<A = any, B = any> =
616
+ Sink<A, B> |
617
+ Duplex<any, A, B> |
618
+ MessageStream
619
+
620
+ type PipeOutput<A> =
621
+ A extends Sink<any> ? ReturnType<A> :
622
+ A extends Duplex<any, any, any> ? ReturnType<A['sink']> :
623
+ A extends MessageStream ? Promise<void> :
624
+ never
625
+
626
+ // single item pipe output includes pipe source types
627
+ type SingleItemPipeOutput<A> =
628
+ A extends Iterable<any> ? A :
629
+ A extends AsyncIterable<any> ? A :
630
+ A extends SourceFn ? ReturnType<A> :
631
+ A extends Duplex<any, any, any> ? A['source'] :
632
+ PipeOutput<A>
633
+
634
+ type PipeFnInput<A> =
635
+ A extends Iterable<any> ? A :
636
+ A extends AsyncIterable<any> ? A :
637
+ A extends SourceFn ? ReturnType<A> :
638
+ A extends Transform<any, any> ? ReturnType<A> :
639
+ A extends Duplex<any, any, any> ? A['source'] :
640
+ never
641
+
642
+ export function pipe<
643
+ A extends PipeSource
644
+ > (
645
+ source: A
646
+ ): SingleItemPipeOutput<A>
647
+ // two items, source to sink
648
+ export function pipe<
649
+ A extends PipeSource,
650
+ B extends PipeSink<PipeFnInput<A>>
651
+ > (
652
+ source: A,
653
+ sink: B
654
+ ): PipeOutput<B>
655
+
656
+ // three items, source to sink with transform(s) in between
657
+ export function pipe<
658
+ A extends PipeSource,
659
+ B extends PipeTransform<PipeFnInput<A>>,
660
+ C extends PipeSink<PipeFnInput<B>>
661
+ > (
662
+ source: A,
663
+ transform1: B,
664
+ sink: C
665
+ ): PipeOutput<C>
666
+
667
+ // many items, source to sink with transform(s) in between
668
+ export function pipe<
669
+ A extends PipeSource,
670
+ B extends PipeTransform<PipeFnInput<A>>,
671
+ C extends PipeTransform<PipeFnInput<B>>,
672
+ D extends PipeSink<PipeFnInput<C>>
673
+ > (
674
+ source: A,
675
+ transform1: B,
676
+ transform2: C,
677
+ sink: D
678
+ ): PipeOutput<D>
679
+
680
+ // lots of items, source to sink with transform(s) in between
681
+ export function pipe<
682
+ A extends PipeSource,
683
+ B extends PipeTransform<PipeFnInput<A>>,
684
+ C extends PipeTransform<PipeFnInput<B>>,
685
+ D extends PipeTransform<PipeFnInput<C>>,
686
+ E extends PipeSink<PipeFnInput<D>>
687
+ > (
688
+ source: A,
689
+ transform1: B,
690
+ transform2: C,
691
+ transform3: D,
692
+ sink: E
693
+ ): PipeOutput<E>
694
+
695
+ // lots of items, source to sink with transform(s) in between
696
+ export function pipe<
697
+ A extends PipeSource,
698
+ B extends PipeTransform<PipeFnInput<A>>,
699
+ C extends PipeTransform<PipeFnInput<B>>,
700
+ D extends PipeTransform<PipeFnInput<C>>,
701
+ E extends PipeTransform<PipeFnInput<D>>,
702
+ F extends PipeSink<PipeFnInput<E>>
703
+ > (
704
+ source: A,
705
+ transform1: B,
706
+ transform2: C,
707
+ transform3: D,
708
+ transform4: E,
709
+ sink: F
710
+ ): PipeOutput<F>
711
+
712
+ // lots of items, source to sink with transform(s) in between
713
+ export function pipe<
714
+ A extends PipeSource,
715
+ B extends PipeTransform<PipeFnInput<A>>,
716
+ C extends PipeTransform<PipeFnInput<B>>,
717
+ D extends PipeTransform<PipeFnInput<C>>,
718
+ E extends PipeTransform<PipeFnInput<D>>,
719
+ F extends PipeTransform<PipeFnInput<E>>,
720
+ G extends PipeSink<PipeFnInput<F>>
721
+ > (
722
+ source: A,
723
+ transform1: B,
724
+ transform2: C,
725
+ transform3: D,
726
+ transform4: E,
727
+ transform5: F,
728
+ sink: G
729
+ ): PipeOutput<G>
730
+
731
+ // lots of items, source to sink with transform(s) in between
732
+ export function pipe<
733
+ A extends PipeSource,
734
+ B extends PipeTransform<PipeFnInput<A>>,
735
+ C extends PipeTransform<PipeFnInput<B>>,
736
+ D extends PipeTransform<PipeFnInput<C>>,
737
+ E extends PipeTransform<PipeFnInput<D>>,
738
+ F extends PipeTransform<PipeFnInput<E>>,
739
+ G extends PipeTransform<PipeFnInput<F>>,
740
+ H extends PipeSink<PipeFnInput<G>>
741
+ > (
742
+ source: A,
743
+ transform1: B,
744
+ transform2: C,
745
+ transform3: D,
746
+ transform4: E,
747
+ transform5: F,
748
+ transform6: G,
749
+ sink: H
750
+ ): PipeOutput<H>
751
+
752
+ // lots of items, source to sink with transform(s) in between
753
+ export function pipe<
754
+ A extends PipeSource,
755
+ B extends PipeTransform<PipeFnInput<A>>,
756
+ C extends PipeTransform<PipeFnInput<B>>,
757
+ D extends PipeTransform<PipeFnInput<C>>,
758
+ E extends PipeTransform<PipeFnInput<D>>,
759
+ F extends PipeTransform<PipeFnInput<E>>,
760
+ G extends PipeTransform<PipeFnInput<F>>,
761
+ H extends PipeTransform<PipeFnInput<G>>,
762
+ I extends PipeSink<PipeFnInput<H>>
763
+ > (
764
+ source: A,
765
+ transform1: B,
766
+ transform2: C,
767
+ transform3: D,
768
+ transform4: E,
769
+ transform5: F,
770
+ transform6: G,
771
+ transform7: H,
772
+ sink: I
773
+ ): PipeOutput<I>
774
+
775
+ // lots of items, source to sink with transform(s) in between
776
+ export function pipe<
777
+ A extends PipeSource,
778
+ B extends PipeTransform<PipeFnInput<A>>,
779
+ C extends PipeTransform<PipeFnInput<B>>,
780
+ D extends PipeTransform<PipeFnInput<C>>,
781
+ E extends PipeTransform<PipeFnInput<D>>,
782
+ F extends PipeTransform<PipeFnInput<E>>,
783
+ G extends PipeTransform<PipeFnInput<F>>,
784
+ H extends PipeTransform<PipeFnInput<G>>,
785
+ I extends PipeTransform<PipeFnInput<H>>,
786
+ J extends PipeSink<PipeFnInput<I>>
787
+ > (
788
+ source: A,
789
+ transform1: B,
790
+ transform2: C,
791
+ transform3: D,
792
+ transform4: E,
793
+ transform5: F,
794
+ transform6: G,
795
+ transform7: H,
796
+ transform8: I,
797
+ sink: J
798
+ ): PipeOutput<J>
799
+
800
+ // lots of items, source to sink with transform(s) in between
801
+ export function pipe<
802
+ A extends PipeSource,
803
+ B extends PipeTransform<PipeFnInput<A>>,
804
+ C extends PipeTransform<PipeFnInput<B>>,
805
+ D extends PipeTransform<PipeFnInput<C>>,
806
+ E extends PipeTransform<PipeFnInput<D>>,
807
+ F extends PipeTransform<PipeFnInput<E>>,
808
+ G extends PipeTransform<PipeFnInput<F>>,
809
+ H extends PipeTransform<PipeFnInput<G>>,
810
+ I extends PipeTransform<PipeFnInput<H>>,
811
+ J extends PipeTransform<PipeFnInput<I>>,
812
+ K extends PipeSink<PipeFnInput<J>>
813
+ > (
814
+ source: A,
815
+ transform1: B,
816
+ transform2: C,
817
+ transform3: D,
818
+ transform4: E,
819
+ transform5: F,
820
+ transform6: G,
821
+ transform7: H,
822
+ transform8: I,
823
+ transform9: J,
824
+ sink: K
825
+ ): PipeOutput<K>
826
+
827
+ // lots of items, source to sink with transform(s) in between
828
+ export function pipe<
829
+ A extends PipeSource,
830
+ B extends PipeTransform<PipeFnInput<A>>,
831
+ C extends PipeTransform<PipeFnInput<B>>,
832
+ D extends PipeTransform<PipeFnInput<C>>,
833
+ E extends PipeTransform<PipeFnInput<D>>,
834
+ F extends PipeTransform<PipeFnInput<E>>,
835
+ G extends PipeTransform<PipeFnInput<F>>,
836
+ H extends PipeTransform<PipeFnInput<G>>,
837
+ I extends PipeTransform<PipeFnInput<H>>,
838
+ J extends PipeTransform<PipeFnInput<I>>,
839
+ K extends PipeTransform<PipeFnInput<J>>,
840
+ L extends PipeSink<PipeFnInput<K>>
841
+ > (
842
+ source: A,
843
+ transform1: B,
844
+ transform2: C,
845
+ transform3: D,
846
+ transform4: E,
847
+ transform5: F,
848
+ transform6: G,
849
+ transform7: H,
850
+ transform8: I,
851
+ transform9: J,
852
+ transform10: K,
853
+ sink: L
854
+ ): PipeOutput<L>
855
+ export function pipe (...input: any[]): any {
856
+ const sources = input.map(source => {
857
+ if (isMessageStream(source)) {
858
+ return messageStreamToDuplex(source)
859
+ }
860
+
861
+ return source
862
+ })
863
+
864
+ // @ts-expect-error it-pipe types say args cannot be spread like this
865
+ return itPipe(...sources)
866
+ }