starpc 0.4.9 → 0.5.2

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 (65) hide show
  1. package/Makefile +1 -0
  2. package/README.md +20 -10
  3. package/dist/echo/client-test.d.ts +1 -0
  4. package/dist/echo/client-test.js +20 -18
  5. package/dist/echo/echo.pb.d.ts +162 -10
  6. package/dist/echo/echo.pb.js +48 -5
  7. package/dist/echo/server.d.ts +8 -4
  8. package/dist/echo/server.js +29 -37
  9. package/dist/rpcstream/rpcstream.d.ts +6 -6
  10. package/dist/rpcstream/rpcstream.js +92 -51
  11. package/dist/rpcstream/rpcstream.pb.d.ts +44 -0
  12. package/dist/rpcstream/rpcstream.pb.js +151 -4
  13. package/dist/srpc/client.d.ts +3 -4
  14. package/dist/srpc/client.js +12 -46
  15. package/dist/srpc/common-rpc.d.ts +3 -2
  16. package/dist/srpc/common-rpc.js +12 -0
  17. package/dist/srpc/definition.d.ts +3 -3
  18. package/dist/srpc/handler.d.ts +2 -3
  19. package/dist/srpc/handler.js +5 -22
  20. package/dist/srpc/index.d.ts +2 -2
  21. package/dist/srpc/index.js +2 -2
  22. package/dist/srpc/packet.js +0 -32
  23. package/dist/srpc/pushable.d.ts +2 -0
  24. package/dist/srpc/pushable.js +13 -0
  25. package/dist/srpc/rpcproto.pb.d.ts +6 -0
  26. package/dist/srpc/rpcproto.pb.js +84 -0
  27. package/dist/srpc/server.d.ts +1 -0
  28. package/dist/srpc/server.js +7 -0
  29. package/dist/srpc/ts-proto-rpc.d.ts +3 -4
  30. package/e2e/e2e.ts +4 -3
  31. package/e2e/e2e_test.go +24 -1
  32. package/echo/client-test.ts +23 -18
  33. package/echo/echo.pb.go +33 -20
  34. package/echo/echo.pb.ts +75 -20
  35. package/echo/echo.proto +4 -0
  36. package/echo/echo_srpc.pb.go +77 -0
  37. package/echo/server.go +18 -0
  38. package/echo/server.ts +47 -41
  39. package/integration/integration.go +1 -2
  40. package/integration/integration.ts +5 -1
  41. package/integration/integration_srpc.pb.go +139 -0
  42. package/package.json +13 -9
  43. package/patches/{ts-poet+4.14.0.patch → ts-poet+4.15.0.patch} +1 -1
  44. package/patches/ts-proto+1.116.0.patch +14 -0
  45. package/srpc/client.ts +16 -50
  46. package/srpc/common-rpc.ts +14 -2
  47. package/srpc/definition.ts +3 -3
  48. package/srpc/handler.ts +17 -34
  49. package/srpc/index.ts +2 -2
  50. package/srpc/muxed-conn.go +2 -2
  51. package/srpc/packet-rw.go +4 -6
  52. package/srpc/packet.ts +0 -33
  53. package/srpc/pushable.ts +17 -0
  54. package/srpc/rpcproto.pb.ts +106 -0
  55. package/srpc/server-pipe.go +2 -2
  56. package/srpc/server.go +2 -2
  57. package/srpc/server.ts +8 -0
  58. package/srpc/ts-proto-rpc.ts +4 -6
  59. package/srpc/websocket.go +2 -2
  60. package/dist/echo/sever.d.ts +0 -0
  61. package/dist/echo/sever.js +0 -1
  62. package/dist/srpc/observable-source.d.ts +0 -9
  63. package/dist/srpc/observable-source.js +0 -25
  64. package/echo/sever.ts +0 -0
  65. package/srpc/observable-source.ts +0 -40
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starpc",
3
- "version": "0.4.9",
3
+ "version": "0.5.2",
4
4
  "description": "Streaming protobuf RPC service protocol over any two-way channel.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -35,14 +35,16 @@
