@trevrpc/trevrpc-js 0.2.3 → 1.1.0
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 +127 -46
- package/package.json +12 -45
- package/src/bytes.js +10 -0
- package/src/call.js +520 -0
- package/src/channel.js +258 -288
- package/src/client.js +543 -1254
- package/src/datagram.js +336 -0
- package/src/framing.js +151 -548
- package/src/generator.js +66 -109
- package/src/index.d.ts +235 -463
- package/src/index.js +4 -3
- package/src/qmux/frames.js +262 -0
- package/src/qmux/params.js +104 -0
- package/src/qmux/session.js +739 -0
- package/src/race.js +90 -0
- package/src/status.js +24 -70
- package/src/varint.js +73 -0
- package/src/wire.js +21 -89
- package/examples/greeter/README.md +0 -35
- package/examples/greeter/client.js +0 -172
- package/examples/greeter/greeter.node.trevrpc.d.ts +0 -53
- package/examples/greeter/greeter.node.trevrpc.js +0 -9
- package/examples/greeter/greeter.proto +0 -18
- package/examples/greeter/greeter.trevrpc.d.ts +0 -55
- package/examples/greeter/greeter.trevrpc.js +0 -103
- package/examples/greeter/index.html +0 -140
- package/examples/greeter/static-server.js +0 -168
- package/src/advanced.d.ts +0 -29
- package/src/advanced.js +0 -1
- package/src/browser-channel.js +0 -49
- package/src/connect.js +0 -6
- package/src/connect.node.js +0 -6
- package/src/index.node.d.ts +0 -10
- package/src/index.node.js +0 -3
- package/src/native-loader.js +0 -118
- package/src/node-advanced.d.ts +0 -27
- package/src/node-advanced.js +0 -1
- package/src/node-generated.d.ts +0 -24
- package/src/node-generated.js +0 -126
- package/src/node-index.d.ts +0 -1
- package/src/node-index.js +0 -7
- package/src/node.d.ts +0 -298
- package/src/node.js +0 -1456
- package/src/runtime.js +0 -66
- package/src/server-response.js +0 -49
- package/src/timer.js +0 -29
- package/src/webtransport.js +0 -639
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# trevrpc-js
|
|
2
2
|
|
|
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,
|
|
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 call Go or Rust servers over WebTransport, falling back to QMux over WebSocket where WebTransport is missing or UDP is blocked.
|
|
4
|
+
|
|
5
|
+
`trevrpc-js` is the **browser client**: a WebTransport runtime with a QMux over WebSocket fallback, plus `protoc-gen-trevrpc-js`, which generates typed clients (no server code). It has no Node.js runtime; servers are written in Go or Rust, native QUIC clients in Go or Rust, and HTTP/3 clients in Kotlin.
|
|
4
6
|
|
|
5
7
|
Full documentation: https://trev.zip/llc/TrevRPC/wiki
|
|
6
8
|
|
|
@@ -22,75 +24,154 @@ message HelloRequest { string name = 1; }
|
|
|
22
24
|
message HelloReply { string message = 1; }
|
|
23
25
|
```
|
|
24
26
|
|
|
25
|
-
Generate with
|
|
27
|
+
Generate a client with the protoc plugin (it needs Node.js to run):
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
npm install @trevrpc/trevrpc-js
|
|
31
|
+
protoc \
|
|
32
|
+
--plugin=protoc-gen-trevrpc-js=node_modules/.bin/protoc-gen-trevrpc-js \
|
|
33
|
+
--trevrpc-js_out=src/gen \
|
|
34
|
+
greeter.proto
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This writes `greeter.trevrpc.js` and `greeter.trevrpc.d.ts`. Plugin parameters (`--trevrpc-js_out=key=value,key=value:dir`):
|
|
38
|
+
|
|
39
|
+
| Parameter | Default | Meaning |
|
|
40
|
+
| --------------------- | ------------------------ | ---------------------------------------------------------------------------------- |
|
|
41
|
+
| `runtime_import` | `@trevrpc/trevrpc-js` | Module specifier the generated JavaScript imports. |
|
|
42
|
+
| `runtime_type_import` | same as `runtime_import` | Module specifier the declarations import; required when `runtime_import` is a URL. |
|
|
43
|
+
| `file_suffix` | `.trevrpc.js` | Suffix of the generated files. |
|
|
26
44
|
|
|
27
45
|
## Client
|
|
28
46
|
|
|
29
47
|
```js
|
|
30
48
|
import { connect } from "@trevrpc/trevrpc-js";
|
|
31
|
-
import { GreeterClient } from "./greeter.trevrpc.js";
|
|
32
49
|
|
|
33
|
-
|
|
34
|
-
|
|
50
|
+
import { GreeterClient } from "./gen/greeter.trevrpc.js";
|
|
51
|
+
|
|
52
|
+
const channel = await connect("https://example.com:4433/trevrpc");
|
|
53
|
+
const client = new GreeterClient(channel, { timeoutMs: 5_000 });
|
|
35
54
|
|
|
36
55
|
// Unary
|
|
37
56
|
const reply = await client.sayHello({ name: "TrevRPC" });
|
|
38
57
|
|
|
39
58
|
// Server streaming
|
|
40
|
-
const
|
|
41
|
-
for await (const
|
|
59
|
+
const replies = await client.lotsOfReplies({ name: "TrevRPC" });
|
|
60
|
+
for await (const reply of replies) console.log(reply.message);
|
|
42
61
|
|
|
43
62
|
// Client streaming
|
|
44
|
-
const
|
|
45
|
-
await
|
|
46
|
-
await
|
|
47
|
-
const summary = await
|
|
63
|
+
const upload = await client.lotsOfGreetings();
|
|
64
|
+
await upload.send({ name: "Alice" });
|
|
65
|
+
await upload.send({ name: "Bob" });
|
|
66
|
+
const summary = await upload.closeAndRecv();
|
|
67
|
+
|
|
68
|
+
// Client streaming from an iterable or async iterable
|
|
69
|
+
const batch = await client.lotsOfGreetings();
|
|
70
|
+
await batch.sendAll([{ name: "Alice" }, { name: "Bob" }]);
|
|
71
|
+
const batchSummary = await batch.closeAndRecv();
|
|
48
72
|
|
|
49
73
|
// Bidirectional streaming
|
|
50
|
-
const
|
|
51
|
-
await
|
|
52
|
-
console.log(await
|
|
53
|
-
await
|
|
54
|
-
for await (const
|
|
74
|
+
const chat = await client.bidiHello();
|
|
75
|
+
await chat.send({ name: "Alice" });
|
|
76
|
+
console.log(await chat.recv());
|
|
77
|
+
await chat.closeSend();
|
|
78
|
+
for await (const reply of chat) console.log(reply.message);
|
|
55
79
|
|
|
56
80
|
channel.close();
|
|
57
81
|
```
|
|
58
82
|
|
|
59
|
-
|
|
83
|
+
### Call options
|
|
84
|
+
|
|
85
|
+
Every method takes `CallOptions` as its last argument; the same options can be given to the client constructor as defaults, and per-call `metadata` is merged over the defaults per key.
|
|
86
|
+
|
|
87
|
+
| Option | Meaning |
|
|
88
|
+
| -------------------- | --------------------------------------------------------------------------------------------- |
|
|
89
|
+
| `timeoutMs` | Deadline for the whole call, from the request to the final status. Also sent to the server. |
|
|
90
|
+
| `signal` | `AbortSignal` that cancels the call. |
|
|
91
|
+
| `metadata` | Request metadata (`{ key: "value" }` or a `Map`); keys are lowercase, `trevrpc-` is reserved. |
|
|
92
|
+
| `maxFrameSize` | Largest frame accepted or sent in bytes (default 4 MiB). |
|
|
93
|
+
| `onResponseHeaders` | Called with the response headers. |
|
|
94
|
+
| `onResponseMetadata` | Called with the trailing metadata of a successful call. |
|
|
60
95
|
|
|
61
|
-
|
|
96
|
+
The client enforces no other response limits: there is no cap on the number of streamed
|
|
97
|
+
messages, their total size, or idle time between them. Use `timeoutMs` or `signal` to bound a
|
|
98
|
+
call.
|
|
99
|
+
|
|
100
|
+
### Errors, cancellation and streams
|
|
101
|
+
|
|
102
|
+
- Failed calls throw `TrevRpcError` with a `code` (`Code.NotFound`, ...), `statusMessage` and `metadata`. A deadline surfaces as `Code.DeadlineExceeded`, a cancelled call as `Code.Cancelled` (with the abort reason as `cause`), and a lost connection as `Code.Unavailable`.
|
|
103
|
+
- Streaming responses end with the server's final status: iteration finishes after `OK` and throws the `TrevRpcError` otherwise. `stream.status` settles with the same outcome and carries trailing metadata. Leaving a `for await` loop early, or calling `close()`, cancels the call.
|
|
104
|
+
- Response headers and trailing metadata are separate. Every call reports them to the `onResponseHeaders(headers)` and `onResponseMetadata(metadata)` callbacks of its options: the headers (empty when the server sent none) as soon as the first response frame arrives, and the trailing metadata of the final status when the call succeeds, before its result is returned or its stream ends. A failed call carries its metadata on the `TrevRpcError`, and a callback that throws fails the call. Server-streaming and bidirectional calls also have a `headers` promise that resolves as soon as the first response frame arrives, so headers are available before the first message, and rejects with the call's error if the call fails first.
|
|
105
|
+
- Frames follow the [wire format](https://trev.zip/llc/TrevRPC/wiki/Protocol-and-Wire-Format): a type, a length and a payload, with messages sent as raw protobuf. The client skips frame types it does not know, and `maxFrameSize` limits every frame, unknown ones included.
|
|
106
|
+
- The server's response decides the outcome of a call, and a call reports it as soon as the final status arrives. If the server ends a call before the upload is done (client-streaming and bidirectional calls), the upload is cancelled and `send()` rejects at once, without waiting for the server to stop reading and whether or not the caller receives:
|
|
107
|
+
- with the server's `TrevRpcError` if its status is not OK, and the same error is the call's outcome (`closeAndRecv()`, `recv()`, `status`);
|
|
108
|
+
- with `Code.Cancelled` ("the server completed the call before the request stream ended") if its status is `OK`, for instance a server that answers after the first messages. The call itself succeeded: `closeAndRecv()` returns the response, and `recv()` and `status` end with `OK`;
|
|
109
|
+
- with the failure of the response stream, for instance `Code.Unavailable` for a lost connection, if the server stopped reading the request and the response then failed. A bare "stream closed" error from the transport is used only when the response gives no other explanation.
|
|
110
|
+
|
|
111
|
+
`send()` calls that are blocked on backpressure fail the same way, and so do all sends of a call that is cancelled or whose deadline expires. A deadline or abort that fires after the final status arrived does not change the outcome, even if the server has not finished the response stream yet.
|
|
112
|
+
|
|
113
|
+
`sendAll(requests)` sends every message of an iterable or async iterable and then closes the request stream. When the server ends the call first it stops and resolves, so `closeAndRecv()`, `recv()`, or `status` report the server's outcome instead of an upload error. Requests that throw cancel the call.
|
|
114
|
+
|
|
115
|
+
- To find the final status while the caller only sends, a client-streaming or bidirectional call reads the response ahead of `recv()` while its request side is open: at most 32 frames or 1 MiB (plus one frame of at most `maxFrameSize`) are held; beyond that reading pauses and flow control applies until `recv()` takes messages. Responses read ahead are delivered in order, including before an error that ended the stream.
|
|
116
|
+
|
|
117
|
+
### Connections
|
|
118
|
+
|
|
119
|
+
`connect()` opens a session and resolves when it is ready; it rejects if no transport can establish one. After that the `Channel` re-establishes the session in the background with exponential backoff whenever it is lost. Calls fail fast with `Unavailable` while `channel.state` is `"reconnecting"` and are never replayed; use `await channel.waitUntilReady()` or the `onStateChange` option to follow the state. `channel.close()` stops reconnecting.
|
|
62
120
|
|
|
63
121
|
```js
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const server = await NodeServer.listen({
|
|
69
|
-
host: "127.0.0.1",
|
|
70
|
-
port: 50051,
|
|
71
|
-
certFile: "cert.pem",
|
|
72
|
-
keyFile: "key.pem",
|
|
122
|
+
const channel = await connect(url, {
|
|
123
|
+
signal: AbortSignal.timeout(10_000), // cancels the initial connection only
|
|
124
|
+
serverCertificateHashes: [{ algorithm: "sha-256", value: certificateSha256 }],
|
|
125
|
+
onStateChange: ({ state, attempt, error }) => console.log(state, attempt, error),
|
|
73
126
|
});
|
|
127
|
+
```
|
|
74
128
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
},
|
|
129
|
+
`serverCertificateHashes` trusts a self-signed ECDSA certificate that is valid for at most 14 days by its SHA-256 hash, which is how development servers and the interop tests are reached. It applies to WebTransport only: the WebSocket fallback needs a certificate the browser trusts. `allowPooling`, `congestionControl` and `requireUnreliable` are passed to the `WebTransport` constructor.
|
|
130
|
+
|
|
131
|
+
`connect()` always offers the WebTransport protocol `trevrpc-1` (wire version 1) through the `protocols` constructor option; it cannot be changed. If the server selects any other protocol, that connection attempt fails with `Code.FailedPrecondition` and the channel carries on as after any failed dial: the WebSocket fallback starts, or a reconnect is retried with backoff. An empty or missing selection means version 1, because older browsers cannot negotiate protocols and the Rust server cannot echo one.
|
|
132
|
+
|
|
133
|
+
Any object with `createBidirectionalStream()`, including a plain `WebTransport` session, can be used as the transport of a generated client. Create such a session with `new WebTransport(url, { protocols: ["trevrpc-1"] })` so the server knows the wire version. Requests name the method by its full path, `/<package.Service>/<Method>` (for example `/example.greeter.Greeter/SayHello`), which the generated clients build from the service and method names.
|
|
134
|
+
|
|
135
|
+
### WebSocket fallback
|
|
136
|
+
|
|
137
|
+
`connect()` races two transports. WebTransport starts first; QMux over WebSocket (subprotocol `qmux-02.trevrpc-1`, served by Go and Rust on the server's TCP port) starts after `fallbackDelayMs` (default 300) or immediately if WebTransport fails or the browser has no `WebTransport` API. The first session to be ready wins and the other is closed. After the WebSocket wins, reconnects use it directly until `reprobeIntervalMs` (default 5 minutes) has passed since WebTransport was lost, then race again.
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
const channel = await connect("https://example.com:4433/trevrpc", {
|
|
141
|
+
transports: ["webtransport", "websocket"], // default; the first is preferred, a single entry disables the race
|
|
142
|
+
webSocketUrl: "wss://example.com:4433/trevrpc", // default: `url` with the wss: scheme
|
|
143
|
+
fallbackDelayMs: 300,
|
|
144
|
+
reprobeIntervalMs: 5 * 60_000,
|
|
145
|
+
onStateChange: ({ state, transport }) => console.log(state, transport),
|
|
93
146
|
});
|
|
147
|
+
console.log(channel.transport); // "webtransport" or "websocket"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`channel.transport` and `ChannelStateChange.transport` (set when the channel becomes ready) report which one is in use. `WebSocket` replaces the global WebSocket constructor, like `WebTransport` does for WebTransport. The QMux client is in-tree (`src/qmux/`); WebSocket backpressure polls `bufferedAmount` against a 1 MiB limit.
|
|
94
151
|
|
|
95
|
-
|
|
152
|
+
## Interop client
|
|
153
|
+
|
|
154
|
+
`trevrpc-interop-browser` runs the cross-runtime interop scenarios from [`interop/README.md`](../interop/README.md) in a real browser over WebTransport, QMux over WebSocket, or the fallback between them. It is built from `interop/proto/interop.proto` with the generator in this package, so it also serves as a complete example of generated client code.
|
|
155
|
+
|
|
156
|
+
```sh
|
|
157
|
+
trevrpc-interop-browser --browser chromium|firefox|safari \
|
|
158
|
+
--url https://127.0.0.1:4433/trevrpc \
|
|
159
|
+
--cert-hash <hex sha256 of the server certificate DER> \
|
|
160
|
+
[--transport webtransport|websocket|auto] [--ws-url wss://127.0.0.1:4433/trevrpc] \
|
|
161
|
+
[--expect-transport webtransport|websocket]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
It prints `ok <name>` or `fail <name>: <reason>` per scenario and exits 0 only if all scenarios passed. Chromium and Firefox are launched through Playwright from `PLAYWRIGHT_BROWSERS_PATH`, or from `TREVRPC_BROWSER_CHROMIUM` and `TREVRPC_BROWSER_FIREFOX` when those name an executable, and ignore certificate errors because WSS cannot pin a certificate hash. Safari is driven through `safaridriver` (`TREVRPC_BROWSER_SAFARI` overrides its path), which needs `sudo safaridriver --enable` once; Safari cannot ignore certificate errors, so the server certificate must be trusted by the system, as `interop/run.py` does. The CLI serves the page from a loopback origin; the page fetches its input from there and posts each result back, so where the browser has no WebTransport at all every scenario fails with a clear reason. `--write-page <dir>` writes that page to a directory instead, and `--page-url <url> --cert <pem> --key <pem>` opens it from a TrevRPC server that serves the directory through its HTTP fallback; the page then reports to the CLI over HTTPS, at the origin in its `report` query parameter. `--page-protocol h2|h3` has the browser load that page over HTTP/2 or HTTP/3 and fails every scenario unless it did (`h3` in Chromium only).
|
|
165
|
+
|
|
166
|
+
## Development
|
|
167
|
+
|
|
168
|
+
```sh
|
|
169
|
+
npm ci
|
|
170
|
+
npm run check # format check, lint, typecheck, tests
|
|
171
|
+
npm run build # generate the Echo client and bundle the interop page into build/interop/
|
|
172
|
+
npm run interop -- --browser chromium --url ... --cert-hash ...
|
|
96
173
|
```
|
|
174
|
+
|
|
175
|
+
`nix build .#trevrpc-js` runs the same checks and installs the library, the plugin and the wrapped `trevrpc-interop-browser` with matching Playwright browsers. The pinned `playwright` version must equal nixpkgs' `playwright-driver`; `nix run .#sync-playwright` updates it.
|
|
176
|
+
|
|
177
|
+
Tests (`node --test`) cover the wire format against `testdata/wire-golden-vectors.txt` (frame bytes, and the frame reader against the shared byte streams), the QMux frames against `testdata/qmux-golden-vectors.txt`, framing, the generator, and the client — deadlines, cancellation, stream termination and reconnection — against in-memory fake servers, including a fake Echo server that runs all interop scenarios.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trevrpc/trevrpc-js",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Browser WebTransport client and protobuf.js code generator for TrevRPC",
|
|
5
5
|
"homepage": "https://trev.zip/llc/TrevRPC",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://trev.zip/llc/TrevRPC/issues"
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"src/**",
|
|
19
19
|
"bin/protoc-gen-trevrpc-js.js",
|
|
20
|
-
"examples/greeter/**",
|
|
21
20
|
"README.md",
|
|
22
21
|
"LICENSE"
|
|
23
22
|
],
|
|
@@ -25,65 +24,33 @@
|
|
|
25
24
|
"sideEffects": false,
|
|
26
25
|
"exports": {
|
|
27
26
|
".": {
|
|
28
|
-
"node": {
|
|
29
|
-
"types": "./src/index.node.d.ts",
|
|
30
|
-
"import": "./src/index.node.js"
|
|
31
|
-
},
|
|
32
|
-
"browser": {
|
|
33
|
-
"types": "./src/index.d.ts",
|
|
34
|
-
"import": "./src/index.js"
|
|
35
|
-
},
|
|
36
27
|
"types": "./src/index.d.ts",
|
|
37
28
|
"import": "./src/index.js"
|
|
38
|
-
},
|
|
39
|
-
"./node": {
|
|
40
|
-
"types": "./src/node.d.ts",
|
|
41
|
-
"import": "./src/node-index.js"
|
|
42
|
-
},
|
|
43
|
-
"./advanced": {
|
|
44
|
-
"types": "./src/advanced.d.ts",
|
|
45
|
-
"import": "./src/advanced.js"
|
|
46
|
-
},
|
|
47
|
-
"./node/advanced": {
|
|
48
|
-
"types": "./src/node-advanced.d.ts",
|
|
49
|
-
"import": "./src/node-advanced.js"
|
|
50
|
-
},
|
|
51
|
-
"./node/generated": {
|
|
52
|
-
"types": "./src/node-generated.d.ts",
|
|
53
|
-
"import": "./src/node-generated.js"
|
|
54
29
|
}
|
|
55
30
|
},
|
|
56
31
|
"publishConfig": {
|
|
57
32
|
"access": "public"
|
|
58
33
|
},
|
|
59
34
|
"scripts": {
|
|
60
|
-
"build
|
|
61
|
-
"build:native:test": "TREVRPC_NODE_TEST_HOOKS=ON npm run build:native:cmake",
|
|
62
|
-
"build:native:cmake": "cmake -S native -B build/native -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Release} -DTREVRPC_NODE_TEST_HOOKS=${TREVRPC_NODE_TEST_HOOKS:?} ${TREVRPC_C_ROOT:+-DTREVRPC_C_ROOT=$TREVRPC_C_ROOT} ${NODE_INCLUDE_DIR:+-DNODE_INCLUDE_DIR=$NODE_INCLUDE_DIR} && cmake --build build/native --config ${CMAKE_BUILD_TYPE:-Release}",
|
|
63
|
-
"check": "npm run lint && npm run typecheck && npm run build:native:test && npm test && npm run build:native && npm run verify:native:production",
|
|
64
|
-
"example:greeter": "node examples/greeter/static-server.js",
|
|
35
|
+
"build": "node interop-browser/build.js",
|
|
65
36
|
"format": "oxfmt --write",
|
|
66
37
|
"format:check": "oxfmt --check",
|
|
67
38
|
"lint": "oxlint --deny-warnings",
|
|
68
|
-
"
|
|
69
|
-
"test": "node --test --test-concurrency=1 test/*.test.js",
|
|
70
|
-
"
|
|
71
|
-
"
|
|
39
|
+
"typecheck": "npm run build && tsc -p tsconfig.json",
|
|
40
|
+
"test": "npm run build && node --test --test-concurrency=1 test/*.test.js",
|
|
41
|
+
"check": "npm run format:check && npm run lint && npm run typecheck && npm test",
|
|
42
|
+
"interop": "node interop-browser/cli.js"
|
|
72
43
|
},
|
|
73
44
|
"dependencies": {
|
|
74
|
-
"protobufjs": "^8.8.
|
|
45
|
+
"protobufjs": "^8.8.1"
|
|
75
46
|
},
|
|
76
47
|
"devDependencies": {
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"playwright": "1.61.1",
|
|
48
|
+
"playwright": "1.64.1",
|
|
49
|
+
"rolldown": "^1.2.14",
|
|
80
50
|
"typescript": "^7.0.2"
|
|
81
51
|
},
|
|
82
52
|
"engines": {
|
|
83
|
-
"node": ">=24
|
|
53
|
+
"node": ">=24"
|
|
84
54
|
},
|
|
85
|
-
"packageManager": "npm@12.0
|
|
86
|
-
"optionalDependencies": {
|
|
87
|
-
"@trevrpc/trevrpc-js-native-linux-x64-gnu": "0.2.3"
|
|
88
|
-
}
|
|
55
|
+
"packageManager": "npm@12.2.0"
|
|
89
56
|
}
|
package/src/bytes.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Returns a Uint8Array view over bytes, typed-array views, or ArrayBuffers without copying. */
|
|
2
|
+
export function byteView(value) {
|
|
3
|
+
if (value instanceof Uint8Array) {
|
|
4
|
+
return value;
|
|
5
|
+
}
|
|
6
|
+
if (ArrayBuffer.isView(value)) {
|
|
7
|
+
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
8
|
+
}
|
|
9
|
+
return new Uint8Array(value ?? 0);
|
|
10
|
+
}
|