starpc 0.1.7 → 0.2.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.
Files changed (73) hide show
  1. package/Makefile +1 -0
  2. package/README.md +24 -5
  3. package/dist/echo/client-test.d.ts +2 -0
  4. package/dist/echo/client-test.js +35 -0
  5. package/dist/echo/echo.d.ts +124 -0
  6. package/dist/echo/echo.js +42 -0
  7. package/dist/echo/index.d.ts +3 -0
  8. package/dist/echo/index.js +3 -0
  9. package/dist/echo/server.d.ts +8 -0
  10. package/dist/echo/server.js +50 -0
  11. package/dist/echo/sever.d.ts +0 -0
  12. package/dist/echo/sever.js +1 -0
  13. package/dist/srpc/broadcast-channel.d.ts +3 -2
  14. package/dist/srpc/broadcast-channel.js +2 -2
  15. package/dist/srpc/client-rpc.d.ts +4 -28
  16. package/dist/srpc/client-rpc.js +9 -152
  17. package/dist/srpc/client.d.ts +2 -2
  18. package/dist/srpc/client.js +56 -84
  19. package/dist/srpc/common-rpc.d.ts +24 -0
  20. package/dist/srpc/common-rpc.js +140 -0
  21. package/dist/srpc/conn.d.ts +8 -3
  22. package/dist/srpc/conn.js +19 -3
  23. package/dist/srpc/definition.d.ts +15 -0
  24. package/dist/srpc/definition.js +1 -0
  25. package/dist/srpc/handler.d.ts +24 -0
  26. package/dist/srpc/handler.js +123 -0
  27. package/dist/srpc/index.d.ts +7 -3
  28. package/dist/srpc/index.js +6 -2
  29. package/dist/srpc/message.d.ts +11 -0
  30. package/dist/srpc/message.js +32 -0
  31. package/dist/srpc/mux.d.ts +11 -0
  32. package/dist/srpc/mux.js +36 -0
  33. package/dist/srpc/packet.d.ts +4 -4
  34. package/dist/srpc/packet.js +38 -27
  35. package/dist/srpc/rpcproto.d.ts +18 -43
  36. package/dist/srpc/rpcproto.js +37 -78
  37. package/dist/srpc/server-rpc.d.ts +11 -0
  38. package/dist/srpc/server-rpc.js +55 -0
  39. package/dist/srpc/server.d.ts +14 -0
  40. package/dist/srpc/server.js +31 -0
  41. package/dist/srpc/websocket.d.ts +3 -2
  42. package/dist/srpc/websocket.js +4 -4
  43. package/e2e/README.md +10 -0
  44. package/e2e/e2e.ts +27 -0
  45. package/echo/client-test.ts +41 -0
  46. package/echo/echo.ts +45 -0
  47. package/echo/index.ts +3 -0
  48. package/echo/server.ts +57 -0
  49. package/echo/sever.ts +0 -0
  50. package/integration/integration.bash +1 -1
  51. package/integration/integration.ts +10 -42
  52. package/package.json +18 -17
  53. package/srpc/broadcast-channel.ts +8 -3
  54. package/srpc/client-rpc.go +1 -9
  55. package/srpc/client-rpc.ts +11 -175
  56. package/srpc/client.ts +58 -99
  57. package/srpc/common-rpc.ts +171 -0
  58. package/srpc/conn.ts +33 -5
  59. package/srpc/definition.ts +30 -0
  60. package/srpc/handler.ts +174 -0
  61. package/srpc/index.ts +7 -3
  62. package/srpc/message.ts +60 -0
  63. package/srpc/mux.ts +56 -0
  64. package/srpc/packet.go +4 -11
  65. package/srpc/packet.ts +44 -30
  66. package/srpc/rpcproto.pb.go +54 -118
  67. package/srpc/rpcproto.proto +7 -12
  68. package/srpc/rpcproto.ts +38 -101
  69. package/srpc/rpcproto_vtproto.pb.go +58 -210
  70. package/srpc/server-rpc.go +5 -10
  71. package/srpc/server-rpc.ts +65 -0
  72. package/srpc/server.ts +56 -0
  73. package/srpc/websocket.ts +6 -4
