ts-proto 1.117.1 → 1.120.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.
- package/CHANGELOG.md +21 -0
- package/README.markdown +22 -0
- package/build/generate-grpc-web.js +82 -17
- package/build/generate-services.js +8 -1
- package/build/main.js +2 -8
- package/build/types.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
# [1.120.0](https://github.com/stephenh/ts-proto/compare/v1.119.0...v1.120.0) (2022-07-21)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add meta-data to stream error ([#620](https://github.com/stephenh/ts-proto/issues/620)) ([b68f301](https://github.com/stephenh/ts-proto/commit/b68f301b5fb4222a3fce676a7e5036cf00e77e11))
|
|
7
|
+
|
|
8
|
+
# [1.119.0](https://github.com/stephenh/ts-proto/compare/v1.118.0...v1.119.0) (2022-07-21)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* support grpc-web client straming ([#617](https://github.com/stephenh/ts-proto/issues/617)) ([d3e7f1f](https://github.com/stephenh/ts-proto/commit/d3e7f1f4aac12db87c54d6357e557a351c96a2ca))
|
|
14
|
+
|
|
15
|
+
# [1.118.0](https://github.com/stephenh/ts-proto/compare/v1.117.1...v1.118.0) (2022-07-19)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* add dynamic upStreamCodes option ([#618](https://github.com/stephenh/ts-proto/issues/618)) ([3091023](https://github.com/stephenh/ts-proto/commit/309102313273e09aef3a8480f4b46360ad82adaa))
|
|
21
|
+
|
|
1
22
|
## [1.117.1](https://github.com/stephenh/ts-proto/compare/v1.117.0...v1.117.1) (2022-07-16)
|
|
2
23
|
|
|
3
24
|
|
package/README.markdown
CHANGED
|
@@ -99,6 +99,13 @@ plugins:
|
|
|
99
99
|
path: ../node_modules/ts-proto/protoc-gen-ts_proto
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
+
To prevent `buf push` from reading irrelevent `.proto` files, configure `buf.yaml` like so:
|
|
103
|
+
|
|
104
|
+
```yaml
|
|
105
|
+
build:
|
|
106
|
+
excludes: [node_modules]
|
|
107
|
+
```
|
|
108
|
+
|
|
102
109
|
You can also use the official plugin published to the Buf Registry.
|
|
103
110
|
|
|
104
111
|
```yaml
|
|
@@ -110,6 +117,8 @@ plugins:
|
|
|
110
117
|
|
|
111
118
|
# Goals
|
|
112
119
|
|
|
120
|
+
In terms of the code that `ts-proto` generates, the general goals are:
|
|
121
|
+
|
|
113
122
|
- Idiomatic TypeScript/ES6 types
|
|
114
123
|
- `ts-proto` is a clean break from either the built-in Google/Java-esque JS code of `protoc` or the "make `.d.ts` files the `*.js` comments" approach of `protobufjs`
|
|
115
124
|
- (Techically the `protobufjs/minimal` package is used for actually reading/writing bytes.)
|
|
@@ -118,6 +127,17 @@ plugins:
|
|
|
118
127
|
- As much as possible, types are just interfaces, so you can work with messages just like regular hashes/data structures.
|
|
119
128
|
- Only supports codegen `*.proto`-to-`*.ts` workflow, currently no runtime reflection/loading of dynamic `.proto` files
|
|
120
129
|
|
|
130
|
+
## Non-Goals
|
|
131
|
+
|
|
132
|
+
Note that ts-proto is not an out-of-the-box RPC framework; instead it's more of a swiss-army knife (as witnessed by its many config options), that lets you build _exactly_ the RPC framework you'd like on top of it (i.e. that best integrates with your company's protobuf ecosystem; for better or worse, protobuf RPC is still a somewhat fragmented ecosystem).
|
|
133
|
+
|
|
134
|
+
If you'd like an out-of-the-box RPC framework built on top of ts-proto, there are a few examples:
|
|
135
|
+
|
|
136
|
+
- [nice-grpc](https://github.com/deeplay-io/nice-grpc)
|
|
137
|
+
- [starpc](https://github.com/aperturerobotics/starpc)
|
|
138
|
+
|
|
139
|
+
(Note for potential contributors, if you develop other frameworks/mini-frameworks, or even blog posts/tutorials, on using `ts-proto`, we're happy to link to them.)
|
|
140
|
+
|
|
121
141
|
# Example Types
|
|
122
142
|
|
|
123
143
|
The generated types are "just data", i.e.:
|
|
@@ -483,6 +503,8 @@ If you need ts-proto customizations or priority support for your company, you ca
|
|
|
483
503
|
|
|
484
504
|
# Development
|
|
485
505
|
|
|
506
|
+
This section describes how to contribute directly to ts-proto, i.e. it's not required for running `ts-proto` in `protoc` or using the generated TypeScript.
|
|
507
|
+
|
|
486
508
|
**Requirements**
|
|
487
509
|
|
|
488
510
|
- [Docker](https://www.docker.com) or [protoc](https://github.com/protocolbuffers/protobuf/releases) v3.19.1
|
|
@@ -18,7 +18,7 @@ function generateGrpcClientImpl(ctx, _fileDesc, serviceDesc) {
|
|
|
18
18
|
// Create the constructor(rpc: Rpc)
|
|
19
19
|
chunks.push((0, ts_poet_1.code) `
|
|
20
20
|
private readonly rpc: Rpc;
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
constructor(rpc: Rpc) {
|
|
23
23
|
`);
|
|
24
24
|
chunks.push((0, ts_poet_1.code) `this.rpc = rpc;`);
|
|
@@ -46,9 +46,11 @@ function generateRpcMethod(ctx, serviceDesc, methodDesc) {
|
|
|
46
46
|
return (0, ts_poet_1.code) `
|
|
47
47
|
${methodDesc.formattedName}(
|
|
48
48
|
request: ${inputType},
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
options?: {
|
|
50
|
+
metadata?: grpc.Metadata,
|
|
51
|
+
rpcOptions?: grpc.RpcOptions,
|
|
52
|
+
}): ${returns} {
|
|
53
|
+
return this.rpc.stream(${methodDescName(serviceDesc, methodDesc)}, request, options?.metadata, options?.rpcOptions)
|
|
52
54
|
}
|
|
53
55
|
`;
|
|
54
56
|
}
|
|
@@ -83,7 +85,7 @@ exports.generateGrpcServiceDesc = generateGrpcServiceDesc;
|
|
|
83
85
|
* implementation.
|
|
84
86
|
*/
|
|
85
87
|
function generateGrpcMethodDesc(ctx, serviceDesc, methodDesc) {
|
|
86
|
-
const inputType = (0, types_1.
|
|
88
|
+
const inputType = (0, types_1.rawRequestType)(ctx, methodDesc);
|
|
87
89
|
const outputType = (0, types_1.responseType)(ctx, methodDesc);
|
|
88
90
|
// grpc-web expects this to be a class, but the ts-proto messages are just interfaces.
|
|
89
91
|
//
|
|
@@ -109,13 +111,15 @@ function generateGrpcMethodDesc(ctx, serviceDesc, methodDesc) {
|
|
|
109
111
|
deserializeBinary(data: Uint8Array) {
|
|
110
112
|
return { ...${outputType}.decode(data), toObject() { return this; } };
|
|
111
113
|
}
|
|
112
|
-
|
|
114
|
+
}`;
|
|
115
|
+
const streaming = methodDesc.clientStreaming || methodDesc.serverStreaming;
|
|
116
|
+
const methodDef = streaming ? 'MethodDefinitionish' : 'UnaryMethodDefinitionish';
|
|
113
117
|
return (0, ts_poet_1.code) `
|
|
114
|
-
export const ${methodDescName(serviceDesc, methodDesc)}:
|
|
118
|
+
export const ${methodDescName(serviceDesc, methodDesc)}: ${methodDef} = {
|
|
115
119
|
methodName: "${methodDesc.name}",
|
|
116
120
|
service: ${serviceDesc.name}Desc,
|
|
117
|
-
requestStream: false,
|
|
118
|
-
responseStream: ${methodDesc.serverStreaming ?
|
|
121
|
+
requestStream: ${methodDesc.clientStreaming ? true : false},
|
|
122
|
+
responseStream: ${methodDesc.serverStreaming ? true : false},
|
|
119
123
|
requestType: ${requestFn} as any,
|
|
120
124
|
responseType: ${responseFn} as any,
|
|
121
125
|
};
|
|
@@ -133,6 +137,10 @@ function addGrpcWebMisc(ctx, hasStreamingMethods) {
|
|
|
133
137
|
interface UnaryMethodDefinitionishR extends ${grpc}.UnaryMethodDefinition<any, any> { requestStream: any; responseStream: any; }
|
|
134
138
|
`);
|
|
135
139
|
chunks.push((0, ts_poet_1.code) `type UnaryMethodDefinitionish = UnaryMethodDefinitionishR;`);
|
|
140
|
+
chunks.push((0, ts_poet_1.code) `
|
|
141
|
+
interface MethodDefinitionishR extends ${grpc}.MethodDefinition<any, any> { requestStream: any; responseStream: any; }
|
|
142
|
+
`);
|
|
143
|
+
chunks.push((0, ts_poet_1.code) `type MethodDefinitionish = MethodDefinitionishR;`);
|
|
136
144
|
chunks.push(generateGrpcWebRpcType(ctx, options.returnObservable, hasStreamingMethods));
|
|
137
145
|
chunks.push(generateGrpcWebImpl(ctx, options.returnObservable, hasStreamingMethods));
|
|
138
146
|
return (0, ts_poet_1.joinCode)(chunks, { on: '\n\n' });
|
|
@@ -156,8 +164,14 @@ function generateGrpcWebRpcType(ctx, returnObservable, hasStreamingMethods) {
|
|
|
156
164
|
methodDesc: T,
|
|
157
165
|
request: any,
|
|
158
166
|
metadata: grpc.Metadata | undefined,
|
|
159
|
-
): ${(0, types_1.observableType)(ctx)}<any
|
|
160
|
-
|
|
167
|
+
): ${(0, types_1.observableType)(ctx)}<any>;`);
|
|
168
|
+
chunks.push((0, ts_poet_1.code) `
|
|
169
|
+
stream<T extends MethodDefinitionish>(
|
|
170
|
+
methodDesc: T,
|
|
171
|
+
request: ${(0, types_1.observableType)(ctx)}<any>,
|
|
172
|
+
metadata: grpc.Metadata | undefined,
|
|
173
|
+
rpcOptions: grpc.RpcOptions | undefined
|
|
174
|
+
): ${(0, types_1.observableType)(ctx)}<any>;`);
|
|
161
175
|
}
|
|
162
176
|
chunks.push((0, ts_poet_1.code) `}`);
|
|
163
177
|
return (0, ts_poet_1.joinCode)(chunks, { on: '\n' });
|
|
@@ -170,6 +184,7 @@ function generateGrpcWebImpl(ctx, returnObservable, hasStreamingMethods) {
|
|
|
170
184
|
${hasStreamingMethods ? 'streamingTransport?: grpc.TransportFactory,' : ``}
|
|
171
185
|
debug?: boolean,
|
|
172
186
|
metadata?: grpc.Metadata,
|
|
187
|
+
upStreamRetryCodes?: number[],
|
|
173
188
|
}
|
|
174
189
|
`;
|
|
175
190
|
const chunks = [];
|
|
@@ -177,7 +192,7 @@ function generateGrpcWebImpl(ctx, returnObservable, hasStreamingMethods) {
|
|
|
177
192
|
export class GrpcWebImpl {
|
|
178
193
|
private host: string;
|
|
179
194
|
private options: ${options};
|
|
180
|
-
|
|
195
|
+
|
|
181
196
|
constructor(host: string, options: ${options}) {
|
|
182
197
|
this.host = host;
|
|
183
198
|
this.options = options;
|
|
@@ -191,6 +206,7 @@ function generateGrpcWebImpl(ctx, returnObservable, hasStreamingMethods) {
|
|
|
191
206
|
}
|
|
192
207
|
if (hasStreamingMethods) {
|
|
193
208
|
chunks.push(createInvokeMethod(ctx));
|
|
209
|
+
chunks.push(createStreamMethod(ctx));
|
|
194
210
|
}
|
|
195
211
|
chunks.push((0, ts_poet_1.code) `}`);
|
|
196
212
|
return (0, ts_poet_1.joinCode)(chunks, { trim: false });
|
|
@@ -258,7 +274,7 @@ function createObservableUnaryMethod(ctx) {
|
|
|
258
274
|
},
|
|
259
275
|
});
|
|
260
276
|
}).pipe(${take}(1));
|
|
261
|
-
}
|
|
277
|
+
}
|
|
262
278
|
`;
|
|
263
279
|
}
|
|
264
280
|
function createInvokeMethod(ctx) {
|
|
@@ -268,8 +284,7 @@ function createInvokeMethod(ctx) {
|
|
|
268
284
|
_request: any,
|
|
269
285
|
metadata: grpc.Metadata | undefined
|
|
270
286
|
): ${(0, types_1.observableType)(ctx)}<any> {
|
|
271
|
-
|
|
272
|
-
const upStreamCodes = [2, 4, 8, 9, 10, 13, 14, 15];
|
|
287
|
+
const upStreamCodes = this.options.upStreamRetryCodes || [];
|
|
273
288
|
const DEFAULT_TIMEOUT_TIME: number = 3_000;
|
|
274
289
|
const request = { ..._request, ...methodDesc.requestType };
|
|
275
290
|
const maybeCombinedMetadata =
|
|
@@ -285,13 +300,16 @@ function createInvokeMethod(ctx) {
|
|
|
285
300
|
metadata: maybeCombinedMetadata,
|
|
286
301
|
debug: this.options.debug,
|
|
287
302
|
onMessage: (next) => observer.next(next),
|
|
288
|
-
onEnd: (code: ${grpc}.Code, message: string) => {
|
|
303
|
+
onEnd: (code: ${grpc}.Code, message: string, trailers: ${grpc}.Metadata) => {
|
|
289
304
|
if (code === 0) {
|
|
290
305
|
observer.complete();
|
|
291
306
|
} else if (upStreamCodes.includes(code)) {
|
|
292
307
|
setTimeout(upStream, DEFAULT_TIMEOUT_TIME);
|
|
293
308
|
} else {
|
|
294
|
-
|
|
309
|
+
const err = new Error(message) as any;
|
|
310
|
+
err.code = code;
|
|
311
|
+
err.metadata = trailers;
|
|
312
|
+
observer.error(err);
|
|
295
313
|
}
|
|
296
314
|
},
|
|
297
315
|
});
|
|
@@ -302,3 +320,50 @@ function createInvokeMethod(ctx) {
|
|
|
302
320
|
}
|
|
303
321
|
`;
|
|
304
322
|
}
|
|
323
|
+
function createStreamMethod(ctx) {
|
|
324
|
+
return (0, ts_poet_1.code) `
|
|
325
|
+
stream<T extends MethodDefinitionish>(
|
|
326
|
+
methodDesc: T,
|
|
327
|
+
_request: ${(0, types_1.observableType)(ctx)}<any>,
|
|
328
|
+
metadata: ${grpc}.Metadata | undefined,
|
|
329
|
+
rpcOptions: ${grpc}.RpcOptions | undefined
|
|
330
|
+
): ${(0, types_1.observableType)(ctx)}<any> {
|
|
331
|
+
const defaultOptions = {
|
|
332
|
+
host: this.host,
|
|
333
|
+
debug: rpcOptions?.debug || this.options.debug,
|
|
334
|
+
transport: rpcOptions?.transport || this.options.streamingTransport || this.options.transport,
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
let started = false;
|
|
338
|
+
const client = ${grpc}.client(methodDesc, defaultOptions);
|
|
339
|
+
|
|
340
|
+
const subscription = _request.subscribe((_req: any) => {
|
|
341
|
+
const request = { ..._req, ...methodDesc.requestType };
|
|
342
|
+
if (!started) {
|
|
343
|
+
client.start(metadata);
|
|
344
|
+
started = true;
|
|
345
|
+
}
|
|
346
|
+
client.send(request);
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
subscription.add(() => {
|
|
350
|
+
client.finishSend();
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
return new Observable((observer) => {
|
|
354
|
+
client.onEnd((code: ${grpc}.Code, message: string, trailers: ${grpc}.Metadata) => {
|
|
355
|
+
subscription.unsubscribe();
|
|
356
|
+
if (code === 0) {
|
|
357
|
+
observer.complete();
|
|
358
|
+
} else {
|
|
359
|
+
observer.error(new Error(\`Error \${code} \${message}\`));
|
|
360
|
+
}
|
|
361
|
+
})
|
|
362
|
+
client.onMessage((res: any) => {
|
|
363
|
+
observer.next(res);
|
|
364
|
+
})
|
|
365
|
+
observer.add(() => client.close())
|
|
366
|
+
}).pipe(share());
|
|
367
|
+
}
|
|
368
|
+
`;
|
|
369
|
+
}
|
|
@@ -42,7 +42,14 @@ function generateService(ctx, fileDesc, sourceInfo, serviceDesc) {
|
|
|
42
42
|
// Use metadata as last argument for interface only configuration
|
|
43
43
|
if (options.outputClientImpl === 'grpc-web') {
|
|
44
44
|
// We have to use grpc.Metadata where grpc will come from @improbable-eng
|
|
45
|
-
|
|
45
|
+
if (methodDesc.clientStreaming) {
|
|
46
|
+
params.push((0, ts_poet_1.code) `options?: {
|
|
47
|
+
metadata?: grpc.Metadata,
|
|
48
|
+
rpcOptions?: grpc.RpcOptions}`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
params.push((0, ts_poet_1.code) `metadata?: grpc.Metadata`);
|
|
52
|
+
}
|
|
46
53
|
}
|
|
47
54
|
else if (options.addGrpcMetadata) {
|
|
48
55
|
const Metadata = (0, ts_poet_1.imp)('Metadata@@grpc/grpc-js');
|
package/build/main.js
CHANGED
|
@@ -115,7 +115,6 @@ function generateFile(ctx, fileDesc) {
|
|
|
115
115
|
}
|
|
116
116
|
}, options);
|
|
117
117
|
}
|
|
118
|
-
let hasServerStreamingMethods = false;
|
|
119
118
|
let hasStreamingMethods = false;
|
|
120
119
|
(0, visit_1.visitServices)(fileDesc, sourceInfo, (serviceDesc, sInfo) => {
|
|
121
120
|
if (options.nestJs) {
|
|
@@ -155,12 +154,7 @@ function generateFile(ctx, fileDesc) {
|
|
|
155
154
|
chunks.push((0, generate_grpc_web_1.generateGrpcClientImpl)(ctx, fileDesc, serviceDesc));
|
|
156
155
|
chunks.push((0, generate_grpc_web_1.generateGrpcServiceDesc)(fileDesc, serviceDesc));
|
|
157
156
|
serviceDesc.method.forEach((method) => {
|
|
158
|
-
|
|
159
|
-
chunks.push((0, generate_grpc_web_1.generateGrpcMethodDesc)(ctx, serviceDesc, method));
|
|
160
|
-
}
|
|
161
|
-
if (method.serverStreaming) {
|
|
162
|
-
hasServerStreamingMethods = true;
|
|
163
|
-
}
|
|
157
|
+
chunks.push((0, generate_grpc_web_1.generateGrpcMethodDesc)(ctx, serviceDesc, method));
|
|
164
158
|
});
|
|
165
159
|
}
|
|
166
160
|
}
|
|
@@ -179,7 +173,7 @@ function generateFile(ctx, fileDesc) {
|
|
|
179
173
|
chunks.push((0, generate_services_1.generateRpcType)(ctx, hasStreamingMethods));
|
|
180
174
|
}
|
|
181
175
|
else if (options.outputClientImpl === 'grpc-web') {
|
|
182
|
-
chunks.push((0, generate_grpc_web_1.addGrpcWebMisc)(ctx,
|
|
176
|
+
chunks.push((0, generate_grpc_web_1.addGrpcWebMisc)(ctx, hasStreamingMethods));
|
|
183
177
|
}
|
|
184
178
|
}
|
|
185
179
|
if (options.context) {
|
package/build/types.js
CHANGED
|
@@ -641,7 +641,7 @@ function responseObservable(ctx, methodDesc) {
|
|
|
641
641
|
exports.responseObservable = responseObservable;
|
|
642
642
|
function responsePromiseOrObservable(ctx, methodDesc) {
|
|
643
643
|
const { options } = ctx;
|
|
644
|
-
if (options.returnObservable || methodDesc.serverStreaming) {
|
|
644
|
+
if (options.returnObservable || methodDesc.serverStreaming || methodDesc.clientStreaming) {
|
|
645
645
|
return responseObservable(ctx, methodDesc);
|
|
646
646
|
}
|
|
647
647
|
return responsePromise(ctx, methodDesc);
|