@libp2p/utils 6.7.2 → 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 (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 +370 -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 +865 -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,865 @@
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 = Promise.withResolvers<void>()
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
+
175
+ while (true) {
176
+ if (readBuffer.byteLength >= bytesToRead) {
177
+ // if we are about to exit the loop this promise will not be awaited
178
+ // so resolve it to prevent and unhandled promise rejections that may
179
+ // occur
180
+ hasBytes.resolve()
181
+
182
+ break
183
+ }
184
+
185
+ await raceSignal(hasBytes.promise, options?.signal)
186
+
187
+ if (isEOF(stream)) {
188
+ if (readBuffer.byteLength === 0 && options?.bytes == null) {
189
+ return null
190
+ }
191
+
192
+ break
193
+ }
194
+
195
+ hasBytes = Promise.withResolvers<void>()
196
+ }
197
+
198
+ const toRead = options?.bytes ?? readBuffer.byteLength
199
+
200
+ if (readBuffer.byteLength < toRead) {
201
+ if (isEOF(stream)) {
202
+ stream.log.error('closed while reading %d/%d bytes', readBuffer.byteLength, toRead)
203
+ throw new UnexpectedEOFError(`Unexpected EOF - stream closed while reading ${readBuffer.byteLength}/${toRead} bytes`)
204
+ }
205
+
206
+ return byteStream.read(options)
207
+ }
208
+
209
+ const output = readBuffer.sublist(0, toRead)
210
+ readBuffer.consume(toRead)
211
+
212
+ return output
213
+ },
214
+ async write (data: Uint8Array | Uint8ArrayList, options?: AbortOptions) {
215
+ if (unwrapped === true) {
216
+ throw new UnwrappedError('Stream was unwrapped')
217
+ }
218
+
219
+ if (!stream.send(data)) {
220
+ await pEvent(stream, 'drain', {
221
+ signal: options?.signal,
222
+ rejectionEvents: ['close']
223
+ })
224
+ }
225
+ },
226
+ unwrap () {
227
+ // already unwrapped, just return the original stream
228
+ if (unwrapped) {
229
+ return stream
230
+ }
231
+
232
+ // only unwrap once
233
+ unwrapped = true
234
+ stream.removeEventListener('message', byteStreamOnMessageListener)
235
+ stream.removeEventListener('close', byteStreamOnCloseListener)
236
+ stream.removeEventListener('remoteCloseWrite', byteStreamOnRemoteCloseWrite)
237
+
238
+ // emit any unread data
239
+ if (readBuffer.byteLength > 0) {
240
+ stream.log('stream unwrapped with %d unread bytes', readBuffer.byteLength)
241
+ stream.push(readBuffer)
242
+ }
243
+
244
+ return stream
245
+ }
246
+ }
247
+
248
+ return byteStream
249
+ }
250
+
251
+ export interface LengthPrefixedStream<S extends MessageStream = MessageStream> {
252
+ /**
253
+ * Read the next length-prefixed number of bytes from the stream
254
+ */
255
+ read(options?: AbortOptions): Promise<Uint8ArrayList>
256
+
257
+ /**
258
+ * Write the passed bytes to the stream prefixed by their length
259
+ */
260
+ write(data: Uint8Array | Uint8ArrayList, options?: AbortOptions): Promise<void>
261
+
262
+ /**
263
+ * Write passed list of bytes, prefix by their individual lengths to the stream as a single write
264
+ */
265
+ writeV(input: Array<Uint8Array | Uint8ArrayList>, options?: AbortOptions): Promise<void>
266
+
267
+ /**
268
+ * Returns the underlying stream
269
+ */
270
+ unwrap(): S
271
+ }
272
+
273
+ export interface LengthPrefixedStreamOpts extends ByteStreamOpts {
274
+ lengthEncoder (value: number): Uint8ArrayList | Uint8Array
275
+ lengthDecoder (data: Uint8ArrayList): number
276
+ maxLengthLength: number
277
+ maxDataLength: number
278
+ }
279
+
280
+ export function lpStream <T extends MessageStream> (stream: T, opts: Partial<LengthPrefixedStreamOpts> = {}): LengthPrefixedStream<T> {
281
+ const bytes = byteStream(stream, opts)
282
+
283
+ if (opts.maxDataLength != null && opts.maxLengthLength == null) {
284
+ // if max data length is set but max length length is not, calculate the
285
+ // max length length needed to encode max data length
286
+ opts.maxLengthLength = varint.encodingLength(opts.maxDataLength)
287
+ }
288
+
289
+ const decodeLength = opts?.lengthDecoder ?? varint.decode
290
+ const encodeLength = opts?.lengthEncoder ?? varint.encode
291
+
292
+ const lpStream: LengthPrefixedStream<any> = {
293
+ async read (options?: AbortOptions) {
294
+ let dataLength: number = -1
295
+ const lengthBuffer = new Uint8ArrayList()
296
+
297
+ while (true) {
298
+ // read one byte at a time until we can decode a varint
299
+ const buf = await bytes.read({
300
+ ...options,
301
+ bytes: 1
302
+ })
303
+
304
+ // the underlying resource closed gracefully
305
+ if (buf == null) {
306
+ break
307
+ }
308
+
309
+ // append byte and try to decode
310
+ lengthBuffer.append(buf)
311
+
312
+ try {
313
+ dataLength = decodeLength(lengthBuffer)
314
+ } catch (err) {
315
+ if (err instanceof RangeError) {
316
+ continue
317
+ }
318
+
319
+ throw err
320
+ }
321
+
322
+ if (dataLength < 0) {
323
+ throw new InvalidMessageLengthError('Invalid message length')
324
+ }
325
+
326
+ if (opts?.maxLengthLength != null && lengthBuffer.byteLength > opts.maxLengthLength) {
327
+ throw new InvalidDataLengthLengthError(`Message length length too long - ${lengthBuffer.byteLength} > ${opts.maxLengthLength}`)
328
+ }
329
+
330
+ if (dataLength > -1) {
331
+ break
332
+ }
333
+ }
334
+
335
+ if (opts?.maxDataLength != null && dataLength > opts.maxDataLength) {
336
+ throw new InvalidDataLengthError(`Message length too long - ${dataLength} > ${opts.maxDataLength}`)
337
+ }
338
+
339
+ const buf = await bytes.read({
340
+ ...options,
341
+ bytes: dataLength
342
+ })
343
+
344
+ if (buf == null) {
345
+ stream.log.error('tried to read %d bytes but the stream closed', dataLength)
346
+ throw new UnexpectedEOFError(`Unexpected EOF - tried to read ${dataLength} bytes but the stream closed`)
347
+ }
348
+
349
+ if (buf.byteLength !== dataLength) {
350
+ stream.log.error('read %d/%d bytes before the stream closed', buf.byteLength, dataLength)
351
+ throw new UnexpectedEOFError(`Unexpected EOF - read ${buf.byteLength}/${dataLength} bytes before the stream closed`)
352
+ }
353
+
354
+ return buf
355
+ },
356
+ async write (data, options?: AbortOptions) {
357
+ // encode, write
358
+ await bytes.write(new Uint8ArrayList(encodeLength(data.byteLength), data), options)
359
+ },
360
+ async writeV (data, options?: AbortOptions) {
361
+ const list = new Uint8ArrayList(
362
+ ...data.flatMap(buf => ([encodeLength(buf.byteLength), buf]))
363
+ )
364
+
365
+ // encode, write
366
+ await bytes.write(list, options)
367
+ },
368
+ unwrap () {
369
+ return bytes.unwrap()
370
+ }
371
+ }
372
+
373
+ return lpStream
374
+ }
375
+
376
+ /**
377
+ * A protobuf decoder - takes a byte array and returns an object
378
+ */
379
+ export interface ProtobufDecoder<T> {
380
+ (data: Uint8Array | Uint8ArrayList): T
381
+ }
382
+
383
+ /**
384
+ * A protobuf encoder - takes an object and returns a byte array
385
+ */
386
+ export interface ProtobufEncoder<T> {
387
+ (data: T): Uint8Array
388
+ }
389
+
390
+ /**
391
+ * Convenience methods for working with protobuf streams
392
+ */
393
+ export interface ProtobufStream<S extends MessageStream = MessageStream> {
394
+ /**
395
+ * Read the next length-prefixed byte array from the stream and decode it as the passed protobuf format
396
+ */
397
+ read<T>(proto: { decode: ProtobufDecoder<T> }, options?: AbortOptions): Promise<T>
398
+
399
+ /**
400
+ * Encode the passed object as a protobuf message and write it's length-prefixed bytes to the stream
401
+ */
402
+ write<T>(data: T, proto: { encode: ProtobufEncoder<T> }, options?: AbortOptions): Promise<void>
403
+
404
+ /**
405
+ * Encode the passed objects as protobuf messages and write their length-prefixed bytes to the stream as a single write
406
+ */
407
+ writeV<T>(input: T[], proto: { encode: ProtobufEncoder<T> }, options?: AbortOptions): Promise<void>
408
+
409
+ /**
410
+ * Returns an object with read/write methods for operating on one specific type of protobuf message
411
+ */
412
+ pb<T>(proto: { encode: ProtobufEncoder<T>, decode: ProtobufDecoder<T> }): ProtobufMessageStream<T, S>
413
+
414
+ /**
415
+ * Returns the underlying stream
416
+ */
417
+ unwrap(): S
418
+ }
419
+
420
+ /**
421
+ * A message reader/writer that only uses one type of message
422
+ */
423
+ export interface ProtobufMessageStream <T, S extends MessageStream = MessageStream> {
424
+ /**
425
+ * Read a message from the stream
426
+ */
427
+ read(options?: AbortOptions): Promise<T>
428
+
429
+ /**
430
+ * Write a message to the stream
431
+ */
432
+ write(d: T, options?: AbortOptions): Promise<void>
433
+
434
+ /**
435
+ * Write several messages to the stream
436
+ */
437
+ writeV(d: T[], options?: AbortOptions): Promise<void>
438
+
439
+ /**
440
+ * Unwrap the underlying protobuf stream
441
+ */
442
+ unwrap(): ProtobufStream<S>
443
+ }
444
+
445
+ export interface ProtobufStreamOpts extends LengthPrefixedStreamOpts {
446
+
447
+ }
448
+
449
+ export function pbStream <T extends MessageStream = Stream> (stream: T, opts?: Partial<ProtobufStreamOpts>): ProtobufStream<T> {
450
+ const lp = lpStream(stream, opts)
451
+
452
+ const pbStream: ProtobufStream<T> = {
453
+ read: async (proto, options?: AbortOptions) => {
454
+ // readLP, decode
455
+ const value = await lp.read(options)
456
+
457
+ return proto.decode(value)
458
+ },
459
+ write: async (message, proto, options?: AbortOptions) => {
460
+ // encode, writeLP
461
+ await lp.write(proto.encode(message), options)
462
+ },
463
+ writeV: async (messages, proto, options?: AbortOptions) => {
464
+ // encode, writeLP
465
+ await lp.writeV(messages.map(message => proto.encode(message)), options)
466
+ },
467
+ pb: (proto) => {
468
+ return {
469
+ read: async (options) => pbStream.read(proto, options),
470
+ write: async (d, options) => pbStream.write(d, proto, options),
471
+ writeV: async (d, options) => pbStream.writeV(d, proto, options),
472
+ unwrap: () => pbStream
473
+ }
474
+ },
475
+ unwrap: () => {
476
+ return lp.unwrap()
477
+ }
478
+ }
479
+
480
+ return pbStream
481
+ }
482
+
483
+ export async function echo (stream: MessageStream, options?: AbortOptions): Promise<void> {
484
+ const log = stream.log.newScope('echo')
485
+ const start = Date.now()
486
+
487
+ let bytes = 0
488
+
489
+ try {
490
+ for await (const buf of stream) {
491
+ bytes += buf.byteLength
492
+
493
+ if (!stream.send(buf)) {
494
+ stream.pause()
495
+
496
+ await pEvent(stream, 'drain', {
497
+ rejectionEvents: [
498
+ 'close'
499
+ ],
500
+ ...options
501
+ })
502
+
503
+ stream.resume()
504
+ }
505
+ }
506
+
507
+ log('echoed %d bytes in %dms', bytes, Date.now() - start)
508
+
509
+ await stream.close(options)
510
+ } catch (err: any) {
511
+ stream.abort(err)
512
+ }
513
+ }
514
+
515
+ export type PipeInput = Iterable<Uint8Array | Uint8ArrayList> | AsyncIterable<Uint8Array | Uint8ArrayList> | Stream
516
+
517
+ function isMessageStream (obj?: any): obj is Stream {
518
+ return obj?.addEventListener != null
519
+ }
520
+
521
+ export function messageStreamToDuplex (stream: Stream): Duplex<AsyncGenerator<Uint8ArrayList | Uint8Array>, Iterable<Uint8ArrayList | Uint8Array> | AsyncIterable<Uint8ArrayList | Uint8Array>, Promise<void>> {
522
+ const source = pushable<Uint8ArrayList | Uint8Array>()
523
+ let onError: PromiseWithResolvers<IteratorResult<Uint8ArrayList | Uint8Array>> | undefined
524
+
525
+ const onMessage = (evt: StreamMessageEvent): void => {
526
+ source.push(evt.data)
527
+ }
528
+
529
+ const onRemoteCloseWrite = (): void => {
530
+ source.end()
531
+
532
+ stream.removeEventListener('message', onMessage)
533
+ stream.removeEventListener('close', onClose)
534
+ stream.removeEventListener('remoteCloseWrite', onRemoteCloseWrite)
535
+ }
536
+
537
+ const onClose = (evt: StreamCloseEvent): void => {
538
+ source.end(evt.error)
539
+
540
+ if (evt.error != null) {
541
+ onError?.reject(evt.error)
542
+ }
543
+
544
+ stream.removeEventListener('message', onMessage)
545
+ stream.removeEventListener('close', onClose)
546
+ stream.removeEventListener('remoteCloseWrite', onRemoteCloseWrite)
547
+ }
548
+
549
+ stream.addEventListener('message', onMessage)
550
+ stream.addEventListener('close', onClose, {
551
+ once: true
552
+ })
553
+ stream.addEventListener('remoteCloseWrite', onRemoteCloseWrite, {
554
+ once: true
555
+ })
556
+
557
+ return {
558
+ source,
559
+ async sink (source: Source<Uint8Array | Uint8ArrayList>) {
560
+ async function * toGenerator (): AsyncGenerator<Uint8Array | Uint8ArrayList> {
561
+ yield * source
562
+ }
563
+
564
+ const gen = toGenerator()
565
+
566
+ while (true) {
567
+ onError = Promise.withResolvers<IteratorResult<Uint8ArrayList | Uint8Array>>()
568
+
569
+ const { done, value } = await Promise.race([
570
+ gen.next(),
571
+ onError.promise
572
+ ])
573
+
574
+ if (stream.writeStatus === 'closing' || stream.writeStatus === 'closed') {
575
+ break
576
+ }
577
+
578
+ if (value != null) {
579
+ if (!stream.send(value)) {
580
+ await Promise.race([
581
+ pEvent(stream, 'drain', {
582
+ rejectionEvents: [
583
+ 'close'
584
+ ]
585
+ })
586
+ ])
587
+ }
588
+ }
589
+
590
+ if (done === true) {
591
+ break
592
+ }
593
+ }
594
+
595
+ await stream.close()
596
+ }
597
+ }
598
+ }
599
+
600
+ interface SourceFn<A = any> { (): A }
601
+
602
+ type PipeSource<A = any> =
603
+ Iterable<A> |
604
+ AsyncIterable<A> |
605
+ SourceFn<A> |
606
+ Duplex<A, any, any> |
607
+ MessageStream
608
+
609
+ type PipeTransform<A = any, B = any> =
610
+ Transform<A, B> |
611
+ Duplex<B, A> |
612
+ MessageStream
613
+
614
+ type PipeSink<A = any, B = any> =
615
+ Sink<A, B> |
616
+ Duplex<any, A, B> |
617
+ MessageStream
618
+
619
+ type PipeOutput<A> =
620
+ A extends Sink<any> ? ReturnType<A> :
621
+ A extends Duplex<any, any, any> ? ReturnType<A['sink']> :
622
+ A extends MessageStream ? Promise<void> :
623
+ never
624
+
625
+ // single item pipe output includes pipe source types
626
+ type SingleItemPipeOutput<A> =
627
+ A extends Iterable<any> ? A :
628
+ A extends AsyncIterable<any> ? A :
629
+ A extends SourceFn ? ReturnType<A> :
630
+ A extends Duplex<any, any, any> ? A['source'] :
631
+ PipeOutput<A>
632
+
633
+ type PipeFnInput<A> =
634
+ A extends Iterable<any> ? A :
635
+ A extends AsyncIterable<any> ? A :
636
+ A extends SourceFn ? ReturnType<A> :
637
+ A extends Transform<any, any> ? ReturnType<A> :
638
+ A extends Duplex<any, any, any> ? A['source'] :
639
+ never
640
+
641
+ export function pipe<
642
+ A extends PipeSource
643
+ > (
644
+ source: A
645
+ ): SingleItemPipeOutput<A>
646
+ // two items, source to sink
647
+ export function pipe<
648
+ A extends PipeSource,
649
+ B extends PipeSink<PipeFnInput<A>>
650
+ > (
651
+ source: A,
652
+ sink: B
653
+ ): PipeOutput<B>
654
+
655
+ // three items, source to sink with transform(s) in between
656
+ export function pipe<
657
+ A extends PipeSource,
658
+ B extends PipeTransform<PipeFnInput<A>>,
659
+ C extends PipeSink<PipeFnInput<B>>
660
+ > (
661
+ source: A,
662
+ transform1: B,
663
+ sink: C
664
+ ): PipeOutput<C>
665
+
666
+ // many items, source to sink with transform(s) in between
667
+ export function pipe<
668
+ A extends PipeSource,
669
+ B extends PipeTransform<PipeFnInput<A>>,
670
+ C extends PipeTransform<PipeFnInput<B>>,
671
+ D extends PipeSink<PipeFnInput<C>>
672
+ > (
673
+ source: A,
674
+ transform1: B,
675
+ transform2: C,
676
+ sink: D
677
+ ): PipeOutput<D>
678
+
679
+ // lots of items, source to sink with transform(s) in between
680
+ export function pipe<
681
+ A extends PipeSource,
682
+ B extends PipeTransform<PipeFnInput<A>>,
683
+ C extends PipeTransform<PipeFnInput<B>>,
684
+ D extends PipeTransform<PipeFnInput<C>>,
685
+ E extends PipeSink<PipeFnInput<D>>
686
+ > (
687
+ source: A,
688
+ transform1: B,
689
+ transform2: C,
690
+ transform3: D,
691
+ sink: E
692
+ ): PipeOutput<E>
693
+
694
+ // lots of items, source to sink with transform(s) in between
695
+ export function pipe<
696
+ A extends PipeSource,
697
+ B extends PipeTransform<PipeFnInput<A>>,
698
+ C extends PipeTransform<PipeFnInput<B>>,
699
+ D extends PipeTransform<PipeFnInput<C>>,
700
+ E extends PipeTransform<PipeFnInput<D>>,
701
+ F extends PipeSink<PipeFnInput<E>>
702
+ > (
703
+ source: A,
704
+ transform1: B,
705
+ transform2: C,
706
+ transform3: D,
707
+ transform4: E,
708
+ sink: F
709
+ ): PipeOutput<F>
710
+
711
+ // lots of items, source to sink with transform(s) in between
712
+ export function pipe<
713
+ A extends PipeSource,
714
+ B extends PipeTransform<PipeFnInput<A>>,
715
+ C extends PipeTransform<PipeFnInput<B>>,
716
+ D extends PipeTransform<PipeFnInput<C>>,
717
+ E extends PipeTransform<PipeFnInput<D>>,
718
+ F extends PipeTransform<PipeFnInput<E>>,
719
+ G extends PipeSink<PipeFnInput<F>>
720
+ > (
721
+ source: A,
722
+ transform1: B,
723
+ transform2: C,
724
+ transform3: D,
725
+ transform4: E,
726
+ transform5: F,
727
+ sink: G
728
+ ): PipeOutput<G>
729
+
730
+ // lots of items, source to sink with transform(s) in between
731
+ export function pipe<
732
+ A extends PipeSource,
733
+ B extends PipeTransform<PipeFnInput<A>>,
734
+ C extends PipeTransform<PipeFnInput<B>>,
735
+ D extends PipeTransform<PipeFnInput<C>>,
736
+ E extends PipeTransform<PipeFnInput<D>>,
737
+ F extends PipeTransform<PipeFnInput<E>>,
738
+ G extends PipeTransform<PipeFnInput<F>>,
739
+ H extends PipeSink<PipeFnInput<G>>
740
+ > (
741
+ source: A,
742
+ transform1: B,
743
+ transform2: C,
744
+ transform3: D,
745
+ transform4: E,
746
+ transform5: F,
747
+ transform6: G,
748
+ sink: H
749
+ ): PipeOutput<H>
750
+
751
+ // lots of items, source to sink with transform(s) in between
752
+ export function pipe<
753
+ A extends PipeSource,
754
+ B extends PipeTransform<PipeFnInput<A>>,
755
+ C extends PipeTransform<PipeFnInput<B>>,
756
+ D extends PipeTransform<PipeFnInput<C>>,
757
+ E extends PipeTransform<PipeFnInput<D>>,
758
+ F extends PipeTransform<PipeFnInput<E>>,
759
+ G extends PipeTransform<PipeFnInput<F>>,
760
+ H extends PipeTransform<PipeFnInput<G>>,
761
+ I extends PipeSink<PipeFnInput<H>>
762
+ > (
763
+ source: A,
764
+ transform1: B,
765
+ transform2: C,
766
+ transform3: D,
767
+ transform4: E,
768
+ transform5: F,
769
+ transform6: G,
770
+ transform7: H,
771
+ sink: I
772
+ ): PipeOutput<I>
773
+
774
+ // lots of items, source to sink with transform(s) in between
775
+ export function pipe<
776
+ A extends PipeSource,
777
+ B extends PipeTransform<PipeFnInput<A>>,
778
+ C extends PipeTransform<PipeFnInput<B>>,
779
+ D extends PipeTransform<PipeFnInput<C>>,
780
+ E extends PipeTransform<PipeFnInput<D>>,
781
+ F extends PipeTransform<PipeFnInput<E>>,
782
+ G extends PipeTransform<PipeFnInput<F>>,
783
+ H extends PipeTransform<PipeFnInput<G>>,
784
+ I extends PipeTransform<PipeFnInput<H>>,
785
+ J extends PipeSink<PipeFnInput<I>>
786
+ > (
787
+ source: A,
788
+ transform1: B,
789
+ transform2: C,
790
+ transform3: D,
791
+ transform4: E,
792
+ transform5: F,
793
+ transform6: G,
794
+ transform7: H,
795
+ transform8: I,
796
+ sink: J
797
+ ): PipeOutput<J>
798
+
799
+ // lots of items, source to sink with transform(s) in between
800
+ export function pipe<
801
+ A extends PipeSource,
802
+ B extends PipeTransform<PipeFnInput<A>>,
803
+ C extends PipeTransform<PipeFnInput<B>>,
804
+ D extends PipeTransform<PipeFnInput<C>>,
805
+ E extends PipeTransform<PipeFnInput<D>>,
806
+ F extends PipeTransform<PipeFnInput<E>>,
807
+ G extends PipeTransform<PipeFnInput<F>>,
808
+ H extends PipeTransform<PipeFnInput<G>>,
809
+ I extends PipeTransform<PipeFnInput<H>>,
810
+ J extends PipeTransform<PipeFnInput<I>>,
811
+ K extends PipeSink<PipeFnInput<J>>
812
+ > (
813
+ source: A,
814
+ transform1: B,
815
+ transform2: C,
816
+ transform3: D,
817
+ transform4: E,
818
+ transform5: F,
819
+ transform6: G,
820
+ transform7: H,
821
+ transform8: I,
822
+ transform9: J,
823
+ sink: K
824
+ ): PipeOutput<K>
825
+
826
+ // lots of items, source to sink with transform(s) in between
827
+ export function pipe<
828
+ A extends PipeSource,
829
+ B extends PipeTransform<PipeFnInput<A>>,
830
+ C extends PipeTransform<PipeFnInput<B>>,
831
+ D extends PipeTransform<PipeFnInput<C>>,
832
+ E extends PipeTransform<PipeFnInput<D>>,
833
+ F extends PipeTransform<PipeFnInput<E>>,
834
+ G extends PipeTransform<PipeFnInput<F>>,
835
+ H extends PipeTransform<PipeFnInput<G>>,
836
+ I extends PipeTransform<PipeFnInput<H>>,
837
+ J extends PipeTransform<PipeFnInput<I>>,
838
+ K extends PipeTransform<PipeFnInput<J>>,
839
+ L extends PipeSink<PipeFnInput<K>>
840
+ > (
841
+ source: A,
842
+ transform1: B,
843
+ transform2: C,
844
+ transform3: D,
845
+ transform4: E,
846
+ transform5: F,
847
+ transform6: G,
848
+ transform7: H,
849
+ transform8: I,
850
+ transform9: J,
851
+ transform10: K,
852
+ sink: L
853
+ ): PipeOutput<L>
854
+ export function pipe (...input: any[]): any {
855
+ const sources = input.map(source => {
856
+ if (isMessageStream(source)) {
857
+ return messageStreamToDuplex(source)
858
+ }
859
+
860
+ return source
861
+ })
862
+
863
+ // @ts-expect-error it-pipe types say args cannot be spread like this
864
+ return itPipe(...sources)
865
+ }