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/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

View File

@@ -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<FetchMock.MockResponse>();
const sendRoomMessageResolvers = Promise.withResolvers<FetchMock.MockResponse>();
const reqProm = new Promise<IContent>((resolve) => {
fetchMock.putOnce(
new RegExp("/send/m.room.encrypted/"),
async (url: string, opts: RequestInit): Promise<FetchMock.MockResponse> => {
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

View File

@@ -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<boolean>();
const keyBackupStatus = Promise.withResolvers<boolean>();
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<void> = defer<void>();
const awaitHasQueriedNewBackup: PromiseWithResolvers<void> = Promise.withResolvers<void>();
fetchMock.get(
"express:/_matrix/client/v3/room_keys/keys/:room_id/:session_id",

View File

@@ -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<string, Promise<string>> {
const mskRequestDefer = defer<string>();
const sskRequestDefer = defer<string>();
const uskRequestDefer = defer<string>();
const backupKeyRequestDefer = defer<string>();
const mskRequestResolvers = Promise.withResolvers<string>();
const sskRequestResolvers = Promise.withResolvers<string>();
const uskRequestResolvers = Promise.withResolvers<string>();
const backupKeyRequestResolvers = Promise.withResolvers<string>();
fetchMock.put(
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 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<string, Promise<string>> {
);
const promiseMap = new Map<string, Promise<string>>();
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;
}

View File

@@ -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 {

View File

@@ -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<string>();
let opponentData = defer<string>();
let ourData = Promise.withResolvers<string>();
let opponentData = Promise.withResolvers<string>();
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<IMyDevice>();
mocked(client.getDevice).mockReturnValue(deferred.promise);
const deviceResolvers = Promise.withResolvers<IMyDevice>();
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" },

View File

@@ -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<boolean>();
const seenLiveEventDeferred = Promise.withResolvers<boolean>();
const listener = (
ev: MatrixEvent,
room?: Room,

View File

@@ -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<void>();
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<void>();
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<void>();
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;
});
});

View File

@@ -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<Response>();
const fetchFn = jest.fn().mockReturnValue(deferred.promise);
const responseResolvers = Promise.withResolvers<Response>();
const fetchFn = jest.fn().mockReturnValue(responseResolvers.promise);
const mockLogger = {
debug: jest.fn(),
} 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");
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,

View File

@@ -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<void>();
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();
});

View File

@@ -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<any>, returnVal?: Promise<any>) {
@@ -51,7 +50,7 @@ function waitForMockCallOnce(method: MockedFunction<any>, returnVal?: Promise<an
}
function createAsyncHandle(method: MockedFunction<any>) {
const { reject, resolve, promise } = defer();
const { reject, resolve, promise } = Promise.withResolvers<void>();
method.mockImplementation(() => promise);
return { reject, resolve };
}

View File

@@ -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);

View File

@@ -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<void>();
mockClient.decryptEventIfNeeded.mockReturnValue(deferred.promise);
const decryptEventResolvers = Promise.withResolvers<void>();
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]);

View File

