ts-proto 1.138.0 → 1.140.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
@@ -352,6 +352,10 @@ Generated code will be placed in the Gradle build directory.
352
352
 
353
353
  - With `--ts_proto_opt=unrecognizedEnum=false` enums will not contain an `UNRECOGNIZED` key with value of -1.
354
354
 
355
+ - With `--ts_proto_opt=removeEnumPrefix=true` generated enums will have the enum name removed from members.
356
+
357
+ `FooBar.FOO_BAR_BAZ = "FOO_BAR_BAZ"` will generate `FooBar.BAZ = "FOO_BAR_BAZ"`
358
+
355
359
  - With `--ts_proto_opt=lowerCaseServiceMethods=true`, the method names of service methods will be lowered/camel-case, i.e. `service.findFoo` instead of `service.FindFoo`.
356
360
 
357
361
  - With `--ts_proto_opt=snakeToCamel=false`, fields will be kept snake case in both the message keys and the `toJSON` / `fromJSON` methods.
@@ -400,7 +404,7 @@ Generated code will be placed in the Gradle build directory.
400
404
 
401
405
  - 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.
402
406
 
403
- Specifically `outputEncodeMethods`, `outputJsonMethods`, and `outputClientImpl` will all be false, and `lowerCaseServiceMethods` will be true.
407
+ Specifically `outputEncodeMethods`, `outputJsonMethods`, and `outputClientImpl` will all be false, `lowerCaseServiceMethods` will be true and `outputServices` will be ignored.
404
408
 
405
409
  Note that `addGrpcMetadata`, `addNestjsRestParameter` and `returnObservable` will still be false.
406
410
 
