@webda/grpc 4.0.0-beta.6 → 4.0.0-beta.8

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/.webda/cache CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
- "sourceDigest": "2434da489c662daa01eb691dba3180bf",
3
- "moduleDigest": "f6610a7c821cec29a3cdef273083e886",
2
+ "sourceDigest": "a444cd7809b69acc33d4b7bc919a585b",
3
+ "moduleDigest": "8a17cf2ce8f74b1729855c804d71ddc8",
4
4
  "generator": {
5
- "@webda/compiler": "4.0.0-beta.6",
6
- "@webda/content-mapper": "4.0.0-beta.6"
5
+ "@webda/compiler": "4.0.0-beta.8",
6
+ "@webda/content-mapper": "4.0.0-beta.7"
7
7
  }
8
8
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.0.0-beta.8](https://github.com/loopingz/webda.io/compare/grpc-v4.0.0-beta.7...grpc-v4.0.0-beta.8) (2026-10-12)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * enable strict mode in ten small packages ([#872](https://github.com/loopingz/webda.io/issues/872)) ([ecad571](https://github.com/loopingz/webda.io/commit/ecad571d67963ea4c35b47ddfafc1b91c2d7b12f))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @webda/utils bumped to 4.0.0-beta.8
16
+ * devDependencies
17
+ * @webda/compiler bumped to 4.0.0-beta.8
18
+ * @webda/core bumped to 4.0.0-beta.8
19
+ * @webda/test bumped to 4.0.0-beta.7
20
+ * peerDependencies
21
+ * @webda/core bumped from ^4.0.0-beta.1 to ^4.0.0-beta.8
22
+
23
+ ## [4.0.0-beta.7](https://github.com/loopingz/webda.io/compare/grpc-v4.0.0-beta.6...grpc-v4.0.0-beta.7) (2026-10-11)
24
+
25
+
26
+ ### ⚠ BREAKING CHANGES
27
+
28
+ * GraphQL now exposes every non-CRUD operation by default (opt out with exposeOperations: []); titled nested input types are renamed <title>Input; the Object scalar no longer JSON-parses string variables; duplicate GraphQL type/field names are startup errors; operations declaring grpc.streaming: "client" | "bidi" now receive an async iterable of messages instead of a collected array.
29
+
30
+ ### Features
31
+
32
+ * streaming operations on gRPC, WebSocket and GraphQL ([#829](https://github.com/loopingz/webda.io/issues/829)) ([e67bfea](https://github.com/loopingz/webda.io/commit/e67bfea1984e32ad7172e486602cb532369e62f5))
33
+
34
+
35
+ ### Dependencies
36
+
37
+ * The following workspace dependencies were updated
38
+ * dependencies
39
+ * @webda/utils bumped to 4.0.0-beta.7
40
+ * devDependencies
41
+ * @webda/compiler bumped to 4.0.0-beta.7
42
+ * @webda/core bumped to 4.0.0-beta.7
43
+ * peerDependencies
44
+ * @webda/core bumped from ^4.0.0-beta.1 to ^4.0.0-beta.7
45
+
3
46
  ## [4.0.0-beta.6](https://github.com/loopingz/webda.io/compare/grpc-v4.0.0-beta.5...grpc-v4.0.0-beta.6) (2026-10-09)
4
47
 
5
48
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  This module is part of Webda Application Framework that allows you to quickly develop applications with all modern prerequisites: Security, Extensibility, GraphQL, REST, CloudNative [https://webda.io](https://webda.io)
4
4
 
5
- <img src="https://webda.io/images/webda.svg" width="128" />
5
+ <img src="https://webda.io/img/webda.svg" width="128" />
6
6
 
7
7
  ![CI](https://github.com/loopingz/webda.io/workflows/CI/badge.svg)
8
8
 
@@ -13,8 +13,6 @@ This module is part of Webda Application Framework that allows you to quickly de
13
13
 
14
14
  <!-- README_HEADER -->
15
15
 
16
- # @webda/grpc
17
-
18
16
  > gRPC transport for Webda — exposes registered operations as gRPC services over HTTP/2, with auto-generated `.proto` files from the operation registry.
19
17
 
20
18
  ## When to use it
@@ -43,10 +41,12 @@ pnpm add @webda/grpc
43
41
  }
44
42
  ```
45
43
 
46
- | Parameter | Type | Default | Required | Description |
47
- |---|---|---|---|---|
48
- | `protoFile` | string | `".webda/app.proto"` | No | Path where the generated `.proto` file is written and loaded from |
49
- | `packageName` | string | `"webda"` | No | Protobuf package name used in the generated `.proto` |
44
+ | Parameter | Type | Default | Required | Description |
45
+ | ------------------- | ------ | -------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
46
+ | `protoFile` | string | `".webda/app.proto"` | No | Path where the generated `.proto` file is written and loaded from |
47
+ | `packageName` | string | `"webda"` | No | Protobuf package name used in the generated `.proto` |
48
+ | `maxQueuedMessages` | number | `1000` | No | Most unconsumed messages a client or bidirectional call may queue; above it the call fails with `RESOURCE_EXHAUSTED` |
49
+ | `maxQueuedBytes` | number | `16777216` | No | Most bytes of unconsumed messages a client or bidirectional call may queue; above it the call fails with `RESOURCE_EXHAUSTED` |
50
50
 
51
51
  ## Usage
52
52
 
@@ -74,6 +74,19 @@ export class PostService extends Service {
74
74
  // grpcurl -plaintext -d '{"title":"Hello"}' localhost:18080 webda.PostService/CreatePost
75
75
  ```
76
76
 
77
+ ## Streaming
78
+
79
+ All four modes (unary, server, client and bidirectional streams) are live: messages are sent as the generator yields,
80
+ and input messages reach the operation as they are received. Request metadata is readable through
81
+ `useContext().getHttpContext()`. See [Operations](../../docs/pages/Concepts/Operations.md#streaming-operations) for the
82
+ signatures, and for the same `async *connect(frames)` served as a WebSocket by the REST transport.
83
+
84
+ Messages are the JSON-equivalent objects of the operation: a chunk that is not an object is sent as `{ value }` and
85
+ `__`-prefixed keys never leave the server. A compressed message ends the call with `UNIMPLEMENTED`, a message over
86
+ 4 MiB (`GRPC_MAX_MESSAGE_SIZE`) with `RESOURCE_EXHAUSTED`, and one that cannot be decoded with `INVALID_ARGUMENT`. When
87
+ the client cancels, the generator's `return()` is called so its `finally` runs; an operation that throws
88
+ `OperationCancelledError` while the client is still connected ends with `CANCELLED`.
89
+
77
90
  ## Reference
78
91
 
79
92
  - API reference: see the auto-generated typedoc at `docs/pages/Modules/grpc/`.
@@ -81,6 +94,7 @@ export class PostService extends Service {
81
94
  - Related: [`@webda/graphql`](../graphql) for the GraphQL transport; [`@webda/core`](../core) for the `OperationsTransport` base class and operation registration.
82
95
 
83
96
  <!-- README_FOOTER -->
97
+
84
98
  ## Sponsors
85
99
 
86
100
  <!--
@@ -89,12 +103,12 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
89
103
 
90
104
  Arize AI is a machine learning observability and model monitoring platform. It helps you visualize, monitor, and explain your machine learning models. [Learn more](https://arize.com)
91
105
 
92
- [<img src="https://arize.com/hubfs/arize/brand/arize-logomark-1.png" width="200">](https://arize.com)
106
+ [<img src="https://webda.io/img/sponsors/arize.svg" width="200">](https://arize.com)
93
107
 
94
108
  Loopingz is a software development company that provides consulting and development services. [Learn more](https://loopingz.com)
95
109
 
96
- [<img src="https://loopingz.com/images/logo.png" width="200">](https://loopingz.com)
110
+ [<img src="https://webda.io/img/sponsors/loopingz.png" width="200">](https://loopingz.com)
97
111
 
98
112
  Tellae is an innovative consulting firm specialized in cities transportation issues. We provide our clients, both public and private, with solutions to support your strategic and operational decisions. [Learn more](https://tellae.fr)
99
113
 
100
- [<img src="https://tellae.fr/" width="200">](https://tellae.fr)
114
+ [<img src="https://webda.io/img/sponsors/tellae.svg" width="200">](https://tellae.fr)
@@ -0,0 +1,55 @@
1
+ import type { ServerResponse } from "node:http";
2
+ import { HttpContext, StreamingOperationContext } from "@webda/core";
3
+ import type { GrpcStream } from "./grpc-stream.js";
4
+ /**
5
+ * A gRPC message for a value: objects as they are, anything else as `{ value }`, `__` keys removed
6
+ * @param value - an operation result or streamed chunk
7
+ * @returns the message
8
+ */
9
+ export declare function toGrpcMessage(value: unknown): Record<string, unknown>;
10
+ /**
11
+ * The context of one gRPC call: request metadata as HTTP headers (so useContext().getHttpContext() works and the
12
+ * session loads), the unary request message as input, and streamed output sent message by message.
13
+ */
14
+ export declare class GrpcOperationContext extends StreamingOperationContext {
15
+ private readonly stream;
16
+ private readonly response;
17
+ private message?;
18
+ /**
19
+ * @param httpContext - request line and metadata
20
+ * @param stream - the gRPC stream of the call
21
+ * @param response - the HTTP/2 response (drain/close events)
22
+ */
23
+ constructor(httpContext: HttpContext, stream: GrpcStream, response: ServerResponse);
24
+ /**
25
+ * The single request message of a unary or server-streaming call
26
+ * @param message - decoded message
27
+ * @returns this
28
+ */
29
+ setMessage(message: unknown): this;
30
+ /**
31
+ * @override
32
+ */
33
+ getRawInput(limit?: number): Promise<Buffer>;
34
+ /**
35
+ * `getInput()` reads the body through this method, not through `getRawInput()`
36
+ * @override
37
+ */
38
+ getRawInputAsString(limit?: number): Promise<string>;
39
+ /**
40
+ * The response is over
41
+ * @override
42
+ */
43
+ protected get connectionEnded(): boolean;
44
+ /**
45
+ * One gRPC message per chunk
46
+ * @override
47
+ */
48
+ protected sendChunk(chunk: any): boolean;
49
+ /**
50
+ * Wait for the response buffer to empty, or the response to close or fail
51
+ * @override
52
+ */
53
+ protected waitForDrain(signal: AbortSignal): Promise<void>;
54
+ }
55
+ //# sourceMappingURL=grpc-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grpc-context.d.ts","sourceRoot":"","sources":["../src/grpc-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,yBAAyB,EAAiB,MAAM,aAAa,CAAC;AACpF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKrE;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,yBAAyB;IAU/D,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAV3B,OAAO,CAAC,OAAO,CAAC,CAAS;IAEzB;;;;OAIG;IACH,YACE,WAAW,EAAE,WAAW,EACP,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,cAAc,EAG1C;IAED;;;;OAIG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAGjC;IAED;;OAEG;IACG,WAAW,CAAC,KAAK,GAAE,MAAyB,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnE;IAED;;;OAGG;IACG,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEzD;IAED;;;OAGG;IACH,SAAS,KAAK,eAAe,IAAI,OAAO,CAGvC;IAED;;;OAGG;IACH,SAAS,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAEvC;IAED;;;OAGG;IACH,UAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ/D;CACF"}
@@ -0,0 +1,83 @@
1
+ import { once } from "node:events";
2
+ import { StreamingOperationContext, toPublicChunk } from "@webda/core";
3
+ /**
4
+ * A gRPC message for a value: objects as they are, anything else as `{ value }`, `__` keys removed
5
+ * @param value - an operation result or streamed chunk
6
+ * @returns the message
7
+ */
8
+ export function toGrpcMessage(value) {
9
+ const plain = toPublicChunk(value);
10
+ return typeof plain === "object" && plain !== null && !Array.isArray(plain)
11
+ ? plain
12
+ : { value: plain };
13
+ }
14
+ /**
15
+ * The context of one gRPC call: request metadata as HTTP headers (so useContext().getHttpContext() works and the
16
+ * session loads), the unary request message as input, and streamed output sent message by message.
17
+ */
18
+ export class GrpcOperationContext extends StreamingOperationContext {
19
+ stream;
20
+ response;
21
+ message;
22
+ /**
23
+ * @param httpContext - request line and metadata
24
+ * @param stream - the gRPC stream of the call
25
+ * @param response - the HTTP/2 response (drain/close events)
26
+ */
27
+ constructor(httpContext, stream, response) {
28
+ super(httpContext);
29
+ this.stream = stream;
30
+ this.response = response;
31
+ }
32
+ /**
33
+ * The single request message of a unary or server-streaming call
34
+ * @param message - decoded message
35
+ * @returns this
36
+ */
37
+ setMessage(message) {
38
+ this.message = Buffer.from(JSON.stringify(message ?? {}));
39
+ return this;
40
+ }
41
+ /**
42
+ * @override
43
+ */
44
+ async getRawInput(limit = 10 * 1024 * 1024) {
45
+ return (this.message ?? Buffer.from("{}")).subarray(0, limit);
46
+ }
47
+ /**
48
+ * `getInput()` reads the body through this method, not through `getRawInput()`
49
+ * @override
50
+ */
51
+ async getRawInputAsString(limit) {
52
+ return (await this.getRawInput(limit)).toString();
53
+ }
54
+ /**
55
+ * The response is over
56
+ * @override
57
+ */
58
+ get connectionEnded() {
59
+ const response = this.response;
60
+ return Boolean(response.writableEnded || response.destroyed);
61
+ }
62
+ /**
63
+ * One gRPC message per chunk
64
+ * @override
65
+ */
66
+ sendChunk(chunk) {
67
+ return this.stream.send(toGrpcMessage(chunk));
68
+ }
69
+ /**
70
+ * Wait for the response buffer to empty, or the response to close or fail
71
+ * @override
72
+ */
73
+ async waitForDrain(signal) {
74
+ await Promise.race([
75
+ once(this.response, "drain", { signal }),
76
+ once(this.response, "close", { signal }),
77
+ once(this.response, "error", { signal }).then(([error]) => {
78
+ throw error;
79
+ })
80
+ ]);
81
+ }
82
+ }
83
+ //# sourceMappingURL=grpc-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grpc-context.js","sourceRoot":"","sources":["../src/grpc-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC,OAAO,EAAe,yBAAyB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGpF;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACzE,CAAC,CAAE,KAAiC;QACpC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,oBAAqB,SAAQ,yBAAyB;IAU9C,MAAM;IACN,QAAQ;IAVnB,OAAO,CAAU;IAEzB;;;;OAIG;IACH,YACE,WAAwB,EACP,MAAkB,EAClB,QAAwB;QAEzC,KAAK,CAAC,WAAW,CAAC,CAAC;sBAHF,MAAM;wBACN,QAAQ;IAG3B,CAAC;IAED;;;;OAIG;IACH,UAAU,CAAC,OAAgB;QACzB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,KAAK,GAAW,EAAE,GAAG,IAAI,GAAG,IAAI;QAChD,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAChE,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,mBAAmB,CAAC,KAAc;QACtC,OAAO,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACpD,CAAC;IAED;;;OAGG;IACH,IAAc,eAAe;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAA4D,CAAC;QACnF,OAAO,OAAO,CAAC,QAAQ,CAAC,aAAa,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC/D,CAAC;IAED;;;OAGG;IACO,SAAS,CAAC,KAAU;QAC5B,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IAChD,CAAC;IAED;;;OAGG;IACO,KAAK,CAAC,YAAY,CAAC,MAAmB;QAC9C,MAAM,OAAO,CAAC,IAAI,CAAC;YACjB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC;YACxC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC;YACxC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE;gBACxD,MAAM,KAAK,CAAC;YACd,CAAC,CAAC;SACH,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -1,5 +1,7 @@
1
1
  import type { Http2ServerRequest, Http2ServerResponse } from "node:http2";
2
2
  import type { IncomingMessage, ServerResponse } from "node:http";
3
+ /** Largest accepted gRPC message (declared frame length), same as the gRPC default of 4 MiB */
4
+ export declare const GRPC_MAX_MESSAGE_SIZE: number;
3
5
  /**
4
6
  * Parsed gRPC method definition with serializer/deserializer functions.
5
7
  */
@@ -30,7 +32,7 @@ export declare class GrpcStream<RequestType = any, ResponseType = any> {
30
32
  private onEndHandler?;
31
33
  private onCancelHandler?;
32
34
  private chunk;
33
- private messageLength;
35
+ private failed;
34
36
  private request;
35
37
  private response;
36
38
  private definition;
@@ -41,12 +43,19 @@ export declare class GrpcStream<RequestType = any, ResponseType = any> {
41
43
  * @param definition - protobuf method definition providing serialize/deserialize functions
42
44
  */
43
45
  constructor(req: IncomingMessage | Http2ServerRequest, res: ServerResponse | Http2ServerResponse, definition: GrpcMethodDef<RequestType, ResponseType>);
46
+ /**
47
+ * Stop processing the stream and end it with an error status
48
+ * @param status - gRPC status code
49
+ * @param message - status message
50
+ * @param err - optional underlying error to log
51
+ */
52
+ private fail;
44
53
  /**
45
54
  * Register handler for incoming messages
46
55
  * @param handler - callback invoked with each deserialized request message
47
56
  * @returns this instance for chaining
48
57
  */
49
- onMessage(handler: (message: RequestType) => void | Promise<void>): this;
58
+ onMessage(handler: (message: RequestType, size: number) => void | Promise<void>): this;
50
59
  /**
51
60
  * Register handler for stream end (client finished sending)
52
61
  * @param handler - callback invoked when the client closes the send side of the stream
@@ -62,9 +71,9 @@ export declare class GrpcStream<RequestType = any, ResponseType = any> {
62
71
  /**
63
72
  * Send a response message with gRPC framing.
64
73
  * @param message - the response message to serialize and send
65
- * @returns void
74
+ * @returns false when the response buffer is full (wait for "drain")
66
75
  */
67
- send(message: ResponseType): void;
76
+ send(message: ResponseType): boolean;
68
77
  /**
69
78
  * End the response with gRPC status trailers.
70
79
  * @param status - gRPC status code (0 = OK)
@@ -1 +1 @@
1
- {"version":3,"file":"grpc-stream.d.ts","sourceRoot":"","sources":["../src/grpc-stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,GAAG,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG;IACjD,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC;IACvC,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,GAAG,CAAC;IACzC,iBAAiB,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC;IACxC,mBAAmB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,GAAG,CAAC;IAC1C,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,UAAU,CAAC,WAAW,GAAG,GAAG,EAAE,YAAY,GAAG,GAAG;IAC3D,wDAAwD;IACxD,MAAM,CAAC,EAAE,SAAgB;IAEzB,OAAO,CAAC,gBAAgB,CAAiD;IACzE,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,eAAe,CAAC,CAAa;IACrC,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,QAAQ,CAAuC;IACvD,OAAO,CAAC,UAAU,CAA2C;IAE7D;;;;;OAKG;IACH,YACE,GAAG,EAAE,eAAe,GAAG,kBAAkB,EACzC,GAAG,EAAE,cAAc,GAAG,mBAAmB,EACzC,UAAU,EAAE,aAAa,CAAC,WAAW,EAAE,YAAY,CAAC,EAyCrD;IAED;;;;OAIG;IACH,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAGvE;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI,CAG/B;IAED;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI,CAGlC;IAED;;;;OAIG;IACH,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAOhC;IAED;;;;;OAKG;IACH,GAAG,CAAC,MAAM,GAAE,MAAU,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAO9C;IAED;;;;OAIG;IACH,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAGrC;IAED;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAE/C;CACF;AAED,wBAAwB;AACxB,eAAO,MAAM,UAAU;aACrB,EAAE,EAAE,CAAC;aACL,SAAS,EAAE,CAAC;aACZ,OAAO,EAAE,CAAC;aACV,gBAAgB,EAAE,CAAC;aACnB,iBAAiB,EAAE,CAAC;aACpB,SAAS,EAAE,CAAC;aACZ,cAAc,EAAE,CAAC;aACjB,iBAAiB,EAAE,CAAC;aACpB,kBAAkB,EAAE,CAAC;aACrB,mBAAmB,EAAE,CAAC;aACtB,OAAO,EAAE,EAAE;aACX,YAAY,EAAE,EAAE;aAChB,aAAa,EAAE,EAAE;aACjB,QAAQ,EAAE,EAAE;aACZ,WAAW,EAAE,EAAE;aACf,SAAS,EAAE,EAAE;aACb,eAAe,EAAE,EAAE;CACX,CAAC"}
1
+ {"version":3,"file":"grpc-stream.d.ts","sourceRoot":"","sources":["../src/grpc-stream.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGjE,+FAA+F;AAC/F,eAAO,MAAM,qBAAqB,QAAkB,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,GAAG,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG;IACjD,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC;IACvC,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,GAAG,CAAC;IACzC,iBAAiB,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC;IACxC,mBAAmB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,GAAG,CAAC;IAC1C,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,UAAU,CAAC,WAAW,GAAG,GAAG,EAAE,YAAY,GAAG,GAAG;IAC3D,wDAAwD;IACxD,MAAM,CAAC,EAAE,SAAgB;IAEzB,OAAO,CAAC,gBAAgB,CAA+D;IACvF,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,eAAe,CAAC,CAAa;IACrC,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,QAAQ,CAAuC;IACvD,OAAO,CAAC,UAAU,CAA2C;IAE7D;;;;;OAKG;IACH,YACE,GAAG,EAAE,eAAe,GAAG,kBAAkB,EACzC,GAAG,EAAE,cAAc,GAAG,mBAAmB,EACzC,UAAU,EAAE,aAAa,CAAC,WAAW,EAAE,YAAY,CAAC,EAqDrD;IAED;;;;;OAKG;IACH,OAAO,CAAC,IAAI;IAUZ;;;;OAIG;IACH,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAGrF;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI,CAG/B;IAED;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI,CAGlC;IAED;;;;OAIG;IACH,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAOnC;IAED;;;;;OAKG;IACH,GAAG,CAAC,MAAM,GAAE,MAAU,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAa9C;IAED;;;;OAIG;IACH,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAGrC;IAED;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAE/C;CACF;AAED,wBAAwB;AACxB,eAAO,MAAM,UAAU;aACrB,EAAE,EAAE,CAAC;aACL,SAAS,EAAE,CAAC;aACZ,OAAO,EAAE,CAAC;aACV,gBAAgB,EAAE,CAAC;aACnB,iBAAiB,EAAE,CAAC;aACpB,SAAS,EAAE,CAAC;aACZ,cAAc,EAAE,CAAC;aACjB,iBAAiB,EAAE,CAAC;aACpB,kBAAkB,EAAE,CAAC;aACrB,mBAAmB,EAAE,CAAC;aACtB,OAAO,EAAE,EAAE;aACX,YAAY,EAAE,EAAE;aAChB,aAAa,EAAE,EAAE;aACjB,QAAQ,EAAE,EAAE;aACZ,WAAW,EAAE,EAAE;aACf,SAAS,EAAE,EAAE;aACb,eAAe,EAAE,EAAE;CACX,CAAC"}
@@ -1,3 +1,6 @@
1
+ import { useLog } from "@webda/workout";
2
+ /** Largest accepted gRPC message (declared frame length), same as the gRPC default of 4 MiB */
3
+ export const GRPC_MAX_MESSAGE_SIZE = 4 * 1024 * 1024;
1
4
  /**
2
5
  * gRPC bidirectional stream handler.
3
6
  *
@@ -17,7 +20,7 @@ export class GrpcStream {
17
20
  onEndHandler;
18
21
  onCancelHandler;
19
22
  chunk = null;
20
- messageLength = 0;
23
+ failed = false;
21
24
  request;
22
25
  response;
23
26
  definition;
@@ -35,37 +38,71 @@ export class GrpcStream {
35
38
  this.response.setHeader("Content-Type", "application/grpc");
36
39
  this.response.setHeader("Grpc-Accept-Encoding", "identity");
37
40
  this.response.setHeader("Grpc-Encoding", "identity");
38
- // Parse incoming gRPC frames
41
+ // Parse incoming gRPC frames (1-byte flag + 4-byte big-endian length + payload); a chunk may hold part of a
42
+ // header, several frames, or both
39
43
  req.on("data", (data) => {
40
- if (this.chunk === null) {
41
- this.messageLength = data.readUInt32BE(1);
42
- this.chunk = data;
43
- }
44
- else {
45
- this.chunk = Buffer.concat([this.chunk, data]);
46
- }
47
- // Check if we have a complete message
48
- while (this.chunk && this.chunk.length >= 5 + this.messageLength) {
49
- const message = this.chunk.subarray(5, 5 + this.messageLength);
50
- const deserialized = this.definition.requestDeserialize(message);
51
- this.onMessageHandler?.(deserialized);
52
- // Advance to next frame
53
- this.chunk = this.chunk.subarray(5 + this.messageLength);
54
- if (this.chunk.length >= 5) {
55
- this.messageLength = this.chunk.readUInt32BE(1);
44
+ if (this.failed)
45
+ return;
46
+ this.chunk = this.chunk ? Buffer.concat([this.chunk, data]) : data;
47
+ while (this.chunk.length >= 5) {
48
+ const compressed = this.chunk.readUInt8(0);
49
+ const length = this.chunk.readUInt32BE(1);
50
+ // Reject on the header alone: never buffer an oversized or compressed body
51
+ if (compressed)
52
+ return this.fail(GrpcStatus.UNIMPLEMENTED, "compressed messages are not supported");
53
+ if (length > GRPC_MAX_MESSAGE_SIZE) {
54
+ return this.fail(GrpcStatus.RESOURCE_EXHAUSTED, `message exceeds ${GRPC_MAX_MESSAGE_SIZE} bytes`);
55
+ }
56
+ if (this.chunk.length < 5 + length)
57
+ break;
58
+ const message = this.chunk.subarray(5, 5 + length);
59
+ this.chunk = this.chunk.subarray(5 + length);
60
+ let decoded;
61
+ try {
62
+ decoded = this.definition.requestDeserialize(message);
56
63
  }
57
- else if (this.chunk.length === 0) {
58
- this.chunk = null;
64
+ catch (err) {
65
+ return this.fail(GrpcStatus.INVALID_ARGUMENT, `cannot decode message: ${err?.message}`);
66
+ }
67
+ try {
68
+ const result = this.onMessageHandler?.(decoded, length);
69
+ if (result && typeof result.catch === "function") {
70
+ result.catch(err => this.fail(GrpcStatus.INTERNAL, "message handler failed", err));
71
+ }
72
+ }
73
+ catch (err) {
74
+ return this.fail(GrpcStatus.INTERNAL, "message handler failed", err);
59
75
  }
60
76
  }
77
+ if (this.chunk.length === 0)
78
+ this.chunk = null;
61
79
  });
62
80
  req.on("end", () => {
63
81
  this.onEndHandler?.();
64
82
  });
83
+ // "close" also fires after a normal end: only a close before the response ended is a cancellation
65
84
  req.on("close", () => {
66
- this.onCancelHandler?.();
85
+ if (!this.response.writableEnded)
86
+ this.onCancelHandler?.();
67
87
  });
68
88
  }
89
+ /**
90
+ * Stop processing the stream and end it with an error status
91
+ * @param status - gRPC status code
92
+ * @param message - status message
93
+ * @param err - optional underlying error to log
94
+ */
95
+ fail(status, message, err) {
96
+ if (err)
97
+ useLog("ERROR", `gRPC stream: ${message}`, err);
98
+ if (this.failed)
99
+ return;
100
+ this.failed = true;
101
+ this.chunk = null;
102
+ this.end(status, message);
103
+ // The response is over whatever the client does next: whoever feeds on this stream must stop
104
+ this.onCancelHandler?.();
105
+ }
69
106
  /**
70
107
  * Register handler for incoming messages
71
108
  * @param handler - callback invoked with each deserialized request message
@@ -96,7 +133,7 @@ export class GrpcStream {
96
133
  /**
97
134
  * Send a response message with gRPC framing.
98
135
  * @param message - the response message to serialize and send
99
- * @returns void
136
+ * @returns false when the response buffer is full (wait for "drain")
100
137
  */
101
138
  send(message) {
102
139
  const responseBuffer = this.definition.responseSerialize(message);
@@ -104,7 +141,7 @@ export class GrpcStream {
104
141
  frame.writeUInt8(0, 0); // Not compressed
105
142
  frame.writeUInt32BE(responseBuffer.length, 1);
106
143
  responseBuffer.copy(frame, 5);
107
- this.response.write(frame);
144
+ return this.response.write(frame) !== false;
108
145
  }
109
146
  /**
110
147
  * End the response with gRPC status trailers.
@@ -117,6 +154,13 @@ export class GrpcStream {
117
154
  if (message) {
118
155
  trailers["grpc-message"] = encodeURIComponent(message);
119
156
  }
157
+ if (this.response.headersSent === false) {
158
+ // Trailers-Only response: nothing was sent, the status goes in the headers
159
+ for (const [name, value] of Object.entries(trailers))
160
+ this.response.setHeader(name, value);
161
+ this.response.end();
162
+ return;
163
+ }
120
164
  this.response.addTrailers?.(trailers);
121
165
  this.response.end();
122
166
  }
@@ -1 +1 @@
1
- {"version":3,"file":"grpc-stream.js","sourceRoot":"","sources":["../src/grpc-stream.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"grpc-stream.js","sourceRoot":"","sources":["../src/grpc-stream.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,+FAA+F;AAC/F,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAcrD;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,UAAU;IACrB,wDAAwD;IACxD,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAEjB,gBAAgB,CAA+D;IAC/E,YAAY,CAAc;IAC1B,eAAe,CAAc;IAC7B,KAAK,GAAkB,IAAI,CAAC;IAC5B,MAAM,GAAG,KAAK,CAAC;IACf,OAAO,CAAuC;IAC9C,QAAQ,CAAuC;IAC/C,UAAU,CAA2C;IAE7D;;;;;OAKG;IACH,YACE,GAAyC,EACzC,GAAyC,EACzC,UAAoD;QAEpD,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC;QACnB,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;QACpB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAE7B,4BAA4B;QAC5B,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;QAC5D,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,sBAAsB,EAAE,UAAU,CAAC,CAAC;QAC5D,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;QAErD,4GAA4G;QAC5G,kCAAkC;QAClC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YAC9B,IAAI,IAAI,CAAC,MAAM;gBAAE,OAAO;YACxB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACnE,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBAC9B,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;gBAC1C,2EAA2E;gBAC3E,IAAI,UAAU;oBAAE,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,uCAAuC,CAAC,CAAC;gBACpG,IAAI,MAAM,GAAG,qBAAqB,EAAE,CAAC;oBACnC,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,kBAAkB,EAAE,mBAAmB,qBAAqB,QAAQ,CAAC,CAAC;gBACpG,CAAC;gBACD,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,MAAM;oBAAE,MAAM;gBAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC;gBACnD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;gBAC7C,IAAI,OAAoB,CAAC;gBACzB,IAAI,CAAC;oBACH,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;gBACxD,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,EAAE,0BAA2B,GAAa,EAAE,OAAO,EAAE,CAAC,CAAC;gBACrG,CAAC;gBACD,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;oBACxD,IAAI,MAAM,IAAI,OAAQ,MAAwB,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;wBACnE,MAAwB,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,wBAAwB,EAAE,GAAG,CAAC,CAAC,CAAC;oBACxG,CAAC;gBACH,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,wBAAwB,EAAE,GAAG,CAAC,CAAC;gBACvE,CAAC;YACH,CAAC;YACD,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACjD,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACjB,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;QAEH,kGAAkG;QAClG,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACnB,IAAI,CAAE,IAAI,CAAC,QAAgB,CAAC,aAAa;gBAAE,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;QACtE,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACK,IAAI,CAAC,MAAc,EAAE,OAAe,EAAE,GAAa;QACzD,IAAI,GAAG;YAAE,MAAM,CAAC,OAAO,EAAE,gBAAgB,OAAO,EAAE,EAAE,GAAG,CAAC,CAAC;QACzD,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC1B,6FAA6F;QAC7F,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,OAAqE;QAC7E,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAmB;QACvB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,QAAQ,CAAC,OAAmB;QAC1B,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC;QAC/B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,IAAI,CAAC,OAAqB;QACxB,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAClE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QACtD,KAAK,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,iBAAiB;QACzC,KAAK,CAAC,aAAa,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC9C,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC9B,OAAQ,IAAI,CAAC,QAAqB,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC;IAC5D,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,MAAM,GAAW,CAAC,EAAE,OAAgB;QACtC,MAAM,QAAQ,GAA2B,EAAE,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3E,IAAI,OAAO,EAAE,CAAC;YACZ,QAAQ,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;YACxC,2EAA2E;YAC3E,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;gBAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC3F,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QACA,IAAI,CAAC,QAAgB,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;IACtB,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,OAAqB;QAC7B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACd,CAAC;IAED;;;;;OAKG;IACH,SAAS,CAAC,MAAc,EAAE,OAAe;QACvC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5B,CAAC;CACF;AAED,wBAAwB;AACxB,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,EAAE,EAAE,CAAC;IACL,SAAS,EAAE,CAAC;IACZ,OAAO,EAAE,CAAC;IACV,gBAAgB,EAAE,CAAC;IACnB,iBAAiB,EAAE,CAAC;IACpB,SAAS,EAAE,CAAC;IACZ,cAAc,EAAE,CAAC;IACjB,iBAAiB,EAAE,CAAC;IACpB,kBAAkB,EAAE,CAAC;IACrB,mBAAmB,EAAE,CAAC;IACtB,OAAO,EAAE,EAAE;IACX,YAAY,EAAE,EAAE;IAChB,aAAa,EAAE,EAAE;IACjB,QAAQ,EAAE,EAAE;IACZ,WAAW,EAAE,EAAE;IACf,SAAS,EAAE,EAAE;IACb,eAAe,EAAE,EAAE;CACX,CAAC"}
@@ -1,4 +1,4 @@
1
- import { OperationsTransport, OperationsTransportParameters, OperationDefinition } from "@webda/core";
1
+ import { OperationDefinition, OperationsTransport, OperationsTransportParameters } from "@webda/core";
2
2
  import type { IncomingMessage, ServerResponse } from "node:http";
3
3
  /**
4
4
  * Parameters for the gRPC service
@@ -8,12 +8,26 @@ export declare class GrpcServiceParameters extends OperationsTransportParameters
8
8
  * Path to the generated .proto file
9
9
  * @default ".webda/app.proto"
10
10
  */
11
- protoFile?: string;
11
+ protoFile: string;
12
12
  /**
13
13
  * Protobuf package name
14
14
  * @default "webda"
15
15
  */
16
- packageName?: string;
16
+ packageName: string;
17
+ /**
18
+ * Most unconsumed messages a client or bidirectional operation may have queued; above it the call fails with
19
+ * RESOURCE_EXHAUSTED
20
+ *
21
+ * @default 1000
22
+ */
23
+ maxQueuedMessages: number;
24
+ /**
25
+ * Most bytes of unconsumed messages a client or bidirectional operation may have queued; above it the call fails
26
+ * with RESOURCE_EXHAUSTED
27
+ *
28
+ * @default 16777216
29
+ */
30
+ maxQueuedBytes: number;
17
31
  /**
18
32
  * Load and apply default parameter values.
19
33
  * @param params - raw configuration object to load into this parameters instance
@@ -39,6 +53,11 @@ export declare class GrpcService<T extends GrpcServiceParameters = GrpcServicePa
39
53
  private rpcToOperation;
40
54
  /** Map of gRPC path → method definition */
41
55
  private rpcMethods;
56
+ /**
57
+ * @param params - raw service parameters
58
+ * @returns the loaded parameters (with the operations include/exclude filter)
59
+ */
60
+ static createConfiguration(params?: any): GrpcServiceParameters;
42
61
  /**
43
62
  * Generate a .proto file from the current operation registry.
44
63
  *
@@ -51,6 +70,10 @@ export declare class GrpcService<T extends GrpcServiceParameters = GrpcServicePa
51
70
  * @returns the number of distinct gRPC service groups derived from operation prefixes
52
71
  */
53
72
  private countServices;
73
+ /**
74
+ * Load the .proto file and map its RPC methods to operations (also callable after registering operations late)
75
+ */
76
+ loadDefinitions(): void;
54
77
  /**
55
78
  * Initialize the gRPC transport: load proto, build RPC map, hook into HTTP/2.
56
79
  * @returns a promise resolving to this service instance once initialization is complete
@@ -61,7 +84,14 @@ export declare class GrpcService<T extends GrpcServiceParameters = GrpcServicePa
61
84
  */
62
85
  private buildRpcMap;
63
86
  /**
64
- * Handle an incoming gRPC request.
87
+ * The request line and metadata of a gRPC call as an HttpContext (HTTP/2 pseudo-headers removed)
88
+ * @param req - the HTTP/2 request
89
+ * @returns the http context
90
+ */
91
+ private httpContextOf;
92
+ /**
93
+ * Handle an incoming gRPC request: one path for the four modes. Incoming messages feed the operation (as its
94
+ * input, or as the stream in `operationInputStream`), streamed output is sent as it is produced.
65
95
  *
66
96
  * Called by the HttpServer when content-type is application/grpc.
67
97
  *
@@ -71,30 +101,18 @@ export declare class GrpcService<T extends GrpcServiceParameters = GrpcServicePa
71
101
  */
72
102
  handleGrpcRequest(req: IncomingMessage, res: ServerResponse): Promise<void>;
73
103
  /**
74
- * Handle a unary gRPC call (single request → single response).
75
- * @param stream - the active GrpcStream for this request
76
- * @param opId - the Webda operation ID to invoke
77
- * @param methodDef - the protobuf method definition with serializer functions
78
- * @returns a promise that resolves when the unary response has been sent
79
- */
80
- private handleUnary;
81
- /**
82
- * Handle server streaming (single request → stream of responses).
83
- * Maps to AsyncGenerator operations.
84
- * @param stream - the active GrpcStream for this request
85
- * @param opId - the Webda operation ID to invoke
86
- * @param methodDef - the protobuf method definition with serializer functions
87
- * @returns a promise that resolves when all response frames have been sent
104
+ * Proto3 `optional` fields deserialize with an extra `_field: "field"` marker (synthetic oneof tracking): drop
105
+ * those so the payload matches the backing JSON schema
106
+ * @param message - decoded message
107
+ * @returns the message without markers
88
108
  */
89
- private handleServerStreaming;
109
+ private cleanMessage;
90
110
  /**
91
- * Handle client or bidirectional streaming.
92
- * @param stream - the active GrpcStream for this request
93
- * @param opId - the Webda operation ID to invoke
94
- * @param streaming - streaming mode, either "client" or "bidi"
95
- * @returns a promise that resolves when the operation completes and the response is sent
111
+ * The unary response for an operation output (a plain string/number is wrapped as `{ value }`)
112
+ * @param output - the context output
113
+ * @returns the response message
96
114
  */
97
- private handleStreamingRequest;
115
+ private unaryResponse;
98
116
  /**
99
117
  * Map a Webda error to a gRPC status code.
100
118
  * @param err - the error thrown by a Webda operation, expected to have a getResponseCode method
@@ -1 +1 @@
1
- {"version":3,"file":"grpcservice.service.d.ts","sourceRoot":"","sources":["../src/grpcservice.service.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,6BAA6B,EAC7B,mBAAmB,EAWpB,MAAM,aAAa,CAAC;AAOrB,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,6BAA6B;IACtE;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,IAAI,CAAC,MAAM,GAAE,GAAQ,GAAG,IAAI,CAK3B;CACF;AAED;;;;;;;;;;GAUG;AACH,qBAAa,WAAW,CACtB,CAAC,SAAS,qBAAqB,GAAG,qBAAqB,CACvD,SAAQ,mBAAmB,CAAC,CAAC,CAAC;IAC9B,kCAAkC;IAClC,OAAO,CAAC,WAAW,CAAgC;IACnD,sCAAsC;IACtC,OAAO,CAAC,cAAc,CAAkC;IACxD,2CAA2C;IAC3C,OAAO,CAAC,UAAU,CAAkE;IAEpF;;;;OAIG;IAKG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CA4B3B;IAED;;;;OAIG;IACH,OAAO,CAAC,aAAa;IASrB;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA0D1B;IAED;;OAEG;IACH,OAAO,CAAC,WAAW;IAkCnB;;;;;;;;OAQG;IACG,iBAAiB,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmChF;IAED;;;;;;OAMG;IACH,OAAO,CAAC,WAAW;IAoDnB;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAqC7B;;;;;;OAMG;IACH,OAAO,CAAC,sBAAsB;IA2C9B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAoBzB;;;;;OAKG;IACH,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,GAAG,IAAI,CAE5E;CACF"}
1
+ {"version":3,"file":"grpcservice.service.d.ts","sourceRoot":"","sources":["../src/grpcservice.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,mBAAmB,EACnB,mBAAmB,EACnB,6BAA6B,EAW9B,MAAM,aAAa,CAAC;AAQrB,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,6BAA6B;IACtE;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;OAKG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAE1B;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,IAAI,CAAC,MAAM,GAAE,GAAQ,GAAG,IAAI,CAO3B;CACF;AAED;;;;;;;;;;GAUG;AACH,qBAAa,WAAW,CAAC,CAAC,SAAS,qBAAqB,GAAG,qBAAqB,CAAE,SAAQ,mBAAmB,CAAC,CAAC,CAAC;IAC9G,kCAAkC;IAClC,OAAO,CAAC,WAAW,CAAgC;IACnD,sCAAsC;IACtC,OAAO,CAAC,cAAc,CAAkC;IACxD,2CAA2C;IAC3C,OAAO,CAAC,UAAU,CAAkE;IAEpF;;;OAGG;IACH,MAAM,CAAC,mBAAmB,CAAC,MAAM,GAAE,GAAQ,GAAG,qBAAqB,CAElE;IAED;;;;OAIG;IAKG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CA4B3B;IAED;;;;OAIG;IACH,OAAO,CAAC,aAAa;IASrB;;OAEG;IACH,eAAe,IAAI,IAAI,CAwBtB;IAED;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAmC1B;IAED;;OAEG;IACH,OAAO,CAAC,WAAW;IAkCnB;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAQrB;;;;;;;;;OASG;IACG,iBAAiB,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqFhF;IAED;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAMpB;;;;OAIG;IACH,OAAO,CAAC,aAAa;IASrB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAoBzB;;;;;OAKG;IACH,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,GAAG,IAAI,CAE5E;CACF"}