ts-proto 1.138.0 → 1.139.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.
@@ -0,0 +1,318 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateUnwrapShallow = exports.generateWrapShallow = exports.generateUnwrapDeep = exports.generateWrapDeep = exports.isWrapperType = void 0;
4
+ const ts_poet_1 = require("ts-poet");
5
+ const types_1 = require("./types");
6
+ const options_1 = require("./options");
7
+ /** Whether we need to generate `.wrap` and `.unwrap` methods for the given type. */
8
+ function isWrapperType(fullProtoTypeName) {
9
+ return ((0, types_1.isStructTypeName)(fullProtoTypeName) ||
10
+ (0, types_1.isAnyValueTypeName)(fullProtoTypeName) ||
11
+ (0, types_1.isListValueTypeName)(fullProtoTypeName) ||
12
+ (0, types_1.isFieldMaskTypeName)(fullProtoTypeName));
13
+ }
14
+ exports.isWrapperType = isWrapperType;
15
+ /**
16
+ * Converts ts-proto's idiomatic Struct/Value/ListValue representation to the proto messages.
17
+ *
18
+ * We do this deeply b/c NestJS does not invoke wrappers recursively.
19
+ */
20
+ function generateWrapDeep(ctx, fullProtoTypeName, fieldNames) {
21
+ const chunks = [];
22
+ if ((0, types_1.isStructTypeName)(fullProtoTypeName)) {
23
+ let setStatement = "struct.fields[key] = Value.wrap(object[key]);";
24
+ if (ctx.options.useMapType) {
25
+ setStatement = "struct.fields.set(key, Value.wrap(object[key]));";
26
+ }
27
+ chunks.push((0, ts_poet_1.code) `wrap(object: {[key: string]: any} | undefined): Struct {
28
+ const struct = createBaseStruct();
29
+ if (object !== undefined) {
30
+ Object.keys(object).forEach(key => {
31
+ ${setStatement}
32
+ });
33
+ }
34
+ return struct;
35
+ }`);
36
+ }
37
+ if ((0, types_1.isAnyValueTypeName)(fullProtoTypeName)) {
38
+ // Turn ts-proto representation --> proto representation
39
+ chunks.push((0, ts_poet_1.code) `wrap(value: any): Value {
40
+ const result = {} as any;
41
+ if (value === null) {
42
+ result.${fieldNames.nullValue} = NullValue.NULL_VALUE;
43
+ } else if (typeof value === 'boolean') {
44
+ result.${fieldNames.boolValue} = value;
45
+ } else if (typeof value === 'number') {
46
+ result.${fieldNames.numberValue} = value;
47
+ } else if (typeof value === 'string') {
48
+ result.${fieldNames.stringValue} = value;
49
+ } else if (Array.isArray(value)) {
50
+ result.${fieldNames.listValue} = ListValue.wrap(value);
51
+ } else if (typeof value === 'object') {
52
+ result.${fieldNames.structValue} = Struct.wrap(value);
53
+ } else if (typeof value !== 'undefined') {
54
+ throw new Error('Unsupported any value type: ' + typeof value);
55
+ }
56
+ return result;
57
+ }`);
58
+ }
59
+ if ((0, types_1.isListValueTypeName)(fullProtoTypeName)) {
60
+ const maybeReadyOnly = ctx.options.useReadonlyTypes ? "Readonly" : "";
61
+ chunks.push((0, ts_poet_1.code) `wrap(array: ${maybeReadyOnly}Array<any> | undefined): ListValue {
62
+ const result = createBaseListValue()${maybeAsAny(ctx.options)};
63
+ result.values = (array ?? []).map(Value.wrap);
64
+ return result;
65
+ }`);
66
+ }
67
+ if ((0, types_1.isFieldMaskTypeName)(fullProtoTypeName)) {
68
+ chunks.push((0, ts_poet_1.code) `wrap(paths: ${maybeReadonly(ctx.options)} string[]): FieldMask {
69
+ const result = createBaseFieldMask()${maybeAsAny(ctx.options)};
70
+ result.paths = paths;
71
+ return result;
72
+ }`);
73
+ }
74
+ return chunks;
75
+ }
76
+ exports.generateWrapDeep = generateWrapDeep;
77
+ /**
78
+ * Converts proto's Struct/Value?listValue messages to ts-proto's idiomatic representation.
79
+ *
80
+ * We do this deeply b/c NestJS does not invoke wrappers recursively.
81
+ */
82
+ function generateUnwrapDeep(ctx, fullProtoTypeName, fieldNames) {
83
+ const chunks = [];
84
+ if ((0, types_1.isStructTypeName)(fullProtoTypeName)) {
85
+ if (ctx.options.useMapType) {
86
+ chunks.push((0, ts_poet_1.code) `unwrap(message: Struct): {[key: string]: any} {
87
+ const object: { [key: string]: any } = {};
88
+ [...message.fields.keys()].forEach((key) => {
89
+ object[key] = Value.unwrap(message.fields.get(key));
90
+ });
91
+ return object;
92
+ }`);
93
+ }
94
+ else {
95
+ chunks.push((0, ts_poet_1.code) `unwrap(message: Struct): {[key: string]: any} {
96
+ const object: { [key: string]: any } = {};
97
+ if (message.fields) {
98
+ Object.keys(message.fields).forEach(key => {
99
+ object[key] = Value.unwrap(message.fields[key]);
100
+ });
101
+ }
102
+ return object;
103
+ }`);
104
+ }
105
+ }
106
+ if ((0, types_1.isAnyValueTypeName)(fullProtoTypeName)) {
107
+ // We check hasOwnProperty because the incoming `message` has been serde-ing
108
+ // by the NestJS/protobufjs runtime, and so has a base class with default values
109
+ // that throw off the simpler checks we do in generateUnwrapShallow
110
+ chunks.push((0, ts_poet_1.code) `unwrap(message: any): string | number | boolean | Object | null | Array<any> | undefined {
111
+ if (message?.hasOwnProperty('${fieldNames.stringValue}') && message.${fieldNames.stringValue} !== undefined) {
112
+ return message.${fieldNames.stringValue};
113
+ } else if (message?.hasOwnProperty('${fieldNames.numberValue}') && message?.${fieldNames.numberValue} !== undefined) {
114
+ return message.${fieldNames.numberValue};
115
+ } else if (message?.hasOwnProperty('${fieldNames.boolValue}') && message?.${fieldNames.boolValue} !== undefined) {
116
+ return message.${fieldNames.boolValue};
117
+ } else if (message?.hasOwnProperty('${fieldNames.structValue}') && message?.${fieldNames.structValue} !== undefined) {
118
+ return Struct.unwrap(message.${fieldNames.structValue} as any);
119
+ } else if (message?.hasOwnProperty('${fieldNames.listValue}') && message?.${fieldNames.listValue} !== undefined) {
120
+ return ListValue.unwrap(message.${fieldNames.listValue});
121
+ } else if (message?.hasOwnProperty('${fieldNames.nullValue}') && message?.${fieldNames.nullValue} !== undefined) {
122
+ return null;
123
+ }
124
+ return undefined;
125
+ }`);
126
+ }
127
+ if ((0, types_1.isListValueTypeName)(fullProtoTypeName)) {
128
+ chunks.push((0, ts_poet_1.code) `unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "ListValue"}): Array<any> {
129
+ if (message?.hasOwnProperty('values') && Array.isArray(message.values)) {
130
+ return message.values.map(Value.unwrap);
131
+ } else {
132
+ return message as any;
133
+ }
134
+ }`);
135
+ }
136
+ if ((0, types_1.isFieldMaskTypeName)(fullProtoTypeName)) {
137
+ chunks.push((0, ts_poet_1.code) `unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "FieldMask"}): string[] {
138
+ return message.paths;
139
+ }`);
140
+ }
141
+ return chunks;
142
+ }
143
+ exports.generateUnwrapDeep = generateUnwrapDeep;
144
+ /**
145
+ * Converts ts-proto's idiomatic Struct/Value/ListValue representation to the proto messages.
146
+ *
147
+ * We do this shallow's b/c ts-proto's encode methods handle the recursion.
148
+ */
149
+ function generateWrapShallow(ctx, fullProtoTypeName, fieldNames) {
150
+ const chunks = [];
151
+ if ((0, types_1.isStructTypeName)(fullProtoTypeName)) {
152
+ let setStatement = "struct.fields[key] = object[key];";
153
+ if (ctx.options.useMapType) {
154
+ setStatement = "struct.fields.set(key, object[key]);";
155
+ }
156
+ chunks.push((0, ts_poet_1.code) `wrap(object: {[key: string]: any} | undefined): Struct {
157
+ const struct = createBaseStruct();
158
+ if (object !== undefined) {
159
+ Object.keys(object).forEach(key => {
160
+ ${setStatement}
161
+ });
162
+ }
163
+ return struct;
164
+ }`);
165
+ }
166
+ if ((0, types_1.isAnyValueTypeName)(fullProtoTypeName)) {
167
+ if (ctx.options.oneof === options_1.OneofOption.UNIONS) {
168
+ chunks.push((0, ts_poet_1.code) `wrap(value: any): Value {
169
+ const result = createBaseValue()${maybeAsAny(ctx.options)};
170
+ if (value === null) {
171
+ result.kind = {$case: '${fieldNames.nullValue}', ${fieldNames.nullValue}: NullValue.NULL_VALUE};
172
+ } else if (typeof value === 'boolean') {
173
+ result.kind = {$case: '${fieldNames.boolValue}', ${fieldNames.boolValue}: value};
174
+ } else if (typeof value === 'number') {
175
+ result.kind = {$case: '${fieldNames.numberValue}', ${fieldNames.numberValue}: value};
176
+ } else if (typeof value === 'string') {
177
+ result.kind = {$case: '${fieldNames.stringValue}', ${fieldNames.stringValue}: value};
178
+ } else if (Array.isArray(value)) {
179
+ result.kind = {$case: '${fieldNames.listValue}', ${fieldNames.listValue}: value};
180
+ } else if (typeof value === 'object') {
181
+ result.kind = {$case: '${fieldNames.structValue}', ${fieldNames.structValue}: value};
182
+ } else if (typeof value !== 'undefined') {
183
+ throw new Error('Unsupported any value type: ' + typeof value);
184
+ }
185
+ return result;
186
+ }`);
187
+ }
188
+ else {
189
+ chunks.push((0, ts_poet_1.code) `wrap(value: any): Value {
190
+ const result = createBaseValue()${maybeAsAny(ctx.options)};
191
+ if (value === null) {
192
+ result.${fieldNames.nullValue} = NullValue.NULL_VALUE;
193
+ } else if (typeof value === 'boolean') {
194
+ result.${fieldNames.boolValue} = value;
195
+ } else if (typeof value === 'number') {
196
+ result.${fieldNames.numberValue} = value;
197
+ } else if (typeof value === 'string') {
198
+ result.${fieldNames.stringValue} = value;
199
+ } else if (Array.isArray(value)) {
200
+ result.${fieldNames.listValue} = value;
201
+ } else if (typeof value === 'object') {
202
+ result.${fieldNames.structValue} = value;
203
+ } else if (typeof value !== 'undefined') {
204
+ throw new Error('Unsupported any value type: ' + typeof value);
205
+ }
206
+ return result;
207
+ }`);
208
+ }
209
+ }
210
+ if ((0, types_1.isListValueTypeName)(fullProtoTypeName)) {
211
+ const maybeReadyOnly = ctx.options.useReadonlyTypes ? "Readonly" : "";
212
+ chunks.push((0, ts_poet_1.code) `wrap(array: ${maybeReadyOnly}Array<any> | undefined): ListValue {
213
+ const result = createBaseListValue()${maybeAsAny(ctx.options)};
214
+ result.values = array ?? [];
215
+ return result;
216
+ }`);
217
+ }
218
+ if ((0, types_1.isFieldMaskTypeName)(fullProtoTypeName)) {
219
+ chunks.push((0, ts_poet_1.code) `wrap(paths: ${maybeReadonly(ctx.options)} string[]): FieldMask {
220
+ const result = createBaseFieldMask()${maybeAsAny(ctx.options)};
221
+ result.paths = paths;
222
+ return result;
223
+ }`);
224
+ }
225
+ return chunks;
226
+ }
227
+ exports.generateWrapShallow = generateWrapShallow;
228
+ /**
229
+ * Converts proto's Struct/Value?listValue messages to ts-proto's idiomatic representation.
230
+ *
231
+ * We do this shallowly b/c ts-proto's decode methods handle recursion.
232
+ */
233
+ function generateUnwrapShallow(ctx, fullProtoTypeName, fieldNames) {
234
+ const chunks = [];
235
+ if ((0, types_1.isStructTypeName)(fullProtoTypeName)) {
236
+ if (ctx.options.useMapType) {
237
+ chunks.push((0, ts_poet_1.code) `unwrap(message: Struct): {[key: string]: any} {
238
+ const object: { [key: string]: any } = {};
239
+ [...message.fields.keys()].forEach((key) => {
240
+ object[key] = message.fields.get(key);
241
+ });
242
+ return object;
243
+ }`);
244
+ }
245
+ else {
246
+ chunks.push((0, ts_poet_1.code) `unwrap(message: Struct): {[key: string]: any} {
247
+ const object: { [key: string]: any } = {};
248
+ if (message.fields) {
249
+ Object.keys(message.fields).forEach(key => {
250
+ object[key] = message.fields[key];
251
+ });
252
+ }
253
+ return object;
254
+ }`);
255
+ }
256
+ }
257
+ if ((0, types_1.isAnyValueTypeName)(fullProtoTypeName)) {
258
+ if (ctx.options.oneof === options_1.OneofOption.UNIONS) {
259
+ chunks.push((0, ts_poet_1.code) `unwrap(message: Value): string | number | boolean | Object | null | Array<any> | undefined {
260
+ if (message.kind?.$case === '${fieldNames.nullValue}') {
261
+ return null;
262
+ } else if (message.kind?.$case === '${fieldNames.numberValue}') {
263
+ return message.kind?.${fieldNames.numberValue};
264
+ } else if (message.kind?.$case === '${fieldNames.stringValue}') {
265
+ return message.kind?.${fieldNames.stringValue};
266
+ } else if (message.kind?.$case === '${fieldNames.boolValue}') {
267
+ return message.kind?.${fieldNames.boolValue};
268
+ } else if (message.kind?.$case === '${fieldNames.structValue}') {
269
+ return message.kind?.${fieldNames.structValue};
270
+ } else if (message.kind?.$case === '${fieldNames.listValue}') {
271
+ return message.kind?.${fieldNames.listValue};
272
+ } else {
273
+ return undefined;
274
+ }
275
+ }`);
276
+ }
277
+ else {
278
+ chunks.push((0, ts_poet_1.code) `unwrap(message: any): string | number | boolean | Object | null | Array<any> | undefined {
279
+ if (message.${fieldNames.stringValue} !== undefined) {
280
+ return message.${fieldNames.stringValue};
281
+ } else if (message?.${fieldNames.numberValue} !== undefined) {
282
+ return message.${fieldNames.numberValue};
283
+ } else if (message?.${fieldNames.boolValue} !== undefined) {
284
+ return message.${fieldNames.boolValue};
285
+ } else if (message?.${fieldNames.structValue} !== undefined) {
286
+ return message.${fieldNames.structValue} as any;
287
+ } else if (message?.${fieldNames.listValue} !== undefined) {
288
+ return message.${fieldNames.listValue};
289
+ } else if (message?.${fieldNames.nullValue} !== undefined) {
290
+ return null;
291
+ }
292
+ return undefined;
293
+ }`);
294
+ }
295
+ }
296
+ if ((0, types_1.isListValueTypeName)(fullProtoTypeName)) {
297
+ chunks.push((0, ts_poet_1.code) `unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "ListValue"}): Array<any> {
298
+ if (message?.hasOwnProperty('values') && Array.isArray(message.values)) {
299
+ return message.values;
300
+ } else {
301
+ return message as any;
302
+ }
303
+ }`);
304
+ }
305
+ if ((0, types_1.isFieldMaskTypeName)(fullProtoTypeName)) {
306
+ chunks.push((0, ts_poet_1.code) `unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "FieldMask"}): string[] {
307
+ return message.paths;
308
+ }`);
309
+ }
310
+ return chunks;
311
+ }
312
+ exports.generateUnwrapShallow = generateUnwrapShallow;
313
+ function maybeReadonly(options) {
314
+ return options.useReadonlyTypes ? "readonly " : "";
315
+ }
316
+ function maybeAsAny(options) {
317
+ return options.useReadonlyTypes ? " as any" : "";
318
+ }
package/build/main.js CHANGED
@@ -18,6 +18,7 @@ const ConditionalOutput_1 = require("ts-poet/build/ConditionalOutput");
18
18
  const generate_grpc_js_1 = require("./generate-grpc-js");