35
35
  "scripts": {
36
36
  "build": "rimraf ./dist && tsc --project tsconfig.build.json --outDir ./dist/",
37
37
  "check": "tsc",
38
- "deps": "depcheck",
38
+ "deps": "depcheck --ignores 'bufferutil,utf-8-validate'",
39
39
  "codegen": "npm run gen",
40
40
  "ci": "npm run build && npm run lint:js && npm run lint:go",
41
41
  "format": "prettier --write './{srpc,echo,e2e,integration,rpcstream}/**/(*.ts|*.tsx|*.html|*.css)'",
42
42
  "gen": "make genproto",
43
43
  "test": "npm run test:js && npm run test:go",
44
44
  "test:go": "make test",
45
- "test:js": "npm run build && cd e2e && esbuild e2e.ts --sourcemap --outfile=e2e.js --bundle --platform=node && node ./e2e.js",
45
+ "build:e2e": "npm run build && cd e2e && esbuild e2e.ts --sourcemap --outfile=e2e.js --bundle --platform=node",
46
+ "test:js": "npm run build:e2e && cd e2e && node ./e2e.js",
47
+ "debug:js": "npm run build:e2e && cd e2e && node --inspect --inspect-brk ./e2e.js",
46
48
  "test:integration": "make integration",
47
49
  "integration": "npm run test:integration",
48
50
  "lint": "npm run lint:go && npm run lint:js",
@@ -56,16 +58,18 @@
56
58
  "singleQuote": true
57
59
  },
58
60
  "devDependencies": {
59
- "@typescript-eslint/eslint-plugin": "^5.30.0",
60
- "@typescript-eslint/parser": "^5.30.0",
61
+ "@typescript-eslint/eslint-plugin": "^5.30.3",
62
+ "@typescript-eslint/parser": "^5.30.3",
63
+ "bufferutil": "^4.0.6",
61
64
  "depcheck": "^1.4.3",
62
- "esbuild": "^0.14.47",
65
+ "esbuild": "^0.14.48",
63
66
  "eslint": "^8.18.0",
64
67
  "eslint-config-prettier": "^8.5.0",
65
68
  "prettier": "^2.7.1",
66
69
  "rimraf": "^3.0.2",
67
- "ts-proto": "^1.115.5",
68
- "typescript": "^4.7.4"
70
+ "ts-proto": "^1.116.0",
71
+ "typescript": "^4.7.4",
72
+ "utf-8-validate": "^5.0.9"
69
73
  },
70
74
  "dependencies": {
71
75
  "@libp2p/interface-connection": "^2.1.1",
@@ -73,6 +77,7 @@
73
77
  "@libp2p/mplex": "^4.0.0",
74
78
  "event-iterator": "^2.0.0",
75
79
  "isomorphic-ws": "^5.0.0",
80
+ "it-first": "^1.0.7",
76
81
  "it-length-prefixed": "^7.0.1",
77
82
  "it-pipe": "^2.0.3",
78
83
  "it-pushable": "^3.0.0",
@@ -81,7 +86,6 @@
81
86
  "long": "^5.2.0",
82
87
  "patch-package": "^6.4.7",
83
88
  "protobufjs": "^6.11.3",
84
- "rxjs": "^7.5.5",
85
89
  "ws": "^8.8.0"
86
90
  }
87
91
  }
@@ -1,5 +1,5 @@
1
1
  diff --git a/node_modules/ts-poet/build/Import.js b/node_modules/ts-poet/build/Import.js
2
- index fe1f6d2..280df8f 100644
2
+ index b92bea3..608add9 100644
3
3
  --- a/node_modules/ts-poet/build/Import.js
4
4
  +++ b/node_modules/ts-poet/build/Import.js