@@ -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<string>();
const authRequestResultResolvers = Promise.withResolvers<string>();
const authRequestCalledPromise = new Promise<void>((resolve) => {
const mockHttpApi = {
authedRequest: async () => {
resolve();
return await authRequestResultDefer.promise;
return await authRequestResultResolvers.promise;
},
} as unknown as Mocked<MatrixHttpApi<IHttpOpts & { onlyData: true }>>;
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;

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 { 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<OutgoingRequest[]>();
const firstOutgoingRequestResolvers = Promise.withResolvers<OutgoingRequest[]>();
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<void>[] = [];
// deferreds which will provide the results of OlmMachine.outgoingRequests
const outgoingRequestResultDeferreds: IDeferred<OutgoingRequest[]>[] = [];
const outgoingRequestResultDeferreds: PromiseWithResolvers<OutgoingRequest[]>[] = [];
for (let i = 0; i < 3; i++) {
const resultDeferred = defer<OutgoingRequest[]>();
const resultDeferred = Promise.withResolvers<OutgoingRequest[]>();
const calledPromise = new Promise<void>((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<OutgoingRequest[]>();
const firstOutgoingRequestResolvers = Promise.withResolvers<OutgoingRequest[]>();
olmMachine.outgoingRequests.mockImplementationOnce(async (): Promise<OutgoingRequest[]> => {
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<void>();
const firstRequestResolvers = Promise.withResolvers<void>();
olmMachine.outgoingRequests.mockImplementationOnce(async (): Promise<OutgoingRequest[]> => {
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;

View File

@@ -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<OlmMachine>;
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) {
const deferred = defer<void>();
const sessionImportedResolvers = Promise.withResolvers<void>();
if (!expectedSession[roomId]) {
expectedSession[roomId] = {};
}
expectedSession[roomId][sessionId] = deferred;
return deferred.promise;
expectedSession[roomId][sessionId] = sessionImportedResolvers;
return sessionImportedResolvers.promise;
}
function mockClearSession(sessionId: string): Mocked<IMegolmSessionData> {
@@ -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<void>();
const awaitKeyImported = Promise.withResolvers<void>();
const roomId = "!roomId";
const sessionId = "sessionId";
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 () => {
const blockOnServerRequest = defer<void>();
const blockOnServerRequest = Promise.withResolvers<void>();
fetchMock.get(`express:/_matrix/client/v3/room_keys/keys/!roomId/:session_id`, async (url, request) => {
await blockOnServerRequest.promise;
return [mockCipherKey];
});
const awaitKey2Imported = defer<void>();
const awaitKey2Imported = Promise.withResolvers<void>();
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<void>();
const requestRoomKeyCalled = defer<void>();
const blockOnServerRequest = Promise.withResolvers<void>();
const requestRoomKeyCalled = Promise.withResolvers<void>();
// 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<void>();
const rateDeferred = Promise.withResolvers<void>();
keyQuerySpy.mockImplementation(
// @ts-ignore
@@ -544,7 +543,7 @@ describe("PerSessionKeyBackupDownloader", () => {
// @ts-ignore
const keyQuerySpy: SpyInstance = jest.spyOn(downloader, "queryKeyBackup");
const errorDeferred = defer<void>();
const errorDeferred = Promise.withResolvers<void>();
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<void>();
const keyImported = Promise.withResolvers<void>();
mockRustBackupManager.importBackedUpRoomKeys
.mockImplementationOnce(async () => {
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 { 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<void>();
const insideOlmShareRoom = defer<void>();
const deferredShare = Promise.withResolvers<void>();
const insideOlmShareRoom = Promise.withResolvers<void>();
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<void>();
const secondTargetMembers = defer<void>();
const firstTargetMembers = Promise.withResolvers<void>();
const secondTargetMembers = Promise.withResolvers<void>();
mockOlmMachine.shareRoomKey.mockResolvedValue([]);

View File

@@ -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<Array<any>>();
mocked(olmMachine.outgoingRequests).mockReturnValueOnce(firstOutgoingRequestsDefer.promise);
const firstOutgoingRequestsResolvers = Promise.withResolvers<Array<any>>();
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);
});

View File

@@ -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<Record<string, boolean>>;
let retryFn: ((event: MatrixEvent | null, attempt: number, err: MatrixError) => number) | 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 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<Record<string, boolean>>();
const deferB = defer<Record<string, boolean>>();
const deferA = Promise.withResolvers<Record<string, boolean>>();
const deferB = Promise.withResolvers<Record<string, boolean>>();
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<void>();
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<Record<string, boolean>>();
const deferB = defer<Record<string, boolean>>();
const deferA = Promise.withResolvers<Record<string, boolean>>();
const deferB = Promise.withResolvers<Record<string, boolean>>();
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<Record<string, boolean>>();
const deferA = Promise.withResolvers<Record<string, boolean>>();
const allExpectedEventsSeenInOrderPromise = new Promise((resolve) => {
scheduler.setProcessFunction(function (event) {
const id = expectOrder.shift();

View File

@@ -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<AccountDataClient>;
let accountDataPromise: IDeferred<void>;
let accountDataPromise: PromiseWithResolvers<void>;
beforeEach(() => {
accountDataAdapter = mockAccountDataClient();
accountDataPromise = defer();
accountDataPromise = Promise.withResolvers();
accountDataAdapter.setAccountData.mockImplementation(() => {
accountDataPromise.resolve();
return Promise.resolve({});

View File

@@ -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<void>();
const postMessageSuccessResolvers = Promise.withResolvers<void>();
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);

View File

@@ -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<Event>();
const setupResolvers = Promise.withResolvers<Event>();
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<Event>();
const writeResolvers = Promise.withResolvers<Event>();
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<Event>();
const requestSuccessResolvers = Promise.withResolvers<Event>();
// 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<void>();
store.on("closed", deferred.resolve);
const storeClosedResolvers = Promise.withResolvers<void>();
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<void>();
const workerPostMessageResolvers = Promise.withResolvers<void>();
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<void>();
store.on("closed", deferred.resolve);
const storeClosedResolvers = Promise.withResolvers<void>();
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 () => {

View File

@@ -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<EmittedEvents, ClientEventHa
if (existingData && deepCompare(existingData.event.content, content)) return {};
// 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 {
// 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<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
// type matches.
if (event.getType() === eventType) updatedDefer.resolve();
if (event.getType() === eventType) updatedResolvers.resolve();
}
this.addListener(ClientEvent.AccountData, accountDataListener);
try {
const result = await retryNetworkOperation(5, () => 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<EmittedEvents, ClientEventHa
} else if (!hasDontNotifyRule) {
// Remove the existing one before setting the mute push rule
// 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)
.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;
}
}

View File

@@ -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<O extends IHttpOpts> extends FetchHttpApi<O> {
total: 0,
abortController,
} as Upload;
const deferred = defer<UploadResponse>();
const uploadResolvers = Promise.withResolvers<UploadResponse>();
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<O extends IHttpOpts> extends FetchHttpApi<O> {
}
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 ((<Error>err).name === "AbortError") {
deferred.reject(err);
uploadResolvers.reject(err);
return;
}
deferred.reject(new ConnectionError("request failed", <Error>err));
uploadResolvers.reject(new ConnectionError("request failed", <Error>err));
}
break;
}
@@ -153,16 +153,16 @@ export class MatrixHttpApi<O extends IHttpOpts> extends FetchHttpApi<O> {
.then((response) => {
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
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;

View File

@@ -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<T> {
private data: IAuthData & MatrixError["data"];
private emailSid?: string;
private requestingEmailToken = false;
private attemptAuthDeferred: IDeferred<T> | null = null;
private attemptAuthDeferred: PromiseWithResolvers<T> | null = null;
private chosenFlow: UIAFlow | null = null;
private currentStage: string | null = null;
@@ -298,7 +297,7 @@ export class InteractiveAuth<T> {
public async attemptAuth(): Promise<T> {
// 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;

View File

@@ -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<boolean>();
this.leavePromiseResolvers = defer<boolean>();
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<boolean>;
private leavePromiseResolvers?: IDeferred<boolean>;
public async onRTCSessionMemberUpdate(memberships: CallMembership[]): Promise<void> {
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 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<void>;
private nextLoopDeferred?: PromiseWithResolvers<void>;
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;

View File

@@ -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<InnerType extends RustSdkCryptoJs.Qr | RustSdkCry
VerifierEventHandlerMap & VerificationRequestEventHandlerMap
> {
/** 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(
protected inner: InnerType,
@@ -486,7 +485,7 @@ abstract class BaseRustVerifer<InnerType extends RustSdkCryptoJs.Qr | RustSdkCry
) {
super();
this.completionDeferred = defer();
this.completionDeferred = Promise.withResolvers();
// As with RustVerificationRequest, we need to avoid a reference cycle.
// See the comments in RustVerificationRequest.

View File

@@ -21,7 +21,7 @@ limitations under the License.
import { logger } from "./logger.ts";
import { type MatrixEvent } from "./models/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 { type ISendEventResponse } from "./@types/requests.ts";
@@ -29,7 +29,7 @@ const DEBUG = false; // set true to enable console logging.
interface IQueueEntry<T> {
event: MatrixEvent;
defer: IDeferred<T>;
resolvers: PromiseWithResolvers<T>;
attempts: number;
}
@@ -70,7 +70,7 @@ export class MatrixScheduler<T = ISendEventResponse> {
// 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<string, IQueueEntry<T>[]> = {};
@@ -188,15 +188,15 @@ export class MatrixScheduler<T = ISendEventResponse> {
if (!this.queues[queueName]) {
this.queues[queueName] = [];
}
const deferred = defer<T>();
const eventResolvers = Promise.withResolvers<T>();
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<T = ISendEventResponse> {
// 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<T = ISendEventResponse> {
logger.info("clearing queue '%s'", queueName);
let obj: IQueueEntry<T> | undefined;
while ((obj = this.removeNextEvent(queueName))) {
obj.defer.reject(err);
obj.resolvers.reject(err);
}
this.disableQueue(queueName);
}

View File

@@ -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<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
// if we try to connect again
private startPromise?: Promise<void>;
@@ -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<T>();
const def = Promise.withResolvers<T>();
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 { 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<typeof setTimeout>;
private connectionReturnedDefer?: IDeferred<boolean>;
private connectionReturnedResolvers?: PromiseWithResolvers<boolean>;
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<boolean> {
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;
}
};

View File

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