├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .git-blame-ignore-revs ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── PULL_REQUEST_TEMPLATE.md ├── actions │ ├── sign-release-tarball │ │ └── action.yml │ └── upload-release-assets │ │ └── action.yml ├── labels.yml ├── release-drafter.yml ├── renovate.json └── workflows │ ├── backport.yml │ ├── docs-pr-netlify.yaml │ ├── downstream-end-to-end-tests.yml │ ├── notify-downstream.yaml │ ├── pull_request.yaml │ ├── release-checks.yml │ ├── release-drafter-workflow.yml │ ├── release-drafter.yml │ ├── release-gitflow.yml │ ├── release-make.yml │ ├── release-npm.yml │ ├── release.yml │ ├── sonarcloud.yml │ ├── sonarqube.yml │ ├── static_analysis.yml │ ├── sync-labels.yml │ ├── tests.yml │ ├── triage-incoming.yml │ ├── triage-labelled.yml │ └── triage-stale.yml ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc ├── .prettierignore ├── .prettierrc.cjs ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── babel.config.cjs ├── code_style.md ├── docs ├── SUMMARY.md ├── storage-notes.md └── warning-on-unverified-devices.md ├── examples ├── node │ ├── README.md │ ├── app.js │ ├── package.json │ └── tsconfig.json └── voip │ ├── README.md │ ├── browserTest.js │ ├── index.html │ └── lib │ └── matrix.js ├── git-hooks └── pre-commit ├── jest.config.ts ├── knip.ts ├── package.json ├── scripts ├── changelog_head.py └── release │ └── merge-release-notes.cjs ├── sonar-project.properties ├── spec ├── MockStorageApi.ts ├── TestClient.ts ├── integ │ ├── crypto │ │ ├── cross-signing.spec.ts │ │ ├── crypto.spec.ts │ │ ├── device-dehydration.spec.ts │ │ ├── megolm-backup.spec.ts │ │ ├── olm-utils.ts │ │ ├── rust-crypto.spec.ts │ │ ├── to-device-messages.spec.ts │ │ └── verification.spec.ts │ ├── matrix-client-event-emitter.spec.ts │ ├── matrix-client-event-timeline.spec.ts │ ├── matrix-client-methods.spec.ts │ ├── matrix-client-opts.spec.ts │ ├── matrix-client-relations.spec.ts │ ├── matrix-client-retrying.spec.ts │ ├── matrix-client-room-timeline.spec.ts │ ├── matrix-client-syncing-errors.spec.ts │ ├── matrix-client-syncing.spec.ts │ ├── matrix-client-unread-notifications.spec.ts │ ├── rendezvous │ │ └── MSC4108SignInWithQR.spec.ts │ ├── sliding-sync-sdk.spec.ts │ └── sliding-sync.spec.ts ├── olm-loader.ts ├── setupTests.ts ├── slowReporter.cjs ├── test-utils │ ├── AccountDataAccumulator.ts │ ├── E2EKeyReceiver.ts │ ├── E2EKeyResponder.ts │ ├── SyncResponder.ts │ ├── beacon.ts │ ├── client.ts │ ├── emitter.ts │ ├── flushPromises.ts │ ├── mockEndpoints.ts │ ├── oidc.ts │ ├── test-data │ │ ├── .gitignore │ │ ├── generate-test-data.py │ │ └── index.ts │ ├── test-utils.ts │ ├── test_indexeddb_cryptostore_dump │ │ ├── README.md │ │ ├── empty_account │ │ │ ├── README.md │ │ │ ├── dump.json │ │ │ └── index.ts │ │ ├── full_account │ │ │ ├── README.md │ │ │ ├── dump.json │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── no_cached_msk_dump │ │ │ ├── README.md │ │ │ ├── dump.json │ │ │ └── index.ts │ │ └── unverified │ │ │ ├── README.md │ │ │ ├── dump.json │ │ │ └── index.ts │ ├── thread.ts │ ├── webrtc.ts │ └── webrtcReports.ts └── unit │ ├── NamespacedValue.spec.ts │ ├── ReEmitter.spec.ts │ ├── ToDeviceMessageQueue.spec.ts │ ├── autodiscovery.spec.ts │ ├── base64.spec.ts │ ├── common-crypto │ └── key-passphrase.spec.ts │ ├── content-helpers.spec.ts │ ├── content-repo.spec.ts │ ├── crypto-api │ └── recovery-key.spec.ts │ ├── crypto │ └── store │ │ ├── CryptoStore.spec.ts │ │ └── IndexedDBCryptoStore.spec.ts │ ├── digest.spec.ts │ ├── embedded.spec.ts │ ├── event-mapper.spec.ts │ ├── event-timeline-set.spec.ts │ ├── event-timeline.spec.ts │ ├── extensible_events_v1 │ ├── ExtensibleEvent.spec.ts │ ├── MessageEvent.spec.ts │ ├── PollEndEvent.spec.ts │ ├── PollResponseEvent.spec.ts │ ├── PollStartEvent.spec.ts │ └── utilities.spec.ts │ ├── feature.spec.ts │ ├── filter-component.spec.ts │ ├── filter.spec.ts │ ├── http-api │ ├── __snapshots__ │ │ └── index.spec.ts.snap │ ├── errors.spec.ts │ ├── fetch.spec.ts │ ├── index.spec.ts │ └── utils.spec.ts │ ├── interactive-auth.spec.ts │ ├── local_notifications.spec.ts │ ├── location.spec.ts │ ├── logger.spec.ts │ ├── login.spec.ts │ ├── matrix-client.spec.ts │ ├── matrixrtc │ ├── CallMembership.spec.ts │ ├── LivekitFocus.spec.ts │ ├── MatrixRTCSession.spec.ts │ ├── MatrixRTCSessionManager.spec.ts │ ├── MembershipManager.spec.ts │ ├── RoomAndToDeviceTransport.spec.ts │ ├── RoomKeyTransport.spec.ts │ ├── ToDeviceKeyTransport.spec.ts │ ├── memberManagerTestEnvironment.ts │ └── mocks.ts │ ├── models │ ├── MSC3089Branch.spec.ts │ ├── MSC3089TreeSpace.spec.ts │ ├── beacon.spec.ts │ ├── event.spec.ts │ ├── poll.spec.ts │ ├── room-receipts.spec.ts │ └── thread.spec.ts │ ├── notifications.spec.ts │ ├── oidc │ ├── authorize.spec.ts │ ├── register.spec.ts │ ├── tokenRefresher.spec.ts │ └── validate.spec.ts │ ├── pusher.spec.ts │ ├── pushprocessor.spec.ts │ ├── queueToDevice.spec.ts │ ├── randomstring.spec.ts │ ├── read-receipt.spec.ts │ ├── realtime-callbacks.spec.ts │ ├── receipt-accumulator.spec.ts │ ├── relations.spec.ts │ ├── rendezvous │ ├── DummyTransport.ts │ ├── MSC4108RendezvousSession.spec.ts │ └── channels │ │ └── MSC4108SecureChannel.spec.ts │ ├── room-hierarchy.spec.ts │ ├── room-member.spec.ts │ ├── room-state.spec.ts │ ├── room.spec.ts │ ├── rust-crypto │ ├── CrossSigningIdentity.spec.ts │ ├── KeyClaimManager.spec.ts │ ├── OutgoingRequestProcessor.spec.ts │ ├── OutgoingRequestsManager.spec.ts │ ├── PerSessionKeyBackupDownloader.spec.ts │ ├── RoomEncryptor.spec.ts │ ├── __snapshots__ │ │ └── rust-crypto.spec.ts.snap │ ├── backup.spec.ts │ ├── device-converter.spec.ts │ ├── rust-crypto.spec.ts │ ├── secret-storage.spec.ts │ └── verification.spec.ts │ ├── scheduler.spec.ts │ ├── secret-storage.spec.ts │ ├── stores │ ├── indexeddb-store-worker.spec.ts │ ├── indexeddb.spec.ts │ └── memory.spec.ts │ ├── sync-accumulator.spec.ts │ ├── testing.spec.ts │ ├── thread-utils.spec.ts │ ├── timeline-window.spec.ts │ ├── user.spec.ts │ ├── utils.spec.ts │ └── webrtc │ ├── call.spec.ts │ ├── callEventHandler.spec.ts │ ├── callFeed.spec.ts │ ├── groupCall.spec.ts │ ├── groupCallEventHandler.spec.ts │ ├── mediaHandler.spec.ts │ └── stats │ ├── __snapshots__ │ └── callFeedStatsReporter.spec.ts.snap │ ├── callFeedStatsReporter.spec.ts │ ├── callStatsReportGatherer.spec.ts │ ├── connectionStatsReportBuilder.spec.ts │ ├── connectionStatsReporter.spec.ts │ ├── groupCallStats.spec.ts │ ├── media │ ├── mediaSsrcHandler.spec.ts │ ├── mediaTrackHandler.spec.ts │ └── mediaTrackStatsHandler.spec.ts │ ├── statsReportEmitter.spec.ts │ ├── summaryStatsReportGatherer.spec.ts │ ├── trackStatsBuilder.spec.ts │ ├── transportStatsBuilder.spec.ts │ └── valueFormatter.spec.ts ├── src ├── @types │ ├── AESEncryptedSecretStoragePayload.ts │ ├── IIdentityServerProvider.ts │ ├── PushRules.ts │ ├── another-json.d.ts │ ├── auth.ts │ ├── beacon.ts │ ├── common.ts │ ├── crypto.ts │ ├── event.ts │ ├── events.ts │ ├── extensible_events.ts │ ├── global.d.ts │ ├── local_notifications.ts │ ├── location.ts │ ├── matrix-sdk-crypto-wasm.d.ts │ ├── media.ts │ ├── membership.ts │ ├── oidc-client-ts.d.ts │ ├── partials.ts │ ├── polls.ts │ ├── read_receipts.ts │ ├── registration.ts │ ├── requests.ts │ ├── search.ts │ ├── signed.ts │ ├── spaces.ts │ ├── state_events.ts │ ├── synapse.ts │ ├── sync.ts │ ├── threepids.ts │ ├── topic.ts │ └── uia.ts ├── NamespacedValue.ts ├── ReEmitter.ts ├── ToDeviceMessageQueue.ts ├── autodiscovery.ts ├── base64.ts ├── browser-index.ts ├── client.ts ├── common-crypto │ ├── CryptoBackend.ts │ ├── README.md │ └── key-passphrase.ts ├── content-helpers.ts ├── content-repo.ts ├── crypto-api │ ├── CryptoEvent.ts │ ├── CryptoEventHandlerMap.ts │ ├── index.ts │ ├── key-passphrase.ts │ ├── keybackup.ts │ ├── recovery-key.ts │ └── verification.ts ├── crypto │ └── store │ │ ├── base.ts │ │ ├── indexeddb-crypto-store-backend.ts │ │ ├── indexeddb-crypto-store.ts │ │ ├── localStorage-crypto-store.ts │ │ └── memory-crypto-store.ts ├── digest.ts ├── embedded.ts ├── errors.ts ├── event-mapper.ts ├── extensible_events_v1 │ ├── ExtensibleEvent.ts │ ├── InvalidEventError.ts │ ├── MessageEvent.ts │ ├── PollEndEvent.ts │ ├── PollResponseEvent.ts │ ├── PollStartEvent.ts │ └── utilities.ts ├── feature.ts ├── filter-component.ts ├── filter.ts ├── http-api │ ├── errors.ts │ ├── fetch.ts │ ├── index.ts │ ├── interface.ts │ ├── method.ts │ ├── prefix.ts │ ├── refresh.ts │ └── utils.ts ├── index.ts ├── indexeddb-helpers.ts ├── indexeddb-worker.ts ├── interactive-auth.ts ├── logger.ts ├── matrix.ts ├── matrixrtc │ ├── CallMembership.ts │ ├── EncryptionManager.ts │ ├── IKeyTransport.ts │ ├── IMembershipManager.ts │ ├── LegacyMembershipManager.ts │ ├── LivekitFocus.ts │ ├── MatrixRTCSession.ts │ ├── MatrixRTCSessionManager.ts │ ├── NewMembershipManager.ts │ ├── NewMembershipManagerActionScheduler.ts │ ├── RoomAndToDeviceKeyTransport.ts │ ├── RoomKeyTransport.ts │ ├── ToDeviceKeyTransport.ts │ ├── focus.ts │ ├── index.ts │ └── types.ts ├── models │ ├── MSC3089Branch.ts │ ├── MSC3089TreeSpace.ts │ ├── ToDeviceMessage.ts │ ├── beacon.ts │ ├── compare-event-ordering.ts │ ├── device.ts │ ├── event-context.ts │ ├── event-status.ts │ ├── event-timeline-set.ts │ ├── event-timeline.ts │ ├── event.ts │ ├── invites-ignorer-types.ts │ ├── invites-ignorer.ts │ ├── poll.ts │ ├── profile-keys.ts │ ├── read-receipt.ts │ ├── related-relations.ts │ ├── relations-container.ts │ ├── relations.ts │ ├── room-member.ts │ ├── room-receipts.ts │ ├── room-state.ts │ ├── room-summary.ts │ ├── room.ts │ ├── search-result.ts │ ├── thread.ts │ ├── typed-event-emitter.ts │ └── user.ts ├── oidc │ ├── authorize.ts │ ├── discovery.ts │ ├── error.ts │ ├── index.ts │ ├── register.ts │ ├── tokenRefresher.ts │ └── validate.ts ├── pushprocessor.ts ├── randomstring.ts ├── realtime-callbacks.ts ├── receipt-accumulator.ts ├── rendezvous │ ├── MSC4108SignInWithQR.ts │ ├── RendezvousChannel.ts │ ├── RendezvousCode.ts │ ├── RendezvousError.ts │ ├── RendezvousFailureReason.ts │ ├── RendezvousIntent.ts │ ├── RendezvousTransport.ts │ ├── channels │ │ ├── MSC4108SecureChannel.ts │ │ └── index.ts │ ├── index.ts │ └── transports │ │ ├── MSC4108RendezvousSession.ts │ │ └── index.ts ├── room-hierarchy.ts ├── rust-crypto │ ├── CrossSigningIdentity.ts │ ├── DehydratedDeviceManager.ts │ ├── KeyClaimManager.ts │ ├── OutgoingRequestProcessor.ts │ ├── OutgoingRequestsManager.ts │ ├── PerSessionKeyBackupDownloader.ts │ ├── RoomEncryptor.ts │ ├── backup.ts │ ├── constants.ts │ ├── device-converter.ts │ ├── index.ts │ ├── libolm_migration.ts │ ├── rust-crypto.ts │ ├── secret-storage.ts │ └── verification.ts ├── scheduler.ts ├── secret-storage.ts ├── serverCapabilities.ts ├── service-types.ts ├── sliding-sync-sdk.ts ├── sliding-sync.ts ├── store │ ├── index.ts │ ├── indexeddb-backend.ts │ ├── indexeddb-local-backend.ts │ ├── indexeddb-remote-backend.ts │ ├── indexeddb-store-worker.ts │ ├── indexeddb.ts │ ├── local-storage-events-emitter.ts │ ├── memory.ts │ └── stub.ts ├── sync-accumulator.ts ├── sync.ts ├── testing.ts ├── thread-utils.ts ├── timeline-window.ts ├── types.ts ├── utils.ts ├── utils │ ├── decryptAESSecretStorageItem.ts │ ├── encryptAESSecretStorageItem.ts │ └── internal │ │ └── deriveKeys.ts ├── version-support.ts └── webrtc │ ├── audioContext.ts │ ├── call.ts │ ├── callEventHandler.ts │ ├── callEventTypes.ts │ ├── callFeed.ts │ ├── groupCall.ts │ ├── groupCallEventHandler.ts │ ├── mediaHandler.ts │ └── stats │ ├── callFeedStatsReporter.ts │ ├── callStatsReportGatherer.ts │ ├── callStatsReportSummary.ts │ ├── connectionStats.ts │ ├── connectionStatsBuilder.ts │ ├── connectionStatsReportBuilder.ts │ ├── groupCallStats.ts │ ├── media │ ├── mediaSsrcHandler.ts │ ├── mediaTrackHandler.ts │ ├── mediaTrackStats.ts │ └── mediaTrackStatsHandler.ts │ ├── statsReport.ts │ ├── statsReportEmitter.ts │ ├── summaryStatsReportGatherer.ts │ ├── trackStatsBuilder.ts │ ├── transportStats.ts │ ├── transportStatsBuilder.ts │ └── valueFormatter.ts ├── tsconfig-build.json ├── tsconfig.json ├── typedoc.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Aviral Dasgupta 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | root = true 16 | 17 | [*] 18 | charset=utf-8 19 | end_of_line = lf 20 | insert_final_newline = true 21 | indent_style = space 22 | indent_size = 4 23 | trim_trailing_whitespace = true 24 | 25 | [*.{yml,yaml}] 26 | indent_size = 4 27 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | _docs 2 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Minor white-space adjustments 2 | 1d1d59c75744e1f6a2be1cb3e0d1bd9ded5f8025 3 | # Import ordering and spacing: eslint-plugin-import 4 | 80aaa6c32b50601f82e0c991c24e5a4590f39463 5 | # Minor white-space adjustment 6 | 8fb036ba2d01fab66dc4373802ccf19b5cac8541 7 | # Minor white-space adjustment 8 | b63de6a902a9e1f8ffd7697dea33820fc04f028e 9 | 3ca84cfc491b0987eec1f13f13cae58d2032bf54 10 | # Conform to new typescript eslint rules 11 | a87858840b57514603f63e2abbbda4f107f05a77 12 | 5cf6684129a921295f5593173f16f192336fe0a2 13 | # Comply with new member-delimiter-style rule 14 | b2ad957d298720d3e026b6bd91be0c403338361a 15 | # Fix semicolons in TS files 16 | e2ec8952e38b8fea3f0ccaa09ecb42feeba0d923 17 | # Migrate to `eslint-plugin-matrix-org` 18 | # and `babel/...` to `@babel/...` migration 19 | 09fac77ce0d9bcf6637088c29afab84084f0e739 20 | 102704e91a70643bcc09721e14b0d909f0ef55c6 21 | # Eslint formatting 22 | cec00cd303787fa9008b6c48826e75ed438036fa 23 | # Minor eslint changes 24 | 68bb8182e4e62d8f450f80c408c4b231b8725f1b 25 | c979ff6696e30ab8983ac416a3590996d84d3560 26 | f4a7395e3a3751a1a8e92dd302c49175a3296ad2 27 | # eslint --fix for dangley commas on function calls 28 | 423175f5397910b0afe3112d6fb18283fc7d27d4 29 | # eslint ---fix for prefer-const 30 | 7bca05af644e8b997dae81e568a3913d8f18d7ca 31 | # Fix linting on tests 32 | cee7f7a280a8c20bafc21c0a2911f60851f7a7ca 33 | # eslint --fix 34 | 0fa9f7c6098822db1ae214f352fd1fe5c248b02c 35 | # eslint --fix for lots of white-space 36 | 5abf6b9f208801c5022a47023150b5846cb0b309 37 | # eslint --fix 38 | 7ed65407e6cdf292ce3cf659310c68d19dcd52b2 39 | # Switch to ESLint from JSHint (Google eslint rules as a base) 40 | e057956ede9ad1a931ff8050c411aca7907e0394 41 | # prettier 42 | 349c2c2587c2885bb69eda4aa078b5383724cf5e 43 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @matrix-org/element-web-reviewers 2 | /.github/workflows/** @matrix-org/element-web-team 3 | /package.json @matrix-org/element-web-team 4 | /yarn.lock @matrix-org/element-web-team 5 | /scripts/** @matrix-org/element-web-team 6 | /src/webrtc @matrix-org/element-call-reviewers 7 | /src/matrixrtc @matrix-org/element-call-reviewers 8 | /spec/*/webrtc @matrix-org/element-call-reviewers 9 | /spec/*/matrixrtc @matrix-org/element-call-reviewers 10 | 11 | /src/crypto-api @matrix-org/element-crypto-web-reviewers 12 | /src/crypto @matrix-org/element-crypto-web-reviewers 13 | /src/rust-crypto @matrix-org/element-crypto-web-reviewers 14 | /spec/integ/crypto @matrix-org/element-crypto-web-reviewers 15 | /spec/unit/crypto.spec.ts @matrix-org/element-crypto-web-reviewers 16 | /spec/unit/crypto @matrix-org/element-crypto-web-reviewers 17 | /spec/unit/rust-crypto @matrix-org/element-crypto-web-reviewers 18 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: matrixdotorg 2 | liberapay: matrixdotorg 3 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Checklist 4 | 5 | - [ ] Tests written for new code (and old code if feasible). 6 | - [ ] New or updated `public`/`exported` symbols have accurate [TSDoc](https://tsdoc.org/) documentation. 7 | - [ ] Linter and other CI checks pass. 8 | - [ ] Sign-off given on the changes (see [CONTRIBUTING.md](https://github.com/matrix-org/matrix-js-sdk/blob/develop/CONTRIBUTING.md)). 9 | -------------------------------------------------------------------------------- /.github/actions/sign-release-tarball/action.yml: -------------------------------------------------------------------------------- 1 | name: Sign Release Tarball 2 | description: Generates signature for release tarball and uploads it as a release asset 3 | inputs: 4 | gpg-fingerprint: 5 | description: Fingerprint of the GPG key to use for signing the tarball. 6 | required: true 7 | upload-url: 8 | description: GitHub release upload URL to upload the signature file to. 9 | required: true 10 | runs: 11 | using: composite 12 | steps: 13 | - name: Generate tarball signature 14 | shell: bash 15 | run: | 16 | git -c tar.tar.gz.command='gzip -cn' archive --format=tar.gz --prefix="${REPO#*/}-${VERSION#v}/" -o "/tmp/${VERSION}.tar.gz" "${VERSION}" 17 | gpg -u "$GPG_FINGERPRINT" --armor --output "${VERSION}.tar.gz.asc" --detach-sig "/tmp/${VERSION}.tar.gz" 18 | rm "/tmp/${VERSION}.tar.gz" 19 | env: 20 | GPG_FINGERPRINT: ${{ inputs.gpg-fingerprint }} 21 | REPO: ${{ github.repository }} 22 | 23 | - name: Upload tarball signature 24 | if: ${{ inputs.upload-url }} 25 | uses: shogo82148/actions-upload-release-asset@d22998fda4c1407f60d1ab48cd6fe67f360f34de # v1 26 | with: 27 | upload_url: ${{ inputs.upload-url }} 28 | asset_path: ${{ env.VERSION }}.tar.gz.asc 29 | -------------------------------------------------------------------------------- /.github/actions/upload-release-assets/action.yml: -------------------------------------------------------------------------------- 1 | name: Upload release assets 2 | description: Uploads assets to an existing release and optionally signs them 3 | inputs: 4 | gpg-fingerprint: 5 | description: Fingerprint of the GPG key to use for signing the assets, if any. 6 | required: false 7 | upload-url: 8 | description: GitHub release upload URL to upload the assets to. 9 | required: true 10 | asset-path: 11 | description: | 12 | The path to the asset you want to upload, if any. You can use glob patterns here. 13 | Will be GPG signed and an `.asc` file included in the release artifacts if `gpg-fingerprint` is set. 14 | required: true 15 | runs: 16 | using: composite 17 | steps: 18 | - name: Sign assets 19 | if: inputs.gpg-fingerprint 20 | shell: bash 21 | run: | 22 | for FILE in $ASSET_PATH 23 | do 24 | gpg -u "$GPG_FINGERPRINT" --armor --output "$FILE".asc --detach-sig "$FILE" 25 | done 26 | env: 27 | GPG_FINGERPRINT: ${{ inputs.gpg-fingerprint }} 28 | ASSET_PATH: ${{ inputs.asset-path }} 29 | 30 | - name: Upload asset signatures 31 | if: inputs.gpg-fingerprint 32 | uses: shogo82148/actions-upload-release-asset@d22998fda4c1407f60d1ab48cd6fe67f360f34de # v1 33 | with: 34 | upload_url: ${{ inputs.upload-url }} 35 | asset_path: ${{ inputs.asset-path }}.asc 36 | 37 | - name: Upload assets 38 | uses: shogo82148/actions-upload-release-asset@d22998fda4c1407f60d1ab48cd6fe67f360f34de # v1 39 | with: 40 | upload_url: ${{ inputs.upload-url }} 41 | asset_path: ${{ inputs.asset-path }} 42 | -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | - name: "A-Element-R" 2 | description: "Issues affecting the port of Element's crypto layer to Rust" 3 | color: "bfd4f2" 4 | - name: "A-Packaging" 5 | description: "Packaging, signing, releasing" 6 | color: "bfd4f2" 7 | - name: "A-Technical-Debt" 8 | color: "bfd4f2" 9 | - name: "A-Testing" 10 | description: "Testing, code coverage, etc." 11 | color: "bfd4f2" 12 | - name: "backport staging" 13 | description: "Label to automatically backport PR to staging branch" 14 | color: "B60205" 15 | - name: "Dependencies" 16 | description: "Pull requests that update a dependency file" 17 | color: "0366d6" 18 | - name: "Easy" 19 | color: "5dc9f7" 20 | - name: "Sponsored" 21 | color: "ffc8f4" 22 | - name: "T-Deprecation" 23 | description: "A pull request that makes something deprecated" 24 | color: "98e6ae" 25 | - name: "T-Other" 26 | description: "Questions, user support, anything else" 27 | color: "98e6ae" 28 | - name: "X-Blocked" 29 | color: "ff7979" 30 | - name: "X-Breaking-Change" 31 | color: "ff7979" 32 | - name: "X-Reverted" 33 | description: "PR has been reverted" 34 | color: "F68AA3" 35 | - name: "X-Upcoming-Release-Blocker" 36 | description: "This does not affect the current release cycle but will affect the next one" 37 | color: "e99695" 38 | - name: "Z-Community-PR" 39 | description: "Issue is solved by a community member's PR" 40 | color: "ededed" 41 | - name: "Z-Flaky-Test" 42 | description: "A test is raising false alarms" 43 | color: "ededed" 44 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: "v$RESOLVED_VERSION" 2 | tag-template: "v$RESOLVED_VERSION" 3 | change-template: "* $TITLE ([#$NUMBER]($URL)). Contributed by @$AUTHOR." 4 | categories: 5 | - title: "🚨 BREAKING CHANGES" 6 | label: "X-Breaking-Change" 7 | - title: "🦖 Deprecations" 8 | label: "T-Deprecation" 9 | - title: "✨ Features" 10 | label: "T-Enhancement" 11 | - title: "🐛 Bug Fixes" 12 | label: "T-Defect" 13 | - title: "🧰 Maintenance" 14 | label: "Dependencies" 15 | collapse-after: 5 16 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. 17 | version-resolver: 18 | major: 19 | labels: 20 | - "X-Breaking-Change" 21 | default: minor 22 | exclude-labels: 23 | - "T-Task" 24 | - "X-Reverted" 25 | - "backport staging" 26 | exclude-contributors: 27 | - "RiotRobot" 28 | template: | 29 | $CHANGES 30 | #no-changes-template: "" 31 | prerelease: true 32 | prerelease-identifier: rc 33 | include-pre-releases: false 34 | stable-ref: master 35 | staging-ref: staging 36 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["github>matrix-org/renovate-config-element-web"] 4 | } 5 | -------------------------------------------------------------------------------- /.github/workflows/backport.yml: -------------------------------------------------------------------------------- 1 | name: Backport 2 | on: 3 | pull_request_target: 4 | types: 5 | - closed 6 | - labeled 7 | branches: 8 | - develop 9 | 10 | permissions: {} # We use ELEMENT_BOT_TOKEN instead 11 | 12 | jobs: 13 | backport: 14 | name: Backport 15 | runs-on: ubuntu-24.04 16 | # Only react to merged PRs for security reasons. 17 | # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. 18 | if: > 19 | github.event.pull_request.merged 20 | && ( 21 | github.event.action == 'closed' 22 | || ( 23 | github.event.action == 'labeled' 24 | && contains(github.event.label.name, 'backport') 25 | ) 26 | ) 27 | steps: 28 | - uses: tibdex/backport@9565281eda0731b1d20c4025c43339fb0a23812e # v2 29 | with: 30 | labels_template: "<%= JSON.stringify([...labels, 'X-Release-Blocker']) %>" 31 | # We can't use GITHUB_TOKEN here or CI won't run on the new PR 32 | github_token: ${{ secrets.ELEMENT_BOT_TOKEN }} 33 | -------------------------------------------------------------------------------- /.github/workflows/docs-pr-netlify.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy documentation PR preview 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["Static Analysis"] 6 | types: 7 | - completed 8 | permissions: {} 9 | jobs: 10 | netlify: 11 | if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' 12 | runs-on: ubuntu-24.04 13 | permissions: 14 | actions: read 15 | deployments: write 16 | steps: 17 | - name: 📥 Download artifact 18 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 19 | with: 20 | github-token: ${{ secrets.GITHUB_TOKEN }} 21 | run-id: ${{ github.event.workflow_run.id }} 22 | name: docs 23 | path: docs 24 | 25 | - name: 📤 Deploy to Netlify 26 | uses: matrix-org/netlify-pr-preview@9805cd123fc9a7e421e35340a05e1ebc5dee46b5 # v3 27 | with: 28 | path: docs 29 | owner: ${{ github.event.workflow_run.head_repository.owner.login }} 30 | branch: ${{ github.event.workflow_run.head_branch }} 31 | revision: ${{ github.event.workflow_run.head_sha }} 32 | token: ${{ secrets.NETLIFY_AUTH_TOKEN }} 33 | site_id: ${{ secrets.NETLIFY_SITE_ID }} 34 | desc: Documentation preview 35 | deployment_env: PR Documentation Preview 36 | environment: PR Documentation Preview 37 | -------------------------------------------------------------------------------- /.github/workflows/downstream-end-to-end-tests.yml: -------------------------------------------------------------------------------- 1 | # Triggers after the "Downstream artifacts" build has finished, to run the 2 | # matrix-react-sdk playwright tests (with access to repo secrets) 3 | 4 | name: matrix-react-sdk End to End Tests 5 | on: 6 | merge_group: 7 | types: [checks_requested] 8 | 9 | pull_request: {} 10 | 11 | # For now at least, we don't run this or the downstream-end-to-end-tests against pushes 12 | # to develop or master. 13 | # 14 | #push: 15 | # branches: [develop, master] 16 | permissions: {} # No permissions required 17 | concurrency: 18 | group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.run_id }} 19 | cancel-in-progress: ${{ github.event.workflow_run.event == 'pull_request' }} 20 | 21 | jobs: 22 | playwright: 23 | name: Playwright 24 | uses: element-hq/element-web/.github/workflows/end-to-end-tests.yaml@develop 25 | permissions: 26 | actions: read 27 | issues: read 28 | pull-requests: read 29 | with: 30 | matrix-js-sdk-sha: ${{ github.sha }} 31 | # We only want to run the playwright tests on merge queue to prevent regressions 32 | # from creeping in. They take a long time to run and consume multiple concurrent runners. 33 | skip: ${{ github.event_name != 'merge_group' }} 34 | -------------------------------------------------------------------------------- /.github/workflows/notify-downstream.yaml: -------------------------------------------------------------------------------- 1 | name: Notify Downstream Projects 2 | on: 3 | push: 4 | branches: [develop] 5 | concurrency: ${{ github.workflow }}-${{ github.ref }} 6 | permissions: {} # We use ELEMENT_BOT_TOKEN instead 7 | jobs: 8 | notify-downstream: 9 | # Only respect triggers from our develop branch, ignore that of forks 10 | if: github.repository == 'matrix-org/matrix-js-sdk' 11 | continue-on-error: true 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | include: 16 | - repo: element-hq/element-web 17 | event: element-web-notify 18 | 19 | runs-on: ubuntu-24.04 20 | steps: 21 | - name: Notify matrix-react-sdk repo that a new SDK build is on develop so it can CI against it 22 | uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3 23 | with: 24 | token: ${{ secrets.ELEMENT_BOT_TOKEN }} 25 | repository: ${{ matrix.repo }} 26 | event-type: ${{ matrix.event }} 27 | -------------------------------------------------------------------------------- /.github/workflows/release-checks.yml: -------------------------------------------------------------------------------- 1 | name: Release Sanity checks 2 | on: 3 | workflow_call: 4 | secrets: 5 | ELEMENT_BOT_TOKEN: 6 | required: false 7 | inputs: 8 | repository: 9 | type: string 10 | required: false 11 | default: ${{ github.repository }} 12 | description: "The repository (in form owner/repo) to check for release blockers" 13 | 14 | permissions: {} 15 | jobs: 16 | checks: 17 | name: Sanity checks 18 | runs-on: ubuntu-24.04 19 | steps: 20 | - name: Check for X-Release-Blocker label on any open issues or PRs 21 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 22 | env: 23 | REPO: ${{ inputs.repository }} 24 | with: 25 | github-token: ${{ secrets.ELEMENT_BOT_TOKEN || secrets.GITHUB_TOKEN }} 26 | script: | 27 | const { REPO } = process.env; 28 | const { data } = await github.rest.search.issuesAndPullRequests({ 29 | q: `repo:${REPO} label:X-Release-Blocker is:open`, 30 | per_page: 50, 31 | }); 32 | 33 | if (data.total_count) { 34 | data.items.forEach(item => { 35 | core.error(`Release blocker: ${item.html_url}`); 36 | }); 37 | core.setFailed(`Found release blockers!`); 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | # Generates the draft release for the js-sdk 2 | # Normally triggered whenever anything is merged to the staging branch, but 3 | # also has a workflow dispatch trigger in case it needs running manually due 4 | # to failures / workflow updates etc. 5 | name: Release Drafter 6 | on: 7 | push: 8 | branches: [staging] 9 | workflow_dispatch: {} 10 | concurrency: ${{ github.workflow }} 11 | permissions: {} 12 | jobs: 13 | draft: 14 | permissions: 15 | contents: write 16 | uses: matrix-org/matrix-js-sdk/.github/workflows/release-drafter-workflow.yml@develop 17 | -------------------------------------------------------------------------------- /.github/workflows/release-npm.yml: -------------------------------------------------------------------------------- 1 | name: Publish to npm 2 | on: 3 | workflow_call: 4 | secrets: 5 | NPM_TOKEN: 6 | required: true 7 | outputs: 8 | id: 9 | description: "The npm package@version string we published" 10 | value: ${{ jobs.npm.outputs.id }} 11 | permissions: {} 12 | jobs: 13 | npm: 14 | name: Publish to npm 15 | runs-on: ubuntu-24.04 16 | permissions: 17 | contents: read 18 | id-token: write 19 | outputs: 20 | id: ${{ steps.npm-publish.outputs.id }} 21 | steps: 22 | - name: 🧮 Checkout code 23 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 24 | with: 25 | ref: staging 26 | 27 | - name: 🔧 Yarn cache 28 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 29 | with: 30 | cache: "yarn" 31 | registry-url: "https://registry.npmjs.org" 32 | node-version-file: package.json 33 | 34 | - name: 🔨 Install dependencies 35 | run: "yarn install --frozen-lockfile" 36 | 37 | - name: 🚀 Publish to npm 38 | id: npm-publish 39 | run: | 40 | npm publish --provenance --access public --tag next 41 | release=$(jq -r '"\(.name)@\(.version)"' package.json) 42 | echo "id=$release" >> $GITHUB_OUTPUT 43 | env: 44 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 45 | 46 | - name: 🎖️ Add `latest` dist-tag to final releases 47 | if: steps.npm-publish.outputs.id && !contains(steps.npm-publish.outputs.id, '-rc.') 48 | run: npm dist-tag add "$release" latest 49 | env: 50 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 51 | release: ${{ steps.npm-publish.outputs.id }} 52 | -------------------------------------------------------------------------------- /.github/workflows/sonarqube.yml: -------------------------------------------------------------------------------- 1 | name: SonarQube 2 | on: 3 | workflow_run: 4 | workflows: ["Tests"] 5 | types: 6 | - completed 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch }} 9 | cancel-in-progress: true 10 | permissions: {} 11 | jobs: 12 | sonarqube: 13 | name: 🩻 SonarQube 14 | if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event != 'merge_group' 15 | permissions: 16 | actions: read 17 | statuses: write 18 | id-token: write # sonar 19 | uses: matrix-org/matrix-js-sdk/.github/workflows/sonarcloud.yml@develop 20 | secrets: 21 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 22 | ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} 23 | with: 24 | sharded: true 25 | -------------------------------------------------------------------------------- /.github/workflows/sync-labels.yml: -------------------------------------------------------------------------------- 1 | name: Sync labels 2 | on: 3 | workflow_dispatch: {} 4 | schedule: 5 | - cron: "0 1 * * *" # 1am every day 6 | push: 7 | branches: 8 | - develop 9 | paths: 10 | - .github/labels.yml 11 | permissions: {} # We use ELEMENT_BOT_TOKEN instead 12 | jobs: 13 | sync-labels: 14 | uses: element-hq/element-meta/.github/workflows/sync-labels.yml@develop 15 | with: 16 | LABELS: | 17 | element-hq/element-meta 18 | .github/labels.yml 19 | DELETE: true 20 | WET: true 21 | secrets: 22 | ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} 23 | -------------------------------------------------------------------------------- /.github/workflows/triage-incoming.yml: -------------------------------------------------------------------------------- 1 | name: Move new issues into Issue triage board 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | permissions: {} # We use ELEMENT_BOT_TOKEN instead 7 | jobs: 8 | automate-project-columns-next: 9 | runs-on: ubuntu-24.04 10 | steps: 11 | - uses: actions/add-to-project@main 12 | with: 13 | project-url: https://github.com/orgs/element-hq/projects/120 14 | github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} 15 | -------------------------------------------------------------------------------- /.github/workflows/triage-labelled.yml: -------------------------------------------------------------------------------- 1 | name: Move labelled issues to correct projects 2 | 3 | on: 4 | issues: 5 | types: [labeled] 6 | permissions: {} # We use ELEMENT_BOT_TOKEN instead 7 | jobs: 8 | call-triage-labelled: 9 | uses: element-hq/element-web/.github/workflows/triage-labelled.yml@develop 10 | secrets: 11 | ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} 12 | -------------------------------------------------------------------------------- /.github/workflows/triage-stale.yml: -------------------------------------------------------------------------------- 1 | name: Close stale PRs 2 | on: 3 | workflow_dispatch: {} 4 | schedule: 5 | - cron: "30 1 * * *" 6 | permissions: {} 7 | jobs: 8 | close: 9 | runs-on: ubuntu-24.04 10 | permissions: 11 | actions: write 12 | issues: write 13 | pull-requests: write 14 | steps: 15 | - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9 16 | with: 17 | operations-per-run: 250 18 | days-before-issue-stale: -1 19 | days-before-issue-close: -1 20 | days-before-pr-stale: 180 21 | days-before-pr-close: 0 22 | close-pr-message: "This PR has been automatically closed because it has been stale for 180 days. If you wish to continue working on this PR, please ping a maintainer to reopen it." 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_docs 2 | .DS_Store 3 | 4 | node_modules 5 | /.npmrc 6 | /*.log 7 | package-lock.json 8 | .lock-wscript 9 | build/Release 10 | coverage 11 | lib-cov 12 | out 13 | /dist 14 | /lib 15 | 16 | # version file and tarball created by `npm pack` / `yarn pack` 17 | /git-revision.txt 18 | /matrix-js-sdk-*.tgz 19 | 20 | .vscode 21 | .vscode/ 22 | .idea/ 23 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | npx lint-staged 4 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "*.(ts|tsx)": ["eslint --fix", "prettier --write"], 3 | "*.(py|md|yaml)": ["prettier --write"] 4 | } 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /_docs 2 | .DS_Store 3 | 4 | /.npmrc 5 | /*.log 6 | package-lock.json 7 | .lock-wscript 8 | build/Release 9 | coverage 10 | lib-cov 11 | out 12 | /dist 13 | /lib 14 | /examples/browser/lib 15 | /examples/crypto-browser/lib 16 | /examples/voip/lib 17 | 18 | # version file and tarball created by `npm pack` / `yarn pack` 19 | /git-revision.txt 20 | /matrix-js-sdk-*.tgz 21 | 22 | .vscode 23 | .vscode/ 24 | 25 | # This file is owned, parsed, and generated by allchange, which doesn't comply with prettier 26 | /CHANGELOG.md 27 | 28 | # These files are also autogenerated 29 | /spec/test-utils/test-data/index.ts 30 | /spec/test-utils/test_indexeddb_cryptostore_dump/dump.json 31 | -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = require("eslint-plugin-matrix-org/.prettierrc.js"); 2 | -------------------------------------------------------------------------------- /babel.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | sourceMaps: true, 3 | presets: [ 4 | [ 5 | "@babel/preset-env", 6 | { 7 | targets: { 8 | esmodules: true, 9 | }, 10 | // We want to output ES modules for the final build (mostly to ensure that 11 | // async imports work correctly). However, jest doesn't support ES modules very 12 | // well yet (see https://github.com/jestjs/jest/issues/9430), so we use commonjs 13 | // when testing. 14 | modules: process.env.NODE_ENV === "test" ? "commonjs" : false, 15 | }, 16 | ], 17 | [ 18 | "@babel/preset-typescript", 19 | { 20 | // When using the transpiled javascript in `lib`, Node.js requires `.js` extensions on any `import` 21 | // specifiers. However, Jest uses the TS source (via babel) and fails to resolve the `.js` names. 22 | // To resolve this,we use the `.ts` names in the source, and rewrite the `import` specifiers to use 23 | // `.js` during transpilation, *except* when we are targetting Jest. 24 | rewriteImportExtensions: process.env.NODE_ENV !== "test", 25 | }, 26 | ], 27 | ], 28 | plugins: [ 29 | ["@babel/plugin-proposal-decorators", { version: "2023-11" }], 30 | "@babel/plugin-transform-numeric-separator", 31 | "@babel/plugin-transform-class-properties", 32 | "@babel/plugin-transform-object-rest-spread", 33 | "@babel/plugin-syntax-dynamic-import", 34 | "@babel/plugin-transform-runtime", 35 | [ 36 | "search-and-replace", 37 | { 38 | // Since rewriteImportExtensions doesn't work on dynamic imports (yet), we need to manually replace 39 | // the dynamic rust-crypto import. 40 | // (see https://github.com/babel/babel/issues/16750) 41 | rules: 42 | process.env.NODE_ENV !== "test" 43 | ? [ 44 | { 45 | search: "./rust-crypto/index.ts", 46 | replace: "./rust-crypto/index.js", 47 | }, 48 | ] 49 | : [], 50 | }, 51 | ], 52 | ], 53 | }; 54 | -------------------------------------------------------------------------------- /docs/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Introduction](../README.md) 4 | 5 | # Deep dive 6 | 7 | - [Storage notes](storage-notes.md) 8 | - [Unverified devices](warning-on-unverified-devices.md) 9 | -------------------------------------------------------------------------------- /docs/storage-notes.md: -------------------------------------------------------------------------------- 1 | # Browser Storage Notes 2 | 3 | ## Overview 4 | 5 | Browsers examined: Firefox 67, Chrome 75 6 | 7 | The examination below applies to the default, non-persistent storage policy. 8 | 9 | ## Quota Measurement 10 | 11 | Browsers appear to enforce and measure the quota in terms of space on disk, not 12 | data stored, so you may be able to store more data than the simple sum of all 13 | input data depending on how compressible your data is. 14 | 15 | ## Quota Limit 16 | 17 | Specs and documentation suggest we should consistently receive 18 | `QuotaExceededError` when we're near space limits, but the reality is a bit 19 | blurrier. 20 | 21 | When we are low on disk space overall or near the group limit / origin quota: 22 | 23 | - Chrome 24 | - Log database may fail to start with AbortError 25 | - IndexedDB fails to start for crypto: AbortError in connect from 26 | indexeddb-store-worker 27 | - When near the quota, QuotaExceededError is used more consistently 28 | - Firefox 29 | - The first error will be QuotaExceededError 30 | - Future write attempts will fail with various errors when space is low, 31 | including nonsense like "InvalidStateError: A mutation operation was 32 | attempted on a database that did not allow mutations." 33 | - Once you start getting errors, the DB is effectively wedged in read-only 34 | mode 35 | - Can revive access if you reopen the DB 36 | 37 | ## Cache Eviction 38 | 39 | While the Storage Standard says all storage for an origin group should be 40 | limited by a single quota, in practice, browsers appear to handle `localStorage` 41 | separately from the others, so it has a separate quota limit and isn't evicted 42 | when low on space. 43 | 44 | - Chrome, Firefox 45 | - IndexedDB for origin deleted 46 | - Local Storage remains in place 47 | 48 | ## Persistent Storage 49 | 50 | Storage Standard offers a `navigator.storage.persist` API that can be used to 51 | request persistent storage that won't be deleted by the browser because of low 52 | space. 53 | 54 | - Chrome 55 | - Chrome 75 seems to grant this without any prompt based on [interaction 56 | criteria](https://developers.google.com/web/updates/2016/06/persistent-storage) 57 | - Firefox 58 | - Firefox 67 shows a prompt to grant 59 | - Reverting persistent seems to require revoking permission _and_ clearing 60 | site data 61 | 62 | ## Storage Estimation 63 | 64 | Storage Standard offers a `navigator.storage.estimate` API to get some clue of 65 | how much space remains. 66 | 67 | - Chrome, Firefox 68 | - Can run this at any time to request an estimate of space remaining 69 | - Firefox 70 | - Returns `0` for `usage` if a site is persisted 71 | -------------------------------------------------------------------------------- /docs/warning-on-unverified-devices.md: -------------------------------------------------------------------------------- 1 | Random notes from Matthew on the two possible approaches for warning users about unexpected 2 | unverified devices popping up in their rooms.... 3 | 4 | # Original idea... 5 | 6 | Warn when an existing user adds an unknown device to a room. 7 | 8 | Warn when a user joins the room with unverified or unknown devices. 9 | 10 | Warn when you initial sync if the room has any unverified devices in it. 11 | ^ this is good enough if we're doing local storage. 12 | OR, better: 13 | Warn when you initial sync if the room has any new undefined devices since you were last there. 14 | => This means persisting the rooms that devices are in, across initial syncs. 15 | 16 | # Updated idea... 17 | 18 | Warn when the user tries to send a message: 19 | 20 | - If the room has unverified devices which the user has not yet been told about in the context of this room 21 | ...or in the context of this user? currently all verification is per-user, not per-room. 22 | ...this should be good enough. 23 | 24 | - so track whether we have warned the user or not about unverified devices - blocked, unverified, verified, unverified_warned. 25 | throw an error when trying to encrypt if there are pure unverified devices there 26 | app will have to search for the devices which are pure unverified to warn about them - have to do this from MembersList anyway? 27 | - or megolm could warn which devices are causing the problems. 28 | 29 | Why do we wait to establish outbound sessions? It just makes a horrible pause when we first try to send a message... but could otherwise unnecessarily consume resources? 30 | -------------------------------------------------------------------------------- /examples/node/README.md: -------------------------------------------------------------------------------- 1 | This is a functional terminal app which allows you to see the room list for a user, join rooms, send messages and view room membership lists. 2 | 3 | To try it out, you will need to edit `app.js` to configure it for your `homeserver`, `access_token` and `user_id`. Then run: 4 | 5 | ``` 6 | $ npm install 7 | $ node app 8 | ``` 9 | 10 | Example output: 11 | 12 | ``` 13 | Room List: 14 | [0] Room Invite (0 members) 15 | [1] Room Invite (0 members) 16 | [2] My New Room (2 members) 17 | [3] @megan:localhost (1 members) 18 | [4] True Stuff (7 members) 19 | Global commands: 20 | '/help' : Show this help. 21 | Room list index commands: 22 | '/enter ' Enter a room, e.g. '/enter 5' 23 | Room commands: 24 | '/exit' Return to the room list index. 25 | '/members' Show the room member list. 26 | 27 | $ /enter 2 28 | 29 | [2015-06-12 15:14:54] Megan2 <<< herro 30 | [2015-06-12 15:22:58] Me >>> hey 31 | [2015-06-12 15:23:00] Me >>> whats up? 32 | [2015-06-12 15:25:40] Megan2 <<< not a lot 33 | [2015-06-12 15:25:47] Megan2 --- [State: m.room.topic updated to: {"topic":"xXx_topic_goes_here_xXx"}] 34 | [2015-06-12 15:25:55] Megan2 --- [State: m.room.name updated to: {"name":"My Newer Room"}] 35 | 36 | $ /members 37 | 38 | Membership list for room "My Newer Room" 39 | ---------------------------------------- 40 | join :: @example:localhost (Me) 41 | leave :: @fred:localhost (@fred:localhost) 42 | invite :: @earl:localhost (@earl:localhost) 43 | join :: Megan2 (@megan:localhost) 44 | invite :: @toejam:localhost (@toejam:localhost) 45 | ``` 46 | -------------------------------------------------------------------------------- /examples/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-app", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "author": "", 7 | "license": "Apache 2.0", 8 | "type": "module", 9 | "dependencies": { 10 | "cli-color": "^1.0.0", 11 | "matrix-js-sdk": "^34.5.0" 12 | }, 13 | "devDependencies": { 14 | "@types/cli-color": "^2.0.6", 15 | "typescript": "^5.6.2" 16 | }, 17 | "engines": { 18 | "node": ">=20.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/node/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "noImplicitAny": false, 7 | "noEmit": true, 8 | "skipLibCheck": true, 9 | "allowJs": true, 10 | "checkJs": true, 11 | "strict": true 12 | }, 13 | "include": ["app.js"] 14 | } 15 | -------------------------------------------------------------------------------- /examples/voip/README.md: -------------------------------------------------------------------------------- 1 | To try it out, **you must build the SDK first** and then host this folder: 2 | 3 | ``` 4 | $ npm run build 5 | $ cd examples/voip 6 | $ python -m SimpleHTTPServer 8003 7 | ``` 8 | 9 | Then visit `http://localhost:8003`. 10 | -------------------------------------------------------------------------------- /examples/voip/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | VoIP Test 4 | 5 | 6 | 7 | 8 | 9 | You can place and receive calls with this example. Make sure to edit the constants in 10 | browserTest.js first. 11 |
12 |
13 | 14 | 15 | 16 |
17 | 18 | 19 |
20 | 21 | 22 | 23 | 33 | -------------------------------------------------------------------------------- /examples/voip/lib/matrix.js: -------------------------------------------------------------------------------- 1 | ../../../dist/browser-matrix.js -------------------------------------------------------------------------------- /git-hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # pre-commit: script to run checks on a working copy before commit 4 | # 5 | # To use, symlink it into .git/hooks: 6 | # ln -s ../../git-hooks/pre-commit .git/hooks 7 | # 8 | 9 | set -e 10 | 11 | # create a temp dir 12 | tmpdir=`mktemp -d` 13 | trap 'rm -rf "$tmpdir"' EXIT 14 | 15 | # get a copy of the index 16 | git checkout-index --prefix="$tmpdir/" -a 17 | 18 | # keep node_modules/.bin on the path 19 | rootdir=`git rev-parse --show-toplevel` 20 | export PATH="$rootdir/node_modules/.bin:$PATH" 21 | 22 | # now run our checks 23 | cd "$tmpdir" 24 | yarn lint 25 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | /* Copyright 2023 The Matrix.org Foundation C.I.C. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | import type { Config } from "jest"; 17 | import { env } from "process"; 18 | 19 | const config: Config = { 20 | testEnvironment: "node", 21 | testMatch: ["/spec/**/*.spec.{js,ts}"], 22 | setupFilesAfterEnv: ["/spec/setupTests.ts"], 23 | collectCoverageFrom: ["/src/**/*.{js,ts}"], 24 | coverageReporters: ["text-summary", "lcov"], 25 | testResultsProcessor: "@casualbot/jest-sonar-reporter", 26 | 27 | // Always print out a summary if there are any failing tests. Normally 28 | // a summary is only printed if there are more than 20 test *suites*. 29 | reporters: [["default", { summaryThreshold: 0 }]], 30 | }; 31 | 32 | // if we're running under GHA, enable the GHA reporter 33 | if (env["GITHUB_ACTIONS"] !== undefined) { 34 | const reporters: Config["reporters"] = [ 35 | ["github-actions", { silent: false }], 36 | // as above: always show a summary if there were any failing tests. 37 | ["summary", { summaryThreshold: 0 }], 38 | ]; 39 | 40 | // if we're running against the develop branch, also enable the slow test reporter 41 | if (env["GITHUB_REF"] == "refs/heads/develop") { 42 | reporters.push("/spec/slowReporter.cjs"); 43 | } 44 | config.reporters = reporters; 45 | } 46 | 47 | export default config; 48 | -------------------------------------------------------------------------------- /knip.ts: -------------------------------------------------------------------------------- 1 | import { KnipConfig } from "knip"; 2 | 3 | export default { 4 | entry: [ 5 | "src/index.ts", 6 | "src/types.ts", 7 | "src/browser-index.ts", 8 | "src/indexeddb-worker.ts", 9 | "src/crypto-api/index.ts", 10 | "src/testing.ts", 11 | "src/matrix.ts", 12 | "src/utils.ts", // not really an entrypoint but we have deprecated `defer` there 13 | "scripts/**", 14 | "spec/**", 15 | // XXX: these look entirely unused 16 | "src/crypto/aes.ts", 17 | "src/crypto/crypto.ts", 18 | "src/crypto/recoverykey.ts", 19 | // XXX: these should be re-exported by one of the supported exports 20 | "src/matrixrtc/index.ts", 21 | "src/sliding-sync.ts", 22 | "src/webrtc/groupCall.ts", 23 | "src/webrtc/stats/media/mediaTrackStats.ts", 24 | "src/rendezvous/RendezvousChannel.ts", 25 | ], 26 | project: ["**/*.{js,ts}"], 27 | ignore: ["examples/**"], 28 | ignoreDependencies: [ 29 | // Required for `action-validator` 30 | "@action-validator/*", 31 | // Used for git pre-commit hooks 32 | "husky", 33 | // Used in script which only runs in environment with `@octokit/rest` installed 34 | "@octokit/rest", 35 | // Used by jest 36 | "jest-environment-jsdom", 37 | "babel-jest", 38 | "ts-node", 39 | // Used by `@babel/plugin-transform-runtime` 40 | "@babel/runtime", 41 | ], 42 | ignoreBinaries: [ 43 | // Used when available by reusable workflow `.github/workflows/release-make.yml` 44 | "dist", 45 | ], 46 | ignoreExportsUsedInFile: true, 47 | includeEntryExports: false, 48 | exclude: ["enumMembers"], 49 | } satisfies KnipConfig; 50 | -------------------------------------------------------------------------------- /scripts/changelog_head.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Outputs the body of the first entry of changelog file on stdin 5 | """ 6 | 7 | import re 8 | import sys 9 | 10 | found_first_header = False 11 | for line in sys.stdin: 12 | line = line.strip() 13 | if re.match(r"^Changes in \[.*\]", line): 14 | if found_first_header: 15 | break 16 | found_first_header = True 17 | elif not re.match(r"^=+$", line) and len(line) > 0: 18 | print(line) 19 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=matrix-js-sdk 2 | sonar.organization=matrix-org 3 | 4 | # Encoding of the source code. Default is default system encoding 5 | #sonar.sourceEncoding=UTF-8 6 | 7 | sonar.sources=src 8 | sonar.tests=spec 9 | sonar.exclusions=docs,examples,git-hooks 10 | 11 | sonar.typescript.tsconfigPath=./tsconfig.json 12 | sonar.javascript.lcov.reportPaths=coverage/lcov.info 13 | sonar.coverage.exclusions=spec/**/* 14 | sonar.testExecutionReportPaths=coverage/jest-sonar-report.xml 15 | 16 | sonar.lang.patterns.ts=**/*.ts,**/*.tsx 17 | -------------------------------------------------------------------------------- /spec/MockStorageApi.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015, 2016 OpenMarket Ltd 3 | Copyright 2019, 2022 The Matrix.org Foundation C.I.C. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | /** 19 | * A mock implementation of the webstorage api 20 | */ 21 | export class MockStorageApi { 22 | public data: Record = {}; 23 | public keys: string[] = []; 24 | public length = 0; 25 | 26 | public setItem(k: string, v: string): void { 27 | this.data[k] = v; 28 | this.recalc(); 29 | } 30 | 31 | public getItem(k: string): string | null { 32 | return this.data[k] || null; 33 | } 34 | 35 | public removeItem(k: string): void { 36 | delete this.data[k]; 37 | this.recalc(); 38 | } 39 | 40 | public key(index: number): string { 41 | return this.keys[index]; 42 | } 43 | 44 | private recalc(): void { 45 | const keys: string[] = []; 46 | for (const k in this.data) { 47 | if (!this.data.hasOwnProperty(k)) { 48 | continue; 49 | } 50 | keys.push(k); 51 | } 52 | this.keys = keys; 53 | this.length = keys.length; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /spec/olm-loader.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Vector creations Ltd 3 | Copyright 2019 The Matrix.org Foundation C.I.C. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | import { logger } from "../src/logger"; 19 | 20 | // try to load the olm library. 21 | try { 22 | // eslint-disable-next-line @typescript-eslint/no-require-imports 23 | globalThis.Olm = require("@matrix-org/olm"); 24 | logger.log("loaded libolm"); 25 | } catch (e) { 26 | logger.warn("unable to run crypto tests: libolm not available", e); 27 | } 28 | -------------------------------------------------------------------------------- /spec/setupTests.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | jest.mock("../src/http-api/utils", () => ({ 18 | ...jest.requireActual("../src/http-api/utils"), 19 | // We mock timeoutSignal otherwise it causes tests to leave timers running 20 | timeoutSignal: () => new AbortController().signal, 21 | })); 22 | 23 | // Dont make test fail too soon due to timeouts while debugging. 24 | if (process.env.VSCODE_INSPECTOR_OPTIONS) { 25 | jest.setTimeout(60 * 1000 * 5); // 5 minutes 26 | } 27 | -------------------------------------------------------------------------------- /spec/test-utils/emitter.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * Filter emitter.emit mock calls to find relevant events 19 | * eg: 20 | * ``` 21 | * const emitSpy = jest.spyOn(state, 'emit'); 22 | * << actions >> 23 | * const beaconLivenessEmits = emitCallsByEventType(BeaconEvent.New, emitSpy); 24 | * expect(beaconLivenessEmits.length).toBe(1); 25 | * ``` 26 | */ 27 | export const filterEmitCallsByEventType = (eventType: string, spy: jest.SpyInstance) => 28 | spy.mock.calls.filter((args) => args[0] === eventType); 29 | -------------------------------------------------------------------------------- /spec/test-utils/flushPromises.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Jest now uses @sinonjs/fake-timers which exposes tickAsync() and a number of 18 | // other async methods which break the event loop, letting scheduled promise 19 | // callbacks run. Unfortunately, Jest doesn't expose these, so we have to do 20 | // it manually (this is what sinon does under the hood). We do both in a loop 21 | // until the thing we expect happens: hopefully this is the least flakey way 22 | // and avoids assuming anything about the app's behaviour. 23 | const realSetTimeout = setTimeout; 24 | export function flushPromises() { 25 | return new Promise((r) => { 26 | realSetTimeout(r, 1); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /spec/test-utils/oidc.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export { makeDelegatedAuthConfig, mockOpenIdConfiguration } from "../../src/testing.ts"; 18 | -------------------------------------------------------------------------------- /spec/test-utils/test-data/.gitignore: -------------------------------------------------------------------------------- 1 | /env 2 | -------------------------------------------------------------------------------- /spec/test-utils/test_indexeddb_cryptostore_dump/README.md: -------------------------------------------------------------------------------- 1 | ## Dumps of libolm indexeddb cryptostore 2 | 3 | This directory contains several dumps of real indexeddb stores from a session using 4 | libolm crypto. 5 | 6 | Each directory contains, in dump.json, a dump of data created by pasting the following 7 | code into the browser console; and in index.ts, details of the user, pickle key, 8 | and corresponding key query and backup responses (`DumpDataSetInfo`). 9 | 10 | The dump is created by pasting the following into the browser console: 11 | 12 | ```javascript 13 | async function exportIndexedDb(name) { 14 | const db = await new Promise((resolve, reject) => { 15 | const dbReq = indexedDB.open(name); 16 | dbReq.onerror = reject; 17 | dbReq.onsuccess = () => resolve(dbReq.result); 18 | }); 19 | 20 | const storeNames = db.objectStoreNames; 21 | const exports = {}; 22 | for (const store of storeNames) { 23 | exports[store] = []; 24 | const txn = db.transaction(store, "readonly"); 25 | const objectStore = txn.objectStore(store); 26 | await new Promise((resolve, reject) => { 27 | const cursorReq = objectStore.openCursor(); 28 | cursorReq.onerror = reject; 29 | cursorReq.onsuccess = (event) => { 30 | const cursor = event.target.result; 31 | if (cursor) { 32 | const entry = { value: cursor.value }; 33 | if (!objectStore.keyPath) { 34 | entry.key = cursor.key; 35 | } 36 | exports[store].push(entry); 37 | cursor.continue(); 38 | } else { 39 | resolve(); 40 | } 41 | }; 42 | }); 43 | } 44 | return exports; 45 | } 46 | 47 | window.saveAs( 48 | new Blob([JSON.stringify(await exportIndexedDb("matrix-js-sdk:crypto"), null, 2)], { 49 | type: "application/json;charset=utf-8", 50 | }), 51 | "dump.json", 52 | ); 53 | ``` 54 | 55 | The pickle key is extracted via `mxMatrixClientPeg.get().crypto.olmDevice.pickleKey`. 56 | -------------------------------------------------------------------------------- /spec/test-utils/test_indexeddb_cryptostore_dump/empty_account/README.md: -------------------------------------------------------------------------------- 1 | ## Dump of an empty libolm indexeddb cryptostore to test skipping migration 2 | 3 | A dump of an account which is almost completely empty, and totally unsuitable 4 | for use as a real account. 5 | 6 | This dump was manually created by copying and editing full_account. 7 | 8 | Created to test 9 | ["Unable to restore session" error due due to half-initialised legacy indexeddb crypto store #27447](https://github.com/element-hq/element-web/issues/27447). 10 | We should not launch the Rust migration code when we find a DB in this state. 11 | -------------------------------------------------------------------------------- /spec/test-utils/test_indexeddb_cryptostore_dump/empty_account/dump.json: -------------------------------------------------------------------------------- 1 | { 2 | "account": [], 3 | "device_data": [], 4 | "inbound_group_sessions": [], 5 | "inbound_group_sessions_withheld": [], 6 | "notified_error_devices": [], 7 | "outgoingRoomKeyRequests": [], 8 | "parked_shared_history": [], 9 | "rooms": [], 10 | "session_problems": [], 11 | "sessions": [], 12 | "sessions_needing_backup": [], 13 | "shared_history_inbound_group_sessions": [] 14 | } 15 | -------------------------------------------------------------------------------- /spec/test-utils/test_indexeddb_cryptostore_dump/empty_account/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type DumpDataSetInfo } from "../index"; 18 | 19 | /** 20 | * A key query response containing the current keys of the tested user. 21 | * To be used during tests with fetchmock. 22 | */ 23 | const KEYS_QUERY_RESPONSE = { device_keys: { "@emptyuser:example.com": {} } }; 24 | 25 | /** 26 | * A dataset containing the information for the tested user. 27 | * To be used during tests. 28 | */ 29 | export const EMPTY_ACCOUNT_DATASET: DumpDataSetInfo = { 30 | userId: "@emptyuser:example.com", 31 | deviceId: "EMPTYDEVIC", 32 | pickleKey: "+/bcdefghijklmnopqrstu1/zyxvutsrqponmlkjih2", 33 | keyQueryResponse: KEYS_QUERY_RESPONSE, 34 | dumpPath: "spec/test-utils/test_indexeddb_cryptostore_dump/empty_account/dump.json", 35 | }; 36 | -------------------------------------------------------------------------------- /spec/test-utils/test_indexeddb_cryptostore_dump/full_account/README.md: -------------------------------------------------------------------------------- 1 | ## Dump of a libolm indexeddb cryptostore to test migration of a full account 2 | 3 | A dump of an account containing a complete set of data to migrate. 4 | The data set is substantial enough to allow for testing of chunking mechanisms and progress reporting during the migration process. 5 | -------------------------------------------------------------------------------- /spec/test-utils/test_indexeddb_cryptostore_dump/no_cached_msk_dump/README.md: -------------------------------------------------------------------------------- 1 | ## Dump of a libolm indexeddb cryptostore where the msk is not cached 2 | 3 | A dump simulating an account where the identity was verified, but the msk was not in cache. 4 | Used to test that the owner identity local trust is migrated correctly. 5 | -------------------------------------------------------------------------------- /spec/test-utils/test_indexeddb_cryptostore_dump/unverified/README.md: -------------------------------------------------------------------------------- 1 | ## Dump of a libolm indexeddb cryptostore where the identity is not trusted. 2 | 3 | A dump of an account where the identity was not verified. 4 | Used as a test case for migration of the identity local trust. 5 | -------------------------------------------------------------------------------- /spec/unit/ReEmitter.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // eslint-disable-next-line no-restricted-imports 18 | import { EventEmitter } from "events"; 19 | 20 | import { ReEmitter } from "../../src/ReEmitter"; 21 | 22 | const EVENTNAME = "UnknownEntry"; 23 | 24 | class EventSource extends EventEmitter { 25 | doTheThing() { 26 | this.emit(EVENTNAME, "foo", "bar"); 27 | } 28 | 29 | doAnError() { 30 | this.emit("error"); 31 | } 32 | } 33 | 34 | class EventTarget extends EventEmitter {} 35 | 36 | describe("ReEmitter", function () { 37 | it("Re-Emits events with the same args", function () { 38 | const src = new EventSource(); 39 | const tgt = new EventTarget(); 40 | 41 | const handler = jest.fn(); 42 | tgt.on(EVENTNAME, handler); 43 | 44 | const reEmitter = new ReEmitter(tgt); 45 | reEmitter.reEmit(src, [EVENTNAME]); 46 | 47 | src.doTheThing(); 48 | 49 | // Args should be the args passed to 'emit' after the event name, and 50 | // also the source object of the event which re-emitter adds 51 | expect(handler).toHaveBeenCalledWith("foo", "bar", src); 52 | }); 53 | 54 | it("Doesn't throw if no handler for 'error' event", function () { 55 | const src = new EventSource(); 56 | const tgt = new EventTarget(); 57 | 58 | const reEmitter = new ReEmitter(tgt); 59 | reEmitter.reEmit(src, ["error"]); 60 | 61 | // without the workaround in ReEmitter, this would throw 62 | src.doAnError(); 63 | 64 | const handler = jest.fn(); 65 | tgt.on("error", handler); 66 | 67 | src.doAnError(); 68 | 69 | // Now we've attached an error handler, it should be called 70 | expect(handler).toHaveBeenCalled(); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /spec/unit/base64.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { TextEncoder, TextDecoder } from "util"; 18 | 19 | import { decodeBase64, encodeBase64, encodeUnpaddedBase64, encodeUnpaddedBase64Url } from "../../src/base64"; 20 | 21 | describe("Base64 encoding", () => { 22 | it("Should decode properly encoded data", () => { 23 | const decoded = new TextDecoder().decode(decodeBase64("ZW5jb2RpbmcgaGVsbG8gd29ybGQ=")); 24 | 25 | expect(decoded).toStrictEqual("encoding hello world"); 26 | }); 27 | 28 | it("Should encode unpadded URL-safe base64", () => { 29 | // Chosen to have padding and multiple instances of / and + in the base64 30 | const toEncode = "???????⊕⊗⊗"; 31 | const data = new TextEncoder().encode(toEncode); 32 | 33 | const encoded = encodeUnpaddedBase64Url(data); 34 | expect(encoded).toEqual("Pz8_Pz8_P-KKleKKl-KKlw"); 35 | }); 36 | 37 | it("Should decode URL-safe base64", () => { 38 | const decoded = new TextDecoder().decode(decodeBase64("Pz8_Pz8_P-KKleKKl-KKlw==")); 39 | 40 | expect(decoded).toStrictEqual("???????⊕⊗⊗"); 41 | }); 42 | 43 | it("Encode unpadded should not have padding", () => { 44 | const toEncode = "encoding hello world"; 45 | const data = new TextEncoder().encode(toEncode); 46 | 47 | const paddedEncoded = encodeBase64(data); 48 | const unpaddedEncoded = encodeUnpaddedBase64(data); 49 | 50 | expect(paddedEncoded).not.toEqual(unpaddedEncoded); 51 | 52 | const padding = paddedEncoded.charAt(paddedEncoded.length - 1); 53 | expect(padding).toStrictEqual("="); 54 | }); 55 | 56 | it("Decode should be indifferent to padding", () => { 57 | const withPadding = "ZW5jb2RpbmcgaGVsbG8gd29ybGQ="; 58 | const withoutPadding = "ZW5jb2RpbmcgaGVsbG8gd29ybGQ"; 59 | 60 | const decodedPad = decodeBase64(withPadding); 61 | const decodedNoPad = decodeBase64(withoutPadding); 62 | 63 | expect(decodedPad).toStrictEqual(decodedNoPad); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /spec/unit/common-crypto/key-passphrase.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Matrix.org Foundation C.I.C. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { keyFromAuthData } from "../../../src/common-crypto/key-passphrase.ts"; 18 | 19 | describe("key-passphrase", () => { 20 | describe("keyFromAuthData", () => { 21 | it("should throw an error if salt or iterations are missing", async () => { 22 | // missing salt 23 | expect(() => keyFromAuthData({ private_key_iterations: 5 }, "passphrase")).toThrow( 24 | "Salt and/or iterations not found: this backup cannot be restored with a passphrase", 25 | ); 26 | 27 | // missing iterations 28 | expect(() => keyFromAuthData({ private_key_salt: "salt" }, "passphrase")).toThrow( 29 | "Salt and/or iterations not found: this backup cannot be restored with a passphrase", 30 | ); 31 | }); 32 | 33 | it("should derive key from auth data", async () => { 34 | const key = await keyFromAuthData({ private_key_salt: "salt", private_key_iterations: 5 }, "passphrase"); 35 | expect(key).toBeDefined(); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /spec/unit/crypto-api/recovery-key.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Matrix.org Foundation C.I.C. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { decodeRecoveryKey, encodeRecoveryKey } from "../../../src/crypto-api"; 18 | 19 | describe("recovery key", () => { 20 | describe("decodeRecoveryKey", () => { 21 | it("should thrown an incorrect length error", () => { 22 | const key = [0, 1]; 23 | const encodedKey = encodeRecoveryKey(key)!; 24 | 25 | expect(() => decodeRecoveryKey(encodedKey)).toThrow("Incorrect length"); 26 | }); 27 | 28 | it("should thrown an incorrect parity", () => { 29 | const key = Array.from({ length: 32 }, (_, i) => i); 30 | let encodedKey = encodeRecoveryKey(key)!; 31 | // Mutate the encoded key to have incorrect parity 32 | encodedKey = encodedKey.replace("EsSz", "EsSZ"); 33 | 34 | expect(() => decodeRecoveryKey(encodedKey!)).toThrow("Incorrect parity"); 35 | }); 36 | 37 | it("should decode a valid encoded key", () => { 38 | const key = Array.from({ length: 32 }, (_, i) => i); 39 | const encodedKey = encodeRecoveryKey(key)!; 40 | 41 | expect(decodeRecoveryKey(encodedKey)).toEqual(new Uint8Array(key)); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /spec/unit/digest.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { encodeUnpaddedBase64Url } from "../../src"; 18 | import { sha256 } from "../../src/digest"; 19 | 20 | describe("sha256", () => { 21 | it("should hash a string", async () => { 22 | const hash = await sha256("test"); 23 | expect(encodeUnpaddedBase64Url(hash)).toBe("n4bQgYhMfWWaL-qgxVrQFaO_TxsrC4Is0V1sFbDwCgg"); 24 | }); 25 | 26 | it("should hash a string with emoji", async () => { 27 | const hash = await sha256("test 🍱"); 28 | expect(encodeUnpaddedBase64Url(hash)).toBe("X2aDNrrwfq3nCTOl90R9qg9ynxhHnSzsMqtrdYX-SGw"); 29 | }); 30 | 31 | it("throws if webcrypto is not available", async () => { 32 | const oldCrypto = globalThis.crypto; 33 | try { 34 | globalThis.crypto = {} as any; 35 | await expect(sha256("test")).rejects.toThrow(); 36 | } finally { 37 | globalThis.crypto = oldCrypto; 38 | } 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /spec/unit/extensible_events_v1/ExtensibleEvent.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 - 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type ExtensibleEventType, type IPartialEvent } from "../../../src/@types/extensible_events"; 18 | import { ExtensibleEvent } from "../../../src/extensible_events_v1/ExtensibleEvent"; 19 | 20 | class MockEvent extends ExtensibleEvent { 21 | public constructor(wireEvent: IPartialEvent) { 22 | super(wireEvent); 23 | } 24 | 25 | public serialize(): IPartialEvent { 26 | throw new Error("Not implemented for tests"); 27 | } 28 | 29 | public isEquivalentTo(primaryEventType: ExtensibleEventType): boolean { 30 | throw new Error("Not implemented for tests"); 31 | } 32 | } 33 | 34 | describe("ExtensibleEvent", () => { 35 | it("should expose the wire event directly", () => { 36 | const input: IPartialEvent = { type: "org.example.custom", content: { hello: "world" } }; 37 | const event = new MockEvent(input); 38 | expect(event.wireFormat).toBe(input); 39 | expect(event.wireContent).toBe(input.content); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /spec/unit/feature.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { buildFeatureSupportMap, Feature, ServerSupport } from "../../src/feature"; 18 | 19 | describe("Feature detection", () => { 20 | it("checks the matrix version", async () => { 21 | const support = await buildFeatureSupportMap({ 22 | versions: ["v1.3"], 23 | unstable_features: {}, 24 | }); 25 | 26 | expect(support.get(Feature.Thread)).toBe(ServerSupport.Stable); 27 | expect(support.get(Feature.ThreadUnreadNotifications)).toBe(ServerSupport.Unsupported); 28 | }); 29 | 30 | it("checks the matrix msc number", async () => { 31 | const support = await buildFeatureSupportMap({ 32 | versions: ["v1.2"], 33 | unstable_features: { 34 | "org.matrix.msc3771": true, 35 | "org.matrix.msc3773": true, 36 | }, 37 | }); 38 | expect(support.get(Feature.ThreadUnreadNotifications)).toBe(ServerSupport.Unstable); 39 | }); 40 | 41 | it("requires two MSCs to pass", async () => { 42 | const support = await buildFeatureSupportMap({ 43 | versions: ["v1.2"], 44 | unstable_features: { 45 | "org.matrix.msc3771": false, 46 | "org.matrix.msc3773": true, 47 | }, 48 | }); 49 | expect(support.get(Feature.ThreadUnreadNotifications)).toBe(ServerSupport.Unsupported); 50 | }); 51 | 52 | it("requires two MSCs OR matrix versions to pass", async () => { 53 | const support = await buildFeatureSupportMap({ 54 | versions: ["v1.4"], 55 | unstable_features: { 56 | "org.matrix.msc3771": false, 57 | "org.matrix.msc3773": true, 58 | }, 59 | }); 60 | expect(support.get(Feature.ThreadUnreadNotifications)).toBe(ServerSupport.Stable); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /spec/unit/http-api/__snapshots__/index.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`MatrixHttpApi should return expected object from \`getContentUri\` 1`] = ` 4 | { 5 | "base": "http://baseUrl", 6 | "params": { 7 | "access_token": "token", 8 | }, 9 | "path": "/_matrix/media/v3/upload", 10 | } 11 | `; 12 | -------------------------------------------------------------------------------- /spec/unit/local_notifications.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type LocalNotificationSettings } from "../../src/@types/local_notifications"; 18 | import { LOCAL_NOTIFICATION_SETTINGS_PREFIX, type MatrixClient } from "../../src/matrix"; 19 | import { TestClient } from "../TestClient"; 20 | 21 | let client: MatrixClient; 22 | 23 | describe("Local notification settings", () => { 24 | beforeEach(() => { 25 | client = new TestClient("@alice:matrix.org", "123", undefined, undefined, undefined).client; 26 | client.setAccountData = jest.fn(); 27 | }); 28 | 29 | describe("Lets you set local notification settings", () => { 30 | it("stores settings in account data", () => { 31 | const deviceId = "device"; 32 | const settings: LocalNotificationSettings = { is_silenced: true }; 33 | client.setLocalNotificationSettings(deviceId, settings); 34 | 35 | expect(client.setAccountData).toHaveBeenCalledWith( 36 | `${LOCAL_NOTIFICATION_SETTINGS_PREFIX.name}.${deviceId}`, 37 | settings, 38 | ); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /spec/unit/logger.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* eslint-disable no-console */ 18 | 19 | import loglevel from "loglevel"; 20 | 21 | import { logger } from "../../src/logger.ts"; 22 | 23 | afterEach(() => { 24 | jest.restoreAllMocks(); 25 | }); 26 | 27 | describe("logger", () => { 28 | it("should log to console by default", () => { 29 | jest.spyOn(console, "debug").mockReturnValue(undefined); 30 | logger.debug("test1"); 31 | logger.log("test2"); 32 | 33 | expect(console.debug).toHaveBeenCalledWith("test1"); 34 | expect(console.debug).toHaveBeenCalledWith("test2"); 35 | }); 36 | 37 | it("should allow creation of child loggers which add a prefix", () => { 38 | jest.spyOn(loglevel, "getLogger"); 39 | jest.spyOn(console, "debug").mockReturnValue(undefined); 40 | 41 | const childLogger = logger.getChild("[prefix1]"); 42 | expect(loglevel.getLogger).toHaveBeenCalledWith("matrix-[prefix1]"); 43 | childLogger.debug("test1"); 44 | expect(console.debug).toHaveBeenCalledWith("[prefix1]", "test1"); 45 | 46 | const grandchildLogger = childLogger.getChild("[prefix2]"); 47 | expect(loglevel.getLogger).toHaveBeenCalledWith("matrix-[prefix1][prefix2]"); 48 | grandchildLogger.debug("test2"); 49 | expect(console.debug).toHaveBeenCalledWith("[prefix1][prefix2]", "test2"); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /spec/unit/matrixrtc/LivekitFocus.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { isLivekitFocus, isLivekitFocusActive, isLivekitFocusConfig } from "../../../src/matrixrtc/LivekitFocus"; 18 | 19 | describe("LivekitFocus", () => { 20 | it("isLivekitFocus", () => { 21 | expect( 22 | isLivekitFocus({ 23 | type: "livekit", 24 | livekit_service_url: "http://test.com", 25 | livekit_alias: "test", 26 | }), 27 | ).toBeTruthy(); 28 | expect(isLivekitFocus({ type: "livekit" })).toBeFalsy(); 29 | expect( 30 | isLivekitFocus({ type: "not-livekit", livekit_service_url: "http://test.com", livekit_alias: "test" }), 31 | ).toBeFalsy(); 32 | expect( 33 | isLivekitFocus({ type: "livekit", other_service_url: "http://test.com", livekit_alias: "test" }), 34 | ).toBeFalsy(); 35 | expect( 36 | isLivekitFocus({ type: "livekit", livekit_service_url: "http://test.com", other_alias: "test" }), 37 | ).toBeFalsy(); 38 | }); 39 | it("isLivekitFocusActive", () => { 40 | expect( 41 | isLivekitFocusActive({ 42 | type: "livekit", 43 | focus_selection: "oldest_membership", 44 | }), 45 | ).toBeTruthy(); 46 | expect(isLivekitFocusActive({ type: "livekit" })).toBeFalsy(); 47 | expect(isLivekitFocusActive({ type: "not-livekit", focus_selection: "oldest_membership" })).toBeFalsy(); 48 | }); 49 | it("isLivekitFocusConfig", () => { 50 | expect( 51 | isLivekitFocusConfig({ 52 | type: "livekit", 53 | livekit_service_url: "http://test.com", 54 | }), 55 | ).toBeTruthy(); 56 | expect(isLivekitFocusConfig({ type: "livekit" })).toBeFalsy(); 57 | expect(isLivekitFocusConfig({ type: "not-livekit", livekit_service_url: "http://test.com" })).toBeFalsy(); 58 | expect(isLivekitFocusConfig({ type: "livekit", other_service_url: "oldest_membership" })).toBeFalsy(); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /spec/unit/matrixrtc/memberManagerTestEnvironment.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | This file adds a custom test environment for the MembershipManager.spec.ts 19 | It can be used with the comment at the top of the file: 20 | 21 | @jest-environment ./spec/unit/matrixrtc/memberManagerTestEnvironment.ts 22 | 23 | It is very specific to the MembershipManager.spec.ts file and introduces the following behaviour: 24 | - The describe each block in the MembershipManager.spec.ts will go through describe block names `LegacyMembershipManager` and `MembershipManager` 25 | - It will check all tests that are a child or indirect child of the `LegacyMembershipManager` block and skip the ones which include "!FailsForLegacy" 26 | in their test name. 27 | */ 28 | 29 | import { TestEnvironment } from "jest-environment-jsdom"; 30 | 31 | import { logger as rootLogger } from "../../../src/logger"; 32 | const logger = rootLogger.getChild("[MatrixRTCSession]"); 33 | 34 | class MemberManagerTestEnvironment extends TestEnvironment { 35 | handleTestEvent(event: any) { 36 | if (event.name === "test_start" && event.test.name.includes("!FailsForLegacy")) { 37 | let parent = event.test.parent; 38 | let isLegacy = false; 39 | while (parent) { 40 | if (parent.name === "LegacyMembershipManager") { 41 | isLegacy = true; 42 | break; 43 | } else { 44 | parent = parent.parent; 45 | } 46 | } 47 | if (isLegacy) { 48 | logger.info("skip test: ", event.test.name); 49 | event.test.mode = "skip"; 50 | } 51 | } 52 | } 53 | } 54 | module.exports = MemberManagerTestEnvironment; 55 | -------------------------------------------------------------------------------- /spec/unit/pusher.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import MockHttpBackend from "matrix-mock-request"; 18 | 19 | import { MatrixClient, PUSHER_ENABLED } from "../../src/matrix"; 20 | import { mkPusher } from "../test-utils/test-utils"; 21 | 22 | const realSetTimeout = setTimeout; 23 | function flushPromises() { 24 | return new Promise((r) => { 25 | realSetTimeout(r, 1); 26 | }); 27 | } 28 | 29 | let client: MatrixClient; 30 | let httpBackend: MockHttpBackend; 31 | 32 | describe("Pushers", () => { 33 | beforeEach(() => { 34 | httpBackend = new MockHttpBackend(); 35 | client = new MatrixClient({ 36 | baseUrl: "https://my.home.server", 37 | accessToken: "my.access.token", 38 | fetchFn: httpBackend.fetchFn as typeof globalThis.fetch, 39 | }); 40 | }); 41 | 42 | describe("supports remotely toggling push notifications", () => { 43 | it("migration support when connecting to a legacy homeserver", async () => { 44 | httpBackend.when("GET", "/_matrix/client/versions").respond(200, { 45 | unstable_features: { 46 | "org.matrix.msc3881": false, 47 | }, 48 | }); 49 | httpBackend.when("GET", "/pushers").respond(200, { 50 | pushers: [ 51 | mkPusher(), 52 | mkPusher({ [PUSHER_ENABLED.name]: true }), 53 | mkPusher({ [PUSHER_ENABLED.name]: false }), 54 | ], 55 | }); 56 | 57 | const promise = client.getPushers(); 58 | 59 | await httpBackend.flushAllExpected(); 60 | await flushPromises(); 61 | 62 | const response = await promise; 63 | 64 | expect(response.pushers[0][PUSHER_ENABLED.name]).toBe(true); 65 | expect(response.pushers[1][PUSHER_ENABLED.name]).toBe(true); 66 | expect(response.pushers[2][PUSHER_ENABLED.name]).toBe(false); 67 | }); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /spec/unit/rust-crypto/__snapshots__/rust-crypto.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`RustCrypto importing and exporting room keys should import and export keys 1`] = ` 4 | { 5 | "algorithm": "m.megolm.v1.aes-sha2", 6 | "forwarding_curve25519_key_chain": [], 7 | "org.matrix.msc3061.shared_history": false, 8 | "room_id": "!room:id", 9 | "sender_claimed_keys": { 10 | "ed25519": "QdgHgdpDgihgovpPzUiThXur1fbErTFh7paFvNKSgN0", 11 | }, 12 | "sender_key": "WimPd2udAU/1S/+YBpPbmr9L+0H5H+BnAVHSwDxlPGc", 13 | "session_id": "FYOoKQSwe4d9jhTZ/LQCZFJINjPEqZ7Or4Z08reP92M", 14 | "session_key": "AQAAAABZ0jXQOprFfXe41tIFmAtHxflJp4O2hM/vzQQpOazOCFeWSoW5P3Z9Q+voU3eXehMwyP8/hm/Q8xLP6/PmJdy+71se/17kdFwcDGgLxBWfa4ODM9zlI4EjKbNqmiii5loJ7rBhA/XXaw80m0hfU6zTDX/KrO55J0Pt4vJ0LDa3LBWDqCkEsHuHfY4U2fy0AmRSSDYzxKmezq+GdPK3j/dj", 15 | } 16 | `; 17 | 18 | exports[`RustCrypto importing and exporting room keys should import and export keys as JSON 1`] = ` 19 | { 20 | "algorithm": "m.megolm.v1.aes-sha2", 21 | "forwarding_curve25519_key_chain": [], 22 | "org.matrix.msc3061.shared_history": false, 23 | "room_id": "!room:id", 24 | "sender_claimed_keys": { 25 | "ed25519": "QdgHgdpDgihgovpPzUiThXur1fbErTFh7paFvNKSgN0", 26 | }, 27 | "sender_key": "WimPd2udAU/1S/+YBpPbmr9L+0H5H+BnAVHSwDxlPGc", 28 | "session_id": "FYOoKQSwe4d9jhTZ/LQCZFJINjPEqZ7Or4Z08reP92M", 29 | "session_key": "AQAAAABZ0jXQOprFfXe41tIFmAtHxflJp4O2hM/vzQQpOazOCFeWSoW5P3Z9Q+voU3eXehMwyP8/hm/Q8xLP6/PmJdy+71se/17kdFwcDGgLxBWfa4ODM9zlI4EjKbNqmiii5loJ7rBhA/XXaw80m0hfU6zTDX/KrO55J0Pt4vJ0LDa3LBWDqCkEsHuHfY4U2fy0AmRSSDYzxKmezq+GdPK3j/dj", 30 | } 31 | `; 32 | -------------------------------------------------------------------------------- /spec/unit/rust-crypto/device-converter.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type DeviceKeys, DeviceVerification } from "../../../src"; 18 | import { downloadDeviceToJsDevice } from "../../../src/rust-crypto/device-converter"; 19 | 20 | describe("device-converter", () => { 21 | const userId = "@alice:example.com"; 22 | const deviceId = "xcvf"; 23 | 24 | // All parameters for QueryDevice initialization 25 | const keys = { 26 | [`ed25519:${deviceId}`]: "key1", 27 | [`curve25519:${deviceId}`]: "key2", 28 | }; 29 | const algorithms = ["algo1", "algo2"]; 30 | const signatures = { [userId]: { [deviceId]: "sign1" } }; 31 | const displayName = "display name"; 32 | const unsigned = { 33 | device_display_name: displayName, 34 | }; 35 | 36 | describe("downloadDeviceToJsDevice", () => { 37 | it("should convert a QueryDevice to a Device", () => { 38 | const queryDevice: DeviceKeys[keyof DeviceKeys] = { 39 | keys, 40 | algorithms, 41 | device_id: deviceId, 42 | user_id: userId, 43 | signatures, 44 | unsigned, 45 | }; 46 | const device = downloadDeviceToJsDevice(queryDevice); 47 | 48 | expect(device.deviceId).toBe(deviceId); 49 | expect(device.userId).toBe(userId); 50 | expect(device.verified).toBe(DeviceVerification.Unverified); 51 | expect(device.getIdentityKey()).toBe(keys[`curve25519:${deviceId}`]); 52 | expect(device.getFingerprint()).toBe(keys[`ed25519:${deviceId}`]); 53 | expect(device.displayName).toBe(displayName); 54 | }); 55 | 56 | it("should add empty signatures", () => { 57 | const queryDevice: DeviceKeys[keyof DeviceKeys] = { 58 | keys, 59 | algorithms, 60 | device_id: deviceId, 61 | user_id: userId, 62 | }; 63 | const device = downloadDeviceToJsDevice(queryDevice); 64 | 65 | expect(device.signatures.size).toBe(0); 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /spec/unit/stores/indexeddb-store-worker.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import "fake-indexeddb/auto"; 18 | 19 | import { type LocalIndexedDBStoreBackend } from "../../../src/store/indexeddb-local-backend"; 20 | import { IndexedDBStoreWorker } from "../../../src/store/indexeddb-store-worker"; 21 | 22 | function setupWorker(worker: IndexedDBStoreWorker): void { 23 | worker.onMessage({ data: { command: "setupWorker", args: [] } } as any); 24 | worker.onMessage({ data: { command: "connect", seq: 1 } } as any); 25 | } 26 | 27 | describe("IndexedDBStore Worker", () => { 28 | it("should pass 'closed' event via postMessage", async () => { 29 | const postMessageSuccessResolvers = Promise.withResolvers(); 30 | const postMessage = jest.fn().mockImplementation(({ seq, command }) => { 31 | if (seq === 1 && command === "cmd_success") { 32 | postMessageSuccessResolvers.resolve(); 33 | } 34 | }); 35 | const worker = new IndexedDBStoreWorker(postMessage); 36 | setupWorker(worker); 37 | 38 | await postMessageSuccessResolvers.promise; 39 | 40 | // @ts-ignore - private field access 41 | (worker.backend as LocalIndexedDBStoreBackend).db!.onclose!({} as Event); 42 | expect(postMessage).toHaveBeenCalledWith({ 43 | command: "closed", 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /spec/unit/thread-utils.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type IEvent } from "../../src"; 18 | import { secureRandomString } from "../../src/randomstring"; 19 | import { getRelationsThreadFilter } from "../../src/thread-utils"; 20 | 21 | function makeEvent(relatesToEvent: string, relType: string): Partial { 22 | return { 23 | event_id: secureRandomString(10), 24 | type: "m.room.message", 25 | content: { 26 | "msgtype": "m.text", 27 | "body": "foo", 28 | "m.relates_to": { 29 | rel_type: relType, 30 | event_id: relatesToEvent, 31 | }, 32 | }, 33 | }; 34 | } 35 | 36 | describe("getRelationsThreadFilter", () => { 37 | it("should filter out relations directly to the thread root event", () => { 38 | const threadId = "thisIsMyThreadRoot"; 39 | 40 | const reactionToRoot = makeEvent(threadId, "m.annotation"); 41 | const editToRoot = makeEvent(threadId, "m.replace"); 42 | const firstThreadedReply = makeEvent(threadId, "m.thread"); 43 | const reactionToThreadedEvent = makeEvent(firstThreadedReply.event_id!, "m.annotation"); 44 | 45 | const filteredEvents = [reactionToRoot, editToRoot, firstThreadedReply, reactionToThreadedEvent].filter( 46 | getRelationsThreadFilter(threadId), 47 | ); 48 | 49 | expect(filteredEvents).toEqual([firstThreadedReply, reactionToThreadedEvent]); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /spec/unit/webrtc/stats/connectionStatsReporter.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | import { ConnectionStatsBuilder } from "../../../../src/webrtc/stats/connectionStatsBuilder"; 17 | 18 | describe("ConnectionStatsReporter", () => { 19 | describe("should on bandwidth stats", () => { 20 | it("build bandwidth report if chromium starts attributes available", () => { 21 | const stats = { 22 | availableIncomingBitrate: 1000, 23 | availableOutgoingBitrate: 2000, 24 | } as RTCIceCandidatePairStats; 25 | expect(ConnectionStatsBuilder.buildBandwidthReport(stats)).toEqual({ download: 1, upload: 2 }); 26 | }); 27 | it("build empty bandwidth report if chromium starts attributes not available", () => { 28 | const stats = {} as RTCIceCandidatePairStats; 29 | expect(ConnectionStatsBuilder.buildBandwidthReport(stats)).toEqual({ download: 0, upload: 0 }); 30 | }); 31 | }); 32 | 33 | describe("should on connection stats", () => { 34 | it("build bandwidth report if chromium starts attributes available", () => { 35 | const stats = { 36 | availableIncomingBitrate: 1000, 37 | availableOutgoingBitrate: 2000, 38 | } as RTCIceCandidatePairStats; 39 | expect(ConnectionStatsBuilder.buildBandwidthReport(stats)).toEqual({ download: 1, upload: 2 }); 40 | }); 41 | it("build empty bandwidth report if chromium starts attributes not available", () => { 42 | const stats = {} as RTCIceCandidatePairStats; 43 | expect(ConnectionStatsBuilder.buildBandwidthReport(stats)).toEqual({ download: 0, upload: 0 }); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /spec/unit/webrtc/stats/media/mediaSsrcHandler.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | import { type Mid, type Ssrc, MediaSsrcHandler } from "../../../../../src/webrtc/stats/media/mediaSsrcHandler"; 17 | import { REMOTE_SFU_DESCRIPTION } from "../../../../test-utils/webrtc"; 18 | 19 | describe("MediaSsrcHandler", () => { 20 | const remoteMap = new Map([ 21 | ["0", ["2963372119"]], 22 | ["1", ["1212931603"]], 23 | ]); 24 | let handler: MediaSsrcHandler; 25 | beforeEach(() => { 26 | handler = new MediaSsrcHandler(); 27 | }); 28 | describe("should parse description", () => { 29 | it("and build mid ssrc map", async () => { 30 | handler.parse(REMOTE_SFU_DESCRIPTION, "remote"); 31 | expect(handler.getSsrcToMidMap("remote")).toEqual(remoteMap); 32 | }); 33 | }); 34 | 35 | describe("should on find mid by ssrc", () => { 36 | it("and return mid if mapping exists.", async () => { 37 | handler.parse(REMOTE_SFU_DESCRIPTION, "remote"); 38 | expect(handler.findMidBySsrc("2963372119", "remote")).toEqual("0"); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /spec/unit/webrtc/stats/valueFormatter.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | import { ValueFormatter } from "../../../../src/webrtc/stats/valueFormatter"; 17 | 18 | describe("ValueFormatter", () => { 19 | describe("on get non negative values", () => { 20 | it("formatter shod return number", async () => { 21 | expect(ValueFormatter.getNonNegativeValue("2")).toEqual(2); 22 | expect(ValueFormatter.getNonNegativeValue(0)).toEqual(0); 23 | expect(ValueFormatter.getNonNegativeValue("-2")).toEqual(0); 24 | expect(ValueFormatter.getNonNegativeValue("")).toEqual(0); 25 | expect(ValueFormatter.getNonNegativeValue(NaN)).toEqual(0); 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/@types/AESEncryptedSecretStoragePayload.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Matrix.org Foundation C.I.C. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * An AES-encrypted secret storage payload. 19 | * See https://spec.matrix.org/v1.11/client-server-api/#msecret_storagev1aes-hmac-sha2-1 20 | */ 21 | export interface AESEncryptedSecretStoragePayload { 22 | [key: string]: any; // extensible 23 | /** the initialization vector in base64 */ 24 | iv: string; 25 | /** the ciphertext in base64 */ 26 | ciphertext: string; 27 | /** the HMAC in base64 */ 28 | mac: string; 29 | } 30 | -------------------------------------------------------------------------------- /src/@types/IIdentityServerProvider.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export interface IIdentityServerProvider { 18 | /** 19 | * Gets an access token for use against the identity server, 20 | * for the associated client. 21 | * @returns Promise which resolves to the access token. 22 | */ 23 | getAccessToken(): Promise; 24 | } 25 | -------------------------------------------------------------------------------- /src/@types/another-json.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | declare module "another-json" { 18 | export function stringify(o: object): string; 19 | } 20 | -------------------------------------------------------------------------------- /src/@types/common.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export type NonEmptyArray = [T, ...T[]]; 18 | 19 | // Based on https://stackoverflow.com/a/53229857/3532235 20 | export type Without = { [P in Exclude]?: never }; 21 | export type XOR = T | U extends object ? (Without & U) | (Without & T) : T | U; 22 | export type Writeable = { -readonly [P in keyof T]: T[P] }; 23 | 24 | export type EmptyObject = Record; 25 | -------------------------------------------------------------------------------- /src/@types/crypto.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022-2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import type { ISignatures } from "./signed.ts"; 18 | 19 | // Backwards compatible re-export 20 | export type { EventDecryptionResult as IEventDecryptionResult } from "../common-crypto/CryptoBackend.ts"; 21 | 22 | interface Extensible { 23 | [key: string]: any; 24 | } 25 | 26 | /* eslint-disable camelcase */ 27 | 28 | /** The result of a call to {@link crypto-api!CryptoApi.exportRoomKeys} */ 29 | export interface IMegolmSessionData extends Extensible { 30 | /** Sender's Curve25519 device key */ 31 | sender_key: string; 32 | /** Devices which forwarded this session to us (normally empty). */ 33 | forwarding_curve25519_key_chain: string[]; 34 | /** Other keys the sender claims. */ 35 | sender_claimed_keys: Record; 36 | /** Room this session is used in */ 37 | room_id: string; 38 | /** Unique id for the session */ 39 | session_id: string; 40 | /** Base64'ed key data */ 41 | session_key: string; 42 | algorithm?: string; 43 | untrusted?: boolean; 44 | } 45 | 46 | /* eslint-enable camelcase */ 47 | 48 | /** the type of the `device_keys` parameter on `/_matrix/client/v3/keys/upload` 49 | * 50 | * @see https://spec.matrix.org/v1.5/client-server-api/#post_matrixclientv3keysupload 51 | */ 52 | export interface IDeviceKeys { 53 | algorithms: Array; 54 | device_id: string; // eslint-disable-line camelcase 55 | user_id: string; // eslint-disable-line camelcase 56 | keys: Record; 57 | signatures?: ISignatures; 58 | } 59 | 60 | /** the type of the `one_time_keys` and `fallback_keys` parameters on `/_matrix/client/v3/keys/upload` 61 | * 62 | * @see https://spec.matrix.org/v1.5/client-server-api/#post_matrixclientv3keysupload 63 | */ 64 | export interface IOneTimeKey { 65 | key: string; 66 | fallback?: boolean; 67 | signatures?: ISignatures; 68 | } 69 | -------------------------------------------------------------------------------- /src/@types/global.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export {}; 18 | 19 | declare global { 20 | // use `number` as the return type in all cases for globalThis.set{Interval,Timeout}, 21 | // so we don't accidentally use the methods on NodeJS.Timeout - they only exist in a subset of environments. 22 | // The overload for clear{Interval,Timeout} is resolved as expected. 23 | // We use `ReturnType` in the code to be agnostic of if this definition gets loaded. 24 | function setInterval(handler: TimerHandler, timeout: number, ...arguments: any[]): number; 25 | function setTimeout(handler: TimerHandler, timeout: number, ...arguments: any[]): number; 26 | 27 | namespace NodeJS { 28 | interface Global { 29 | // marker variable used to detect both the browser & node entrypoints being used at once 30 | __js_sdk_entrypoint: unknown; 31 | } 32 | } 33 | 34 | // Chrome-specific getUserMedia constraints 35 | interface MediaTrackConstraints { 36 | mandatory?: { 37 | chromeMediaSource: string; 38 | chromeMediaSourceId: string; 39 | }; 40 | } 41 | 42 | interface Navigator { 43 | // We check for the webkit-prefixed getUserMedia to detect if we're 44 | // on webkit: we should check if we still need to do this 45 | webkitGetUserMedia?: unknown; 46 | } 47 | 48 | export interface Uint8ArrayToBase64Options { 49 | alphabet?: "base64" | "base64url"; 50 | omitPadding?: boolean; 51 | } 52 | 53 | interface Uint8Array { 54 | // https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.tobase64 55 | toBase64?(options?: Uint8ArrayToBase64Options): string; 56 | } 57 | 58 | export interface Uint8ArrayFromBase64Options { 59 | alphabet?: "base64"; // Our fallback code only handles base64. 60 | lastChunkHandling?: "loose"; // Our fallback code doesn't support other handling at this time. 61 | } 62 | 63 | interface Uint8ArrayConstructor { 64 | // https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.frombase64 65 | fromBase64?(base64: string, options?: Uint8ArrayFromBase64Options): Uint8Array; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/@types/local_notifications.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export interface LocalNotificationSettings { 18 | is_silenced: boolean; 19 | } 20 | -------------------------------------------------------------------------------- /src/@types/matrix-sdk-crypto-wasm.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import type * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm"; 18 | 19 | declare module "@matrix-org/matrix-sdk-crypto-wasm" { 20 | interface SecretsBundle { 21 | // eslint-disable-next-line @typescript-eslint/naming-convention 22 | to_json(): Promise<{ 23 | cross_signing: { 24 | master_key: string; 25 | self_signing_key: string; 26 | user_signing_key: string; 27 | }; 28 | backup?: { 29 | algorithm: string; 30 | key: string; 31 | backup_version: string; 32 | }; 33 | }>; 34 | } 35 | 36 | interface Device { 37 | requestVerification(methods?: any[]): [RustSdkCryptoJs.VerificationRequest, RustSdkCryptoJs.ToDeviceRequest]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/@types/membership.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * Well-known values (from the spec or MSCs) that are allowed in the 19 | * {@link Membership} type. 20 | */ 21 | export enum KnownMembership { 22 | /** 23 | * The user has been banned from the room, and is no longer allowed to join 24 | * it until they are un-banned from the room (by having their membership 25 | * state set to a value other than ban). 26 | */ 27 | Ban = "ban", 28 | /** 29 | * The user has been invited to join a room, but has not yet joined it. 30 | * They may not participate in the room until they join. 31 | * */ 32 | Invite = "invite", 33 | /** 34 | * The user has joined the room (possibly after accepting an invite), and 35 | * may participate in it. 36 | */ 37 | Join = "join", 38 | /** 39 | * The user has knocked on the room, requesting permission to participate. 40 | * They may not participate in the room until they join. 41 | */ 42 | Knock = "knock", 43 | /** 44 | * The user was once joined to the room, but has since left (possibly by 45 | * choice, or possibly by being kicked). 46 | */ 47 | Leave = "leave", 48 | } 49 | 50 | /** 51 | * The membership state for a user in a room [1]. A value from 52 | * {@link KnownMembership} should be used where available, but all string values 53 | * are allowed to provide flexibility for upcoming spec changes or proposals. 54 | * 55 | * [1] https://spec.matrix.org/latest/client-server-api/#mroommember 56 | */ 57 | export type Membership = KnownMembership | string; 58 | -------------------------------------------------------------------------------- /src/@types/oidc-client-ts.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import "oidc-client-ts"; 18 | 19 | declare module "oidc-client-ts" { 20 | interface OidcMetadata { 21 | // Add the missing device_authorization_endpoint field to the OidcMetadata interface 22 | device_authorization_endpoint?: string; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/@types/read_receipts.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Šimon Brandner 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export enum ReceiptType { 18 | Read = "m.read", 19 | FullyRead = "m.fully_read", 20 | ReadPrivate = "m.read.private", 21 | } 22 | 23 | export const MAIN_ROOM_TIMELINE = "main"; 24 | 25 | export interface Receipt { 26 | ts: number; 27 | thread_id?: string; 28 | } 29 | 30 | export interface WrappedReceipt { 31 | eventId: string; 32 | data: Receipt; 33 | } 34 | 35 | export interface CachedReceipt { 36 | type: ReceiptType; 37 | userId: string; 38 | data: Receipt; 39 | } 40 | 41 | export type ReceiptCache = Map; 42 | 43 | export interface ReceiptContent { 44 | [eventId: string]: { 45 | [key in ReceiptType | string]: { 46 | [userId: string]: Receipt; 47 | }; 48 | }; 49 | } 50 | 51 | // We will only hold a synthetic receipt if we do not have a real receipt or the synthetic is newer. 52 | // map: receipt type → user Id → receipt 53 | export type Receipts = Map>; 54 | 55 | export type CachedReceiptStructure = { 56 | eventId: string; 57 | receiptType: string | ReceiptType; 58 | userId: string; 59 | receipt: Receipt; 60 | synthetic: boolean; 61 | }; 62 | -------------------------------------------------------------------------------- /src/@types/signed.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export interface ISignatures { 18 | [entity: string]: { 19 | [keyId: string]: string; 20 | }; 21 | } 22 | 23 | export interface ISigned { 24 | signatures?: ISignatures; 25 | } 26 | -------------------------------------------------------------------------------- /src/@types/spaces.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type IPublicRoomsChunkRoom } from "../client.ts"; 18 | import { type RoomType } from "./event.ts"; 19 | import { type IStrippedState } from "../sync-accumulator.ts"; 20 | 21 | // Types relating to Rooms of type `m.space` and related APIs 22 | 23 | /* eslint-disable camelcase */ 24 | export interface IHierarchyRelation extends IStrippedState { 25 | origin_server_ts: number; 26 | content: { 27 | order?: string; 28 | suggested?: boolean; 29 | via?: string[]; 30 | }; 31 | } 32 | 33 | export interface IHierarchyRoom extends IPublicRoomsChunkRoom { 34 | room_type?: RoomType | string; 35 | children_state: IHierarchyRelation[]; 36 | } 37 | /* eslint-enable camelcase */ 38 | -------------------------------------------------------------------------------- /src/@types/synapse.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type IdServerUnbindResult } from "./partials.ts"; 18 | 19 | // Types relating to Synapse Admin APIs 20 | 21 | /* eslint-disable camelcase */ 22 | export interface ISynapseAdminWhoisResponse { 23 | user_id: string; 24 | devices: { 25 | [deviceId: string]: { 26 | sessions: { 27 | connections: { 28 | ip: string; 29 | last_seen: number; // millis since epoch 30 | user_agent: string; 31 | }[]; 32 | }[]; 33 | }; 34 | }; 35 | } 36 | 37 | export interface ISynapseAdminDeactivateResponse { 38 | id_server_unbind_result: IdServerUnbindResult; 39 | } 40 | /* eslint-enable camelcase */ 41 | -------------------------------------------------------------------------------- /src/@types/sync.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { ServerControlledNamespacedValue } from "../NamespacedValue.ts"; 18 | 19 | /** 20 | * https://github.com/matrix-org/matrix-doc/pull/3773 21 | * 22 | * @experimental 23 | */ 24 | export const UNREAD_THREAD_NOTIFICATIONS = new ServerControlledNamespacedValue( 25 | "unread_thread_notifications", 26 | "org.matrix.msc3773.unread_thread_notifications", 27 | ); 28 | -------------------------------------------------------------------------------- /src/@types/threepids.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export enum ThreepidMedium { 18 | Email = "email", 19 | Phone = "msisdn", 20 | } 21 | 22 | // TODO: Are these types universal, or specific to just /account/3pid? 23 | export interface IThreepid { 24 | medium: ThreepidMedium; 25 | address: string; 26 | validated_at: number; // eslint-disable-line camelcase 27 | added_at: number; // eslint-disable-line camelcase 28 | bound?: boolean; 29 | } 30 | -------------------------------------------------------------------------------- /src/@types/topic.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | import { NamespacedValue } from "../NamespacedValue.ts"; 17 | import { type IMessageRendering } from "./extensible_events.ts"; 18 | 19 | /** 20 | * Extensible topic event type based on MSC3765 21 | * https://github.com/matrix-org/matrix-spec-proposals/pull/3765 22 | * 23 | * @example 24 | * ``` 25 | * { 26 | * "type": "m.room.topic, 27 | * "state_key": "", 28 | * "content": { 29 | * "topic": "All about **pizza**", 30 | * "m.topic": [{ 31 | * "body": "All about **pizza**", 32 | * "mimetype": "text/plain", 33 | * }, { 34 | * "body": "All about pizza", 35 | * "mimetype": "text/html", 36 | * }], 37 | * } 38 | * } 39 | * ``` 40 | */ 41 | 42 | /** 43 | * The event type for an m.topic event (in content) 44 | */ 45 | export const M_TOPIC = new NamespacedValue("m.topic"); 46 | 47 | /** 48 | * The event content for an m.topic event (in content) 49 | */ 50 | export type MTopicContent = IMessageRendering[]; 51 | 52 | /** 53 | * The event definition for an m.topic event (in content) 54 | */ 55 | export type MTopicEvent = { "m.topic": MTopicContent }; 56 | 57 | /** 58 | * The event content for an m.room.topic event 59 | */ 60 | export type MRoomTopicEventContent = { 61 | topic: string | null | undefined; 62 | } & Partial; 63 | -------------------------------------------------------------------------------- /src/@types/uia.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type AuthDict } from "../interactive-auth.ts"; 18 | 19 | /** 20 | * Helper type to represent HTTP request body for a UIA enabled endpoint 21 | */ 22 | export type UIARequest = T & { 23 | auth?: AuthDict; 24 | }; 25 | 26 | /** 27 | * Helper type to represent HTTP response body for a UIA enabled endpoint 28 | * @deprecated - a successful response for a UIA enabled endpoint is no different, UIA is signalled via an error 29 | */ 30 | export type UIAResponse = T; 31 | -------------------------------------------------------------------------------- /src/browser-index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import * as matrixcs from "./matrix.ts"; 18 | 19 | type BrowserMatrix = typeof matrixcs; 20 | declare global { 21 | /* eslint-disable no-var, camelcase */ 22 | var __js_sdk_entrypoint: boolean; 23 | var matrixcs: BrowserMatrix; 24 | /* eslint-enable no-var */ 25 | } 26 | 27 | if (globalThis.__js_sdk_entrypoint) { 28 | throw new Error("Multiple matrix-js-sdk entrypoints detected!"); 29 | } 30 | globalThis.__js_sdk_entrypoint = true; 31 | 32 | // just *accessing* indexedDB throws an exception in firefox with indexeddb disabled. 33 | let indexedDB: IDBFactory | undefined; 34 | try { 35 | indexedDB = globalThis.indexedDB; 36 | } catch {} 37 | 38 | // if our browser (appears to) support indexeddb, use an indexeddb crypto store. 39 | if (indexedDB) { 40 | matrixcs.setCryptoStoreFactory(() => new matrixcs.IndexedDBCryptoStore(indexedDB!, "matrix-js-sdk:crypto")); 41 | } 42 | 43 | export * from "./matrix.ts"; 44 | globalThis.matrixcs = matrixcs; 45 | -------------------------------------------------------------------------------- /src/common-crypto/README.md: -------------------------------------------------------------------------------- 1 | This directory contains functionality which is common to both the legacy (libolm-based) crypto implementation, 2 | and the new rust-based implementation. 3 | 4 | It is an internal module, and is _not_ directly exposed to applications. 5 | -------------------------------------------------------------------------------- /src/common-crypto/key-passphrase.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Matrix.org Foundation C.I.C. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { deriveRecoveryKeyFromPassphrase } from "../crypto-api/index.ts"; 18 | 19 | /* eslint-disable camelcase */ 20 | interface IAuthData { 21 | private_key_salt?: string; 22 | private_key_iterations?: number; 23 | private_key_bits?: number; 24 | } 25 | 26 | /** 27 | * Derive a backup key from a passphrase using the salt and iterations from the auth data. 28 | * @param authData - The auth data containing the salt and iterations 29 | * @param passphrase - The passphrase to derive the key from 30 | * @deprecated Deriving a backup key from a passphrase is not part of the matrix spec. Instead, a random key is generated and stored/shared via 4S. 31 | */ 32 | export function keyFromAuthData(authData: IAuthData, passphrase: string): Promise { 33 | if (!authData.private_key_salt || !authData.private_key_iterations) { 34 | throw new Error("Salt and/or iterations not found: " + "this backup cannot be restored with a passphrase"); 35 | } 36 | 37 | return deriveRecoveryKeyFromPassphrase( 38 | passphrase, 39 | authData.private_key_salt, 40 | authData.private_key_iterations, 41 | authData.private_key_bits, 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /src/crypto-api/CryptoEventHandlerMap.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Matrix.org Foundation C.I.C. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { type CryptoEvent } from "./CryptoEvent.ts"; 18 | import { type VerificationRequest } from "./verification.ts"; 19 | import { type UserVerificationStatus } from "./index.ts"; 20 | import { type RustBackupCryptoEventMap } from "../rust-crypto/backup.ts"; 21 | import { type EmptyObject } from "../@types/common.ts"; 22 | 23 | /** 24 | * A map of the {@link CryptoEvent} fired by the {@link CryptoApi} and their payloads. 25 | */ 26 | export type CryptoEventHandlerMap = { 27 | [CryptoEvent.VerificationRequestReceived]: (request: VerificationRequest) => void; 28 | [CryptoEvent.UserTrustStatusChanged]: (userId: string, userTrustLevel: UserVerificationStatus) => void; 29 | [CryptoEvent.KeyBackupDecryptionKeyCached]: (version: string) => void; 30 | [CryptoEvent.KeysChanged]: (data: EmptyObject) => void; 31 | [CryptoEvent.WillUpdateDevices]: (users: string[], initialFetch: boolean) => void; 32 | [CryptoEvent.DevicesUpdated]: (users: string[], initialFetch: boolean) => void; 33 | [CryptoEvent.LegacyCryptoStoreMigrationProgress]: (progress: number, total: number) => void; 34 | [CryptoEvent.DehydratedDeviceCreated]: () => void; 35 | [CryptoEvent.DehydratedDeviceUploaded]: () => void; 36 | [CryptoEvent.RehydrationStarted]: () => void; 37 | [CryptoEvent.RehydrationProgress]: (roomKeyCount: number, toDeviceCount: number) => void; 38 | [CryptoEvent.RehydrationCompleted]: () => void; 39 | [CryptoEvent.RehydrationError]: (msg: string) => void; 40 | [CryptoEvent.DehydrationKeyCached]: () => void; 41 | [CryptoEvent.DehydratedDeviceRotationError]: (msg: string) => void; 42 | } & RustBackupCryptoEventMap; 43 | -------------------------------------------------------------------------------- /src/crypto-api/key-passphrase.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Matrix.org Foundation C.I.C. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const DEFAULT_BIT_SIZE = 256; 18 | 19 | /** 20 | * Derive a recovery key from a passphrase and salt using PBKDF2. 21 | * @see https://spec.matrix.org/v1.11/client-server-api/#deriving-keys-from-passphrases 22 | * 23 | * @param passphrase - The passphrase to derive the key from 24 | * @param salt - The salt to use in the derivation 25 | * @param iterations - The number of iterations to use in the derivation 26 | * @param numBits - The number of bits to derive 27 | */ 28 | export async function deriveRecoveryKeyFromPassphrase( 29 | passphrase: string, 30 | salt: string, 31 | iterations: number, 32 | numBits = DEFAULT_BIT_SIZE, 33 | ): Promise { 34 | if (!globalThis.crypto.subtle || !TextEncoder) { 35 | throw new Error("Password-based backup is not available on this platform"); 36 | } 37 | 38 | const key = await globalThis.crypto.subtle.importKey( 39 | "raw", 40 | new TextEncoder().encode(passphrase), 41 | { name: "PBKDF2" }, 42 | false, 43 | ["deriveBits"], 44 | ); 45 | 46 | const keybits = await globalThis.crypto.subtle.deriveBits( 47 | { 48 | name: "PBKDF2", 49 | salt: new TextEncoder().encode(salt), 50 | iterations: iterations, 51 | hash: "SHA-512", 52 | }, 53 | key, 54 | numBits, 55 | ); 56 | 57 | return new Uint8Array(keybits); 58 | } 59 | -------------------------------------------------------------------------------- /src/crypto-api/recovery-key.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Matrix.org Foundation C.I.C. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import bs58 from "bs58"; 18 | 19 | // picked arbitrarily but to try & avoid clashing with any bitcoin ones 20 | // (which are also base58 encoded, but bitcoin's involve a lot more hashing) 21 | const OLM_RECOVERY_KEY_PREFIX = [0x8b, 0x01]; 22 | const KEY_SIZE = 32; 23 | 24 | /** 25 | * Encode a recovery key using the Matrix {@link https://spec.matrix.org/v1.11/appendices/#cryptographic-key-representation | Cryptographic key representation} 26 | * @param key 27 | */ 28 | export function encodeRecoveryKey(key: ArrayLike): string | undefined { 29 | const buf = new Uint8Array(OLM_RECOVERY_KEY_PREFIX.length + key.length + 1); 30 | buf.set(OLM_RECOVERY_KEY_PREFIX, 0); 31 | buf.set(key, OLM_RECOVERY_KEY_PREFIX.length); 32 | 33 | let parity = 0; 34 | for (let i = 0; i < buf.length - 1; ++i) { 35 | parity ^= buf[i]; 36 | } 37 | buf[buf.length - 1] = parity; 38 | const base58key = bs58.encode(buf); 39 | 40 | return base58key.match(/.{1,4}/g)?.join(" "); 41 | } 42 | 43 | /** 44 | * Decode a recovery key encoded with the Matrix {@link https://spec.matrix.org/v1.11/appendices/#cryptographic-key-representation | Cryptographic key representation} encoding. 45 | * @param recoveryKey 46 | */ 47 | export function decodeRecoveryKey(recoveryKey: string): Uint8Array { 48 | const result = bs58.decode(recoveryKey.replace(/ /g, "")); 49 | 50 | let parity = 0; 51 | for (const b of result) { 52 | parity ^= b; 53 | } 54 | if (parity !== 0) { 55 | throw new Error("Incorrect parity"); 56 | } 57 | 58 | for (let i = 0; i < OLM_RECOVERY_KEY_PREFIX.length; ++i) { 59 | if (result[i] !== OLM_RECOVERY_KEY_PREFIX[i]) { 60 | throw new Error("Incorrect prefix"); 61 | } 62 | } 63 | 64 | if (result.length !== OLM_RECOVERY_KEY_PREFIX.length + KEY_SIZE + 1) { 65 | throw new Error("Incorrect length"); 66 | } 67 | 68 | return Uint8Array.from(result.slice(OLM_RECOVERY_KEY_PREFIX.length, OLM_RECOVERY_KEY_PREFIX.length + KEY_SIZE)); 69 | } 70 | -------------------------------------------------------------------------------- /src/digest.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * Computes a SHA-256 hash of a string (after utf-8 encoding) and returns it as an ArrayBuffer. 19 | * 20 | * @param plaintext The string to hash 21 | * @returns An Uint8Array containing the SHA-256 hash of the input string 22 | * @throws If the subtle crypto API is not available, for example if the code is running 23 | * in a web page with an insecure context (eg. served over plain HTTP). 24 | */ 25 | export async function sha256(plaintext: string): Promise { 26 | if (!globalThis.crypto.subtle) { 27 | throw new Error("Crypto.subtle is not available: insecure context?"); 28 | } 29 | const utf8 = new TextEncoder().encode(plaintext); 30 | 31 | const digest = await globalThis.crypto.subtle.digest("SHA-256", utf8); 32 | 33 | return new Uint8Array(digest); 34 | } 35 | -------------------------------------------------------------------------------- /src/errors.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export enum InvalidCryptoStoreState { 18 | TooNew = "TOO_NEW", 19 | } 20 | 21 | export class InvalidCryptoStoreError extends Error { 22 | public static TOO_NEW = InvalidCryptoStoreState.TooNew; 23 | 24 | public constructor(public readonly reason: InvalidCryptoStoreState) { 25 | const message = 26 | `Crypto store is invalid because ${reason}, ` + 27 | `please stop the client, delete all data and start the client again`; 28 | super(message); 29 | this.name = "InvalidCryptoStoreError"; 30 | } 31 | } 32 | 33 | export class KeySignatureUploadError extends Error { 34 | public constructor( 35 | message: string, 36 | public readonly value: any, 37 | ) { 38 | super(message); 39 | } 40 | } 41 | 42 | /** 43 | * It is invalid to call most methods once {@link MatrixClient#stopClient} has been called. 44 | * 45 | * This error will be thrown if you attempt to do so. 46 | * 47 | * {@link MatrixClient#stopClient} itself is an exception to this: it may safely be called multiple times on the same 48 | * instance. 49 | */ 50 | export class ClientStoppedError extends Error { 51 | public constructor() { 52 | super("MatrixClient has been stopped"); 53 | } 54 | } 55 | 56 | /** 57 | * This error is thrown when the Homeserver does not support the delayed events enpdpoints. 58 | */ 59 | export class UnsupportedDelayedEventsEndpointError extends Error { 60 | public constructor( 61 | message: string, 62 | public clientEndpoint: "sendDelayedEvent" | "updateDelayedEvent" | "sendDelayedStateEvent" | "getDelayedEvents", 63 | ) { 64 | super(message); 65 | this.name = "UnsupportedDelayedEventsEndpointError"; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/extensible_events_v1/ExtensibleEvent.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 - 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type ExtensibleEventType, type IPartialEvent } from "../@types/extensible_events.ts"; 18 | 19 | /** 20 | * Represents an Extensible Event in Matrix. 21 | */ 22 | export abstract class ExtensibleEvent { 23 | protected constructor(public readonly wireFormat: IPartialEvent) {} 24 | 25 | /** 26 | * Shortcut to wireFormat.content 27 | */ 28 | public get wireContent(): TContent { 29 | return this.wireFormat.content; 30 | } 31 | 32 | /** 33 | * Serializes the event into a format which can be used to send the 34 | * event to the room. 35 | * @returns The serialized event. 36 | */ 37 | public abstract serialize(): IPartialEvent; 38 | 39 | /** 40 | * Determines if this event is equivalent to the provided event type. 41 | * This is recommended over `instanceof` checks due to issues in the JS 42 | * runtime (and layering of dependencies in some projects). 43 | * 44 | * Implementations should pass this check off to their super classes 45 | * if their own checks fail. Some primary implementations do not extend 46 | * fallback classes given they support the primary type first. Thus, 47 | * those classes may return false if asked about their fallback 48 | * representation. 49 | * 50 | * Note that this only checks primary event types: legacy events, like 51 | * m.room.message, should/will fail this check. 52 | * @param primaryEventType - The (potentially namespaced) event 53 | * type. 54 | * @returns True if this event *could* be represented as the 55 | * given type. 56 | */ 57 | public abstract isEquivalentTo(primaryEventType: ExtensibleEventType): boolean; 58 | } 59 | -------------------------------------------------------------------------------- /src/extensible_events_v1/InvalidEventError.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 - 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * Thrown when an event is unforgivably unparsable. 19 | */ 20 | export class InvalidEventError extends Error { 21 | public constructor(message: string) { 22 | super(message); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/extensible_events_v1/utilities.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 - 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type Optional } from "matrix-events-sdk"; 18 | 19 | /** 20 | * Determines if the given optional was provided a value. 21 | * @param s - The optional to test. 22 | * @returns True if the value is defined. 23 | */ 24 | export function isProvided(s: Optional): s is T { 25 | return s !== null && s !== undefined; 26 | } 27 | 28 | /** 29 | * Determines if the given optional string is a defined string. 30 | * @param s - The input string. 31 | * @returns True if the input is a defined string. 32 | */ 33 | export function isOptionalAString(s: Optional): s is string { 34 | return isProvided(s) && typeof s === "string"; 35 | } 36 | -------------------------------------------------------------------------------- /src/http-api/method.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export enum Method { 18 | Get = "GET", 19 | Put = "PUT", 20 | Post = "POST", 21 | Delete = "DELETE", 22 | Options = "OPTIONS", 23 | Head = "HEAD", 24 | Patch = "PATCH", 25 | } 26 | -------------------------------------------------------------------------------- /src/http-api/prefix.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export enum ClientPrefix { 18 | /** 19 | * A constant representing the URI path for Client-Server API endpoints versioned at v1. 20 | */ 21 | V1 = "/_matrix/client/v1", 22 | /** 23 | * A constant representing the URI path for Client-Server API endpoints versioned at v3. 24 | */ 25 | V3 = "/_matrix/client/v3", 26 | /** 27 | * A constant representing the URI path for as-yet unspecified Client-Server HTTP APIs. 28 | */ 29 | Unstable = "/_matrix/client/unstable", 30 | } 31 | 32 | export enum IdentityPrefix { 33 | /** 34 | * URI path for the v2 identity API 35 | */ 36 | V2 = "/_matrix/identity/v2", 37 | } 38 | 39 | export enum MediaPrefix { 40 | /** 41 | * A constant representing the URI path for Client-Server API Media endpoints versioned at v1. 42 | */ 43 | V1 = "/_matrix/media/v1", 44 | /** 45 | * A constant representing the URI path for Client-Server API Media endpoints versioned at v3. 46 | */ 47 | V3 = "/_matrix/media/v3", 48 | } 49 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import * as matrixcs from "./matrix.ts"; 18 | 19 | if (globalThis.__js_sdk_entrypoint) { 20 | throw new Error("Multiple matrix-js-sdk entrypoints detected!"); 21 | } 22 | globalThis.__js_sdk_entrypoint = true; 23 | 24 | export * from "./matrix.ts"; 25 | export default matrixcs; 26 | -------------------------------------------------------------------------------- /src/indexeddb-helpers.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 New Vector Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * Check if an IndexedDB database exists. The only way to do so is to try opening it, so 19 | * we do that and then delete it did not exist before. 20 | * 21 | * @param indexedDB - The `indexedDB` interface 22 | * @param dbName - The database name to test for 23 | * @returns Whether the database exists 24 | */ 25 | export function exists(indexedDB: IDBFactory, dbName: string): Promise { 26 | return new Promise((resolve, reject) => { 27 | let exists = true; 28 | const req = indexedDB.open(dbName); 29 | req.onupgradeneeded = (): void => { 30 | // Since we did not provide an explicit version when opening, this event 31 | // should only fire if the DB did not exist before at any version. 32 | exists = false; 33 | }; 34 | req.onblocked = (): void => reject(req.error); 35 | req.onsuccess = (): void => { 36 | const db = req.result; 37 | db.close(); 38 | if (!exists) { 39 | // The DB did not exist before, but has been created as part of this 40 | // existence check. Delete it now to restore previous state. Delete can 41 | // actually take a while to complete in some browsers, so don't wait for 42 | // it. This won't block future open calls that a store might issue next to 43 | // properly set up the DB. 44 | indexedDB.deleteDatabase(dbName); 45 | } 46 | resolve(exists); 47 | }; 48 | req.onerror = (): void => reject(req.error); 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /src/indexeddb-worker.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Vector Creations Ltd 3 | Copyright 2019 The Matrix.org Foundation C.I.C. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | /** 19 | * Separate exports file for the indexeddb web worker, which is designed 20 | * to be used separately 21 | */ 22 | 23 | /** The {@link IndexedDBStoreWorker} class. */ 24 | export { IndexedDBStoreWorker } from "./store/indexeddb-store-worker.ts"; 25 | -------------------------------------------------------------------------------- /src/matrixrtc/IKeyTransport.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type CallMembership } from "./CallMembership.ts"; 18 | 19 | export enum KeyTransportEvents { 20 | ReceivedKeys = "received_keys", 21 | NotSupportedError = "not_supported_error", 22 | } 23 | 24 | export type KeyTransportEventsHandlerMap = { 25 | [KeyTransportEvents.ReceivedKeys]: KeyTransportEventListener; 26 | [KeyTransportEvents.NotSupportedError]: () => void; 27 | }; 28 | 29 | export type KeyTransportEventListener = ( 30 | userId: string, 31 | deviceId: string, 32 | keyBase64Encoded: string, 33 | index: number, 34 | timestamp: number, 35 | ) => void; 36 | 37 | /** 38 | * Generic interface for the transport used to share room keys. 39 | * Keys can be shared using different transports, e.g. to-device messages or room messages. 40 | */ 41 | export interface IKeyTransport { 42 | /** 43 | * Sends the current user media key to the given members. 44 | * @param keyBase64Encoded 45 | * @param index 46 | * @param members - The participants that should get they key 47 | */ 48 | sendKey(keyBase64Encoded: string, index: number, members: CallMembership[]): Promise; 49 | 50 | /** Subscribe to keys from this transport. */ 51 | on(event: KeyTransportEvents.ReceivedKeys, listener: KeyTransportEventListener): this; 52 | /** Unsubscribe from keys from this transport. */ 53 | off(event: KeyTransportEvents.ReceivedKeys, listener: KeyTransportEventListener): this; 54 | 55 | /** Once start is called the underlying transport will subscribe to its transport system. 56 | * Before start is called this transport will not emit any events. 57 | */ 58 | start(): void; 59 | /** Once stop is called the underlying transport will unsubscribe from its transport system. 60 | * After stop is called this transport will not emit any events. 61 | */ 62 | stop(): void; 63 | } 64 | -------------------------------------------------------------------------------- /src/matrixrtc/LivekitFocus.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 New Vector Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type Focus } from "./focus.ts"; 18 | 19 | export interface LivekitFocusConfig extends Focus { 20 | type: "livekit"; 21 | livekit_service_url: string; 22 | } 23 | 24 | export const isLivekitFocusConfig = (object: any): object is LivekitFocusConfig => 25 | object.type === "livekit" && "livekit_service_url" in object; 26 | 27 | export interface LivekitFocus extends LivekitFocusConfig { 28 | livekit_alias: string; 29 | } 30 | 31 | export const isLivekitFocus = (object: any): object is LivekitFocus => 32 | isLivekitFocusConfig(object) && "livekit_alias" in object; 33 | 34 | export interface LivekitFocusActive extends Focus { 35 | type: "livekit"; 36 | focus_selection: "oldest_membership"; 37 | } 38 | export const isLivekitFocusActive = (object: any): object is LivekitFocusActive => 39 | object.type === "livekit" && "focus_selection" in object; 40 | -------------------------------------------------------------------------------- /src/matrixrtc/focus.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * Information about a MatrixRTC conference focus. The only attribute that 19 | * the js-sdk (currently) knows about is the type: applications can extend 20 | * this class for different types of focus. 21 | */ 22 | export interface Focus { 23 | type: string; 24 | [key: string]: unknown; 25 | } 26 | -------------------------------------------------------------------------------- /src/matrixrtc/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export * from "./CallMembership.ts"; 18 | export type * from "./focus.ts"; 19 | export * from "./LivekitFocus.ts"; 20 | export * from "./MatrixRTCSession.ts"; 21 | export * from "./MatrixRTCSessionManager.ts"; 22 | export type * from "./types.ts"; 23 | export { Status } from "./types.ts"; 24 | export { MembershipManagerEvent } from "./IMembershipManager.ts"; 25 | -------------------------------------------------------------------------------- /src/models/ToDeviceMessage.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export type ToDevicePayload = Record; 18 | 19 | export interface ToDeviceMessage { 20 | userId: string; 21 | deviceId: string; 22 | payload: ToDevicePayload; 23 | } 24 | 25 | export interface ToDeviceBatch { 26 | eventType: string; 27 | batch: ToDeviceMessage[]; 28 | } 29 | 30 | // Only used internally 31 | export interface ToDeviceBatchWithTxnId extends ToDeviceBatch { 32 | txnId: string; 33 | } 34 | 35 | // Only used internally 36 | export interface IndexedToDeviceBatch extends ToDeviceBatchWithTxnId { 37 | id: number; 38 | } 39 | -------------------------------------------------------------------------------- /src/models/event-status.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 - 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * Enum for event statuses. 19 | * @readonly 20 | */ 21 | export enum EventStatus { 22 | /** The event was not sent and will no longer be retried. */ 23 | NOT_SENT = "not_sent", 24 | 25 | /** The message is being encrypted */ 26 | ENCRYPTING = "encrypting", 27 | 28 | /** The event is in the process of being sent. */ 29 | SENDING = "sending", 30 | 31 | /** The event is in a queue waiting to be sent. */ 32 | QUEUED = "queued", 33 | 34 | /** The event has been sent to the server, but we have not yet received the echo. */ 35 | SENT = "sent", 36 | 37 | /** The event was cancelled before it was successfully sent. */ 38 | CANCELLED = "cancelled", 39 | } 40 | -------------------------------------------------------------------------------- /src/models/invites-ignorer-types.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { UnstableValue } from "matrix-events-sdk"; 18 | 19 | /// The event type storing the user's individual policies. 20 | /// 21 | /// Exported for testing purposes. 22 | export const POLICIES_ACCOUNT_EVENT_TYPE = new UnstableValue("m.policies", "org.matrix.msc3847.policies"); 23 | 24 | /// The key within the user's individual policies storing the user's ignored invites. 25 | /// 26 | /// Exported for testing purposes. 27 | export const IGNORE_INVITES_ACCOUNT_EVENT_KEY = new UnstableValue( 28 | "m.ignore.invites", 29 | "org.matrix.msc3847.ignore.invites", 30 | ); 31 | 32 | /// The types of recommendations understood. 33 | export enum PolicyRecommendation { 34 | Ban = "m.ban", 35 | } 36 | 37 | /** 38 | * The various scopes for policies. 39 | */ 40 | export enum PolicyScope { 41 | /** 42 | * The policy deals with an individual user, e.g. reject invites 43 | * from this user. 44 | */ 45 | User = "m.policy.user", 46 | 47 | /** 48 | * The policy deals with a room, e.g. reject invites towards 49 | * a specific room. 50 | */ 51 | Room = "m.policy.room", 52 | 53 | /** 54 | * The policy deals with a server, e.g. reject invites from 55 | * this server. 56 | */ 57 | Server = "m.policy.server", 58 | } 59 | -------------------------------------------------------------------------------- /src/models/profile-keys.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The timezone the user is currently in. The value of this property should 3 | * match a timezone provided in https://www.iana.org/time-zones. 4 | * 5 | * @see https://github.com/matrix-org/matrix-spec-proposals/blob/clokep/profile-tz/proposals/4175-profile-field-time-zone.md 6 | * @experimental 7 | */ 8 | export const ProfileKeyMSC4175Timezone = "us.cloke.msc4175.tz"; 9 | -------------------------------------------------------------------------------- /src/models/related-relations.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type Relations, type RelationsEvent, type EventHandlerMap } from "./relations.ts"; 18 | import { type MatrixEvent } from "./event.ts"; 19 | import { type Listener } from "./typed-event-emitter.ts"; 20 | 21 | export class RelatedRelations { 22 | private relations: Relations[]; 23 | 24 | public constructor(relations: Relations[]) { 25 | this.relations = relations.filter((r) => !!r); 26 | } 27 | 28 | public getRelations(): MatrixEvent[] { 29 | return this.relations.reduce((c, p) => [...c, ...p.getRelations()], []); 30 | } 31 | 32 | public on(ev: T, fn: Listener): void { 33 | this.relations.forEach((r) => r.on(ev, fn)); 34 | } 35 | 36 | public off(ev: T, fn: Listener): void { 37 | this.relations.forEach((r) => r.off(ev, fn)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/models/room-summary.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * A stripped m.room.member event which contains the key renderable fields from the event, 19 | * sent only in simplified sliding sync (not `/v3/sync`). 20 | * This is very similar to MSC4186Hero from sliding-sync.ts but an internal format with 21 | * camelCase rather than underscores. 22 | */ 23 | export type Hero = { 24 | userId: string; 25 | displayName?: string; 26 | avatarUrl?: string; 27 | /** 28 | * If true, the hero is from an MSC4186 summary, in which case `displayName` and `avatarUrl` will 29 | * have been set by the server if available. If false, the `Hero` has been constructed from a `/v3/sync` response, 30 | * so these fields will always be undefined. 31 | */ 32 | fromMSC4186: boolean; 33 | }; 34 | 35 | /** 36 | * High level summary information for a room, as returned by `/v3/sync`. 37 | */ 38 | export interface IRoomSummary { 39 | /** 40 | * The room heroes: a selected set of members that can be used when summarising or 41 | * generating a name for a room. List of user IDs. 42 | */ 43 | "m.heroes": string[]; 44 | /** 45 | * The number of joined members in the room. 46 | */ 47 | "m.joined_member_count"?: number; 48 | /** 49 | * The number of invited members in the room. 50 | */ 51 | "m.invited_member_count"?: number; 52 | } 53 | 54 | interface IInfo { 55 | /** The title of the room (e.g. `m.room.name`) */ 56 | title: string; 57 | /** The description of the room (e.g. `m.room.topic`) */ 58 | desc?: string; 59 | /** The number of joined users. */ 60 | numMembers?: number; 61 | /** The list of aliases for this room. */ 62 | aliases?: string[]; 63 | /** The timestamp for this room. */ 64 | timestamp?: number; 65 | } 66 | 67 | /** 68 | * Construct a new Room Summary. A summary can be used for display on a recent 69 | * list, without having to load the entire room list into memory. 70 | * @param roomId - Required. The ID of this room. 71 | * @param info - Optional. The summary info. Additional keys are supported. 72 | */ 73 | export class RoomSummary { 74 | public constructor( 75 | public readonly roomId: string, 76 | info?: IInfo, 77 | ) {} 78 | } 79 | -------------------------------------------------------------------------------- /src/models/search-result.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { EventContext } from "./event-context.ts"; 18 | import { type EventMapper } from "../event-mapper.ts"; 19 | import { type IResultContext, type ISearchResult } from "../@types/search.ts"; 20 | 21 | export class SearchResult { 22 | /** 23 | * Create a SearchResponse from the response to /search 24 | */ 25 | 26 | public static fromJson(jsonObj: ISearchResult, eventMapper: EventMapper): SearchResult { 27 | const jsonContext = jsonObj.context || ({} as IResultContext); 28 | let eventsBefore = (jsonContext.events_before || []).map(eventMapper); 29 | let eventsAfter = (jsonContext.events_after || []).map(eventMapper); 30 | 31 | const context = new EventContext(eventMapper(jsonObj.result)); 32 | 33 | // Filter out any contextual events which do not correspond to the same timeline (thread or room) 34 | const threadRootId = context.ourEvent.threadRootId; 35 | eventsBefore = eventsBefore.filter((e) => e.threadRootId === threadRootId); 36 | eventsAfter = eventsAfter.filter((e) => e.threadRootId === threadRootId); 37 | 38 | context.setPaginateToken(jsonContext.start, true); 39 | context.addEvents(eventsBefore, true); 40 | context.addEvents(eventsAfter, false); 41 | context.setPaginateToken(jsonContext.end, false); 42 | 43 | return new SearchResult(jsonObj.rank, context); 44 | } 45 | 46 | /** 47 | * Construct a new SearchResult 48 | * 49 | * @param rank - where this SearchResult ranks in the results 50 | * @param context - the matching event and its 51 | * context 52 | */ 53 | public constructor( 54 | public readonly rank: number, 55 | public readonly context: EventContext, 56 | ) {} 57 | } 58 | -------------------------------------------------------------------------------- /src/oidc/error.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * Errors expected to be encountered during OIDC discovery, client registration, and authentication. 19 | * Not intended to be displayed directly to the user. 20 | */ 21 | export enum OidcError { 22 | NotSupported = "OIDC authentication not supported", 23 | Misconfigured = "OIDC is misconfigured", 24 | General = "Something went wrong with OIDC discovery", 25 | OpSupport = "Configured OIDC OP does not support required functions", 26 | DynamicRegistrationNotSupported = "Dynamic registration not supported", 27 | DynamicRegistrationFailed = "Dynamic registration failed", 28 | DynamicRegistrationInvalid = "Dynamic registration invalid response", 29 | CodeExchangeFailed = "Failed to exchange code for token", 30 | InvalidBearerTokenResponse = "Invalid bearer token response", 31 | InvalidIdToken = "Invalid ID token", 32 | MissingOrInvalidStoredState = "State required to finish logging in is not found in storage.", 33 | } 34 | -------------------------------------------------------------------------------- /src/oidc/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import type { SigningKey } from "oidc-client-ts"; 18 | import { type ValidatedAuthMetadata } from "./validate.ts"; 19 | 20 | export * from "./authorize.ts"; 21 | export * from "./discovery.ts"; 22 | export * from "./error.ts"; 23 | export * from "./register.ts"; 24 | export * from "./tokenRefresher.ts"; 25 | export * from "./validate.ts"; 26 | 27 | /** 28 | * Validated config for native OIDC authentication, as returned by {@link discoverAndValidateOIDCIssuerWellKnown}. 29 | * Contains metadata and signing keys from the issuer's well-known (https://oidc-issuer.example.com/.well-known/openid-configuration). 30 | */ 31 | export interface OidcClientConfig extends ValidatedAuthMetadata { 32 | signingKeys: SigningKey[] | null; 33 | } 34 | -------------------------------------------------------------------------------- /src/rendezvous/RendezvousChannel.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type RendezvousCode, type RendezvousIntent, type RendezvousFailureReason } from "./index.ts"; 18 | 19 | export interface RendezvousChannel { 20 | /** 21 | * @returns the checksum/confirmation digits to be shown to the user 22 | */ 23 | connect(): Promise; 24 | 25 | /** 26 | * Send a payload via the channel. 27 | * @param data - payload to send 28 | */ 29 | send(data: T): Promise; 30 | 31 | /** 32 | * Receive a payload from the channel. 33 | * @returns the received payload 34 | */ 35 | receive(): Promise | undefined>; 36 | 37 | /** 38 | * Close the channel and clear up any resources. 39 | */ 40 | close(): Promise; 41 | 42 | /** 43 | * @returns a representation of the channel that can be encoded in a QR or similar 44 | */ 45 | generateCode(intent: RendezvousIntent): Promise; 46 | 47 | cancel(reason: RendezvousFailureReason): Promise; 48 | } 49 | -------------------------------------------------------------------------------- /src/rendezvous/RendezvousCode.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type RendezvousTransportDetails, type RendezvousIntent } from "./index.ts"; 18 | 19 | export interface RendezvousCode { 20 | intent: RendezvousIntent; 21 | rendezvous?: { 22 | transport: RendezvousTransportDetails; 23 | algorithm: string; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /src/rendezvous/RendezvousError.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type RendezvousFailureReason } from "./index.ts"; 18 | 19 | export class RendezvousError extends Error { 20 | public constructor( 21 | message: string, 22 | public readonly code: RendezvousFailureReason, 23 | ) { 24 | super(message); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/rendezvous/RendezvousFailureReason.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export type RendezvousFailureListener = (reason: RendezvousFailureReason) => void; 18 | 19 | export type RendezvousFailureReason = MSC4108FailureReason | ClientRendezvousFailureReason; 20 | 21 | export enum MSC4108FailureReason { 22 | AuthorizationExpired = "authorization_expired", 23 | DeviceAlreadyExists = "device_already_exists", 24 | DeviceNotFound = "device_not_found", 25 | UnexpectedMessageReceived = "unexpected_message_received", 26 | UnsupportedProtocol = "unsupported_protocol", 27 | UserCancelled = "user_cancelled", 28 | } 29 | 30 | export enum ClientRendezvousFailureReason { 31 | /** The sign in request has expired */ 32 | Expired = "expired", 33 | /** The homeserver is lacking support for the required features */ 34 | HomeserverLacksSupport = "homeserver_lacks_support", 35 | /** The secure channel verification failed meaning that it might be compromised */ 36 | InsecureChannelDetected = "insecure_channel_detected", 37 | /** An invalid/incompatible QR code was scanned */ 38 | InvalidCode = "invalid_code", 39 | /** The other device is not signed in */ 40 | OtherDeviceNotSignedIn = "other_device_not_signed_in", 41 | /** The other device is already signed in */ 42 | OtherDeviceAlreadySignedIn = "other_device_already_signed_in", 43 | /** Other */ 44 | Unknown = "unknown", 45 | /** The user declined the sign in request */ 46 | UserDeclined = "user_declined", 47 | /** The rendezvous request is missing an ETag header */ 48 | ETagMissing = "etag_missing", 49 | } 50 | -------------------------------------------------------------------------------- /src/rendezvous/RendezvousIntent.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export enum RendezvousIntent { 18 | LOGIN_ON_NEW_DEVICE = "login.start", 19 | RECIPROCATE_LOGIN_ON_EXISTING_DEVICE = "login.reciprocate", 20 | } 21 | -------------------------------------------------------------------------------- /src/rendezvous/RendezvousTransport.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type RendezvousFailureListener, type RendezvousFailureReason } from "./index.ts"; 18 | 19 | export interface RendezvousTransportDetails { 20 | type: string; 21 | } 22 | 23 | /** 24 | * Interface representing a generic rendezvous transport. 25 | */ 26 | export interface RendezvousTransport { 27 | /** 28 | * Ready state of the transport. This is set to true when the transport is ready to be used. 29 | */ 30 | readonly ready: boolean; 31 | 32 | /** 33 | * Listener for cancellation events. This is called when the rendezvous is cancelled or fails. 34 | */ 35 | onFailure?: RendezvousFailureListener; 36 | 37 | /** 38 | * @returns the transport details that can be encoded in a QR or similar 39 | */ 40 | details(): Promise; 41 | 42 | /** 43 | * Send data via the transport. 44 | * @param data - the data itself 45 | */ 46 | send(data: T): Promise; 47 | 48 | /** 49 | * Receive data from the transport. 50 | */ 51 | receive(): Promise | undefined>; 52 | 53 | /** 54 | * Cancel the rendezvous. This will call `onCancelled()` if it is set. 55 | * @param reason - the reason for the cancellation/failure 56 | */ 57 | cancel(reason: RendezvousFailureReason): Promise; 58 | } 59 | -------------------------------------------------------------------------------- /src/rendezvous/channels/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export * from "./MSC4108SecureChannel.ts"; 18 | -------------------------------------------------------------------------------- /src/rendezvous/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export * from "./MSC4108SignInWithQR.ts"; 18 | export type * from "./RendezvousChannel.ts"; 19 | export type * from "./RendezvousCode.ts"; 20 | export * from "./RendezvousError.ts"; 21 | export * from "./RendezvousFailureReason.ts"; 22 | export * from "./RendezvousIntent.ts"; 23 | export type * from "./RendezvousTransport.ts"; 24 | export * from "./transports/index.ts"; 25 | export * from "./channels/index.ts"; 26 | -------------------------------------------------------------------------------- /src/rendezvous/transports/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export * from "./MSC4108RendezvousSession.ts"; 18 | -------------------------------------------------------------------------------- /src/rust-crypto/constants.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** The prefix used on indexeddbs created by rust-crypto */ 18 | export const RUST_SDK_STORE_PREFIX = "matrix-js-sdk"; 19 | -------------------------------------------------------------------------------- /src/rust-crypto/secret-storage.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type SecretStorageKey, type ServerSideSecretStorage } from "../secret-storage.ts"; 18 | 19 | /** 20 | * Check that the private cross signing keys (master, self signing, user signing) are stored in the secret storage and encrypted with the default secret storage key. 21 | * 22 | * @param secretStorage - The secret store using account data 23 | * @returns True if the cross-signing keys are all stored and encrypted with the same secret storage key. 24 | * 25 | * @internal 26 | */ 27 | export async function secretStorageContainsCrossSigningKeys(secretStorage: ServerSideSecretStorage): Promise { 28 | return secretStorageCanAccessSecrets(secretStorage, [ 29 | "m.cross_signing.master", 30 | "m.cross_signing.user_signing", 31 | "m.cross_signing.self_signing", 32 | ]); 33 | } 34 | 35 | /** 36 | * 37 | * Check that the secret storage can access the given secrets using the default key. 38 | * 39 | * @param secretStorage - The secret store using account data 40 | * @param secretNames - The secret names to check 41 | * @returns True if all the given secrets are accessible and encrypted with the given key. 42 | * 43 | * @internal 44 | */ 45 | export async function secretStorageCanAccessSecrets( 46 | secretStorage: ServerSideSecretStorage, 47 | secretNames: SecretStorageKey[], 48 | ): Promise { 49 | const defaultKeyId = await secretStorage.getDefaultKeyId(); 50 | if (!defaultKeyId) return false; 51 | 52 | for (const secretName of secretNames) { 53 | // check which keys this particular secret is encrypted with 54 | const record = (await secretStorage.isStored(secretName)) || {}; 55 | // if it's not encrypted with the right key, there is no point continuing 56 | if (!(defaultKeyId in record)) return false; 57 | } 58 | 59 | return true; 60 | } 61 | -------------------------------------------------------------------------------- /src/service-types.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 - 2021 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export enum SERVICE_TYPES { 18 | IS = "SERVICE_TYPE_IS", // An identity server 19 | IM = "SERVICE_TYPE_IM", // An integration manager 20 | } 21 | -------------------------------------------------------------------------------- /src/store/indexeddb-backend.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type ISavedSync } from "./index.ts"; 18 | import { type IEvent, type IStateEventWithRoomId, type IStoredClientOpts, type ISyncResponse } from "../matrix.ts"; 19 | import { type IndexedToDeviceBatch, type ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage.ts"; 20 | 21 | export interface IIndexedDBBackend { 22 | connect(onClose?: () => void): Promise; 23 | syncToDatabase(userTuples: UserTuple[]): Promise; 24 | isNewlyCreated(): Promise; 25 | setSyncData(syncData: ISyncResponse): Promise; 26 | getSavedSync(): Promise; 27 | getNextBatchToken(): Promise; 28 | clearDatabase(): Promise; 29 | getOutOfBandMembers(roomId: string): Promise; 30 | setOutOfBandMembers(roomId: string, membershipEvents: IStateEventWithRoomId[]): Promise; 31 | clearOutOfBandMembers(roomId: string): Promise; 32 | getUserPresenceEvents(): Promise; 33 | getClientOptions(): Promise; 34 | storeClientOptions(options: IStoredClientOpts): Promise; 35 | saveToDeviceBatches(batches: ToDeviceBatchWithTxnId[]): Promise; 36 | getOldestToDeviceBatch(): Promise; 37 | removeToDeviceBatch(id: number): Promise; 38 | destroy(): Promise; 39 | } 40 | 41 | export type UserTuple = [userId: string, presenceEvent: Partial]; 42 | -------------------------------------------------------------------------------- /src/store/local-storage-events-emitter.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { TypedEventEmitter } from "../models/typed-event-emitter.ts"; 18 | 19 | export enum LocalStorageErrors { 20 | Global = "Global", 21 | SetItemError = "setItem", 22 | GetItemError = "getItem", 23 | RemoveItemError = "removeItem", 24 | ClearError = "clear", 25 | QuotaExceededError = "QuotaExceededError", 26 | } 27 | 28 | type EventHandlerMap = { 29 | [LocalStorageErrors.Global]: (error: Error) => void; 30 | [LocalStorageErrors.SetItemError]: (error: Error) => void; 31 | [LocalStorageErrors.GetItemError]: (error: Error) => void; 32 | [LocalStorageErrors.RemoveItemError]: (error: Error) => void; 33 | [LocalStorageErrors.ClearError]: (error: Error) => void; 34 | [LocalStorageErrors.QuotaExceededError]: (error: Error) => void; 35 | }; 36 | 37 | /** 38 | * Used in element-web as a temporary hack to handle all the localStorage errors on the highest level possible 39 | * As of 15.11.2021 (DD/MM/YYYY) we're not properly handling local storage exceptions anywhere. 40 | * This store, as an event emitter, is used to re-emit local storage exceptions so that we can handle them 41 | * and show some kind of a "It's dead Jim" modal to the users, telling them that hey, 42 | * maybe you should check out your disk, as it's probably dying and your session may die with it. 43 | * See: https://github.com/vector-im/element-web/issues/18423 44 | */ 45 | class LocalStorageErrorsEventsEmitter extends TypedEventEmitter {} 46 | export const localStorageErrorsEventsEmitter = new LocalStorageErrorsEventsEmitter(); 47 | -------------------------------------------------------------------------------- /src/thread-utils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { THREAD_RELATION_TYPE } from "./models/thread.ts"; 18 | import { type IEvent } from "./models/event.ts"; 19 | 20 | /** 21 | * Returns a filter function for the /relations endpoint to filter out relations directly 22 | * to the thread root event that should not live in the thread timeline 23 | * 24 | * @param threadId - the thread ID (ie. the event ID of the root event of the thread) 25 | * @returns the filtered list of events 26 | */ 27 | export function getRelationsThreadFilter(threadId: string): (e: Partial) => boolean { 28 | return (e: Partial) => 29 | e.content?.["m.relates_to"]?.event_id !== threadId || 30 | e.content?.["m.relates_to"]?.rel_type === THREAD_RELATION_TYPE.name; 31 | } 32 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | * This file is a secondary entrypoint for the js-sdk library, for use by Typescript projects. 19 | * It exposes low-level types and interfaces reflecting structures defined in the Matrix specification. 20 | * 21 | * Remember to only export *public* types from this file. 22 | */ 23 | 24 | export type * from "./@types/media.ts"; 25 | export * from "./@types/membership.ts"; 26 | export type * from "./@types/event.ts"; 27 | export type * from "./@types/events.ts"; 28 | export type * from "./@types/state_events.ts"; 29 | export type * from "./@types/AESEncryptedSecretStoragePayload.ts"; 30 | 31 | /** The different methods for device and user verification */ 32 | export enum VerificationMethod { 33 | /** Short authentication string (emoji or decimals). 34 | * 35 | * @see https://spec.matrix.org/v1.9/client-server-api/#short-authentication-string-sas-verification 36 | */ 37 | Sas = "m.sas.v1", 38 | 39 | /** 40 | * Verification by showing a QR code which is scanned by the other device. 41 | * 42 | * @see https://spec.matrix.org/v1.9/client-server-api/#qr-codes 43 | */ 44 | ShowQrCode = "m.qr_code.show.v1", 45 | 46 | /** 47 | * Verification by scanning a QR code that is shown by the other device. 48 | * 49 | * @see https://spec.matrix.org/v1.9/client-server-api/#qr-codes 50 | */ 51 | ScanQrCode = "m.qr_code.scan.v1", 52 | 53 | /** 54 | * Verification by confirming that we have scanned a QR code. 55 | * 56 | * @see https://spec.matrix.org/v1.9/client-server-api/#qr-codes 57 | */ 58 | Reciprocate = "m.reciprocate.v1", 59 | } 60 | -------------------------------------------------------------------------------- /src/utils/decryptAESSecretStorageItem.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Matrix.org Foundation C.I.C. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { decodeBase64 } from "../base64.ts"; 18 | import { deriveKeys } from "./internal/deriveKeys.ts"; 19 | import { type AESEncryptedSecretStoragePayload } from "../@types/AESEncryptedSecretStoragePayload.ts"; 20 | 21 | /** 22 | * Decrypt an AES-encrypted Secret Storage item. 23 | * 24 | * @param data - the encrypted data, returned by {@link utils/encryptAESSecretStorageItem.default | encryptAESSecretStorageItem}. 25 | * @param key - the encryption key to use as an input to the HKDF function which is used to derive the AES key. Must 26 | * be the same as provided to {@link utils/encryptAESSecretStorageItem.default | encryptAESSecretStorageItem}. 27 | * @param name - the name of the secret. Also used as an input to the HKDF operation which is used to derive the AES 28 | * key, so again must be the same as provided to {@link utils/encryptAESSecretStorageItem.default | encryptAESSecretStorageItem}. 29 | */ 30 | export default async function decryptAESSecretStorageItem( 31 | data: AESEncryptedSecretStoragePayload, 32 | key: Uint8Array, 33 | name: string, 34 | ): Promise { 35 | const [aesKey, hmacKey] = await deriveKeys(key, name); 36 | 37 | const ciphertext = decodeBase64(data.ciphertext); 38 | 39 | if (!(await globalThis.crypto.subtle.verify({ name: "HMAC" }, hmacKey, decodeBase64(data.mac), ciphertext))) { 40 | throw new Error(`Error decrypting secret ${name}: bad MAC`); 41 | } 42 | 43 | const plaintext = await globalThis.crypto.subtle.decrypt( 44 | { 45 | name: "AES-CTR", 46 | counter: decodeBase64(data.iv), 47 | length: 64, 48 | }, 49 | aesKey, 50 | ciphertext, 51 | ); 52 | 53 | return new TextDecoder().decode(new Uint8Array(plaintext)); 54 | } 55 | -------------------------------------------------------------------------------- /src/utils/internal/deriveKeys.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Matrix.org Foundation C.I.C. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // salt for HKDF, with 8 bytes of zeros 18 | const zeroSalt = new Uint8Array(8); 19 | 20 | /** 21 | * Derive AES and HMAC keys from a master key. 22 | * 23 | * This is used for deriving secret storage keys: see https://spec.matrix.org/v1.11/client-server-api/#msecret_storagev1aes-hmac-sha2 (step 1). 24 | * 25 | * @param key 26 | * @param name 27 | */ 28 | export async function deriveKeys(key: Uint8Array, name: string): Promise<[CryptoKey, CryptoKey]> { 29 | const hkdfkey = await globalThis.crypto.subtle.importKey("raw", key, { name: "HKDF" }, false, ["deriveBits"]); 30 | const keybits = await globalThis.crypto.subtle.deriveBits( 31 | { 32 | name: "HKDF", 33 | salt: zeroSalt, 34 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 35 | // @ts-ignore: https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/879 36 | info: new TextEncoder().encode(name), 37 | hash: "SHA-256", 38 | }, 39 | hkdfkey, 40 | 512, 41 | ); 42 | 43 | const aesKey = keybits.slice(0, 32); 44 | const hmacKey = keybits.slice(32); 45 | 46 | const aesProm = globalThis.crypto.subtle.importKey("raw", aesKey, { name: "AES-CTR" }, false, [ 47 | "encrypt", 48 | "decrypt", 49 | ]); 50 | 51 | const hmacProm = globalThis.crypto.subtle.importKey( 52 | "raw", 53 | hmacKey, 54 | { 55 | name: "HMAC", 56 | hash: { name: "SHA-256" }, 57 | }, 58 | false, 59 | ["sign", "verify"], 60 | ); 61 | 62 | return Promise.all([aesProm, hmacProm]); 63 | } 64 | -------------------------------------------------------------------------------- /src/version-support.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * A list of the spec versions which the js-sdk is compatible with. 19 | * 20 | * In practice, this means: when we connect to a server, it must declare support for one of the versions in this list. 21 | * 22 | * Note that it does not *necessarily* mean that the js-sdk has good support for all the features in the listed spec 23 | * versions; only that we should be able to provide a base level of functionality with a server that offers support for 24 | * any of the listed versions. 25 | */ 26 | export const SUPPORTED_MATRIX_VERSIONS = ["v1.1", "v1.2", "v1.3", "v1.4", "v1.5", "v1.6", "v1.7", "v1.8", "v1.9"]; 27 | 28 | /** 29 | * The oldest Matrix specification version the js-sdk supports. 30 | */ 31 | export const MINIMUM_MATRIX_VERSION = SUPPORTED_MATRIX_VERSIONS[0]; 32 | 33 | /** 34 | * The most recent Matrix specification version the js-sdk supports. 35 | */ 36 | export const MAXIMUM_MATRIX_VERSION = SUPPORTED_MATRIX_VERSIONS[SUPPORTED_MATRIX_VERSIONS.length - 1]; 37 | -------------------------------------------------------------------------------- /src/webrtc/audioContext.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | let audioContext: AudioContext | null = null; 18 | let refCount = 0; 19 | 20 | /** 21 | * Acquires a reference to the shared AudioContext. 22 | * It's highly recommended to reuse this AudioContext rather than creating your 23 | * own, because multiple AudioContexts can be problematic in some browsers. 24 | * Make sure to call releaseContext when you're done using it. 25 | * @returns The shared AudioContext 26 | */ 27 | export const acquireContext = (): AudioContext => { 28 | if (audioContext === null) audioContext = new AudioContext(); 29 | refCount++; 30 | return audioContext; 31 | }; 32 | 33 | /** 34 | * Signals that one of the references to the shared AudioContext has been 35 | * released, allowing the context and associated hardware resources to be 36 | * cleaned up if nothing else is using it. 37 | */ 38 | export const releaseContext = (): void => { 39 | refCount--; 40 | if (refCount === 0) { 41 | audioContext?.close(); 42 | audioContext = null; 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /src/webrtc/callEventTypes.ts: -------------------------------------------------------------------------------- 1 | // allow non-camelcase as these are events type that go onto the wire 2 | /* eslint-disable camelcase */ 3 | 4 | import { type CallErrorCode } from "./call.ts"; 5 | 6 | // TODO: Change to "sdp_stream_metadata" when MSC3077 is merged 7 | export const SDPStreamMetadataKey = "org.matrix.msc3077.sdp_stream_metadata"; 8 | 9 | export enum SDPStreamMetadataPurpose { 10 | Usermedia = "m.usermedia", 11 | Screenshare = "m.screenshare", 12 | } 13 | 14 | export interface SDPStreamMetadataObject { 15 | purpose: SDPStreamMetadataPurpose; 16 | audio_muted: boolean; 17 | video_muted: boolean; 18 | } 19 | 20 | export interface SDPStreamMetadata { 21 | [key: string]: SDPStreamMetadataObject; 22 | } 23 | 24 | export interface CallCapabilities { 25 | "m.call.transferee": boolean; 26 | "m.call.dtmf": boolean; 27 | } 28 | 29 | export interface CallReplacesTarget { 30 | id: string; 31 | display_name: string; 32 | avatar_url: string; 33 | } 34 | 35 | export interface MCallBase { 36 | call_id: string; 37 | conf_id?: string; 38 | version: string | number; 39 | party_id?: string; 40 | sender_session_id?: string; 41 | dest_session_id?: string; 42 | } 43 | 44 | export interface MCallAnswer extends MCallBase { 45 | answer: RTCSessionDescription; 46 | capabilities?: CallCapabilities; 47 | [SDPStreamMetadataKey]: SDPStreamMetadata; 48 | } 49 | 50 | export interface MCallSelectAnswer extends MCallBase { 51 | selected_party_id: string; 52 | } 53 | 54 | export interface MCallInviteNegotiate extends MCallBase { 55 | offer: RTCSessionDescription; 56 | description: RTCSessionDescription; 57 | lifetime: number; 58 | capabilities?: CallCapabilities; 59 | invitee?: string; 60 | sender_session_id?: string; 61 | dest_session_id?: string; 62 | [SDPStreamMetadataKey]: SDPStreamMetadata; 63 | } 64 | 65 | export interface MCallSDPStreamMetadataChanged extends MCallBase { 66 | [SDPStreamMetadataKey]: SDPStreamMetadata; 67 | } 68 | 69 | export interface MCallReplacesEvent extends MCallBase { 70 | replacement_id: string; 71 | target_user: CallReplacesTarget; 72 | create_call: string; 73 | await_call: string; 74 | target_room: string; 75 | } 76 | 77 | export interface MCAllAssertedIdentity extends MCallBase { 78 | asserted_identity: { 79 | id: string; 80 | display_name: string; 81 | avatar_url: string; 82 | }; 83 | } 84 | 85 | export interface MCallCandidates extends MCallBase { 86 | candidates: Omit[]; 87 | } 88 | 89 | export interface MCallHangupReject extends MCallBase { 90 | reason?: CallErrorCode; 91 | } 92 | 93 | /* eslint-enable camelcase */ 94 | -------------------------------------------------------------------------------- /src/webrtc/stats/callStatsReportSummary.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | export interface CallStatsReportSummary { 14 | receivedMedia: number; 15 | receivedAudioMedia: number; 16 | receivedVideoMedia: number; 17 | audioTrackSummary: TrackSummary; 18 | videoTrackSummary: TrackSummary; 19 | 20 | isFirstCollection: boolean; 21 | } 22 | 23 | export interface TrackSummary { 24 | count: number; 25 | muted: number; 26 | maxJitter: number; 27 | maxPacketLoss: number; 28 | concealedAudio: number; 29 | totalAudio: number; 30 | } 31 | -------------------------------------------------------------------------------- /src/webrtc/stats/connectionStats.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { type TransportStats } from "./transportStats.ts"; 18 | import { type Bitrate } from "./media/mediaTrackStats.ts"; 19 | 20 | export interface ConnectionStatsBandwidth { 21 | /** 22 | * bytes per second 23 | */ 24 | download: number; 25 | /** 26 | * bytes per second 27 | */ 28 | upload: number; 29 | } 30 | 31 | export interface ConnectionStatsBitrate extends Bitrate { 32 | audio?: Bitrate; 33 | video?: Bitrate; 34 | } 35 | 36 | export interface PacketLoss { 37 | total: number; 38 | download: number; 39 | upload: number; 40 | } 41 | 42 | export class ConnectionStats { 43 | public bandwidth: ConnectionStatsBitrate = {} as ConnectionStatsBitrate; 44 | public bitrate: ConnectionStatsBitrate = {} as ConnectionStatsBitrate; 45 | public packetLoss: PacketLoss = {} as PacketLoss; 46 | public transport: TransportStats[] = []; 47 | } 48 | -------------------------------------------------------------------------------- /src/webrtc/stats/connectionStatsBuilder.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | import { type Bitrate } from "./media/mediaTrackStats.ts"; 17 | 18 | export class ConnectionStatsBuilder { 19 | public static buildBandwidthReport(now: RTCIceCandidatePairStats): Bitrate { 20 | const availableIncomingBitrate = now.availableIncomingBitrate; 21 | const availableOutgoingBitrate = now.availableOutgoingBitrate; 22 | 23 | return { 24 | download: availableIncomingBitrate ? Math.round(availableIncomingBitrate / 1000) : 0, 25 | upload: availableOutgoingBitrate ? Math.round(availableOutgoingBitrate / 1000) : 0, 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/webrtc/stats/media/mediaSsrcHandler.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { parse as parseSdp } from "sdp-transform"; 18 | 19 | export type Mid = string; 20 | export type Ssrc = string; 21 | export type MapType = "local" | "remote"; 22 | 23 | export class MediaSsrcHandler { 24 | private readonly ssrcToMid = { local: new Map(), remote: new Map() }; 25 | 26 | public findMidBySsrc(ssrc: Ssrc, type: "local" | "remote"): Mid | undefined { 27 | let mid: Mid | undefined; 28 | this.ssrcToMid[type].forEach((ssrcs, m) => { 29 | if (ssrcs.find((s) => s == ssrc)) { 30 | mid = m; 31 | return; 32 | } 33 | }); 34 | return mid; 35 | } 36 | 37 | public parse(description: string, type: MapType): void { 38 | const sdp = parseSdp(description); 39 | const ssrcToMid = new Map(); 40 | sdp.media.forEach((m) => { 41 | if ((!!m.mid && m.type === "video") || m.type === "audio") { 42 | const ssrcs: Ssrc[] = []; 43 | m.ssrcs?.forEach((ssrc) => { 44 | if (ssrc.attribute === "cname") { 45 | ssrcs.push(`${ssrc.id}`); 46 | } 47 | }); 48 | ssrcToMid.set(`${m.mid}`, ssrcs); 49 | } 50 | }); 51 | this.ssrcToMid[type] = ssrcToMid; 52 | } 53 | 54 | public getSsrcToMidMap(type: MapType): Map { 55 | return this.ssrcToMid[type]; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/webrtc/stats/statsReportEmitter.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { TypedEventEmitter } from "../../models/typed-event-emitter.ts"; 18 | import { 19 | type ByteSentStatsReport, 20 | type CallFeedReport, 21 | type ConnectionStatsReport, 22 | StatsReport, 23 | type SummaryStatsReport, 24 | } from "./statsReport.ts"; 25 | 26 | export type StatsReportHandlerMap = { 27 | [StatsReport.BYTE_SENT_STATS]: (report: ByteSentStatsReport) => void; 28 | [StatsReport.CONNECTION_STATS]: (report: ConnectionStatsReport) => void; 29 | [StatsReport.CALL_FEED_REPORT]: (report: CallFeedReport) => void; 30 | [StatsReport.SUMMARY_STATS]: (report: SummaryStatsReport) => void; 31 | }; 32 | 33 | export class StatsReportEmitter extends TypedEventEmitter { 34 | public emitByteSendReport(byteSentStats: ByteSentStatsReport): void { 35 | this.emit(StatsReport.BYTE_SENT_STATS, byteSentStats); 36 | } 37 | 38 | public emitConnectionStatsReport(report: ConnectionStatsReport): void { 39 | this.emit(StatsReport.CONNECTION_STATS, report); 40 | } 41 | 42 | public emitCallFeedReport(report: CallFeedReport): void { 43 | this.emit(StatsReport.CALL_FEED_REPORT, report); 44 | } 45 | 46 | public emitSummaryStatsReport(report: SummaryStatsReport): void { 47 | this.emit(StatsReport.SUMMARY_STATS, report); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/webrtc/stats/transportStats.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export interface TransportStats { 18 | ip: string; 19 | type: string; 20 | localIp: string; 21 | isFocus: boolean; 22 | localCandidateType: string; 23 | remoteCandidateType: string; 24 | networkType: string; 25 | rtt: number; 26 | } 27 | -------------------------------------------------------------------------------- /src/webrtc/stats/transportStatsBuilder.ts: -------------------------------------------------------------------------------- 1 | import { type TransportStats } from "./transportStats.ts"; 2 | 3 | export class TransportStatsBuilder { 4 | public static buildReport( 5 | report: RTCStatsReport | undefined, 6 | now: RTCIceCandidatePairStats, 7 | conferenceStatsTransport: TransportStats[], 8 | isFocus: boolean, 9 | ): TransportStats[] { 10 | const localUsedCandidate = report?.get(now.localCandidateId); 11 | const remoteUsedCandidate = report?.get(now.remoteCandidateId); 12 | 13 | // RTCIceCandidateStats 14 | // https://w3c.github.io/webrtc-stats/#icecandidate-dict* 15 | if (remoteUsedCandidate && localUsedCandidate) { 16 | const remoteIpAddress = 17 | remoteUsedCandidate.ip !== undefined ? remoteUsedCandidate.ip : remoteUsedCandidate.address; 18 | const remotePort = remoteUsedCandidate.port; 19 | const ip = `${remoteIpAddress}:${remotePort}`; 20 | 21 | const localIpAddress = 22 | localUsedCandidate.ip !== undefined ? localUsedCandidate.ip : localUsedCandidate.address; 23 | const localPort = localUsedCandidate.port; 24 | const localIp = `${localIpAddress}:${localPort}`; 25 | 26 | const type = remoteUsedCandidate.protocol; 27 | 28 | // Save the address unless it has been saved already. 29 | if ( 30 | !conferenceStatsTransport.some( 31 | (t: TransportStats) => t.ip === ip && t.type === type && t.localIp === localIp, 32 | ) 33 | ) { 34 | conferenceStatsTransport.push({ 35 | ip, 36 | type, 37 | localIp, 38 | isFocus, 39 | localCandidateType: localUsedCandidate.candidateType, 40 | remoteCandidateType: remoteUsedCandidate.candidateType, 41 | networkType: localUsedCandidate.networkType, 42 | rtt: now.currentRoundTripTime ? now.currentRoundTripTime * 1000 : NaN, 43 | } as TransportStats); 44 | } 45 | } 46 | return conferenceStatsTransport; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/webrtc/stats/valueFormatter.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Matrix.org Foundation C.I.C. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | export class ValueFormatter { 14 | public static getNonNegativeValue(imput: any): number { 15 | let value = imput; 16 | 17 | if (typeof value !== "number") { 18 | value = Number(value); 19 | } 20 | 21 | if (isNaN(value)) { 22 | return 0; 23 | } 24 | 25 | return Math.max(0, value); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig-build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "forceConsistentCasingInFileNames": true, 5 | "declarationMap": true, 6 | "sourceMap": true, 7 | "noEmit": false, 8 | "outDir": "./lib", 9 | "rootDir": "src" 10 | }, 11 | "exclude": ["./spec/**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "experimentalDecorators": false, 5 | "esModuleInterop": true, 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "noUnusedLocals": true, 9 | "noEmit": true, 10 | "declaration": true, 11 | "strict": true, 12 | "allowImportingTsExtensions": true, 13 | "lib": ["es2024"] 14 | }, 15 | "include": ["./src/**/*.ts", "./spec/**/*.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://typedoc.org/schema.json", 3 | "plugin": ["typedoc-plugin-mdn-links", "typedoc-plugin-missing-exports", "typedoc-plugin-coverage"], 4 | "coverageLabel": "TypeDoc", 5 | "entryPoints": ["src/matrix.ts", "src/crypto-api", "src/types.ts", "src/testing.ts", "src/utils/*.ts"], 6 | "excludeExternals": true, 7 | "out": "_docs" 8 | } 9 | --------------------------------------------------------------------------------