ts-proto 1.179.0 → 1.181.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
@@ -555,6 +555,10 @@ Generated code will be placed in the Gradle build directory.
555
555
 
556
556
  - With `--ts_proto_opt=useNullAsOptional=true`, `undefined` values will be converted to `null`, and if you use `optional` label in your `.proto` file, the field will have `undefined` type as well. for example:
557
557
 
558
+ - With `--ts_proto_opt=typePrefix=MyPrefix`, the generated interfaces, enums, and factories will have a prefix of `MyPrefix` in their names.
559
+
560
+ - With `--ts_proto_opt=typeSuffix=MySuffix`, the generated interfaces, enums, and factories will have a suffix of `MySuffix` in their names.
561
+
558
562
  ```protobuf
559
563
  message ProfileInfo {
560
564
  int32 id = 1;
@@ -754,7 +758,7 @@ ts-protoc --ts_proto_out=./output -I=./protos ./protoc/*.proto
754
758
  # Todo
755
759
 
756
760
  - Support the string-based encoding of duration in `fromJSON`/`toJSON`
757
- - Make `oneof=unions` the default behavior in 2.0
761
+ - Make `oneof=unions-value` the default behavior in 2.0
758
762
  - Probably change `forceLong` default in 2.0, should default to `forceLong=long`
759
763
  - Make `esModuleInterop=true` the default in 2.0
760
764
 
@@ -772,11 +776,11 @@ Will generate a `Foo` type with two fields: `field_a: string | undefined;` and `
772
776
 
773
777
  With this output, you'll have to check both `if object.field_a` and `if object.field_b`, and if you set one, you'll have to remember to unset the other.
774
778
 
775
- Instead, we recommend using the `oneof=unions` option, which will change the output to be an Abstract Data Type/ADT like:
779
+ Instead, we recommend using the `oneof=unions-value` option, which will change the output to be an Algebraic Data Type/ADT like:
776
780
 
777
781
  ```typescript
778
782
  interface YourMessage {
779
- eitherField?: { $case: "field_a"; field_a: string } | { $case: "field_b"; field_b: string };
783
+ eitherField?: { $case: "field_a"; value: string } | { $case: "field_b"; value: string };
780
784
  }
781
785
  ```
782
786
 
@@ -784,11 +788,21 @@ As this will automatically enforce only one of `field_a` or `field_b` "being set
784
788
 
785
789
  (Note that `eitherField` is optional b/c `oneof` in Protobuf means "at most one field" is set, and does not mean one of the fields _must_ be set.)
786
790
 
787
- In ts-proto's currently-unscheduled 2.x release, `oneof=unions` will become the default behavior.
791
+ In ts-proto's currently-unscheduled 2.x release, `oneof=unions-value` will become the default behavior.
792
+
793
+ There is also a `oneof=unions` option, which generates a union where the field names are included in each option:
794
+
795
+ ```typescript
796
+ interface YourMessage {
797
+ eitherField?: { $case: "field_a"; field_a: string } | { $case: "field_b"; field_b: string };
798
+ }
799
+ ```
800
+
801
+ This is no longer recommended as it can be difficult to write code and types to handle multiple oneof options:
788
802
 
789
803
  ## OneOf Type Helpers
790
804
 
791
- The following helper types may make it easier to work with the types generated from `oneof=unions`:
805
+ The following helper types may make it easier to work with the types generated from `oneof=unions`, though they are generally not needed if you use `oneof=unions-value`:
792
806
 
793
807
  ```ts
794
808
  /** Extracts all the case names from a oneOf field. */
@@ -799,19 +813,45 @@ type OneOfValues<T> = T extends { $case: infer U extends string; [key: string]:
799
813
 
800
814
  /** Extracts the specific type of a oneOf case based on its field name */
801
815
  type OneOfCase<T, K extends OneOfCases<T>> = T extends {
816
+ $case: K;
817
+ [key: string]: unknown;
818
+ }
819
+ ? T
820
+ : never;
821
+
822
+ /** Extracts the specific type of a value type from a oneOf field */
823
+ type OneOfValue<T, K extends OneOfCases<T>> = T extends {
802
824
  $case: infer U extends K;
803
825
  [key: string]: unknown;
804
826
  }
805
827
  ? T[U]
806
828
  : never;
829
+ ```
807
830
 
