starpc 0.22.8 → 0.23.0

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.
@@ -7,12 +7,12 @@ async function runRPC() {
7
7
  const ws = new WebSocket(addr);
8
8
  const channel = new WebSocketConn(ws, 'outbound');
9
9
  const client = channel.buildClient();
10
- console.log('Running RpcStream test via WebSocket..');
11
- await runRpcStreamTest(client);
12
10
  console.log('Running client test via WebSocket..');
13
11
  await runClientTest(client);
14
12
  console.log('Running abort controller test via WebSocket..');
15
13
  await runAbortControllerTest(client);
14
+ console.log('Running RpcStream test via WebSocket..');
15
+ await runRpcStreamTest(client);
16
16
  }
17
17
  process.on('unhandledRejection', (ev) => {
18
18
  console.error('Unhandled rejection', ev);
@@ -1,5 +1,4 @@
1
- import type { Direction, Stream } from '@libp2p/interface/connection';
2
- import type { StreamMuxerFactory } from '@libp2p/interface/stream-muxer';
1
+ import type { Direction, Stream, StreamMuxerFactory } from '@libp2p/interface';
3
2
  import type { Duplex, Source } from 'it-stream-types';
4
3
  import { Uint8ArrayList } from 'uint8arraylist';
5
4
  import type { OpenStreamFunc, Stream as SRPCStream } from './stream.js';
@@ -16,8 +15,8 @@ export declare class Conn implements Duplex<AsyncGenerator<Uint8Array | Uint8Arr
16
15
  private muxer;
17
16
  private server?;
18
17
  constructor(server?: StreamHandler, connParams?: ConnParams);
19
- get sink(): import("it-stream-types").Sink<Source<Uint8Array | Uint8ArrayList>, Promise<void>>;
20
- get source(): AsyncGenerator<Uint8Array, any, unknown>;
18
+ get sink(): import("it-stream-types").Sink<AsyncGenerator<Uint8Array | Uint8ArrayList, any, unknown>, unknown>;
19
+ get source(): AsyncGenerator<Uint8Array | Uint8ArrayList, any, unknown>;
21
20
  get streams(): Stream[];
22
21
  buildClient(): Client;
23
22
  openStream(): Promise<SRPCStream>;
package/dist/srpc/conn.js CHANGED
@@ -2,6 +2,7 @@ import { yamux } from '@chainsafe/libp2p-yamux';
2
2
  import { pipe } from 'it-pipe';
3
3
  import isPromise from 'is-promise';
4
4
  import { pushable } from 'it-pushable';
5
+ import { defaultLogger } from '@libp2p/logger';
5
6
  import { Client } from './client.js';
6
7
  import { combineUint8ArrayListTransform } from './array-list.js';
7
8
  import { parseLengthPrefixTransform, prependLengthPrefixTransform, } from './packet.js';
@@ -25,7 +26,10 @@ export class Conn {
25
26
  if (server) {
26
27
  this.server = server;
27
28
  }
28
- const muxerFactory = connParams?.muxerFactory ?? yamux()();
29
+ const muxerFactory = connParams?.muxerFactory ??
30
+ yamux({ enableKeepAlive: false })({
31
+ logger: defaultLogger(),
32
+ });
29
33
  this.muxer = muxerFactory.createStreamMuxer({
30
34
  onIncomingStream: this.handleIncomingStream.bind(this),
31
35
  direction: connParams?.direction || 'outbound',
@@ -11,7 +11,7 @@ export { Mux, StaticMux, createMux } from './mux.js';
11
11
  export { BroadcastChannelDuplex, newBroadcastChannelDuplex, BroadcastChannelConn, } from './broadcast-channel.js';
12
12
  export { MessagePortDuplex, newMessagePortDuplex, MessagePortConn, } from './message-port.js';
13
13
  export { MessageDefinition, DecodeMessageTransform, buildDecodeMessageTransform, EncodeMessageTransform, buildEncodeMessageTransform, memoProto, memoProtoDecode, } from './message.js';
14
- export { parseLengthPrefixTransform, prependLengthPrefixTransform, decodePacketSource, encodePacketSource, } from './packet.js';
14
+ export { parseLengthPrefixTransform, prependLengthPrefixTransform, decodePacketSource, encodePacketSource, uint32LEDecode, uint32LEEncode, decodeUint32Le, encodeUint32Le, lengthPrefixDecode, lengthPrefixEncode, prependPacketLen, } from './packet.js';
15
15
  export { combineUint8ArrayListTransform } from './array-list.js';
16
16
  export { ValueCtr } from './value-ctr.js';
17
17
  export { OpenStreamCtr } from './open-stream-ctr.js';
@@ -10,7 +10,7 @@ export { StaticMux, createMux } from './mux.js';
10
10
  export { BroadcastChannelDuplex, newBroadcastChannelDuplex, BroadcastChannelConn, } from './broadcast-channel.js';
11
11
  export { MessagePortDuplex, newMessagePortDuplex, MessagePortConn, } from './message-port.js';
12
12
  export { buildDecodeMessageTransform, buildEncodeMessageTransform, memoProto, memoProtoDecode, } from './message.js';
13
- export { parseLengthPrefixTransform, prependLengthPrefixTransform, decodePacketSource, encodePacketSource, } from './packet.js';
13
+ export { parseLengthPrefixTransform, prependLengthPrefixTransform, decodePacketSource, encodePacketSource, uint32LEDecode, uint32LEEncode, decodeUint32Le, encodeUint32Le, lengthPrefixDecode, lengthPrefixEncode, prependPacketLen, } from './packet.js';
14
14
  export { combineUint8ArrayListTransform } from './array-list.js';
15
15
  export { ValueCtr } from './value-ctr.js';
16
16
  export { OpenStreamCtr } from './open-stream-ctr.js';
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -15,7 +15,7 @@ export function memoProtoDecode(def) {
15
15
  //
16
16
  // set memoize if you expect to repeatedly see the same message.
17
17
  export function buildDecodeMessageTransform(def, memoize) {
18
- const decode = memoize ? memoProtoDecode(def) : def.decode.bind(def);
18
+ const decode = !memoize ? def.decode.bind(def) : memoProtoDecode(def);
19
19
  // decodeMessageSource unmarshals and async yields encoded Messages.
20
20
  return async function* decodeMessageSource(source) {
21
21
  for await (const pkt of source) {
@@ -33,11 +33,11 @@ export function buildDecodeMessageTransform(def, memoize) {
33
33
  // buildEncodeMessageTransform builds a source of decoded messages.
34
34
  // set memoize if you expect to repeatedly encode the same message.
35
35
  export function buildEncodeMessageTransform(def, memoize) {
36
- const encode = memoize
37
- ? memoProto(def)
38
- : (msg) => {
36
+ const encode = !memoize
37
+ ? (msg) => {
39
38
  return def.encode(msg).finish();
40
- };
39
+ }
40
+ : memoProto(def);
41
41
  // encodeMessageSource encodes messages to byte arrays.
42
42
  return async function* encodeMessageSource(source) {
43
43
  for await (const pkt of source) {
@@ -1,11 +1,26 @@
1
1
  import { Uint8ArrayList } from 'uint8arraylist';
2
- import { Packet } from './rpcproto.pb.js';
3
2
  import { Source, Transform } from 'it-stream-types';
3
+ import { Packet } from './rpcproto.pb.js';
4
4
  export declare const decodePacketSource: import("./message.js").DecodeMessageTransform<Packet>;
5
5
  export declare const encodePacketSource: import("./message.js").EncodeMessageTransform<Packet>;
6
- export declare function prependLengthPrefixTransform(): Transform<Source<Uint8Array | Uint8ArrayList>, AsyncGenerator<Uint8Array, void, undefined> | Generator<Uint8Array, void, undefined>>;
7
- export declare function parseLengthPrefixTransform(): Transform<Source<Uint8Array | Uint8ArrayList>, // Allow both AsyncIterable and Iterable
8
- AsyncGenerator<Uint8ArrayList, void, unknown> | Generator<Uint8ArrayList, void, unknown>>;
6
+ export declare const uint32LEDecode: {
7
+ (data: Uint8ArrayList): number;
8
+ bytes: number;
9
+ };
10
+ export declare const uint32LEEncode: {
11
+ (value: number): Uint8ArrayList;
12
+ bytes: number;
13
+ };
14
+ export declare function lengthPrefixEncode(source: Source<Uint8Array | Uint8ArrayList>, lengthEncoder: typeof uint32LEEncode): AsyncGenerator<Uint8ArrayList, void, unknown>;
15
+ export declare function lengthPrefixDecode(source: Source<Uint8Array | Uint8ArrayList>, lengthDecoder: typeof uint32LEDecode): AsyncGenerator<Uint8ArrayList, void, unknown>;
16
+ export declare function prependLengthPrefixTransform(lengthEncoder?: {
17
+ (value: number): Uint8ArrayList;
18
+ bytes: number;
19
+ }): Transform<Source<Uint8Array | Uint8ArrayList>, AsyncGenerator<Uint8ArrayList, void, undefined> | Generator<Uint8ArrayList, void, undefined>>;
20
+ export declare function parseLengthPrefixTransform(lengthDecoder?: {
21
+ (data: Uint8ArrayList): number;
22
+ bytes: number;
23
+ }): Transform<Source<Uint8Array | Uint8ArrayList>, AsyncGenerator<Uint8ArrayList, void, unknown> | Generator<Uint8ArrayList, void, unknown>>;
9
24
  export declare function encodeUint32Le(value: number): Uint8Array;
10
25
  export declare function decodeUint32Le(data: Uint8Array): number;
11
26
  export declare function prependPacketLen(msgData: Uint8Array): Uint8Array;
@@ -1,4 +1,3 @@
1
- import { encode as lengthPrefixEncode, decode as lengthPrefixDecode, } from 'it-length-prefixed';
2
1
  import { Uint8ArrayList } from 'uint8arraylist';
3
2
  import { Packet } from './rpcproto.pb.js';
4
3
  import { buildDecodeMessageTransform, buildEncodeMessageTransform, } from './message.js';
@@ -7,7 +6,7 @@ export const decodePacketSource = buildDecodeMessageTransform(Packet);
7
6
  // encodePacketSource encodes packets from a packet object stream.
8
7
  export const encodePacketSource = buildEncodeMessageTransform(Packet);
9
8
  // uint32LEDecode removes the length prefix.
10
- const uint32LEDecode = (data) => {
9
+ export const uint32LEDecode = (data) => {
11
10
  if (data.length < 4) {
12
11
  throw RangeError('Could not decode int32BE');
13
12
  }
@@ -15,24 +14,53 @@ const uint32LEDecode = (data) => {
15
14
  };
16
15
  uint32LEDecode.bytes = 4;
17
16
  // uint32LEEncode adds the length prefix.
18
- const uint32LEEncode = (value) => {
17
+ export const uint32LEEncode = (value) => {
19
18
  const data = new Uint8ArrayList(new Uint8Array(4));
20
19
  data.setUint32(0, value, true);
21
20
  return data;
22
21
  };
23
22
  uint32LEEncode.bytes = 4;
23
+ // lengthPrefixEncode transforms a source to a length-prefixed Uint8ArrayList stream.
24
+ export async function* lengthPrefixEncode(source, lengthEncoder) {
25
+ for await (const chunk of source) {
26
+ // Encode the length of the chunk.
27
+ const length = chunk instanceof Uint8Array ? chunk.length : chunk.byteLength;
28
+ const lengthEncoded = lengthEncoder(length);
29
+ // Concatenate the length prefix and the data.
30
+ yield new Uint8ArrayList(lengthEncoded, chunk);
31
+ }
32
+ }
33
+ // lengthPrefixDecode decodes a length-prefixed source to a Uint8ArrayList stream.
34
+ export async function* lengthPrefixDecode(source, lengthDecoder) {
35
+ const buffer = new Uint8ArrayList();
36
+ for await (const chunk of source) {
37
+ buffer.append(chunk);
38
+ // Continue extracting messages while buffer contains enough data for decoding.
39
+ while (buffer.length >= lengthDecoder.bytes) {
40
+ const messageLength = lengthDecoder(buffer);
41
+ const totalLength = lengthDecoder.bytes + messageLength;
42
+ if (buffer.length < totalLength)
43
+ break; // Wait for more data if the full message hasn't arrived.
44
+ // Extract the message excluding the length prefix.
45
+ const message = buffer.sublist(lengthDecoder.bytes, totalLength);
46
+ yield message;
47
+ // Remove the processed message from the buffer.
48
+ buffer.consume(totalLength);
49
+ }
50
+ }
51
+ }
24
52
  // prependLengthPrefixTransform adds a length prefix to a message source.
25
53
  // little-endian uint32
26
- export function prependLengthPrefixTransform() {
54
+ export function prependLengthPrefixTransform(lengthEncoder = uint32LEEncode) {
27
55
  return (source) => {
28
- return lengthPrefixEncode(source, { lengthEncoder: uint32LEEncode });
56
+ return lengthPrefixEncode(source, lengthEncoder);
29
57
  };
30
58
  }
31
59
  // parseLengthPrefixTransform parses the length prefix from a message source.
32
60
  // little-endian uint32
33
- export function parseLengthPrefixTransform() {
61
+ export function parseLengthPrefixTransform(lengthDecoder = uint32LEDecode) {
34
62
  return (source) => {
35
- return lengthPrefixDecode(source, { lengthDecoder: uint32LEDecode });
63
+ return lengthPrefixDecode(source, lengthDecoder);
36
64
  };
37
65
  }
38
66
  // encodeUint32Le encodes the number as a uint32 with little endian.
@@ -1,5 +1,5 @@
1
1
  /// <reference types="ws" />
2
- import { Direction } from '@libp2p/interface/connection';
2
+ import { Direction } from '@libp2p/interface';
3
3
  import type WebSocket from '@aptre/it-ws/web-socket';
4
4
  import { Conn } from './conn.js';
5
5
  import { Server } from './server.js';
@@ -1,13 +1,16 @@
1
1
  import { pipe } from 'it-pipe';
2
2
  import duplex from '@aptre/it-ws/duplex';
3
3
  import { Conn } from './conn.js';
4
+ import { combineUint8ArrayListTransform } from './array-list.js';
4
5
  // WebSocketConn implements a connection with a WebSocket and optional Server.
5
6
  export class WebSocketConn extends Conn {
6
7
  constructor(socket, direction, server) {
7
8
  super(server, { direction });
8
9
  this.socket = socket;
9
10
  const socketDuplex = duplex(socket);
10
- pipe(socketDuplex, this, socketDuplex);
11
+ pipe(socketDuplex, this,
12
+ // it-ws only supports sending Uint8Array.
13
+ combineUint8ArrayListTransform(), socketDuplex);
11
14
  }
12
15
  // getSocket returns the websocket.
13
16
  getSocket() {
package/go.mod CHANGED
@@ -9,7 +9,7 @@ require (
9
9
  )
10
10
 
11
11
  require (
12
- github.com/aperturerobotics/util v1.13.2 // latest
12
+ github.com/aperturerobotics/util v1.13.3 // latest
13
13
  github.com/libp2p/go-libp2p v0.32.2 // latest
14
14
  github.com/libp2p/go-yamux/v4 v4.0.2-0.20240206065824-7222fbc3459d // master
15
15
  github.com/sirupsen/logrus v1.9.3 // latest
@@ -35,6 +35,6 @@ require (
35
35
  github.com/spaolacci/murmur3 v1.1.0 // indirect
36
36
  golang.org/x/crypto v0.17.0 // indirect
37
37
  golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
38
- golang.org/x/sys v0.15.0 // indirect
38
+ golang.org/x/sys v0.16.0 // indirect
39
39
  lukechampine.com/blake3 v1.2.1 // indirect
40
40
  )
package/go.sum CHANGED
@@ -1,5 +1,7 @@
1
1
  github.com/aperturerobotics/util v1.13.2 h1:MTe8MO+Tfo9d3Z4Zi6Akm//rIymy5gyoAOIEHQdFrOU=
2
2
  github.com/aperturerobotics/util v1.13.2/go.mod h1:d84OAQAGXCpl7JOBstnal91Lm6nKgk+vBgtHPgxBYrQ=
3
+ github.com/aperturerobotics/util v1.13.3 h1:N4JXKYql0R/A2hMuV79SZ2vaftf8tiN8DUJ8sGc7Rso=
4
+ github.com/aperturerobotics/util v1.13.3/go.mod h1:JdziNd9tR6lWqc9bSMIe1At8Gagrg986rmtZuPCv6+w=
3
5
  github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4
6
  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
5
7
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -63,6 +65,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
63
65
  golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
64
66
  golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
65
67
  golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
68
+ golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
69
+ golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
66
70
  google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
67
71
  google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
68
72
  gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -6,7 +6,7 @@ unset GOARCH
6
6
 
7
7
  echo "Compiling ts..."
8
8
  # ../node_modules/.bin/tsc --out integration.js --project tsconfig.json
9
- ../node_modules/.bin/esbuild integration.ts --bundle --platform=node --outfile=integration.js
9
+ ../node_modules/.bin/esbuild integration.ts --bundle --sourcemap --platform=node --outfile=integration.js
10
10
 
11
11
  echo "Compiling go..."
12
12
  go build -o integration -v ./
@@ -13,14 +13,14 @@ async function runRPC() {
13
13
  const channel = new WebSocketConn(ws, 'outbound')
14
14
  const client = channel.buildClient()
15
15
 
16
- console.log('Running RpcStream test via WebSocket..')
17
- await runRpcStreamTest(client)
18
-
19
16
  console.log('Running client test via WebSocket..')
20
17
  await runClientTest(client)
21
18
 
22
19
  console.log('Running abort controller test via WebSocket..')
23
20
  await runAbortControllerTest(client)
21
+
22
+ console.log('Running RpcStream test via WebSocket..')
23
+ await runRpcStreamTest(client)
24
24
  }
25
25
 
26
26
  process.on('unhandledRejection', (ev) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starpc",
3
- "version": "0.22.8",
3
+ "version": "0.23.0",
4
4
  "description": "Streaming protobuf RPC service protocol over any two-way channel.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -74,13 +74,13 @@
74
74
  },
75
75
  "dependencies": {
76
76
  "@aptre/it-ws": "^1.0.0",
77
- "@chainsafe/libp2p-yamux": "^5.0.0",
78
- "@libp2p/interface": "^0.1.2",
77
+ "@chainsafe/libp2p-yamux": "^6.0.2",
78
+ "@libp2p/interface": "^1.1.3",
79
+ "@libp2p/logger": "^4.0.6",
79
80
  "event-iterator": "^2.0.0",
80
81
  "is-promise": "^4.0.0",
81
82
  "isomorphic-ws": "^5.0.0",
82
83
  "it-first": "^3.0.3",
83
- "it-length-prefixed": "^9.0.4",
84
84
  "it-pipe": "^3.0.1",
85
85
  "it-pushable": "^3.2.3",
86
86
  "it-stream-types": "^2.0.1",
package/srpc/conn.ts CHANGED
@@ -1,14 +1,16 @@
1
1
  import { yamux } from '@chainsafe/libp2p-yamux'
2
- import type { Direction, Stream } from '@libp2p/interface/connection'
3
2
  import type {
3
+ Direction,
4
+ Stream,
4
5
  StreamMuxer,
5
6
  StreamMuxerFactory,
6
- } from '@libp2p/interface/stream-muxer'
7
+ } from '@libp2p/interface'
7
8
  import { pipe } from 'it-pipe'
8
9
  import type { Duplex, Source } from 'it-stream-types'
9
10
  import { Uint8ArrayList } from 'uint8arraylist'
10
11
  import isPromise from 'is-promise'
11
12
  import { pushable, Pushable } from 'it-pushable'
13
+ import { defaultLogger } from '@libp2p/logger'
12
14
 
13
15
  import type { OpenStreamFunc, Stream as SRPCStream } from './stream.js'
14
16
  import { Client } from './client.js'
@@ -73,7 +75,11 @@ export class Conn
73
75
  if (server) {
74
76
  this.server = server
75
77
  }
76
- const muxerFactory = connParams?.muxerFactory ?? yamux()()
78
+ const muxerFactory =
79
+ connParams?.muxerFactory ??
80
+ yamux({ enableKeepAlive: false })({
81
+ logger: defaultLogger(),
82
+ })
77
83
  this.muxer = muxerFactory.createStreamMuxer({
78
84
  onIncomingStream: this.handleIncomingStream.bind(this),
79
85
  direction: connParams?.direction || 'outbound',
package/srpc/index.ts CHANGED
@@ -32,6 +32,13 @@ export {
32
32
  prependLengthPrefixTransform,
33
33
  decodePacketSource,
34
34
  encodePacketSource,
35
+ uint32LEDecode,
36
+ uint32LEEncode,
37
+ decodeUint32Le,
38
+ encodeUint32Le,
39
+ lengthPrefixDecode,
40
+ lengthPrefixEncode,
41
+ prependPacketLen,
35
42
  } from './packet.js'
36
43
  export { combineUint8ArrayListTransform } from './array-list.js'
37
44
  export { ValueCtr } from './value-ctr.js'
File without changes
package/srpc/message.ts CHANGED
@@ -42,7 +42,7 @@ export function buildDecodeMessageTransform<T>(
42
42
  def: MessageDefinition<T>,
43
43
  memoize?: boolean,
44
44
  ): DecodeMessageTransform<T> {
45
- const decode = memoize ? memoProtoDecode(def) : def.decode.bind(def)
45
+ const decode = !memoize ? def.decode.bind(def) : memoProtoDecode(def)
46
46
 
47
47
  // decodeMessageSource unmarshals and async yields encoded Messages.
48
48
  return async function* decodeMessageSource(
@@ -71,11 +71,11 @@ export function buildEncodeMessageTransform<T>(
71
71
  def: MessageDefinition<T>,
72
72
  memoize?: boolean,
73
73
  ): EncodeMessageTransform<T> {
74
- const encode = memoize
75
- ? memoProto(def)
76
- : (msg: T): Uint8Array => {
74
+ const encode = !memoize
75
+ ? (msg: T): Uint8Array => {
77
76
  return def.encode(msg).finish()
78
77
  }
78
+ : memoProto(def)
79
79
 
80
80
  // encodeMessageSource encodes messages to byte arrays.
81
81
  return async function* encodeMessageSource(
package/srpc/packet.ts CHANGED
@@ -1,15 +1,11 @@
1
- import {
2
- encode as lengthPrefixEncode,
3
- decode as lengthPrefixDecode,
4
- } from 'it-length-prefixed'
5
1
  import { Uint8ArrayList } from 'uint8arraylist'
2
+ import { Source, Transform } from 'it-stream-types'
6
3
 
7
4
  import { Packet } from './rpcproto.pb.js'
8
5
  import {
9
6
  buildDecodeMessageTransform,
10
7
  buildEncodeMessageTransform,
11
8
  } from './message.js'
12
- import { Source, Transform } from 'it-stream-types'
13
9
 
14
10
  // decodePacketSource decodes packets from a binary data stream.
15
11
  export const decodePacketSource = buildDecodeMessageTransform<Packet>(Packet)
@@ -18,7 +14,7 @@ export const decodePacketSource = buildDecodeMessageTransform<Packet>(Packet)
18
14
  export const encodePacketSource = buildEncodeMessageTransform<Packet>(Packet)
19
15
 
20
16
  // uint32LEDecode removes the length prefix.
21
- const uint32LEDecode = (data: Uint8ArrayList) => {
17
+ export const uint32LEDecode = (data: Uint8ArrayList) => {
22
18
  if (data.length < 4) {
23
19
  throw RangeError('Could not decode int32BE')
24
20
  }
@@ -28,34 +24,80 @@ const uint32LEDecode = (data: Uint8ArrayList) => {
28
24
  uint32LEDecode.bytes = 4
29
25
 
30
26
  // uint32LEEncode adds the length prefix.
31
- const uint32LEEncode = (value: number) => {
27
+ export const uint32LEEncode = (value: number) => {
32
28
  const data = new Uint8ArrayList(new Uint8Array(4))
33
29
  data.setUint32(0, value, true)
34
30
  return data
35
31
  }
36
32
  uint32LEEncode.bytes = 4
37
33
 
34
+ // lengthPrefixEncode transforms a source to a length-prefixed Uint8ArrayList stream.
35
+ export async function* lengthPrefixEncode(
36
+ source: Source<Uint8Array | Uint8ArrayList>,
37
+ lengthEncoder: typeof uint32LEEncode,
38
+ ) {
39
+ for await (const chunk of source) {
40
+ // Encode the length of the chunk.
41
+ const length = chunk instanceof Uint8Array ? chunk.length : chunk.byteLength
42
+ const lengthEncoded = lengthEncoder(length)
43
+
44
+ // Concatenate the length prefix and the data.
45
+ yield new Uint8ArrayList(lengthEncoded, chunk)
46
+ }
47
+ }
48
+
49
+ // lengthPrefixDecode decodes a length-prefixed source to a Uint8ArrayList stream.
50
+ export async function* lengthPrefixDecode(
51
+ source: Source<Uint8Array | Uint8ArrayList>,
52
+ lengthDecoder: typeof uint32LEDecode,
53
+ ) {
54
+ const buffer = new Uint8ArrayList()
55
+
56
+ for await (const chunk of source) {
57
+ buffer.append(chunk)
58
+
59
+ // Continue extracting messages while buffer contains enough data for decoding.
60
+ while (buffer.length >= lengthDecoder.bytes) {
61
+ const messageLength = lengthDecoder(buffer)
62
+ const totalLength = lengthDecoder.bytes + messageLength
63
+
64
+ if (buffer.length < totalLength) break // Wait for more data if the full message hasn't arrived.
65
+
66
+ // Extract the message excluding the length prefix.
67
+ const message = buffer.sublist(lengthDecoder.bytes, totalLength)
68
+ yield message
69
+
70
+ // Remove the processed message from the buffer.
71
+ buffer.consume(totalLength)
72
+ }
73
+ }
74
+ }
75
+
38
76
  // prependLengthPrefixTransform adds a length prefix to a message source.
39
77
  // little-endian uint32
40
- export function prependLengthPrefixTransform(): Transform<
78
+ export function prependLengthPrefixTransform(
79
+ lengthEncoder = uint32LEEncode,
80
+ ): Transform<
41
81
  Source<Uint8Array | Uint8ArrayList>,
42
- | AsyncGenerator<Uint8Array, void, undefined>
43
- | Generator<Uint8Array, void, undefined>
82
+ | AsyncGenerator<Uint8ArrayList, void, undefined>
83
+ | Generator<Uint8ArrayList, void, undefined>
44
84
  > {
45
85
  return (source: Source<Uint8Array | Uint8ArrayList>) => {
46
- return lengthPrefixEncode(source, { lengthEncoder: uint32LEEncode })
86
+ return lengthPrefixEncode(source, lengthEncoder)
47
87
  }
48
88
  }
49
89
 
50
90
  // parseLengthPrefixTransform parses the length prefix from a message source.
51
91
  // little-endian uint32
52
- export function parseLengthPrefixTransform(): Transform<
53
- Source<Uint8Array | Uint8ArrayList>, // Allow both AsyncIterable and Iterable
92
+ export function parseLengthPrefixTransform(
93
+ lengthDecoder = uint32LEDecode,
94
+ ): Transform<
95
+ Source<Uint8Array | Uint8ArrayList>,
54
96
  | AsyncGenerator<Uint8ArrayList, void, unknown>
55
97
  | Generator<Uint8ArrayList, void, unknown>
56
98
  > {
57
99
  return (source: Source<Uint8Array | Uint8ArrayList>) => {
58
- return lengthPrefixDecode(source, { lengthDecoder: uint32LEDecode })
100
+ return lengthPrefixDecode(source, lengthDecoder)
59
101
  }
60
102
  }
61
103
 
package/srpc/websocket.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  import { pipe } from 'it-pipe'
2
- import { Direction } from '@libp2p/interface/connection'
2
+ import { Direction } from '@libp2p/interface'
3
3
 
4
4
  import duplex from '@aptre/it-ws/duplex'
5
5
  import type WebSocket from '@aptre/it-ws/web-socket'
6
6
 
7
7
  import { Conn } from './conn.js'
8
8
  import { Server } from './server.js'
9
+ import { combineUint8ArrayListTransform } from './array-list.js'
9
10
 
10
11
  // WebSocketConn implements a connection with a WebSocket and optional Server.
11
12
  export class WebSocketConn extends Conn {
@@ -16,7 +17,13 @@ export class WebSocketConn extends Conn {
16
17
  super(server, { direction })
17
18
  this.socket = socket
18
19
  const socketDuplex = duplex(socket)
19
- pipe(socketDuplex, this, socketDuplex)
20
+ pipe(
21
+ socketDuplex,
22
+ this,
23
+ // it-ws only supports sending Uint8Array.
24
+ combineUint8ArrayListTransform(),
25
+ socketDuplex,
26
+ )
20
27
  }
21
28
 
22
29
  // getSocket returns the websocket.