diff --git a/knip.ts b/knip.ts index 4709fdbc3..afffa74b7 100644 --- a/knip.ts +++ b/knip.ts @@ -9,6 +9,7 @@ export default { "src/crypto-api/index.ts", "src/testing.ts", "src/matrix.ts", + "src/utils.ts", // not really an entrypoint but we have deprecated `defer` there "scripts/**", "spec/**", // XXX: these look entirely unused diff --git a/spec/integ/crypto/crypto.spec.ts b/spec/integ/crypto/crypto.spec.ts index 1594753e5..f292f9ff4 100644 --- a/spec/integ/crypto/crypto.spec.ts +++ b/spec/integ/crypto/crypto.spec.ts @@ -59,7 +59,7 @@ import { } from "../../../src/matrix"; import { E2EKeyReceiver } from "../../test-utils/E2EKeyReceiver"; import { type ISyncResponder, SyncResponder } from "../../test-utils/SyncResponder"; -import { defer, escapeRegExp } from "../../../src/utils"; +import { escapeRegExp } from "../../../src/utils"; import { downloadDeviceToJsDevice } from "../../../src/rust-crypto/device-converter"; import { flushPromises } from "../../test-utils/flushPromises"; import { @@ -1283,13 +1283,13 @@ describe("crypto", () => { const inboundGroupSessionPromise = expectSendRoomKey("@bob:xyz", testOlmAccount); // ... and finally, send the room key. We block the response until `sendRoomMessageDefer` completes. - const sendRoomMessageDefer = defer(); + const sendRoomMessageResolvers = Promise.withResolvers(); const reqProm = new Promise((resolve) => { fetchMock.putOnce( new RegExp("/send/m.room.encrypted/"), async (url: string, opts: RequestInit): Promise => { resolve(JSON.parse(opts.body as string)); - return await sendRoomMessageDefer.promise; + return await sendRoomMessageResolvers.promise; }, { // append to the list of intercepts on this path (since we have some tests that call @@ -1318,7 +1318,7 @@ describe("crypto", () => { // release the send request const resp = { event_id: "$event_id" }; - sendRoomMessageDefer.resolve(resp); + sendRoomMessageResolvers.resolve(resp); expect(await sendProm).toEqual(resp); // still pending at this point diff --git a/spec/integ/crypto/megolm-backup.spec.ts b/spec/integ/crypto/megolm-backup.spec.ts index 897875094..bbb489b10 100644 --- a/spec/integ/crypto/megolm-backup.spec.ts +++ b/spec/integ/crypto/megolm-backup.spec.ts @@ -36,7 +36,6 @@ import { advanceTimersUntil, awaitDecryption, syncPromise } from "../../test-uti import * as testData from "../../test-utils/test-data"; import { type KeyBackupInfo, type KeyBackupSession } from "../../../src/crypto-api/keybackup"; import { flushPromises } from "../../test-utils/flushPromises"; -import { defer, type IDeferred } from "../../../src/utils"; import { decodeRecoveryKey, DecryptionFailureCode, CryptoEvent, type CryptoApi } from "../../../src/crypto-api"; import { type KeyBackup } from "../../../src/rust-crypto/backup.ts"; @@ -861,7 +860,7 @@ describe("megolm-keys backup", () => { expect(await aliceCrypto.getKeyBackupInfo()).toStrictEqual(testData.SIGNED_BACKUP_DATA); // Delete the backup and we are expecting the key backup to be disabled - const keyBackupStatus = defer(); + const keyBackupStatus = Promise.withResolvers(); aliceClient.once(CryptoEvent.KeyBackupStatus, (enabled) => keyBackupStatus.resolve(enabled)); await aliceCrypto.deleteKeyBackupVersion(testData.SIGNED_BACKUP_DATA.version!); expect(await keyBackupStatus.promise).toBe(false); @@ -1158,7 +1157,7 @@ describe("megolm-keys backup", () => { // A check backup should happen at some point await aliceCrypto.checkKeyBackupAndEnable(); - const awaitHasQueriedNewBackup: IDeferred = defer(); + const awaitHasQueriedNewBackup: PromiseWithResolvers = Promise.withResolvers(); fetchMock.get( "express:/_matrix/client/v3/room_keys/keys/:room_id/:session_id", diff --git a/spec/integ/crypto/verification.spec.ts b/spec/integ/crypto/verification.spec.ts index 53e49e1d0..99c646ccd 100644 --- a/spec/integ/crypto/verification.spec.ts +++ b/spec/integ/crypto/verification.spec.ts @@ -44,7 +44,7 @@ import { type Verifier, VerifierEvent, } from "../../../src/crypto-api/verification"; -import { defer, escapeRegExp } from "../../../src/utils"; +import { escapeRegExp } from "../../../src/utils"; import { awaitDecryption, emitPromise, getSyncResponse, syncPromise } from "../../test-utils/test-utils"; import { SyncResponder } from "../../test-utils/SyncResponder"; import { @@ -1540,10 +1540,10 @@ function expectSendToDeviceMessage(msgtype: string): Promise<{ messages: any }> * @returns a map of secret name to promise that will resolve (with the id of the secret request) when the secret is requested. */ function mockSecretRequestAndGetPromises(): Map> { - const mskRequestDefer = defer(); - const sskRequestDefer = defer(); - const uskRequestDefer = defer(); - const backupKeyRequestDefer = defer(); + const mskRequestResolvers = Promise.withResolvers(); + const sskRequestResolvers = Promise.withResolvers(); + const uskRequestResolvers = Promise.withResolvers(); + const backupKeyRequestResolvers = Promise.withResolvers(); fetchMock.put( new RegExp(`/_matrix/client/(r0|v3)/sendToDevice/m.secret.request`), @@ -1555,13 +1555,13 @@ function mockSecretRequestAndGetPromises(): Map> { const name = content.name; const requestId = content.request_id; if (name == "m.cross_signing.user_signing") { - uskRequestDefer.resolve(requestId); + uskRequestResolvers.resolve(requestId); } else if (name == "m.cross_signing.master") { - mskRequestDefer.resolve(requestId); + mskRequestResolvers.resolve(requestId); } else if (name == "m.cross_signing.self_signing") { - sskRequestDefer.resolve(requestId); + sskRequestResolvers.resolve(requestId); } else if (name == "m.megolm_backup.v1") { - backupKeyRequestDefer.resolve(requestId); + backupKeyRequestResolvers.resolve(requestId); } } return {}; @@ -1570,10 +1570,10 @@ function mockSecretRequestAndGetPromises(): Map> { ); const promiseMap = new Map>(); - promiseMap.set("m.cross_signing.master", mskRequestDefer.promise); - promiseMap.set("m.cross_signing.self_signing", sskRequestDefer.promise); - promiseMap.set("m.cross_signing.user_signing", uskRequestDefer.promise); - promiseMap.set("m.megolm_backup.v1", backupKeyRequestDefer.promise); + promiseMap.set("m.cross_signing.master", mskRequestResolvers.promise); + promiseMap.set("m.cross_signing.self_signing", sskRequestResolvers.promise); + promiseMap.set("m.cross_signing.user_signing", uskRequestResolvers.promise); + promiseMap.set("m.megolm_backup.v1", backupKeyRequestResolvers.promise); return promiseMap; } diff --git a/spec/integ/matrix-client-methods.spec.ts b/spec/integ/matrix-client-methods.spec.ts index 1964cbe13..f4e207b50 100644 --- a/spec/integ/matrix-client-methods.spec.ts +++ b/spec/integ/matrix-client-methods.spec.ts @@ -1796,6 +1796,27 @@ describe("MatrixClient", function () { expect(client.getUserIdLocalpart()).toBe("alice"); }); }); + + describe("setRoomMutePushRule", () => { + it("should set room push rule to muted", async () => { + const roomId = "!roomId:server"; + const client = new MatrixClient({ + baseUrl: "http://localhost", + fetchFn: httpBackend.fetchFn as typeof globalThis.fetch, + }); + client.pushRules = { + global: { + room: [{ rule_id: roomId, actions: [], default: false, enabled: false }], + }, + }; + + const path = `/pushrules/global/room/${encodeURIComponent(roomId)}`; + httpBackend.when("DELETE", path).respond(200, {}); + httpBackend.when("PUT", path).respond(200, {}); + client.setRoomMutePushRule("global", roomId, true); + await httpBackend.flush(""); + }); + }); }); function withThreadId(event: MatrixEvent, newThreadId: string): MatrixEvent { diff --git a/spec/integ/rendezvous/MSC4108SignInWithQR.spec.ts b/spec/integ/rendezvous/MSC4108SignInWithQR.spec.ts index dd956bea2..c2c608cb8 100644 --- a/spec/integ/rendezvous/MSC4108SignInWithQR.spec.ts +++ b/spec/integ/rendezvous/MSC4108SignInWithQR.spec.ts @@ -26,7 +26,6 @@ import { PayloadType, RendezvousError, } from "../../../src/rendezvous"; -import { defer } from "../../../src/utils"; import { ClientPrefix, DEVICE_CODE_SCOPE, @@ -112,8 +111,8 @@ describe("MSC4108SignInWithQR", () => { let opponentLogin: MSC4108SignInWithQR; beforeEach(async () => { - let ourData = defer(); - let opponentData = defer(); + let ourData = Promise.withResolvers(); + let opponentData = Promise.withResolvers(); const ourMockSession = { send: jest.fn(async (newData) => { @@ -122,7 +121,7 @@ describe("MSC4108SignInWithQR", () => { receive: jest.fn(() => { const prom = opponentData.promise; prom.then(() => { - opponentData = defer(); + opponentData = Promise.withResolvers(); }); return prom; }), @@ -141,7 +140,7 @@ describe("MSC4108SignInWithQR", () => { receive: jest.fn(() => { const prom = ourData.promise; prom.then(() => { - ourData = defer(); + ourData = Promise.withResolvers(); }); return prom; }), @@ -334,11 +333,11 @@ describe("MSC4108SignInWithQR", () => { // @ts-ignore await opponentLogin.receive(); - const deferred = defer(); - mocked(client.getDevice).mockReturnValue(deferred.promise); + const deviceResolvers = Promise.withResolvers(); + mocked(client.getDevice).mockReturnValue(deviceResolvers.promise); ourLogin.cancel(MSC4108FailureReason.UserCancelled).catch(() => {}); - deferred.resolve({} as IMyDevice); + deviceResolvers.resolve({} as IMyDevice); const secrets = { cross_signing: { master_key: "mk", user_signing_key: "usk", self_signing_key: "ssk" }, diff --git a/spec/integ/sliding-sync-sdk.spec.ts b/spec/integ/sliding-sync-sdk.spec.ts index cf249b4b4..449090a17 100644 --- a/spec/integ/sliding-sync-sdk.spec.ts +++ b/spec/integ/sliding-sync-sdk.spec.ts @@ -48,7 +48,6 @@ import { type SyncApiOptions, SyncState } from "../../src/sync"; import { type 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"; import { type SyncCryptoCallbacks } from "../../src/common-crypto/CryptoBackend"; @@ -369,7 +368,7 @@ describe("SlidingSyncSdk", () => { }); it("can be created with live events", async () => { - const seenLiveEventDeferred = defer(); + const seenLiveEventDeferred = Promise.withResolvers(); const listener = ( ev: MatrixEvent, room?: Room, diff --git a/spec/unit/ToDeviceMessageQueue.spec.ts b/spec/unit/ToDeviceMessageQueue.spec.ts index 765d85e4f..a3c86585e 100644 --- a/spec/unit/ToDeviceMessageQueue.spec.ts +++ b/spec/unit/ToDeviceMessageQueue.spec.ts @@ -5,7 +5,6 @@ import { getMockClientWithEventEmitter } from "../test-utils/client"; import { StubStore } from "../../src/store/stub"; import { type IndexedToDeviceBatch } from "../../src/models/ToDeviceMessage"; import { SyncState } from "../../src/sync"; -import { defer } from "../../src/utils"; describe("onResumedSync", () => { let batch: IndexedToDeviceBatch | null; @@ -60,7 +59,7 @@ describe("onResumedSync", () => { }); it("resends queue after connectivity restored", async () => { - const deferred = defer(); + const successResolvers = Promise.withResolvers(); onSendToDeviceFailure = () => { expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); @@ -73,15 +72,15 @@ describe("onResumedSync", () => { onSendToDeviceSuccess = () => { expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(3); expect(store.removeToDeviceBatch).toHaveBeenCalled(); - deferred.resolve(); + successResolvers.resolve(); }; queue.start(); - return deferred.promise; + return successResolvers.promise; }); it("does not resend queue if client sync still catching up", async () => { - const deferred = defer(); + const successResolvers = Promise.withResolvers(); onSendToDeviceFailure = () => { expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); @@ -89,15 +88,15 @@ describe("onResumedSync", () => { resumeSync(SyncState.Catchup, SyncState.Catchup); expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); - deferred.resolve(); + successResolvers.resolve(); }; queue.start(); - return deferred.promise; + return successResolvers.promise; }); it("does not resend queue if connectivity restored after queue stopped", async () => { - const deferred = defer(); + const successResolvers = Promise.withResolvers(); onSendToDeviceFailure = () => { expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); @@ -107,10 +106,10 @@ describe("onResumedSync", () => { resumeSync(SyncState.Syncing, SyncState.Catchup); expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); - deferred.resolve(); + successResolvers.resolve(); }; queue.start(); - return deferred.promise; + return successResolvers.promise; }); }); diff --git a/spec/unit/http-api/fetch.spec.ts b/spec/unit/http-api/fetch.spec.ts index ae4ee8017..1f6355792 100644 --- a/spec/unit/http-api/fetch.spec.ts +++ b/spec/unit/http-api/fetch.spec.ts @@ -29,7 +29,7 @@ import { Method, } from "../../../src"; import { emitPromise } from "../../test-utils/test-utils"; -import { defer, type QueryDict, sleep } from "../../../src/utils"; +import { type QueryDict, sleep } from "../../../src/utils"; import { type Logger } from "../../../src/logger"; describe("FetchHttpApi", () => { @@ -525,8 +525,8 @@ describe("FetchHttpApi", () => { it("should not log query parameters", async () => { jest.useFakeTimers(); - const deferred = defer(); - const fetchFn = jest.fn().mockReturnValue(deferred.promise); + const responseResolvers = Promise.withResolvers(); + const fetchFn = jest.fn().mockReturnValue(responseResolvers.promise); const mockLogger = { debug: jest.fn(), } as unknown as Mocked; @@ -538,7 +538,7 @@ describe("FetchHttpApi", () => { }); const prom = api.requestOtherUrl(Method.Get, "https://server:8448/some/path?query=param#fragment"); jest.advanceTimersByTime(1234); - deferred.resolve({ ok: true, status: 200, text: () => Promise.resolve("RESPONSE") } as Response); + responseResolvers.resolve({ ok: true, status: 200, text: () => Promise.resolve("RESPONSE") } as Response); await prom; expect(mockLogger.debug).not.toHaveBeenCalledWith("fragment"); expect(mockLogger.debug).not.toHaveBeenCalledWith("query"); @@ -557,7 +557,7 @@ describe("FetchHttpApi", () => { }); it("should not make multiple concurrent refresh token requests", async () => { - const deferredTokenRefresh = defer<{ accessToken: string; refreshToken: string }>(); + const deferredTokenRefresh = Promise.withResolvers<{ accessToken: string; refreshToken: string }>(); const fetchFn = jest.fn().mockResolvedValue({ ok: false, status: tokenInactiveError.httpStatus, @@ -612,7 +612,7 @@ describe("FetchHttpApi", () => { }); it("should use newly refreshed token if request starts mid-refresh", async () => { - const deferredTokenRefresh = defer<{ accessToken: string; refreshToken: string }>(); + const deferredTokenRefresh = Promise.withResolvers<{ accessToken: string; refreshToken: string }>(); const fetchFn = jest.fn().mockResolvedValue({ ok: false, status: tokenInactiveError.httpStatus, diff --git a/spec/unit/matrix-client.spec.ts b/spec/unit/matrix-client.spec.ts index 671924975..f5299a346 100644 --- a/spec/unit/matrix-client.spec.ts +++ b/spec/unit/matrix-client.spec.ts @@ -73,7 +73,7 @@ import { PolicyRecommendation, PolicyScope, } from "../../src/models/invites-ignorer"; -import { defer, type QueryDict } from "../../src/utils"; +import { type QueryDict } from "../../src/utils"; import { type SyncState } from "../../src/sync"; import * as featureUtils from "../../src/feature"; import { StubStore } from "../../src/store/stub"; @@ -1997,8 +1997,8 @@ describe("MatrixClient", function () { }); it("should cancel an event which is encrypting", async () => { - const encryptEventDefer = defer(); - mockCrypto.encryptEvent.mockReturnValue(encryptEventDefer.promise); + const encryptEventResolvers = Promise.withResolvers(); + mockCrypto.encryptEvent.mockReturnValue(encryptEventResolvers.promise); const statusPromise = testUtils.emitPromise(event, "Event.status"); // @ts-ignore protected method access @@ -2009,7 +2009,7 @@ describe("MatrixClient", function () { assertCancelled(); // now let the encryption complete, and check that the message is not sent. - encryptEventDefer.resolve(); + encryptEventResolvers.resolve(); await encryptAndSendPromise; assertCancelled(); }); diff --git a/spec/unit/matrixrtc/MembershipManager.spec.ts b/spec/unit/matrixrtc/MembershipManager.spec.ts index f0173506c..afc339f41 100644 --- a/spec/unit/matrixrtc/MembershipManager.spec.ts +++ b/spec/unit/matrixrtc/MembershipManager.spec.ts @@ -30,7 +30,6 @@ import { import { LegacyMembershipManager } from "../../../src/matrixrtc/LegacyMembershipManager"; import { makeMockClient, makeMockRoom, membershipTemplate, mockCallMembership, type MockClient } from "./mocks"; import { MembershipManager } from "../../../src/matrixrtc/NewMembershipManager"; -import { defer } from "../../../src/utils"; import { logger } from "../../../src/logger.ts"; function waitForMockCall(method: MockedFunction, returnVal?: Promise) { @@ -51,7 +50,7 @@ function waitForMockCallOnce(method: MockedFunction, returnVal?: Promise) { - const { reject, resolve, promise } = defer(); + const { reject, resolve, promise } = Promise.withResolvers(); method.mockImplementation(() => promise); return { reject, resolve }; } diff --git a/spec/unit/matrixrtc/ToDeviceKeyTransport.spec.ts b/spec/unit/matrixrtc/ToDeviceKeyTransport.spec.ts index e120eeb88..80ff7fc4e 100644 --- a/spec/unit/matrixrtc/ToDeviceKeyTransport.spec.ts +++ b/spec/unit/matrixrtc/ToDeviceKeyTransport.spec.ts @@ -22,7 +22,6 @@ import { ToDeviceKeyTransport } from "../../../src/matrixrtc/ToDeviceKeyTranspor import { getMockClientWithEventEmitter } from "../../test-utils/client.ts"; import { type Statistics } from "../../../src/matrixrtc"; import { KeyTransportEvents } from "../../../src/matrixrtc/IKeyTransport.ts"; -import { defer } from "../../../src/utils.ts"; import { type Logger } from "../../../src/logger.ts"; describe("ToDeviceKeyTransport", () => { @@ -108,9 +107,14 @@ describe("ToDeviceKeyTransport", () => { }); it("should emit when a key is received", async () => { - const deferred = defer<{ userId: string; deviceId: string; keyBase64Encoded: string; index: number }>(); + const receivedKeyResolvers = Promise.withResolvers<{ + userId: string; + deviceId: string; + keyBase64Encoded: string; + index: number; + }>(); transport.on(KeyTransportEvents.ReceivedKeys, (userId, deviceId, keyBase64Encoded, index, timestamp) => { - deferred.resolve({ userId, deviceId, keyBase64Encoded, index }); + receivedKeyResolvers.resolve({ userId, deviceId, keyBase64Encoded, index }); }); transport.start(); @@ -136,7 +140,7 @@ describe("ToDeviceKeyTransport", () => { }), ); - const { userId, deviceId, keyBase64Encoded, index } = await deferred.promise; + const { userId, deviceId, keyBase64Encoded, index } = await receivedKeyResolvers.promise; expect(userId).toBe("@bob:example.org"); expect(deviceId).toBe("BOBDEVICE"); expect(keyBase64Encoded).toBe(testEncoded); diff --git a/spec/unit/room-state.spec.ts b/spec/unit/room-state.spec.ts index ab8ecda9a..da3f5de6c 100644 --- a/spec/unit/room-state.spec.ts +++ b/spec/unit/room-state.spec.ts @@ -25,7 +25,6 @@ import { EventType, RelationType, UNSTABLE_MSC2716_MARKER } from "../../src/@typ import { MatrixEvent, MatrixEventEvent } from "../../src/models/event"; import { M_BEACON } from "../../src/@types/beacon"; import { type MatrixClient } from "../../src/client"; -import { defer } from "../../src/utils"; import { Room } from "../../src/models/room"; import { KnownMembership } from "../../src/@types/membership"; import { DecryptionFailureCode } from "../../src/crypto-api"; @@ -1084,8 +1083,8 @@ describe("RoomState", function () { timestamp: Date.now() + 1, }); - const deferred = defer(); - mockClient.decryptEventIfNeeded.mockReturnValue(deferred.promise); + const decryptEventResolvers = Promise.withResolvers(); + mockClient.decryptEventIfNeeded.mockReturnValue(decryptEventResolvers.promise); state.setStateEvents([beacon1, beacon2]); const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1)) as Beacon; @@ -1095,7 +1094,7 @@ describe("RoomState", function () { // update type after '''decryption''' decryptingRelatedEvent.event.type = M_BEACON.name; decryptingRelatedEvent.event.content = locationEvent.event.content; - deferred.resolve(); + decryptEventResolvers.resolve(); await prom; expect(addLocationsSpy).toHaveBeenCalledWith([decryptingRelatedEvent]); diff --git a/spec/unit/rust-crypto/OutgoingRequestProcessor.spec.ts b/spec/unit/rust-crypto/OutgoingRequestProcessor.spec.ts index e278dfbd8..77a901b45 100644 --- a/spec/unit/rust-crypto/OutgoingRequestProcessor.spec.ts +++ b/spec/unit/rust-crypto/OutgoingRequestProcessor.spec.ts @@ -40,7 +40,6 @@ import { type UIAuthCallback, } from "../../../src"; import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor"; -import { defer } from "../../../src/utils"; describe("OutgoingRequestProcessor", () => { /** the OutgoingRequestProcessor implementation under test */ @@ -285,13 +284,13 @@ describe("OutgoingRequestProcessor", () => { new RustSdkCryptoJs.DeviceId("TEST_DEVICE"), ); - const authRequestResultDefer = defer(); + const authRequestResultResolvers = Promise.withResolvers(); const authRequestCalledPromise = new Promise((resolve) => { const mockHttpApi = { authedRequest: async () => { resolve(); - return await authRequestResultDefer.promise; + return await authRequestResultResolvers.promise; }, } as unknown as Mocked>; processor = new OutgoingRequestProcessor(olmMachine, mockHttpApi); @@ -308,7 +307,7 @@ describe("OutgoingRequestProcessor", () => { olmMachine.close(); // the HTTP request completes... - authRequestResultDefer.resolve("{}"); + authRequestResultResolvers.resolve("{}"); // ... and `makeOutgoingRequest` resolves satisfactorily await result; diff --git a/spec/unit/rust-crypto/OutgoingRequestsManager.spec.ts b/spec/unit/rust-crypto/OutgoingRequestsManager.spec.ts index 5772efbc2..429df62b8 100644 --- a/spec/unit/rust-crypto/OutgoingRequestsManager.spec.ts +++ b/spec/unit/rust-crypto/OutgoingRequestsManager.spec.ts @@ -20,7 +20,6 @@ import { type OutgoingRequest } from "@matrix-org/matrix-sdk-crypto-wasm"; import { type OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor"; import { OutgoingRequestsManager } from "../../../src/rust-crypto/OutgoingRequestsManager"; -import { defer, type IDeferred } from "../../../src/utils"; import { logger } from "../../../src/logger"; describe("OutgoingRequestsManager", () => { @@ -70,11 +69,11 @@ describe("OutgoingRequestsManager", () => { const request2 = new RustSdkCryptoJs.KeysUploadRequest("foo2", "{}"); const request3 = new RustSdkCryptoJs.KeysBackupRequest("foo3", "{}", "1"); - const firstOutgoingRequestDefer = defer(); + const firstOutgoingRequestResolvers = Promise.withResolvers(); olmMachine.outgoingRequests .mockImplementationOnce(async () => { - return firstOutgoingRequestDefer.promise; + return firstOutgoingRequestResolvers.promise; }) .mockImplementationOnce(async () => { return [request3]; @@ -87,7 +86,7 @@ describe("OutgoingRequestsManager", () => { const thirdRequest = manager.doProcessOutgoingRequests(); // let the first request complete - firstOutgoingRequestDefer.resolve([request1, request2]); + firstOutgoingRequestResolvers.resolve([request1, request2]); await firstRequest; await secondRequest; @@ -112,10 +111,10 @@ describe("OutgoingRequestsManager", () => { const outgoingRequestCalledPromises: Promise[] = []; // deferreds which will provide the results of OlmMachine.outgoingRequests - const outgoingRequestResultDeferreds: IDeferred[] = []; + const outgoingRequestResultDeferreds: PromiseWithResolvers[] = []; for (let i = 0; i < 3; i++) { - const resultDeferred = defer(); + const resultDeferred = Promise.withResolvers(); const calledPromise = new Promise((resolve) => { olmMachine.outgoingRequests.mockImplementationOnce(() => { resolve(); @@ -187,10 +186,10 @@ describe("OutgoingRequestsManager", () => { it("When the manager is stopped after outgoingRequests() call, do not make sever requests", async () => { const request1 = new RustSdkCryptoJs.KeysQueryRequest("foo", "{}"); - const firstOutgoingRequestDefer = defer(); + const firstOutgoingRequestResolvers = Promise.withResolvers(); olmMachine.outgoingRequests.mockImplementationOnce(async (): Promise => { - return firstOutgoingRequestDefer.promise; + return firstOutgoingRequestResolvers.promise; }); const firstRequest = manager.doProcessOutgoingRequests(); @@ -199,7 +198,7 @@ describe("OutgoingRequestsManager", () => { manager.stop(); // let the first request complete - firstOutgoingRequestDefer.resolve([request1]); + firstOutgoingRequestResolvers.resolve([request1]); await firstRequest; @@ -210,7 +209,7 @@ describe("OutgoingRequestsManager", () => { const request1 = new RustSdkCryptoJs.KeysQueryRequest("11", "{}"); const request2 = new RustSdkCryptoJs.KeysUploadRequest("12", "{}"); - const firstRequestDefer = defer(); + const firstRequestResolvers = Promise.withResolvers(); olmMachine.outgoingRequests.mockImplementationOnce(async (): Promise => { return [request1, request2]; @@ -219,7 +218,7 @@ describe("OutgoingRequestsManager", () => { processor.makeOutgoingRequest .mockImplementationOnce(async () => { manager.stop(); - return firstRequestDefer.promise; + return firstRequestResolvers.promise; }) .mockImplementationOnce(async () => { return; @@ -227,7 +226,7 @@ describe("OutgoingRequestsManager", () => { const firstRequest = manager.doProcessOutgoingRequests(); - firstRequestDefer.resolve(); + firstRequestResolvers.resolve(); await firstRequest; diff --git a/spec/unit/rust-crypto/PerSessionKeyBackupDownloader.spec.ts b/spec/unit/rust-crypto/PerSessionKeyBackupDownloader.spec.ts index 43cf68798..2691b95e4 100644 --- a/spec/unit/rust-crypto/PerSessionKeyBackupDownloader.spec.ts +++ b/spec/unit/rust-crypto/PerSessionKeyBackupDownloader.spec.ts @@ -21,7 +21,6 @@ import fetchMock from "fetch-mock-jest"; import { PerSessionKeyBackupDownloader } from "../../../src/rust-crypto/PerSessionKeyBackupDownloader"; import { logger } from "../../../src/logger"; -import { defer, type IDeferred } from "../../../src/utils"; import { type RustBackupCryptoEventMap, type RustBackupCryptoEvents, @@ -57,15 +56,15 @@ describe("PerSessionKeyBackupDownloader", () => { let mockOlmMachine: Mocked; let mockBackupDecryptor: Mocked; - let expectedSession: { [roomId: string]: { [sessionId: string]: IDeferred } }; + let expectedSession: { [roomId: string]: { [sessionId: string]: PromiseWithResolvers } }; function expectSessionImported(roomId: string, sessionId: string) { - const deferred = defer(); + const sessionImportedResolvers = Promise.withResolvers(); if (!expectedSession[roomId]) { expectedSession[roomId] = {}; } - expectedSession[roomId][sessionId] = deferred; - return deferred.promise; + expectedSession[roomId][sessionId] = sessionImportedResolvers; + return sessionImportedResolvers.promise; } function mockClearSession(sessionId: string): Mocked { @@ -115,9 +114,9 @@ describe("PerSessionKeyBackupDownloader", () => { mockRustBackupManager.importBackedUpRoomKeys.mockImplementation(async (keys) => { const roomId = keys[0].room_id; const sessionId = keys[0].session_id; - const deferred = expectedSession[roomId] && expectedSession[roomId][sessionId]; - if (deferred) { - deferred.resolve(); + const sessionImportedResolvers = expectedSession[roomId] && expectedSession[roomId][sessionId]; + if (sessionImportedResolvers) { + sessionImportedResolvers.resolve(); } }); @@ -143,7 +142,7 @@ describe("PerSessionKeyBackupDownloader", () => { }); it("Should download and import a missing key from backup", async () => { - const awaitKeyImported = defer(); + const awaitKeyImported = Promise.withResolvers(); const roomId = "!roomId"; const sessionId = "sessionId"; const expectAPICall = new Promise((resolve) => { @@ -168,14 +167,14 @@ describe("PerSessionKeyBackupDownloader", () => { }); it("Should not hammer the backup if the key is requested repeatedly", async () => { - const blockOnServerRequest = defer(); + const blockOnServerRequest = Promise.withResolvers(); fetchMock.get(`express:/_matrix/client/v3/room_keys/keys/!roomId/:session_id`, async (url, request) => { await blockOnServerRequest.promise; return [mockCipherKey]; }); - const awaitKey2Imported = defer(); + const awaitKey2Imported = Promise.withResolvers(); mockRustBackupManager.importBackedUpRoomKeys.mockImplementation(async (keys) => { if (keys[0].session_id === "sessionId2") { @@ -267,8 +266,8 @@ describe("PerSessionKeyBackupDownloader", () => { it("Should stop properly", async () => { // Simulate a call to stop while request is in flight - const blockOnServerRequest = defer(); - const requestRoomKeyCalled = defer(); + const blockOnServerRequest = Promise.withResolvers(); + const requestRoomKeyCalled = Promise.withResolvers(); // Mock the request to block fetchMock.get(`express:/_matrix/client/v3/room_keys/keys/:roomId/:sessionId`, async (url, request) => { @@ -490,7 +489,7 @@ describe("PerSessionKeyBackupDownloader", () => { // @ts-ignore access to private function const keyQuerySpy: SpyInstance = jest.spyOn(downloader, "queryKeyBackup"); - const rateDeferred = defer(); + const rateDeferred = Promise.withResolvers(); keyQuerySpy.mockImplementation( // @ts-ignore @@ -544,7 +543,7 @@ describe("PerSessionKeyBackupDownloader", () => { // @ts-ignore const keyQuerySpy: SpyInstance = jest.spyOn(downloader, "queryKeyBackup"); - const errorDeferred = defer(); + const errorDeferred = Promise.withResolvers(); keyQuerySpy.mockImplementation( // @ts-ignore @@ -588,7 +587,7 @@ describe("PerSessionKeyBackupDownloader", () => { }); it("On Unknown error on import skip the key and continue", async () => { - const keyImported = defer(); + const keyImported = Promise.withResolvers(); mockRustBackupManager.importBackedUpRoomKeys .mockImplementationOnce(async () => { throw new Error("Didn't work"); diff --git a/spec/unit/rust-crypto/RoomEncryptor.spec.ts b/spec/unit/rust-crypto/RoomEncryptor.spec.ts index b7263ac45..83993e6aa 100644 --- a/spec/unit/rust-crypto/RoomEncryptor.spec.ts +++ b/spec/unit/rust-crypto/RoomEncryptor.spec.ts @@ -29,7 +29,6 @@ import { type Mocked } from "jest-mock"; import { HistoryVisibility, type MatrixEvent, type Room, type RoomMember } from "../../../src"; import { RoomEncryptor, toRustHistoryVisibility } from "../../../src/rust-crypto/RoomEncryptor"; import { type KeyClaimManager } from "../../../src/rust-crypto/KeyClaimManager"; -import { defer } from "../../../src/utils"; import { type OutgoingRequestsManager } from "../../../src/rust-crypto/OutgoingRequestsManager"; import { KnownMembership } from "../../../src/@types/membership"; import { @@ -120,8 +119,8 @@ describe("RoomEncryptor", () => { const defaultDevicesIsolationMode = new AllDevicesIsolationMode(false); it("should ensure that there is only one shareRoomKey at a time", async () => { - const deferredShare = defer(); - const insideOlmShareRoom = defer(); + const deferredShare = Promise.withResolvers(); + const insideOlmShareRoom = Promise.withResolvers(); mockOlmMachine.shareRoomKey.mockImplementationOnce(async () => { insideOlmShareRoom.resolve(); @@ -149,8 +148,8 @@ describe("RoomEncryptor", () => { // Regression test for https://github.com/element-hq/element-web/issues/26684 it("Should maintain order of encryption requests", async () => { - const firstTargetMembers = defer(); - const secondTargetMembers = defer(); + const firstTargetMembers = Promise.withResolvers(); + const secondTargetMembers = Promise.withResolvers(); mockOlmMachine.shareRoomKey.mockResolvedValue([]); diff --git a/spec/unit/rust-crypto/rust-crypto.spec.ts b/spec/unit/rust-crypto/rust-crypto.spec.ts index 987de13ea..65804d7a1 100644 --- a/spec/unit/rust-crypto/rust-crypto.spec.ts +++ b/spec/unit/rust-crypto/rust-crypto.spec.ts @@ -68,7 +68,6 @@ import { import * as testData from "../../test-utils/test-data"; import { E2EKeyReceiver } from "../../test-utils/E2EKeyReceiver"; import { E2EKeyResponder } from "../../test-utils/E2EKeyResponder"; -import { defer } from "../../../src/utils"; import { logger } from "../../../src/logger"; import { OutgoingRequestsManager } from "../../../src/rust-crypto/OutgoingRequestsManager"; import { ClientEvent, type ClientEventHandlerMap } from "../../../src/client"; @@ -928,8 +927,8 @@ describe("RustCrypto", () => { it("should go round the loop again if another sync completes while the first `outgoingRequests` is running", async () => { // the first call to `outgoingMessages` will return a promise which blocks for a while - const firstOutgoingRequestsDefer = defer>(); - mocked(olmMachine.outgoingRequests).mockReturnValueOnce(firstOutgoingRequestsDefer.promise); + const firstOutgoingRequestsResolvers = Promise.withResolvers>(); + mocked(olmMachine.outgoingRequests).mockReturnValueOnce(firstOutgoingRequestsResolvers.promise); // the second will return a KeysQueryRequest. const testReq = new KeysQueryRequest("1234", "{}"); @@ -946,7 +945,7 @@ describe("RustCrypto", () => { // the first call now completes, *with an empty result*, which would normally cause us to exit the loop, but // we should have a second call queued. It should trigger a call to `makeOutgoingRequest`. - firstOutgoingRequestsDefer.resolve([]); + firstOutgoingRequestsResolvers.resolve([]); await awaitCallToMakeOutgoingRequest(); expect(olmMachine.outgoingRequests).toHaveBeenCalledTimes(2); }); diff --git a/spec/unit/scheduler.spec.ts b/spec/unit/scheduler.spec.ts index 3972a47a9..0cb16df1e 100644 --- a/spec/unit/scheduler.spec.ts +++ b/spec/unit/scheduler.spec.ts @@ -1,7 +1,6 @@ // This file had a function whose name is all caps, which displeases eslint /* eslint new-cap: "off" */ -import { defer, type IDeferred } from "../../src/utils"; import { MatrixError } from "../../src/http-api"; import { MatrixScheduler } from "../../src/scheduler"; import * as utils from "../test-utils/test-utils"; @@ -14,7 +13,7 @@ describe("MatrixScheduler", function () { let scheduler: MatrixScheduler>; let retryFn: ((event: MatrixEvent | null, attempt: number, err: MatrixError) => number) | null; let queueFn: ((event: MatrixEvent) => string | null) | null; - let deferred: IDeferred>; + let deferred: PromiseWithResolvers>; const roomId = "!foo:bar"; const eventA = utils.mkMessage({ user: "@alice:bar", @@ -44,7 +43,7 @@ describe("MatrixScheduler", function () { ); retryFn = null; queueFn = null; - deferred = defer(); + deferred = Promise.withResolvers(); }); it("should process events in a queue in a FIFO manner", async function () { @@ -54,8 +53,8 @@ describe("MatrixScheduler", function () { queueFn = function () { return "one_big_queue"; }; - const deferA = defer>(); - const deferB = defer>(); + const deferA = Promise.withResolvers>(); + const deferB = Promise.withResolvers>(); let yieldedA = false; scheduler.setProcessFunction(function (event) { if (yieldedA) { @@ -79,9 +78,9 @@ describe("MatrixScheduler", function () { it("should invoke the retryFn on failure and wait the amount of time specified", async function () { const waitTimeMs = 1500; - const retryDefer = defer(); + const retryResolvers = Promise.withResolvers(); retryFn = function () { - retryDefer.resolve(); + retryResolvers.resolve(); return waitTimeMs; }; queueFn = function () { @@ -109,7 +108,7 @@ describe("MatrixScheduler", function () { await Promise.resolve(); expect(procCount).toEqual(1); deferred.reject({}); - await retryDefer.promise; + await retryResolvers.promise; expect(procCount).toEqual(1); jest.advanceTimersByTime(waitTimeMs); await Promise.resolve(); @@ -127,8 +126,8 @@ describe("MatrixScheduler", function () { return "yep"; }; - const deferA = defer>(); - const deferB = defer>(); + const deferA = Promise.withResolvers>(); + const deferB = Promise.withResolvers>(); let procCount = 0; scheduler.setProcessFunction(function (ev) { procCount += 1; @@ -180,7 +179,7 @@ describe("MatrixScheduler", function () { }; const expectOrder = [eventA.getId(), eventB.getId(), eventD.getId()]; - const deferA = defer>(); + const deferA = Promise.withResolvers>(); const allExpectedEventsSeenInOrderPromise = new Promise((resolve) => { scheduler.setProcessFunction(function (event) { const id = expectOrder.shift(); diff --git a/spec/unit/secret-storage.spec.ts b/spec/unit/secret-storage.spec.ts index 13094bf10..c8780cb09 100644 --- a/spec/unit/secret-storage.spec.ts +++ b/spec/unit/secret-storage.spec.ts @@ -30,7 +30,6 @@ import { import { secureRandomString } from "../../src/randomstring"; import { type SecretInfo } from "../../src/secret-storage.ts"; import { type AccountDataEvents, ClientEvent, MatrixEvent, TypedEventEmitter } from "../../src"; -import { defer, type IDeferred } from "../../src/utils"; declare module "../../src/@types/event" { interface SecretStorageAccountDataEvents { @@ -289,10 +288,10 @@ describe("ServerSideSecretStorageImpl", function () { describe("setDefaultKeyId", function () { let secretStorage: ServerSideSecretStorage; let accountDataAdapter: Mocked; - let accountDataPromise: IDeferred; + let accountDataPromise: PromiseWithResolvers; beforeEach(() => { accountDataAdapter = mockAccountDataClient(); - accountDataPromise = defer(); + accountDataPromise = Promise.withResolvers(); accountDataAdapter.setAccountData.mockImplementation(() => { accountDataPromise.resolve(); return Promise.resolve({}); diff --git a/spec/unit/stores/indexeddb-store-worker.spec.ts b/spec/unit/stores/indexeddb-store-worker.spec.ts index af0b345fc..7a683f874 100644 --- a/spec/unit/stores/indexeddb-store-worker.spec.ts +++ b/spec/unit/stores/indexeddb-store-worker.spec.ts @@ -18,7 +18,6 @@ import "fake-indexeddb/auto"; import { type LocalIndexedDBStoreBackend } from "../../../src/store/indexeddb-local-backend"; import { IndexedDBStoreWorker } from "../../../src/store/indexeddb-store-worker"; -import { defer } from "../../../src/utils"; function setupWorker(worker: IndexedDBStoreWorker): void { worker.onMessage({ data: { command: "setupWorker", args: [] } } as any); @@ -27,16 +26,16 @@ function setupWorker(worker: IndexedDBStoreWorker): void { describe("IndexedDBStore Worker", () => { it("should pass 'closed' event via postMessage", async () => { - const deferred = defer(); + const postMessageSuccessResolvers = Promise.withResolvers(); const postMessage = jest.fn().mockImplementation(({ seq, command }) => { if (seq === 1 && command === "cmd_success") { - deferred.resolve(); + postMessageSuccessResolvers.resolve(); } }); const worker = new IndexedDBStoreWorker(postMessage); setupWorker(worker); - await deferred.promise; + await postMessageSuccessResolvers.promise; // @ts-ignore - private field access (worker.backend as LocalIndexedDBStoreBackend).db!.onclose!({} as Event); diff --git a/spec/unit/stores/indexeddb.spec.ts b/spec/unit/stores/indexeddb.spec.ts index 7d0108fb7..2672334e4 100644 --- a/spec/unit/stores/indexeddb.spec.ts +++ b/spec/unit/stores/indexeddb.spec.ts @@ -21,7 +21,6 @@ import { IDBFactory } from "fake-indexeddb"; import { IndexedDBStore, type IStateEventWithRoomId, MemoryStore, User, UserEvent } from "../../../src"; import { emitPromise } from "../../test-utils/test-utils"; import { type LocalIndexedDBStoreBackend } from "../../../src/store/indexeddb-local-backend"; -import { defer } from "../../../src/utils"; describe("IndexedDBStore", () => { afterEach(() => { @@ -80,7 +79,7 @@ describe("IndexedDBStore", () => { it("Should load presence events on startup", async () => { // 1. Create idb database const indexedDB = new IDBFactory(); - const setupDefer = defer(); + const setupResolvers = Promise.withResolvers(); const req = indexedDB.open("matrix-js-sdk:db3", 1); let db: IDBDatabase; req.onupgradeneeded = () => { @@ -89,11 +88,11 @@ describe("IndexedDBStore", () => { db.createObjectStore("accountData", { keyPath: ["type"] }); db.createObjectStore("sync", { keyPath: ["clobber"] }); }; - req.onsuccess = setupDefer.resolve; - await setupDefer.promise; + req.onsuccess = setupResolvers.resolve; + await setupResolvers.promise; // 2. Fill in user presence data - const writeDefer = defer(); + const writeResolvers = Promise.withResolvers(); const transaction = db!.transaction(["users"], "readwrite"); const objectStore = transaction.objectStore("users"); const request = objectStore.put({ @@ -106,8 +105,8 @@ describe("IndexedDBStore", () => { type: "m.presence", }, }); - request.onsuccess = writeDefer.resolve; - await writeDefer.promise; + request.onsuccess = writeResolvers.resolve; + await writeResolvers.promise; // 3. Close database req.result.close(); @@ -201,7 +200,7 @@ describe("IndexedDBStore", () => { }); it("should resolve isNewlyCreated to false if database existed already but needs upgrade", async () => { - const deferred = defer(); + const requestSuccessResolvers = Promise.withResolvers(); // seed db3 to Version 1 so it forces a migration const req = indexedDB.open("matrix-js-sdk:db3", 1); req.onupgradeneeded = () => { @@ -210,8 +209,8 @@ describe("IndexedDBStore", () => { db.createObjectStore("accountData", { keyPath: ["type"] }); db.createObjectStore("sync", { keyPath: ["clobber"] }); }; - req.onsuccess = deferred.resolve; - await deferred.promise; + req.onsuccess = requestSuccessResolvers.resolve; + await requestSuccessResolvers.promise; req.result.close(); const store = new IndexedDBStore({ @@ -232,20 +231,20 @@ describe("IndexedDBStore", () => { }); await store.startup(); - const deferred = defer(); - store.on("closed", deferred.resolve); + const storeClosedResolvers = Promise.withResolvers(); + store.on("closed", storeClosedResolvers.resolve); // @ts-ignore - private field access (store.backend as LocalIndexedDBStoreBackend).db!.onclose!({} as Event); - await deferred.promise; + await storeClosedResolvers.promise; }); it("should use remote backend if workerFactory passed", async () => { - const deferred = defer(); + const workerPostMessageResolvers = Promise.withResolvers(); class MockWorker { postMessage(data: any) { if (data.command === "setupWorker") { - deferred.resolve(); + workerPostMessageResolvers.resolve(); } } } @@ -257,7 +256,7 @@ describe("IndexedDBStore", () => { workerFactory: () => new MockWorker() as Worker, }); store.startup(); - await deferred.promise; + await workerPostMessageResolvers.promise; }); it("remote worker should pass closed event", async () => { @@ -273,10 +272,10 @@ describe("IndexedDBStore", () => { }); store.startup(); - const deferred = defer(); - store.on("closed", deferred.resolve); + const storeClosedResolvers = Promise.withResolvers(); + store.on("closed", storeClosedResolvers.resolve); (worker as any).onmessage({ data: { command: "closed" } }); - await deferred.promise; + await storeClosedResolvers.promise; }); it("remote worker should pass command failures", async () => { diff --git a/src/client.ts b/src/client.ts index 6974f35fa..74e86ecab 100644 --- a/src/client.ts +++ b/src/client.ts @@ -52,7 +52,7 @@ import { type GroupCallEventHandlerEventHandlerMap, } from "./webrtc/groupCallEventHandler.ts"; import * as utils from "./utils.ts"; -import { deepCompare, defer, noUnsafeEventProps, type QueryDict, replaceParam, safeSet, sleep } from "./utils.ts"; +import { deepCompare, noUnsafeEventProps, type QueryDict, replaceParam, safeSet, sleep } from "./utils.ts"; import { Direction, EventTimeline } from "./models/event-timeline.ts"; import { type IActionsObject, PushProcessor } from "./pushprocessor.ts"; import { AutoDiscovery, type AutoDiscoveryAction } from "./autodiscovery.ts"; @@ -2191,7 +2191,7 @@ export class MatrixClient extends TypedEventEmitter(); + const updatedResolvers = Promise.withResolvers(); function accountDataListener(event: MatrixEvent): void { // Note that we cannot safely check that the content matches what we expected, because there is a race: // * We set the new content @@ -2203,13 +2203,13 @@ export class MatrixClient extends TypedEventEmitter this.setAccountDataRaw(eventType, content)); - await updatedDefer.promise; + await updatedResolvers.promise; return result; } finally { this.removeListener(ClientEvent.AccountData, accountDataListener); @@ -5173,24 +5173,24 @@ export class MatrixClient extends TypedEventEmitter(); this.deletePushRule(scope, PushRuleKind.RoomSpecific, roomPushRule.rule_id) .then(() => { this.addPushRule(scope, PushRuleKind.RoomSpecific, roomId, { actions: [PushRuleActionName.DontNotify], }) .then(() => { - deferred.resolve(); + doneResolvers.resolve(); }) .catch((err) => { - deferred.reject(err); + doneResolvers.reject(err); }); }) .catch((err) => { - deferred.reject(err); + doneResolvers.reject(err); }); - promise = deferred.promise; + promise = doneResolvers.promise; } } diff --git a/src/http-api/index.ts b/src/http-api/index.ts index cd5db0a86..1474cdde9 100644 --- a/src/http-api/index.ts +++ b/src/http-api/index.ts @@ -24,7 +24,7 @@ import { type UploadResponse, } from "./interface.ts"; import { MediaPrefix } from "./prefix.ts"; -import { defer, type QueryDict, removeElement } from "../utils.ts"; +import { type QueryDict, removeElement } from "../utils.ts"; import * as callbacks from "../realtime-callbacks.ts"; import { Method } from "./method.ts"; import { ConnectionError } from "./errors.ts"; @@ -65,14 +65,14 @@ export class MatrixHttpApi extends FetchHttpApi { total: 0, abortController, } as Upload; - const deferred = defer(); + const uploadResolvers = Promise.withResolvers(); if (globalThis.XMLHttpRequest) { const xhr = new globalThis.XMLHttpRequest(); const timeoutFn = function (): void { xhr.abort(); - deferred.reject(new Error("Timeout")); + uploadResolvers.reject(new Error("Timeout")); }; // set an initial timeout of 30s; we'll advance it each time we get a progress notification @@ -91,16 +91,16 @@ export class MatrixHttpApi extends FetchHttpApi { } if (xhr.status >= 400) { - deferred.reject(parseErrorResponse(xhr, xhr.responseText)); + uploadResolvers.reject(parseErrorResponse(xhr, xhr.responseText)); } else { - deferred.resolve(JSON.parse(xhr.responseText)); + uploadResolvers.resolve(JSON.parse(xhr.responseText)); } } catch (err) { if ((err).name === "AbortError") { - deferred.reject(err); + uploadResolvers.reject(err); return; } - deferred.reject(new ConnectionError("request failed", err)); + uploadResolvers.reject(new ConnectionError("request failed", err)); } break; } @@ -153,16 +153,16 @@ export class MatrixHttpApi extends FetchHttpApi { .then((response) => { return this.opts.onlyData ? response : response.json(); }) - .then(deferred.resolve, deferred.reject); + .then(uploadResolvers.resolve, uploadResolvers.reject); } // remove the upload from the list on completion - upload.promise = deferred.promise.finally(() => { + upload.promise = uploadResolvers.promise.finally(() => { removeElement(this.uploads, (elem) => elem === upload); }); abortController.signal.addEventListener("abort", () => { removeElement(this.uploads, (elem) => elem === upload); - deferred.reject(new DOMException("Aborted", "AbortError")); + uploadResolvers.reject(new DOMException("Aborted", "AbortError")); }); this.uploads.push(upload); return upload.promise; diff --git a/src/interactive-auth.ts b/src/interactive-auth.ts index 0e02d2903..74aafb6b3 100644 --- a/src/interactive-auth.ts +++ b/src/interactive-auth.ts @@ -18,7 +18,6 @@ limitations under the License. import { logger } from "./logger.ts"; import { type MatrixClient } from "./client.ts"; -import { defer, type IDeferred } from "./utils.ts"; import { MatrixError } from "./http-api/index.ts"; import { type UserIdentifier } from "./@types/auth.ts"; @@ -262,7 +261,7 @@ export class InteractiveAuth { private data: IAuthData & MatrixError["data"]; private emailSid?: string; private requestingEmailToken = false; - private attemptAuthDeferred: IDeferred | null = null; + private attemptAuthDeferred: PromiseWithResolvers | null = null; private chosenFlow: UIAFlow | null = null; private currentStage: string | null = null; @@ -298,7 +297,7 @@ export class InteractiveAuth { public async attemptAuth(): Promise { // This promise will be quite long-lived and will resolve when the // request is authenticated and completes successfully. - this.attemptAuthDeferred = defer(); + this.attemptAuthDeferred = Promise.withResolvers(); // pluck the promise out now, as doRequest may clear before we return const promise = this.attemptAuthDeferred.promise; diff --git a/src/matrixrtc/NewMembershipManager.ts b/src/matrixrtc/NewMembershipManager.ts index fad78e216..ddc755d53 100644 --- a/src/matrixrtc/NewMembershipManager.ts +++ b/src/matrixrtc/NewMembershipManager.ts @@ -169,7 +169,7 @@ export class MembershipManager } this.fociPreferred = fociPreferred; this.focusActive = focusActive; - this.leavePromiseDefer = undefined; + this.leavePromiseResolvers = undefined; this.activated = true; this.oldStatus = this.status; this.state = MembershipManager.defaultState; @@ -188,8 +188,8 @@ export class MembershipManager this.emit(MembershipManagerEvent.StatusChanged, this.oldStatus, this.status); } if (!this.scheduler.running) { - this.leavePromiseDefer?.resolve(true); - this.leavePromiseDefer = undefined; + this.leavePromiseResolvers?.resolve(true); + this.leavePromiseResolvers = undefined; } }); } @@ -207,16 +207,16 @@ export class MembershipManager // We use the promise to track if we already scheduled a leave event // So we do not check scheduler.actions/scheduler.insertions - if (!this.leavePromiseDefer) { + if (!this.leavePromiseResolvers) { // reset scheduled actions so we will not do any new actions. - this.leavePromiseDefer = defer(); + this.leavePromiseResolvers = defer(); this.activated = false; this.scheduler.initiateLeave(); - if (timeout) setTimeout(() => this.leavePromiseDefer?.resolve(false), timeout); + if (timeout) setTimeout(() => this.leavePromiseResolvers?.resolve(false), timeout); } - return this.leavePromiseDefer.promise; + return this.leavePromiseResolvers.promise; } - private leavePromiseDefer?: IDeferred; + private leavePromiseResolvers?: IDeferred; public async onRTCSessionMemberUpdate(memberships: CallMembership[]): Promise { const userId = this.client.getUserId(); diff --git a/src/rust-crypto/OutgoingRequestsManager.ts b/src/rust-crypto/OutgoingRequestsManager.ts index 0e64433b8..a2fdf37a3 100644 --- a/src/rust-crypto/OutgoingRequestsManager.ts +++ b/src/rust-crypto/OutgoingRequestsManager.ts @@ -18,7 +18,7 @@ import { type OlmMachine, type OutgoingRequest } from "@matrix-org/matrix-sdk-cr import { type OutgoingRequestProcessor } from "./OutgoingRequestProcessor.ts"; import { type Logger } from "../logger.ts"; -import { defer, type IDeferred, logDuration } from "../utils.ts"; +import { logDuration } from "../utils.ts"; /** * OutgoingRequestsManager: responsible for processing outgoing requests from the OlmMachine. @@ -39,7 +39,7 @@ export class OutgoingRequestsManager { * will resolve once that next iteration completes. If it is undefined, there have been no new calls * to `doProcessOutgoingRequests` since the current iteration started. */ - private nextLoopDeferred?: IDeferred; + private nextLoopDeferred?: PromiseWithResolvers; public constructor( private readonly logger: Logger, @@ -74,7 +74,7 @@ export class OutgoingRequestsManager { // In order to circumvent the race, we set a flag which tells the loop to go round once again even if the // queue appears to be empty. if (!this.nextLoopDeferred) { - this.nextLoopDeferred = defer(); + this.nextLoopDeferred = Promise.withResolvers(); } // ... and wait for it to complete. @@ -99,14 +99,14 @@ export class OutgoingRequestsManager { this.outgoingRequestLoopRunning = true; try { while (!this.stopped && this.nextLoopDeferred) { - const deferred = this.nextLoopDeferred; + const loopTickResolvers = this.nextLoopDeferred; // reset `nextLoopDeferred` so that any future calls to `doProcessOutgoingRequests` are queued // for another additional iteration. this.nextLoopDeferred = undefined; // make the requests and feed the results back to the `nextLoopDeferred` - await this.processOutgoingRequests().then(deferred.resolve, deferred.reject); + await this.processOutgoingRequests().then(loopTickResolvers.resolve, loopTickResolvers.reject); } } finally { this.outgoingRequestLoopRunning = false; diff --git a/src/rust-crypto/verification.ts b/src/rust-crypto/verification.ts index f2684d10d..fbe7cc750 100644 --- a/src/rust-crypto/verification.ts +++ b/src/rust-crypto/verification.ts @@ -34,7 +34,6 @@ import { type OutgoingRequestProcessor } from "./OutgoingRequestProcessor.ts"; import { TypedReEmitter } from "../ReEmitter.ts"; import { type MatrixEvent } from "../models/event.ts"; import { EventType, MsgType } from "../@types/event.ts"; -import { defer, type IDeferred } from "../utils.ts"; import { VerificationMethod } from "../types.ts"; import type { Logger } from "../logger.ts"; @@ -478,7 +477,7 @@ abstract class BaseRustVerifer { /** A deferred which completes when the verification completes (or rejects when it is cancelled/fails) */ - protected readonly completionDeferred: IDeferred; + protected readonly completionDeferred: PromiseWithResolvers; public constructor( protected inner: InnerType, @@ -486,7 +485,7 @@ abstract class BaseRustVerifer { event: MatrixEvent; - defer: IDeferred; + resolvers: PromiseWithResolvers; attempts: number; } @@ -70,7 +70,7 @@ export class MatrixScheduler { // queueName: [{ // event: MatrixEvent, // event to send - // defer: Deferred, // defer to resolve/reject at the END of the retries + // defer: PromiseWithResolvers, // defer to resolve/reject at the END of the retries // attempts: Number // number of times we've called processFn // }, ...] private readonly queues: Record[]> = {}; @@ -188,15 +188,15 @@ export class MatrixScheduler { if (!this.queues[queueName]) { this.queues[queueName] = []; } - const deferred = defer(); + const eventResolvers = Promise.withResolvers(); this.queues[queueName].push({ event: event, - defer: deferred, + resolvers: eventResolvers, attempts: 0, }); debuglog("Queue algorithm dumped event %s into queue '%s'", event.getId(), queueName); this.startProcessingQueues(); - return deferred.promise; + return eventResolvers.promise; } private startProcessingQueues(): void { @@ -239,7 +239,7 @@ export class MatrixScheduler { // remove this from the queue this.removeNextEvent(queueName); debuglog("Queue '%s' sent event %s", queueName, obj.event.getId()); - obj.defer.resolve(res); + obj.resolvers.resolve(res); // keep processing this.processQueue(queueName); }, @@ -279,7 +279,7 @@ export class MatrixScheduler { logger.info("clearing queue '%s'", queueName); let obj: IQueueEntry | undefined; while ((obj = this.removeNextEvent(queueName))) { - obj.defer.reject(err); + obj.resolvers.reject(err); } this.disableQueue(queueName); } diff --git a/src/store/indexeddb-remote-backend.ts b/src/store/indexeddb-remote-backend.ts index 26ec0613d..293953ff7 100644 --- a/src/store/indexeddb-remote-backend.ts +++ b/src/store/indexeddb-remote-backend.ts @@ -15,7 +15,6 @@ limitations under the License. */ import { logger } from "../logger.ts"; -import { defer, type IDeferred } from "../utils.ts"; import { type ISavedSync } from "./index.ts"; import { type IStoredClientOpts } from "../client.ts"; import { type IStateEventWithRoomId, type ISyncResponse } from "../matrix.ts"; @@ -26,7 +25,7 @@ export class RemoteIndexedDBStoreBackend implements IIndexedDBBackend { private worker?: Worker; private nextSeq = 0; // The currently in-flight requests to the actual backend - private inFlight: Record> = {}; // seq: promise + private inFlight: Record> = {}; // seq: promise // Once we start connecting, we keep the promise and re-use it // if we try to connect again private startPromise?: Promise; @@ -164,7 +163,7 @@ export class RemoteIndexedDBStoreBackend implements IIndexedDBBackend { // the promise automatically gets rejected return Promise.resolve().then(() => { const seq = this.nextSeq++; - const def = defer(); + const def = Promise.withResolvers(); this.inFlight[seq] = def; diff --git a/src/sync.ts b/src/sync.ts index 1419d1ccf..4c9a78fa6 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -28,7 +28,7 @@ import { type Optional } from "matrix-events-sdk"; import type { SyncCryptoCallbacks } from "./common-crypto/CryptoBackend.ts"; import { User } from "./models/user.ts"; import { NotificationCountType, Room, RoomEvent } from "./models/room.ts"; -import { deepCopy, defer, type IDeferred, noUnsafeEventProps, promiseMapSeries, unsafeProp } from "./utils.ts"; +import { deepCopy, noUnsafeEventProps, promiseMapSeries, unsafeProp } from "./utils.ts"; import { Filter } from "./filter.ts"; import { EventTimeline } from "./models/event-timeline.ts"; import { logger } from "./logger.ts"; @@ -220,7 +220,7 @@ export class SyncApi { private catchingUp = false; private running = false; private keepAliveTimer?: ReturnType; - private connectionReturnedDefer?: IDeferred; + private connectionReturnedResolvers?: PromiseWithResolvers; private notifEvents: MatrixEvent[] = []; // accumulator of sync events in the current sync response private failedSyncCount = 0; // Number of consecutive failed /sync requests private storeIsInvalid = false; // flag set if the store needs to be cleared before we can start @@ -792,7 +792,7 @@ export class SyncApi { * @returns True if this resulted in a request being retried. */ public retryImmediately(): boolean { - if (!this.connectionReturnedDefer) { + if (!this.connectionReturnedResolvers) { return false; } this.startKeepAlives(0); @@ -916,9 +916,9 @@ export class SyncApi { if (!this.running) { debuglog("Sync no longer running: exiting."); - if (this.connectionReturnedDefer) { - this.connectionReturnedDefer.reject(); - this.connectionReturnedDefer = undefined; + if (this.connectionReturnedResolvers) { + this.connectionReturnedResolvers.reject(); + this.connectionReturnedResolvers = undefined; } this.updateSyncState(SyncState.Stopped); } @@ -999,9 +999,9 @@ export class SyncApi { private async onSyncError(err: MatrixError): Promise { if (!this.running) { debuglog("Sync no longer running: exiting"); - if (this.connectionReturnedDefer) { - this.connectionReturnedDefer.reject(); - this.connectionReturnedDefer = undefined; + if (this.connectionReturnedResolvers) { + this.connectionReturnedResolvers.reject(); + this.connectionReturnedResolvers = undefined; } this.updateSyncState(SyncState.Stopped); return true; // abort @@ -1551,10 +1551,10 @@ export class SyncApi { } else { this.pokeKeepAlive(); } - if (!this.connectionReturnedDefer) { - this.connectionReturnedDefer = defer(); + if (!this.connectionReturnedResolvers) { + this.connectionReturnedResolvers = Promise.withResolvers(); } - return this.connectionReturnedDefer.promise; + return this.connectionReturnedResolvers.promise; } /** @@ -1562,7 +1562,7 @@ export class SyncApi { * reachable. * * On failure, schedules a call back to itself. On success, resolves - * this.connectionReturnedDefer. + * this.connectionReturnedResolvers. * * @param connDidFail - True if a connectivity failure has been detected. Optional. */ @@ -1571,18 +1571,18 @@ export class SyncApi { // we are in a keepAlive, retrying to connect, but the syncronization // was stopped, so we are stopping the retry. clearTimeout(this.keepAliveTimer); - if (this.connectionReturnedDefer) { - this.connectionReturnedDefer.reject("SyncApi.stop() was called"); - this.connectionReturnedDefer = undefined; + if (this.connectionReturnedResolvers) { + this.connectionReturnedResolvers.reject("SyncApi.stop() was called"); + this.connectionReturnedResolvers = undefined; } return; } const success = (): void => { clearTimeout(this.keepAliveTimer); - if (this.connectionReturnedDefer) { - this.connectionReturnedDefer.resolve(connDidFail); - this.connectionReturnedDefer = undefined; + if (this.connectionReturnedResolvers) { + this.connectionReturnedResolvers.resolve(connDidFail); + this.connectionReturnedResolvers = undefined; } }; diff --git a/src/utils.ts b/src/utils.ts index 9a822f92f..b5e178bc6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -436,23 +436,14 @@ export function isNullOrUndefined(val: any): boolean { return val === null || val === undefined; } -export interface IDeferred { - resolve: (value: T | Promise) => void; - reject: (reason?: any) => void; - promise: Promise; -} +export type IDeferred = PromiseWithResolvers; -// Returns a Deferred +/** + * Creates a deferred promise. This is a promise that can be resolved or rejected. + * @deprecated use {@link Promise.withResolvers} instead. + */ export function defer(): IDeferred { - let resolve!: IDeferred["resolve"]; - let reject!: IDeferred["reject"]; - - const promise = new Promise((_resolve, _reject) => { - resolve = _resolve; - reject = _reject; - }); - - return { resolve, reject, promise }; + return Promise.withResolvers(); } export async function promiseMapSeries(