package/e2e/e2e.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { pipe } from 'it-pipe'
2
+ import { createHandler, createMux, Server, Client, Conn } from '../srpc'
3
+ import { EchoerDefinition, EchoerServer, runClientTest } from '../echo'
4
+
5
+ async function runRPC() {
6
+ const mux = createMux()
7
+ const echoer = new EchoerServer()
8
+ mux.register(createHandler(EchoerDefinition, echoer))
9
+ const server = new Server(mux)
10
+
11
+ const clientConn = new Conn()
12
+ const serverConn = new Conn(server)
13
+ pipe(clientConn, serverConn, clientConn)
14
+ const client = new Client(clientConn.buildOpenStreamFunc())
15
+
16
+ await runClientTest(client)
17
+ }
18
+
19
+ runRPC()
20
+ .then(() => {
21
+ console.log('finished successfully')
22
+ })
23
+ .catch((err) => {
24
+ console.log('failed')
25
+ console.error(err)
26
+ process.exit(1)
27
+ })
@@ -0,0 +1,41 @@
1
+ import { Client } from '../srpc/index.js'
2
+ import { EchoerClientImpl, EchoMsg } from './echo.js'
3
+ import { Observable } from 'rxjs'
4
+
5
+ export async function runClientTest(client: Client) {
6
+ const demoServiceClient = new EchoerClientImpl(client)
7
+
8
+ console.log('Calling Echo: unary call...')
9
+ let result = await demoServiceClient.Echo({
10
+ body: 'Hello world!',
11
+ })
12
+ console.log('success: output', result.body)
13
+
14
+ // observable for client requests
15
+ const clientRequestStream = new Observable<EchoMsg>((subscriber) => {
16
+ subscriber.next({ body: 'Hello world from streaming request.' })
17
+ subscriber.complete()
18
+ })
19
+
20
+ console.log('Calling EchoClientStream: client -> server...')
21
+ result = await demoServiceClient.EchoClientStream(clientRequestStream)
22
+ console.log('success: output', result.body)
23
+
24
+ console.log('Calling EchoServerStream: server -> client...')
25
+ const serverStream = demoServiceClient.EchoServerStream({
26
+ body: 'Hello world from server to client streaming request.',
27
+ })
28
+ await new Promise<void>((resolve, reject) => {
29
+ serverStream.subscribe({
30
+ next(result) {
31
+ console.log('server: output', result.body)
32
+ },
33
+ complete() {
34
+ resolve()
35
+ },
36
+ error(err: Error) {
37
+ reject(err)
38
+ },
39
+ })
40
+ })
41
+ }
package/echo/echo.ts CHANGED
@@ -125,6 +125,51 @@ export class EchoerClientImpl implements Echoer {
125
125
  }
126
126
  }
127
127
 
