ts-proto 1.84.0 → 1.87.1

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 CHANGED
@@ -1,3 +1,31 @@
1
+ ## [1.87.1](https://github.com/stephenh/ts-proto/compare/v1.87.0...v1.87.1) (2021-11-21)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * code generation for int64 map values in fromPartial and fromJson ([#395](https://github.com/stephenh/ts-proto/issues/395)) ([d3ea8eb](https://github.com/stephenh/ts-proto/commit/d3ea8eb69e19a5e45fcc1766c4af1194b17e48fc))
7
+
8
+ # [1.87.0](https://github.com/stephenh/ts-proto/compare/v1.86.0...v1.87.0) (2021-11-16)
9
+
10
+
11
+ ### Features
12
+
13
+ * Use ternary operator for conditional assignments ([#394](https://github.com/stephenh/ts-proto/issues/394)) ([d84c084](https://github.com/stephenh/ts-proto/commit/d84c084fb56c958c184f8971479979b8bfb17ccc))
14
+
15
+ # [1.86.0](https://github.com/stephenh/ts-proto/compare/v1.85.0...v1.86.0) (2021-11-15)
16
+
17
+
18
+ ### Features
19
+
20
+ * Initialize lists with map ([#387](https://github.com/stephenh/ts-proto/issues/387)) ([200e674](https://github.com/stephenh/ts-proto/commit/200e674e4baf2640d67720ad535fe042d291d4a0))
21
+
22
+ # [1.85.0](https://github.com/stephenh/ts-proto/compare/v1.84.0...v1.85.0) (2021-11-02)
23
+
24
+
25
+ ### Features
26
+
27
+ * Streaming support ([#373](https://github.com/stephenh/ts-proto/issues/373)) ([459b94f](https://github.com/stephenh/ts-proto/commit/459b94f5b2988d58d186461332e888c3e511603a))
28
+
1
29
  # [1.84.0](https://github.com/stephenh/ts-proto/compare/v1.83.3...v1.84.0) (2021-11-02)
2
30
 
3
31
 
package/README.markdown CHANGED
@@ -70,7 +70,7 @@ It will also generate client implementations of `PingService`; currently [Twirp]
70
70
  - `npm install ts-proto`
71
71
  - `protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=. ./simple.proto`
72
72
  - (Note that the output parameter name, `ts_proto_out`, is named based on the suffix of the plugin's name, i.e. "ts_proto" suffix in the `--plugin=./node_modules/.bin/protoc-gen-ts_proto` parameter becomes the `_out` prefix, per `protoc`'s CLI conventions.)
73
- - On Windows, use `protoc --plugin=protoc-gen-ts_proto=.\node_modules\.bin\protoc-gen-ts_proto.cmd --ts_proto_out=. ./simple.proto`
73
+ - On Windows, use `protoc --plugin=protoc-gen-ts_proto=.\node_modules\.bin\protoc-gen-ts_proto.cmd --ts_proto_out=. ./simple.proto` (see [#93](https://github.com/stephenh/ts-proto/issues/93))
74
74
  - Ensure you're using a modern `protoc`, i.e. the original `protoc` `3.0.0` doesn't support the `_opt` flag
75
75
 
76
76
  This will generate `*.ts` source files for the given `*.proto` types.
@@ -43,9 +43,7 @@ function generateRpcMethod(ctx, serviceDesc, methodDesc) {
43
43
  const { options, utils } = ctx;
44
44
  const inputType = types_1.requestType(ctx, methodDesc);
45
45
  const partialInputType = ts_poet_1.code `${utils.DeepPartial}<${inputType}>`;
46
- const returns = options.returnObservable || methodDesc.serverStreaming
47
- ? types_1.responseObservable(ctx, methodDesc)
48
- : types_1.responsePromise(ctx, methodDesc);
46
+ const returns = types_1.responsePromiseOrObservable(ctx, methodDesc);
49
47
  const method = methodDesc.serverStreaming ? 'invoke' : 'unary';
50
48
  return ts_poet_1.code `
51
49
  ${methodDesc.formattedName}(
@@ -55,15 +55,7 @@ function generateService(ctx, fileDesc, sourceInfo, serviceDesc) {
55
55
  if (options.addNestjsRestParameter) {
56
56
  params.push(ts_poet_1.code `...rest: any`);
57
57
  }
58
- // Return observable for interface only configuration, passing returnObservable=true and methodDesc.serverStreaming=true
59
- let returnType;
60
- if (options.returnObservable || methodDesc.serverStreaming) {
61
- returnType = types_1.responseObservable(ctx, methodDesc);
62
- }
63
- else {
64
- returnType = types_1.responsePromise(ctx, methodDesc);
65
- }
66
- chunks.push(ts_poet_1.code `${methodDesc.formattedName}(${ts_poet_1.joinCode(params, { on: ',' })}): ${returnType};`);
58
+ chunks.push(ts_poet_1.code `${methodDesc.formattedName}(${ts_poet_1.joinCode(params, { on: ',' })}): ${types_1.responsePromiseOrObservable(ctx, methodDesc)};`);
67
59
  // If this is a batch method, auto-generate the singular version of it
68
60
  if (options.context) {
69
61
  const batchMethod = types_1.detectBatchMethod(ctx, fileDesc, serviceDesc, methodDesc);
@@ -83,22 +75,50 @@ function generateRegularRpcMethod(ctx, fileDesc, serviceDesc, methodDesc) {
83
75
  utils_1.assertInstanceOf(methodDesc, utils_1.FormattedMethodDescriptor);
84
76
  const { options } = ctx;
85
77
  const Reader = ts_poet_1.imp('Reader@protobufjs/minimal');
78
+ const rawInputType = types_1.rawRequestType(ctx, methodDesc);
86
79
  const inputType = types_1.requestType(ctx, methodDesc);
87
80
  const outputType = types_1.responseType(ctx, methodDesc);
88
81
  const params = [...(options.context ? [ts_poet_1.code `ctx: Context`] : []), ts_poet_1.code `request: ${inputType}`];
89
82
  const maybeCtx = options.context ? 'ctx,' : '';
83
+ let encode = ts_poet_1.code `${rawInputType}.encode(request).finish()`;
84
+ let decode = ts_poet_1.code `data => ${outputType}.decode(new ${Reader}(data))`;
85
+ if (methodDesc.clientStreaming) {
86
+ encode = ts_poet_1.code `request.pipe(${ts_poet_1.imp('map@rxjs/operators')}(request => ${encode}))`;
87
+ }
88
+ let returnVariable;
89
+ if (options.returnObservable || methodDesc.serverStreaming) {
90
+ returnVariable = 'result';
91
+ decode = ts_poet_1.code `result.pipe(${ts_poet_1.imp('map@rxjs/operators')}(${decode}))`;
92
+ }
93
+ else {
94
+ returnVariable = 'promise';
95
+ decode = ts_poet_1.code `promise.then(${decode})`;
96
+ }
97
+ let rpcMethod;
98
+ if (methodDesc.clientStreaming && methodDesc.serverStreaming) {
99
+ rpcMethod = 'bidirectionalStreamingRequest';
100
+ }
101
+ else if (methodDesc.serverStreaming) {
102
+ rpcMethod = 'serverStreamingRequest';
103
+ }
104
+ else if (methodDesc.clientStreaming) {
105
+ rpcMethod = 'clientStreamingRequest';
106
+ }
107
+ else {
108
+ rpcMethod = 'request';
109
+ }
90
110
  return ts_poet_1.code `
91
111
  ${methodDesc.formattedName}(
92
112
  ${ts_poet_1.joinCode(params, { on: ',' })}
93
- ): ${types_1.responsePromise(ctx, methodDesc)} {
94
- const data = ${inputType}.encode(request).finish();
95
- const promise = this.rpc.request(
113
+ ): ${types_1.responsePromiseOrObservable(ctx, methodDesc)} {
114
+ const data = ${encode};
115
+ const ${returnVariable} = this.rpc.${rpcMethod}(
96
116
  ${maybeCtx}
97
117
  "${utils_1.maybePrefixPackage(fileDesc, serviceDesc.name)}",
98
118
  "${methodDesc.name}",
99
119
  data
100
120
  );
101
- return promise.then(data => ${outputType}.decode(new ${Reader}(data)));
121
+ return ${decode};
102
122
  }
103
123
  `;
104
124
  }
@@ -217,24 +237,41 @@ function generateCachingRpcMethod(ctx, fileDesc, serviceDesc, methodDesc) {
217
237
  *
218
238
  * This lets clients pass in their own request-promise-ish client.
219
239
  *
240
+ * This also requires clientStreamingRequest, serverStreamingRequest and
241
+ * bidirectionalStreamingRequest methods if any of the RPCs is streaming.
242
+ *
220
243
  * We don't export this because if a project uses multiple `*.proto` files,
221
244
  * we don't want our the barrel imports in `index.ts` to have multiple `Rpc`
222
245
  * types.
223
246
  */
224
- function generateRpcType(ctx) {
247
+ function generateRpcType(ctx, hasStreamingMethods) {
225
248
  const { options } = ctx;
226
249
  const maybeContext = options.context ? '<Context>' : '';
227
250
  const maybeContextParam = options.context ? 'ctx: Context,' : '';
228
- return ts_poet_1.code `
229
- interface Rpc${maybeContext} {
230
- request(
251
+ const methods = [[ts_poet_1.code `request`, ts_poet_1.code `Uint8Array`, ts_poet_1.code `Promise<Uint8Array>`]];
252
+ if (hasStreamingMethods) {
253
+ const observable = ts_poet_1.imp('Observable@rxjs');
254
+ methods.push([ts_poet_1.code `clientStreamingRequest`, ts_poet_1.code `${observable}<Uint8Array>`, ts_poet_1.code `Promise<Uint8Array>`]);
255
+ methods.push([ts_poet_1.code `serverStreamingRequest`, ts_poet_1.code `Uint8Array`, ts_poet_1.code `${observable}<Uint8Array>`]);
256
+ methods.push([
257
+ ts_poet_1.code `bidirectionalStreamingRequest`,
258
+ ts_poet_1.code `${observable}<Uint8Array>`,
259
+ ts_poet_1.code `${observable}<Uint8Array>`,
260
+ ]);
261
+ }
262
+ const chunks = [];
263
+ chunks.push(ts_poet_1.code ` interface Rpc${maybeContext} {`);
264
+ methods.forEach((method) => {
265
+ chunks.push(ts_poet_1.code `
266
+ ${method[0]}(
231
267
  ${maybeContextParam}
232
268
  service: string,
233
269
  method: string,
234
- data: Uint8Array
235
- ): Promise<Uint8Array>;
236
- }
237
- `;
270
+ data: ${method[1]}
271
+ ): ${method[2]};`);
272
+ });
273
+ chunks.push(ts_poet_1.code ` }`);
274
+ return ts_poet_1.joinCode(chunks, { on: '\n' });
238
275
  }
239
276
  exports.generateRpcType = generateRpcType;
240
277
  function generateDataLoadersType() {
package/build/main.js CHANGED
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.contextTypeVar = exports.makeUtils = exports.generateFile = void 0;
4
4
  const ts_poet_1 = require("ts-poet");
5
- const ts_proto_descriptors_1 = require("ts-proto-descriptors");
6
5
  const types_1 = require("./types");
7
6
  const sourceInfo_1 = require("./sourceInfo");
8
7
  const utils_1 = require("./utils");
@@ -91,6 +90,7 @@ function generateFile(ctx, fileDesc) {
91
90
  }
92
91
  }, options);
93
92
  }
93
+ let hasServerStreamingMethods = false;
94
94
  let hasStreamingMethods = false;
95
95
  visit_1.visitServices(fileDesc, sourceInfo, (serviceDesc, sInfo) => {
96
96
  if (options.nestJs) {
@@ -126,18 +126,23 @@ function generateFile(ctx, fileDesc) {
126
126
  serviceDesc.method.forEach((method) => {
127
127
  chunks.push(generate_grpc_web_1.generateGrpcMethodDesc(ctx, serviceDesc, method));
128
128
  if (method.serverStreaming) {
129
- hasStreamingMethods = true;
129
+ hasServerStreamingMethods = true;
130
130
  }
131
131
  });
132
132
  }
133
133
  }
134
+ serviceDesc.method.forEach((methodDesc, index) => {
135
+ if (methodDesc.serverStreaming || methodDesc.clientStreaming) {
136
+ hasStreamingMethods = true;
137
+ }
138
+ });
134
139
  });
135
140
  if (options.outputServices === options_1.ServiceOption.DEFAULT && options.outputClientImpl && fileDesc.service.length > 0) {
136
141
  if (options.outputClientImpl === true) {
137
- chunks.push(generate_services_1.generateRpcType(ctx));
142
+ chunks.push(generate_services_1.generateRpcType(ctx, hasStreamingMethods));
138
143
  }
139
144
  else if (options.outputClientImpl === 'grpc-web') {
140
- chunks.push(generate_grpc_web_1.addGrpcWebMisc(ctx, hasStreamingMethods));
145
+ chunks.push(generate_grpc_web_1.addGrpcWebMisc(ctx, hasServerStreamingMethods));
141
146
  }
142
147
  }
143
148
  if (options.context) {
@@ -280,13 +285,15 @@ function makeDeepPartial(options, longs) {
280
285
  `;
281
286
  }
282
287
  const maybeExport = options.exportCommonSymbols ? 'export' : '';
283
- const maybeLong = options.forceLong === options_1.LongOption.LONG ? ts_poet_1.code ` | ${longs.Long}` : '';
288
+ // Allow passing longs as numbers or strings, nad we'll convert them
289
+ const maybeLong = options.forceLong === options_1.LongOption.LONG ? ts_poet_1.code ` : T extends ${longs.Long} ? string | number | Long ` : '';
284
290
  const keys = options.outputTypeRegistry ? ts_poet_1.code `Exclude<keyof T, '$type'>` : ts_poet_1.code `keyof T`;
285
291
  // Based on the type from ts-essentials
286
292
  const DeepPartial = ts_poet_1.conditionalOutput('DeepPartial', ts_poet_1.code `
287
- type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined${maybeLong};
288
- ${maybeExport} type DeepPartial<T> = T extends Builtin
293
+ type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
294
+ ${maybeExport} type DeepPartial<T> = T extends Builtin
289
295
  ? T
296
+ ${maybeLong}
290
297
  : T extends Array<infer U>
291
298
  ? Array<DeepPartial<U>>
292
299
  : T extends ReadonlyArray<infer U>
@@ -733,20 +740,6 @@ function generateFromJson(ctx, fullName, messageDesc) {
733
740
  fromJSON(${messageDesc.field.length > 0 ? 'object' : '_'}: any): ${fullName} {
734
741
  const message = { ...base${fullName} } as ${fullName};
735
742
  `);
736
- // initialize all lists
737
- messageDesc.field.filter(types_1.isRepeated).forEach((field) => {
738
- const value = types_1.isMapType(ctx, messageDesc, field) ? '{}' : '[]';
739
- const name = case_1.maybeSnakeToCamel(field.name, options);
740
- chunks.push(ts_poet_1.code `message.${name} = ${value};`);
741
- });
742
- // initialize all buffers
743
- messageDesc.field
744
- .filter((field) => !types_1.isRepeated(field) && !types_1.isWithinOneOf(field) && types_1.isBytes(field))
745
- .forEach((field) => {
746
- const value = options.env === options_1.EnvOption.NODE ? 'Buffer.alloc(0)' : 'new Uint8Array()';
747
- const name = case_1.maybeSnakeToCamel(field.name, options);
748
- chunks.push(ts_poet_1.code `message.${name} = ${value};`);
749
- });
750
743
  // add a check for each incoming field
751
744
  messageDesc.field.forEach((field) => {
752
745
  const fieldName = case_1.maybeSnakeToCamel(field.name, options);
@@ -807,6 +800,9 @@ function generateFromJson(ctx, fullName, messageDesc) {
807
800
  return ts_poet_1.code `${utils.bytesFromBase64}(${from} as string)`;
808
801
  }
809
802
  }
803
+ else if (types_1.isLong(valueType) && options.forceLong === options_1.LongOption.LONG) {
804
+ return ts_poet_1.code `Long.fromString(${from} as string)`;
805
+ }
810
806
  else if (types_1.isEnum(valueType)) {
811
807
  return ts_poet_1.code `${from} as number`;
812
808
  }
@@ -841,42 +837,41 @@ function generateFromJson(ctx, fullName, messageDesc) {
841
837
  }
842
838
  };
843
839
  // and then use the snippet to handle repeated fields if necessary
844
- chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
845
840
  if (types_1.isRepeated(field)) {
846
841
  if (types_1.isMapType(ctx, messageDesc, field)) {
842
+ chunks.push(ts_poet_1.code `message.${fieldName} = {};`);
843
+ chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
847
844
  const i = maybeCastToNumber(ctx, messageDesc, field, 'key');
848
845
  chunks.push(ts_poet_1.code `
849
846
  Object.entries(object.${fieldName}).forEach(([key, value]) => {
850
847
  message.${fieldName}[${i}] = ${readSnippet('value')};
851
848
  });
852
849
  `);
850
+ chunks.push(ts_poet_1.code `}`);
853
851
  }
854
852
  else {
853
+ // Explicit `any` type required to make TS with noImplicitAny happy. `object` is also `any` here.
855
854
  chunks.push(ts_poet_1.code `
856
- for (const e of object.${fieldName}) {
857
- message.${fieldName}.push(${readSnippet('e')});
858
- }
855
+ message.${fieldName} = (object.${fieldName} ?? []).map((e: any) => ${readSnippet('e')});
859
856
  `);
860
857
  }
861
858
  }
862
859
  else if (types_1.isWithinOneOfThatShouldBeUnion(options, field)) {
860
+ chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
863
861
  const oneofName = case_1.maybeSnakeToCamel(messageDesc.oneofDecl[field.oneofIndex].name, options);
864
862
  chunks.push(ts_poet_1.code `
865
863
  message.${oneofName} = { $case: '${fieldName}', ${fieldName}: ${readSnippet(`object.${fieldName}`)} }
866
864
  `);
865
+ chunks.push(ts_poet_1.code `}`);
867
866
  }
868
867
  else {
869
- chunks.push(ts_poet_1.code `message.${fieldName} = ${readSnippet(`object.${fieldName}`)};`);
870
- }
871
- // set the default value (TODO Support bytes)
872
- if (!types_1.isRepeated(field) &&
873
- field.type !== ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_BYTES &&
874
- options.oneof !== options_1.OneofOption.UNIONS) {
875
- const v = types_1.isWithinOneOf(field) ? 'undefined' : types_1.defaultValue(ctx, field);
876
- chunks.push(ts_poet_1.code `} else {`);
877
- chunks.push(ts_poet_1.code `message.${fieldName} = ${v};`);
868
+ const fallback = types_1.isWithinOneOf(field) ? 'undefined' : types_1.defaultValue(ctx, field);
869
+ chunks.push(ts_poet_1.code `
870
+ message.${fieldName} = (object.${fieldName} !== undefined && object.${fieldName} !== null)
871
+ ? ${readSnippet(`object.${fieldName}`)}
872
+ : ${fallback};
873
+ `);
878
874
  }
879
- chunks.push(ts_poet_1.code `}`);
880
875
  });
881
876
  // and then wrap up the switch/while/return
882
877
  chunks.push(ts_poet_1.code `return message`);
@@ -929,6 +924,9 @@ function generateToJson(ctx, fullName, messageDesc) {
929
924
  else if (types_1.isTimestamp(valueType) && options.useDate === options_1.DateOption.TIMESTAMP) {
930
925
  return ts_poet_1.code `${utils.fromTimestamp}(${from}).toISOString()`;
931
926
  }
927
+ else if (types_1.isLong(valueType) && options.forceLong === options_1.LongOption.LONG) {
928
+ return ts_poet_1.code `${from}.toString()`;
929
+ }
932
930
  else if (types_1.isScalar(valueType) || types_1.isValueType(ctx, valueType)) {
933
931
  return ts_poet_1.code `${from}`;
934
932
  }
@@ -1006,7 +1004,10 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1006
1004
  messageDesc.field.forEach((field) => {
1007
1005
  const fieldName = case_1.maybeSnakeToCamel(field.name, options);
1008
1006
  const readSnippet = (from) => {
1009
- if (types_1.isPrimitive(field) ||
1007
+ if ((types_1.isLong(field) || types_1.isLongValueType(field)) && options.forceLong === options_1.LongOption.LONG) {
1008
+ return ts_poet_1.code `Long.fromValue(${from})`;
1009
+ }
1010
+ else if (types_1.isPrimitive(field) ||
1010
1011
  (types_1.isTimestamp(field) && (options.useDate === options_1.DateOption.DATE || options.useDate === options_1.DateOption.STRING)) ||
1011
1012
  types_1.isValueType(ctx, field)) {
1012
1013
  return ts_poet_1.code `${from}`;
@@ -1021,6 +1022,9 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1021
1022
  else if (types_1.isEnum(valueType)) {
1022
1023
  return ts_poet_1.code `${from} as number`;
1023
1024
  }
1025
+ else if (types_1.isLong(valueType) && options.forceLong === options_1.LongOption.LONG) {
1026
+ return ts_poet_1.code `Long.fromValue(${from})`;
1027
+ }
1024
1028
  else {
1025
1029
  const cstr = case_1.capitalize(types_1.basicTypeName(ctx, valueType).toCodeString());
1026
1030
  return ts_poet_1.code `${cstr}(${from})`;
@@ -1049,11 +1053,9 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1049
1053
  };
1050
1054
  // and then use the snippet to handle repeated fields if necessary
1051
1055
  if (types_1.isRepeated(field)) {
1052
- const value = types_1.isMapType(ctx, messageDesc, field) ? '{}' : '[]';
1053
- const name = case_1.maybeSnakeToCamel(field.name, options);
1054
- chunks.push(ts_poet_1.code `message.${name} = ${value};`);
1055
- chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
1056
1056
  if (types_1.isMapType(ctx, messageDesc, field)) {
1057
+ chunks.push(ts_poet_1.code `message.${fieldName} = {};`);
1058
+ chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
1057
1059
  const i = maybeCastToNumber(ctx, messageDesc, field, 'key');
1058
1060
  chunks.push(ts_poet_1.code `
1059
1061
  Object.entries(object.${fieldName}).forEach(([key, value]) => {
@@ -1062,15 +1064,13 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1062
1064
  }
1063
1065
  });
1064
1066
  `);
1067
+ chunks.push(ts_poet_1.code `}`);
1065
1068
  }
1066
1069
  else {
1067
1070
  chunks.push(ts_poet_1.code `
1068
- for (const e of object.${fieldName}) {
1069
- message.${fieldName}.push(${readSnippet('e')});
1070
- }
1071
+ message.${fieldName} = (object.${fieldName} ?? []).map((e) => ${readSnippet('e')});
1071
1072
  `);
1072
1073
  }
1073
- chunks.push(ts_poet_1.code `}`);
1074
1074
  }
1075
1075
  else if (types_1.isWithinOneOfThatShouldBeUnion(options, field)) {
1076
1076
  let oneofName = case_1.maybeSnakeToCamel(messageDesc.oneofDecl[field.oneofIndex].name, options);
@@ -1085,30 +1085,18 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1085
1085
  }
1086
1086
  `);
1087
1087
  }
1088
- else if ((types_1.isLong(field) || types_1.isLongValueType(field)) && options.forceLong === options_1.LongOption.LONG) {
1089
- const v = readSnippet(`object.${fieldName}`);
1090
- const type = types_1.basicTypeName(ctx, field);
1091
- chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
1092
- chunks.push(ts_poet_1.code `message.${fieldName} = ${v} as ${type};`);
1093
- chunks.push(ts_poet_1.code `} else {`);
1094
- const fallback = types_1.isWithinOneOf(field) ? 'undefined' : types_1.defaultValue(ctx, field);
1095
- chunks.push(ts_poet_1.code `message.${fieldName} = ${fallback}`);
1096
- chunks.push(ts_poet_1.code `}`);
1097
- }
1098
- else if (types_1.isPrimitive(field) ||
1099
- (types_1.isTimestamp(field) && (options.useDate === options_1.DateOption.DATE || options.useDate === options_1.DateOption.STRING)) ||
1100
- types_1.isValueType(ctx, field)) {
1088
+ else if (readSnippet(`x`).toCodeString() == 'x') {
1101
1089
  // An optimized case of the else below that works when `readSnippet` returns the plain input
1102
1090
  const fallback = types_1.isWithinOneOf(field) ? 'undefined' : types_1.defaultValue(ctx, field);
1103
1091
  chunks.push(ts_poet_1.code `message.${fieldName} = object.${fieldName} ?? ${fallback};`);
1104
1092
  }
1105
1093
  else {
1106
- chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
1107
- chunks.push(ts_poet_1.code `message.${fieldName} = ${readSnippet(`object.${fieldName}`)};`);
1108
- chunks.push(ts_poet_1.code `} else {`);
1109
1094
  const fallback = types_1.isWithinOneOf(field) ? 'undefined' : types_1.defaultValue(ctx, field);
1110
- chunks.push(ts_poet_1.code `message.${fieldName} = ${fallback}`);
1111
- chunks.push(ts_poet_1.code `}`);
1095
+ chunks.push(ts_poet_1.code `
1096
+ message.${fieldName} = (object.${fieldName} !== undefined && object.${fieldName} !== null)
1097
+ ? ${readSnippet(`object.${fieldName}`)}
1098
+ : ${fallback};
1099
+ `);
1112
1100
  }
1113
1101
  });
1114
1102
  // and then wrap up the switch/while/return
package/build/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.detectBatchMethod = exports.responseObservable = exports.responsePromise = exports.responseType = exports.requestType = exports.detectMapType = exports.toTypeName = exports.getEnumMethod = exports.messageToTypeName = exports.wrapperTypeName = exports.valueTypeName = exports.isEmptyType = exports.isLongValueType = exports.isBytesValueType = exports.isValueType = exports.isTimestamp = exports.isMapType = exports.isLong = exports.isRepeated = exports.isWithinOneOfThatShouldBeUnion = exports.isWithinOneOf = exports.isEnum = exports.isMessage = exports.isBytes = exports.isPrimitive = exports.isScalar = exports.createTypeMap = exports.notDefaultCheck = exports.defaultValue = exports.packedType = exports.toReaderCall = exports.basicTypeName = exports.basicLongWireType = exports.basicWireType = void 0;
3
+ exports.detectBatchMethod = exports.responsePromiseOrObservable = exports.responseObservable = exports.responsePromise = exports.responseType = exports.requestType = exports.rawRequestType = exports.detectMapType = exports.toTypeName = exports.getEnumMethod = exports.messageToTypeName = exports.wrapperTypeName = exports.valueTypeName = exports.isEmptyType = exports.isLongValueType = exports.isBytesValueType = exports.isValueType = exports.isTimestamp = exports.isMapType = exports.isLong = exports.isRepeated = exports.isWithinOneOfThatShouldBeUnion = exports.isWithinOneOf = exports.isEnum = exports.isMessage = exports.isBytes = exports.isPrimitive = exports.isScalar = exports.createTypeMap = exports.notDefaultCheck = exports.defaultValue = exports.packedType = exports.toReaderCall = exports.basicTypeName = exports.basicLongWireType = exports.basicWireType = void 0;
4
4
  const ts_proto_descriptors_1 = require("ts-proto-descriptors");
5
5
  const ts_poet_1 = require("ts-poet");
6
6
  const options_1 = require("./options");
@@ -512,8 +512,12 @@ function detectMapType(ctx, messageDesc, fieldDesc) {
512
512
  return undefined;
513
513
  }
514
514
  exports.detectMapType = detectMapType;
515
+ function rawRequestType(ctx, methodDesc) {
516
+ return messageToTypeName(ctx, methodDesc.inputType);
517
+ }
518
+ exports.rawRequestType = rawRequestType;
515
519
  function requestType(ctx, methodDesc) {
516
- let typeName = messageToTypeName(ctx, methodDesc.inputType);
520
+ let typeName = rawRequestType(ctx, methodDesc);
517
521
  if (methodDesc.clientStreaming) {
518
522
  return ts_poet_1.code `${ts_poet_1.imp('Observable@rxjs')}<${typeName}>`;
519
523
  }
@@ -532,6 +536,14 @@ function responseObservable(ctx, methodDesc) {
532
536
  return ts_poet_1.code `${ts_poet_1.imp('Observable@rxjs')}<${responseType(ctx, methodDesc)}>`;
533
537
  }
534
538
  exports.responseObservable = responseObservable;
539
+ function responsePromiseOrObservable(ctx, methodDesc) {
540
+ const { options } = ctx;
541
+ if (options.returnObservable || methodDesc.serverStreaming) {
542
+ return responseObservable(ctx, methodDesc);
543
+ }
544
+ return responsePromise(ctx, methodDesc);
545
+ }
546
+ exports.responsePromiseOrObservable = responsePromiseOrObservable;
535
547
  function detectBatchMethod(ctx, fileDesc, serviceDesc, methodDesc) {
536
548
  const { typeMap } = ctx;
537
549
  const nameMatches = methodDesc.name.startsWith('Batch');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "1.84.0",
3
+ "version": "1.87.1",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",