ts-proto 1.112.1 → 1.114.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 +21 -0
- package/README.markdown +57 -46
- package/build/enums.js +34 -6
- package/build/main.js +3 -2
- package/build/schema.js +188 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
# [1.114.0](https://github.com/stephenh/ts-proto/compare/v1.113.0...v1.114.0) (2022-05-27)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* Official Buf Plugin ([#573](https://github.com/stephenh/ts-proto/issues/573)) ([e6272c4](https://github.com/stephenh/ts-proto/commit/e6272c487f73ff96afb4146a7998a892c4b43f14))
|
|
7
|
+
|
|
8
|
+
# [1.113.0](https://github.com/stephenh/ts-proto/compare/v1.112.2...v1.113.0) (2022-05-27)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add options to schema output ([#437](https://github.com/stephenh/ts-proto/issues/437)) ([e8e4e39](https://github.com/stephenh/ts-proto/commit/e8e4e3937292e07280f5154b584e60124118f093))
|
|
14
|
+
|
|
15
|
+
## [1.112.2](https://github.com/stephenh/ts-proto/compare/v1.112.1...v1.112.2) (2022-05-18)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* enum type returns 'UNRECOGNIZED' or '-1' in xxxToJSON/xxxToNumber ([#566](https://github.com/stephenh/ts-proto/issues/566)) ([19911a1](https://github.com/stephenh/ts-proto/commit/19911a1c62e3fe5c8a0eb0a795489340512898da))
|
|
21
|
+
|
|
1
22
|
## [1.112.1](https://github.com/stephenh/ts-proto/compare/v1.112.0...v1.112.1) (2022-05-06)
|
|
2
23
|
|
|
3
24
|
|
package/README.markdown
CHANGED
|
@@ -13,17 +13,17 @@
|
|
|
13
13
|
- [Table of contents](#table-of-contents)
|
|
14
14
|
- [Overview](#overview)
|
|
15
15
|
- [QuickStart](#quickstart)
|
|
16
|
-
|
|
16
|
+
- [Buf](#buf)
|
|
17
17
|
- [Goals](#goals)
|
|
18
18
|
- [Example Types](#example-types)
|
|
19
19
|
- [Highlights](#highlights)
|
|
20
20
|
- [Auto-Batching / N+1 Prevention](#auto-batching--n1-prevention)
|
|
21
21
|
- [Usage](#usage)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
- [Supported options](#supported-options)
|
|
23
|
+
- [Only Types](#only-types)
|
|
24
|
+
- [NestJS Support](#nestjs-support)
|
|
25
|
+
- [Watch Mode](#watch-mode)
|
|
26
|
+
- [Basic gRPC implementation](#basic-grpc-implementation)
|
|
27
27
|
- [Sponsors](#sponsors)
|
|
28
28
|
- [Development](#development)
|
|
29
29
|
- [Assumptions](#assumptions)
|
|
@@ -97,7 +97,16 @@ plugins:
|
|
|
97
97
|
out: ../gen/ts
|
|
98
98
|
strategy: all
|
|
99
99
|
path: ../node_modules/ts-proto/protoc-gen-ts_proto
|
|
100
|
-
```
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
You can also use the official plugin published to the Buf Registry.
|
|
103
|
+
|
|
104
|
+
```yaml
|
|
105
|
+
version: v1
|
|
106
|
+
plugins:
|
|
107
|
+
- remote: buf.build/roundrock-io/plugins/ts-proto
|
|
108
|
+
out: ../gen/ts
|
|
109
|
+
```
|
|
101
110
|
|
|
102
111
|
# Goals
|
|
103
112
|
|
|
@@ -174,7 +183,7 @@ creating a class and calling the right getters/setters.
|
|
|
174
183
|
|
|
175
184
|
(Configurable with the `useDate` parameter.)
|
|
176
185
|
|
|
177
|
-
- `fromJSON`/`toJSON` use the [proto3 canonical JSON encoding format](https://developers.google.com/protocol-buffers/docs/proto3#json) (e.g. timestamps are ISO strings), unlike [`protobufjs`](https://github.com/protobufjs/protobuf.js/issues/1304).
|
|
186
|
+
- `fromJSON`/`toJSON` use the [proto3 canonical JSON encoding format](https://developers.google.com/protocol-buffers/docs/proto3#json) (e.g. timestamps are ISO strings), unlike [`protobufjs`](https://github.com/protobufjs/protobuf.js/issues/1304).
|
|
178
187
|
|
|
179
188
|
- ObjectIds can be mapped as `mongodb.ObjectId`
|
|
180
189
|
|
|
@@ -220,7 +229,7 @@ protoc --plugin=node_modules/ts-proto/protoc-gen-ts_proto ./batching.proto -I.
|
|
|
220
229
|
|
|
221
230
|
`ts-proto` can also be invoked with [Gradle](https://gradle.org) using the [protobuf-gradle-plugin](https://github.com/google/protobuf-gradle-plugin):
|
|
222
231
|
|
|
223
|
-
```
|
|
232
|
+
```groovy
|
|
224
233
|
protobuf {
|
|
225
234
|
plugins {
|
|
226
235
|
// `ts` can be replaced by any unused plugin name, e.g. `tsproto`
|
|
@@ -366,19 +375,19 @@ Generated code will be placed in the Gradle build directory.
|
|
|
366
375
|
|
|
367
376
|
- With `--ts_proto_opt=metadataType=Foo@./some-file`, ts-proto add a generic (framework-agnostic) metadata field to the generic service definition.
|
|
368
377
|
|
|
369
|
-
- With `--ts_proto_opt=outputServices=generic-definitions,outputServices=default`, ts-proto will output both generic definitions and interfaces.
|
|
378
|
+
- 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.
|
|
370
379
|
|
|
371
380
|
- With `--ts_proto_opt=outputServices=false`, or `=none`, ts-proto will output NO service definitions.
|
|
372
381
|
|
|
373
382
|
- With `--ts_proto_opt=emitImportedFiles=false`, ts-proto will not emit `google/protobuf/*` files unless you explicit add files to `protoc` like this
|
|
374
|
-
`protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto my_message.proto google/protobuf/duration.proto`
|
|
383
|
+
`protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto my_message.proto google/protobuf/duration.proto`
|
|
375
384
|
|
|
376
|
-
- With `--ts_proto_opt=fileSuffix=<SUFFIX>`, ts-proto will emit generated files using the specified suffix. A `helloworld.proto` file with `fileSuffix=.pb` would be generated as `helloworld.pb.ts`. This is common behavior in other protoc plugins and provides a way to quickly glob all the generated files.
|
|
385
|
+
- With `--ts_proto_opt=fileSuffix=<SUFFIX>`, ts-proto will emit generated files using the specified suffix. A `helloworld.proto` file with `fileSuffix=.pb` would be generated as `helloworld.pb.ts`. This is common behavior in other protoc plugins and provides a way to quickly glob all the generated files.
|
|
377
386
|
|
|
378
387
|
- With `--ts_proto_opt=enumsAsLiterals=true`, the generated enum types will be enum-ish object with `as const`.
|
|
379
388
|
|
|
380
389
|
- With `--ts_proto_opt=useExactTypes=false`, the generated `fromPartial` method will not use Exact types.
|
|
381
|
-
|
|
390
|
+
|
|
382
391
|
The default behavior is `useExactTypes=true`, which makes `fromPartial` use Exact type for its argument to make TypeScript reject any unknown properties.
|
|
383
392
|
|
|
384
393
|
- With `--ts_proto_opt=unknownFields=true`, all unknown fields will be parsed and output as arrays of buffers.
|
|
@@ -396,6 +405,7 @@ Generated code will be placed in the Gradle build directory.
|
|
|
396
405
|
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.
|
|
397
406
|
|
|
398
407
|
### Only Types
|
|
408
|
+
|
|
399
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.:
|
|
400
410
|
|
|
401
411
|
`--ts_proto_opt=onlyTypes=true`.
|
|
@@ -417,7 +427,7 @@ If you want to run `ts-proto` on every change of a proto file, you'll need to us
|
|
|
417
427
|
|
|
418
428
|
`ts-proto` is RPC framework agnostic - how you transmit your data to and from
|
|
419
429
|
your data source is up to you. The generated client implementations all expect
|
|
420
|
-
a `rpc` parameter, which type is defined like this:
|
|
430
|
+
a `rpc` parameter, which type is defined like this:
|
|
421
431
|
|
|
422
432
|
```ts
|
|
423
433
|
interface Rpc {
|
|
@@ -425,7 +435,7 @@ interface Rpc {
|
|
|
425
435
|
}
|
|
426
436
|
```
|
|
427
437
|
|
|
428
|
-
If you're working with gRPC, a simple implementation could look like this:
|
|
438
|
+
If you're working with gRPC, a simple implementation could look like this:
|
|
429
439
|
|
|
430
440
|
```ts
|
|
431
441
|
type RpcImpl = (service: string, method: string, data: Uint8Array) => Promise<Uint8Array>;
|
|
@@ -455,14 +465,14 @@ const sendRequest: RpcImpl = (service, method, data) => {
|
|
|
455
465
|
});
|
|
456
466
|
};
|
|
457
467
|
|
|
458
|
-
const rpc: Rpc = { request: sendRequest }
|
|
468
|
+
const rpc: Rpc = { request: sendRequest };
|
|
459
469
|
```
|
|
460
470
|
|
|
461
471
|
# Sponsors
|
|
462
472
|
|
|
463
473
|
Kudos to our sponsors:
|
|
464
474
|
|
|
465
|
-
|
|
475
|
+
- [ngrok](https://ngrok.com) funded ts-proto's initial grpc-web support.
|
|
466
476
|
|
|
467
477
|
If you need ts-proto customizations or priority support for your company, you can ping me at [via email](mailto:stephen.haberman@gmail.com).
|
|
468
478
|
|
|
@@ -481,9 +491,10 @@ The commands below assume you have **Docker** installed. To use a **local** copy
|
|
|
481
491
|
- Run `yarn install` to install the dependencies.
|
|
482
492
|
- Run `yarn build:test` or `yarn build:test:local` to generate the test files.
|
|
483
493
|
> _This runs the following commands:_
|
|
484
|
-
>
|
|
485
|
-
>
|
|
486
|
-
>
|
|
494
|
+
>
|
|
495
|
+
> - `proto2bin` — Converts integration test `.proto` files to `.bin`.
|
|
496
|
+
> - `bin2ts` — Runs `ts-proto` on the `.bin` files to generate `.ts` files.
|
|
497
|
+
> - `proto2pbjs` — Generates a reference implementation using `pbjs` for testing compatibility.
|
|
487
498
|
- Run `yarn test`
|
|
488
499
|
|
|
489
500
|
**Workflow**
|
|
@@ -494,8 +505,8 @@ The commands below assume you have **Docker** installed. To use a **local** copy
|
|
|
494
505
|
_Since the proto files were not changed, you only need to regenerate the typescript files._
|
|
495
506
|
- Run `yarn test` to verify the typescript files are compatible with the reference implementation, and pass other tests.
|
|
496
507
|
- Updating or adding `.proto` files in the integration directory:
|
|
497
|
-
- Run `yarn watch` to automatically regenerate test files when proto files change.
|
|
498
|
-
- Or run `yarn build:test` to regenerate all integration test files.
|
|
508
|
+
- Run `yarn watch` to automatically regenerate test files when proto files change.
|
|
509
|
+
- Or run `yarn build:test` to regenerate all integration test files.
|
|
499
510
|
- Run `yarn test` to retest.
|
|
500
511
|
|
|
501
512
|
**Contributing**
|
|
@@ -503,7 +514,7 @@ The commands below assume you have **Docker** installed. To use a **local** copy
|
|
|
503
514
|
- Run `yarn build:test` and `yarn test` to make sure everything works.
|
|
504
515
|
- Run `yarn prettier` to format the typescript files.
|
|
505
516
|
- Commit the changes:
|
|
506
|
-
- Also include the generated `.bin` files for the tests where you added or modified `.proto` files.
|
|
517
|
+
- Also include the generated `.bin` files for the tests where you added or modified `.proto` files.
|
|
507
518
|
> These are checked into git so that the test suite can run without having to invoke the `protoc` build chain.
|
|
508
519
|
- Also include the generated `.ts` files.
|
|
509
520
|
- Create a pull request
|
|
@@ -594,7 +605,7 @@ Foo.encode({ bar: '' }); // => { }, writes an empty Foo object, in protobuf bina
|
|
|
594
605
|
Reading JSON will also initialize the default values. Since senders may either omit unset fields, or set them to the default value, use `fromJSON` to normalize the input.
|
|
595
606
|
|
|
596
607
|
```typescript
|
|
597
|
-
Foo.fromJSON({
|
|
608
|
+
Foo.fromJSON({}); // => { bar: '' }
|
|
598
609
|
Foo.fromJSON({ bar: '' }); // => { bar: '' }
|
|
599
610
|
Foo.fromJSON({ bar: 'baz' }); // => { bar: 'baz' }
|
|
600
611
|
```
|
|
@@ -603,7 +614,7 @@ When writing JSON, `ts-proto` currently does **not** normalize message when conv
|
|
|
603
614
|
|
|
604
615
|
```typescript
|
|
605
616
|
// Current ts-proto behavior
|
|
606
|
-
Foo.toJSON({
|
|
617
|
+
Foo.toJSON({}); // => { }
|
|
607
618
|
Foo.toJSON({ bar: undefined }); // => { }
|
|
608
619
|
Foo.toJSON({ bar: '' }); // => { bar: '' } - note: this is the default value, but it's not omitted
|
|
609
620
|
Foo.toJSON({ bar: 'baz' }); // => { bar: 'baz' }
|
|
@@ -611,13 +622,13 @@ Foo.toJSON({ bar: 'baz' }); // => { bar: 'baz' }
|
|
|
611
622
|
|
|
612
623
|
```typescript
|
|
613
624
|
// Possible future behavior, where ts-proto would normalize message
|
|
614
|
-
Foo.toJSON({
|
|
625
|
+
Foo.toJSON({}); // => { }
|
|
615
626
|
Foo.toJSON({ bar: undefined }); // => { }
|
|
616
627
|
Foo.toJSON({ bar: '' }); // => { } - note: omitting the default value, as expected
|
|
617
628
|
Foo.toJSON({ bar: 'baz' }); // => { bar: 'baz' }
|
|
618
629
|
```
|
|
619
630
|
|
|
620
|
-
-
|
|
631
|
+
- Please open an issue if you need this behavior.
|
|
621
632
|
|
|
622
633
|
# Well-Known Types
|
|
623
634
|
|
|
@@ -626,25 +637,24 @@ Their interpretation is defined by the Protobuf specification, and libraries are
|
|
|
626
637
|
|
|
627
638
|
`ts-proto` currently automatically converts these messages to their corresponding native types.
|
|
628
639
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
640
|
+
- [google.protobuf.BoolValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#boolvalue) ⇆ `boolean`
|
|
641
|
+
- [google.protobuf.BytesValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#bytesvalue) ⇆ `Uint8Array`
|
|
642
|
+
- [google.protobuf.DoubleValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#doublevalue) ⇆ `number`
|
|
643
|
+
- [google.protobuf.FieldMask](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#fieldmask) ⇆ `string[]`
|
|
644
|
+
- [google.protobuf.FloatValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#floatvalue) ⇆ `number`
|
|
645
|
+
- [google.protobuf.Int32Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#int32value) ⇆ `number`
|
|
646
|
+
- [google.protobuf.Int64Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#int64value) ⇆ `number`
|
|
647
|
+
- [google.protobuf.ListValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#listvalue) ⇆ `any[]`
|
|
648
|
+
- [google.protobuf.UInt32Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#uint32value) ⇆ `number`
|
|
649
|
+
- [google.protobuf.UInt64Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#uint64value) ⇆ `number`
|
|
650
|
+
- [google.protobuf.StringValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#stringvalue) ⇆ `string`
|
|
651
|
+
- [google.protobuf.Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#value) ⇆ `any` (i.e. `number | string | boolean | null | array | object`)
|
|
652
|
+
- [google.protobuf.Struct](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#struct) ⇆ `{ [key: string]: any }`
|
|
642
653
|
|
|
643
654
|
## Wrapper Types
|
|
644
655
|
|
|
645
656
|
Wrapper Types are messages containing a single primitive field, and can be imported in `.proto` files with `import "google/protobuf/wrappers.proto"`.
|
|
646
657
|
|
|
647
|
-
|
|
648
658
|
Since these are _messages_, their default value is `undefined`, allowing you to distinguish unset primitives from their default values, when using Wrapper Types.
|
|
649
659
|
`ts-proto` generates these fields as `<primitive> | undefined`.
|
|
650
660
|
|
|
@@ -671,13 +681,13 @@ interface ExampleMessage {
|
|
|
671
681
|
When encoding a message the primitive value is converted back to its corresponding wrapper type:
|
|
672
682
|
|
|
673
683
|
```typescript
|
|
674
|
-
ExampleMessage.encode({ name: 'foo' }) // => { name: { value: 'foo' } }, in binary
|
|
684
|
+
ExampleMessage.encode({ name: 'foo' }); // => { name: { value: 'foo' } }, in binary
|
|
675
685
|
```
|
|
676
686
|
|
|
677
687
|
When calling toJSON, the value is not converted, because wrapper types are idiomatic in JSON.
|
|
678
688
|
|
|
679
689
|
```typescript
|
|
680
|
-
ExampleMessage.toJSON({ name: 'foo' }) // => { name: 'foo' }
|
|
690
|
+
ExampleMessage.toJSON({ name: 'foo' }); // => { name: 'foo' }
|
|
681
691
|
```
|
|
682
692
|
|
|
683
693
|
## JSON Types (Struct Types)
|
|
@@ -687,7 +697,7 @@ For this reason, Protobuf offers several additional types to represent arbitrary
|
|
|
687
697
|
|
|
688
698
|
These are called Struct Types, and can be imported in `.proto` files with `import "google/protobuf/struct.proto"`.
|
|
689
699
|
|
|
690
|
-
- [google.protobuf.Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#Value) ⇆ `any`
|
|
700
|
+
- [google.protobuf.Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#Value) ⇆ `any`
|
|
691
701
|
- This is the most general type, and can represent any JSON value (i.e. `number | string | boolean | null | array | object`).
|
|
692
702
|
- [google.protobuf.ListValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#ListValue) ⇆ `any[]`
|
|
693
703
|
- To represent a JSON array
|
|
@@ -717,8 +727,9 @@ interface ExampleMessage {
|
|
|
717
727
|
```
|
|
718
728
|
|
|
719
729
|
Encoding a JSON value embedded in a message, converts it to a Struct Type:
|
|
730
|
+
|
|
720
731
|
```typescript
|
|
721
|
-
ExampleMessage.encode({ anything: {
|
|
732
|
+
ExampleMessage.encode({ anything: { name: 'hello' } });
|
|
722
733
|
/* Outputs the following structure, encoded in protobuf binary format:
|
|
723
734
|
{
|
|
724
735
|
anything: Value {
|
|
@@ -735,7 +746,7 @@ ExampleMessage.encode({ anything: { "name": "hello" } })
|
|
|
735
746
|
}
|
|
736
747
|
}*/
|
|
737
748
|
|
|
738
|
-
ExampleMessage.encode({ anything: true })
|
|
749
|
+
ExampleMessage.encode({ anything: true });
|
|
739
750
|
/* Outputs the following structure encoded in protobuf binary format:
|
|
740
751
|
{
|
|
741
752
|
anything: Value {
|
package/build/enums.js
CHANGED
|
@@ -43,11 +43,11 @@ function generateEnum(ctx, fullName, enumDesc, sourceInfo) {
|
|
|
43
43
|
}
|
|
44
44
|
if (options.outputJsonMethods) {
|
|
45
45
|
chunks.push(ts_poet_1.code `\n`);
|
|
46
|
-
chunks.push(generateEnumToJson(fullName, enumDesc));
|
|
46
|
+
chunks.push(generateEnumToJson(ctx, fullName, enumDesc));
|
|
47
47
|
}
|
|
48
48
|
if (options.stringEnums && options.outputEncodeMethods) {
|
|
49
49
|
chunks.push(ts_poet_1.code `\n`);
|
|
50
|
-
chunks.push(generateEnumToNumber(fullName, enumDesc));
|
|
50
|
+
chunks.push(generateEnumToNumber(ctx, fullName, enumDesc));
|
|
51
51
|
}
|
|
52
52
|
return ts_poet_1.joinCode(chunks, { on: '\n' });
|
|
53
53
|
}
|
|
@@ -87,7 +87,8 @@ function generateEnumFromJson(ctx, fullName, enumDesc) {
|
|
|
87
87
|
}
|
|
88
88
|
exports.generateEnumFromJson = generateEnumFromJson;
|
|
89
89
|
/** Generates a function with a big switch statement to encode our enum -> JSON. */
|
|
90
|
-
function generateEnumToJson(fullName, enumDesc) {
|
|
90
|
+
function generateEnumToJson(ctx, fullName, enumDesc) {
|
|
91
|
+
const { options, utils } = ctx;
|
|
91
92
|
const chunks = [];
|
|
92
93
|
const functionName = case_1.camelCase(fullName) + 'ToJSON';
|
|
93
94
|
chunks.push(ts_poet_1.code `export function ${ts_poet_1.def(functionName)}(object: ${fullName}): string {`);
|
|
@@ -95,14 +96,28 @@ function generateEnumToJson(fullName, enumDesc) {
|
|
|
95
96
|
for (const valueDesc of enumDesc.value) {
|
|
96
97
|
chunks.push(ts_poet_1.code `case ${fullName}.${valueDesc.name}: return "${valueDesc.name}";`);
|
|
97
98
|
}
|
|
98
|
-
|
|
99
|
+
if (options.unrecognizedEnum) {
|
|
100
|
+
chunks.push(ts_poet_1.code `
|
|
101
|
+
case ${fullName}.${UNRECOGNIZED_ENUM_NAME}:
|
|
102
|
+
default:
|
|
103
|
+
return "${UNRECOGNIZED_ENUM_NAME}";
|
|
104
|
+
`);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// We use globalThis to avoid conflicts on protobuf types named `Error`.
|
|
108
|
+
chunks.push(ts_poet_1.code `
|
|
109
|
+
default:
|
|
110
|
+
throw new ${utils.globalThis}.Error("Unrecognized enum value " + object + " for enum ${fullName}");
|
|
111
|
+
`);
|
|
112
|
+
}
|
|
99
113
|
chunks.push(ts_poet_1.code `}`);
|
|
100
114
|
chunks.push(ts_poet_1.code `}`);
|
|
101
115
|
return ts_poet_1.joinCode(chunks, { on: '\n' });
|
|
102
116
|
}
|
|
103
117
|
exports.generateEnumToJson = generateEnumToJson;
|
|
104
118
|
/** Generates a function with a big switch statement to encode our string enum -> int value. */
|
|
105
|
-
function generateEnumToNumber(fullName, enumDesc) {
|
|
119
|
+
function generateEnumToNumber(ctx, fullName, enumDesc) {
|
|
120
|
+
const { options, utils } = ctx;
|
|
106
121
|
const chunks = [];
|
|
107
122
|
const functionName = case_1.camelCase(fullName) + 'ToNumber';
|
|
108
123
|
chunks.push(ts_poet_1.code `export function ${ts_poet_1.def(functionName)}(object: ${fullName}): number {`);
|
|
@@ -110,7 +125,20 @@ function generateEnumToNumber(fullName, enumDesc) {
|
|
|
110
125
|
for (const valueDesc of enumDesc.value) {
|
|
111
126
|
chunks.push(ts_poet_1.code `case ${fullName}.${valueDesc.name}: return ${valueDesc.number};`);
|
|
112
127
|
}
|
|
113
|
-
|
|
128
|
+
if (options.unrecognizedEnum) {
|
|
129
|
+
chunks.push(ts_poet_1.code `
|
|
130
|
+
case ${fullName}.${UNRECOGNIZED_ENUM_NAME}:
|
|
131
|
+
default:
|
|
132
|
+
return ${UNRECOGNIZED_ENUM_VALUE};
|
|
133
|
+
`);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// We use globalThis to avoid conflicts on protobuf types named `Error`.
|
|
137
|
+
chunks.push(ts_poet_1.code `
|
|
138
|
+
default:
|
|
139
|
+
throw new ${utils.globalThis}.Error("Unrecognized enum value " + object + " for enum ${fullName}");
|
|
140
|
+
`);
|
|
141
|
+
}
|
|
114
142
|
chunks.push(ts_poet_1.code `}`);
|
|
115
143
|
chunks.push(ts_poet_1.code `}`);
|
|
116
144
|
return ts_poet_1.joinCode(chunks, { on: '\n' });
|
package/build/main.js
CHANGED
|
@@ -453,7 +453,7 @@ function makeNiceGrpcServerStreamingMethodResult() {
|
|
|
453
453
|
const NiceGrpcServerStreamingMethodResult = ts_poet_1.conditionalOutput('ServerStreamingMethodResult', ts_poet_1.code `
|
|
454
454
|
export type ServerStreamingMethodResult<Response> = {
|
|
455
455
|
[Symbol.asyncIterator](): AsyncIterator<Response, void>;
|
|
456
|
-
};
|
|
456
|
+
};
|
|
457
457
|
`);
|
|
458
458
|
return { NiceGrpcServerStreamingMethodResult };
|
|
459
459
|
}
|
|
@@ -1004,7 +1004,8 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
|
|
|
1004
1004
|
return ts_poet_1.code `Long.fromValue(${from} as Long | string)`;
|
|
1005
1005
|
}
|
|
1006
1006
|
else if (types_1.isEnum(valueField)) {
|
|
1007
|
-
|
|
1007
|
+
const fromJson = types_1.getEnumMethod(ctx, valueField.typeName, 'FromJSON');
|
|
1008
|
+
return ts_poet_1.code `${fromJson}(${from})`;
|
|
1008
1009
|
}
|
|
1009
1010
|
else {
|
|
1010
1011
|
const cstr = case_1.capitalize(valueType.toCodeString());
|
package/build/schema.js
CHANGED
|
@@ -5,16 +5,50 @@ const ts_proto_descriptors_1 = require("ts-proto-descriptors");
|
|
|
5
5
|
const ts_poet_1 = require("ts-poet");
|
|
6
6
|
const visit_1 = require("./visit");
|
|
7
7
|
const utils_1 = require("./utils");
|
|
8
|
+
const types_1 = require("./types");
|
|
9
|
+
const minimal_1 = require("protobufjs/minimal");
|
|
8
10
|
const fileDescriptorProto = ts_poet_1.imp('FileDescriptorProto@ts-proto-descriptors');
|
|
11
|
+
const extensionCache = {};
|
|
9
12
|
function generateSchema(ctx, fileDesc, sourceInfo) {
|
|
10
13
|
var _a;
|
|
11
14
|
const { options } = ctx;
|
|
12
15
|
const chunks = [];
|
|
16
|
+
fileDesc.extension.forEach((extension) => {
|
|
17
|
+
if (!(extension.extendee in extensionCache)) {
|
|
18
|
+
extensionCache[extension.extendee] = {};
|
|
19
|
+
}
|
|
20
|
+
extensionCache[extension.extendee][extension.number] = extension;
|
|
21
|
+
});
|
|
13
22
|
chunks.push(ts_poet_1.code `
|
|
23
|
+
type ProtoMetaMessageOptions = {
|
|
24
|
+
options?: { [key: string]: any };
|
|
25
|
+
fields?: { [key: string]: { [key: string]: any } };
|
|
26
|
+
oneof?: { [key: string]: { [key: string]: any } };
|
|
27
|
+
nested?: { [key: string]: ProtoMetaMessageOptions };
|
|
28
|
+
};
|
|
29
|
+
|
|
14
30
|
export interface ProtoMetadata {
|
|
15
31
|
fileDescriptor: ${fileDescriptorProto};
|
|
16
32
|
references: { [key: string]: any };
|
|
17
33
|
dependencies?: ProtoMetadata[];
|
|
34
|
+
options?: {
|
|
35
|
+
options?: { [key: string]: any };
|
|
36
|
+
services?: {
|
|
37
|
+
[key: string]: {
|
|
38
|
+
options?: { [key: string]: any };
|
|
39
|
+
methods?: { [key: string]: { [key: string]: any } };
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
messages?: {
|
|
43
|
+
[key: string]: ProtoMetaMessageOptions;
|
|
44
|
+
};
|
|
45
|
+
enums?: {
|
|
46
|
+
[key: string]: {
|
|
47
|
+
options?: { [key: string]: any };
|
|
48
|
+
values?: { [key: string]: { [key: string]: any } };
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
};
|
|
18
52
|
}
|
|
19
53
|
`);
|
|
20
54
|
const references = [];
|
|
@@ -42,13 +76,167 @@ function generateSchema(ctx, fileDesc, sourceInfo) {
|
|
|
42
76
|
descriptor.sourceCodeInfo = {
|
|
43
77
|
location: ((_a = descriptor.sourceCodeInfo) === null || _a === void 0 ? void 0 : _a.location.filter((loc) => loc['leadingComments'] || loc['trailingComments'])) || [],
|
|
44
78
|
};
|
|
79
|
+
let fileOptions;
|
|
80
|
+
if (fileDesc.options) {
|
|
81
|
+
fileOptions = encodedOptionsToOptions(ctx, '.google.protobuf.FileOptions', fileDesc.options['_unknownFields']);
|
|
82
|
+
delete fileDesc.options['_unknownFields'];
|
|
83
|
+
}
|
|
84
|
+
const messagesOptions = [];
|
|
85
|
+
(fileDesc.messageType || []).forEach((message) => {
|
|
86
|
+
const resolvedMessage = resolveMessageOptions(ctx, message);
|
|
87
|
+
if (resolvedMessage) {
|
|
88
|
+
messagesOptions.push(resolvedMessage);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
const servicesOptions = [];
|
|
92
|
+
(fileDesc.service || []).forEach((service) => {
|
|
93
|
+
const methodsOptions = [];
|
|
94
|
+
service.method.forEach((method) => {
|
|
95
|
+
if (method.options) {
|
|
96
|
+
const methodOptions = encodedOptionsToOptions(ctx, '.google.protobuf.MethodOptions', method.options['_unknownFields']);
|
|
97
|
+
delete method.options['_unknownFields'];
|
|
98
|
+
if (methodOptions) {
|
|
99
|
+
methodsOptions.push(ts_poet_1.code `'${method.name}': ${methodOptions}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
let serviceOptions;
|
|
104
|
+
if (service.options) {
|
|
105
|
+
serviceOptions = encodedOptionsToOptions(ctx, '.google.protobuf.ServiceOptions', service.options['_unknownFields']);
|
|
106
|
+
delete service.options['_unknownFields'];
|
|
107
|
+
}
|
|
108
|
+
if (methodsOptions.length > 0 || serviceOptions) {
|
|
109
|
+
servicesOptions.push(ts_poet_1.code `
|
|
110
|
+
'${service.name}': {
|
|
111
|
+
options: ${serviceOptions},
|
|
112
|
+
methods: {${ts_poet_1.joinCode(methodsOptions, { on: ',' })}}
|
|
113
|
+
}
|
|
114
|
+
`);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
const enumsOptions = [];
|
|
118
|
+
(fileDesc.enumType || []).forEach((Enum) => {
|
|
119
|
+
const valuesOptions = [];
|
|
120
|
+
Enum.value.forEach((value) => {
|
|
121
|
+
if (value.options) {
|
|
122
|
+
const valueOptions = encodedOptionsToOptions(ctx, '.google.protobuf.EnumValueOptions', value.options['_unknownFields']);
|
|
123
|
+
delete value.options['_unknownFields'];
|
|
124
|
+
if (valueOptions) {
|
|
125
|
+
valuesOptions.push(ts_poet_1.code `'${value.name}': ${valueOptions}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
let enumOptions;
|
|
130
|
+
if (Enum.options) {
|
|
131
|
+
enumOptions = encodedOptionsToOptions(ctx, '.google.protobuf.EnumOptions', Enum.options['_unknownFields']);
|
|
132
|
+
delete Enum.options['_unknownFields'];
|
|
133
|
+
}
|
|
134
|
+
if (valuesOptions.length > 0 || enumOptions) {
|
|
135
|
+
enumsOptions.push(ts_poet_1.code `
|
|
136
|
+
'${Enum.name}': {
|
|
137
|
+
options: ${enumOptions},
|
|
138
|
+
values: {${ts_poet_1.joinCode(valuesOptions, { on: ',' })}}
|
|
139
|
+
}
|
|
140
|
+
`);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
45
143
|
chunks.push(ts_poet_1.code `
|
|
46
144
|
export const ${ts_poet_1.def('protoMetadata')}: ProtoMetadata = {
|
|
47
145
|
fileDescriptor: ${fileDescriptorProto}.fromPartial(${descriptor}),
|
|
48
146
|
references: { ${ts_poet_1.joinCode(references, { on: ',' })} },
|
|
49
147
|
dependencies: [${ts_poet_1.joinCode(dependencies, { on: ',' })}],
|
|
148
|
+
${fileOptions || messagesOptions.length > 0 || servicesOptions.length > 0 || enumsOptions.length > 0
|
|
149
|
+
? ts_poet_1.code `options: {
|
|
150
|
+
${fileOptions ? ts_poet_1.code `options: ${fileOptions},` : ''}
|
|
151
|
+
${messagesOptions.length > 0 ? ts_poet_1.code `messages: {${ts_poet_1.joinCode(messagesOptions, { on: ',' })}},` : ''}
|
|
152
|
+
${servicesOptions.length > 0 ? ts_poet_1.code `services: {${ts_poet_1.joinCode(servicesOptions, { on: ',' })}},` : ''}
|
|
153
|
+
${enumsOptions.length > 0 ? ts_poet_1.code `enums: {${ts_poet_1.joinCode(enumsOptions, { on: ',' })}}` : ''}
|
|
154
|
+
}`
|
|
155
|
+
: ''}
|
|
50
156
|
}
|
|
51
157
|
`);
|
|
52
158
|
return chunks;
|
|
53
159
|
}
|
|
54
160
|
exports.generateSchema = generateSchema;
|
|
161
|
+
function getExtensionValue(ctx, extension, data) {
|
|
162
|
+
if (extension.type == ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_MESSAGE) {
|
|
163
|
+
const typeName = types_1.basicTypeName(ctx, extension);
|
|
164
|
+
const resultBuffer = Buffer.concat(data.map((d) => {
|
|
165
|
+
// Skip length byte
|
|
166
|
+
const reader = new minimal_1.Reader(d);
|
|
167
|
+
reader.uint32();
|
|
168
|
+
return reader.buf.slice(reader.pos);
|
|
169
|
+
}));
|
|
170
|
+
const result = resultBuffer.toString('base64');
|
|
171
|
+
return ts_poet_1.code `'${extension.name}': ${typeName}.decode(Buffer.from('${result}', 'base64'))`;
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
const reader = new minimal_1.Reader(data[0]);
|
|
175
|
+
let value = reader[types_1.toReaderCall(extension)]();
|
|
176
|
+
if (typeof value === 'string') {
|
|
177
|
+
value = ts_poet_1.code `"${value}"`;
|
|
178
|
+
}
|
|
179
|
+
return ts_poet_1.code `'${extension.name}': ${value}`;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** Takes the protoc's input of options as proto-encoded messages, and turns them into embedded-able-in-source-code representations. */
|
|
183
|
+
function encodedOptionsToOptions(ctx, extendee, encodedOptions) {
|
|
184
|
+
if (!encodedOptions) {
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
const resultOptions = [];
|
|
188
|
+
for (const [key, value] of Object.entries(encodedOptions)) {
|
|
189
|
+
const extension = extensionCache[extendee][parseInt(key, 10) >>> 3];
|
|
190
|
+
resultOptions.push(getExtensionValue(ctx, extension, value));
|
|
191
|
+
}
|
|
192
|
+
if (resultOptions.length == 0) {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
return ts_poet_1.code `{${ts_poet_1.joinCode(resultOptions, { on: ',' })}}`;
|
|
196
|
+
}
|
|
197
|
+
function resolveMessageOptions(ctx, message) {
|
|
198
|
+
const fieldsOptions = [];
|
|
199
|
+
message.field.forEach((field) => {
|
|
200
|
+
if (field.options) {
|
|
201
|
+
const fieldOptions = encodedOptionsToOptions(ctx, '.google.protobuf.FieldOptions', field.options['_unknownFields']);
|
|
202
|
+
delete field.options['_unknownFields'];
|
|
203
|
+
if (fieldOptions) {
|
|
204
|
+
fieldsOptions.push(ts_poet_1.code `'${field.name}': ${fieldOptions}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
const oneOfsOptions = [];
|
|
209
|
+
message.oneofDecl.forEach((oneOf) => {
|
|
210
|
+
if (oneOf.options) {
|
|
211
|
+
const oneOfOptions = encodedOptionsToOptions(ctx, '.google.protobuf.OneofOptions', oneOf.options['_unknownFields']);
|
|
212
|
+
delete oneOf.options['_unknownFields'];
|
|
213
|
+
if (oneOfOptions) {
|
|
214
|
+
oneOfsOptions.push(ts_poet_1.code `'${oneOf.name}': ${oneOfOptions}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
let nestedOptions = [];
|
|
219
|
+
if (message.nestedType && message.nestedType.length > 0) {
|
|
220
|
+
message.nestedType.forEach((nested) => {
|
|
221
|
+
const resolvedMessage = resolveMessageOptions(ctx, nested);
|
|
222
|
+
if (resolvedMessage) {
|
|
223
|
+
nestedOptions.push(resolvedMessage);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
let messageOptions;
|
|
228
|
+
if (message.options) {
|
|
229
|
+
messageOptions = encodedOptionsToOptions(ctx, '.google.protobuf.MessageOptions', message.options['_unknownFields']);
|
|
230
|
+
delete message.options['_unknownFields'];
|
|
231
|
+
}
|
|
232
|
+
if (fieldsOptions.length > 0 || oneOfsOptions.length > 0 || nestedOptions.length > 0 || messageOptions) {
|
|
233
|
+
return ts_poet_1.code `
|
|
234
|
+
'${message.name}': {
|
|
235
|
+
${messageOptions ? ts_poet_1.code `options: ${messageOptions},` : ''}
|
|
236
|
+
${fieldsOptions.length > 0 ? ts_poet_1.code `fields: {${ts_poet_1.joinCode(fieldsOptions, { on: ',' })}},` : ''}
|
|
237
|
+
${oneOfsOptions.length > 0 ? ts_poet_1.code `oneof: {${ts_poet_1.joinCode(oneOfsOptions, { on: ',' })}},` : ''}
|
|
238
|
+
${nestedOptions.length > 0 ? ts_poet_1.code `nested: {${ts_poet_1.joinCode(nestedOptions, { on: ',' })}},` : ''}
|
|
239
|
+
}
|
|
240
|
+
`;
|
|
241
|
+
}
|
|
242
|
+
}
|