diff --git a/packages/grpc-es/README.md b/packages/grpc-es/README.md index 71eded2..95b505a 100644 --- a/packages/grpc-es/README.md +++ b/packages/grpc-es/README.md @@ -45,4 +45,8 @@ new GrpcEsServer({ jsonOptions: { useProtoFieldName: true } }) console.log(`🏃 Grpc Server is running on port ${PORT}`) ``` +### Limitations + +This project currently supports only "UnaryCall" in gRPC calls. Other gRPC call types, such as server streaming, client streaming, and bidirectional streaming, are not supported at this time. + For more information, see the [documentation](https://connectrpc.com/docs/node/getting-started). diff --git a/packages/grpc-es/buf.lock b/packages/grpc-es/buf.lock index 5888dc0..0f45807 100644 --- a/packages/grpc-es/buf.lock +++ b/packages/grpc-es/buf.lock @@ -4,3 +4,6 @@ deps: - name: buf.build/googleapis/googleapis commit: 8bc2c51e08c447cd8886cdea48a73e14 digest: b5:b7e0ac9d192bd0eae88160101269550281448c51f25121cd0d51957661a350aab07001bc145fe9029a8da10b99ff000ae5b284ecaca9c75f2a99604a04d9b4ab + - name: buf.build/protocolbuffers/wellknowntypes + commit: ee20af7d5b6044139bb9051283720274 + digest: b5:690b89ac58ca2ddc9b34eac662d8348754b464c9acc689caf4a4bea613f0704124636a53e5dbc0cc4ea2efeb696b560537f96bc1188419b94c1dcf86c997f6a3 diff --git a/packages/grpc-es/buf.yaml b/packages/grpc-es/buf.yaml index fa1661b..5985dd6 100644 --- a/packages/grpc-es/buf.yaml +++ b/packages/grpc-es/buf.yaml @@ -2,6 +2,7 @@ version: v2 modules: - path: example/proto deps: + - buf.build/protocolbuffers/wellknowntypes - buf.build/googleapis/googleapis breaking: use: diff --git a/packages/grpc-es/example/gen/example/v1/example_connect.ts b/packages/grpc-es/example/gen/example/v1/example_connect.ts index fa3ea65..bef2ca7 100644 --- a/packages/grpc-es/example/gen/example/v1/example_connect.ts +++ b/packages/grpc-es/example/gen/example/v1/example_connect.ts @@ -3,7 +3,7 @@ /* eslint-disable */ // @ts-nocheck -import { AddRequest, AddResponse, EchoRequest, EchoResponse } from "./example_pb.js"; +import { AddRequest, AddResponse, ChatRequest, ChatResponse, EchoRequest, EchoResponse, EchoStreamRequest, EchoStreamResponse, StreamEchoRequest, StreamEchoResponse } from "./example_pb.js"; import { MethodKind } from "@bufbuild/protobuf"; /** @@ -21,6 +21,33 @@ export const ExampleService = { O: EchoResponse, kind: MethodKind.Unary, }, + /** + * @generated from rpc echo.v1.ExampleService.EchoStream + */ + echoStream: { + name: "EchoStream", + I: EchoStreamRequest, + O: EchoStreamResponse, + kind: MethodKind.ServerStreaming, + }, + /** + * @generated from rpc echo.v1.ExampleService.StreamEcho + */ + streamEcho: { + name: "StreamEcho", + I: StreamEchoRequest, + O: StreamEchoResponse, + kind: MethodKind.ClientStreaming, + }, + /** + * @generated from rpc echo.v1.ExampleService.Chat + */ + chat: { + name: "Chat", + I: ChatRequest, + O: ChatResponse, + kind: MethodKind.BiDiStreaming, + }, /** * @generated from rpc echo.v1.ExampleService.Add */ diff --git a/packages/grpc-es/example/gen/example/v1/example_pb.ts b/packages/grpc-es/example/gen/example/v1/example_pb.ts index 736ac89..a12ce89 100644 --- a/packages/grpc-es/example/gen/example/v1/example_pb.ts +++ b/packages/grpc-es/example/gen/example/v1/example_pb.ts @@ -4,7 +4,7 @@ // @ts-nocheck import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; -import { Message, proto3, protoInt64 } from "@bufbuild/protobuf"; +import { Message, proto3, protoInt64, Timestamp } from "@bufbuild/protobuf"; /** * @generated from message echo.v1.EchoRequest @@ -52,6 +52,11 @@ export class EchoResponse extends Message { */ message = ""; + /** + * @generated from field: google.protobuf.Timestamp response_time = 2; + */ + responseTime?: Timestamp; + constructor(data?: PartialMessage) { super(); proto3.util.initPartial(data, this); @@ -61,6 +66,7 @@ export class EchoResponse extends Message { static readonly typeName = "echo.v1.EchoResponse"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ { no: 1, name: "message", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "response_time", kind: "message", T: Timestamp }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): EchoResponse { @@ -80,6 +86,276 @@ export class EchoResponse extends Message { } } +/** + * @generated from message echo.v1.EchoStreamRequest + */ +export class EchoStreamRequest extends Message { + /** + * @generated from field: string message = 1; + */ + message = ""; + + /** + * @generated from field: int32 repeat_count = 2; + */ + repeatCount = 0; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "echo.v1.EchoStreamRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "message", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "repeat_count", kind: "scalar", T: 5 /* ScalarType.INT32 */ }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): EchoStreamRequest { + return new EchoStreamRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): EchoStreamRequest { + return new EchoStreamRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): EchoStreamRequest { + return new EchoStreamRequest().fromJsonString(jsonString, options); + } + + static equals(a: EchoStreamRequest | PlainMessage | undefined, b: EchoStreamRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(EchoStreamRequest, a, b); + } +} + +/** + * @generated from message echo.v1.EchoStreamResponse + */ +export class EchoStreamResponse extends Message { + /** + * @generated from field: string message = 1; + */ + message = ""; + + /** + * @generated from field: int32 sequence_number = 2; + */ + sequenceNumber = 0; + + /** + * @generated from field: google.protobuf.Timestamp response_time = 3; + */ + responseTime?: Timestamp; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "echo.v1.EchoStreamResponse"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "message", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "sequence_number", kind: "scalar", T: 5 /* ScalarType.INT32 */ }, + { no: 3, name: "response_time", kind: "message", T: Timestamp }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): EchoStreamResponse { + return new EchoStreamResponse().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): EchoStreamResponse { + return new EchoStreamResponse().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): EchoStreamResponse { + return new EchoStreamResponse().fromJsonString(jsonString, options); + } + + static equals(a: EchoStreamResponse | PlainMessage | undefined, b: EchoStreamResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(EchoStreamResponse, a, b); + } +} + +/** + * @generated from message echo.v1.StreamEchoRequest + */ +export class StreamEchoRequest extends Message { + /** + * @generated from field: string message_part = 1; + */ + messagePart = ""; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "echo.v1.StreamEchoRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "message_part", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): StreamEchoRequest { + return new StreamEchoRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): StreamEchoRequest { + return new StreamEchoRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): StreamEchoRequest { + return new StreamEchoRequest().fromJsonString(jsonString, options); + } + + static equals(a: StreamEchoRequest | PlainMessage | undefined, b: StreamEchoRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(StreamEchoRequest, a, b); + } +} + +/** + * @generated from message echo.v1.StreamEchoResponse + */ +export class StreamEchoResponse extends Message { + /** + * @generated from field: string full_message = 1; + */ + fullMessage = ""; + + /** + * @generated from field: google.protobuf.Timestamp response_time = 2; + */ + responseTime?: Timestamp; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "echo.v1.StreamEchoResponse"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "full_message", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "response_time", kind: "message", T: Timestamp }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): StreamEchoResponse { + return new StreamEchoResponse().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): StreamEchoResponse { + return new StreamEchoResponse().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): StreamEchoResponse { + return new StreamEchoResponse().fromJsonString(jsonString, options); + } + + static equals(a: StreamEchoResponse | PlainMessage | undefined, b: StreamEchoResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(StreamEchoResponse, a, b); + } +} + +/** + * @generated from message echo.v1.ChatRequest + */ +export class ChatRequest extends Message { + /** + * @generated from field: string user_id = 1; + */ + userId = ""; + + /** + * @generated from field: string message = 2; + */ + message = ""; + + /** + * @generated from field: google.protobuf.Timestamp response_time = 3; + */ + responseTime?: Timestamp; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "echo.v1.ChatRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "message", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "response_time", kind: "message", T: Timestamp }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): ChatRequest { + return new ChatRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): ChatRequest { + return new ChatRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): ChatRequest { + return new ChatRequest().fromJsonString(jsonString, options); + } + + static equals(a: ChatRequest | PlainMessage | undefined, b: ChatRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(ChatRequest, a, b); + } +} + +/** + * @generated from message echo.v1.ChatResponse + */ +export class ChatResponse extends Message { + /** + * @generated from field: string user_id = 1; + */ + userId = ""; + + /** + * @generated from field: string message = 2; + */ + message = ""; + + /** + * @generated from field: google.protobuf.Timestamp response_time = 3; + */ + responseTime?: Timestamp; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "echo.v1.ChatResponse"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "message", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "response_time", kind: "message", T: Timestamp }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): ChatResponse { + return new ChatResponse().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): ChatResponse { + return new ChatResponse().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): ChatResponse { + return new ChatResponse().fromJsonString(jsonString, options); + } + + static equals(a: ChatResponse | PlainMessage | undefined, b: ChatResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(ChatResponse, a, b); + } +} + /** * @generated from message echo.v1.AddRequest */ diff --git a/packages/grpc-es/example/proto/example/v1/example.proto b/packages/grpc-es/example/proto/example/v1/example.proto index d2c1772..f12ff73 100644 --- a/packages/grpc-es/example/proto/example/v1/example.proto +++ b/packages/grpc-es/example/proto/example/v1/example.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package echo.v1; import "google/api/annotations.proto"; +import "google/protobuf/timestamp.proto"; service ExampleService { rpc Echo(EchoRequest) returns (EchoResponse) { @@ -10,6 +11,9 @@ service ExampleService { get: "/example/v1/echo" }; } + rpc EchoStream(EchoStreamRequest) returns (stream EchoStreamResponse); + rpc StreamEcho(stream StreamEchoRequest) returns (StreamEchoResponse); + rpc Chat(stream ChatRequest) returns (stream ChatResponse); rpc Add(AddRequest) returns (AddResponse) { option (google.api.http) = { @@ -25,6 +29,39 @@ message EchoRequest { message EchoResponse { string message = 1; + google.protobuf.Timestamp response_time = 2; +} + +message EchoStreamRequest { + string message = 1; + int32 repeat_count = 2; +} + +message EchoStreamResponse { + string message = 1; + int32 sequence_number = 2; + google.protobuf.Timestamp response_time = 3; +} + +message StreamEchoRequest { + string message_part = 1; +} + +message StreamEchoResponse { + string full_message = 1; + google.protobuf.Timestamp response_time = 2; +} + +message ChatRequest { + string user_id = 1; + string message = 2; + google.protobuf.Timestamp response_time = 3; +} + +message ChatResponse { + string user_id = 1; + string message = 2; + google.protobuf.Timestamp response_time = 3; } message AddRequest { diff --git a/packages/grpc-es/package.json b/packages/grpc-es/package.json index 571988f..1ed29ba 100644 --- a/packages/grpc-es/package.json +++ b/packages/grpc-es/package.json @@ -1,6 +1,7 @@ { "name": "@kanziw/grpc-es", - "version": "0.2.1", + "type": "module", + "version": "0.2.2", "description": "A collection of grpc libraries using connect-es", "repository": { "type": "git", diff --git a/packages/grpc-es/src/server/server.ts b/packages/grpc-es/src/server/server.ts index fc170ab..7527fe6 100644 --- a/packages/grpc-es/src/server/server.ts +++ b/packages/grpc-es/src/server/server.ts @@ -4,7 +4,7 @@ import type { HandlerContext, ServiceImpl } from '@connectrpc/connect' import { connectNodeAdapter } from '@connectrpc/connect-node' import { UniversalHandlerOptions } from '@connectrpc/connect/protocol' import { Code, GrpcError } from './status.js' -import type { Handler, Interceptor } from './types.js' +import type { Interceptor } from './types.js' export class GrpcEsServer { private interceptors: Interceptor[] = [] @@ -18,17 +18,18 @@ export class GrpcEsServer { } register(service: Service, partialImplementation: Partial>): this { - const implementation = { ...makeUnimplementedService(service), ...partialImplementation } as ServiceImpl - const interceptorAppliedImplementation = {} as ServiceImpl + type Implementation = ServiceImpl + const implementation = { ...makeUnimplementedService(service), ...partialImplementation } as Implementation + const interceptorAppliedImplementation = {} as Implementation for (const [key, handler] of Object.entries(implementation)) { let appliedHandler = handler for (const interceptor of this.interceptors) { const currentHandler = appliedHandler - appliedHandler = ((req: AnyMessage & AsyncIterable, ctx: HandlerContext) => - interceptor(req, ctx, currentHandler)) as Handler + appliedHandler = (req: AnyMessage & AsyncIterable, ctx: HandlerContext) => + interceptor(req, ctx, currentHandler) } - interceptorAppliedImplementation[key as keyof ServiceImpl] = appliedHandler + interceptorAppliedImplementation[key as keyof Implementation] = appliedHandler } this.services.push({ service, implementation: interceptorAppliedImplementation }) diff --git a/packages/grpc-es/tsconfig.json b/packages/grpc-es/tsconfig.json index ba9b178..3a612ab 100644 --- a/packages/grpc-es/tsconfig.json +++ b/packages/grpc-es/tsconfig.json @@ -3,6 +3,8 @@ "compilerOptions": { "rootDir": "src", "outDir": "lib", + "module": "NodeNext", + "moduleResolution": "NodeNext" }, "include": ["src"] }