@webda/grpc 4.0.0-beta.5 → 4.0.0-beta.7
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 +4 -4
- package/CHANGELOG.md +46 -0
- package/README.md +24 -10
- package/lib/grpc-context.d.ts +55 -0
- package/lib/grpc-context.d.ts.map +1 -0
- package/lib/grpc-context.js +83 -0
- package/lib/grpc-context.js.map +1 -0
- package/lib/grpc-stream.d.ts +13 -4
- package/lib/grpc-stream.d.ts.map +1 -1
- package/lib/grpc-stream.js +67 -23
- package/lib/grpc-stream.js.map +1 -1
- package/lib/grpcservice.service.d.ts +41 -23
- package/lib/grpcservice.service.d.ts.map +1 -1
- package/lib/grpcservice.service.js +155 -169
- package/lib/grpcservice.service.js.map +1 -1
- package/lib/proto-generator.js +1 -1
- package/lib/proto-generator.js.map +1 -1
- package/package.json +8 -5
- package/src/connect.e2e.spec.ts +114 -0
- package/src/grpc-context.ts +93 -0
- package/src/grpc-live.spec.ts +377 -0
- package/src/grpc-stream.spec.ts +122 -1
- package/src/grpc-stream.ts +63 -25
- package/src/grpcservice.service.ts +165 -196
- package/src/grpcservice.spec.ts +29 -442
- package/src/proto-generator.spec.ts +17 -0
- package/src/proto-generator.ts +1 -1
- package/src/query-statements.spec.ts +117 -0
- package/test/fixture.ts +166 -0
- package/webda.module.json +10 -0
package/.webda/cache
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"sourceDigest": "
|
|
3
|
-
"moduleDigest": "
|
|
2
|
+
"sourceDigest": "d81dae599dbdf192e0ea05f6a23c8636",
|
|
3
|
+
"moduleDigest": "8a17cf2ce8f74b1729855c804d71ddc8",
|
|
4
4
|
"generator": {
|
|
5
|
-
"@webda/compiler": "4.0.0-beta.
|
|
6
|
-
"@webda/content-mapper": "4.0.0-beta.
|
|
5
|
+
"@webda/compiler": "4.0.0-beta.7",
|
|
6
|
+
"@webda/content-mapper": "4.0.0-beta.7"
|
|
7
7
|
}
|
|
8
8
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [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)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* 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.
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* 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))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Dependencies
|
|
16
|
+
|
|
17
|
+
* The following workspace dependencies were updated
|
|
18
|
+
* dependencies
|
|
19
|
+
* @webda/utils bumped to 4.0.0-beta.7
|
|
20
|
+
* devDependencies
|
|
21
|
+
* @webda/compiler bumped to 4.0.0-beta.7
|
|
22
|
+
* @webda/core bumped to 4.0.0-beta.7
|
|
23
|
+
* peerDependencies
|
|
24
|
+
* @webda/core bumped from ^4.0.0-beta.1 to ^4.0.0-beta.7
|
|
25
|
+
|
|
26
|
+
## [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)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### ⚠ BREAKING CHANGES
|
|
30
|
+
|
|
31
|
+
* **ql:** DELETE, UPDATE, SELECT, SET and WHERE are reserved uppercase keywords; Query.type is required; parse().toString() prints the canonical query instead of the source tokens; the root parse rule is now `(statement | filterQuery) EOF` for code walking the ANTLR tree.
|
|
32
|
+
|
|
33
|
+
### Features
|
|
34
|
+
|
|
35
|
+
* **ql:** WebdaQL DELETE/UPDATE/SELECT statements, bulk deleteMany/updateMany, filter-only Query operations ([#818](https://github.com/loopingz/webda.io/issues/818)) ([add9503](https://github.com/loopingz/webda.io/commit/add95031aff982cd69a635c519b4ddaa6874ecbe))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
### Dependencies
|
|
39
|
+
|
|
40
|
+
* The following workspace dependencies were updated
|
|
41
|
+
* devDependencies
|
|
42
|
+
* @webda/compiler bumped to 4.0.0-beta.6
|
|
43
|
+
* @webda/core bumped to 4.0.0-beta.6
|
|
44
|
+
* @webda/test bumped to 4.0.0-beta.6
|
|
45
|
+
* @webda/utils bumped to 4.0.0-beta.6
|
|
46
|
+
* peerDependencies
|
|
47
|
+
* @webda/core bumped from ^4.0.0-beta.1 to ^4.0.0-beta.6
|
|
48
|
+
|
|
3
49
|
## [4.0.0-beta.5](https://github.com/loopingz/webda.io/compare/grpc-v4.0.0-beta.4...grpc-v4.0.0-beta.5) (2026-10-08)
|
|
4
50
|
|
|
5
51
|
|
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/
|
|
5
|
+
<img src="https://webda.io/img/webda.svg" width="128" />
|
|
6
6
|
|
|
7
7
|

|
|
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
|
|
47
|
-
|
|
48
|
-
| `protoFile`
|
|
49
|
-
| `packageName`
|
|
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://
|
|
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://
|
|
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.
|
|
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"}
|
package/lib/grpc-stream.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
74
|
+
* @returns false when the response buffer is full (wait for "drain")
|
|
66
75
|
*/
|
|
67
|
-
send(message: ResponseType):
|
|
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)
|
package/lib/grpc-stream.d.ts.map
CHANGED
|
@@ -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;
|
|
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;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"}
|
package/lib/grpc-stream.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
58
|
-
this.
|
|
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.
|
|
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
|
|
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
|
}
|
package/lib/grpc-stream.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grpc-stream.js","sourceRoot":"","sources":["../src/grpc-stream.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"grpc-stream.js","sourceRoot":"","sources":["../src/grpc-stream.ts"],"names":[],"mappings":"AAEA,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,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC;IAC9C,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
|
|
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
|
|
@@ -14,6 +14,20 @@ export declare class GrpcServiceParameters extends OperationsTransportParameters
|
|
|
14
14
|
* @default "webda"
|
|
15
15
|
*/
|
|
16
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
|
-
*
|
|
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
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* @param
|
|
77
|
-
* @
|
|
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
|
|
109
|
+
private cleanMessage;
|
|
90
110
|
/**
|
|
91
|
-
*
|
|
92
|
-
* @param
|
|
93
|
-
* @
|
|
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
|
|
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,
|
|
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,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;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,CAuBtB;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"}
|