@trevrpc/trevrpc-js 0.1.8 → 0.1.9
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/README.md +53 -159
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,202 +1,96 @@
|
|
|
1
1
|
# trevrpc-js
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
TrevRPC is an RPC framework like gRPC, but uses QUIC (and HTTP/3 / WebTransport) instead of HTTP/2. Define services in protobuf, generate typed clients and servers, and run them over QUIC.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Full documentation: https://trev.zip/llc/TrevRPC/wiki
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Protobuf
|
|
8
8
|
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
greeter.trevrpc.d.ts
|
|
12
|
-
greeter.node.trevrpc.js
|
|
13
|
-
greeter.node.trevrpc.d.ts
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
The ordinary module contains the protobuf root, service descriptors, typed clients, and client factories. The Node companion contains typed handler interfaces and registration helpers backed by `@trevrpc/trevrpc-js/node/generated`.
|
|
17
|
-
|
|
18
|
-
```sh
|
|
19
|
-
protoc \
|
|
20
|
-
--plugin=protoc-gen-trevrpc-js=./node_modules/.bin/protoc-gen-trevrpc-js \
|
|
21
|
-
--trevrpc-js_out=. \
|
|
22
|
-
greeter.proto
|
|
23
|
-
```
|
|
9
|
+
```proto
|
|
10
|
+
syntax = "proto3";
|
|
24
11
|
|
|
25
|
-
|
|
12
|
+
package example.greeter;
|
|
26
13
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
import { connect } from "@trevrpc/trevrpc-js";
|
|
33
|
-
import { GreeterClient } from "./greeter.trevrpc.js";
|
|
34
|
-
|
|
35
|
-
const channel = await connect("https://localhost:50051/trevrpc", {
|
|
36
|
-
timeoutMs: 10_000,
|
|
37
|
-
congestionControl: "low-latency",
|
|
38
|
-
serverCertificateHashes: [{ algorithm: "sha-256", value: certificateHash }],
|
|
39
|
-
});
|
|
40
|
-
try {
|
|
41
|
-
const client = new GreeterClient(channel, { timeoutMs: 5_000 });
|
|
42
|
-
const reply = await client.sayHello({ name: "TrevRPC" });
|
|
43
|
-
console.log(reply.message);
|
|
44
|
-
} finally {
|
|
45
|
-
channel.close({ closeCode: 0, reason: "client shutdown" });
|
|
14
|
+
service Greeter {
|
|
15
|
+
rpc SayHello(HelloRequest) returns (HelloReply);
|
|
16
|
+
rpc LotsOfReplies(HelloRequest) returns (stream HelloReply);
|
|
17
|
+
rpc LotsOfGreetings(stream HelloRequest) returns (HelloReply);
|
|
18
|
+
rpc BidiHello(stream HelloRequest) returns (stream HelloReply);
|
|
46
19
|
}
|
|
47
|
-
```
|
|
48
20
|
|
|
49
|
-
|
|
21
|
+
message HelloRequest { string name = 1; }
|
|
22
|
+
message HelloReply { string message = 1; }
|
|
23
|
+
```
|
|
50
24
|
|
|
51
|
-
|
|
25
|
+
Generate with `protoc-gen-trevrpc-js` (`--trevrpc-js_out`).
|
|
52
26
|
|
|
53
|
-
|
|
27
|
+
## Client
|
|
54
28
|
|
|
55
29
|
```js
|
|
56
30
|
import { connect } from "@trevrpc/trevrpc-js";
|
|
57
31
|
import { GreeterClient } from "./greeter.trevrpc.js";
|
|
58
32
|
|
|
59
|
-
const channel = await connect("https://
|
|
60
|
-
|
|
61
|
-
caCertFile: "localhost-cert.pem",
|
|
62
|
-
});
|
|
63
|
-
try {
|
|
64
|
-
console.log(channel.endpoint); // frozen { host, port }
|
|
65
|
-
console.log(channel.options); // frozen effective NodeChannelOptions
|
|
66
|
-
const client = new GreeterClient(channel, { timeoutMs: 5_000 });
|
|
67
|
-
console.log((await client.sayHello({ name: "TrevRPC" })).message);
|
|
68
|
-
} finally {
|
|
69
|
-
channel.close();
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
URL and `{ host, port, ...options }` forms normalize to the same public state. Channels reconnect for future calls after native transport loss, but never replay or resume an operation that already failed.
|
|
74
|
-
|
|
75
|
-
## Streaming clients
|
|
76
|
-
|
|
77
|
-
Server-streaming methods resolve to `ResponseAsyncIterable<T>`:
|
|
33
|
+
const channel = await connect("https://127.0.0.1:50051/trevrpc");
|
|
34
|
+
const client = new GreeterClient(channel);
|
|
78
35
|
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
try {
|
|
82
|
-
for await (const reply of replies) {
|
|
83
|
-
console.log(reply.message);
|
|
84
|
-
}
|
|
85
|
-
const terminal = await replies.status;
|
|
86
|
-
console.log(terminal.code, terminal.message, terminal.metadata);
|
|
87
|
-
} finally {
|
|
88
|
-
await replies.close();
|
|
89
|
-
}
|
|
90
|
-
```
|
|
36
|
+
// Unary
|
|
37
|
+
const reply = await client.sayHello({ name: "TrevRPC" });
|
|
91
38
|
|
|
92
|
-
|
|
39
|
+
// Server streaming
|
|
40
|
+
const stream = await client.lotsOfReplies({ name: "TrevRPC" });
|
|
41
|
+
for await (const r of stream) console.log(r.message);
|
|
93
42
|
|
|
94
|
-
|
|
43
|
+
// Client streaming
|
|
44
|
+
const cs = await client.lotsOfGreetings();
|
|
45
|
+
await cs.send({ name: "Alice" });
|
|
46
|
+
await cs.send({ name: "Bob" });
|
|
47
|
+
const summary = await cs.closeAndRecv();
|
|
95
48
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
49
|
+
// Bidirectional streaming
|
|
50
|
+
const bidi = await client.bidiHello();
|
|
51
|
+
await bidi.send({ name: "Alice" });
|
|
52
|
+
console.log(await bidi.recv());
|
|
53
|
+
await bidi.closeSend();
|
|
54
|
+
for await (const r of bidi) console.log(r.message);
|
|
99
55
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Client streaming:
|
|
103
|
-
|
|
104
|
-
```js
|
|
105
|
-
const call = await client.lotsOfGreetings();
|
|
106
|
-
await call.send({ name: "Alice" });
|
|
107
|
-
await call.send({ name: "Bob" });
|
|
108
|
-
const { message, metadata } = await call.closeAndRecvWithResponse();
|
|
56
|
+
channel.close();
|
|
109
57
|
```
|
|
110
58
|
|
|
111
|
-
|
|
59
|
+
Node uses the same API via `@trevrpc/trevrpc-js/node` (native MsQuic).
|
|
112
60
|
|
|
113
|
-
|
|
114
|
-
const call = await client.bidiHello();
|
|
115
|
-
try {
|
|
116
|
-
await call.send({ name: "Alice" });
|
|
117
|
-
console.log(await call.recv());
|
|
118
|
-
await call.closeSend();
|
|
119
|
-
while ((await call.recv()) != null) {}
|
|
120
|
-
console.log(await call.status);
|
|
121
|
-
} finally {
|
|
122
|
-
await call.close();
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
`timeoutMs` is one absolute RPC deadline that begins before transport setup and covers upload, send, receive, and cleanup. The same `DeadlineExceeded` object wins signal and rejection settlement. `streamIdleTimeoutMs` remains an independent per-read inactivity bound.
|
|
127
|
-
|
|
128
|
-
## Typed Node servers
|
|
129
|
-
|
|
130
|
-
Import the generated registration helper from the Node companion. Direct protobuf responses are never confused with metadata envelopes, even when they have `message` or `messages` properties.
|
|
61
|
+
## Server (Node)
|
|
131
62
|
|
|
132
63
|
```js
|
|
133
|
-
import { Code, createStreamingResponse, createUnaryResponse } from "@trevrpc/trevrpc-js";
|
|
134
64
|
import { NodeServer } from "@trevrpc/trevrpc-js/node";
|
|
135
65
|
import { registerGreeterServer } from "./greeter.node.trevrpc.js";
|
|
66
|
+
import { createUnaryResponse, createStreamingResponse } from "@trevrpc/trevrpc-js";
|
|
136
67
|
|
|
137
68
|
const server = await NodeServer.listen({
|
|
138
69
|
host: "127.0.0.1",
|
|
139
70
|
port: 50051,
|
|
140
|
-
certFile: "
|
|
141
|
-
keyFile: "
|
|
71
|
+
certFile: "cert.pem",
|
|
72
|
+
keyFile: "key.pem",
|
|
142
73
|
});
|
|
143
74
|
|
|
144
75
|
registerGreeterServer(server, {
|
|
145
|
-
sayHello(
|
|
146
|
-
|
|
147
|
-
return createUnaryResponse(
|
|
148
|
-
{ message: `Hello, ${request.name || "world"}` },
|
|
149
|
-
{ trailer: "unary" },
|
|
150
|
-
);
|
|
76
|
+
sayHello(req) {
|
|
77
|
+
return createUnaryResponse({ message: `hello, ${req.name}` });
|
|
151
78
|
},
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
79
|
+
lotsOfReplies(req) {
|
|
80
|
+
return createStreamingResponse([
|
|
81
|
+
{ message: `hello, ${req.name}` },
|
|
82
|
+
{ message: `goodbye, ${req.name}` },
|
|
83
|
+
]);
|
|
157
84
|
},
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
message: "complete",
|
|
163
|
-
metadata: { trailer: "server-stream" },
|
|
164
|
-
});
|
|
85
|
+
async lotsOfGreetings(reqs) {
|
|
86
|
+
const names = [];
|
|
87
|
+
for await (const r of reqs) names.push(r.name);
|
|
88
|
+
return createUnaryResponse({ message: names.join(", ") });
|
|
165
89
|
},
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
for await (const request of requests) yield { message: request.name };
|
|
90
|
+
async *bidiHello(reqs) {
|
|
91
|
+
for await (const r of reqs) yield { message: `hello, ${r.name}` };
|
|
169
92
|
},
|
|
170
93
|
});
|
|
171
94
|
|
|
172
95
|
await server.serve();
|
|
173
96
|
```
|
|
174
|
-
|
|
175
|
-
Client-streaming completion uses streaming wire semantics: exactly one response message followed by exactly one terminal status; it never uses unary `respond()`.
|
|
176
|
-
|
|
177
|
-
`NodeServerCall.signal` covers effective deadlines, server shutdown, local close, and cancellation observed by native I/O. The current native bridge cannot promptly report remote peer cancellation while a handler is idle and performs no call I/O; cancellation becomes observable when the handler next enters native call I/O.
|
|
178
|
-
|
|
179
|
-
## Native package support
|
|
180
|
-
|
|
181
|
-
The core tarball is platform-neutral. Native code is supplied by the exact optional package `@trevrpc/trevrpc-js-native-linux-x64-gnu@0.1.8`.
|
|
182
|
-
|
|
183
|
-
Initial support is Linux x86-64, glibc 2.42 or newer, and Node 24. Browser usage still installs and runs on Darwin, ARM, and musl. Requesting a native Node channel on an unsupported or incomplete installation throws `TrevRpcError(Code.Unavailable)` with the detected target, supported targets, expected optional package, whether loading was missing or failed, and the original loader error as `cause`.
|
|
184
|
-
|
|
185
|
-
All addon errors crossing public Node transport and server boundaries become `TrevRpcError` unless already normalized. `nativeCode` and `cause` preserve native provenance.
|
|
186
|
-
|
|
187
|
-
## Package resolution
|
|
188
|
-
|
|
189
|
-
Conditional ESM exports are authoritative; the package has no legacy top-level `main` or `types`. Use TypeScript NodeNext resolution for Node consumers and Bundler resolution with browser conditions for browser consumers.
|
|
190
|
-
|
|
191
|
-
## Native development
|
|
192
|
-
|
|
193
|
-
Installation never builds native code. In a source checkout:
|
|
194
|
-
|
|
195
|
-
```sh
|
|
196
|
-
npm run build:native:test
|
|
197
|
-
npm test
|
|
198
|
-
npm run build:native
|
|
199
|
-
npm run verify:native:production
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
The development fallback is only `build/native/trevrpc_native.node` in a source checkout.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trevrpc/trevrpc-js",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "JavaScript WebTransport runtime and protobuf.js code generator for TrevRPC",
|
|
5
5
|
"homepage": "https://trev.zip/llc/TrevRPC",
|
|
6
6
|
"bugs": {
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"typescript": "^7.0.2"
|
|
80
80
|
},
|
|
81
81
|
"optionalDependencies": {
|
|
82
|
-
"@trevrpc/trevrpc-js-native-linux-x64-gnu": "0.1.
|
|
82
|
+
"@trevrpc/trevrpc-js-native-linux-x64-gnu": "0.1.9"
|
|
83
83
|
},
|
|
84
84
|
"engines": {
|
|
85
85
|
"node": ">=24 <25"
|