reactor-effect-native 0.0.0-reserved.0 → 0.3.0-rc.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/Dockerfile +40 -0
- package/LICENSE +201 -0
- package/NOTICE +28 -0
- package/README.md +181 -2
- package/dist/_internal/bridge.d.ts +105 -0
- package/dist/_internal/bridge.d.ts.map +1 -0
- package/dist/_internal/bridge.js +597 -0
- package/dist/_internal/bridge.js.map +1 -0
- package/dist/_internal/isolated/child.d.ts +2 -0
- package/dist/_internal/isolated/child.d.ts.map +1 -0
- package/dist/_internal/isolated/child.js +176 -0
- package/dist/_internal/isolated/child.js.map +1 -0
- package/dist/_internal/isolated/host.d.ts +147 -0
- package/dist/_internal/isolated/host.d.ts.map +1 -0
- package/dist/_internal/isolated/host.js +645 -0
- package/dist/_internal/isolated/host.js.map +1 -0
- package/dist/_internal/isolated/protocol.d.ts +399 -0
- package/dist/_internal/isolated/protocol.d.ts.map +1 -0
- package/dist/_internal/isolated/protocol.js +250 -0
- package/dist/_internal/isolated/protocol.js.map +1 -0
- package/dist/_internal/peer.d.ts +112 -0
- package/dist/_internal/peer.d.ts.map +1 -0
- package/dist/_internal/peer.js +555 -0
- package/dist/_internal/peer.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/isolated.d.ts +25 -0
- package/dist/isolated.d.ts.map +1 -0
- package/dist/isolated.js +39 -0
- package/dist/isolated.js.map +1 -0
- package/lib/darwin-arm64/libreactor_effect_native.dylib +0 -0
- package/lib/darwin-arm64/native-identity.json +19 -0
- package/lib/linux-x64/libreactor_effect_native.so +0 -0
- package/lib/linux-x64/native-identity.json +21 -0
- package/notices/effect-LICENSE +21 -0
- package/notices/native-rust/NOTICE.md +27 -0
- package/notices/native-rust/bytes-1.12.1-LICENSE +25 -0
- package/notices/native-rust/itoa-1.0.18-LICENSE-APACHE +176 -0
- package/notices/native-rust/memchr-2.8.3-LICENSE-MIT +21 -0
- package/notices/native-rust/prost-0.13.5-LICENSE +201 -0
- package/notices/native-rust/serde-1.0.229-LICENSE-APACHE +176 -0
- package/notices/native-rust/serde_json-1.0.151-LICENSE-APACHE +176 -0
- package/notices/native-rust/zmij-1.0.23-LICENSE-MIT +23 -0
- package/notices/reactor-webrtc/WebRTC-LICENSE +29 -0
- package/notices/reactor-webrtc/WebRTC-PATENTS +24 -0
- package/notices/reactor-webrtc/reactor-webrtc-linux-x64-release.sbom.json +660 -0
- package/notices/reactor-webrtc/reactor-webrtc-mac-arm64-release.sbom.json +631 -0
- package/notices/reactor-webrtc/third-party/README.md +58 -0
- package/notices/reactor-webrtc/third-party/abseil-cpp/LICENSE +203 -0
- package/notices/reactor-webrtc/third-party/abseil-cpp/README.chromium +55 -0
- package/notices/reactor-webrtc/third-party/boringssl/LICENSE +238 -0
- package/notices/reactor-webrtc/third-party/boringssl/README.chromium +15 -0
- package/notices/reactor-webrtc/third-party/compiler-rt/LICENSE.TXT +311 -0
- package/notices/reactor-webrtc/third-party/compiler-rt/README.chromium +15 -0
- package/notices/reactor-webrtc/third-party/cpuinfo/LICENSE +27 -0
- package/notices/reactor-webrtc/third-party/cpuinfo/README.chromium +15 -0
- package/notices/reactor-webrtc/third-party/dav1d/LICENSE +23 -0
- package/notices/reactor-webrtc/third-party/dav1d/README.chromium +40 -0
- package/notices/reactor-webrtc/third-party/farmhash/LICENSE +19 -0
- package/notices/reactor-webrtc/third-party/farmhash/README.chromium +21 -0
- package/notices/reactor-webrtc/third-party/fft2d/LICENSE +3 -0
- package/notices/reactor-webrtc/third-party/fft2d/README.chromium +24 -0
- package/notices/reactor-webrtc/third-party/flatbuffers/LICENSE +202 -0
- package/notices/reactor-webrtc/third-party/flatbuffers/README.chromium +18 -0
- package/notices/reactor-webrtc/third-party/fp16/LICENSE +12 -0
- package/notices/reactor-webrtc/third-party/fp16/README.chromium +16 -0
- package/notices/reactor-webrtc/third-party/fxdiv/LICENSE +10 -0
- package/notices/reactor-webrtc/third-party/fxdiv/README.chromium +16 -0
- package/notices/reactor-webrtc/third-party/gemmlowp/LICENSE +202 -0
- package/notices/reactor-webrtc/third-party/gemmlowp/README.chromium +14 -0
- package/notices/reactor-webrtc/third-party/grpc/LICENSE +610 -0
- package/notices/reactor-webrtc/third-party/grpc/README.chromium +30 -0
- package/notices/reactor-webrtc/third-party/jsoncpp/LICENSE +55 -0
- package/notices/reactor-webrtc/third-party/jsoncpp/README.chromium +20 -0
- package/notices/reactor-webrtc/third-party/libaom/LICENSE +27 -0
- package/notices/reactor-webrtc/third-party/libaom/PATENTS +108 -0
- package/notices/reactor-webrtc/third-party/libaom/README.chromium +61 -0
- package/notices/reactor-webrtc/third-party/libgav1/LICENSE +202 -0
- package/notices/reactor-webrtc/third-party/libgav1/README.chromium +33 -0
- package/notices/reactor-webrtc/third-party/libjpeg_turbo/LICENSE.md.chromium +236 -0
- package/notices/reactor-webrtc/third-party/libjpeg_turbo/README.chromium +66 -0
- package/notices/reactor-webrtc/third-party/libsrtp/LICENSE +35 -0
- package/notices/reactor-webrtc/third-party/libsrtp/README.chromium +17 -0
- package/notices/reactor-webrtc/third-party/libvpx/LICENSE +31 -0
- package/notices/reactor-webrtc/third-party/libvpx/PATENTS +23 -0
- package/notices/reactor-webrtc/third-party/libvpx/README.chromium +73 -0
- package/notices/reactor-webrtc/third-party/libyuv/LICENSE +29 -0
- package/notices/reactor-webrtc/third-party/libyuv/README.chromium +13 -0
- package/notices/reactor-webrtc/third-party/neon_2_sse/LICENSE +29 -0
- package/notices/reactor-webrtc/third-party/neon_2_sse/README.chromium +21 -0
- package/notices/reactor-webrtc/third-party/opus/COPYING +44 -0
- package/notices/reactor-webrtc/third-party/opus/README.chromium +41 -0
- package/notices/reactor-webrtc/third-party/perfetto/LICENSE +285 -0
- package/notices/reactor-webrtc/third-party/perfetto/README.chromium +12 -0
- package/notices/reactor-webrtc/third-party/pffft/LICENSE +45 -0
- package/notices/reactor-webrtc/third-party/pffft/README.chromium +43 -0
- package/notices/reactor-webrtc/third-party/protobuf/LICENSE +32 -0
- package/notices/reactor-webrtc/third-party/protobuf/README.chromium +325 -0
- package/notices/reactor-webrtc/third-party/pthreadpool/LICENSE +26 -0
- package/notices/reactor-webrtc/third-party/pthreadpool/README.chromium +19 -0
- package/notices/reactor-webrtc/third-party/re2/LICENSE +27 -0
- package/notices/reactor-webrtc/third-party/re2/README.chromium +15 -0
- package/notices/reactor-webrtc/third-party/rnnoise/COPYING +31 -0
- package/notices/reactor-webrtc/third-party/rnnoise/README.chromium +30 -0
- package/notices/reactor-webrtc/third-party/ruy/LICENSE +202 -0
- package/notices/reactor-webrtc/third-party/ruy/README.chromium +20 -0
- package/notices/reactor-webrtc/third-party/tflite/LICENSE +203 -0
- package/notices/reactor-webrtc/third-party/tflite/README.chromium +22 -0
- package/notices/reactor-webrtc/third-party/xnnpack/LICENSE +31 -0
- package/notices/reactor-webrtc/third-party/xnnpack/README.chromium +18 -0
- package/notices/reactor-webrtc/third-party/zlib/LICENSE +22 -0
- package/notices/reactor-webrtc/third-party/zlib/README.chromium +38 -0
- package/notices/reactor-webrtc-LICENSE +201 -0
- package/notices/reactor-webrtc-NOTICE.md +32 -0
- package/package.json +58 -4
- package/rust/.cargo/config.toml +5 -0
- package/rust/Cargo.lock +208 -0
- package/rust/Cargo.toml +125 -0
- package/rust/build.rs +298 -0
- package/rust/clippy.toml +7 -0
- package/rust/include/reactor_effect_native.h +177 -0
- package/rust/src/abi.rs +258 -0
- package/rust/src/error.rs +149 -0
- package/rust/src/ffi/memory.rs +256 -0
- package/rust/src/ffi/tests.rs +719 -0
- package/rust/src/ffi.rs +474 -0
- package/rust/src/lib.rs +38 -0
- package/rust/src/peer/callbacks.rs +145 -0
- package/rust/src/peer/media.rs +221 -0
- package/rust/src/peer/owner/tests.rs +188 -0
- package/rust/src/peer/owner.rs +353 -0
- package/rust/src/peer/shared.rs +323 -0
- package/rust/src/peer/tests.rs +513 -0
- package/rust/src/peer.rs +189 -0
- package/rust/src/protocol/event.rs +238 -0
- package/rust/src/protocol/request.rs +347 -0
- package/rust/src/protocol/stats.rs +356 -0
- package/rust/src/protocol.rs +71 -0
- package/rust/src/sync/gate.rs +159 -0
- package/rust/src/sync/notifier.rs +136 -0
- package/rust/src/sync/queue.rs +384 -0
- package/rust/src/sync.rs +20 -0
- package/rust/src/test_support.rs +99 -0
- package/rust-toolchain.toml +7 -0
- package/scripts/build.sh +35 -0
- package/scripts/install-linux-toolchain.sh +47 -0
- package/scripts/stage.mjs +147 -0
- package/scripts/stage.sh +31 -0
package/Dockerfile
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1.7
|
|
2
|
+
FROM --platform=linux/amd64 rust:1.90.0-bookworm@sha256:3914072ca0c3b8aad871db9169a651ccfce30cf58303e5d6f2db16d1d8a7e58f AS build
|
|
3
|
+
|
|
4
|
+
WORKDIR /work
|
|
5
|
+
COPY scripts/install-linux-toolchain.sh ./scripts/install-linux-toolchain.sh
|
|
6
|
+
RUN chmod 755 scripts/install-linux-toolchain.sh \
|
|
7
|
+
&& scripts/install-linux-toolchain.sh
|
|
8
|
+
RUN apt-get update \
|
|
9
|
+
&& apt-get install -y --no-install-recommends \
|
|
10
|
+
file \
|
|
11
|
+
git \
|
|
12
|
+
tar \
|
|
13
|
+
zstd \
|
|
14
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
15
|
+
|
|
16
|
+
RUN rustup component add clippy rustfmt
|
|
17
|
+
|
|
18
|
+
COPY rust-toolchain.toml ./
|
|
19
|
+
COPY rust/Cargo.toml rust/Cargo.lock rust/build.rs rust/clippy.toml ./rust/
|
|
20
|
+
COPY rust/.cargo/config.toml ./rust/.cargo/config.toml
|
|
21
|
+
COPY rust/src ./rust/src
|
|
22
|
+
COPY rust/include ./rust/include
|
|
23
|
+
|
|
24
|
+
ENV CARGO_TARGET_DIR=/target \
|
|
25
|
+
CC=clang-21 \
|
|
26
|
+
CXX=clang++-21
|
|
27
|
+
|
|
28
|
+
# reactor-webrtc-sys downloads the pinned Linux x64 libwebrtc prebuilt,
|
|
29
|
+
# verifies its published SHA-256, and compiles its C++ glue with pinned LLVM 21.
|
|
30
|
+
RUN cargo fmt --manifest-path rust/Cargo.toml -- --check
|
|
31
|
+
RUN cargo test --locked --manifest-path rust/Cargo.toml --all-targets -- --nocapture
|
|
32
|
+
RUN cargo clippy --locked --manifest-path rust/Cargo.toml --all-targets -- -D warnings
|
|
33
|
+
RUN RUSTDOCFLAGS="-D warnings" cargo doc --locked --manifest-path rust/Cargo.toml --no-deps --document-private-items
|
|
34
|
+
RUN cargo build --locked --manifest-path rust/Cargo.toml --release
|
|
35
|
+
RUN test "$(uname -m)" = "x86_64" \
|
|
36
|
+
&& file /target/release/libreactor_effect_native.so \
|
|
37
|
+
&& ldd /target/release/libreactor_effect_native.so
|
|
38
|
+
|
|
39
|
+
FROM scratch AS artifact
|
|
40
|
+
COPY --from=build /target/release/libreactor_effect_native.so /libreactor_effect_native.so
|
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright 2026 Reactor Technologies, Inc.
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
reactor-effect-native
|
|
2
|
+
|
|
3
|
+
This independent SDK is not an official Reactor SDK.
|
|
4
|
+
|
|
5
|
+
The native transport is built from the Rust bridge in rust/ against:
|
|
6
|
+
reactor-webrtc, commit bebf63e42624ee440e066b692494dd3229d29ae7 (Apache-2.0).
|
|
7
|
+
It incorporates upstream Google WebRTC binaries and associated third-party code.
|
|
8
|
+
The pinned reactor-webrtc Apache terms are retained in
|
|
9
|
+
notices/reactor-webrtc-LICENSE. Exact macOS arm64 and Linux x64 prebuilt SBOMs,
|
|
10
|
+
plus the pinned WebRTC BSD license and patent grant, are retained under
|
|
11
|
+
notices/reactor-webrtc/. Component-specific third-party redistribution texts
|
|
12
|
+
for every component named by those SBOMs are retained under
|
|
13
|
+
notices/reactor-webrtc/third-party/; its README names the exact pinned source
|
|
14
|
+
revision and retained license/copyright or patent file for each component.
|
|
15
|
+
The Rust crates linked into the native bridge and their selected license texts
|
|
16
|
+
are recorded under notices/native-rust/.
|
|
17
|
+
Native builds pin and verify the upstream binary artifact checksum.
|
|
18
|
+
No reactor-ffi or reactor-core session implementation is included.
|
|
19
|
+
|
|
20
|
+
Protocol material, the canonical session implementation and their upstream
|
|
21
|
+
notices are distributed with the reactor-effect-client package this host
|
|
22
|
+
depends on.
|
|
23
|
+
|
|
24
|
+
Effect is MIT-licensed. The package declares Effect as a peer dependency; its
|
|
25
|
+
license is retained in notices/effect-LICENSE. Koffi is an optional dependency
|
|
26
|
+
used only by this native implementation; its installed distribution retains its
|
|
27
|
+
upstream license. Development tooling and node_modules are excluded from the
|
|
28
|
+
npm package.
|
package/README.md
CHANGED
|
@@ -1,5 +1,184 @@
|
|
|
1
1
|
# reactor-effect-native
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Native WebRTC host for [`reactor-effect-client`](https://www.npmjs.com/package/reactor-effect-client). `reactor-effect-native` is a small Rust `cdylib` over the pinned `reactor-webrtc` revision, loaded through the optional [Koffi](https://koffi.dev) FFI dependency. Reactor session ownership, HTTP, command correlation, cancellation, and model policy stay in `reactor-effect-client`; this package owns transport and decoded media only.
|
|
4
4
|
|
|
5
|
-
This
|
|
5
|
+
This is not an official Reactor SDK. Native WebRTC dependencies are attributed in [NOTICE](./NOTICE) and [`notices/`](./notices/).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install reactor-effect-client reactor-effect-native effect@4.0.0-rc.115 @effect/platform-node@4.0.0-rc.115
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`reactor-effect-client`, Effect `4.0.0-rc.115` and `@effect/platform-node` `4.0.0-rc.115` are exact peer dependencies. Koffi is an optional dependency; it and the shared library are loaded only when `Native.layer()` is built, so merely importing this module remains safe when the optional dependency is absent. `@effect/platform-node` supplies the Node services an application provides around its scoped operation, and the [isolated host](#isolated-host) runs its child processes on it; this package loads it only when `Native.Isolated.layer()` is built. A consumer using that prerelease should retain the root override `"@effect/platform-node-shared": "4.0.0-rc.115"`, because the platform's caret range otherwise permits a later prerelease with a different Effect peer.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Effect, Layer } from "effect";
|
|
19
|
+
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
20
|
+
import * as Reactor from "reactor-effect-client";
|
|
21
|
+
import * as Native from "reactor-effect-native";
|
|
22
|
+
|
|
23
|
+
const clientLayer = Reactor.layer().pipe(
|
|
24
|
+
Layer.provide(Layer.mergeAll(Reactor.FetchHttp.layer, NodeServices.layer, Native.layer())),
|
|
25
|
+
);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`Native.layer(nativeOptions)` supplies the `PeerFactory` for `Reactor.layer()`. Building it loads Koffi and the library and verifies the staged artifact once, so a missing or invalid library fails the layer with a `Native` error, and an invalid option with `InvalidInput`, before any `Client` exists to allocate a remote session. Constructing a factory makes no allocation. `nativeOptions.libraryPath` can select a staged native artifact; its caller owns artifact provenance. `nativeOptions.shutdownTimeout`, a `Duration.Input` of 10 seconds by default, bounds how long closing a connection waits for the native owner join (see [Media path](#media-path-abi-3)). The separate `Native.uploadFile` helper requires the host file services when used.
|
|
29
|
+
|
|
30
|
+
`Native.media(session)` obtains a decoded-media generation after the session connects. `media.video(name)` emits owned BGRA frames and `media.audio(name)` emits owned interleaved signed 16-bit PCM. Frame IDs and microsecond timestamps use `bigint`. Retaining a frame retains JavaScript-owned bytes, and native media has no browser track handles. Media values stay bound to their negotiated generation: a reconnect creates a new generation and existing readers end or fail with their source.
|
|
31
|
+
|
|
32
|
+
Native transport provides:
|
|
33
|
+
|
|
34
|
+
- STUN/TURN ICE configuration and offerer signaling;
|
|
35
|
+
- reliable ordered binary `control` and `data` channels;
|
|
36
|
+
- transceiver direction and sender bitrate controls;
|
|
37
|
+
- WebRTC statistics;
|
|
38
|
+
- owned decoded BGRA video with Reactor frame metadata;
|
|
39
|
+
- owned interleaved signed 16-bit PCM audio;
|
|
40
|
+
- typed failure classes;
|
|
41
|
+
- immediate close fencing plus joined callback quiescence during shutdown.
|
|
42
|
+
|
|
43
|
+
The current public native peer accepts at most one incoming video track and one incoming audio track. Pinned `reactor-webrtc` delivers `RemoteTrack` callbacks without the transceiver MID/native identity needed to join multiple same-kind callbacks to SDP mappings without relying on arrival order. `NativePeer.prepare` therefore rejects an ambiguous declaration with `UnsupportedCapability` before native negotiation. Multiple outgoing tracks remain distinct by their declared transceivers.
|
|
44
|
+
|
|
45
|
+
## Isolated host
|
|
46
|
+
|
|
47
|
+
`Native.Isolated.layer(options)` supplies the same `PeerFactory` with each connection generation's native peer in a child process of its own, driven over Effect RPC. Use it where a native failure must end one connection rather than the application: a crash inside libwebrtc, or an owner join that never completes, then takes down only that child, and the session fails or closes as it would for a lost transport. The in-process `Native.layer` stays the default and is unchanged.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
const clientLayer = Reactor.layer().pipe(
|
|
51
|
+
Layer.provide(
|
|
52
|
+
Layer.mergeAll(Reactor.FetchHttp.layer, NodeServices.layer, Native.Isolated.layer()),
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
It takes the same `libraryPath` and `shutdownTimeout` options, and `Native.media(session)` reads its media as it does the in-process host's.
|
|
58
|
+
|
|
59
|
+
- **Preflight.** Building the layer forks a probe child that loads and verifies the library and opens a native peer, then shuts it down, so a missing or invalid library fails the layer with a `Native` error before any `Client` exists.
|
|
60
|
+
- **One child per peer.** `PeerFactory.make()` forks the peer's child at once, so it starts while the session allocates. The child is never respawned and nothing is replayed into it: when it dies, calls already handed to it fail with outcome `unknown`, later calls are refused as `not-submitted`, and the connection fails with `Native` ("native WebRTC child process exited"). A reconnect makes a new peer, and with it a new child. A child whose parent dies exits too.
|
|
61
|
+
- **Credentials stay in the parent.** Allocation, the session token, command correlation and termination never leave the parent process. The child sees ICE configuration, SDP, channel bytes and media, and starts with an empty environment and none of the parent's runtime flags.
|
|
62
|
+
- **Close.** `shutdown` asks the child to close and join its native peer and exit, within `shutdownTimeout` (10 seconds by default). On expiry the child is killed and the close reports `Shutdown` ("native child shutdown exceeded its deadline; child process killed"), which `Session.close` records in `localErrors` before it terminates the remote session. Nothing is retained, so unlike the in-process host a later peer is unaffected. `close()` retires the peer in the parent at once: no later event or frame from its child reaches the session.
|
|
63
|
+
- **Media.** Each track of a generation has one RPC stream from its child, opened by its first reader, and every reader of the track shares it with the same per-reader bounds as in-process. Each chunk carries one frame and the child sends the next only once the parent has acknowledged it, so each stream's credit is one frame. While the parent's channel is busy the child holds up to 8 video frames and 256 audio blocks, the native queues' depths, and evicts the oldest, counting it in `droppedVideo` or `droppedAudio` instead of `deliveredVideo` or `deliveredAudio`. Every frame keeps the admission `sequence` the native queue gave it, so an evicted frame is a gap that `recorder` reports, as it reports a frame the native queue dropped. `readerOverflows` counts the parent's readers and the child's.
|
|
64
|
+
|
|
65
|
+
It has costs the in-process host does not:
|
|
66
|
+
|
|
67
|
+
- starting a child takes about half a second (0.43–0.56 s from `make` to an open native peer on a 4-core Linux x64 runner), most of it loading Effect's RPC modules and verifying the staged library; it overlaps allocation, and the layer's probe pays it once more at build;
|
|
68
|
+
- every frame is copied across the IPC channel and then once more, because Node's advanced serialization delivers a message's typed arrays as views into one shared message buffer, and each frame's data must be the whole of an exact allocation of its own (a 1344x768 BGRA frame is about 4 MB);
|
|
69
|
+
- the parent must be Node: under Bun, building the layer fails with `UnsupportedCapability`, not submitted.
|
|
70
|
+
|
|
71
|
+
## Package layout
|
|
72
|
+
|
|
73
|
+
| Path | Contents |
|
|
74
|
+
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
|
|
75
|
+
| `dist/` | The compiled TypeScript entry point and its declarations, and the isolated host's child entry |
|
|
76
|
+
| `lib/<platform>-<arch>/` | The staged shared library and its `native-identity.json` sidecar for each shipped host |
|
|
77
|
+
| `rust/` | The `reactor-effect-native` crate: Cargo manifest and lock, lint configuration, build script, header, source |
|
|
78
|
+
| `rust/examples/far_peer/` | Test-only libwebrtc sender for the media load tests; not packaged or staged |
|
|
79
|
+
| `rust-toolchain.toml` | The Rust toolchain the native scripts use, the one CI pins |
|
|
80
|
+
| `scripts/build.sh` | Builds the crate for the current host and stages it |
|
|
81
|
+
| `scripts/stage.sh`, `stage.mjs` | Stage an already built library; `stage.mjs` is the single staging owner |
|
|
82
|
+
| `scripts/install-linux-toolchain.sh` | Explicit root-only LLVM 21 installer for opted-in Debian/Ubuntu build environments |
|
|
83
|
+
|
|
84
|
+
The crate's `src/` follows the peer's threads. `ffi.rs` exports the C ABI and `abi.rs` mirrors the header's constants, which a test checks against the header. `peer/` holds the peer handle, its owner thread (`owner.rs`), which owns every libwebrtc object, and the libwebrtc callbacks (`callbacks.rs`, `media.rs`) that copy into the state the threads share (`shared.rs`). `protocol/` is the JSON of requests, responses and event headers, and `sync/` holds the bounded queues, the callback gate and the notifier. Unit tests sit beside the code they test; a module with a larger suite keeps it in a `tests.rs` of its own.
|
|
85
|
+
|
|
86
|
+
## Media path (ABI 4)
|
|
87
|
+
|
|
88
|
+
The bridge owns one libwebrtc peer on a Rust thread. Every peer in a process shares one libwebrtc factory, created on the first `prepare` and never destroyed, because reactor-webrtc requires one factory per process.
|
|
89
|
+
|
|
90
|
+
libwebrtc callbacks copy each decoded frame once into a bounded, typed Rust queue and set a readiness bit. They never invoke or wait for JavaScript. The queues hold 8 video frames (333 ms at 24 fps), 256 PCM blocks (2.56 s of 10 ms blocks) and 1,024 transport events. One notifier thread per peer passes the readiness bits to a Koffi callback; it is the only native thread that ever waits on JavaScript. The callback wakes one pump fiber per queue. Each pump reads with synchronous, nonblocking takes that copy a frame straight into memory its consumer then owns, and yields between items so observers run at their own pace. No media call uses the libuv thread pool, and none is in flight when a reader is interrupted.
|
|
91
|
+
|
|
92
|
+
Each decoded frame and PCM block is numbered on its track, from 0, as it enters its queue and before the queue can evict anything, and the number reaches JavaScript as the frame's `sequence`. A full media queue evicts its oldest item and counts it, so `media.snapshot` reports what was dropped, delivered and still queued, and each eviction is a gap in the sequences a reader receives, at its position: `recorder(stream)` from `reactor-effect-client` turns a track's frames into the frames plus a `Lost { after, count }` for each gap. The stall load test checks that those counts add up to the bridge's own drop count. A full event queue is not pruned: it retires the connection with an `Overflow` failure.
|
|
93
|
+
|
|
94
|
+
The C header, `rust/include/reactor_effect_native.h`, is the ABI contract. Readiness is a callback passed to `reactor_effect_peer_create`; `reactor_effect_peer_take_event`, `_take_video` and `_take_audio` report `BUFFER_TOO_SMALL` with the required size and keep the item queued until a take succeeds.
|
|
95
|
+
|
|
96
|
+
`close()` fences new callback and host-call admission immediately. The host registers every `call`, `send` and `shutdown` before dispatching it to Koffi, and keeps that ownership until the foreign call actually returns, even when its Effect waiter is interrupted. Running the peer's `shutdown` effect drains those active and queued foreign calls, joins the Rust owner, the callback guards and the notifier thread, then destroys the handle and unregisters the Koffi callback once. The join is an asynchronous Koffi call because the notifier may be waiting for the JavaScript thread to run its callback. The C ABI requires other hosts to perform the same foreign-call drain before destruction, and to join from a thread other than the one that runs the callback. Joining the Rust owner alone cannot observe calls queued in a host executor.
|
|
97
|
+
|
|
98
|
+
The host waits for that drain and join for at most `shutdownTimeout`; a healthy join took 13–62 ms in the media load tests on Node and Bun. On expiry the host stops waiting and the shutdown fails with `Shutdown` ("native owner join exceeded its deadline; handle retained"), which `Session.close` records in `localErrors` before it terminates the remote session. The join itself goes on: it keeps the handle and the registered Koffi callback, and destroys and unregisters them only if it completes. Until then the host holds the bridge, and because every peer of the library shares its one libwebrtc factory, preflight and peer creation fail with a `Native` error before any allocation rather than start another owner on a factory that may be wedged.
|
|
99
|
+
|
|
100
|
+
## Failure classes
|
|
101
|
+
|
|
102
|
+
Every native failure is one of a closed set of classes, which the host maps to the `ReactorError` reason's `_tag`:
|
|
103
|
+
|
|
104
|
+
| Status | `reason._tag` | Raised when |
|
|
105
|
+
| ------ | --------------- | --------------------------------------------------------------------------- |
|
|
106
|
+
| `-1` | `InvalidInput` | the bridge rejects a request or argument |
|
|
107
|
+
| `-2` | `Native` | libwebrtc or the bridge fails in a way it cannot classify |
|
|
108
|
+
| `-3` | `Overflow` | a queue, buffer or message bound is exceeded |
|
|
109
|
+
| `-4` | `Protocol` | the remote peer breaks the negotiated contract, such as an undeclared track |
|
|
110
|
+
| `-5` | `SdpRejected` | libwebrtc refuses to create or apply an offer or answer |
|
|
111
|
+
| `-6` | `ChannelClosed` | a send targets a data channel that is not open |
|
|
112
|
+
| `3` | `Closed` | the peer is fenced or shut down |
|
|
113
|
+
|
|
114
|
+
Pinned reactor-webrtc reports every libwebrtc error as a string, so the bridge classifies a failure by the operation that produced it. That text can contain SDP, so it is never in the message and never in the error's cause chain, which exporters such as `OtlpTracer` render. It stays `Redacted` for explicit inspection: in the `Native` reason's `backendMessage` beside its `status`, or, for a classified failure, in `context.detail` as `{ status, backendMessage }`.
|
|
115
|
+
|
|
116
|
+
When the connection state reaches `failed`, the host reads its statistics once, in the pump that delivers events, so later events wait behind it and the connection scope owns the read. A read that fails, or outlasts its 2 s deadline on the fiber's `Clock`, reports `Disconnected`. A candidate pair that succeeded or was nominated means ICE worked and the DTLS or SCTP transport above it failed: `TransportFailed`. Otherwise the failure is `IceFailed`. Both reasons carry the number of candidate pairs the statistics listed, and `IceFailed` the local candidate types tried. The session reports a data channel that closes as `ChannelClosed`, naming the channel. A `disconnected` state still fails the connection as `Disconnected`, and decode failures are not reported: reactor-webrtc surfaces neither ICE connection state nor decoder errors.
|
|
117
|
+
|
|
118
|
+
## Build and stage
|
|
119
|
+
|
|
120
|
+
Native code requires Rust 1.90, Clang 21 on Linux (the platform compiler on macOS), curl, tar with zstd support (or `zstd`), and a SHA-256 tool. From the workspace root:
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
bun run native:build # sh packages/native/scripts/build.sh
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The pinned `reactor-webrtc-sys` build downloads its matching libwebrtc prebuilt and verifies the published SHA-256 before linking. macOS prebuilts target macOS 13.0 or later. The script uses the checked-in Cargo configuration and stages the result under `lib/<platform>-<arch>/`. `scripts/stage.mjs` verifies the library's embedded ABI, target, release profile and source SHA-256 against the current native build inputs, then writes the binary and `native-identity.json` with its SHA-256 and build identity. The source identity covers the Cargo manifest/lock, build script, Cargo configuration, C header and Rust source; build identity also records compiler versions and relevant build flags. A stale binary is rejected before it can be staged. `node scripts/stage.mjs --source-hash` prints the identity of the current sources; pull-request CI keys its staged-artifact cache on it (with the build recipe, toolchain and runner image) and skips the Rust build when none of them changed, while a run on `main` always builds the library it qualifies.
|
|
127
|
+
|
|
128
|
+
Native tests and normal installed-package preflight use this staged artifact. Preflight verifies the binary hash and compares its loaded build identity with the sidecar. There is no fallback to `rust/target/debug` or `release`.
|
|
129
|
+
|
|
130
|
+
Linux native builds require LLVM/Clang 21. The pinned libwebrtc prebuilt ships the matching libc++ headers, and older distro Clang releases are not a supported compiler for this native bridge. `scripts/build.sh` and `scripts/test.sh` never install system packages: on Linux they use an explicit `CC`/`CXX` when supplied, otherwise they require `clang-21` and `clang++-21` on `PATH` and fail with a toolchain-specific error if unavailable. macOS compiler selection is unchanged.
|
|
131
|
+
|
|
132
|
+
For opted-in Debian 12 (bookworm) or Ubuntu 24.04 (noble) build environments, the package includes an explicit root-only installer:
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
sudo ./packages/native/scripts/install-linux-toolchain.sh
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The installer uses the official apt.llvm.org LLVM 21 repository, verifies the repository signing key fingerprint `6084F3CF814B57C1CF12EFD515CF4D18AF4F7421` before adding the repository, and installs only the `clang-21` toolchain package after its bootstrap HTTPS/GPG requirements. The pinned libwebrtc artifact supplies the libc++/libc++abi headers and static archives used by the glue build, so system libc++ packages are intentionally not installed. The installer does not invoke `sudo` itself and is only called explicitly by Docker/CI recipes; ordinary local build/test scripts never modify the host package set.
|
|
139
|
+
|
|
140
|
+
## Qualification
|
|
141
|
+
|
|
142
|
+
Local qualification is credential-free:
|
|
143
|
+
|
|
144
|
+
```sh
|
|
145
|
+
bun run native:test # sh packages/native/scripts/test.sh
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The script checks Rust formatting, then runs the Rust tests, clippy with the crate's lint set, and rustdoc, all with warnings denied, and builds the test far peer. It then runs the JavaScript suite in `packages/native/test` against the staged artifact twice, on Node and on Bun; it never restages a second release library. The Rust tests check the ABI's constants against the C header and each JSON shape against what the host parses, and drive every entry point through the C ABI, including null and undersized arguments, a caught panic and a closed peer. Loopback tests negotiate with a second local peer on the shared factory through the bridge's owner thread, and exercise real libwebrtc ICE/DTLS/SCTP, ordered binary messages on both channels, video encode/decode on two lanes with their per-frame metadata, PCM audio, stream stats, and the close fence. Other tests cover queue accounting under seeded random operations, direction and bitrate controls, readiness coalescing, callback quiescence, and a shutdown that must wait for a notifier still inside the host callback. None of it contacts Reactor or generates paid media.
|
|
149
|
+
|
|
150
|
+
The media load tests receive from `rust/examples/far_peer/`, a libwebrtc sender on the same pinned reactor-webrtc that sends 1344x768 BGRA at 24 fps with per-frame metadata, plus 48 kHz PCM, and echoes both channels. It holds its congestion controller at 8 Mbps: on loopback the estimate follows only how promptly the host schedules both processes, and on a busy runner it backs off until the encoder drops most frames.
|
|
151
|
+
|
|
152
|
+
The tests assert what the bridge owns: how many frames reach it, how many it drops, and how many it holds, queued natively or taken but not yet seen by the subscriber. End-to-end latency also carries the far peer's encoder and pacer and the receiver's jitter buffer, and received audio arrives at the pace of libwebrtc's playout clock; both follow how the host schedules the two processes, so each test prints them, with both processes' CPU use and the codec and pacing counters, without asserting them. Through Koffi and the staged library, on each runtime:
|
|
153
|
+
|
|
154
|
+
- one session must receive at least 20 frames per second for 10 s, drop at most 1% of them and hold at most two at p95; audio must keep reaching it at 20 blocks per second or more, none dropped, and control-channel round trips stay under 100 ms at p95;
|
|
155
|
+
- two concurrent sessions must each meet the same bounds for 10 s;
|
|
156
|
+
- across a 250 ms event-loop stall the native queue may evict only what overflowed its 8 frames while JavaScript was blocked, within one, so at 24 fps at most one frame; across a 2 s stall it must evict what overflows, within two, lose no audio, and drain;
|
|
157
|
+
- a session is renewed three times while its predecessor streams; each replacement must keep receiving at least 15 frames per second over the 4 s from the start of its predecessor's shutdown, which must take under 2 s, dropping at most one frame, with no 500 ms gap and no audio lost;
|
|
158
|
+
- every session above closes cleanly: its native owner joins without a `Shutdown` failure, in under a fifth of the default `shutdownTimeout`, so a deadline that would fire on a slow but healthy join fails the suite;
|
|
159
|
+
- a canonical `Session` over the far peer, through a coordinator stand-in that relays its signaling, receives video and closes with `localClosed`, no `localErrors` and its remote termination confirmed, under the same bound;
|
|
160
|
+
- an answer whose candidates point at an unreachable documentation address fails ICE, and the events pump's classification reports `IceFailed` with its candidate-pair detail. libwebrtc reports the failure once it has pruned the last timed-out pair, so that detail lists no pairs.
|
|
161
|
+
|
|
162
|
+
The C fixture in `test/session-fixture.c` implements the same header without libwebrtc, including a notifier thread. It drives the canonical session, blocks foreign calls while interrupting their Effect waiters, and checks that the Koffi callback is unregistered only after shutdown joined the notifier. Holding a shutdown join open, it checks that `Session.close` returns at the deadline with `Shutdown` in `localErrors` and the remote session terminated, that the bridge is neither destroyed nor unregistered until the join completes, and that the process admits no new peer until then. Holding a statistics call open, it checks that a failed connection's classification ends on a `TestClock` with `Disconnected`. The lifetime fixture drains queued calls while one active call remains held, verifies that shutdown/destruction have not run, then releases the final call and verifies one destruction. Its tombstone reports unsafe ordering without intentionally dereferencing freed memory. These checks establish ownership and ordering; they do not claim an observed heap-corruption incident.
|
|
163
|
+
|
|
164
|
+
`test/frame-allocation.test.ts` scripts the video queue of a minimal ABI library. Each take must hand over the one exact-size buffer native code copied the frame into, with no JavaScript copy of its pixels except the trim of a frame smaller than its predecessor, and a public media generation's Stream consumer must receive that same buffer. The frame-ownership check it shares with the client and browser suites, `reactor-effect-test-kit/frames`, also runs on the frames of the canonical far-peer session.
|
|
165
|
+
|
|
166
|
+
`test/isolated.test.ts` runs the isolated host with real child processes, on Node; on Bun it checks only that the layer refuses to build. Over the C fixture it checks that successive generations each reach their own child through one parent, that a child killed mid-call fails that call as `unknown`, refuses later calls before dispatch and is never respawned, that a child exits when its parent dies with a native call in flight, that a shutdown held past its deadline kills the child and still terminates the remote session, that events and frames a retired, shut down or killed child sends late never reach it or a later peer, and that a cancelled wait never takes a later call's reply. Over the far peer it checks that a reader that stops reading fails alone with `Overflow` while each stream holds one unacknowledged one-frame chunk, and that a canonical session's frames arrive as exact allocations after the IPC hop and the session closes cleanly. The child entry is the built one, so the suite needs `bun run build` first.
|
|
167
|
+
|
|
168
|
+
`scripts/stage.sh` stages an already-built shared library under the package runtime layout, `lib/<platform>-<arch>/`. For example:
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
./packages/native/scripts/stage.sh packages/native/rust/target/release/libreactor_effect_native.dylib darwin-arm64
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Linux x64 has a reproducible container build that starts from the package source, installs Rust 1.90 plus the package-owned LLVM 21 recipe and auxiliary archive tools, runs the native Rust tests, clippy and rustdoc, and exports only the resulting shared library. It requires an explicit Docker context so the script never changes the caller's active context:
|
|
175
|
+
|
|
176
|
+
```sh
|
|
177
|
+
DOCKER_CONTEXT=default bun run native:linux-x64
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
That stages `lib/linux-x64/libreactor_effect_native.so`. The final runtime image only needs the packaged shared object and its normal glibc system dependencies; Cargo, clang and the libwebrtc archive are build-time inputs.
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
Apache-2.0. See [NOTICE](./NOTICE) and [`notices/`](./notices/).
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { ReactorError } from "reactor-effect-client";
|
|
2
|
+
import type { ErrorContext, FailureCode } from "reactor-effect-client";
|
|
3
|
+
/** A status outside the ABI's classes is an unclassified native failure. */
|
|
4
|
+
export declare const failureCode: (status: number) => FailureCode | "Native";
|
|
5
|
+
/**
|
|
6
|
+
* A failure the native ABI returned with `status`, classified by its failure
|
|
7
|
+
* class. libwebrtc's own text can contain peer SDP or caller-supplied
|
|
8
|
+
* signaling material, so it is kept Redacted for explicit inspection: in the
|
|
9
|
+
* `Native` reason for an unclassified failure, and in `context.detail`, beside
|
|
10
|
+
* the status, for a classified one.
|
|
11
|
+
*/
|
|
12
|
+
export declare const nativeFailure: (status: number, backendText: string, message: (code: FailureCode | "Native") => string, context: ErrorContext, detail?: Readonly<Record<string, unknown>>) => ReactorError;
|
|
13
|
+
export declare const NativeCall: Readonly<{
|
|
14
|
+
readonly Prepare: 1;
|
|
15
|
+
readonly Answer: 2;
|
|
16
|
+
readonly Direction: 3;
|
|
17
|
+
readonly MaxBitrate: 4;
|
|
18
|
+
readonly Stats: 5;
|
|
19
|
+
readonly MediaSnapshot: 6;
|
|
20
|
+
}>;
|
|
21
|
+
export type NativeCall = (typeof NativeCall)[keyof typeof NativeCall];
|
|
22
|
+
/** Readiness bits the native notifier passes to the host callback. */
|
|
23
|
+
export declare const Ready: Readonly<{
|
|
24
|
+
readonly Events: 1;
|
|
25
|
+
readonly Video: 2;
|
|
26
|
+
readonly Audio: 4;
|
|
27
|
+
}>;
|
|
28
|
+
export declare const resolveNativeBridge: (path?: string) => Promise<string>;
|
|
29
|
+
export declare const checkNativeBridge: (path?: string) => Promise<void>;
|
|
30
|
+
/** Validate the staged artifact used by both source and installed-package tests. */
|
|
31
|
+
export declare const verifyStagedNativeBridge: (path: string) => Promise<Readonly<Record<string, unknown>>>;
|
|
32
|
+
export interface NativePacket {
|
|
33
|
+
readonly header: Readonly<Record<string, unknown>>;
|
|
34
|
+
readonly payload: Uint8Array<ArrayBuffer>;
|
|
35
|
+
}
|
|
36
|
+
export interface NativeVideo {
|
|
37
|
+
readonly track: number;
|
|
38
|
+
readonly width: number;
|
|
39
|
+
readonly height: number;
|
|
40
|
+
readonly frameId: bigint;
|
|
41
|
+
readonly timestampMicros: bigint;
|
|
42
|
+
readonly sequence: bigint;
|
|
43
|
+
readonly data: Uint8Array<ArrayBuffer>;
|
|
44
|
+
readonly metadata: Uint8Array<ArrayBuffer>;
|
|
45
|
+
}
|
|
46
|
+
export interface NativeAudio {
|
|
47
|
+
readonly track: number;
|
|
48
|
+
readonly sampleRate: number;
|
|
49
|
+
readonly channels: number;
|
|
50
|
+
readonly sequence: bigint;
|
|
51
|
+
readonly samples: Int16Array<ArrayBuffer>;
|
|
52
|
+
}
|
|
53
|
+
/** A take: the item, `undefined` when the queue is empty, `null` once it is closed. */
|
|
54
|
+
export type Take<A> = A | undefined | null;
|
|
55
|
+
/**
|
|
56
|
+
* One native peer. Media and transport events never cross into JavaScript on
|
|
57
|
+
* their own: a native notifier thread invokes `onReady` on the JavaScript
|
|
58
|
+
* thread, and the host drains each named queue with synchronous takes, one
|
|
59
|
+
* copy per item into memory the consumer then owns.
|
|
60
|
+
*/
|
|
61
|
+
export declare class NativeBridge {
|
|
62
|
+
/**
|
|
63
|
+
* Refuse a new peer on a library that holds a retained join. Every peer of a
|
|
64
|
+
* loaded library shares its one libwebrtc factory, so an owner that did not
|
|
65
|
+
* join may have wedged the threads a new owner would need; failing here keeps
|
|
66
|
+
* a caller from stacking wedged owners and allocating remote sessions for them.
|
|
67
|
+
*/
|
|
68
|
+
static requireUsable(path: string): void;
|
|
69
|
+
private readonly api;
|
|
70
|
+
private readonly notify;
|
|
71
|
+
private handle;
|
|
72
|
+
private closed;
|
|
73
|
+
private readonly active;
|
|
74
|
+
private requestBytes;
|
|
75
|
+
private shutdownTask;
|
|
76
|
+
private readonly videoHeader;
|
|
77
|
+
private readonly audioHeader;
|
|
78
|
+
private readonly eventLength;
|
|
79
|
+
private event;
|
|
80
|
+
private metadata;
|
|
81
|
+
private nextVideo;
|
|
82
|
+
private nextAudio;
|
|
83
|
+
constructor(path: string, onReady: (ready: number) => void);
|
|
84
|
+
private require;
|
|
85
|
+
private withHandle;
|
|
86
|
+
call(operation: NativeCall, request?: Uint8Array): Promise<unknown>;
|
|
87
|
+
send(channel: "control" | "data", bytes: Uint8Array): Promise<void>;
|
|
88
|
+
/** Synchronous and nonblocking; call from the readiness callback until empty. */
|
|
89
|
+
takeEvent(): Take<NativePacket>;
|
|
90
|
+
/** Synchronous and nonblocking; the returned bytes belong to the caller. */
|
|
91
|
+
takeVideo(): Take<NativeVideo>;
|
|
92
|
+
/** Synchronous and nonblocking; the returned samples belong to the caller. */
|
|
93
|
+
takeAudio(): Take<NativeAudio>;
|
|
94
|
+
close(): void;
|
|
95
|
+
shutdown(): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* The host stopped waiting for this bridge's join. The join still owns the
|
|
98
|
+
* handle: destruction and unregistration run only once it completes.
|
|
99
|
+
*/
|
|
100
|
+
retain(): void;
|
|
101
|
+
private finishShutdown;
|
|
102
|
+
}
|
|
103
|
+
export declare const encodeNativeJson: (value: unknown) => Uint8Array<ArrayBuffer>;
|
|
104
|
+
export declare const encodeNativeText: (value: string) => Uint8Array<ArrayBuffer>;
|
|
105
|
+
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/_internal/bridge.ts"],"names":[],"mappings":"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"}
|