ts-proto 2.19.0 → 2.21.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/README.markdown CHANGED
@@ -481,10 +481,18 @@ With `--ts_proto_opt=protoJsonFormat=false`, strict proto3 compliance is disable
481
481
 
482
482
  - With `--ts_proto_opt=nestJs=true`, the defaults will change to generate [NestJS protobuf](https://docs.nestjs.com/microservices/grpc) friendly types & service interfaces that can be used in both the client-side and server-side of NestJS protobuf implementations. See the [nestjs readme](NESTJS.markdown) for more information and implementation examples.
483
483
 
484
- Specifically `outputEncodeMethods`, `outputJsonMethods`, and `outputClientImpl` will all be false, `lowerCaseServiceMethods` will be true and `outputServices` will be ignored.
484
+ Specifically `outputEncodeMethods`, `outputJsonMethods`, `outputPartialMethods`, and `outputClientImpl` will all be false, `lowerCaseServiceMethods` will be true and `outputServices` will be ignored.
485
+
486
+ If `outputPartialMethods=true` is set as well, the `FooServiceClient` methods will accept `DeepPartial` requests and the `FooServiceController` methods may return `DeepPartial` responses, since any missing fields are received as their default values.
485
487
 
486
488
  Note that `addGrpcMetadata`, `addNestjsRestParameter`, `returnObservable` and `useNestjsFqnServiceName` will still be false.
487
489
 
490
+ - With `--ts_proto_opt=nestJs=client` or `--ts_proto_opt=nestJs=server`, only the client-side or server-side NestJS service output will be generated, with the same changed defaults as `nestJs=true`.
491
+
492
+ With `nestJs=client`, only the `FooServiceClient` interface is generated, i.e. there is no `FooServiceController` interface or `@FooServiceControllerMethods()` decorator, so the output doesn't import `@nestjs/microservices`. With `nestJs=server`, only the controller interface and decorator are generated. The message types and the `FOO_PACKAGE_NAME`/`FOO_SERVICE_NAME` consts are generated in both cases.
493
+
494
+ `nestJs=client,nestJs=server` is the same as `nestJs=true`.
495
+
488
496
  - With `--ts_proto_opt=useDate=false`, fields of type `google.protobuf.Timestamp` will not be mapped to type `Date` in the generated types. See [Timestamp](#timestamp) for more details.
489
497
 
490
498
  - With `--ts_proto_opt=useMongoObjectId=true`, fields of a type called ObjectId where the message is constructed to have on field called value that is a string will be mapped to type `mongodb.ObjectId` in the generated types. This will require your project to install the mongodb npm package. See [ObjectId](#objectid) for more details.
@@ -34,20 +34,26 @@ function generateNestjsServiceController(ctx, fileDesc, sourceInfo, serviceDesc)
34
34
  if (options.addNestjsRestParameter) {
35
35
  params.push((0, ts_poet_1.code) `...rest: any`);
36
36
  }
37
+ // NestJS encodes the response with @grpc/proto-loader, which leaves out any missing fields,
38
+ // i.e. the client receives them as their default values, so it's okay to accept partial responses
39
+ let outputType = (0, types_1.responseType)(ctx, methodDesc);
40
+ if (options.outputPartialMethods) {
41
+ outputType = (0, ts_poet_1.code) `${ctx.utils.DeepPartial}<${outputType}>`;
42
+ }
37
43
  // Return observable for interface only configuration, passing returnObservable=true and methodDesc.serverStreaming=true
38
44
  let returns;
39
45
  if ((0, types_1.isEmptyType)(methodDesc.outputType)) {
40
46
  returns = (0, ts_poet_1.code) `void | Promise<void>`;
41
47
  }
42
48
  else if (options.returnObservable || methodDesc.serverStreaming) {
43
- returns = (0, ts_poet_1.code) `${(0, types_1.responseObservable)(ctx, methodDesc)}`;
49
+ returns = (0, ts_poet_1.code) `${(0, types_1.observableType)(ctx)}<${outputType}>`;
44
50
  }
45
51
  else {
46
52
  // generate nestjs union type
47
53
  returns = (0, ts_poet_1.code) `
48
- ${(0, types_1.responsePromise)(ctx, methodDesc)}
49
- | ${(0, types_1.responseObservable)(ctx, methodDesc)}
50
- | ${(0, types_1.responseType)(ctx, methodDesc)}
54
+ Promise<${outputType}>
55
+ | ${(0, types_1.observableType)(ctx)}<${outputType}>
56
+ | ${outputType}
51
57
  `;
52
58
  }
53
59
  chunks.push((0, ts_poet_1.code) `
@@ -84,7 +90,9 @@ function generateNestjsServiceClient(ctx, fileDesc, sourceInfo, serviceDesc) {
84
90
  if (options.context) {
85
91
  params.push((0, ts_poet_1.code) `ctx: Context`);
86
92
  }
87
- params.push((0, ts_poet_1.code) `request: ${(0, types_1.requestType)(ctx, methodDesc)}`);
93
+ // NestJS encodes the request with @grpc/proto-loader, which leaves out any missing fields,
94
+ // i.e. the server receives them as their default values, so it's okay to accept partial requests
95
+ params.push((0, ts_poet_1.code) `request: ${(0, types_1.requestType)(ctx, methodDesc, options.outputPartialMethods)}`);
88
96
  // Use metadata as last argument for interface only configuration
89
97
  if (options.addGrpcMetadata) {
90
98
  const q = options.addNestjsRestParameter ? "" : "?";
package/build/src/main.js CHANGED
@@ -69,7 +69,7 @@ function generateFile(ctx, fileDesc) {
69
69
  chunks.push((0, enums_1.generateEnum)(ctx, fullName, enumDesc, sInfo));
70
70
  });
71
71
  // If nestJs=true export [package]_PACKAGE_NAME and [service]_SERVICE_NAME const
72
- if (options.nestJs) {
72
+ if (options.nestJs.length > 0) {
73
73
  if (options.exportCommonSymbols) {
74
74
  const prefix = (0, case_1.camelToSnake)(fileDesc.package.replace(/\./g, "_"));
75
75
  chunks.push((0, ts_poet_1.code) `export const ${prefix}_PACKAGE_NAME = '${fileDesc.package}';`);
@@ -85,7 +85,7 @@ function generateFile(ctx, fileDesc) {
85
85
  options.outputJsonMethods ||
86
86
  options.outputTypeAnnotations ||
87
87
  options.outputTypeRegistry ||
88
- options.nestJs) {
88
+ options.nestJs.length > 0) {
89
89
  // then add the encoder/decoder/base instance
90
90
  (0, visit_1.visit)(fileDesc, sourceInfo, (fullName, message, _sInfo, fullProtoTypeName) => {
91
91
  const fullTypeName = (0, utils_1.maybePrefixPackage)(fileDesc, fullProtoTypeName);
@@ -98,7 +98,7 @@ function generateFile(ctx, fileDesc) {
98
98
  (options.outputDecodeIncludeTypes === "" ||
99
99
  new RegExp(options.outputDecodeIncludeTypes).test(fullTypeName))) ||
100
100
  options.outputPartialMethods ||
101
- (outputWrapAndUnwrap && !(options.nestJs && (0, types_1.isAnyValueTypeName)(fullTypeName)))) {
101
+ (outputWrapAndUnwrap && !(options.nestJs.length > 0 && (0, types_1.isAnyValueTypeName)(fullTypeName)))) {
102
102
  chunks.push(generateBaseInstanceFactory(ctx, fullName, message, fullTypeName));
103
103
  }
104
104
  const staticMembers = [];
@@ -168,7 +168,7 @@ function generateFile(ctx, fileDesc) {
168
168
  structValue: (0, utils_1.getFieldName)({ name: "struct_value", jsonName: (0, case_1.snakeToCamel)("struct_value") }, ctx.options),
169
169
  listValue: (0, utils_1.getFieldName)({ name: "list_value", jsonName: (0, case_1.snakeToCamel)("list_value") }, ctx.options),
170
170
  };
171
- if (options.nestJs) {
171
+ if (options.nestJs.length > 0) {
172
172
  staticMembers.push(...(0, generate_struct_wrappers_1.generateWrapDeep)(ctx, fullTypeName, structFieldNames));
173
173
  staticMembers.push(...(0, generate_struct_wrappers_1.generateUnwrapDeep)(ctx, fullTypeName, structFieldNames));
174
174
  }
@@ -232,7 +232,7 @@ function generateFile(ctx, fileDesc) {
232
232
  chunks.push((0, ts_poet_1.code) `export const ${name}: ${ctx.utils.Extension}<${type}> = ${extensionInfo};`);
233
233
  }
234
234
  }
235
- if (options.nestJs) {
235
+ if (options.nestJs.length > 0) {
236
236
  if (fileDesc.messageType.find(hasStructTypeField)) {
237
237
  chunks.push(makeProtobufStructWrapper(options));
238
238
  }
@@ -240,14 +240,18 @@ function generateFile(ctx, fileDesc) {
240
240
  let hasServerStreamingMethods = false;
241
241
  let hasStreamingMethods = false;
242
242
  (0, visit_1.visitServices)(fileDesc, sourceInfo, (serviceDesc, sInfo) => {
243
- if (options.nestJs) {
243
+ if (options.nestJs.length > 0) {
244
244
  // NestJS is sufficiently different that we special case the client/server interfaces
245
- // generate nestjs grpc client interface
246
- chunks.push((0, generate_nestjs_1.generateNestjsServiceClient)(ctx, fileDesc, sInfo, serviceDesc));
247
- // and the service controller interface
248
- chunks.push((0, generate_nestjs_1.generateNestjsServiceController)(ctx, fileDesc, sInfo, serviceDesc));
249
- // generate nestjs grpc service controller decorator
250
- chunks.push((0, generate_nestjs_1.generateNestjsGrpcServiceMethodsDecorator)(ctx, fileDesc, serviceDesc));
245
+ if (options.nestJs.includes("client")) {
246
+ // generate nestjs grpc client interface
247
+ chunks.push((0, generate_nestjs_1.generateNestjsServiceClient)(ctx, fileDesc, sInfo, serviceDesc));
248
+ }
249
+ if (options.nestJs.includes("server")) {
250
+ // generate nestjs grpc service controller interface
251
+ chunks.push((0, generate_nestjs_1.generateNestjsServiceController)(ctx, fileDesc, sInfo, serviceDesc));
252
+ // generate nestjs grpc service controller decorator
253
+ chunks.push((0, generate_nestjs_1.generateNestjsGrpcServiceMethodsDecorator)(ctx, fileDesc, serviceDesc));
254
+ }
251
255
  let serviceConstName = `${(0, case_1.camelToSnake)(serviceDesc.name)}_NAME`;
252
256
  if (!serviceDesc.name.toLowerCase().endsWith("service")) {
253
257
  serviceConstName = `${(0, case_1.camelToSnake)(serviceDesc.name)}_SERVICE_NAME`;
@@ -84,7 +84,7 @@ export type Options = {
84
84
  useNestjsFqnServiceName: boolean;
85
85
  returnObservable: boolean;
86
86
  lowerCaseServiceMethods: boolean;
87
- nestJs: boolean;
87
+ nestJs: Array<"client" | "server">;
88
88
  env: EnvOption;
89
89
  unrecognizedEnum: boolean;
90
90
  unrecognizedEnumName: string;
@@ -100,7 +100,7 @@ function defaultOptions() {
100
100
  metadataType: undefined,
101
101
  addNestjsRestParameter: false,
102
102
  useNestjsFqnServiceName: false,
103
- nestJs: false,
103
+ nestJs: [],
104
104
  env: EnvOption.BOTH,
105
105
  unrecognizedEnum: true,
106
106
  unrecognizedEnumName: "UNRECOGNIZED",
@@ -162,17 +162,32 @@ function optionsFromParameter(parameter) {
162
162
  }
163
163
  Object.assign(options, parsed);
164
164
  }
165
+ // Treat nestJs=true as both the client & server interfaces, and nestJs=false as neither
166
+ if (options.nestJs === true) {
167
+ options.nestJs = ["client", "server"];
168
+ }
169
+ else if (options.nestJs === false) {
170
+ options.nestJs = [];
171
+ }
172
+ else if (typeof options.nestJs === "string") {
173
+ options.nestJs = [options.nestJs];
174
+ }
175
+ for (const value of options.nestJs) {
176
+ if (value !== "client" && value !== "server") {
177
+ throw new Error(`nestJs must be true, false, client, or server, but got '${value}'`);
178
+ }
179
+ }
165
180
  // onlyTypes=true implies outputJsonMethods=false,outputEncodeMethods=false,outputClientImpl=false,nestJs=false
166
181
  if (options.onlyTypes) {
167
182
  options.outputJsonMethods = false;
168
183
  options.outputEncodeMethods = false;
169
184
  options.outputClientImpl = false;
170
- options.nestJs = false;
185
+ options.nestJs = [];
171
186
  }
172
187
  else if (!options.outputJsonMethods &&
173
188
  !options.outputEncodeMethods &&
174
189
  !options.outputClientImpl &&
175
- !options.nestJs) {
190
+ options.nestJs.length === 0) {
176
191
  options.onlyTypes = true;
177
192
  }
178
193
  // Treat forceLong=true as LONG
@@ -189,11 +204,11 @@ function optionsFromParameter(parameter) {
189
204
  }
190
205
  // Assume the user wants the default service output, unless they're using nestJs, which has
191
206
  // its own controllers output (although nestjs users can ask for other services too).
192
- if (options.outputServices.length == 0 && !options.nestJs) {
207
+ if (options.outputServices.length == 0 && options.nestJs.length === 0) {
193
208
  options.outputServices = [ServiceOption.DEFAULT];
194
209
  }
195
210
  // If using nestJs + other services, add the encode methods back
196
- if (options.nestJs && options.outputServices.length > 0) {
211
+ if (options.nestJs.length > 0 && options.outputServices.length > 0) {
197
212
  options.outputEncodeMethods = true;
198
213
  }
199
214
  // Handle outputSchema=true
@@ -242,7 +257,7 @@ function optionsFromParameter(parameter) {
242
257
  options.useDate = DateOption.STRING;
243
258
  }
244
259
  }
245
- if (options.nestJs) {
260
+ if (options.nestJs.length > 0) {
246
261
  options.initializeFieldsAsUndefined = false;
247
262
  }
248
263
  else {
@@ -267,7 +282,7 @@ function optionsFromParameter(parameter) {
267
282
  (options.outputTypeRegistry ||
268
283
  options.outputTypeAnnotations === "static-only" ||
269
284
  options.outputServices.includes(ServiceOption.GENERIC) ||
270
- options.nestJs ||
285
+ options.nestJs.length > 0 ||
271
286
  options.useMongoObjectId)) {
272
287
  throw new Error("outputStandaloneFunctions=true can't be used with outputTypeRegistry=true, outputTypeAnnotations=static-only, outputServices=generic-definitions, nestJs=true, or useMongoObjectId=true");
273
288
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "2.19.0",
3
+ "version": "2.21.0",
4
4
  "description": "",
5
5
  "main": "build/src/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",