ts-proto 1.114.5 → 1.115.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.115.0](https://github.com/stephenh/ts-proto/compare/v1.114.7...v1.115.0) (2022-06-02)
2
+
3
+
4
+ ### Features
5
+
6
+ * added option 'useJsonWireFormat' ([#576](https://github.com/stephenh/ts-proto/issues/576)) ([a71b145](https://github.com/stephenh/ts-proto/commit/a71b145c81136b8d8e3681b6753146d3ceeff179)), closes [#571](https://github.com/stephenh/ts-proto/issues/571)
7
+
8
+ ## [1.114.7](https://github.com/stephenh/ts-proto/compare/v1.114.6...v1.114.7) (2022-05-28)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * Fix version number for Buf plugin. ([dc1fb7e](https://github.com/stephenh/ts-proto/commit/dc1fb7e766ed4115d026e7ff0e9a9480c1e42380))
14
+
15
+ ## [1.114.6](https://github.com/stephenh/ts-proto/compare/v1.114.5...v1.114.6) (2022-05-28)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * Bump node in ts-proto.Dockerfile. ([42f3cea](https://github.com/stephenh/ts-proto/commit/42f3ceac5323056b1b845b51919ecddaa32bf258))
21
+
1
22
  ## [1.114.5](https://github.com/stephenh/ts-proto/compare/v1.114.4...v1.114.5) (2022-05-28)
2
23
 
3
24
 
package/README.markdown CHANGED
@@ -104,7 +104,7 @@ You can also use the official plugin published to the Buf Registry.
104
104
  ```yaml
105
105
  version: v1
106
106
  plugins:
107
- - remote: buf.build/roundrock-io/plugins/ts-proto
107
+ - remote: buf.build/stephenh/plugins/ts-proto
108
108
  out: ../gen/ts
109
109
  ```
110
110
 
@@ -274,34 +274,36 @@ Generated code will be placed in the Gradle build directory.
274
274
 
275
275
  Currently `browser` doesn't have any specific behavior other than being "not `node`". It probably will soon/at some point.
276
276
 
277
- - With `--ts_proto_opt=useOptionals=true`, non-scalar fields are declared as optional TypeScript properties, e.g. `field?: Message` instead of the default `field: Message | undefined`.
277
+ - With `--ts_proto_opt=useOptionals=messages` (for message fields) or `--ts_proto_opt=useOptionals=all` (for message and scalar fields), fields are declared as optional keys, e.g. `field?: Message` instead of the default `field: Message | undefined`.
278
278
 
279
- ts-proto defaults to `useOptionals=false`, e.g. `field: Message | undefined`, because it is the most safe for use cases like:
279
+ ts-proto defaults to `useOptionals=none` because it:
280
+
281
+ 1. Prevents typos when initializing messages, and
282
+ 2. Provides the most consistent API to readers
283
+ 3. Ensures production messages are properly initialized with all fields.
280
284
 
281
- ```typescript
282
- interface SomeMessage {
283
- firstName: string | undefined;
284
- lastName: string | undefined;
285
- }
286
-
287
- const data = { firstName: 'a', lastTypo: 'b' };
288
-
289
- // This would compile if `lastName` was `lastName?`, even though the
290
- // `lastTypo` key above means that `lastName` is not assigned.
291
- const message: SomeMessage = {
292
- ...data,
293
- };
294
- ```
285
+ For typo prevention, optional fields make it easy for extra fields to slip into a message (until we get [Exact Types](https://github.com/microsoft/TypeScript/issues/12936)), i.e.:
295
286
 
296
- However, the type-safety of `useOptionals=false` is admittedly tedious if you have many inherently-unused fields, so you can use `useOptionals=true` if that trade-off makes sense for your project.
287
+ ```typescript
288
+ interface SomeMessage {
289
+ firstName: string | undefined;
290
+ lastName: string | undefined;
291
+ }
292
+ // Declared with a typo
293
+ const data = { firstName: 'a', lastTypo: 'b' };
294
+ // With useOptionals=none, this correctly fails to compile; if `lastName` was optional, it would not
295
+ const message: SomeMessage = { ...data };
296
+ ```
297
+
298
+ For a consistent API, if `SomeMessage.lastName` is optional `lastName?`, then readers have to check _two_ empty conditions: a) is `lastName` `undefined` (b/c it was created in-memory and left unset), or b) is `lastName` empty string (b/c we read `SomeMessage` off the wire and correctly set `lastName` to empty string)?
297
299
 
298
- You can also use the generated `SomeMessage.fromPartial` methods to opt into the optionality on a per-call-site basis. The `fromPartial` allows the creator/writer to have default values applied (i.e. `undefined` --> `0`), and the return value will still be the non-optional type that provides a consistent view (i.e. always `0`) to clients.
300
+ For ensuring proper initialization, if later `SomeMessage.middleInitial` is added, but it's marked as optional `middleInitial?`, you may have many call sites in production code that _should_ now be passing `middleInitial` to create a valid `SomeMessage`, but are not.
299
301
 
300
- Eventually if TypeScript supports [Exact Types](https://github.com/microsoft/TypeScript/issues/12936), that should allow ts-proto to switch to `useOptionals=true` as the default/only behavior, have the generated `Message.encode`/`Message.toPartial`/etc. methods accept `Exact<T>` versions of the message types, and the result would be both safe + succinct.
302
+ So, between typo-prevention, reader inconsistency, and proper initialization, ts-proto recommends using `useOptionals=none` as the "most safe" option.
301
303
 
302
- Also see the comment in [this issue](https://github.com/stephenh/ts-proto/issues/120#issuecomment-678375833) which explains the nuance behind making all fields optional (currently `useOptionals` only makes message fields optional), specifically that a message created with `const message: Message = { ...key not set... }` (so `key` is `undefined`) vs. `const message = Message.decode(...key not set...)` (so `key` is the default value) would look different to clients.
304
+ All that said, this approach does require writers/creators to set every field (although `fromPartial` is meant to address this), so if you still want to have optional fields, you can set `useOptionals=messages` or `useOptionals=all`.
303
305
 
304
- Note that RPC methods, like `service.ping({ key: ... })`, accept `DeepPartial` versions of the request messages, because of the same rationale that it makes it easy for the writer call-site to get default values for free, and because the "reader" is the internal ts-proto serialization code, it can apply the defaults as necessary.
306
+ (See [this issue](https://github.com/stephenh/ts-proto/issues/120#issuecomment-678375833) and [this issue](https://github.com/stephenh/ts-proto/issues/397#issuecomment-977259118) for discussions on `useOptional`.)
305
307
 
306
308
  - With `--ts_proto_opt=exportCommonSymbols=false`, utility types like `DeepPartial` won't be `export`d.
307
309
 
@@ -404,6 +406,11 @@ Generated code will be placed in the Gradle build directory.
404
406
 
405
407
  Note that, as indicated, this means Object.keys will not include set-by-default fields, so if you have code that iterates over messages keys in a generic fashion, it will have to also iterate over keys inherited from the prototype.
406
408
 
409
+ - With `--ts_proto_opt=useJsonWireFormat=true`, the generated code will reflect the JSON representation of Protobuf messages.
410
+
411
+ Requires `onlyTypes=true`. Implies `useDate=string` and `stringEnums=true`. This option is to generate types that can be directly used with marshalling/unmarshalling Protobuf messages serialized as JSON.
412
+ You may also want to set `useOptionals=all`, as gRPC gateways are not required to send default value for scalar values.
413
+
407
414
  ### Only Types
408
415
 
409
416
  If you're looking for `ts-proto` to generate only types for your Protobuf types then passing all three of `outputEncodeMethods`, `outputJsonMethods`, and `outputClientImpl` as `false` is probably what you want, i.e.:
package/build/enums.js CHANGED
@@ -20,15 +20,16 @@ function generateEnum(ctx, fullName, enumDesc, sourceInfo) {
20
20
  chunks.push(ts_poet_1.code `export ${options.constEnums ? 'const ' : ''}enum ${ts_poet_1.def(fullName)} {`);
21
21
  }
22
22
  const delimiter = options.enumsAsLiterals ? ':' : '=';
23
+ const stringEnums = options.stringEnums || (options.onlyTypes && options.useJsonWireFormat);
23
24
  enumDesc.value.forEach((valueDesc, index) => {
24
25
  var _a;
25
26
  const info = sourceInfo.lookup(sourceInfo_1.Fields.enum.value, index);
26
27
  utils_1.maybeAddComment(info, chunks, (_a = valueDesc.options) === null || _a === void 0 ? void 0 : _a.deprecated, `${valueDesc.name} - `);
27
- chunks.push(ts_poet_1.code `${valueDesc.name} ${delimiter} ${options.stringEnums ? `"${valueDesc.name}"` : valueDesc.number.toString()},`);
28
+ chunks.push(ts_poet_1.code `${valueDesc.name} ${delimiter} ${stringEnums ? `"${valueDesc.name}"` : valueDesc.number.toString()},`);
28
29
  });
29
30
  if (options.unrecognizedEnum)
30
31
  chunks.push(ts_poet_1.code `
31
- ${UNRECOGNIZED_ENUM_NAME} ${delimiter} ${options.stringEnums ? `"${UNRECOGNIZED_ENUM_NAME}"` : UNRECOGNIZED_ENUM_VALUE.toString()},`);
32
+ ${UNRECOGNIZED_ENUM_NAME} ${delimiter} ${stringEnums ? `"${UNRECOGNIZED_ENUM_NAME}"` : UNRECOGNIZED_ENUM_VALUE.toString()},`);
32
33
  if (options.enumsAsLiterals) {
33
34
  chunks.push(ts_poet_1.code `} as const`);
34
35
  chunks.push(ts_poet_1.code `\n`);
@@ -37,7 +38,7 @@ function generateEnum(ctx, fullName, enumDesc, sourceInfo) {
37
38
  else {
38
39
  chunks.push(ts_poet_1.code `}`);
39
40
  }
40
- if (options.outputJsonMethods || (options.stringEnums && options.outputEncodeMethods)) {
41
+ if (options.outputJsonMethods || (stringEnums && options.outputEncodeMethods)) {
41
42
  chunks.push(ts_poet_1.code `\n`);
42
43
  chunks.push(generateEnumFromJson(ctx, fullName, enumDesc));
43
44
  }
@@ -45,7 +46,7 @@ function generateEnum(ctx, fullName, enumDesc, sourceInfo) {
45
46
  chunks.push(ts_poet_1.code `\n`);
46
47
  chunks.push(generateEnumToJson(ctx, fullName, enumDesc));
47
48
  }
48
- if (options.stringEnums && options.outputEncodeMethods) {
49
+ if (stringEnums && options.outputEncodeMethods) {
49
50
  chunks.push(ts_poet_1.code `\n`);
50
51
  chunks.push(generateEnumToNumber(ctx, fullName, enumDesc));
51
52
  }
package/build/options.js CHANGED
@@ -67,6 +67,7 @@ function defaultOptions() {
67
67
  useExactTypes: true,
68
68
  unknownFields: false,
69
69
  usePrototypeForDefaults: false,
70
+ useJsonWireFormat: false,
70
71
  };
71
72
  }
72
73
  exports.defaultOptions = defaultOptions;
package/build/types.js CHANGED
@@ -436,6 +436,7 @@ function isEmptyType(typeName) {
436
436
  }
437
437
  exports.isEmptyType = isEmptyType;
438
438
  function valueTypeName(ctx, typeName) {
439
+ const useJsonWireFormat = ctx.options.onlyTypes && ctx.options.useJsonWireFormat;
439
440
  switch (typeName) {
440
441
  case '.google.protobuf.StringValue':
441
442
  return ts_poet_1.code `string`;
@@ -451,7 +452,7 @@ function valueTypeName(ctx, typeName) {
451
452
  case '.google.protobuf.BoolValue':
452
453
  return ts_poet_1.code `boolean`;
453
454
  case '.google.protobuf.BytesValue':
454
- return ctx.options.env === options_1.EnvOption.NODE ? ts_poet_1.code `Buffer` : ts_poet_1.code `Uint8Array`;
455
+ return ctx.options.env === options_1.EnvOption.NODE ? ts_poet_1.code `Buffer` : useJsonWireFormat ? ts_poet_1.code `string` : ts_poet_1.code `Uint8Array`;
455
456
  case '.google.protobuf.ListValue':
456
457
  return ts_poet_1.code `Array<any>`;
457
458
  case '.google.protobuf.Value':
@@ -459,7 +460,11 @@ function valueTypeName(ctx, typeName) {
459
460
  case '.google.protobuf.Struct':
460
461
  return ts_poet_1.code `{[key: string]: any}`;
461
462
  case '.google.protobuf.FieldMask':
462
- return ts_poet_1.code `string[]`;
463
+ return useJsonWireFormat ? ts_poet_1.code `string` : ts_poet_1.code `string[]`;
464
+ case '.google.protobuf.Duration':
465
+ return useJsonWireFormat ? ts_poet_1.code `string` : undefined;
466
+ case '.google.protobuf.Timestamp':
467
+ return useJsonWireFormat ? ts_poet_1.code `string` : undefined;
463
468
  default:
464
469
  return undefined;
465
470
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "1.114.5",
3
+ "version": "1.115.0",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",