@mertushka/webrtc-node 0.1.0-alpha.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/CMakeLists.txt +143 -0
- package/LICENSE +373 -0
- package/README.md +166 -0
- package/docs/README.md +14 -0
- package/docs/architecture.md +38 -0
- package/docs/conformance.md +53 -0
- package/docs/development.md +161 -0
- package/docs/divergences.md +230 -0
- package/examples/datachannel.js +57 -0
- package/index.d.ts +340 -0
- package/lib/index.js +4139 -0
- package/lib/load-native.js +42 -0
- package/package.json +94 -0
- package/scripts/check-api-surface.js +149 -0
- package/scripts/check-ci-evidence.js +124 -0
- package/scripts/check-native-integration.js +124 -0
- package/scripts/check-package-artifact.js +91 -0
- package/scripts/check-prebuilds.js +31 -0
- package/scripts/check-wpt-results.js +117 -0
- package/scripts/check-wpt-selection.js +72 -0
- package/scripts/ensure-wpt.js +118 -0
- package/scripts/install-native.js +116 -0
- package/scripts/package-prebuild.js +69 -0
- package/scripts/print-wpt-manifest.js +7 -0
- package/scripts/run-docker-linux-ci.ps1 +73 -0
- package/scripts/run-docker-linux-ci.sh +97 -0
- package/scripts/run-wpt-smoke.js +32 -0
- package/scripts/run-wpt-subset.js +1193 -0
- package/scripts/write-ci-evidence.js +118 -0
- package/scripts/write-wpt-report.js +143 -0
- package/src/native/addon.cc +1202 -0
- package/wpt-manifest.json +129 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
The package is intentionally split between a thin native layer and a
|
|
4
|
+
browser-compatible JavaScript facade.
|
|
5
|
+
|
|
6
|
+
## Native Addon
|
|
7
|
+
|
|
8
|
+
`src/native/addon.cc` owns `libdatachannel` handles and exposes a small
|
|
9
|
+
Node-API surface to JavaScript. It must remain ABI-stable and must not use
|
|
10
|
+
direct V8 or NAN APIs.
|
|
11
|
+
|
|
12
|
+
Native callbacks never call JavaScript directly from `libdatachannel` callback
|
|
13
|
+
threads. They dispatch back to Node through a thread-safe function.
|
|
14
|
+
|
|
15
|
+
## JavaScript Facade
|
|
16
|
+
|
|
17
|
+
`lib/index.js` implements the W3C-facing behavior:
|
|
18
|
+
|
|
19
|
+
- WebIDL-style conversions
|
|
20
|
+
- DOM-style `EventTarget` behavior
|
|
21
|
+
- promise timing and operations-chain behavior
|
|
22
|
+
- WebRTC state mapping
|
|
23
|
+
- `DOMException`-style errors
|
|
24
|
+
- data-channel message, open, close, and buffered amount semantics
|
|
25
|
+
|
|
26
|
+
Keep browser-compatible behavior in JavaScript unless native behavior is
|
|
27
|
+
required for correctness.
|
|
28
|
+
|
|
29
|
+
## Type Declarations
|
|
30
|
+
|
|
31
|
+
`index.d.ts` describes the public API. Any runtime API change must update the
|
|
32
|
+
declarations and `scripts/check-api-surface.js` as needed.
|
|
33
|
+
|
|
34
|
+
## Scope Boundary
|
|
35
|
+
|
|
36
|
+
The public scope is `RTCPeerConnection` plus `RTCDataChannel` for the WebRTC
|
|
37
|
+
data-channel profile. Media tracks, transceivers, RTP sender/receiver APIs,
|
|
38
|
+
stats, DTMF, and capture devices are not implemented.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Conformance
|
|
2
|
+
|
|
3
|
+
The compatibility target is the selected WPT subset in `wpt-manifest.json`.
|
|
4
|
+
This project targets the WebRTC data-channel profile exposed through
|
|
5
|
+
`RTCPeerConnection` and `RTCDataChannel`.
|
|
6
|
+
|
|
7
|
+
## Selected Scope
|
|
8
|
+
|
|
9
|
+
Expected-pass coverage currently includes:
|
|
10
|
+
|
|
11
|
+
- `RTCPeerConnection` construction, descriptions, signaling state, ICE state,
|
|
12
|
+
ICE candidates, and data-channel negotiation
|
|
13
|
+
- `RTCDataChannel` construction, id assignment, negotiated channels, ready
|
|
14
|
+
state, open/message/close/error behavior, send variants, binary type, and
|
|
15
|
+
buffered amount behavior
|
|
16
|
+
- WebRTC-shaped constructors and events such as `RTCSessionDescription`,
|
|
17
|
+
`RTCIceCandidate`, `RTCDataChannelEvent`, and ICE events
|
|
18
|
+
|
|
19
|
+
Out-of-scope WPT areas are grouped in the manifest as `notApplicable`,
|
|
20
|
+
`needsShim`, or `expectedFail`. Media and RTP APIs are outside this package's
|
|
21
|
+
public scope.
|
|
22
|
+
|
|
23
|
+
## Running WPT
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm run wpt:ensure
|
|
27
|
+
npm run wpt:selection:check
|
|
28
|
+
npm run wpt:test
|
|
29
|
+
npm run wpt:check:strict
|
|
30
|
+
npm run wpt:report -- --output wpt-report.md
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`wpt:test` writes `wpt-results.json`. `wpt:check:strict` requires every selected
|
|
34
|
+
subtest to pass and fails if a worker retry was needed.
|
|
35
|
+
|
|
36
|
+
## CI Evidence
|
|
37
|
+
|
|
38
|
+
Each CI matrix job writes:
|
|
39
|
+
|
|
40
|
+
- `ci-evidence.json`
|
|
41
|
+
- `wpt-results.json`
|
|
42
|
+
- `wpt-report.md`
|
|
43
|
+
- `wpt-manifest.json`
|
|
44
|
+
- `wpt-manifest.txt`
|
|
45
|
+
|
|
46
|
+
After downloading CI artifacts into `ci-artifacts/`, validate the full matrix:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
npm run ci:evidence:check -- --artifacts ci-artifacts
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The verifier checks Linux, macOS, and Windows artifacts across Node 20, 22, and
|
|
53
|
+
24. It rejects missing jobs, pin mismatches, WPT failures, and WPT retries.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Development
|
|
2
|
+
|
|
3
|
+
This project builds a Node-API addon and a W3C-style JavaScript facade. Use the
|
|
4
|
+
lockfile and keep generated artifacts out of commits.
|
|
5
|
+
|
|
6
|
+
## Setup
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm ci
|
|
10
|
+
npm run native:check
|
|
11
|
+
npm run build
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`native:check` verifies the pinned `libdatachannel` commit, Node-API usage, and
|
|
15
|
+
thread-safe callback dispatch. The build uses `cmake-js` and links
|
|
16
|
+
`LibDataChannel::LibDataChannelStatic`.
|
|
17
|
+
|
|
18
|
+
If a local `libdatachannel/` checkout exists, CMake verifies it against the
|
|
19
|
+
pinned commit. Otherwise it fetches the pinned upstream commit with
|
|
20
|
+
`FetchContent`.
|
|
21
|
+
|
|
22
|
+
## Local Checks
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
npm run check
|
|
26
|
+
npm test
|
|
27
|
+
npm run api:check
|
|
28
|
+
npm run types:check
|
|
29
|
+
npm run pack:check
|
|
30
|
+
npm run wpt:selection:check
|
|
31
|
+
npm run wpt:smoke
|
|
32
|
+
npm run wpt:smoke:check
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Run the full selected WPT suite before claiming conformance changes:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
npm run wpt:ensure
|
|
39
|
+
npm run wpt:test
|
|
40
|
+
npm run wpt:check:strict
|
|
41
|
+
npm run wpt:report -- --output wpt-report.md
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The default `CI` workflow keeps pull requests fast by running the native
|
|
45
|
+
OS/Node matrix with unit tests plus a small WPT smoke subset on Ubuntu Node 24.
|
|
46
|
+
The full selected WPT matrix is in the `Conformance` workflow, which runs on
|
|
47
|
+
manual dispatch, weekly schedule, and version tags.
|
|
48
|
+
|
|
49
|
+
Only source-relevant changes run the native matrix, package-artifact source
|
|
50
|
+
build, and WPT smoke job. Source-relevant paths include `lib/`, `src/`,
|
|
51
|
+
`scripts/`, `test/`, `examples/`, `build-containers/`, `CMakeLists.txt`,
|
|
52
|
+
package files, TypeScript/API config, Biome config, and `wpt-manifest.json`.
|
|
53
|
+
Documentation, agent notes, and workflow-only maintenance changes run the
|
|
54
|
+
lightweight quality gate only.
|
|
55
|
+
|
|
56
|
+
By default, WPT is fetched into the ignored `wpt/` cache. Set `WPT_DIR` to use a
|
|
57
|
+
different pinned checkout.
|
|
58
|
+
|
|
59
|
+
For focused debugging:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
npm run wpt:test -- webrtc/RTCDataChannel-close.html
|
|
63
|
+
npm run wpt:test -- "webrtc/RTCDataChannel-send.html#Sending in ondatachannel should work"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Bare file targets use manifest include/exclude rules. A `file#subtest` selector
|
|
67
|
+
runs one exact WPT subtest.
|
|
68
|
+
|
|
69
|
+
## Formatting and Linting
|
|
70
|
+
|
|
71
|
+
Biome is used for JavaScript, TypeScript, and JSON formatting/linting:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
npm run check
|
|
75
|
+
npm run lint
|
|
76
|
+
npm run format:check
|
|
77
|
+
npm run format
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`npm run check` is the Biome gate used by GitHub Actions. The full Quality job
|
|
81
|
+
also runs `npm run types:check` and `npm run pack:check`. WPT selection checks
|
|
82
|
+
run after the native addon is built because the WPT harness loads the public
|
|
83
|
+
WebRTC facade. `pack:check` validates the npm source artifact contents. Use
|
|
84
|
+
`npm run format` before sending a pull request.
|
|
85
|
+
|
|
86
|
+
## Package Artifact
|
|
87
|
+
|
|
88
|
+
CI has a Linux `Package artifact` job that packs the npm source package,
|
|
89
|
+
extracts it in a clean directory, installs dependencies, builds the native
|
|
90
|
+
addon, and requires the package. This guards against missing files in
|
|
91
|
+
`package.json#files` before npm publication.
|
|
92
|
+
|
|
93
|
+
## Prebuilt Releases
|
|
94
|
+
|
|
95
|
+
The release workflow keeps `cmake-js` as the native build backend. Platform jobs
|
|
96
|
+
build `build/Release/webrtc_node.node`, then `npm run prebuild:package` creates
|
|
97
|
+
`prebuild-artifacts/webrtc-node-v<version>-napi-v8-<target>.tar.gz`. The npm
|
|
98
|
+
publish job downloads those artifacts, verifies the expected prebuild set,
|
|
99
|
+
uploads them to the GitHub Release, runs `pack:check`, and publishes the source
|
|
100
|
+
package. Prebuilds are not bundled inside the npm tarball.
|
|
101
|
+
|
|
102
|
+
Publishing uses npm trusted publishing with GitHub Actions OIDC, not an
|
|
103
|
+
`NPM_TOKEN` secret. Configure the npm package trusted publisher for repository
|
|
104
|
+
`mertushka/webrtc-node`, workflow filename `release.yml`, and environment `npm`
|
|
105
|
+
if the GitHub release environment is kept.
|
|
106
|
+
|
|
107
|
+
Manual `workflow_dispatch` releases expect a GitHub Release named
|
|
108
|
+
`v<package.json version>` to already exist, because prebuilt archives are
|
|
109
|
+
uploaded as release assets before `npm publish` runs.
|
|
110
|
+
|
|
111
|
+
Supported release targets are:
|
|
112
|
+
|
|
113
|
+
- `linux-x64-glibc`
|
|
114
|
+
- `linux-x64-musl`
|
|
115
|
+
- `darwin-x64`
|
|
116
|
+
- `darwin-arm64`
|
|
117
|
+
- `win32-x64`
|
|
118
|
+
|
|
119
|
+
Use `WEBRTC_NODE_NATIVE_PATH=/absolute/path/to/webrtc_node.node` to test a
|
|
120
|
+
specific local native binary. Use `npm install --build-from-source` to force the
|
|
121
|
+
install script to compile with `cmake-js`.
|
|
122
|
+
|
|
123
|
+
## Docker Linux Slice
|
|
124
|
+
|
|
125
|
+
GitHub Actions is the authoritative conformance gate. The Docker helpers are
|
|
126
|
+
optional local reproducers for Linux CI behavior when a contributor has Docker
|
|
127
|
+
available.
|
|
128
|
+
|
|
129
|
+
On Linux or macOS:
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
bash scripts/run-docker-linux-ci.sh --node-image node:20-bookworm --artifacts-dir ci-artifacts/docker-linux-node20
|
|
133
|
+
bash scripts/run-docker-linux-ci.sh --node-image node:22-bookworm --artifacts-dir ci-artifacts/docker-linux-node22
|
|
134
|
+
bash scripts/run-docker-linux-ci.sh --node-image node:24-bookworm --artifacts-dir ci-artifacts/docker-linux-node24
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
On Windows with Docker Desktop:
|
|
138
|
+
|
|
139
|
+
```powershell
|
|
140
|
+
./scripts/run-docker-linux-ci.ps1 -NodeImage node:20-bookworm -ArtifactsDir ci-artifacts/docker-linux-node20
|
|
141
|
+
./scripts/run-docker-linux-ci.ps1 -NodeImage node:22-bookworm -ArtifactsDir ci-artifacts/docker-linux-node22
|
|
142
|
+
./scripts/run-docker-linux-ci.ps1 -NodeImage node:24-bookworm -ArtifactsDir ci-artifacts/docker-linux-node24
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Target a single WPT case:
|
|
146
|
+
|
|
147
|
+
```sh
|
|
148
|
+
bash scripts/run-docker-linux-ci.sh \
|
|
149
|
+
--node-image node:24-bookworm \
|
|
150
|
+
--artifacts-dir ci-artifacts/docker-linux-node24-close \
|
|
151
|
+
--wpt-selector "webrtc/RTCDataChannel-close.html#Repeated open/send/echo/close datachannel works"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```powershell
|
|
155
|
+
./scripts/run-docker-linux-ci.ps1 -NodeImage node:24-bookworm `
|
|
156
|
+
-ArtifactsDir ci-artifacts/docker-linux-node24-close `
|
|
157
|
+
-WptSelector "webrtc/RTCDataChannel-close.html#Repeated open/send/echo/close datachannel works"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The helper writes logs and WPT artifacts under the selected `ci-artifacts/`
|
|
161
|
+
directory.
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Intentional W3C Divergences
|
|
2
|
+
|
|
3
|
+
This package targets W3C-style `RTCPeerConnection` and `RTCDataChannel`
|
|
4
|
+
behavior for Node.js. It intentionally does not expose browser media APIs. The
|
|
5
|
+
current implementation diverges from W3C WebRTC in the following places.
|
|
6
|
+
|
|
7
|
+
## Local SDP application
|
|
8
|
+
|
|
9
|
+
`libdatachannel` generates local SDP inside `setLocalDescription()`. It does not
|
|
10
|
+
provide a public API that applies arbitrary caller-supplied local SDP. The JS
|
|
11
|
+
facade accepts `setLocalDescription(description)` for browser compatibility, but
|
|
12
|
+
currently uses the description type and lets libdatachannel generate the actual
|
|
13
|
+
local SDP.
|
|
14
|
+
|
|
15
|
+
Impact: WPT cases that mutate local SDP between `createOffer()` and
|
|
16
|
+
`setLocalDescription()` are expected to fail until the binding grows a validated
|
|
17
|
+
SDP-munging strategy or native support.
|
|
18
|
+
|
|
19
|
+
## Media APIs
|
|
20
|
+
|
|
21
|
+
This package intentionally excludes media tracks, transceivers, RTP
|
|
22
|
+
senders/receivers, stats, DTMF, capture devices, and `ontrack`. libdatachannel
|
|
23
|
+
has RTP/SRTP track primitives, but they do not map directly to the exposed
|
|
24
|
+
`RTCPeerConnection` plus `RTCDataChannel` scope.
|
|
25
|
+
|
|
26
|
+
Impact: media-oriented WPT files are marked not applicable in
|
|
27
|
+
`wpt-manifest.json`.
|
|
28
|
+
|
|
29
|
+
The selected `historical.html` subset excludes only the legacy
|
|
30
|
+
`RTCRtpTransceiver` member check because `RTCRtpTransceiver` is not exposed.
|
|
31
|
+
|
|
32
|
+
## RTCConfiguration
|
|
33
|
+
|
|
34
|
+
The JS facade validates and stores W3C-shaped `RTCConfiguration` dictionaries,
|
|
35
|
+
including `iceServers`, `iceTransportPolicy`, `bundlePolicy`, `rtcpMuxPolicy`,
|
|
36
|
+
`iceCandidatePoolSize`, and `certificates`. `getConfiguration()` returns a
|
|
37
|
+
clone of that JS-visible configuration and `setConfiguration()` applies W3C
|
|
38
|
+
validation and immutable-field checks.
|
|
39
|
+
|
|
40
|
+
libdatachannel consumes ICE servers and transport policy at peer-connection
|
|
41
|
+
construction time. The current binding does not reconfigure the native ICE
|
|
42
|
+
transport after construction, so `setConfiguration()` is a W3C-compatible facade
|
|
43
|
+
operation for validation and JS-visible state rather than a native ICE restart
|
|
44
|
+
or transport reconfiguration mechanism.
|
|
45
|
+
|
|
46
|
+
Impact: the selected WPT suite covers constructor, `getConfiguration()`, and
|
|
47
|
+
`setConfiguration()` validation for ICE servers, ICE candidate pool size,
|
|
48
|
+
certificates, bundle policy, RTCP mux policy, and ICE transport policy. Media
|
|
49
|
+
candidate-gathering and media SDP RTCP-mux validation cases remain outside the
|
|
50
|
+
expected-pass set.
|
|
51
|
+
|
|
52
|
+
## Certificates
|
|
53
|
+
|
|
54
|
+
`RTCPeerConnection.generateCertificate()` and `RTCCertificate` are implemented
|
|
55
|
+
as native-backed WebRTC objects with generated PEM/key material, expiration,
|
|
56
|
+
and fingerprint accessors. `RTCPeerConnection` validates expired certificates,
|
|
57
|
+
rejects `setConfiguration()` calls that change the certificate set, and passes
|
|
58
|
+
the first configured certificate into libdatachannel's `rtc::Configuration` so
|
|
59
|
+
the generated DTLS fingerprint appears in local SDP.
|
|
60
|
+
|
|
61
|
+
libdatachannel accepts one certificate/key pair for a peer connection. The W3C
|
|
62
|
+
configuration dictionary allows a sequence of certificates, and WPT has a case
|
|
63
|
+
that expects all configured certificate fingerprints to appear in SDP. The
|
|
64
|
+
current binding preserves the JS-visible certificate set for W3C
|
|
65
|
+
`getConfiguration()`/`setConfiguration()` behavior but uses only the first
|
|
66
|
+
certificate for native DTLS identity.
|
|
67
|
+
|
|
68
|
+
Impact: the selected WPT suite covers certificate generation, unsupported
|
|
69
|
+
algorithm rejection, expiration validation, fingerprint object shape, and
|
|
70
|
+
certificate immutability in `setConfiguration()`, plus single-certificate SDP
|
|
71
|
+
fingerprint generation. The multi-certificate SDP fingerprint case remains
|
|
72
|
+
outside expected-pass until the binding has a defensible multi-certificate
|
|
73
|
+
strategy.
|
|
74
|
+
|
|
75
|
+
## Transport object surface
|
|
76
|
+
|
|
77
|
+
`RTCPeerConnection.sctp` is implemented as a data-channel-backed
|
|
78
|
+
`RTCSctpTransport` facade, with a minimal `RTCDtlsTransport` object available at
|
|
79
|
+
`sctp.transport`. The facade is created only when SDP contains an
|
|
80
|
+
`m=application` data section. It tracks `connecting`, `connected`, and `closed`
|
|
81
|
+
from peer/data-channel state, exposes same-process remote certificates through
|
|
82
|
+
`getRemoteCertificates()` after connection, and exposes negotiated
|
|
83
|
+
`maxMessageSize` and post-connect `maxChannels` where the current native
|
|
84
|
+
binding can support them.
|
|
85
|
+
As in browsers, these transport facade objects are exposed from
|
|
86
|
+
`RTCPeerConnection.sctp` and related attributes rather than being publicly
|
|
87
|
+
constructible. The data-channel DTLS facade reports `"new"` until both local and
|
|
88
|
+
remote descriptions are present, then `"connecting"` until the data transport is
|
|
89
|
+
connected.
|
|
90
|
+
|
|
91
|
+
Impact: the selected WPT suite covers SCTP transport creation, state events,
|
|
92
|
+
`maxChannels`, and `maxMessageSize`. A minimal `RTCIceTransport` facade is
|
|
93
|
+
available at `sctp.transport.iceTransport`; it exposes ICE role, component,
|
|
94
|
+
state, gathering state, candidate accessors, selected candidate pair, and ICE
|
|
95
|
+
parameters from the data-channel peer-connection state.
|
|
96
|
+
`getSelectedCandidatePair()` returns a WebIDL-shaped `RTCIceCandidatePair`
|
|
97
|
+
object, not a plain object, when libdatachannel exposes a connected local/remote
|
|
98
|
+
candidate pair.
|
|
99
|
+
|
|
100
|
+
Impact: the selected WPT suite covers connected data-channel selected candidate
|
|
101
|
+
pairs, unconnected SCTP ICE transport behavior, data-channel disconnected
|
|
102
|
+
transport behavior after peer close, ICE restart transport-object identity for
|
|
103
|
+
data channels, and data-channel SCTP ICE transport gathering-state and
|
|
104
|
+
connected-state consistency. The pinned `RTCIceTransport.html?rest`
|
|
105
|
+
candidate-pair test has a runner shim for its `"completed"`/`"complete"`
|
|
106
|
+
gathering-state typo; the runtime keeps the WebRTC enum value `"complete"`.
|
|
107
|
+
Disconnected timing and media transport graph cases remain outside the current
|
|
108
|
+
expected-pass set.
|
|
109
|
+
|
|
110
|
+
## Data channel closing
|
|
111
|
+
|
|
112
|
+
libdatachannel does not expose a native `closing` ready state. The JS facade
|
|
113
|
+
synthesizes `readyState === "closing"` and a `closing` event when
|
|
114
|
+
`RTCDataChannel.close()` is called. If application data was queued in the same
|
|
115
|
+
task, the facade marks the channel as closing immediately but briefly delays the
|
|
116
|
+
native SCTP close so the just-queued message can be delivered before reset.
|
|
117
|
+
|
|
118
|
+
Impact: local close behavior is browser-shaped in the JS layer. The current WPT
|
|
119
|
+
runner passes `RTCDataChannel-close.html`, including remote `closing`, repeated
|
|
120
|
+
open/send/close, and peer-close `RTCErrorEvent` cases.
|
|
121
|
+
|
|
122
|
+
For same-process peers that have exchanged SDP through this facade, the JS layer
|
|
123
|
+
also pairs peer connections and data channels by SDP/stream id. That pairing is
|
|
124
|
+
used only to synthesize remote data-channel close/error events when native close
|
|
125
|
+
callbacks are late or uneven under stress; send-drain closes still wait for
|
|
126
|
+
native delivery so queued messages are not truncated.
|
|
127
|
+
|
|
128
|
+
## Incoming data channel event timing
|
|
129
|
+
|
|
130
|
+
libdatachannel can report an incoming data channel and its first channel events
|
|
131
|
+
from native callbacks before browser-style JS event handlers have been attached.
|
|
132
|
+
The JS facade queues incoming `datachannel` announcements and holds early
|
|
133
|
+
`open`/`message`/`close` events for that channel until after the
|
|
134
|
+
`datachannel` event has been dispatched.
|
|
135
|
+
|
|
136
|
+
Impact: this is the EventTarget timing layer required for WPT-compatible
|
|
137
|
+
ordering. The current runner passes full `RTCPeerConnection-ondatachannel.html`,
|
|
138
|
+
including the cases that close or send from inside the `datachannel` handler.
|
|
139
|
+
|
|
140
|
+
## Worker-transferable data channels
|
|
141
|
+
|
|
142
|
+
Browser WebRTC exposes worker transfer behavior for `RTCDataChannel` through the
|
|
143
|
+
structured clone/transfer machinery. The current Node facade does not implement
|
|
144
|
+
transferable `RTCDataChannel` objects or a Worker-backed channel owner model.
|
|
145
|
+
|
|
146
|
+
Impact: worker and transferable data-channel WPT files remain outside
|
|
147
|
+
expected-pass, including `RTCDataChannel-worker*.js`,
|
|
148
|
+
`RTCDataChannel-worker-GC.html`, and `transfer-datachannel.html`.
|
|
149
|
+
|
|
150
|
+
## bufferedAmount
|
|
151
|
+
|
|
152
|
+
libdatachannel reports bytes buffered in its SCTP transport queue. W3C
|
|
153
|
+
`bufferedAmount` increases synchronously for every `send()` call and decreases
|
|
154
|
+
later. The JS facade maintains its own W3C-style counter and does not surface
|
|
155
|
+
native buffered-amount-low callbacks directly because they are based on
|
|
156
|
+
libdatachannel's transport queue and can race the JS-visible counter.
|
|
157
|
+
|
|
158
|
+
Impact: this is intentionally shimmed in JS. The current WPT runner passes
|
|
159
|
+
`RTCDataChannel-bufferedAmount.html`.
|
|
160
|
+
|
|
161
|
+
## End-of-candidates
|
|
162
|
+
|
|
163
|
+
libdatachannel candidate callbacks do not directly match browser
|
|
164
|
+
`icecandidate` event end-of-candidates semantics. The JS facade synthesizes a
|
|
165
|
+
final `icecandidate` event with `candidate === null` when gathering reaches
|
|
166
|
+
`complete`.
|
|
167
|
+
|
|
168
|
+
Impact: the selected WPT suite now covers candidate target validation, SDP
|
|
169
|
+
candidate insertion, and end-of-candidates mutation in
|
|
170
|
+
`RTCPeerConnection-addIceCandidate.html`. Operations-chain timing and
|
|
171
|
+
media-transceiver connection setup remain outside the selected scope.
|
|
172
|
+
|
|
173
|
+
## ICE candidate errors
|
|
174
|
+
|
|
175
|
+
`RTCPeerConnectionIceErrorEvent` and the `onicecandidateerror` handler
|
|
176
|
+
attribute are exposed for WebRTC API shape compatibility. The current
|
|
177
|
+
libdatachannel binding does not surface STUN/TURN candidate-gathering failures
|
|
178
|
+
as browser `icecandidateerror` events.
|
|
179
|
+
|
|
180
|
+
Impact: constructor and handler-attribute behavior are covered locally, while
|
|
181
|
+
`RTCPeerConnection-onicecandidateerror.https.html` remains outside the selected
|
|
182
|
+
expected-pass WPT set until native ICE-server error events are available.
|
|
183
|
+
|
|
184
|
+
## ICE restart
|
|
185
|
+
|
|
186
|
+
The default libdatachannel/libjuice path only accepts custom local ICE
|
|
187
|
+
credentials before candidate gathering starts. After gathering has started, the
|
|
188
|
+
facade treats `restartIce()` as a W3C-shaped renegotiation request and preserves
|
|
189
|
+
existing data channels through the offer/answer exchange, but it does not claim
|
|
190
|
+
fresh native ICE ufrag/password credentials.
|
|
191
|
+
|
|
192
|
+
Impact: the selected suite covers data-channel liveness with
|
|
193
|
+
`RTCDataChannel-iceRestart.html`, closed-state no-op behavior from
|
|
194
|
+
`RTCPeerConnection-restartIce.https.html`, and data-channel explicit rollback
|
|
195
|
+
gathering timing from
|
|
196
|
+
`RTCPeerConnection-explicit-rollback-iceGatheringState.html`. When native
|
|
197
|
+
gathering is already complete, the JS facade replays browser-shaped
|
|
198
|
+
`gathering`/`complete` events for the restart offer so WPT-visible task timing
|
|
199
|
+
matches the WebRTC API, but this does not imply fresh native ICE credentials.
|
|
200
|
+
WPT cases that assert fresh ICE credentials, media behavior, or detailed
|
|
201
|
+
restart signaling remain outside the current expected-pass set.
|
|
202
|
+
|
|
203
|
+
## SCTP stream limit
|
|
204
|
+
|
|
205
|
+
The audited libdatachannel commit negotiates up to 1024 SCTP streams internally.
|
|
206
|
+
The JS facade exposes that connected-state `RTCSctpTransport.maxChannels` limit
|
|
207
|
+
even if the native limit read arrives slightly after the SCTP connected event.
|
|
208
|
+
Browser WebRTC/WPT includes cases around stream ids up to 65534.
|
|
209
|
+
For WebIDL construction compatibility, negotiated channels with ids above the
|
|
210
|
+
native limit can be constructed and keep their requested `id` while remaining in
|
|
211
|
+
the `"connecting"` state. They are not backed by a native SCTP stream and are
|
|
212
|
+
not expected to become usable until libdatachannel can negotiate that stream
|
|
213
|
+
range.
|
|
214
|
+
|
|
215
|
+
Impact: the selected WPT suite covers the construction-time negotiated id
|
|
216
|
+
`65534` case. Functional high-id transport cases remain outside expected-pass
|
|
217
|
+
unless libdatachannel is configured or changed to support the larger range.
|
|
218
|
+
Full `RTCDataChannel-id.html` coverage now passes; the JS facade assigns
|
|
219
|
+
browser-visible IDs when a remote answer determines the DTLS role before
|
|
220
|
+
libdatachannel exposes a native stream id.
|
|
221
|
+
|
|
222
|
+
## Event and promise timing
|
|
223
|
+
|
|
224
|
+
Native libdatachannel callbacks run on internal worker threads. The addon uses a
|
|
225
|
+
Node-API thread-safe function and the JS facade dispatches DOM-style events on
|
|
226
|
+
the Node thread. Some exact browser task-source ordering is still being aligned
|
|
227
|
+
with WPT.
|
|
228
|
+
|
|
229
|
+
Impact: high-level negotiation and data-channel messaging work; precise timing
|
|
230
|
+
tests remain gated by the WPT harness.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { RTCPeerConnection } = require("..");
|
|
4
|
+
|
|
5
|
+
async function main() {
|
|
6
|
+
const pc1 = new RTCPeerConnection();
|
|
7
|
+
const pc2 = new RTCPeerConnection();
|
|
8
|
+
let timeout;
|
|
9
|
+
|
|
10
|
+
const cleanup = () => {
|
|
11
|
+
if (timeout) clearTimeout(timeout);
|
|
12
|
+
pc1.close();
|
|
13
|
+
pc2.close();
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
pc1.addEventListener("icecandidate", ({ candidate }) => {
|
|
17
|
+
if (candidate) pc2.addIceCandidate(candidate).catch(console.error);
|
|
18
|
+
});
|
|
19
|
+
pc2.addEventListener("icecandidate", ({ candidate }) => {
|
|
20
|
+
if (candidate) pc1.addIceCandidate(candidate).catch(console.error);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
pc2.addEventListener("datachannel", ({ channel }) => {
|
|
24
|
+
channel.addEventListener("message", ({ data }) => {
|
|
25
|
+
console.log(`pc2 received: ${data}`);
|
|
26
|
+
channel.send(`echo: ${data}`);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const channel = pc1.createDataChannel("chat");
|
|
31
|
+
channel.addEventListener("open", () => {
|
|
32
|
+
channel.send("hello from Node");
|
|
33
|
+
});
|
|
34
|
+
channel.addEventListener("message", ({ data }) => {
|
|
35
|
+
console.log(`pc1 received: ${data}`);
|
|
36
|
+
cleanup();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const offer = await pc1.createOffer();
|
|
40
|
+
await pc1.setLocalDescription(offer);
|
|
41
|
+
await pc2.setRemoteDescription(pc1.localDescription);
|
|
42
|
+
|
|
43
|
+
const answer = await pc2.createAnswer();
|
|
44
|
+
await pc2.setLocalDescription(answer);
|
|
45
|
+
await pc1.setRemoteDescription(pc2.localDescription);
|
|
46
|
+
|
|
47
|
+
timeout = setTimeout(() => {
|
|
48
|
+
cleanup();
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
console.error("Timed out waiting for data-channel echo");
|
|
51
|
+
}, 10000).unref();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
main().catch((error) => {
|
|
55
|
+
console.error(error);
|
|
56
|
+
process.exitCode = 1;
|
|
57
|
+
});
|