package/build/enums.js CHANGED
@@ -23,8 +23,10 @@ function generateEnum(ctx, fullName, enumDesc, sourceInfo) {
23
23
  enumDesc.value.forEach((valueDesc, index) => {
24
24
  var _a;
25
25
  const info = sourceInfo.lookup(sourceInfo_1.Fields.enum.value, index);
26
- (0, utils_1.maybeAddComment)(info, chunks, (_a = valueDesc.options) === null || _a === void 0 ? void 0 : _a.deprecated, `${valueDesc.name} - `);
27
- chunks.push((0, ts_poet_1.code) `${valueDesc.name} ${delimiter} ${options.stringEnums ? `"${valueDesc.name}"` : valueDesc.number.toString()},`);
26
+ const valueName = getValueName(ctx, fullName, valueDesc);
27
+ const memberName = getMemberName(ctx, fullName, valueDesc);
28
+ (0, utils_1.maybeAddComment)(info, chunks, (_a = valueDesc.options) === null || _a === void 0 ? void 0 : _a.deprecated, `${memberName} - `);
29
+ chunks.push((0, ts_poet_1.code) `${memberName} ${delimiter} ${options.stringEnums ? `"${valueName}"` : valueDesc.number.toString()},`);
28
30
  });
29
31
  if (options.unrecognizedEnum)
30
32
  chunks.push((0, ts_poet_1.code) `
@@ -60,10 +62,12 @@ function generateEnumFromJson(ctx, fullName, enumDesc) {
60
62
  chunks.push((0, ts_poet_1.code) `export function ${(0, ts_poet_1.def)(functionName)}(object: any): ${fullName} {`);
61
63
  chunks.push((0, ts_poet_1.code) `switch (object) {`);
62
64
  for (const valueDesc of enumDesc.value) {
65
+ const memberName = getMemberName(ctx, fullName, valueDesc);
66
+ const valueName = getValueName(ctx, fullName, valueDesc);
63
67
  chunks.push((0, ts_poet_1.code) `
64
68
  case ${valueDesc.number}:
65
- case "${valueDesc.name}":
66
- return ${fullName}.${valueDesc.name};
69
+ case "${valueName}":
70
+ return ${fullName}.${memberName};
67
71
  `);
68
72
  }
69
73
  if (options.unrecognizedEnum) {
@@ -95,10 +99,13 @@ function generateEnumToJson(ctx, fullName, enumDesc) {
95
99
  chunks.push((0, ts_poet_1.code) `switch (object) {`);
96
100
  for (const valueDesc of enumDesc.value) {
97
101
  if (ctx.options.useNumericEnumForJson) {
98
- chunks.push((0, ts_poet_1.code) `case ${fullName}.${valueDesc.name}: return ${valueDesc.number};`);
102
+ const memberName = getMemberName(ctx, fullName, valueDesc);
103
+ chunks.push((0, ts_poet_1.code) `case ${fullName}.${memberName}: return ${valueDesc.number};`);
99
104
  }
100
105
  else {
101
- chunks.push((0, ts_poet_1.code) `case ${fullName}.${valueDesc.name}: return "${valueDesc.name}";`);
106
+ const memberName = getMemberName(ctx, fullName, valueDesc);
107
+ const valueName = getValueName(ctx, fullName, valueDesc);
108
+ chunks.push((0, ts_poet_1.code) `case ${fullName}.${memberName}: return "${valueName}";`);
102
109
  }
103
110
  }
104
111
  if (options.unrecognizedEnum) {
@@ -137,7 +144,7 @@ function generateEnumToNumber(ctx, fullName, enumDesc) {
137
144
  chunks.push((0, ts_poet_1.code) `export function ${(0, ts_poet_1.def)(functionName)}(object: ${fullName}): number {`);
138
145
  chunks.push((0, ts_poet_1.code) `switch (object) {`);
139
146
  for (const valueDesc of enumDesc.value) {
140
- chunks.push((0, ts_poet_1.code) `case ${fullName}.${valueDesc.name}: return ${valueDesc.number};`);
147
+ chunks.push((0, ts_poet_1.code) `case ${fullName}.${getMemberName(ctx, fullName, valueDesc)}: return ${valueDesc.number};`);
141
148
  }
142
149
  if (options.unrecognizedEnum) {
143
150
  chunks.push((0, ts_poet_1.code) `
@@ -158,3 +165,12 @@ function generateEnumToNumber(ctx, fullName, enumDesc) {
158
165
  return (0, ts_poet_1.joinCode)(chunks, { on: "\n" });
159
166
  }
160
167
  exports.generateEnumToNumber = generateEnumToNumber;
168
+ function getMemberName(ctx, fullName, valueDesc) {
169
+ if (ctx.options.removeEnumPrefix) {
170
+ return valueDesc.name.replace(`${(0, case_1.camelToSnake)(fullName)}_`, "");
171
+ }
172
+ return valueDesc.name;
173
+ }
174
+ function getValueName(ctx, fullName, valueDesc) {
175
+ return valueDesc.name;
176
+ }
@@ -39,6 +39,8 @@ exports.generateGrpcClientImpl = generateGrpcClientImpl;
39
39
  /** Creates the RPC methods that client code actually calls. */
40
40
  function generateRpcMethod(ctx, serviceDesc, methodDesc) {
41
41
  (0, utils_1.assertInstanceOf)(methodDesc, utils_1.FormattedMethodDescriptor);
42
+ const { options } = ctx;
43
+ const { useAbortSignal } = options;
42
44
  const requestMessage = (0, types_1.rawRequestType)(ctx, methodDesc);
43
45
  const inputType = (0, types_1.requestType)(ctx, methodDesc, true);
44
46
  const returns = (0, types_1.responsePromiseOrObservable)(ctx, methodDesc);
@@ -47,6 +49,7 @@ function generateRpcMethod(ctx, serviceDesc, methodDesc) {
47
49
  ${methodDesc.formattedName}(
48
50
  request: ${inputType},
49
51
  metadata?: grpc.Metadata,
52
+ ${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
50
53
  ): ${returns} {
51
54
  throw new Error('ts-proto does not yet support client streaming!');
52
55
  }
@@ -57,10 +60,12 @@ function generateRpcMethod(ctx, serviceDesc, methodDesc) {
57
60
  ${methodDesc.formattedName}(
58
61
  request: ${inputType},
59
62
  metadata?: grpc.Metadata,
63
+ ${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
60
64
  ): ${returns} {
61
65
  return this.rpc.${method}(
62
66
  ${methodDescName(serviceDesc, methodDesc)},
63
67
  ${requestMessage}.fromPartial(request),
68
+ ${useAbortSignal ? "abortSignal," : ""}
64
69
  metadata,
65
70
  );
66
71
  }
@@ -145,6 +150,8 @@ exports.addGrpcWebMisc = addGrpcWebMisc;
145
150
  /** Makes an `Rpc` interface to decouple from the low-level grpc-web `grpc.invoke and grpc.unary`/etc. methods. */
146
151
  function generateGrpcWebRpcType(ctx, returnObservable, hasStreamingMethods) {
147
152
  const chunks = [];
153
+ const { options } = ctx;
154
+ const { useAbortSignal } = options;
148
155
  chunks.push((0, ts_poet_1.code) `interface Rpc {`);
149
156
  const wrapper = returnObservable ? (0, types_1.observableType)(ctx) : "Promise";
150
157
  chunks.push((0, ts_poet_1.code) `
@@ -152,6 +159,7 @@ function generateGrpcWebRpcType(ctx, returnObservable, hasStreamingMethods) {
152
159
  methodDesc: T,
153
160
  request: any,
154
161
  metadata: grpc.Metadata | undefined,
162
+ ${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
155
163
  ): ${wrapper}<any>;
156
164
  `);
157
165
  if (hasStreamingMethods) {
@@ -160,6 +168,7 @@ function generateGrpcWebRpcType(ctx, returnObservable, hasStreamingMethods) {
160
168
  methodDesc: T,
161
169
  request: any,
162
170
  metadata: grpc.Metadata | undefined,
171
+ ${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
163
172
  ): ${(0, types_1.observableType)(ctx)}<any>;
164
173
  `);
165
174
  }
@@ -201,11 +210,23 @@ function generateGrpcWebImpl(ctx, returnObservable, hasStreamingMethods) {
201
210
  return (0, ts_poet_1.joinCode)(chunks, { trim: false });
202
211
  }
203
212
  function createPromiseUnaryMethod(ctx) {
213
+ const { options } = ctx;
214
+ const { useAbortSignal } = options;
215
+ const maybeAbortSignal = useAbortSignal
216
+ ? `
217
+ const abortHandler = () => {
218
+ client.close();
219
+ reject(new Error("Aborted"));
220
+ }
221
+
222
+ if (abortSignal) abortSignal.addEventListener("abort", abortHandler);`
223
+ : "";
204
224
  return (0, ts_poet_1.code) `
205
225
  unary<T extends UnaryMethodDefinitionish>(
206
226
  methodDesc: T,
207
227
  _request: any,
208
- metadata: grpc.Metadata | undefined
228
+ metadata: grpc.Metadata | undefined,
229
+ ${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
209
230
  ): Promise<any> {
210
231
  const request = { ..._request, ...methodDesc.requestType };
211
232
  const maybeCombinedMetadata =
@@ -213,7 +234,7 @@ function createPromiseUnaryMethod(ctx) {
213
234
  ? new ${BrowserHeaders}({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
214
235
  : metadata || this.options.metadata;
215
236
  return new Promise((resolve, reject) => {
216
- ${grpc}.unary(methodDesc, {
237
+ ${useAbortSignal ? `const client =` : ""} ${grpc}.unary(methodDesc, {
217
238
  request,
218
239
  host: this.host,
219
240
  metadata: maybeCombinedMetadata,
@@ -228,16 +249,29 @@ function createPromiseUnaryMethod(ctx) {
228
249
  }
229
250
  },
230
251
  });
252
+
253
+ ${maybeAbortSignal}
231
254
  });
232
255
  }
233
256
  `;
234
257
  }
235
258
  function createObservableUnaryMethod(ctx) {
259
+ const { options } = ctx;
260
+ const { useAbortSignal } = options;
261
+ const maybeAbortSignal = useAbortSignal
262
+ ? `
263
+ const abortHandler = () => {
264
+ observer.error("Aborted");
265
+ client.close();
266
+ };
267
+ if (abortSignal) abortSignal.addEventListener("abort", abortHandler);`
268
+ : "";
236
269
  return (0, ts_poet_1.code) `
237
270
  unary<T extends UnaryMethodDefinitionish>(
238
271
  methodDesc: T,
239
272
  _request: any,
240
- metadata: grpc.Metadata | undefined
273
+ metadata: grpc.Metadata | undefined,
274
+ ${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
241
275
  ): ${(0, types_1.observableType)(ctx)}<any> {
242
276
  const request = { ..._request, ...methodDesc.requestType };
243
277
  const maybeCombinedMetadata =
@@ -245,7 +279,7 @@ function createObservableUnaryMethod(ctx) {
245
279
  ? new ${BrowserHeaders}({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
246
280
  : metadata || this.options.metadata;
247
281
  return new Observable(observer => {
248
- ${grpc}.unary(methodDesc, {
282
+ ${useAbortSignal ? `const client =` : ""} ${grpc}.unary(methodDesc, {
249
283
  request,
250
284
  host: this.host,
251
285
  metadata: maybeCombinedMetadata,
@@ -261,16 +295,31 @@ function createObservableUnaryMethod(ctx) {
261
295
  }
262
296
  },
263
297
  });
298
+
299
+
300
+ ${maybeAbortSignal}
301
+
264
302
  }).pipe(${take}(1));
265
303
  }
266
304
  `;
267
305
  }
268
306
  function createInvokeMethod(ctx) {
307
+ const { options } = ctx;
308
+ const { useAbortSignal } = options;
309
+ const maybeAbortSignal = useAbortSignal
310
+ ? `
311
+ const abortHandler = () => {
312
+ observer.error("Aborted");
313
+ client.close();
314
+ };
315
+ if (abortSignal) abortSignal.addEventListener("abort", abortHandler);`
316
+ : "";
269
317
  return (0, ts_poet_1.code) `
270
318
  invoke<T extends UnaryMethodDefinitionish>(
271
319
  methodDesc: T,
272
320
  _request: any,
273
- metadata: grpc.Metadata | undefined
321
+ metadata: grpc.Metadata | undefined,
322
+ ${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
274
323
  ): ${(0, types_1.observableType)(ctx)}<any> {
275
324
  const upStreamCodes = this.options.upStreamRetryCodes || [];
276
325
  const DEFAULT_TIMEOUT_TIME: number = 3_000;
@@ -301,7 +350,11 @@ function createInvokeMethod(ctx) {
301
350
  }
302
351
  },
303
352
  });
304
- observer.add(() => client.close());
353
+ observer.add(() => {
354
+ if (!observer.closed) return client.close()
355
+ });
356
+
357
+ ${maybeAbortSignal}
305
358
  });
306
359
  upStream();
307
360
  }).pipe(${share}());
@@ -39,9 +39,6 @@ function generateService(ctx, fileDesc, sourceInfo, serviceDesc) {
39
39
  const partialInput = options.outputClientImpl === "grpc-web";
40
40
  const inputType = (0, types_1.requestType)(ctx, methodDesc, partialInput);
41
41
  params.push((0, ts_poet_1.code) `request: ${inputType}`);
42
- if (options.useAbortSignal) {
43
- params.push((0, ts_poet_1.code) `abortSignal?: AbortSignal`);
44
- }
45
42
  // Use metadata as last argument for interface only configuration
46
43
  if (options.outputClientImpl === "grpc-web") {
47
44
  // We have to use grpc.Metadata where grpc will come from @improbable-eng
@@ -56,6 +53,9 @@ function generateService(ctx, fileDesc, sourceInfo, serviceDesc) {
56
53
  const Metadata = (0, ts_poet_1.imp)(options.metadataType);
57
54
  params.push((0, ts_poet_1.code) `metadata?: ${Metadata}`);
58
55
  }
56
+ if (options.useAbortSignal) {
57
+ params.push((0, ts_poet_1.code) `abortSignal?: AbortSignal`);
58
+ }
59
59
  if (options.addNestjsRestParameter) {
60
60
  params.push((0, ts_poet_1.code) `...rest: any`);
61
61
  }
@@ -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/build/options.js CHANGED
@@ -52,6 +52,7 @@ function defaultOptions() {
52
52
  outputTypeRegistry: false,
53
53
  stringEnums: false,
54
54
  constEnums: false,
55
+ removeEnumPrefix: false,
55
56
  enumsAsLiterals: false,
56
57
  outputClientImpl: true,
57
58
  outputServices: [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "1.138.0",
3
+ "version": "1.140.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",