Compare commits

...

28 Commits

Author SHA1 Message Date
RiotRobot 5bffb7df4f v31.6.1 2024-03-28 16:44:30 +00:00
David Baker 9e1897dcd0 Merge pull request #4136 from matrix-org/backport-4135-to-staging
Fix merging of default push rules (backport)
2024-03-28 16:35:36 +00:00
David Baker 5f3ddc37a1 Merge pull request #4135 from matrix-org/t3chguy/fix/27173
Fix merging of default push rules

(cherry picked from commit 78a225795b)
2024-03-28 16:28:33 +00:00
RiotRobot 2da6c0c605 v31.6.0 2024-03-26 16:25:15 +00:00
RiotRobot b8078f9916 v31.6.0-rc.0 2024-03-19 15:05:53 +00:00
Andy Balaam 92342c07ed Introduce Membership TS type (take 2) (#4107)
* Introduce Membership TS type

* Adapt the Membership TS type to be an enum

* Add docstrings for KnownMembership and Membership

* Move Membership types into a separate file, exported from types.ts

---------

Co-authored-by: Stanislav Demydiuk <s.demydiuk@gmail.com>
2024-03-18 12:47:23 +00:00
Valere 3e989006aa Migrate own identity local trust to rust crypto (#4090)
* Migrate own identity trust to rust crypto

* Fix gendoc not happy if msk of IDownloadKeyResult has a signature

* add missing mock

* code review

* Code review

* Review gh suggestion

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* Review gh suggestion

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* Review gh suggestion

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* Review gh suggestion

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* review move function down in file

* Review gh suggestion

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* Review gh suggestion

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* Review: Cleaning tests, renaming

* Review: better comment

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* Comment paragraphs

* retry until initial  key query is successfull

* review quick nits

* missing mock in test

---------

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2024-03-18 08:57:53 +00:00
Kim Brose 8e0ef5ff2c fix automatic DM avatar with functional members (#4017)
* fix automatic DM avatar with functional members

* update comments

* lint

* add tests for functional members

* keep functional members out of the public API

- remove public API for functional members, reverting most of 0ce2d82, f9b41f6, e65fb24
- remove tests for functional members public API c114bf5
- add shared functional members getter for both room name and avatar fallback generation

* filter functional members from more candidates

- remove from hero(es)
- remove from previous members

* add tests for fallback avatars with functional members

* Add docstring for getFunctionalMembers

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* inline getInvitedAndJoinedFunctionalMemberCount

* update comments for getAvatarFallbackMember

* use correct list of heroes in getAvatarFallbackMember

* remove redundant type annotation

* optimize performance of invitedAndJoinedFunctionalMemberCount

* calculate nonFunctionalMemberCount in one step

instead of iterating redundantly

* clean up functional member tests with review feedback

* lint

* Update src/models/room.ts

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* apply feedback about comments

* non-functional per review, lint

---------

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2024-03-13 17:01:11 +00:00
Michael Telatynski 78d05942a3 Merge remote-tracking branch 'origin/develop' into develop 2024-03-12 18:53:54 +00:00
Michael Telatynski c22a6858c8 Temporarily disable broken step in the release process
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-03-12 18:53:44 +00:00
RiotRobot 461aeae281 Resetting package fields for development 2024-03-12 18:34:34 +00:00
RiotRobot 0511e313d3 Merge branch 'master' into develop 2024-03-12 18:34:33 +00:00
RiotRobot da3d5c4a43 v31.5.0 2024-03-12 18:33:38 +00:00
renovate[bot] 4c26b55c9a Update dependency typedoc to v0.25.11 (#4102)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-12 13:38:32 +00:00
Michael Telatynski 3711ad7e61 Export types describing all specced media event formats (#4092)
* Export types describing all specced media event formats

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate PR

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Move types to a dedicated export

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add readme entry

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-03-08 21:07:26 +00:00
Michael Telatynski 3031152444 Add .m.rule.is_room_mention push rule to DEFAULT_OVERRIDE_RULES (#4100)
* Add intentional mentions push rules to DEFAULT_OVERRIDE_RULES

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-03-08 20:21:12 +00:00
Daniel Salinas 51ebd2fcde Fix race condition with sliding sync extensions (#4089)
* Fix race condition with sliding sync extensions

* Fix types on sliding-sync spec test

* Prettier fixes
2024-03-07 23:56:36 +00:00
David Baker 27dd856778 Don't re-fetch thread root if we already have it (#4088)
The root event of a thread used to arrive with the pagination request, but this was unspecced and so got changed to simply fetch the root event. In many (almost all) cases this shouldn't be necessary because the thread should already have its root event: re-use it if it's already there. This is only in pagination, so there's no reason to believe that the root event would have changed and needs to be re-fetched.

This removes a number of duplicate calls to the /event/ endpoint from the tests.
2024-03-06 14:10:35 +00:00
renovate[bot] 7fee37680f Update typedoc (#4098)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2024-03-06 08:49:16 +00:00
renovate[bot] 2541ca04c2 Update all non-major dependencies (#4096)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2024-03-05 17:54:30 +00:00
Richard van der Hoff d55c6a36df PR template: reminder to document your stuff (#4081)
* PR template: reminder to document your stuff

* link to tsdoc, not typedoc

* add full stops
2024-03-05 17:10:33 +00:00
Timo 8c0736a719 Make sending ContentLoaded optional for a widgetClient (#4086)
* add sendContentLoaded option to widgetClient

Signed-off-by: Timo K <toger5@hotmail.de>

* review

Signed-off-by: Timo K <toger5@hotmail.de>

* add tests

Signed-off-by: Timo K <toger5@hotmail.de>

* another try to get the coverage up

Signed-off-by: Timo K <toger5@hotmail.de>

* self review

Signed-off-by: Timo K <toger5@hotmail.de>

---------

Signed-off-by: Timo K <toger5@hotmail.de>
2024-03-05 16:59:07 +00:00
renovate[bot] 50b042d1ff Update tspascoal/get-user-teams-membership digest to 57e9f42 (#4094)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-05 16:14:15 +00:00
renovate[bot] a818dc1e9d Update shogo82148/actions-upload-release-asset digest to 5bd52f0 (#4093)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-05 15:56:46 +00:00
renovate[bot] d8dae65a4d Update typescript-eslint monorepo to v7.1.0 (#4099)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-05 15:43:37 +00:00
renovate[bot] 8be286308c Update dependency @types/node to v18.19.21 (#4095)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-05 15:16:30 +00:00
renovate[bot] 84498bf77d Update babel monorepo to v7.24.0 (#4097)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-05 14:50:28 +00:00
Michael Telatynski a1f4b07b7d Fix bad string split destructuring
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-03-05 14:31:57 +00:00
95 changed files with 74384 additions and 73181 deletions
+1
View File
@@ -0,0 +1 @@
_docs
-3
View File
@@ -103,11 +103,8 @@ module.exports = {
},
},
{
// We don't need amazing docs in our spec files
files: ["src/**/*.ts"],
rules: {
"tsdoc/syntax": "error",
// We use some select jsdoc rules as the tsdoc linter has only one rule
"jsdoc/no-types": "error",
"jsdoc/empty-tags": "error",
"jsdoc/check-property-names": "error",
+4 -3
View File
@@ -2,6 +2,7 @@
## Checklist
- [ ] Tests written for new code (and old code if feasible)
- [ ] Linter and other CI checks pass
- [ ] Sign-off given on the changes (see [CONTRIBUTING.md](https://github.com/matrix-org/matrix-js-sdk/blob/develop/CONTRIBUTING.md))
- [ ] Tests written for new code (and old code if feasible).
- [ ] New or updated `public`/`exported` symbols have accurate [TSDoc](https://tsdoc.org/) documentation.
- [ ] Linter and other CI checks pass.
- [ ] Sign-off given on the changes (see [CONTRIBUTING.md](https://github.com/matrix-org/matrix-js-sdk/blob/develop/CONTRIBUTING.md)).
@@ -22,7 +22,7 @@ runs:
- name: Upload tarball signature
if: ${{ inputs.upload-url }}
uses: shogo82148/actions-upload-release-asset@dccd6d23e64fd6a746dce6814c0bde0a04886085 # v1
uses: shogo82148/actions-upload-release-asset@5bd52f05dd8076794da5975d4c0a4f3bce7dd8f5 # v1
with:
upload_url: ${{ inputs.upload-url }}
asset_path: ${{ env.VERSION }}.tar.gz.asc
@@ -29,13 +29,13 @@ runs:
- name: Upload asset signatures
if: inputs.gpg-fingerprint
uses: shogo82148/actions-upload-release-asset@dccd6d23e64fd6a746dce6814c0bde0a04886085 # v1
uses: shogo82148/actions-upload-release-asset@5bd52f05dd8076794da5975d4c0a4f3bce7dd8f5 # v1
with:
upload_url: ${{ inputs.upload-url }}
asset_path: ${{ inputs.asset-path }}.asc
- name: Upload assets
uses: shogo82148/actions-upload-release-asset@dccd6d23e64fd6a746dce6814c0bde0a04886085 # v1
uses: shogo82148/actions-upload-release-asset@5bd52f05dd8076794da5975d4c0a4f3bce7dd8f5 # v1
with:
upload_url: ${{ inputs.upload-url }}
asset_path: ${{ inputs.asset-path }}
+1 -1
View File
@@ -16,7 +16,7 @@ concurrency:
jobs:
build-element-web:
name: Build element-web
uses: matrix-org/matrix-react-sdk/.github/workflows/element-web.yaml@v3.92.0
uses: matrix-org/matrix-react-sdk/.github/workflows/element-web.yaml@v3.93.0
with:
matrix-js-sdk-sha: ${{ github.sha }}
react-sdk-repository: matrix-org/matrix-react-sdk
+1 -1
View File
@@ -47,7 +47,7 @@ jobs:
steps:
- name: Check membership
if: github.event.pull_request.user.login != 'renovate[bot]'
uses: tspascoal/get-user-teams-membership@ba78054988f58bea69b7c6136d563236f8ed2fc0 # v3
uses: tspascoal/get-user-teams-membership@57e9f42acd78f4d0f496b3be4368fc5f62696662 # v3
id: teams
with:
username: ${{ github.event.pull_request.user.login }}
+9 -9
View File
@@ -294,15 +294,15 @@ jobs:
remove-labels: X-Upcoming-Release-Blocker
add-labels: X-Release-Blocker
- name: Wait for master->develop gitflow merge
if: inputs.final
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
with:
ref: master
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
check-name: merge
allowed-conclusions: success
# - name: Wait for master->develop gitflow merge
# if: inputs.final
# uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
# with:
# ref: master
# repo-token: ${{ secrets.GITHUB_TOKEN }}
# wait-interval: 10
# check-name: merge
# allowed-conclusions: success
bump-downstreams:
name: Update npm dependency in downstream projects
+15 -26
View File
@@ -39,12 +39,6 @@ jobs:
- name: 🧮 Checkout code
uses: actions/checkout@v4
- name: 🧮 Checkout gh-pages
uses: actions/checkout@v4
with:
ref: gh-pages
path: _docs
- name: 🔧 Yarn cache
uses: actions/setup-node@v4
with:
@@ -53,26 +47,21 @@ jobs:
- name: 🔨 Install dependencies
run: "yarn install --frozen-lockfile"
- name: 🔨 Install symlinks
run: |
sudo apt-get update
sudo apt-get install -y symlinks
- name: 📖 Generate docs
run: |
yarn tpv purge --yes --out _docs --stale --major 10
yarn gendoc
symlinks -rc _docs
find _docs -xtype l -delete
run: yarn gendoc
- name: 🔨 Set up git
run: |
git config --global user.email "releases@riot.im"
git config --global user.name "RiotRobot"
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: _docs
- name: 🚀 Deploy
run: |
git add . --all
git commit -m "Update docs"
git push
working-directory: _docs
docs-deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: docs
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
-7
View File
@@ -83,13 +83,6 @@ jobs:
- name: Generate Docs
run: "yarn run gendoc --treatWarningsAsErrors"
# Upload artifact duplicates symlink contents so we do this to save 75% space
- name: Flatten symlink and write _redirects
run: |
find _docs -mindepth 1 -maxdepth 1 ! -type f ! -name stable -printf '/%f/* /stable/:splat\n' > _docs/_redirects
find _docs -mindepth 1 -maxdepth 1 -type l -delete
find _docs -mindepth 1 -maxdepth 1 -type d -execdir mv {} stable \; -quit
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
+34
View File
@@ -1,3 +1,37 @@
Changes in [31.6.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v31.6.1) (2024-03-28)
==================================================================================================
## 🐛 Bug Fixes
* Fix merging of default push rules ([#4136](https://github.com/matrix-org/matrix-js-sdk/pull/4136)).
Changes in [31.6.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v31.6.0) (2024-03-26)
==================================================================================================
## ✨ Features
* Introduce Membership TS type (take 2) ([#4107](https://github.com/matrix-org/matrix-js-sdk/pull/4107)). Contributed by @andybalaam.
* fix automatic DM avatar with functional members ([#4017](https://github.com/matrix-org/matrix-js-sdk/pull/4017)). Contributed by @HarHarLinks.
* Export types describing all specced media event formats ([#4092](https://github.com/matrix-org/matrix-js-sdk/pull/4092)). Contributed by @t3chguy.
* Add `.m.rule.is_room_mention` push rule to DEFAULT\_OVERRIDE\_RULES ([#4100](https://github.com/matrix-org/matrix-js-sdk/pull/4100)). Contributed by @t3chguy.
* Make sending ContentLoaded optional for a widgetClient ([#4086](https://github.com/matrix-org/matrix-js-sdk/pull/4086)). Contributed by @toger5.
## 🐛 Bug Fixes
* Migrate own identity local trust to rust crypto ([#4090](https://github.com/matrix-org/matrix-js-sdk/pull/4090)). Contributed by @BillCarsonFr.
* Fix race condition with sliding sync extensions ([#4089](https://github.com/matrix-org/matrix-js-sdk/pull/4089)). Contributed by @zzorba.
Changes in [31.5.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v31.5.0) (2024-03-12)
==================================================================================================
## ✨ Features
* Update MSC2965 OIDC Discovery implementation ([#4064](https://github.com/matrix-org/matrix-js-sdk/pull/4064)). Contributed by @t3chguy.
## 🐛 Bug Fixes
* Add basic retry for rust crypto outgoing requests ([#4061](https://github.com/matrix-org/matrix-js-sdk/pull/4061)). Contributed by @BillCarsonFr.
Changes in [31.4.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v31.4.0) (2024-02-27)
==================================================================================================
## ✨ Features
+6 -2
View File
@@ -192,6 +192,10 @@ via an optional `callback` argument. The callback support is now deprecated:
new methods do not include a `callback` argument, and in the future it may be
removed from existing methods.
## Low level types
There are some low level TypeScript types exported via the `matrix-js-sdk/lib/types` entrypoint to not bloat the main entrypoint.
## Examples
This section provides some useful code snippets which demonstrate the
@@ -212,7 +216,7 @@ const matrixClient = sdk.createClient({
```javascript
matrixClient.on(RoomMemberEvent.Membership, function (event, member) {
if (member.membership === "invite" && member.userId === myUserId) {
if (member.membership === KnownMembership.Invite && member.userId === myUserId) {
matrixClient.joinRoom(member.roomId).then(function () {
console.log("Auto-joined %s", member.roomId);
});
@@ -294,7 +298,7 @@ host the API reference from the source files like this:
```
$ yarn gendoc
$ cd _docs
$ cd docs
$ python -m http.server 8005
```
+1 -1
View File
@@ -115,7 +115,7 @@ rl.on("line", function (line) {
if (line.indexOf("/join ") === 0) {
var roomIndex = line.split(" ")[1];
viewingRoom = roomList[roomIndex];
if (viewingRoom.getMember(myUserId).membership === "invite") {
if (viewingRoom.getMember(myUserId).membership === KnownMembership.Invite) {
// join the room first
matrixClient.joinRoom(viewingRoom.roomId).then(
function (room) {
+6 -8
View File
@@ -1,6 +1,6 @@
{
"name": "matrix-js-sdk",
"version": "31.5.0-rc.0",
"version": "31.6.1",
"description": "Matrix Client-Server SDK for Javascript",
"engines": {
"node": ">=18.0.0"
@@ -67,8 +67,8 @@
"uuid": "9"
},
"devDependencies": {
"@action-validator/cli": "^0.5.3",
"@action-validator/core": "^0.5.3",
"@action-validator/cli": "^0.6.0",
"@action-validator/core": "^0.6.0",
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
"@babel/eslint-parser": "^7.12.10",
@@ -97,7 +97,7 @@
"babel-jest": "^29.0.0",
"debug": "^4.3.4",
"domexception": "^4.0.0",
"eslint": "8.56.0",
"eslint": "8.57.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^9.0.0",
"eslint-import-resolver-typescript": "^3.5.1",
@@ -121,13 +121,11 @@
"prettier": "3.2.5",
"rimraf": "^5.0.0",
"ts-node": "^10.9.1",
"typedoc": "^0.24.0",
"typedoc": "^0.25.10",
"typedoc-plugin-coverage": "^3.0.0",
"typedoc-plugin-mdn-links": "^3.0.3",
"typedoc-plugin-missing-exports": "^2.0.0",
"typedoc-plugin-versions": "^0.2.3",
"typedoc-plugin-versions-cli": "^0.1.12",
"typescript": "^5.0.0"
"typescript": "^5.3.3"
},
"@casualbot/jest-sonar-reporter": {
"outputDirectory": "coverage",
+1 -1
View File
@@ -45,7 +45,7 @@ async function getRelease(github, dependency) {
if (dependency.includes("/")) {
let rest;
[owner, rest] = dependency.split("/")[0];
[owner, rest] = dependency.split("/");
if (dependency.includes("@")) {
[repo, tag] = rest.split("@");
+11 -7
View File
@@ -99,6 +99,7 @@ import {
} from "./olm-utils";
import { ToDevicePayload } from "../../../src/models/ToDeviceMessage";
import { AccountDataAccumulator } from "../../test-utils/AccountDataAccumulator";
import { KnownMembership } from "../../../src/@types/membership";
afterEach(() => {
// reset fake-indexeddb after each test, to make sure we don't leak connections
@@ -1242,7 +1243,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
content: { algorithm: "m.megolm.v1.aes-sha2" },
}),
testUtils.mkMembership({
mship: "join",
mship: KnownMembership.Join,
sender: aliceClient.getUserId()!,
}),
],
@@ -1661,7 +1662,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
type: "m.room.member",
state_key: "@alice:localhost",
content: {
membership: "invite",
membership: KnownMembership.Invite,
},
},
],
@@ -1810,7 +1811,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
type: "m.room.member",
state_key: "@alice:localhost",
content: {
membership: "invite",
membership: KnownMembership.Invite,
},
},
],
@@ -1886,7 +1887,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
{
type: "m.room.member",
state_key: aliceClient.getUserId(),
content: { membership: "join" },
content: { membership: KnownMembership.Join },
event_id: "$alijoin",
},
],
@@ -1913,7 +1914,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
{
type: "m.room.member",
state_key: "@other:user",
content: { membership: "invite" },
content: { membership: KnownMembership.Invite },
event_id: "$otherinvite",
},
],
@@ -2061,7 +2062,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
fetchMock.getOnce(new RegExp(membersPath), {
chunk: [
testUtils.mkMembershipCustom({
membership: "join",
membership: KnownMembership.Join,
sender: "@bob:xyz",
}),
],
@@ -3090,7 +3091,10 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
const roomResponse = {
state: {
events: [
mkMembershipCustom({ membership: "join", sender: persistentStoreClient.getSafeUserId() }),
mkMembershipCustom({
membership: KnownMembership.Join,
sender: persistentStoreClient.getSafeUserId(),
}),
...stateEvents,
],
},
+3 -2
View File
@@ -36,6 +36,7 @@ import { TestClient } from "../../TestClient";
import { CRYPTO_ENABLED, IClaimKeysRequest, IQueryKeysRequest, IUploadKeysRequest } from "../../../src/client";
import { ClientEvent, IContent, ISendEventResponse, MatrixClient, MatrixEvent } from "../../../src/matrix";
import { DeviceInfo } from "../../../src/crypto/deviceinfo";
import { KnownMembership } from "../../../src/@types/membership";
let aliTestClient: TestClient;
const roomId = "!room:localhost";
@@ -316,11 +317,11 @@ function firstSync(testClient: TestClient): Promise<void> {
state: {
events: [
testUtils.mkMembership({
mship: "join",
mship: KnownMembership.Join,
user: aliUserId,
}),
testUtils.mkMembership({
mship: "join",
mship: KnownMembership.Join,
user: bobUserId,
}),
],
+189 -139
View File
@@ -20,6 +20,9 @@ import fetchMock from "fetch-mock-jest";
import { createClient, CryptoEvent, IndexedDBCryptoStore } from "../../../src";
import { populateStore } from "../../test-utils/test_indexeddb_cryptostore_dump";
import { MSK_NOT_CACHED_DATASET } from "../../test-utils/test_indexeddb_cryptostore_dump/no_cached_msk_dump";
import { IDENTITY_NOT_TRUSTED_DATASET } from "../../test-utils/test_indexeddb_cryptostore_dump/unverified";
import { FULL_ACCOUNT_DATASET } from "../../test-utils/test_indexeddb_cryptostore_dump/full_account";
jest.setTimeout(15000);
@@ -93,150 +96,197 @@ describe("MatrixClient.initRustCrypto", () => {
await matrixClient.initRustCrypto();
});
it("should migrate from libolm", async () => {
fetchMock.get("path:/_matrix/client/v3/room_keys/version", {
auth_data: {
public_key: "q+HZiJdHl2Yopv9GGvv7EYSzDMrAiRknK4glSdoaomI",
signatures: {
"@vdhtest200713:matrix.org": {
"ed25519:gh9fGr39eNZUdWynEMJ/q/WZq/Pk/foFxHXFBFm18ZI":
"reDp6Mu+j+tfUL3/T6f5OBT3N825Lzpc43vvG+RvjX6V+KxXzodBQArgCoeEHLtL9OgSBmNrhTkSOX87MWCKAw",
"ed25519:KMFSTJSMLB":
"F8tyV5W6wNi0GXTdSg+gxSCULQi0EYxdAAqfkyNq58KzssZMw5i+PRA0aI2b+D7NH/aZaJrtiYNHJ0gWLSQvAw",
},
},
},
version: "7",
algorithm: "m.megolm_backup.v1.curve25519-aes-sha2",
etag: "1",
count: 79,
describe("Libolm Migration", () => {
beforeEach(() => {
fetchMock.reset();
});
fetchMock.post("path:/_matrix/client/v3/keys/query", {
device_keys: {
"@vdhtest200713:matrix.org": {
KMFSTJSMLB: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "KMFSTJSMLB",
keys: {
"curve25519:KMFSTJSMLB": "LKv0bKbc0EC4h0jknbemv3QalEkeYvuNeUXVRgVVTTU",
"ed25519:KMFSTJSMLB": "qK70DEqIXq7T+UU3v/al47Ab4JkMEBLpNrTBMbS5rrw",
},
user_id: "@vdhtest200713:matrix.org",
signatures: {
"@vdhtest200713:matrix.org": {
"ed25519:KMFSTJSMLB":
"aE+PdxLAdwQ/xfJwLmqebvt/lrT97fZas2SQFFrM+dPmHxQtjyS8csm88BLfGRjJKK1B/vWev3AaKqQZwLTUAw",
"ed25519:lDvg6vi3P80L9XFNpUSU+5Y87m3p6yHcC83jhSU4Q5k":
"lCd4SA/JT1nnxsgN9yQaLJQhH5hkLMVVx6ba5JAjL1wpWVqyPxzMJHImX6vTztk6S8rybcdfYkea5W/Ii+4HCQ",
},
},
it("should migrate from libolm", async () => {
fetchMock.get("path:/_matrix/client/v3/room_keys/version", FULL_ACCOUNT_DATASET.backupResponse);
fetchMock.post("path:/_matrix/client/v3/keys/query", FULL_ACCOUNT_DATASET.keyQueryResponse);
const testStoreName = "test-store";
await populateStore(testStoreName, FULL_ACCOUNT_DATASET.dumpPath);
const cryptoStore = new IndexedDBCryptoStore(indexedDB, testStoreName);
const matrixClient = createClient({
baseUrl: "http://test.server",
userId: FULL_ACCOUNT_DATASET.userId,
deviceId: FULL_ACCOUNT_DATASET.deviceId,
cryptoStore,
pickleKey: FULL_ACCOUNT_DATASET.pickleKey,
});
const progressListener = jest.fn();
matrixClient.addListener(CryptoEvent.LegacyCryptoStoreMigrationProgress, progressListener);
await matrixClient.initRustCrypto();
const verificationStatus = await matrixClient
.getCrypto()!
.getDeviceVerificationStatus(FULL_ACCOUNT_DATASET.userId, FULL_ACCOUNT_DATASET.deviceId);
// Check that the current device and identity trust is migrated correctly just after migration
expect(verificationStatus).toBeDefined();
expect(verificationStatus!.crossSigningVerified).toEqual(true);
expect(verificationStatus!.signedByOwner).toEqual(true);
// Do some basic checks on the imported data
const deviceKeys = await matrixClient.getCrypto()!.getOwnDeviceKeys();
expect(deviceKeys.curve25519).toEqual("LKv0bKbc0EC4h0jknbemv3QalEkeYvuNeUXVRgVVTTU");
expect(deviceKeys.ed25519).toEqual("qK70DEqIXq7T+UU3v/al47Ab4JkMEBLpNrTBMbS5rrw");
expect(await matrixClient.getCrypto()!.getActiveSessionBackupVersion()).toEqual("7");
expect(await matrixClient.getCrypto()!.isEncryptionEnabledInRoom("!CWLUCoEWXSFyTCOtfL:matrix.org")).toBe(
true,
);
// check the progress callback
expect(progressListener.mock.calls.length).toBeGreaterThan(50);
// The first call should have progress == 0
const [firstProgress, totalSteps] = progressListener.mock.calls[0];
expect(totalSteps).toBeGreaterThan(3000);
expect(firstProgress).toEqual(0);
for (let i = 1; i < progressListener.mock.calls.length - 1; i++) {
const [progress, total] = progressListener.mock.calls[i];
expect(total).toEqual(totalSteps);
expect(progress).toBeGreaterThan(progressListener.mock.calls[i - 1][0]);
expect(progress).toBeLessThanOrEqual(totalSteps);
}
// The final call should have progress == total == -1
expect(progressListener).toHaveBeenLastCalledWith(-1, -1);
}, 60000);
describe("Legacy trust migration", () => {
async function populateAndStartLegacyCryptoStore(dumpPath: string): Promise<IndexedDBCryptoStore> {
const testStoreName = "test-store";
await populateStore(testStoreName, dumpPath);
const cryptoStore = new IndexedDBCryptoStore(indexedDB, testStoreName);
await cryptoStore.startup();
return cryptoStore;
}
it("should not revert to untrusted if legacy was trusted but msk not in cache, big account", async () => {
fetchMock.get("path:/_matrix/client/v3/room_keys/version", {
status: 404,
body: {
errcode: "M_NOT_FOUND",
error: "No backup found",
},
},
},
master_keys: {
"@vdhtest200713:matrix.org": {
user_id: "@vdhtest200713:matrix.org",
usage: ["master"],
keys: {
"ed25519:gh9fGr39eNZUdWynEMJ/q/WZq/Pk/foFxHXFBFm18ZI":
"gh9fGr39eNZUdWynEMJ/q/WZq/Pk/foFxHXFBFm18ZI",
});
fetchMock.post("path:/_matrix/client/v3/keys/query", FULL_ACCOUNT_DATASET.keyQueryResponse);
const cryptoStore = await populateAndStartLegacyCryptoStore(FULL_ACCOUNT_DATASET.dumpPath);
// Remove the master key from the cache
await cryptoStore.doTxn("readwrite", [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => {
const objectStore = txn.objectStore("account");
objectStore.delete(`ssss_cache:master`);
});
const matrixClient = createClient({
baseUrl: "http://test.server",
userId: FULL_ACCOUNT_DATASET.userId,
deviceId: FULL_ACCOUNT_DATASET.deviceId,
cryptoStore,
pickleKey: FULL_ACCOUNT_DATASET.pickleKey,
});
await matrixClient.initRustCrypto();
const verificationStatus = await matrixClient
.getCrypto()!
.getUserVerificationStatus(FULL_ACCOUNT_DATASET.userId);
expect(verificationStatus.isCrossSigningVerified()).toBe(true);
}, 60000);
it("should not revert to untrusted if legacy was trusted but msk not in cache", async () => {
fetchMock.get("path:/_matrix/client/v3/room_keys/version", MSK_NOT_CACHED_DATASET.backupResponse);
fetchMock.post("path:/_matrix/client/v3/keys/query", MSK_NOT_CACHED_DATASET.keyQueryResponse);
const cryptoStore = await populateAndStartLegacyCryptoStore(MSK_NOT_CACHED_DATASET.dumpPath);
const matrixClient = createClient({
baseUrl: "http://test.server",
userId: MSK_NOT_CACHED_DATASET.userId,
deviceId: MSK_NOT_CACHED_DATASET.deviceId,
cryptoStore,
pickleKey: MSK_NOT_CACHED_DATASET.pickleKey,
});
await matrixClient.initRustCrypto();
const verificationStatus = await matrixClient
.getCrypto()!
.getUserVerificationStatus("@migration:localhost");
expect(verificationStatus.isCrossSigningVerified()).toBe(true);
});
it("should not migrate local trust if key has changed", async () => {
fetchMock.get("path:/_matrix/client/v3/room_keys/version", MSK_NOT_CACHED_DATASET.backupResponse);
fetchMock.post("path:/_matrix/client/v3/keys/query", MSK_NOT_CACHED_DATASET.rotatedKeyQueryResponse);
const cryptoStore = await populateAndStartLegacyCryptoStore(MSK_NOT_CACHED_DATASET.dumpPath);
const matrixClient = createClient({
baseUrl: "http://test.server",
userId: MSK_NOT_CACHED_DATASET.userId,
deviceId: MSK_NOT_CACHED_DATASET.deviceId,
cryptoStore,
pickleKey: MSK_NOT_CACHED_DATASET.pickleKey,
});
await matrixClient.initRustCrypto();
const verificationStatus = await matrixClient
.getCrypto()!
.getUserVerificationStatus("@migration:localhost");
expect(verificationStatus.isCrossSigningVerified()).toBe(false);
});
it("should not migrate local trust if was not trusted in legacy", async () => {
// Just 404 here for the test
fetchMock.get("path:/_matrix/client/v3/room_keys/version", {
status: 404,
body: {
errcode: "M_NOT_FOUND",
error: "No backup found",
},
signatures: {
"@vdhtest200713:matrix.org": {
"ed25519:MWOGVUTXZN":
"stOu1aHbhsWB/Aj5M/HqBR83QzME+682C995Uc8JxSmmyrlWmgG8QrnoUDG2OFR1t6zNQ+QLEilU4WNEOV73DQ",
},
},
},
},
self_signing_keys: {
"@vdhtest200713:matrix.org": {
user_id: "@vdhtest200713:matrix.org",
usage: ["self_signing"],
keys: {
"ed25519:lDvg6vi3P80L9XFNpUSU+5Y87m3p6yHcC83jhSU4Q5k":
"lDvg6vi3P80L9XFNpUSU+5Y87m3p6yHcC83jhSU4Q5k",
},
signatures: {
"@vdhtest200713:matrix.org": {
"ed25519:gh9fGr39eNZUdWynEMJ/q/WZq/Pk/foFxHXFBFm18ZI":
"HKTC7NoBhAkfJtmemmkn/HvCCgBQViWZ0uH7aGPRaWMDFgD8T7Q+y1j3FKZv4mhSopR85Fq3FRyXsG8OVvGeBA",
},
},
},
},
user_signing_keys: {
"@vdhtest200713:matrix.org": {
user_id: "@vdhtest200713:matrix.org",
usage: ["user_signing"],
keys: {
"ed25519:YShqO/3u5vQ0uucojraWrtoLrek0CYrurN/vH/YPMg8":
"YShqO/3u5vQ0uucojraWrtoLrek0CYrurN/vH/YPMg8",
},
signatures: {
"@vdhtest200713:matrix.org": {
"ed25519:gh9fGr39eNZUdWynEMJ/q/WZq/Pk/foFxHXFBFm18ZI":
"u8VOi4IaeRJwDgy2ftK02NJQPdBijy8f/0+WnHGG72yfOvMthwWzEw8SrRSNG8glBNrfHinKwCyJJzAJwyepCQ",
},
},
},
},
});
fetchMock.post("path:/_matrix/client/v3/keys/query", IDENTITY_NOT_TRUSTED_DATASET.keyQueryResponse);
const cryptoStore = await populateAndStartLegacyCryptoStore(IDENTITY_NOT_TRUSTED_DATASET.dumpPath);
const matrixClient = createClient({
baseUrl: "http://test.server",
userId: IDENTITY_NOT_TRUSTED_DATASET.userId,
deviceId: IDENTITY_NOT_TRUSTED_DATASET.deviceId,
cryptoStore,
pickleKey: IDENTITY_NOT_TRUSTED_DATASET.pickleKey,
});
await matrixClient.initRustCrypto();
const verificationStatus = await matrixClient
.getCrypto()!
.getUserVerificationStatus("@untrusted:localhost");
expect(verificationStatus.isCrossSigningVerified()).toBe(false);
});
});
const testStoreName = "test-store";
await populateStore(testStoreName);
const cryptoStore = new IndexedDBCryptoStore(indexedDB, testStoreName);
const matrixClient = createClient({
baseUrl: "http://test.server",
userId: "@vdhtest200713:matrix.org",
deviceId: "KMFSTJSMLB",
cryptoStore,
pickleKey: "+1k2Ppd7HIisUY824v7JtV3/oEE4yX0TqtmNPyhaD7o",
});
const progressListener = jest.fn();
matrixClient.addListener(CryptoEvent.LegacyCryptoStoreMigrationProgress, progressListener);
await matrixClient.initRustCrypto();
const verificationStatus = await matrixClient
.getCrypto()!
.getDeviceVerificationStatus("@vdhtest200713:matrix.org", "KMFSTJSMLB");
// Check that the current device and identity trust is migrated correctly just after migration
expect(verificationStatus).toBeDefined();
expect(verificationStatus!.crossSigningVerified).toEqual(true);
expect(verificationStatus!.signedByOwner).toEqual(true);
// Do some basic checks on the imported data
const deviceKeys = await matrixClient.getCrypto()!.getOwnDeviceKeys();
expect(deviceKeys.curve25519).toEqual("LKv0bKbc0EC4h0jknbemv3QalEkeYvuNeUXVRgVVTTU");
expect(deviceKeys.ed25519).toEqual("qK70DEqIXq7T+UU3v/al47Ab4JkMEBLpNrTBMbS5rrw");
expect(await matrixClient.getCrypto()!.getActiveSessionBackupVersion()).toEqual("7");
expect(await matrixClient.getCrypto()!.isEncryptionEnabledInRoom("!CWLUCoEWXSFyTCOtfL:matrix.org")).toBe(true);
// check the progress callback
expect(progressListener.mock.calls.length).toBeGreaterThan(50);
// The first call should have progress == 0
const [firstProgress, totalSteps] = progressListener.mock.calls[0];
expect(totalSteps).toBeGreaterThan(3000);
expect(firstProgress).toEqual(0);
for (let i = 1; i < progressListener.mock.calls.length - 1; i++) {
const [progress, total] = progressListener.mock.calls[i];
expect(total).toEqual(totalSteps);
expect(progress).toBeGreaterThan(progressListener.mock.calls[i - 1][0]);
expect(progress).toBeLessThanOrEqual(totalSteps);
}
// The final call should have progress == total == -1
expect(progressListener).toHaveBeenLastCalledWith(-1, -1);
}, 60000);
});
});
describe("MatrixClient.clearStores", () => {
+4 -3
View File
@@ -19,6 +19,7 @@ limitations under the License.
import { TestClient } from "../TestClient";
import * as testUtils from "../test-utils/test-utils";
import { logger } from "../../src/logger";
import { KnownMembership } from "../../src/@types/membership";
const ROOM_ID = "!room:id";
@@ -43,7 +44,7 @@ function getSyncResponse(roomMembers: string[]) {
stateEvents,
roomMembers.map((m) =>
testUtils.mkMembership({
mship: "join",
mship: KnownMembership.Join,
sender: m,
}),
),
@@ -323,7 +324,7 @@ describe("DeviceList management:", function () {
timeline: {
events: [
testUtils.mkMembership({
mship: "leave",
mship: KnownMembership.Leave,
sender: "@bob:xyz",
}),
],
@@ -357,7 +358,7 @@ describe("DeviceList management:", function () {
timeline: {
events: [
testUtils.mkMembership({
mship: "leave",
mship: KnownMembership.Leave,
sender: "@bob:xyz",
}),
],
@@ -28,6 +28,7 @@ import {
} from "../../src";
import * as utils from "../test-utils/test-utils";
import { TestClient } from "../TestClient";
import { KnownMembership } from "../../src/@types/membership";
describe("MatrixClient events", function () {
const selfUserId = "@alice:localhost";
@@ -85,7 +86,7 @@ describe("MatrixClient events", function () {
events: [
utils.mkMembership({
room: "!erufh:bar",
mship: "join",
mship: KnownMembership.Join,
user: "@foo:bar",
}),
utils.mkEvent({
@@ -272,7 +273,7 @@ describe("MatrixClient events", function () {
membersInvokeCount++;
expect(member.roomId).toEqual("!erufh:bar");
expect(member.userId).toEqual("@foo:bar");
expect(member.membership).toEqual("join");
expect(member.membership).toEqual(KnownMembership.Join);
});
client!.on(RoomStateEvent.NewMember, function (event, state, member) {
newMemberInvokeCount++;
@@ -310,7 +311,7 @@ describe("MatrixClient events", function () {
});
client!.on(RoomMemberEvent.Membership, function (event, member) {
membershipInvokeCount++;
expect(member.membership).toEqual("join");
expect(member.membership).toEqual(KnownMembership.Join);
});
client!.startClient();
@@ -36,6 +36,7 @@ import { TestClient } from "../TestClient";
import { FeatureSupport, Thread, ThreadEvent } from "../../src/models/thread";
import { emitPromise } from "../test-utils/test-utils";
import { Feature, ServerSupport } from "../../src/feature";
import { KnownMembership } from "../../src/@types/membership";
const userId = "@alice:localhost";
const userName = "Alice";
@@ -63,7 +64,7 @@ const buildRelationPaginationQuery = (params: QueryDict): string => {
const USER_MEMBERSHIP_EVENT = utils.mkMembership({
room: roomId,
mship: "join",
mship: KnownMembership.Join,
user: userId,
name: userName,
event: false,
@@ -98,7 +99,7 @@ const INITIAL_SYNC_DATA = {
events: [
withoutRoomId(ROOM_NAME_EVENT),
utils.mkMembership({
mship: "join",
mship: KnownMembership.Join,
user: otherUserId,
name: "Bob",
event: false,
@@ -607,11 +608,6 @@ describe("MatrixClient event timelines", function () {
await client.stopClient(); // we don't need the client to be syncing at this time
const room = client.getRoom(roomId)!;
httpBackend
.when("GET", "/rooms/!foo%3Abar/event/" + encodeURIComponent(THREAD_ROOT.event_id!))
.respond(200, function () {
return THREAD_ROOT;
});
httpBackend
.when("GET", "/rooms/!foo%3Abar/event/" + encodeURIComponent(THREAD_ROOT.event_id!))
.respond(200, function () {
@@ -1545,9 +1541,7 @@ describe("MatrixClient event timelines", function () {
expect(timelineSets).not.toBeNull();
respondToThreads(threadsResponse);
respondToThreads(threadsResponse);
respondToEvent(THREAD_ROOT);
respondToEvent(THREAD2_ROOT);
respondToThread(THREAD_ROOT, [THREAD_REPLY]);
respondToThread(THREAD2_ROOT, [THREAD2_REPLY]);
await flushHttp(room.fetchRoomThreads());
const threadIds = room.getThreads().map((thread) => thread.id);
@@ -1566,7 +1560,6 @@ describe("MatrixClient event timelines", function () {
thread.initialEventsFetched = true;
const prom = emitPromise(room, ThreadEvent.NewReply);
respondToEvent(THREAD_ROOT_UPDATED);
respondToEvent(THREAD2_ROOT);
await room.addLiveEvents([THREAD_REPLY2]);
await httpBackend.flushAllExpected();
await prom;
@@ -1643,7 +1636,7 @@ describe("MatrixClient event timelines", function () {
...THREAD_ROOT.unsigned!["m.relations"],
"io.element.thread": {
...THREAD_ROOT.unsigned!["m.relations"]!["io.element.thread"],
count: 2,
count: 1,
latest_event: THREAD_REPLY,
},
},
@@ -1692,7 +1685,6 @@ describe("MatrixClient event timelines", function () {
thread.initialEventsFetched = true;
const prom = emitPromise(room, ThreadEvent.Update);
respondToEvent(THREAD_ROOT_UPDATED);
respondToEvent(THREAD2_ROOT);
await room.addLiveEvents([THREAD_REPLY_REACTION]);
await httpBackend.flushAllExpected();
await prom;
@@ -1930,7 +1922,7 @@ describe("MatrixClient event timelines", function () {
// a state event, followed by a redaction thereof
const event = utils.mkMembership({
mship: "join",
mship: KnownMembership.Join,
user: otherUserId,
});
const redaction = utils.mkEvent({
@@ -2007,11 +1999,6 @@ describe("MatrixClient event timelines", function () {
},
},
});
httpBackend
.when("GET", "/rooms/!foo%3Abar/event/" + encodeURIComponent(THREAD_ROOT.event_id!))
.respond(200, function () {
return THREAD_ROOT;
});
httpBackend
.when("GET", "/rooms/!foo%3Abar/event/" + encodeURIComponent(THREAD_ROOT.event_id!))
.respond(200, function () {
+6 -5
View File
@@ -27,6 +27,7 @@ import { ISearchResults } from "../../src/@types/search";
import { IStore } from "../../src/store";
import { CryptoBackend } from "../../src/common-crypto/CryptoBackend";
import { SetPresence } from "../../src/sync";
import { KnownMembership } from "../../src/@types/membership";
describe("MatrixClient", function () {
const userId = "@alice:localhost";
@@ -162,7 +163,7 @@ describe("MatrixClient", function () {
utils.mkMembership({
user: userId,
room: roomId,
mship: "join",
mship: KnownMembership.Join,
event: true,
}),
]);
@@ -182,7 +183,7 @@ describe("MatrixClient", function () {
utils.mkMembership({
user: userId,
room: roomId,
mship: "join",
mship: KnownMembership.Join,
event: true,
}),
]);
@@ -269,7 +270,7 @@ describe("MatrixClient", function () {
utils.mkMembership({
user: userId,
room: roomId,
mship: "knock",
mship: KnownMembership.Knock,
event: true,
}),
]);
@@ -1912,7 +1913,7 @@ const buildEventJoinRules = () =>
new MatrixEvent({
age: 80123696,
content: {
join_rule: "invite",
join_rule: KnownMembership.Invite,
},
event_id: "$6JDDeDp7fEc0F6YnTWMruNcKWFltR3e9wk7wWDDJrAU",
origin_server_ts: 1643815441191,
@@ -1966,7 +1967,7 @@ const buildEventMember = () =>
content: {
avatar_url: "mxc://matrix.org/aNtbVcFfwotudypZcHsIcPOc",
displayname: "andybalaam-test1",
membership: "join",
membership: KnownMembership.Join,
},
event_id: "$Ex5eVmMs_ti784mo8bgddynbwLvy6231lCycJr7Cl9M",
origin_server_ts: 1643815439608,
+3 -2
View File
@@ -6,6 +6,7 @@ import { MatrixScheduler } from "../../src/scheduler";
import { MemoryStore } from "../../src/store/memory";
import { MatrixError } from "../../src/http-api";
import { IStore } from "../../src/store";
import { KnownMembership } from "../../src/@types/membership";
describe("MatrixClient opts", function () {
const baseUrl = "http://localhost.or.something";
@@ -43,13 +44,13 @@ describe("MatrixClient opts", function () {
}),
utils.mkMembership({
room: roomId,
mship: "join",
mship: KnownMembership.Join,
user: userB,
name: "Bob",
}),
utils.mkMembership({
room: roomId,
mship: "join",
mship: KnownMembership.Join,
user: userId,
name: "Alice",
}),
+13 -12
View File
@@ -30,6 +30,7 @@ import {
Room,
} from "../../src";
import { TestClient } from "../TestClient";
import { KnownMembership } from "../../src/@types/membership";
describe("MatrixClient room timelines", function () {
const userId = "@alice:localhost";
@@ -42,7 +43,7 @@ describe("MatrixClient room timelines", function () {
const USER_MEMBERSHIP_EVENT = utils.mkMembership({
room: roomId,
mship: "join",
mship: KnownMembership.Join,
user: userId,
name: userName,
});
@@ -76,7 +77,7 @@ describe("MatrixClient room timelines", function () {
ROOM_NAME_EVENT,
utils.mkMembership({
room: roomId,
mship: "join",
mship: KnownMembership.Join,
user: otherUserId,
name: "Bob",
}),
@@ -316,7 +317,7 @@ describe("MatrixClient room timelines", function () {
// make an m.room.member event for alice's join
const joinMshipEvent = utils.mkMembership({
mship: "join",
mship: KnownMembership.Join,
user: userId,
room: roomId,
name: "Old Alice",
@@ -326,7 +327,7 @@ describe("MatrixClient room timelines", function () {
// make an m.room.member event with prev_content for alice's nick
// change
const oldMshipEvent = utils.mkMembership({
mship: "join",
mship: KnownMembership.Join,
user: userId,
room: roomId,
name: userName,
@@ -335,7 +336,7 @@ describe("MatrixClient room timelines", function () {
oldMshipEvent.prev_content = {
displayname: "Old Alice",
avatar_url: undefined,
membership: "join",
membership: KnownMembership.Join,
};
// set the list of events to return on scrollback (/messages)
@@ -487,7 +488,7 @@ describe("MatrixClient room timelines", function () {
utils.mkMembership({
user: userId,
room: roomId,
mship: "join",
mship: KnownMembership.Join,
name: "New Name",
}),
utils.mkMessage({ user: userId, room: roomId }),
@@ -554,13 +555,13 @@ describe("MatrixClient room timelines", function () {
utils.mkMembership({
user: userC,
room: roomId,
mship: "join",
mship: KnownMembership.Join,
name: "C",
}),
utils.mkMembership({
user: userC,
room: roomId,
mship: "invite",
mship: KnownMembership.Invite,
skey: userD,
}),
];
@@ -571,9 +572,9 @@ describe("MatrixClient room timelines", function () {
return Promise.all([httpBackend!.flush("/sync", 1), utils.syncPromise(client!)]).then(function () {
expect(room.currentState.getMembers().length).toEqual(4);
expect(room.currentState.getMember(userC)!.name).toEqual("C");
expect(room.currentState.getMember(userC)!.membership).toEqual("join");
expect(room.currentState.getMember(userC)!.membership).toEqual(KnownMembership.Join);
expect(room.currentState.getMember(userD)!.name).toEqual(userD);
expect(room.currentState.getMember(userD)!.membership).toEqual("invite");
expect(room.currentState.getMember(userD)!.membership).toEqual(KnownMembership.Invite);
});
});
});
@@ -598,9 +599,9 @@ describe("MatrixClient room timelines", function () {
expect(room.timeline[0].event).toEqual(eventData[0]);
expect(room.currentState.getMembers().length).toEqual(2);
expect(room.currentState.getMember(userId)!.name).toEqual(userName);
expect(room.currentState.getMember(userId)!.membership).toEqual("join");
expect(room.currentState.getMember(userId)!.membership).toEqual(KnownMembership.Join);
expect(room.currentState.getMember(otherUserId)!.name).toEqual("Bob");
expect(room.currentState.getMember(otherUserId)!.membership).toEqual("join");
expect(room.currentState.getMember(otherUserId)!.membership).toEqual(KnownMembership.Join);
});
});
});
+43 -42
View File
@@ -46,6 +46,7 @@ import * as utils from "../test-utils/test-utils";
import { TestClient } from "../TestClient";
import { emitPromise, mkEvent, mkMessage } from "../test-utils/test-utils";
import { THREAD_RELATION_TYPE } from "../../src/models/thread";
import { KnownMembership } from "../../src/@types/membership";
describe("MatrixClient syncing", () => {
const selfUserId = "@alice:localhost";
@@ -123,7 +124,7 @@ describe("MatrixClient syncing", () => {
type: "m.room.member",
state_key: selfUserId,
content: {
membership: "invite",
membership: KnownMembership.Invite,
},
},
],
@@ -151,10 +152,10 @@ describe("MatrixClient syncing", () => {
type: "m.room.member",
state_key: selfUserId,
content: {
membership: "leave",
membership: KnownMembership.Leave,
},
prev_content: {
membership: "invite",
membership: KnownMembership.Invite,
},
// XXX: And other fields required on an event
},
@@ -167,10 +168,10 @@ describe("MatrixClient syncing", () => {
type: "m.room.member",
state_key: selfUserId,
content: {
membership: "leave",
membership: KnownMembership.Leave,
},
prev_content: {
membership: "invite",
membership: KnownMembership.Invite,
},
// XXX: And other fields required on an event
},
@@ -193,22 +194,22 @@ describe("MatrixClient syncing", () => {
// Room, string, string
fires++;
expect(room.roomId).toBe(roomId);
expect(membership).toBe("invite");
expect(membership).toBe(KnownMembership.Invite);
expect(oldMembership).toBeFalsy();
// Second fire: a leave
client!.once(RoomEvent.MyMembership, (room, membership, oldMembership) => {
fires++;
expect(room.roomId).toBe(roomId);
expect(membership).toBe("leave");
expect(oldMembership).toBe("invite");
expect(membership).toBe(KnownMembership.Leave);
expect(oldMembership).toBe(KnownMembership.Invite);
// Third/final fire: a second invite
client!.once(RoomEvent.MyMembership, (room, membership, oldMembership) => {
fires++;
expect(room.roomId).toBe(roomId);
expect(membership).toBe("invite");
expect(oldMembership).toBe("leave");
expect(membership).toBe(KnownMembership.Invite);
expect(oldMembership).toBe(KnownMembership.Leave);
});
});
@@ -238,7 +239,7 @@ describe("MatrixClient syncing", () => {
type: "m.room.member",
state_key: selfUserId,
content: {
membership: "knock",
membership: KnownMembership.Knock,
},
},
],
@@ -266,10 +267,10 @@ describe("MatrixClient syncing", () => {
type: "m.room.member",
state_key: selfUserId,
content: {
membership: "leave",
membership: KnownMembership.Leave,
},
prev_content: {
membership: "knock",
membership: KnownMembership.Knock,
},
// XXX: And other fields required on an event
},
@@ -282,10 +283,10 @@ describe("MatrixClient syncing", () => {
type: "m.room.member",
state_key: selfUserId,
content: {
membership: "leave",
membership: KnownMembership.Leave,
},
prev_content: {
membership: "knock",
membership: KnownMembership.Knock,
},
// XXX: And other fields required on an event
},
@@ -308,22 +309,22 @@ describe("MatrixClient syncing", () => {
// Room, string, string
fires++;
expect(room.roomId).toBe(roomId);
expect(membership).toBe("knock");
expect(membership).toBe(KnownMembership.Knock);
expect(oldMembership).toBeFalsy();
// Second fire: a leave
client!.once(RoomEvent.MyMembership, (room, membership, oldMembership) => {
fires++;
expect(room.roomId).toBe(roomId);
expect(membership).toBe("leave");
expect(oldMembership).toBe("knock");
expect(membership).toBe(KnownMembership.Leave);
expect(oldMembership).toBe(KnownMembership.Knock);
// Third/final fire: a second knock
client!.once(RoomEvent.MyMembership, (room, membership, oldMembership) => {
fires++;
expect(room.roomId).toBe(roomId);
expect(membership).toBe("knock");
expect(oldMembership).toBe("leave");
expect(membership).toBe(KnownMembership.Knock);
expect(oldMembership).toBe(KnownMembership.Leave);
});
});
@@ -381,7 +382,7 @@ describe("MatrixClient syncing", () => {
type: "m.room.member",
state_key: selfUserId,
content: {
membership: "invite",
membership: KnownMembership.Invite,
},
},
],
@@ -421,7 +422,7 @@ describe("MatrixClient syncing", () => {
type: "m.room.member",
state_key: selfUserId,
content: {
membership: "knock",
membership: KnownMembership.Knock,
},
},
],
@@ -533,12 +534,12 @@ describe("MatrixClient syncing", () => {
events: [
utils.mkMembership({
room: roomOne,
mship: "join",
mship: KnownMembership.Join,
user: otherUserId,
}),
utils.mkMembership({
room: roomOne,
mship: "join",
mship: KnownMembership.Join,
user: selfUserId,
}),
utils.mkEvent({
@@ -556,7 +557,7 @@ describe("MatrixClient syncing", () => {
syncData.rooms.join[roomOne].state.events.push(
utils.mkMembership({
room: roomOne,
mship: "invite",
mship: KnownMembership.Invite,
user: userC,
}) as IStateEvent,
);
@@ -589,7 +590,7 @@ describe("MatrixClient syncing", () => {
syncData.rooms.join[roomOne].state.events.push(
utils.mkMembership({
room: roomOne,
mship: "invite",
mship: KnownMembership.Invite,
user: userC,
}) as IStateEvent,
);
@@ -617,7 +618,7 @@ describe("MatrixClient syncing", () => {
syncData.rooms.join[roomOne].state.events.push(
utils.mkMembership({
room: roomOne,
mship: "invite",
mship: KnownMembership.Invite,
user: userC,
}) as IStateEvent,
);
@@ -644,7 +645,7 @@ describe("MatrixClient syncing", () => {
syncData.rooms.join[roomOne].state.events.push(
utils.mkMembership({
room: roomOne,
mship: "invite",
mship: KnownMembership.Invite,
user: userC,
}) as IStateEvent,
);
@@ -719,12 +720,12 @@ describe("MatrixClient syncing", () => {
}),
utils.mkMembership({
room: roomOne,
mship: "join",
mship: KnownMembership.Join,
user: otherUserId,
}),
utils.mkMembership({
room: roomOne,
mship: "join",
mship: KnownMembership.Join,
user: selfUserId,
}),
utils.mkEvent({
@@ -750,13 +751,13 @@ describe("MatrixClient syncing", () => {
events: [
utils.mkMembership({
room: roomTwo,
mship: "join",
mship: KnownMembership.Join,
user: otherUserId,
name: otherDisplayName,
}),
utils.mkMembership({
room: roomTwo,
mship: "join",
mship: KnownMembership.Join,
user: selfUserId,
}),
utils.mkEvent({
@@ -1247,7 +1248,7 @@ describe("MatrixClient syncing", () => {
const USER_MEMBERSHIP_EVENT = utils.mkMembership({
room: roomOne,
mship: "join",
mship: KnownMembership.Join,
user: userA,
});
@@ -1508,12 +1509,12 @@ describe("MatrixClient syncing", () => {
}),
utils.mkMembership({
room: roomOne,
mship: "join",
mship: KnownMembership.Join,
user: otherUserId,
}),
utils.mkMembership({
room: roomOne,
mship: "join",
mship: KnownMembership.Join,
user: selfUserId,
}),
utils.mkEvent({
@@ -1605,12 +1606,12 @@ describe("MatrixClient syncing", () => {
}),
utils.mkMembership({
room: roomOne,
mship: "join",
mship: KnownMembership.Join,
user: otherUserId,
}),
utils.mkMembership({
room: roomOne,
mship: "join",
mship: KnownMembership.Join,
user: selfUserId,
}),
utils.mkEvent({
@@ -1761,12 +1762,12 @@ describe("MatrixClient syncing", () => {
events: [
utils.mkMembership({
room: roomId,
mship: "join",
mship: KnownMembership.Join,
user: otherUserId,
}),
utils.mkMembership({
room: roomId,
mship: "join",
mship: KnownMembership.Join,
user: selfUserId,
}),
utils.mkEvent({
@@ -1901,7 +1902,7 @@ describe("MatrixClient syncing", () => {
it("should return a room based on the room initialSync API", async () => {
httpBackend!.when("GET", `/rooms/${encodeURIComponent(roomOne)}/initialSync`).respond(200, {
room_id: roomOne,
membership: "leave",
membership: KnownMembership.Leave,
messages: {
start: "start",
end: "end",
@@ -1950,7 +1951,7 @@ describe("MatrixClient syncing", () => {
const room = await prom;
expect(room.roomId).toBe(roomOne);
expect(room.getMyMembership()).toBe("leave");
expect(room.getMyMembership()).toBe(KnownMembership.Leave);
expect(room.name).toBe("Room Name");
expect(room.currentState.getStateEvents("m.room.name", "")?.getId()).toBe("$eventId");
expect(room.timeline[0].getContent().body).toBe("Message 1");
@@ -2042,7 +2043,7 @@ describe("MatrixClient syncing (IndexedDB version)", () => {
type: "m.room.member",
state_key: selfUserId,
content: {
membership: "invite",
membership: KnownMembership.Invite,
},
},
],
@@ -34,6 +34,7 @@ import { TestClient } from "../TestClient";
import { ReceiptType } from "../../src/@types/read_receipts";
import { mkThread } from "../test-utils/thread";
import { SyncState } from "../../src/sync";
import { KnownMembership } from "../../src/@types/membership";
const userA = "@alice:localhost";
const userB = "@bob:localhost";
@@ -229,7 +230,7 @@ describe("MatrixClient syncing", () => {
content: {
avatar_url: "",
displayname: userB,
membership: "join",
membership: KnownMembership.Join,
},
origin_server_ts: 2,
sender: userB,
@@ -270,7 +271,7 @@ describe("MatrixClient syncing", () => {
},
{
content: {
join_rule: "invite",
join_rule: KnownMembership.Invite,
},
origin_server_ts: 4,
sender: userB,
@@ -316,7 +317,7 @@ describe("MatrixClient syncing", () => {
avatar_url: "",
displayname: userA,
is_direct: true,
membership: "invite",
membership: KnownMembership.Invite,
},
origin_server_ts: 8,
sender: userB,
@@ -338,7 +339,7 @@ describe("MatrixClient syncing", () => {
content: {
avatar_url: "",
displayname: userA,
membership: "join",
membership: KnownMembership.Join,
},
origin_server_ts: 10,
sender: userA,
+20 -19
View File
@@ -43,6 +43,7 @@ import { IStoredClientOpts } from "../../src";
import { logger } from "../../src/logger";
import { emitPromise } from "../test-utils/test-utils";
import { defer } from "../../src/utils";
import { KnownMembership } from "../../src/@types/membership";
describe("SlidingSyncSdk", () => {
let client: MatrixClient | undefined;
@@ -189,7 +190,7 @@ describe("SlidingSyncSdk", () => {
name: "A",
required_state: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: "join" }, selfUserId),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
mkOwnStateEvent(EventType.RoomName, { name: "A" }, ""),
],
@@ -204,7 +205,7 @@ describe("SlidingSyncSdk", () => {
required_state: [],
timeline: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: "join" }, selfUserId),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
mkOwnEvent(EventType.RoomMessage, { body: "hello B" }),
mkOwnEvent(EventType.RoomMessage, { body: "world B" }),
@@ -216,7 +217,7 @@ describe("SlidingSyncSdk", () => {
required_state: [],
timeline: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: "join" }, selfUserId),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
mkOwnEvent(EventType.RoomMessage, { body: "hello C" }),
mkOwnEvent(EventType.RoomMessage, { body: "world C" }),
@@ -229,7 +230,7 @@ describe("SlidingSyncSdk", () => {
required_state: [],
timeline: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: "join" }, selfUserId),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
mkOwnEvent(EventType.RoomMessage, { body: "hello D" }),
mkOwnEvent(EventType.RoomMessage, { body: "world D" }),
@@ -244,7 +245,7 @@ describe("SlidingSyncSdk", () => {
invite_state: [
{
type: EventType.RoomMember,
content: { membership: "invite" },
content: { membership: KnownMembership.Invite },
state_key: selfUserId,
sender: "@bob:localhost",
event_id: "$room_e_invite",
@@ -265,7 +266,7 @@ describe("SlidingSyncSdk", () => {
name: "#foo:localhost",
required_state: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: "join" }, selfUserId),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
mkOwnStateEvent(EventType.RoomCanonicalAlias, { alias: "#foo:localhost" }, ""),
mkOwnStateEvent(EventType.RoomName, { name: "This should be ignored" }, ""),
@@ -281,7 +282,7 @@ describe("SlidingSyncSdk", () => {
required_state: [],
timeline: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: "join" }, selfUserId),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
],
joined_count: 5,
@@ -293,7 +294,7 @@ describe("SlidingSyncSdk", () => {
required_state: [],
timeline: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: "join" }, selfUserId),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
mkOwnEvent(EventType.RoomMessage, { body: "live event" }),
],
@@ -308,7 +309,7 @@ describe("SlidingSyncSdk", () => {
const gotRoom = client!.getRoom(roomA);
expect(gotRoom).toBeTruthy();
expect(gotRoom!.name).toEqual(data[roomA].name);
expect(gotRoom!.getMyMembership()).toEqual("join");
expect(gotRoom!.getMyMembership()).toEqual(KnownMembership.Join);
assertTimelineEvents(gotRoom!.getLiveTimeline().getEvents().slice(-2), data[roomA].timeline);
});
@@ -318,7 +319,7 @@ describe("SlidingSyncSdk", () => {
const gotRoom = client!.getRoom(roomB);
expect(gotRoom).toBeTruthy();
expect(gotRoom!.name).toEqual(data[roomB].name);
expect(gotRoom!.getMyMembership()).toEqual("join");
expect(gotRoom!.getMyMembership()).toEqual(KnownMembership.Join);
assertTimelineEvents(gotRoom!.getLiveTimeline().getEvents().slice(-5), data[roomB].timeline);
});
@@ -372,7 +373,7 @@ describe("SlidingSyncSdk", () => {
const gotRoom = client!.getRoom(roomH);
expect(gotRoom).toBeTruthy();
expect(gotRoom!.name).toEqual(data[roomH].name);
expect(gotRoom!.getMyMembership()).toEqual("join");
expect(gotRoom!.getMyMembership()).toEqual(KnownMembership.Join);
// check the entire timeline is correct
assertTimelineEvents(gotRoom!.getLiveTimeline().getEvents(), data[roomH].timeline);
await expect(seenLiveEventDeferred.promise).resolves.toBeTruthy();
@@ -383,7 +384,7 @@ describe("SlidingSyncSdk", () => {
await emitPromise(client!, ClientEvent.Room);
const gotRoom = client!.getRoom(roomE);
expect(gotRoom).toBeTruthy();
expect(gotRoom!.getMyMembership()).toEqual("invite");
expect(gotRoom!.getMyMembership()).toEqual(KnownMembership.Invite);
expect(gotRoom!.currentState.getJoinRule()).toEqual(JoinRule.Invite);
});
@@ -603,9 +604,9 @@ describe("SlidingSyncSdk", () => {
required_state: [],
timeline: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: "join" }, selfUserId),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: "invite" }, invitee),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Invite }, invitee),
],
});
await httpBackend!.flush("/profile", 1, 1000);
@@ -719,7 +720,7 @@ describe("SlidingSyncSdk", () => {
required_state: [],
timeline: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: "join" }, selfUserId),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
mkOwnEvent(EventType.RoomMessage, { body: "hello" }),
],
@@ -923,7 +924,7 @@ describe("SlidingSyncSdk", () => {
required_state: [],
timeline: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: "join" }, selfUserId),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
mkOwnEvent(EventType.RoomMessage, { body: "hello" }),
],
@@ -964,7 +965,7 @@ describe("SlidingSyncSdk", () => {
required_state: [],
timeline: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: "join" }, selfUserId),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
mkOwnEvent(EventType.RoomMessage, { body: "hello" }),
],
@@ -1050,12 +1051,12 @@ describe("SlidingSyncSdk", () => {
required_state: [],
timeline: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: "join" }, selfUserId),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
{
type: EventType.RoomMember,
state_key: alice,
content: { membership: "join" },
content: { membership: KnownMembership.Join },
sender: alice,
origin_server_ts: Date.now(),
event_id: "$alice",
+5 -5
View File
@@ -107,8 +107,8 @@ describe("SlidingSync", () => {
onRequest: (initial) => {
return { initial: initial };
},
onResponse: (res) => {
return {};
onResponse: async (res) => {
return;
},
when: () => ExtensionState.PreProcess,
};
@@ -1572,7 +1572,7 @@ describe("SlidingSync", () => {
onPreExtensionRequest = () => {
return extReq;
};
onPreExtensionResponse = (resp) => {
onPreExtensionResponse = async (resp) => {
extensionOnResponseCalled = true;
callbackOrder.push("onPreExtensionResponse");
expect(resp).toEqual(extResp);
@@ -1613,7 +1613,7 @@ describe("SlidingSync", () => {
return undefined;
};
let responseCalled = false;
onPreExtensionResponse = (resp) => {
onPreExtensionResponse = async (resp) => {
responseCalled = true;
};
httpBackend!
@@ -1649,7 +1649,7 @@ describe("SlidingSync", () => {
};
let responseCalled = false;
const callbackOrder: string[] = [];
onPostExtensionResponse = (resp) => {
onPostExtensionResponse = async (resp) => {
expect(resp).toEqual(extResp);
responseCalled = true;
callbackOrder.push("onPostExtensionResponse");
+4 -3
View File
@@ -19,6 +19,7 @@ import {
import { SyncState } from "../../src/sync";
import { eventMapperFor } from "../../src/event-mapper";
import { TEST_ROOM_ID } from "./test-data";
import { KnownMembership, Membership } from "../../src/@types/membership";
/**
* Return a promise that is resolved when the client next emits a
@@ -87,7 +88,7 @@ export function getSyncResponse(roomMembers: string[], roomId = TEST_ROOM_ID): I
for (let i = 0; i < roomMembers.length; i++) {
roomResponse.state.events.push(
mkMembershipCustom({
membership: "join",
membership: KnownMembership.Join,
sender: roomMembers[i],
}),
);
@@ -251,7 +252,7 @@ export function mkPresence(opts: IPresenceOpts & { event?: boolean }): Partial<I
interface IMembershipOpts {
room?: string;
mship: string;
mship: Membership;
sender?: string;
user?: string;
skey?: string;
@@ -297,7 +298,7 @@ export function mkMembership(opts: IMembershipOpts & { event?: boolean }): Parti
}
export function mkMembershipCustom<T>(
base: T & { membership: string; sender: string; content?: IContent },
base: T & { membership: Membership; sender: string; content?: IContent },
): T & { type: EventType; sender: string; state_key: string; content: IContent } & GeneratedMetadata {
const content = base.content || {};
return mkEventCustom({
@@ -1,11 +1,13 @@
## Dump of libolm indexeddb cryptostore
## Dumps of libolm indexeddb cryptostore
This directory contains a dump of a real indexeddb store from a session using
This directory contains several dumps of real indexeddb stores from a session using
libolm crypto.
The corresponding pickle key is `+1k2Ppd7HIisUY824v7JtV3/oEE4yX0TqtmNPyhaD7o`.
Each directory contains, in dump.json, a dump of data created by pasting the following
code into the browser console; and in index.ts, details of the user, pickle key,
and corresponding key query and backup responses (`DumpDataSetInfo`).
It was created by pasting the following into the browser console:
The dump is created by pasting the following into the browser console:
```javascript
async function exportIndexedDb(name) {
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
## Dump of a libolm indexeddb cryptostore to test migration of a full account
A dump of an account containing a complete set of data to migrate.
The data set is substantial enough to allow for testing of chunking mechanisms and progress reporting during the migration process.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,109 @@
import { DumpDataSetInfo } from "../index";
/**
* A key query response containing the current keys of the tested user.
* To be used during tests with fetchmock.
*/
const KEYS_QUERY_RESPONSE: any = {
device_keys: {
"@vdhtest200713:matrix.org": {
KMFSTJSMLB: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "KMFSTJSMLB",
keys: {
"curve25519:KMFSTJSMLB": "LKv0bKbc0EC4h0jknbemv3QalEkeYvuNeUXVRgVVTTU",
"ed25519:KMFSTJSMLB": "qK70DEqIXq7T+UU3v/al47Ab4JkMEBLpNrTBMbS5rrw",
},
user_id: "@vdhtest200713:matrix.org",
signatures: {
"@vdhtest200713:matrix.org": {
"ed25519:KMFSTJSMLB":
"aE+PdxLAdwQ/xfJwLmqebvt/lrT97fZas2SQFFrM+dPmHxQtjyS8csm88BLfGRjJKK1B/vWev3AaKqQZwLTUAw",
"ed25519:lDvg6vi3P80L9XFNpUSU+5Y87m3p6yHcC83jhSU4Q5k":
"lCd4SA/JT1nnxsgN9yQaLJQhH5hkLMVVx6ba5JAjL1wpWVqyPxzMJHImX6vTztk6S8rybcdfYkea5W/Ii+4HCQ",
},
},
},
},
},
master_keys: {
"@vdhtest200713:matrix.org": {
user_id: "@vdhtest200713:matrix.org",
usage: ["master"],
keys: {
"ed25519:gh9fGr39eNZUdWynEMJ/q/WZq/Pk/foFxHXFBFm18ZI": "gh9fGr39eNZUdWynEMJ/q/WZq/Pk/foFxHXFBFm18ZI",
},
signatures: {
"@vdhtest200713:matrix.org": {
"ed25519:MWOGVUTXZN":
"stOu1aHbhsWB/Aj5M/HqBR83QzME+682C995Uc8JxSmmyrlWmgG8QrnoUDG2OFR1t6zNQ+QLEilU4WNEOV73DQ",
},
},
},
},
self_signing_keys: {
"@vdhtest200713:matrix.org": {
user_id: "@vdhtest200713:matrix.org",
usage: ["self_signing"],
keys: {
"ed25519:lDvg6vi3P80L9XFNpUSU+5Y87m3p6yHcC83jhSU4Q5k": "lDvg6vi3P80L9XFNpUSU+5Y87m3p6yHcC83jhSU4Q5k",
},
signatures: {
"@vdhtest200713:matrix.org": {
"ed25519:gh9fGr39eNZUdWynEMJ/q/WZq/Pk/foFxHXFBFm18ZI":
"HKTC7NoBhAkfJtmemmkn/HvCCgBQViWZ0uH7aGPRaWMDFgD8T7Q+y1j3FKZv4mhSopR85Fq3FRyXsG8OVvGeBA",
},
},
},
},
user_signing_keys: {
"@vdhtest200713:matrix.org": {
user_id: "@vdhtest200713:matrix.org",
usage: ["user_signing"],
keys: {
"ed25519:YShqO/3u5vQ0uucojraWrtoLrek0CYrurN/vH/YPMg8": "YShqO/3u5vQ0uucojraWrtoLrek0CYrurN/vH/YPMg8",
},
signatures: {
"@vdhtest200713:matrix.org": {
"ed25519:gh9fGr39eNZUdWynEMJ/q/WZq/Pk/foFxHXFBFm18ZI":
"u8VOi4IaeRJwDgy2ftK02NJQPdBijy8f/0+WnHGG72yfOvMthwWzEw8SrRSNG8glBNrfHinKwCyJJzAJwyepCQ",
},
},
},
},
};
/**
* A `/room_keys/version` response containing the current server-side backup info.
* To be used during tests with fetchmock.
*/
const BACKUP_RESPONSE: any = {
auth_data: {
public_key: "q+HZiJdHl2Yopv9GGvv7EYSzDMrAiRknK4glSdoaomI",
signatures: {
"@vdhtest200713:matrix.org": {
"ed25519:gh9fGr39eNZUdWynEMJ/q/WZq/Pk/foFxHXFBFm18ZI":
"reDp6Mu+j+tfUL3/T6f5OBT3N825Lzpc43vvG+RvjX6V+KxXzodBQArgCoeEHLtL9OgSBmNrhTkSOX87MWCKAw",
"ed25519:KMFSTJSMLB":
"F8tyV5W6wNi0GXTdSg+gxSCULQi0EYxdAAqfkyNq58KzssZMw5i+PRA0aI2b+D7NH/aZaJrtiYNHJ0gWLSQvAw",
},
},
},
version: "7",
algorithm: "m.megolm_backup.v1.curve25519-aes-sha2",
etag: "1",
count: 79,
};
/**
* A dataset containing the information for the tested user.
* To be used during tests.
*/
export const FULL_ACCOUNT_DATASET: DumpDataSetInfo = {
userId: "@vdhtest200713:matrix.org",
deviceId: "KMFSTJSMLB",
pickleKey: "+1k2Ppd7HIisUY824v7JtV3/oEE4yX0TqtmNPyhaD7o",
backupResponse: BACKUP_RESPONSE,
keyQueryResponse: KEYS_QUERY_RESPONSE,
dumpPath: "spec/test-utils/test_indexeddb_cryptostore_dump/full_account/dump.json",
};
@@ -18,11 +18,12 @@ import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
/**
* Populate an IndexedDB store with the test data from this directory.
* Populate an IndexedDB store with a set of test data.
*
* @param name - Name of the IndexedDB database to create.
* @param dumpPath - The path to the dump file to import.
*/
export async function populateStore(name: string): Promise<IDBDatabase> {
export async function populateStore(name: string, dumpPath: string): Promise<IDBDatabase> {
const req = indexedDB.open(name, 11);
const db = await new Promise<IDBDatabase>((resolve, reject) => {
@@ -42,7 +43,7 @@ export async function populateStore(name: string): Promise<IDBDatabase> {
};
});
await importData(db);
await importData(db, dumpPath);
return db;
}
@@ -100,8 +101,8 @@ function upgradeDatabase(oldVersion: number, db: IDBDatabase) {
}
}
async function importData(db: IDBDatabase) {
const path = resolve("spec/test-utils/test_indexeddb_cryptostore_dump/dump.json");
async function importData(db: IDBDatabase, dumpPath: string) {
const path = resolve(dumpPath);
const json: Record<string, Array<{ key?: any; value: any }>> = JSON.parse(
await readFile(path, { encoding: "utf8" }),
);
@@ -134,3 +135,20 @@ async function importData(db: IDBDatabase) {
});
}
}
export interface DumpDataSetInfo {
/** The user ID to use for the test.*/
userId: string;
/** The device ID to use for the test.*/
deviceId: string;
/** The path to the dump file to import via {@link populateStore}.*/
dumpPath: string;
/** The pickle key to use for the dumped account.*/
pickleKey: string;
/** The response to use for the keys query. */
keyQueryResponse: any;
/** The response to use for the backup query.*/
backupResponse?: any;
/** Additional dump info specific for some tests.*/
[key: string]: any;
}
@@ -0,0 +1,4 @@
## Dump of a libolm indexeddb cryptostore where the msk is not cached
A dump simulating an account where the identity was verified, but the msk was not in cache.
Used to test that the owner identity local trust is migrated correctly.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,260 @@
import { KeyBackupInfo } from "../../../../src/crypto-api/keybackup";
import { DumpDataSetInfo } from "../index";
/**
* A key query response containing the current keys of the tested user.
* To be used during tests with fetchmock.
*/
const KEY_QUERY_RESPONSE: any = {
device_keys: {
"@migration:localhost": {
CBGTADUILV: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "CBGTADUILV",
keys: {
"curve25519:CBGTADUILV": "gqhFlc7Wzc1wmmmAu3ySIEe4LtDcBK/bdzrtZg+mMSg",
"ed25519:CBGTADUILV": "q1q3L1Il4l61c/6TmI4fYWMsseNMJJYE2Y0r+5ajKQI",
},
signatures: {
"@migration:localhost": {
"ed25519:CBGTADUILV":
"ppSmA0slyQ7RJOFn+qZSLCGeHN6/jAmqKvUZo5Q1hWk0ugkKycRoSUi9TOfbfAVSf8xvFirXy2VGXQbEVPJqAA",
"ed25519:d+4HhsodR2Zqv4Z5V0VxPfy8zbjLjUCdCyv5qme5Ygc":
"cFLWl1fjehLrzrEn3UnmZMIgy3C23WMgGRsn4e6Z/55vmen4KMs8bLpgZaDoWhIdn/8siHRWafA5sFdzK2NsBQ",
"ed25519:bmFmNcVPvaqrlNzmyKn9uU+QRHyx2QRbn/bUAlTH760":
"C6EeqNPcaQyuZgo8+HOUywc/TMkW5IMjg7aoxyu93X//KcNNXKRfj1banYP6XqyPuQITLamBYc1089Jpt9g4Cw",
"ed25519:RkQzi0+aKIL9Y+GzsN23xMz3i3QRkH03G5aqqEbbuy4":
"YwBN/SbCxO8hPgv1B9JY2WVFK4LNK9vq1UNVrkF2j0ZDw9LrvaOws72mbmzZ0nbD3ohcEZ8rXsEosxEVr5r7AQ",
},
},
user_id: "@migration:localhost",
unsigned: {
device_display_name: "localhost:8080: Chrome on macOS",
},
},
TMWBMDZPFT: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "TMWBMDZPFT",
keys: {
"curve25519:TMWBMDZPFT": "oYP9EXvHMbliFdfk8jPvUw0KhAd0+PBqdMslJAt/ZGQ",
"ed25519:TMWBMDZPFT": "IyfPT67JutFWJsUxrxSqEWxgRjKn9B/w78uKU4OBj1E",
},
signatures: {
"@migration:localhost": {
"ed25519:TMWBMDZPFT":
"IWIuuDag4ZMDhMObYV63X7dBYEUYNHYR0Yu/bwLvQh5ieDjQSrZSLOzDrgCyPCM4hkc4JlhneQpJsYo1lUH7DA",
"ed25519:d+4HhsodR2Zqv4Z5V0VxPfy8zbjLjUCdCyv5qme5Ygc":
"iEcTKElQu4CAsQIXmBaZmXwfB6Diut+4ZXakP1ob7OIDMrCYBcgXsBFYg6GuxwL0LCTVcUgbUw7VuPKSvM8UAA",
"ed25519:MYgcP5P7P6KucWjLvTRofY5PWxsf+WDj2BiXtqOO5Gw":
"KcBLDWkCwZyIzlBkC29PNzHxx7Br14TYlhBfREEEQo/Rd34ZZUYwbQ8iPhB8S1GVq3YwgAV6piYIcxpQin+dBA",
"ed25519:HGN9m99VprMuQBDA3o+KZKcEYTaGmiaujrkygjScMnY":
"VqrvA148Uxib9TNFI1rc9r8qpwTojCkqLofEz9dMLc/XV3U14WD5/LDEhMuCwNu6wsu/uO+dS4AmJlJnN/iAAg",
"ed25519:Nt0L/p+UVHMx603sYHXwXja+VyQIUVFvu0vDBYn56Zk":
"D1COHzROOTNlCn8b1zI9+6phUtF0OVqWxLfOLnX5t14H2oENYV2ASgaxsdmXcSZPrGzaJkmSOginHHzsabe5CA",
"ed25519:bmFmNcVPvaqrlNzmyKn9uU+QRHyx2QRbn/bUAlTH760":
"SFSDrsi3GQ9jjBYUc2aUSzf777/0NfQWrOBi2CK+v5VQY3FkyHBln3K4YzvxIKSVIhOaQtBlEDtfQb33kwTgDg",
"ed25519:RkQzi0+aKIL9Y+GzsN23xMz3i3QRkH03G5aqqEbbuy4":
"BtJkzQe0YFAa8gJiYXYtzGtktl9vZMNYl5jd4DA8Toi4VxgosJNZQE7lT5qpYU0BrlFn46QIs/38X8JhSt+wAQ",
},
},
user_id: "@migration:localhost",
unsigned: {
device_display_name: "localhost:8080: Chrome on macOS",
},
},
},
},
failures: {},
master_keys: {
"@migration:localhost": {
keys: {
"ed25519:cFjUBAhAZ2tjYF1TpQtYNA3x9XRzTiIdP2N2EvRaOH4": "cFjUBAhAZ2tjYF1TpQtYNA3x9XRzTiIdP2N2EvRaOH4",
},
signatures: {
"@migration:localhost": {
"ed25519:TMWBMDZPFT":
"RrPUnYoekK7wZGrLNXshgoupF8v53S/vJyvkBJi+q9THh4Qrf3CieuVJFx8mwtmEZgGoA2tSroAVnRqvEQ+IBQ",
"ed25519:cFjUBAhAZ2tjYF1TpQtYNA3x9XRzTiIdP2N2EvRaOH4":
"o4CbtdU3IqJK90UXAEBtxps2m4XBYvWJI2nbVlzBaGRr+Xt/3vtwDMlc5G970kPQWBbs/koYJh8MSaE7Fm1mAg",
"ed25519:CBGTADUILV":
"AgZoG+ix8aW3FAW6v+/Xu+QJpxzvsx5itbB8RyqMet9YlNqX90vYIbBV7IoV2WFY2WdANYEffX2CE0FpR6NnCg",
},
},
usage: ["master"],
user_id: "@migration:localhost",
},
},
self_signing_keys: {
"@migration:localhost": {
keys: {
"ed25519:RkQzi0+aKIL9Y+GzsN23xMz3i3QRkH03G5aqqEbbuy4": "RkQzi0+aKIL9Y+GzsN23xMz3i3QRkH03G5aqqEbbuy4",
},
signatures: {
"@migration:localhost": {
"ed25519:cFjUBAhAZ2tjYF1TpQtYNA3x9XRzTiIdP2N2EvRaOH4":
"hs8VqoTfipDjC2pzFdmzb1aENhDjVV+gc86fuYftczaCcsXUWop/NPwoF51Ie6Nb3YL0N7ZZAUrycuJP5hFbDg",
},
},
usage: ["self_signing"],
user_id: "@migration:localhost",
},
},
user_signing_keys: {
"@migration:localhost": {
keys: {
"ed25519:WNJ2G3Ig5EdC4wYiRKcK7bhLP2+I4wI6V7SKgJTXdw8": "WNJ2G3Ig5EdC4wYiRKcK7bhLP2+I4wI6V7SKgJTXdw8",
},
signatures: {
"@migration:localhost": {
"ed25519:cFjUBAhAZ2tjYF1TpQtYNA3x9XRzTiIdP2N2EvRaOH4":
"Vlba5rJQxG+ussVLoycvHcin7Ghv0uUeClDqDbM+RPF+jx9w4ozbcuEOTJdyzyPA+GxN9Kzh2lmVFMMQGyvNAw",
},
},
usage: ["user_signing"],
user_id: "@migration:localhost",
},
},
};
/**
* A new key query response for the same user simulating a cross-signing key reset.
* To be used during tests with fetchmock.
*/
const ROTATED_KEY_QUERY_RESPONSE: any = {
device_keys: {
"@migration:localhost": {
TMWBMDZPFT: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "TMWBMDZPFT",
keys: {
"curve25519:TMWBMDZPFT": "oYP9EXvHMbliFdfk8jPvUw0KhAd0+PBqdMslJAt/ZGQ",
"ed25519:TMWBMDZPFT": "IyfPT67JutFWJsUxrxSqEWxgRjKn9B/w78uKU4OBj1E",
},
signatures: {
"@migration:localhost": {
"ed25519:TMWBMDZPFT":
"IWIuuDag4ZMDhMObYV63X7dBYEUYNHYR0Yu/bwLvQh5ieDjQSrZSLOzDrgCyPCM4hkc4JlhneQpJsYo1lUH7DA",
"ed25519:d+4HhsodR2Zqv4Z5V0VxPfy8zbjLjUCdCyv5qme5Ygc":
"iEcTKElQu4CAsQIXmBaZmXwfB6Diut+4ZXakP1ob7OIDMrCYBcgXsBFYg6GuxwL0LCTVcUgbUw7VuPKSvM8UAA",
"ed25519:MYgcP5P7P6KucWjLvTRofY5PWxsf+WDj2BiXtqOO5Gw":
"KcBLDWkCwZyIzlBkC29PNzHxx7Br14TYlhBfREEEQo/Rd34ZZUYwbQ8iPhB8S1GVq3YwgAV6piYIcxpQin+dBA",
"ed25519:HGN9m99VprMuQBDA3o+KZKcEYTaGmiaujrkygjScMnY":
"VqrvA148Uxib9TNFI1rc9r8qpwTojCkqLofEz9dMLc/XV3U14WD5/LDEhMuCwNu6wsu/uO+dS4AmJlJnN/iAAg",
"ed25519:Nt0L/p+UVHMx603sYHXwXja+VyQIUVFvu0vDBYn56Zk":
"D1COHzROOTNlCn8b1zI9+6phUtF0OVqWxLfOLnX5t14H2oENYV2ASgaxsdmXcSZPrGzaJkmSOginHHzsabe5CA",
"ed25519:bmFmNcVPvaqrlNzmyKn9uU+QRHyx2QRbn/bUAlTH760":
"SFSDrsi3GQ9jjBYUc2aUSzf777/0NfQWrOBi2CK+v5VQY3FkyHBln3K4YzvxIKSVIhOaQtBlEDtfQb33kwTgDg",
"ed25519:RkQzi0+aKIL9Y+GzsN23xMz3i3QRkH03G5aqqEbbuy4":
"BtJkzQe0YFAa8gJiYXYtzGtktl9vZMNYl5jd4DA8Toi4VxgosJNZQE7lT5qpYU0BrlFn46QIs/38X8JhSt+wAQ",
},
},
user_id: "@migration:localhost",
unsigned: {
device_display_name: "localhost:8080: Chrome on macOS",
},
},
XFZFSCUOFL: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "XFZFSCUOFL",
keys: {
"curve25519:XFZFSCUOFL": "aN2Ty+0rutNkrRtxhV+ciI8GhF4epSxzL7bAOr8zfkc",
"ed25519:XFZFSCUOFL": "V7CPhXdfLFk+qAOFivrpFskmunVTeuM+EOM3DMlDxkI",
},
signatures: {
"@migration:localhost": {
"ed25519:XFZFSCUOFL":
"4Pqc2FWJ5p/L/tSlfUBIlcQzLmN5CksJriAibY8LSDAXdGYiQJ7hvKqneEuVhrMYwqyIxb4bAad+r6wnY0/7Cg",
"ed25519:RkQzi0+aKIL9Y+GzsN23xMz3i3QRkH03G5aqqEbbuy4":
"yH8pKnD+E8YaawS+1NCjwy0cf2WzBRff9BBNX4YnAuTyc6s5b1QqNfu9DP5qblw8TZ7hZmaziePZKsjRiqJLBg",
"ed25519:OEv0wHLusJx7zTCc0h3HbNIHLIxlGZKh63tc2ptKb+Y":
"M8SfAiEUzd7AsWp8InS7BxV3cRqV3MjMxks4DwSxsVxvkCco2JWybKgev+vTZyM6XDg930o0FObQOxWm4+CkBw",
},
},
user_id: "@migration:localhost",
unsigned: {
device_display_name: "localhost:8080: Chrome on macOS",
},
},
},
},
failures: {},
master_keys: {
"@migration:localhost": {
user_id: "@migration:localhost",
usage: ["master"],
keys: {
"ed25519:rXCrBin/+xyh+yW//vWte+2UV0et1ZHTWfalp/Ekack": "rXCrBin/+xyh+yW//vWte+2UV0et1ZHTWfalp/Ekack",
},
signatures: {
"@migration:localhost": {
"ed25519:XFZFSCUOFL":
"C8aswtyUABWvj2DInehVoh2P/EDbwRhlIk51LtV3L71POUCh7pZuyXRMMWKZeyRvHRmEllXBtRkH1iol/p56Bg",
},
},
},
},
self_signing_keys: {
"@migration:localhost": {
user_id: "@migration:localhost",
usage: ["self_signing"],
keys: {
"ed25519:OEv0wHLusJx7zTCc0h3HbNIHLIxlGZKh63tc2ptKb+Y": "OEv0wHLusJx7zTCc0h3HbNIHLIxlGZKh63tc2ptKb+Y",
},
signatures: {
"@migration:localhost": {
"ed25519:rXCrBin/+xyh+yW//vWte+2UV0et1ZHTWfalp/Ekack":
"dH596pGp8+f8dlwd81UrKDWoRDd24yAqqMSLqR4fJHyfszbn7qCvQA6LYZ023TLmk33FKcJqRtd2v/ykTmS3Bg",
},
},
},
},
user_signing_keys: {
"@migration:localhost": {
user_id: "@migration:localhost",
usage: ["user_signing"],
keys: {
"ed25519:8XHpC3MeMReIfYneWIRX8c4ANgJuQ1+oFrktBcLka4o": "8XHpC3MeMReIfYneWIRX8c4ANgJuQ1+oFrktBcLka4o",
},
signatures: {
"@migration:localhost": {
"ed25519:rXCrBin/+xyh+yW//vWte+2UV0et1ZHTWfalp/Ekack":
"FX6ylagvx3IG1zMf/ayYgDb/1+x0/F28pHQqzQMGGssAmc15nat/R6AF0QO7Qg7uqTAf04ohuZtWax3dTwjNDQ",
},
},
},
},
};
/**
* A `/room_keys/version` response containing the current server-side backup info.
* To be used during tests with fetchmock.
*/
const BACKUP_RESPONSE: KeyBackupInfo = {
auth_data: {
public_key: "2ffIfIB4oryqZpsJQjQNUaxgCzxliC6A4PJvnrN+XAA",
signatures: {
"@migration:localhost": {
"ed25519:TMWBMDZPFT":
"qBvalid/G4hnSF3hAeX4TtRN6/BqprgiYnLEtDuatyQ5WxWr0s4uSOyvHSglsRdpoo32FDBHfTIZkCOVxSLwAA",
},
},
},
version: "2",
algorithm: "m.megolm_backup.v1.curve25519-aes-sha2",
etag: "0",
count: 0,
};
/**
* A dataset containing the information for the tested user.
* To be used during tests.
*/
export const MSK_NOT_CACHED_DATASET: DumpDataSetInfo = {
userId: "@migration:localhost",
deviceId: "CBGTADUILV",
pickleKey: "qEURMepfkMvoBQGaWlI9MZKYnDMsSAiW8aFTKXaeDV0",
keyQueryResponse: KEY_QUERY_RESPONSE,
rotatedKeyQueryResponse: ROTATED_KEY_QUERY_RESPONSE,
backupResponse: BACKUP_RESPONSE,
dumpPath: "spec/test-utils/test_indexeddb_cryptostore_dump/no_cached_msk_dump/dump.json",
};
@@ -0,0 +1,4 @@
## Dump of a libolm indexeddb cryptostore where the identity is not trusted.
A dump of an account where the identity was not verified.
Used as a test case for migration of the identity local trust.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,110 @@
import { DumpDataSetInfo } from "../index";
/**
* A key query response containing the current keys of the tested user.
* To be used during tests with fetchmock.
*/
const KEY_QUERY_RESPONSE = {
device_keys: {
"@untrusted:localhost": {
IXNYALOZWU: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "IXNYALOZWU",
keys: {
"curve25519:IXNYALOZWU": "EHMQEtJd9INJg28HwKK8Te1EX8obR3VTtyNwf/rcczM",
"ed25519:IXNYALOZWU": "OxMfZHsYJvroTp1RtjUOejpWbRBryN6VsojC5dKR74U",
},
signatures: {
"@untrusted:localhost": {
"ed25519:IXNYALOZWU":
"tWaTiRKc95ZCqM2qrKTdq1sQ3DPFgw3vdrOVmWIHQwj92DCgJtnQ9uymLMOq+MSb80bdBBjXwrNeOufgaL/6CQ",
"ed25519:+ik0n/QnBPq8H/48wAT+54slKk1SL7NIk/HtiN/cNEg":
"+QXZFLiAv+k7UXgAP6AXLk/PdZ3TlJ77M23m73v8qvavAlnkLBAjKNA3BG39JTQET5UhW5DnCohwsbGP+aY1Cw",
},
},
user_id: "@untrusted:localhost",
unsigned: {
device_display_name: "localhost:8080: Chrome on macOS",
},
},
VJPSPVPWZT: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "VJPSPVPWZT",
keys: {
"curve25519:VJPSPVPWZT": "+RxCNRFPqBZJm6PLjEJsSdFixGWQJygD5Os11/+6PC0",
"ed25519:VJPSPVPWZT": "wqH7xK/DQya8m05Vy4rnacjugGNBiY+7Ml6wyRVkM9U",
},
signatures: {
"@untrusted:localhost": {
"ed25519:VJPSPVPWZT":
"XC+RoKL/zVZOIwk/bGEQJlJu49QicY1v6vSDMHA2y0/fpX/MD4KiWGD7+W5DFD54E8FrFVTsIgkzat561qdTBQ",
},
},
user_id: "@untrusted:localhost",
unsigned: {
device_display_name: "localhost:8080: Chrome on macOS",
},
},
},
},
failures: {},
master_keys: {
"@untrusted:localhost": {
keys: {
"ed25519:Uahbc3+Rk65y0ku6T2RL/29fEA9Bum6+OaqptG6df3g": "Uahbc3+Rk65y0ku6T2RL/29fEA9Bum6+OaqptG6df3g",
},
signatures: {
"@untrusted:localhost": {
"ed25519:IXNYALOZWU":
"KdAdyKO2sb3Di3bdK+oxf+gjMSmW/sisRNvpKZORPKwmy2SGaKGYkecBtslunoFjnb+hjIESgweQu6cHoNX4AA",
"ed25519:Uahbc3+Rk65y0ku6T2RL/29fEA9Bum6+OaqptG6df3g":
"b0R9Id5HxHYo+MA22Vlq0OckTrWnSWhgHLvF8Wr4e154JdtOyK7N0aXPQPkrLB0fmyVmGdbDa9xs9jsfINGmDw",
},
},
usage: ["master"],
user_id: "@untrusted:localhost",
},
},
self_signing_keys: {
"@untrusted:localhost": {
keys: {
"ed25519:+ik0n/QnBPq8H/48wAT+54slKk1SL7NIk/HtiN/cNEg": "+ik0n/QnBPq8H/48wAT+54slKk1SL7NIk/HtiN/cNEg",
},
signatures: {
"@untrusted:localhost": {
"ed25519:Uahbc3+Rk65y0ku6T2RL/29fEA9Bum6+OaqptG6df3g":
"z/5z51jbRpyDQhYnfUHhhb5fUbzRDlfjD8mZA2ZGStpE/F41lDyxjlvF2W/E2CJ27bmJFdk7nC+ZCwriYfYxBw",
},
},
usage: ["self_signing"],
user_id: "@untrusted:localhost",
},
},
user_signing_keys: {
"@untrusted:localhost": {
keys: {
"ed25519:L/8HbQWnK9OidAcDVB+Az9b0Mx3OdBtIMFsUjV6qgSQ": "L/8HbQWnK9OidAcDVB+Az9b0Mx3OdBtIMFsUjV6qgSQ",
},
signatures: {
"@untrusted:localhost": {
"ed25519:Uahbc3+Rk65y0ku6T2RL/29fEA9Bum6+OaqptG6df3g":
"UuNvzebLQn31LYGbx+ADe60BB25kWy4SVVyd9BXlY/tAZMoA8Tmq1e2R2tJJtPdJxC/Oogktj2+iikZV/YMjAQ",
},
},
usage: ["user_signing"],
user_id: "@untrusted:localhost",
},
},
};
/**
* A dataset containing the information for the tested user.
* To be used during tests.
*/
export const IDENTITY_NOT_TRUSTED_DATASET: DumpDataSetInfo = {
userId: "@untrusted:localhost",
deviceId: "VJPSPVPWZT",
pickleKey: "WVllQb4Lk/WwP4Q7iBfeTUHpgydZm9YqXI1B5bTvnIM",
keyQueryResponse: KEY_QUERY_RESPONSE,
dumpPath: "spec/test-utils/test_indexeddb_cryptostore_dump/unverified/dump.json",
};
+3 -2
View File
@@ -25,6 +25,7 @@ import { UserTrustLevel } from "../../src/crypto/CrossSigning";
import { CryptoBackend } from "../../src/common-crypto/CryptoBackend";
import { EventDecryptionResult } from "../../src/common-crypto/CryptoBackend";
import * as testData from "../test-utils/test-data";
import { KnownMembership } from "../../src/@types/membership";
const Olm = global.Olm;
@@ -463,7 +464,7 @@ describe("Crypto", function () {
type: "m.room.member",
sender: "@alice:example.com",
room_id: roomId,
content: { membership: "invite" },
content: { membership: KnownMembership.Invite },
state_key: "@bob:example.com",
}),
]);
@@ -795,7 +796,7 @@ describe("Crypto", function () {
type: "m.room.member",
sender: "@clara:example.com",
room_id: roomId,
content: { membership: "invite" },
content: { membership: KnownMembership.Invite },
state_key: "@bob:example.com",
}),
]);
+3 -2
View File
@@ -36,6 +36,7 @@ import { DeviceTrustLevel } from "../../../../src/crypto/CrossSigning";
import { MegolmEncryption as MegolmEncryptionClass } from "../../../../src/crypto/algorithms/megolm";
import { recursiveMapToObject } from "../../../../src/utils";
import { sleep } from "../../../../src/utils";
import { KnownMembership } from "../../../../src/@types/membership";
const MegolmDecryption = algorithms.DECRYPTION_CLASSES.get("m.megolm.v1.aes-sha2")!;
const MegolmEncryption = algorithms.ENCRYPTION_CLASSES.get("m.megolm.v1.aes-sha2")!;
@@ -806,11 +807,11 @@ describe("MegolmDecryption", function () {
aliceRoom.getEncryptionTargetMembers = jest.fn().mockResolvedValue([
{
userId: "@alice:example.com",
membership: "join",
membership: KnownMembership.Join,
},
{
userId: "@bob:example.com",
membership: "join",
membership: KnownMembership.Join,
},
]);
const BOB_DEVICES = {
+17 -3
View File
@@ -87,9 +87,12 @@ describe("RoomWidgetClient", () => {
client.stopClient();
});
const makeClient = async (capabilities: ICapabilities): Promise<void> => {
const makeClient = async (
capabilities: ICapabilities,
sendContentLoaded: boolean | undefined = undefined,
): Promise<void> => {
const baseUrl = "https://example.org";
client = createRoomWidgetClient(widgetApi, capabilities, "!1:example.org", { baseUrl });
client = createRoomWidgetClient(widgetApi, capabilities, "!1:example.org", { baseUrl }, sendContentLoaded);
expect(widgetApi.start).toHaveBeenCalled(); // needs to have been called early in order to not miss messages
widgetApi.emit("ready");
await client.startClient();
@@ -143,7 +146,7 @@ describe("RoomWidgetClient", () => {
});
});
describe("messages", () => {
describe("initialization", () => {
it("requests permissions for specific message types", async () => {
await makeClient({ sendMessage: [MsgType.Text], receiveMessage: [MsgType.Text] });
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
@@ -158,6 +161,15 @@ describe("RoomWidgetClient", () => {
expect(widgetApi.requestCapabilityToReceiveMessage).toHaveBeenCalledWith();
});
it("sends content loaded when configured", async () => {
await makeClient({});
expect(widgetApi.sendContentLoaded).toHaveBeenCalled();
});
it("does not sent content loaded when configured", async () => {
await makeClient({}, false);
expect(widgetApi.sendContentLoaded).not.toHaveBeenCalled();
});
// No point in testing sending and receiving since it's done exactly the
// same way as non-message events
});
@@ -305,12 +317,14 @@ describe("RoomWidgetClient", () => {
expect(await emittedSync).toEqual(SyncState.Syncing);
});
});
describe("oidc token", () => {
it("requests an oidc token", async () => {
await makeClient({});
expect(await client.getOpenIdToken()).toStrictEqual(testOIDCToken);
});
});
it("gets TURN servers", async () => {
const server1: ITurnServer = {
uris: [
+11 -10
View File
@@ -7,6 +7,7 @@ import { MatrixClient } from "../../src/matrix";
import { Room } from "../../src/models/room";
import { RoomMember } from "../../src/models/room-member";
import { EventTimelineSet } from "../../src/models/event-timeline-set";
import { KnownMembership } from "../../src/@types/membership";
describe("EventTimeline", function () {
const roomId = "!foo:bar";
@@ -50,7 +51,7 @@ describe("EventTimeline", function () {
const events = [
utils.mkMembership({
room: roomId,
mship: "invite",
mship: KnownMembership.Invite,
user: userB,
skey: userA,
event: true,
@@ -87,7 +88,7 @@ describe("EventTimeline", function () {
const state = [
utils.mkMembership({
room: roomId,
mship: "invite",
mship: KnownMembership.Invite,
user: userB,
skey: userA,
event: true,
@@ -203,11 +204,11 @@ describe("EventTimeline", function () {
it("should set event.sender for new and old events", function () {
const sentinel = new RoomMember(roomId, userA);
sentinel.name = "Alice";
sentinel.membership = "join";
sentinel.membership = KnownMembership.Join;
const oldSentinel = new RoomMember(roomId, userA);
sentinel.name = "Old Alice";
sentinel.membership = "join";
sentinel.membership = KnownMembership.Join;
mocked(timeline.getState(EventTimeline.FORWARDS)!).getSentinelMember.mockImplementation(function (uid) {
if (uid === userA) {
@@ -246,11 +247,11 @@ describe("EventTimeline", function () {
it("should set event.target for new and old m.room.member events", function () {
const sentinel = new RoomMember(roomId, userA);
sentinel.name = "Alice";
sentinel.membership = "join";
sentinel.membership = KnownMembership.Join;
const oldSentinel = new RoomMember(roomId, userA);
sentinel.name = "Old Alice";
sentinel.membership = "join";
sentinel.membership = KnownMembership.Join;
mocked(timeline.getState(EventTimeline.FORWARDS)!).getSentinelMember.mockImplementation(function (uid) {
if (uid === userA) {
@@ -267,14 +268,14 @@ describe("EventTimeline", function () {
const newEv = utils.mkMembership({
room: roomId,
mship: "invite",
mship: KnownMembership.Invite,
user: userB,
skey: userA,
event: true,
});
const oldEv = utils.mkMembership({
room: roomId,
mship: "ban",
mship: KnownMembership.Ban,
user: userB,
skey: userA,
event: true,
@@ -291,7 +292,7 @@ describe("EventTimeline", function () {
const events = [
utils.mkMembership({
room: roomId,
mship: "invite",
mship: KnownMembership.Invite,
user: userB,
skey: userA,
event: true,
@@ -330,7 +331,7 @@ describe("EventTimeline", function () {
const events = [
utils.mkMembership({
room: roomId,
mship: "invite",
mship: KnownMembership.Invite,
user: userB,
skey: userA,
event: true,
+7 -6
View File
@@ -71,6 +71,7 @@ import * as featureUtils from "../../src/feature";
import { StubStore } from "../../src/store/stub";
import { SecretStorageKeyDescriptionAesV1, ServerSideSecretStorageImpl } from "../../src/secret-storage";
import { CryptoBackend } from "../../src/common-crypto/CryptoBackend";
import { KnownMembership } from "../../src/@types/membership";
jest.useFakeTimers();
@@ -750,7 +751,7 @@ describe("MatrixClient", function () {
it("should get (unstable) file trees with valid state", async () => {
const roomId = "!room:example.org";
const mockRoom = {
getMyMembership: () => "join",
getMyMembership: () => KnownMembership.Join,
currentState: {
getStateEvents: (eventType, stateKey) => {
/* eslint-disable jest/no-conditional-expect */
@@ -789,7 +790,7 @@ describe("MatrixClient", function () {
it("should not get (unstable) file trees if not joined", async () => {
const roomId = "!room:example.org";
const mockRoom = {
getMyMembership: () => "leave", // "not join"
getMyMembership: () => KnownMembership.Leave, // "not join"
} as unknown as Room;
client.getRoom = (getRoomId) => {
expect(getRoomId).toEqual(roomId);
@@ -812,7 +813,7 @@ describe("MatrixClient", function () {
it("should not get (unstable) file trees with invalid create contents", async () => {
const roomId = "!room:example.org";
const mockRoom = {
getMyMembership: () => "join",
getMyMembership: () => KnownMembership.Join,
currentState: {
getStateEvents: (eventType, stateKey) => {
/* eslint-disable jest/no-conditional-expect */
@@ -849,7 +850,7 @@ describe("MatrixClient", function () {
it("should not get (unstable) file trees with invalid purpose/subtype contents", async () => {
const roomId = "!room:example.org";
const mockRoom = {
getMyMembership: () => "join",
getMyMembership: () => KnownMembership.Join,
currentState: {
getStateEvents: (eventType, stateKey) => {
/* eslint-disable jest/no-conditional-expect */
@@ -1309,7 +1310,7 @@ describe("MatrixClient", function () {
describe("redactEvent", () => {
const roomId = "!room:example.org";
const mockRoom = {
getMyMembership: () => "join",
getMyMembership: () => KnownMembership.Join,
currentState: {
getStateEvents: (eventType, stateKey) => {
if (eventType === EventType.RoomEncryption) {
@@ -1448,7 +1449,7 @@ describe("MatrixClient", function () {
const txnId = "m12345";
const mockRoom = {
getMyMembership: () => "join",
getMyMembership: () => KnownMembership.Join,
updatePendingEvent: (event: MatrixEvent, status: EventStatus) => event.setStatus(status),
hasEncryptionStateEvent: jest.fn().mockReturnValue(true),
} as unknown as Room;
+2 -1
View File
@@ -15,6 +15,7 @@ limitations under the License.
*/
import { EventTimeline, EventType, MatrixClient, MatrixError, MatrixEvent, Room } from "../../../src";
import { KnownMembership } from "../../../src/@types/membership";
import { CallMembershipData } from "../../../src/matrixrtc/CallMembership";
import { MatrixRTCSession, MatrixRTCSessionEvent } from "../../../src/matrixrtc/MatrixRTCSession";
import { EncryptionKeysEventContent } from "../../../src/matrixrtc/types";
@@ -76,7 +77,7 @@ describe("MatrixRTCSession", () => {
it("ignores memberships events of members not in the room", () => {
const mockRoom = makeMockRoom([membershipTemplate]);
mockRoom.hasMembershipState = (state) => state === "join";
mockRoom.hasMembershipState = (state) => state === KnownMembership.Join;
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
expect(sess?.memberships.length).toEqual(0);
});
+7 -6
View File
@@ -25,6 +25,7 @@ import {
} from "../../../src/models/MSC3089TreeSpace";
import { DEFAULT_ALPHABET } from "../../../src/utils";
import { MatrixError } from "../../../src/http-api";
import { KnownMembership } from "../../../src/@types/membership";
describe("MSC3089TreeSpace", () => {
let client: MatrixClient;
@@ -458,14 +459,14 @@ describe("MSC3089TreeSpace", () => {
expect(stateKey).toBeUndefined();
return [
// Partial implementations
{ getContent: () => ({ membership: "join" }), getStateKey: () => joinMemberId },
{ getContent: () => ({ membership: "knock" }), getStateKey: () => knockMemberId },
{ getContent: () => ({ membership: "invite" }), getStateKey: () => inviteMemberId },
{ getContent: () => ({ membership: "leave" }), getStateKey: () => leaveMemberId },
{ getContent: () => ({ membership: "ban" }), getStateKey: () => banMemberId },
{ getContent: () => ({ membership: KnownMembership.Join }), getStateKey: () => joinMemberId },
{ getContent: () => ({ membership: KnownMembership.Knock }), getStateKey: () => knockMemberId },
{ getContent: () => ({ membership: KnownMembership.Invite }), getStateKey: () => inviteMemberId },
{ getContent: () => ({ membership: KnownMembership.Leave }), getStateKey: () => leaveMemberId },
{ getContent: () => ({ membership: KnownMembership.Ban }), getStateKey: () => banMemberId },
// ensure we don't kick ourselves
{ getContent: () => ({ membership: "join" }), getStateKey: () => selfUserId },
{ getContent: () => ({ membership: KnownMembership.Join }), getStateKey: () => selfUserId },
];
},
};
+462 -157
View File
@@ -1,8 +1,35 @@
import * as utils from "../test-utils/test-utils";
import { IActionsObject, PushProcessor } from "../../src/pushprocessor";
import { ConditionKind, EventType, IContent, MatrixClient, MatrixEvent, PushRuleActionName, RuleId } from "../../src";
import {
ConditionKind,
EventType,
IContent,
IPushRule,
MatrixClient,
MatrixEvent,
PushRuleActionName,
RuleId,
TweakName,
} from "../../src";
import { mockClientMethodsUser } from "../test-utils/client";
const msc3914RoomCallRule: IPushRule = {
rule_id: ".org.matrix.msc3914.rule.room.call",
default: true,
enabled: true,
conditions: [
{
kind: ConditionKind.EventMatch,
key: "type",
pattern: "org.matrix.msc3401.call",
},
{
kind: ConditionKind.CallStarted,
},
],
actions: [PushRuleActionName.Notify, { set_tweak: TweakName.Sound, value: "default" }],
};
describe("NotificationService", function () {
const testUserId = "@ali:matrix.org";
const testDisplayName = "Alice M";
@@ -12,164 +39,150 @@ describe("NotificationService", function () {
let pushProcessor: PushProcessor;
const msc3914RoomCallRule = {
rule_id: ".org.matrix.msc3914.rule.room.call",
default: true,
enabled: true,
conditions: [
{
kind: "event_match",
key: "type",
pattern: "org.matrix.msc3401.call",
},
{
kind: "call_started",
},
],
actions: ["notify", { set_tweak: "sound", value: "default" }],
};
// These would be better if individual rules were configured in the tests themselves.
const matrixClient = {
getRoom: function () {
return {
currentState: {
getMember: function () {
return {
name: testDisplayName,
};
},
getJoinedMemberCount: function () {
return 0;
},
members: {},
},
};
},
...mockClientMethodsUser(testUserId),
supportsIntentionalMentions: () => true,
pushRules: {
device: {},
global: {
content: [
{
actions: [
"notify",
{
set_tweak: "sound",
value: "default",
},
{
set_tweak: "highlight",
},
],
enabled: true,
pattern: "ali",
rule_id: ".m.rule.contains_user_name",
},
{
actions: [
"notify",
{
set_tweak: "sound",
value: "default",
},
{
set_tweak: "highlight",
},
],
enabled: true,
pattern: "coffee",
rule_id: "coffee",
},
{
actions: [
"notify",
{
set_tweak: "sound",
value: "default",
},
{
set_tweak: "highlight",
},
],
enabled: true,
pattern: "foo*bar",
rule_id: "foobar",
},
],
override: [
{
actions: [
"notify",
{
set_tweak: "sound",
value: "default",
},
{
set_tweak: "highlight",
},
],
conditions: [
{
kind: "contains_display_name",
},
],
enabled: true,
rule_id: ".m.rule.contains_display_name",
},
{
actions: [
"notify",
{
set_tweak: "sound",
value: "default",
},
],
conditions: [
{
is: "2",
kind: "room_member_count",
},
],
enabled: true,
rule_id: ".m.rule.room_one_to_one",
},
],
room: [],
sender: [],
underride: [
msc3914RoomCallRule,
{
actions: ["dont-notify"],
conditions: [
{
key: "content.msgtype",
kind: "event_match",
pattern: "m.notice",
},
],
enabled: true,
rule_id: ".m.rule.suppress_notices",
},
{
actions: [
"notify",
{
set_tweak: "highlight",
value: false,
},
],
conditions: [],
enabled: true,
rule_id: ".m.rule.fallback",
},
],
},
},
} as unknown as MatrixClient;
let matrixClient: MatrixClient;
beforeEach(function () {
// These would be better if individual rules were configured in the tests themselves.
matrixClient = {
getRoom: function () {
return {
currentState: {
getMember: function () {
return {
name: testDisplayName,
};
},
getJoinedMemberCount: function () {
return 0;
},
members: {},
},
};
},
...mockClientMethodsUser(testUserId),
supportsIntentionalMentions: () => true,
pushRules: {
device: {},
global: {
content: [
{
actions: [
"notify",
{
set_tweak: "sound",
value: "default",
},
{
set_tweak: "highlight",
},
],
enabled: true,
pattern: "ali",
rule_id: ".m.rule.contains_user_name",
},
{
actions: [
"notify",
{
set_tweak: "sound",
value: "default",
},
{
set_tweak: "highlight",
},
],
enabled: true,
pattern: "coffee",
rule_id: "coffee",
},
{
actions: [
"notify",
{
set_tweak: "sound",
value: "default",
},
{
set_tweak: "highlight",
},
],
enabled: true,
pattern: "foo*bar",
rule_id: "foobar",
},
],
override: [
{
actions: [
"notify",
{
set_tweak: "sound",
value: "default",
},
{
set_tweak: "highlight",
},
],
conditions: [
{
kind: "contains_display_name",
},
],
enabled: true,
default: true,
rule_id: ".m.rule.contains_display_name",
},
{
actions: [
"notify",
{
set_tweak: "sound",
value: "default",
},
],
conditions: [
{
is: "2",
kind: "room_member_count",
},
],
enabled: true,
rule_id: ".m.rule.room_one_to_one",
},
],
room: [],
sender: [],
underride: [
msc3914RoomCallRule,
{
actions: ["dont-notify"],
conditions: [
{
key: "content.msgtype",
kind: "event_match",
pattern: "m.notice",
},
],
enabled: true,
rule_id: ".m.rule.suppress_notices",
},
{
actions: [
"notify",
{
set_tweak: "highlight",
value: false,
},
],
conditions: [],
enabled: true,
rule_id: ".m.rule.fallback",
},
],
},
},
} as unknown as MatrixClient;
testEvent = utils.mkEvent({
type: "m.room.message",
room: testRoomId,
@@ -699,3 +712,295 @@ describe("Test PushProcessor.partsForDottedKey", function () {
expect(PushProcessor.partsForDottedKey(path)).toStrictEqual(expected);
});
});
describe("rewriteDefaultRules", () => {
it("should add default rules in the correct order", () => {
const pushRules = PushProcessor.rewriteDefaultRules({
device: {},
global: {
content: [],
override: [
// Include user-defined push rules inbetween .m.rule.master and other default rules to assert they are maintained in-order.
{
rule_id: ".m.rule.master",
default: true,
enabled: false,
conditions: [],
actions: [],
},
{
actions: [
PushRuleActionName.Notify,
{
set_tweak: TweakName.Sound,
value: "default",
},
{
set_tweak: TweakName.Highlight,
},
],
enabled: true,
pattern: "coffee",
rule_id: "coffee",
default: false,
},
{
actions: [
PushRuleActionName.Notify,
{
set_tweak: TweakName.Sound,
value: "default",
},
{
set_tweak: TweakName.Highlight,
},
],
conditions: [
{
kind: ConditionKind.ContainsDisplayName,
},
],
enabled: true,
default: true,
rule_id: ".m.rule.contains_display_name",
},
{
actions: [
PushRuleActionName.Notify,
{
set_tweak: TweakName.Sound,
value: "default",
},
],
conditions: [
{
is: "2",
kind: ConditionKind.RoomMemberCount,
},
],
enabled: true,
rule_id: ".m.rule.room_one_to_one",
default: true,
},
],
room: [],
sender: [],
underride: [
{
actions: [
PushRuleActionName.Notify,
{
set_tweak: TweakName.Highlight,
value: false,
},
],
conditions: [],
enabled: true,
rule_id: "user-defined",
default: false,
},
msc3914RoomCallRule,
{
actions: [
PushRuleActionName.Notify,
{
set_tweak: TweakName.Highlight,
value: false,
},
],
conditions: [],
enabled: true,
rule_id: ".m.rule.fallback",
default: true,
},
],
},
});
// By the time we get here, we expect the PushProcessor to have merged the new .m.rule.is_room_mention rule into the existing list of rules.
// Check that has happened, and that it is in the right place.
const containsDisplayNameRuleIdx = pushRules.global.override?.findIndex(
(rule) => rule.rule_id === RuleId.ContainsDisplayName,
);
expect(containsDisplayNameRuleIdx).toBeGreaterThan(-1);
const isRoomMentionRuleIdx = pushRules.global.override?.findIndex(
(rule) => rule.rule_id === RuleId.IsRoomMention,
);
expect(isRoomMentionRuleIdx).toBeGreaterThan(-1);
const mReactionRuleIdx = pushRules.global.override?.findIndex((rule) => rule.rule_id === ".m.rule.reaction");
expect(mReactionRuleIdx).toBeGreaterThan(-1);
expect(containsDisplayNameRuleIdx).toBeLessThan(isRoomMentionRuleIdx!);
expect(isRoomMentionRuleIdx).toBeLessThan(mReactionRuleIdx!);
expect(pushRules.global.override?.map((r) => r.rule_id)).toEqual([
".m.rule.master",
"coffee",
".m.rule.contains_display_name",
".m.rule.room_one_to_one",
".m.rule.is_room_mention",
".m.rule.reaction",
".org.matrix.msc3786.rule.room.server_acl",
]);
expect(pushRules.global.underride?.map((r) => r.rule_id)).toEqual([
"user-defined",
".org.matrix.msc3914.rule.room.call",
// Assert that unknown default rules are maintained
".m.rule.fallback",
]);
});
it("should add missing msc3914 rule in correct place", () => {
const pushRules = PushProcessor.rewriteDefaultRules({
device: {},
global: {
// Sample push rules from a Synapse user.
// Note that rules 2 and 3 are backwards, this will trigger a warning in the console.
underride: [
{
conditions: [
{
kind: "event_match",
key: "type",
pattern: "m.call.invite",
},
],
actions: [
"notify",
{
set_tweak: "sound",
value: "ring",
},
{
set_tweak: "highlight",
value: false,
},
],
rule_id: ".m.rule.call",
default: true,
enabled: true,
},
{
conditions: [
{
kind: "event_match",
key: "type",
pattern: "m.room.message",
},
{
kind: "room_member_count",
is: "2",
},
],
actions: [
"notify",
{
set_tweak: "sound",
value: "TEST1",
},
{
set_tweak: "highlight",
value: false,
},
],
rule_id: ".m.rule.room_one_to_one",
default: true,
enabled: true,
},
{
conditions: [
{
kind: "event_match",
key: "type",
pattern: "m.room.encrypted",
},
{
kind: "room_member_count",
is: "2",
},
],
actions: [
"notify",
{
set_tweak: "sound",
value: "TEST2",
},
{
set_tweak: "highlight",
value: false,
},
],
rule_id: ".m.rule.encrypted_room_one_to_one",
default: true,
enabled: true,
},
{
conditions: [
{
kind: "event_match",
key: "type",
pattern: "m.room.message",
},
],
actions: ["dont_notify"],
rule_id: ".m.rule.message",
default: true,
enabled: true,
},
{
conditions: [
{
kind: "event_match",
key: "type",
pattern: "m.room.encrypted",
},
],
actions: ["dont_notify"],
rule_id: ".m.rule.encrypted",
default: true,
enabled: true,
},
{
conditions: [
{
kind: "event_match",
key: "type",
pattern: "im.vector.modular.widgets",
},
{
kind: "event_match",
key: "content.type",
pattern: "jitsi",
},
{
kind: "event_match",
key: "state_key",
pattern: "*",
},
],
actions: [
"notify",
{
set_tweak: "highlight",
value: false,
},
],
rule_id: ".im.vector.jitsi",
default: true,
enabled: true,
},
] as IPushRule[],
},
});
expect(pushRules.global.underride?.map((r) => r.rule_id)).toEqual([
".m.rule.call",
".org.matrix.msc3914.rule.room.call",
".m.rule.room_one_to_one",
".m.rule.encrypted_room_one_to_one",
".m.rule.message",
".m.rule.encrypted",
".im.vector.jitsi",
]);
});
});
+22 -21
View File
@@ -27,6 +27,7 @@ import {
UNSTABLE_MSC2666_QUERY_MUTUAL_ROOMS,
UNSTABLE_MSC2666_SHARED_ROOMS,
} from "../../src";
import { KnownMembership } from "../../src/@types/membership";
describe("RoomMember", function () {
const roomId = "!foo:bar";
@@ -50,7 +51,7 @@ describe("RoomMember", function () {
room: roomId,
user: userA,
content: {
membership: "join",
membership: KnownMembership.Join,
avatar_url: "mxc://flibble/wibble",
},
});
@@ -282,11 +283,11 @@ describe("RoomMember", function () {
describe("isKicked", () => {
it("should return false if membership is not `leave`", () => {
const member1 = new RoomMember(roomId, userA);
member1.membership = "join";
member1.membership = KnownMembership.Join;
expect(member1.isKicked()).toBeFalsy();
const member2 = new RoomMember(roomId, userA);
member2.membership = "invite";
member2.membership = KnownMembership.Invite;
expect(member2.isKicked()).toBeFalsy();
const member3 = new RoomMember(roomId, userA);
@@ -295,17 +296,17 @@ describe("RoomMember", function () {
it("should return false if the membership event is unknown", () => {
const member = new RoomMember(roomId, userA);
member.membership = "leave";
member.membership = KnownMembership.Leave;
expect(member.isKicked()).toBeFalsy();
});
it("should return false if the member left of their own accord", () => {
const member = new RoomMember(roomId, userA);
member.membership = "leave";
member.membership = KnownMembership.Leave;
member.events.member = utils.mkMembership({
event: true,
sender: userA,
mship: "leave",
mship: KnownMembership.Leave,
skey: userA,
});
expect(member.isKicked()).toBeFalsy();
@@ -313,11 +314,11 @@ describe("RoomMember", function () {
it("should return true if the member's leave was sent by another user", () => {
const member = new RoomMember(roomId, userA);
member.membership = "leave";
member.membership = KnownMembership.Leave;
member.events.member = utils.mkMembership({
event: true,
sender: userB,
mship: "leave",
mship: KnownMembership.Leave,
skey: userA,
});
expect(member.isKicked()).toBeTruthy();
@@ -327,11 +328,11 @@ describe("RoomMember", function () {
describe("getDMInviter", () => {
it("should return userId of the sender of the invite if is_direct=true", () => {
const member = new RoomMember(roomId, userA);
member.membership = "invite";
member.membership = KnownMembership.Invite;
member.events.member = utils.mkMembership({
event: true,
sender: userB,
mship: "invite",
mship: KnownMembership.Invite,
skey: userA,
});
member.events.member.event.content!.is_direct = true;
@@ -340,11 +341,11 @@ describe("RoomMember", function () {
it("should not return userId of the sender of the invite if is_direct=false", () => {
const member = new RoomMember(roomId, userA);
member.membership = "invite";
member.membership = KnownMembership.Invite;
member.events.member = utils.mkMembership({
event: true,
sender: userB,
mship: "invite",
mship: KnownMembership.Invite,
skey: userA,
});
member.events.member.event.content!.is_direct = false;
@@ -355,7 +356,7 @@ describe("RoomMember", function () {
describe("setMembershipEvent", function () {
const joinEvent = utils.mkMembership({
event: true,
mship: "join",
mship: KnownMembership.Join,
user: userA,
room: roomId,
name: "Alice",
@@ -363,7 +364,7 @@ describe("RoomMember", function () {
const inviteEvent = utils.mkMembership({
event: true,
mship: "invite",
mship: KnownMembership.Invite,
user: userB,
skey: userA,
room: roomId,
@@ -371,10 +372,10 @@ describe("RoomMember", function () {
it("should set 'membership' and assign the event to 'events.member'.", function () {
member.setMembershipEvent(inviteEvent);
expect(member.membership).toEqual("invite");
expect(member.membership).toEqual(KnownMembership.Invite);
expect(member.events.member).toEqual(inviteEvent);
member.setMembershipEvent(joinEvent);
expect(member.membership).toEqual("join");
expect(member.membership).toEqual(KnownMembership.Join);
expect(member.events.member).toEqual(joinEvent);
});
@@ -387,13 +388,13 @@ describe("RoomMember", function () {
return [
utils.mkMembership({
event: true,
mship: "join",
mship: KnownMembership.Join,
room: roomId,
user: userB,
}),
utils.mkMembership({
event: true,
mship: "join",
mship: KnownMembership.Join,
room: roomId,
user: userC,
name: "Alice",
@@ -443,7 +444,7 @@ describe("RoomMember", function () {
it("should set 'name' to user_id if it is just whitespace", function () {
const joinEvent = utils.mkMembership({
event: true,
mship: "join",
mship: KnownMembership.Join,
user: userA,
room: roomId,
name: " \u200b ",
@@ -457,7 +458,7 @@ describe("RoomMember", function () {
it("should disambiguate users on a fuzzy displayname match", function () {
const joinEvent = utils.mkMembership({
event: true,
mship: "join",
mship: KnownMembership.Join,
user: userA,
room: roomId,
name: "Alíce\u200b", // note diacritic and zero width char
@@ -471,7 +472,7 @@ describe("RoomMember", function () {
return [
utils.mkMembership({
event: true,
mship: "join",
mship: KnownMembership.Join,
room: roomId,
user: userC,
name: "Alice",
+62 -37
View File
@@ -28,6 +28,7 @@ import { MatrixClient } from "../../src/client";
import { DecryptionError } from "../../src/crypto/algorithms";
import { defer } from "../../src/utils";
import { Room } from "../../src/models/room";
import { KnownMembership } from "../../src/@types/membership";
describe("RoomState", function () {
const roomId = "!foo:bar";
@@ -44,14 +45,14 @@ describe("RoomState", function () {
utils.mkMembership({
// userA joined
event: true,
mship: "join",
mship: KnownMembership.Join,
user: userA,
room: roomId,
}),
utils.mkMembership({
// userB joined
event: true,
mship: "join",
mship: KnownMembership.Join,
user: userB,
room: roomId,
}),
@@ -102,20 +103,20 @@ describe("RoomState", function () {
it("should return a member which changes as state changes", function () {
const member = state.getMember(userB);
expect(member?.membership).toEqual("join");
expect(member?.membership).toEqual(KnownMembership.Join);
expect(member?.name).toEqual(userB);
state.setStateEvents([
utils.mkMembership({
room: roomId,
user: userB,
mship: "leave",
mship: KnownMembership.Leave,
event: true,
name: "BobGone",
}),
]);
expect(member?.membership).toEqual("leave");
expect(member?.membership).toEqual(KnownMembership.Leave);
expect(member?.name).toEqual("BobGone");
});
});
@@ -131,17 +132,17 @@ describe("RoomState", function () {
utils.mkMembership({
room: roomId,
user: userA,
mship: "leave",
mship: KnownMembership.Leave,
event: true,
name: "AliceIsGone",
}),
]);
const postLeaveUser = state.getSentinelMember(userA);
expect(preLeaveUser?.membership).toEqual("join");
expect(preLeaveUser?.membership).toEqual(KnownMembership.Join);
expect(preLeaveUser?.name).toEqual(userA);
expect(postLeaveUser?.membership).toEqual("leave");
expect(postLeaveUser?.membership).toEqual(KnownMembership.Leave);
expect(postLeaveUser?.name).toEqual("AliceIsGone");
});
});
@@ -166,7 +167,7 @@ describe("RoomState", function () {
it("should return a single MatrixEvent if a state_key was specified", function () {
const event = state.getStateEvents("m.room.member", userA);
expect(event?.getContent()).toMatchObject({
membership: "join",
membership: KnownMembership.Join,
});
});
});
@@ -176,13 +177,13 @@ describe("RoomState", function () {
const memberEvents = [
utils.mkMembership({
user: "@cleo:bar",
mship: "invite",
mship: KnownMembership.Invite,
room: roomId,
event: true,
}),
utils.mkMembership({
user: "@daisy:bar",
mship: "join",
mship: KnownMembership.Join,
room: roomId,
event: true,
}),
@@ -202,13 +203,13 @@ describe("RoomState", function () {
const memberEvents = [
utils.mkMembership({
user: "@cleo:bar",
mship: "invite",
mship: KnownMembership.Invite,
room: roomId,
event: true,
}),
utils.mkMembership({
user: "@daisy:bar",
mship: "join",
mship: KnownMembership.Join,
room: roomId,
event: true,
}),
@@ -228,7 +229,7 @@ describe("RoomState", function () {
const events = [
utils.mkMembership({
user: "@cleo:bar",
mship: "invite",
mship: KnownMembership.Invite,
room: roomId,
event: true,
}),
@@ -282,7 +283,7 @@ describe("RoomState", function () {
it("should call setPowerLevelEvent on a new RoomMember if power levels exist", function () {
const memberEvent = utils.mkMembership({
mship: "join",
mship: KnownMembership.Join,
user: userC,
room: roomId,
event: true,
@@ -312,7 +313,7 @@ describe("RoomState", function () {
it("should call setMembershipEvent on the right RoomMember", function () {
const memberEvent = utils.mkMembership({
user: userB,
mship: "leave",
mship: KnownMembership.Leave,
room: roomId,
event: true,
});
@@ -447,7 +448,7 @@ describe("RoomState", function () {
it("should add a new member", function () {
const oobMemberEvent = utils.mkMembership({
user: userLazy,
mship: "join",
mship: KnownMembership.Join,
room: roomId,
event: true,
});
@@ -462,7 +463,7 @@ describe("RoomState", function () {
state.setOutOfBandMembers([
utils.mkMembership({
user: userLazy,
mship: "join",
mship: KnownMembership.Join,
room: roomId,
event: true,
}),
@@ -474,7 +475,7 @@ describe("RoomState", function () {
const userLazy = "@oob:hs";
const oobMemberEvent = utils.mkMembership({
user: userLazy,
mship: "join",
mship: KnownMembership.Join,
room: roomId,
event: true,
});
@@ -491,7 +492,7 @@ describe("RoomState", function () {
it("should never overwrite existing members", function () {
const oobMemberEvent = utils.mkMembership({
user: userA,
mship: "join",
mship: KnownMembership.Join,
room: roomId,
event: true,
});
@@ -506,7 +507,7 @@ describe("RoomState", function () {
const doesntExistYetUserId = "@doesntexistyet:hs";
const oobMemberEvent = utils.mkMembership({
user: doesntExistYetUserId,
mship: "join",
mship: KnownMembership.Join,
room: roomId,
event: true,
});
@@ -529,7 +530,7 @@ describe("RoomState", function () {
state.setOutOfBandMembers([
utils.mkMembership({
user: userLazy,
mship: "join",
mship: KnownMembership.Join,
room: roomId,
event: true,
}),
@@ -554,7 +555,7 @@ describe("RoomState", function () {
copy.setOutOfBandMembers([
utils.mkMembership({
user: userA,
mship: "join",
mship: KnownMembership.Join,
room: roomId,
event: true,
}),
@@ -563,7 +564,7 @@ describe("RoomState", function () {
state.setOutOfBandMembers([
utils.mkMembership({
user: userLazy,
mship: "join",
mship: KnownMembership.Join,
room: roomId,
event: true,
}),
@@ -576,7 +577,7 @@ describe("RoomState", function () {
copy.setStateEvents([
utils.mkMembership({
user: userLazy,
mship: "join",
mship: KnownMembership.Join,
room: roomId,
event: true,
}),
@@ -697,9 +698,13 @@ describe("RoomState", function () {
});
it("should update after adding joined member", function () {
state.setStateEvents([utils.mkMembership({ event: true, mship: "join", user: userA, room: roomId })]);
state.setStateEvents([
utils.mkMembership({ event: true, mship: KnownMembership.Join, user: userA, room: roomId }),
]);
expect(state.getJoinedMemberCount()).toEqual(1);
state.setStateEvents([utils.mkMembership({ event: true, mship: "join", user: userC, room: roomId })]);
state.setStateEvents([
utils.mkMembership({ event: true, mship: KnownMembership.Join, user: userC, room: roomId }),
]);
expect(state.getJoinedMemberCount()).toEqual(2);
});
});
@@ -710,9 +715,13 @@ describe("RoomState", function () {
});
it("should update after adding invited member", function () {
state.setStateEvents([utils.mkMembership({ event: true, mship: "invite", user: userA, room: roomId })]);
state.setStateEvents([
utils.mkMembership({ event: true, mship: KnownMembership.Invite, user: userA, room: roomId }),
]);
expect(state.getInvitedMemberCount()).toEqual(1);
state.setStateEvents([utils.mkMembership({ event: true, mship: "invite", user: userC, room: roomId })]);
state.setStateEvents([
utils.mkMembership({ event: true, mship: KnownMembership.Invite, user: userC, room: roomId }),
]);
expect(state.getInvitedMemberCount()).toEqual(2);
});
});
@@ -723,19 +732,27 @@ describe("RoomState", function () {
});
it("should, once used, override counting members from state", function () {
state.setStateEvents([utils.mkMembership({ event: true, mship: "join", user: userA, room: roomId })]);
state.setStateEvents([
utils.mkMembership({ event: true, mship: KnownMembership.Join, user: userA, room: roomId }),
]);
expect(state.getJoinedMemberCount()).toEqual(1);
state.setJoinedMemberCount(100);
expect(state.getJoinedMemberCount()).toEqual(100);
state.setStateEvents([utils.mkMembership({ event: true, mship: "join", user: userC, room: roomId })]);
state.setStateEvents([
utils.mkMembership({ event: true, mship: KnownMembership.Join, user: userC, room: roomId }),
]);
expect(state.getJoinedMemberCount()).toEqual(100);
});
it("should, once used, override counting members from state, " + "also after clone", function () {
state.setStateEvents([utils.mkMembership({ event: true, mship: "join", user: userA, room: roomId })]);
state.setStateEvents([
utils.mkMembership({ event: true, mship: KnownMembership.Join, user: userA, room: roomId }),
]);
state.setJoinedMemberCount(100);
const copy = state.clone();
copy.setStateEvents([utils.mkMembership({ event: true, mship: "join", user: userC, room: roomId })]);
copy.setStateEvents([
utils.mkMembership({ event: true, mship: KnownMembership.Join, user: userC, room: roomId }),
]);
expect(state.getJoinedMemberCount()).toEqual(100);
});
});
@@ -746,19 +763,27 @@ describe("RoomState", function () {
});
it("should, once used, override counting members from state", function () {
state.setStateEvents([utils.mkMembership({ event: true, mship: "invite", user: userB, room: roomId })]);
state.setStateEvents([
utils.mkMembership({ event: true, mship: KnownMembership.Invite, user: userB, room: roomId }),
]);
expect(state.getInvitedMemberCount()).toEqual(1);
state.setInvitedMemberCount(100);
expect(state.getInvitedMemberCount()).toEqual(100);
state.setStateEvents([utils.mkMembership({ event: true, mship: "invite", user: userC, room: roomId })]);
state.setStateEvents([
utils.mkMembership({ event: true, mship: KnownMembership.Invite, user: userC, room: roomId }),
]);
expect(state.getInvitedMemberCount()).toEqual(100);
});
it("should, once used, override counting members from state, " + "also after clone", function () {
state.setStateEvents([utils.mkMembership({ event: true, mship: "invite", user: userB, room: roomId })]);
state.setStateEvents([
utils.mkMembership({ event: true, mship: KnownMembership.Invite, user: userB, room: roomId }),
]);
state.setInvitedMemberCount(100);
const copy = state.clone();
copy.setStateEvents([utils.mkMembership({ event: true, mship: "invite", user: userC, room: roomId })]);
copy.setStateEvents([
utils.mkMembership({ event: true, mship: KnownMembership.Invite, user: userC, room: roomId }),
]);
expect(state.getInvitedMemberCount()).toEqual(100);
});
});
+397 -324
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -30,6 +30,7 @@ import { RoomEncryptor, toRustHistoryVisibility } from "../../../src/rust-crypto
import { KeyClaimManager } from "../../../src/rust-crypto/KeyClaimManager";
import { defer } from "../../../src/utils";
import { OutgoingRequestsManager } from "../../../src/rust-crypto/OutgoingRequestsManager";
import { KnownMembership } from "../../../src/@types/membership";
describe("RoomEncryptor", () => {
describe("History Visibility", () => {
@@ -54,7 +55,7 @@ describe("RoomEncryptor", () => {
const mockRoomMember = {
userId: "@alice:example.org",
membership: "join",
membership: KnownMembership.Join,
} as unknown as Mocked<RoomMember>;
function createMockEvent(text: string): Mocked<MatrixEvent> {
@@ -99,6 +99,8 @@ describe("initRustCrypto", () => {
isBackupEnabled: jest.fn().mockResolvedValue(false),
verifyBackup: jest.fn().mockResolvedValue({ trusted: jest.fn().mockReturnValue(false) }),
getBackupKeys: jest.fn(),
getIdentity: jest.fn().mockResolvedValue(null),
trackedUsers: jest.fn(),
} as unknown as Mocked<OlmMachine>;
}
@@ -180,6 +182,7 @@ describe("initRustCrypto", () => {
jest.spyOn(Migration, "migrateMegolmSessions").mockResolvedValue(undefined);
const testOlmMachine = makeTestOlmMachine();
testOlmMachine.trackedUsers.mockResolvedValue([]);
jest.spyOn(OlmMachine, "initFromStore").mockResolvedValue(testOlmMachine);
});
+2 -1
View File
@@ -6,6 +6,7 @@ import { MatrixError } from "../../src/http-api";
import { MatrixScheduler } from "../../src/scheduler";
import * as utils from "../test-utils/test-utils";
import { MatrixEvent } from "../../src";
import { KnownMembership } from "../../src/@types/membership";
jest.useFakeTimers();
@@ -336,7 +337,7 @@ describe("MatrixScheduler", function () {
utils.mkMembership({
user: "@alice:bar",
room: roomId,
mship: "join",
mship: KnownMembership.Join,
event: true,
}),
),
+16 -15
View File
@@ -31,6 +31,7 @@ import {
} from "../../src/sync-accumulator";
import { IRoomSummary } from "../../src";
import * as utils from "../test-utils/test-utils";
import { KnownMembership, Membership } from "../../src/@types/membership";
// The event body & unsigned object get frozen to assert that they don't get altered
// by the impl
@@ -95,7 +96,7 @@ describe("SyncAccumulator", function () {
ephemeral: { events: [] },
unread_notifications: {},
state: {
events: [member("alice", "join"), member("bob", "join")],
events: [member("alice", KnownMembership.Join), member("bob", KnownMembership.Join)],
},
summary: {
"m.heroes": undefined,
@@ -111,7 +112,7 @@ describe("SyncAccumulator", function () {
knock: {
"!knock": {
knock_state: {
events: [member("alice", "knock")],
events: [member("alice", KnownMembership.Knock)],
},
},
},
@@ -128,7 +129,7 @@ describe("SyncAccumulator", function () {
// 10 timeline messages with a prev_batch of "pinned_to_1"
sa.accumulate(
syncSkeleton({
state: { events: [member("alice", "join")] },
state: { events: [member("alice", KnownMembership.Join)] },
timeline: {
events: [
msg("alice", "1"),
@@ -204,7 +205,7 @@ describe("SyncAccumulator", function () {
it("should remove the stored timeline on limited syncs", () => {
sa.accumulate(
syncSkeleton({
state: { events: [member("alice", "join")] },
state: { events: [member("alice", KnownMembership.Join)] },
timeline: {
events: [msg("alice", "1"), msg("alice", "2"), msg("alice", "3")],
prev_batch: "pinned_to_1",
@@ -312,7 +313,7 @@ describe("SyncAccumulator", function () {
events: [
{
content: {
membership: "invite",
membership: KnownMembership.Invite,
},
state_key: "bob",
sender: "alice",
@@ -336,7 +337,7 @@ describe("SyncAccumulator", function () {
const rejectMemberEvent: IStateEvent = {
event_id: "$" + Math.random(),
content: {
membership: "leave",
membership: KnownMembership.Leave,
},
origin_server_ts: 123456789,
state_key: "bob",
@@ -344,7 +345,7 @@ describe("SyncAccumulator", function () {
type: "m.room.member",
unsigned: {
prev_content: {
membership: "invite",
membership: KnownMembership.Invite,
},
},
};
@@ -366,7 +367,7 @@ describe("SyncAccumulator", function () {
it("should accumulate knock state", () => {
const initKnockState = {
events: [member("alice", "knock")],
events: [member("alice", KnownMembership.Knock)],
};
sa.accumulate(
syncSkeleton(
@@ -453,7 +454,7 @@ describe("SyncAccumulator", function () {
const inviteStateEvents = [
{
content: {
membership: "invite",
membership: KnownMembership.Invite,
},
state_key: "bob",
sender: "alice",
@@ -482,7 +483,7 @@ describe("SyncAccumulator", function () {
const memberEvent: IStateEvent = {
event_id: "$" + Math.random(),
content: {
membership: "leave",
membership: KnownMembership.Leave,
},
origin_server_ts: 123456789,
state_key: "bob",
@@ -490,7 +491,7 @@ describe("SyncAccumulator", function () {
type: "m.room.member",
unsigned: {
prev_content: {
membership: "knock",
membership: KnownMembership.Knock,
},
},
};
@@ -528,7 +529,7 @@ describe("SyncAccumulator", function () {
const memberEvent: IStateEvent = {
event_id: "$" + Math.random(),
content: {
membership: "leave",
membership: KnownMembership.Leave,
},
origin_server_ts: 123456789,
state_key: "bob",
@@ -536,7 +537,7 @@ describe("SyncAccumulator", function () {
type: "m.room.member",
unsigned: {
prev_content: {
membership: "knock",
membership: KnownMembership.Knock,
},
},
};
@@ -933,7 +934,7 @@ function makeKnockState(): IKnockState {
name: "Room",
},
}) as IStrippedState,
member("bob", "knock"),
member("bob", KnownMembership.Knock),
],
};
}
@@ -950,7 +951,7 @@ function msg(localpart: string, text: string) {
};
}
function member(localpart: string, membership: string) {
function member(localpart: string, membership: Membership) {
return {
event_id: "$" + Math.random(),
content: {
+12 -11
View File
@@ -47,6 +47,7 @@ import { flushPromises } from "../../test-utils/flushPromises";
import { CallFeedReport } from "../../../src/webrtc/stats/statsReport";
import { CallFeedStatsReporter } from "../../../src/webrtc/stats/callFeedStatsReporter";
import { StatsReportEmitter } from "../../../src/webrtc/stats/statsReportEmitter";
import { KnownMembership } from "../../../src/@types/membership";
const FAKE_STATE_EVENTS = [
{
@@ -143,7 +144,7 @@ describe("Group Call", function () {
groupCall = new GroupCall(mockClient, room, GroupCallType.Video, false, GroupCallIntent.Prompt);
room.currentState.members[FAKE_USER_ID_1] = {
userId: FAKE_USER_ID_1,
membership: "join",
membership: KnownMembership.Join,
} as unknown as RoomMember;
});
@@ -651,11 +652,11 @@ describe("Group Call", function () {
client1Room.currentState.members[FAKE_USER_ID_1] = client2Room.currentState.members[FAKE_USER_ID_1] = {
userId: FAKE_USER_ID_1,
membership: "join",
membership: KnownMembership.Join,
} as unknown as RoomMember;
client1Room.currentState.members[FAKE_USER_ID_2] = client2Room.currentState.members[FAKE_USER_ID_2] = {
userId: FAKE_USER_ID_2,
membership: "join",
membership: KnownMembership.Join,
} as unknown as RoomMember;
groupCall1 = new GroupCall(
@@ -831,11 +832,11 @@ describe("Group Call", function () {
room.currentState.getStateEvents = jest.fn().mockImplementation(mockGetStateEvents());
room.currentState.members[FAKE_USER_ID_1] = {
userId: FAKE_USER_ID_1,
membership: "join",
membership: KnownMembership.Join,
} as unknown as RoomMember;
room.currentState.members[FAKE_USER_ID_2] = {
userId: FAKE_USER_ID_2,
membership: "join",
membership: KnownMembership.Join,
} as unknown as RoomMember;
});
@@ -1026,11 +1027,11 @@ describe("Group Call", function () {
room = new Room(FAKE_ROOM_ID, mockClient, FAKE_USER_ID_2);
room.currentState.members[FAKE_USER_ID_1] = {
userId: FAKE_USER_ID_1,
membership: "join",
membership: KnownMembership.Join,
} as unknown as RoomMember;
room.currentState.members[FAKE_USER_ID_2] = {
userId: FAKE_USER_ID_2,
membership: "join",
membership: KnownMembership.Join,
} as unknown as RoomMember;
groupCall = await createAndEnterGroupCall(mockClient, room);
@@ -1260,11 +1261,11 @@ describe("Group Call", function () {
room = new Room(FAKE_ROOM_ID, mockClient, FAKE_USER_ID_1);
room.currentState.members[FAKE_USER_ID_1] = {
userId: FAKE_USER_ID_1,
membership: "join",
membership: KnownMembership.Join,
} as unknown as RoomMember;
room.currentState.members[FAKE_USER_ID_2] = {
userId: FAKE_USER_ID_2,
membership: "join",
membership: KnownMembership.Join,
} as unknown as RoomMember;
room.currentState.getStateEvents = jest.fn().mockImplementation(mockGetStateEvents());
@@ -1741,11 +1742,11 @@ describe("Group Call", function () {
const room = new Room(FAKE_ROOM_ID, mockClient, FAKE_USER_ID_1);
room.currentState.members[FAKE_USER_ID_1] = {
userId: FAKE_USER_ID_1,
membership: "join",
membership: KnownMembership.Join,
} as unknown as RoomMember;
room.currentState.members[FAKE_USER_ID_2] = {
userId: FAKE_USER_ID_2,
membership: "join",
membership: KnownMembership.Join,
} as unknown as RoomMember;
room.currentState.getStateEvents = jest.fn().mockImplementation(mockGetStateEvents());
groupCall = await createAndEnterGroupCall(mockClient, room);
@@ -31,6 +31,7 @@ import { RoomState } from "../../../src/models/room-state";
import { GroupCallEventHandler, GroupCallEventHandlerEvent } from "../../../src/webrtc/groupCallEventHandler";
import { flushPromises } from "../../test-utils/flushPromises";
import { makeMockGroupCallStateEvent, MockCallMatrixClient } from "../../test-utils/webrtc";
import { KnownMembership } from "../../../src/@types/membership";
const FAKE_USER_ID = "@alice:test.dummy";
const FAKE_DEVICE_ID = "AAAAAAA";
@@ -50,7 +51,7 @@ describe("Group Call Event Handler", function () {
mockMember = {
userId: FAKE_USER_ID,
membership: "join",
membership: KnownMembership.Join,
} as unknown as RoomMember;
const mockEvent = makeMockGroupCallStateEvent(FAKE_ROOM_ID, FAKE_GROUP_CALL_ID);
+252
View File
@@ -0,0 +1,252 @@
/*
Copyright 2024 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { MsgType } from "../@types/event";
/**
* Information on encrypted media attachments.
*
* Used within `m.room.message` events that reference files, such as `m.file` and `m.image`.
*
* @see https://spec.matrix.org/v1.9/client-server-api/#extensions-to-mroommessage-msgtypes
*/
export interface EncryptedFile {
/**
* The URL to the file.
*/
url: string;
/**
* A JSON Web Key object.
*/
key: {
alg: string;
key_ops: string[]; // eslint-disable-line camelcase
kty: string;
k: string;
ext: boolean;
};
/**
* The 128-bit unique counter block used by AES-CTR, encoded as unpadded base64.
*/
iv: string;
/**
* A map from an algorithm name to a hash of the ciphertext, encoded as unpadded base64.
* Clients should support the SHA-256 hash, which uses the key `sha256`.
*/
hashes: { [alg: string]: string };
/**
* Version of the encrypted attachment's protocol. Must be `v2`.
*/
v: string;
}
interface ThumbnailInfo {
/**
* The mimetype of the image, e.g. image/jpeg.
*/
mimetype?: string;
/**
* The intended display width of the image in pixels.
* This may differ from the intrinsic dimensions of the image file.
*/
w?: number;
/**
* The intended display height of the image in pixels.
* This may differ from the intrinsic dimensions of the image file.
*/
h?: number;
/**
* Size of the image in bytes.
*/
size?: number;
}
interface BaseInfo {
mimetype?: string;
size?: number;
}
/**
* Information on media attachments of msgtype `m.file`
*
* Used within `m.room.message` events that reference files.
*
* @see https://spec.matrix.org/v1.9/client-server-api/#mfile
*/
export interface FileInfo extends BaseInfo {
/**
* Information on the encrypted thumbnail file, as specified in End-to-end encryption.
* Only present if the thumbnail is encrypted.
* @see https://spec.matrix.org/v1.9/client-server-api/#sending-encrypted-attachments
*/
thumbnail_file?: EncryptedFile;
/**
* Metadata about the image referred to in thumbnail_url.
*/
thumbnail_info?: ThumbnailInfo;
/**
* The URL to the thumbnail of the file. Only present if the thumbnail is unencrypted.
*/
thumbnail_url?: string;
}
/**
* Information on media attachments of msgtype `m.image`
*
* Used within `m.room.message` events that reference images.
*
* @see https://spec.matrix.org/v1.9/client-server-api/#mimage
*/
export interface ImageInfo extends FileInfo, ThumbnailInfo {}
/**
* Information on media attachments of msgtype `m.audio`
*
* Used within `m.room.message` events that reference audio files.
*
* @see https://spec.matrix.org/v1.9/client-server-api/#maudio
*/
export interface AudioInfo extends BaseInfo {
/**
* The duration of the audio in milliseconds.
*/
duration?: number;
}
/**
* Information on media attachments of msgtype `m.video`
*
* Used within `m.room.message` events that reference video files.
*
* @see https://spec.matrix.org/v1.9/client-server-api/#mvideo
*/
export interface VideoInfo extends AudioInfo, ImageInfo {
/**
* The duration of the video in milliseconds.
*/
duration?: number;
}
/**
* Union type representing the `content.info` field of all specified media events.
*/
export type MediaEventInfo = FileInfo | ImageInfo | AudioInfo | VideoInfo;
interface BaseContent {
/**
* Required if the file is encrypted. Information on the encrypted file, as specified in End-to-end encryption.
* @see https://spec.matrix.org/v1.9/client-server-api/#sending-encrypted-attachments
*/
file?: EncryptedFile;
/**
* Required if the file is unencrypted. The URL (typically mxc:// URI) to the file.
*/
url?: string;
}
/**
* Content format of media events with msgtype `m.file`
*
* @see https://spec.matrix.org/v1.9/client-server-api/#mfile
*/
export interface FileContent extends BaseContent {
/**
* A human-readable description of the file.
* This is recommended to be the filename of the original upload.
*/
body: string;
/**
* The original filename of the uploaded file.
*/
filename?: string;
/**
* Information about the file referred to in url.
*/
info?: FileInfo;
/**
* One of: [m.file].
*/
msgtype: MsgType.File;
}
/**
* Content format of media events with msgtype `m.image`
*
* @see https://spec.matrix.org/v1.9/client-server-api/#mimage
*/
export interface ImageContent extends BaseContent {
/**
* A textual representation of the image.
* This could be the alt text of the image, the filename of the image,
* or some kind of content description for accessibility e.g. image attachment.
*/
body: string;
/**
* Metadata about the image referred to in url.
*/
info?: ImageInfo;
/**
* One of: [m.image].
*/
msgtype: MsgType.Image;
}
/**
* Content format of media events with msgtype `m.audio`
*
* @see https://spec.matrix.org/v1.9/client-server-api/#maudio
*/
export interface AudioContent extends BaseContent {
/**
* A description of the audio e.g. Bee Gees - Stayin Alive,
* or some kind of content description for accessibility e.g. audio attachment.
*/
body: string;
/**
* Metadata for the audio clip referred to in url.
*/
info?: AudioInfo;
/**
* One of: [m.audio].
*/
msgtype: MsgType.Audio;
}
/**
* Content format of media events with msgtype `m.video`
*
* @see https://spec.matrix.org/v1.9/client-server-api/#mvideo
*/
export interface VideoContent extends BaseContent {
/**
* A description of the video e.g. Gangnam style,
* or some kind of content description for accessibility e.g. video attachment.
*/
body: string;
/**
* Metadata about the video clip referred to in url.
*/
info?: VideoInfo;
/**
* One of: [m.video].
*/
msgtype: MsgType.Video;
}
/**
* Type representing media event contents for `m.room.message` events listed in the Matrix specification
*/
export type MediaEventContent = FileContent | ImageContent | AudioContent | VideoContent;
+57
View File
@@ -0,0 +1,57 @@
/*
Copyright 2024 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* Well-known values (from the spec or MSCs) that are allowed in the
* {@link Membership} type.
*/
export enum KnownMembership {
/**
* The user has been banned from the room, and is no longer allowed to join
* it until they are un-banned from the room (by having their membership
* state set to a value other than ban).
*/
Ban = "ban",
/**
* The user has been invited to join a room, but has not yet joined it.
* They may not participate in the room until they join.
* */
Invite = "invite",
/**
* The user has joined the room (possibly after accepting an invite), and
* may participate in it.
*/
Join = "join",
/**
* The user has knocked on the room, requesting permission to participate.
* They may not participate in the room until they join.
*/
Knock = "knock",
/**
* The user was once joined to the room, but has since left (possibly by
* choice, or possibly by being kicked).
*/
Leave = "leave",
}
/**
* The membership state for a user in a room [1]. A value from
* {@link KnownMembership} should be used where available, but all string values
* are allowed to provide flexibility for upcoming spec changes or proposals.
*
* [1] https://spec.matrix.org/latest/client-server-api/#mroommember
*/
export type Membership = KnownMembership | string;
+11 -1
View File
@@ -162,11 +162,21 @@ export interface ICreateRoomOpts {
}
export interface IRoomDirectoryOptions {
/**
* The remote server to query for the room list.
* Optional. If unspecified, get the local homeserver's public room list.
*/
server?: string;
/**
* Maximum number of entries to return
*/
limit?: number;
/**
* Token to paginate from
*/
since?: string;
// Filter parameters
/** Filter parameters */
filter?: {
// String to search for
generic_search_term?: string;
+23 -25
View File
@@ -222,6 +222,7 @@ import {
import { RegisterRequest, RegisterResponse } from "./@types/registration";
import { MatrixRTCSessionManager } from "./matrixrtc/MatrixRTCSessionManager";
import { getRelationsThreadFilter } from "./thread-utils";
import { KnownMembership, Membership } from "./@types/membership";
export type Store = IStore;
@@ -721,7 +722,7 @@ export interface IOpenIDToken {
interface IRoomInitialSyncResponse {
room_id: string;
membership: "invite" | "join" | "leave" | "ban";
membership: Membership;
messages?: {
start?: string;
end?: string;
@@ -866,7 +867,7 @@ interface IThirdPartyUser {
interface IRoomSummary extends Omit<IPublicRoomsChunkRoom, "canonical_alias" | "aliases"> {
room_type?: RoomType;
membership?: string;
membership?: Membership;
is_encrypted: boolean;
}
@@ -3286,7 +3287,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* Encrypts and sends a given object via Olm to-device messages to a given
* set of devices.
*
* @param userDeviceMap - mapping from userId to deviceInfo
* @param userDeviceInfoArr - list of deviceInfo objects representing the devices to send to
*
* @param payload - fields to include in the encrypted payload
*
@@ -4357,7 +4358,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
}
const room = this.getRoom(roomIdOrAlias);
if (room?.hasMembershipState(this.credentials.userId!, "join")) return room;
if (room?.hasMembershipState(this.credentials.userId!, KnownMembership.Join)) return room;
let signPromise: Promise<IThirdPartySigned | void> = Promise.resolve();
@@ -4386,7 +4387,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
// with the resolved ID - this method is supposed to no-op if we already
// were in the room, after all.
const resolvedRoom = this.getRoom(roomId);
if (resolvedRoom?.hasMembershipState(this.credentials.userId!, "join")) return resolvedRoom;
if (resolvedRoom?.hasMembershipState(this.credentials.userId!, KnownMembership.Join)) return resolvedRoom;
const syncApi = new SyncApi(this, this.clientOpts, this.buildSyncApiOptions());
const syncRoom = syncApi.createRoom(roomId);
@@ -4406,7 +4407,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
*/
public knockRoom(roomIdOrAlias: string, opts: KnockRoomOpts = {}): Promise<{ room_id: string }> {
const room = this.getRoom(roomIdOrAlias);
if (room?.hasMembershipState(this.credentials.userId!, "knock")) {
if (room?.hasMembershipState(this.credentials.userId!, KnownMembership.Knock)) {
return Promise.resolve({ room_id: room.roomId });
}
@@ -5548,7 +5549,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @returns Rejects: with an error response.
*/
public invite(roomId: string, userId: string, reason?: string): Promise<{}> {
return this.membershipChange(roomId, userId, "invite", reason);
return this.membershipChange(roomId, userId, KnownMembership.Invite, reason);
}
/**
@@ -5603,7 +5604,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @returns Rejects: with an error response.
*/
public leave(roomId: string): Promise<{}> {
return this.membershipChange(roomId, undefined, "leave");
return this.membershipChange(roomId, undefined, KnownMembership.Leave);
}
/**
@@ -5661,7 +5662,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @returns Rejects: with an error response.
*/
public ban(roomId: string, userId: string, reason?: string): Promise<{}> {
return this.membershipChange(roomId, userId, "ban", reason);
return this.membershipChange(roomId, userId, KnownMembership.Ban, reason);
}
/**
@@ -5720,7 +5721,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
private membershipChange(
roomId: string,
userId: string | undefined,
membership: string,
membership: Membership | "forget",
reason?: string,
): Promise<{}> {
// API returns an empty object
@@ -6565,8 +6566,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
const timelineSet = eventTimeline.getTimelineSet();
timelineSet.addEventsToTimeline(matrixEvents, backwards, eventTimeline, newToken ?? null);
if (!newToken && backwards) {
const originalEvent = await this.fetchRoomEvent(eventTimeline.getRoomId() ?? "", thread.id);
timelineSet.addEventsToTimeline([mapper(originalEvent)], true, eventTimeline, null);
const originalEvent =
thread.rootEvent ??
mapper(await this.fetchRoomEvent(eventTimeline.getRoomId() ?? "", thread.id));
timelineSet.addEventsToTimeline([originalEvent], true, eventTimeline, null);
}
this.processAggregatedTimelineEvents(timelineSet.room, matrixEvents);
@@ -7498,7 +7501,6 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* store client options with boolean/string/numeric values
* to know in the next session what flags the sync data was
* created with (e.g. lazy loading)
* @param opts - the complete set of client options
* @returns for store operation
*/
public storeClientOptions(): Promise<void> {
@@ -8480,12 +8482,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
}
/**
* @param options - Options for this request
* @param server - The remote server to query for the room list.
* Optional. If unspecified, get the local home
* server's public room list.
* @param limit - Maximum number of entries to return
* @param since - Token to paginate from
* @param params - Options for this request
* @returns Promise which resolves: IPublicRoomsResponse
* @returns Rejects: with an error response.
*/
@@ -8601,9 +8598,9 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
/**
* Query the user directory with a term matching user IDs, display names and domains.
* @param term - the term with which to search.
* @param limit - the maximum number of results to return. The server will
* apply a limit if unspecified.
* @param options
* @param options.term - the term with which to search.
* @param options.limit - the maximum number of results to return. The server will apply a limit if unspecified.
* @returns Promise which resolves: an array of results.
*/
public searchUserDirectory({ term, limit }: { term: string; limit?: number }): Promise<IUserDirectoryResponse> {
@@ -9008,8 +9005,9 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
/**
* Perform a server-side search.
* @param next_batch - the batch token to pass in the query string
* @param body - the JSON object to pass to the request body.
* @param params
* @param params.next_batch - the batch token to pass in the query string
* @param params.body - the JSON object to pass to the request body.
* @param abortSignal - optional signal used to cancel the http request.
* @returns Promise which resolves to the search response object.
* @returns Rejects: with an error response.
@@ -9783,7 +9781,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
*/
public unstableGetFileTreeSpace(roomId: string): MSC3089TreeSpace | null {
const room = this.getRoom(roomId);
if (room?.getMyMembership() !== "join") return null;
if (room?.getMyMembership() !== KnownMembership.Join) return null;
const createEvent = room.currentState.getStateEvents(EventType.RoomCreate, "");
const purposeEvent = room.currentState.getStateEvents(
+8 -5
View File
@@ -55,7 +55,14 @@ function checkPayloadLength(payloadString: string): void {
}
interface IInitOpts {
/**
* (Optional) data from exported device that must be re-created.
* If present, opts.pickleKey is ignored (exported data already provides a pickle key)
*/
fromExportedDevice?: IExportedDevice;
/**
* (Optional) pickle key to set instead of default one
*/
pickleKey?: string;
}
@@ -174,11 +181,7 @@ export class OlmDevice {
*
* Reads the device keys from the OlmAccount object.
*
* @param fromExportedDevice - (Optional) data from exported device
* that must be re-created.
* If present, opts.pickleKey is ignored
* (exported data already provides a pickle key)
* @param pickleKey - (Optional) pickle key to set instead of default one
* @param IInitOpts - opts to initialise the OlmAccount with
*/
public async init({ pickleKey, fromExportedDevice }: IInitOpts = {}): Promise<void> {
let e2eKeys;
+2 -1
View File
@@ -44,6 +44,7 @@ import { RoomKeyRequestState } from "../OutgoingRoomKeyRequestManager";
import { OlmGroupSessionExtraData } from "../../@types/crypto";
import { MatrixError } from "../../http-api";
import { immediate, MapWithDefault } from "../../utils";
import { KnownMembership } from "../../@types/membership";
// determine whether the key can be shared with invitees
export function isRoomSharedHistory(room: Room): boolean {
@@ -1680,7 +1681,7 @@ export class MegolmDecryption extends DecryptionAlgorithm {
const fromInviter =
memberEvent?.getSender() === senderKeyUser ||
(memberEvent?.getUnsigned()?.prev_sender === senderKeyUser &&
memberEvent?.getPrevContent()?.membership === "invite");
memberEvent?.getPrevContent()?.membership === KnownMembership.Invite);
if (room && fromInviter) {
return true;
+4 -28
View File
@@ -104,6 +104,7 @@ import { Device, DeviceMap } from "../models/device";
import { deviceInfoToDevice } from "./device-converter";
import { ClientPrefix, MatrixError, Method } from "../http-api";
import { decodeBase64, encodeBase64 } from "../base64";
import { KnownMembership } from "../@types/membership";
/* re-exports for backwards compatibility */
export type {
@@ -473,8 +474,6 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
*
* @param cryptoStore - storage for the crypto layer.
*
* @param roomList - An initialised RoomList object
*
* @param verificationMethods - Array of verification methods to use.
* Each element can either be a string from MatrixClient.verificationMethods
* or a class that implements a verification method.
@@ -842,15 +841,6 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
* secret storage (if it has been setup)
*
* The cross-signing API is currently UNSTABLE and may change without notice.
*
* @param authUploadDeviceSigningKeys - Function
* called to await an interactive auth flow when uploading device signing keys.
* @param setupNewCrossSigning - Optional. Reset even if keys
* already exist.
* Args:
* A function that makes the request requiring auth. Receives the
* auth data as an object. Can be called multiple times, first with an empty
* authDict, to obtain the flows.
*/
public async bootstrapCrossSigning({
authUploadDeviceSigningKeys,
@@ -964,20 +954,6 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
*
* The Secure Secret Storage API is currently UNSTABLE and may change without notice.
*
* @param createSecretStorageKey - Optional. Function
* called to await a secret storage key creation flow.
* Returns a Promise which resolves to an object with public key metadata, encoded private
* recovery key which should be disposed of after displaying to the user,
* and raw private key to avoid round tripping if needed.
* @param keyBackupInfo - The current key backup object. If passed,
* the passphrase and recovery key from this backup will be used.
* @param setupNewKeyBackup - If true, a new key backup version will be
* created and the private key stored in the new SSSS store. Ignored if keyBackupInfo
* is supplied.
* @param setupNewSecretStorage - Optional. Reset even if keys already exist.
* @param getKeyBackupPassphrase - Optional. Function called to get the user's
* current key backup passphrase. Should return a promise that resolves with a Buffer
* containing the key, or rejects if the key cannot be obtained.
* Returns:
* A promise which resolves to key creation data for
* SecretStorage#addKey: an object with `passphrase` etc fields.
@@ -3529,7 +3505,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
// ignore any rooms which we have left
const myMembership = room.getMyMembership();
return myMembership === "join" || myMembership === "invite";
return myMembership === KnownMembership.Join || myMembership === KnownMembership.Invite;
});
}
@@ -4012,12 +3988,12 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
// the result of anyway, as we'll need to do a query again once all the members are fetched
// by calling _trackRoomDevices
if (roomId in this.roomDeviceTrackingState) {
if (member.membership == "join") {
if (member.membership == KnownMembership.Join) {
logger.log("Join event for " + member.userId + " in " + roomId);
// make sure we are tracking the deviceList for this user
this.deviceList.startTrackingDeviceList(member.userId);
} else if (
member.membership == "invite" &&
member.membership == KnownMembership.Invite &&
this.clientStore.getRoom(roomId)?.shouldEncryptForInvitedMembers()
) {
logger.log("Invite event for " + member.userId + " in " + roomId);
@@ -495,8 +495,9 @@ export class VerificationRequest<C extends IVerificationChannel = IVerificationC
/**
* Cancels the request, sending a cancellation to the other party
* @param reason - the error reason to send the cancellation with
* @param code - the error code to send the cancellation with
* @param params
* @param params.reason - the error reason to send the cancellation with
* @param params.code - the error code to send the cancellation with
* @returns resolves when the event has been sent.
*/
public async cancel({ reason = "User declined", code = "m.user" } = {}): Promise<void> {
+12 -1
View File
@@ -108,11 +108,22 @@ export class RoomWidgetClient extends MatrixClient {
private lifecycle?: AbortController;
private syncState: SyncState | null = null;
/**
*
* @param widgetApi - The widget api to use for communication.
* @param capabilities - The capabilities the widget client will request.
* @param roomId - The room id the widget is associated with.
* @param opts - The configuration options for this client.
* @param sendContentLoaded - Whether to send a content loaded widget action immediately after initial setup.
* Set to `false` if the widget uses `waitForIFrameLoad=true` (in this case the client does not expect a content loaded action at all),
* or if the the widget wants to send the `ContentLoaded` action at a later point in time after the initial setup.
*/
public constructor(
private readonly widgetApi: WidgetApi,
private readonly capabilities: ICapabilities,
private readonly roomId: string,
opts: IMatrixClientCreateOpts,
sendContentLoaded: boolean,
) {
super(opts);
@@ -165,7 +176,7 @@ export class RoomWidgetClient extends MatrixClient {
// does *not* (yes, that is the right way around) wait for this event. Let's
// start sending this, then once this has rolled out, we can change element-web to
// use waitForIFrameLoad=false and have a widget API that's less racy.
widgetApi.sendContentLoaded();
if (sendContentLoaded) widgetApi.sendContentLoaded();
}
public async startClient(opts: IStartClientOpts = {}): Promise<void> {
+2 -2
View File
@@ -111,8 +111,8 @@ export class FetchHttpApi<O extends IHttpOpts> {
*
* @param body - The HTTP JSON body.
*
* @param opts - additional options.
* When `opts.doNotAttemptTokenRefresh` is true, token refresh will not be attempted
* @param paramOpts - additional options.
* When `paramOpts.doNotAttemptTokenRefresh` is true, token refresh will not be attempted
* when an expired token is encountered. Used to only attempt token refresh once.
*
* @returns Promise which resolves to
+16 -1
View File
@@ -164,11 +164,26 @@ export function createClient(opts: ICreateClientOpts): MatrixClient {
return new MatrixClient(amendClientOpts(opts));
}
/**
* Construct a Matrix Client that works in a widget.
* This client has a subset of features compared to a full client.
* It uses the widget-api to communicate with matrix. (widget \<-\> client \<-\> homeserver)
* @returns A new matrix client with a subset of features.
* @param opts - The configuration options for this client. These configuration
* options will be passed directly to {@link MatrixClient}.
* @param widgetApi - The widget api to use for communication.
* @param capabilities - The capabilities the widget client will request.
* @param roomId - The room id the widget is associated with.
* @param sendContentLoaded - Whether to send a content loaded widget action immediately after initial setup.
* Set to `false` if the widget uses `waitForIFrameLoad=true` (in this case the client does not expect a content loaded action at all),
* or if the the widget wants to send the `ContentLoaded` action at a later point in time after the initial setup.
*/
export function createRoomWidgetClient(
widgetApi: WidgetApi,
capabilities: ICapabilities,
roomId: string,
opts: ICreateClientOpts,
sendContentLoaded = true,
): MatrixClient {
return new RoomWidgetClient(widgetApi, capabilities, roomId, amendClientOpts(opts));
return new RoomWidgetClient(widgetApi, capabilities, roomId, amendClientOpts(opts), sendContentLoaded);
}
+5 -3
View File
@@ -23,10 +23,12 @@ import { EventType } from "../@types/event";
import { CallMembership, CallMembershipData } from "./CallMembership";
import { RoomStateEvent } from "../models/room-state";
import { Focus } from "./focus";
import { MatrixError, MatrixEvent } from "../matrix";
import { randomString, secureRandomBase64Url } from "../randomstring";
import { EncryptionKeysEventContent } from "./types";
import { decodeBase64, encodeUnpaddedBase64 } from "../base64";
import { KnownMembership } from "../@types/membership";
import { MatrixError } from "../http-api/errors";
import { MatrixEvent } from "../models/event";
const MEMBERSHIP_EXPIRY_TIME = 60 * 60 * 1000;
const MEMBER_EVENT_CHECK_PERIOD = 2 * 60 * 1000; // How often we check to see if we need to re-send our member event
@@ -156,7 +158,7 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
logger.info(`Ignoring expired device membership ${membership.sender}/${membership.deviceId}`);
continue;
}
if (!room.hasMembershipState(membership.sender ?? "", "join")) {
if (!room.hasMembershipState(membership.sender ?? "", KnownMembership.Join)) {
logger.info(`Ignoring membership of user ${membership.sender} who is not in the room.`);
continue;
}
@@ -610,7 +612,7 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
/**
* Constructs our own membership
* @param prevEvent - The previous version of our call membership, if any
* @param prevMembership - The previous value of our call membership, if any
*/
private makeMyMembership(prevMembership?: CallMembership): CallMembershipData {
if (this.relativeExpiry === undefined) {
+3 -2
View File
@@ -33,6 +33,7 @@ import { MSC3089Branch } from "./MSC3089Branch";
import { isRoomSharedHistory } from "../crypto/algorithms/megolm";
import { ISendEventResponse } from "../@types/requests";
import { FileType } from "../http-api";
import { KnownMembership } from "../@types/membership";
/**
* The recommended defaults for a tree space's power levels. Note that this
@@ -291,11 +292,11 @@ export class MSC3089TreeSpace {
await dir.delete();
}
const kickMemberships = ["invite", "knock", "join"];
const kickMemberships = [KnownMembership.Invite, KnownMembership.Knock, KnownMembership.Join];
const members = this.room.currentState.getStateEvents(EventType.RoomMember);
for (const member of members) {
const isNotUs = member.getStateKey() !== this.client.getUserId();
if (isNotUs && kickMemberships.includes(member.getContent().membership!)) {
if (isNotUs && kickMemberships.includes(member.getContent().membership! as KnownMembership)) {
const stateKey = member.getStateKey();
if (!stateKey) {
throw new Error("State key not found for branch");
+3 -3
View File
@@ -159,7 +159,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
* @param client - the Matrix client which owns this EventTimelineSet,
* can be omitted if room is specified.
* @param thread - the thread to which this timeline set relates.
* @param isThreadTimeline - Whether this timeline set relates to a thread list timeline
* @param threadListType - the type of thread list represented, if any
* (e.g., All threads or My threads)
*/
public constructor(
@@ -674,6 +674,8 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
*
* Will fire "Room.timeline" for each event added.
*
* @param event - the event to add
* @param timeline - the timeline onto which to add it
* @param options - addEventToTimeline options
*
* @remarks
@@ -771,8 +773,6 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
*
* @internal
*
* @param options - addEventToTimeline options
*
* @remarks
* Fires {@link RoomEvent.Timeline}
*/
+3 -2
View File
@@ -47,6 +47,7 @@ import { WITHHELD_MESSAGES } from "../crypto/OlmDevice";
import { IAnnotatedPushRule } from "../@types/PushRules";
import { Room } from "./room";
import { EventTimeline } from "./event-timeline";
import { Membership } from "../@types/membership";
export { EventStatus } from "./event-status";
@@ -54,7 +55,7 @@ export { EventStatus } from "./event-status";
export interface IContent {
[key: string]: any;
"msgtype"?: MsgType | string;
"membership"?: string;
"membership"?: Membership;
"avatar_url"?: string;
"displayname"?: string;
"m.relates_to"?: IEventRelation;
@@ -92,7 +93,7 @@ export interface IEvent {
origin_server_ts: number;
txn_id?: string;
state_key?: string;
membership?: string;
membership?: Membership;
unsigned: IUnsigned;
redacts?: string;
+3 -2
View File
@@ -140,8 +140,9 @@ export class IgnoredInvites {
/**
* Find out whether an invite should be ignored.
*
* @param sender - The user id for the user who issued the invite.
* @param roomId - The room to which the user is invited.
* @param params
* @param params.sender - The user id for the user who issued the invite.
* @param params.roomId - The room to which the user is invited.
* @returns A rule matching the entity, if any was found, `null` otherwise.
*/
public async getRuleForInvite({
+5 -4
View File
@@ -22,6 +22,7 @@ import { RoomState } from "./room-state";
import { logger } from "../logger";
import { TypedEventEmitter } from "./typed-event-emitter";
import { EventType } from "../@types/event";
import { KnownMembership, Membership } from "../@types/membership";
export enum RoomMemberEvent {
Membership = "RoomMember.membership",
@@ -119,7 +120,7 @@ export class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomMemberEve
/**
* The membership state for this room member e.g. 'join'.
*/
public membership?: string;
public membership?: Membership;
/**
* True if the member's name is disambiguated.
*/
@@ -314,7 +315,7 @@ export class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomMemberEve
public isKicked(): boolean {
return (
this.membership === "leave" &&
this.membership === KnownMembership.Leave &&
this.events.member !== undefined &&
this.events.member.getSender() !== this.events.member.getStateKey()
);
@@ -340,12 +341,12 @@ export class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomMemberEve
let memberContent = memberEvent.getContent();
let inviteSender: string | undefined = memberEvent.getSender();
if (memberContent.membership === "join") {
if (memberContent.membership === KnownMembership.Join) {
memberContent = memberEvent.getPrevContent();
inviteSender = memberEvent.getUnsigned().prev_sender;
}
if (memberContent.membership === "invite" && memberContent.is_direct) {
if (memberContent.membership === KnownMembership.Invite && memberContent.is_direct) {
return inviteSender;
}
}
+1 -1
View File
@@ -98,7 +98,7 @@ export class RoomReceipts {
/**
* Look for dangling receipts for the given event ID,
* and add them to the thread of unthread receipts if found.
* @param eventId - the event ID to look for
* @param event - the event to look for
*/
private onTimelineEvent = (event: MatrixEvent): void => {
const eventId = event.getId();
+15 -9
View File
@@ -25,6 +25,7 @@ import { TypedEventEmitter } from "./typed-event-emitter";
import { Beacon, BeaconEvent, BeaconEventHandlerMap, getBeaconInfoIdentifier, BeaconIdentifier } from "./beacon";
import { TypedReEmitter } from "../ReEmitter";
import { M_BEACON, M_BEACON_INFO } from "../@types/beacon";
import { KnownMembership } from "../@types/membership";
export interface IMarkerFoundOptions {
/** Whether the timeline was empty before the marker event arrived in the
@@ -206,7 +207,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
}
if (this.joinedMemberCount === null) {
this.joinedMemberCount = this.getMembers().reduce((count, m) => {
return m.membership === "join" ? count + 1 : count;
return m.membership === KnownMembership.Join ? count + 1 : count;
}, 0);
}
return this.joinedMemberCount;
@@ -230,7 +231,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
}
if (this.invitedMemberCount === null) {
this.invitedMemberCount = this.getMembers().reduce((count, m) => {
return m.membership === "invite" ? count + 1 : count;
return m.membership === KnownMembership.Invite ? count + 1 : count;
}, 0);
}
return this.invitedMemberCount;
@@ -297,13 +298,15 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
/**
* Get state events from the state of the room.
* @param eventType - The event type of the state event.
* @param stateKey - Optional. The state_key of the state event. If
* this is `undefined` then all matching state events will be
* returned.
* @returns A list of events if state_key was
* `undefined`, else a single event (or null if no match found).
* @returns A list of events
*/
public getStateEvents(eventType: EventType | string): MatrixEvent[];
/**
* Get state events from the state of the room.
* @param eventType - The event type of the state event.
* @param stateKey - The state_key of the state event.
* @returns A single event (or null if no match found).
*/
public getStateEvents(eventType: EventType | string, stateKey: string): MatrixEvent | null;
public getStateEvents(eventType: EventType | string, stateKey?: string): MatrixEvent[] | MatrixEvent | null {
if (!this.events.has(eventType)) {
@@ -432,7 +435,10 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
// leave events apparently elide the displayname or avatar_url,
// so let's fake one up so that we don't leak user ids
// into the timeline
if (event.getContent().membership === "leave" || event.getContent().membership === "ban") {
if (
event.getContent().membership === KnownMembership.Leave ||
event.getContent().membership === KnownMembership.Ban
) {
event.getContent().avatar_url = event.getContent().avatar_url || event.getPrevContent().avatar_url;
event.getContent().displayname =
event.getContent().displayname || event.getPrevContent().displayname;
@@ -774,7 +780,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
*/
public maySendRedactionForEvent(mxEvent: MatrixEvent, userId: string): boolean {
const member = this.getMember(userId);
if (!member || member.membership === "leave") return false;
if (!member || member.membership === KnownMembership.Leave) return false;
if (mxEvent.status || mxEvent.isRedacted()) return false;
+83 -42
View File
@@ -68,6 +68,7 @@ import { ReadReceipt, synthesizeReceipt } from "./read-receipt";
import { isPollEvent, Poll, PollEvent } from "./poll";
import { RoomReceipts } from "./room-receipts";
import { compareEventOrdering } from "./compare-event-ordering";
import { KnownMembership, Membership } from "../@types/membership";
// These constants are used as sane defaults when the homeserver doesn't support
// the m.room_versions capability. In practice, KNOWN_SAFE_ROOM_VERSION should be
@@ -177,7 +178,7 @@ export type RoomEventHandlerMap = {
* @param membership - The new membership value
* @param prevMembership - The previous membership value
*/
[RoomEvent.MyMembership]: (room: Room, membership: string, prevMembership?: string) => void;
[RoomEvent.MyMembership]: (room: Room, membership: Membership, prevMembership?: Membership) => void;
/**
* Fires whenever a room's tags are updated.
* @param event - The tags event
@@ -351,7 +352,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
private readonly pendingEventList?: MatrixEvent[];
// read by megolm via getter; boolean value - null indicates "use global value"
private blacklistUnverifiedDevices?: boolean;
private selfMembership?: string;
private selfMembership?: Membership;
private summaryHeroes: string[] | null = null;
// flags to stop logspam about missing m.room.create events
private getTypeWarning = false;
@@ -863,8 +864,8 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
/**
* @returns the membership type (join | leave | invite | knock) for the logged in user
*/
public getMyMembership(): string {
return this.selfMembership ?? "leave";
public getMyMembership(): Membership {
return this.selfMembership ?? KnownMembership.Leave;
}
/**
@@ -878,7 +879,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
return me.getDMInviter();
}
if (this.selfMembership === "invite") {
if (this.selfMembership === KnownMembership.Invite) {
// fall back to summary information
const memberCount = this.getInvitedAndJoinedMemberCount();
if (memberCount === 2) {
@@ -914,37 +915,69 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
return this.myUserId;
}
public getAvatarFallbackMember(): RoomMember | undefined {
const memberCount = this.getInvitedAndJoinedMemberCount();
if (memberCount > 2) {
return;
/**
* Gets the "functional members" in this room.
*
* Returns the list of userIDs from the `io.element.functional_members` event. Does not consider the
* current membership states of those users.
*
* @see https://github.com/element-hq/element-meta/blob/develop/spec/functional_members.md.
*/
private getFunctionalMembers(): string[] {
const mFunctionalMembers = this.currentState.getStateEvents(UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, "");
if (Array.isArray(mFunctionalMembers?.getContent().service_members)) {
return mFunctionalMembers!.getContent().service_members;
}
const hasHeroes = Array.isArray(this.summaryHeroes) && this.summaryHeroes.length;
return [];
}
public getAvatarFallbackMember(): RoomMember | undefined {
const functionalMembers = this.getFunctionalMembers();
// Only generate a fallback avatar if the conversation is with a single specific other user (a "DM").
let nonFunctionalMemberCount = 0;
this.getMembers()!.forEach((m) => {
if (m.membership !== "join" && m.membership !== "invite") return;
if (functionalMembers.includes(m.userId)) return;
nonFunctionalMemberCount++;
});
if (nonFunctionalMemberCount > 2) return;
// Prefer the list of heroes, if present. It should only include the single other user in the DM.
const nonFunctionalHeroes = this.summaryHeroes?.filter((h) => !functionalMembers.includes(h));
const hasHeroes = Array.isArray(nonFunctionalHeroes) && nonFunctionalHeroes.length;
if (hasHeroes) {
const availableMember = this.summaryHeroes!.map((userId) => {
return this.getMember(userId);
}).find((member) => !!member);
const availableMember = nonFunctionalHeroes
.map((userId) => {
return this.getMember(userId);
})
.find((member) => !!member);
if (availableMember) {
return availableMember;
}
}
const members = this.currentState.getMembers();
// could be different than memberCount
// as this includes left members
if (members.length <= 2) {
const availableMember = members.find((m) => {
// Consider *all*, including previous, members, to generate the avatar for DMs where the other user left.
// Needed to generate a matching avatar for rooms named "Empty Room (was Alice)".
const members = this.getMembers();
const nonFunctionalMembers = members?.filter((m) => !functionalMembers.includes(m.userId));
if (nonFunctionalMembers.length <= 2) {
const availableMember = nonFunctionalMembers.find((m) => {
return m.userId !== this.myUserId;
});
if (availableMember) {
return availableMember;
}
}
// if all else fails, try falling back to a user,
// and create a one-off member for it
// If all else failed, but the homeserver gave us heroes that previously could not be found in the room members,
// trust and try falling back to a hero, creating a one-off member for it
if (hasHeroes) {
const availableUser = this.summaryHeroes!.map((userId) => {
return this.client.getUser(userId);
}).find((user) => !!user);
const availableUser = nonFunctionalHeroes
.map((userId) => {
return this.client.getUser(userId);
})
.find((user) => !!user);
if (availableUser) {
const member = new RoomMember(this.roomId, availableUser.userId);
member.user = availableUser;
@@ -957,11 +990,11 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
* Sets the membership this room was received as during sync
* @param membership - join | leave | invite
*/
public updateMyMembership(membership: string): void {
public updateMyMembership(membership: Membership): void {
const prevMembership = this.selfMembership;
this.selfMembership = membership;
if (prevMembership !== membership) {
if (membership === "leave") {
if (membership === KnownMembership.Leave) {
this.cleanupAfterLeaving();
}
this.emit(RoomEvent.MyMembership, this, membership, prevMembership);
@@ -970,7 +1003,12 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
private async loadMembersFromServer(): Promise<IStateEventWithRoomId[]> {
const lastSyncToken = this.client.store.getSyncToken();
const response = await this.client.members(this.roomId, undefined, "leave", lastSyncToken ?? undefined);
const response = await this.client.members(
this.roomId,
undefined,
KnownMembership.Leave,
lastSyncToken ?? undefined,
);
return response.chunk;
}
@@ -1677,7 +1715,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
* @returns A list of currently joined members.
*/
public getJoinedMembers(): RoomMember[] {
return this.getMembersWithMembership("join");
return this.getMembersWithMembership(KnownMembership.Join);
}
/**
@@ -1712,7 +1750,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
* @param membership - The membership state.
* @returns A list of members with the given membership state.
*/
public getMembersWithMembership(membership: string): RoomMember[] {
public getMembersWithMembership(membership: Membership): RoomMember[] {
return this.currentState.getMembers().filter(function (m) {
return m.membership === membership;
});
@@ -1725,9 +1763,9 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
*/
public async getEncryptionTargetMembers(): Promise<RoomMember[]> {
await this.loadMembersIfNeeded();
let members = this.getMembersWithMembership("join");
let members = this.getMembersWithMembership(KnownMembership.Join);
if (this.shouldEncryptForInvitedMembers()) {
members = members.concat(this.getMembersWithMembership("invite"));
members = members.concat(this.getMembersWithMembership(KnownMembership.Invite));
}
return members;
}
@@ -1758,7 +1796,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
* @param membership - The membership e.g. `'join'`
* @returns True if this user_id has the given membership state.
*/
public hasMembershipState(userId: string, membership: string): boolean {
public hasMembershipState(userId: string, membership: Membership): boolean {
const member = this.getMember(userId);
if (!member) {
return false;
@@ -3087,7 +3125,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
const membership = membershipEvent.getContent().membership;
this.updateMyMembership(membership!);
if (membership === "invite") {
if (membership === KnownMembership.Invite) {
const strippedStateEvents = membershipEvent.getUnsigned().invite_room_state || [];
strippedStateEvents.forEach((strippedEvent) => {
const existingEvent = this.currentState.getStateEvents(strippedEvent.type, strippedEvent.state_key);
@@ -3173,7 +3211,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
*/
public maySendMessage(): boolean {
return (
this.getMyMembership() === "join" &&
this.getMyMembership() === KnownMembership.Join &&
(this.hasEncryptionStateEvent()
? this.currentState.maySendEvent(EventType.RoomMessageEncrypted, this.myUserId)
: this.currentState.maySendEvent(EventType.RoomMessage, this.myUserId))
@@ -3186,7 +3224,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
* @returns true if the user should be permitted to issue invites for this room.
*/
public canInvite(userId: string): boolean {
let canInvite = this.getMyMembership() === "join";
let canInvite = this.getMyMembership() === KnownMembership.Join;
const powerLevelsEvent = this.currentState.getStateEvents(EventType.RoomPowerLevels, "");
const powerLevels = powerLevelsEvent && powerLevelsEvent.getContent();
const me = this.getMember(userId);
@@ -3351,11 +3389,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
let inviteJoinCount = joinedMemberCount + invitedMemberCount - 1;
// get service members (e.g. helper bots) for exclusion
let excludedUserIds: string[] = [];
const mFunctionalMembers = this.currentState.getStateEvents(UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, "");
if (Array.isArray(mFunctionalMembers?.getContent().service_members)) {
excludedUserIds = mFunctionalMembers!.getContent().service_members;
}
const excludedUserIds = this.getFunctionalMembers();
// get members that are NOT ourselves and are actually in the room.
let otherNames: string[] = [];
@@ -3372,7 +3406,10 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
});
} else {
let otherMembers = this.currentState.getMembers().filter((m) => {
return m.userId !== userId && (m.membership === "invite" || m.membership === "join");
return (
m.userId !== userId &&
(m.membership === KnownMembership.Invite || m.membership === KnownMembership.Join)
);
});
otherMembers = otherMembers.filter(({ userId }) => {
// filter service members
@@ -3400,7 +3437,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
const myMembership = this.getMyMembership();
// if I have created a room and invited people through
// 3rd party invites
if (myMembership == "join") {
if (myMembership == KnownMembership.Join) {
const thirdPartyInvites = this.currentState.getStateEvents(EventType.RoomThirdPartyInvite);
if (thirdPartyInvites?.length) {
@@ -3424,7 +3461,11 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
leftNames = this.currentState
.getMembers()
.filter((m) => {
return m.userId !== userId && m.membership !== "invite" && m.membership !== "join";
return (
m.userId !== userId &&
m.membership !== KnownMembership.Invite &&
m.membership !== KnownMembership.Join
);
})
.map((m) => m.name);
}
+3 -3
View File
@@ -113,10 +113,10 @@ export class OidcTokenRefresher {
*
* This function is intended to be overriden by the consumer when persistence is necessary.
*
* @param accessToken - new access token
* @param refreshToken - OPTIONAL new refresh token
* @param tokens.accessToken - new access token
* @param tokens.refreshToken - OPTIONAL new refresh token
*/
public async persistTokens(_tokens: { accessToken: string; refreshToken?: string }): Promise<void> {
public async persistTokens(tokens: { accessToken: string; refreshToken?: string }): Promise<void> {
// NOOP
}
+134 -52
View File
@@ -25,8 +25,8 @@ import {
ICallStartedPrefixCondition,
IContainsDisplayNameCondition,
IEventMatchCondition,
IEventPropertyIsCondition,
IEventPropertyContainsCondition,
IEventPropertyIsCondition,
IPushRule,
IPushRules,
IRoomMemberCountCondition,
@@ -56,8 +56,31 @@ const RULEKINDS_IN_ORDER = [
// more details.
// 2. We often want to start using push rules ahead of the server supporting them,
// and so we can put them here.
const DEFAULT_OVERRIDE_RULES: IPushRule[] = [
{
const DEFAULT_OVERRIDE_RULES: Record<string, IPushRule> = {
".m.rule.is_room_mention": {
// Matrix v1.7
rule_id: ".m.rule.is_room_mention",
default: true,
enabled: true,
conditions: [
{
kind: ConditionKind.EventPropertyIs,
key: "content.m\\.mentions.room",
value: true,
},
{
kind: ConditionKind.SenderNotificationPermission,
key: "room",
},
],
actions: [
PushRuleActionName.Notify,
{
set_tweak: TweakName.Highlight,
},
],
},
".m.rule.reaction": {
// For homeservers which don't support MSC2153 yet
rule_id: ".m.rule.reaction",
default: true,
@@ -71,7 +94,7 @@ const DEFAULT_OVERRIDE_RULES: IPushRule[] = [
],
actions: [PushRuleActionName.DontNotify],
},
{
".org.matrix.msc3786.rule.room.server_acl": {
// For homeservers which don't support MSC3786 yet
rule_id: ".org.matrix.msc3786.rule.room.server_acl",
default: true,
@@ -90,10 +113,32 @@ const DEFAULT_OVERRIDE_RULES: IPushRule[] = [
],
actions: [],
},
};
// A special rule id for `EXPECTED_DEFAULT_OVERRIDE_RULE_IDS` and friends which denotes where user-defined rules live in the order.
const UserDefinedRules = Symbol("UserDefinedRules");
type OrderedRules = Array<string | typeof UserDefinedRules>;
const EXPECTED_DEFAULT_OVERRIDE_RULE_IDS: OrderedRules = [
RuleId.Master,
UserDefinedRules,
RuleId.SuppressNotices,
RuleId.InviteToSelf,
RuleId.MemberEvent,
RuleId.IsUserMention,
RuleId.ContainsDisplayName,
RuleId.IsRoomMention,
RuleId.AtRoomNotification,
RuleId.Tombstone,
".m.rule.reaction",
".m.rule.room.server_acl",
".org.matrix.msc3786.rule.room.server_acl",
".m.rule.suppress_edits",
];
const DEFAULT_UNDERRIDE_RULES: IPushRule[] = [
{
const DEFAULT_UNDERRIDE_RULES: Record<string, IPushRule> = {
".org.matrix.msc3914.rule.room.call": {
// For homeservers which don't support MSC3914 yet
rule_id: ".org.matrix.msc3914.rule.room.call",
default: true,
@@ -110,8 +155,79 @@ const DEFAULT_UNDERRIDE_RULES: IPushRule[] = [
],
actions: [PushRuleActionName.Notify, { set_tweak: TweakName.Sound, value: "default" }],
},
};
const EXPECTED_DEFAULT_UNDERRIDE_RULE_IDS: OrderedRules = [
UserDefinedRules,
RuleId.IncomingCall,
".org.matrix.msc3914.rule.room.call",
RuleId.EncryptedDM,
RuleId.DM,
RuleId.Message,
RuleId.EncryptedMessage,
];
/**
* Make sure that each of the rules listed in `defaultRuleIds` is listed in the given set of push rules.
*
* @param kind - the kind of push rule set being merged.
* @param incomingRules - the existing set of known push rules for the user.
* @param defaultRules - a lookup table for the default definitions of push rules.
* @param orderedRuleIds - the IDs of the expected push rules, in order.
*
* @returns A copy of `incomingRules`, with any missing default rules inserted in the right place.
*/
function mergeRulesWithDefaults(
kind: PushRuleKind,
incomingRules: IPushRule[],
defaultRules: Record<string, IPushRule>,
orderedRuleIds: OrderedRules,
): IPushRule[] {
// Split the incomingRules into defaults and custom
const incomingDefaultRules = incomingRules.filter((rule) => rule.default);
const incomingCustomRules = incomingRules.filter((rule) => !rule.default);
function insertDefaultPushRule(ruleId: OrderedRules[number]): void {
if (ruleId === UserDefinedRules) {
// Re-insert any user-defined rules that were in `incomingRules`
newRules.push(...incomingCustomRules);
} else if (ruleId in defaultRules) {
logger.warn(`Adding default global ${kind} push rule ${ruleId}`);
newRules.push(defaultRules[ruleId]);
} else {
logger.warn(`Missing default global ${kind} push rule ${ruleId}`);
}
}
let nextExpectedRuleIdIndex = 0;
const newRules: IPushRule[] = [];
// Merge our expected rules (including the incoming custom rules) into the incoming default rules.
for (const rule of incomingDefaultRules) {
const ruleIndex = orderedRuleIds.indexOf(rule.rule_id);
if (ruleIndex === -1) {
// an unrecognised rule; copy it over
newRules.push(rule);
continue;
}
while (ruleIndex > nextExpectedRuleIdIndex) {
// insert new rules
const defaultRuleId = orderedRuleIds[nextExpectedRuleIdIndex];
insertDefaultPushRule(defaultRuleId);
nextExpectedRuleIdIndex += 1;
}
// copy over the existing rule
newRules.push(rule);
nextExpectedRuleIdIndex += 1;
}
// Now copy over any remaining default rules
for (const ruleId of orderedRuleIds.slice(nextExpectedRuleIdIndex)) {
insertDefaultPushRule(ruleId);
}
return newRules;
}
export interface IActionsObject {
/** Whether this event should notify the user or not. */
notify: boolean;
@@ -175,53 +291,19 @@ export class PushProcessor {
if (!newRules.global.underride) newRules.global.underride = [];
// Merge the client-level defaults with the ones from the server
const globalOverrides = newRules.global.override;
for (const originalOverride of DEFAULT_OVERRIDE_RULES) {
const existingRule = globalOverrides.find((r) => r.rule_id === originalOverride.rule_id);
newRules.global.override = mergeRulesWithDefaults(
PushRuleKind.Override,
newRules.global.override,
DEFAULT_OVERRIDE_RULES,
EXPECTED_DEFAULT_OVERRIDE_RULE_IDS,
);
// Dynamically add the user ID as the value for the is_user_mention rule.
let override: IPushRule;
if (originalOverride.rule_id === RuleId.IsUserMention) {
// If the user ID wasn't provided, skip the rule.
if (!userId) {
continue;
}
override = JSON.parse(JSON.stringify(originalOverride)); // deep clone
override.conditions![0].value = userId;
} else {
override = originalOverride;
}
if (existingRule) {
// Copy over the actions, default, and conditions. Don't touch the user's preference.
existingRule.default = override.default;
existingRule.conditions = override.conditions;
existingRule.actions = override.actions;
} else {
// Add the rule
const ruleId = override.rule_id;
logger.warn(`Adding default global override for ${ruleId}`);
globalOverrides.push(override);
}
}
const globalUnderrides = newRules.global.underride ?? [];
for (const underride of DEFAULT_UNDERRIDE_RULES) {
const existingRule = globalUnderrides.find((r) => r.rule_id === underride.rule_id);
if (existingRule) {
// Copy over the actions, default, and conditions. Don't touch the user's preference.
existingRule.default = underride.default;
existingRule.conditions = underride.conditions;
existingRule.actions = underride.actions;
} else {
// Add the rule
const ruleId = underride.rule_id;
logger.warn(`Adding default global underride for ${ruleId}`);
globalUnderrides.push(underride);
}
}
newRules.global.underride = mergeRulesWithDefaults(
PushRuleKind.Underride,
newRules.global.underride,
DEFAULT_UNDERRIDE_RULES,
EXPECTED_DEFAULT_UNDERRIDE_RULE_IDS,
);
return newRules;
}
+3 -2
View File
@@ -34,6 +34,7 @@ import { RoomMember } from "../models/room-member";
import { HistoryVisibility } from "../@types/partials";
import { OutgoingRequestsManager } from "./OutgoingRequestsManager";
import { logDuration } from "../utils";
import { KnownMembership } from "../@types/membership";
/**
* RoomEncryptor: responsible for encrypting messages to a given room
@@ -100,8 +101,8 @@ export class RoomEncryptor {
*/
public onRoomMembership(member: RoomMember): void {
if (
member.membership == "join" ||
(member.membership == "invite" && this.room.shouldEncryptForInvitedMembers())
member.membership == KnownMembership.Join ||
(member.membership == KnownMembership.Invite && this.room.shouldEncryptForInvitedMembers())
) {
// make sure we are tracking the deviceList for this user
logDuration(this.prefixedLogger, "updateTrackedUsers", async () => {
+22 -7
View File
@@ -23,7 +23,11 @@ import { ServerSideSecretStorage } from "../secret-storage";
import { ICryptoCallbacks } from "../crypto";
import { Logger } from "../logger";
import { CryptoStore, MigrationState } from "../crypto/store/base";
import { migrateFromLegacyCrypto, migrateRoomSettingsFromLegacyCrypto } from "./libolm_migration";
import {
migrateFromLegacyCrypto,
migrateLegacyLocalTrustIfNeeded,
migrateRoomSettingsFromLegacyCrypto,
} from "./libolm_migration";
/**
* Create a new `RustCrypto` implementation
@@ -190,13 +194,24 @@ async function initOlmMachine(
// We need to do an initial keys query so that the rust stack can properly update trust of
// the user device and identity from the migrated private keys.
// If not done, there is a short period where the own device/identity trust will be undefined after migration.
try {
await rustCrypto.userHasCrossSigningKeys(userId);
} catch (e) {
// We don't want to fail the startup if this fails, but we do want to log it.
// It will be retried by the sdk.
logger.error("Failed to check for cross-signing keys after migration", e);
let initialKeyQueryDone = false;
while (!initialKeyQueryDone) {
try {
await rustCrypto.userHasCrossSigningKeys(userId);
initialKeyQueryDone = true;
} catch (e) {
// If the initial key query fails, we retry until it succeeds.
logger.error("Failed to check for cross-signing keys after migration, retrying", e);
}
}
// If the private master cross-signing key was not cached in the legacy store, the rust session
// will not be able to establish the trust of the user identity.
// That means that after migration the session could revert to unverified.
// In order to avoid asking the users to re-verify their sessions, we need to migrate the legacy local trust
// (if the legacy session was already verified) to the new session.
await migrateLegacyLocalTrustIfNeeded({ legacyCryptoStore, rustCrypto, logger });
await legacyCryptoStore.setMigrationState(MigrationState.INITIAL_OWN_KEY_QUERY_DONE);
}
}
+100
View File
@@ -23,6 +23,8 @@ import { decryptAES, IEncryptedPayload } from "../crypto/aes";
import { IHttpOpts, MatrixHttpApi } from "../http-api";
import { requestKeyBackupVersion } from "./backup";
import { IRoomEncryption } from "../crypto/RoomList";
import { CrossSigningKeyInfo } from "../crypto-api";
import { RustCrypto } from "./rust-crypto";
/**
* Determine if any data needs migrating from the legacy store, and do so.
@@ -380,3 +382,101 @@ async function getAndDecryptCachedSecretKey(
return encodedKey === null ? undefined : await decryptAES(encodedKey, legacyPickleKey, name);
}
/**
* Check if the user's published identity (ie, public cross-signing keys) was trusted by the legacy session,
* and if so mark it as trusted in the Rust session if needed.
*
* By default, if the legacy session didn't have the private MSK, the migrated session will revert to unverified,
* even if the user has verified the session in the past.
*
* This only occurs if the private MSK was not cached in the crypto store (USK and SSK private keys won't help
* to establish trust: the trust is rooted in the MSK).
*
* Rust crypto will only consider the current session as trusted if we import the private MSK itself.
*
* We could prompt the user to verify the session again, but it's probably better to just mark the user identity
* as locally verified if it was before.
*
* See https://github.com/element-hq/element-web/issues/27079
*
* @param args - Argument object.
*/
export async function migrateLegacyLocalTrustIfNeeded(args: {
/** The legacy crypto store that is migrated. */
legacyCryptoStore: CryptoStore;
/** The migrated rust crypto stack. */
rustCrypto: RustCrypto;
/** The logger to use */
logger: Logger;
}): Promise<void> {
const { legacyCryptoStore, rustCrypto, logger } = args;
// Get the public cross-signing identity from rust.
const rustOwnIdentity = await rustCrypto.getOwnIdentity();
if (!rustOwnIdentity) {
// There are no cross-signing keys published server side, so nothing to do here.
return;
}
if (rustOwnIdentity.isVerified()) {
// The rust session already trusts the keys, so again, nothing to do.
return;
}
const legacyLocallyTrustedMSK = await getLegacyTrustedPublicMasterKeyBase64(legacyCryptoStore);
if (!legacyLocallyTrustedMSK) {
// The user never verified their identity in the legacy session, so nothing to do.
return;
}
const mskInfo: CrossSigningKeyInfo = JSON.parse(rustOwnIdentity.masterKey);
if (!mskInfo.keys || Object.keys(mskInfo.keys).length === 0) {
// This should not happen, but let's be safe
logger.error("Post Migration | Unexpected error: no master key in the rust session.");
return;
}
const rustSeenMSK = Object.values(mskInfo.keys)[0];
if (rustSeenMSK && rustSeenMSK == legacyLocallyTrustedMSK) {
logger.info(`Post Migration: Migrating legacy trusted MSK: ${legacyLocallyTrustedMSK} to locally verified.`);
// Let's mark the user identity as locally verified as part of the migration.
await rustOwnIdentity!.verify();
// As well as marking the MSK as trusted, `OlmMachine.verify` returns a
// `SignatureUploadRequest` which will publish a signature of the MSK using
// this device. In this case, we ignore the request: since the user hasn't
// actually re-verified the MSK, we don't publish a new signature. (`.verify`
// doesn't store the signature, and if we drop the request here it won't be
// retried.)
//
// Not publishing the signature is consistent with the behaviour of
// matrix-crypto-sdk when the private key is imported via
// `importCrossSigningKeys`, and when the identity is verified via interactive
// verification.
//
// [Aside: device signatures on the MSK are not considered by the rust-sdk to
// establish the trust of the user identity so in any case, what we actually do
// here is somewhat moot.]
}
}
/**
* Checks if the legacy store has a trusted public master key, and returns it if so.
*
* @param legacyStore - The legacy store to check.
*
* @returns `null` if there were no cross signing keys or if they were not trusted. The trusted public master key if it was.
*/
async function getLegacyTrustedPublicMasterKeyBase64(legacyStore: CryptoStore): Promise<string | null> {
let maybeTrustedKeys: string | null = null;
await legacyStore.doTxn("readonly", "account", (txn) => {
legacyStore.getCrossSigningKeys(txn, (keys) => {
// can be an empty object after resetting cross-signing keys, see storeTrustedSelfKeys
const msk = keys?.master;
if (msk && Object.keys(msk.keys).length != 0) {
// `msk.keys` is an object with { [`ed25519:${pubKey}`]: pubKey }
maybeTrustedKeys = Object.values(msk.keys)[0];
}
});
});
return maybeTrustedKeys;
}
+10
View File
@@ -1625,6 +1625,16 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
this.logger.warn("onKeyVerificationRequest: Error processing outgoing requests", e);
});
}
/**
* Returns the cross-signing user identity of the current user.
*
* Not part of the public crypto-api interface.
* Used during migration from legacy js-crypto to update local trust if needed.
*/
public async getOwnIdentity(): Promise<RustSdkCryptoJs.OwnUserIdentity | undefined> {
return await this.olmMachine.getIdentity(new RustSdkCryptoJs.UserId(this.userId));
}
}
class EventDecryptor {
+9 -8
View File
@@ -46,6 +46,7 @@ import { EventType } from "./@types/event";
import { IPushRules } from "./@types/PushRules";
import { RoomStateEvent } from "./models/room-state";
import { RoomMemberEvent } from "./models/room-member";
import { KnownMembership } from "./@types/membership";
// Number of consecutive failed syncs that will lead to a syncState of ERROR as opposed
// to RECONNECTING. This is needed to inform the client of server issues when the
@@ -218,7 +219,7 @@ class ExtensionAccountData implements Extension<ExtensionAccountDataRequest, Ext
};
}
public onResponse(data: ExtensionAccountDataResponse): void {
public async onResponse(data: ExtensionAccountDataResponse): Promise<void> {
if (data.global && data.global.length > 0) {
this.processGlobalAccountData(data.global);
}
@@ -288,7 +289,7 @@ class ExtensionTyping implements Extension<ExtensionTypingRequest, ExtensionTypi
};
}
public onResponse(data: ExtensionTypingResponse): void {
public async onResponse(data: ExtensionTypingResponse): Promise<void> {
if (!data?.rooms) {
return;
}
@@ -327,7 +328,7 @@ class ExtensionReceipts implements Extension<ExtensionReceiptsRequest, Extension
return undefined; // don't send a JSON object for subsequent requests, we don't need to.
}
public onResponse(data: ExtensionReceiptsResponse): void {
public async onResponse(data: ExtensionReceiptsResponse): Promise<void> {
if (!data?.rooms) {
return;
}
@@ -455,7 +456,7 @@ export class SlidingSyncSdk {
* @returns A promise which resolves once the room has been added to the
* store.
*/
public async peek(_roomId: string): Promise<Room> {
public async peek(roomId: string): Promise<Room> {
return null!; // TODO
}
@@ -649,7 +650,7 @@ export class SlidingSyncSdk {
inviteStateEvents.forEach((e) => {
this.client.emit(ClientEvent.Event, e);
});
room.updateMyMembership("invite");
room.updateMyMembership(KnownMembership.Invite);
return;
}
@@ -719,7 +720,7 @@ export class SlidingSyncSdk {
// local fields must be set before any async calls because call site assumes
// synchronous execution prior to emitting SlidingSyncState.Complete
room.updateMyMembership("join");
room.updateMyMembership(KnownMembership.Join);
room.recalculate();
if (roomData.initial) {
@@ -845,7 +846,7 @@ export class SlidingSyncSdk {
const client = this.client;
// For each invited room member we want to give them a displayname/avatar url
// if they have one (the m.room.member invites don't contain this).
room.getMembersWithMembership("invite").forEach(function (member) {
room.getMembersWithMembership(KnownMembership.Invite).forEach(function (member) {
if (member.requestedProfileInfo) return;
member.requestedProfileInfo = true;
// try to get a cached copy first.
@@ -865,7 +866,7 @@ export class SlidingSyncSdk {
// the code paths remain the same between invite/join display name stuff
// which is a worthy trade-off for some minor pollution.
const inviteEvent = member.events.member!;
if (inviteEvent.getContent().membership !== "invite") {
if (inviteEvent.getContent().membership !== KnownMembership.Invite) {
// between resolving and now they have since joined, so don't clobber
return;
}
+19 -15
View File
@@ -282,7 +282,7 @@ export interface Extension<Req extends {}, Res extends {}> {
* A function which is called when there is response JSON under this extension.
* @param data - The response JSON under the extension name.
*/
onResponse(data: Res): void;
onResponse(data: Res): Promise<void>;
/**
* Controls when onResponse should be called.
* @returns The state when it should be called.
@@ -546,20 +546,24 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
return ext;
}
private onPreExtensionsResponse(ext: Record<string, object>): void {
Object.keys(ext).forEach((extName) => {
if (this.extensions[extName].when() == ExtensionState.PreProcess) {
this.extensions[extName].onResponse(ext[extName]);
}
});
private async onPreExtensionsResponse(ext: Record<string, object>): Promise<void> {
await Promise.all(
Object.keys(ext).map(async (extName) => {
if (this.extensions[extName].when() == ExtensionState.PreProcess) {
await this.extensions[extName].onResponse(ext[extName]);
}
}),
);
}
private onPostExtensionsResponse(ext: Record<string, object>): void {
Object.keys(ext).forEach((extName) => {
if (this.extensions[extName].when() == ExtensionState.PostProcess) {
this.extensions[extName].onResponse(ext[extName]);
}
});
private async onPostExtensionsResponse(ext: Record<string, object>): Promise<void> {
await Promise.all(
Object.keys(ext).map(async (extName) => {
if (this.extensions[extName].when() == ExtensionState.PostProcess) {
await this.extensions[extName].onResponse(ext[extName]);
}
}),
);
}
/**
@@ -921,7 +925,7 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
if (!resp) {
continue;
}
this.onPreExtensionsResponse(resp.extensions);
await this.onPreExtensionsResponse(resp.extensions);
for (const roomId in resp.rooms) {
await this.invokeRoomDataListeners(roomId, resp!.rooms[roomId]);
@@ -938,7 +942,7 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
}
}
this.invokeLifecycleListeners(SlidingSyncState.Complete, resp);
this.onPostExtensionsResponse(resp.extensions);
await this.onPostExtensionsResponse(resp.extensions);
listKeysWithUpdates.forEach((listKey: string) => {
const list = this.lists.get(listKey);
if (!list) {
+2 -1
View File
@@ -32,6 +32,7 @@ import { IStateEventWithRoomId } from "../@types/search";
import { IndexedToDeviceBatch, ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage";
import { IStoredClientOpts } from "../client";
import { MapWithDefault } from "../utils";
import { KnownMembership } from "../@types/membership";
function isValidFilterId(filterId?: string | number | null): boolean {
const isValidStr =
@@ -118,7 +119,7 @@ export class MemoryStore implements IStore {
* updated.
*/
private onRoomMember = (event: MatrixEvent | null, state: RoomState, member: RoomMember): void => {
if (member.membership === "invite") {
if (member.membership === KnownMembership.Invite) {
// We do NOT add invited members because people love to typo user IDs
// which would then show up in these lists (!)
return;
+3 -2
View File
@@ -61,6 +61,7 @@ import { IEventsResponse } from "./@types/requests";
import { UNREAD_THREAD_NOTIFICATIONS } from "./@types/sync";
import { Feature, ServerSupport } from "./feature";
import { Crypto } from "./crypto";
import { KnownMembership } from "./@types/membership";
const DEBUG = true;
@@ -1720,7 +1721,7 @@ export class SyncApi {
const client = this.client;
// For each invited room member we want to give them a displayname/avatar url
// if they have one (the m.room.member invites don't contain this).
room.getMembersWithMembership("invite").forEach(function (member) {
room.getMembersWithMembership(KnownMembership.Invite).forEach(function (member) {
if (member.requestedProfileInfo) return;
member.requestedProfileInfo = true;
// try to get a cached copy first.
@@ -1740,7 +1741,7 @@ export class SyncApi {
// the code paths remain the same between invite/join display name stuff
// which is a worthy trade-off for some minor pollution.
const inviteEvent = member.events.member;
if (inviteEvent?.getContent().membership !== "invite") {
if (inviteEvent?.getContent().membership !== KnownMembership.Invite) {
// between resolving and now they have since joined, so don't clobber
return;
}
+25
View File
@@ -0,0 +1,25 @@
/*
Copyright 2024 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
* This file is a secondary entrypoint for the js-sdk library, for use by Typescript projects.
* It exposes low-level types and interfaces reflecting structures defined in the Matrix specification.
*
* Remember to only export *public* types from this file.
*/
export type * from "./@types/media";
export * from "./@types/membership";
+2 -2
View File
@@ -1235,7 +1235,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
/**
* Starts/stops screensharing
* @param enabled - the desired screensharing state
* @param desktopCapturerSourceId - optional id of the desktop capturer source to use
* @param opts - screen sharing options
* @returns new screensharing state
*/
public async setScreensharingEnabled(enabled: boolean, opts?: IScreensharingOpts): Promise<boolean> {
@@ -1292,7 +1292,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
* Starts/stops screensharing
* Should be used ONLY if the opponent doesn't support SDPStreamMetadata
* @param enabled - the desired screensharing state
* @param desktopCapturerSourceId - optional id of the desktop capturer source to use
* @param opts - screen sharing options
* @returns new screensharing state
*/
private async setScreensharingEnabledWithoutMetadataSupport(
+2 -1
View File
@@ -34,6 +34,7 @@ import {
} from "./stats/statsReport";
import { SummaryStatsReportGatherer } from "./stats/summaryStatsReportGatherer";
import { CallFeedStatsReporter } from "./stats/callFeedStatsReporter";
import { KnownMembership } from "../@types/membership";
export enum GroupCallIntent {
Ring = "m.ring",
@@ -1485,7 +1486,7 @@ export class GroupCall extends TypedEventEmitter<
}
// Must have a connected device and be joined to the room
if (validDevices.length > 0 && member?.membership === "join") {
if (validDevices.length > 0 && member?.membership === KnownMembership.Join) {
const deviceMap = new Map<string, ParticipantState>();
participants.set(member, deviceMap);
+4 -1
View File
@@ -31,6 +31,9 @@ export type MediaHandlerEventHandlerMap = {
};
export interface IScreensharingOpts {
/**
* sourceId for Electron DesktopCapturer
*/
desktopCapturerSourceId?: string;
audio?: boolean;
// For electron screen capture, there are very few options for detecting electron
@@ -351,7 +354,7 @@ export class MediaHandler extends TypedEventEmitter<
}
/**
* @param desktopCapturerSourceId - sourceId for Electron DesktopCapturer
* @param opts - screensharing stream options
* @param reusable - is allowed to be reused by the MediaHandler
* @returns based on passed parameters
*/
+1 -6
View File
@@ -10,10 +10,5 @@
"declaration": true,
"strict": true
},
"include": ["./src/**/*.ts", "./spec/**/*.ts"],
"typedocOptions": {
"entryPoints": ["src/matrix.ts"],
"excludeExternals": true,
"out": "_docs"
}
"include": ["./src/**/*.ts", "./spec/**/*.ts"]
}
+6 -7
View File
@@ -1,9 +1,8 @@
{
"plugin": [
"typedoc-plugin-mdn-links",
"typedoc-plugin-missing-exports",
"typedoc-plugin-versions",
"typedoc-plugin-coverage"
],
"coverageLabel": "TypeDoc"
"$schema": "https://typedoc.org/schema.json",
"plugin": ["typedoc-plugin-mdn-links", "typedoc-plugin-missing-exports", "typedoc-plugin-coverage"],
"coverageLabel": "TypeDoc",
"entryPoints": ["src/matrix.ts", "src/types.ts"],
"excludeExternals": true,
"out": "_docs"
}
+237 -272
View File
@@ -7,25 +7,25 @@
resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
"@action-validator/cli@^0.5.3":
version "0.5.4"
resolved "https://registry.yarnpkg.com/@action-validator/cli/-/cli-0.5.4.tgz#44b41881f717753e4fec247aaf73a99c88481e32"
integrity sha512-Puj/f8E8OzPCmDDkVslU+hvTwxM+VL9GOqmuEpdwp4E2Ufsni0lJMNvkHMBc3Da13WwGG49ydOvGVTpMT5H+7A==
"@action-validator/cli@^0.6.0":
version "0.6.0"
resolved "https://registry.yarnpkg.com/@action-validator/cli/-/cli-0.6.0.tgz#02a29e322d3794903896183281eed2b55c685851"
integrity sha512-Z8TYOK4GqUIpI0UuspUJPB6dhr0niTumhwI5iiZVqFRXm4u05bZawnFKltpvoFUfJg9mHbbIBlleqsRJAgl53Q==
dependencies:
chalk "5.2.0"
"@action-validator/core@^0.5.3":
version "0.5.4"
resolved "https://registry.yarnpkg.com/@action-validator/core/-/core-0.5.4.tgz#160a6d9ac8919a1eed0a0b0a7780b82ef03686fe"
integrity sha512-uODXaU5sJw9CilmjVG9IUpc1ENivixQI7+DtebWgG80PMQBfZ/4b5PKXJ5ESJVlvafrHcuIjN0qou99zN2bDtw==
"@action-validator/core@^0.6.0":
version "0.6.0"
resolved "https://registry.yarnpkg.com/@action-validator/core/-/core-0.6.0.tgz#8fbaf45562a5377140815b79cc1ac9f610ff63e5"
integrity sha512-tPglwCr8Mm8SWzwnVewwFmqRx91F0WvMsM0BRAqH4CLalyGndm53Xvp+UcUSzswpk1wkjIDYI7RyEhWMLyPkig==
"@ampproject/remapping@^2.2.0":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630"
integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==
version "2.3.0"
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
dependencies:
"@jridgewell/gen-mapping" "^0.3.0"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.24"
"@babel/cli@^7.12.10":
version "7.23.9"
@@ -64,26 +64,26 @@
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730"
integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==
"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9", "@babel/compat-data@^7.23.3", "@babel/compat-data@^7.23.5":
"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9", "@babel/compat-data@^7.23.5":
version "7.23.5"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98"
integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==
"@babel/core@^7.0.0", "@babel/core@^7.12.10":
version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.9.tgz#b028820718000f267870822fec434820e9b1e4d1"
integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==
version "7.24.0"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.0.tgz#56cbda6b185ae9d9bed369816a8f4423c5f2ff1b"
integrity sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==
dependencies:
"@ampproject/remapping" "^2.2.0"
"@babel/code-frame" "^7.23.5"
"@babel/generator" "^7.23.6"
"@babel/helper-compilation-targets" "^7.23.6"
"@babel/helper-module-transforms" "^7.23.3"
"@babel/helpers" "^7.23.9"
"@babel/parser" "^7.23.9"
"@babel/template" "^7.23.9"
"@babel/traverse" "^7.23.9"
"@babel/types" "^7.23.9"
"@babel/helpers" "^7.24.0"
"@babel/parser" "^7.24.0"
"@babel/template" "^7.24.0"
"@babel/traverse" "^7.24.0"
"@babel/types" "^7.24.0"
convert-source-map "^2.0.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
@@ -209,9 +209,9 @@
semver "^6.3.1"
"@babel/helper-create-class-features-plugin@^7.22.15":
version "7.23.10"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.10.tgz#25d55fafbaea31fd0e723820bb6cc3df72edf7ea"
integrity sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw==
version "7.24.0"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.0.tgz#fc7554141bdbfa2d17f7b4b80153b9b090e5d158"
integrity sha512-QAH+vfvts51BCsNZ2PhY6HAggnlS6omLLFTsIpeqZk/MmJ6cW7tgz5yRv0fMJThcr6FmbMrENh1RgrWPTYA76g==
dependencies:
"@babel/helper-annotate-as-pure" "^7.22.5"
"@babel/helper-environment-visitor" "^7.22.20"
@@ -313,7 +313,12 @@
dependencies:
"@babel/types" "^7.22.5"
"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
version "7.24.0"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz#945681931a52f15ce879fd5b86ce2dae6d3d7f2a"
integrity sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==
"@babel/helper-plugin-utils@^7.20.2":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295"
integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==
@@ -395,14 +400,14 @@
"@babel/traverse" "^7.22.15"
"@babel/types" "^7.22.15"
"@babel/helpers@^7.23.9":
version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.9.tgz#c3e20bbe7f7a7e10cb9b178384b4affdf5995c7d"
integrity sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==
"@babel/helpers@^7.24.0":
version "7.24.0"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.0.tgz#a3dd462b41769c95db8091e49cfe019389a9409b"
integrity sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==
dependencies:
"@babel/template" "^7.23.9"
"@babel/traverse" "^7.23.9"
"@babel/types" "^7.23.9"
"@babel/template" "^7.24.0"
"@babel/traverse" "^7.24.0"
"@babel/types" "^7.24.0"
"@babel/highlight@^7.22.13", "@babel/highlight@^7.23.4":
version "7.23.4"
@@ -418,7 +423,7 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.16.tgz#180aead7f247305cce6551bea2720934e2fa2c95"
integrity sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==
"@babel/parser@^7.22.15", "@babel/parser@^7.23.9":
"@babel/parser@^7.22.15":
version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b"
integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==
@@ -428,6 +433,11 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9"
integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==
"@babel/parser@^7.24.0":
version "7.24.0"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.0.tgz#26a3d1ff49031c53a97d03b604375f028746a9ac"
integrity sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz#5cd1c87ba9380d0afb78469292c954fee5d2411a"
@@ -884,14 +894,14 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-syntax-numeric-separator" "^7.10.4"
"@babel/plugin-transform-object-rest-spread@^7.23.4":
version "7.23.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz#2b9c2d26bf62710460bdc0d1730d4f1048361b83"
integrity sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==
"@babel/plugin-transform-object-rest-spread@^7.24.0":
version "7.24.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.0.tgz#7b836ad0088fdded2420ce96d4e1d3ed78b71df1"
integrity sha512-y/yKMm7buHpFFXfxVFS4Vk1ToRJDilIa6fKRioB9Vjichv58TDGXTvqV0dN7plobAmTW5eSEGXDngE+Mm+uO+w==
dependencies:
"@babel/compat-data" "^7.23.3"
"@babel/helper-compilation-targets" "^7.22.15"
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/compat-data" "^7.23.5"
"@babel/helper-compilation-targets" "^7.23.6"
"@babel/helper-plugin-utils" "^7.24.0"
"@babel/plugin-syntax-object-rest-spread" "^7.8.3"
"@babel/plugin-transform-parameters" "^7.23.3"
@@ -975,12 +985,12 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-transform-runtime@^7.12.10":
version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.9.tgz#2c64d0680fc8e09e1dfe8fd5c646fe72abd82004"
integrity sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ==
version "7.24.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.0.tgz#e308fe27d08b74027d42547081eefaf4f2ffbcc9"
integrity sha512-zc0GA5IitLKJrSfXlXmp8KDqLrnGECK7YRfQBmEKg1NmBOQ7e+KuclBEKJgzifQeUYLdNiAw4B4bjyvzWVLiSA==
dependencies:
"@babel/helper-module-imports" "^7.22.15"
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/helper-plugin-utils" "^7.24.0"
babel-plugin-polyfill-corejs2 "^0.4.8"
babel-plugin-polyfill-corejs3 "^0.9.0"
babel-plugin-polyfill-regenerator "^0.5.5"
@@ -1064,13 +1074,13 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/preset-env@^7.12.11":
version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.23.9.tgz#beace3b7994560ed6bf78e4ae2073dff45387669"
integrity sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==
version "7.24.0"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.24.0.tgz#11536a7f4b977294f0bdfad780f01a8ac8e183fc"
integrity sha512-ZxPEzV9IgvGn73iK0E6VB9/95Nd7aMFpbE0l8KQFDG70cOV9IxRP7Y2FUPmlK0v6ImlLqYX50iuZ3ZTVhOF2lA==
dependencies:
"@babel/compat-data" "^7.23.5"
"@babel/helper-compilation-targets" "^7.23.6"
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/helper-plugin-utils" "^7.24.0"
"@babel/helper-validator-option" "^7.23.5"
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.23.3"
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.23.3"
@@ -1123,7 +1133,7 @@
"@babel/plugin-transform-new-target" "^7.23.3"
"@babel/plugin-transform-nullish-coalescing-operator" "^7.23.4"
"@babel/plugin-transform-numeric-separator" "^7.23.4"
"@babel/plugin-transform-object-rest-spread" "^7.23.4"
"@babel/plugin-transform-object-rest-spread" "^7.24.0"
"@babel/plugin-transform-object-super" "^7.23.3"
"@babel/plugin-transform-optional-catch-binding" "^7.23.4"
"@babel/plugin-transform-optional-chaining" "^7.23.4"
@@ -1186,20 +1196,20 @@
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4":
version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7"
integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==
version "7.24.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.0.tgz#584c450063ffda59697021430cb47101b085951e"
integrity sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==
dependencies:
regenerator-runtime "^0.14.0"
"@babel/template@^7.22.15", "@babel/template@^7.23.9":
version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.23.9.tgz#f881d0487cba2828d3259dcb9ef5005a9731011a"
integrity sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==
"@babel/template@^7.22.15", "@babel/template@^7.24.0":
version "7.24.0"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50"
integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==
dependencies:
"@babel/code-frame" "^7.23.5"
"@babel/parser" "^7.23.9"
"@babel/types" "^7.23.9"
"@babel/parser" "^7.24.0"
"@babel/types" "^7.24.0"
"@babel/template@^7.3.3":
version "7.22.15"
@@ -1226,10 +1236,10 @@
debug "^4.1.0"
globals "^11.1.0"
"@babel/traverse@^7.23.9":
version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.9.tgz#2f9d6aead6b564669394c5ce0f9302bb65b9d950"
integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==
"@babel/traverse@^7.24.0":
version "7.24.0"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.0.tgz#4a408fbf364ff73135c714a2ab46a5eab2831b1e"
integrity sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==
dependencies:
"@babel/code-frame" "^7.23.5"
"@babel/generator" "^7.23.6"
@@ -1237,8 +1247,8 @@
"@babel/helper-function-name" "^7.23.0"
"@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.23.9"
"@babel/types" "^7.23.9"
"@babel/parser" "^7.24.0"
"@babel/types" "^7.24.0"
debug "^4.3.1"
globals "^11.1.0"
@@ -1251,10 +1261,10 @@
"@babel/helper-validator-identifier" "^7.22.15"
to-fast-properties "^2.0.0"
"@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.4.4":
version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002"
integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==
"@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.24.0", "@babel/types@^7.4.4":
version "7.24.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf"
integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==
dependencies:
"@babel/helper-string-parser" "^7.23.4"
"@babel/helper-validator-identifier" "^7.22.20"
@@ -1340,18 +1350,18 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@eslint/js@8.56.0":
version "8.56.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b"
integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==
"@eslint/js@8.57.0":
version "8.57.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f"
integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
"@humanwhocodes/config-array@^0.11.13":
version "0.11.13"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297"
integrity sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==
"@humanwhocodes/config-array@^0.11.14":
version "0.11.14"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b"
integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==
dependencies:
"@humanwhocodes/object-schema" "^2.0.1"
debug "^4.1.1"
"@humanwhocodes/object-schema" "^2.0.2"
debug "^4.3.1"
minimatch "^3.0.5"
"@humanwhocodes/module-importer@^1.0.1":
@@ -1359,10 +1369,10 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
"@humanwhocodes/object-schema@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044"
integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==
"@humanwhocodes/object-schema@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917"
integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==
"@isaacs/cliui@^8.0.2":
version "8.0.2"
@@ -1610,24 +1620,29 @@
"@types/yargs" "^17.0.8"
chalk "^4.0.0"
"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2":
version "0.3.3"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098"
integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==
"@jridgewell/gen-mapping@^0.3.2", "@jridgewell/gen-mapping@^0.3.5":
version "0.3.5"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
dependencies:
"@jridgewell/set-array" "^1.0.1"
"@jridgewell/set-array" "^1.2.1"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/trace-mapping" "^0.3.24"
"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0":
"@jridgewell/resolve-uri@^3.0.3":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721"
integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
"@jridgewell/set-array@^1.0.1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
"@jridgewell/resolve-uri@^3.1.0":
version "3.1.2"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
"@jridgewell/set-array@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
version "1.4.15"
@@ -1650,10 +1665,10 @@
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
version "0.3.22"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c"
integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.24":
version "0.3.25"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
dependencies:
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
@@ -1933,9 +1948,9 @@
undici-types "~5.26.4"
"@types/node@18":
version "18.19.17"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.17.tgz#a581a9fb4b2cfdbc61f008804f4436b2d5c40354"
integrity sha512-SzyGKgwPzuWp2SHhlpXKzCX0pIOfcI4V2eF37nNBJOhwlegQ83omtVQ1XxZpDE06V/d6AQvfQdPfnw0tRC//Ng==
version "18.19.21"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.21.tgz#f4ca1ac8ffb05ee4b89163c2d6fac9a1a59ee149"
integrity sha512-2Q2NeB6BmiTFQi4DHBzncSoq/cJMLDdhPaAoJFnFCyD9a8VPZRf7a1GAwp1Edb7ROaZc5Jz/tnZyL6EsWMRaqw==
dependencies:
undici-types "~5.26.4"
@@ -1960,9 +1975,9 @@
integrity sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==
"@types/semver@^7.5.0":
version "7.5.6"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339"
integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==
version "7.5.8"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
"@types/stack-utils@^2.0.0":
version "2.0.3"
@@ -1997,15 +2012,15 @@
"@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^7.0.0":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.2.tgz#c13a34057be425167cc4a765158c46fdf2fd981d"
integrity sha512-/XtVZJtbaphtdrWjr+CJclaCVGPtOdBpFEnvtNf/jRV0IiEemRrL0qABex/nEt8isYcnFacm3nPHYQwL+Wb7qg==
version "7.1.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.1.tgz#dd71fc5c7ecec745ca26ece506d84d203a205c0e"
integrity sha512-zioDz623d0RHNhvx0eesUmGfIjzrk18nSBC8xewepKXbBvN/7c1qImV7Hg8TI1URTxKax7/zxfxj3Uph8Chcuw==
dependencies:
"@eslint-community/regexpp" "^4.5.1"
"@typescript-eslint/scope-manager" "7.0.2"
"@typescript-eslint/type-utils" "7.0.2"
"@typescript-eslint/utils" "7.0.2"
"@typescript-eslint/visitor-keys" "7.0.2"
"@typescript-eslint/scope-manager" "7.1.1"
"@typescript-eslint/type-utils" "7.1.1"
"@typescript-eslint/utils" "7.1.1"
"@typescript-eslint/visitor-keys" "7.1.1"
debug "^4.3.4"
graphemer "^1.4.0"
ignore "^5.2.4"
@@ -2014,14 +2029,14 @@
ts-api-utils "^1.0.1"
"@typescript-eslint/parser@^7.0.0":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.0.2.tgz#95c31233d343db1ca1df8df7811b5b87ca7b1a68"
integrity sha512-GdwfDglCxSmU+QTS9vhz2Sop46ebNCXpPPvsByK7hu0rFGRHL+AusKQJ7SoN+LbLh6APFpQwHKmDSwN35Z700Q==
version "7.1.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.1.1.tgz#6a9d0a5c9ccdf5dbd3cb8c949728c64e24e07d1f"
integrity sha512-ZWUFyL0z04R1nAEgr9e79YtV5LbafdOtN7yapNbn1ansMyaegl2D4bL7vHoJ4HPSc4CaLwuCVas8CVuneKzplQ==
dependencies:
"@typescript-eslint/scope-manager" "7.0.2"
"@typescript-eslint/types" "7.0.2"
"@typescript-eslint/typescript-estree" "7.0.2"
"@typescript-eslint/visitor-keys" "7.0.2"
"@typescript-eslint/scope-manager" "7.1.1"
"@typescript-eslint/types" "7.1.1"
"@typescript-eslint/typescript-estree" "7.1.1"
"@typescript-eslint/visitor-keys" "7.1.1"
debug "^4.3.4"
"@typescript-eslint/scope-manager@5.62.0":
@@ -2032,21 +2047,21 @@
"@typescript-eslint/types" "5.62.0"
"@typescript-eslint/visitor-keys" "5.62.0"
"@typescript-eslint/scope-manager@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz#6ec4cc03752758ddd1fdaae6fbd0ed9a2ca4fe63"
integrity sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==
"@typescript-eslint/scope-manager@7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.1.1.tgz#9e301803ff8e21a74f50c6f89a4baccad9a48f93"
integrity sha512-cirZpA8bJMRb4WZ+rO6+mnOJrGFDd38WoXCEI57+CYBqta8Yc8aJym2i7vyqLL1vVYljgw0X27axkUXz32T8TA==
dependencies:
"@typescript-eslint/types" "7.0.2"
"@typescript-eslint/visitor-keys" "7.0.2"
"@typescript-eslint/types" "7.1.1"
"@typescript-eslint/visitor-keys" "7.1.1"
"@typescript-eslint/type-utils@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.0.2.tgz#a7fc0adff0c202562721357e7478207d380a757b"
integrity sha512-IKKDcFsKAYlk8Rs4wiFfEwJTQlHcdn8CLwLaxwd6zb8HNiMcQIFX9sWax2k4Cjj7l7mGS5N1zl7RCHOVwHq2VQ==
"@typescript-eslint/type-utils@7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.1.1.tgz#aee820d5bedd39b83c18585a526cc520ddb7a226"
integrity sha512-5r4RKze6XHEEhlZnJtR3GYeCh1IueUHdbrukV2KSlLXaTjuSfeVF8mZUVPLovidCuZfbVjfhi4c0DNSa/Rdg5g==
dependencies:
"@typescript-eslint/typescript-estree" "7.0.2"
"@typescript-eslint/utils" "7.0.2"
"@typescript-eslint/typescript-estree" "7.1.1"
"@typescript-eslint/utils" "7.1.1"
debug "^4.3.4"
ts-api-utils "^1.0.1"
@@ -2055,10 +2070,10 @@
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f"
integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==
"@typescript-eslint/types@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.0.2.tgz#b6edd108648028194eb213887d8d43ab5750351c"
integrity sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==
"@typescript-eslint/types@7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.1.1.tgz#ca33ba7cf58224fb46a84fea62593c2c53cd795f"
integrity sha512-KhewzrlRMrgeKm1U9bh2z5aoL4s7K3tK5DwHDn8MHv0yQfWFz/0ZR6trrIHHa5CsF83j/GgHqzdbzCXJ3crx0Q==
"@typescript-eslint/typescript-estree@5.62.0":
version "5.62.0"
@@ -2073,13 +2088,13 @@
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/typescript-estree@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz#3c6dc8a3b9799f4ef7eca0d224ded01974e4cb39"
integrity sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==
"@typescript-eslint/typescript-estree@7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.1.tgz#09c54af0151a1b05d0875c0fc7fe2ec7a2476ece"
integrity sha512-9ZOncVSfr+sMXVxxca2OJOPagRwT0u/UHikM2Rd6L/aB+kL/QAuTnsv6MeXtjzCJYb8PzrXarypSGIPx3Jemxw==
dependencies:
"@typescript-eslint/types" "7.0.2"
"@typescript-eslint/visitor-keys" "7.0.2"
"@typescript-eslint/types" "7.1.1"
"@typescript-eslint/visitor-keys" "7.1.1"
debug "^4.3.4"
globby "^11.1.0"
is-glob "^4.0.3"
@@ -2087,17 +2102,17 @@
semver "^7.5.4"
ts-api-utils "^1.0.1"
"@typescript-eslint/utils@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.0.2.tgz#8756123054cd934c8ba7db6a6cffbc654b10b5c4"
integrity sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==
"@typescript-eslint/utils@7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.1.1.tgz#bdeeb789eee4af5d3fb5400a69566d4dbf97ff3b"
integrity sha512-thOXM89xA03xAE0lW7alstvnyoBUbBX38YtY+zAUcpRPcq9EIhXPuJ0YTv948MbzmKh6e1AUszn5cBFK49Umqg==
dependencies:
"@eslint-community/eslint-utils" "^4.4.0"
"@types/json-schema" "^7.0.12"
"@types/semver" "^7.5.0"
"@typescript-eslint/scope-manager" "7.0.2"
"@typescript-eslint/types" "7.0.2"
"@typescript-eslint/typescript-estree" "7.0.2"
"@typescript-eslint/scope-manager" "7.1.1"
"@typescript-eslint/types" "7.1.1"
"@typescript-eslint/typescript-estree" "7.1.1"
semver "^7.5.4"
"@typescript-eslint/utils@^5.10.0":
@@ -2122,12 +2137,12 @@
"@typescript-eslint/types" "5.62.0"
eslint-visitor-keys "^3.3.0"
"@typescript-eslint/visitor-keys@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz#2899b716053ad7094962beb895d11396fc12afc7"
integrity sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==
"@typescript-eslint/visitor-keys@7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.1.tgz#e6538a58c9b157f03bcbb29e3b6a92fe39a6ab0d"
integrity sha512-yTdHDQxY7cSoCcAtiBzVzxleJhkGB9NncSIyMYe2+OGON1ZsP9zOPws/Pqgopa65jvknOjlk/w7ulPlZ78PiLQ==
dependencies:
"@typescript-eslint/types" "7.0.2"
"@typescript-eslint/types" "7.1.1"
eslint-visitor-keys "^3.4.1"
"@ungap/structured-clone@^1.2.0":
@@ -2355,11 +2370,6 @@ asn1js@^3.0.1, asn1js@^3.0.5:
pvutils "^1.1.3"
tslib "^2.4.0"
async@^3.2.4:
version "3.2.4"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -2508,13 +2518,13 @@ browserslist@^4.21.9:
node-releases "^2.0.14"
update-browserslist-db "^1.0.13"
browserslist@^4.22.2:
version "4.22.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.3.tgz#299d11b7e947a6b843981392721169e27d60c5a6"
integrity sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==
browserslist@^4.22.2, browserslist@^4.22.3:
version "4.23.0"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab"
integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==
dependencies:
caniuse-lite "^1.0.30001580"
electron-to-chromium "^1.4.648"
caniuse-lite "^1.0.30001587"
electron-to-chromium "^1.4.668"
node-releases "^2.0.14"
update-browserslist-db "^1.0.13"
@@ -2566,11 +2576,16 @@ camelcase@^6.2.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
caniuse-lite@^1.0.30001565, caniuse-lite@^1.0.30001580:
caniuse-lite@^1.0.30001565:
version "1.0.30001583"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001583.tgz#abb2970cc370801dc7e27bf290509dc132cfa390"
integrity sha512-acWTYaha8xfhA/Du/z4sNZjHUWjkiuoAi2LM+T/aL+kemKQgPT1xBb/YKjlQ0Qo8gvbHsGNplrEJ+9G3gL7i4Q==
caniuse-lite@^1.0.30001587:
version "1.0.30001594"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001594.tgz#bea552414cd52c2d0c985ed9206314a696e685f5"
integrity sha512-VblSX6nYqyJVs8DKFMldE2IVCJjZ225LW00ydtUWwh5hk9IfkTOffO6r8gJNsH0qqqeAF8KrbMYA2VEwTlGW5g==
chalk@5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3"
@@ -2581,7 +2596,7 @@ chalk@5.3.0:
resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385"
integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==
chalk@^2.4.1, chalk@^2.4.2:
chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -2647,14 +2662,6 @@ cli-cursor@^4.0.0:
dependencies:
restore-cursor "^4.0.0"
cli-diff@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/cli-diff/-/cli-diff-1.0.0.tgz#c56f1fa17849a629bf07154a2bd199aefe742964"
integrity sha512-XOVrll4VMhxBv26WqV6OH9cWqRxBXthh3uZ3dtg+CLqB8m0R6QJiSoDIXQNXDAeo/FAkQ+kF9Ph8NhQskU3LpQ==
dependencies:
chalk "^2.4.1"
diff "^3.5.0"
cli-truncate@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-4.0.0.tgz#6cc28a2924fee9e25ce91e973db56c7066e6172a"
@@ -2768,11 +2775,11 @@ convert-source-map@^2.0.0:
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
core-js-compat@^3.31.0, core-js-compat@^3.34.0:
version "3.35.1"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.35.1.tgz#215247d7edb9e830efa4218ff719beb2803555e2"
integrity sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw==
version "3.36.0"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.36.0.tgz#087679119bc2fdbdefad0d45d8e5d307d45ba190"
integrity sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw==
dependencies:
browserslist "^4.22.2"
browserslist "^4.22.3"
core-js@^3.0.0:
version "3.32.0"
@@ -2904,11 +2911,6 @@ diff-sequences@^29.6.3:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921"
integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==
diff@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
diff@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
@@ -2947,11 +2949,16 @@ eastasianwidth@^0.2.0:
resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
electron-to-chromium@^1.4.601, electron-to-chromium@^1.4.648:
electron-to-chromium@^1.4.601:
version "1.4.655"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.655.tgz#112410db0d7f9c2b4ed8baa3b1b548522a6f89d4"
integrity sha512-2yszojF7vIZ68adIOvzV4bku8OZad9w5H9xF3ZAMZjPuOjBarlflUkjN6DggdV+L71WZuKUfKUhov/34+G5QHg==
electron-to-chromium@^1.4.668:
version "1.4.692"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.692.tgz#82139d20585a4b2318a02066af7593a3e6bec993"
integrity sha512-d5rZRka9n2Y3MkWRN74IoAsxR0HK3yaAt7T50e3iT9VZmCCQDT3geXUO5ZRMhDToa1pkCeQXuNo+0g+NfDOVPA==
emittery@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"
@@ -3063,9 +3070,9 @@ es-to-primitive@^1.2.1:
is-symbol "^1.0.2"
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
version "3.1.2"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
escape-string-regexp@^1.0.5:
version "1.0.5"
@@ -3163,9 +3170,9 @@ eslint-plugin-jest@^27.1.6:
"@typescript-eslint/utils" "^5.10.0"
eslint-plugin-jsdoc@^48.0.0:
version "48.1.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.1.0.tgz#565363770b433485bfc70dc862b50b7f780529ec"
integrity sha512-g9S8ukmTd1DVcV/xeBYPPXOZ6rc8WJ4yi0+MVxJ1jBOrz5kmxV9gJJQ64ltCqIWFnBChLIhLVx3tbTSarqVyFA==
version "48.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.0.tgz#a726fbd6fa286fad8fc14f0a6aca48488d188d95"
integrity sha512-O2B1XLBJnUCRkggFzUQ+PBYJDit8iAgXdlu8ucolqGrbmOWPvttZQZX8d1sC0MbqDMSLs8SHSQxaNPRY1RQREg==
dependencies:
"@es-joy/jsdoccomment" "~0.42.0"
are-docs-informative "^0.0.2"
@@ -3243,16 +3250,16 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
eslint@8.56.0:
version "8.56.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.56.0.tgz#4957ce8da409dc0809f99ab07a1b94832ab74b15"
integrity sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==
eslint@8.57.0:
version "8.57.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668"
integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==
dependencies:
"@eslint-community/eslint-utils" "^4.2.0"
"@eslint-community/regexpp" "^4.6.1"
"@eslint/eslintrc" "^2.1.4"
"@eslint/js" "8.56.0"
"@humanwhocodes/config-array" "^0.11.13"
"@eslint/js" "8.57.0"
"@humanwhocodes/config-array" "^0.11.14"
"@humanwhocodes/module-importer" "^1.0.1"
"@nodelib/fs.walk" "^1.2.8"
"@ungap/structured-clone" "^1.2.0"
@@ -3559,15 +3566,6 @@ form-data@^4.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
fs-extra@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
universalify "^2.0.0"
fs-readdir-recursive@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27"
@@ -3738,7 +3736,7 @@ gopd@^1.0.1:
dependencies:
get-intrinsic "^1.1.3"
graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
graceful-fs@^4.2.4, graceful-fs@^4.2.9:
version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
@@ -3793,9 +3791,9 @@ has@^1.0.3:
integrity sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==
hasown@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c"
integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==
version "2.0.1"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.1.tgz#26f48f039de2c0f8d3356c223fb8d50253519faa"
integrity sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==
dependencies:
function-bind "^1.1.2"
@@ -3844,9 +3842,9 @@ human-signals@^5.0.0:
integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==
husky@^9.0.0:
version "9.0.10"
resolved "https://registry.yarnpkg.com/husky/-/husky-9.0.10.tgz#ddca8908deb5f244e9286865ebc80b54387672c2"
integrity sha512-TQGNknoiy6bURzIO77pPRu+XHi6zI7T93rX+QnJsoYFf3xdjKOur+IlfqzJGMHIK/wXrLg+GsvMs8Op7vI2jVA==
version "9.0.11"
resolved "https://registry.yarnpkg.com/husky/-/husky-9.0.11.tgz#fc91df4c756050de41b3e478b2158b87c1e79af9"
integrity sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==
iconv-lite@0.6.3:
version "0.6.3"
@@ -4714,18 +4712,9 @@ json5@^2.2.3:
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
jsonc-parser@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76"
integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==
jsonfile@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
dependencies:
universalify "^2.0.0"
optionalDependencies:
graceful-fs "^4.1.6"
version "3.2.1"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a"
integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==
jwt-decode@^4.0.0:
version "4.0.0"
@@ -4972,7 +4961,7 @@ min-indent@^1.0.0:
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
minimatch@9.0.3, minimatch@^9.0.0, minimatch@^9.0.1:
minimatch@9.0.3, minimatch@^9.0.1, minimatch@^9.0.3:
version "9.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
@@ -5348,7 +5337,7 @@ pretty-format@^29.0.0, pretty-format@^29.7.0:
ansi-styles "^5.0.0"
react-is "^18.0.0"
prompts@^2.0.1, prompts@^2.4.2:
prompts@^2.0.1:
version "2.4.2"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==
@@ -5651,14 +5640,14 @@ semver@^6.3.0, semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.3.7, semver@^7.6.0:
semver@^7.3.7, semver@^7.5.4, semver@^7.6.0:
version "7.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
dependencies:
lru-cache "^6.0.0"
semver@^7.5.3, semver@^7.5.4:
semver@^7.5.3:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
@@ -5703,10 +5692,10 @@ shebang-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
shiki@^0.14.1:
version "0.14.3"
resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.3.tgz#d1a93c463942bdafb9866d74d619a4347d0bbf64"
integrity sha512-U3S/a+b0KS+UkTyMjoNojvTgrBHjgp7L6ovhFVZsXmBGnVdQ4K4U9oK0z63w538S91ATngv1vXigHCSWOwnr+g==
shiki@^0.14.7:
version "0.14.7"
resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.7.tgz#c3c9e1853e9737845f1d2ef81b31bcfb07056d4e"
integrity sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==
dependencies:
ansi-sequence-parser "^1.1.0"
jsonc-parser "^3.2.0"
@@ -6040,9 +6029,9 @@ tr46@~0.0.3:
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
ts-api-utils@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331"
integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==
version "1.2.1"
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.2.1.tgz#f716c7e027494629485b21c0df6180f4d08f5e8b"
integrity sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==
ts-node@^10.9.1:
version "10.9.2"
@@ -6172,48 +6161,29 @@ typedoc-plugin-coverage@^3.0.0:
integrity sha512-fxeJRrxQR3yM/aFZU7mOuatgRCztiMCbeNiCRVZKY6VNgOcVMC1HS+ZfZnlbLLteUOdeWleSQ6yntuipz5zi6A==
typedoc-plugin-mdn-links@^3.0.3:
version "3.1.16"
resolved "https://registry.yarnpkg.com/typedoc-plugin-mdn-links/-/typedoc-plugin-mdn-links-3.1.16.tgz#936408cff409b8f9b2b55d9f6589850c488a9858"
integrity sha512-Jdnw3tI3KOOMMahUNiCwKXJcH+Ov9y3syRQ8HP1ce8nT2O9Bsg9aqqa0LWye+CdCiSpWJ/USzOK3MFNxRYzL4A==
version "3.1.17"
resolved "https://registry.yarnpkg.com/typedoc-plugin-mdn-links/-/typedoc-plugin-mdn-links-3.1.17.tgz#cb9214f0a60a9a347c778e228c307b9d033ba5cb"
integrity sha512-DCkMuXna8qipSwfzXl6c1x7nBeZUW/qWCACoaaP/cUzUyy/1MXedUyteuVsfw6+Sod61gIAr9ccwH78nrdwu6A==
typedoc-plugin-missing-exports@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-2.2.0.tgz#e39a04bab5b0d7f7b28507d64c07a4c40b788648"
integrity sha512-2+XR1IcyQ5UwXZVJe9NE6HrLmNufT9i5OwoIuuj79VxuA3eYq+Y6itS9rnNV1D7UeQnUSH8kISYD73gHE5zw+w==
typedoc-plugin-versions-cli@^0.1.12:
version "0.1.12"
resolved "https://registry.yarnpkg.com/typedoc-plugin-versions-cli/-/typedoc-plugin-versions-cli-0.1.12.tgz#3e20c4e4078d8aec827dc3cc4686069d6e3c8ab2"
integrity sha512-tmGXo8T6gGW3hajMh+cZTRo50w6JJyOuCWBALGxZM0TOaRL4n0J3SanO2vFUrVd25QR/O5/1pdnTKW1ldcXmXg==
dependencies:
async "^3.2.4"
cli-diff "^1.0.0"
prompts "^2.4.2"
semver "^7.3.7"
yargs "^17.5.1"
typedoc-plugin-versions@^0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/typedoc-plugin-versions/-/typedoc-plugin-versions-0.2.4.tgz#e670f8c4adc323dffc792b390bf9c8f11447afdf"
integrity sha512-mWJHl4oXyWMxrcxVWDvZvLSm+wr3SIEYH+pAiEcyQPJXiq59gLWdmiulqVz2OUG35plOpC5x7sOF5JsIa+plsw==
dependencies:
fs-extra "^10.1.0"
semver "^7.3.7"
typedoc@^0.24.0:
version "0.24.8"
resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.24.8.tgz#cce9f47ba6a8d52389f5e583716a2b3b4335b63e"
integrity sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==
typedoc@^0.25.10:
version "0.25.11"
resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.25.11.tgz#75080c594c1e26b7676f90faebb367fb5a32dc8d"
integrity sha512-5MbI1W/FOG6oXsd8bdssQidSTeKh8Kt3xA5uKVzI+K99uzP8EGN45uPnPvQesyaWdD+89s4wCQdtWEd8QUbiRg==
dependencies:
lunr "^2.3.9"
marked "^4.3.0"
minimatch "^9.0.0"
shiki "^0.14.1"
minimatch "^9.0.3"
shiki "^0.14.7"
typescript@^5.0.0:
version "5.1.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274"
integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==
typescript@^5.3.3:
version "5.3.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37"
integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==
unbox-primitive@^1.0.2:
version "1.0.2"
@@ -6263,11 +6233,6 @@ universalify@^0.2.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0"
integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==
universalify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
update-browserslist-db@^1.0.13:
version "1.0.13"
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4"
@@ -6524,7 +6489,7 @@ yargs-parser@^21.1.1:
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
yargs@^17.3.1, yargs@^17.5.1:
yargs@^17.3.1:
version "17.7.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==