808
- /** Extracts the specific type of a value type from a oneOf field */
809
- export type OneOfValue<T, K extends OneOfCases<T>> = T extends {
831
+ For comparison, the equivalents for `oneof=unions-value`:
832
+
833
+ ```ts
834
+ /** Extracts all the case names from a oneOf field. */
835
+ type OneOfCases<T> = T['$case'];
836
+
837
+ /** Extracts a union of all the value types from a oneOf field */
838
+ type OneOfValues<T> = T['value'];
839
+
840
+ /** Extracts the specific type of a oneOf case based on its field name */
841
+ type OneOfCase<T, K extends OneOfCases<T>> = T extends {
810
842
  $case: K;
811
843
  [key: string]: unknown;
812
844
  }
813
845
  ? T
814
846
  : never;
847
+
848
+ /** Extracts the specific type of a value type from a oneOf field */
849
+ type OneOfValue<T, K extends OneOfCases<T>> = T extends {
850
+ $case: infer U extends K;
851
+ value: unknown;
852
+ }
853
+ ? T[U]
854
+ : never;
815
855
  ```
816
856
 
817
857
  # Default values and unset fields
@@ -193,6 +193,27 @@ function generateWrapShallow(ctx, fullProtoTypeName, fieldNames) {
193
193
  throw new ${ctx.utils.globalThis}.Error('Unsupported any value type: ' + typeof value);
194
194
  }
195
195
  return result;
196
+ }`);
197
+ }
198
+ else if (ctx.options.oneof === options_1.OneofOption.UNIONS_VALUE) {
199
+ chunks.push((0, ts_poet_1.code) `wrap(value: any): Value {
200
+ const result = createBaseValue()${maybeAsAny(ctx.options)};
201
+ if (value === null) {
202
+ result.kind = {$case: '${fieldNames.nullValue}', value };
203
+ } else if (typeof value === 'boolean') {
204
+ result.kind = {$case: '${fieldNames.boolValue}', value };
205
+ } else if (typeof value === 'number') {
206
+ result.kind = {$case: '${fieldNames.numberValue}', value };
207
+ } else if (typeof value === 'string') {
208
+ result.kind = {$case: '${fieldNames.stringValue}', value };
209
+ } else if (${ctx.utils.globalThis}.Array.isArray(value)) {
210
+ result.kind = {$case: '${fieldNames.listValue}', value };
211
+ } else if (typeof value === 'object') {
212
+ result.kind = {$case: '${fieldNames.structValue}', value };
213
+ } else if (typeof value !== 'undefined') {
214
+ throw new ${ctx.utils.globalThis}.Error('Unsupported any value type: ' + typeof value);
215
+ }
216
+ return result;
196
217
  }`);
197
218
  }
198
219
  else {
@@ -284,6 +305,11 @@ function generateUnwrapShallow(ctx, fullProtoTypeName, fieldNames) {
284
305
  } else {
285
306
  return undefined;
286
307
  }
308
+ }`);
309
+ }
310
+ else if (ctx.options.oneof === options_1.OneofOption.UNIONS_VALUE) {
311
+ chunks.push((0, ts_poet_1.code) `unwrap(message: Value): string | number | boolean | Object | null | Array<any> | undefined {
312
+ return message.kind?.value;
287
313
  }`);
288
314
  }
