diff --git a/README.markdown b/README.markdown index de04740f2..1ef3d13be 100644 --- a/README.markdown +++ b/README.markdown @@ -381,6 +381,8 @@ Generated code will be placed in the Gradle build directory. - With `--ts_proto_opt=outputServices=false`, or `=none`, ts-proto will output NO service definitions. +- With `--ts_proto_opt=useAsyncIterable=true`, the generated services will use `AsyncIterable` instead of `Observable`. + - With `--ts_proto_opt=emitImportedFiles=false`, ts-proto will not emit `google/protobuf/*` files unless you explicit add files to `protoc` like this `protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto my_message.proto google/protobuf/duration.proto` @@ -411,6 +413,7 @@ Generated code will be placed in the Gradle build directory. 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. You may also want to set `useOptionals=all`, as gRPC gateways are not required to send default value for scalar values. + ### NestJS Support We have a great way of working together with [nestjs](https://docs.nestjs.com/microservices/grpc). `ts-proto` generates `interfaces` and `decorators` for you controller, client. For more information see the [nestjs readme](NESTJS.markdown). diff --git a/integration/angular/simple-message.bin b/integration/angular/simple-message.bin index 7a2445434..1c6d51f2e 100644 Binary files a/integration/angular/simple-message.bin and b/integration/angular/simple-message.bin differ diff --git a/integration/async-iterable-services/parameters.txt b/integration/async-iterable-services/parameters.txt new file mode 100644 index 000000000..6d94ef9f6 --- /dev/null +++ b/integration/async-iterable-services/parameters.txt @@ -0,0 +1 @@ +useAsyncIterable=true diff --git a/integration/async-iterable-services/simple.bin b/integration/async-iterable-services/simple.bin new file mode 100644 index 000000000..6ad828185 Binary files /dev/null and b/integration/async-iterable-services/simple.bin differ diff --git a/integration/async-iterable-services/simple.proto b/integration/async-iterable-services/simple.proto new file mode 100644 index 000000000..6456036f0 --- /dev/null +++ b/integration/async-iterable-services/simple.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package simple; + +service Test { + rpc BidiStreaming(stream TestMessage) returns (stream TestMessage) {} +} + +message TestMessage { + string value = 1; +} diff --git a/integration/async-iterable-services/simple.ts b/integration/async-iterable-services/simple.ts new file mode 100644 index 000000000..8a335f812 --- /dev/null +++ b/integration/async-iterable-services/simple.ts @@ -0,0 +1,138 @@ +/* eslint-disable */ +import * as _m0 from 'protobufjs/minimal'; + +export const protobufPackage = 'simple'; + +export interface TestMessage { + value: string; +} + +function createBaseTestMessage(): TestMessage { + return { value: '' }; +} + +export const TestMessage = { + encode(message: TestMessage, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.value !== '') { + writer.uint32(10).string(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): TestMessage { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTestMessage(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + // encodeTransform encodes a source of message objects. + // Transform + async *encodeTransform( + source: AsyncIterable | Iterable + ): AsyncIterable { + for await (const pkt of source) { + if (Array.isArray(pkt)) { + for (const p of pkt) { + yield* [TestMessage.encode(p).finish()]; + } + } else { + yield* [TestMessage.encode(pkt).finish()]; + } + } + }, + + // decodeTransform decodes a source of encoded messages. + // Transform + async *decodeTransform( + source: AsyncIterable | Iterable + ): AsyncIterable { + for await (const pkt of source) { + if (Array.isArray(pkt)) { + for (const p of pkt) { + yield* [TestMessage.decode(p)]; + } + } else { + yield* [TestMessage.decode(pkt)]; + } + } + }, + + fromJSON(object: any): TestMessage { + return { + value: isSet(object.value) ? String(object.value) : '', + }; + }, + + toJSON(message: TestMessage): unknown { + const obj: any = {}; + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial, I>>(object: I): TestMessage { + const message = createBaseTestMessage(); + message.value = object.value ?? ''; + return message; + }, +}; + +export interface Test { + BidiStreaming(request: AsyncIterable): AsyncIterable; +} + +export class TestClientImpl implements Test { + private readonly rpc: Rpc; + constructor(rpc: Rpc) { + this.rpc = rpc; + this.BidiStreaming = this.BidiStreaming.bind(this); + } + BidiStreaming(request: AsyncIterable): AsyncIterable { + const data = TestMessage.encodeTransform(request); + const result = this.rpc.bidirectionalStreamingRequest('simple.Test', 'BidiStreaming', data); + return TestMessage.decodeTransform(result); + } +} + +interface Rpc { + request(service: string, method: string, data: Uint8Array): Promise; + clientStreamingRequest(service: string, method: string, data: AsyncIterable): Promise; + serverStreamingRequest(service: string, method: string, data: Uint8Array): AsyncIterable; + bidirectionalStreamingRequest( + service: string, + method: string, + data: AsyncIterable + ): AsyncIterable; +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin + ? P + : P & { [K in keyof P]: Exact } & Record>, never>; + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/integration/avoid-import-conflicts-types-only/simple.bin b/integration/avoid-import-conflicts-types-only/simple.bin index 75915195a..cce5b2dda 100644 Binary files a/integration/avoid-import-conflicts-types-only/simple.bin and b/integration/avoid-import-conflicts-types-only/simple.bin differ diff --git a/integration/avoid-import-conflicts-types-only/simple2.bin b/integration/avoid-import-conflicts-types-only/simple2.bin index 4a0fddc47..9cabb6450 100644 Binary files a/integration/avoid-import-conflicts-types-only/simple2.bin and b/integration/avoid-import-conflicts-types-only/simple2.bin differ diff --git a/integration/avoid-import-conflicts/simple.bin b/integration/avoid-import-conflicts/simple.bin index 57bd5482b..de9aa5ecb 100644 Binary files a/integration/avoid-import-conflicts/simple.bin and b/integration/avoid-import-conflicts/simple.bin differ diff --git a/integration/avoid-import-conflicts/simple2.bin b/integration/avoid-import-conflicts/simple2.bin index 7b35fb39e..eac364294 100644 Binary files a/integration/avoid-import-conflicts/simple2.bin and b/integration/avoid-import-conflicts/simple2.bin differ diff --git a/integration/barrel-imports/bar.bin b/integration/barrel-imports/bar.bin index 8b84bf739..4bf3c3d5d 100644 Binary files a/integration/barrel-imports/bar.bin and b/integration/barrel-imports/bar.bin differ diff --git a/integration/barrel-imports/foo.bin b/integration/barrel-imports/foo.bin index 26df53209..94b6993ee 100644 Binary files a/integration/barrel-imports/foo.bin and b/integration/barrel-imports/foo.bin differ diff --git a/integration/batching-with-context/batching.bin b/integration/batching-with-context/batching.bin index 5d6ad57e0..690868e26 100644 Binary files a/integration/batching-with-context/batching.bin and b/integration/batching-with-context/batching.bin differ diff --git a/integration/batching/batching.bin b/integration/batching/batching.bin index 5d6ad57e0..690868e26 100644 Binary files a/integration/batching/batching.bin and b/integration/batching/batching.bin differ diff --git a/integration/bytes-as-base64/message.bin b/integration/bytes-as-base64/message.bin index b035d9e09..51cbccd6e 100644 Binary files a/integration/bytes-as-base64/message.bin and b/integration/bytes-as-base64/message.bin differ diff --git a/integration/bytes-node/point.bin b/integration/bytes-node/point.bin index da2590837..17cf5a338 100644 Binary files a/integration/bytes-node/point.bin and b/integration/bytes-node/point.bin differ diff --git a/integration/const-enum/const-enum.bin b/integration/const-enum/const-enum.bin index 978b99505..199d5f65e 100644 Binary files a/integration/const-enum/const-enum.bin and b/integration/const-enum/const-enum.bin differ diff --git a/integration/enums-as-literals-with-string-enums/enums-as-literals-with-string-enums.bin b/integration/enums-as-literals-with-string-enums/enums-as-literals-with-string-enums.bin index b5d5687ae..373515557 100644 Binary files a/integration/enums-as-literals-with-string-enums/enums-as-literals-with-string-enums.bin and b/integration/enums-as-literals-with-string-enums/enums-as-literals-with-string-enums.bin differ diff --git a/integration/enums-as-literals/enums-as-literals.bin b/integration/enums-as-literals/enums-as-literals.bin index 890fee420..813acd888 100644 Binary files a/integration/enums-as-literals/enums-as-literals.bin and b/integration/enums-as-literals/enums-as-literals.bin differ diff --git a/integration/fieldmask/fieldmask.bin b/integration/fieldmask/fieldmask.bin index 528593715..7a228dc33 100644 Binary files a/integration/fieldmask/fieldmask.bin and b/integration/fieldmask/fieldmask.bin differ diff --git a/integration/file-suffix/child.bin b/integration/file-suffix/child.bin index 7c9268976..0fd847f1b 100644 Binary files a/integration/file-suffix/child.bin and b/integration/file-suffix/child.bin differ diff --git a/integration/file-suffix/parent.bin b/integration/file-suffix/parent.bin index 222ccda6a..a33f45e6c 100644 Binary files a/integration/file-suffix/parent.bin and b/integration/file-suffix/parent.bin differ diff --git a/integration/generic-metadata/hero.bin b/integration/generic-metadata/hero.bin index ad7849d17..fa5e65377 100644 Binary files a/integration/generic-metadata/hero.bin and b/integration/generic-metadata/hero.bin differ diff --git a/integration/generic-metadata/hero.ts b/integration/generic-metadata/hero.ts index c34c95abb..3772395e8 100644 --- a/integration/generic-metadata/hero.ts +++ b/integration/generic-metadata/hero.ts @@ -1,6 +1,6 @@ /* eslint-disable */ -import { Observable } from 'rxjs'; import { Foo } from './some-file'; +import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import * as _m0 from 'protobufjs/minimal'; diff --git a/integration/generic-service-definitions-and-services/simple.bin b/integration/generic-service-definitions-and-services/simple.bin index f101042df..933b0e061 100644 Binary files a/integration/generic-service-definitions-and-services/simple.bin and b/integration/generic-service-definitions-and-services/simple.bin differ diff --git a/integration/generic-service-definitions/simple.bin b/integration/generic-service-definitions/simple.bin index f101042df..933b0e061 100644 Binary files a/integration/generic-service-definitions/simple.bin and b/integration/generic-service-definitions/simple.bin differ diff --git a/integration/global-this/global-this.bin b/integration/global-this/global-this.bin index 4a39e83e8..b2c28839f 100644 Binary files a/integration/global-this/global-this.bin and b/integration/global-this/global-this.bin differ diff --git a/integration/grpc-js/google/protobuf/wrappers.bin b/integration/grpc-js/google/protobuf/wrappers.bin index 7f34d6ce0..1a0e29c14 100644 Binary files a/integration/grpc-js/google/protobuf/wrappers.bin and b/integration/grpc-js/google/protobuf/wrappers.bin differ diff --git a/integration/grpc-js/simple.bin b/integration/grpc-js/simple.bin index a4adec606..bf3ff7795 100644 Binary files a/integration/grpc-js/simple.bin and b/integration/grpc-js/simple.bin differ diff --git a/integration/grpc-web-go-server/example.bin b/integration/grpc-web-go-server/example.bin index 1c0736d42..be836c175 100644 Binary files a/integration/grpc-web-go-server/example.bin and b/integration/grpc-web-go-server/example.bin differ diff --git a/integration/grpc-web-no-streaming-observable/example.bin b/integration/grpc-web-no-streaming-observable/example.bin index 0b96ce0c6..391a3c933 100644 Binary files a/integration/grpc-web-no-streaming-observable/example.bin and b/integration/grpc-web-no-streaming-observable/example.bin differ diff --git a/integration/grpc-web-no-streaming-observable/example.ts b/integration/grpc-web-no-streaming-observable/example.ts index d41f7911c..384b0a7b1 100644 --- a/integration/grpc-web-no-streaming-observable/example.ts +++ b/integration/grpc-web-no-streaming-observable/example.ts @@ -1,8 +1,8 @@ /* eslint-disable */ import { grpc } from '@improbable-eng/grpc-web'; -import { Observable } from 'rxjs'; import { BrowserHeaders } from 'browser-headers'; import { take } from 'rxjs/operators'; +import { Observable } from 'rxjs'; import * as _m0 from 'protobufjs/minimal'; export const protobufPackage = 'rpx'; diff --git a/integration/grpc-web-no-streaming/example.bin b/integration/grpc-web-no-streaming/example.bin index 0b96ce0c6..391a3c933 100644 Binary files a/integration/grpc-web-no-streaming/example.bin and b/integration/grpc-web-no-streaming/example.bin differ diff --git a/integration/grpc-web/example.bin b/integration/grpc-web/example.bin index 72f331cf3..6e2af21dd 100644 Binary files a/integration/grpc-web/example.bin and b/integration/grpc-web/example.bin differ diff --git a/integration/grpc-web/example.ts b/integration/grpc-web/example.ts index 016f6f1aa..54bf53348 100644 --- a/integration/grpc-web/example.ts +++ b/integration/grpc-web/example.ts @@ -1,8 +1,8 @@ /* eslint-disable */ import { grpc } from '@improbable-eng/grpc-web'; -import { Observable } from 'rxjs'; import { BrowserHeaders } from 'browser-headers'; import { share } from 'rxjs/operators'; +import { Observable } from 'rxjs'; import * as _m0 from 'protobufjs/minimal'; export const protobufPackage = 'rpx'; diff --git a/integration/lower-case-svc-methods/math.bin b/integration/lower-case-svc-methods/math.bin index eddba34a4..ff87493eb 100644 Binary files a/integration/lower-case-svc-methods/math.bin and b/integration/lower-case-svc-methods/math.bin differ diff --git a/integration/meta-typings/google/protobuf/wrappers.bin b/integration/meta-typings/google/protobuf/wrappers.bin index 7f34d6ce0..1a0e29c14 100644 Binary files a/integration/meta-typings/google/protobuf/wrappers.bin and b/integration/meta-typings/google/protobuf/wrappers.bin differ diff --git a/integration/meta-typings/google/type/date.bin b/integration/meta-typings/google/type/date.bin index 680ba7e6d..2cadbfb71 100644 Binary files a/integration/meta-typings/google/type/date.bin and b/integration/meta-typings/google/type/date.bin differ diff --git a/integration/meta-typings/import_dir/thing.bin b/integration/meta-typings/import_dir/thing.bin index 18377ed61..f6c44d98b 100644 Binary files a/integration/meta-typings/import_dir/thing.bin and b/integration/meta-typings/import_dir/thing.bin differ diff --git a/integration/meta-typings/simple.bin b/integration/meta-typings/simple.bin index 0fffa492d..b08057fac 100644 Binary files a/integration/meta-typings/simple.bin and b/integration/meta-typings/simple.bin differ diff --git a/integration/nestjs-metadata-grpc-js/hero.bin b/integration/nestjs-metadata-grpc-js/hero.bin index ad7849d17..fa5e65377 100644 Binary files a/integration/nestjs-metadata-grpc-js/hero.bin and b/integration/nestjs-metadata-grpc-js/hero.bin differ diff --git a/integration/nestjs-metadata-grpc-js/hero.ts b/integration/nestjs-metadata-grpc-js/hero.ts index fc92d83db..6f1c67d35 100644 --- a/integration/nestjs-metadata-grpc-js/hero.ts +++ b/integration/nestjs-metadata-grpc-js/hero.ts @@ -1,7 +1,7 @@ /* eslint-disable */ import { GrpcMethod, GrpcStreamMethod } from '@nestjs/microservices'; -import { Observable } from 'rxjs'; import { Metadata } from '@grpc/grpc-js'; +import { Observable } from 'rxjs'; export const protobufPackage = 'hero'; diff --git a/integration/nestjs-metadata-observables/hero.bin b/integration/nestjs-metadata-observables/hero.bin index ad7849d17..fa5e65377 100644 Binary files a/integration/nestjs-metadata-observables/hero.bin and b/integration/nestjs-metadata-observables/hero.bin differ diff --git a/integration/nestjs-metadata-observables/hero.ts b/integration/nestjs-metadata-observables/hero.ts index 64dc37d37..f9cfd75f2 100644 --- a/integration/nestjs-metadata-observables/hero.ts +++ b/integration/nestjs-metadata-observables/hero.ts @@ -1,7 +1,7 @@ /* eslint-disable */ import { GrpcMethod, GrpcStreamMethod } from '@nestjs/microservices'; -import { Observable } from 'rxjs'; import { Metadata } from '@grpc/grpc-js'; +import { Observable } from 'rxjs'; export const protobufPackage = 'hero'; diff --git a/integration/nestjs-metadata-restparameters/hero.bin b/integration/nestjs-metadata-restparameters/hero.bin index ad7849d17..fa5e65377 100644 Binary files a/integration/nestjs-metadata-restparameters/hero.bin and b/integration/nestjs-metadata-restparameters/hero.bin differ diff --git a/integration/nestjs-metadata-restparameters/hero.ts b/integration/nestjs-metadata-restparameters/hero.ts index fe0e557a2..cd80864ee 100644 --- a/integration/nestjs-metadata-restparameters/hero.ts +++ b/integration/nestjs-metadata-restparameters/hero.ts @@ -1,7 +1,7 @@ /* eslint-disable */ import { GrpcMethod, GrpcStreamMethod } from '@nestjs/microservices'; -import { Observable } from 'rxjs'; import { Metadata } from '@grpc/grpc-js'; +import { Observable } from 'rxjs'; export const protobufPackage = 'hero'; diff --git a/integration/nestjs-metadata/hero.bin b/integration/nestjs-metadata/hero.bin index ad7849d17..fa5e65377 100644 Binary files a/integration/nestjs-metadata/hero.bin and b/integration/nestjs-metadata/hero.bin differ diff --git a/integration/nestjs-metadata/hero.ts b/integration/nestjs-metadata/hero.ts index fc92d83db..6f1c67d35 100644 --- a/integration/nestjs-metadata/hero.ts +++ b/integration/nestjs-metadata/hero.ts @@ -1,7 +1,7 @@ /* eslint-disable */ import { GrpcMethod, GrpcStreamMethod } from '@nestjs/microservices'; -import { Observable } from 'rxjs'; import { Metadata } from '@grpc/grpc-js'; +import { Observable } from 'rxjs'; export const protobufPackage = 'hero'; diff --git a/integration/nestjs-restparameters/hero.bin b/integration/nestjs-restparameters/hero.bin index ad7849d17..fa5e65377 100644 Binary files a/integration/nestjs-restparameters/hero.bin and b/integration/nestjs-restparameters/hero.bin differ diff --git a/integration/nestjs-simple-observables/hero.bin b/integration/nestjs-simple-observables/hero.bin index ad7849d17..fa5e65377 100644 Binary files a/integration/nestjs-simple-observables/hero.bin and b/integration/nestjs-simple-observables/hero.bin differ diff --git a/integration/nestjs-simple-restparameters/hero.bin b/integration/nestjs-simple-restparameters/hero.bin index 0e82b21bc..a4ea1d80b 100644 Binary files a/integration/nestjs-simple-restparameters/hero.bin and b/integration/nestjs-simple-restparameters/hero.bin differ diff --git a/integration/nestjs-simple/hero.bin b/integration/nestjs-simple/hero.bin index 3ce8c0002..ff0c00459 100644 Binary files a/integration/nestjs-simple/hero.bin and b/integration/nestjs-simple/hero.bin differ diff --git a/integration/nestjs-simple/hero.ts b/integration/nestjs-simple/hero.ts index 7028e042f..8bcda1c31 100644 --- a/integration/nestjs-simple/hero.ts +++ b/integration/nestjs-simple/hero.ts @@ -1,7 +1,7 @@ /* eslint-disable */ import { GrpcMethod, GrpcStreamMethod } from '@nestjs/microservices'; -import { Observable } from 'rxjs'; import { Timestamp } from './google/protobuf/timestamp'; +import { Observable } from 'rxjs'; import { Empty } from './google/protobuf/empty'; export const protobufPackage = 'hero'; diff --git a/integration/nice-grpc/google/protobuf/wrappers.bin b/integration/nice-grpc/google/protobuf/wrappers.bin index 7f34d6ce0..1a0e29c14 100644 Binary files a/integration/nice-grpc/google/protobuf/wrappers.bin and b/integration/nice-grpc/google/protobuf/wrappers.bin differ diff --git a/integration/nice-grpc/simple.bin b/integration/nice-grpc/simple.bin index a4adec606..bf3ff7795 100644 Binary files a/integration/nice-grpc/simple.bin and b/integration/nice-grpc/simple.bin differ diff --git a/integration/no-proto-package/no-proto-package.bin b/integration/no-proto-package/no-proto-package.bin index b23df4108..b74879455 100644 Binary files a/integration/no-proto-package/no-proto-package.bin and b/integration/no-proto-package/no-proto-package.bin differ diff --git a/integration/oneof-properties/oneof.bin b/integration/oneof-properties/oneof.bin index e3c94035b..9b2e19274 100644 Binary files a/integration/oneof-properties/oneof.bin and b/integration/oneof-properties/oneof.bin differ diff --git a/integration/oneof-unions-snake/simple.bin b/integration/oneof-unions-snake/simple.bin index ca4927412..e23c9276b 100644 Binary files a/integration/oneof-unions-snake/simple.bin and b/integration/oneof-unions-snake/simple.bin differ diff --git a/integration/oneof-unions/oneof.bin b/integration/oneof-unions/oneof.bin index 999540787..66fdb7d0a 100644 Binary files a/integration/oneof-unions/oneof.bin and b/integration/oneof-unions/oneof.bin differ diff --git a/integration/only-types-grpc-metadata/only-types-grpc-metadata.bin b/integration/only-types-grpc-metadata/only-types-grpc-metadata.bin index e43e9b997..84219b749 100644 Binary files a/integration/only-types-grpc-metadata/only-types-grpc-metadata.bin and b/integration/only-types-grpc-metadata/only-types-grpc-metadata.bin differ diff --git a/integration/only-types/reservation.bin b/integration/only-types/reservation.bin index 27024a9e9..cb6da9d38 100644 Binary files a/integration/only-types/reservation.bin and b/integration/only-types/reservation.bin differ diff --git a/integration/options/options.bin b/integration/options/options.bin index a96b5e3cb..b65e5ce8a 100644 Binary files a/integration/options/options.bin and b/integration/options/options.bin differ diff --git a/integration/options/something/something.bin b/integration/options/something/something.bin index 0950190ca..aa13b2d7a 100644 Binary files a/integration/options/something/something.bin and b/integration/options/something/something.bin differ diff --git a/integration/point/point.bin b/integration/point/point.bin index a9f42b5d2..9058b1fb5 100644 Binary files a/integration/point/point.bin and b/integration/point/point.bin differ diff --git a/integration/return-observable/observable.bin b/integration/return-observable/observable.bin index 267c620b9..944618a6f 100644 Binary files a/integration/return-observable/observable.bin and b/integration/return-observable/observable.bin differ diff --git a/integration/simple-deprecated-fields/simple.bin b/integration/simple-deprecated-fields/simple.bin index edc1b92bd..42d8de641 100644 Binary files a/integration/simple-deprecated-fields/simple.bin and b/integration/simple-deprecated-fields/simple.bin differ diff --git a/integration/simple-esmodule-interop/simple.bin b/integration/simple-esmodule-interop/simple.bin index fd3a1d254..d45ce2e9c 100644 Binary files a/integration/simple-esmodule-interop/simple.bin and b/integration/simple-esmodule-interop/simple.bin differ diff --git a/integration/simple-json-name/simple.bin b/integration/simple-json-name/simple.bin index 29222dfc2..2bf586ef1 100644 Binary files a/integration/simple-json-name/simple.bin and b/integration/simple-json-name/simple.bin differ diff --git a/integration/simple-long-string/google/protobuf/wrappers.bin b/integration/simple-long-string/google/protobuf/wrappers.bin index 7f34d6ce0..1a0e29c14 100644 Binary files a/integration/simple-long-string/google/protobuf/wrappers.bin and b/integration/simple-long-string/google/protobuf/wrappers.bin differ diff --git a/integration/simple-long-string/simple.bin b/integration/simple-long-string/simple.bin index 486951c66..28e423265 100644 Binary files a/integration/simple-long-string/simple.bin and b/integration/simple-long-string/simple.bin differ diff --git a/integration/simple-long/google/protobuf/wrappers.bin b/integration/simple-long/google/protobuf/wrappers.bin index 7f34d6ce0..1a0e29c14 100644 Binary files a/integration/simple-long/google/protobuf/wrappers.bin and b/integration/simple-long/google/protobuf/wrappers.bin differ diff --git a/integration/simple-long/simple.bin b/integration/simple-long/simple.bin index 28e779a04..9203007e0 100644 Binary files a/integration/simple-long/simple.bin and b/integration/simple-long/simple.bin differ diff --git a/integration/simple-optionals/google/protobuf/wrappers.bin b/integration/simple-optionals/google/protobuf/wrappers.bin index 7f34d6ce0..1a0e29c14 100644 Binary files a/integration/simple-optionals/google/protobuf/wrappers.bin and b/integration/simple-optionals/google/protobuf/wrappers.bin differ diff --git a/integration/simple-optionals/import_dir/thing.bin b/integration/simple-optionals/import_dir/thing.bin index 18377ed61..f6c44d98b 100644 Binary files a/integration/simple-optionals/import_dir/thing.bin and b/integration/simple-optionals/import_dir/thing.bin differ diff --git a/integration/simple-optionals/simple.bin b/integration/simple-optionals/simple.bin index d6a2033ae..9ce4f7a89 100644 Binary files a/integration/simple-optionals/simple.bin and b/integration/simple-optionals/simple.bin differ diff --git a/integration/simple-optionals/thing.bin b/integration/simple-optionals/thing.bin index 132073529..a72f3c251 100644 Binary files a/integration/simple-optionals/thing.bin and b/integration/simple-optionals/thing.bin differ diff --git a/integration/simple-proto2/simple.bin b/integration/simple-proto2/simple.bin index c879526ac..6ccfa84ff 100644 Binary files a/integration/simple-proto2/simple.bin and b/integration/simple-proto2/simple.bin differ diff --git a/integration/simple-prototype-defaults/google/protobuf/wrappers.bin b/integration/simple-prototype-defaults/google/protobuf/wrappers.bin index 7f34d6ce0..1a0e29c14 100644 Binary files a/integration/simple-prototype-defaults/google/protobuf/wrappers.bin and b/integration/simple-prototype-defaults/google/protobuf/wrappers.bin differ diff --git a/integration/simple-prototype-defaults/google/type/date.bin b/integration/simple-prototype-defaults/google/type/date.bin index 680ba7e6d..2cadbfb71 100644 Binary files a/integration/simple-prototype-defaults/google/type/date.bin and b/integration/simple-prototype-defaults/google/type/date.bin differ diff --git a/integration/simple-prototype-defaults/import_dir/thing.bin b/integration/simple-prototype-defaults/import_dir/thing.bin index 18377ed61..f6c44d98b 100644 Binary files a/integration/simple-prototype-defaults/import_dir/thing.bin and b/integration/simple-prototype-defaults/import_dir/thing.bin differ diff --git a/integration/simple-prototype-defaults/simple.bin b/integration/simple-prototype-defaults/simple.bin index 321bb33c3..8568423b4 100644 Binary files a/integration/simple-prototype-defaults/simple.bin and b/integration/simple-prototype-defaults/simple.bin differ diff --git a/integration/simple-snake/google/protobuf/wrappers.bin b/integration/simple-snake/google/protobuf/wrappers.bin index 7f34d6ce0..1a0e29c14 100644 Binary files a/integration/simple-snake/google/protobuf/wrappers.bin and b/integration/simple-snake/google/protobuf/wrappers.bin differ diff --git a/integration/simple-snake/import_dir/thing.bin b/integration/simple-snake/import_dir/thing.bin index 18377ed61..f6c44d98b 100644 Binary files a/integration/simple-snake/import_dir/thing.bin and b/integration/simple-snake/import_dir/thing.bin differ diff --git a/integration/simple-snake/simple.bin b/integration/simple-snake/simple.bin index a8fca9efd..55e98e3b4 100644 Binary files a/integration/simple-snake/simple.bin and b/integration/simple-snake/simple.bin differ diff --git a/integration/simple-string-enums/simple.bin b/integration/simple-string-enums/simple.bin index 091d8c01e..55d91037d 100644 Binary files a/integration/simple-string-enums/simple.bin and b/integration/simple-string-enums/simple.bin differ diff --git a/integration/simple-unrecognized-enum/google/protobuf/wrappers.bin b/integration/simple-unrecognized-enum/google/protobuf/wrappers.bin index 7f34d6ce0..1a0e29c14 100644 Binary files a/integration/simple-unrecognized-enum/google/protobuf/wrappers.bin and b/integration/simple-unrecognized-enum/google/protobuf/wrappers.bin differ diff --git a/integration/simple-unrecognized-enum/import_dir/thing.bin b/integration/simple-unrecognized-enum/import_dir/thing.bin index 18377ed61..f6c44d98b 100644 Binary files a/integration/simple-unrecognized-enum/import_dir/thing.bin and b/integration/simple-unrecognized-enum/import_dir/thing.bin differ diff --git a/integration/simple-unrecognized-enum/simple.bin b/integration/simple-unrecognized-enum/simple.bin index d6a2033ae..9ce4f7a89 100644 Binary files a/integration/simple-unrecognized-enum/simple.bin and b/integration/simple-unrecognized-enum/simple.bin differ diff --git a/integration/simple/google/protobuf/wrappers.bin b/integration/simple/google/protobuf/wrappers.bin index 7f34d6ce0..1a0e29c14 100644 Binary files a/integration/simple/google/protobuf/wrappers.bin and b/integration/simple/google/protobuf/wrappers.bin differ diff --git a/integration/simple/google/type/date.bin b/integration/simple/google/type/date.bin index 680ba7e6d..2cadbfb71 100644 Binary files a/integration/simple/google/type/date.bin and b/integration/simple/google/type/date.bin differ diff --git a/integration/simple/import_dir/thing.bin b/integration/simple/import_dir/thing.bin index 18377ed61..f6c44d98b 100644 Binary files a/integration/simple/import_dir/thing.bin and b/integration/simple/import_dir/thing.bin differ diff --git a/integration/simple/simple.bin b/integration/simple/simple.bin index 321bb33c3..8568423b4 100644 Binary files a/integration/simple/simple.bin and b/integration/simple/simple.bin differ diff --git a/integration/struct/struct.bin b/integration/struct/struct.bin index d9a1b260e..19d086ce4 100644 Binary files a/integration/struct/struct.bin and b/integration/struct/struct.bin differ diff --git a/integration/type-registry/bar/bar.bin b/integration/type-registry/bar/bar.bin index 3aa6c75d1..79ada77e7 100644 Binary files a/integration/type-registry/bar/bar.bin and b/integration/type-registry/bar/bar.bin differ diff --git a/integration/type-registry/foo.bin b/integration/type-registry/foo.bin index 85b788882..be317fd86 100644 Binary files a/integration/type-registry/foo.bin and b/integration/type-registry/foo.bin differ diff --git a/integration/types-with-underscores/file.bin b/integration/types-with-underscores/file.bin index eeb5f3f23..5d9305c90 100644 Binary files a/integration/types-with-underscores/file.bin and b/integration/types-with-underscores/file.bin differ diff --git a/integration/unknown-fields/google/protobuf/compiler/plugin.bin b/integration/unknown-fields/google/protobuf/compiler/plugin.bin index d43fa9612..8c8cb0dcc 100644 Binary files a/integration/unknown-fields/google/protobuf/compiler/plugin.bin and b/integration/unknown-fields/google/protobuf/compiler/plugin.bin differ diff --git a/integration/unknown-fields/google/protobuf/descriptor.bin b/integration/unknown-fields/google/protobuf/descriptor.bin index 54bbcea7e..d19d5ee6c 100644 Binary files a/integration/unknown-fields/google/protobuf/descriptor.bin and b/integration/unknown-fields/google/protobuf/descriptor.bin differ diff --git a/integration/unknown-fields/options.bin b/integration/unknown-fields/options.bin index 949f7217a..9fc92b752 100644 Binary files a/integration/unknown-fields/options.bin and b/integration/unknown-fields/options.bin differ diff --git a/integration/unknown-fields/something/something.bin b/integration/unknown-fields/something/something.bin index 0f4e0f37a..15a89c3b2 100644 Binary files a/integration/unknown-fields/something/something.bin and b/integration/unknown-fields/something/something.bin differ diff --git a/integration/use-date-false/metadata.bin b/integration/use-date-false/metadata.bin index c4c8c7256..2708b60c9 100644 Binary files a/integration/use-date-false/metadata.bin and b/integration/use-date-false/metadata.bin differ diff --git a/integration/use-date-string/use-date-string.bin b/integration/use-date-string/use-date-string.bin index 79e656e01..49fc54997 100644 Binary files a/integration/use-date-string/use-date-string.bin and b/integration/use-date-string/use-date-string.bin differ diff --git a/integration/use-date-true/use-date-true.bin b/integration/use-date-true/use-date-true.bin index a5b93133c..4def41ba6 100644 Binary files a/integration/use-date-true/use-date-true.bin and b/integration/use-date-true/use-date-true.bin differ diff --git a/integration/use-exact-types-false/foo.bin b/integration/use-exact-types-false/foo.bin index b98932d4b..146a6e859 100644 Binary files a/integration/use-exact-types-false/foo.bin and b/integration/use-exact-types-false/foo.bin differ diff --git a/integration/use-json-wire-format/use-json-wire-format.bin b/integration/use-json-wire-format/use-json-wire-format.bin index 723fabc32..c3cc7979e 100644 Binary files a/integration/use-json-wire-format/use-json-wire-format.bin and b/integration/use-json-wire-format/use-json-wire-format.bin differ diff --git a/integration/use-objectid-true-external-import/objectid/objectid.bin b/integration/use-objectid-true-external-import/objectid/objectid.bin index 4dab2c110..023150f2f 100644 Binary files a/integration/use-objectid-true-external-import/objectid/objectid.bin and b/integration/use-objectid-true-external-import/objectid/objectid.bin differ diff --git a/integration/use-objectid-true-external-import/use-objectid-true.bin b/integration/use-objectid-true-external-import/use-objectid-true.bin index 781274c90..4cc9c6606 100644 Binary files a/integration/use-objectid-true-external-import/use-objectid-true.bin and b/integration/use-objectid-true-external-import/use-objectid-true.bin differ diff --git a/integration/use-optionals-all/test.bin b/integration/use-optionals-all/test.bin index 11c02597f..565d5b9db 100644 Binary files a/integration/use-optionals-all/test.bin and b/integration/use-optionals-all/test.bin differ diff --git a/integration/value/value.bin b/integration/value/value.bin index 3564a9fe8..36b6acfbe 100644 Binary files a/integration/value/value.bin and b/integration/value/value.bin differ diff --git a/integration/vector-tile/vector_tile.bin b/integration/vector-tile/vector_tile.bin index dad36b4bb..3859f1b73 100644 Binary files a/integration/vector-tile/vector_tile.bin and b/integration/vector-tile/vector_tile.bin differ diff --git a/src/generate-async-iterable.ts b/src/generate-async-iterable.ts new file mode 100644 index 000000000..ced7f7c7e --- /dev/null +++ b/src/generate-async-iterable.ts @@ -0,0 +1,43 @@ +import { code, Code } from 'ts-poet'; + +/** Creates a function to transform a message Source to a Uint8Array Source. */ +export function generateEncodeTransform(fullName: string): Code { + return code` + // encodeTransform encodes a source of message objects. + // Transform<${fullName}, Uint8Array> + async *encodeTransform( + source: AsyncIterable<${fullName} | ${fullName}[]> | Iterable<${fullName} | ${fullName}[]> + ): AsyncIterable { + for await (const pkt of source) { + if (Array.isArray(pkt)) { + for (const p of pkt) { + yield* [${fullName}.encode(p).finish()] + } + } else { + yield* [${fullName}.encode(pkt).finish()] + } + } + } + `; +} + +/** Creates a function to transform a Uint8Array Source to a message Source. */ +export function generateDecodeTransform(fullName: string): Code { + return code` + // decodeTransform decodes a source of encoded messages. + // Transform + async *decodeTransform( + source: AsyncIterable | Iterable + ): AsyncIterable<${fullName}> { + for await (const pkt of source) { + if (Array.isArray(pkt)) { + for (const p of pkt) { + yield* [${fullName}.decode(p)] + } + } else { + yield* [${fullName}.decode(pkt)] + } + } + } + `; +} diff --git a/src/generate-grpc-web.ts b/src/generate-grpc-web.ts index 79c7376e9..a4c2639de 100644 --- a/src/generate-grpc-web.ts +++ b/src/generate-grpc-web.ts @@ -1,5 +1,5 @@ import { MethodDescriptorProto, FileDescriptorProto, ServiceDescriptorProto } from 'ts-proto-descriptors'; -import { rawRequestType, requestType, responsePromiseOrObservable, responseType } from './types'; +import { rawRequestType, requestType, responsePromiseOrObservable, responseType, observableType } from './types'; import { Code, code, imp, joinCode } from 'ts-poet'; import { Context } from './context'; import { assertInstanceOf, FormattedMethodDescriptor, maybePrefixPackage } from './utils'; @@ -8,12 +8,11 @@ const grpc = imp('grpc@@improbable-eng/grpc-web'); const share = imp('share@rxjs/operators'); const take = imp('take@rxjs/operators'); const BrowserHeaders = imp('BrowserHeaders@browser-headers'); -const Observable = imp('Observable@rxjs'); /** Generates a client that uses the `@improbable-web/grpc-web` library. */ export function generateGrpcClientImpl( ctx: Context, - fileDesc: FileDescriptorProto, + _fileDesc: FileDescriptorProto, serviceDesc: ServiceDescriptorProto ): Code { const chunks: Code[] = []; @@ -154,18 +153,18 @@ export function addGrpcWebMisc(ctx: Context, hasStreamingMethods: boolean): Code interface UnaryMethodDefinitionishR extends ${grpc}.UnaryMethodDefinition { requestStream: any; responseStream: any; } `); chunks.push(code`type UnaryMethodDefinitionish = UnaryMethodDefinitionishR;`); - chunks.push(generateGrpcWebRpcType(options.returnObservable, hasStreamingMethods)); - chunks.push(generateGrpcWebImpl(options.returnObservable, hasStreamingMethods)); + chunks.push(generateGrpcWebRpcType(ctx, options.returnObservable, hasStreamingMethods)); + chunks.push(generateGrpcWebImpl(ctx, options.returnObservable, hasStreamingMethods)); return joinCode(chunks, { on: '\n\n' }); } /** Makes an `Rpc` interface to decouple from the low-level grpc-web `grpc.invoke and grpc.unary`/etc. methods. */ -function generateGrpcWebRpcType(returnObservable: boolean, hasStreamingMethods: boolean): Code { +function generateGrpcWebRpcType(ctx: Context, returnObservable: boolean, hasStreamingMethods: boolean): Code { const chunks: Code[] = []; chunks.push(code`interface Rpc {`); - const wrapper = returnObservable ? Observable : 'Promise'; + const wrapper = returnObservable ? observableType(ctx) : 'Promise'; chunks.push(code` unary( methodDesc: T, @@ -180,7 +179,7 @@ function generateGrpcWebRpcType(returnObservable: boolean, hasStreamingMethods: methodDesc: T, request: any, metadata: grpc.Metadata | undefined, - ): ${Observable}; + ): ${observableType(ctx)}; `); } @@ -189,7 +188,7 @@ function generateGrpcWebRpcType(returnObservable: boolean, hasStreamingMethods: } /** Implements the `Rpc` interface by making calls using the `grpc.unary` method. */ -function generateGrpcWebImpl(returnObservable: boolean, hasStreamingMethods: boolean): Code { +function generateGrpcWebImpl(ctx: Context, returnObservable: boolean, hasStreamingMethods: boolean): Code { const options = code` { transport?: grpc.TransportFactory, @@ -212,13 +211,13 @@ function generateGrpcWebImpl(returnObservable: boolean, hasStreamingMethods: boo `); if (returnObservable) { - chunks.push(createObservableUnaryMethod()); + chunks.push(createObservableUnaryMethod(ctx)); } else { chunks.push(createPromiseUnaryMethod()); } if (hasStreamingMethods) { - chunks.push(createInvokeMethod()); + chunks.push(createInvokeMethod(ctx)); } chunks.push(code`}`); @@ -260,13 +259,13 @@ function createPromiseUnaryMethod(): Code { `; } -function createObservableUnaryMethod(): Code { +function createObservableUnaryMethod(ctx: Context): Code { return code` unary( methodDesc: T, _request: any, metadata: grpc.Metadata | undefined - ): ${Observable} { + ): ${observableType(ctx)} { const request = { ..._request, ...methodDesc.requestType }; const maybeCombinedMetadata = metadata && this.options.metadata @@ -293,13 +292,13 @@ function createObservableUnaryMethod(): Code { `; } -function createInvokeMethod() { +function createInvokeMethod(ctx: Context) { return code` invoke( methodDesc: T, _request: any, metadata: grpc.Metadata | undefined - ): ${Observable} { + ): ${observableType(ctx)} { // Status Response Codes (https://developers.google.com/maps-booking/reference/grpc-api/status_codes) const upStreamCodes = [2, 4, 8, 9, 10, 13, 14, 15]; const DEFAULT_TIMEOUT_TIME: number = 3_000; diff --git a/src/generate-services.ts b/src/generate-services.ts index f64768591..8f258bdaa 100644 --- a/src/generate-services.ts +++ b/src/generate-services.ts @@ -7,10 +7,10 @@ import { rawRequestType, responsePromiseOrObservable, responseType, + observableType, } from './types'; import { assertInstanceOf, FormattedMethodDescriptor, maybeAddComment, maybePrefixPackage, singular } from './utils'; import SourceInfo, { Fields } from './sourceInfo'; -import { camelCase } from './case'; import { contextTypeVar } from './main'; import { Context } from './context'; @@ -34,7 +34,7 @@ export function generateService( sourceInfo: SourceInfo, serviceDesc: ServiceDescriptorProto ): Code { - const { options, utils } = ctx; + const { options } = ctx; const chunks: Code[] = []; maybeAddComment(sourceInfo, chunks, serviceDesc.options?.deprecated); @@ -121,12 +121,20 @@ function generateRegularRpcMethod( decode = code`data => ${utils.fromTimestamp}(${rawOutputType}.decode(new ${Reader}(data)))`; } if (methodDesc.clientStreaming) { - encode = code`request.pipe(${imp('map@rxjs/operators')}(request => ${encode}))`; + if (options.useAsyncIterable) { + encode = code`${rawInputType}.encodeTransform(request)`; + } else { + encode = code`request.pipe(${imp('map@rxjs/operators')}(request => ${encode}))`; + } } let returnVariable: string; if (options.returnObservable || methodDesc.serverStreaming) { returnVariable = 'result'; - decode = code`result.pipe(${imp('map@rxjs/operators')}(${decode}))`; + if (options.useAsyncIterable) { + decode = code`${rawOutputType}.decodeTransform(result)`; + } else { + decode = code`result.pipe(${imp('map@rxjs/operators')}(${decode}))`; + } } else { returnVariable = 'promise'; decode = code`promise.then(${decode})`; @@ -207,7 +215,7 @@ export function generateServiceClientImpl( } /** We've found a BatchXxx method, create a synthetic GetXxx method that calls it. */ -function generateBatchingRpcMethod(ctx: Context, batchMethod: BatchMethod): Code { +function generateBatchingRpcMethod(_ctx: Context, batchMethod: BatchMethod): Code { const { methodDesc, singleMethodName, @@ -315,7 +323,7 @@ export function generateRpcType(ctx: Context, hasStreamingMethods: boolean): Cod const maybeContextParam = options.context ? 'ctx: Context,' : ''; const methods = [[code`request`, code`Uint8Array`, code`Promise`]]; if (hasStreamingMethods) { - const observable = imp('Observable@rxjs'); + const observable = observableType(ctx); methods.push([code`clientStreamingRequest`, code`${observable}`, code`Promise`]); methods.push([code`serverStreamingRequest`, code`Uint8Array`, code`${observable}`]); methods.push([ diff --git a/src/main.ts b/src/main.ts index 111206010..665de308c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -67,6 +67,7 @@ import { generateGrpcMethodDesc, generateGrpcServiceDesc, } from './generate-grpc-web'; +import { generateEncodeTransform, generateDecodeTransform } from './generate-async-iterable'; import { generateEnum } from './enums'; import { visit, visitServices } from './visit'; import { DateOption, EnvOption, LongOption, OneofOption, Options, ServiceOption } from './options'; @@ -165,6 +166,10 @@ export function generateFile(ctx: Context, fileDesc: FileDescriptorProto): [stri staticMembers.push(generateEncode(ctx, fullName, message)); staticMembers.push(generateDecode(ctx, fullName, message)); } + if (options.useAsyncIterable) { + staticMembers.push(generateEncodeTransform(fullName)); + staticMembers.push(generateDecodeTransform(fullName)); + } if (options.outputJsonMethods) { staticMembers.push(generateFromJson(ctx, fullName, fullTypeName, message)); staticMembers.push(generateToJson(ctx, fullName, fullTypeName, message)); diff --git a/src/options.ts b/src/options.ts index 22f2dfde8..a449ad87e 100644 --- a/src/options.ts +++ b/src/options.ts @@ -61,6 +61,7 @@ export type Options = { onlyTypes: boolean; emitImportedFiles: boolean; useExactTypes: boolean; + useAsyncIterable: boolean; unknownFields: boolean; usePrototypeForDefaults: boolean; useJsonWireFormat: boolean; @@ -99,6 +100,7 @@ export function defaultOptions(): Options { onlyTypes: false, emitImportedFiles: true, useExactTypes: true, + useAsyncIterable: false, unknownFields: false, usePrototypeForDefaults: false, useJsonWireFormat: false, diff --git a/src/types.ts b/src/types.ts index 8243bb0f3..77e9ceef5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -660,6 +660,13 @@ export function rawRequestType(ctx: Context, methodDesc: MethodDescriptorProto): return messageToTypeName(ctx, methodDesc.inputType); } +export function observableType(ctx: Context): Code { + if (ctx.options.useAsyncIterable) { + return code`AsyncIterable`; + } + return code`${imp('Observable@rxjs')}`; +} + export function requestType(ctx: Context, methodDesc: MethodDescriptorProto, partial: boolean = false): Code { let typeName = rawRequestType(ctx, methodDesc); @@ -668,7 +675,7 @@ export function requestType(ctx: Context, methodDesc: MethodDescriptorProto, par } if (methodDesc.clientStreaming) { - return code`${imp('Observable@rxjs')}<${typeName}>`; + return code`${observableType(ctx)}<${typeName}>`; } return typeName; } @@ -686,7 +693,7 @@ export function responsePromise(ctx: Context, methodDesc: MethodDescriptorProto) } export function responseObservable(ctx: Context, methodDesc: MethodDescriptorProto): Code { - return code`${imp('Observable@rxjs')}<${responseType(ctx, methodDesc)}>`; + return code`${observableType(ctx)}<${responseType(ctx, methodDesc)}>`; } export function responsePromiseOrObservable(ctx: Context, methodDesc: MethodDescriptorProto): Code { diff --git a/tests/options-test.ts b/tests/options-test.ts index 8c45aec96..bd772f641 100644 --- a/tests/options-test.ts +++ b/tests/options-test.ts @@ -37,6 +37,7 @@ describe('options', () => { "stringEnums": false, "unknownFields": false, "unrecognizedEnum": true, + "useAsyncIterable": false, "useDate": "timestamp", "useExactTypes": true, "useJsonWireFormat": false,