19
19
  const generate_generic_service_definition_1 = require("./generate-generic-service-definition");
20
20
  const generate_nice_grpc_1 = require("./generate-nice-grpc");
21
+ const generate_struct_wrappers_1 = require("./generate-struct-wrappers");
21
22
  function generateFile(ctx, fileDesc) {
22
23
  var _a;
23
24
  const { options, utils } = ctx;
@@ -72,11 +73,18 @@ function generateFile(ctx, fileDesc) {
72
73
  chunks.push(makeProtobufTimestampWrapper());
73
74
  }
74
75
  }
75
- if (options.outputEncodeMethods || options.outputJsonMethods || options.outputTypeRegistry) {
76
+ // We add `nestJs` here because enough though it doesn't use our encode/decode methods
77
+ // for most/vanilla messages, we do generate static wrap/unwrap methods for the special
78
+ // Struct/Value/wrapper types and use the `wrappers[...]` to have NestJS know about them.
79
+ if (options.outputEncodeMethods || options.outputJsonMethods || options.outputTypeRegistry || options.nestJs) {
76
80
  // then add the encoder/decoder/base instance
77
81
  (0, visit_1.visit)(fileDesc, sourceInfo, (fullName, message, _sInfo, fullProtoTypeName) => {
78
82
  const fullTypeName = (0, utils_1.maybePrefixPackage)(fileDesc, fullProtoTypeName);
79
- chunks.push(generateBaseInstanceFactory(ctx, fullName, message, fullTypeName));
83
+ const outputWrapAndUnwrap = (0, generate_struct_wrappers_1.isWrapperType)(fullTypeName);
84
+ // Only decode, fromPartial, and wrap use the createBase method
85
+ if (options.outputEncodeMethods || options.outputPartialMethods || outputWrapAndUnwrap) {
86
+ chunks.push(generateBaseInstanceFactory(ctx, fullName, message, fullTypeName));
87
+ }
80
88
  const staticMembers = [];
81
89
  if (options.outputTypeRegistry) {
82
90
  staticMembers.push((0, ts_poet_1.code) `$type: '${fullTypeName}' as const`);
@@ -104,13 +112,21 @@ function generateFile(ctx, fileDesc) {
104
112
  structValue: (0, case_1.maybeSnakeToCamel)("struct_value", ctx.options),
105
113
  listValue: (0, case_1.maybeSnakeToCamel)("list_value", ctx.options),
106
114
  };
107
- staticMembers.push(...generateWrap(ctx, fullTypeName, structFieldNames));
108
- staticMembers.push(...generateUnwrap(ctx, fullTypeName, structFieldNames));
109
- chunks.push((0, ts_poet_1.code) `
110
- export const ${(0, ts_poet_1.def)(fullName)} = {
111
- ${(0, ts_poet_1.joinCode)(staticMembers, { on: ",\n\n" })}
112
- };
113
- `);
115
+ if (options.nestJs) {
116
+ staticMembers.push(...(0, generate_struct_wrappers_1.generateWrapDeep)(ctx, fullTypeName, structFieldNames));
117
+ staticMembers.push(...(0, generate_struct_wrappers_1.generateUnwrapDeep)(ctx, fullTypeName, structFieldNames));
118
+ }
119
+ else {
120
+ staticMembers.push(...(0, generate_struct_wrappers_1.generateWrapShallow)(ctx, fullTypeName, structFieldNames));
121
+ staticMembers.push(...(0, generate_struct_wrappers_1.generateUnwrapShallow)(ctx, fullTypeName, structFieldNames));
122
+ }
123
+ if (staticMembers.length > 0) {
124
+ chunks.push((0, ts_poet_1.code) `
125
+ export const ${(0, ts_poet_1.def)(fullName)} = {
126
+ ${(0, ts_poet_1.joinCode)(staticMembers, { on: ",\n\n" })}
127
+ };
128
+ `);
129
+ }
114
130
  if (options.outputTypeRegistry) {
115
131
  const messageTypeRegistry = (0, utils_1.impFile)(options, "messageTypeRegistry@./typeRegistry");
116
132
  chunks.push((0, ts_poet_1.code) `
@@ -119,6 +135,11 @@ function generateFile(ctx, fileDesc) {
119
135
  }
120
136
  }, options);
121
137
  }
138
+ if (options.nestJs) {
139
+ if (fileDesc.messageType.find((message) => message.field.find(types_1.isStructType))) {
140
+ chunks.push(makeProtobufStructWrapper(options));
141
+ }
142
+ }
122
143
  let hasServerStreamingMethods = false;
123
144
  let hasStreamingMethods = false;
124
145
  (0, visit_1.visitServices)(fileDesc, sourceInfo, (serviceDesc, sInfo) => {
@@ -244,6 +265,15 @@ function makeProtobufTimestampWrapper() {
244
265
  },
245
266
  } as any;`;
246
267
  }
268
+ function makeProtobufStructWrapper(options) {
269
+ const wrappers = (0, ts_poet_1.imp)("wrappers@protobufjs");
270
+ const Struct = (0, utils_1.impProto)(options, "google/protobuf/struct", "Struct");
271
+ return (0, ts_poet_1.code) `
272
+ ${wrappers}['.google.protobuf.Struct'] = {
273
+ fromObject: ${Struct}.wrap,
274
+ toObject: ${Struct}.unwrap,
275
+ } as any;`;
276
+ }
247
277
  function makeLongUtils(options, bytes) {
248
278
  // Regardless of which `forceLong` config option we're using, we always use
249
279
  // the `long` library to either represent or at least sanity-check 64-bit values
@@ -771,6 +801,53 @@ function generateDecode(ctx, fullName, messageDesc) {
771
801
  chunks.push((0, ts_poet_1.code) `}`);
772
802
  return (0, ts_poet_1.joinCode)(chunks, { on: "\n" });
773
803
  }
804
+ /** Returns a generic writer.doSomething based on the basic type */
805
+ function getEncodeWriteSnippet(ctx, field) {
806
+ const { options, utils } = ctx;
807
+ if ((0, types_1.isEnum)(field) && options.stringEnums) {
808
+ const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
809
+ const toNumber = (0, types_1.getEnumMethod)(ctx, field.typeName, "ToNumber");
810
+ return (place) => (0, ts_poet_1.code) `writer.uint32(${tag}).${(0, types_1.toReaderCall)(field)}(${toNumber}(${place}))`;
811
+ }
812
+ else if ((0, types_1.isLong)(field) && options.forceLong === options_1.LongOption.BIGINT) {
813
+ const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
814
+ return (place) => (0, ts_poet_1.code) `writer.uint32(${tag}).${(0, types_1.toReaderCall)(field)}(${place}.toString())`;
815
+ }
816
+ else if ((0, types_1.isScalar)(field) || (0, types_1.isEnum)(field)) {
817
+ const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
818
+ return (place) => (0, ts_poet_1.code) `writer.uint32(${tag}).${(0, types_1.toReaderCall)(field)}(${place})`;
819
+ }
820
+ else if ((0, types_1.isObjectId)(field) && options.useMongoObjectId) {
821
+ const tag = ((field.number << 3) | 2) >>> 0;
822
+ const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
823
+ return (place) => (0, ts_poet_1.code) `${type}.encode(${utils.toProtoObjectId}(${place}), writer.uint32(${tag}).fork()).ldelim()`;
824
+ }
825
+ else if ((0, types_1.isTimestamp)(field) && (options.useDate === options_1.DateOption.DATE || options.useDate === options_1.DateOption.STRING)) {
826
+ const tag = ((field.number << 3) | 2) >>> 0;
827
+ const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
828
+ return (place) => (0, ts_poet_1.code) `${type}.encode(${utils.toTimestamp}(${place}), writer.uint32(${tag}).fork()).ldelim()`;
829
+ }
830
+ else if ((0, types_1.isValueType)(ctx, field)) {
831
+ const maybeTypeField = options.outputTypeRegistry ? `$type: '${field.typeName.slice(1)}',` : "";
832
+ const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
833
+ const wrappedValue = (place) => {
834
+ if ((0, types_1.isAnyValueType)(field) || (0, types_1.isListValueType)(field) || (0, types_1.isStructType)(field) || (0, types_1.isFieldMaskType)(field)) {
835
+ return (0, ts_poet_1.code) `${type}.wrap(${place})`;
836
+ }
837
+ return (0, ts_poet_1.code) `{${maybeTypeField} value: ${place}!}`;
838
+ };
839
+ const tag = ((field.number << 3) | 2) >>> 0;
840
+ return (place) => (0, ts_poet_1.code) `${type}.encode(${wrappedValue(place)}, writer.uint32(${tag}).fork()).ldelim()`;
841
+ }
842
+ else if ((0, types_1.isMessage)(field)) {
843
+ const tag = ((field.number << 3) | 2) >>> 0;
844
+ const type = (0, types_1.basicTypeName)(ctx, field);
845
+ return (place) => (0, ts_poet_1.code) `${type}.encode(${place}, writer.uint32(${tag}).fork()).ldelim()`;
846
+ }
847
+ else {
848
+ throw new Error(`Unhandled field ${field}`);
849
+ }
850
+ }
774
851
  /** Creates a function to encode a message by loop overing the tags. */
775
852
  function generateEncode(ctx, fullName, messageDesc) {
776
853
  const { options, utils, typeMap } = ctx;
@@ -783,54 +860,15 @@ function generateEncode(ctx, fullName, messageDesc) {
783
860
  writer: ${Writer} = ${Writer}.create(),
784
861
  ): ${Writer} {
785
862
  `);
863
+ const processedOneofs = new Set();
864
+ const oneOfFieldsDict = messageDesc.field
865
+ .filter((field) => (0, types_1.isWithinOneOfThatShouldBeUnion)(options, field))
866
+ .reduce((result, field) => ((result[field.oneofIndex] || (result[field.oneofIndex] = [])).push(field), result), {});
786
867
  // then add a case for each field
787
868
  messageDesc.field.forEach((field) => {
788
869
  const fieldName = (0, case_1.maybeSnakeToCamel)(field.name, options);
789
870
  // get a generic writer.doSomething based on the basic type
790
- let writeSnippet;
791
- if ((0, types_1.isEnum)(field) && options.stringEnums) {
792
- const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
793
- const toNumber = (0, types_1.getEnumMethod)(ctx, field.typeName, "ToNumber");
794
- writeSnippet = (place) => (0, ts_poet_1.code) `writer.uint32(${tag}).${(0, types_1.toReaderCall)(field)}(${toNumber}(${place}))`;
795
- }
796
- else if ((0, types_1.isLong)(field) && options.forceLong === options_1.LongOption.BIGINT) {
797
- const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
798
- writeSnippet = (place) => (0, ts_poet_1.code) `writer.uint32(${tag}).${(0, types_1.toReaderCall)(field)}(${place}.toString())`;
799
- }
800
- else if ((0, types_1.isScalar)(field) || (0, types_1.isEnum)(field)) {
801
- const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
802
- writeSnippet = (place) => (0, ts_poet_1.code) `writer.uint32(${tag}).${(0, types_1.toReaderCall)(field)}(${place})`;
803
- }
804
- else if ((0, types_1.isObjectId)(field) && options.useMongoObjectId) {
805
- const tag = ((field.number << 3) | 2) >>> 0;
806
- const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
807
- writeSnippet = (place) => (0, ts_poet_1.code) `${type}.encode(${utils.toProtoObjectId}(${place}), writer.uint32(${tag}).fork()).ldelim()`;
808
- }
809
- else if ((0, types_1.isTimestamp)(field) && (options.useDate === options_1.DateOption.DATE || options.useDate === options_1.DateOption.STRING)) {
810
- const tag = ((field.number << 3) | 2) >>> 0;
811
- const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
812
- writeSnippet = (place) => (0, ts_poet_1.code) `${type}.encode(${utils.toTimestamp}(${place}), writer.uint32(${tag}).fork()).ldelim()`;
813
- }
814
- else if ((0, types_1.isValueType)(ctx, field)) {
815
- const maybeTypeField = options.outputTypeRegistry ? `$type: '${field.typeName.slice(1)}',` : "";
816
- const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
817
- const wrappedValue = (place) => {
818
- if ((0, types_1.isAnyValueType)(field) || (0, types_1.isListValueType)(field) || (0, types_1.isStructType)(field) || (0, types_1.isFieldMaskType)(field)) {
819
- return (0, ts_poet_1.code) `${type}.wrap(${place})`;
820
- }
821
- return (0, ts_poet_1.code) `{${maybeTypeField} value: ${place}!}`;
822
- };
823
- const tag = ((field.number << 3) | 2) >>> 0;
824
- writeSnippet = (place) => (0, ts_poet_1.code) `${type}.encode(${wrappedValue(place)}, writer.uint32(${tag}).fork()).ldelim()`;
825
- }
826
- else if ((0, types_1.isMessage)(field)) {
827
- const tag = ((field.number << 3) | 2) >>> 0;
828
- const type = (0, types_1.basicTypeName)(ctx, field);
829
- writeSnippet = (place) => (0, ts_poet_1.code) `${type}.encode(${place}, writer.uint32(${tag}).fork()).ldelim()`;
830
- }
831
- else {
832
- throw new Error(`Unhandled field ${field}`);
833
- }
871
+ const writeSnippet = getEncodeWriteSnippet(ctx, field);
834
872
  const isOptional = (0, types_1.isOptionalProperty)(field, messageDesc.options, options);
835
873
  if ((0, types_1.isRepeated)(field)) {
836
874
  if ((0, types_1.isMapType)(ctx, messageDesc, field)) {
@@ -925,12 +963,19 @@ function generateEncode(ctx, fullName, messageDesc) {
925
963
  }
926
964
  }
927
965
  else if ((0, types_1.isWithinOneOfThatShouldBeUnion)(options, field)) {
928
- let oneofName = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options);
929
- chunks.push((0, ts_poet_1.code) `
930
- if (message.${oneofName}?.$case === '${fieldName}') {
931
- ${writeSnippet(`message.${oneofName}.${fieldName}`)};
932
- }
933
- `);
966
+ if (!processedOneofs.has(field.oneofIndex)) {
967
+ processedOneofs.add(field.oneofIndex);
968
+ const oneofName = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options);
969
+ chunks.push((0, ts_poet_1.code) `switch (message.${oneofName}?.$case) {`);
970
+ for (const oneOfField of oneOfFieldsDict[field.oneofIndex]) {
971
+ const writeSnippet = getEncodeWriteSnippet(ctx, oneOfField);
972
+ const oneOfFieldName = (0, case_1.maybeSnakeToCamel)(oneOfField.name, ctx.options);
973
+ chunks.push((0, ts_poet_1.code) `case "${oneOfFieldName}":
974
+ ${writeSnippet(`message.${oneofName}.${oneOfFieldName}`)};
975
+ break;`);
976
+ }
977
+ chunks.push((0, ts_poet_1.code) `}`);
978
+ }
934
979
  }
935
980
  else if ((0, types_1.isWithinOneOf)(field)) {
936
981
  // Oneofs don't have a default value check b/c they need to denote which-oneof presence
@@ -1553,164 +1598,6 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1553
1598
  chunks.push((0, ts_poet_1.code) `}`);
1554
1599
  return (0, ts_poet_1.joinCode)(chunks, { on: "\n" });
1555
1600
  }
1556
- function generateWrap(ctx, fullProtoTypeName, fieldNames) {
1557
- const chunks = [];
1558
- if ((0, types_1.isStructTypeName)(fullProtoTypeName)) {
1559
- let setStatement = "struct.fields[key] = object[key];";
1560
- if (ctx.options.useMapType) {
1561
- setStatement = "struct.fields.set(key, object[key]);";
1562
- }
1563
- chunks.push((0, ts_poet_1.code) `wrap(object: {[key: string]: any} | undefined): Struct {
1564
- const struct = createBaseStruct();
1565
- if (object !== undefined) {
1566
- Object.keys(object).forEach(key => {
1567
- ${setStatement}
1568
- });
1569
- }
1570
- return struct;
1571
- }`);
1572
- }
1573
- if ((0, types_1.isAnyValueTypeName)(fullProtoTypeName)) {
1574
- if (ctx.options.oneof === options_1.OneofOption.UNIONS) {
1575
- chunks.push((0, ts_poet_1.code) `wrap(value: any): Value {
1576
- const result = createBaseValue()${maybeAsAny(ctx.options)};
1577
-
1578
- if (value === null) {
1579
- result.kind = {$case: '${fieldNames.nullValue}', ${fieldNames.nullValue}: NullValue.NULL_VALUE};
1580
- } else if (typeof value === 'boolean') {
1581
- result.kind = {$case: '${fieldNames.boolValue}', ${fieldNames.boolValue}: value};
1582
- } else if (typeof value === 'number') {
1583
- result.kind = {$case: '${fieldNames.numberValue}', ${fieldNames.numberValue}: value};
1584
- } else if (typeof value === 'string') {
1585
- result.kind = {$case: '${fieldNames.stringValue}', ${fieldNames.stringValue}: value};
1586
- } else if (Array.isArray(value)) {
1587
- result.kind = {$case: '${fieldNames.listValue}', ${fieldNames.listValue}: value};
1588
- } else if (typeof value === 'object') {
1589
- result.kind = {$case: '${fieldNames.structValue}', ${fieldNames.structValue}: value};
1590
- } else if (typeof value !== 'undefined') {
1591
- throw new Error('Unsupported any value type: ' + typeof value);
1592
- }
1593
-
1594
- return result;
1595
- }`);
1596
- }
1597
- else {
1598
- chunks.push((0, ts_poet_1.code) `wrap(value: any): Value {
1599
- const result = createBaseValue()${maybeAsAny(ctx.options)};
1600
-
1601
- if (value === null) {
1602
- result.${fieldNames.nullValue} = NullValue.NULL_VALUE;
1603
- } else if (typeof value === 'boolean') {
1604
- result.${fieldNames.boolValue} = value;
1605
- } else if (typeof value === 'number') {
1606
- result.${fieldNames.numberValue} = value;
1607
- } else if (typeof value === 'string') {
1608
- result.${fieldNames.stringValue} = value;
1609
- } else if (Array.isArray(value)) {
1610
- result.${fieldNames.listValue} = value;
1611
- } else if (typeof value === 'object') {
1612
- result.${fieldNames.structValue} = value;
1613
- } else if (typeof value !== 'undefined') {
1614
- throw new Error('Unsupported any value type: ' + typeof value);
1615
- }
1616
-
1617
- return result;
1618
- }`);
1619
- }
1620
- }
1621
- if ((0, types_1.isListValueTypeName)(fullProtoTypeName)) {
1622
- chunks.push((0, ts_poet_1.code) `wrap(value: ${ctx.options.useReadonlyTypes ? "ReadonlyArray<any>" : "Array<any>"} | undefined): ListValue {
1623
- const result = createBaseListValue()${maybeAsAny(ctx.options)};
1624
-
1625
- result.values = value ?? [];
1626
-
1627
- return result;
1628
- }`);
1629
- }
1630
- if ((0, types_1.isFieldMaskTypeName)(fullProtoTypeName)) {
1631
- chunks.push((0, ts_poet_1.code) `wrap(paths: ${maybeReadonly(ctx.options)} string[]): FieldMask {
1632
- const result = createBaseFieldMask()${maybeAsAny(ctx.options)};
1633
-
1634
- result.paths = paths;
1635
-
1636
- return result;
1637
- }`);
1638
- }
1639
- return chunks;
1640
- }
1641
- function generateUnwrap(ctx, fullProtoTypeName, fieldNames) {
1642
- const chunks = [];
1643
- if ((0, types_1.isStructTypeName)(fullProtoTypeName)) {
1644
- if (ctx.options.useMapType) {
1645
- chunks.push((0, ts_poet_1.code) `unwrap(message: Struct): {[key: string]: any} {
1646
- const object: { [key: string]: any } = {};
1647
- [...message.fields.keys()].forEach((key) => {
1648
- object[key] = message.fields.get(key);
1649
- });
1650
- return object;
1651
- }`);
1652
- }
1653
- else {
1654
- chunks.push((0, ts_poet_1.code) `unwrap(message: Struct): {[key: string]: any} {
1655
- const object: { [key: string]: any } = {};
1656
- Object.keys(message.fields).forEach(key => {
1657
- object[key] = message.fields[key];
1658
- });
1659
- return object;
1660
- }`);
1661
- }
1662
- }
1663
- if ((0, types_1.isAnyValueTypeName)(fullProtoTypeName)) {
1664
- if (ctx.options.oneof === options_1.OneofOption.UNIONS) {
1665
- chunks.push((0, ts_poet_1.code) `unwrap(message: Value): string | number | boolean | Object | null | Array<any> | undefined {
1666
- if (message.kind?.$case === '${fieldNames.nullValue}') {
1667
- return null;
1668
- } else if (message.kind?.$case === '${fieldNames.numberValue}') {
1669
- return message.kind?.${fieldNames.numberValue};
1670
- } else if (message.kind?.$case === '${fieldNames.stringValue}') {
1671
- return message.kind?.${fieldNames.stringValue};
1672
- } else if (message.kind?.$case === '${fieldNames.boolValue}') {
1673
- return message.kind?.${fieldNames.boolValue};
1674
- } else if (message.kind?.$case === '${fieldNames.structValue}') {
1675
- return message.kind?.${fieldNames.structValue};
1676
- } else if (message.kind?.$case === '${fieldNames.listValue}') {
1677
- return message.kind?.${fieldNames.listValue};
1678
- } else {
1679
- return undefined;
1680
- }
1681
- }`);
1682
- }
1683
- else {
1684
- chunks.push((0, ts_poet_1.code) `unwrap(message: Value): string | number | boolean | Object | null | Array<any> | undefined {
1685
- if (message?.${fieldNames.stringValue} !== undefined) {
1686
- return message.${fieldNames.stringValue};
1687
- } else if (message?.${fieldNames.numberValue} !== undefined) {
1688
- return message.${fieldNames.numberValue};
1689
- } else if (message?.${fieldNames.boolValue} !== undefined) {
1690
- return message.${fieldNames.boolValue};
1691
- } else if (message?.${fieldNames.structValue} !== undefined) {
1692
- return message.${fieldNames.structValue};
1693
- } else if (message?.${fieldNames.listValue} !== undefined) {
1694
- return message.${fieldNames.listValue};
1695
- } else if (message?.${fieldNames.nullValue} !== undefined) {
1696
- return null;
1697
- }
1698
- return undefined;
1699
- }`);
1700
- }
1701
- }
1702
- if ((0, types_1.isListValueTypeName)(fullProtoTypeName)) {
1703
- chunks.push((0, ts_poet_1.code) `unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "ListValue"}): Array<any> {
1704
- return message.values;
1705
- }`);
1706
- }
1707
- if ((0, types_1.isFieldMaskTypeName)(fullProtoTypeName)) {
1708
- chunks.push((0, ts_poet_1.code) `unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "FieldMask"}): string[] {
1709
- return message.paths;
1710
- }`);
1711
- }
1712
- return chunks;
1713
- }
1714
1601
  exports.contextTypeVar = "Context extends DataLoaders";
1715
1602
  function maybeCastToNumber(ctx, messageDesc, field, variableName) {
1716
1603
  const { keyType } = (0, types_1.detectMapType)(ctx, messageDesc, field);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "1.138.0",
3
+ "version": "1.139.0",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",
@@ -28,8 +28,8 @@
28
28
  "author": "",
29
29
  "license": "ISC",
30
30
  "devDependencies": {
31
- "@grpc/grpc-js": "^1.2.12",
32
- "@grpc/proto-loader": "^0.5.6",
31
+ "@grpc/grpc-js": "^1.3.0",
32
+ "@grpc/proto-loader": "^0.6.0",
33
33
  "@improbable-eng/grpc-web": "^0.14.0",
34
34
  "@improbable-eng/grpc-web-node-http-transport": "^0.14.0",
35
35
  "@nestjs/common": "^8.2.2",