289
315
  else {
package/build/main.js CHANGED
@@ -448,6 +448,12 @@ function makeDeepPartial(options, longs) {
448
448
  ? { [K in keyof Omit<T, '$case'>]?: DeepPartial<T[K]> } & { ${maybeReadonly(options)}$case: T['$case'] }
449
449
  `;
450
450
  }
451
+ else if (options.oneof === options_1.OneofOption.UNIONS_VALUE) {
452
+ oneofCase = `
453
+ : T extends { ${maybeReadonly(options)}$case: string; value: unknown; }
454
+ ? { ${maybeReadonly(options)}$case: T['$case']; value?: DeepPartial<T['value']>; }
455
+ `;
456
+ }
451
457
  const maybeExport = options.exportCommonSymbols ? "export" : "";
452
458
  // Allow passing longs as numbers or strings, nad we'll convert them
453
459
  const maybeLong = options.forceLong === options_1.LongOption.LONG ? (0, ts_poet_1.code) ` : T extends ${longs.Long} ? string | number | Long ` : "";
@@ -711,7 +717,8 @@ function generateOneofProperty(ctx, messageDesc, oneofIndex, sourceInfo) {
711
717
  const unionType = (0, ts_poet_1.joinCode)(fields.map((f) => {
712
718
  let fieldName = (0, case_1.maybeSnakeToCamel)(f.name, options);
713
719
  let typeName = (0, types_1.toTypeName)(ctx, messageDesc, f);
714
- return (0, ts_poet_1.code) `{ ${mbReadonly}$case: '${fieldName}', ${mbReadonly}${fieldName}: ${typeName} }`;
720
+ let valueName = (0, utils_1.oneofValueName)(fieldName, options);
721
+ return (0, ts_poet_1.code) `{ ${mbReadonly}$case: '${fieldName}', ${mbReadonly}${valueName}: ${typeName} }`;
715
722
  }), { on: " | " });
716
723
  const name = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[oneofIndex].name, options);
717
724
  return (0, ts_poet_1.code) `${mbReadonly}${name}?: ${unionType} | ${(0, utils_1.nullOrUndefined)(options)},`;
@@ -979,9 +986,10 @@ function generateDecode(ctx, fullName, messageDesc) {
979
986
  const oneofNameWithMessage = options.useJsonName
980
987
  ? messageProperty
981
988
  : (0, utils_1.getPropertyAccessor)("message", (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options));
989
+ const valueName = (0, utils_1.oneofValueName)(fieldName, options);
982
990
  chunks.push((0, ts_poet_1.code) `
983
991
  ${tagCheck}
984
- ${oneofNameWithMessage} = { $case: '${fieldName}', ${fieldName}: ${readSnippet} };
992
+ ${oneofNameWithMessage} = { $case: '${fieldName}', ${valueName}: ${readSnippet} };
985
993
  `);
986
994
  }
987
995
  else {
@@ -1267,8 +1275,9 @@ function generateEncode(ctx, fullName, messageDesc) {
1267
1275
  for (const oneOfField of oneOfFieldsDict[field.oneofIndex]) {
1268
1276
  const writeSnippet = getEncodeWriteSnippet(ctx, oneOfField);
1269
1277
  const oneOfFieldName = (0, case_1.maybeSnakeToCamel)(oneOfField.name, ctx.options);
1278
+ const valueName = (0, utils_1.oneofValueName)(oneOfFieldName, ctx.options);
1270
1279
  chunks.push((0, ts_poet_1.code) `case "${oneOfFieldName}":
1271
- ${writeSnippet(`${oneofNameWithMessage}.${oneOfFieldName}`)};
1280
+ ${writeSnippet(`${oneofNameWithMessage}.${valueName}`)};
1272
1281
  break;`);
1273
1282
  }
1274
1283
  chunks.push((0, ts_poet_1.code) `}`);
@@ -1761,8 +1770,9 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
1761
1770
  const fieldName = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options);
1762
1771
  chunks.push((0, ts_poet_1.code) `${fieldName}: `);
1763
1772
  }
1773
+ const valueName = (0, utils_1.oneofValueName)(fieldKey, options);
1764
1774
  const ternaryIf = (0, ts_poet_1.code) `${ctx.utils.isSet}(${jsonProperty})`;
1765
- const ternaryThen = (0, ts_poet_1.code) `{ $case: '${fieldName}', ${fieldKey}: ${readSnippet(`${jsonProperty}`)}`;
1775
+ const ternaryThen = (0, ts_poet_1.code) `{ $case: '${fieldName}', ${valueName}: ${readSnippet(`${jsonProperty}`)}`;
1766
1776
  chunks.push((0, ts_poet_1.code) `${ternaryIf} ? ${ternaryThen}} : `);
1767
1777
  if (field === lastCase) {
1768
1778
  chunks.push((0, ts_poet_1.code) `${(0, utils_1.nullOrUndefined)(options)},`);
@@ -1972,9 +1982,10 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
1972
1982
  const oneofNameWithMessage = options.useJsonName
1973
1983
  ? messageProperty
1974
1984
  : (0, utils_1.getPropertyAccessor)("message", (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options));
1985
+ const valueName = (0, utils_1.oneofValueName)(fieldName, options);
1975
1986
  chunks.push((0, ts_poet_1.code) `
1976
1987
  if (${oneofNameWithMessage}?.$case === '${fieldName}') {
1977
- ${jsonProperty} = ${readSnippet(`${oneofNameWithMessage}.${fieldName}`)};
1988
+ ${jsonProperty} = ${readSnippet(`${oneofNameWithMessage}.${valueName}`)};
1978
1989
  }
1979
1990
  `);
1980
1991
  }
@@ -2148,14 +2159,15 @@ function generateFromPartial(ctx, fullName, messageDesc) {
2148
2159
  const oneofName = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options);
2149
2160
  const oneofNameWithMessage = (0, utils_1.getPropertyAccessor)("message", oneofName);
2150
2161
  const oneofNameWithObject = (0, utils_1.getPropertyAccessor)("object", oneofName);
2151
- const v = readSnippet(`${oneofNameWithObject}.${fieldName}`);
2162
+ const valueName = (0, utils_1.oneofValueName)(fieldName, options);
2163
+ const v = readSnippet(`${oneofNameWithObject}.${valueName}`);
2152
2164
  chunks.push((0, ts_poet_1.code) `
2153
2165
  if (
2154
2166
  ${oneofNameWithObject}?.$case === '${fieldName}'
2155
- && ${oneofNameWithObject}?.${fieldName} !== undefined
2156
- && ${oneofNameWithObject}?.${fieldName} !== null
2167
+ && ${oneofNameWithObject}?.${valueName} !== undefined
2168
+ && ${oneofNameWithObject}?.${valueName} !== null
2157
2169
  ) {
2158
- ${oneofNameWithMessage} = { $case: '${fieldName}', ${fieldName}: ${v} };
2170
+ ${oneofNameWithMessage} = { $case: '${fieldName}', ${valueName}: ${v} };
2159
2171
  }
2160
2172
  `);
2161
2173
  }
@@ -22,7 +22,8 @@ export declare enum EnvOption {
22
22
  }
23
23
  export declare enum OneofOption {
24
24
  PROPERTIES = "properties",
25
- UNIONS = "unions"
25
+ UNIONS = "unions",
26
+ UNIONS_VALUE = "unions-value"
26
27
  }
27
28
  export declare enum ServiceOption {
28
29
  GRPC = "grpc-js",
@@ -98,6 +99,8 @@ export type Options = {
98
99
  annotateFilesWithVersion: boolean;
99
100
  noDefaultsForOptionals: boolean;
100
101
  bigIntLiteral: boolean;
102
+ typePrefix: string;
103
+ typeSuffix: string;
101
104
  };
102
105
  export declare function defaultOptions(): Options;
103
106
  export declare function optionsFromParameter(parameter: string | undefined): Options;
package/build/options.js CHANGED
@@ -30,6 +30,7 @@ var OneofOption;
30
30
  (function (OneofOption) {
31
31
  OneofOption["PROPERTIES"] = "properties";
32
32
  OneofOption["UNIONS"] = "unions";
33
+ OneofOption["UNIONS_VALUE"] = "unions-value";
33
34
  })(OneofOption || (exports.OneofOption = OneofOption = {}));
34
35
  var ServiceOption;
35
36
  (function (ServiceOption) {
@@ -105,6 +106,8 @@ function defaultOptions() {
105
106
  annotateFilesWithVersion: true,
106
107
  noDefaultsForOptionals: false,
107
108
  bigIntLiteral: true,
109
+ typePrefix: "",
110
+ typeSuffix: "",
108
111
  };
109
112
  }
110
113
  exports.defaultOptions = defaultOptions;
package/build/types.js CHANGED
@@ -407,7 +407,9 @@ function isWithinOneOf(field) {
407
407
  }
408
408
  exports.isWithinOneOf = isWithinOneOf;
409
409
  function isWithinOneOfThatShouldBeUnion(options, field) {
410
- return isWithinOneOf(field) && options.oneof === options_1.OneofOption.UNIONS && !field.proto3Optional;
410
+ return (isWithinOneOf(field) &&
411
+ (options.oneof === options_1.OneofOption.UNIONS || options.oneof === options_1.OneofOption.UNIONS_VALUE) &&
412
+ !field.proto3Optional);
411
413
  }
412
414
  exports.isWithinOneOfThatShouldBeUnion = isWithinOneOfThatShouldBeUnion;
413
415
  function isRepeated(field) {
package/build/utils.d.ts CHANGED
@@ -73,6 +73,7 @@ export declare function arrowFunction(params: string, body: Code | string, isOne
73
73
  export declare function nullOrUndefined(options: Pick<Options, "useNullAsOptional">, hasProto3Optional?: boolean): string;
74
74
  export declare function maybeCheckIsNotNull(options: Pick<Options, "useNullAsOptional">, typeName: string, prefix?: string): string;
75
75
  export declare function maybeCheckIsNull(options: Pick<Options, "useNullAsOptional">, typeName: string, prefix?: string): string;
76
+ export declare function oneofValueName(fieldName: string, options: Pick<Options, "oneof">): string;
76
77
  export declare function withOrMaybeCheckIsNotNull(options: Pick<Options, "useNullAsOptional">, typeName: string): string;
77
78
  export declare function withOrMaybeCheckIsNull(options: Pick<Options, "useNullAsOptional">, typeName: string): string;
78
79
  export declare function withAndMaybeCheckIsNotNull(options: Pick<Options, "useNullAsOptional">, typeName: string): string;
package/build/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getVersions = exports.withAndMaybeCheckIsNull = exports.withAndMaybeCheckIsNotNull = exports.withOrMaybeCheckIsNull = exports.withOrMaybeCheckIsNotNull = exports.maybeCheckIsNull = exports.maybeCheckIsNotNull = exports.nullOrUndefined = exports.arrowFunction = exports.tryCatchBlock = exports.impProto = exports.impFile = exports.getPropertyAccessor = exports.safeAccessor = exports.getFieldName = exports.getFieldJsonName = exports.FormattedMethodDescriptor = exports.assertInstanceOf = exports.maybePrefixPackage = exports.maybeAddComment = exports.upperFirst = exports.lowerFirst = exports.singular = exports.fail = exports.readToBuffer = exports.generateIndexFiles = exports.protoFilesToGenerate = void 0;
3
+ exports.getVersions = exports.withAndMaybeCheckIsNull = exports.withAndMaybeCheckIsNotNull = exports.withOrMaybeCheckIsNull = exports.withOrMaybeCheckIsNotNull = exports.oneofValueName = exports.maybeCheckIsNull = exports.maybeCheckIsNotNull = exports.nullOrUndefined = exports.arrowFunction = exports.tryCatchBlock = exports.impProto = exports.impFile = exports.getPropertyAccessor = exports.safeAccessor = exports.getFieldName = exports.getFieldJsonName = exports.FormattedMethodDescriptor = exports.assertInstanceOf = exports.maybePrefixPackage = exports.maybeAddComment = exports.upperFirst = exports.lowerFirst = exports.singular = exports.fail = exports.readToBuffer = exports.generateIndexFiles = exports.protoFilesToGenerate = void 0;
4
4
  const path = require("path");
5
5
  const ts_poet_1 = require("ts-poet");
6
6
  const options_1 = require("./options");
@@ -266,6 +266,10 @@ function maybeCheckIsNull(options, typeName, prefix) {
266
266
  return options.useNullAsOptional ? ` ${prefix} ${typeName} === null` : "";
267
267
  }
268
268
  exports.maybeCheckIsNull = maybeCheckIsNull;
269
+ function oneofValueName(fieldName, options) {
270
+ return options.oneof === options_1.OneofOption.UNIONS ? fieldName : "value";
271
+ }
272
+ exports.oneofValueName = oneofValueName;
269
273
  function withOrMaybeCheckIsNotNull(options, typeName) {
270
274
  return maybeCheckIsNotNull(options, typeName, "||");
271
275
  }
package/build/visit.js CHANGED
@@ -11,8 +11,9 @@ function visit(proto, sourceInfo, messageFn, options, enumFn = () => { }, tsPref
11
11
  const protoFullName = protoPrefix + enumDesc.name;
12
12
  // I.e. FooBar_ZazInner
13
13
  const tsFullName = tsPrefix + (0, case_1.maybeSnakeToCamel)(enumDesc.name, options);
14
+ const tsFullNameWithAffixes = `${options.typePrefix}${tsFullName}${options.typeSuffix}`;
14
15
  const nestedSourceInfo = sourceInfo.open(childEnumType, index);
15
- enumFn(tsFullName, enumDesc, nestedSourceInfo, protoFullName);
16
+ enumFn(tsFullNameWithAffixes, enumDesc, nestedSourceInfo, protoFullName);
16
17
  });
17
18
  const messages = "messageType" in proto ? proto.messageType : proto.nestedType;
18
19
  const childType = isRootFile ? sourceInfo_1.Fields.file.message_type : sourceInfo_1.Fields.message.nested_type;
@@ -21,8 +22,9 @@ function visit(proto, sourceInfo, messageFn, options, enumFn = () => { }, tsPref
21
22
  const protoFullName = protoPrefix + message.name;
22
23
  // I.e. FooBar_ZazInner
23
24
  const tsFullName = tsPrefix + (0, case_1.maybeSnakeToCamel)(messageName(message), options);
25
+ const tsFullNameWithAffixes = `${options.typePrefix}${tsFullName}${options.typeSuffix}`;
24
26
  const nestedSourceInfo = sourceInfo.open(childType, index);
25
- messageFn(tsFullName, message, nestedSourceInfo, protoFullName);
27
+ messageFn(tsFullNameWithAffixes, message, nestedSourceInfo, protoFullName);
26
28
  const delim = options.useSnakeTypeName ? "_" : "";
27
29
  visit(message, nestedSourceInfo, messageFn, options, enumFn, tsFullName + delim, protoFullName + ".");
28
30
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "1.179.0",
3
+ "version": "1.181.0",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",