@moq/web-transport 0.0.4 → 0.1.1
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 +141 -0
- package/{index.d.ts → napi.d.ts} +2 -0
- package/package.json +9 -9
- package/src/datagrams.ts +1 -1
- package/src/index.ts +3 -1
- package/src/request.ts +24 -0
- package/src/server.ts +24 -0
- package/src/session.ts +1 -1
- package/web-transport.darwin-arm64.node +0 -0
- package/web-transport.darwin-x64.node +0 -0
- package/web-transport.linux-arm64-gnu.node +0 -0
- package/web-transport.linux-x64-gnu.node +0 -0
- package/web-transport.win32-x64-msvc.node +0 -0
- /package/{index.js → napi.js} +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# @moq/web-transport
|
|
2
|
+
|
|
3
|
+
WebTransport for Node.js, powered by native QUIC/HTTP3 via NAPI-RS.
|
|
4
|
+
|
|
5
|
+
Provides a custom client and server API for setup, then exposes the standard [W3C WebTransport API](https://www.w3.org/TR/webtransport/) for streams, datagrams, and session lifecycle.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun add @moq/web-transport
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Client
|
|
14
|
+
|
|
15
|
+
Connect to a WebTransport server using the `Session` class, which implements the W3C `WebTransport` interface:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import Session from "@moq/web-transport";
|
|
19
|
+
|
|
20
|
+
const session = new Session("https://example.com:4443/path");
|
|
21
|
+
await session.ready;
|
|
22
|
+
|
|
23
|
+
// Use the standard W3C WebTransport API from here on
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Certificate options
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
// Skip certificate verification (testing only!)
|
|
30
|
+
const session = new Session("https://localhost:4443", {
|
|
31
|
+
serverCertificateDisableVerify: true,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Pin to specific certificate hashes
|
|
35
|
+
const session = new Session("https://localhost:4443", {
|
|
36
|
+
serverCertificateHashes: [
|
|
37
|
+
{ algorithm: "sha-256", value: certHash },
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Polyfill
|
|
43
|
+
|
|
44
|
+
Install `Session` as the global `WebTransport` for libraries that expect the browser API:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { install } from "@moq/web-transport";
|
|
48
|
+
|
|
49
|
+
install(); // globalThis.WebTransport = Session (no-op if already defined)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Server
|
|
53
|
+
|
|
54
|
+
Use `Server` to accept incoming connections. Each `Request` can be accepted (returning a W3C `Session`) or rejected:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { Server } from "@moq/web-transport";
|
|
58
|
+
import fs from "node:fs";
|
|
59
|
+
|
|
60
|
+
const cert = fs.readFileSync("cert.pem");
|
|
61
|
+
const key = fs.readFileSync("key.pem");
|
|
62
|
+
|
|
63
|
+
const server = Server.bind("[::]:4443", cert, key);
|
|
64
|
+
|
|
65
|
+
while (true) {
|
|
66
|
+
const request = await server.accept();
|
|
67
|
+
if (!request) break;
|
|
68
|
+
|
|
69
|
+
const url = await request.url;
|
|
70
|
+
console.log("incoming session:", url);
|
|
71
|
+
|
|
72
|
+
const session = await request.ok(); // or request.reject(404)
|
|
73
|
+
|
|
74
|
+
// Use the standard W3C WebTransport API from here on
|
|
75
|
+
handleSession(session);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Stop accepting new connections
|
|
79
|
+
server.close();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## W3C WebTransport API
|
|
83
|
+
|
|
84
|
+
Once you have a `Session` (client or server-side), the API follows the [W3C WebTransport spec](https://www.w3.org/TR/webtransport/):
|
|
85
|
+
|
|
86
|
+
### Bidirectional streams
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
// Open a bidirectional stream
|
|
90
|
+
const stream = await session.createBidirectionalStream();
|
|
91
|
+
const writer = stream.writable.getWriter();
|
|
92
|
+
await writer.write(new Uint8Array([1, 2, 3]));
|
|
93
|
+
await writer.close();
|
|
94
|
+
|
|
95
|
+
// Accept incoming bidirectional streams
|
|
96
|
+
const reader = session.incomingBidirectionalStreams.getReader();
|
|
97
|
+
const { value: incoming } = await reader.read();
|
|
98
|
+
const data = await new Response(incoming.readable).arrayBuffer();
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Unidirectional streams
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
// Open a unidirectional stream
|
|
105
|
+
const writable = await session.createUnidirectionalStream();
|
|
106
|
+
const writer = writable.getWriter();
|
|
107
|
+
await writer.write(new TextEncoder().encode("hello"));
|
|
108
|
+
await writer.close();
|
|
109
|
+
|
|
110
|
+
// Accept incoming unidirectional streams
|
|
111
|
+
const reader = session.incomingUnidirectionalStreams.getReader();
|
|
112
|
+
const { value: readable } = await reader.read();
|
|
113
|
+
const text = await new Response(readable).text();
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Datagrams
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
// Send datagrams
|
|
120
|
+
const writer = session.datagrams.writable.getWriter();
|
|
121
|
+
await writer.write(new Uint8Array([0x01, 0x02]));
|
|
122
|
+
|
|
123
|
+
// Receive datagrams
|
|
124
|
+
const reader = session.datagrams.readable.getReader();
|
|
125
|
+
const { value: datagram } = await reader.read();
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Closing
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
// Close gracefully
|
|
132
|
+
session.close({ closeCode: 0, reason: "done" });
|
|
133
|
+
|
|
134
|
+
// Wait for the session to close
|
|
135
|
+
const info = await session.closed;
|
|
136
|
+
console.log(info.closeCode, info.reason);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
MIT OR Apache-2.0
|
package/{index.d.ts → napi.d.ts}
RENAMED
|
@@ -59,6 +59,8 @@ export declare class NapiServer {
|
|
|
59
59
|
static bind(addr: string, certPem: Buffer, keyPem: Buffer): NapiServer;
|
|
60
60
|
/** Accept the next incoming WebTransport session request. */
|
|
61
61
|
accept(): Promise<NapiRequest | null>;
|
|
62
|
+
/** Close the server, stopping it from accepting new connections. */
|
|
63
|
+
close(): void;
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
/** An established WebTransport session. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moq/web-transport",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "WebTransport polyfill for Node.js via QUIC/HTTP3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "(MIT OR Apache-2.0)",
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
"types": "./src/index.ts",
|
|
22
22
|
"files": [
|
|
23
23
|
"./src",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
24
|
+
"napi.js",
|
|
25
|
+
"napi.d.ts",
|
|
26
26
|
"*.node"
|
|
27
27
|
],
|
|
28
28
|
"scripts": {
|
|
29
|
-
"build": "napi build --platform --release --manifest-path ../../rs/web-transport-node/Cargo.toml --output-dir .",
|
|
29
|
+
"build": "napi build --platform --release --manifest-path ../../rs/web-transport-node/Cargo.toml --output-dir . --js napi.js --dts napi.d.ts",
|
|
30
30
|
"check": "tsc --noEmit",
|
|
31
31
|
"release": "bun scripts/release.ts"
|
|
32
32
|
},
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
"@types/node": "^24.3.0"
|
|
37
37
|
},
|
|
38
38
|
"optionalDependencies": {
|
|
39
|
-
"@moq/web-transport-darwin-x64": "0.
|
|
40
|
-
"@moq/web-transport-darwin-arm64": "0.
|
|
41
|
-
"@moq/web-transport-win32-x64-msvc": "0.
|
|
42
|
-
"@moq/web-transport-linux-x64-gnu": "0.
|
|
43
|
-
"@moq/web-transport-linux-arm64-gnu": "0.
|
|
39
|
+
"@moq/web-transport-darwin-x64": "0.1.1",
|
|
40
|
+
"@moq/web-transport-darwin-arm64": "0.1.1",
|
|
41
|
+
"@moq/web-transport-win32-x64-msvc": "0.1.1",
|
|
42
|
+
"@moq/web-transport-linux-x64-gnu": "0.1.1",
|
|
43
|
+
"@moq/web-transport-linux-arm64-gnu": "0.1.1"
|
|
44
44
|
},
|
|
45
45
|
"keywords": [
|
|
46
46
|
"webtransport",
|
package/src/datagrams.ts
CHANGED
package/src/index.ts
CHANGED
package/src/request.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { NapiRequest } from "../napi.js";
|
|
2
|
+
import Session from "./session.ts";
|
|
3
|
+
|
|
4
|
+
export class Request {
|
|
5
|
+
#inner: NapiRequest;
|
|
6
|
+
|
|
7
|
+
/** @internal */
|
|
8
|
+
constructor(inner: NapiRequest) {
|
|
9
|
+
this.#inner = inner;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get url(): Promise<string> {
|
|
13
|
+
return this.#inner.url;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async ok(): Promise<Session> {
|
|
17
|
+
const session = await this.#inner.ok();
|
|
18
|
+
return new Session(session);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
reject(status: number): Promise<void> {
|
|
22
|
+
return this.#inner.reject(status);
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { NapiServer } from "../napi.js";
|
|
2
|
+
import { Request } from "./request.ts";
|
|
3
|
+
|
|
4
|
+
export class Server {
|
|
5
|
+
#inner: NapiServer;
|
|
6
|
+
|
|
7
|
+
private constructor(inner: NapiServer) {
|
|
8
|
+
this.#inner = inner;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static bind(addr: string, certPem: Buffer, keyPem: Buffer): Server {
|
|
12
|
+
return new Server(NapiServer.bind(addr, certPem, keyPem));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async accept(): Promise<Request | null> {
|
|
16
|
+
const inner = await this.#inner.accept();
|
|
17
|
+
if (!inner) return null;
|
|
18
|
+
return new Request(inner);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
close(): void {
|
|
22
|
+
this.#inner.close();
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/session.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NapiClient, type NapiRecvStream, type NapiSendStream, type NapiSession } from "../
|
|
1
|
+
import { NapiClient, type NapiRecvStream, type NapiSendStream, type NapiSession } from "../napi.js";
|
|
2
2
|
import { Datagrams } from "./datagrams.ts";
|
|
3
3
|
|
|
4
4
|
function wrapRecvStream(recv: NapiRecvStream): ReadableStream<Uint8Array> {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
/package/{index.js → napi.js}
RENAMED
|
File without changes
|