starpc 0.30.0 → 0.31.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 (98) hide show
  1. package/Makefile +14 -16
  2. package/README.md +12 -2
  3. package/cmd/protoc-gen-es-starpc/plugin.ts +24 -0
  4. package/cmd/protoc-gen-es-starpc/protoc-gen-es-starpc +2 -0
  5. package/cmd/protoc-gen-es-starpc/protoc-gen-es-starpc.ts +19 -0
  6. package/cmd/protoc-gen-es-starpc/typescript.ts +183 -0
  7. package/dist/cmd/protoc-gen-es-starpc/plugin.d.ts +1 -0
  8. package/dist/cmd/protoc-gen-es-starpc/plugin.js +22 -0
  9. package/dist/cmd/protoc-gen-es-starpc/protoc-gen-es-starpc.js +17 -0
  10. package/dist/cmd/protoc-gen-es-starpc/typescript.d.ts +2 -0
  11. package/dist/cmd/protoc-gen-es-starpc/typescript.js +167 -0
  12. package/dist/e2e/mock/mock_pb.d.ts +21 -0
  13. package/dist/e2e/mock/mock_pb.js +36 -0
  14. package/dist/e2e/mock/mock_srpc.pb.d.ts +52 -0
  15. package/dist/e2e/mock/mock_srpc.pb.js +44 -0
  16. package/dist/echo/EchoerClientImpl.d.ts +0 -0
  17. package/dist/echo/EchoerClientImpl.js +1 -0
  18. package/dist/echo/client-test.js +8 -6
  19. package/dist/echo/echo_pb.d.ts +21 -0
  20. package/dist/echo/echo_pb.js +36 -0
  21. package/dist/echo/echo_srpc.pb.d.ts +145 -0
  22. package/dist/echo/echo_srpc.pb.js +131 -0
  23. package/dist/echo/index.d.ts +2 -1
  24. package/dist/echo/index.js +2 -1
  25. package/dist/echo/server.d.ts +10 -7
  26. package/dist/echo/server.js +2 -3
  27. package/dist/package.json +100 -0
  28. package/dist/rpcstream/index.d.ts +1 -1
  29. package/dist/rpcstream/index.js +1 -1
  30. package/dist/rpcstream/rpcstream.d.ts +6 -4
  31. package/dist/rpcstream/rpcstream.js +17 -15
  32. package/dist/rpcstream/rpcstream_pb.d.ts +92 -0
  33. package/dist/rpcstream/rpcstream_pb.js +117 -0
  34. package/dist/srpc/client-rpc.d.ts +1 -1
  35. package/dist/srpc/client-rpc.js +2 -2
  36. package/dist/srpc/client.d.ts +2 -2
  37. package/dist/srpc/common-rpc.d.ts +7 -6
  38. package/dist/srpc/common-rpc.js +10 -10
  39. package/dist/srpc/definition.d.ts +12 -12
  40. package/dist/srpc/handler.d.ts +2 -2
  41. package/dist/srpc/handler.js +1 -1
  42. package/dist/srpc/index.d.ts +4 -3
  43. package/dist/srpc/index.js +2 -2
  44. package/dist/srpc/invoker.d.ts +4 -3
  45. package/dist/srpc/invoker.js +7 -4
  46. package/dist/srpc/message.d.ts +7 -11
  47. package/dist/srpc/message.js +8 -14
  48. package/dist/srpc/packet.d.ts +1 -1
  49. package/dist/srpc/packet.js +1 -1
  50. package/dist/srpc/{ts-proto-rpc.d.ts → proto-rpc.d.ts} +1 -1
  51. package/dist/srpc/proto-rpc.js +1 -0
  52. package/dist/srpc/pushable.d.ts +2 -0
  53. package/dist/srpc/pushable.js +5 -0
  54. package/dist/srpc/rpcproto_pb.d.ts +132 -0
  55. package/dist/srpc/rpcproto_pb.js +165 -0
  56. package/dist/srpc/server-rpc.d.ts +1 -1
  57. package/dist/srpc/stream.d.ts +1 -1
  58. package/e2e/mock/mock.pb.go +22 -1
  59. package/e2e/mock/mock_pb.ts +64 -0
  60. package/e2e/mock/mock_srpc.pb.ts +77 -0
  61. package/echo/EchoerClientImpl.ts +0 -0
  62. package/echo/client-test.ts +10 -6
  63. package/echo/echo.pb.go +22 -1
  64. package/echo/echo_pb.ts +64 -0
  65. package/echo/echo_srpc.pb.ts +244 -0
  66. package/echo/index.ts +4 -3
  67. package/echo/server.ts +15 -13
  68. package/go.mod +3 -3
  69. package/go.sum +6 -6
  70. package/integration/integration.bash +11 -3
  71. package/package.json +17 -14
  72. package/srpc/client-rpc.ts +5 -4
  73. package/srpc/client.ts +2 -2
  74. package/srpc/common-rpc.ts +20 -19
  75. package/srpc/definition.ts +35 -24
  76. package/srpc/handler.ts +7 -9
  77. package/srpc/index.ts +8 -3
  78. package/srpc/invoker.ts +34 -17
  79. package/srpc/message.ts +25 -39
  80. package/srpc/packet.ts +1 -1
  81. package/srpc/{ts-proto-rpc.ts → proto-rpc.ts} +3 -2
  82. package/srpc/pushable.ts +9 -1
  83. package/srpc/rpcproto.pb.go +102 -1
  84. package/srpc/rpcproto_pb.ts +265 -0
  85. package/srpc/server-rpc.ts +1 -1
  86. package/srpc/stream.ts +1 -1
  87. package/dist/e2e/mock/mock.pb.d.ts +0 -110
  88. package/dist/e2e/mock/mock.pb.js +0 -117
  89. package/dist/echo/echo.pb.d.ts +0 -494
  90. package/dist/echo/echo.pb.js +0 -178
  91. package/dist/rpcstream/rpcstream.pb.d.ts +0 -195
  92. package/dist/rpcstream/rpcstream.pb.js +0 -331
  93. package/dist/srpc/rpcproto.pb.d.ts +0 -291
  94. package/dist/srpc/rpcproto.pb.js +0 -448
  95. package/e2e/mock/mock.pb.ts +0 -200
  96. package/echo/echo.pb.ts +0 -335
  97. package/srpc/rpcproto.pb.ts +0 -608
  98. /package/dist/{srpc/ts-proto-rpc.js → cmd/protoc-gen-es-starpc/protoc-gen-es-starpc.d.ts} +0 -0
