Merge branch 'master' into kegan/syncv3

This commit is contained in:
Kegan Dougal 2021-12-02 10:54:07 +00:00
commit f193418ed1
68 changed files with 533 additions and 394 deletions

View file

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

View file

@ -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",

View file

@ -19,7 +19,7 @@ limitations under the License.
// we do need to return a disposable from EventEmitter.on, or at least have a method here to easily track a subscription to an EventEmitter // 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 = {}) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,10 +119,13 @@ 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);
if (serializedItem) {
const transformedSerializedItem = this._serializedTransformer(serializedItem);
this._queuedItems.push({ this._queuedItems.push({
json: JSON.stringify(serializedItem) json: JSON.stringify(transformedSerializedItem)
}); });
} }
}
_persistQueuedItems(items: QueuedItem[]): void { _persistQueuedItems(items: QueuedItem[]): void {
try { try {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

@ -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}`;
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -13,8 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and 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";

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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";
/* /*

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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";
} }
} }

View file

@ -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 "";
} }

View file

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

View file

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

View file

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

View file

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