1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-06 12:02:40 +03:00

Deprecate utils function defer in favour of Promise.withResolvers (#4829)

* Switch from defer to Promise.withResolvers

As supported by the outgoing LTS version (v22) which has 99% support of ES2024

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

* delint

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

* Deprecate defer instead of killing it

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

* Knip

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

* Iterate based on review

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

* Iterate based on review

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

* Iterate based on review

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

* Improve coverage

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2025-05-09 11:16:35 +01:00
committed by GitHub
parent d24c5d8b2b
commit 1fcc375dd5
32 changed files with 220 additions and 221 deletions

View File

@@ -9,6 +9,7 @@ export default {
"src/crypto-api/index.ts", "src/crypto-api/index.ts",
"src/testing.ts", "src/testing.ts",
"src/matrix.ts", "src/matrix.ts",
"src/utils.ts", // not really an entrypoint but we have deprecated `defer` there
"scripts/**", "scripts/**",
"spec/**", "spec/**",
// XXX: these look entirely unused // XXX: these look entirely unused

View File

@@ -59,7 +59,7 @@ import {
} from "../../../src/matrix"; } from "../../../src/matrix";
import { E2EKeyReceiver } from "../../test-utils/E2EKeyReceiver"; import { E2EKeyReceiver } from "../../test-utils/E2EKeyReceiver";
import { type ISyncResponder, SyncResponder } from "../../test-utils/SyncResponder"; 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 { downloadDeviceToJsDevice } from "../../../src/rust-crypto/device-converter";
import { flushPromises } from "../../test-utils/flushPromises"; import { flushPromises } from "../../test-utils/flushPromises";
import { import {
@@ -1283,13 +1283,13 @@ describe("crypto", () => {
const inboundGroupSessionPromise = expectSendRoomKey("@bob:xyz", testOlmAccount); const inboundGroupSessionPromise = expectSendRoomKey("@bob:xyz", testOlmAccount);
// ... and finally, send the room key. We block the response until `sendRoomMessageDefer` completes. // ... and finally, send the room key. We block the response until `sendRoomMessageDefer` completes.
const sendRoomMessageDefer = defer<FetchMock.MockResponse>(); const sendRoomMessageResolvers = Promise.withResolvers<FetchMock.MockResponse>();
const reqProm = new Promise<IContent>((resolve) => { const reqProm = new Promise<IContent>((resolve) => {
fetchMock.putOnce( fetchMock.putOnce(
new RegExp("/send/m.room.encrypted/"), new RegExp("/send/m.room.encrypted/"),
async (url: string, opts: RequestInit): Promise<FetchMock.MockResponse> => { async (url: string, opts: RequestInit): Promise<FetchMock.MockResponse> => {
resolve(JSON.parse(opts.body as string)); 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 // 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 // release the send request
const resp = { event_id: "$event_id" }; const resp = { event_id: "$event_id" };
sendRoomMessageDefer.resolve(resp); sendRoomMessageResolvers.resolve(resp);
expect(await sendProm).toEqual(resp); expect(await sendProm).toEqual(resp);
// still pending at this point // still pending at this point

View File

@@ -36,7 +36,6 @@ import { advanceTimersUntil, awaitDecryption, syncPromise } from "../../test-uti
import * as testData from "../../test-utils/test-data"; import * as testData from "../../test-utils/test-data";
import { type KeyBackupInfo, type KeyBackupSession } from "../../../src/crypto-api/keybackup"; import { type KeyBackupInfo, type KeyBackupSession } from "../../../src/crypto-api/keybackup";
import { flushPromises } from "../../test-utils/flushPromises"; import { flushPromises } from "../../test-utils/flushPromises";
import { defer, type IDeferred } from "../../../src/utils";
import { decodeRecoveryKey, DecryptionFailureCode, CryptoEvent, type CryptoApi } from "../../../src/crypto-api"; import { decodeRecoveryKey, DecryptionFailureCode, CryptoEvent, type CryptoApi } from "../../../src/crypto-api";
import { type KeyBackup } from "../../../src/rust-crypto/backup.ts"; 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); expect(await aliceCrypto.getKeyBackupInfo()).toStrictEqual(testData.SIGNED_BACKUP_DATA);
// Delete the backup and we are expecting the key backup to be disabled // Delete the backup and we are expecting the key backup to be disabled
const keyBackupStatus = defer<boolean>(); const keyBackupStatus = Promise.withResolvers<boolean>();
aliceClient.once(CryptoEvent.KeyBackupStatus, (enabled) => keyBackupStatus.resolve(enabled)); aliceClient.once(CryptoEvent.KeyBackupStatus, (enabled) => keyBackupStatus.resolve(enabled));
await aliceCrypto.deleteKeyBackupVersion(testData.SIGNED_BACKUP_DATA.version!); await aliceCrypto.deleteKeyBackupVersion(testData.SIGNED_BACKUP_DATA.version!);
expect(await keyBackupStatus.promise).toBe(false); expect(await keyBackupStatus.promise).toBe(false);
@@ -1158,7 +1157,7 @@ describe("megolm-keys backup", () => {
// A check backup should happen at some point // A check backup should happen at some point
await aliceCrypto.checkKeyBackupAndEnable(); await aliceCrypto.checkKeyBackupAndEnable();
const awaitHasQueriedNewBackup: IDeferred<void> = defer<void>(); const awaitHasQueriedNewBackup: PromiseWithResolvers<void> = Promise.withResolvers<void>();
fetchMock.get( fetchMock.get(
"express:/_matrix/client/v3/room_keys/keys/:room_id/:session_id", "express:/_matrix/client/v3/room_keys/keys/:room_id/:session_id",

View File

@@ -44,7 +44,7 @@ import {
type Verifier, type Verifier,
VerifierEvent, VerifierEvent,
} from "../../../src/crypto-api/verification"; } 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 { awaitDecryption, emitPromise, getSyncResponse, syncPromise } from "../../test-utils/test-utils";
import { SyncResponder } from "../../test-utils/SyncResponder"; import { SyncResponder } from "../../test-utils/SyncResponder";
import { 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. * @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<string, Promise<string>> { function mockSecretRequestAndGetPromises(): Map<string, Promise<string>> {
const mskRequestDefer = defer<string>(); const mskRequestResolvers = Promise.withResolvers<string>();
const sskRequestDefer = defer<string>(); const sskRequestResolvers = Promise.withResolvers<string>();
const uskRequestDefer = defer<string>(); const uskRequestResolvers = Promise.withResolvers<string>();
const backupKeyRequestDefer = defer<string>(); const backupKeyRequestResolvers = Promise.withResolvers<string>();
fetchMock.put( fetchMock.put(
new RegExp(`/_matrix/client/(r0|v3)/sendToDevice/m.secret.request`), new RegExp(`/_matrix/client/(r0|v3)/sendToDevice/m.secret.request`),
@@ -1555,13 +1555,13 @@ function mockSecretRequestAndGetPromises(): Map<string, Promise<string>> {
const name = content.name; const name = content.name;
const requestId = content.request_id; const requestId = content.request_id;
if (name == "m.cross_signing.user_signing") { if (name == "m.cross_signing.user_signing") {
uskRequestDefer.resolve(requestId); uskRequestResolvers.resolve(requestId);
} else if (name == "m.cross_signing.master") { } else if (name == "m.cross_signing.master") {
mskRequestDefer.resolve(requestId); mskRequestResolvers.resolve(requestId);
} else if (name == "m.cross_signing.self_signing") { } else if (name == "m.cross_signing.self_signing") {
sskRequestDefer.resolve(requestId); sskRequestResolvers.resolve(requestId);
} else if (name == "m.megolm_backup.v1") { } else if (name == "m.megolm_backup.v1") {
backupKeyRequestDefer.resolve(requestId); backupKeyRequestResolvers.resolve(requestId);
} }
} }
return {}; return {};
@@ -1570,10 +1570,10 @@ function mockSecretRequestAndGetPromises(): Map<string, Promise<string>> {
); );
const promiseMap = new Map<string, Promise<string>>(); const promiseMap = new Map<string, Promise<string>>();
promiseMap.set("m.cross_signing.master", mskRequestDefer.promise); promiseMap.set("m.cross_signing.master", mskRequestResolvers.promise);
promiseMap.set("m.cross_signing.self_signing", sskRequestDefer.promise); promiseMap.set("m.cross_signing.self_signing", sskRequestResolvers.promise);
promiseMap.set("m.cross_signing.user_signing", uskRequestDefer.promise); promiseMap.set("m.cross_signing.user_signing", uskRequestResolvers.promise);
promiseMap.set("m.megolm_backup.v1", backupKeyRequestDefer.promise); promiseMap.set("m.megolm_backup.v1", backupKeyRequestResolvers.promise);
return promiseMap; return promiseMap;
} }

View File

@@ -1796,6 +1796,27 @@ describe("MatrixClient", function () {
expect(client.getUserIdLocalpart()).toBe("alice"); 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 { function withThreadId(event: MatrixEvent, newThreadId: string): MatrixEvent {

View File

@@ -26,7 +26,6 @@ import {
PayloadType, PayloadType,
RendezvousError, RendezvousError,
} from "../../../src/rendezvous"; } from "../../../src/rendezvous";
import { defer } from "../../../src/utils";
import { import {
ClientPrefix, ClientPrefix,
DEVICE_CODE_SCOPE, DEVICE_CODE_SCOPE,
@@ -112,8 +111,8 @@ describe("MSC4108SignInWithQR", () => {
let opponentLogin: MSC4108SignInWithQR; let opponentLogin: MSC4108SignInWithQR;
beforeEach(async () => { beforeEach(async () => {
let ourData = defer<string>(); let ourData = Promise.withResolvers<string>();
let opponentData = defer<string>(); let opponentData = Promise.withResolvers<string>();
const ourMockSession = { const ourMockSession = {
send: jest.fn(async (newData) => { send: jest.fn(async (newData) => {
@@ -122,7 +121,7 @@ describe("MSC4108SignInWithQR", () => {
receive: jest.fn(() => { receive: jest.fn(() => {
const prom = opponentData.promise; const prom = opponentData.promise;
prom.then(() => { prom.then(() => {
opponentData = defer(); opponentData = Promise.withResolvers();
}); });
return prom; return prom;
}), }),
@@ -141,7 +140,7 @@ describe("MSC4108SignInWithQR", () => {
receive: jest.fn(() => { receive: jest.fn(() => {
const prom = ourData.promise; const prom = ourData.promise;
prom.then(() => { prom.then(() => {
ourData = defer(); ourData = Promise.withResolvers();
}); });
return prom; return prom;
}), }),
@@ -334,11 +333,11 @@ describe("MSC4108SignInWithQR", () => {
// @ts-ignore // @ts-ignore
await opponentLogin.receive(); await opponentLogin.receive();
const deferred = defer<IMyDevice>(); const deviceResolvers = Promise.withResolvers<IMyDevice>();
mocked(client.getDevice).mockReturnValue(deferred.promise); mocked(client.getDevice).mockReturnValue(deviceResolvers.promise);
ourLogin.cancel(MSC4108FailureReason.UserCancelled).catch(() => {}); ourLogin.cancel(MSC4108FailureReason.UserCancelled).catch(() => {});
deferred.resolve({} as IMyDevice); deviceResolvers.resolve({} as IMyDevice);
const secrets = { const secrets = {
cross_signing: { master_key: "mk", user_signing_key: "usk", self_signing_key: "ssk" }, cross_signing: { master_key: "mk", user_signing_key: "usk", self_signing_key: "ssk" },

View File

@@ -48,7 +48,6 @@ import { type SyncApiOptions, SyncState } from "../../src/sync";
import { type IStoredClientOpts } from "../../src"; import { type IStoredClientOpts } from "../../src";
import { logger } from "../../src/logger"; import { logger } from "../../src/logger";
import { emitPromise } from "../test-utils/test-utils"; import { emitPromise } from "../test-utils/test-utils";
import { defer } from "../../src/utils";
import { KnownMembership } from "../../src/@types/membership"; import { KnownMembership } from "../../src/@types/membership";
import { type SyncCryptoCallbacks } from "../../src/common-crypto/CryptoBackend"; import { type SyncCryptoCallbacks } from "../../src/common-crypto/CryptoBackend";
@@ -369,7 +368,7 @@ describe("SlidingSyncSdk", () => {
}); });
it("can be created with live events", async () => { it("can be created with live events", async () => {
const seenLiveEventDeferred = defer<boolean>(); const seenLiveEventDeferred = Promise.withResolvers<boolean>();
const listener = ( const listener = (
ev: MatrixEvent, ev: MatrixEvent,
room?: Room, room?: Room,

View File

@@ -5,7 +5,6 @@ import { getMockClientWithEventEmitter } from "../test-utils/client";
import { StubStore } from "../../src/store/stub"; import { StubStore } from "../../src/store/stub";
import { type IndexedToDeviceBatch } from "../../src/models/ToDeviceMessage"; import { type IndexedToDeviceBatch } from "../../src/models/ToDeviceMessage";
import { SyncState } from "../../src/sync"; import { SyncState } from "../../src/sync";
import { defer } from "../../src/utils";
describe("onResumedSync", () => { describe("onResumedSync", () => {
let batch: IndexedToDeviceBatch | null; let batch: IndexedToDeviceBatch | null;
@@ -60,7 +59,7 @@ describe("onResumedSync", () => {
}); });
it("resends queue after connectivity restored", async () => { it("resends queue after connectivity restored", async () => {
const deferred = defer(); const successResolvers = Promise.withResolvers<void>();
onSendToDeviceFailure = () => { onSendToDeviceFailure = () => {
expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1);
@@ -73,15 +72,15 @@ describe("onResumedSync", () => {
onSendToDeviceSuccess = () => { onSendToDeviceSuccess = () => {
expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(3); expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(3);
expect(store.removeToDeviceBatch).toHaveBeenCalled(); expect(store.removeToDeviceBatch).toHaveBeenCalled();
deferred.resolve(); successResolvers.resolve();
}; };
queue.start(); queue.start();
return deferred.promise; return successResolvers.promise;
}); });
it("does not resend queue if client sync still catching up", async () => { it("does not resend queue if client sync still catching up", async () => {
const deferred = defer(); const successResolvers = Promise.withResolvers<void>();
onSendToDeviceFailure = () => { onSendToDeviceFailure = () => {
expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1);
@@ -89,15 +88,15 @@ describe("onResumedSync", () => {
resumeSync(SyncState.Catchup, SyncState.Catchup); resumeSync(SyncState.Catchup, SyncState.Catchup);
expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1);
deferred.resolve(); successResolvers.resolve();
}; };
queue.start(); queue.start();
return deferred.promise; return successResolvers.promise;
}); });
it("does not resend queue if connectivity restored after queue stopped", async () => { it("does not resend queue if connectivity restored after queue stopped", async () => {
const deferred = defer(); const successResolvers = Promise.withResolvers<void>();
onSendToDeviceFailure = () => { onSendToDeviceFailure = () => {
expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1);
@@ -107,10 +106,10 @@ describe("onResumedSync", () => {
resumeSync(SyncState.Syncing, SyncState.Catchup); resumeSync(SyncState.Syncing, SyncState.Catchup);
expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1);
deferred.resolve(); successResolvers.resolve();
}; };
queue.start(); queue.start();
return deferred.promise; return successResolvers.promise;
}); });
}); });

View File

@@ -29,7 +29,7 @@ import {
Method, Method,
} from "../../../src"; } from "../../../src";
import { emitPromise } from "../../test-utils/test-utils"; 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"; import { type Logger } from "../../../src/logger";
describe("FetchHttpApi", () => { describe("FetchHttpApi", () => {
@@ -525,8 +525,8 @@ describe("FetchHttpApi", () => {
it("should not log query parameters", async () => { it("should not log query parameters", async () => {
jest.useFakeTimers(); jest.useFakeTimers();
const deferred = defer<Response>(); const responseResolvers = Promise.withResolvers<Response>();
const fetchFn = jest.fn().mockReturnValue(deferred.promise); const fetchFn = jest.fn().mockReturnValue(responseResolvers.promise);
const mockLogger = { const mockLogger = {
debug: jest.fn(), debug: jest.fn(),
} as unknown as Mocked<Logger>; } as unknown as Mocked<Logger>;
@@ -538,7 +538,7 @@ describe("FetchHttpApi", () => {
}); });
const prom = api.requestOtherUrl(Method.Get, "https://server:8448/some/path?query=param#fragment"); const prom = api.requestOtherUrl(Method.Get, "https://server:8448/some/path?query=param#fragment");
jest.advanceTimersByTime(1234); 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; await prom;
expect(mockLogger.debug).not.toHaveBeenCalledWith("fragment"); expect(mockLogger.debug).not.toHaveBeenCalledWith("fragment");
expect(mockLogger.debug).not.toHaveBeenCalledWith("query"); expect(mockLogger.debug).not.toHaveBeenCalledWith("query");
@@ -557,7 +557,7 @@ describe("FetchHttpApi", () => {
}); });
it("should not make multiple concurrent refresh token requests", async () => { 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({ const fetchFn = jest.fn().mockResolvedValue({
ok: false, ok: false,
status: tokenInactiveError.httpStatus, status: tokenInactiveError.httpStatus,
@@ -612,7 +612,7 @@ describe("FetchHttpApi", () => {
}); });
it("should use newly refreshed token if request starts mid-refresh", async () => { 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({ const fetchFn = jest.fn().mockResolvedValue({
ok: false, ok: false,
status: tokenInactiveError.httpStatus, status: tokenInactiveError.httpStatus,

View File

@@ -73,7 +73,7 @@ import {
PolicyRecommendation, PolicyRecommendation,
PolicyScope, PolicyScope,
} from "../../src/models/invites-ignorer"; } 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 { type SyncState } from "../../src/sync";
import * as featureUtils from "../../src/feature"; import * as featureUtils from "../../src/feature";
import { StubStore } from "../../src/store/stub"; import { StubStore } from "../../src/store/stub";
@@ -1997,8 +1997,8 @@ describe("MatrixClient", function () {
}); });
it("should cancel an event which is encrypting", async () => { it("should cancel an event which is encrypting", async () => {
const encryptEventDefer = defer(); const encryptEventResolvers = Promise.withResolvers<void>();
mockCrypto.encryptEvent.mockReturnValue(encryptEventDefer.promise); mockCrypto.encryptEvent.mockReturnValue(encryptEventResolvers.promise);
const statusPromise = testUtils.emitPromise(event, "Event.status"); const statusPromise = testUtils.emitPromise(event, "Event.status");
// @ts-ignore protected method access // @ts-ignore protected method access
@@ -2009,7 +2009,7 @@ describe("MatrixClient", function () {
assertCancelled(); assertCancelled();
// now let the encryption complete, and check that the message is not sent. // now let the encryption complete, and check that the message is not sent.
encryptEventDefer.resolve(); encryptEventResolvers.resolve();
await encryptAndSendPromise; await encryptAndSendPromise;
assertCancelled(); assertCancelled();
}); });

View File

@@ -30,7 +30,6 @@ import {
import { LegacyMembershipManager } from "../../../src/matrixrtc/LegacyMembershipManager"; import { LegacyMembershipManager } from "../../../src/matrixrtc/LegacyMembershipManager";
import { makeMockClient, makeMockRoom, membershipTemplate, mockCallMembership, type MockClient } from "./mocks"; import { makeMockClient, makeMockRoom, membershipTemplate, mockCallMembership, type MockClient } from "./mocks";
import { MembershipManager } from "../../../src/matrixrtc/NewMembershipManager"; import { MembershipManager } from "../../../src/matrixrtc/NewMembershipManager";
import { defer } from "../../../src/utils";
import { logger } from "../../../src/logger.ts"; import { logger } from "../../../src/logger.ts";
function waitForMockCall(method: MockedFunction<any>, returnVal?: Promise<any>) { function waitForMockCall(method: MockedFunction<any>, returnVal?: Promise<any>) {
@@ -51,7 +50,7 @@ function waitForMockCallOnce(method: MockedFunction<any>, returnVal?: Promise<an
} }
function createAsyncHandle(method: MockedFunction<any>) { function createAsyncHandle(method: MockedFunction<any>) {
const { reject, resolve, promise } = defer(); const { reject, resolve, promise } = Promise.withResolvers<void>();
method.mockImplementation(() => promise); method.mockImplementation(() => promise);
return { reject, resolve }; return { reject, resolve };
} }

View File

@@ -22,7 +22,6 @@ import { ToDeviceKeyTransport } from "../../../src/matrixrtc/ToDeviceKeyTranspor
import { getMockClientWithEventEmitter } from "../../test-utils/client.ts"; import { getMockClientWithEventEmitter } from "../../test-utils/client.ts";
import { type Statistics } from "../../../src/matrixrtc"; import { type Statistics } from "../../../src/matrixrtc";
import { KeyTransportEvents } from "../../../src/matrixrtc/IKeyTransport.ts"; import { KeyTransportEvents } from "../../../src/matrixrtc/IKeyTransport.ts";
import { defer } from "../../../src/utils.ts";
import { type Logger } from "../../../src/logger.ts"; import { type Logger } from "../../../src/logger.ts";
describe("ToDeviceKeyTransport", () => { describe("ToDeviceKeyTransport", () => {
@@ -108,9 +107,14 @@ describe("ToDeviceKeyTransport", () => {
}); });
it("should emit when a key is received", async () => { 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) => { transport.on(KeyTransportEvents.ReceivedKeys, (userId, deviceId, keyBase64Encoded, index, timestamp) => {
deferred.resolve({ userId, deviceId, keyBase64Encoded, index }); receivedKeyResolvers.resolve({ userId, deviceId, keyBase64Encoded, index });
}); });
transport.start(); 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(userId).toBe("@bob:example.org");
expect(deviceId).toBe("BOBDEVICE"); expect(deviceId).toBe("BOBDEVICE");
expect(keyBase64Encoded).toBe(testEncoded); expect(keyBase64Encoded).toBe(testEncoded);

View File

@@ -25,7 +25,6 @@ import { EventType, RelationType, UNSTABLE_MSC2716_MARKER } from "../../src/@typ
import { MatrixEvent, MatrixEventEvent } from "../../src/models/event"; import { MatrixEvent, MatrixEventEvent } from "../../src/models/event";
import { M_BEACON } from "../../src/@types/beacon"; import { M_BEACON } from "../../src/@types/beacon";
import { type MatrixClient } from "../../src/client"; import { type MatrixClient } from "../../src/client";
import { defer } from "../../src/utils";
import { Room } from "../../src/models/room"; import { Room } from "../../src/models/room";
import { KnownMembership } from "../../src/@types/membership"; import { KnownMembership } from "../../src/@types/membership";
import { DecryptionFailureCode } from "../../src/crypto-api"; import { DecryptionFailureCode } from "../../src/crypto-api";
@@ -1084,8 +1083,8 @@ describe("RoomState", function () {
timestamp: Date.now() + 1, timestamp: Date.now() + 1,
}); });
const deferred = defer<void>(); const decryptEventResolvers = Promise.withResolvers<void>();
mockClient.decryptEventIfNeeded.mockReturnValue(deferred.promise); mockClient.decryptEventIfNeeded.mockReturnValue(decryptEventResolvers.promise);
state.setStateEvents([beacon1, beacon2]); state.setStateEvents([beacon1, beacon2]);
const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1)) as Beacon; const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1)) as Beacon;
@@ -1095,7 +1094,7 @@ describe("RoomState", function () {
// update type after '''decryption''' // update type after '''decryption'''
decryptingRelatedEvent.event.type = M_BEACON.name; decryptingRelatedEvent.event.type = M_BEACON.name;
decryptingRelatedEvent.event.content = locationEvent.event.content; decryptingRelatedEvent.event.content = locationEvent.event.content;
deferred.resolve(); decryptEventResolvers.resolve();
await prom; await prom;
expect(addLocationsSpy).toHaveBeenCalledWith([decryptingRelatedEvent]); expect(addLocationsSpy).toHaveBeenCalledWith([decryptingRelatedEvent]);

View File

@@ -40,7 +40,6 @@ import {
type UIAuthCallback, type UIAuthCallback,
} from "../../../src"; } from "../../../src";
import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor"; import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor";
import { defer } from "../../../src/utils";
describe("OutgoingRequestProcessor", () => { describe("OutgoingRequestProcessor", () => {
/** the OutgoingRequestProcessor implementation under test */ /** the OutgoingRequestProcessor implementation under test */
@@ -285,13 +284,13 @@ describe("OutgoingRequestProcessor", () => {
new RustSdkCryptoJs.DeviceId("TEST_DEVICE"), new RustSdkCryptoJs.DeviceId("TEST_DEVICE"),
); );
const authRequestResultDefer = defer<string>(); const authRequestResultResolvers = Promise.withResolvers<string>();
const authRequestCalledPromise = new Promise<void>((resolve) => { const authRequestCalledPromise = new Promise<void>((resolve) => {
const mockHttpApi = { const mockHttpApi = {
authedRequest: async () => { authedRequest: async () => {
resolve(); resolve();
return await authRequestResultDefer.promise; return await authRequestResultResolvers.promise;
}, },
} as unknown as Mocked<MatrixHttpApi<IHttpOpts & { onlyData: true }>>; } as unknown as Mocked<MatrixHttpApi<IHttpOpts & { onlyData: true }>>;
processor = new OutgoingRequestProcessor(olmMachine, mockHttpApi); processor = new OutgoingRequestProcessor(olmMachine, mockHttpApi);
@@ -308,7 +307,7 @@ describe("OutgoingRequestProcessor", () => {
olmMachine.close(); olmMachine.close();
// the HTTP request completes... // the HTTP request completes...
authRequestResultDefer.resolve("{}"); authRequestResultResolvers.resolve("{}");
// ... and `makeOutgoingRequest` resolves satisfactorily // ... and `makeOutgoingRequest` resolves satisfactorily
await result; await result;

View File

@@ -20,7 +20,6 @@ import { type OutgoingRequest } from "@matrix-org/matrix-sdk-crypto-wasm";
import { type OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor"; import { type OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor";
import { OutgoingRequestsManager } from "../../../src/rust-crypto/OutgoingRequestsManager"; import { OutgoingRequestsManager } from "../../../src/rust-crypto/OutgoingRequestsManager";
import { defer, type IDeferred } from "../../../src/utils";
import { logger } from "../../../src/logger"; import { logger } from "../../../src/logger";
describe("OutgoingRequestsManager", () => { describe("OutgoingRequestsManager", () => {
@@ -70,11 +69,11 @@ describe("OutgoingRequestsManager", () => {
const request2 = new RustSdkCryptoJs.KeysUploadRequest("foo2", "{}"); const request2 = new RustSdkCryptoJs.KeysUploadRequest("foo2", "{}");
const request3 = new RustSdkCryptoJs.KeysBackupRequest("foo3", "{}", "1"); const request3 = new RustSdkCryptoJs.KeysBackupRequest("foo3", "{}", "1");
const firstOutgoingRequestDefer = defer<OutgoingRequest[]>(); const firstOutgoingRequestResolvers = Promise.withResolvers<OutgoingRequest[]>();
olmMachine.outgoingRequests olmMachine.outgoingRequests
.mockImplementationOnce(async () => { .mockImplementationOnce(async () => {
return firstOutgoingRequestDefer.promise; return firstOutgoingRequestResolvers.promise;
}) })
.mockImplementationOnce(async () => { .mockImplementationOnce(async () => {
return [request3]; return [request3];
@@ -87,7 +86,7 @@ describe("OutgoingRequestsManager", () => {
const thirdRequest = manager.doProcessOutgoingRequests(); const thirdRequest = manager.doProcessOutgoingRequests();
// let the first request complete // let the first request complete
firstOutgoingRequestDefer.resolve([request1, request2]); firstOutgoingRequestResolvers.resolve([request1, request2]);
await firstRequest; await firstRequest;
await secondRequest; await secondRequest;
@@ -112,10 +111,10 @@ describe("OutgoingRequestsManager", () => {
const outgoingRequestCalledPromises: Promise<void>[] = []; const outgoingRequestCalledPromises: Promise<void>[] = [];
// deferreds which will provide the results of OlmMachine.outgoingRequests // deferreds which will provide the results of OlmMachine.outgoingRequests
const outgoingRequestResultDeferreds: IDeferred<OutgoingRequest[]>[] = []; const outgoingRequestResultDeferreds: PromiseWithResolvers<OutgoingRequest[]>[] = [];
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
const resultDeferred = defer<OutgoingRequest[]>(); const resultDeferred = Promise.withResolvers<OutgoingRequest[]>();
const calledPromise = new Promise<void>((resolve) => { const calledPromise = new Promise<void>((resolve) => {
olmMachine.outgoingRequests.mockImplementationOnce(() => { olmMachine.outgoingRequests.mockImplementationOnce(() => {
resolve(); resolve();
@@ -187,10 +186,10 @@ describe("OutgoingRequestsManager", () => {
it("When the manager is stopped after outgoingRequests() call, do not make sever requests", async () => { it("When the manager is stopped after outgoingRequests() call, do not make sever requests", async () => {
const request1 = new RustSdkCryptoJs.KeysQueryRequest("foo", "{}"); const request1 = new RustSdkCryptoJs.KeysQueryRequest("foo", "{}");
const firstOutgoingRequestDefer = defer<OutgoingRequest[]>(); const firstOutgoingRequestResolvers = Promise.withResolvers<OutgoingRequest[]>();
olmMachine.outgoingRequests.mockImplementationOnce(async (): Promise<OutgoingRequest[]> => { olmMachine.outgoingRequests.mockImplementationOnce(async (): Promise<OutgoingRequest[]> => {
return firstOutgoingRequestDefer.promise; return firstOutgoingRequestResolvers.promise;
}); });
const firstRequest = manager.doProcessOutgoingRequests(); const firstRequest = manager.doProcessOutgoingRequests();
@@ -199,7 +198,7 @@ describe("OutgoingRequestsManager", () => {
manager.stop(); manager.stop();
// let the first request complete // let the first request complete
firstOutgoingRequestDefer.resolve([request1]); firstOutgoingRequestResolvers.resolve([request1]);
await firstRequest; await firstRequest;
@@ -210,7 +209,7 @@ describe("OutgoingRequestsManager", () => {
const request1 = new RustSdkCryptoJs.KeysQueryRequest("11", "{}"); const request1 = new RustSdkCryptoJs.KeysQueryRequest("11", "{}");
const request2 = new RustSdkCryptoJs.KeysUploadRequest("12", "{}"); const request2 = new RustSdkCryptoJs.KeysUploadRequest("12", "{}");
const firstRequestDefer = defer<void>(); const firstRequestResolvers = Promise.withResolvers<void>();
olmMachine.outgoingRequests.mockImplementationOnce(async (): Promise<OutgoingRequest[]> => { olmMachine.outgoingRequests.mockImplementationOnce(async (): Promise<OutgoingRequest[]> => {
return [request1, request2]; return [request1, request2];
@@ -219,7 +218,7 @@ describe("OutgoingRequestsManager", () => {
processor.makeOutgoingRequest processor.makeOutgoingRequest
.mockImplementationOnce(async () => { .mockImplementationOnce(async () => {
manager.stop(); manager.stop();
return firstRequestDefer.promise; return firstRequestResolvers.promise;
}) })
.mockImplementationOnce(async () => { .mockImplementationOnce(async () => {
return; return;
@@ -227,7 +226,7 @@ describe("OutgoingRequestsManager", () => {
const firstRequest = manager.doProcessOutgoingRequests(); const firstRequest = manager.doProcessOutgoingRequests();
firstRequestDefer.resolve(); firstRequestResolvers.resolve();
await firstRequest; await firstRequest;

View File

@@ -21,7 +21,6 @@ import fetchMock from "fetch-mock-jest";
import { PerSessionKeyBackupDownloader } from "../../../src/rust-crypto/PerSessionKeyBackupDownloader"; import { PerSessionKeyBackupDownloader } from "../../../src/rust-crypto/PerSessionKeyBackupDownloader";
import { logger } from "../../../src/logger"; import { logger } from "../../../src/logger";
import { defer, type IDeferred } from "../../../src/utils";
import { import {
type RustBackupCryptoEventMap, type RustBackupCryptoEventMap,
type RustBackupCryptoEvents, type RustBackupCryptoEvents,
@@ -57,15 +56,15 @@ describe("PerSessionKeyBackupDownloader", () => {
let mockOlmMachine: Mocked<OlmMachine>; let mockOlmMachine: Mocked<OlmMachine>;
let mockBackupDecryptor: Mocked<BackupDecryptor>; let mockBackupDecryptor: Mocked<BackupDecryptor>;
let expectedSession: { [roomId: string]: { [sessionId: string]: IDeferred<void> } }; let expectedSession: { [roomId: string]: { [sessionId: string]: PromiseWithResolvers<void> } };
function expectSessionImported(roomId: string, sessionId: string) { function expectSessionImported(roomId: string, sessionId: string) {
const deferred = defer<void>(); const sessionImportedResolvers = Promise.withResolvers<void>();
if (!expectedSession[roomId]) { if (!expectedSession[roomId]) {
expectedSession[roomId] = {}; expectedSession[roomId] = {};
} }
expectedSession[roomId][sessionId] = deferred; expectedSession[roomId][sessionId] = sessionImportedResolvers;
return deferred.promise; return sessionImportedResolvers.promise;
} }
function mockClearSession(sessionId: string): Mocked<IMegolmSessionData> { function mockClearSession(sessionId: string): Mocked<IMegolmSessionData> {
@@ -115,9 +114,9 @@ describe("PerSessionKeyBackupDownloader", () => {
mockRustBackupManager.importBackedUpRoomKeys.mockImplementation(async (keys) => { mockRustBackupManager.importBackedUpRoomKeys.mockImplementation(async (keys) => {
const roomId = keys[0].room_id; const roomId = keys[0].room_id;
const sessionId = keys[0].session_id; const sessionId = keys[0].session_id;
const deferred = expectedSession[roomId] && expectedSession[roomId][sessionId]; const sessionImportedResolvers = expectedSession[roomId] && expectedSession[roomId][sessionId];
if (deferred) { if (sessionImportedResolvers) {
deferred.resolve(); sessionImportedResolvers.resolve();
} }
}); });
@@ -143,7 +142,7 @@ describe("PerSessionKeyBackupDownloader", () => {
}); });
it("Should download and import a missing key from backup", async () => { it("Should download and import a missing key from backup", async () => {
const awaitKeyImported = defer<void>(); const awaitKeyImported = Promise.withResolvers<void>();
const roomId = "!roomId"; const roomId = "!roomId";
const sessionId = "sessionId"; const sessionId = "sessionId";
const expectAPICall = new Promise<void>((resolve) => { const expectAPICall = new Promise<void>((resolve) => {
@@ -168,14 +167,14 @@ describe("PerSessionKeyBackupDownloader", () => {
}); });
it("Should not hammer the backup if the key is requested repeatedly", async () => { it("Should not hammer the backup if the key is requested repeatedly", async () => {
const blockOnServerRequest = defer<void>(); const blockOnServerRequest = Promise.withResolvers<void>();
fetchMock.get(`express:/_matrix/client/v3/room_keys/keys/!roomId/:session_id`, async (url, request) => { fetchMock.get(`express:/_matrix/client/v3/room_keys/keys/!roomId/:session_id`, async (url, request) => {
await blockOnServerRequest.promise; await blockOnServerRequest.promise;
return [mockCipherKey]; return [mockCipherKey];
}); });
const awaitKey2Imported = defer<void>(); const awaitKey2Imported = Promise.withResolvers<void>();
mockRustBackupManager.importBackedUpRoomKeys.mockImplementation(async (keys) => { mockRustBackupManager.importBackedUpRoomKeys.mockImplementation(async (keys) => {
if (keys[0].session_id === "sessionId2") { if (keys[0].session_id === "sessionId2") {
@@ -267,8 +266,8 @@ describe("PerSessionKeyBackupDownloader", () => {
it("Should stop properly", async () => { it("Should stop properly", async () => {
// Simulate a call to stop while request is in flight // Simulate a call to stop while request is in flight
const blockOnServerRequest = defer<void>(); const blockOnServerRequest = Promise.withResolvers<void>();
const requestRoomKeyCalled = defer<void>(); const requestRoomKeyCalled = Promise.withResolvers<void>();
// Mock the request to block // Mock the request to block
fetchMock.get(`express:/_matrix/client/v3/room_keys/keys/:roomId/:sessionId`, async (url, request) => { 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 // @ts-ignore access to private function
const keyQuerySpy: SpyInstance = jest.spyOn(downloader, "queryKeyBackup"); const keyQuerySpy: SpyInstance = jest.spyOn(downloader, "queryKeyBackup");
const rateDeferred = defer<void>(); const rateDeferred = Promise.withResolvers<void>();
keyQuerySpy.mockImplementation( keyQuerySpy.mockImplementation(
// @ts-ignore // @ts-ignore
@@ -544,7 +543,7 @@ describe("PerSessionKeyBackupDownloader", () => {
// @ts-ignore // @ts-ignore
const keyQuerySpy: SpyInstance = jest.spyOn(downloader, "queryKeyBackup"); const keyQuerySpy: SpyInstance = jest.spyOn(downloader, "queryKeyBackup");
const errorDeferred = defer<void>(); const errorDeferred = Promise.withResolvers<void>();
keyQuerySpy.mockImplementation( keyQuerySpy.mockImplementation(
// @ts-ignore // @ts-ignore
@@ -588,7 +587,7 @@ describe("PerSessionKeyBackupDownloader", () => {
}); });
it("On Unknown error on import skip the key and continue", async () => { it("On Unknown error on import skip the key and continue", async () => {
const keyImported = defer<void>(); const keyImported = Promise.withResolvers<void>();
mockRustBackupManager.importBackedUpRoomKeys mockRustBackupManager.importBackedUpRoomKeys
.mockImplementationOnce(async () => { .mockImplementationOnce(async () => {
throw new Error("Didn't work"); throw new Error("Didn't work");

View File

@@ -29,7 +29,6 @@ import { type Mocked } from "jest-mock";
import { HistoryVisibility, type MatrixEvent, type Room, type RoomMember } from "../../../src"; import { HistoryVisibility, type MatrixEvent, type Room, type RoomMember } from "../../../src";
import { RoomEncryptor, toRustHistoryVisibility } from "../../../src/rust-crypto/RoomEncryptor"; import { RoomEncryptor, toRustHistoryVisibility } from "../../../src/rust-crypto/RoomEncryptor";
import { type KeyClaimManager } from "../../../src/rust-crypto/KeyClaimManager"; import { type KeyClaimManager } from "../../../src/rust-crypto/KeyClaimManager";
import { defer } from "../../../src/utils";
import { type OutgoingRequestsManager } from "../../../src/rust-crypto/OutgoingRequestsManager"; import { type OutgoingRequestsManager } from "../../../src/rust-crypto/OutgoingRequestsManager";
import { KnownMembership } from "../../../src/@types/membership"; import { KnownMembership } from "../../../src/@types/membership";
import { import {
@@ -120,8 +119,8 @@ describe("RoomEncryptor", () => {
const defaultDevicesIsolationMode = new AllDevicesIsolationMode(false); const defaultDevicesIsolationMode = new AllDevicesIsolationMode(false);
it("should ensure that there is only one shareRoomKey at a time", async () => { it("should ensure that there is only one shareRoomKey at a time", async () => {
const deferredShare = defer<void>(); const deferredShare = Promise.withResolvers<void>();
const insideOlmShareRoom = defer<void>(); const insideOlmShareRoom = Promise.withResolvers<void>();
mockOlmMachine.shareRoomKey.mockImplementationOnce(async () => { mockOlmMachine.shareRoomKey.mockImplementationOnce(async () => {
insideOlmShareRoom.resolve(); insideOlmShareRoom.resolve();
@@ -149,8 +148,8 @@ describe("RoomEncryptor", () => {
// Regression test for https://github.com/element-hq/element-web/issues/26684 // Regression test for https://github.com/element-hq/element-web/issues/26684
it("Should maintain order of encryption requests", async () => { it("Should maintain order of encryption requests", async () => {
const firstTargetMembers = defer<void>(); const firstTargetMembers = Promise.withResolvers<void>();
const secondTargetMembers = defer<void>(); const secondTargetMembers = Promise.withResolvers<void>();
mockOlmMachine.shareRoomKey.mockResolvedValue([]); mockOlmMachine.shareRoomKey.mockResolvedValue([]);

View File

@@ -68,7 +68,6 @@ import {
import * as testData from "../../test-utils/test-data"; import * as testData from "../../test-utils/test-data";
import { E2EKeyReceiver } from "../../test-utils/E2EKeyReceiver"; import { E2EKeyReceiver } from "../../test-utils/E2EKeyReceiver";
import { E2EKeyResponder } from "../../test-utils/E2EKeyResponder"; import { E2EKeyResponder } from "../../test-utils/E2EKeyResponder";
import { defer } from "../../../src/utils";
import { logger } from "../../../src/logger"; import { logger } from "../../../src/logger";
import { OutgoingRequestsManager } from "../../../src/rust-crypto/OutgoingRequestsManager"; import { OutgoingRequestsManager } from "../../../src/rust-crypto/OutgoingRequestsManager";
import { ClientEvent, type ClientEventHandlerMap } from "../../../src/client"; 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 () => { 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 // the first call to `outgoingMessages` will return a promise which blocks for a while
const firstOutgoingRequestsDefer = defer<Array<any>>(); const firstOutgoingRequestsResolvers = Promise.withResolvers<Array<any>>();
mocked(olmMachine.outgoingRequests).mockReturnValueOnce(firstOutgoingRequestsDefer.promise); mocked(olmMachine.outgoingRequests).mockReturnValueOnce(firstOutgoingRequestsResolvers.promise);
// the second will return a KeysQueryRequest. // the second will return a KeysQueryRequest.
const testReq = new KeysQueryRequest("1234", "{}"); 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 // 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`. // we should have a second call queued. It should trigger a call to `makeOutgoingRequest`.
firstOutgoingRequestsDefer.resolve([]); firstOutgoingRequestsResolvers.resolve([]);
await awaitCallToMakeOutgoingRequest(); await awaitCallToMakeOutgoingRequest();
expect(olmMachine.outgoingRequests).toHaveBeenCalledTimes(2); expect(olmMachine.outgoingRequests).toHaveBeenCalledTimes(2);
}); });

View File

@@ -1,7 +1,6 @@
// This file had a function whose name is all caps, which displeases eslint // This file had a function whose name is all caps, which displeases eslint
/* eslint new-cap: "off" */ /* eslint new-cap: "off" */
import { defer, type IDeferred } from "../../src/utils";
import { MatrixError } from "../../src/http-api"; import { MatrixError } from "../../src/http-api";
import { MatrixScheduler } from "../../src/scheduler"; import { MatrixScheduler } from "../../src/scheduler";
import * as utils from "../test-utils/test-utils"; import * as utils from "../test-utils/test-utils";
@@ -14,7 +13,7 @@ describe("MatrixScheduler", function () {
let scheduler: MatrixScheduler<Record<string, boolean>>; let scheduler: MatrixScheduler<Record<string, boolean>>;
let retryFn: ((event: MatrixEvent | null, attempt: number, err: MatrixError) => number) | null; let retryFn: ((event: MatrixEvent | null, attempt: number, err: MatrixError) => number) | null;
let queueFn: ((event: MatrixEvent) => string | null) | null; let queueFn: ((event: MatrixEvent) => string | null) | null;
let deferred: IDeferred<Record<string, boolean>>; let deferred: PromiseWithResolvers<Record<string, boolean>>;
const roomId = "!foo:bar"; const roomId = "!foo:bar";
const eventA = utils.mkMessage({ const eventA = utils.mkMessage({
user: "@alice:bar", user: "@alice:bar",
@@ -44,7 +43,7 @@ describe("MatrixScheduler", function () {
); );
retryFn = null; retryFn = null;
queueFn = null; queueFn = null;
deferred = defer(); deferred = Promise.withResolvers();
}); });
it("should process events in a queue in a FIFO manner", async function () { it("should process events in a queue in a FIFO manner", async function () {
@@ -54,8 +53,8 @@ describe("MatrixScheduler", function () {
queueFn = function () { queueFn = function () {
return "one_big_queue"; return "one_big_queue";
}; };
const deferA = defer<Record<string, boolean>>(); const deferA = Promise.withResolvers<Record<string, boolean>>();
const deferB = defer<Record<string, boolean>>(); const deferB = Promise.withResolvers<Record<string, boolean>>();
let yieldedA = false; let yieldedA = false;
scheduler.setProcessFunction(function (event) { scheduler.setProcessFunction(function (event) {
if (yieldedA) { 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 () { it("should invoke the retryFn on failure and wait the amount of time specified", async function () {
const waitTimeMs = 1500; const waitTimeMs = 1500;
const retryDefer = defer(); const retryResolvers = Promise.withResolvers<void>();
retryFn = function () { retryFn = function () {
retryDefer.resolve(); retryResolvers.resolve();
return waitTimeMs; return waitTimeMs;
}; };
queueFn = function () { queueFn = function () {
@@ -109,7 +108,7 @@ describe("MatrixScheduler", function () {
await Promise.resolve(); await Promise.resolve();
expect(procCount).toEqual(1); expect(procCount).toEqual(1);
deferred.reject({}); deferred.reject({});
await retryDefer.promise; await retryResolvers.promise;
expect(procCount).toEqual(1); expect(procCount).toEqual(1);
jest.advanceTimersByTime(waitTimeMs); jest.advanceTimersByTime(waitTimeMs);
await Promise.resolve(); await Promise.resolve();
@@ -127,8 +126,8 @@ describe("MatrixScheduler", function () {
return "yep"; return "yep";
}; };
const deferA = defer<Record<string, boolean>>(); const deferA = Promise.withResolvers<Record<string, boolean>>();
const deferB = defer<Record<string, boolean>>(); const deferB = Promise.withResolvers<Record<string, boolean>>();
let procCount = 0; let procCount = 0;
scheduler.setProcessFunction(function (ev) { scheduler.setProcessFunction(function (ev) {
procCount += 1; procCount += 1;
@@ -180,7 +179,7 @@ describe("MatrixScheduler", function () {
}; };
const expectOrder = [eventA.getId(), eventB.getId(), eventD.getId()]; const expectOrder = [eventA.getId(), eventB.getId(), eventD.getId()];
const deferA = defer<Record<string, boolean>>(); const deferA = Promise.withResolvers<Record<string, boolean>>();
const allExpectedEventsSeenInOrderPromise = new Promise((resolve) => { const allExpectedEventsSeenInOrderPromise = new Promise((resolve) => {
scheduler.setProcessFunction(function (event) { scheduler.setProcessFunction(function (event) {
const id = expectOrder.shift(); const id = expectOrder.shift();

View File

@@ -30,7 +30,6 @@ import {
import { secureRandomString } from "../../src/randomstring"; import { secureRandomString } from "../../src/randomstring";
import { type SecretInfo } from "../../src/secret-storage.ts"; import { type SecretInfo } from "../../src/secret-storage.ts";
import { type AccountDataEvents, ClientEvent, MatrixEvent, TypedEventEmitter } from "../../src"; import { type AccountDataEvents, ClientEvent, MatrixEvent, TypedEventEmitter } from "../../src";
import { defer, type IDeferred } from "../../src/utils";
declare module "../../src/@types/event" { declare module "../../src/@types/event" {
interface SecretStorageAccountDataEvents { interface SecretStorageAccountDataEvents {
@@ -289,10 +288,10 @@ describe("ServerSideSecretStorageImpl", function () {
describe("setDefaultKeyId", function () { describe("setDefaultKeyId", function () {
let secretStorage: ServerSideSecretStorage; let secretStorage: ServerSideSecretStorage;
let accountDataAdapter: Mocked<AccountDataClient>; let accountDataAdapter: Mocked<AccountDataClient>;
let accountDataPromise: IDeferred<void>; let accountDataPromise: PromiseWithResolvers<void>;
beforeEach(() => { beforeEach(() => {
accountDataAdapter = mockAccountDataClient(); accountDataAdapter = mockAccountDataClient();
accountDataPromise = defer(); accountDataPromise = Promise.withResolvers();
accountDataAdapter.setAccountData.mockImplementation(() => { accountDataAdapter.setAccountData.mockImplementation(() => {
accountDataPromise.resolve(); accountDataPromise.resolve();
return Promise.resolve({}); return Promise.resolve({});

View File

@@ -18,7 +18,6 @@ import "fake-indexeddb/auto";
import { type LocalIndexedDBStoreBackend } from "../../../src/store/indexeddb-local-backend"; import { type LocalIndexedDBStoreBackend } from "../../../src/store/indexeddb-local-backend";
import { IndexedDBStoreWorker } from "../../../src/store/indexeddb-store-worker"; import { IndexedDBStoreWorker } from "../../../src/store/indexeddb-store-worker";
import { defer } from "../../../src/utils";
function setupWorker(worker: IndexedDBStoreWorker): void { function setupWorker(worker: IndexedDBStoreWorker): void {
worker.onMessage({ data: { command: "setupWorker", args: [] } } as any); worker.onMessage({ data: { command: "setupWorker", args: [] } } as any);
@@ -27,16 +26,16 @@ function setupWorker(worker: IndexedDBStoreWorker): void {
describe("IndexedDBStore Worker", () => { describe("IndexedDBStore Worker", () => {
it("should pass 'closed' event via postMessage", async () => { it("should pass 'closed' event via postMessage", async () => {
const deferred = defer<void>(); const postMessageSuccessResolvers = Promise.withResolvers<void>();
const postMessage = jest.fn().mockImplementation(({ seq, command }) => { const postMessage = jest.fn().mockImplementation(({ seq, command }) => {
if (seq === 1 && command === "cmd_success") { if (seq === 1 && command === "cmd_success") {
deferred.resolve(); postMessageSuccessResolvers.resolve();
} }
}); });
const worker = new IndexedDBStoreWorker(postMessage); const worker = new IndexedDBStoreWorker(postMessage);
setupWorker(worker); setupWorker(worker);
await deferred.promise; await postMessageSuccessResolvers.promise;
// @ts-ignore - private field access // @ts-ignore - private field access
(worker.backend as LocalIndexedDBStoreBackend).db!.onclose!({} as Event); (worker.backend as LocalIndexedDBStoreBackend).db!.onclose!({} as Event);

View File

@@ -21,7 +21,6 @@ import { IDBFactory } from "fake-indexeddb";
import { IndexedDBStore, type IStateEventWithRoomId, MemoryStore, User, UserEvent } from "../../../src"; import { IndexedDBStore, type IStateEventWithRoomId, MemoryStore, User, UserEvent } from "../../../src";
import { emitPromise } from "../../test-utils/test-utils"; import { emitPromise } from "../../test-utils/test-utils";
import { type LocalIndexedDBStoreBackend } from "../../../src/store/indexeddb-local-backend"; import { type LocalIndexedDBStoreBackend } from "../../../src/store/indexeddb-local-backend";
import { defer } from "../../../src/utils";
describe("IndexedDBStore", () => { describe("IndexedDBStore", () => {
afterEach(() => { afterEach(() => {
@@ -80,7 +79,7 @@ describe("IndexedDBStore", () => {
it("Should load presence events on startup", async () => { it("Should load presence events on startup", async () => {
// 1. Create idb database // 1. Create idb database
const indexedDB = new IDBFactory(); const indexedDB = new IDBFactory();
const setupDefer = defer<Event>(); const setupResolvers = Promise.withResolvers<Event>();
const req = indexedDB.open("matrix-js-sdk:db3", 1); const req = indexedDB.open("matrix-js-sdk:db3", 1);
let db: IDBDatabase; let db: IDBDatabase;
req.onupgradeneeded = () => { req.onupgradeneeded = () => {
@@ -89,11 +88,11 @@ describe("IndexedDBStore", () => {
db.createObjectStore("accountData", { keyPath: ["type"] }); db.createObjectStore("accountData", { keyPath: ["type"] });
db.createObjectStore("sync", { keyPath: ["clobber"] }); db.createObjectStore("sync", { keyPath: ["clobber"] });
}; };
req.onsuccess = setupDefer.resolve; req.onsuccess = setupResolvers.resolve;
await setupDefer.promise; await setupResolvers.promise;
// 2. Fill in user presence data // 2. Fill in user presence data
const writeDefer = defer<Event>(); const writeResolvers = Promise.withResolvers<Event>();
const transaction = db!.transaction(["users"], "readwrite"); const transaction = db!.transaction(["users"], "readwrite");
const objectStore = transaction.objectStore("users"); const objectStore = transaction.objectStore("users");
const request = objectStore.put({ const request = objectStore.put({
@@ -106,8 +105,8 @@ describe("IndexedDBStore", () => {
type: "m.presence", type: "m.presence",
}, },
}); });
request.onsuccess = writeDefer.resolve; request.onsuccess = writeResolvers.resolve;
await writeDefer.promise; await writeResolvers.promise;
// 3. Close database // 3. Close database
req.result.close(); req.result.close();
@@ -201,7 +200,7 @@ describe("IndexedDBStore", () => {
}); });
it("should resolve isNewlyCreated to false if database existed already but needs upgrade", async () => { it("should resolve isNewlyCreated to false if database existed already but needs upgrade", async () => {
const deferred = defer<Event>(); const requestSuccessResolvers = Promise.withResolvers<Event>();
// seed db3 to Version 1 so it forces a migration // seed db3 to Version 1 so it forces a migration
const req = indexedDB.open("matrix-js-sdk:db3", 1); const req = indexedDB.open("matrix-js-sdk:db3", 1);
req.onupgradeneeded = () => { req.onupgradeneeded = () => {
@@ -210,8 +209,8 @@ describe("IndexedDBStore", () => {
db.createObjectStore("accountData", { keyPath: ["type"] }); db.createObjectStore("accountData", { keyPath: ["type"] });
db.createObjectStore("sync", { keyPath: ["clobber"] }); db.createObjectStore("sync", { keyPath: ["clobber"] });
}; };
req.onsuccess = deferred.resolve; req.onsuccess = requestSuccessResolvers.resolve;
await deferred.promise; await requestSuccessResolvers.promise;
req.result.close(); req.result.close();
const store = new IndexedDBStore({ const store = new IndexedDBStore({
@@ -232,20 +231,20 @@ describe("IndexedDBStore", () => {
}); });
await store.startup(); await store.startup();
const deferred = defer<void>(); const storeClosedResolvers = Promise.withResolvers<void>();
store.on("closed", deferred.resolve); store.on("closed", storeClosedResolvers.resolve);
// @ts-ignore - private field access // @ts-ignore - private field access
(store.backend as LocalIndexedDBStoreBackend).db!.onclose!({} as Event); (store.backend as LocalIndexedDBStoreBackend).db!.onclose!({} as Event);
await deferred.promise; await storeClosedResolvers.promise;
}); });
it("should use remote backend if workerFactory passed", async () => { it("should use remote backend if workerFactory passed", async () => {
const deferred = defer<void>(); const workerPostMessageResolvers = Promise.withResolvers<void>();
class MockWorker { class MockWorker {
postMessage(data: any) { postMessage(data: any) {
if (data.command === "setupWorker") { if (data.command === "setupWorker") {
deferred.resolve(); workerPostMessageResolvers.resolve();
} }
} }
} }
@@ -257,7 +256,7 @@ describe("IndexedDBStore", () => {
workerFactory: () => new MockWorker() as Worker, workerFactory: () => new MockWorker() as Worker,
}); });
store.startup(); store.startup();
await deferred.promise; await workerPostMessageResolvers.promise;
}); });
it("remote worker should pass closed event", async () => { it("remote worker should pass closed event", async () => {
@@ -273,10 +272,10 @@ describe("IndexedDBStore", () => {
}); });
store.startup(); store.startup();
const deferred = defer<void>(); const storeClosedResolvers = Promise.withResolvers<void>();
store.on("closed", deferred.resolve); store.on("closed", storeClosedResolvers.resolve);
(worker as any).onmessage({ data: { command: "closed" } }); (worker as any).onmessage({ data: { command: "closed" } });
await deferred.promise; await storeClosedResolvers.promise;
}); });
it("remote worker should pass command failures", async () => { it("remote worker should pass command failures", async () => {

View File

@@ -52,7 +52,7 @@ import {
type GroupCallEventHandlerEventHandlerMap, type GroupCallEventHandlerEventHandlerMap,
} from "./webrtc/groupCallEventHandler.ts"; } from "./webrtc/groupCallEventHandler.ts";
import * as utils from "./utils.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 { Direction, EventTimeline } from "./models/event-timeline.ts";
import { type IActionsObject, PushProcessor } from "./pushprocessor.ts"; import { type IActionsObject, PushProcessor } from "./pushprocessor.ts";
import { AutoDiscovery, type AutoDiscoveryAction } from "./autodiscovery.ts"; import { AutoDiscovery, type AutoDiscoveryAction } from "./autodiscovery.ts";
@@ -2191,7 +2191,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
if (existingData && deepCompare(existingData.event.content, content)) return {}; if (existingData && deepCompare(existingData.event.content, content)) return {};
// Create a promise which will resolve when the update is received // Create a promise which will resolve when the update is received
const updatedDefer = defer<void>(); const updatedResolvers = Promise.withResolvers<void>();
function accountDataListener(event: MatrixEvent): void { function accountDataListener(event: MatrixEvent): void {
// Note that we cannot safely check that the content matches what we expected, because there is a race: // Note that we cannot safely check that the content matches what we expected, because there is a race:
// * We set the new content // * We set the new content
@@ -2203,13 +2203,13 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
// //
// Anyway, what we *shouldn't* do is get stuck in a loop. I think the best we can do is check that the event // Anyway, what we *shouldn't* do is get stuck in a loop. I think the best we can do is check that the event
// type matches. // type matches.
if (event.getType() === eventType) updatedDefer.resolve(); if (event.getType() === eventType) updatedResolvers.resolve();
} }
this.addListener(ClientEvent.AccountData, accountDataListener); this.addListener(ClientEvent.AccountData, accountDataListener);
try { try {
const result = await retryNetworkOperation(5, () => this.setAccountDataRaw(eventType, content)); const result = await retryNetworkOperation(5, () => this.setAccountDataRaw(eventType, content));
await updatedDefer.promise; await updatedResolvers.promise;
return result; return result;
} finally { } finally {
this.removeListener(ClientEvent.AccountData, accountDataListener); this.removeListener(ClientEvent.AccountData, accountDataListener);
@@ -5173,24 +5173,24 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
} else if (!hasDontNotifyRule) { } else if (!hasDontNotifyRule) {
// Remove the existing one before setting the mute push rule // Remove the existing one before setting the mute push rule
// This is a workaround to SYN-590 (Push rule update fails) // This is a workaround to SYN-590 (Push rule update fails)
const deferred = utils.defer(); const doneResolvers = Promise.withResolvers<void>();
this.deletePushRule(scope, PushRuleKind.RoomSpecific, roomPushRule.rule_id) this.deletePushRule(scope, PushRuleKind.RoomSpecific, roomPushRule.rule_id)
.then(() => { .then(() => {
this.addPushRule(scope, PushRuleKind.RoomSpecific, roomId, { this.addPushRule(scope, PushRuleKind.RoomSpecific, roomId, {
actions: [PushRuleActionName.DontNotify], actions: [PushRuleActionName.DontNotify],
}) })
.then(() => { .then(() => {
deferred.resolve(); doneResolvers.resolve();
}) })
.catch((err) => { .catch((err) => {
deferred.reject(err); doneResolvers.reject(err);
}); });
}) })
.catch((err) => { .catch((err) => {
deferred.reject(err); doneResolvers.reject(err);
}); });
promise = deferred.promise; promise = doneResolvers.promise;
} }
} }

View File

@@ -24,7 +24,7 @@ import {
type UploadResponse, type UploadResponse,
} from "./interface.ts"; } from "./interface.ts";
import { MediaPrefix } from "./prefix.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 * as callbacks from "../realtime-callbacks.ts";
import { Method } from "./method.ts"; import { Method } from "./method.ts";
import { ConnectionError } from "./errors.ts"; import { ConnectionError } from "./errors.ts";
@@ -65,14 +65,14 @@ export class MatrixHttpApi<O extends IHttpOpts> extends FetchHttpApi<O> {
total: 0, total: 0,
abortController, abortController,
} as Upload; } as Upload;
const deferred = defer<UploadResponse>(); const uploadResolvers = Promise.withResolvers<UploadResponse>();
if (globalThis.XMLHttpRequest) { if (globalThis.XMLHttpRequest) {
const xhr = new globalThis.XMLHttpRequest(); const xhr = new globalThis.XMLHttpRequest();
const timeoutFn = function (): void { const timeoutFn = function (): void {
xhr.abort(); 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 // set an initial timeout of 30s; we'll advance it each time we get a progress notification
@@ -91,16 +91,16 @@ export class MatrixHttpApi<O extends IHttpOpts> extends FetchHttpApi<O> {
} }
if (xhr.status >= 400) { if (xhr.status >= 400) {
deferred.reject(parseErrorResponse(xhr, xhr.responseText)); uploadResolvers.reject(parseErrorResponse(xhr, xhr.responseText));
} else { } else {
deferred.resolve(JSON.parse(xhr.responseText)); uploadResolvers.resolve(JSON.parse(xhr.responseText));
} }
} catch (err) { } catch (err) {
if ((<Error>err).name === "AbortError") { if ((<Error>err).name === "AbortError") {
deferred.reject(err); uploadResolvers.reject(err);
return; return;
} }
deferred.reject(new ConnectionError("request failed", <Error>err)); uploadResolvers.reject(new ConnectionError("request failed", <Error>err));
} }
break; break;
} }
@@ -153,16 +153,16 @@ export class MatrixHttpApi<O extends IHttpOpts> extends FetchHttpApi<O> {
.then((response) => { .then((response) => {
return this.opts.onlyData ? <UploadResponse>response : response.json(); return this.opts.onlyData ? <UploadResponse>response : response.json();
}) })
.then(deferred.resolve, deferred.reject); .then(uploadResolvers.resolve, uploadResolvers.reject);
} }
// remove the upload from the list on completion // remove the upload from the list on completion
upload.promise = deferred.promise.finally(() => { upload.promise = uploadResolvers.promise.finally(() => {
removeElement(this.uploads, (elem) => elem === upload); removeElement(this.uploads, (elem) => elem === upload);
}); });
abortController.signal.addEventListener("abort", () => { abortController.signal.addEventListener("abort", () => {
removeElement(this.uploads, (elem) => elem === upload); removeElement(this.uploads, (elem) => elem === upload);
deferred.reject(new DOMException("Aborted", "AbortError")); uploadResolvers.reject(new DOMException("Aborted", "AbortError"));
}); });
this.uploads.push(upload); this.uploads.push(upload);
return upload.promise; return upload.promise;

View File

@@ -18,7 +18,6 @@ limitations under the License.
import { logger } from "./logger.ts"; import { logger } from "./logger.ts";
import { type MatrixClient } from "./client.ts"; import { type MatrixClient } from "./client.ts";
import { defer, type IDeferred } from "./utils.ts";
import { MatrixError } from "./http-api/index.ts"; import { MatrixError } from "./http-api/index.ts";
import { type UserIdentifier } from "./@types/auth.ts"; import { type UserIdentifier } from "./@types/auth.ts";
@@ -262,7 +261,7 @@ export class InteractiveAuth<T> {
private data: IAuthData & MatrixError["data"]; private data: IAuthData & MatrixError["data"];
private emailSid?: string; private emailSid?: string;
private requestingEmailToken = false; private requestingEmailToken = false;
private attemptAuthDeferred: IDeferred<T> | null = null; private attemptAuthDeferred: PromiseWithResolvers<T> | null = null;
private chosenFlow: UIAFlow | null = null; private chosenFlow: UIAFlow | null = null;
private currentStage: string | null = null; private currentStage: string | null = null;
@@ -298,7 +297,7 @@ export class InteractiveAuth<T> {
public async attemptAuth(): Promise<T> { public async attemptAuth(): Promise<T> {
// This promise will be quite long-lived and will resolve when the // This promise will be quite long-lived and will resolve when the
// request is authenticated and completes successfully. // 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 // pluck the promise out now, as doRequest may clear before we return
const promise = this.attemptAuthDeferred.promise; const promise = this.attemptAuthDeferred.promise;

View File

@@ -169,7 +169,7 @@ export class MembershipManager
} }
this.fociPreferred = fociPreferred; this.fociPreferred = fociPreferred;
this.focusActive = focusActive; this.focusActive = focusActive;
this.leavePromiseDefer = undefined; this.leavePromiseResolvers = undefined;
this.activated = true; this.activated = true;
this.oldStatus = this.status; this.oldStatus = this.status;
this.state = MembershipManager.defaultState; this.state = MembershipManager.defaultState;
@@ -188,8 +188,8 @@ export class MembershipManager
this.emit(MembershipManagerEvent.StatusChanged, this.oldStatus, this.status); this.emit(MembershipManagerEvent.StatusChanged, this.oldStatus, this.status);
} }
if (!this.scheduler.running) { if (!this.scheduler.running) {
this.leavePromiseDefer?.resolve(true); this.leavePromiseResolvers?.resolve(true);
this.leavePromiseDefer = undefined; this.leavePromiseResolvers = undefined;
} }
}); });
} }
@@ -207,16 +207,16 @@ export class MembershipManager
// We use the promise to track if we already scheduled a leave event // We use the promise to track if we already scheduled a leave event
// So we do not check scheduler.actions/scheduler.insertions // 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. // reset scheduled actions so we will not do any new actions.
this.leavePromiseDefer = defer<boolean>(); this.leavePromiseResolvers = defer<boolean>();
this.activated = false; this.activated = false;
this.scheduler.initiateLeave(); 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<boolean>; private leavePromiseResolvers?: IDeferred<boolean>;
public async onRTCSessionMemberUpdate(memberships: CallMembership[]): Promise<void> { public async onRTCSessionMemberUpdate(memberships: CallMembership[]): Promise<void> {
const userId = this.client.getUserId(); const userId = this.client.getUserId();

View File

@@ -18,7 +18,7 @@ import { type OlmMachine, type OutgoingRequest } from "@matrix-org/matrix-sdk-cr
import { type OutgoingRequestProcessor } from "./OutgoingRequestProcessor.ts"; import { type OutgoingRequestProcessor } from "./OutgoingRequestProcessor.ts";
import { type Logger } from "../logger.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. * 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 * will resolve once that next iteration completes. If it is undefined, there have been no new calls
* to `doProcessOutgoingRequests` since the current iteration started. * to `doProcessOutgoingRequests` since the current iteration started.
*/ */
private nextLoopDeferred?: IDeferred<void>; private nextLoopDeferred?: PromiseWithResolvers<void>;
public constructor( public constructor(
private readonly logger: Logger, 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 // 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. // queue appears to be empty.
if (!this.nextLoopDeferred) { if (!this.nextLoopDeferred) {
this.nextLoopDeferred = defer(); this.nextLoopDeferred = Promise.withResolvers();
} }
// ... and wait for it to complete. // ... and wait for it to complete.
@@ -99,14 +99,14 @@ export class OutgoingRequestsManager {
this.outgoingRequestLoopRunning = true; this.outgoingRequestLoopRunning = true;
try { try {
while (!this.stopped && this.nextLoopDeferred) { while (!this.stopped && this.nextLoopDeferred) {
const deferred = this.nextLoopDeferred; const loopTickResolvers = this.nextLoopDeferred;
// reset `nextLoopDeferred` so that any future calls to `doProcessOutgoingRequests` are queued // reset `nextLoopDeferred` so that any future calls to `doProcessOutgoingRequests` are queued
// for another additional iteration. // for another additional iteration.
this.nextLoopDeferred = undefined; this.nextLoopDeferred = undefined;
// make the requests and feed the results back to the `nextLoopDeferred` // 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 { } finally {
this.outgoingRequestLoopRunning = false; this.outgoingRequestLoopRunning = false;

View File

@@ -34,7 +34,6 @@ import { type OutgoingRequestProcessor } from "./OutgoingRequestProcessor.ts";
import { TypedReEmitter } from "../ReEmitter.ts"; import { TypedReEmitter } from "../ReEmitter.ts";
import { type MatrixEvent } from "../models/event.ts"; import { type MatrixEvent } from "../models/event.ts";
import { EventType, MsgType } from "../@types/event.ts"; import { EventType, MsgType } from "../@types/event.ts";
import { defer, type IDeferred } from "../utils.ts";
import { VerificationMethod } from "../types.ts"; import { VerificationMethod } from "../types.ts";
import type { Logger } from "../logger.ts"; import type { Logger } from "../logger.ts";
@@ -478,7 +477,7 @@ abstract class BaseRustVerifer<InnerType extends RustSdkCryptoJs.Qr | RustSdkCry
VerifierEventHandlerMap & VerificationRequestEventHandlerMap VerifierEventHandlerMap & VerificationRequestEventHandlerMap
> { > {
/** A deferred which completes when the verification completes (or rejects when it is cancelled/fails) */ /** A deferred which completes when the verification completes (or rejects when it is cancelled/fails) */
protected readonly completionDeferred: IDeferred<void>; protected readonly completionDeferred: PromiseWithResolvers<void>;
public constructor( public constructor(
protected inner: InnerType, protected inner: InnerType,
@@ -486,7 +485,7 @@ abstract class BaseRustVerifer<InnerType extends RustSdkCryptoJs.Qr | RustSdkCry
) { ) {
super(); super();
this.completionDeferred = defer(); this.completionDeferred = Promise.withResolvers();
// As with RustVerificationRequest, we need to avoid a reference cycle. // As with RustVerificationRequest, we need to avoid a reference cycle.
// See the comments in RustVerificationRequest. // See the comments in RustVerificationRequest.

View File

@@ -21,7 +21,7 @@ limitations under the License.
import { logger } from "./logger.ts"; import { logger } from "./logger.ts";
import { type MatrixEvent } from "./models/event.ts"; import { type MatrixEvent } from "./models/event.ts";
import { EventType } from "./@types/event.ts"; import { EventType } from "./@types/event.ts";
import { defer, type IDeferred, removeElement } from "./utils.ts"; import { removeElement } from "./utils.ts";
import { calculateRetryBackoff, type MatrixError } from "./http-api/index.ts"; import { calculateRetryBackoff, type MatrixError } from "./http-api/index.ts";
import { type ISendEventResponse } from "./@types/requests.ts"; import { type ISendEventResponse } from "./@types/requests.ts";
@@ -29,7 +29,7 @@ const DEBUG = false; // set true to enable console logging.
interface IQueueEntry<T> { interface IQueueEntry<T> {
event: MatrixEvent; event: MatrixEvent;
defer: IDeferred<T>; resolvers: PromiseWithResolvers<T>;
attempts: number; attempts: number;
} }
@@ -70,7 +70,7 @@ export class MatrixScheduler<T = ISendEventResponse> {
// queueName: [{ // queueName: [{
// event: MatrixEvent, // event to send // 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 // attempts: Number // number of times we've called processFn
// }, ...] // }, ...]
private readonly queues: Record<string, IQueueEntry<T>[]> = {}; private readonly queues: Record<string, IQueueEntry<T>[]> = {};
@@ -188,15 +188,15 @@ export class MatrixScheduler<T = ISendEventResponse> {
if (!this.queues[queueName]) { if (!this.queues[queueName]) {
this.queues[queueName] = []; this.queues[queueName] = [];
} }
const deferred = defer<T>(); const eventResolvers = Promise.withResolvers<T>();
this.queues[queueName].push({ this.queues[queueName].push({
event: event, event: event,
defer: deferred, resolvers: eventResolvers,
attempts: 0, attempts: 0,
}); });
debuglog("Queue algorithm dumped event %s into queue '%s'", event.getId(), queueName); debuglog("Queue algorithm dumped event %s into queue '%s'", event.getId(), queueName);
this.startProcessingQueues(); this.startProcessingQueues();
return deferred.promise; return eventResolvers.promise;
} }
private startProcessingQueues(): void { private startProcessingQueues(): void {
@@ -239,7 +239,7 @@ export class MatrixScheduler<T = ISendEventResponse> {
// remove this from the queue // remove this from the queue
this.removeNextEvent(queueName); this.removeNextEvent(queueName);
debuglog("Queue '%s' sent event %s", queueName, obj.event.getId()); debuglog("Queue '%s' sent event %s", queueName, obj.event.getId());
obj.defer.resolve(res); obj.resolvers.resolve(res);
// keep processing // keep processing
this.processQueue(queueName); this.processQueue(queueName);
}, },
@@ -279,7 +279,7 @@ export class MatrixScheduler<T = ISendEventResponse> {
logger.info("clearing queue '%s'", queueName); logger.info("clearing queue '%s'", queueName);
let obj: IQueueEntry<T> | undefined; let obj: IQueueEntry<T> | undefined;
while ((obj = this.removeNextEvent(queueName))) { while ((obj = this.removeNextEvent(queueName))) {
obj.defer.reject(err); obj.resolvers.reject(err);
} }
this.disableQueue(queueName); this.disableQueue(queueName);
} }

View File

@@ -15,7 +15,6 @@ limitations under the License.
*/ */
import { logger } from "../logger.ts"; import { logger } from "../logger.ts";
import { defer, type IDeferred } from "../utils.ts";
import { type ISavedSync } from "./index.ts"; import { type ISavedSync } from "./index.ts";
import { type IStoredClientOpts } from "../client.ts"; import { type IStoredClientOpts } from "../client.ts";
import { type IStateEventWithRoomId, type ISyncResponse } from "../matrix.ts"; import { type IStateEventWithRoomId, type ISyncResponse } from "../matrix.ts";
@@ -26,7 +25,7 @@ export class RemoteIndexedDBStoreBackend implements IIndexedDBBackend {
private worker?: Worker; private worker?: Worker;
private nextSeq = 0; private nextSeq = 0;
// The currently in-flight requests to the actual backend // The currently in-flight requests to the actual backend
private inFlight: Record<number, IDeferred<any>> = {}; // seq: promise private inFlight: Record<number, PromiseWithResolvers<any>> = {}; // seq: promise
// Once we start connecting, we keep the promise and re-use it // Once we start connecting, we keep the promise and re-use it
// if we try to connect again // if we try to connect again
private startPromise?: Promise<void>; private startPromise?: Promise<void>;
@@ -164,7 +163,7 @@ export class RemoteIndexedDBStoreBackend implements IIndexedDBBackend {
// the promise automatically gets rejected // the promise automatically gets rejected
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
const seq = this.nextSeq++; const seq = this.nextSeq++;
const def = defer<T>(); const def = Promise.withResolvers<T>();
this.inFlight[seq] = def; this.inFlight[seq] = def;

View File

@@ -28,7 +28,7 @@ import { type Optional } from "matrix-events-sdk";
import type { SyncCryptoCallbacks } from "./common-crypto/CryptoBackend.ts"; import type { SyncCryptoCallbacks } from "./common-crypto/CryptoBackend.ts";
import { User } from "./models/user.ts"; import { User } from "./models/user.ts";
import { NotificationCountType, Room, RoomEvent } from "./models/room.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 { Filter } from "./filter.ts";
import { EventTimeline } from "./models/event-timeline.ts"; import { EventTimeline } from "./models/event-timeline.ts";
import { logger } from "./logger.ts"; import { logger } from "./logger.ts";
@@ -220,7 +220,7 @@ export class SyncApi {
private catchingUp = false; private catchingUp = false;
private running = false; private running = false;
private keepAliveTimer?: ReturnType<typeof setTimeout>; private keepAliveTimer?: ReturnType<typeof setTimeout>;
private connectionReturnedDefer?: IDeferred<boolean>; private connectionReturnedResolvers?: PromiseWithResolvers<boolean>;
private notifEvents: MatrixEvent[] = []; // accumulator of sync events in the current sync response private notifEvents: MatrixEvent[] = []; // accumulator of sync events in the current sync response
private failedSyncCount = 0; // Number of consecutive failed /sync requests 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 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. * @returns True if this resulted in a request being retried.
*/ */
public retryImmediately(): boolean { public retryImmediately(): boolean {
if (!this.connectionReturnedDefer) { if (!this.connectionReturnedResolvers) {
return false; return false;
} }
this.startKeepAlives(0); this.startKeepAlives(0);
@@ -916,9 +916,9 @@ export class SyncApi {
if (!this.running) { if (!this.running) {
debuglog("Sync no longer running: exiting."); debuglog("Sync no longer running: exiting.");
if (this.connectionReturnedDefer) { if (this.connectionReturnedResolvers) {
this.connectionReturnedDefer.reject(); this.connectionReturnedResolvers.reject();
this.connectionReturnedDefer = undefined; this.connectionReturnedResolvers = undefined;
} }
this.updateSyncState(SyncState.Stopped); this.updateSyncState(SyncState.Stopped);
} }
@@ -999,9 +999,9 @@ export class SyncApi {
private async onSyncError(err: MatrixError): Promise<boolean> { private async onSyncError(err: MatrixError): Promise<boolean> {
if (!this.running) { if (!this.running) {
debuglog("Sync no longer running: exiting"); debuglog("Sync no longer running: exiting");
if (this.connectionReturnedDefer) { if (this.connectionReturnedResolvers) {
this.connectionReturnedDefer.reject(); this.connectionReturnedResolvers.reject();
this.connectionReturnedDefer = undefined; this.connectionReturnedResolvers = undefined;
} }
this.updateSyncState(SyncState.Stopped); this.updateSyncState(SyncState.Stopped);
return true; // abort return true; // abort
@@ -1551,10 +1551,10 @@ export class SyncApi {
} else { } else {
this.pokeKeepAlive(); this.pokeKeepAlive();
} }
if (!this.connectionReturnedDefer) { if (!this.connectionReturnedResolvers) {
this.connectionReturnedDefer = defer(); this.connectionReturnedResolvers = Promise.withResolvers();
} }
return this.connectionReturnedDefer.promise; return this.connectionReturnedResolvers.promise;
} }
/** /**
@@ -1562,7 +1562,7 @@ export class SyncApi {
* reachable. * reachable.
* *
* On failure, schedules a call back to itself. On success, resolves * 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. * @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 // we are in a keepAlive, retrying to connect, but the syncronization
// was stopped, so we are stopping the retry. // was stopped, so we are stopping the retry.
clearTimeout(this.keepAliveTimer); clearTimeout(this.keepAliveTimer);
if (this.connectionReturnedDefer) { if (this.connectionReturnedResolvers) {
this.connectionReturnedDefer.reject("SyncApi.stop() was called"); this.connectionReturnedResolvers.reject("SyncApi.stop() was called");
this.connectionReturnedDefer = undefined; this.connectionReturnedResolvers = undefined;
} }
return; return;
} }
const success = (): void => { const success = (): void => {
clearTimeout(this.keepAliveTimer); clearTimeout(this.keepAliveTimer);
if (this.connectionReturnedDefer) { if (this.connectionReturnedResolvers) {
this.connectionReturnedDefer.resolve(connDidFail); this.connectionReturnedResolvers.resolve(connDidFail);
this.connectionReturnedDefer = undefined; this.connectionReturnedResolvers = undefined;
} }
}; };

View File

@@ -436,23 +436,14 @@ export function isNullOrUndefined(val: any): boolean {
return val === null || val === undefined; return val === null || val === undefined;
} }
export interface IDeferred<T> { export type IDeferred<T> = PromiseWithResolvers<T>;
resolve: (value: T | Promise<T>) => void;
reject: (reason?: any) => void;
promise: Promise<T>;
}
// 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<T = void>(): IDeferred<T> { export function defer<T = void>(): IDeferred<T> {
let resolve!: IDeferred<T>["resolve"]; return Promise.withResolvers<T>();
let reject!: IDeferred<T>["reject"];
const promise = new Promise<T>((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
return { resolve, reject, promise };
} }
export async function promiseMapSeries<T>( export async function promiseMapSeries<T>(