package/Makefile CHANGED
@@ -77,27 +77,25 @@ genproto: vendor node_modules $(GOIMPORTS) $(PROTOWRAP) $(PROTOC_GEN_GO) $(PROTO
77
77
  set -eo pipefail; \
78
78
  export PROJECT=$$(go list -m); \
79
79
  export PATH=$$(pwd)/hack/bin:$${PATH}; \
80
- mkdir -p $$(pwd)/vendor/$$(dirname $${PROJECT}); \
80
+ export OUT=$$(pwd)/vendor; \
81
+ mkdir -p $${OUT}/$$(dirname $${PROJECT}); \
81
82
  rm $$(pwd)/vendor/$${PROJECT} || true; \
82
83
  ln -s $$(pwd) $$(pwd)/vendor/$${PROJECT} ; \
83
84
  protogen() { \
84
85
  $(PROTOWRAP) \
85
- -I $$(pwd)/vendor \
86
- --plugin=./node_modules/.bin/protoc-gen-ts_proto \
87
- --go-lite_out=$$(pwd)/vendor \
86
+ -I $${OUT} \
87
+ --plugin=./node_modules/.bin/protoc-gen-es \
88
+ --plugin=./cmd/protoc-gen-es-starpc/protoc-gen-es-starpc \
89
+ --go-lite_out=$${OUT} \
88
90
  --go-lite_opt=features=marshal+unmarshal+size+equal+json+clone \
89
- --go-starpc_out=$$(pwd)/vendor \
90
- --ts_proto_out=$$(pwd)/vendor \
91
- --ts_proto_opt=esModuleInterop=true \
92
- --ts_proto_opt=fileSuffix=.pb \
93
- --ts_proto_opt=importSuffix=.js \
94
- --ts_proto_opt=forceLong=long \
95
- --ts_proto_opt=oneof=unions \
96
- --ts_proto_opt=outputServices=default,outputServices=generic-definitions \
97
- --ts_proto_opt=useAbortSignal=true \
98
- --ts_proto_opt=useAsyncIterable=true \
99
- --ts_proto_opt=useDate=true \
100
- --proto_path $$(pwd)/vendor \
91
+ --go-starpc_out=$${OUT} \
92
+ --es_out=$${OUT} \
93
+ --es_opt target=ts \
94
+ --es_opt ts_nocheck=false \
95
+ --es-starpc_out=$${OUT} \
96
+ --es-starpc_opt target=ts \
97
+ --es-starpc_opt ts_nocheck=false \
98
+ --proto_path $${OUT} \
101
99
  --print_structure \
102
100
  --only_specified_files \
103
101
  $$(\
package/README.md CHANGED
@@ -26,6 +26,10 @@ Supports **client-to-server and bidirectional streaming** in the web browser.
26
26
  The library leverages libp2p streams with `@chainsafe/libp2p-yamux` to
27
27
  coordinate balancing many ongoing RPCs over a single connection.
28
28
 
29
+ starpc uses [protobuf-go-lite] to generate reflection-free Go code.
30
+
31
+ [protobuf-go-lite]: https://github.com/aperturerobotics/protobuf-go-lite
32
+
29
33
  ## Usage
30
34
 
31
35
  Start with the [protobuf-project] template repository on the "starpc" branch.
@@ -121,12 +125,12 @@ This examples demonstrates connecting to a WebSocket server:
121
125
 
122
126
  ```typescript
123
127
  import { WebSocketConn } from 'srpc'
124
- import { EchoerClientImpl } from 'srpc/echo'
128
+ import { EchoerClient } from 'srpc/echo'
125
129
 
126
130
  const ws = new WebSocket('ws://localhost:5000/demo')
127
131
  const channel = new WebSocketConn(ws)
128
132
  const client = channel.buildClient()
129
- const demoServiceClient = new EchoerClientImpl(client)
133
+ const demoServiceClient = new EchoerClient(client)
130
134
 
131
135
  const result = await demoServiceClient.Echo({
132
136
  body: "Hello world!"
@@ -195,6 +199,12 @@ Uses [vtprotobuf] to generate Protobuf marshal / unmarshal code.
195
199
 
196
200
  [vtprotobuf]: https://github.com/planetscale/vtprotobuf
197
201
 
202
+ Uses [protobuf-es] to serialize Protobuf in TypeScript.
203
+
204
+ [protobuf-es]: https://github.com/bufbuild/protobuf-es
205
+
206
+ `protoc-gen-es-starpc` is a heavily modified version of `protoc-gen-connect-es`.
207
+
198
208
  ## Developing on MacOS
199
209
 
200
210
  On MacOS, some homebrew packages are required for `yarn gen`:
@@ -0,0 +1,24 @@
1
+ // Copyright 2024 Aperture Robotics, LLC.
2
+ // Copyright 2021-2024 The Connect Authors
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+
16
+ import { createEcmaScriptPlugin } from '@bufbuild/protoplugin'
17
+ import { generateTs } from './typescript.js'
18
+ import { version } from '../../package.json'
19
+
20
+ export const protocGenEsStarpc = createEcmaScriptPlugin({
21
+ name: 'protoc-gen-es-starpc',
22
+ version: `v${String(version)}`,
23
+ generateTs,
24
+ })
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bash
2
+ exec tsx "./cmd/protoc-gen-es-starpc/protoc-gen-es-starpc.ts" "$@"
@@ -0,0 +1,19 @@
1
+ // Copyright 2024 Aperture Robotics, LLC.
2
+ // Copyright 2021-2024 The Connect Authors
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+
16
+ import { runNodeJs } from '@bufbuild/protoplugin'
17
+ import { protocGenEsStarpc } from './plugin.js'
18
+
19
+ runNodeJs(protocGenEsStarpc)
@@ -0,0 +1,183 @@
1
+ // Copyright 2024 Aperture Robotics, LLC.
2
+ // Copyright 2021-2024 The Connect Authors
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+
16
+ import type { DescService } from '@bufbuild/protobuf'
17
+ import { MethodIdempotency, MethodKind } from '@bufbuild/protobuf'
18
+ import type { GeneratedFile, Schema } from '@bufbuild/protoplugin/ecmascript'
19
+ import { createImportSymbol, localName } from '@bufbuild/protoplugin/ecmascript'
20
+
21
+ export function generateTs(schema: Schema) {
22
+ for (const protoFile of schema.files) {
23
+ const file = schema.generateFile(protoFile.name + '_srpc.pb.ts')
24
+ file.preamble(protoFile)
25
+ for (const service of protoFile.services) {
26
+ generateService(schema, file, service)
27
+ }
28
+ }
29
+ }
30
+
31
+ // prettier-ignore
32
+ function generateService(
33
+ schema: Schema,
34
+ f: GeneratedFile,
35
+ service: DescService
36
+ ) {
37
+ const { MethodKind: rtMethodKind, MethodIdempotency: rtMethodIdempotency, PartialMessage } =
38
+ schema.runtime;
39
+ const MessageStream = createImportSymbol("MessageStream", "starpc")
40
+
41
+ // NOTE: This matches generateService from @connectrpc/protoc-gen-connect-es.
42
+ f.print(f.jsDoc(service));
43
+ f.print(f.exportDecl("const", localName(service)), "Definition = {");
44
+ f.print(` typeName: `, f.string(service.typeName), `,`);
45
+ f.print(" methods: {");
46
+ for (const method of service.methods) {
47
+ f.print(f.jsDoc(method, " "));
48
+ f.print(" ", method.name, ": {");
49
+ f.print(` name: `, f.string(method.name), `,`);
50
+ f.print(" I: ", method.input, ",");
51
+ f.print(" O: ", method.output, ",");
52
+ f.print(
53
+ " kind: ",
54
+ rtMethodKind,
55
+ ".",
56
+ MethodKind[method.methodKind],
57
+ ","
58
+ );
59
+ if (method.idempotency !== undefined) {
60
+ f.print(
61
+ " idempotency: ",
62
+ rtMethodIdempotency,
63
+ ".",
64
+ MethodIdempotency[method.idempotency],
65
+ ","
66
+ );
67
+ }
68
+ // In case we start supporting options, we have to surface them here
69
+ f.print(" },");
70
+ }
71
+ f.print(" }");
72
+ f.print("} as const;");
73
+ f.print();
74
+
75
+ // Generate the service interface
76
+ f.print(f.jsDoc(service));
77
+ f.print("export interface ", localName(service), " {");
78
+ for (const method of service.methods) {
79
+ f.print(f.jsDoc(method, " "));
80
+ f.print(" ", method.name, "(");
81
+ if (method.methodKind === MethodKind.Unary) {
82
+ f.print("request: ", PartialMessage, "<", method.input, ">, abortSignal?: AbortSignal");
83
+ } else if (method.methodKind === MethodKind.ServerStreaming) {
84
+ f.print("request: ", PartialMessage, "<", method.input, ">, abortSignal?: AbortSignal");
85
+ } else if (method.methodKind === MethodKind.ClientStreaming) {
86
+ f.print("request: ", MessageStream, "<", method.input, ">, abortSignal?: AbortSignal");
87
+ } else if (method.methodKind === MethodKind.BiDiStreaming) {
88
+ f.print("request: ", MessageStream, "<", method.input, ">, abortSignal?: AbortSignal");
89
+ }
90
+ f.print("): ");
91
+ if (method.methodKind === MethodKind.Unary) {
92
+ f.print("Promise<", PartialMessage, "<", method.output, ">>");
93
+ } else if (method.methodKind === MethodKind.ServerStreaming) {
94
+ f.print(MessageStream, "<", method.output, ">");
95
+ } else if (method.methodKind === MethodKind.ClientStreaming) {
96
+ f.print("Promise<", PartialMessage, "<", method.output, ">>");
97
+ } else if (method.methodKind === MethodKind.BiDiStreaming) {
98
+ f.print(MessageStream, "<", method.output, ">");
99
+ }
100
+ f.print();
101
+ }
102
+ f.print("}");
103
+ f.print();
104
+
105
+
106
+ // Generate the service name constant
107
+ f.print("export const ", localName(service), "ServiceName = ", localName(service), "Definition.typeName");
108
+ f.print();
109
+
110
+ // Generate the client implementation
111
+ f.print("export class ", localName(service), "Client implements ", localName(service), " {");
112
+ f.print(" private readonly rpc: ", createImportSymbol("ProtoRpc", "starpc"));
113
+ f.print(" private readonly service: string");
114
+ f.print(" constructor(rpc: ProtoRpc, opts?: { service?: string }) {");
115
+ f.print(" this.service = opts?.service || ", localName(service), "ServiceName");
116
+ f.print(" this.rpc = rpc");
117
+ for (const method of service.methods) {
118
+ f.print(" this.", method.name, " = this.", method.name, ".bind(this)");
119
+ }
120
+ f.print(" }");
121
+
122
+ const buildDecodeMessageTransformSymbol = createImportSymbol("buildDecodeMessageTransform", "starpc")
123
+ const buildEncodeMessageTransformSymbol = createImportSymbol("buildEncodeMessageTransform", "starpc")
124
+ for (const method of service.methods) {
125
+ f.print(f.jsDoc(method, " "));
126
+ f.print(" ", method.methodKind === MethodKind.Unary || method.methodKind === MethodKind.ClientStreaming ? "async " : "", method.name, "(");
127
+ if (method.methodKind === MethodKind.Unary) {
128
+ f.print("request: ", PartialMessage, "<", method.input, ">, abortSignal?: AbortSignal");
129
+ } else if (method.methodKind === MethodKind.ServerStreaming) {
130
+ f.print("request: ", PartialMessage, "<", method.input, ">, abortSignal?: AbortSignal");
131
+ } else if (method.methodKind === MethodKind.ClientStreaming) {
132
+ f.print("request: ", MessageStream, "<", method.input, ">, abortSignal?: AbortSignal");
133
+ } else if (method.methodKind === MethodKind.BiDiStreaming) {
134
+ f.print("request: ", MessageStream, "<", method.input, ">, abortSignal?: AbortSignal");
135
+ }
136
+ f.print("): ");
137
+ if (method.methodKind === MethodKind.Unary) {
138
+ f.print("Promise<", PartialMessage, "<", method.output, ">> {");
139
+ f.print(" const requestMsg = new ", method.input, "(request)");
140
+ f.print(" const result = await this.rpc.request(");
141
+ f.print(" this.service,");
142
+ f.print(" ", localName(service), "Definition.methods.", method.name, ".name,");
143
+ f.print(" requestMsg.toBinary(),");
144
+ f.print(" abortSignal || undefined,");
145
+ f.print(" )");
146
+ f.print(" return ", method.output, ".fromBinary(result)");
147
+ f.print(" }");
148
+ } else if (method.methodKind === MethodKind.ServerStreaming) {
149
+ f.print(MessageStream, "<", method.output, "> {");
150
+ f.print(" const requestMsg = new ", method.input, "(request)");
151
+ f.print(" const result = this.rpc.serverStreamingRequest(");
152
+ f.print(" this.service,");
153
+ f.print(" ", localName(service), "Definition.methods.", method.name, ".name,");
154
+ f.print(" requestMsg.toBinary(),");
155
+ f.print(" abortSignal || undefined,");
156
+ f.print(" )");
157
+ f.print(" return ", buildDecodeMessageTransformSymbol, "(", method.output, ")(result)");
158
+ f.print(" }");
159
+ } else if (method.methodKind === MethodKind.ClientStreaming) {
160
+ f.print("Promise<", PartialMessage, "<", method.output, ">> {");
161
+ f.print(" const result = await this.rpc.clientStreamingRequest(");
162
+ f.print(" this.service,");
163
+ f.print(" ", localName(service), "Definition.methods.", method.name, ".name,");
164
+ f.print(" ", buildEncodeMessageTransformSymbol, "(", method.input, ")(request),");
165
+ f.print(" abortSignal || undefined,");
166
+ f.print(" )");
167
+ f.print(" return ", method.output, ".fromBinary(result)");
168
+ f.print(" }");
169
+ } else if (method.methodKind === MethodKind.BiDiStreaming) {
170
+ f.print(MessageStream, "<", method.output, "> {");
171
+ f.print(" const result = this.rpc.bidirectionalStreamingRequest(");
172
+ f.print(" this.service,");
173
+ f.print(" ", localName(service), "Definition.methods.", method.name, ".name,");
174
+ f.print(" ", buildEncodeMessageTransformSymbol, "(", method.input, ")(request),");
175
+ f.print(" abortSignal || undefined,");
176
+ f.print(" )");
177
+ f.print(" return ", buildDecodeMessageTransformSymbol, "(", method.output, ")(result)");
178
+ f.print(" }");
179
+ }
180
+ f.print();
181
+ }
182
+ f.print("}");
183
+ }
@@ -0,0 +1 @@
1
+ export declare const protocGenEsStarpc: import("@bufbuild/protoplugin").Plugin;
@@ -0,0 +1,22 @@
1
+ // Copyright 2024 Aperture Robotics, LLC.
2
+ // Copyright 2021-2024 The Connect Authors
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+ import { createEcmaScriptPlugin } from '@bufbuild/protoplugin';
16
+ import { generateTs } from './typescript.js';
17
+ import { version } from '../../package.json';
18
+ export const protocGenEsStarpc = createEcmaScriptPlugin({
19
+ name: 'protoc-gen-es-starpc',
20
+ version: `v${String(version)}`,
21
+ generateTs,
22
+ });
@@ -0,0 +1,17 @@
1
+ // Copyright 2024 Aperture Robotics, LLC.
2
+ // Copyright 2021-2024 The Connect Authors
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+ import { runNodeJs } from '@bufbuild/protoplugin';
16
+ import { protocGenEsStarpc } from './plugin.js';
17
+ runNodeJs(protocGenEsStarpc);
@@ -0,0 +1,2 @@
1
+ import type { Schema } from '@bufbuild/protoplugin/ecmascript';
2
+ export declare function generateTs(schema: Schema): void;
@@ -0,0 +1,167 @@
1
+ // Copyright 2024 Aperture Robotics, LLC.
2
+ // Copyright 2021-2024 The Connect Authors
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+ import { MethodIdempotency, MethodKind } from '@bufbuild/protobuf';
16
+ import { createImportSymbol, localName } from '@bufbuild/protoplugin/ecmascript';
17
+ export function generateTs(schema) {
18
+ for (const protoFile of schema.files) {
19
+ const file = schema.generateFile(protoFile.name + '_srpc.pb.ts');
20
+ file.preamble(protoFile);
21
+ for (const service of protoFile.services) {
22
+ generateService(schema, file, service);
23
+ }
24
+ }
25
+ }
26
+ // prettier-ignore
27
+ function generateService(schema, f, service) {
28
+ const { MethodKind: rtMethodKind, MethodIdempotency: rtMethodIdempotency, PartialMessage } = schema.runtime;
29
+ const MessageStream = createImportSymbol("MessageStream", "starpc");
30
+ // NOTE: This matches generateService from @connectrpc/protoc-gen-connect-es.
31
+ f.print(f.jsDoc(service));
32
+ f.print(f.exportDecl("const", localName(service)), "Definition = {");
33
+ f.print(` typeName: `, f.string(service.typeName), `,`);
34
+ f.print(" methods: {");
35
+ for (const method of service.methods) {
36
+ f.print(f.jsDoc(method, " "));
37
+ f.print(" ", method.name, ": {");
38
+ f.print(` name: `, f.string(method.name), `,`);
39
+ f.print(" I: ", method.input, ",");
40
+ f.print(" O: ", method.output, ",");
41
+ f.print(" kind: ", rtMethodKind, ".", MethodKind[method.methodKind], ",");
42
+ if (method.idempotency !== undefined) {
43
+ f.print(" idempotency: ", rtMethodIdempotency, ".", MethodIdempotency[method.idempotency], ",");
44
+ }
45
+ // In case we start supporting options, we have to surface them here
46
+ f.print(" },");
47
+ }
48
+ f.print(" }");
49
+ f.print("} as const;");
50
+ f.print();
51
+ // Generate the service interface
52
+ f.print(f.jsDoc(service));
53
+ f.print("export interface ", localName(service), " {");
54
+ for (const method of service.methods) {
55
+ f.print(f.jsDoc(method, " "));
56
+ f.print(" ", method.name, "(");
57
+ if (method.methodKind === MethodKind.Unary) {
58
+ f.print("request: ", PartialMessage, "<", method.input, ">, abortSignal?: AbortSignal");
59
+ }
60
+ else if (method.methodKind === MethodKind.ServerStreaming) {
61
+ f.print("request: ", PartialMessage, "<", method.input, ">, abortSignal?: AbortSignal");
62
+ }
63
+ else if (method.methodKind === MethodKind.ClientStreaming) {
64
+ f.print("request: ", MessageStream, "<", method.input, ">, abortSignal?: AbortSignal");
65
+ }
66
+ else if (method.methodKind === MethodKind.BiDiStreaming) {
67
+ f.print("request: ", MessageStream, "<", method.input, ">, abortSignal?: AbortSignal");
68
+ }
69
+ f.print("): ");
70
+ if (method.methodKind === MethodKind.Unary) {
71
+ f.print("Promise<", PartialMessage, "<", method.output, ">>");
72
+ }
73
+ else if (method.methodKind === MethodKind.ServerStreaming) {
74
+ f.print(MessageStream, "<", method.output, ">");
75
+ }
76
+ else if (method.methodKind === MethodKind.ClientStreaming) {
77
+ f.print("Promise<", PartialMessage, "<", method.output, ">>");
78
+ }
79
+ else if (method.methodKind === MethodKind.BiDiStreaming) {
80
+ f.print(MessageStream, "<", method.output, ">");
81
+ }
82
+ f.print();
83
+ }
84
+ f.print("}");
85
+ f.print();
86
+ // Generate the service name constant
87
+ f.print("export const ", localName(service), "ServiceName = ", localName(service), "Definition.typeName");
88
+ f.print();
89
+ // Generate the client implementation
90
+ f.print("export class ", localName(service), "Client implements ", localName(service), " {");
91
+ f.print(" private readonly rpc: ", createImportSymbol("ProtoRpc", "starpc"));
92
+ f.print(" private readonly service: string");
93
+ f.print(" constructor(rpc: ProtoRpc, opts?: { service?: string }) {");
94
+ f.print(" this.service = opts?.service || ", localName(service), "ServiceName");
95
+ f.print(" this.rpc = rpc");
96
+ for (const method of service.methods) {
97
+ f.print(" this.", method.name, " = this.", method.name, ".bind(this)");
98
+ }
99
+ f.print(" }");
100
+ const buildDecodeMessageTransformSymbol = createImportSymbol("buildDecodeMessageTransform", "starpc");
101
+ const buildEncodeMessageTransformSymbol = createImportSymbol("buildEncodeMessageTransform", "starpc");
102
+ for (const method of service.methods) {
103
+ f.print(f.jsDoc(method, " "));
104
+ f.print(" ", method.methodKind === MethodKind.Unary || method.methodKind === MethodKind.ClientStreaming ? "async " : "", method.name, "(");
105
+ if (method.methodKind === MethodKind.Unary) {
106
+ f.print("request: ", PartialMessage, "<", method.input, ">, abortSignal?: AbortSignal");
107
+ }
108
+ else if (method.methodKind === MethodKind.ServerStreaming) {
109
+ f.print("request: ", PartialMessage, "<", method.input, ">, abortSignal?: AbortSignal");
110
+ }
111
+ else if (method.methodKind === MethodKind.ClientStreaming) {
112
+ f.print("request: ", MessageStream, "<", method.input, ">, abortSignal?: AbortSignal");
113
+ }
114
+ else if (method.methodKind === MethodKind.BiDiStreaming) {
115
+ f.print("request: ", MessageStream, "<", method.input, ">, abortSignal?: AbortSignal");
116
+ }
117
+ f.print("): ");
118
+ if (method.methodKind === MethodKind.Unary) {
119
+ f.print("Promise<", PartialMessage, "<", method.output, ">> {");
120
+ f.print(" const requestMsg = new ", method.input, "(request)");
121
+ f.print(" const result = await this.rpc.request(");
122
+ f.print(" this.service,");
123
+ f.print(" ", localName(service), "Definition.methods.", method.name, ".name,");
124
+ f.print(" requestMsg.toBinary(),");
125
+ f.print(" abortSignal || undefined,");
126
+ f.print(" )");
127
+ f.print(" return ", method.output, ".fromBinary(result)");
128
+ f.print(" }");
129
+ }
130
+ else if (method.methodKind === MethodKind.ServerStreaming) {
131
+ f.print(MessageStream, "<", method.output, "> {");
132
+ f.print(" const requestMsg = new ", method.input, "(request)");
133
+ f.print(" const result = this.rpc.serverStreamingRequest(");
134
+ f.print(" this.service,");
135
+ f.print(" ", localName(service), "Definition.methods.", method.name, ".name,");
136
+ f.print(" requestMsg.toBinary(),");
137
+ f.print(" abortSignal || undefined,");
138
+ f.print(" )");
139
+ f.print(" return ", buildDecodeMessageTransformSymbol, "(", method.output, ")(result)");
140
+ f.print(" }");
141
+ }
142
+ else if (method.methodKind === MethodKind.ClientStreaming) {
143
+ f.print("Promise<", PartialMessage, "<", method.output, ">> {");
144
+ f.print(" const result = await this.rpc.clientStreamingRequest(");
145
+ f.print(" this.service,");
146
+ f.print(" ", localName(service), "Definition.methods.", method.name, ".name,");
147
+ f.print(" ", buildEncodeMessageTransformSymbol, "(", method.input, ")(request),");
148
+ f.print(" abortSignal || undefined,");
149
+ f.print(" )");
150
+ f.print(" return ", method.output, ".fromBinary(result)");
151
+ f.print(" }");
152
+ }
153
+ else if (method.methodKind === MethodKind.BiDiStreaming) {
154
+ f.print(MessageStream, "<", method.output, "> {");
155
+ f.print(" const result = this.rpc.bidirectionalStreamingRequest(");
156
+ f.print(" this.service,");
157
+ f.print(" ", localName(service), "Definition.methods.", method.name, ".name,");
158
+ f.print(" ", buildEncodeMessageTransformSymbol, "(", method.input, ")(request),");
159
+ f.print(" abortSignal || undefined,");
160
+ f.print(" )");
161
+ f.print(" return ", buildDecodeMessageTransformSymbol, "(", method.output, ")(result)");
162
+ f.print(" }");
163
+ }
164
+ f.print();
165
+ }
166
+ f.print("}");
167
+ }
@@ -0,0 +1,21 @@
1
+ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from '@bufbuild/protobuf';
2
+ import { Message, proto3 } from '@bufbuild/protobuf';
3
+ /**
4
+ * MockMsg is the mock message body.
5
+ *
6
+ * @generated from message e2e.mock.MockMsg
7
+ */
8
+ export declare class MockMsg extends Message<MockMsg> {
9
+ /**
10
+ * @generated from field: string body = 1;
11
+ */
12
+ body: string;
13
+ constructor(data?: PartialMessage<MockMsg>);
14
+ static readonly runtime: typeof proto3;
15
+ static readonly typeName = "e2e.mock.MockMsg";
16
+ static readonly fields: FieldList;
17
+ static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): MockMsg;
18
+ static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): MockMsg;
19
+ static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): MockMsg;
20
+ static equals(a: MockMsg | PlainMessage<MockMsg> | undefined, b: MockMsg | PlainMessage<MockMsg> | undefined): boolean;
21
+ }
@@ -0,0 +1,36 @@
1
+ // @generated by protoc-gen-es v1.8.0 with parameter "target=ts,ts_nocheck=false"
2
+ // @generated from file github.com/aperturerobotics/starpc/e2e/mock/mock.proto (package e2e.mock, syntax proto3)
3
+ /* eslint-disable */
4
+ import { Message, proto3 } from '@bufbuild/protobuf';
5
+ /**
6
+ * MockMsg is the mock message body.
7
+ *
8
+ * @generated from message e2e.mock.MockMsg
9
+ */
10
+ export class MockMsg extends Message {
11
+ constructor(data) {
12
+ super();
13
+ /**
14
+ * @generated from field: string body = 1;
15
+ */
16
+ this.body = '';
17
+ proto3.util.initPartial(data, this);
18
+ }
19
+ static fromBinary(bytes, options) {
20
+ return new MockMsg().fromBinary(bytes, options);
21
+ }
22
+ static fromJson(jsonValue, options) {
23
+ return new MockMsg().fromJson(jsonValue, options);
24
+ }
25
+ static fromJsonString(jsonString, options) {
26
+ return new MockMsg().fromJsonString(jsonString, options);
27
+ }
28
+ static equals(a, b) {
29
+ return proto3.util.equals(MockMsg, a, b);
30
+ }
31
+ }
32
+ MockMsg.runtime = proto3;
33
+ MockMsg.typeName = 'e2e.mock.MockMsg';
34
+ MockMsg.fields = proto3.util.newFieldList(() => [
35
+ { no: 1, name: 'body', kind: 'scalar', T: 9 /* ScalarType.STRING */ },
36
+ ]);
@@ -0,0 +1,52 @@
1
+ import { MockMsg } from './mock_pb.js';
2
+ import type { PartialMessage } from '@bufbuild/protobuf';
3
+ import { MethodKind } from '@bufbuild/protobuf';
4
+ import { ProtoRpc } from 'starpc';
5
+ /**
6
+ * Mock service mocks some RPCs for the e2e tests.
7
+ *
8
+ * @generated from service e2e.mock.Mock
9
+ */
10
+ export declare const MockDefinition: {
11
+ readonly typeName: "e2e.mock.Mock";
12
+ readonly methods: {
13
+ /**
14
+ * MockRequest runs a mock unary request.
15
+ *
16
+ * @generated from rpc e2e.mock.Mock.MockRequest
17
+ */
18
+ readonly MockRequest: {
19
+ readonly name: "MockRequest";
20
+ readonly I: typeof MockMsg;
21
+ readonly O: typeof MockMsg;
22
+ readonly kind: MethodKind.Unary;
23
+ };
24
+ };
25
+ };
26
+ /**
27
+ * Mock service mocks some RPCs for the e2e tests.
28
+ *
29
+ * @generated from service e2e.mock.Mock
30
+ */
31
+ export interface Mock {
32
+ /**
33
+ * MockRequest runs a mock unary request.
34
+ *
35
+ * @generated from rpc e2e.mock.Mock.MockRequest
36
+ */
37
+ MockRequest(request: PartialMessage<MockMsg>, abortSignal?: AbortSignal): Promise<PartialMessage<MockMsg>>;
38
+ }
39
+ export declare const MockServiceName: "e2e.mock.Mock";
40
+ export declare class MockClient implements Mock {
41
+ private readonly rpc;
42
+ private readonly service;
43
+ constructor(rpc: ProtoRpc, opts?: {
44
+ service?: string;
45
+ });
46
+ /**
47
+ * MockRequest runs a mock unary request.
48
+ *
49
+ * @generated from rpc e2e.mock.Mock.MockRequest
50
+ */
51
+ MockRequest(request: PartialMessage<MockMsg>, abortSignal?: AbortSignal): Promise<PartialMessage<MockMsg>>;
52
+ }