5
5
  @@ -293,6 +293,14 @@ function emitImports(imports, ourModulePath, importMappings) {
@@ -0,0 +1,14 @@
1
+ diff --git a/node_modules/ts-proto/build/utils.js b/node_modules/ts-proto/build/utils.js
2
+ index 2148e97..e19b35f 100644
3
+ --- a/node_modules/ts-proto/build/utils.js
4
+ +++ b/node_modules/ts-proto/build/utils.js
5
+ @@ -170,7 +170,8 @@ function getPropertyAccessor(objectName, propertyName, optional = false) {
6
+ }
7
+ exports.getPropertyAccessor = getPropertyAccessor;
8
+ function impProto(options, module, type) {
9
+ - const importString = `${type}@./${module}${options.fileSuffix}`;
10
+ + // NOTE: TypeScript resolves importing foo.js to importing foo.ts.
11
+ + const importString = `${type}@./${module}${options.fileSuffix}.js`;
12
+ if (options.onlyTypes) {
13
+ return (0, ts_poet_1.imp)('t:' + importString);
14
+ }
package/srpc/client.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { pipe } from 'it-pipe'
2
2
  import { pushable, Pushable } from 'it-pushable'
3
- import { Observable, from as observableFrom } from 'rxjs'
4
3
 
5
4
  import type { TsProtoRpc } from './ts-proto-rpc.js'
6
5
  import type { OpenStreamFunc } from './stream.js'
7
6
  import { ClientRPC } from './client-rpc.js'
7
+ import { writeToPushable } from './pushable.js'
8
8
  import {
9
9
  decodePacketSource,
10
10
  encodePacketSource,
@@ -12,21 +12,6 @@ import {
12
12
  prependLengthPrefixTransform,
13
13
  } from './packet.js'
14
14
 
15
- // writeClientStream registers the subscriber to write the client data stream.
16
- function writeClientStream(call: ClientRPC, data: Observable<Uint8Array>) {
17
- data.subscribe({
18
- next(value) {
19
- call.writeCallData(value)
20
- },
21
- error(err) {
22
- call.close(err)
23
- },
24
- complete() {
25
- call.writeCallData(undefined, true)
26
- },
27
- })
28
- }
29
-
30
15
  // Client implements the ts-proto Rpc interface with the drpcproto protocol.
31
16
  export class Client implements TsProtoRpc {
32
17
  // openStreamFn is a promise which contains the OpenStreamFunc.
@@ -92,10 +77,10 @@ export class Client implements TsProtoRpc {
92
77
  public async clientStreamingRequest(
93
78
  service: string,
94
79
  method: string,
95
- data: Observable<Uint8Array>
80
+ data: AsyncIterable<Uint8Array>
96
81
  ): Promise<Uint8Array> {
97
82
  const call = await this.startRpc(service, method, null)
98
- writeClientStream(call, data)
83
+ call.writeCallDataFromSource(data)
99
84
  for await (const data of call.rpcDataSource) {
100
85
  call.close()
101
86
  return data
@@ -110,21 +95,13 @@ export class Client implements TsProtoRpc {
110
95
  service: string,
111
96
  method: string,
112
97
  data: Uint8Array
113
- ): Observable<Uint8Array> {
114
- const pushServerData: Pushable<Uint8Array> = pushable({ objectMode: true })
115
- const serverData = observableFrom(pushServerData)
98
+ ): AsyncIterable<Uint8Array> {
99
+ const serverData: Pushable<Uint8Array> = pushable({ objectMode: true })
116
100
  this.startRpc(service, method, data)
117
101
  .then(async (call) => {
118
- try {
119
- for await (const data of call.rpcDataSource) {
120
- pushServerData.push(data)
121
- }
122
- } catch (err) {
123
- pushServerData.throw(err as Error)
124
- }
125
- pushServerData.end()
102
+ return writeToPushable(call.rpcDataSource, serverData)
126
103
  })
127
- .catch(pushServerData.throw.bind(pushServerData))
104
+ .catch(serverData.throw.bind(serverData))
128
105
  return serverData
129
106
  }
130
107
 
@@ -132,33 +109,22 @@ export class Client implements TsProtoRpc {
132
109
  public bidirectionalStreamingRequest(
133
110
  service: string,
134
111
  method: string,
135
- data: Observable<Uint8Array>
136
- ): Observable<Uint8Array> {
137
- const pushServerData: Pushable<Uint8Array> = pushable({ objectMode: true })
138
- const serverData = observableFrom(pushServerData)
112
+ data: AsyncIterable<Uint8Array>
113
+ ): AsyncIterable<Uint8Array> {
114
+ const serverData: Pushable<Uint8Array> = pushable({ objectMode: true })
139
115
  this.startRpc(service, method, null)
140
116
  .then(async (call) => {
117
+ call.writeCallDataFromSource(data)
141
118
  try {
142
- data.subscribe({
143
- next(value) {
144
- call.writeCallData(value)
145
- },
146
- error(err) {
147
- call.close(err)
148
- },
149
- complete() {
150
- call.close()
151
- },
152
- })
153
- for await (const data of call.rpcDataSource) {
154
- pushServerData.push(data)
119
+ for await (const message of call.rpcDataSource) {
120
+ serverData.push(message)
155
121
  }
156
122
  } catch (err) {
157
- pushServerData.throw(err as Error)
123
+ serverData.throw(err as Error)
158
124
  }
159
- pushServerData.end()
125
+ serverData.end()
160
126
  })
161
- .catch(pushServerData.throw.bind(pushServerData))
127
+ .catch(serverData.throw.bind(serverData))
162
128
  return serverData
163
129
  }
164
130
 
@@ -1,4 +1,4 @@
1
- import type { Source, Sink } from 'it-stream-types'
1
+ import type { Sink } from 'it-stream-types'
2
2
  import { pushable } from 'it-pushable'
3
3
 
4
4
  import type { CallData, CallStart } from './rpcproto.pb.js'
@@ -16,7 +16,7 @@ export class CommonRPC {
16
16
  end: (err?: Error) => void
17
17
  }
18
18
  // rpcDataSource emits incoming client RPC messages to the caller.
19
- public readonly rpcDataSource: Source<Uint8Array>
19
+ public readonly rpcDataSource: AsyncIterable<Uint8Array>
20
20
  // _rpcDataSource is used to write to the rpc message source.
21
21
  private readonly _rpcDataSource: {
22
22
  push: (val: Uint8Array) => void
@@ -60,6 +60,18 @@ export class CommonRPC {
60
60
  })
61
61
  }
62
62
 
63
+ // writeCallDataFromSource writes all call data from the iterable.
64
+ public async writeCallDataFromSource(dataSource: AsyncIterable<Uint8Array>) {
65
+ try {
66
+ for await (const data of dataSource) {
67
+ this.writeCallData(data)
68
+ }
69
+ this.writeCallData(undefined, true)
70
+ } catch (err) {
71
+ this.close(err as Error)
72
+ }
73
+ }
74
+
63
75
  // writePacket writes a packet to the stream.
64
76
  protected async writePacket(packet: Packet) {
65
77
  this._source.push(packet)
@@ -15,16 +15,16 @@ export interface Definition {
15
15
 
16
16
  // MethodDefinition describes the method definitions generated by ts-proto.
17
17
  // use --ts_proto_opt=outputServices=default,outputServices=generic-definitions
18
- export interface MethodDefinition<RequestType, ResponseType> {
18
+ export interface MethodDefinition<RequestT, ResponseT> {
19
19
  // name is the name and function name of the method.
20
20
  // e.x.: Echo
21
21
  name: string
22
22
  // requestType is the object used for the request.
23
- requestType: MessageDefinition<RequestType>
23
+ requestType: MessageDefinition<RequestT>
24
24
  // requestStream indicates the request is a stream.
25
25
  requestStream: boolean
26
26
  // responseType is the object used for the response.
27
- responseType: MessageDefinition<ResponseType>
27
+ responseType: MessageDefinition<ResponseT>
28
28
  // responseStream indicates the response is a stream.
29
29
  responseStream: boolean
30
30
  }
package/srpc/handler.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import type { Sink, Source } from 'it-stream-types'
2
2
  import { pipe } from 'it-pipe'
3
3
  import { pushable } from 'it-pushable'
4
- import { Observable, from as observableFrom } from 'rxjs'
5
4
 
6
5
  import { Definition, MethodDefinition } from './definition.js'
7
6
  import {
8
7
  buildDecodeMessageTransform,
9
8
  buildEncodeMessageTransform,
10
9
  } from './message.js'
10
+ import { writeToPushable } from './pushable.js'
11
11
 
12
12
  // InvokeFn describes an SRPC call method invoke function.
13
13
  export type InvokeFn = (
@@ -65,21 +65,21 @@ export class StaticHandler implements Handler {
65
65
  }
66
66
 
67
67
  // MethodProto is a function which matches one of the RPC signatures.
68
- type MethodProto =
69
- | ((request: unknown) => Promise<unknown>)
70
- | ((request: unknown) => Observable<unknown>)
71
- | ((request: Observable<unknown>) => Promise<unknown>)
72
- | ((request: Observable<unknown>) => Observable<unknown>)
68
+ type MethodProto<R, O> =
69
+ | ((request: R) => Promise<O>)
70
+ | ((request: R) => AsyncIterable<O>)
71
+ | ((request: AsyncIterable<R>) => Promise<O>)
72
+ | ((request: AsyncIterable<R>) => AsyncIterable<O>)
73
73
 
74
74
  // createInvokeFn builds an InvokeFn from a method definition and a function prototype.
75
- export function createInvokeFn(
76
- methodInfo: MethodDefinition<unknown, unknown>,
77
- methodProto: MethodProto
75
+ export function createInvokeFn<R, O>(
76
+ methodInfo: MethodDefinition<R, O>,
77
+ methodProto: MethodProto<R, O>
78
78
  ): InvokeFn {
79
- const requestDecode = buildDecodeMessageTransform(methodInfo.requestType)
79
+ const requestDecode = buildDecodeMessageTransform<R>(methodInfo.requestType)
80
80
  return async (dataSource: Source<Uint8Array>, dataSink: Sink<Uint8Array>) => {
81
81
  // responseSink is a Sink for response messages.
82
- const responseSink = pushable<unknown>({
82
+ const responseSink = pushable<O>({
83
83
  objectMode: true,
84
84
  })
85
85
 
@@ -96,8 +96,8 @@ export function createInvokeFn(
96
96
  // build the request argument.
97
97
  let requestArg: any
98
98
  if (methodInfo.requestStream) {
99
- // convert the request data source into an Observable<T>
100
- requestArg = observableFrom(requestSource)
99
+ // use the request source as the argument.
100
+ requestArg = requestSource
101
101
  } else {
102
102
  // receive a single message for the argument.
103
103
  for await (const msg of requestSource) {
@@ -119,27 +119,10 @@ export function createInvokeFn(
119
119
  throw new Error('return value was undefined')
120
120
  }
121
121
  if (methodInfo.responseStream) {
122
- const responseObs = responseObj as Observable<unknown>
123
- if (!responseObs.subscribe) {
124
- throw new Error('expected return value to be an Observable')
125
- }
126
- return new Promise<void>((resolve, reject) => {
127
- responseObs.subscribe({
128
- next(value) {
129
- responseSink.push(value)
130
- },
131
- error: (err: any) => {
132
- responseSink.throw(err)
133
- reject(err)
134
- },
135
- complete: () => {
136
- responseSink.end()
137
- resolve()
138
- },
139
- })
140
- })
122
+ const response = responseObj as AsyncIterable<O>
123
+ return writeToPushable(response, responseSink)
141
124
  } else {
142
- const responsePromise = responseObj as Promise<unknown>
125
+ const responsePromise = responseObj as Promise<O>
143
126
  if (!responsePromise.then) {
144
127
  throw new Error('expected return value to be a Promise')
145
128
  }
@@ -166,7 +149,7 @@ export function createHandler(definition: Definition, impl: any): Handler {
166
149
  const methodMap: MethodMap = {}
167
150
  for (const methodInfo of Object.values(definition.methods)) {
168
151
  const methodName = methodInfo.name
169
- let methodProto: MethodProto = impl[methodName]
152
+ let methodProto: MethodProto<unknown, unknown> = impl[methodName]
170
153
  if (!methodProto) {
171
154
  continue
172
155
  }
package/srpc/index.ts CHANGED
@@ -4,7 +4,7 @@ export { Server } from './server.js'
4
4
  export { Conn, ConnParams } from './conn.js'
5
5
  export { Handler, InvokeFn, createHandler, createInvokeFn } from './handler.js'
6
6
  export { Packet, CallStart, CallData } from './rpcproto.pb.js'
7
- export { Mux, createMux } from './mux.js'
7
+ export { Mux, StaticMux, createMux } from './mux.js'
8
8
  export {
9
9
  BroadcastChannelDuplex,
10
10
  newBroadcastChannelDuplex,
@@ -15,4 +15,4 @@ export {
15
15
  newMessagePortIterable,
16
16
  MessagePortConn,
17
17
  } from './message-port.js'
18
- export { ObservableSource } from './observable-source.js'
18
+ export { writeToPushable } from './pushable'
@@ -19,9 +19,9 @@ func NewOpenStreamWithMuxedConn(conn network.MuxedConn) OpenStreamFunc {
19
19
  if err != nil {
20
20
  return nil, err
21
21
  }
22
- rw := NewPacketReadWriter(mstrm, msgHandler)
22
+ rw := NewPacketReadWriter(mstrm)
23
23
  go func() {
24
- err := rw.ReadPump()
24
+ err := rw.ReadPump(msgHandler)
25
25
  if err != nil {
26
26
  _ = rw.Close()
27
27
  }
package/srpc/packet-rw.go CHANGED
@@ -16,15 +16,13 @@ var maxMessageSize = 1e7
16
16
  type PacketReaderWriter struct {
17
17
  // rw is the io.ReadWriterCloser
18
18
  rw io.ReadWriteCloser
19
- // cb is the callback
20
- cb PacketHandler
21
19
  // buf is the buffered data
22
20
  buf bytes.Buffer
23
21
  }
24
22
 
25
23
  // NewPacketReadWriter constructs a new read/writer.
26
- func NewPacketReadWriter(rw io.ReadWriteCloser, cb PacketHandler) *PacketReaderWriter {
27
- return &PacketReaderWriter{rw: rw, cb: cb}
24
+ func NewPacketReadWriter(rw io.ReadWriteCloser) *PacketReaderWriter {
25
+ return &PacketReaderWriter{rw: rw}
28
26
  }
29
27
 
30
28
  // WritePacket writes a packet to the writer.
@@ -41,7 +39,7 @@ func (r *PacketReaderWriter) WritePacket(p *Packet) error {
41
39
  }
42
40
 
43
41
  // ReadPump executes the read pump in a goroutine.
44
- func (r *PacketReaderWriter) ReadPump() error {
42
+ func (r *PacketReaderWriter) ReadPump(cb PacketHandler) error {
45
43
  var currLen uint32
46
44
  buf := make([]byte, 2048)
47
45
  for {
@@ -78,7 +76,7 @@ func (r *PacketReaderWriter) ReadPump() error {
78
76
  if err := npkt.UnmarshalVT(pkt); err != nil {
79
77
  return err
80
78
  }
81
- if err := r.cb(npkt); err != nil {
79
+ if err := cb(npkt); err != nil {
82
80
  return err
83
81
  }
84
82
  }
package/srpc/packet.ts CHANGED
@@ -88,36 +88,3 @@ export function prependPacketLen(msgData: Uint8Array): Uint8Array {
88
88
  merged.set(msgData, msgLenData.length)
89
89
  return merged
90
90
  }
91
-
92
- /*
93
- // buildCallDataPacket builds a CallData packet.
94
- function buildCallDataPacket(data: Uint8Array): Packet {
95
- const callData: CallData = {
96
- data: p,
97
- complete: false,
98
- error: '',
99
- }
100
- const pkt: Packet = {
101
- body: {
102
- $case: 'callData',
103
- callData: callData,
104
- }
105
- }
106
- return pkt
107
- }
108
-
109
- // wrapCallDataTransform is a transformer that wraps call data into a Packet.
110
- export async function* wrapCallDataTransform(
111
- source: Source<Uint8Array | Uint8Array>
112
- ): AsyncIterable<Packet> {
113
- for await (const pkt of source) {
114
- if (Array.isArray(pkt)) {
115
- for (const p of pkt) {
116
- yield* [buildCallDataPacket(p)]
117
- }
118
- } else {
119
- yield* [buildCallDataPacket(pkt)]
120
- }
121
- }
122
- }
123
- */
@@ -0,0 +1,17 @@
1
+ import { Pushable } from 'it-pushable'
2
+
3
+ // writeToPushable writes the incoming server data to the pushable.
4
+ export async function writeToPushable<T>(
5
+ dataSource: AsyncIterable<T>,
6
+ out: Pushable<T>
7
+ ) {
8
+ try {
9
+ for await (const data of dataSource) {
10
+ out.push(data)
11
+ }
12
+ out.end()
13
+ } catch (err) {
14
+ out.end(err as Error)
15
+ throw err
16
+ }
17
+ }
@@ -95,6 +95,40 @@ export const Packet = {
95
95
  return message
96
96
  },
97
97
 
98
+ // encodeTransform encodes a source of message objects.
99
+ // Transform<Packet, Uint8Array>
100
+ async *encodeTransform(
101
+ source: AsyncIterable<Packet | Packet[]> | Iterable<Packet | Packet[]>
102
+ ): AsyncIterable<Uint8Array> {
103
+ for await (const pkt of source) {
104
+ if (Array.isArray(pkt)) {
105
+ for (const p of pkt) {
106
+ yield* [Packet.encode(p).finish()]
107
+ }
108
+ } else {
109
+ yield* [Packet.encode(pkt).finish()]
110
+ }
111
+ }
112
+ },
113
+
114
+ // decodeTransform decodes a source of encoded messages.
115
+ // Transform<Uint8Array, Packet>
116
+ async *decodeTransform(
117
+ source:
118
+ | AsyncIterable<Uint8Array | Uint8Array[]>
119
+ | Iterable<Uint8Array | Uint8Array[]>
120
+ ): AsyncIterable<Packet> {
121
+ for await (const pkt of source) {
122
+ if (Array.isArray(pkt)) {
123
+ for (const p of pkt) {
124
+ yield* [Packet.decode(p)]
125
+ }
126
+ } else {
127
+ yield* [Packet.decode(pkt)]
128
+ }
129
+ }
130
+ },
131
+
98
132
  fromJSON(object: any): Packet {
99
133
  return {
100
134
  body: isSet(object.callStart)
@@ -203,6 +237,42 @@ export const CallStart = {
203
237
  return message
204
238
  },
205
239
 
240
+ // encodeTransform encodes a source of message objects.
241
+ // Transform<CallStart, Uint8Array>
242
+ async *encodeTransform(
243
+ source:
244
+ | AsyncIterable<CallStart | CallStart[]>
245
+ | Iterable<CallStart | CallStart[]>
246
+ ): AsyncIterable<Uint8Array> {
247
+ for await (const pkt of source) {
248
+ if (Array.isArray(pkt)) {
249
+ for (const p of pkt) {
250
+ yield* [CallStart.encode(p).finish()]
251
+ }
252
+ } else {
253
+ yield* [CallStart.encode(pkt).finish()]
254
+ }
255
+ }
256
+ },
257
+
258
+ // decodeTransform decodes a source of encoded messages.
259
+ // Transform<Uint8Array, CallStart>
260
+ async *decodeTransform(
261
+ source:
262
+ | AsyncIterable<Uint8Array | Uint8Array[]>
263
+ | Iterable<Uint8Array | Uint8Array[]>
264
+ ): AsyncIterable<CallStart> {
265
+ for await (const pkt of source) {
266
+ if (Array.isArray(pkt)) {
267
+ for (const p of pkt) {
268
+ yield* [CallStart.decode(p)]
269
+ }
270
+ } else {
271
+ yield* [CallStart.decode(pkt)]
272
+ }
273
+ }
274
+ },
275
+
206
276
  fromJSON(object: any): CallStart {
207
277
  return {
208
278
  rpcService: isSet(object.rpcService) ? String(object.rpcService) : '',
@@ -294,6 +364,42 @@ export const CallData = {
294
364
  return message
295
365
  },
296
366
 
367
+ // encodeTransform encodes a source of message objects.
368
+ // Transform<CallData, Uint8Array>
369
+ async *encodeTransform(
370
+ source:
371
+ | AsyncIterable<CallData | CallData[]>
372
+ | Iterable<CallData | CallData[]>
373
+ ): AsyncIterable<Uint8Array> {
374
+ for await (const pkt of source) {
375
+ if (Array.isArray(pkt)) {
376
+ for (const p of pkt) {
377
+ yield* [CallData.encode(p).finish()]
378
+ }
379
+ } else {
380
+ yield* [CallData.encode(pkt).finish()]
381
+ }
382
+ }
383
+ },
384
+
385
+ // decodeTransform decodes a source of encoded messages.
386
+ // Transform<Uint8Array, CallData>
387
+ async *decodeTransform(
388
+ source:
389
+ | AsyncIterable<Uint8Array | Uint8Array[]>
390
+ | Iterable<Uint8Array | Uint8Array[]>
391
+ ): AsyncIterable<CallData> {
392
+ for await (const pkt of source) {
393
+ if (Array.isArray(pkt)) {
394
+ for (const p of pkt) {
395
+ yield* [CallData.decode(p)]
396
+ }
397
+ } else {
398
+ yield* [CallData.decode(pkt)]
399
+ }
400
+ }
401
+ },
402
+
297
403
  fromJSON(object: any): CallData {
298
404
  return {
299
405
  data: isSet(object.data)
@@ -14,9 +14,9 @@ func NewServerPipe(server *Server) OpenStreamFunc {
14
14
  go func() {
15
15
  _ = server.HandleStream(ctx, srvPipe)
16
16
  }()
17
- clientPrw := NewPacketReadWriter(clientPipe, msgHandler)
17
+ clientPrw := NewPacketReadWriter(clientPipe)
18
18
  go func() {
19
- err := clientPrw.ReadPump()
19
+ err := clientPrw.ReadPump(msgHandler)
20
20
  if err != nil {
21
21
  _ = clientPrw.Close()
22
22
  }
package/srpc/server.go CHANGED
@@ -30,9 +30,9 @@ func (s *Server) HandleStream(ctx context.Context, rwc io.ReadWriteCloser) error
30
30
  subCtx, subCtxCancel := context.WithCancel(ctx)
31
31
  defer subCtxCancel()
32
32
  serverRPC := NewServerRPC(subCtx, s.mux)
33
- prw := NewPacketReadWriter(rwc, serverRPC.HandlePacket)
33
+ prw := NewPacketReadWriter(rwc)
34
34
  serverRPC.SetWriter(prw)
35
- err := prw.ReadPump()
35
+ err := prw.ReadPump(serverRPC.HandlePacket)
36
36
  _ = rwc.Close()
37
37
  return err
38
38
  }
package/srpc/server.ts CHANGED
@@ -48,6 +48,14 @@ export class Server implements StreamHandler {
48
48
  return rpc
49
49
  }
50
50
 
51
+ // handlePacketDuplex handles an incoming Uint8Array duplex.
52
+ // skips the packet length prefix transform.
53
+ public handlePacketDuplex(stream: Duplex<Uint8Array>): ServerRPC {
54
+ const rpc = this.startRpc()
55
+ pipe(stream, decodePacketSource, rpc, encodePacketSource, stream)
56
+ return rpc
57
+ }
58
+
51
59
  // handlePacketStream handles an incoming Packet duplex.
52
60
  public handlePacketStream(stream: Duplex<Packet>): ServerRPC {
53
61
  const rpc = this.startRpc()
@@ -1,5 +1,3 @@
1
- import type { Observable } from 'rxjs'
2
-
3
1
  // TsProtoRpc matches the Rpc interface generated by ts-proto.
4
2
  export interface TsProtoRpc {
5
3
  // request fires a one-off unary RPC request.
@@ -12,18 +10,18 @@ export interface TsProtoRpc {
12
10
  clientStreamingRequest(
13
11
  service: string,
14
12
  method: string,
15
- data: Observable<Uint8Array>
13
+ data: AsyncIterable<Uint8Array>
16
14
  ): Promise<Uint8Array>
17
15
  // serverStreamingRequest fires a one-way server->client streaming request.
18
16
  serverStreamingRequest(
19
17
  service: string,
20
18
  method: string,
21
19
  data: Uint8Array
22
- ): Observable<Uint8Array>
20
+ ): AsyncIterable<Uint8Array>
23
21
  // bidirectionalStreamingRequest implements a two-way streaming request.
24
22
  bidirectionalStreamingRequest(
25
23
  service: string,
26
24
  method: string,
27
- data: Observable<Uint8Array>
28
- ): Observable<Uint8Array>
25
+ data: AsyncIterable<Uint8Array>
26
+ ): AsyncIterable<Uint8Array>
29
27
  }