ts-proto 1.114.7 → 1.115.2

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.2](https://github.com/stephenh/ts-proto/compare/v1.115.1...v1.115.2) (2022-06-03)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * simplify handling useJsonWireFormat=true and fix onlyTypes=true ([#583](https://github.com/stephenh/ts-proto/issues/583)) ([6e7f938](https://github.com/stephenh/ts-proto/commit/6e7f9387d144f506ded982604f32981ac5e327de))
7
+
8
+ ## [1.115.1](https://github.com/stephenh/ts-proto/compare/v1.115.0...v1.115.1) (2022-06-02)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * Bump protobufjs. ([#585](https://github.com/stephenh/ts-proto/issues/585)) ([ba6b85d](https://github.com/stephenh/ts-proto/commit/ba6b85d9d5b7ce191dce898c462cf164a6b1e308))
14
+
15
+ # [1.115.0](https://github.com/stephenh/ts-proto/compare/v1.114.7...v1.115.0) (2022-06-02)
16
+
17
+
18
+ ### Features
19
+
20
+ * 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)
21
+
1
22
  ## [1.114.7](https://github.com/stephenh/ts-proto/compare/v1.114.6...v1.114.7) (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' };
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.:
288
286
 
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
- ```
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)?
295
299
 
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.
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.
297
301
 
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.
302
+ So, between typo-prevention, reader inconsistency, and proper initialization, ts-proto recommends using `useOptionals=none` as the "most safe" option.
299
303
 
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.
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`.
301
305
 
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.
303
-
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
 
@@ -394,7 +396,7 @@ Generated code will be placed in the Gradle build directory.
394
396
 
395
397
  - With `--ts_proto_opt=onlyTypes=true`, only types will be emitted, and imports for `long` and `protobufjs/minimal` will be excluded.
396
398
 
397
- Note: _This is a combination_ of `outputJsonMethods=false,outputEncodeMethods=false,outputClientImpl=false,nestJs=false`
399
+ This is the same as setting `outputJsonMethods=false,outputEncodeMethods=false,outputClientImpl=false,nestJs=false`
398
400
 
399
401
  - With `--ts_proto_opt=usePrototypeForDefaults=true`, the generated code will wrap new objects with `Object.create`.
400
402
 
@@ -404,11 +406,10 @@ 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
 
407
- ### Only Types
408
-
409
- 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.:
409
+ - With `--ts_proto_opt=useJsonWireFormat=true`, the generated code will reflect the JSON representation of Protobuf messages.
410
410
 
411
- `--ts_proto_opt=onlyTypes=true`.
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.
412
413
 
413
414
  ### NestJS Support
414
415
 
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;
@@ -87,8 +88,17 @@ function optionsFromParameter(parameter) {
87
88
  }
88
89
  Object.assign(options, parsed);
89
90
  }
90
- // We should promote onlyTypes to its own documented flag, but just an alias for now
91
- if (!options.outputJsonMethods && !options.outputEncodeMethods && !options.outputClientImpl && !options.nestJs) {
91
+ // onlyTypes=true implies outputJsonMethods=false,outputEncodeMethods=false,outputClientImpl=false,nestJs=false
92
+ if (options.onlyTypes) {
93
+ options.outputJsonMethods = false;
94
+ options.outputEncodeMethods = false;
95
+ options.outputClientImpl = false;
96
+ options.nestJs = false;
97
+ }
98
+ else if (!options.outputJsonMethods &&
99
+ !options.outputEncodeMethods &&
100
+ !options.outputClientImpl &&
101
+ !options.nestJs) {
92
102
  options.onlyTypes = true;
93
103
  }
94
104
  // Treat forceLong=true as LONG
@@ -123,6 +133,17 @@ function optionsFromParameter(parameter) {
123
133
  else if (typeof options.snakeToCamel === 'string') {
124
134
  options.snakeToCamel = [options.snakeToCamel];
125
135
  }
136
+ if (options.useJsonWireFormat) {
137
+ if (!options.onlyTypes) {
138
+ // useJsonWireFormat requires onlyTypes=true
139
+ options.useJsonWireFormat = false;
140
+ }
141
+ else {
142
+ // useJsonWireFormat implies stringEnums=true and useDate=string
143
+ options.stringEnums = true;
144
+ options.useDate = DateOption.STRING;
145
+ }
146
+ }
126
147
  return options;
127
148
  }
128
149
  exports.optionsFromParameter = optionsFromParameter;
package/build/types.js CHANGED
@@ -451,7 +451,11 @@ function valueTypeName(ctx, typeName) {
451
451
  case '.google.protobuf.BoolValue':
452
452
  return ts_poet_1.code `boolean`;
453
453
  case '.google.protobuf.BytesValue':
454
- return ctx.options.env === options_1.EnvOption.NODE ? ts_poet_1.code `Buffer` : ts_poet_1.code `Uint8Array`;
454
+ return ctx.options.env === options_1.EnvOption.NODE
455
+ ? ts_poet_1.code `Buffer`
456
+ : ctx.options.useJsonWireFormat
457
+ ? ts_poet_1.code `string`
458
+ : ts_poet_1.code `Uint8Array`;
455
459
  case '.google.protobuf.ListValue':
456
460
  return ts_poet_1.code `Array<any>`;
457
461
  case '.google.protobuf.Value':
@@ -459,7 +463,11 @@ function valueTypeName(ctx, typeName) {
459
463
  case '.google.protobuf.Struct':
460
464
  return ts_poet_1.code `{[key: string]: any}`;
461
465
  case '.google.protobuf.FieldMask':
462
- return ts_poet_1.code `string[]`;
466
+ return ctx.options.useJsonWireFormat ? ts_poet_1.code `string` : ts_poet_1.code `string[]`;
467
+ case '.google.protobuf.Duration':
468
+ return ctx.options.useJsonWireFormat ? ts_poet_1.code `string` : undefined;
469
+ case '.google.protobuf.Timestamp':
470
+ return ctx.options.useJsonWireFormat ? ts_poet_1.code `string` : undefined;
463
471
  default:
464
472
  return undefined;
465
473
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "1.114.7",
3
+ "version": "1.115.2",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",
@@ -65,8 +65,8 @@
65
65
  "@types/object-hash": "^1.3.0",
66
66
  "dataloader": "^1.4.0",
67
67
  "object-hash": "^1.3.1",
68
- "protobufjs": "^6.8.8",
69
- "ts-poet": "^4.11.0",
68
+ "protobufjs": "^6.11.3",
69
+ "ts-poet": "^t 4.11.0",
70
70
  "ts-proto-descriptors": "1.6.0"
71
71
  }
72
72
  }