forked from mystiq/hydrogen-web
Merge branch 'master' into kegan/syncv3
This commit is contained in:
commit
f193418ed1
68 changed files with 533 additions and 394 deletions
|
@ -1,7 +1,9 @@
|
||||||
# Typescript migration
|
# Typescript style guide
|
||||||
|
|
||||||
## Introduce `abstract` & `override`
|
## Use `type` rather than `interface` for named parameters and POJO return values.
|
||||||
|
|
||||||
- find all methods and getters that throw or are empty in base classes and turn into abstract method or if all methods are abstract, into an interface.
|
`type` and `interface` can be used somewhat interchangebly used, but let's use `type` to describe data and `interface` to describe (polymorphic) behaviour.
|
||||||
- change child impls to not call super.method and to add override
|
|
||||||
- don't allow implicit override in ts config
|
Good examples of data are option objects to have named parameters, and POJO (plain old javascript objects) without any methods, just fields.
|
||||||
|
|
||||||
|
Also see [this playground](https://www.typescriptlang.org/play?#code/C4TwDgpgBACghgJwgO2AeTMAlge2QZygF4oBvAKCiqmTgFsIAuKfYBLZAcwG5LqATCABs4IAPzNkAVzoAjCAl4BfcuVCQoAYQAWWIfwzY8hEvCSpDuAlABkZPlQDGOITgTNW7LstWOR+QjMUYHtqKGcCNilHYDcAChxMK3xmIIsk4wBKewcoFRVyPzgArV19KAgAD2AUfkDEYNDqCM9o2IQEjIJmHT0DLvxsijCw-ClIDsSjAkzeEebjEIYAuE5oEgADABJSKeSAOloGJSgsQh29433nVwQlDbnqfKA)
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"fake-indexeddb": "^3.1.2",
|
"fake-indexeddb": "^3.1.2",
|
||||||
"finalhandler": "^1.1.1",
|
"finalhandler": "^1.1.1",
|
||||||
"impunity": "^1.0.8",
|
"impunity": "^1.0.9",
|
||||||
"mdn-polyfills": "^5.20.0",
|
"mdn-polyfills": "^5.20.0",
|
||||||
"postcss": "^8.1.1",
|
"postcss": "^8.1.1",
|
||||||
"postcss-css-variables": "^0.17.0",
|
"postcss-css-variables": "^0.17.0",
|
||||||
|
|
|
@ -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
|
// 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 {EventEmitter} from "../utils/EventEmitter";
|
||||||
import {Disposables} from "../utils/Disposables.js";
|
import {Disposables} from "../utils/Disposables";
|
||||||
|
|
||||||
export class ViewModel extends EventEmitter {
|
export class ViewModel extends EventEmitter {
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ViewModel} from "../ViewModel.js";
|
import {ViewModel} from "../ViewModel.js";
|
||||||
import {createEnum} from "../../utils/enum.js";
|
import {createEnum} from "../../utils/enum";
|
||||||
import {ConnectionStatus} from "../../matrix/net/Reconnector.js";
|
import {ConnectionStatus} from "../../matrix/net/Reconnector.js";
|
||||||
import {SyncStatus} from "../../matrix/Sync.js";
|
import {SyncStatus} from "../../matrix/Sync.js";
|
||||||
|
|
||||||
|
|
|
@ -188,7 +188,7 @@ import {NullLogItem, NullLogger} from "../../../../logging/NullLogger";
|
||||||
import {HomeServer as MockHomeServer} from "../../../../mocks/HomeServer.js";
|
import {HomeServer as MockHomeServer} from "../../../../mocks/HomeServer.js";
|
||||||
// other imports
|
// other imports
|
||||||
import {BaseMessageTile} from "./tiles/BaseMessageTile.js";
|
import {BaseMessageTile} from "./tiles/BaseMessageTile.js";
|
||||||
import {MappedList} from "../../../../observable/list/MappedList.js";
|
import {MappedList} from "../../../../observable/list/MappedList";
|
||||||
import {ObservableValue} from "../../../../observable/ObservableValue";
|
import {ObservableValue} from "../../../../observable/ObservableValue";
|
||||||
import {PowerLevels} from "../../../../matrix/room/PowerLevels.js";
|
import {PowerLevels} from "../../../../matrix/room/PowerLevels.js";
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {BaseObservableList} from "../../../../observable/list/BaseObservableList";
|
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
|
// 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.
|
// for now, tileCreator should be stable in whether it returns a tile or not.
|
||||||
|
@ -253,7 +253,7 @@ export class TilesCollection extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import {ObservableArray} from "../../../../observable/list/ObservableArray.js";
|
import {ObservableArray} from "../../../../observable/list/ObservableArray";
|
||||||
import {UpdateAction} from "./UpdateAction.js";
|
import {UpdateAction} from "./UpdateAction.js";
|
||||||
|
|
||||||
export function tests() {
|
export function tests() {
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import {BaseMessageTile} from "./BaseMessageTile.js";
|
import {BaseMessageTile} from "./BaseMessageTile.js";
|
||||||
import {stringAsBody} from "../MessageBody.js";
|
import {stringAsBody} from "../MessageBody.js";
|
||||||
import {createEnum} from "../../../../../utils/enum.js";
|
import {createEnum} from "../../../../../utils/enum";
|
||||||
|
|
||||||
export const BodyFormat = createEnum("Plain", "Html");
|
export const BodyFormat = createEnum("Plain", "Html");
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {BaseMessageTile} from "./BaseMessageTile.js";
|
import {BaseMessageTile} from "./BaseMessageTile.js";
|
||||||
import {formatSize} from "../../../../../utils/formatSize.js";
|
import {formatSize} from "../../../../../utils/formatSize";
|
||||||
import {SendStatus} from "../../../../../matrix/room/sending/PendingEvent.js";
|
import {SendStatus} from "../../../../../matrix/room/sending/PendingEvent.js";
|
||||||
|
|
||||||
export class FileTile extends BaseMessageTile {
|
export class FileTile extends BaseMessageTile {
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import {ViewModel} from "../../ViewModel.js";
|
import {ViewModel} from "../../ViewModel.js";
|
||||||
import {KeyType} from "../../../matrix/ssss/index.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");
|
export const Status = createEnum("Enabled", "SetupKey", "SetupPhrase", "Pending");
|
||||||
|
|
||||||
|
|
|
@ -17,15 +17,17 @@ limitations under the License.
|
||||||
|
|
||||||
import {LogItem} from "./LogItem";
|
import {LogItem} from "./LogItem";
|
||||||
import {LogLevel, LogFilter} from "./LogFilter";
|
import {LogLevel, LogFilter} from "./LogFilter";
|
||||||
import type {ILogger, ILogExport, FilterCreator, LabelOrValues, LogCallback, ILogItem} from "./types";
|
import type {ILogger, ILogExport, FilterCreator, LabelOrValues, LogCallback, ILogItem, ISerializedItem} from "./types";
|
||||||
import type {Platform} from "../platform/web/Platform.js";
|
import type {Platform} from "../platform/web/Platform.js";
|
||||||
|
|
||||||
export abstract class BaseLogger implements ILogger {
|
export abstract class BaseLogger implements ILogger {
|
||||||
protected _openItems: Set<LogItem> = new Set();
|
protected _openItems: Set<LogItem> = new Set();
|
||||||
protected _platform: Platform;
|
protected _platform: Platform;
|
||||||
|
protected _serializedTransformer: (item: ISerializedItem) => ISerializedItem;
|
||||||
|
|
||||||
constructor({platform}) {
|
constructor({platform, serializedTransformer = (item: ISerializedItem) => item}) {
|
||||||
this._platform = platform;
|
this._platform = platform;
|
||||||
|
this._serializedTransformer = serializedTransformer;
|
||||||
}
|
}
|
||||||
|
|
||||||
log(labelOrValues: LabelOrValues, logLevel: LogLevel = LogLevel.Info): void {
|
log(labelOrValues: LabelOrValues, logLevel: LogLevel = LogLevel.Info): void {
|
||||||
|
|
|
@ -26,7 +26,7 @@ import {BaseLogger} from "./BaseLogger";
|
||||||
import type {Interval} from "../platform/web/dom/Clock";
|
import type {Interval} from "../platform/web/dom/Clock";
|
||||||
import type {Platform} from "../platform/web/Platform.js";
|
import type {Platform} from "../platform/web/Platform.js";
|
||||||
import type {BlobHandle} from "../platform/web/dom/BlobHandle.js";
|
import type {BlobHandle} from "../platform/web/dom/BlobHandle.js";
|
||||||
import type {ILogItem, ILogExport} from "./types";
|
import type {ILogItem, ILogExport, ISerializedItem} from "./types";
|
||||||
import type {LogFilter} from "./LogFilter";
|
import type {LogFilter} from "./LogFilter";
|
||||||
|
|
||||||
type QueuedItem = {
|
type QueuedItem = {
|
||||||
|
@ -40,7 +40,7 @@ export class IDBLogger extends BaseLogger {
|
||||||
private readonly _flushInterval: Interval;
|
private readonly _flushInterval: Interval;
|
||||||
private _queuedItems: QueuedItem[];
|
private _queuedItems: QueuedItem[];
|
||||||
|
|
||||||
constructor(options: {name: string, flushInterval?: number, limit?: number, platform: Platform}) {
|
constructor(options: {name: string, flushInterval?: number, limit?: number, platform: Platform, serializedTransformer?: (item: ISerializedItem) => ISerializedItem}) {
|
||||||
super(options);
|
super(options);
|
||||||
const {name, flushInterval = 60 * 1000, limit = 3000} = options;
|
const {name, flushInterval = 60 * 1000, limit = 3000} = options;
|
||||||
this._name = name;
|
this._name = name;
|
||||||
|
@ -119,9 +119,12 @@ export class IDBLogger extends BaseLogger {
|
||||||
|
|
||||||
_persistItem(logItem: ILogItem, filter: LogFilter, forced: boolean): void {
|
_persistItem(logItem: ILogItem, filter: LogFilter, forced: boolean): void {
|
||||||
const serializedItem = logItem.serialize(filter, undefined, forced);
|
const serializedItem = logItem.serialize(filter, undefined, forced);
|
||||||
this._queuedItems.push({
|
if (serializedItem) {
|
||||||
json: JSON.stringify(serializedItem)
|
const transformedSerializedItem = this._serializedTransformer(serializedItem);
|
||||||
});
|
this._queuedItems.push({
|
||||||
|
json: JSON.stringify(transformedSerializedItem)
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_persistQueuedItems(items: QueuedItem[]): void {
|
_persistQueuedItems(items: QueuedItem[]): void {
|
||||||
|
|
|
@ -19,7 +19,7 @@ import {Room} from "./room/Room.js";
|
||||||
import {ArchivedRoom} from "./room/ArchivedRoom.js";
|
import {ArchivedRoom} from "./room/ArchivedRoom.js";
|
||||||
import {RoomStatus} from "./room/RoomStatus.js";
|
import {RoomStatus} from "./room/RoomStatus.js";
|
||||||
import {Invite} from "./room/Invite.js";
|
import {Invite} from "./room/Invite.js";
|
||||||
import {Pusher} from "./push/Pusher.js";
|
import {Pusher} from "./push/Pusher";
|
||||||
import { ObservableMap } from "../observable/index.js";
|
import { ObservableMap } from "../observable/index.js";
|
||||||
import {User} from "./User.js";
|
import {User} from "./User.js";
|
||||||
import {DeviceMessageHandler} from "./DeviceMessageHandler.js";
|
import {DeviceMessageHandler} from "./DeviceMessageHandler.js";
|
||||||
|
@ -34,7 +34,7 @@ import {Encryption as MegOlmEncryption} from "./e2ee/megolm/Encryption.js";
|
||||||
import {MEGOLM_ALGORITHM} from "./e2ee/common.js";
|
import {MEGOLM_ALGORITHM} from "./e2ee/common.js";
|
||||||
import {RoomEncryption} from "./e2ee/RoomEncryption.js";
|
import {RoomEncryption} from "./e2ee/RoomEncryption.js";
|
||||||
import {DeviceTracker} from "./e2ee/DeviceTracker.js";
|
import {DeviceTracker} from "./e2ee/DeviceTracker.js";
|
||||||
import {LockMap} from "../utils/LockMap.js";
|
import {LockMap} from "../utils/LockMap";
|
||||||
import {groupBy} from "../utils/groupBy";
|
import {groupBy} from "../utils/groupBy";
|
||||||
import {
|
import {
|
||||||
keyFromCredential as ssssKeyFromCredential,
|
keyFromCredential as ssssKeyFromCredential,
|
||||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {createEnum} from "../utils/enum.js";
|
import {createEnum} from "../utils/enum";
|
||||||
import {lookupHomeserver} from "./well-known.js";
|
import {lookupHomeserver} from "./well-known.js";
|
||||||
import {AbortableOperation} from "../utils/AbortableOperation";
|
import {AbortableOperation} from "../utils/AbortableOperation";
|
||||||
import {ObservableValue} from "../observable/ObservableValue";
|
import {ObservableValue} from "../observable/ObservableValue";
|
||||||
|
@ -27,9 +27,9 @@ import {RequestScheduler} from "./net/RequestScheduler.js";
|
||||||
// import {Sync, SyncStatus} from "./Sync.js";
|
// import {Sync, SyncStatus} from "./Sync.js";
|
||||||
import {Sync3, SyncStatus} from "./Sync3";
|
import {Sync3, SyncStatus} from "./Sync3";
|
||||||
import {Session} from "./Session.js";
|
import {Session} from "./Session.js";
|
||||||
import {PasswordLoginMethod} from "./login/PasswordLoginMethod.js";
|
import {PasswordLoginMethod} from "./login/PasswordLoginMethod";
|
||||||
import {TokenLoginMethod} from "./login/TokenLoginMethod.js";
|
import {TokenLoginMethod} from "./login/TokenLoginMethod";
|
||||||
import {SSOLoginHelper} from "./login/SSOLoginHelper.js";
|
import {SSOLoginHelper} from "./login/SSOLoginHelper";
|
||||||
import {getDehydratedDevice} from "./e2ee/Dehydration.js";
|
import {getDehydratedDevice} from "./e2ee/Dehydration.js";
|
||||||
|
|
||||||
export const LoadStatus = createEnum(
|
export const LoadStatus = createEnum(
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ObservableValue} from "../observable/ObservableValue";
|
import {ObservableValue} from "../observable/ObservableValue";
|
||||||
import {createEnum} from "../utils/enum.js";
|
import {createEnum} from "../utils/enum";
|
||||||
|
|
||||||
const INCREMENTAL_TIMEOUT = 30000;
|
const INCREMENTAL_TIMEOUT = 30000;
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import {MEGOLM_ALGORITHM, DecryptionSource} from "./common.js";
|
import {MEGOLM_ALGORITHM, DecryptionSource} from "./common.js";
|
||||||
import {groupEventsBySession} from "./megolm/decryption/utils";
|
import {groupEventsBySession} from "./megolm/decryption/utils";
|
||||||
import {mergeMap} from "../../utils/mergeMap.js";
|
import {mergeMap} from "../../utils/mergeMap";
|
||||||
import {groupBy} from "../../utils/groupBy";
|
import {groupBy} from "../../utils/groupBy";
|
||||||
import {makeTxnId} from "../common.js";
|
import {makeTxnId} from "../common.js";
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import anotherjson from "../../../lib/another-json/index.js";
|
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");
|
export const DecryptionSource = createEnum("Sync", "Timeline", "Retry");
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {DecryptionChanges} from "./DecryptionChanges.js";
|
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
|
* Class that contains all the state loaded from storage to decrypt the given events
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import {DecryptionError} from "../common.js";
|
import {DecryptionError} from "../common.js";
|
||||||
import {groupBy} from "../../../utils/groupBy";
|
import {groupBy} from "../../../utils/groupBy";
|
||||||
import {MultiLock} from "../../../utils/Lock.js";
|
import {MultiLock} from "../../../utils/Lock";
|
||||||
import {Session} from "./Session.js";
|
import {Session} from "./Session.js";
|
||||||
import {DecryptionResult} from "../DecryptionResult.js";
|
import {DecryptionResult} from "../DecryptionResult.js";
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
export class ConnectionError extends Error {
|
||||||
constructor(message, isTimeout) {
|
constructor(message, isTimeout) {
|
||||||
|
|
|
@ -14,17 +14,10 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class LoginMethod {
|
import type {ILogItem} from "../../logging/types";
|
||||||
constructor({homeserver}) {
|
import type {HomeServerApi} from "../net/HomeServerApi.js";
|
||||||
this.homeserver = homeserver;
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
export interface ILoginMethod {
|
||||||
async login(hsApi, deviceName, log) {
|
homeserver: string;
|
||||||
/*
|
login(hsApi: HomeServerApi, deviceName: string, log: ILogItem): Promise<Record<string, any>>;
|
||||||
Regardless of the login method, SessionContainer.startWithLogin()
|
|
||||||
can do SomeLoginMethod.login()
|
|
||||||
*/
|
|
||||||
throw("Not Implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {LoginMethod} from "./LoginMethod.js";
|
|
||||||
|
|
||||||
export class PasswordLoginMethod extends LoginMethod {
|
|
||||||
constructor(options) {
|
|
||||||
super(options);
|
|
||||||
this.username = options.username;
|
|
||||||
this.password = options.password;
|
|
||||||
}
|
|
||||||
|
|
||||||
async login(hsApi, deviceName, log) {
|
|
||||||
return await hsApi.passwordLogin(this.username, this.password, deviceName, {log}).response();
|
|
||||||
}
|
|
||||||
}
|
|
35
src/matrix/login/PasswordLoginMethod.ts
Normal file
35
src/matrix/login/PasswordLoginMethod.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {ILogItem} from "../../logging/types";
|
||||||
|
import {ILoginMethod} from "./LoginMethod";
|
||||||
|
import {HomeServerApi} from "../net/HomeServerApi.js";
|
||||||
|
|
||||||
|
export class PasswordLoginMethod implements ILoginMethod {
|
||||||
|
private readonly _username: string;
|
||||||
|
private readonly _password: string;
|
||||||
|
public readonly homeserver: string;
|
||||||
|
|
||||||
|
constructor({username, password, homeserver}: {username: string, password: string, homeserver: string}) {
|
||||||
|
this._username = username;
|
||||||
|
this._password = password;
|
||||||
|
this.homeserver = homeserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
async login(hsApi: HomeServerApi, deviceName: string, log: ILogItem): Promise<Record<string, any>> {
|
||||||
|
return await hsApi.passwordLogin(this._username, this._password, deviceName, {log}).response();
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,13 +15,15 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class SSOLoginHelper{
|
export class SSOLoginHelper{
|
||||||
constructor(homeserver) {
|
private _homeserver: string;
|
||||||
|
|
||||||
|
constructor(homeserver: string) {
|
||||||
this._homeserver = homeserver;
|
this._homeserver = homeserver;
|
||||||
}
|
}
|
||||||
|
|
||||||
get homeserver() { return this._homeserver; }
|
get homeserver(): string { return this._homeserver; }
|
||||||
|
|
||||||
createSSORedirectURL(returnURL) {
|
createSSORedirectURL(returnURL: string): string {
|
||||||
return `${this._homeserver}/_matrix/client/r0/login/sso/redirect?redirectUrl=${returnURL}`;
|
return `${this._homeserver}/_matrix/client/r0/login/sso/redirect?redirectUrl=${returnURL}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,16 +14,21 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {LoginMethod} from "./LoginMethod.js";
|
|
||||||
import {makeTxnId} from "../common.js";
|
import {makeTxnId} from "../common.js";
|
||||||
|
import {ILogItem} from "../../logging/types";
|
||||||
|
import {ILoginMethod} from "./LoginMethod";
|
||||||
|
import {HomeServerApi} from "../net/HomeServerApi.js";
|
||||||
|
|
||||||
export class TokenLoginMethod extends LoginMethod {
|
export class TokenLoginMethod implements ILoginMethod {
|
||||||
constructor(options) {
|
private readonly _loginToken: string;
|
||||||
super(options);
|
public readonly homeserver: string;
|
||||||
this._loginToken = options.loginToken;
|
|
||||||
|
constructor({ homeserver, loginToken }: { homeserver: string, loginToken: string}) {
|
||||||
|
this.homeserver = homeserver;
|
||||||
|
this._loginToken = loginToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(hsApi, deviceName, log) {
|
async login(hsApi: HomeServerApi, deviceName: string, log: ILogItem): Promise<Record<string, any>> {
|
||||||
return await hsApi.tokenLogin(this._loginToken, makeTxnId(), deviceName, {log}).response();
|
return await hsApi.tokenLogin(this._loginToken, makeTxnId(), deviceName, {log}).response();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AbortError} from "../../utils/error.js";
|
import {AbortError} from "../../utils/error";
|
||||||
|
|
||||||
export class ExponentialRetryDelay {
|
export class ExponentialRetryDelay {
|
||||||
constructor(createTimeout) {
|
constructor(createTimeout) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {createEnum} from "../../utils/enum.js";
|
import {createEnum} from "../../utils/enum";
|
||||||
import {ObservableValue} from "../../observable/ObservableValue";
|
import {ObservableValue} from "../../observable/ObservableValue";
|
||||||
|
|
||||||
export const ConnectionStatus = createEnum(
|
export const ConnectionStatus = createEnum(
|
||||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AbortError} from "../../utils/error.js";
|
import {AbortError} from "../../utils/error";
|
||||||
import {HomeServerError} from "../error.js";
|
import {HomeServerError} from "../error.js";
|
||||||
import {HomeServerApi} from "./HomeServerApi.js";
|
import {HomeServerApi} from "./HomeServerApi.js";
|
||||||
import {ExponentialRetryDelay} from "./ExponentialRetryDelay.js";
|
import {ExponentialRetryDelay} from "./ExponentialRetryDelay.js";
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
export class Pusher {
|
|
||||||
constructor(description) {
|
|
||||||
this._description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
static httpPusher(host, appId, pushkey, data) {
|
|
||||||
return new Pusher({
|
|
||||||
kind: "http",
|
|
||||||
append: true, // as pushkeys are shared between multiple users on one origin
|
|
||||||
data: Object.assign({}, data, {url: host + "/_matrix/push/v1/notify"}),
|
|
||||||
pushkey,
|
|
||||||
app_id: appId,
|
|
||||||
app_display_name: "Hydrogen",
|
|
||||||
device_display_name: "Hydrogen",
|
|
||||||
lang: "en"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static createDefaultPayload(sessionId) {
|
|
||||||
return {session_id: sessionId};
|
|
||||||
}
|
|
||||||
|
|
||||||
async enable(hsApi, log) {
|
|
||||||
try {
|
|
||||||
log.set("endpoint", new URL(this._description.data.endpoint).host);
|
|
||||||
} catch {
|
|
||||||
log.set("endpoint", null);
|
|
||||||
}
|
|
||||||
await hsApi.setPusher(this._description, {log}).response();
|
|
||||||
}
|
|
||||||
|
|
||||||
async disable(hsApi, log) {
|
|
||||||
const deleteDescription = Object.assign({}, this._description, {kind: null});
|
|
||||||
await hsApi.setPusher(deleteDescription, {log}).response();
|
|
||||||
}
|
|
||||||
|
|
||||||
serialize() {
|
|
||||||
return this._description;
|
|
||||||
}
|
|
||||||
|
|
||||||
equals(pusher) {
|
|
||||||
if (this._description.app_id !== pusher._description.app_id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (this._description.pushkey !== pusher._description.pushkey) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return JSON.stringify(this._description.data) === JSON.stringify(pusher._description.data);
|
|
||||||
}
|
|
||||||
}
|
|
90
src/matrix/push/Pusher.ts
Normal file
90
src/matrix/push/Pusher.ts
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type {HomeServerApi} from "../net/HomeServerApi.js";
|
||||||
|
import type {ILogItem} from "../../logging/types";
|
||||||
|
|
||||||
|
export interface IPusherDescription {
|
||||||
|
kind: "http" | "email" | "null";
|
||||||
|
lang: string;
|
||||||
|
device_display_name: string;
|
||||||
|
app_display_name: string;
|
||||||
|
app_id: string;
|
||||||
|
pushkey: string;
|
||||||
|
data: IPusherData;
|
||||||
|
append?: boolean;
|
||||||
|
profile_tag?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPusherData {
|
||||||
|
format?: string;
|
||||||
|
url?: string;
|
||||||
|
endpoint?: PushSubscriptionJSON["endpoint"];
|
||||||
|
keys?: PushSubscriptionJSON["keys"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Pusher {
|
||||||
|
private readonly _description: IPusherDescription;
|
||||||
|
|
||||||
|
constructor(description: IPusherDescription) {
|
||||||
|
this._description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
static httpPusher(host: string, appId: string, pushkey: string, data: IPusherData): Pusher {
|
||||||
|
return new Pusher({
|
||||||
|
kind: "http",
|
||||||
|
append: true, // as pushkeys are shared between multiple users on one origin
|
||||||
|
data: Object.assign({}, data, {url: host + "/_matrix/push/v1/notify"}),
|
||||||
|
pushkey,
|
||||||
|
app_id: appId,
|
||||||
|
app_display_name: "Hydrogen",
|
||||||
|
device_display_name: "Hydrogen",
|
||||||
|
lang: "en"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static createDefaultPayload(sessionId: string): {session_id: string} {
|
||||||
|
return {session_id: sessionId};
|
||||||
|
}
|
||||||
|
|
||||||
|
async enable(hsApi: HomeServerApi, log: ILogItem): Promise<void> {
|
||||||
|
try {
|
||||||
|
log.set("endpoint", new URL(this._description.data.endpoint!).host);
|
||||||
|
} catch {
|
||||||
|
log.set("endpoint", null);
|
||||||
|
}
|
||||||
|
await hsApi.setPusher(this._description, {log}).response();
|
||||||
|
}
|
||||||
|
|
||||||
|
async disable(hsApi: HomeServerApi, log: ILogItem): Promise<void> {
|
||||||
|
const deleteDescription = Object.assign({}, this._description, {kind: null});
|
||||||
|
await hsApi.setPusher(deleteDescription, {log}).response();
|
||||||
|
}
|
||||||
|
|
||||||
|
serialize(): IPusherDescription {
|
||||||
|
return this._description;
|
||||||
|
}
|
||||||
|
|
||||||
|
equals(pusher): boolean {
|
||||||
|
if (this._description.app_id !== pusher._description.app_id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this._description.pushkey !== pusher._description.pushkey) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return JSON.stringify(this._description.data) === JSON.stringify(pusher._description.data);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ObservableMap} from "../../../observable/map/ObservableMap.js";
|
import {ObservableMap} from "../../../observable/map/ObservableMap.js";
|
||||||
import {RetainedValue} from "../../../utils/RetainedValue.js";
|
import {RetainedValue} from "../../../utils/RetainedValue";
|
||||||
|
|
||||||
export class MemberList extends RetainedValue {
|
export class MemberList extends RetainedValue {
|
||||||
constructor({members, closeCallback}) {
|
constructor({members, closeCallback}) {
|
||||||
|
|
|
@ -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
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import {createEnum} from "../../../utils/enum.js";
|
import {createEnum} from "../../../utils/enum";
|
||||||
import {AbortError} from "../../../utils/error.js";
|
import {AbortError} from "../../../utils/error";
|
||||||
import {REDACTION_TYPE} from "../common.js";
|
import {REDACTION_TYPE} from "../common.js";
|
||||||
import {getRelationFromContent, getRelationTarget, setRelationTarget} from "../timeline/relations.js";
|
import {getRelationFromContent, getRelationTarget, setRelationTarget} from "../timeline/relations.js";
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {SortedArray} from "../../../observable/list/SortedArray.js";
|
import {SortedArray} from "../../../observable/list/SortedArray";
|
||||||
import {ConnectionError} from "../../error.js";
|
import {ConnectionError} from "../../error.js";
|
||||||
import {PendingEvent, SendStatus} from "./PendingEvent.js";
|
import {PendingEvent, SendStatus} from "./PendingEvent.js";
|
||||||
import {makeTxnId, isTxnId} from "../../common.js";
|
import {makeTxnId, isTxnId} from "../../common.js";
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {SortedArray, AsyncMappedList, ConcatList, ObservableArray} from "../../../observable/index.js";
|
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 {Direction} from "./Direction";
|
||||||
import {TimelineReader} from "./persistence/TimelineReader.js";
|
import {TimelineReader} from "./persistence/TimelineReader.js";
|
||||||
import {PendingEventEntry} from "./entries/PendingEventEntry.js";
|
import {PendingEventEntry} from "./entries/PendingEventEntry.js";
|
||||||
|
|
|
@ -14,12 +14,33 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class SessionInfoStorage {
|
interface ISessionInfo {
|
||||||
constructor(name) {
|
id: string;
|
||||||
|
deviceId: string;
|
||||||
|
userId: string;
|
||||||
|
homeserver: string;
|
||||||
|
homeServer: string; // deprecate this over time
|
||||||
|
accessToken: string;
|
||||||
|
lastUsed: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: this should probably be in platform/types?
|
||||||
|
interface ISessionInfoStorage {
|
||||||
|
getAll(): Promise<ISessionInfo[]>;
|
||||||
|
updateLastUsed(id: string, timestamp: number): Promise<void>;
|
||||||
|
get(id: string): Promise<ISessionInfo | undefined>;
|
||||||
|
add(sessionInfo: ISessionInfo): Promise<void>;
|
||||||
|
delete(sessionId: string): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SessionInfoStorage implements ISessionInfoStorage {
|
||||||
|
private readonly _name: string;
|
||||||
|
|
||||||
|
constructor(name: string) {
|
||||||
this._name = name;
|
this._name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAll() {
|
getAll(): Promise<ISessionInfo[]> {
|
||||||
const sessionsJson = localStorage.getItem(this._name);
|
const sessionsJson = localStorage.getItem(this._name);
|
||||||
if (sessionsJson) {
|
if (sessionsJson) {
|
||||||
const sessions = JSON.parse(sessionsJson);
|
const sessions = JSON.parse(sessionsJson);
|
||||||
|
@ -30,7 +51,7 @@ export class SessionInfoStorage {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateLastUsed(id, timestamp) {
|
async updateLastUsed(id: string, timestamp: number): Promise<void> {
|
||||||
const sessions = await this.getAll();
|
const sessions = await this.getAll();
|
||||||
if (sessions) {
|
if (sessions) {
|
||||||
const session = sessions.find(session => session.id === id);
|
const session = sessions.find(session => session.id === id);
|
||||||
|
@ -41,20 +62,20 @@ export class SessionInfoStorage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(id) {
|
async get(id: string): Promise<ISessionInfo | undefined> {
|
||||||
const sessions = await this.getAll();
|
const sessions = await this.getAll();
|
||||||
if (sessions) {
|
if (sessions) {
|
||||||
return sessions.find(session => session.id === id);
|
return sessions.find(session => session.id === id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async add(sessionInfo) {
|
async add(sessionInfo: ISessionInfo): Promise<void> {
|
||||||
const sessions = await this.getAll();
|
const sessions = await this.getAll();
|
||||||
sessions.push(sessionInfo);
|
sessions.push(sessionInfo);
|
||||||
localStorage.setItem(this._name, JSON.stringify(sessions));
|
localStorage.setItem(this._name, JSON.stringify(sessions));
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(sessionId) {
|
async delete(sessionId: string): Promise<void> {
|
||||||
let sessions = await this.getAll();
|
let sessions = await this.getAll();
|
||||||
sessions = sessions.filter(s => s.id !== sessionId);
|
sessions = sessions.filter(s => s.id !== sessionId);
|
||||||
localStorage.setItem(this._name, JSON.stringify(sessions));
|
localStorage.setItem(this._name, JSON.stringify(sessions));
|
|
@ -18,7 +18,7 @@ import {KeyDescription, Key} from "./common.js";
|
||||||
import {keyFromPassphrase} from "./passphrase.js";
|
import {keyFromPassphrase} from "./passphrase.js";
|
||||||
import {keyFromRecoveryKey} from "./recoveryKey.js";
|
import {keyFromRecoveryKey} from "./recoveryKey.js";
|
||||||
import {SESSION_E2EE_KEY_PREFIX} from "../e2ee/common.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`;
|
const SSSS_KEY = `${SESSION_E2EE_KEY_PREFIX}ssssKey`;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
|
|
||||||
import { IDBRequestError } from "./error";
|
import { IDBRequestError } from "./error";
|
||||||
import { StorageError } from "../common";
|
import { StorageError } from "../common";
|
||||||
import { AbortError } from "../../../utils/error.js";
|
import { AbortError } from "../../../utils/error";
|
||||||
|
|
||||||
let needsSyncPromise = false;
|
let needsSyncPromise = false;
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AbortError} from "../utils/error.js";
|
import {AbortError} from "../utils/error";
|
||||||
|
|
||||||
export class BaseRequest {
|
export class BaseRequest {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AbortError} from "../utils/error.js";
|
import {AbortError} from "../utils/error";
|
||||||
import {BaseObservable} from "./BaseObservable";
|
import {BaseObservable} from "./BaseObservable";
|
||||||
|
|
||||||
// like an EventEmitter, but doesn't have an event type
|
// like an EventEmitter, but doesn't have an event type
|
||||||
|
|
|
@ -20,11 +20,11 @@ import {MappedMap} from "./map/MappedMap.js";
|
||||||
import {JoinedMap} from "./map/JoinedMap.js";
|
import {JoinedMap} from "./map/JoinedMap.js";
|
||||||
import {BaseObservableMap} from "./map/BaseObservableMap.js";
|
import {BaseObservableMap} from "./map/BaseObservableMap.js";
|
||||||
// re-export "root" (of chain) collections
|
// re-export "root" (of chain) collections
|
||||||
export { ObservableArray } from "./list/ObservableArray.js";
|
export { ObservableArray } from "./list/ObservableArray";
|
||||||
export { SortedArray } from "./list/SortedArray.js";
|
export { SortedArray } from "./list/SortedArray";
|
||||||
export { MappedList } from "./list/MappedList.js";
|
export { MappedList } from "./list/MappedList";
|
||||||
export { AsyncMappedList } from "./list/AsyncMappedList.js";
|
export { AsyncMappedList } from "./list/AsyncMappedList";
|
||||||
export { ConcatList } from "./list/ConcatList.js";
|
export { ConcatList } from "./list/ConcatList";
|
||||||
export { ObservableMap } from "./map/ObservableMap.js";
|
export { ObservableMap } from "./map/ObservableMap.js";
|
||||||
|
|
||||||
// avoid circular dependency between these classes
|
// avoid circular dependency between these classes
|
||||||
|
|
|
@ -15,15 +15,14 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList";
|
import {IListObserver} from "./BaseObservableList";
|
||||||
|
import {BaseMappedList, Mapper, Updater, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList";
|
||||||
|
|
||||||
export class AsyncMappedList extends BaseMappedList {
|
export class AsyncMappedList<F,T> extends BaseMappedList<F,T,Promise<T>> implements IListObserver<F> {
|
||||||
constructor(sourceList, mapper, updater, removeCallback) {
|
private _eventQueue: AsyncEvent<F>[] | null = null;
|
||||||
super(sourceList, mapper, updater, removeCallback);
|
private _flushing: boolean = false;
|
||||||
this._eventQueue = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
onSubscribeFirst() {
|
onSubscribeFirst(): void {
|
||||||
this._sourceUnsubscribe = this._sourceList.subscribe(this);
|
this._sourceUnsubscribe = this._sourceList.subscribe(this);
|
||||||
this._eventQueue = [];
|
this._eventQueue = [];
|
||||||
this._mappedValues = [];
|
this._mappedValues = [];
|
||||||
|
@ -35,122 +34,112 @@ export class AsyncMappedList extends BaseMappedList {
|
||||||
this._flush();
|
this._flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _flush() {
|
async _flush(): Promise<void> {
|
||||||
if (this._flushing) {
|
if (this._flushing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._flushing = true;
|
this._flushing = true;
|
||||||
try {
|
try {
|
||||||
while (this._eventQueue.length) {
|
while (this._eventQueue!.length) {
|
||||||
const event = this._eventQueue.shift();
|
const event = this._eventQueue!.shift();
|
||||||
await event.run(this);
|
await event!.run(this);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this._flushing = false;
|
this._flushing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onReset() {
|
onReset(): void {
|
||||||
if (this._eventQueue) {
|
if (this._eventQueue) {
|
||||||
this._eventQueue.push(new ResetEvent());
|
this._eventQueue.push(new ResetEvent());
|
||||||
this._flush();
|
this._flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(index, value) {
|
onAdd(index: number, value: F): void {
|
||||||
if (this._eventQueue) {
|
if (this._eventQueue) {
|
||||||
this._eventQueue.push(new AddEvent(index, value));
|
this._eventQueue.push(new AddEvent(index, value));
|
||||||
this._flush();
|
this._flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdate(index, value, params) {
|
onUpdate(index: number, value: F, params: any): void {
|
||||||
if (this._eventQueue) {
|
if (this._eventQueue) {
|
||||||
this._eventQueue.push(new UpdateEvent(index, value, params));
|
this._eventQueue.push(new UpdateEvent(index, value, params));
|
||||||
this._flush();
|
this._flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(index) {
|
onRemove(index: number): void {
|
||||||
if (this._eventQueue) {
|
if (this._eventQueue) {
|
||||||
this._eventQueue.push(new RemoveEvent(index));
|
this._eventQueue.push(new RemoveEvent(index));
|
||||||
this._flush();
|
this._flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMove(fromIdx, toIdx) {
|
onMove(fromIdx: number, toIdx: number): void {
|
||||||
if (this._eventQueue) {
|
if (this._eventQueue) {
|
||||||
this._eventQueue.push(new MoveEvent(fromIdx, toIdx));
|
this._eventQueue.push(new MoveEvent(fromIdx, toIdx));
|
||||||
this._flush();
|
this._flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnsubscribeLast() {
|
onUnsubscribeLast(): void {
|
||||||
this._sourceUnsubscribe();
|
this._sourceUnsubscribe!();
|
||||||
this._eventQueue = null;
|
this._eventQueue = null;
|
||||||
this._mappedValues = null;
|
this._mappedValues = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddEvent {
|
type AsyncEvent<F> = AddEvent<F> | UpdateEvent<F> | RemoveEvent<F> | MoveEvent<F> | ResetEvent<F>
|
||||||
constructor(index, value) {
|
|
||||||
this.index = index;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
async run(list) {
|
class AddEvent<F> {
|
||||||
|
constructor(public index: number, public value: F) {}
|
||||||
|
|
||||||
|
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
|
||||||
const mappedValue = await list._mapper(this.value);
|
const mappedValue = await list._mapper(this.value);
|
||||||
runAdd(list, this.index, mappedValue);
|
runAdd(list, this.index, mappedValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UpdateEvent {
|
class UpdateEvent<F> {
|
||||||
constructor(index, value, params) {
|
constructor(public index: number, public value: F, public params: any) {}
|
||||||
this.index = index;
|
|
||||||
this.value = value;
|
|
||||||
this.params = params;
|
|
||||||
}
|
|
||||||
|
|
||||||
async run(list) {
|
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
|
||||||
runUpdate(list, this.index, this.value, this.params);
|
runUpdate(list, this.index, this.value, this.params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RemoveEvent {
|
class RemoveEvent<F> {
|
||||||
constructor(index) {
|
constructor(public index: number) {}
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
async run(list) {
|
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
|
||||||
runRemove(list, this.index);
|
runRemove(list, this.index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MoveEvent {
|
class MoveEvent<F> {
|
||||||
constructor(fromIdx, toIdx) {
|
constructor(public fromIdx: number, public toIdx: number) {}
|
||||||
this.fromIdx = fromIdx;
|
|
||||||
this.toIdx = toIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
async run(list) {
|
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
|
||||||
runMove(list, this.fromIdx, this.toIdx);
|
runMove(list, this.fromIdx, this.toIdx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ResetEvent {
|
class ResetEvent<F> {
|
||||||
async run(list) {
|
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
|
||||||
runReset(list);
|
runReset(list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import {ObservableArray} from "./ObservableArray.js";
|
import {ObservableArray} from "./ObservableArray";
|
||||||
import {ListObserver} from "../../mocks/ListObserver.js";
|
import {ListObserver} from "../../mocks/ListObserver.js";
|
||||||
|
|
||||||
export function tests() {
|
export function tests() {
|
||||||
return {
|
return {
|
||||||
"events are emitted in order": async assert => {
|
"events are emitted in order": async assert => {
|
||||||
const double = n => n * n;
|
const double = n => n * n;
|
||||||
const source = new ObservableArray();
|
const source = new ObservableArray<number>();
|
||||||
const mapper = new AsyncMappedList(source, async n => {
|
const mapper = new AsyncMappedList(source, async n => {
|
||||||
await new Promise(r => setTimeout(r, n));
|
await new Promise(r => setTimeout(r, n));
|
||||||
return {n: double(n)};
|
return {n: double(n)};
|
|
@ -21,15 +21,15 @@ import {findAndUpdateInArray} from "./common";
|
||||||
export type Mapper<F,T> = (value: F) => T
|
export type Mapper<F,T> = (value: F) => T
|
||||||
export type Updater<F,T> = (mappedValue: T, params: any, value: F) => void;
|
export type Updater<F,T> = (mappedValue: T, params: any, value: F) => void;
|
||||||
|
|
||||||
export class BaseMappedList<F,T> extends BaseObservableList<T> {
|
export class BaseMappedList<F,T,R = T> extends BaseObservableList<T> {
|
||||||
protected _sourceList: BaseObservableList<F>;
|
protected _sourceList: BaseObservableList<F>;
|
||||||
protected _sourceUnsubscribe: (() => void) | null = null;
|
protected _sourceUnsubscribe: (() => void) | null = null;
|
||||||
_mapper: Mapper<F,T>;
|
_mapper: Mapper<F,R>;
|
||||||
_updater: Updater<F,T>;
|
_updater?: Updater<F,T>;
|
||||||
_removeCallback?: (value: T) => void;
|
_removeCallback?: (value: T) => void;
|
||||||
_mappedValues: T[] | null = null;
|
_mappedValues: T[] | null = null;
|
||||||
|
|
||||||
constructor(sourceList: BaseObservableList<F>, mapper: Mapper<F,T>, updater: Updater<F,T>, removeCallback?: (value: T) => void) {
|
constructor(sourceList: BaseObservableList<F>, mapper: Mapper<F,R>, updater?: Updater<F,T>, removeCallback?: (value: T) => void) {
|
||||||
super();
|
super();
|
||||||
this._sourceList = sourceList;
|
this._sourceList = sourceList;
|
||||||
this._mapper = mapper;
|
this._mapper = mapper;
|
||||||
|
@ -50,12 +50,12 @@ export class BaseMappedList<F,T> extends BaseObservableList<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runAdd<F,T>(list: BaseMappedList<F,T>, index: number, mappedValue: T): void {
|
export function runAdd<F,T,R>(list: BaseMappedList<F,T,R>, index: number, mappedValue: T): void {
|
||||||
list._mappedValues!.splice(index, 0, mappedValue);
|
list._mappedValues!.splice(index, 0, mappedValue);
|
||||||
list.emitAdd(index, mappedValue);
|
list.emitAdd(index, mappedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runUpdate<F,T>(list: BaseMappedList<F,T>, index: number, value: F, params: any): void {
|
export function runUpdate<F,T,R>(list: BaseMappedList<F,T,R>, index: number, value: F, params: any): void {
|
||||||
const mappedValue = list._mappedValues![index];
|
const mappedValue = list._mappedValues![index];
|
||||||
if (list._updater) {
|
if (list._updater) {
|
||||||
list._updater(mappedValue, params, value);
|
list._updater(mappedValue, params, value);
|
||||||
|
@ -63,7 +63,7 @@ export function runUpdate<F,T>(list: BaseMappedList<F,T>, index: number, value:
|
||||||
list.emitUpdate(index, mappedValue, params);
|
list.emitUpdate(index, mappedValue, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runRemove<F,T>(list: BaseMappedList<F,T>, index: number): void {
|
export function runRemove<F,T,R>(list: BaseMappedList<F,T,R>, index: number): void {
|
||||||
const mappedValue = list._mappedValues![index];
|
const mappedValue = list._mappedValues![index];
|
||||||
list._mappedValues!.splice(index, 1);
|
list._mappedValues!.splice(index, 1);
|
||||||
if (list._removeCallback) {
|
if (list._removeCallback) {
|
||||||
|
@ -72,14 +72,14 @@ export function runRemove<F,T>(list: BaseMappedList<F,T>, index: number): void {
|
||||||
list.emitRemove(index, mappedValue);
|
list.emitRemove(index, mappedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runMove<F,T>(list: BaseMappedList<F,T>, fromIdx: number, toIdx: number): void {
|
export function runMove<F,T,R>(list: BaseMappedList<F,T,R>, fromIdx: number, toIdx: number): void {
|
||||||
const mappedValue = list._mappedValues![fromIdx];
|
const mappedValue = list._mappedValues![fromIdx];
|
||||||
list._mappedValues!.splice(fromIdx, 1);
|
list._mappedValues!.splice(fromIdx, 1);
|
||||||
list._mappedValues!.splice(toIdx, 0, mappedValue);
|
list._mappedValues!.splice(toIdx, 0, mappedValue);
|
||||||
list.emitMove(fromIdx, toIdx, mappedValue);
|
list.emitMove(fromIdx, toIdx, mappedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runReset<F,T>(list: BaseMappedList<F,T>): void {
|
export function runReset<F,T,R>(list: BaseMappedList<F,T,R>): void {
|
||||||
list._mappedValues = [];
|
list._mappedValues = [];
|
||||||
list.emitReset();
|
list.emitReset();
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,18 @@ export interface IListObserver<T> {
|
||||||
onMove(from: number, to: number, value: T, list: BaseObservableList<T>): void
|
onMove(from: number, to: number, value: T, list: BaseObservableList<T>): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class BaseObservableList<T> extends BaseObservable<IListObserver<T>> {
|
export function defaultObserverWith<T>(overrides: { [key in keyof IListObserver<T>]?: IListObserver<T>[key] }): IListObserver<T> {
|
||||||
|
const defaults = {
|
||||||
|
onReset(){},
|
||||||
|
onAdd(){},
|
||||||
|
onUpdate(){},
|
||||||
|
onRemove(){},
|
||||||
|
onMove(){},
|
||||||
|
}
|
||||||
|
return Object.assign(defaults, overrides);
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class BaseObservableList<T> extends BaseObservable<IListObserver<T>> implements Iterable<T> {
|
||||||
emitReset() {
|
emitReset() {
|
||||||
for(let h of this._handlers) {
|
for(let h of this._handlers) {
|
||||||
h.onReset(this);
|
h.onReset(this);
|
||||||
|
@ -38,7 +49,7 @@ export abstract class BaseObservableList<T> extends BaseObservable<IListObserver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emitUpdate(index: number, value: T, params: any): void {
|
emitUpdate(index: number, value: T, params?: any): void {
|
||||||
for(let h of this._handlers) {
|
for(let h of this._handlers) {
|
||||||
h.onUpdate(index, value, params, this);
|
h.onUpdate(index, value, params, this);
|
||||||
}
|
}
|
||||||
|
@ -58,6 +69,6 @@ export abstract class BaseObservableList<T> extends BaseObservable<IListObserver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract [Symbol.iterator](): IterableIterator<T>;
|
abstract [Symbol.iterator](): Iterator<T>;
|
||||||
abstract get length(): number;
|
abstract get length(): number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,16 +14,18 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {BaseObservableList} from "./BaseObservableList";
|
import {BaseObservableList, IListObserver} from "./BaseObservableList";
|
||||||
|
|
||||||
export class ConcatList extends BaseObservableList {
|
export class ConcatList<T> extends BaseObservableList<T> implements IListObserver<T> {
|
||||||
constructor(...sourceLists) {
|
protected _sourceLists: BaseObservableList<T>[];
|
||||||
|
protected _sourceUnsubscribes: (() => void)[] | null = null;
|
||||||
|
|
||||||
|
constructor(...sourceLists: BaseObservableList<T>[]) {
|
||||||
super();
|
super();
|
||||||
this._sourceLists = sourceLists;
|
this._sourceLists = sourceLists;
|
||||||
this._sourceUnsubscribes = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_offsetForSource(sourceList) {
|
_offsetForSource(sourceList: BaseObservableList<T>): number {
|
||||||
const listIdx = this._sourceLists.indexOf(sourceList);
|
const listIdx = this._sourceLists.indexOf(sourceList);
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
for (let i = 0; i < listIdx; ++i) {
|
for (let i = 0; i < listIdx; ++i) {
|
||||||
|
@ -32,17 +34,17 @@ export class ConcatList extends BaseObservableList {
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubscribeFirst() {
|
onSubscribeFirst(): void {
|
||||||
this._sourceUnsubscribes = this._sourceLists.map(sourceList => sourceList.subscribe(this));
|
this._sourceUnsubscribes = this._sourceLists.map(sourceList => sourceList.subscribe(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnsubscribeLast() {
|
onUnsubscribeLast(): void {
|
||||||
for (const sourceUnsubscribe of this._sourceUnsubscribes) {
|
for (const sourceUnsubscribe of this._sourceUnsubscribes!) {
|
||||||
sourceUnsubscribe();
|
sourceUnsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onReset() {
|
onReset(): void {
|
||||||
// TODO: not ideal if other source lists are large
|
// TODO: not ideal if other source lists are large
|
||||||
// but working impl for now
|
// but working impl for now
|
||||||
// reset, and
|
// reset, and
|
||||||
|
@ -54,11 +56,11 @@ export class ConcatList extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(index, value, sourceList) {
|
onAdd(index: number, value: T, sourceList: BaseObservableList<T>): void {
|
||||||
this.emitAdd(this._offsetForSource(sourceList) + index, value);
|
this.emitAdd(this._offsetForSource(sourceList) + index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdate(index, value, params, sourceList) {
|
onUpdate(index: number, value: T, params: any, sourceList: BaseObservableList<T>): void {
|
||||||
// if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it
|
// if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it
|
||||||
// as we are not supposed to call `length` on any uninitialized list
|
// as we are not supposed to call `length` on any uninitialized list
|
||||||
if (!this._sourceUnsubscribes) {
|
if (!this._sourceUnsubscribes) {
|
||||||
|
@ -67,16 +69,16 @@ export class ConcatList extends BaseObservableList {
|
||||||
this.emitUpdate(this._offsetForSource(sourceList) + index, value, params);
|
this.emitUpdate(this._offsetForSource(sourceList) + index, value, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(index, value, sourceList) {
|
onRemove(index: number, value: T, sourceList: BaseObservableList<T>): void {
|
||||||
this.emitRemove(this._offsetForSource(sourceList) + index, value);
|
this.emitRemove(this._offsetForSource(sourceList) + index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMove(fromIdx, toIdx, value, sourceList) {
|
onMove(fromIdx: number, toIdx: number, value: T, sourceList: BaseObservableList<T>): void {
|
||||||
const offset = this._offsetForSource(sourceList);
|
const offset = this._offsetForSource(sourceList);
|
||||||
this.emitMove(offset + fromIdx, offset + toIdx, value);
|
this.emitMove(offset + fromIdx, offset + toIdx, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
get length() {
|
get length(): number {
|
||||||
let len = 0;
|
let len = 0;
|
||||||
for (let i = 0; i < this._sourceLists.length; ++i) {
|
for (let i = 0; i < this._sourceLists.length; ++i) {
|
||||||
len += this._sourceLists[i].length;
|
len += this._sourceLists[i].length;
|
||||||
|
@ -104,7 +106,8 @@ export class ConcatList extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import {ObservableArray} from "./ObservableArray.js";
|
import {ObservableArray} from "./ObservableArray";
|
||||||
|
import {defaultObserverWith} from "./BaseObservableList";
|
||||||
export async function tests() {
|
export async function tests() {
|
||||||
return {
|
return {
|
||||||
test_length(assert) {
|
test_length(assert) {
|
||||||
|
@ -133,13 +136,13 @@ export async function tests() {
|
||||||
const list2 = new ObservableArray([11, 12, 13]);
|
const list2 = new ObservableArray([11, 12, 13]);
|
||||||
const all = new ConcatList(list1, list2);
|
const all = new ConcatList(list1, list2);
|
||||||
let fired = false;
|
let fired = false;
|
||||||
all.subscribe({
|
all.subscribe(defaultObserverWith({
|
||||||
onAdd(index, value) {
|
onAdd(index, value) {
|
||||||
fired = true;
|
fired = true;
|
||||||
assert.equal(index, 4);
|
assert.equal(index, 4);
|
||||||
assert.equal(value, 11.5);
|
assert.equal(value, 11.5);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list2.insert(1, 11.5);
|
list2.insert(1, 11.5);
|
||||||
assert(fired);
|
assert(fired);
|
||||||
},
|
},
|
||||||
|
@ -148,13 +151,13 @@ export async function tests() {
|
||||||
const list2 = new ObservableArray([11, 12, 13]);
|
const list2 = new ObservableArray([11, 12, 13]);
|
||||||
const all = new ConcatList(list1, list2);
|
const all = new ConcatList(list1, list2);
|
||||||
let fired = false;
|
let fired = false;
|
||||||
all.subscribe({
|
all.subscribe(defaultObserverWith({
|
||||||
onUpdate(index, value) {
|
onUpdate(index, value) {
|
||||||
fired = true;
|
fired = true;
|
||||||
assert.equal(index, 4);
|
assert.equal(index, 4);
|
||||||
assert.equal(value, 10);
|
assert.equal(value, 10);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list2.emitUpdate(1, 10);
|
list2.emitUpdate(1, 10);
|
||||||
assert(fired);
|
assert(fired);
|
||||||
},
|
},
|
|
@ -15,9 +15,10 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {IListObserver} from "./BaseObservableList";
|
||||||
import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList";
|
import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList";
|
||||||
|
|
||||||
export class MappedList extends BaseMappedList {
|
export class MappedList<F,T> extends BaseMappedList<F,T> implements IListObserver<F> {
|
||||||
onSubscribeFirst() {
|
onSubscribeFirst() {
|
||||||
this._sourceUnsubscribe = this._sourceList.subscribe(this);
|
this._sourceUnsubscribe = this._sourceList.subscribe(this);
|
||||||
this._mappedValues = [];
|
this._mappedValues = [];
|
||||||
|
@ -26,16 +27,16 @@ export class MappedList extends BaseMappedList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onReset() {
|
onReset(): void {
|
||||||
runReset(this);
|
runReset(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(index, value) {
|
onAdd(index: number, value: F): void {
|
||||||
const mappedValue = this._mapper(value);
|
const mappedValue = this._mapper(value);
|
||||||
runAdd(this, index, mappedValue);
|
runAdd(this, index, mappedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdate(index, value, params) {
|
onUpdate(index: number, value: F, params: any): void {
|
||||||
// if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it
|
// if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it
|
||||||
if (!this._mappedValues) {
|
if (!this._mappedValues) {
|
||||||
return;
|
return;
|
||||||
|
@ -43,24 +44,25 @@ export class MappedList extends BaseMappedList {
|
||||||
runUpdate(this, index, value, params);
|
runUpdate(this, index, value, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(index) {
|
onRemove(index: number): void {
|
||||||
runRemove(this, index);
|
runRemove(this, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMove(fromIdx, toIdx) {
|
onMove(fromIdx: number, toIdx: number): void {
|
||||||
runMove(this, fromIdx, toIdx);
|
runMove(this, fromIdx, toIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnsubscribeLast() {
|
onUnsubscribeLast(): void {
|
||||||
this._sourceUnsubscribe();
|
this._sourceUnsubscribe!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import {ObservableArray} from "./ObservableArray.js";
|
import {ObservableArray} from "./ObservableArray";
|
||||||
import {BaseObservableList} from "./BaseObservableList";
|
import {BaseObservableList} from "./BaseObservableList";
|
||||||
|
import {defaultObserverWith} from "./BaseObservableList";
|
||||||
|
|
||||||
export async function tests() {
|
export async function tests() {
|
||||||
class MockList extends BaseObservableList {
|
class MockList extends BaseObservableList<number> {
|
||||||
get length() {
|
get length() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -74,26 +76,26 @@ export async function tests() {
|
||||||
const source = new MockList();
|
const source = new MockList();
|
||||||
const mapped = new MappedList(source, n => {return {n: n*n};});
|
const mapped = new MappedList(source, n => {return {n: n*n};});
|
||||||
let fired = false;
|
let fired = false;
|
||||||
const unsubscribe = mapped.subscribe({
|
const unsubscribe = mapped.subscribe(defaultObserverWith({
|
||||||
onAdd(idx, value) {
|
onAdd(idx, value) {
|
||||||
fired = true;
|
fired = true;
|
||||||
assert.equal(idx, 0);
|
assert.equal(idx, 0);
|
||||||
assert.equal(value.n, 36);
|
assert.equal(value.n, 36);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
source.emitAdd(0, 6);
|
source.emitAdd(0, 6);
|
||||||
assert(fired);
|
assert(fired);
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
},
|
},
|
||||||
test_update(assert) {
|
test_update(assert) {
|
||||||
const source = new MockList();
|
const source = new MockList();
|
||||||
const mapped = new MappedList(
|
const mapped = new MappedList<number, { n: number, m?: number }>(
|
||||||
source,
|
source,
|
||||||
n => {return {n: n*n};},
|
n => {return {n: n*n};},
|
||||||
(o, p, n) => o.m = n*n
|
(o, p, n) => o.m = n*n
|
||||||
);
|
);
|
||||||
let fired = false;
|
let fired = false;
|
||||||
const unsubscribe = mapped.subscribe({
|
const unsubscribe = mapped.subscribe(defaultObserverWith({
|
||||||
onAdd() {},
|
onAdd() {},
|
||||||
onUpdate(idx, value) {
|
onUpdate(idx, value) {
|
||||||
fired = true;
|
fired = true;
|
||||||
|
@ -101,7 +103,7 @@ export async function tests() {
|
||||||
assert.equal(value.n, 36);
|
assert.equal(value.n, 36);
|
||||||
assert.equal(value.m, 49);
|
assert.equal(value.m, 49);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
source.emitAdd(0, 6);
|
source.emitAdd(0, 6);
|
||||||
source.emitUpdate(0, 7);
|
source.emitUpdate(0, 7);
|
||||||
assert(fired);
|
assert(fired);
|
||||||
|
@ -113,9 +115,9 @@ export async function tests() {
|
||||||
source,
|
source,
|
||||||
n => {return n*n;}
|
n => {return n*n;}
|
||||||
);
|
);
|
||||||
mapped.subscribe({
|
mapped.subscribe(defaultObserverWith({
|
||||||
onUpdate() { assert.fail(); }
|
onUpdate() { assert.fail(); }
|
||||||
});
|
}));
|
||||||
assert.equal(mapped.findAndUpdate(
|
assert.equal(mapped.findAndUpdate(
|
||||||
n => n === 100,
|
n => n === 100,
|
||||||
() => assert.fail()
|
() => assert.fail()
|
||||||
|
@ -127,9 +129,9 @@ export async function tests() {
|
||||||
source,
|
source,
|
||||||
n => {return n*n;}
|
n => {return n*n;}
|
||||||
);
|
);
|
||||||
mapped.subscribe({
|
mapped.subscribe(defaultObserverWith({
|
||||||
onUpdate() { assert.fail(); }
|
onUpdate() { assert.fail(); }
|
||||||
});
|
}));
|
||||||
let fired = false;
|
let fired = false;
|
||||||
assert.equal(mapped.findAndUpdate(
|
assert.equal(mapped.findAndUpdate(
|
||||||
n => n === 9,
|
n => n === 9,
|
||||||
|
@ -148,14 +150,14 @@ export async function tests() {
|
||||||
n => {return n*n;}
|
n => {return n*n;}
|
||||||
);
|
);
|
||||||
let fired = false;
|
let fired = false;
|
||||||
mapped.subscribe({
|
mapped.subscribe(defaultObserverWith({
|
||||||
onUpdate(idx, n, params) {
|
onUpdate(idx, n, params) {
|
||||||
assert.equal(idx, 1);
|
assert.equal(idx, 1);
|
||||||
assert.equal(n, 9);
|
assert.equal(n, 9);
|
||||||
assert.equal(params, "param");
|
assert.equal(params, "param");
|
||||||
fired = true;
|
fired = true;
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
assert.equal(mapped.findAndUpdate(n => n === 9, () => "param"), true);
|
assert.equal(mapped.findAndUpdate(n => n === 9, () => "param"), true);
|
||||||
assert.equal(fired, true);
|
assert.equal(fired, true);
|
||||||
},
|
},
|
|
@ -16,35 +16,37 @@ limitations under the License.
|
||||||
|
|
||||||
import {BaseObservableList} from "./BaseObservableList";
|
import {BaseObservableList} from "./BaseObservableList";
|
||||||
|
|
||||||
export class ObservableArray extends BaseObservableList {
|
export class ObservableArray<T> extends BaseObservableList<T> {
|
||||||
constructor(initialValues = []) {
|
private _items: T[];
|
||||||
|
|
||||||
|
constructor(initialValues: T[] = []) {
|
||||||
super();
|
super();
|
||||||
this._items = initialValues;
|
this._items = initialValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
append(item) {
|
append(item: T): void {
|
||||||
this._items.push(item);
|
this._items.push(item);
|
||||||
this.emitAdd(this._items.length - 1, item);
|
this.emitAdd(this._items.length - 1, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(idx) {
|
remove(idx: number): void {
|
||||||
const [item] = this._items.splice(idx, 1);
|
const [item] = this._items.splice(idx, 1);
|
||||||
this.emitRemove(idx, item);
|
this.emitRemove(idx, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
insertMany(idx, items) {
|
insertMany(idx: number, items: T[]): void {
|
||||||
for(let item of items) {
|
for(let item of items) {
|
||||||
this.insert(idx, item);
|
this.insert(idx, item);
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insert(idx, item) {
|
insert(idx: number, item: T): void {
|
||||||
this._items.splice(idx, 0, item);
|
this._items.splice(idx, 0, item);
|
||||||
this.emitAdd(idx, item);
|
this.emitAdd(idx, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
move(fromIdx, toIdx) {
|
move(fromIdx: number, toIdx: number): void {
|
||||||
if (fromIdx < this._items.length && toIdx < this._items.length) {
|
if (fromIdx < this._items.length && toIdx < this._items.length) {
|
||||||
const [item] = this._items.splice(fromIdx, 1);
|
const [item] = this._items.splice(fromIdx, 1);
|
||||||
this._items.splice(toIdx, 0, item);
|
this._items.splice(toIdx, 0, item);
|
||||||
|
@ -52,24 +54,24 @@ export class ObservableArray extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update(idx, item, params = null) {
|
update(idx: number, item: T, params: any = null): void {
|
||||||
if (idx < this._items.length) {
|
if (idx < this._items.length) {
|
||||||
this._items[idx] = item;
|
this._items[idx] = item;
|
||||||
this.emitUpdate(idx, item, params);
|
this.emitUpdate(idx, item, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get array() {
|
get array(): Readonly<T[]> {
|
||||||
return this._items;
|
return this._items;
|
||||||
}
|
}
|
||||||
|
|
||||||
at(idx) {
|
at(idx: number): T | undefined {
|
||||||
if (this._items && idx >= 0 && idx < this._items.length) {
|
if (this._items && idx >= 0 && idx < this._items.length) {
|
||||||
return this._items[idx];
|
return this._items[idx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get length() {
|
get length(): number {
|
||||||
return this._items.length;
|
return this._items.length;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,21 +15,23 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {BaseObservableList} from "./BaseObservableList";
|
import {BaseObservableList} from "./BaseObservableList";
|
||||||
import {sortedIndex} from "../../utils/sortedIndex.js";
|
import {sortedIndex} from "../../utils/sortedIndex";
|
||||||
import {findAndUpdateInArray} from "./common";
|
import {findAndUpdateInArray} from "./common";
|
||||||
|
|
||||||
export class SortedArray extends BaseObservableList {
|
export class SortedArray<T> extends BaseObservableList<T> {
|
||||||
constructor(comparator) {
|
private _comparator: (left: T, right: T) => number;
|
||||||
|
private _items: T[] = [];
|
||||||
|
|
||||||
|
constructor(comparator: (left: T, right: T) => number) {
|
||||||
super();
|
super();
|
||||||
this._comparator = comparator;
|
this._comparator = comparator;
|
||||||
this._items = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setManyUnsorted(items) {
|
setManyUnsorted(items: T[]): void {
|
||||||
this.setManySorted(items);
|
this.setManySorted(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
setManySorted(items) {
|
setManySorted(items: T[]): void {
|
||||||
// TODO: we can make this way faster by only looking up the first and last key,
|
// TODO: we can make this way faster by only looking up the first and last key,
|
||||||
// and merging whatever is inbetween with items
|
// and merging whatever is inbetween with items
|
||||||
// if items is not sorted, 💩🌀 will follow!
|
// if items is not sorted, 💩🌀 will follow!
|
||||||
|
@ -42,11 +44,11 @@ export class SortedArray extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
findAndUpdate(predicate, updater) {
|
findAndUpdate(predicate: (value: T) => boolean, updater: (value: T) => any | false): boolean {
|
||||||
return findAndUpdateInArray(predicate, this._items, this, updater);
|
return findAndUpdateInArray(predicate, this._items, this, updater);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAndUpdate(item, updater, updateParams = null) {
|
getAndUpdate(item: T, updater: (existing: T, item: T) => any, updateParams: any = null): void {
|
||||||
const idx = this.indexOf(item);
|
const idx = this.indexOf(item);
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
const existingItem = this._items[idx];
|
const existingItem = this._items[idx];
|
||||||
|
@ -56,7 +58,7 @@ export class SortedArray extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update(item, updateParams = null) {
|
update(item: T, updateParams: any = null): void {
|
||||||
const idx = this.indexOf(item);
|
const idx = this.indexOf(item);
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
this._items[idx] = item;
|
this._items[idx] = item;
|
||||||
|
@ -64,7 +66,7 @@ export class SortedArray extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
indexOf(item) {
|
indexOf(item: T): number {
|
||||||
const idx = sortedIndex(this._items, item, this._comparator);
|
const idx = sortedIndex(this._items, item, this._comparator);
|
||||||
if (idx < this._items.length && this._comparator(this._items[idx], item) === 0) {
|
if (idx < this._items.length && this._comparator(this._items[idx], item) === 0) {
|
||||||
return idx;
|
return idx;
|
||||||
|
@ -73,7 +75,7 @@ export class SortedArray extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getNext(item) {
|
_getNext(item: T): T | undefined {
|
||||||
let idx = sortedIndex(this._items, item, this._comparator);
|
let idx = sortedIndex(this._items, item, this._comparator);
|
||||||
while(idx < this._items.length && this._comparator(this._items[idx], item) <= 0) {
|
while(idx < this._items.length && this._comparator(this._items[idx], item) <= 0) {
|
||||||
idx += 1;
|
idx += 1;
|
||||||
|
@ -81,7 +83,7 @@ export class SortedArray extends BaseObservableList {
|
||||||
return this.get(idx);
|
return this.get(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(item, updateParams = null) {
|
set(item: T, updateParams: any = null): void {
|
||||||
const idx = sortedIndex(this._items, item, this._comparator);
|
const idx = sortedIndex(this._items, item, this._comparator);
|
||||||
if (idx >= this._items.length || this._comparator(this._items[idx], item) !== 0) {
|
if (idx >= this._items.length || this._comparator(this._items[idx], item) !== 0) {
|
||||||
this._items.splice(idx, 0, item);
|
this._items.splice(idx, 0, item);
|
||||||
|
@ -92,21 +94,21 @@ export class SortedArray extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get(idx) {
|
get(idx: number): T | undefined {
|
||||||
return this._items[idx];
|
return this._items[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(idx) {
|
remove(idx: number): void {
|
||||||
const item = this._items[idx];
|
const item = this._items[idx];
|
||||||
this._items.splice(idx, 1);
|
this._items.splice(idx, 1);
|
||||||
this.emitRemove(idx, item);
|
this.emitRemove(idx, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
get array() {
|
get array(): T[] {
|
||||||
return this._items;
|
return this._items;
|
||||||
}
|
}
|
||||||
|
|
||||||
get length() {
|
get length(): number {
|
||||||
return this._items.length;
|
return this._items.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,8 +118,11 @@ export class SortedArray extends BaseObservableList {
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterator that works even if the current value is removed while iterating
|
// iterator that works even if the current value is removed while iterating
|
||||||
class Iterator {
|
class Iterator<T> {
|
||||||
constructor(sortedArray) {
|
private _sortedArray: SortedArray<T> | null
|
||||||
|
private _current: T | null | undefined
|
||||||
|
|
||||||
|
constructor(sortedArray: SortedArray<T>) {
|
||||||
this._sortedArray = sortedArray;
|
this._sortedArray = sortedArray;
|
||||||
this._current = null;
|
this._current = null;
|
||||||
}
|
}
|
||||||
|
@ -145,7 +150,7 @@ class Iterator {
|
||||||
export function tests() {
|
export function tests() {
|
||||||
return {
|
return {
|
||||||
"setManyUnsorted": assert => {
|
"setManyUnsorted": assert => {
|
||||||
const sa = new SortedArray((a, b) => a.localeCompare(b));
|
const sa = new SortedArray<string>((a, b) => a.localeCompare(b));
|
||||||
sa.setManyUnsorted(["b", "a", "c"]);
|
sa.setManyUnsorted(["b", "a", "c"]);
|
||||||
assert.equal(sa.length, 3);
|
assert.equal(sa.length, 3);
|
||||||
assert.equal(sa.get(0), "a");
|
assert.equal(sa.get(0), "a");
|
||||||
|
@ -153,7 +158,7 @@ export function tests() {
|
||||||
assert.equal(sa.get(2), "c");
|
assert.equal(sa.get(2), "c");
|
||||||
},
|
},
|
||||||
"_getNext": assert => {
|
"_getNext": assert => {
|
||||||
const sa = new SortedArray((a, b) => a.localeCompare(b));
|
const sa = new SortedArray<string>((a, b) => a.localeCompare(b));
|
||||||
sa.setManyUnsorted(["b", "a", "f"]);
|
sa.setManyUnsorted(["b", "a", "f"]);
|
||||||
assert.equal(sa._getNext("a"), "b");
|
assert.equal(sa._getNext("a"), "b");
|
||||||
assert.equal(sa._getNext("b"), "f");
|
assert.equal(sa._getNext("b"), "f");
|
||||||
|
@ -162,7 +167,7 @@ export function tests() {
|
||||||
assert.equal(sa._getNext("f"), undefined);
|
assert.equal(sa._getNext("f"), undefined);
|
||||||
},
|
},
|
||||||
"iterator with removals": assert => {
|
"iterator with removals": assert => {
|
||||||
const queue = new SortedArray((a, b) => a.idx - b.idx);
|
const queue = new SortedArray<{idx: number}>((a, b) => a.idx - b.idx);
|
||||||
queue.setManyUnsorted([{idx: 5}, {idx: 3}, {idx: 1}, {idx: 4}, {idx: 2}]);
|
queue.setManyUnsorted([{idx: 5}, {idx: 3}, {idx: 1}, {idx: 4}, {idx: 2}]);
|
||||||
const it = queue[Symbol.iterator]();
|
const it = queue[Symbol.iterator]();
|
||||||
assert.equal(it.next().value.idx, 1);
|
assert.equal(it.next().value.idx, 1);
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {BaseObservableList} from "./BaseObservableList";
|
import {BaseObservableList} from "./BaseObservableList";
|
||||||
import {sortedIndex} from "../../utils/sortedIndex.js";
|
import {sortedIndex} from "../../utils/sortedIndex";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import aesjs from "../../../lib/aes-js/index.js";
|
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";
|
import {Platform as ModernPlatform} from "./Platform.js";
|
||||||
|
|
||||||
export function Platform(container, paths) {
|
export function Platform(container, paths) {
|
||||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
import {createFetchRequest} from "./dom/request/fetch.js";
|
import {createFetchRequest} from "./dom/request/fetch.js";
|
||||||
import {xhrRequest} from "./dom/request/xhr.js";
|
import {xhrRequest} from "./dom/request/xhr.js";
|
||||||
import {StorageFactory} from "../../matrix/storage/idb/StorageFactory";
|
import {StorageFactory} from "../../matrix/storage/idb/StorageFactory";
|
||||||
import {SessionInfoStorage} from "../../matrix/sessioninfo/localstorage/SessionInfoStorage.js";
|
import {SessionInfoStorage} from "../../matrix/sessioninfo/localstorage/SessionInfoStorage";
|
||||||
import {SettingsStorage} from "./dom/SettingsStorage.js";
|
import {SettingsStorage} from "./dom/SettingsStorage.js";
|
||||||
import {Encoding} from "./utils/Encoding.js";
|
import {Encoding} from "./utils/Encoding.js";
|
||||||
import {OlmWorker} from "../../matrix/e2ee/OlmWorker.js";
|
import {OlmWorker} from "../../matrix/e2ee/OlmWorker.js";
|
||||||
|
@ -35,7 +35,7 @@ import {WorkerPool} from "./dom/WorkerPool.js";
|
||||||
import {BlobHandle} from "./dom/BlobHandle.js";
|
import {BlobHandle} from "./dom/BlobHandle.js";
|
||||||
import {hasReadPixelPermission, ImageHandle, VideoHandle} from "./dom/ImageHandle.js";
|
import {hasReadPixelPermission, ImageHandle, VideoHandle} from "./dom/ImageHandle.js";
|
||||||
import {downloadInIframe} from "./dom/download.js";
|
import {downloadInIframe} from "./dom/download.js";
|
||||||
import {Disposables} from "../../utils/Disposables.js";
|
import {Disposables} from "../../utils/Disposables";
|
||||||
import {parseHTML} from "./parsehtml.js";
|
import {parseHTML} from "./parsehtml.js";
|
||||||
import {handleAvatarError} from "./ui/avatar.js";
|
import {handleAvatarError} from "./ui/avatar.js";
|
||||||
|
|
||||||
|
@ -132,11 +132,7 @@ export class Platform {
|
||||||
this.clock = new Clock();
|
this.clock = new Clock();
|
||||||
this.encoding = new Encoding();
|
this.encoding = new Encoding();
|
||||||
this.random = Math.random;
|
this.random = Math.random;
|
||||||
if (options?.development) {
|
this._createLogger(options?.development);
|
||||||
this.logger = new ConsoleLogger({platform: this});
|
|
||||||
} else {
|
|
||||||
this.logger = new IDBLogger({name: "hydrogen_logs", platform: this});
|
|
||||||
}
|
|
||||||
this.history = new History();
|
this.history = new History();
|
||||||
this.onlineStatus = new OnlineStatus();
|
this.onlineStatus = new OnlineStatus();
|
||||||
this._serviceWorkerHandler = null;
|
this._serviceWorkerHandler = null;
|
||||||
|
@ -162,6 +158,21 @@ export class Platform {
|
||||||
this._disposables = new Disposables();
|
this._disposables = new Disposables();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_createLogger(isDevelopment) {
|
||||||
|
// Make sure that loginToken does not end up in the logs
|
||||||
|
const transformer = (item) => {
|
||||||
|
if (item.e?.stack) {
|
||||||
|
item.e.stack = item.e.stack.replace(/(?<=\/\?loginToken=).+/, "<snip>");
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
};
|
||||||
|
if (isDevelopment) {
|
||||||
|
this.logger = new ConsoleLogger({platform: this});
|
||||||
|
} else {
|
||||||
|
this.logger = new IDBLogger({name: "hydrogen_logs", platform: this, serializedTransformer: transformer});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get updateService() {
|
get updateService() {
|
||||||
return this._serviceWorkerHandler;
|
return this._serviceWorkerHandler;
|
||||||
}
|
}
|
||||||
|
@ -272,3 +283,30 @@ export class Platform {
|
||||||
this._disposables.dispose();
|
this._disposables.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import {LogItem} from "../../logging/LogItem";
|
||||||
|
export function tests() {
|
||||||
|
return {
|
||||||
|
"loginToken should not be in logs": (assert) => {
|
||||||
|
const transformer = (item) => {
|
||||||
|
if (item.e?.stack) {
|
||||||
|
item.e.stack = item.e.stack.replace(/(?<=\/\?loginToken=).+/, "<snip>");
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
};
|
||||||
|
const logger = {
|
||||||
|
_queuedItems: [],
|
||||||
|
_serializedTransformer: transformer,
|
||||||
|
_now: () => {}
|
||||||
|
};
|
||||||
|
logger.persist = IDBLogger.prototype._persistItem.bind(logger);
|
||||||
|
const logItem = new LogItem("test", 1, logger);
|
||||||
|
logItem.error = new Error();
|
||||||
|
logItem.error.stack = "main http://localhost:3000/src/main.js:55\n<anonymous> http://localhost:3000/?loginToken=secret:26"
|
||||||
|
logger.persist(logItem, null, false);
|
||||||
|
const item = logger._queuedItems.pop();
|
||||||
|
console.log(item);
|
||||||
|
assert.strictEqual(item.json.search("secret"), -1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AbortError} from "../../../utils/error.js";
|
import {AbortError} from "../../../utils/error";
|
||||||
|
|
||||||
class Timeout {
|
class Timeout {
|
||||||
constructor(ms) {
|
constructor(ms) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AbortError} from "../../../utils/error.js";
|
import {AbortError} from "../../../utils/error";
|
||||||
|
|
||||||
class WorkerState {
|
class WorkerState {
|
||||||
constructor(worker) {
|
constructor(worker) {
|
||||||
|
|
|
@ -19,7 +19,7 @@ import {
|
||||||
AbortError,
|
AbortError,
|
||||||
ConnectionError
|
ConnectionError
|
||||||
} from "../../../../matrix/error.js";
|
} from "../../../../matrix/error.js";
|
||||||
import {abortOnTimeout} from "../../../../utils/timeout.js";
|
import {abortOnTimeout} from "../../../../utils/timeout";
|
||||||
import {addCacheBuster} from "./common.js";
|
import {addCacheBuster} from "./common.js";
|
||||||
import {xhrRequest} from "./xhr.js";
|
import {xhrRequest} from "./xhr.js";
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Range, RangeZone} from "./Range";
|
import {Range, RangeZone} from "./Range";
|
||||||
|
import {defaultObserverWith} from "../../../../observable/list/BaseObservableList";
|
||||||
|
|
||||||
function skipOnIterator<T>(it: Iterator<T>, pos: number): boolean {
|
function skipOnIterator<T>(it: Iterator<T>, pos: number): boolean {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
@ -214,7 +215,7 @@ export class ListRange extends Range {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import {ObservableArray} from "../../../../observable/list/ObservableArray.js";
|
import {ObservableArray} from "../../../../observable/list/ObservableArray";
|
||||||
|
|
||||||
export function tests() {
|
export function tests() {
|
||||||
return {
|
return {
|
||||||
|
@ -268,7 +269,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["b", "c", "d", "e"]);
|
const list = new ObservableArray(["b", "c", "d", "e"]);
|
||||||
const range = new ListRange(1, 3, list.length);
|
const range = new ListRange(1, 3, list.length);
|
||||||
let added = false;
|
let added = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onAdd(idx, value) {
|
onAdd(idx, value) {
|
||||||
added = true;
|
added = true;
|
||||||
const result = range.queryAdd(idx, value, list);
|
const result = range.queryAdd(idx, value, list);
|
||||||
|
@ -280,7 +281,7 @@ export function tests() {
|
||||||
newRange: new ListRange(1, 3, 5)
|
newRange: new ListRange(1, 3, 5)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.insert(0, "a");
|
list.insert(0, "a");
|
||||||
assert(added);
|
assert(added);
|
||||||
},
|
},
|
||||||
|
@ -288,7 +289,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "d", "e"]);
|
||||||
const range = new ListRange(1, 3, list.length);
|
const range = new ListRange(1, 3, list.length);
|
||||||
let added = false;
|
let added = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onAdd(idx, value) {
|
onAdd(idx, value) {
|
||||||
added = true;
|
added = true;
|
||||||
const result = range.queryAdd(idx, value, list);
|
const result = range.queryAdd(idx, value, list);
|
||||||
|
@ -300,7 +301,7 @@ export function tests() {
|
||||||
newRange: new ListRange(1, 3, 5)
|
newRange: new ListRange(1, 3, 5)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.insert(2, "c");
|
list.insert(2, "c");
|
||||||
assert(added);
|
assert(added);
|
||||||
},
|
},
|
||||||
|
@ -308,7 +309,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d"]);
|
const list = new ObservableArray(["a", "b", "c", "d"]);
|
||||||
const range = new ListRange(1, 3, list.length);
|
const range = new ListRange(1, 3, list.length);
|
||||||
let added = false;
|
let added = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onAdd(idx, value) {
|
onAdd(idx, value) {
|
||||||
added = true;
|
added = true;
|
||||||
const result = range.queryAdd(idx, value, list);
|
const result = range.queryAdd(idx, value, list);
|
||||||
|
@ -317,7 +318,7 @@ export function tests() {
|
||||||
newRange: new ListRange(1, 3, 5)
|
newRange: new ListRange(1, 3, 5)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.insert(4, "e");
|
list.insert(4, "e");
|
||||||
assert(added);
|
assert(added);
|
||||||
},
|
},
|
||||||
|
@ -326,7 +327,7 @@ export function tests() {
|
||||||
const viewportItemCount = 4;
|
const viewportItemCount = 4;
|
||||||
const range = new ListRange(0, 3, list.length, viewportItemCount);
|
const range = new ListRange(0, 3, list.length, viewportItemCount);
|
||||||
let added = false;
|
let added = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onAdd(idx, value) {
|
onAdd(idx, value) {
|
||||||
added = true;
|
added = true;
|
||||||
const result = range.queryAdd(idx, value, list);
|
const result = range.queryAdd(idx, value, list);
|
||||||
|
@ -337,7 +338,7 @@ export function tests() {
|
||||||
value: "c"
|
value: "c"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.insert(2, "c");
|
list.insert(2, "c");
|
||||||
assert(added);
|
assert(added);
|
||||||
},
|
},
|
||||||
|
@ -345,7 +346,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(1, 3, list.length);
|
const range = new ListRange(1, 3, list.length);
|
||||||
let removed = false;
|
let removed = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onRemove(idx) {
|
onRemove(idx) {
|
||||||
removed = true;
|
removed = true;
|
||||||
const result = range.queryRemove(idx, list);
|
const result = range.queryRemove(idx, list);
|
||||||
|
@ -357,7 +358,7 @@ export function tests() {
|
||||||
newRange: new ListRange(1, 3, 4)
|
newRange: new ListRange(1, 3, 4)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.remove(0);
|
list.remove(0);
|
||||||
assert(removed);
|
assert(removed);
|
||||||
},
|
},
|
||||||
|
@ -365,7 +366,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(1, 3, list.length);
|
const range = new ListRange(1, 3, list.length);
|
||||||
let removed = false;
|
let removed = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onRemove(idx) {
|
onRemove(idx) {
|
||||||
removed = true;
|
removed = true;
|
||||||
const result = range.queryRemove(idx, list);
|
const result = range.queryRemove(idx, list);
|
||||||
|
@ -378,7 +379,7 @@ export function tests() {
|
||||||
});
|
});
|
||||||
assert.equal(list.length, 4);
|
assert.equal(list.length, 4);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.remove(2);
|
list.remove(2);
|
||||||
assert(removed);
|
assert(removed);
|
||||||
},
|
},
|
||||||
|
@ -386,7 +387,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(1, 3, list.length);
|
const range = new ListRange(1, 3, list.length);
|
||||||
let removed = false;
|
let removed = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onRemove(idx) {
|
onRemove(idx) {
|
||||||
removed = true;
|
removed = true;
|
||||||
const result = range.queryRemove(idx, list);
|
const result = range.queryRemove(idx, list);
|
||||||
|
@ -395,7 +396,7 @@ export function tests() {
|
||||||
newRange: new ListRange(1, 3, 4)
|
newRange: new ListRange(1, 3, 4)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.remove(3);
|
list.remove(3);
|
||||||
assert(removed);
|
assert(removed);
|
||||||
},
|
},
|
||||||
|
@ -403,7 +404,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c"]);
|
const list = new ObservableArray(["a", "b", "c"]);
|
||||||
const range = new ListRange(1, 3, list.length);
|
const range = new ListRange(1, 3, list.length);
|
||||||
let removed = false;
|
let removed = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onRemove(idx) {
|
onRemove(idx) {
|
||||||
removed = true;
|
removed = true;
|
||||||
const result = range.queryRemove(idx, list);
|
const result = range.queryRemove(idx, list);
|
||||||
|
@ -415,7 +416,7 @@ export function tests() {
|
||||||
value: "a"
|
value: "a"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.remove(2);
|
list.remove(2);
|
||||||
assert(removed);
|
assert(removed);
|
||||||
},
|
},
|
||||||
|
@ -423,7 +424,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c"]);
|
const list = new ObservableArray(["a", "b", "c"]);
|
||||||
const range = new ListRange(0, 3, list.length);
|
const range = new ListRange(0, 3, list.length);
|
||||||
let removed = false;
|
let removed = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onRemove(idx) {
|
onRemove(idx) {
|
||||||
removed = true;
|
removed = true;
|
||||||
const result = range.queryRemove(idx, list);
|
const result = range.queryRemove(idx, list);
|
||||||
|
@ -433,7 +434,7 @@ export function tests() {
|
||||||
removeIdx: 2,
|
removeIdx: 2,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.remove(2);
|
list.remove(2);
|
||||||
assert(removed);
|
assert(removed);
|
||||||
},
|
},
|
||||||
|
@ -441,7 +442,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(1, 4, list.length);
|
const range = new ListRange(1, 4, list.length);
|
||||||
let moved = false;
|
let moved = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onMove(fromIdx, toIdx, value) {
|
onMove(fromIdx, toIdx, value) {
|
||||||
moved = true;
|
moved = true;
|
||||||
const result = range.queryMove(fromIdx, toIdx, value, list);
|
const result = range.queryMove(fromIdx, toIdx, value, list);
|
||||||
|
@ -451,7 +452,7 @@ export function tests() {
|
||||||
toIdx: 3
|
toIdx: 3
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.move(2, 3);
|
list.move(2, 3);
|
||||||
assert(moved);
|
assert(moved);
|
||||||
},
|
},
|
||||||
|
@ -459,7 +460,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(2, 5, list.length);
|
const range = new ListRange(2, 5, list.length);
|
||||||
let moved = false;
|
let moved = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onMove(fromIdx, toIdx, value) {
|
onMove(fromIdx, toIdx, value) {
|
||||||
moved = true;
|
moved = true;
|
||||||
const result = range.queryMove(fromIdx, toIdx, value, list);
|
const result = range.queryMove(fromIdx, toIdx, value, list);
|
||||||
|
@ -470,7 +471,7 @@ export function tests() {
|
||||||
value: "a"
|
value: "a"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.move(0, 3); // move "a" to after "d"
|
list.move(0, 3); // move "a" to after "d"
|
||||||
assert(moved);
|
assert(moved);
|
||||||
},
|
},
|
||||||
|
@ -478,7 +479,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(0, 3, list.length);
|
const range = new ListRange(0, 3, list.length);
|
||||||
let moved = false;
|
let moved = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onMove(fromIdx, toIdx, value) {
|
onMove(fromIdx, toIdx, value) {
|
||||||
moved = true;
|
moved = true;
|
||||||
const result = range.queryMove(fromIdx, toIdx, value, list);
|
const result = range.queryMove(fromIdx, toIdx, value, list);
|
||||||
|
@ -489,7 +490,7 @@ export function tests() {
|
||||||
value: "e"
|
value: "e"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.move(4, 1); // move "e" to before "b"
|
list.move(4, 1); // move "e" to before "b"
|
||||||
assert(moved);
|
assert(moved);
|
||||||
},
|
},
|
||||||
|
@ -497,7 +498,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(0, 3, list.length);
|
const range = new ListRange(0, 3, list.length);
|
||||||
let moved = false;
|
let moved = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onMove(fromIdx, toIdx, value) {
|
onMove(fromIdx, toIdx, value) {
|
||||||
moved = true;
|
moved = true;
|
||||||
const result = range.queryMove(fromIdx, toIdx, value, list);
|
const result = range.queryMove(fromIdx, toIdx, value, list);
|
||||||
|
@ -508,7 +509,7 @@ export function tests() {
|
||||||
value: "d"
|
value: "d"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.move(1, 3); // move "b" to after "d"
|
list.move(1, 3); // move "b" to after "d"
|
||||||
assert(moved);
|
assert(moved);
|
||||||
},
|
},
|
||||||
|
@ -516,7 +517,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(2, 5, list.length);
|
const range = new ListRange(2, 5, list.length);
|
||||||
let moved = false;
|
let moved = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onMove(fromIdx, toIdx, value) {
|
onMove(fromIdx, toIdx, value) {
|
||||||
moved = true;
|
moved = true;
|
||||||
const result = range.queryMove(fromIdx, toIdx, value, list);
|
const result = range.queryMove(fromIdx, toIdx, value, list);
|
||||||
|
@ -527,7 +528,7 @@ export function tests() {
|
||||||
value: "b"
|
value: "b"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.move(3, 0); // move "d" to before "a"
|
list.move(3, 0); // move "d" to before "a"
|
||||||
assert(moved);
|
assert(moved);
|
||||||
},
|
},
|
||||||
|
@ -535,7 +536,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(1, 4, list.length);
|
const range = new ListRange(1, 4, list.length);
|
||||||
let moved = false;
|
let moved = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onMove(fromIdx, toIdx, value) {
|
onMove(fromIdx, toIdx, value) {
|
||||||
moved = true;
|
moved = true;
|
||||||
const result = range.queryMove(fromIdx, toIdx, value, list);
|
const result = range.queryMove(fromIdx, toIdx, value, list);
|
||||||
|
@ -546,7 +547,7 @@ export function tests() {
|
||||||
value: "e"
|
value: "e"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.move(0, 4); // move "a" to after "e"
|
list.move(0, 4); // move "a" to after "e"
|
||||||
assert(moved);
|
assert(moved);
|
||||||
},
|
},
|
||||||
|
|
|
@ -43,7 +43,7 @@ export class Range {
|
||||||
return range.start < this.end && this.start < range.end;
|
return range.start < this.end && this.start < range.end;
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachInIterator<T>(it: IterableIterator<T>, callback: ((T, i: number) => void)) {
|
forEachInIterator<T>(it: Iterator<T>, callback: ((T, i: number) => void)) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (i = 0; i < this.start; i += 1) {
|
for (i = 0; i < this.start; i += 1) {
|
||||||
it.next();
|
it.next();
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface IAbortable {
|
export interface IAbortable {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,13 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
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") {
|
if (typeof value === "function") {
|
||||||
value();
|
value();
|
||||||
} else {
|
} 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");
|
return value && (typeof value === "function" || typeof value.dispose === "function");
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Disposables {
|
export class Disposables {
|
||||||
constructor() {
|
private _disposables: Disposable[] | null = [];
|
||||||
this._disposables = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
track(disposable) {
|
track(disposable: Disposable): Disposable {
|
||||||
if (!isDisposable(disposable)) {
|
if (!isDisposable(disposable)) {
|
||||||
throw new Error("Not a disposable");
|
throw new Error("Not a disposable");
|
||||||
}
|
}
|
||||||
|
@ -40,19 +44,23 @@ export class Disposables {
|
||||||
disposeValue(disposable);
|
disposeValue(disposable);
|
||||||
return disposable;
|
return disposable;
|
||||||
}
|
}
|
||||||
this._disposables.push(disposable);
|
this._disposables!.push(disposable);
|
||||||
return disposable;
|
return disposable;
|
||||||
}
|
}
|
||||||
|
|
||||||
untrack(disposable) {
|
untrack(disposable: Disposable): null {
|
||||||
const idx = this._disposables.indexOf(disposable);
|
if (this.isDisposed) {
|
||||||
|
console.warn("Disposables already disposed, cannot untrack");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const idx = this._disposables!.indexOf(disposable);
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
this._disposables.splice(idx, 1);
|
this._disposables!.splice(idx, 1);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose(): void {
|
||||||
if (this._disposables) {
|
if (this._disposables) {
|
||||||
for (const d of this._disposables) {
|
for (const d of this._disposables) {
|
||||||
disposeValue(d);
|
disposeValue(d);
|
||||||
|
@ -61,17 +69,17 @@ export class Disposables {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get isDisposed() {
|
get isDisposed(): boolean {
|
||||||
return this._disposables === null;
|
return this._disposables === null;
|
||||||
}
|
}
|
||||||
|
|
||||||
disposeTracked(value) {
|
disposeTracked(value: Disposable): null {
|
||||||
if (value === undefined || value === null || this.isDisposed) {
|
if (value === undefined || value === null || this.isDisposed) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const idx = this._disposables.indexOf(value);
|
const idx = this._disposables!.indexOf(value);
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
const [foundValue] = this._disposables.splice(idx, 1);
|
const [foundValue] = this._disposables!.splice(idx, 1);
|
||||||
disposeValue(foundValue);
|
disposeValue(foundValue);
|
||||||
} else {
|
} else {
|
||||||
console.warn("disposable not found, did it leak?", value);
|
console.warn("disposable not found, did it leak?", value);
|
|
@ -15,12 +15,10 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class Lock {
|
export class Lock {
|
||||||
constructor() {
|
private _promise?: Promise<void>;
|
||||||
this._promise = null;
|
private _resolve?: (() => void);
|
||||||
this._resolve = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
tryTake() {
|
tryTake(): boolean {
|
||||||
if (!this._promise) {
|
if (!this._promise) {
|
||||||
this._promise = new Promise(resolve => {
|
this._promise = new Promise(resolve => {
|
||||||
this._resolve = resolve;
|
this._resolve = resolve;
|
||||||
|
@ -30,36 +28,36 @@ export class Lock {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async take() {
|
async take(): Promise<void> {
|
||||||
while(!this.tryTake()) {
|
while(!this.tryTake()) {
|
||||||
await this.released();
|
await this.released();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get isTaken() {
|
get isTaken(): boolean {
|
||||||
return !!this._promise;
|
return !!this._promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
release() {
|
release(): void {
|
||||||
if (this._resolve) {
|
if (this._resolve) {
|
||||||
this._promise = null;
|
this._promise = undefined;
|
||||||
const resolve = this._resolve;
|
const resolve = this._resolve;
|
||||||
this._resolve = null;
|
this._resolve = undefined;
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
released() {
|
released(): Promise<void> | undefined {
|
||||||
return this._promise;
|
return this._promise;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MultiLock {
|
export class MultiLock {
|
||||||
constructor(locks) {
|
|
||||||
this.locks = locks;
|
constructor(public readonly locks: Lock[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
release() {
|
release(): void {
|
||||||
for (const lock of this.locks) {
|
for (const lock of this.locks) {
|
||||||
lock.release();
|
lock.release();
|
||||||
}
|
}
|
||||||
|
@ -86,9 +84,9 @@ export function tests() {
|
||||||
lock.tryTake();
|
lock.tryTake();
|
||||||
|
|
||||||
let first;
|
let first;
|
||||||
lock.released().then(() => first = lock.tryTake());
|
lock.released()!.then(() => first = lock.tryTake());
|
||||||
let second;
|
let second;
|
||||||
lock.released().then(() => second = lock.tryTake());
|
lock.released()!.then(() => second = lock.tryTake());
|
||||||
const promise = lock.released();
|
const promise = lock.released();
|
||||||
lock.release();
|
lock.release();
|
||||||
await promise;
|
await promise;
|
|
@ -14,14 +14,12 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Lock} from "./Lock.js";
|
import {Lock} from "./Lock";
|
||||||
|
|
||||||
export class LockMap {
|
export class LockMap<T> {
|
||||||
constructor() {
|
private readonly _map: Map<T, Lock> = new Map();
|
||||||
this._map = new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
async takeLock(key) {
|
async takeLock(key: T): Promise<Lock> {
|
||||||
let lock = this._map.get(key);
|
let lock = this._map.get(key);
|
||||||
if (lock) {
|
if (lock) {
|
||||||
await lock.take();
|
await lock.take();
|
||||||
|
@ -31,10 +29,10 @@ export class LockMap {
|
||||||
this._map.set(key, lock);
|
this._map.set(key, lock);
|
||||||
}
|
}
|
||||||
// don't leave old locks lying around
|
// don't leave old locks lying around
|
||||||
lock.released().then(() => {
|
lock.released()!.then(() => {
|
||||||
// give others a chance to take the lock first
|
// give others a chance to take the lock first
|
||||||
Promise.resolve().then(() => {
|
Promise.resolve().then(() => {
|
||||||
if (!lock.isTaken) {
|
if (!lock!.isTaken) {
|
||||||
this._map.delete(key);
|
this._map.delete(key);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -67,6 +65,7 @@ export function tests() {
|
||||||
ranSecond = true;
|
ranSecond = true;
|
||||||
assert.equal(returnedLock.isTaken, true);
|
assert.equal(returnedLock.isTaken, true);
|
||||||
// peek into internals, naughty
|
// peek into internals, naughty
|
||||||
|
// @ts-ignore
|
||||||
assert.equal(lockMap._map.get("foo"), returnedLock);
|
assert.equal(lockMap._map.get("foo"), returnedLock);
|
||||||
});
|
});
|
||||||
lock.release();
|
lock.release();
|
||||||
|
@ -84,6 +83,7 @@ export function tests() {
|
||||||
// double delay to make sure cleanup logic ran
|
// double delay to make sure cleanup logic ran
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
|
// @ts-ignore
|
||||||
assert.equal(lockMap._map.has("foo"), false);
|
assert.equal(lockMap._map.has("foo"), false);
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,16 +15,18 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class RetainedValue {
|
export class RetainedValue {
|
||||||
constructor(freeCallback) {
|
private readonly _freeCallback: () => void;
|
||||||
|
private _retentionCount: number = 1;
|
||||||
|
|
||||||
|
constructor(freeCallback: () => void) {
|
||||||
this._freeCallback = freeCallback;
|
this._freeCallback = freeCallback;
|
||||||
this._retentionCount = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retain() {
|
retain(): void {
|
||||||
this._retentionCount += 1;
|
this._retentionCount += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
release() {
|
release(): void {
|
||||||
this._retentionCount -= 1;
|
this._retentionCount -= 1;
|
||||||
if (this._retentionCount === 0) {
|
if (this._retentionCount === 0) {
|
||||||
this._freeCallback();
|
this._freeCallback();
|
|
@ -6,8 +6,10 @@
|
||||||
* Based on https://github.com/junkurihara/jscu/blob/develop/packages/js-crypto-hkdf/src/hkdf.ts
|
* 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
|
// 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;
|
length = length / 8;
|
||||||
const len = cryptoDriver.digestSize(hash);
|
const len = cryptoDriver.digestSize(hash);
|
||||||
|
|
|
@ -6,17 +6,19 @@
|
||||||
* Based on https://github.com/junkurihara/jscu/blob/develop/packages/js-crypto-pbkdf/src/pbkdf.ts
|
* 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
|
// not used atm, but might in the future
|
||||||
// forked this code to make it use the cryptoDriver for HMAC that is more backwards-compatible
|
// 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);
|
const arr = new Uint8Array(len);
|
||||||
for(let i=0; i<len; i++) arr[i] = 0xFF && (num >> ((len - i - 1)*8));
|
for(let i=0; i<len; i++) arr[i] = 0xFF && (num >> ((len - i - 1)*8));
|
||||||
return arr;
|
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;
|
const dkLen = length / 8;
|
||||||
if (iterations <= 0) {
|
if (iterations <= 0) {
|
||||||
throw new Error('InvalidIterationCount');
|
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 l = Math.ceil(dkLen/hLen);
|
||||||
const r = dkLen - (l-1)*hLen;
|
const r = dkLen - (l-1)*hLen;
|
||||||
|
|
||||||
const funcF = async (i) => {
|
const funcF = async (i: number) => {
|
||||||
const seed = new Uint8Array(salt.length + 4);
|
const seed = new Uint8Array(salt.length + 4);
|
||||||
seed.set(salt);
|
seed.set(salt);
|
||||||
seed.set(nwbo(i+1, 4), salt.length);
|
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};
|
return {index: i, value: outputF};
|
||||||
};
|
};
|
||||||
|
|
||||||
const Tis = [];
|
const Tis: Promise<{index: number, value: Uint8Array}>[] = [];
|
||||||
const DK = new Uint8Array(dkLen);
|
const DK = new Uint8Array(dkLen);
|
||||||
for(let i = 0; i < l; i++) {
|
for(let i = 0; i < l; i++) {
|
||||||
Tis.push(funcF(i));
|
Tis.push(funcF(i));
|
|
@ -14,12 +14,9 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function createEnum(...values) {
|
export function createEnum(...values: string[]): Readonly<{}> {
|
||||||
const obj = {};
|
const obj = {};
|
||||||
for (const value of values) {
|
for (const value of values) {
|
||||||
if (typeof value !== "string") {
|
|
||||||
throw new Error("Invalid enum value name" + value?.toString());
|
|
||||||
}
|
|
||||||
obj[value] = value;
|
obj[value] = value;
|
||||||
}
|
}
|
||||||
return Object.freeze(obj);
|
return Object.freeze(obj);
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class AbortError extends Error {
|
export class AbortError extends Error {
|
||||||
get name() {
|
get name(): string {
|
||||||
return "AbortError";
|
return "AbortError";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function formatSize(size, decimals = 2) {
|
|
||||||
|
export function formatSize(size: number, decimals: number = 2): string {
|
||||||
if (Number.isSafeInteger(size)) {
|
if (Number.isSafeInteger(size)) {
|
||||||
const base = Math.min(3, Math.floor(Math.log(size) / Math.log(1024)));
|
const base = Math.min(3, Math.floor(Math.log(size) / Math.log(1024)));
|
||||||
const formattedSize = Math.round(size / Math.pow(1024, base)).toFixed(decimals);
|
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`;
|
case 3: return `${formattedSize} GB`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return "";
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
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) {
|
if (src) {
|
||||||
for (const [key, value] of src.entries()) {
|
for (const [key, value] of src.entries()) {
|
||||||
dst.set(key, value);
|
dst.set(key, value);
|
|
@ -22,7 +22,7 @@ limitations under the License.
|
||||||
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
|
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
|
||||||
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
* 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 low = 0;
|
||||||
let high = array.length;
|
let high = array.length;
|
||||||
|
|
|
@ -16,9 +16,12 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ConnectionError} from "../matrix/error.js";
|
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);
|
const timeout = createTimeout(timeoutAmount);
|
||||||
// abort request if timeout finishes first
|
// abort request if timeout finishes first
|
||||||
let timedOut = false;
|
let timedOut = false;
|
|
@ -3124,10 +3124,10 @@ import-fresh@^3.0.0, import-fresh@^3.2.1:
|
||||||
parent-module "^1.0.0"
|
parent-module "^1.0.0"
|
||||||
resolve-from "^4.0.0"
|
resolve-from "^4.0.0"
|
||||||
|
|
||||||
impunity@^1.0.8:
|
impunity@^1.0.9:
|
||||||
version "1.0.8"
|
version "1.0.9"
|
||||||
resolved "https://registry.yarnpkg.com/impunity/-/impunity-1.0.8.tgz#d6db44e8a15ca2cdaed6a5c478a770853cb5a56e"
|
resolved "https://registry.yarnpkg.com/impunity/-/impunity-1.0.9.tgz#8524d96f07c26987519ec693c4c4d3ab49254b03"
|
||||||
integrity sha512-6jMqYrvY2SA/PZ+yheJYd3eJ3zcO8dWmHRVy/BSjnKMEmIVB+lMO30MZOkG+kHH0eJuaGOKv0BrZmgwE6NUDRg==
|
integrity sha512-tfy7GRHeE9JVURKM7dqfTAZItGFeA/DRrlhgMLUuzSig3jF+AYSUV26tGTMGrfCN0Cb9hNz6xrZnNwa5M1hz4Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
colors "^1.3.3"
|
colors "^1.3.3"
|
||||||
commander "^6.1.0"
|
commander "^6.1.0"
|
||||||
|
|
Loading…
Reference in a new issue