You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-26 17:03:12 +03:00
Merge pull request #1907 from matrix-org/gsouquet/ts-last-push
This commit is contained in:
@@ -17,79 +17,19 @@ limitations under the License.
|
|||||||
|
|
||||||
/** @module auto-discovery */
|
/** @module auto-discovery */
|
||||||
|
|
||||||
|
import { IClientWellKnown, IWellKnownConfig } from "./client";
|
||||||
import { logger } from './logger';
|
import { logger } from './logger';
|
||||||
import { URL as NodeURL } from "url";
|
import { URL as NodeURL } from "url";
|
||||||
|
|
||||||
// Dev note: Auto discovery is part of the spec.
|
// Dev note: Auto discovery is part of the spec.
|
||||||
// See: https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery
|
// See: https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery
|
||||||
|
|
||||||
/**
|
export enum AutoDiscoveryAction {
|
||||||
* Description for what an automatically discovered client configuration
|
SUCCESS = "SUCCESS",
|
||||||
* would look like. Although this is a class, it is recommended that it
|
IGNORE = "IGNORE",
|
||||||
* be treated as an interface definition rather than as a class.
|
PROMPT = "PROMPT",
|
||||||
*
|
FAIL_PROMPT = "FAIL_PROMPT",
|
||||||
* Additional properties than those defined here may be present, and
|
FAIL_ERROR = "FAIL_ERROR",
|
||||||
* should follow the Java package naming convention.
|
|
||||||
*/
|
|
||||||
class DiscoveredClientConfig { // eslint-disable-line no-unused-vars
|
|
||||||
// Dev note: this is basically a copy/paste of the .well-known response
|
|
||||||
// object as defined in the spec. It does have additional information,
|
|
||||||
// however. Overall, this exists to serve as a place for documentation
|
|
||||||
// and not functionality.
|
|
||||||
// See https://matrix.org/docs/spec/client_server/r0.4.0.html#get-well-known-matrix-client
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
/**
|
|
||||||
* The homeserver configuration the client should use. This will
|
|
||||||
* always be present on the object.
|
|
||||||
* @type {{state: string, base_url: string}} The configuration.
|
|
||||||
*/
|
|
||||||
this["m.homeserver"] = {
|
|
||||||
/**
|
|
||||||
* The lookup result state. If this is anything other than
|
|
||||||
* AutoDiscovery.SUCCESS then base_url may be falsey. Additionally,
|
|
||||||
* if this is not AutoDiscovery.SUCCESS then the client should
|
|
||||||
* assume the other properties in the client config (such as
|
|
||||||
* the identity server configuration) are not valid.
|
|
||||||
*/
|
|
||||||
state: AutoDiscovery.PROMPT,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the state is AutoDiscovery.FAIL_ERROR or .FAIL_PROMPT
|
|
||||||
* then this will contain a human-readable (English) message
|
|
||||||
* for what went wrong. If the state is none of those previously
|
|
||||||
* mentioned, this will be falsey.
|
|
||||||
*/
|
|
||||||
error: "Something went wrong",
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The base URL clients should use to talk to the homeserver,
|
|
||||||
* particularly for the login process. May be falsey if the
|
|
||||||
* state is not AutoDiscovery.SUCCESS.
|
|
||||||
*/
|
|
||||||
base_url: "https://matrix.org",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The identity server configuration the client should use. This
|
|
||||||
* will always be present on teh object.
|
|
||||||
* @type {{state: string, base_url: string}} The configuration.
|
|
||||||
*/
|
|
||||||
this["m.identity_server"] = {
|
|
||||||
/**
|
|
||||||
* The lookup result state. If this is anything other than
|
|
||||||
* AutoDiscovery.SUCCESS then base_url may be falsey.
|
|
||||||
*/
|
|
||||||
state: AutoDiscovery.PROMPT,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The base URL clients should use for interacting with the
|
|
||||||
* identity server. May be falsey if the state is not
|
|
||||||
* AutoDiscovery.SUCCESS.
|
|
||||||
*/
|
|
||||||
base_url: "https://vector.im",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -102,44 +42,26 @@ export class AutoDiscovery {
|
|||||||
// translate the meaning of the states in the spec, but also
|
// translate the meaning of the states in the spec, but also
|
||||||
// support our own if needed.
|
// support our own if needed.
|
||||||
|
|
||||||
static get ERROR_INVALID() {
|
public static readonly ERROR_INVALID = "Invalid homeserver discovery response";
|
||||||
return "Invalid homeserver discovery response";
|
|
||||||
}
|
|
||||||
|
|
||||||
static get ERROR_GENERIC_FAILURE() {
|
public static readonly ERROR_GENERIC_FAILURE = "Failed to get autodiscovery configuration from server";
|
||||||
return "Failed to get autodiscovery configuration from server";
|
|
||||||
}
|
|
||||||
|
|
||||||
static get ERROR_INVALID_HS_BASE_URL() {
|
public static readonly ERROR_INVALID_HS_BASE_URL = "Invalid base_url for m.homeserver";
|
||||||
return "Invalid base_url for m.homeserver";
|
|
||||||
}
|
|
||||||
|
|
||||||
static get ERROR_INVALID_HOMESERVER() {
|
public static readonly ERROR_INVALID_HOMESERVER = "Homeserver URL does not appear to be a valid Matrix homeserver";
|
||||||
return "Homeserver URL does not appear to be a valid Matrix homeserver";
|
|
||||||
}
|
|
||||||
|
|
||||||
static get ERROR_INVALID_IS_BASE_URL() {
|
public static readonly ERROR_INVALID_IS_BASE_URL = "Invalid base_url for m.identity_server";
|
||||||
return "Invalid base_url for m.identity_server";
|
|
||||||
}
|
|
||||||
|
|
||||||
static get ERROR_INVALID_IDENTITY_SERVER() {
|
// eslint-disable-next-line
|
||||||
return "Identity server URL does not appear to be a valid identity server";
|
public static readonly ERROR_INVALID_IDENTITY_SERVER = "Identity server URL does not appear to be a valid identity server";
|
||||||
}
|
|
||||||
|
|
||||||
static get ERROR_INVALID_IS() {
|
public static readonly ERROR_INVALID_IS = "Invalid identity server discovery response";
|
||||||
return "Invalid identity server discovery response";
|
|
||||||
}
|
|
||||||
|
|
||||||
static get ERROR_MISSING_WELLKNOWN() {
|
public static readonly ERROR_MISSING_WELLKNOWN = "No .well-known JSON file found";
|
||||||
return "No .well-known JSON file found";
|
|
||||||
}
|
|
||||||
|
|
||||||
static get ERROR_INVALID_JSON() {
|
public static readonly ERROR_INVALID_JSON = "Invalid JSON";
|
||||||
return "Invalid JSON";
|
|
||||||
}
|
|
||||||
|
|
||||||
static get ALL_ERRORS() {
|
public static readonly ALL_ERRORS = [
|
||||||
return [
|
|
||||||
AutoDiscovery.ERROR_INVALID,
|
AutoDiscovery.ERROR_INVALID,
|
||||||
AutoDiscovery.ERROR_GENERIC_FAILURE,
|
AutoDiscovery.ERROR_GENERIC_FAILURE,
|
||||||
AutoDiscovery.ERROR_INVALID_HS_BASE_URL,
|
AutoDiscovery.ERROR_INVALID_HS_BASE_URL,
|
||||||
@@ -150,7 +72,6 @@ export class AutoDiscovery {
|
|||||||
AutoDiscovery.ERROR_MISSING_WELLKNOWN,
|
AutoDiscovery.ERROR_MISSING_WELLKNOWN,
|
||||||
AutoDiscovery.ERROR_INVALID_JSON,
|
AutoDiscovery.ERROR_INVALID_JSON,
|
||||||
];
|
];
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The auto discovery failed. The client is expected to communicate
|
* The auto discovery failed. The client is expected to communicate
|
||||||
@@ -158,7 +79,7 @@ export class AutoDiscovery {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
static get FAIL_ERROR() { return "FAIL_ERROR"; }
|
public static readonly FAIL_ERROR = AutoDiscoveryAction.FAIL_ERROR;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The auto discovery failed, however the client may still recover
|
* The auto discovery failed, however the client may still recover
|
||||||
@@ -169,7 +90,7 @@ export class AutoDiscovery {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
static get FAIL_PROMPT() { return "FAIL_PROMPT"; }
|
public static readonly FAIL_PROMPT = AutoDiscoveryAction.FAIL_PROMPT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The auto discovery didn't fail but did not find anything of
|
* The auto discovery didn't fail but did not find anything of
|
||||||
@@ -178,14 +99,14 @@ export class AutoDiscovery {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
static get PROMPT() { return "PROMPT"; }
|
public static readonly PROMPT = AutoDiscoveryAction.PROMPT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The auto discovery was successful.
|
* The auto discovery was successful.
|
||||||
* @return {string}
|
* @return {string}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
static get SUCCESS() { return "SUCCESS"; }
|
public static readonly SUCCESS = AutoDiscoveryAction.SUCCESS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates and verifies client configuration information for purposes
|
* Validates and verifies client configuration information for purposes
|
||||||
@@ -199,7 +120,7 @@ export class AutoDiscovery {
|
|||||||
* configuration, which may include error states. Rejects on unexpected
|
* configuration, which may include error states. Rejects on unexpected
|
||||||
* failure, not when verification fails.
|
* failure, not when verification fails.
|
||||||
*/
|
*/
|
||||||
static async fromDiscoveryConfig(wellknown) {
|
public static async fromDiscoveryConfig(wellknown: string): Promise<IClientWellKnown> {
|
||||||
// Step 1 is to get the config, which is provided to us here.
|
// Step 1 is to get the config, which is provided to us here.
|
||||||
|
|
||||||
// We default to an error state to make the first few checks easier to
|
// We default to an error state to make the first few checks easier to
|
||||||
@@ -240,7 +161,7 @@ export class AutoDiscovery {
|
|||||||
|
|
||||||
// Step 2: Make sure the homeserver URL is valid *looking*. We'll make
|
// Step 2: Make sure the homeserver URL is valid *looking*. We'll make
|
||||||
// sure it points to a homeserver in Step 3.
|
// sure it points to a homeserver in Step 3.
|
||||||
const hsUrl = this._sanitizeWellKnownUrl(
|
const hsUrl = this.sanitizeWellKnownUrl(
|
||||||
wellknown["m.homeserver"]["base_url"],
|
wellknown["m.homeserver"]["base_url"],
|
||||||
);
|
);
|
||||||
if (!hsUrl) {
|
if (!hsUrl) {
|
||||||
@@ -250,7 +171,7 @@ export class AutoDiscovery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Make sure the homeserver URL points to a homeserver.
|
// Step 3: Make sure the homeserver URL points to a homeserver.
|
||||||
const hsVersions = await this._fetchWellKnownObject(
|
const hsVersions = await this.fetchWellKnownObject(
|
||||||
`${hsUrl}/_matrix/client/versions`,
|
`${hsUrl}/_matrix/client/versions`,
|
||||||
);
|
);
|
||||||
if (!hsVersions || !hsVersions.raw["versions"]) {
|
if (!hsVersions || !hsVersions.raw["versions"]) {
|
||||||
@@ -272,7 +193,7 @@ export class AutoDiscovery {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Step 5: Try to pull out the identity server configuration
|
// Step 5: Try to pull out the identity server configuration
|
||||||
let isUrl = "";
|
let isUrl: string | boolean = "";
|
||||||
if (wellknown["m.identity_server"]) {
|
if (wellknown["m.identity_server"]) {
|
||||||
// We prepare a failing identity server response to save lines later
|
// We prepare a failing identity server response to save lines later
|
||||||
// in this branch.
|
// in this branch.
|
||||||
@@ -287,7 +208,7 @@ export class AutoDiscovery {
|
|||||||
|
|
||||||
// Step 5a: Make sure the URL is valid *looking*. We'll make sure it
|
// Step 5a: Make sure the URL is valid *looking*. We'll make sure it
|
||||||
// points to an identity server in Step 5b.
|
// points to an identity server in Step 5b.
|
||||||
isUrl = this._sanitizeWellKnownUrl(
|
isUrl = this.sanitizeWellKnownUrl(
|
||||||
wellknown["m.identity_server"]["base_url"],
|
wellknown["m.identity_server"]["base_url"],
|
||||||
);
|
);
|
||||||
if (!isUrl) {
|
if (!isUrl) {
|
||||||
@@ -299,10 +220,10 @@ export class AutoDiscovery {
|
|||||||
|
|
||||||
// Step 5b: Verify there is an identity server listening on the provided
|
// Step 5b: Verify there is an identity server listening on the provided
|
||||||
// URL.
|
// URL.
|
||||||
const isResponse = await this._fetchWellKnownObject(
|
const isResponse = await this.fetchWellKnownObject(
|
||||||
`${isUrl}/_matrix/identity/api/v1`,
|
`${isUrl}/_matrix/identity/api/v1`,
|
||||||
);
|
);
|
||||||
if (!isResponse || !isResponse.raw || isResponse.action !== "SUCCESS") {
|
if (!isResponse || !isResponse.raw || isResponse.action !== AutoDiscoveryAction.SUCCESS) {
|
||||||
logger.error("Invalid /api/v1 response");
|
logger.error("Invalid /api/v1 response");
|
||||||
failingClientConfig["m.identity_server"].error =
|
failingClientConfig["m.identity_server"].error =
|
||||||
AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER;
|
AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER;
|
||||||
@@ -317,7 +238,7 @@ export class AutoDiscovery {
|
|||||||
|
|
||||||
// Step 6: Now that the identity server is valid, or never existed,
|
// Step 6: Now that the identity server is valid, or never existed,
|
||||||
// populate the IS section.
|
// populate the IS section.
|
||||||
if (isUrl && isUrl.length > 0) {
|
if (isUrl && isUrl.toString().length > 0) {
|
||||||
clientConfig["m.identity_server"] = {
|
clientConfig["m.identity_server"] = {
|
||||||
state: AutoDiscovery.SUCCESS,
|
state: AutoDiscovery.SUCCESS,
|
||||||
error: null,
|
error: null,
|
||||||
@@ -359,7 +280,7 @@ export class AutoDiscovery {
|
|||||||
* configuration, which may include error states. Rejects on unexpected
|
* configuration, which may include error states. Rejects on unexpected
|
||||||
* failure, not when discovery fails.
|
* failure, not when discovery fails.
|
||||||
*/
|
*/
|
||||||
static async findClientConfig(domain) {
|
public static async findClientConfig(domain: string): Promise<IClientWellKnown> {
|
||||||
if (!domain || typeof(domain) !== "string" || domain.length === 0) {
|
if (!domain || typeof(domain) !== "string" || domain.length === 0) {
|
||||||
throw new Error("'domain' must be a string of non-zero length");
|
throw new Error("'domain' must be a string of non-zero length");
|
||||||
}
|
}
|
||||||
@@ -395,13 +316,13 @@ export class AutoDiscovery {
|
|||||||
|
|
||||||
// Step 1: Actually request the .well-known JSON file and make sure it
|
// Step 1: Actually request the .well-known JSON file and make sure it
|
||||||
// at least has a homeserver definition.
|
// at least has a homeserver definition.
|
||||||
const wellknown = await this._fetchWellKnownObject(
|
const wellknown = await this.fetchWellKnownObject(
|
||||||
`https://${domain}/.well-known/matrix/client`,
|
`https://${domain}/.well-known/matrix/client`,
|
||||||
);
|
);
|
||||||
if (!wellknown || wellknown.action !== "SUCCESS") {
|
if (!wellknown || wellknown.action !== AutoDiscoveryAction.SUCCESS) {
|
||||||
logger.error("No response or error when parsing .well-known");
|
logger.error("No response or error when parsing .well-known");
|
||||||
if (wellknown.reason) logger.error(wellknown.reason);
|
if (wellknown.reason) logger.error(wellknown.reason);
|
||||||
if (wellknown.action === "IGNORE") {
|
if (wellknown.action === AutoDiscoveryAction.IGNORE) {
|
||||||
clientConfig["m.homeserver"] = {
|
clientConfig["m.homeserver"] = {
|
||||||
state: AutoDiscovery.PROMPT,
|
state: AutoDiscovery.PROMPT,
|
||||||
error: null,
|
error: null,
|
||||||
@@ -427,12 +348,12 @@ export class AutoDiscovery {
|
|||||||
* @returns {Promise<object>} Resolves to the domain's client config. Can
|
* @returns {Promise<object>} Resolves to the domain's client config. Can
|
||||||
* be an empty object.
|
* be an empty object.
|
||||||
*/
|
*/
|
||||||
static async getRawClientConfig(domain) {
|
public static async getRawClientConfig(domain: string): Promise<IClientWellKnown> {
|
||||||
if (!domain || typeof(domain) !== "string" || domain.length === 0) {
|
if (!domain || typeof(domain) !== "string" || domain.length === 0) {
|
||||||
throw new Error("'domain' must be a string of non-zero length");
|
throw new Error("'domain' must be a string of non-zero length");
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await this._fetchWellKnownObject(
|
const response = await this.fetchWellKnownObject(
|
||||||
`https://${domain}/.well-known/matrix/client`,
|
`https://${domain}/.well-known/matrix/client`,
|
||||||
);
|
);
|
||||||
if (!response) return {};
|
if (!response) return {};
|
||||||
@@ -447,7 +368,7 @@ export class AutoDiscovery {
|
|||||||
* @return {string|boolean} The sanitized URL or a falsey value if the URL is invalid.
|
* @return {string|boolean} The sanitized URL or a falsey value if the URL is invalid.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
static _sanitizeWellKnownUrl(url) {
|
private static sanitizeWellKnownUrl(url: string): string | boolean {
|
||||||
if (!url) return false;
|
if (!url) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -495,8 +416,9 @@ export class AutoDiscovery {
|
|||||||
* @return {Promise<object>} Resolves to the returned state.
|
* @return {Promise<object>} Resolves to the returned state.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
static async _fetchWellKnownObject(url) {
|
private static async fetchWellKnownObject(url: string): Promise<IWellKnownConfig> {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
|
// eslint-disable-next-line
|
||||||
const request = require("./matrix").getRequest();
|
const request = require("./matrix").getRequest();
|
||||||
if (!request) throw new Error("No request library available");
|
if (!request) throw new Error("No request library available");
|
||||||
request(
|
request(
|
||||||
@@ -505,10 +427,10 @@ export class AutoDiscovery {
|
|||||||
if (err || response &&
|
if (err || response &&
|
||||||
(response.statusCode < 200 || response.statusCode >= 300)
|
(response.statusCode < 200 || response.statusCode >= 300)
|
||||||
) {
|
) {
|
||||||
let action = "FAIL_PROMPT";
|
let action = AutoDiscoveryAction.FAIL_PROMPT;
|
||||||
let reason = (err ? err.message : null) || "General failure";
|
let reason = (err ? err.message : null) || "General failure";
|
||||||
if (response && response.statusCode === 404) {
|
if (response && response.statusCode === 404) {
|
||||||
action = "IGNORE";
|
action = AutoDiscoveryAction.IGNORE;
|
||||||
reason = AutoDiscovery.ERROR_MISSING_WELLKNOWN;
|
reason = AutoDiscovery.ERROR_MISSING_WELLKNOWN;
|
||||||
}
|
}
|
||||||
resolve({ raw: {}, action: action, reason: reason, error: err });
|
resolve({ raw: {}, action: action, reason: reason, error: err });
|
||||||
@@ -516,7 +438,7 @@ export class AutoDiscovery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
resolve({ raw: JSON.parse(body), action: "SUCCESS" });
|
resolve({ raw: JSON.parse(body), action: AutoDiscoveryAction.SUCCESS });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
let reason = AutoDiscovery.ERROR_INVALID;
|
let reason = AutoDiscovery.ERROR_INVALID;
|
||||||
if (e.name === "SyntaxError") {
|
if (e.name === "SyntaxError") {
|
||||||
@@ -524,7 +446,7 @@ export class AutoDiscovery {
|
|||||||
}
|
}
|
||||||
resolve({
|
resolve({
|
||||||
raw: {},
|
raw: {},
|
||||||
action: "FAIL_PROMPT",
|
action: AutoDiscoveryAction.FAIL_PROMPT,
|
||||||
reason: reason,
|
reason: reason,
|
||||||
error: e,
|
error: e,
|
||||||
});
|
});
|
||||||
@@ -31,7 +31,7 @@ import { sleep } from './utils';
|
|||||||
import { Group } from "./models/group";
|
import { Group } from "./models/group";
|
||||||
import { Direction, EventTimeline } from "./models/event-timeline";
|
import { Direction, EventTimeline } from "./models/event-timeline";
|
||||||
import { IActionsObject, PushProcessor } from "./pushprocessor";
|
import { IActionsObject, PushProcessor } from "./pushprocessor";
|
||||||
import { AutoDiscovery } from "./autodiscovery";
|
import { AutoDiscovery, AutoDiscoveryAction } from "./autodiscovery";
|
||||||
import * as olmlib from "./crypto/olmlib";
|
import * as olmlib from "./crypto/olmlib";
|
||||||
import { decodeBase64, encodeBase64 } from "./crypto/olmlib";
|
import { decodeBase64, encodeBase64 } from "./crypto/olmlib";
|
||||||
import { IExportedDevice as IOlmDevice } from "./crypto/OlmDevice";
|
import { IExportedDevice as IOlmDevice } from "./crypto/OlmDevice";
|
||||||
@@ -476,14 +476,19 @@ interface IServerVersions {
|
|||||||
unstable_features: Record<string, boolean>;
|
unstable_features: Record<string, boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IClientWellKnown {
|
export interface IClientWellKnown {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
"m.homeserver": {
|
"m.homeserver"?: IWellKnownConfig;
|
||||||
base_url: string;
|
"m.identity_server"?: IWellKnownConfig;
|
||||||
};
|
}
|
||||||
"m.identity_server"?: {
|
|
||||||
base_url: string;
|
export interface IWellKnownConfig {
|
||||||
};
|
raw?: any; // todo typings
|
||||||
|
action?: AutoDiscoveryAction;
|
||||||
|
reason?: string;
|
||||||
|
error?: Error | string;
|
||||||
|
// eslint-disable-next-line
|
||||||
|
base_url?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IKeyBackupPath {
|
interface IKeyBackupPath {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// can't just do InvalidStoreError extends Error
|
// can't just do InvalidStoreError extends Error
|
||||||
// because of http://babeljs.io/docs/usage/caveats/#classes
|
// because of http://babeljs.io/docs/usage/caveats/#classes
|
||||||
export function InvalidStoreError(reason, value) {
|
export function InvalidStoreError(reason: string, value: boolean): void {
|
||||||
const message = `Store is invalid because ${reason}, ` +
|
const message = `Store is invalid because ${reason}, ` +
|
||||||
`please stop the client, delete all data and start the client again`;
|
`please stop the client, delete all data and start the client again`;
|
||||||
const instance = Reflect.construct(Error, [message]);
|
const instance = Reflect.construct(Error, [message]);
|
||||||
@@ -22,7 +22,7 @@ InvalidStoreError.prototype = Object.create(Error.prototype, {
|
|||||||
});
|
});
|
||||||
Reflect.setPrototypeOf(InvalidStoreError, Error);
|
Reflect.setPrototypeOf(InvalidStoreError, Error);
|
||||||
|
|
||||||
export function InvalidCryptoStoreError(reason) {
|
export function InvalidCryptoStoreError(reason: string): void {
|
||||||
const message = `Crypto store is invalid because ${reason}, ` +
|
const message = `Crypto store is invalid because ${reason}, ` +
|
||||||
`please stop the client, delete all data and start the client again`;
|
`please stop the client, delete all data and start the client again`;
|
||||||
const instance = Reflect.construct(Error, [message]);
|
const instance = Reflect.construct(Error, [message]);
|
||||||
@@ -45,8 +45,7 @@ InvalidCryptoStoreError.prototype = Object.create(Error.prototype, {
|
|||||||
Reflect.setPrototypeOf(InvalidCryptoStoreError, Error);
|
Reflect.setPrototypeOf(InvalidCryptoStoreError, Error);
|
||||||
|
|
||||||
export class KeySignatureUploadError extends Error {
|
export class KeySignatureUploadError extends Error {
|
||||||
constructor(message, value) {
|
constructor(message: string, public value: { failures: any }) { // TODO: types
|
||||||
super(message);
|
super(message);
|
||||||
this.value = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,17 +31,22 @@ import { logger } from './logger';
|
|||||||
const TIMER_CHECK_PERIOD_MS = 1000;
|
const TIMER_CHECK_PERIOD_MS = 1000;
|
||||||
|
|
||||||
// counter, for making up ids to return from setTimeout
|
// counter, for making up ids to return from setTimeout
|
||||||
let _count = 0;
|
let count = 0;
|
||||||
|
|
||||||
// the key for our callback with the real global.setTimeout
|
// the key for our callback with the real global.setTimeout
|
||||||
let _realCallbackKey;
|
let realCallbackKey: NodeJS.Timeout | number;
|
||||||
|
|
||||||
// a sorted list of the callbacks to be run.
|
// a sorted list of the callbacks to be run.
|
||||||
// each is an object with keys [runAt, func, params, key].
|
// each is an object with keys [runAt, func, params, key].
|
||||||
const _callbackList = [];
|
const callbackList: {
|
||||||
|
runAt: number;
|
||||||
|
func: (...params: any[]) => void;
|
||||||
|
params: any[];
|
||||||
|
key: number;
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
// var debuglog = logger.log.bind(logger);
|
// var debuglog = logger.log.bind(logger);
|
||||||
const debuglog = function() {};
|
const debuglog = function(...params: any[]) {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace the function used by this module to get the current time.
|
* Replace the function used by this module to get the current time.
|
||||||
@@ -52,10 +57,10 @@ const debuglog = function() {};
|
|||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export function setNow(f) {
|
export function setNow(f: () => number): void {
|
||||||
_now = f || Date.now;
|
now = f || Date.now;
|
||||||
}
|
}
|
||||||
let _now = Date.now;
|
let now = Date.now;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* reimplementation of window.setTimeout, which will call the callback if
|
* reimplementation of window.setTimeout, which will call the callback if
|
||||||
@@ -67,15 +72,14 @@ let _now = Date.now;
|
|||||||
* @return {Number} an identifier for this callback, which may be passed into
|
* @return {Number} an identifier for this callback, which may be passed into
|
||||||
* clearTimeout later.
|
* clearTimeout later.
|
||||||
*/
|
*/
|
||||||
export function setTimeout(func, delayMs) {
|
export function setTimeout(func: (...params: any[]) => void, delayMs: number, ...params: any[]): number {
|
||||||
delayMs = delayMs || 0;
|
delayMs = delayMs || 0;
|
||||||
if (delayMs < 0) {
|
if (delayMs < 0) {
|
||||||
delayMs = 0;
|
delayMs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = Array.prototype.slice.call(arguments, 2);
|
const runAt = now() + delayMs;
|
||||||
const runAt = _now() + delayMs;
|
const key = count++;
|
||||||
const key = _count++;
|
|
||||||
debuglog("setTimeout: scheduling cb", key, "at", runAt,
|
debuglog("setTimeout: scheduling cb", key, "at", runAt,
|
||||||
"(delay", delayMs, ")");
|
"(delay", delayMs, ")");
|
||||||
const data = {
|
const data = {
|
||||||
@@ -87,13 +91,13 @@ export function setTimeout(func, delayMs) {
|
|||||||
|
|
||||||
// figure out where it goes in the list
|
// figure out where it goes in the list
|
||||||
const idx = binarySearch(
|
const idx = binarySearch(
|
||||||
_callbackList, function(el) {
|
callbackList, function(el) {
|
||||||
return el.runAt - runAt;
|
return el.runAt - runAt;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
_callbackList.splice(idx, 0, data);
|
callbackList.splice(idx, 0, data);
|
||||||
_scheduleRealCallback();
|
scheduleRealCallback();
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
@@ -103,68 +107,69 @@ export function setTimeout(func, delayMs) {
|
|||||||
*
|
*
|
||||||
* @param {Number} key result from an earlier setTimeout call
|
* @param {Number} key result from an earlier setTimeout call
|
||||||
*/
|
*/
|
||||||
export function clearTimeout(key) {
|
export function clearTimeout(key: number): void {
|
||||||
if (_callbackList.length === 0) {
|
if (callbackList.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the element from the list
|
// remove the element from the list
|
||||||
let i;
|
let i;
|
||||||
for (i = 0; i < _callbackList.length; i++) {
|
for (i = 0; i < callbackList.length; i++) {
|
||||||
const cb = _callbackList[i];
|
const cb = callbackList[i];
|
||||||
if (cb.key == key) {
|
if (cb.key == key) {
|
||||||
_callbackList.splice(i, 1);
|
callbackList.splice(i, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// iff it was the first one in the list, reschedule our callback.
|
// iff it was the first one in the list, reschedule our callback.
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
_scheduleRealCallback();
|
scheduleRealCallback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// use the real global.setTimeout to schedule a callback to _runCallbacks.
|
// use the real global.setTimeout to schedule a callback to runCallbacks.
|
||||||
function _scheduleRealCallback() {
|
function scheduleRealCallback(): void {
|
||||||
if (_realCallbackKey) {
|
if (realCallbackKey) {
|
||||||
global.clearTimeout(_realCallbackKey);
|
global.clearTimeout(realCallbackKey as NodeJS.Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
const first = _callbackList[0];
|
const first = callbackList[0];
|
||||||
|
|
||||||
if (!first) {
|
if (!first) {
|
||||||
debuglog("_scheduleRealCallback: no more callbacks, not rescheduling");
|
debuglog("scheduleRealCallback: no more callbacks, not rescheduling");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const now = _now();
|
const timestamp = now();
|
||||||
const delayMs = Math.min(first.runAt - now, TIMER_CHECK_PERIOD_MS);
|
const delayMs = Math.min(first.runAt - timestamp, TIMER_CHECK_PERIOD_MS);
|
||||||
|
|
||||||
debuglog("_scheduleRealCallback: now:", now, "delay:", delayMs);
|
debuglog("scheduleRealCallback: now:", timestamp, "delay:", delayMs);
|
||||||
_realCallbackKey = global.setTimeout(_runCallbacks, delayMs);
|
realCallbackKey = global.setTimeout(runCallbacks, delayMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _runCallbacks() {
|
function runCallbacks(): void {
|
||||||
let cb;
|
let cb;
|
||||||
const now = _now();
|
const timestamp = now();
|
||||||
debuglog("_runCallbacks: now:", now);
|
debuglog("runCallbacks: now:", timestamp);
|
||||||
|
|
||||||
// get the list of things to call
|
// get the list of things to call
|
||||||
const callbacksToRun = [];
|
const callbacksToRun = [];
|
||||||
|
// eslint-disable-next-line
|
||||||
while (true) {
|
while (true) {
|
||||||
const first = _callbackList[0];
|
const first = callbackList[0];
|
||||||
if (!first || first.runAt > now) {
|
if (!first || first.runAt > timestamp) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cb = _callbackList.shift();
|
cb = callbackList.shift();
|
||||||
debuglog("_runCallbacks: popping", cb.key);
|
debuglog("runCallbacks: popping", cb.key);
|
||||||
callbacksToRun.push(cb);
|
callbacksToRun.push(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// reschedule the real callback before running our functions, to
|
// reschedule the real callback before running our functions, to
|
||||||
// keep the codepaths the same whether or not our functions
|
// keep the codepaths the same whether or not our functions
|
||||||
// register their own setTimeouts.
|
// register their own setTimeouts.
|
||||||
_scheduleRealCallback();
|
scheduleRealCallback();
|
||||||
|
|
||||||
for (let i = 0; i < callbacksToRun.length; i++) {
|
for (let i = 0; i < callbacksToRun.length; i++) {
|
||||||
cb = callbacksToRun[i];
|
cb = callbacksToRun[i];
|
||||||
@@ -182,7 +187,7 @@ function _runCallbacks() {
|
|||||||
* returns the index of the last element for which func returns
|
* returns the index of the last element for which func returns
|
||||||
* greater than zero, or array.length if no such element exists.
|
* greater than zero, or array.length if no such element exists.
|
||||||
*/
|
*/
|
||||||
function binarySearch(array, func) {
|
function binarySearch<T>(array: T[], func: (v: T) => number): number {
|
||||||
// min is inclusive, max exclusive.
|
// min is inclusive, max exclusive.
|
||||||
let min = 0;
|
let min = 0;
|
||||||
let max = array.length;
|
let max = array.length;
|
||||||
Reference in New Issue
Block a user