use the prebuild rust-sdk package

This commit is contained in:
Timo K.
2026-09-17 09:46:56 +02:00
parent e4049e3d69
commit edf2f39eec
29 changed files with 292 additions and 12959 deletions
-3
View File
@@ -1,3 +0,0 @@
# uniffi-generated MatrixRTC SDK bindings, vendored by scripts/sync-matrix-rtc-sdk.sh
src/matrix-rtc-sdk/generated/** linguist-generated=true
src/matrix-rtc-sdk/generated/**/*.wasm binary
@@ -42,6 +42,14 @@ jobs:
with:
cache: "pnpm"
node-version-file: ".node-version"
- name: Authenticate to GitHub Packages
# The MatrixRTC SDK (@billcarsonfr/matrix-rtc, see .npmrc) is on the GitHub
# Packages npm registry, which needs a token with read:packages even for
# public packages. A workflow's GITHUB_TOKEN only reaches packages of its own
# repository, so this is a personal access token kept as a repository secret.
env:
NPM_REGISTRY_TOKEN: ${{ secrets.MATRIX_RTC_NPM_TOKEN }}
run: echo "//npm.pkg.github.com/:_authToken=${NPM_REGISTRY_TOKEN}" >> ~/.npmrc
- name: Install dependencies
# ignore-pnpmfile should never be commited. Make CI crash if it happened (`pnpmfileChecksum` is present)
run: "pnpm install --frozen-lockfile --ignore-pnpmfile"
+8
View File
@@ -102,6 +102,14 @@ jobs:
with:
cache: "pnpm"
node-version-file: ".node-version"
- name: Authenticate to GitHub Packages
# The MatrixRTC SDK (@billcarsonfr/matrix-rtc, see .npmrc) is on the GitHub
# Packages npm registry, which needs a token with read:packages even for
# public packages. A workflow's GITHUB_TOKEN only reaches packages of its own
# repository, so this is a personal access token kept as a repository secret.
env:
NPM_REGISTRY_TOKEN: ${{ secrets.MATRIX_RTC_NPM_TOKEN }}
run: echo "//npm.pkg.github.com/:_authToken=${NPM_REGISTRY_TOKEN}" >> ~/.npmrc
- name: Install dependencies
run: "pnpm install --frozen-lockfile --ignore-pnpmfile"
- name: Build Storybook
+8
View File
@@ -17,6 +17,14 @@ jobs:
with:
cache: "pnpm"
node-version-file: ".node-version"
- name: Authenticate to GitHub Packages
# The MatrixRTC SDK (@billcarsonfr/matrix-rtc, see .npmrc) is on the GitHub
# Packages npm registry, which needs a token with read:packages even for
# public packages. A workflow's GITHUB_TOKEN only reaches packages of its own
# repository, so this is a personal access token kept as a repository secret.
env:
NPM_REGISTRY_TOKEN: ${{ secrets.MATRIX_RTC_NPM_TOKEN }}
run: echo "//npm.pkg.github.com/:_authToken=${NPM_REGISTRY_TOKEN}" >> ~/.npmrc
- name: Install dependencies
# ignore-pnpmfile should never be commited. Make CI crash if it happened (`pnpmfileChecksum` is present)
run: "pnpm install --frozen-lockfile --ignore-pnpmfile"
+16
View File
@@ -19,6 +19,14 @@ jobs:
with:
cache: "pnpm"
node-version-file: ".node-version"
- name: Authenticate to GitHub Packages
# The MatrixRTC SDK (@billcarsonfr/matrix-rtc, see .npmrc) is on the GitHub
# Packages npm registry, which needs a token with read:packages even for
# public packages. A workflow's GITHUB_TOKEN only reaches packages of its own
# repository, so this is a personal access token kept as a repository secret.
env:
NPM_REGISTRY_TOKEN: ${{ secrets.MATRIX_RTC_NPM_TOKEN }}
run: echo "//npm.pkg.github.com/:_authToken=${NPM_REGISTRY_TOKEN}" >> ~/.npmrc
- name: Install dependencies
# ignore-pnpmfile should never be commited. Make CI crash if it happened (`pnpmfileChecksum` is present)
run: "pnpm install --frozen-lockfile --ignore-pnpmfile"
@@ -65,6 +73,14 @@ jobs:
with:
cache: "pnpm"
node-version-file: ".node-version"
- name: Authenticate to GitHub Packages
# The MatrixRTC SDK (@billcarsonfr/matrix-rtc, see .npmrc) is on the GitHub
# Packages npm registry, which needs a token with read:packages even for
# public packages. A workflow's GITHUB_TOKEN only reaches packages of its own
# repository, so this is a personal access token kept as a repository secret.
env:
NPM_REGISTRY_TOKEN: ${{ secrets.MATRIX_RTC_NPM_TOKEN }}
run: echo "//npm.pkg.github.com/:_authToken=${NPM_REGISTRY_TOKEN}" >> ~/.npmrc
- name: Install dependencies
# ignore-pnpmfile should never be commited. Make CI crash if it happened (`pnpmfileChecksum` is present)
run: pnpm install --frozen-lockfile --ignore-pnpmfile
@@ -25,6 +25,14 @@ jobs:
cache: "pnpm"
node-version-file: ".node-version"
- name: Authenticate to GitHub Packages
# The MatrixRTC SDK (@billcarsonfr/matrix-rtc, see .npmrc) is on the GitHub
# Packages npm registry, which needs a token with read:packages even for
# public packages. A workflow's GITHUB_TOKEN only reaches packages of its own
# repository, so this is a personal access token kept as a repository secret.
env:
NPM_REGISTRY_TOKEN: ${{ secrets.MATRIX_RTC_NPM_TOKEN }}
run: echo "//npm.pkg.github.com/:_authToken=${NPM_REGISTRY_TOKEN}" >> ~/.npmrc
- name: Install Deps
# ignore-pnpmfile should never be commited. Make CI crash if it happened (`pnpmfileChecksum` is present)
run: "pnpm install --frozen-lockfile --ignore-pnpmfile"
+9
View File
@@ -0,0 +1,9 @@
# The MatrixRTC SDK (`@element-hq/matrix-rtc`, aliased in package.json to the
# name the fork's CI publishes it under) comes from the GitHub Packages npm
# registry, which needs a token with `read:packages` even for public
# packages. This is a stop-gap while the SDK is in flux; once it is published
# to npmjs.com this file and the alias go away (docs/matrix_rtc_sdk.md).
# Only the scope->registry mapping lives here; the token goes in your
# user-level npm config (~/.npmrc), never in this file. CI writes it there
# from a repository secret (see .github/workflows).
@billcarsonfr:registry=https://npm.pkg.github.com
+1 -6
View File
@@ -2,10 +2,5 @@
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"printWidth": 80,
"sortPackageJson": false,
"ignorePatterns": [
"pnpm-lock.yaml",
"node_modules",
"dist",
"src/matrix-rtc-sdk/generated"
]
"ignorePatterns": ["pnpm-lock.yaml", "node_modules", "dist"]
}
-3
View File
@@ -28,9 +28,6 @@
"env": {
"builtin": true
},
// uniffi-generated bindings and wasm-bindgen glue, vendored by
// scripts/sync-matrix-rtc-sdk.sh; not ours to lint
"ignorePatterns": ["src/matrix-rtc-sdk/generated/**"],
"rules": {
"element-call/copyright-header": [
"error",
+17 -1
View File
@@ -206,6 +206,19 @@ corepack enable
pnpm install
```
`pnpm install` currently needs a GitHub token: the MatrixRTC SDK
(`@element-hq/matrix-rtc`) is, as a stop-gap until it is published to npmjs.com,
an npm package on the GitHub Packages registry, which rejects anonymous
requests even for public packages. Create a classic personal access token with
the `read:packages` scope and add it to your user-level npm config once:
```sh
echo "//npm.pkg.github.com/:_authToken=<your token>" >> ~/.npmrc
```
See [The MatrixRTC SDK package](./docs/matrix_rtc_sdk.md) for the details, CI
setup and developing against a local crate.
To use it, create a local config by, e.g.,
`cp ./config/config.devenv.json ./public/config.json` and adapt it if necessary.
The `config.devenv.json` config should work with the backend development
@@ -220,6 +233,7 @@ pnpm dev
See also:
- [Developing with linked packages](./docs/linking.md)
- [The MatrixRTC SDK package](./docs/matrix_rtc_sdk.md)
#### Element Call as a component (experimental)
@@ -298,7 +312,9 @@ The package is not published yet. A host installs it as a git dependency on the
whose `prepare` script runs the build on install. That build needs pnpm (via
Corepack) on the host's machine, runs a full `pnpm install` of this repository
and is memory-hungry, since it inherits the `--max-old-space-size` setting of
(so the host needs the GitHub Packages token described in
[The MatrixRTC SDK package](./docs/matrix_rtc_sdk.md) as well) and is
memory-hungry, since it inherits the `--max-old-space-size` setting of
the app build; the host's pnpm also has to allow it to run at all
(`allowBuilds` in its `pnpm-workspace.yaml`). Note that `component/` is a pnpm
project of its own for this reason, so pnpm commands run from inside that
+1
View File
@@ -9,6 +9,7 @@ This folder contains documentation for setup, usage, and development of Element
- [MatrixRTC modes](./matrix_rtc_modes.md)
- [Self-Hosting](./self_hosting.md)
- [Developing with linked packages](./linking.md)
- [The MatrixRTC SDK package](./matrix_rtc_sdk.md) (registry token, CI, local crate)
### Writing code for Element Call (agent-readable conventions)
+3
View File
@@ -29,6 +29,9 @@ Instead, create a file named `.links.cjs` in the Element Call project directory
module.exports = {
"matrix-js-sdk": "../your/path/matrix-js-sdk",
"matrix-widget-api": "../your/path/matrix-widget-api",
// The MatrixRTC SDK: run `npm run build` in that directory first
"@element-hq/matrix-rtc":
"../your/path/matrix-rust-rtc/MatrixSdkArchitectureDraft/web-test-app",
};
```
+135
View File
@@ -0,0 +1,135 @@
# The MatrixRTC SDK package
Element Call's MatrixRTC logic (session membership, keep-alive, transport
tokens, media key exchange) runs in the Rust `matrix-rtc` crate, compiled to
WebAssembly and shipped as the npm package `@element-hq/matrix-rtc`. The
package is built and published by the
[`npm-web-bindings.yml`](https://github.com/element-hq/matrix-rust-rtc/blob/main/.github/workflows/npm-web-bindings.yml)
workflow of the `matrix-rust-rtc` repository, from its
`MatrixSdkArchitectureDraft/web-test-app` directory. Element Call loads it in
`src/matrix-rtc-sdk/index.ts`, the only module that imports the package
directly.
## Where the package comes from
For now the package lives on the **GitHub Packages** npm registry
(`https://npm.pkg.github.com`), not on npmjs.com. **This is a stop-gap** for
the phase in which the crate's API still changes with every push: publishing
from the crate's CI into a registry that comes with the repository needs no
account or secret upstream, and nothing else consumes the package yet. Once
the SDK is ready for consumers beyond Element Call, it should be published to
**npmjs.com** as `@element-hq/matrix-rtc`, which makes everything in this
document about tokens and scopes go away: `pnpm install` works anonymously
again, forks and Renovate can install, component hosts need no setup, and the
alias in `package.json` and the scope line in `.npmrc` are deleted.
Until then, two properties of GitHub Packages shape the setup below:
- **Every request needs a token**, including reads of public packages. There
is no anonymous install.
- **A package is scoped to the owner of the repository whose CI published
it.** While the crate is developed on the `BillCarsonFr` fork, the package is
published as `@billcarsonfr/matrix-rtc`; once it is published from
`element-hq/matrix-rust-rtc` it becomes `@element-hq/matrix-rtc`.
So that the code does not have to change when that happens, `package.json`
installs the fork's package under the final name:
```json
"@element-hq/matrix-rtc": "npm:@billcarsonfr/matrix-rtc@next"
```
and the repository's `.npmrc` maps only the scope to the registry:
```ini
@billcarsonfr:registry=https://npm.pkg.github.com
```
When the package moves to `element-hq`, change the alias to the plain package
and the scope line in `.npmrc` accordingly; nothing else in the repository
refers to the fork's name.
## Setting up your machine
1. Create a **personal access token (classic)** at
<https://github.com/settings/tokens> with only the `read:packages` scope.
Fine-grained tokens do not work with GitHub Packages.
2. Add it to your **user-level** npm config. Never put it in the repository's
`.npmrc`:
```sh
echo "//npm.pkg.github.com/:_authToken=<your token>" >> ~/.npmrc
```
3. Run `pnpm install` as usual.
Without the token, `pnpm install` fails while resolving
`@billcarsonfr/matrix-rtc` with a `401` or `404` from `npm.pkg.github.com`;
that error always means "no or wrong token", never "the package does not
exist".
## CI and other consumers
A workflow's built-in `GITHUB_TOKEN` can only read packages owned by the
repository's own owner, so element-hq's workflows cannot read a package owned
by the `BillCarsonFr` user. The workflows that run `pnpm install` therefore
write a personal access token from the repository secret
`MATRIX_RTC_NPM_TOKEN` (scope `read:packages`) to `~/.npmrc` before installing.
Keep in mind:
- **Pull requests from forks and from Renovate do not receive secrets**, so
their installs fail until the package is readable with the built-in token.
- **Hosts of the [component](../README.md#element-call-as-a-component-experimental)**
install it as a git dependency whose `prepare` script runs
`pnpm install --frozen-lockfile` in this repository, so they need the same
token in their `~/.npmrc` or CI.
Publishing from `element-hq/matrix-rust-rtc` (and granting the Element Call
repository access to the package) would let the built-in token work for
element-hq's own workflows, but that only patches the CI case. The intended end
state is the npmjs.com publication described above, which removes the token
requirement for everyone.
## Versions
The package version tracks the crate's `Cargo.toml`. Every push to the tracked
branches of `matrix-rust-rtc` publishes a `next` pre-release named
`<version>-next.<run number>.<short sha>`, and a `matrix-rtc-v<version>` tag
publishes `latest`. Element Call follows `next`; `pnpm install` picks up a new
pre-release only when the lockfile is updated, e.g. with
`pnpm update @element-hq/matrix-rtc`.
The publish job runs only after the crate's own `cargo test` job passed on a
_push_ (pull-request runs never publish). If a `next` build you expect is
missing, check that workflow's push runs for a failed crate job and re-run it;
its tests are known to be timing-sensitive.
## Developing against a local crate checkout
To run Element Call against a crate you are changing, build the package there
and link it, instead of waiting for CI:
```sh
cd ../matrix-rust-rtc/MatrixSdkArchitectureDraft/web-test-app
npm install && npm run build # produces dist/
```
then add it to `.links.cjs` in this repository and run `pnpm links:on`, as
described in [Developing with linked packages](./linking.md):
```cjs
"@element-hq/matrix-rtc": "../matrix-rust-rtc/MatrixSdkArchitectureDraft/web-test-app",
```
`pnpm links:off` restores the registry package before committing.
## How the wasm is loaded
`initMatrixRtcSdk()` in `src/matrix-rtc-sdk/index.ts` is the single entry
point: it awaits the package's `initAsync`, then installs the log sink that
routes the crate's logs into Element Call's logger. The wasm is imported as
`@element-hq/matrix-rtc/wasm?url`, so the app builds emit it as an asset next
to the other `.wasm` files and the component build inlines it. Unit tests read
the bytes from the installed package instead (`src/utils/test-matrix-rtc.ts`),
since vitest has no server to fetch from. A component host can point at a copy
it serves itself through `initializeElementCall(config, { matrixRtcWasm })`.
+26 -12
View File
@@ -59,8 +59,9 @@ files it touches are listed there.
`src/utils/logger.ts` (S6) so a later swap is one line.
- Writing a matrix-rust-sdk-backed driver (Element X). The interface is shaped
so one can be written; none is written here.
- Publishing the crate as an npm package. Until it exists, the generated
bindings are vendored (§5.1).
- Publishing the crate as an npm package: done upstream (`@element-hq/matrix-rtc`,
built and published by `npm-web-bindings.yml` in matrix-rust-rtc; consumed
here since 2026-09-16, §5.1).
- Turning on MSC4153 (cross-signed sender) enforcement. Parity first (§5.8).
---
@@ -501,13 +502,24 @@ RateLimited`, 403 → `Rejected`, 404/`M_UNRECOGNIZED` → `Unsupported`.
## 5. Decisions and assumptions
1. **Vendored bindings.** `src/matrix-rtc-sdk/generated/` holds `matrix_rtc.ts`,
`matrix_rtc-ffi.ts`, `wasm-bindgen/index.js`, `index_bg.wasm` and a
hand-written `wasm-bindgen/index.d.ts` (no `allowJs` in `tsconfig.json`),
synced by `scripts/sync-matrix-rtc-sdk.sh` (runs `ubrn build web` in the
draft **without** the `runtime-probe` feature, copies). Committed so CI
works. `@ubjs/core` becomes a dependency. The wasm is ~6.5 MB unoptimised.
**Assumption:** committing the binary is acceptable for the draft phase.
1. **The SDK is an npm package.** `@element-hq/matrix-rtc` (the draft's
`web-test-app/`, built by `npm-web-bindings.yml`: uniffi bindings, the
wasm-bindgen glue, a `wasm-opt`'d ~760 KB wasm, `.d.ts`, and `@ubjs/core`
as its own dependency) is a devDependency, aliased in `package.json` to
`@billcarsonfr/matrix-rtc@next` while it is published from the fork, so
the imports already say `@element-hq/matrix-rtc`. It lives on the GitHub
Packages registry: `.npmrc` maps the scope, a `read:packages` token goes
in `~/.npmrc` (CI: the `MATRIX_RTC_NPM_TOKEN` secret). **The GitHub
Packages registry is a stop-gap** for the phase in which the crate's API
moves with every push; once the SDK is ready for consumers beyond Element
Call it belongs on npmjs.com as `@element-hq/matrix-rtc`, which drops the
alias, `.npmrc`, the CI secret and every token requirement
(`docs/matrix_rtc_sdk.md`). `src/matrix-rtc-sdk/`
keeps only the loader (`initMatrixRtcSdk`: package `initAsync` + our log
sink, wasm via `@element-hq/matrix-rtc/wasm?url`) and the curated
re-exports. _History:_ until 2026-09-16 the bindings were vendored under
`src/matrix-rtc-sdk/generated/` by `scripts/sync-matrix-rtc-sdk.sh`; local
crate work now goes through `pnpm links:on` (`docs/linking.md`).
2. **Wasm loading.** Verified: Vite 8 library mode inlines `?url` and
`new URL(…, import.meta.url)` assets as base64 regardless of
`assetsInlineLimit`; `?url&no-inline` emits a file. App builds use `?url`;
@@ -599,12 +611,14 @@ Gate: `cargo test --features uniffi`, `cargo clippy --all-targets --features uni
### S0b — SDK intake ☑
- `scripts/sync-matrix-rtc-sdk.sh`, `src/matrix-rtc-sdk/generated/**`,
- ~~`scripts/sync-matrix-rtc-sdk.sh`, `src/matrix-rtc-sdk/generated/**`~~
(replaced by the `@element-hq/matrix-rtc` package on 2026-09-16, §5.1),
`src/matrix-rtc-sdk/index.ts` (loader + curated re-exports),
`src/matrix-rtc-sdk/index.test.ts` (boot, one join round-trip against the
TS mock driver).
- `package.json` (`@ubjs/core`), `knip.ts` (`ignore` for `generated/**`),
`.oxlintrc.json` `ignorePatterns`, `.oxfmtrc.json` ignore,
- `package.json` (~~`@ubjs/core`~~ now `@element-hq/matrix-rtc`; the
`generated/**` carve-outs in `knip.ts`, `.oxlintrc.json`, `.oxfmtrc.json`,
`vitest.config.ts` and `.gitattributes` went with the vendored files),
`vite.config.ts` (nothing needed for `?url`; verified `vite-plugin-wasm`
ignores it), `tsconfig.json` untouched thanks to the `.d.ts`.
- Gate: `pnpm lint && pnpm format:check && pnpm test:unit`.
-3
View File
@@ -26,10 +26,7 @@ export default {
// This is a shell built-in.
"printf",
],
// uniffi-generated bindings (scripts/sync-matrix-rtc-sdk.sh): every
// export the crate has, most of them unused here by design
ignore: [
"src/matrix-rtc-sdk/generated/**",
// The host-facing driver seam, landed ahead of its consumers (the
// `CallParticipation` layer and the hosts, see element-call-oxidation-plan.md)
"src/driver/**",
+1 -1
View File
@@ -48,6 +48,7 @@
},
"devDependencies": {
"@codecov/vite-plugin": "^1.3.0",
"@element-hq/matrix-rtc": "npm:@billcarsonfr/matrix-rtc@next",
"@fontsource/inconsolata": "^5.1.0",
"@fontsource/inter": "^5.1.0",
"@formatjs/intl-durationformat": "^0.10.0",
@@ -82,7 +83,6 @@
"@types/react-dom": "^19.0.0",
"@types/sdp-transform": "^2.4.5",
"@typescript-eslint/utils": "^8.61.0",
"@ubjs/core": "0.31.0-5",
"@use-gesture/react": "^10.2.11",
"@vector-im/compound-design-tokens": "^10.0.0",
"@vector-im/compound-web": "^10.0.0",
+15 -3
View File
@@ -25,6 +25,9 @@ importers:
'@codecov/vite-plugin':
specifier: ^1.3.0
version: 1.9.1(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.103.1)(terser@5.46.1)(yaml@2.9.0))
'@element-hq/matrix-rtc':
specifier: npm:@billcarsonfr/matrix-rtc@next
version: '@billcarsonfr/matrix-rtc@0.1.0-next.7.c5a5a80(matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/24929be0e741be6a5d0a7226f1c682e245263b8a)'
'@fontsource/inconsolata':
specifier: ^5.1.0
version: 5.3.0
@@ -127,9 +130,6 @@ importers:
'@typescript-eslint/utils':
specifier: ^8.61.0
version: 8.69.0(eslint@8.57.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)
'@ubjs/core':
specifier: 0.31.0-5
version: 0.31.0-5
'@use-gesture/react':
specifier: ^10.2.11
version: 10.3.1(react@19.2.8)
@@ -412,6 +412,13 @@ packages:
resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
engines: {node: '>=18'}
'@billcarsonfr/matrix-rtc@0.1.0-next.7.c5a5a80':
resolution: {integrity: sha512-6Ti4jQZahHPI2Y9F/6dUplSEvoDdWP0OTLJXFUe708YFa13nRwW3t7QoRRbsSB5O0eWQHwu9hHVJQHEjpx4EwA==, tarball: https://npm.pkg.github.com/download/@billcarsonfr/matrix-rtc/0.1.0-next.7.c5a5a80/55919d699c12752b2316ad6d97a00c4b78088057}
version: 0.1.0-next.7.c5a5a80
engines: {node: '>=20'}
peerDependencies:
matrix-js-sdk: '>=42'
'@blazediff/core@1.9.1':
resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==}
@@ -6365,6 +6372,11 @@ snapshots:
'@bcoe/v8-coverage@1.0.2': {}
'@billcarsonfr/matrix-rtc@0.1.0-next.7.c5a5a80(matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/24929be0e741be6a5d0a7226f1c682e245263b8a)':
dependencies:
'@ubjs/core': 0.31.0-5
matrix-js-sdk: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/24929be0e741be6a5d0a7226f1c682e245263b8a
'@blazediff/core@1.9.1': {}
'@bufbuild/protobuf@1.10.1': {}
+2
View File
@@ -3,6 +3,8 @@ supportedArchitectures:
cpu: [current, arm64]
libc: [current, glibc]
minimumReleaseAgeExclude:
# The MatrixRTC SDK is consumed as `next` pre-releases straight from its CI
- "@billcarsonfr/matrix-rtc"
- "@vector-im/compound-design-tokens"
- "@vector-im/compound-web"
- matrix-js-sdk
-52
View File
@@ -1,52 +0,0 @@
#!/usr/bin/env bash
#
# Copyright 2026 Element Creations Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE in the repository root for full details.
#
# Vendors the matrix-rtc crate's uniffi web bindings into
# src/matrix-rtc-sdk/generated/. Until the crate ships as an npm package this
# is how Element Call picks up a new build of it: build it there, copy the
# generated TypeScript, the wasm-bindgen glue and the wasm here.
#
# scripts/sync-matrix-rtc-sdk.sh [path-to-MatrixSdkArchitectureDraft] [--no-build]
#
# The build uses web-test-app/ubrn.element-call.config.yaml (feature `uniffi`
# only, release profile, its own output directory so the app's test build is
# left alone). Pass --no-build to copy whatever was built last.
set -euo pipefail
DRAFT="${1:-../matrix-rust-rtc/MatrixSdkArchitectureDraft}"
BUILD=1
for arg in "$@"; do
[[ "$arg" == "--no-build" ]] && BUILD=0
done
APP="$DRAFT/web-test-app"
GENERATED="$APP/src/generated-element-call"
DEST="$(cd "$(dirname "$0")/.." && pwd)/src/matrix-rtc-sdk/generated"
if [[ ! -f "$APP/ubrn.element-call.config.yaml" ]]; then
echo "No web-test-app/ubrn.element-call.config.yaml under $DRAFT" >&2
exit 1
fi
if [[ "$BUILD" == 1 ]]; then
(cd "$APP" && npx ubrn build web --config ubrn.element-call.config.yaml --release)
fi
mkdir -p "$DEST/wasm-bindgen"
cp "$GENERATED/matrix_rtc.ts" "$GENERATED/matrix_rtc-ffi.ts" "$DEST/"
cp "$GENERATED/wasm-bindgen/index.js" "$GENERATED/wasm-bindgen/index_bg.wasm" "$DEST/wasm-bindgen/"
REV="$(git -C "$DRAFT" rev-parse --short HEAD 2>/dev/null || echo unknown)"
DIRTY="$(git -C "$DRAFT" status --porcelain 2>/dev/null | grep -q . && echo '-dirty' || true)"
cat > "$DEST/VERSION" <<VERSION
matrix-rtc (MatrixSdkArchitectureDraft) ${REV}${DIRTY}
built $(date -u +%Y-%m-%dT%H:%M:%SZ) by scripts/sync-matrix-rtc-sdk.sh
VERSION
echo "Synced matrix-rtc bindings from $DRAFT ($REV$DIRTY) into $DEST"
ls -la "$DEST" "$DEST/wasm-bindgen"
-2
View File
@@ -1,2 +0,0 @@
matrix-rtc (MatrixSdkArchitectureDraft) 61140bc-dirty
built 2026-09-16T12:10:06Z by scripts/sync-matrix-rtc-sdk.sh
@@ -1,121 +0,0 @@
// This file was autogenerated by some hot garbage in the `uniffi-bindgen-react-native` crate.
// Trust me, you don't want to mess with it!
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
// Structs and function types for calling back into Typescript from Rust.
export type UniffiRustFutureContinuationCallback = (data: bigint, pollResult: number) => void;
export type UniffiForeignFutureDroppedCallback = (handle: bigint) => void;
export type UniffiForeignFutureDroppedCallbackStruct = {
handle: bigint;
free: UniffiForeignFutureDroppedCallback;
};
type UniffiCallbackInterfaceMatrixRtcConnectionsListenerMethod0 = (uniffiHandle: bigint, connections: Uint8Array) => UniffiResult<void>;
type UniffiCallbackInterfaceCloneMatrixRtcConnectionsListener = (handle: bigint) => UniffiResult<void>;
type UniffiCallbackInterfaceFreeMatrixRtcConnectionsListener = (handle: bigint) => void;
export type UniffiVTableCallbackInterfaceMatrixRtcConnectionsListener = {
uniffi_free: UniffiCallbackInterfaceFreeMatrixRtcConnectionsListener;
uniffi_clone: UniffiCallbackInterfaceCloneMatrixRtcConnectionsListener;
on_connections_change: UniffiCallbackInterfaceMatrixRtcConnectionsListenerMethod0;
};
type UniffiCallbackInterfaceMatrixRtcKeyMapListenerMethod0 = (uniffiHandle: bigint, keyMap: Uint8Array, change: Uint8Array) => UniffiResult<void>;
type UniffiCallbackInterfaceCloneMatrixRtcKeyMapListener = (handle: bigint) => UniffiResult<void>;
type UniffiCallbackInterfaceFreeMatrixRtcKeyMapListener = (handle: bigint) => void;
export type UniffiVTableCallbackInterfaceMatrixRtcKeyMapListener = {
uniffi_free: UniffiCallbackInterfaceFreeMatrixRtcKeyMapListener;
uniffi_clone: UniffiCallbackInterfaceCloneMatrixRtcKeyMapListener;
on_key_map_change: UniffiCallbackInterfaceMatrixRtcKeyMapListenerMethod0;
};
type UniffiCallbackInterfaceMatrixRtcKeyRejectedListenerMethod0 = (uniffiHandle: bigint, memberId: Uint8Array, reason: Uint8Array) => UniffiResult<void>;
type UniffiCallbackInterfaceCloneMatrixRtcKeyRejectedListener = (handle: bigint) => UniffiResult<void>;
type UniffiCallbackInterfaceFreeMatrixRtcKeyRejectedListener = (handle: bigint) => void;
export type UniffiVTableCallbackInterfaceMatrixRtcKeyRejectedListener = {
uniffi_free: UniffiCallbackInterfaceFreeMatrixRtcKeyRejectedListener;
uniffi_clone: UniffiCallbackInterfaceCloneMatrixRtcKeyRejectedListener;
on_key_rejected: UniffiCallbackInterfaceMatrixRtcKeyRejectedListenerMethod0;
};
type UniffiCallbackInterfaceMatrixRtcLogSinkMethod0 = (uniffiHandle: bigint, level: Uint8Array, target: Uint8Array, message: Uint8Array) => UniffiResult<void>;
type UniffiCallbackInterfaceCloneMatrixRtcLogSink = (handle: bigint) => UniffiResult<void>;
type UniffiCallbackInterfaceFreeMatrixRtcLogSink = (handle: bigint) => void;
export type UniffiVTableCallbackInterfaceMatrixRtcLogSink = {
uniffi_free: UniffiCallbackInterfaceFreeMatrixRtcLogSink;
uniffi_clone: UniffiCallbackInterfaceCloneMatrixRtcLogSink;
log: UniffiCallbackInterfaceMatrixRtcLogSinkMethod0;
};
export type UniffiForeignFutureResultRustBuffer = {
return_value: Uint8Array;
call_status: UniffiRustCallStatus;
};
export type UniffiForeignFutureCompleterustBuffer = (callbackData: bigint, result: UniffiForeignFutureResultRustBuffer) => void;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod0 = (uniffiHandle: bigint, roomId: Uint8Array, eventType: Uint8Array, contentJson: Uint8Array, durationMs: bigint, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod1 = (uniffiHandle: bigint, roomId: Uint8Array, eventType: Uint8Array, stateKey: Uint8Array, contentJson: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod2 = (uniffiHandle: bigint, roomId: Uint8Array, eventType: Uint8Array, contentJson: Uint8Array, delayMs: bigint, stickyDurationMs: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod3 = (uniffiHandle: bigint, roomId: Uint8Array, eventType: Uint8Array, stateKey: Uint8Array, contentJson: Uint8Array, delayMs: bigint, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct;
export type UniffiForeignFutureResultVoid = {
call_status: UniffiRustCallStatus;
};
export type UniffiForeignFutureCompletevoid = (callbackData: bigint, result: UniffiForeignFutureResultVoid) => void;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod4 = (uniffiHandle: bigint, roomId: Uint8Array, delayId: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompletevoid, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod5 = (uniffiHandle: bigint, roomId: Uint8Array, delayId: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompletevoid, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod6 = (uniffiHandle: bigint, request: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompletevoid, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod7 = (uniffiHandle: bigint, request: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompletevoid, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod8 = (uniffiHandle: bigint, recipients: Uint8Array, eventType: Uint8Array, contentJson: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod9 = (uniffiHandle: bigint, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod10 = (uniffiHandle: bigint, request: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod11 = (uniffiHandle: bigint, eventType: Uint8Array, stateKey: Uint8Array, limit: number, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod12 = (uniffiHandle: bigint, eventType: Uint8Array, stateKey: Uint8Array, uniffiFutureCallback: UniffiForeignFutureCompleterustBuffer, uniffiCallbackData: bigint) => UniffiForeignFutureDroppedCallbackStruct;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod13 = (uniffiHandle: bigint, sink: bigint) => UniffiResult<void>;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod14 = (uniffiHandle: bigint, sink: bigint) => UniffiResult<void>;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod15 = (uniffiHandle: bigint, sink: bigint) => UniffiResult<void>;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod16 = (uniffiHandle: bigint) => number;
type UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod17 = (uniffiHandle: bigint, sink: bigint) => UniffiResult<void>;
type UniffiCallbackInterfaceCloneMatrixRtcMatrixDriverCallback = (handle: bigint) => UniffiResult<void>;
type UniffiCallbackInterfaceFreeMatrixRtcMatrixDriverCallback = (handle: bigint) => void;
export type UniffiVTableCallbackInterfaceMatrixRtcMatrixDriverCallback = {
uniffi_free: UniffiCallbackInterfaceFreeMatrixRtcMatrixDriverCallback;
uniffi_clone: UniffiCallbackInterfaceCloneMatrixRtcMatrixDriverCallback;
send_sticky_event: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod0;
send_state_event: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod1;
send_delayed_event: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod2;
send_delayed_state_event: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod3;
restart_delayed_event: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod4;
cancel_delayed_event: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod5;
delegate_delayed_leave_via_homeserver: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod6;
delegate_delayed_leave_via_transport: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod7;
send_to_device: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod8;
get_rtc_transports: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod9;
get_livekit_token: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod10;
read_events: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod11;
read_state: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod12;
subscribe_room_events: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod13;
subscribe_to_device_events: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod14;
subscribe_state_updates: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod15;
is_homeserver_connected: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod16;
subscribe_connectivity: UniffiCallbackInterfaceMatrixRtcMatrixDriverCallbackMethod17;
};
type UniffiCallbackInterfaceMatrixRtcMembershipsListenerMethod0 = (uniffiHandle: bigint, memberships: Uint8Array) => UniffiResult<void>;
type UniffiCallbackInterfaceCloneMatrixRtcMembershipsListener = (handle: bigint) => UniffiResult<void>;
type UniffiCallbackInterfaceFreeMatrixRtcMembershipsListener = (handle: bigint) => void;
export type UniffiVTableCallbackInterfaceMatrixRtcMembershipsListener = {
uniffi_free: UniffiCallbackInterfaceFreeMatrixRtcMembershipsListener;
uniffi_clone: UniffiCallbackInterfaceCloneMatrixRtcMembershipsListener;
on_memberships_change: UniffiCallbackInterfaceMatrixRtcMembershipsListenerMethod0;
};
type UniffiCallbackInterfaceMatrixRtcSessionListenerMethod0 = (uniffiHandle: bigint, session: Uint8Array) => UniffiResult<void>;
type UniffiCallbackInterfaceCloneMatrixRtcSessionListener = (handle: bigint) => UniffiResult<void>;
type UniffiCallbackInterfaceFreeMatrixRtcSessionListener = (handle: bigint) => void;
export type UniffiVTableCallbackInterfaceMatrixRtcSessionListener = {
uniffi_free: UniffiCallbackInterfaceFreeMatrixRtcSessionListener;
uniffi_clone: UniffiCallbackInterfaceCloneMatrixRtcSessionListener;
on_session_change: UniffiCallbackInterfaceMatrixRtcSessionListenerMethod0;
};
type UniffiCallbackInterfaceMatrixRtcStatusListenerMethod0 = (uniffiHandle: bigint, status: Uint8Array) => UniffiResult<void>;
type UniffiCallbackInterfaceCloneMatrixRtcStatusListener = (handle: bigint) => UniffiResult<void>;
type UniffiCallbackInterfaceFreeMatrixRtcStatusListener = (handle: bigint) => void;
export type UniffiVTableCallbackInterfaceMatrixRtcStatusListener = {
uniffi_free: UniffiCallbackInterfaceFreeMatrixRtcStatusListener;
uniffi_clone: UniffiCallbackInterfaceCloneMatrixRtcStatusListener;
on_status_change: UniffiCallbackInterfaceMatrixRtcStatusListenerMethod0;
};
File diff suppressed because it is too large Load Diff
-30
View File
@@ -1,30 +0,0 @@
/*
Copyright 2026 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details.
*/
// Hand-written declaration for the wasm-bindgen glue next to it, which ubrn
// emits as plain JavaScript. Only the loading entry points are declared; the
// exported `ubrn_*` FFI functions are reached through matrix_rtc.ts alone.
// Kept out of the sync script's way: it is not generated.
/** What wasm-bindgen's loader accepts as the module to instantiate. */
export type InitInput =
| RequestInfo
| URL
| Response
| BufferSource
| WebAssembly.Module;
export default function initAsync(
moduleOrPath?:
| { module_or_path?: InitInput | Promise<InitInput> }
| InitInput
| Promise<InitInput>,
): Promise<unknown>;
export function initSync(
module: { module: BufferSource | WebAssembly.Module } | BufferSource | WebAssembly.Module,
): unknown;
File diff suppressed because it is too large Load Diff
+23 -19
View File
@@ -7,17 +7,18 @@ Please see LICENSE in the repository root for full details.
/**
* The MatrixRTC SDK: the Rust `matrix-rtc` crate through its uniffi web
* bindings. Everything Element Call needs to *participate* in a MatrixRTC
* session — the session projection, our own membership with its keep-alive,
* transport tokens and the media key exchange — lives in the crate; this
* module loads it and re-exports the surface Element Call uses.
* bindings, shipped as the `@element-hq/matrix-rtc` npm package. Everything
* Element Call needs to *participate* in a MatrixRTC session — the session
* projection, our own membership with its keep-alive, transport tokens and
* the media key exchange — lives in the crate; this module loads it and
* re-exports the surface Element Call uses.
*
* The bindings under `generated/` are vendored by
* `scripts/sync-matrix-rtc-sdk.sh` and never edited by hand.
* Nothing else imports the package directly: the boot below is the one place
* the wasm is loaded and the crate's logging is wired up, so a caller cannot
* end up with a silent or half-initialised SDK.
*/
import initAsync, { type InitInput } from "./generated/wasm-bindgen/index.js";
import bindings from "./generated/matrix_rtc";
import { initAsync, type WasmSource } from "@element-hq/matrix-rtc";
import { installMatrixRtcLogSink } from "./logSink";
export {
@@ -38,7 +39,7 @@ export {
FfiTransportIntent,
RtcError,
computeSessionsFromEvents,
} from "./generated/matrix_rtc";
} from "@element-hq/matrix-rtc";
export type {
FfiConnectionData,
FfiConnectionWithMembers,
@@ -62,14 +63,14 @@ export type {
RoomEventSinkLike,
StateUpdateSinkLike,
ToDeviceSinkLike,
} from "./generated/matrix_rtc";
} from "@element-hq/matrix-rtc";
/**
* Where to load the wasm from. A URL (or anything `fetch` accepts) in a
* browser; bytes or a compiled module where there is nothing to fetch from,
* such as tests.
*/
export type MatrixRtcWasmSource = InitInput;
export type MatrixRtcWasmSource = WasmSource;
let loading: Promise<void> | null = null;
@@ -77,25 +78,28 @@ let loading: Promise<void> | null = null;
* Loads and initialises the SDK. Idempotent: the first call decides the
* source, later calls await the same load.
*
* Without a `source`, the wasm is the one bundled next to this module (an
* asset URL in the app builds). A host that serves the file from somewhere
* else, or a test runner with no server, passes its own.
* Without a `source`, the wasm is the package's own copy as the bundler
* placed it (an asset URL in the app builds). A host that serves the file
* from somewhere else, or a test runner with no server, passes its own.
*/
export async function initMatrixRtcSdk(
source?: MatrixRtcWasmSource,
): Promise<void> {
loading ??= (async (): Promise<void> => {
await initAsync({ module_or_path: source ?? (await bundledWasm()) });
bindings.initialize();
await initAsync(source ?? (await bundledWasm()));
// The crate is silent until told where to log.
installMatrixRtcLogSink();
})();
await loading;
}
/** The wasm as the bundler placed it, resolved lazily so nothing is fetched until needed. */
/**
* The wasm as the bundler placed it, resolved lazily so nothing is fetched
* until needed. Asked for explicitly, rather than leaving the package to its
* `import.meta.url` default, so that the dev server's dependency
* pre-bundling, which rewrites module URLs, cannot lose track of it.
*/
async function bundledWasm(): Promise<string> {
const { default: url } =
await import("./generated/wasm-bindgen/index_bg.wasm?url");
const { default: url } = await import("@element-hq/matrix-rtc/wasm?url");
return url;
}
+1 -1
View File
@@ -7,7 +7,7 @@ Please see LICENSE in the repository root for full details.
import { type Logger, logger as rootLogger } from "matrix-js-sdk/lib/logger";
import { FfiLogLevel, type LogSink, setLogSink } from "./generated/matrix_rtc";
import { FfiLogLevel, type LogSink, setLogSink } from "@element-hq/matrix-rtc";
/**
* A {@link LogSink} that writes the crate's lines to a matrix-js-sdk logger,
+10 -11
View File
@@ -6,26 +6,25 @@ Please see LICENSE in the repository root for full details.
*/
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { createRequire } from "node:module";
import { join } from "node:path";
import { initMatrixRtcSdk } from "../matrix-rtc-sdk";
/**
* Loads the MatrixRTC SDK for a unit test. The browser builds fetch the wasm
* by URL; under vitest there is no server, so the bytes come from disk.
* by URL; under vitest there is no server, so the bytes come from disk: the
* `.wasm` the installed package exports as `@element-hq/matrix-rtc/wasm`.
*
* Only suites that construct a participation manager need this; do not put
* it in the global setup, where every test file would pay for the boot.
*/
export async function initMatrixRtcSdkForTests(): Promise<void> {
// Relative to this file: under the jsdom environment `import.meta.url` is
// not a file URL and `process.cwd()` is not the repository, but vitest
// still provides `__dirname`.
const wasm = readFileSync(
resolve(
__dirname,
"../matrix-rtc-sdk/generated/wasm-bindgen/index_bg.wasm",
),
// Resolved from this file's directory: under the jsdom environment
// `import.meta.url` is not a file URL and `process.cwd()` is not the
// repository, but vitest still provides `__dirname`.
const wasm = createRequire(join(__dirname, "test-matrix-rtc.ts")).resolve(
"@element-hq/matrix-rtc/wasm",
);
await initMatrixRtcSdk(wasm);
await initMatrixRtcSdk(readFileSync(wasm));
}
-1
View File
@@ -59,7 +59,6 @@ export default defineConfig((configEnv) =>
"src/utils/test-fixtures.ts",
"src/utils/test-matrix-rtc.ts",
"src/utils/test-participation.ts",
"src/matrix-rtc-sdk/generated/**",
"playwright/**",
],
},