starpc 0.25.0 → 0.25.1

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.
package/dist/e2e/e2e.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { pipe } from 'it-pipe';
2
- import { createHandler, createMux, Server, Client, StreamConn } from '../srpc';
2
+ import { createHandler, createMux, Server, Client, StreamConn, ChannelStream, combineUint8ArrayListTransform } from '../srpc';
3
3
  import { EchoerDefinition, EchoerServer, runClientTest } from '../echo';
4
4
  import { runAbortControllerTest, runRpcStreamTest } from '../echo/client-test';
5
5
  async function runRPC() {
@@ -7,13 +7,31 @@ async function runRPC() {
7
7
  const server = new Server(mux.lookupMethodFunc);
8
8
  const echoer = new EchoerServer(server);
9
9
  mux.register(createHandler(EchoerDefinition, echoer));
10
+ // StreamConn is unnecessary since ChannelStream has packet framing.
11
+ // Use it here to include yamux in this e2e test.
10
12
  const clientConn = new StreamConn();
11
13
  const serverConn = new StreamConn(server, { direction: 'inbound' });
12
- pipe(clientConn, serverConn, clientConn);
14
+ // pipe clientConn -> messageStream -> serverConn -> messageStream -> clientConn
15
+ const { port1: clientPort, port2: serverPort } = new MessageChannel();
16
+ const clientChannelStream = new ChannelStream('client', clientPort);
17
+ const serverChannelStream = new ChannelStream('server', serverPort);
18
+ // Pipe the client traffic via the client end of the MessageChannel.
19
+ pipe(clientChannelStream, clientConn, combineUint8ArrayListTransform(), clientChannelStream)
20
+ .catch((err) => clientConn.close(err))
21
+ .then(() => clientConn.close());
22
+ // Pipe the server traffic via the server end of the MessageChannel.
23
+ pipe(serverChannelStream, serverConn, combineUint8ArrayListTransform(), serverChannelStream)
24
+ .catch((err) => serverConn.close(err))
25
+ .then(() => serverConn.close());
26
+ // Build the client
13
27
  const client = new Client(clientConn.buildOpenStreamFunc());
28
+ // Run the tests
14
29
  await runClientTest(client);
15
30
  await runAbortControllerTest(client);
16
31
  await runRpcStreamTest(client);
32
+ // Close cleanly
33
+ clientConn.close();
34
+ serverConn.close();
17
35
  }
18
36
  runRPC()
19
37
  .then(() => {
@@ -1,9 +1,10 @@
1
1
  import type { Sink, Source, Duplex } from 'it-stream-types';
2
2
  export interface ChannelStreamMessage<T> {
3
3
  from: string;
4
- ack?: boolean;
5
- opened?: boolean;
6
- closed?: boolean;
4
+ ack?: true;
5
+ opened?: true;
6
+ alive?: true;
7
+ closed?: true;
7
8
  error?: Error;
8
9
  data?: T;
9
10
  }
@@ -11,7 +12,10 @@ export type ChannelPort = MessagePort | {
11
12
  tx: BroadcastChannel;
12
13
  rx: BroadcastChannel;
13
14
  };
14
- export declare class ChannelStream<T> implements Duplex<AsyncGenerator<T>, Source<T>, Promise<void>> {
15
+ export interface ChannelStreamOpts {
16
+ remoteOpen?: boolean;
17
+ }
18
+ export declare class ChannelStream<T = Uint8Array> implements Duplex<AsyncGenerator<T>, Source<T>, Promise<void>> {
15
19
  readonly channel: ChannelPort;
16
20
  sink: Sink<Source<T>, Promise<void>>;
17
21
  source: AsyncGenerator<T>;
@@ -26,7 +30,7 @@ export declare class ChannelStream<T> implements Duplex<AsyncGenerator<T>, Sourc
26
30
  private _remoteAck?;
27
31
  get isAcked(): boolean;
28
32
  get isOpen(): boolean;
29
- constructor(localId: string, channel: ChannelPort, remoteOpen: boolean);
33
+ constructor(localId: string, channel: ChannelPort, opts?: ChannelStreamOpts);
30
34
  private postMessage;
31
35
  close(error?: Error): void;
32
36
  private onLocalOpened;
@@ -35,4 +39,4 @@ export declare class ChannelStream<T> implements Duplex<AsyncGenerator<T>, Sourc
35
39
  private _createSink;
36
40
  private onMessage;
37
41
  }
38
- export declare function newBroadcastChannelStream<T>(id: string, readName: string, writeName: string, remoteOpen: boolean): ChannelStream<T>;
42
+ export declare function newBroadcastChannelStream<T>(id: string, readName: string, writeName: string, opts?: ChannelStreamOpts): ChannelStream<T>;
@@ -7,21 +7,21 @@ import { pushable } from 'it-pushable';
7
7
  export class ChannelStream {
8
8
  // isAcked checks if the stream is acknowledged by the remote.
9
9
  get isAcked() {
10
- return this.remoteAck || false;
10
+ return this.remoteAck ?? false;
11
11
  }
12
12
  // isOpen checks if the stream is opened by the remote.
13
13
  get isOpen() {
14
- return this.remoteOpen || false;
14
+ return this.remoteOpen ?? false;
15
15
  }
16
- // remoteOpen indicates if we know the remote has already opened the stream.
17
- constructor(localId, channel, remoteOpen) {
16
+ // remoteOpen indicates that we know the remote has already opened the stream.
17
+ constructor(localId, channel, opts) {
18
18
  this.localId = localId;
19
19
  this.channel = channel;
20
20
  this.sink = this._createSink();
21
21
  this.localOpen = false;
22
- this.remoteAck = remoteOpen;
23
- this.remoteOpen = remoteOpen;
24
- if (remoteOpen) {
22
+ this.remoteOpen = opts?.remoteOpen ?? false;
23
+ this.remoteAck = this.remoteOpen;
24
+ if (this.remoteOpen) {
25
25
  this.waitRemoteOpen = Promise.resolve();
26
26
  this.waitRemoteAck = Promise.resolve();
27
27
  }
@@ -159,6 +159,6 @@ export class ChannelStream {
159
159
  }
160
160
  }
161
161
  // newBroadcastChannelStream constructs a ChannelStream with a channel name.
162
- export function newBroadcastChannelStream(id, readName, writeName, remoteOpen) {
163
- return new ChannelStream(id, { tx: new BroadcastChannel(writeName), rx: new BroadcastChannel(readName) }, remoteOpen);
162
+ export function newBroadcastChannelStream(id, readName, writeName, opts) {
163
+ return new ChannelStream(id, { tx: new BroadcastChannel(writeName), rx: new BroadcastChannel(readName) }, opts);
164
164
  }
package/e2e/e2e.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { pipe } from 'it-pipe'
2
- import { createHandler, createMux, Server, Client, StreamConn } from '../srpc'
2
+ import { createHandler, createMux, Server, Client, StreamConn, ChannelStream, combineUint8ArrayListTransform } from '../srpc'
3
3
  import { EchoerDefinition, EchoerServer, runClientTest } from '../echo'
4
4
  import { runAbortControllerTest, runRpcStreamTest } from '../echo/client-test'
5
5
 
@@ -9,16 +9,37 @@ async function runRPC() {
9
9
  const echoer = new EchoerServer(server)
10
10
  mux.register(createHandler(EchoerDefinition, echoer))
11
11
 
12
+ // StreamConn is unnecessary since ChannelStream has packet framing.
13
+ // Use it here to include yamux in this e2e test.
12
14
  const clientConn = new StreamConn()
13
15
  const serverConn = new StreamConn(server, { direction: 'inbound' })
14
16
 
15
- pipe(clientConn, serverConn, clientConn)
17
+ // pipe clientConn -> messageStream -> serverConn -> messageStream -> clientConn
18
+ const {port1: clientPort, port2: serverPort} = new MessageChannel()
19
+ const clientChannelStream = new ChannelStream('client', clientPort)
20
+ const serverChannelStream = new ChannelStream('server', serverPort)
16
21
 
22
+ // Pipe the client traffic via the client end of the MessageChannel.
23
+ pipe(clientChannelStream, clientConn, combineUint8ArrayListTransform(), clientChannelStream)
24
+ .catch((err: Error) => clientConn.close(err))
25
+ .then(() => clientConn.close())
26
+
27
+ // Pipe the server traffic via the server end of the MessageChannel.
28
+ pipe(serverChannelStream, serverConn, combineUint8ArrayListTransform(), serverChannelStream)
29
+ .catch((err: Error) => serverConn.close(err))
30
+ .then(() => serverConn.close())
31
+
32
+ // Build the client
17
33
  const client = new Client(clientConn.buildOpenStreamFunc())
18
34
 
35
+ // Run the tests
19
36
  await runClientTest(client)
20
37
  await runAbortControllerTest(client)
21
38
  await runRpcStreamTest(client)
39
+
40
+ // Close cleanly
41
+ clientConn.close()
42
+ serverConn.close()
22
43
  }
23
44
 
24
45
  runRPC()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starpc",
3
- "version": "0.25.0",
3
+ "version": "0.25.1",
4
4
  "description": "Streaming protobuf RPC service protocol over any two-way channel.",
5
5
  "license": "MIT",
6
6
  "author": {
package/srpc/channel.ts CHANGED
@@ -5,27 +5,39 @@ import { pushable, Pushable } from 'it-pushable'
5
5
  export interface ChannelStreamMessage<T> {
6
6
  // from indicates who sent the message.
7
7
  from: string
8
- // ack indicates a remote joined the stream.
9
- ack?: boolean
8
+ // ack indicates a remote acked establishing the stream.
9
+ ack?: true
10
10
  // opened indicates the remote has opened the stream.
11
- opened?: boolean
11
+ opened?: true
12
+ // alive indicates this is a keep-alive packet.
13
+ // not set unless keep-alives are enabled.
14
+ alive?: true
12
15
  // closed indicates the stream is closed.
13
- closed?: boolean
16
+ closed?: true
14
17
  // error indicates the stream has an error.
15
18
  error?: Error
16
19
  // data is any message data.
17
20
  data?: T
18
21
  }
19
22
 
20
- // Channel represents a channel we can open a stream over.
21
- export type ChannelPort = MessagePort | { tx: BroadcastChannel; rx: BroadcastChannel }
23
+ // ChannelPort represents a channel we can open a stream over.
24
+ export type ChannelPort =
25
+ | MessagePort
26
+ | { tx: BroadcastChannel; rx: BroadcastChannel }
27
+
28
+ // ChannelStreamOpts are options for ChannelStream.
29
+ export interface ChannelStreamOpts {
30
+ // remoteOpen indicates that the remote already knows the channel is open.
31
+ // this skips sending and waiting for the open+ack messages.
32
+ remoteOpen?: boolean
33
+ }
22
34
 
23
35
  // ChannelStream implements a Stream over a BroadcastChannel duplex or MessagePort.
24
36
  //
25
37
  // NOTE: there is no way to tell if a BroadcastChannel or MessagePort is closed.
26
38
  // This implementation sends a "closed" message when close() is called.
27
39
  // However: if the remote is removed w/o closing cleanly, the stream will be left open!
28
- export class ChannelStream<T>
40
+ export class ChannelStream<T = Uint8Array>
29
41
  implements Duplex<AsyncGenerator<T>, Source<T>, Promise<void>>
30
42
  {
31
43
  // channel is the read/write channel.
@@ -58,24 +70,24 @@ export class ChannelStream<T>
58
70
 
59
71
  // isAcked checks if the stream is acknowledged by the remote.
60
72
  public get isAcked() {
61
- return this.remoteAck || false
73
+ return this.remoteAck ?? false
62
74
  }
63
75
 
64
76
  // isOpen checks if the stream is opened by the remote.
65
77
  public get isOpen() {
66
- return this.remoteOpen || false
78
+ return this.remoteOpen ?? false
67
79
  }
68
80
 
69
- // remoteOpen indicates if we know the remote has already opened the stream.
70
- constructor(localId: string, channel: ChannelPort, remoteOpen: boolean) {
81
+ // remoteOpen indicates that we know the remote has already opened the stream.
82
+ constructor(localId: string, channel: ChannelPort, opts?: ChannelStreamOpts) {
71
83
  this.localId = localId
72
84
  this.channel = channel
73
85
  this.sink = this._createSink()
74
86
 
75
87
  this.localOpen = false
76
- this.remoteAck = remoteOpen
77
- this.remoteOpen = remoteOpen
78
- if (remoteOpen) {
88
+ this.remoteOpen = opts?.remoteOpen ?? false
89
+ this.remoteAck = this.remoteOpen
90
+ if (this.remoteOpen) {
79
91
  this.waitRemoteOpen = Promise.resolve()
80
92
  this.waitRemoteAck = Promise.resolve()
81
93
  } else {
@@ -220,11 +232,11 @@ export function newBroadcastChannelStream<T>(
220
232
  id: string,
221
233
  readName: string,
222
234
  writeName: string,
223
- remoteOpen: boolean,
235
+ opts?: ChannelStreamOpts,
224
236
  ): ChannelStream<T> {
225
237
  return new ChannelStream<T>(
226
238
  id,
227
239
  { tx: new BroadcastChannel(writeName), rx: new BroadcastChannel(readName) },
228
- remoteOpen,
240
+ opts,
229
241
  )
230
242
  }