ts-proto 1.83.2 → 1.86.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 CHANGED
@@ -1,3 +1,31 @@
1
+ # [1.86.0](https://github.com/stephenh/ts-proto/compare/v1.85.0...v1.86.0) (2021-11-15)
2
+
3
+
4
+ ### Features
5
+
6
+ * Initialize lists with map ([#387](https://github.com/stephenh/ts-proto/issues/387)) ([200e674](https://github.com/stephenh/ts-proto/commit/200e674e4baf2640d67720ad535fe042d291d4a0))
7
+
8
+ # [1.85.0](https://github.com/stephenh/ts-proto/compare/v1.84.0...v1.85.0) (2021-11-02)
9
+
10
+
11
+ ### Features
12
+
13
+ * Streaming support ([#373](https://github.com/stephenh/ts-proto/issues/373)) ([459b94f](https://github.com/stephenh/ts-proto/commit/459b94f5b2988d58d186461332e888c3e511603a))
14
+
15
+ # [1.84.0](https://github.com/stephenh/ts-proto/compare/v1.83.3...v1.84.0) (2021-11-02)
16
+
17
+
18
+ ### Features
19
+
20
+ * Reduce code size by using nullish coalescing operator in fromPartial ([#376](https://github.com/stephenh/ts-proto/issues/376)) ([19d2ded](https://github.com/stephenh/ts-proto/commit/19d2deda2cf7c47b1b56bfc65cf58653291dba4a))
21
+
22
+ ## [1.83.3](https://github.com/stephenh/ts-proto/compare/v1.83.2...v1.83.3) (2021-10-28)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * fix codegen for maps with wrapper value type ([#370](https://github.com/stephenh/ts-proto/issues/370)) ([dd2481d](https://github.com/stephenh/ts-proto/commit/dd2481df0835faafc561aad4a4d0c9c2ff9d868a))
28
+
1
29
  ## [1.83.2](https://github.com/stephenh/ts-proto/compare/v1.83.1...v1.83.2) (2021-10-26)
2
30
 
3
31
 
@@ -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) {
@@ -632,10 +637,18 @@ function generateEncode(ctx, fullName, messageDesc) {
632
637
  }
633
638
  if (types_1.isRepeated(field)) {
634
639
  if (types_1.isMapType(ctx, messageDesc, field)) {
640
+ const valueType = typeMap.get(field.typeName)[2].field[1];
635
641
  const maybeTypeField = options.outputTypeRegistry ? `$type: '${field.typeName.slice(1)}',` : '';
642
+ const entryWriteSnippet = types_1.isValueType(ctx, valueType)
643
+ ? ts_poet_1.code `
644
+ if (value !== undefined) {
645
+ ${writeSnippet(`{ ${maybeTypeField} key: key as any, value }`)};
646
+ }
647
+ `
648
+ : writeSnippet(`{ ${maybeTypeField} key: key as any, value }`);
636
649
  chunks.push(ts_poet_1.code `
637
650
  Object.entries(message.${fieldName}).forEach(([key, value]) => {
638
- ${writeSnippet(`{ ${maybeTypeField} key: key as any, value }`)};
651
+ ${entryWriteSnippet}
639
652
  });
640
653
  `);
641
654
  }
@@ -725,20 +738,6 @@ function generateFromJson(ctx, fullName, messageDesc) {
725
738
  fromJSON(${messageDesc.field.length > 0 ? 'object' : '_'}: any): ${fullName} {
726
739
  const message = { ...base${fullName} } as ${fullName};
727
740
  `);
728
- // initialize all lists
729
- messageDesc.field.filter(types_1.isRepeated).forEach((field) => {
730
- const value = types_1.isMapType(ctx, messageDesc, field) ? '{}' : '[]';
731
- const name = case_1.maybeSnakeToCamel(field.name, options);
732
- chunks.push(ts_poet_1.code `message.${name} = ${value};`);
733
- });
734
- // initialize all buffers
735
- messageDesc.field
736
- .filter((field) => !types_1.isRepeated(field) && !types_1.isWithinOneOf(field) && types_1.isBytes(field))
737
- .forEach((field) => {
738
- const value = options.env === options_1.EnvOption.NODE ? 'Buffer.alloc(0)' : 'new Uint8Array()';
739
- const name = case_1.maybeSnakeToCamel(field.name, options);
740
- chunks.push(ts_poet_1.code `message.${name} = ${value};`);
741
- });
742
741
  // add a check for each incoming field
743
742
  messageDesc.field.forEach((field) => {
744
743
  const fieldName = case_1.maybeSnakeToCamel(field.name, options);
@@ -814,6 +813,10 @@ function generateFromJson(ctx, fullName, messageDesc) {
814
813
  (options.useDate === options_1.DateOption.DATE || options.useDate === options_1.DateOption.TIMESTAMP)) {
815
814
  return ts_poet_1.code `${utils.fromJsonTimestamp}(${from})`;
816
815
  }
816
+ else if (types_1.isValueType(ctx, valueType)) {
817
+ const type = types_1.basicTypeName(ctx, valueType);
818
+ return ts_poet_1.code `${from} as ${type}`;
819
+ }
817
820
  else {
818
821
  const type = types_1.basicTypeName(ctx, valueType);
819
822
  return ts_poet_1.code `${type}.fromJSON(${from})`;
@@ -829,42 +832,41 @@ function generateFromJson(ctx, fullName, messageDesc) {
829
832
  }
830
833
  };
831
834
  // and then use the snippet to handle repeated fields if necessary
832
- chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
833
835
  if (types_1.isRepeated(field)) {
834
836
  if (types_1.isMapType(ctx, messageDesc, field)) {
837
+ chunks.push(ts_poet_1.code `message.${fieldName} = {};`);
838
+ chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
835
839
  const i = maybeCastToNumber(ctx, messageDesc, field, 'key');
836
840
  chunks.push(ts_poet_1.code `
837
841
  Object.entries(object.${fieldName}).forEach(([key, value]) => {
838
842
  message.${fieldName}[${i}] = ${readSnippet('value')};
839
843
  });
840
844
  `);
845
+ chunks.push(ts_poet_1.code `}`);
841
846
  }
842
847
  else {
848
+ // Explicit `any` type required to make TS with noImplicitAny happy. `object` is also `any` here.
843
849
  chunks.push(ts_poet_1.code `
844
- for (const e of object.${fieldName}) {
845
- message.${fieldName}.push(${readSnippet('e')});
846
- }
850
+ message.${fieldName} = (object.${fieldName} ?? []).map((e: any) => ${readSnippet('e')});
847
851
  `);
848
852
  }
849
853
  }
850
854
  else if (types_1.isWithinOneOfThatShouldBeUnion(options, field)) {
855
+ chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
851
856
  const oneofName = case_1.maybeSnakeToCamel(messageDesc.oneofDecl[field.oneofIndex].name, options);
852
857
  chunks.push(ts_poet_1.code `
853
858
  message.${oneofName} = { $case: '${fieldName}', ${fieldName}: ${readSnippet(`object.${fieldName}`)} }
854
859
  `);
860
+ chunks.push(ts_poet_1.code `}`);
855
861
  }
856
862
  else {
863
+ chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
857
864
  chunks.push(ts_poet_1.code `message.${fieldName} = ${readSnippet(`object.${fieldName}`)};`);
858
- }
859
- // set the default value (TODO Support bytes)
860
- if (!types_1.isRepeated(field) &&
861
- field.type !== ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_BYTES &&
862
- options.oneof !== options_1.OneofOption.UNIONS) {
863
- const v = types_1.isWithinOneOf(field) ? 'undefined' : types_1.defaultValue(ctx, field);
864
865
  chunks.push(ts_poet_1.code `} else {`);
865
- chunks.push(ts_poet_1.code `message.${fieldName} = ${v};`);
866
+ const fallback = types_1.isWithinOneOf(field) ? 'undefined' : types_1.defaultValue(ctx, field);
867
+ chunks.push(ts_poet_1.code `message.${fieldName} = ${fallback};`);
868
+ chunks.push(ts_poet_1.code `}`);
866
869
  }
867
- chunks.push(ts_poet_1.code `}`);
868
870
  });
869
871
  // and then wrap up the switch/while/return
870
872
  chunks.push(ts_poet_1.code `return message`);
@@ -917,7 +919,7 @@ function generateToJson(ctx, fullName, messageDesc) {
917
919
  else if (types_1.isTimestamp(valueType) && options.useDate === options_1.DateOption.TIMESTAMP) {
918
920
  return ts_poet_1.code `${utils.fromTimestamp}(${from}).toISOString()`;
919
921
  }
920
- else if (types_1.isScalar(valueType)) {
922
+ else if (types_1.isScalar(valueType) || types_1.isValueType(ctx, valueType)) {
921
923
  return ts_poet_1.code `${from}`;
922
924
  }
923
925
  else {
@@ -990,12 +992,6 @@ function generateFromPartial(ctx, fullName, messageDesc) {
990
992
  fromPartial(${messageDesc.field.length > 0 ? 'object' : '_'}: ${utils.DeepPartial}<${fullName}>): ${fullName} {
991
993
  const message = { ...base${fullName} } as ${fullName};
992
994
  `);
993
- // initialize all lists
994
- messageDesc.field.filter(types_1.isRepeated).forEach((field) => {
995
- const value = types_1.isMapType(ctx, messageDesc, field) ? '{}' : '[]';
996
- const name = case_1.maybeSnakeToCamel(field.name, options);
997
- chunks.push(ts_poet_1.code `message.${name} = ${value};`);
998
- });
999
995
  // add a check for each incoming field
1000
996
  messageDesc.field.forEach((field) => {
1001
997
  const fieldName = case_1.maybeSnakeToCamel(field.name, options);
@@ -1024,6 +1020,9 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1024
1020
  (options.useDate === options_1.DateOption.DATE || options.useDate === options_1.DateOption.STRING)) {
1025
1021
  return ts_poet_1.code `${from}`;
1026
1022
  }
1023
+ else if (types_1.isValueType(ctx, valueType)) {
1024
+ return ts_poet_1.code `${from}`;
1025
+ }
1027
1026
  else {
1028
1027
  const type = types_1.basicTypeName(ctx, valueType);
1029
1028
  return ts_poet_1.code `${type}.fromPartial(${from})`;
@@ -1040,8 +1039,9 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1040
1039
  };
1041
1040
  // and then use the snippet to handle repeated fields if necessary
1042
1041
  if (types_1.isRepeated(field)) {
1043
- chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
1044
1042
  if (types_1.isMapType(ctx, messageDesc, field)) {
1043
+ chunks.push(ts_poet_1.code `message.${fieldName} = {};`);
1044
+ chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
1045
1045
  const i = maybeCastToNumber(ctx, messageDesc, field, 'key');
1046
1046
  chunks.push(ts_poet_1.code `
1047
1047
  Object.entries(object.${fieldName}).forEach(([key, value]) => {
@@ -1050,12 +1050,11 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1050
1050
  }
1051
1051
  });
1052
1052
  `);
1053
+ chunks.push(ts_poet_1.code `}`);
1053
1054
  }
1054
1055
  else {
1055
1056
  chunks.push(ts_poet_1.code `
1056
- for (const e of object.${fieldName}) {
1057
- message.${fieldName}.push(${readSnippet('e')});
1058
- }
1057
+ message.${fieldName} = (object.${fieldName} ?? []).map((e) => ${readSnippet('e')});
1059
1058
  `);
1060
1059
  }
1061
1060
  }
@@ -1069,25 +1068,34 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1069
1068
  && object.${oneofName}?.${fieldName} !== null
1070
1069
  ) {
1071
1070
  message.${oneofName} = { $case: '${fieldName}', ${fieldName}: ${v} };
1071
+ }
1072
1072
  `);
1073
1073
  }
1074
- else {
1074
+ else if ((types_1.isLong(field) || types_1.isLongValueType(field)) && options.forceLong === options_1.LongOption.LONG) {
1075
+ const v = readSnippet(`object.${fieldName}`);
1076
+ const type = types_1.basicTypeName(ctx, field);
1075
1077
  chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
1076
- if ((types_1.isLong(field) || types_1.isLongValueType(field)) && options.forceLong === options_1.LongOption.LONG) {
1077
- const v = readSnippet(`object.${fieldName}`);
1078
- const type = types_1.basicTypeName(ctx, field);
1079
- chunks.push(ts_poet_1.code `message.${fieldName} = ${v} as ${type};`);
1080
- }
1081
- else {
1082
- chunks.push(ts_poet_1.code `message.${fieldName} = ${readSnippet(`object.${fieldName}`)};`);
1083
- }
1078
+ chunks.push(ts_poet_1.code `message.${fieldName} = ${v} as ${type};`);
1079
+ chunks.push(ts_poet_1.code `} else {`);
1080
+ const fallback = types_1.isWithinOneOf(field) ? 'undefined' : types_1.defaultValue(ctx, field);
1081
+ chunks.push(ts_poet_1.code `message.${fieldName} = ${fallback}`);
1082
+ chunks.push(ts_poet_1.code `}`);
1083
+ }
1084
+ else if (types_1.isPrimitive(field) ||
1085
+ (types_1.isTimestamp(field) && (options.useDate === options_1.DateOption.DATE || options.useDate === options_1.DateOption.STRING)) ||
1086
+ types_1.isValueType(ctx, field)) {
1087
+ // An optimized case of the else below that works when `readSnippet` returns the plain input
1088
+ const fallback = types_1.isWithinOneOf(field) ? 'undefined' : types_1.defaultValue(ctx, field);
1089
+ chunks.push(ts_poet_1.code `message.${fieldName} = object.${fieldName} ?? ${fallback};`);
1084
1090
  }
1085
- if (!types_1.isRepeated(field) && !types_1.isWithinOneOfThatShouldBeUnion(options, field)) {
1091
+ else {
1092
+ chunks.push(ts_poet_1.code `if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`);
1093
+ chunks.push(ts_poet_1.code `message.${fieldName} = ${readSnippet(`object.${fieldName}`)};`);
1086
1094
  chunks.push(ts_poet_1.code `} else {`);
1087
- const v = types_1.isWithinOneOf(field) ? 'undefined' : types_1.defaultValue(ctx, field);
1088
- chunks.push(ts_poet_1.code `message.${fieldName} = ${v}`);
1095
+ const fallback = types_1.isWithinOneOf(field) ? 'undefined' : types_1.defaultValue(ctx, field);
1096
+ chunks.push(ts_poet_1.code `message.${fieldName} = ${fallback};`);
1097
+ chunks.push(ts_poet_1.code `}`);
1089
1098
  }
1090
- chunks.push(ts_poet_1.code `}`);
1091
1099
  });
1092
1100
  // and then wrap up the switch/while/return
1093
1101
  chunks.push(ts_poet_1.code `return message;`);
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.83.2",
3
+ "version": "1.86.0",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",