128
+ /** Echoer service returns the given message. */
129
+ export type EchoerDefinition = typeof EchoerDefinition
130
+ export const EchoerDefinition = {
131
+ name: 'Echoer',
132
+ fullName: 'echo.Echoer',
133
+ methods: {
134
+ /** Echo returns the given message. */
135
+ echo: {
136
+ name: 'Echo',
137
+ requestType: EchoMsg,
138
+ requestStream: false,
139
+ responseType: EchoMsg,
140
+ responseStream: false,
141
+ options: {},
142
+ },
143
+ /** EchoServerStream is an example of a server -> client one-way stream. */
144
+ echoServerStream: {
145
+ name: 'EchoServerStream',
146
+ requestType: EchoMsg,
147
+ requestStream: false,
148
+ responseType: EchoMsg,
149
+ responseStream: true,
150
+ options: {},
151
+ },
152
+ /** EchoClientStream is an example of client->server one-way stream. */
153
+ echoClientStream: {
154
+ name: 'EchoClientStream',
155
+ requestType: EchoMsg,
156
+ requestStream: true,
157
+ responseType: EchoMsg,
158
+ responseStream: false,
159
+ options: {},
160
+ },
161
+ /** EchoBidiStream is an example of a two-way stream. */
162
+ echoBidiStream: {
163
+ name: 'EchoBidiStream',
164
+ requestType: EchoMsg,
165
+ requestStream: true,
166
+ responseType: EchoMsg,
167
+ responseStream: true,
168
+ options: {},
169
+ },
170
+ },
171
+ } as const
172
+
128
173
  interface Rpc {
129
174
  request(
130
175
  service: string,
package/echo/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './echo.js'
2
+ export * from './server.js'
3
+ export { runClientTest } from './client-test.js'
package/echo/server.ts ADDED
@@ -0,0 +1,57 @@
1
+ import { Observable, from as observableFrom } from 'rxjs'
2
+ import { Echoer, EchoMsg } from './echo'
3
+ import { pushable, Pushable } from 'it-pushable'
4
+
5
+ // EchoServer implements the Echoer server.
6
+ export class EchoerServer implements Echoer {
7
+ public async Echo(request: EchoMsg): Promise<EchoMsg> {
8
+ return request
9
+ }
10
+
11
+ public EchoServerStream(request: EchoMsg): Observable<EchoMsg> {
12
+ // send 5 responses, with a 200ms delay for each
13
+ return observableFrom(
14
+ (async function* response(): AsyncIterable<EchoMsg> {
15
+ for (let i = 0; i < 5; i++) {
16
+ yield request
17
+ await new Promise((resolve) => setTimeout(resolve, 200))
18
+ }
19
+ })()
20
+ )
21
+ }
22
+
23
+ public EchoClientStream(request: Observable<EchoMsg>): Promise<EchoMsg> {
24
+ return new Promise<EchoMsg>((resolve, reject) => {
25
+ request.subscribe({
26
+ next(msg) {
27
+ resolve(msg)
28
+ },
29
+ error(err: any) {
30
+ reject(err)
31
+ },
32
+ complete() {
33
+ reject(new Error('none received'))
34
+ },
35
+ })
36
+ })
37
+ }
38
+
39
+ public EchoBidiStream(request: Observable<EchoMsg>): Observable<EchoMsg> {
40
+ // build result observable
41
+ const pushResponse: Pushable<EchoMsg> = pushable({ objectMode: true })
42
+ const response = observableFrom(pushResponse)
43
+ pushResponse.push({ body: 'hello from server' })
44
+ request.subscribe({
45
+ next(msg) {
46
+ pushResponse.push(msg)
47
+ },
48
+ error(err) {
49
+ pushResponse.throw(err)
50
+ },
51
+ complete() {
52
+ pushResponse.end()
53
+ },
54
+ })
55
+ return response
56
+ }
57
+ }
package/echo/sever.ts ADDED
File without changes
@@ -3,7 +3,7 @@ set -eo pipefail
3
3
 
4
4
  echo "Compiling ts..."
5
5
  # ../node_modules/.bin/tsc --out integration.js --project tsconfig.json
6
- ../node_modules/.bin/esbuild integration.ts --bundle --platform=node --outfile=integration.js
6
+ ../node_modules/.bin/esbuild integration.ts --bundle --platform=node --outfile=integration.js
7
7
 
8
8
  echo "Compiling go..."
9
9
  go build -o integration -v ./
@@ -1,7 +1,6 @@
1
- import { WebSocketConn } from '../dist/srpc/websocket.js'
2
- import { EchoerClientImpl, EchoMsg } from '../dist/echo/echo.js'
1
+ import { WebSocketConn } from '../srpc'
2
+ import { runClientTest } from '../echo'
3
3
  import WebSocket from 'isomorphic-ws'
4
- import { Observable } from 'rxjs'
5
4
 
6
5
  async function runRPC() {
7
6
  const addr = 'ws://localhost:5000/demo'
@@ -9,46 +8,15 @@ async function runRPC() {
9
8
  const ws = new WebSocket(addr)
10
9
  const channel = new WebSocketConn(ws)
11
10
  const client = channel.buildClient()
12
- const demoServiceClient = new EchoerClientImpl(client)
13
11
 
14
- console.log('Calling Echo: unary call...')
15
- let result = await demoServiceClient.Echo({
16
- body: "Hello world!"
17
- })
18
- console.log('success: output', result.body)
19
-
20
- // observable for client requests
21
- const clientRequestStream = new Observable<EchoMsg>(subscriber => {
22
- subscriber.next({body: 'Hello world from streaming request.'})
23
- subscriber.complete()
24
- })
25
-
26
- console.log('Calling EchoClientStream: client -> server...')
27
- result = await demoServiceClient.EchoClientStream(clientRequestStream)
28
- console.log('success: output', result.body)
12
+ await runClientTest(client)
13
+ }
29
14
 
30
- console.log('Calling EchoServerStream: server -> client...')
31
- const serverStream = demoServiceClient.EchoServerStream({
32
- body: 'Hello world from server to client streaming request.',
15
+ runRPC()
16
+ .then(() => {
17
+ process.exit(0)
33
18
  })
34
- await new Promise<void>((resolve, reject) => {
35
- serverStream.subscribe({
36
- next(result) {
37
- console.log('server: output', result.body)
38
- },
39
- complete() {
40
- resolve()
41
- },
42
- error(err: Error) {
43
- reject(err)
44
- },
45
- })
19
+ .catch((err) => {
20
+ console.error(err)
21
+ process.exit(1)
46
22
  })
47
- }
48
-
49
- runRPC().then(() => {
50
- process.exit(0)
51
- }).catch((err) => {
52
- console.error(err)
53
- process.exit(1)
54
- })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starpc",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "Streaming protobuf RPC service protocol over any two-way channel.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -15,7 +15,7 @@
15
15
  "url": "http://github.com/paralin"
16
16
  }
17
17
  ],
18
- "main": "dist/srpc/index.js",
18
+ "main": "./dist/srpc/index.js",
19
19
  "types": "./dist/srpc/index.d.ts",
20
20
  "files": [
21
21
  "!**/*.tsbuildinfo",
@@ -34,13 +34,16 @@
34
34
  "url": "git@github.com:aperturerobotics/starpc.git"
35
35
  },
36
36
  "scripts": {
37
- "build": "tsc --project tsconfig.build.json --outDir ./dist/",
37
+ "build": "rimraf ./dist && tsc --project tsconfig.build.json --outDir ./dist/",
38
38
  "check": "tsc",
39
+ "deps": "depcheck",
39
40
  "codegen": "npm run gen",
40
41
  "ci": "npm run build && npm run lint:js && npm run lint:go",
41
- "format": "prettier --write './srpc/**/(*.ts|*.tsx|*.js|*.html|*.css)'",
42
+ "format": "prettier --write './{srpc,echo,e2e,integration}/**/(*.ts|*.tsx|*.html|*.css)'",
42
43
  "gen": "make genproto",
43
- "test": "make test && npm run check",
44
+ "test": "npm run test:js && npm run test:go",
45
+ "test:go": "make test",
46
+ "test:js": "npm run build && cd e2e && esbuild e2e.ts --sourcemap --outfile=e2e.js --bundle --platform=node && node ./e2e.js",
44
47
  "test:integration": "make integration",
45
48
  "integration": "npm run test:integration",
46
49
  "lint": "npm run lint:go && npm run lint:js",
@@ -54,23 +57,21 @@
54
57
  "singleQuote": true
55
58
  },
56
59
  "devDependencies": {
57
- "@types/varint": "^6.0.0",
58
60
  "@typescript-eslint/eslint-plugin": "^5.27.1",
59
61
  "@typescript-eslint/parser": "^5.27.1",
60
- "esbuild": "^0.14.44",
61
- "eslint": "^8.17.0",
62
+ "depcheck": "^1.4.3",
63
+ "esbuild": "^0.14.46",
64
+ "eslint": "^8.18.0",
62
65
  "eslint-config-prettier": "^8.5.0",
63
- "eslint-config-standard-with-typescript": "^21.0.1",
64
- "eslint-plugin-react-hooks": "^4.6.0",
65
- "husky": "^8.0.1",
66
- "prettier": "^2.7.0",
66
+ "prettier": "^2.7.1",
67
+ "rimraf": "^3.0.2",
67
68
  "ts-proto": "^1.115.4",
68
- "typescript": "^4.7.3"
69
+ "typescript": "^4.7.4"
69
70
  },
70
71
  "dependencies": {
71
- "@libp2p/components": "^1.0.0",
72
- "@libp2p/interface-connection": "^1.0.1",
73
- "@libp2p/mplex": "^2.0.0",
72
+ "@libp2p/interface-connection": "^2.0.0",
73
+ "@libp2p/interface-stream-muxer": "^1.0.2",
74
+ "@libp2p/mplex": "^3.0.0",
74
75
  "event-iterator": "^2.0.0",
75
76
  "isomorphic-ws": "^4.0.1",
76
77
  "it-length-prefixed": "^7.0.1",
@@ -78,10 +79,10 @@
78
79
  "it-pushable": "^3.0.0",
79
80
  "it-stream-types": "^1.0.4",
80
81
  "it-ws": "^5.0.2",
82
+ "long": "^5.2.0",
81
83
  "patch-package": "^6.4.7",
82
84
  "protobufjs": "^6.11.3",
83
85
  "rxjs": "^7.5.5",
84
- "ts-poet": "^4.13.0",
85
86
  "ws": "^8.8.0"
86
87
  }
87
88
  }
@@ -1,7 +1,8 @@
1
1
  import type { Duplex, Sink } from 'it-stream-types'
2
- import { Conn } from './conn'
2
+ import { Conn, ConnParams } from './conn'
3
3
  import { EventIterator } from 'event-iterator'
4
4
  import { pipe } from 'it-pipe'
5
+ import { Server } from './server'
5
6
 
6
7
  // BroadcastChannelIterable is a AsyncIterable wrapper for BroadcastChannel.
7
8
  export class BroadcastChannelIterable<T> implements Duplex<T> {
@@ -59,8 +60,12 @@ export class BroadcastChannelConn extends Conn {
59
60
  // channel is the broadcast channel iterable
60
61
  private channel: BroadcastChannelIterable<Uint8Array>
61
62
 
62
- constructor(channel: BroadcastChannel) {
63
- super()
63
+ constructor(
64
+ channel: BroadcastChannel,
65
+ server?: Server,
66
+ connParams?: ConnParams
67
+ ) {
68
+ super(server, connParams)
64
69
  this.channel = new BroadcastChannelIterable<Uint8Array>(channel)
65
70
  pipe(this, this.channel, this)
66
71
  }
@@ -108,8 +108,6 @@ func (r *ClientRPC) HandlePacket(msg *Packet) error {
108
108
  return r.HandleCallStart(b.CallStart)
109
109
  case *Packet_CallData:
110
110
  return r.HandleCallData(b.CallData)
111
- case *Packet_CallStartResp:
112
- return r.HandleCallStartResp(b.CallStartResp)
113
111
  default:
114
112
  return nil
115
113
  }
@@ -127,7 +125,7 @@ func (r *ClientRPC) HandleCallData(pkt *CallData) error {
127
125
  return ErrCompleted
128
126
  }
129
127
 
130
- if data := pkt.GetData(); len(data) != 0 {
128
+ if data := pkt.GetData(); len(data) != 0 || pkt.GetDataIsZero() {
131
129
  select {
132
130
  case <-r.ctx.Done():
133
131
  return context.Canceled
@@ -149,12 +147,6 @@ func (r *ClientRPC) HandleCallData(pkt *CallData) error {
149
147
  return nil
150
148
  }
151
149
 
152
- // HandleCallStartResp handles the CallStartResp packet.
153
- func (r *ClientRPC) HandleCallStartResp(resp *CallStartResp) error {
154
- // client-side calls not supported
155
- return errors.Wrap(ErrUnrecognizedPacket, "call start resp packet unexpected")
156
- }
157
-
158
150
  // Close releases any resources held by the ClientRPC.
159
151
  // not concurrency safe with HandlePacket.
160
152
  func (r *ClientRPC) Close() {
@@ -1,90 +1,25 @@
1
- import type { CallData, CallStart, CallStartResp } from './rpcproto'
2
- import { Packet } from './rpcproto'
3
- import type { Sink } from 'it-stream-types'
4
- import { pushable } from 'it-pushable'
5
-
6
- // DataCb is a callback to handle incoming RPC messages.
7
- // Returns true if more data is expected, false otherwise.
8
- // If returns undefined, assumes more data is expected.
9
- export type DataCb = (data: Uint8Array) => Promise<boolean | void>
1
+ import type { CallStart } from './rpcproto.js'
2
+ import { CommonRPC } from './common-rpc.js'
10
3
 
11
4
  // ClientRPC is an ongoing RPC from the client side.
12
- export class ClientRPC {
13
- // sink is the data sink for incoming messages.
14
- public sink: Sink<Packet>
15
- // source is the packet source for outgoing Packets.
16
- public source: AsyncIterable<Packet>
17
- // _source is used to write to the source.
18
- private readonly _source: {
19
- push: (val: Packet) => void
20
- end: (err?: Error) => void
21
- }
22
- // service is the rpc service
23
- private service: string
24
- // method is the rpc method
25
- private method: string
26
- // dataCb is called with any incoming data.
27
- private dataCb?: DataCb
28
- // started is resolved when the request starts.
29
- private started: Promise<void>
30
- // onStarted is called by the message handler when the request starts.
31
- private onStarted?: (err?: Error) => void
32
- // complete is resolved when the request completes.
33
- // rejected with an error if the call encountered any error.
34
- private complete: Promise<void>
35
- // onComplete is called by the message handler when the call completes.
36
- private onComplete?: (err?: Error) => void
37
- // closed indicates close has been called
38
- private closed: boolean
39
-
40
- constructor(service: string, method: string, dataCb: DataCb | null) {
41
- this.closed = false
42
- this.sink = this._createSink()
43
- const sourcev = this._createSource()
44
- this.source = sourcev
45
- this._source = sourcev
5
+ export class ClientRPC extends CommonRPC {
6
+ constructor(service: string, method: string) {
7
+ super()
46
8
  this.service = service
47
9
  this.method = method
48
- if (dataCb) {
49
- this.dataCb = dataCb
50
- }
51
- this.started = new Promise<void>((resolveStarted, rejectStarted) => {
52
- this.onStarted = (err?: Error) => {
53
- if (err) {
54
- rejectStarted(err)
55
- } else {
56
- resolveStarted()
57
- }
58
- }
59
- })
60
- this.complete = new Promise<void>((resolveComplete, rejectComplete) => {
61
- this.onComplete = (err?: Error) => {
62
- this.closed = true
63
- if (err) {
64
- rejectComplete(err)
65
- } else {
66
- resolveComplete()
67
- }
68
- }
69
- })
70
- }
71
-
72
- // waitStarted returns the started promise.
73
- public waitStarted(): Promise<void> {
74
- return this.started
75
- }
76
-
77
- // waitComplete returns the complete promise.
78
- public waitComplete(): Promise<void> {
79
- return this.complete
80
10
  }
81
11
 
82
12
  // writeCallStart writes the call start packet.
13
+ // if data === undefined and data.length === 0 sends empty data packet.
83
14
  public async writeCallStart(data?: Uint8Array) {
15
+ if (!this.service || !this.method) {
16
+ throw new Error('service and method must be set')
17
+ }
84
18
  const callStart: CallStart = {
85
19
  rpcService: this.service,
86
20
  rpcMethod: this.method,
87
21
  data: data || new Uint8Array(0),
22
+ dataIsZero: !!data && data.length === 0,
88
23
  }
89
24
  await this.writePacket({
90
25
  body: {
@@ -94,110 +29,11 @@ export class ClientRPC {
94
29
  })
95
30
  }
96
31
 
97
- // writeCallData writes the call data packet.
98
- public async writeCallData(
99
- data: Uint8Array,
100
- complete?: boolean,
101
- error?: string
102
- ) {
103
- const callData: CallData = {
104
- data,
105
- complete: complete || false,
106
- error: error || '',
107
- }
108
- await this.writePacket({
109
- body: {
110
- $case: 'callData',
111
- callData,
112
- },
113
- })
114
- }
115
-
116
- // writePacket writes a packet to the stream.
117
- private async writePacket(packet: Packet) {
118
- this._source.push(packet)
119
- }
120
-
121
- // handleMessage handles an incoming encoded Packet.
122
- //
123
- // note: may throw an error if the message was unexpected or invalid.
124
- public async handleMessage(message: Uint8Array) {
125
- return this.handlePacket(Packet.decode(message))
126
- }
127
-
128
- // handlePacket handles an incoming packet.
129
- public async handlePacket(packet: Partial<Packet>) {
130
- switch (packet?.body?.$case) {
131
- case 'callStart':
132
- return this.handleCallStart(packet.body.callStart)
133
- case 'callStartResp':
134
- return this.handleCallStartResp(packet.body.callStartResp)
135
- case 'callData':
136
- return this.handleCallData(packet.body.callData)
137
- }
138
- }
139
-
140
32
  // handleCallStart handles a CallStart packet.
141
- public async handleCallStart(packet: Partial<CallStart>) {
33
+ public override async handleCallStart(packet: Partial<CallStart>) {
142
34
  // we do not implement server -> client RPCs.
143
35
  throw new Error(
144
36
  `unexpected server to client rpc: ${packet.rpcService}/${packet.rpcMethod}`
145
37
  )
146
38
  }
147
-
148
- // handleCallStartResp handles a CallStartResp packet.
149
- public async handleCallStartResp(packet: Partial<CallStartResp>) {
150
- if (packet.error && packet.error.length) {
151
- const err = new Error(packet.error)
152
- this.onStarted!(err)
153
- this.onComplete!(err)
154
- }
155
- }
156
-
157
- // handleCallData handles a CallData packet.
158
- public async handleCallData(packet: Partial<CallData>) {
159
- const data = packet.data
160
- if (this.dataCb && data?.length) {
161
- await this.dataCb(data)
162
- }
163
- if (packet.error && packet.error.length) {
164
- this.onComplete!(new Error(packet.error))
165
- } else if (packet.complete) {
166
- this.onComplete!()
167
- }
168
- }
169
-
170
- // close closes the active call if not already completed.
171
- public async close(err?: Error) {
172
- if (!this.closed) {
173
- await this.writeCallData(new Uint8Array(0), true, err ? err.message : '')
174
- }
175
- if (!err) {
176
- err = new Error('call closed')
177
- }
178
- this.onComplete!(err)
179
- }
180
-
181
- // _createSink initializes the sink field.
182
- private _createSink(): Sink<Packet> {
183
- return async (source) => {
184
- try {
185
- for await (const msg of source) {
186
- await this.handlePacket(msg)
187
- }
188
- } catch (err) {
189
- this.close(err as Error)
190
- }
191
- }
192
- }
193
-
194
- // _createSource initializes the source field.
195
- private _createSource() {
196
- return pushable<Packet>({
197
- objectMode: true,
198
- onEnd: (err?: Error): void => {
199
- this.onComplete!(err)
200
- },
201
- })
202
- }
203
39
  }