@libp2p/utils 6.7.1 → 6.7.2-8484de8a2

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