@libp2p/utils 6.7.1 → 6.7.2-a02cb0461

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