Merge pull request #584 from vector-im/ts-conversion-utils

Convert /utils to typescript
This commit is contained in:
Bruno Windels 2021-11-30 14:13:09 +01:00 committed by GitHub
commit ef712b16f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 104 additions and 90 deletions

View file

@ -19,7 +19,7 @@ limitations under the License.
// we do need to return a disposable from EventEmitter.on, or at least have a method here to easily track a subscription to an EventEmitter
import {EventEmitter} from "../utils/EventEmitter";
import {Disposables} from "../utils/Disposables.js";
import {Disposables} from "../utils/Disposables";
export class ViewModel extends EventEmitter {
constructor(options = {}) {

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import {ViewModel} from "../ViewModel.js";
import {createEnum} from "../../utils/enum.js";
import {createEnum} from "../../utils/enum";
import {ConnectionStatus} from "../../matrix/net/Reconnector.js";
import {SyncStatus} from "../../matrix/Sync.js";

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import {BaseObservableList} from "../../../../observable/list/BaseObservableList";
import {sortedIndex} from "../../../../utils/sortedIndex.js";
import {sortedIndex} from "../../../../utils/sortedIndex";
// maps 1..n entries to 0..1 tile. Entries are what is stored in the timeline, either an event or fragmentboundary
// for now, tileCreator should be stable in whether it returns a tile or not.

View file

@ -16,7 +16,7 @@ limitations under the License.
import {BaseMessageTile} from "./BaseMessageTile.js";
import {stringAsBody} from "../MessageBody.js";
import {createEnum} from "../../../../../utils/enum.js";
import {createEnum} from "../../../../../utils/enum";
export const BodyFormat = createEnum("Plain", "Html");

View file

@ -16,7 +16,7 @@ limitations under the License.
*/
import {BaseMessageTile} from "./BaseMessageTile.js";
import {formatSize} from "../../../../../utils/formatSize.js";
import {formatSize} from "../../../../../utils/formatSize";
import {SendStatus} from "../../../../../matrix/room/sending/PendingEvent.js";
export class FileTile extends BaseMessageTile {

View file

@ -16,7 +16,7 @@ limitations under the License.
import {ViewModel} from "../../ViewModel.js";
import {KeyType} from "../../../matrix/ssss/index.js";
import {createEnum} from "../../../utils/enum.js";
import {createEnum} from "../../../utils/enum";
export const Status = createEnum("Enabled", "SetupKey", "SetupPhrase", "Pending");

View file

@ -34,7 +34,7 @@ import {Encryption as MegOlmEncryption} from "./e2ee/megolm/Encryption.js";
import {MEGOLM_ALGORITHM} from "./e2ee/common.js";
import {RoomEncryption} from "./e2ee/RoomEncryption.js";
import {DeviceTracker} from "./e2ee/DeviceTracker.js";
import {LockMap} from "../utils/LockMap.js";
import {LockMap} from "../utils/LockMap";
import {groupBy} from "../utils/groupBy";
import {
keyFromCredential as ssssKeyFromCredential,

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {createEnum} from "../utils/enum.js";
import {createEnum} from "../utils/enum";
import {lookupHomeserver} from "./well-known.js";
import {AbortableOperation} from "../utils/AbortableOperation";
import {ObservableValue} from "../observable/ObservableValue";

View file

@ -16,7 +16,7 @@ limitations under the License.
*/
import {ObservableValue} from "../observable/ObservableValue";
import {createEnum} from "../utils/enum.js";
import {createEnum} from "../utils/enum";
const INCREMENTAL_TIMEOUT = 30000;

View file

@ -16,7 +16,7 @@ limitations under the License.
import {MEGOLM_ALGORITHM, DecryptionSource} from "./common.js";
import {groupEventsBySession} from "./megolm/decryption/utils";
import {mergeMap} from "../../utils/mergeMap.js";
import {mergeMap} from "../../utils/mergeMap";
import {groupBy} from "../../utils/groupBy";
import {makeTxnId} from "../common.js";

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import anotherjson from "../../../lib/another-json/index.js";
import {createEnum} from "../../utils/enum.js";
import {createEnum} from "../../utils/enum";
export const DecryptionSource = createEnum("Sync", "Timeline", "Retry");

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import {DecryptionChanges} from "./DecryptionChanges.js";
import {mergeMap} from "../../../../utils/mergeMap.js";
import {mergeMap} from "../../../../utils/mergeMap";
/**
* Class that contains all the state loaded from storage to decrypt the given events

View file

@ -16,7 +16,7 @@ limitations under the License.
import {DecryptionError} from "../common.js";
import {groupBy} from "../../../utils/groupBy";
import {MultiLock} from "../../../utils/Lock.js";
import {MultiLock} from "../../../utils/Lock";
import {Session} from "./Session.js";
import {DecryptionResult} from "../DecryptionResult.js";

View file

@ -38,7 +38,7 @@ export class HomeServerError extends Error {
}
}
export {AbortError} from "../utils/error.js";
export {AbortError} from "../utils/error";
export class ConnectionError extends Error {
constructor(message, isTimeout) {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {AbortError} from "../../utils/error.js";
import {AbortError} from "../../utils/error";
export class ExponentialRetryDelay {
constructor(createTimeout) {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {createEnum} from "../../utils/enum.js";
import {createEnum} from "../../utils/enum";
import {ObservableValue} from "../../observable/ObservableValue";
export const ConnectionStatus = createEnum(

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {AbortError} from "../../utils/error.js";
import {AbortError} from "../../utils/error";
import {HomeServerError} from "../error.js";
import {HomeServerApi} from "./HomeServerApi.js";
import {ExponentialRetryDelay} from "./ExponentialRetryDelay.js";

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import {ObservableMap} from "../../../observable/map/ObservableMap.js";
import {RetainedValue} from "../../../utils/RetainedValue.js";
import {RetainedValue} from "../../../utils/RetainedValue";
export class MemberList extends RetainedValue {
constructor({members, closeCallback}) {

View file

@ -13,8 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {createEnum} from "../../../utils/enum.js";
import {AbortError} from "../../../utils/error.js";
import {createEnum} from "../../../utils/enum";
import {AbortError} from "../../../utils/error";
import {REDACTION_TYPE} from "../common.js";
import {getRelationFromContent, getRelationTarget, setRelationTarget} from "../timeline/relations.js";

View file

@ -16,7 +16,7 @@ limitations under the License.
*/
import {SortedArray, AsyncMappedList, ConcatList, ObservableArray} from "../../../observable/index.js";
import {Disposables} from "../../../utils/Disposables.js";
import {Disposables} from "../../../utils/Disposables";
import {Direction} from "./Direction";
import {TimelineReader} from "./persistence/TimelineReader.js";
import {PendingEventEntry} from "./entries/PendingEventEntry.js";

View file

@ -18,7 +18,7 @@ import {KeyDescription, Key} from "./common.js";
import {keyFromPassphrase} from "./passphrase.js";
import {keyFromRecoveryKey} from "./recoveryKey.js";
import {SESSION_E2EE_KEY_PREFIX} from "../e2ee/common.js";
import {createEnum} from "../../utils/enum.js";
import {createEnum} from "../../utils/enum";
const SSSS_KEY = `${SESSION_E2EE_KEY_PREFIX}ssssKey`;

View file

@ -17,7 +17,7 @@ limitations under the License.
import { IDBRequestError } from "./error";
import { StorageError } from "../common";
import { AbortError } from "../../../utils/error.js";
import { AbortError } from "../../../utils/error";
let needsSyncPromise = false;

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {AbortError} from "../utils/error.js";
import {AbortError} from "../utils/error";
export class BaseRequest {
constructor() {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {AbortError} from "../utils/error.js";
import {AbortError} from "../utils/error";
import {BaseObservable} from "./BaseObservable";
// like an EventEmitter, but doesn't have an event type

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import {BaseObservableList} from "./BaseObservableList";
import {sortedIndex} from "../../utils/sortedIndex.js";
import {sortedIndex} from "../../utils/sortedIndex";
import {findAndUpdateInArray} from "./common";
export class SortedArray extends BaseObservableList {

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import {BaseObservableList} from "./BaseObservableList";
import {sortedIndex} from "../../utils/sortedIndex.js";
import {sortedIndex} from "../../utils/sortedIndex";
/*

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import aesjs from "../../../lib/aes-js/index.js";
import {hkdf} from "../../utils/crypto/hkdf.js";
import {hkdf} from "../../utils/crypto/hkdf";
import {Platform as ModernPlatform} from "./Platform.js";
export function Platform(container, paths) {

View file

@ -35,7 +35,7 @@ import {WorkerPool} from "./dom/WorkerPool.js";
import {BlobHandle} from "./dom/BlobHandle.js";
import {hasReadPixelPermission, ImageHandle, VideoHandle} from "./dom/ImageHandle.js";
import {downloadInIframe} from "./dom/download.js";
import {Disposables} from "../../utils/Disposables.js";
import {Disposables} from "../../utils/Disposables";
import {parseHTML} from "./parsehtml.js";
import {handleAvatarError} from "./ui/avatar.js";

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {AbortError} from "../../../utils/error.js";
import {AbortError} from "../../../utils/error";
class Timeout {
constructor(ms) {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {AbortError} from "../../../utils/error.js";
import {AbortError} from "../../../utils/error";
class WorkerState {
constructor(worker) {

View file

@ -19,7 +19,7 @@ import {
AbortError,
ConnectionError
} from "../../../../matrix/error.js";
import {abortOnTimeout} from "../../../../utils/timeout.js";
import {abortOnTimeout} from "../../../../utils/timeout";
import {addCacheBuster} from "./common.js";
import {xhrRequest} from "./xhr.js";

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
interface IAbortable {
export interface IAbortable {
abort();
}

View file

@ -14,7 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
function disposeValue(value) {
export interface IDisposable {
dispose(): void;
}
type Disposable = IDisposable | (() => void);
function disposeValue(value: Disposable): void {
if (typeof value === "function") {
value();
} else {
@ -22,16 +28,14 @@ function disposeValue(value) {
}
}
function isDisposable(value) {
function isDisposable(value: Disposable): boolean {
return value && (typeof value === "function" || typeof value.dispose === "function");
}
export class Disposables {
constructor() {
this._disposables = [];
}
private _disposables: Disposable[] | null = [];
track(disposable) {
track(disposable: Disposable): Disposable {
if (!isDisposable(disposable)) {
throw new Error("Not a disposable");
}
@ -40,19 +44,23 @@ export class Disposables {
disposeValue(disposable);
return disposable;
}
this._disposables.push(disposable);
this._disposables!.push(disposable);
return disposable;
}
untrack(disposable) {
const idx = this._disposables.indexOf(disposable);
untrack(disposable: Disposable): null {
if (this.isDisposed) {
console.warn("Disposables already disposed, cannot untrack");
return null;
}
const idx = this._disposables!.indexOf(disposable);
if (idx >= 0) {
this._disposables.splice(idx, 1);
this._disposables!.splice(idx, 1);
}
return null;
}
dispose() {
dispose(): void {
if (this._disposables) {
for (const d of this._disposables) {
disposeValue(d);
@ -61,17 +69,17 @@ export class Disposables {
}
}
get isDisposed() {
get isDisposed(): boolean {
return this._disposables === null;
}
disposeTracked(value) {
disposeTracked(value: Disposable): null {
if (value === undefined || value === null || this.isDisposed) {
return null;
}
const idx = this._disposables.indexOf(value);
const idx = this._disposables!.indexOf(value);
if (idx !== -1) {
const [foundValue] = this._disposables.splice(idx, 1);
const [foundValue] = this._disposables!.splice(idx, 1);
disposeValue(foundValue);
} else {
console.warn("disposable not found, did it leak?", value);

View file

@ -15,12 +15,10 @@ limitations under the License.
*/
export class Lock {
constructor() {
this._promise = null;
this._resolve = null;
}
private _promise?: Promise<void>;
private _resolve?: (() => void);
tryTake() {
tryTake(): boolean {
if (!this._promise) {
this._promise = new Promise(resolve => {
this._resolve = resolve;
@ -30,36 +28,36 @@ export class Lock {
return false;
}
async take() {
async take(): Promise<void> {
while(!this.tryTake()) {
await this.released();
}
}
get isTaken() {
get isTaken(): boolean {
return !!this._promise;
}
release() {
release(): void {
if (this._resolve) {
this._promise = null;
this._promise = undefined;
const resolve = this._resolve;
this._resolve = null;
this._resolve = undefined;
resolve();
}
}
released() {
released(): Promise<void> | undefined {
return this._promise;
}
}
export class MultiLock {
constructor(locks) {
this.locks = locks;
constructor(public readonly locks: Lock[]) {
}
release() {
release(): void {
for (const lock of this.locks) {
lock.release();
}
@ -86,9 +84,9 @@ export function tests() {
lock.tryTake();
let first;
lock.released().then(() => first = lock.tryTake());
lock.released()!.then(() => first = lock.tryTake());
let second;
lock.released().then(() => second = lock.tryTake());
lock.released()!.then(() => second = lock.tryTake());
const promise = lock.released();
lock.release();
await promise;

View file

@ -14,14 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {Lock} from "./Lock.js";
import {Lock} from "./Lock";
export class LockMap {
constructor() {
this._map = new Map();
}
export class LockMap<T> {
private readonly _map: Map<T, Lock> = new Map();
async takeLock(key) {
async takeLock(key: T): Promise<Lock> {
let lock = this._map.get(key);
if (lock) {
await lock.take();
@ -31,10 +29,10 @@ export class LockMap {
this._map.set(key, lock);
}
// don't leave old locks lying around
lock.released().then(() => {
lock.released()!.then(() => {
// give others a chance to take the lock first
Promise.resolve().then(() => {
if (!lock.isTaken) {
if (!lock!.isTaken) {
this._map.delete(key);
}
});
@ -67,6 +65,7 @@ export function tests() {
ranSecond = true;
assert.equal(returnedLock.isTaken, true);
// peek into internals, naughty
// @ts-ignore
assert.equal(lockMap._map.get("foo"), returnedLock);
});
lock.release();
@ -84,6 +83,7 @@ export function tests() {
// double delay to make sure cleanup logic ran
await Promise.resolve();
await Promise.resolve();
// @ts-ignore
assert.equal(lockMap._map.has("foo"), false);
},

View file

@ -15,16 +15,18 @@ limitations under the License.
*/
export class RetainedValue {
constructor(freeCallback) {
private readonly _freeCallback: () => void;
private _retentionCount: number = 1;
constructor(freeCallback: () => void) {
this._freeCallback = freeCallback;
this._retentionCount = 1;
}
retain() {
retain(): void {
this._retentionCount += 1;
}
release() {
release(): void {
this._retentionCount -= 1;
if (this._retentionCount === 0) {
this._freeCallback();

View file

@ -6,8 +6,10 @@
* Based on https://github.com/junkurihara/jscu/blob/develop/packages/js-crypto-hkdf/src/hkdf.ts
*/
import type {Crypto} from "../../platform/web/dom/Crypto.js";
// forked this code to make it use the cryptoDriver for HMAC that is more backwards-compatible
export async function hkdf(cryptoDriver, key, salt, info, hash, length) {
export async function hkdf(cryptoDriver: Crypto, key: Uint8Array, salt: Uint8Array, info: Uint8Array, hash: "SHA-256" | "SHA-512", length: number): Promise<Uint8Array> {
length = length / 8;
const len = cryptoDriver.digestSize(hash);

View file

@ -6,17 +6,19 @@
* Based on https://github.com/junkurihara/jscu/blob/develop/packages/js-crypto-pbkdf/src/pbkdf.ts
*/
import type {Crypto} from "../../platform/web/dom/Crypto.js";
// not used atm, but might in the future
// forked this code to make it use the cryptoDriver for HMAC that is more backwards-compatible
const nwbo = (num, len) => {
const nwbo = (num: number, len: number): Uint8Array => {
const arr = new Uint8Array(len);
for(let i=0; i<len; i++) arr[i] = 0xFF && (num >> ((len - i - 1)*8));
return arr;
};
export async function pbkdf2(cryptoDriver, password, iterations, salt, hash, length) {
export async function pbkdf2(cryptoDriver: Crypto, password: Uint8Array, iterations: number, salt: Uint8Array, hash: "SHA-256" | "SHA-512", length: number): Promise<Uint8Array> {
const dkLen = length / 8;
if (iterations <= 0) {
throw new Error('InvalidIterationCount');
@ -30,7 +32,7 @@ export async function pbkdf2(cryptoDriver, password, iterations, salt, hash, len
const l = Math.ceil(dkLen/hLen);
const r = dkLen - (l-1)*hLen;
const funcF = async (i) => {
const funcF = async (i: number) => {
const seed = new Uint8Array(salt.length + 4);
seed.set(salt);
seed.set(nwbo(i+1, 4), salt.length);
@ -46,7 +48,7 @@ export async function pbkdf2(cryptoDriver, password, iterations, salt, hash, len
return {index: i, value: outputF};
};
const Tis = [];
const Tis: Promise<{index: number, value: Uint8Array}>[] = [];
const DK = new Uint8Array(dkLen);
for(let i = 0; i < l; i++) {
Tis.push(funcF(i));

View file

@ -14,12 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
export function createEnum(...values) {
export function createEnum(...values: string[]): Readonly<{}> {
const obj = {};
for (const value of values) {
if (typeof value !== "string") {
throw new Error("Invalid enum value name" + value?.toString());
}
obj[value] = value;
}
return Object.freeze(obj);

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
export class AbortError extends Error {
get name() {
get name(): string {
return "AbortError";
}
}

View file

@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
export function formatSize(size, decimals = 2) {
export function formatSize(size: number, decimals: number = 2): string {
if (Number.isSafeInteger(size)) {
const base = Math.min(3, Math.floor(Math.log(size) / Math.log(1024)));
const formattedSize = Math.round(size / Math.pow(1024, base)).toFixed(decimals);
@ -25,4 +26,5 @@ export function formatSize(size, decimals = 2) {
case 3: return `${formattedSize} GB`;
}
}
return "";
}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
export function mergeMap(src, dst) {
export function mergeMap<K, V>(src: Map<K, V> | undefined, dst: Map<K, V>): void {
if (src) {
for (const [key, value] of src.entries()) {
dst.set(key, value);

View file

@ -22,7 +22,7 @@ limitations under the License.
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
export function sortedIndex(array, value, comparator) {
export function sortedIndex<T>(array: T[], value: T, comparator: (x:T, y:T) => number): number {
let low = 0;
let high = array.length;

View file

@ -16,9 +16,12 @@ limitations under the License.
*/
import {ConnectionError} from "../matrix/error.js";
import type {Timeout} from "../platform/web/dom/Clock.js"
import type {IAbortable} from "./AbortableOperation";
type TimeoutCreator = (ms: number) => Timeout;
export function abortOnTimeout(createTimeout, timeoutAmount, requestResult, responsePromise) {
export function abortOnTimeout(createTimeout: TimeoutCreator, timeoutAmount: number, requestResult: IAbortable, responsePromise: Promise<Response>) {
const timeout = createTimeout(timeoutAmount);
// abort request if timeout finishes first
let timedOut = false;