ts-proto 1.106.2 → 1.109.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,24 @@
1
+ # [1.109.0](https://github.com/stephenh/ts-proto/compare/v1.108.0...v1.109.0) (2022-03-13)
2
+
3
+
4
+ ### Features
5
+
6
+ * import proto as type import if onlyTypes is set ([25d8e8b](https://github.com/stephenh/ts-proto/commit/25d8e8b9042142f1032feadc8a799b7a01115cc2))
7
+
8
+ # [1.108.0](https://github.com/stephenh/ts-proto/compare/v1.107.0...v1.108.0) (2022-03-07)
9
+
10
+
11
+ ### Features
12
+
13
+ * represent field masks as `string[]` ([#525](https://github.com/stephenh/ts-proto/issues/525)) ([903b216](https://github.com/stephenh/ts-proto/commit/903b216238db025e24ec3cfb2d20063aec1a40ed))
14
+
15
+ # [1.107.0](https://github.com/stephenh/ts-proto/compare/v1.106.2...v1.107.0) (2022-03-04)
16
+
17
+
18
+ ### Features
19
+
20
+ * Allow simultaneous services and generic service definitions ([#512](https://github.com/stephenh/ts-proto/issues/512)) ([680831e](https://github.com/stephenh/ts-proto/commit/680831e76f1a4ceb4337442a157d7e702cb14bfc))
21
+
1
22
  ## [1.106.2](https://github.com/stephenh/ts-proto/compare/v1.106.1...v1.106.2) (2022-02-27)
2
23
 
3
24
 
package/README.markdown CHANGED
@@ -362,6 +362,8 @@ Generated code will be placed in the Gradle build directory.
362
362
 
363
363
  - With `--ts_proto_opt=outputServices=generic-definitions`, ts-proto will output generic (framework-agnostic) service definitions. These definitions contain descriptors for each method with links to request and response types, which allows to generate server and client stubs at runtime, and also generate strong types for them at compile time. An example of a library that uses this approach is [nice-grpc](https://github.com/deeplay-io/nice-grpc).
364
364
 
365
+ - With `--ts_proto_opt=outputServices=generic-definitions,outputServices=default`, ts-proto will output both generic definitions and interfaces. This is useful if you want to rely on the interfaces, but also have some reflection capabilities at runtime.
366
+
365
367
  - With `--ts_proto_opt=outputServices=false`, or `=none`, ts-proto will output NO service definitions.
366
368
 
367
369
  - With `--ts_proto_opt=emitImportedFiles=false`, ts-proto will not emit `google/protobuf/*` files unless you explicit add files to `protoc` like this
@@ -620,24 +622,19 @@ Their interpretation is defined by the Protobuf specification, and libraries are
620
622
 
621
623
  `ts-proto` currently automatically converts these messages to their corresponding native types.
622
624
 
623
- - Wrapper Types:
624
-
625
- * [google.protobuf.DoubleValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#DoubleValue) ⇆ `number | undefined`
626
- * [google.protobuf.FloatValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#FloatValue) ⇆ `number | undefined`
627
- * [google.protobuf.Int64Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#Int64Value) ⇆ `number | undefined`
628
- * [google.protobuf.UInt64Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#UInt64Value) ⇆ `number | undefined`
629
- * [google.protobuf.Int32Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#Int32Value) ⇆ `number | undefined`
630
- * [google.protobuf.UInt32Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#UInt32Value) ⇆ `number | undefined`
631
- * [google.protobuf.BoolValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#BoolValue) ⇆ `boolean | undefined`
632
- * [google.protobuf.StringValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#StringValue) ⇆ `string | undefined`
633
- * [google.protobuf.BytesValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.BytesValue) ⇆ `Uint8Array | undefined`
634
-
635
- - JSON Types (Struct Types):
636
-
637
- * [google.protobuf.Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#Value) ⇆ `any | undefined` (i.e. `number | string | boolean | null | array | object`)
638
- * [google.protobuf.ListValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#ListValue) ⇆ `any[]`
639
- * [google.protobuf.Struct](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#Struct) ⇆ `{ [key: string]: any } | undefined`
640
- * [google.protobuf.FieldMask](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#fieldmask) ⇆ `string[]` (only in the JSON, `FieldMask` is still a message)
625
+ * [google.protobuf.BoolValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#boolvalue) ⇆ `boolean`
626
+ * [google.protobuf.BytesValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#bytesvalue) ⇆ `Uint8Array`
627
+ * [google.protobuf.DoubleValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#doublevalue) ⇆ `number`
628
+ * [google.protobuf.FieldMask](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#fieldmask) ⇆ `string[]`
629
+ * [google.protobuf.FloatValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#floatvalue) ⇆ `number`
630
+ * [google.protobuf.Int32Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#int32value) ⇆ `number`
631
+ * [google.protobuf.Int64Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#int64value) ⇆ `number`
632
+ * [google.protobuf.ListValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#listvalue) ⇆ `any[]`
633
+ * [google.protobuf.UInt32Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#uint32value) ⇆ `number`
634
+ * [google.protobuf.UInt64Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#uint64value) ⇆ `number`
635
+ * [google.protobuf.StringValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#stringvalue) ⇆ `string`
636
+ * [google.protobuf.Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#value) ⇆ `any` (i.e. `number | string | boolean | null | array | object`)
637
+ * [google.protobuf.Struct](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#struct) ⇆ `{ [key: string]: any }`
641
638
 
642
639
  ## Wrapper Types
643
640
 
package/build/main.js CHANGED
@@ -80,8 +80,8 @@ function generateFile(ctx, fileDesc) {
80
80
  staticMembers.push(generateDecode(ctx, fullName, message));
81
81
  }
82
82
  if (options.outputJsonMethods) {
83
- staticMembers.push(generateFromJson(ctx, fullName, message));
84
- staticMembers.push(generateToJson(ctx, fullName, message));
83
+ staticMembers.push(generateFromJson(ctx, fullName, fullTypeName, message));
84
+ staticMembers.push(generateToJson(ctx, fullName, fullTypeName, message));
85
85
  }
86
86
  if (options.outputPartialMethods) {
87
87
  staticMembers.push(generateFromPartial(ctx, fullName, message));
@@ -118,29 +118,34 @@ function generateFile(ctx, fileDesc) {
118
118
  }
119
119
  chunks.push(ts_poet_1.code `export const ${serviceConstName} = "${serviceDesc.name}";`);
120
120
  }
121
- else if (options.outputServices === options_1.ServiceOption.GRPC) {
122
- chunks.push(generate_grpc_js_1.generateGrpcJsService(ctx, fileDesc, sInfo, serviceDesc));
123
- }
124
- else if (options.outputServices === options_1.ServiceOption.GENERIC) {
125
- chunks.push(generate_generic_service_definition_1.generateGenericServiceDefinition(ctx, fileDesc, sInfo, serviceDesc));
126
- }
127
- else if (options.outputServices === options_1.ServiceOption.DEFAULT) {
128
- // This service could be Twirp or grpc-web or JSON (maybe). So far all of their
129
- // interfaces are fairly similar so we share the same service interface.
130
- chunks.push(generate_services_1.generateService(ctx, fileDesc, sInfo, serviceDesc));
131
- if (options.outputClientImpl === true) {
132
- chunks.push(generate_services_1.generateServiceClientImpl(ctx, fileDesc, serviceDesc));
133
- }
134
- else if (options.outputClientImpl === 'grpc-web') {
135
- chunks.push(generate_grpc_web_1.generateGrpcClientImpl(ctx, fileDesc, serviceDesc));
136
- chunks.push(generate_grpc_web_1.generateGrpcServiceDesc(fileDesc, serviceDesc));
137
- serviceDesc.method.forEach((method) => {
138
- chunks.push(generate_grpc_web_1.generateGrpcMethodDesc(ctx, serviceDesc, method));
139
- if (method.serverStreaming) {
140
- hasServerStreamingMethods = true;
121
+ else {
122
+ const uniqueServices = [...new Set(options.outputServices)].sort();
123
+ uniqueServices.forEach((outputService) => {
124
+ if (outputService === options_1.ServiceOption.GRPC) {
125
+ chunks.push(generate_grpc_js_1.generateGrpcJsService(ctx, fileDesc, sInfo, serviceDesc));
126
+ }
127
+ else if (outputService === options_1.ServiceOption.GENERIC) {
128
+ chunks.push(generate_generic_service_definition_1.generateGenericServiceDefinition(ctx, fileDesc, sInfo, serviceDesc));
129
+ }
130
+ else if (outputService === options_1.ServiceOption.DEFAULT) {
131
+ // This service could be Twirp or grpc-web or JSON (maybe). So far all of their
132
+ // interfaces are fairly similar so we share the same service interface.
133
+ chunks.push(generate_services_1.generateService(ctx, fileDesc, sInfo, serviceDesc));
134
+ if (options.outputClientImpl === true) {
135
+ chunks.push(generate_services_1.generateServiceClientImpl(ctx, fileDesc, serviceDesc));
141
136
  }
142
- });
143
- }
137
+ else if (options.outputClientImpl === 'grpc-web') {
138
+ chunks.push(generate_grpc_web_1.generateGrpcClientImpl(ctx, fileDesc, serviceDesc));
139
+ chunks.push(generate_grpc_web_1.generateGrpcServiceDesc(fileDesc, serviceDesc));
140
+ serviceDesc.method.forEach((method) => {
141
+ chunks.push(generate_grpc_web_1.generateGrpcMethodDesc(ctx, serviceDesc, method));
142
+ if (method.serverStreaming) {
143
+ hasServerStreamingMethods = true;
144
+ }
145
+ });
146
+ }
147
+ }
148
+ });
144
149
  }
145
150
  serviceDesc.method.forEach((methodDesc, index) => {
146
151
  if (methodDesc.serverStreaming || methodDesc.clientStreaming) {
@@ -148,7 +153,9 @@ function generateFile(ctx, fileDesc) {
148
153
  }
149
154
  });
150
155
  });
151
- if (options.outputServices === options_1.ServiceOption.DEFAULT && options.outputClientImpl && fileDesc.service.length > 0) {
156
+ if (options.outputServices.includes(options_1.ServiceOption.DEFAULT) &&
157
+ options.outputClientImpl &&
158
+ fileDesc.service.length > 0) {
152
159
  if (options.outputClientImpl === true) {
153
160
  chunks.push(generate_services_1.generateRpcType(ctx, hasStreamingMethods));
154
161
  }
@@ -597,7 +604,7 @@ function generateDecode(ctx, fullName, messageDesc) {
597
604
  else if (types_1.isValueType(ctx, field)) {
598
605
  const type = types_1.basicTypeName(ctx, field, { keepValueType: true });
599
606
  const unwrap = (decodedValue) => {
600
- if (types_1.isListValueType(field) || types_1.isStructType(field) || types_1.isAnyValueType(field)) {
607
+ if (types_1.isListValueType(field) || types_1.isStructType(field) || types_1.isAnyValueType(field) || types_1.isFieldMaskType(field)) {
601
608
  return ts_poet_1.code `${type}.unwrap(${decodedValue})`;
602
609
  }
603
610
  return ts_poet_1.code `${decodedValue}.value`;
@@ -722,7 +729,7 @@ function generateEncode(ctx, fullName, messageDesc) {
722
729
  const maybeTypeField = options.outputTypeRegistry ? `$type: '${field.typeName.slice(1)}',` : '';
723
730
  const type = types_1.basicTypeName(ctx, field, { keepValueType: true });
724
731
  const wrappedValue = (place) => {
725
- if (types_1.isAnyValueType(field) || types_1.isListValueType(field) || types_1.isStructType(field)) {
732
+ if (types_1.isAnyValueType(field) || types_1.isListValueType(field) || types_1.isStructType(field) || types_1.isFieldMaskType(field)) {
726
733
  return ts_poet_1.code `${type}.wrap(${place})`;
727
734
  }
728
735
  return ts_poet_1.code `{${maybeTypeField} value: ${place}!}`;
@@ -880,7 +887,7 @@ function generateEncode(ctx, fullName, messageDesc) {
880
887
  * This is very similar to decode, we loop through looking for properties, with
881
888
  * a few special cases for https://developers.google.com/protocol-buffers/docs/proto3#json.
882
889
  * */
883
- function generateFromJson(ctx, fullName, messageDesc) {
890
+ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
884
891
  const { options, utils, typeMap } = ctx;
885
892
  const chunks = [];
886
893
  // create the basic function declaration
@@ -892,8 +899,18 @@ function generateFromJson(ctx, fullName, messageDesc) {
892
899
  chunks.push(ts_poet_1.code `$type: ${fullName}.$type,`);
893
900
  }
894
901
  const oneofFieldsCases = messageDesc.oneofDecl.map((oneof, oneofIndex) => messageDesc.field.filter(types_1.isWithinOneOf).filter((field) => field.oneofIndex === oneofIndex));
902
+ const canonicalFromJson = {
903
+ ['google.protobuf.FieldMask']: {
904
+ paths: (from) => ts_poet_1.code `typeof(${from}) === 'string'
905
+ ? ${from}.split(",").filter(Boolean)
906
+ : Array.isArray(${from}?.paths)
907
+ ? ${from}.paths.map(String)
908
+ : []`,
909
+ },
910
+ };
895
911
  // add a check for each incoming field
896
912
  messageDesc.field.forEach((field) => {
913
+ var _a;
897
914
  const fieldName = case_1.maybeSnakeToCamel(field.name, options);
898
915
  const jsonName = utils_1.getFieldJsonName(field, options);
899
916
  const jsonProperty = utils_1.getPropertyAccessor('object', jsonName);
@@ -937,7 +954,8 @@ function generateFromJson(ctx, fullName, messageDesc) {
937
954
  return ts_poet_1.code `${from}`;
938
955
  }
939
956
  else if (types_1.isFieldMaskType(field)) {
940
- return ts_poet_1.code `{paths: ${from}.split(",")}`;
957
+ const type = types_1.basicTypeName(ctx, field, { keepValueType: true });
958
+ return ts_poet_1.code `${type}.unwrap(${type}.fromJSON(${from}))`;
941
959
  }
942
960
  else if (types_1.isListValueType(field)) {
943
961
  return ts_poet_1.code `[...${from}]`;
@@ -1010,7 +1028,10 @@ function generateFromJson(ctx, fullName, messageDesc) {
1010
1028
  }
1011
1029
  };
1012
1030
  // and then use the snippet to handle repeated fields if necessary
1013
- if (types_1.isRepeated(field)) {
1031
+ if ((_a = canonicalFromJson[fullTypeName]) === null || _a === void 0 ? void 0 : _a[fieldName]) {
1032
+ chunks.push(ts_poet_1.code `${fieldName}: ${canonicalFromJson[fullTypeName][fieldName]('object')},`);
1033
+ }
1034
+ else if (types_1.isRepeated(field)) {
1014
1035
  if (types_1.isMapType(ctx, messageDesc, field)) {
1015
1036
  const fieldType = types_1.toTypeName(ctx, messageDesc, field);
1016
1037
  const i = maybeCastToNumber(ctx, messageDesc, field, 'key');
@@ -1083,9 +1104,24 @@ function generateFromJson(ctx, fullName, messageDesc) {
1083
1104
  chunks.push(ts_poet_1.code `}`);
1084
1105
  return ts_poet_1.joinCode(chunks, { on: '\n' });
1085
1106
  }
1086
- function generateToJson(ctx, fullName, messageDesc) {
1107
+ function generateCanonicalToJson(fullName, fullProtobufTypeName) {
1108
+ if (types_1.isFieldMaskTypeName(fullProtobufTypeName)) {
1109
+ return ts_poet_1.code `
1110
+ toJSON(message: ${fullName}): string {
1111
+ return message.paths.join(',');
1112
+ }
1113
+ `;
1114
+ }
1115
+ return undefined;
1116
+ }
1117
+ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
1087
1118
  const { options, utils, typeMap } = ctx;
1088
1119
  const chunks = [];
1120
+ const canonicalToJson = generateCanonicalToJson(fullName, fullProtobufTypeName);
1121
+ if (canonicalToJson) {
1122
+ chunks.push(canonicalToJson);
1123
+ return ts_poet_1.joinCode(chunks, { on: '\n' });
1124
+ }
1089
1125
  // create the basic function declaration
1090
1126
  chunks.push(ts_poet_1.code `
1091
1127
  toJSON(${messageDesc.field.length > 0 ? 'message' : '_'}: ${fullName}): unknown {
@@ -1158,7 +1194,8 @@ function generateToJson(ctx, fullName, messageDesc) {
1158
1194
  return ts_poet_1.code `${from}`;
1159
1195
  }
1160
1196
  else if (types_1.isFieldMaskType(field)) {
1161
- return ts_poet_1.code `${from}.paths.join()`;
1197
+ const type = types_1.basicTypeName(ctx, field, { keepValueType: true });
1198
+ return ts_poet_1.code `${type}.toJSON(${type}.wrap(${from}))`;
1162
1199
  }
1163
1200
  else if (types_1.isMessage(field) && !types_1.isValueType(ctx, field) && !types_1.isMapType(ctx, messageDesc, field)) {
1164
1201
  const type = types_1.basicTypeName(ctx, field, { keepValueType: true });
@@ -1424,6 +1461,11 @@ function generateWrap(ctx, fullProtoTypeName) {
1424
1461
  return result;
1425
1462
  }`);
1426
1463
  }
1464
+ if (types_1.isFieldMaskTypeName(fullProtoTypeName)) {
1465
+ chunks.push(ts_poet_1.code `wrap(paths: string[]): FieldMask {
1466
+ return {paths: paths};
1467
+ }`);
1468
+ }
1427
1469
  return chunks;
1428
1470
  }
1429
1471
  function generateUnwrap(ctx, fullProtoTypeName) {
@@ -1481,6 +1523,11 @@ function generateUnwrap(ctx, fullProtoTypeName) {
1481
1523
  return message.values;
1482
1524
  }`);
1483
1525
  }
1526
+ if (types_1.isFieldMaskTypeName(fullProtoTypeName)) {
1527
+ chunks.push(ts_poet_1.code `unwrap(message: FieldMask): string[] {
1528
+ return message.paths;
1529
+ }`);
1530
+ }
1484
1531
  return chunks;
1485
1532
  }
1486
1533
  exports.contextTypeVar = 'Context extends DataLoaders';
package/build/options.js CHANGED
@@ -51,7 +51,7 @@ function defaultOptions() {
51
51
  constEnums: false,
52
52
  enumsAsLiterals: false,
53
53
  outputClientImpl: true,
54
- outputServices: ServiceOption.DEFAULT,
54
+ outputServices: [],
55
55
  returnObservable: false,
56
56
  addGrpcMetadata: false,
57
57
  addNestjsRestParameter: false,
@@ -95,7 +95,14 @@ function optionsFromParameter(parameter) {
95
95
  }
96
96
  // Treat outputServices=false as NONE
97
97
  if (options.outputServices === false) {
98
- options.outputServices = ServiceOption.NONE;
98
+ options.outputServices = [ServiceOption.NONE];
99
+ }
100
+ // Existing type-coercion inside parseParameter leaves a little to be desired.
101
+ if (typeof options.outputServices == 'string') {
102
+ options.outputServices = [options.outputServices];
103
+ }
104
+ if (options.outputServices.length == 0) {
105
+ options.outputServices = [ServiceOption.DEFAULT];
99
106
  }
100
107
  if (options.useDate === true) {
101
108
  // Treat useDate=true as DATE
package/build/types.js CHANGED
@@ -458,6 +458,8 @@ function valueTypeName(ctx, typeName) {
458
458
  return ts_poet_1.code `any`;
459
459
  case '.google.protobuf.Struct':
460
460
  return ts_poet_1.code `{[key: string]: any}`;
461
+ case '.google.protobuf.FieldMask':
462
+ return ts_poet_1.code `string[]`;
461
463
  default:
462
464
  return undefined;
463
465
  }
package/build/utils.js CHANGED
@@ -137,7 +137,7 @@ class FormattedMethodDescriptor {
137
137
  */
138
138
  static formatName(methodName, options) {
139
139
  let result = methodName;
140
- if (options.lowerCaseServiceMethods || options.outputServices === options_1.ServiceOption.GRPC) {
140
+ if (options.lowerCaseServiceMethods || options.outputServices.includes(options_1.ServiceOption.GRPC)) {
141
141
  result = case_1.camelCase(result);
142
142
  }
143
143
  return result;
@@ -170,6 +170,12 @@ function getPropertyAccessor(objectName, propertyName, optional = false) {
170
170
  }
171
171
  exports.getPropertyAccessor = getPropertyAccessor;
172
172
  function impProto(options, module, type) {
173
- return ts_poet_1.imp(`${type}@./${module}${options.fileSuffix}`);
173
+ const importString = `${type}@./${module}${options.fileSuffix}`;
174
+ if (options.onlyTypes) {
175
+ return ts_poet_1.imp('t:' + importString);
176
+ }
177
+ else {
178
+ return ts_poet_1.imp(importString);
179
+ }
174
180
  }
175
181
  exports.impProto = impProto;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "1.106.2",
3
+ "version": "1.109.0",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",
@@ -11,6 +11,7 @@
11
11
  "build": "yarn tsc",
12
12
  "build:test": "yarn proto2bin && yarn proto2pbjs && yarn bin2ts",
13
13
  "build:test:local": "yarn proto2bin:local && yarn proto2pbjs:local && yarn bin2ts:local",
14
+ "prepare": "yarn build",
14
15
  "proto2bin": "docker-compose run --rm protoc update-bins.sh",
15
16
  "proto2bin-node": "docker-compose run --rm node update-bins.sh",
16
17
  "proto2pbjs": "docker-compose run --rm protoc pbjs.sh",
@@ -63,7 +64,7 @@
63
64
  "dataloader": "^1.4.0",
64
65
  "object-hash": "^1.3.1",
65
66
  "protobufjs": "^6.8.8",
66
- "ts-poet": "^4.5.0",
67
+ "ts-poet": "^4.10.0",
67
68
  "ts-proto-descriptors": "1.6.0"
68
69
  }
69
70
  }