feat: complete Ancestor quote bot with production-ready Docker support

This commit is contained in:
unfunny
2026-09-13 14:14:38 -04:00
parent f2016da05a
commit fe7351a7dc
1145 changed files with 30890 additions and 97684 deletions

View File

@@ -1,7 +1,5 @@
import { type IMatrixApiError as IWidgetMatrixError } from "matrix-widget-api";
import { type IUsageLimit } from "../@types/partials.ts";
import { type MatrixEvent } from "../models/event.ts";
import { NamespacedValue } from "../NamespacedValue.ts";
import { IUsageLimit } from "../@types/partials";
import { MatrixEvent } from "../models/event";
interface IErrorJson extends Partial<IUsageLimit> {
[key: string]: any;
errcode?: string;
@@ -12,70 +10,25 @@ interface IErrorJson extends Partial<IUsageLimit> {
* specific to HTTP responses.
* @param msg - The error message to include.
* @param httpStatus - The HTTP response status code.
* @param httpHeaders - The HTTP response headers.
*/
export declare class HTTPError extends Error {
readonly httpStatus?: number | undefined;
readonly httpHeaders?: Headers | undefined;
constructor(msg: string, httpStatus?: number | undefined, httpHeaders?: Headers | undefined);
/**
* Check if this error was due to rate-limiting on the server side (and should therefore be retried after a delay).
*
* If this returns `true`, {@link getRetryAfterMs} can be called to retrieve the server-side
* recommendation for the retry period.
*
* @returns Whether this error is due to rate-limiting.
*/
isRateLimitError(): boolean;
/**
* @returns The recommended delay in milliseconds to wait before retrying
* the request that triggered this error, or null if no delay is recommended.
* @throws Error if the recommended delay is an invalid value.
* @see {@link safeGetRetryAfterMs} for a version of this check that doesn't throw.
*/
getRetryAfterMs(): number | null;
constructor(msg: string, httpStatus?: number | undefined);
}
/**
* Check if the given (JSON-parsed) response body looks like a Matrix error
* response as specified in https://spec.matrix.org/v1.19/client-server-api/#standard-error-response
*
* @param response - the parsed response body to check
* @returns whether the response is a valid {@link MatrixError}
*/
export declare function isMatrixErrorResponse(response: unknown): response is MatrixError;
export declare class MatrixError extends HTTPError {
readonly httpStatus?: number | undefined;
url?: string | undefined;
event?: MatrixEvent | undefined;
readonly errcode?: string;
readonly error?: string;
data: IErrorJson;
/**
* Construct a Matrix error. This is a JavaScript Error with additional
* information specific to the standard Matrix error response.
* @param errorJson - The Matrix error JSON returned from the homeserver.
* @param httpStatus - The numeric HTTP status code given
* @param httpHeaders - The HTTP response headers given
*/
constructor(errorJson?: IErrorJson, httpStatus?: number, url?: string | undefined, event?: MatrixEvent | undefined, httpHeaders?: Headers);
isRateLimitError(): boolean;
getRetryAfterMs(): number | null;
/**
* @returns this error expressed as a JSON payload
* for use by Widget API error responses.
*/
asWidgetApiErrorData(): IWidgetMatrixError;
/**
* @returns a new {@link MatrixError} from a JSON payload
* received from Widget API error responses.
*/
static fromWidgetApiErrorData(data: IWidgetMatrixError): MatrixError;
constructor(errorJson?: IErrorJson, httpStatus?: number | undefined, url?: string | undefined, event?: MatrixEvent | undefined);
}
/**
* @returns The recommended delay in milliseconds to wait before retrying the request.
* @param error - The error to check for a retry delay.
* @param defaultMs - The delay to use if the error was not due to rate-limiting or if no valid delay is recommended.
*/
export declare function safeGetRetryAfterMs(error: unknown, defaultMs: number): number;
/**
* Construct a ConnectionError. This is a JavaScript Error indicating
* that a request failed because of some error with the connection, either
@@ -86,40 +39,5 @@ export declare class ConnectionError extends Error {
constructor(message: string, cause?: Error);
get name(): string;
}
/**
* Construct a TokenRefreshError. This indicates that a request failed due to the token being expired,
* and attempting to refresh said token also failed but in a way which was not indicative of token invalidation.
* Assumed to be a temporary failure.
*/
export declare class TokenRefreshError extends Error {
constructor(cause?: Error);
get name(): string;
}
/**
* Construct a TokenRefreshError. This indicates that a request failed due to the token being expired,
* and attempting to refresh said token failed in a way indicative of token invalidation.
*/
export declare class TokenRefreshLogoutError extends Error {
constructor(cause?: Error);
get name(): string;
}
export declare const MatrixSafetyErrorCode: NamespacedValue<string, "ORG.MATRIX.MSC4387_SAFETY">;
/***
* This error is thrown when the homeserver refuses to handle an action due to a
* safety concern.
* @see https://github.com/matrix-org/matrix-spec-proposals/pull/4387
*/
export declare class MatrixSafetyError extends MatrixError {
/**
* The kinds of harms detected by the server.
* @see https://github.com/matrix-org/matrix-spec-proposals/pull/4387 for a list of spec defined harms.
*/
readonly harms: Set<string>;
/**
* The date at which a request can be reattempted.
*/
readonly expiry?: Date;
constructor(...props: ConstructorParameters<typeof MatrixError>);
}
export {};
//# sourceMappingURL=errors.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/http-api/errors.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,eAAe,IAAI,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE/E,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,UAAU,UAAW,SAAQ,OAAO,CAAC,WAAW,CAAC;IAC7C,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,qBAAa,SAAU,SAAQ,KAAK;aAGZ,UAAU,CAAC,EAAE,MAAM;aACnB,WAAW,CAAC,EAAE,OAAO;IAHzC,YACI,GAAG,EAAE,MAAM,EACK,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,WAAW,CAAC,EAAE,OAAO,YAAA,EAGxC;IAED;;;;;;;OAOG;IACI,gBAAgB,IAAI,OAAO,CAEjC;IAED;;;;;OAKG;IACI,eAAe,IAAI,MAAM,GAAG,IAAI,CAiBtC;CACJ;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,GAAG,QAAQ,IAAI,WAAW,CAMhF;AAED,qBAAa,WAAY,SAAQ,SAAS;IAkB3B,GAAG,CAAC,EAAE,MAAM;IACZ,KAAK,CAAC,EAAE,WAAW;IAjB9B,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjC,SAAgB,KAAK,CAAC,EAAE,MAAM,CAAC;IAExB,IAAI,EAAE,UAAU,CAAC;IAExB;;;;;;OAMG;IACH,YACI,SAAS,GAAE,UAAe,EAC1B,UAAU,CAAC,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE,MAAM,YAAA,EACZ,KAAK,CAAC,EAAE,WAAW,YAAA,EAC1B,WAAW,CAAC,EAAE,OAAO,EAcxB;IAEM,gBAAgB,IAAI,OAAO,CAKjC;IAEM,eAAe,IAAI,MAAM,GAAG,IAAI,CAatC;IAED;;;OAGG;IACI,oBAAoB,IAAI,kBAAkB,CAiBhD;IAED;;;OAGG;IACH,OAAc,sBAAsB,CAAC,IAAI,EAAE,kBAAkB,GAAG,WAAW,CAE1E;CACJ;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAS7E;AAED;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IACtC,YAAmB,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,EAEhD;IAED,IAAW,IAAI,IAAI,MAAM,CAExB;CACJ;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IACxC,YAAmB,KAAK,CAAC,EAAE,KAAK,EAE/B;IAED,IAAW,IAAI,IAAI,MAAM,CAExB;CACJ;AAED;;;GAGG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAC9C,YAAmB,KAAK,CAAC,EAAE,KAAK,EAE/B;IAED,IAAW,IAAI,IAAI,MAAM,CAExB;CACJ;AAED,eAAO,MAAM,qBAAqB,sDAAyD,CAAC;AAE5F;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,WAAW;IAC9C;;;OAGG;IACH,SAAgB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC;;OAEG;IACH,SAAgB,MAAM,CAAC,EAAE,IAAI,CAAC;IAC9B,YAAmB,GAAG,KAAK,EAAE,qBAAqB,CAAC,OAAO,WAAW,CAAC,EASrE;CACJ"}
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/http-api/errors.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,UAAU,UAAW,SAAQ,OAAO,CAAC,WAAW,CAAC;IAC7C,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,qBAAa,SAAU,SAAQ,KAAK;aACgB,UAAU,CAAC;gBAAxC,GAAG,EAAE,MAAM,EAAkB,UAAU,CAAC,oBAAQ;CAGtE;AAED,qBAAa,WAAY,SAAQ,SAAS;aAclB,UAAU,CAAC;IACpB,GAAG,CAAC;IACJ,KAAK,CAAC;IAdjB,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC;IAE1B,IAAI,EAAE,UAAU,CAAC;IAExB;;;;;OAKG;gBAEC,SAAS,GAAE,UAAe,EACV,UAAU,CAAC,oBAAQ,EAC5B,GAAG,CAAC,oBAAQ,EACZ,KAAK,CAAC,yBAAa;CAcjC;AAED;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBACnB,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK;IAIjD,IAAW,IAAI,IAAI,MAAM,CAExB;CACJ"}

View File

@@ -1,7 +1,13 @@
import _objectSpread from "@babel/runtime/helpers/objectSpread2";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.MatrixError = exports.HTTPError = exports.ConnectionError = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
/*
Copyright 2022 - 2024 The Matrix.org Foundation C.I.C.
Copyright 2022 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.
@@ -16,79 +22,27 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { NamespacedValue } from "../NamespacedValue.js";
import { hasRequiredStringProperty, isRecord } from "../@types/type-guards.js";
/**
* Construct a generic HTTP error. This is a JavaScript Error with additional information
* specific to HTTP responses.
* @param msg - The error message to include.
* @param httpStatus - The HTTP response status code.
* @param httpHeaders - The HTTP response headers.
*/
export class HTTPError extends Error {
constructor(msg, httpStatus, httpHeaders) {
class HTTPError extends Error {
constructor(msg, httpStatus) {
super(msg);
this.httpStatus = httpStatus;
this.httpHeaders = httpHeaders;
}
/**
* Check if this error was due to rate-limiting on the server side (and should therefore be retried after a delay).
*
* If this returns `true`, {@link getRetryAfterMs} can be called to retrieve the server-side
* recommendation for the retry period.
*
* @returns Whether this error is due to rate-limiting.
*/
isRateLimitError() {
return this.httpStatus === 429;
}
/**
* @returns The recommended delay in milliseconds to wait before retrying
* the request that triggered this error, or null if no delay is recommended.
* @throws Error if the recommended delay is an invalid value.
* @see {@link safeGetRetryAfterMs} for a version of this check that doesn't throw.
*/
getRetryAfterMs() {
const retryAfter = this.httpHeaders?.get("Retry-After");
if (retryAfter != null) {
if (/^\d+$/.test(retryAfter)) {
const ms = Number.parseInt(retryAfter) * 1000;
if (!Number.isFinite(ms)) {
throw new Error("Retry-After header integer value is too large");
}
return ms;
}
const date = new Date(retryAfter);
if (date.toUTCString() !== retryAfter) {
throw new Error("Retry-After header value is not a valid HTTP-date or non-negative decimal integer");
}
return date.getTime() - Date.now();
}
return null;
}
}
/**
* Check if the given (JSON-parsed) response body looks like a Matrix error
* response as specified in https://spec.matrix.org/v1.19/client-server-api/#standard-error-response
*
* @param response - the parsed response body to check
* @returns whether the response is a valid {@link MatrixError}
*/
export function isMatrixErrorResponse(response) {
return isRecord(response) && hasRequiredStringProperty(response, "error") && hasRequiredStringProperty(response, "errcode");
}
export class MatrixError extends HTTPError {
exports.HTTPError = HTTPError;
class MatrixError extends HTTPError {
/**
* Construct a Matrix error. This is a JavaScript Error with additional
* information specific to the standard Matrix error response.
* @param errorJson - The Matrix error JSON returned from the homeserver.
* @param httpStatus - The numeric HTTP status code given
* @param httpHeaders - The HTTP response headers given
*/
constructor(errorJson = {}, httpStatus, url, event, httpHeaders) {
constructor(errorJson = {}, httpStatus, url, event) {
let message = errorJson.error || "Unknown message";
if (httpStatus) {
message = `[${httpStatus}] ${message}`;
@@ -96,83 +50,18 @@ export class MatrixError extends HTTPError {
if (url) {
message = `${message} (${url})`;
}
super(`MatrixError: ${message}`, httpStatus, httpHeaders);
// The Matrix 'errcode' value, e.g. "M_FORBIDDEN".
_defineProperty(this, "errcode", void 0);
// The Matrix 'error' value.
_defineProperty(this, "error", void 0);
// The raw Matrix error JSON used to construct this object.
_defineProperty(this, "data", void 0);
super(`MatrixError: ${message}`, httpStatus);
this.httpStatus = httpStatus;
this.url = url;
this.event = event;
// The Matrix 'errcode' value, e.g. "M_FORBIDDEN".
(0, _defineProperty2.default)(this, "errcode", void 0);
// The raw Matrix error JSON used to construct this object.
(0, _defineProperty2.default)(this, "data", void 0);
this.errcode = errorJson.errcode;
this.error = errorJson.error;
this.name = errorJson.errcode || "Unknown error code";
this.data = errorJson;
}
isRateLimitError() {
return this.errcode === "M_LIMIT_EXCEEDED" || (this.errcode === "M_UNKNOWN" || this.errcode === undefined) && super.isRateLimitError();
}
getRetryAfterMs() {
const headerValue = super.getRetryAfterMs();
if (headerValue !== null) {
return headerValue;
}
// Note: retry_after_ms is deprecated as of spec version v1.10
if (this.errcode === "M_LIMIT_EXCEEDED" && "retry_after_ms" in this.data) {
if (!Number.isInteger(this.data.retry_after_ms)) {
throw new Error("retry_after_ms is not an integer");
}
return this.data.retry_after_ms;
}
return null;
}
/**
* @returns this error expressed as a JSON payload
* for use by Widget API error responses.
*/
asWidgetApiErrorData() {
const headers = {};
if (this.httpHeaders) {
for (const [name, value] of this.httpHeaders) {
headers[name] = value;
}
}
return {
http_status: this.httpStatus ?? 400,
http_headers: headers,
url: this.url ?? "",
response: _objectSpread({
errcode: this.errcode ?? "M_UNKNOWN",
error: this.data.error ?? "Unknown message"
}, this.data)
};
}
/**
* @returns a new {@link MatrixError} from a JSON payload
* received from Widget API error responses.
*/
static fromWidgetApiErrorData(data) {
return new MatrixError(data.response, data.http_status, data.url, undefined, new Headers(data.http_headers));
}
}
/**
* @returns The recommended delay in milliseconds to wait before retrying the request.
* @param error - The error to check for a retry delay.
* @param defaultMs - The delay to use if the error was not due to rate-limiting or if no valid delay is recommended.
*/
export function safeGetRetryAfterMs(error, defaultMs) {
if (!(error instanceof HTTPError) || !error.isRateLimitError()) {
return defaultMs;
}
try {
return error.getRetryAfterMs() ?? defaultMs;
} catch {
return defaultMs;
}
}
/**
@@ -181,7 +70,8 @@ export function safeGetRetryAfterMs(error, defaultMs) {
* CORS was not correctly configured on the server, the server didn't response,
* the request timed out, or the internet connection on the client side went down.
*/
export class ConnectionError extends Error {
exports.MatrixError = MatrixError;
class ConnectionError extends Error {
constructor(message, cause) {
super(message + (cause ? `: ${cause.message}` : ""));
}
@@ -189,58 +79,5 @@ export class ConnectionError extends Error {
return "ConnectionError";
}
}
/**
* Construct a TokenRefreshError. This indicates that a request failed due to the token being expired,
* and attempting to refresh said token also failed but in a way which was not indicative of token invalidation.
* Assumed to be a temporary failure.
*/
export class TokenRefreshError extends Error {
constructor(cause) {
super(cause?.message ?? "");
}
get name() {
return "TokenRefreshError";
}
}
/**
* Construct a TokenRefreshError. This indicates that a request failed due to the token being expired,
* and attempting to refresh said token failed in a way indicative of token invalidation.
*/
export class TokenRefreshLogoutError extends Error {
constructor(cause) {
super(cause?.message ?? "");
}
get name() {
return "TokenRefreshLogoutError";
}
}
export const MatrixSafetyErrorCode = new NamespacedValue(null, "ORG.MATRIX.MSC4387_SAFETY");
/***
* This error is thrown when the homeserver refuses to handle an action due to a
* safety concern.
* @see https://github.com/matrix-org/matrix-spec-proposals/pull/4387
*/
export class MatrixSafetyError extends MatrixError {
constructor(...props) {
super(...props);
/**
* The kinds of harms detected by the server.
* @see https://github.com/matrix-org/matrix-spec-proposals/pull/4387 for a list of spec defined harms.
*/
_defineProperty(this, "harms", void 0);
/**
* The date at which a request can be reattempted.
*/
_defineProperty(this, "expiry", void 0);
const body = props[0];
this.harms = new Set(body && "harms" in body && Array.isArray(body.harms) ? body.harms : []);
this.message = `${super.message} (${[...this.harms].join(", ")})`;
if (body && "expiry" in body && typeof body.expiry === "number") {
this.expiry = new Date(body.expiry);
}
}
}
exports.ConnectionError = ConnectionError;
//# sourceMappingURL=errors.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,21 +1,26 @@
import { type TypedEventEmitter } from "../models/typed-event-emitter.ts";
import { Method } from "./method.ts";
import { type BaseRequestOpts, HttpApiEvent, type HttpApiEventHandlerMap, type IHttpOpts, type IRequestOpts, type Body } from "./interface.ts";
import { type QueryDict } from "../utils.ts";
import { TypedEventEmitter } from "../models/typed-event-emitter";
import { Method } from "./method";
import { HttpApiEvent, HttpApiEventHandlerMap, IHttpOpts, IRequestOpts, Body } from "./interface";
import { QueryDict } from "../utils";
interface TypedResponse<T> extends Response {
json(): Promise<T>;
}
export type ResponseType<T, O extends IHttpOpts> = O extends undefined ? T : O extends {
onlyData: true;
} ? T : TypedResponse<T>;
export declare class FetchHttpApi<O extends IHttpOpts> {
private eventEmitter;
readonly opts: O;
private abortController;
private readonly tokenRefresher;
constructor(eventEmitter: TypedEventEmitter<HttpApiEvent, HttpApiEventHandlerMap>, opts: O);
abort(): void;
fetch(resource: URL | string, options?: RequestInit): ReturnType<typeof globalThis.fetch>;
fetch(resource: URL | string, options?: RequestInit): ReturnType<typeof global.fetch>;
/**
* Sets the base URL for the identity server
* @param url - The new base url
*/
setIdBaseUrl(url?: string): void;
idServerRequest<T extends object = Record<string, unknown>>(method: Method, path: string, params: Record<string, string | string[]> | undefined, prefix: string, accessToken?: string): Promise<T>;
idServerRequest<T extends {} = Record<string, unknown>>(method: Method, path: string, params: Record<string, string | string[]> | undefined, prefix: string, accessToken?: string): Promise<ResponseType<T, O>>;
/**
* Perform an authorised request to the homeserver.
* @param method - The HTTP method e.g. "GET".
@@ -27,15 +32,22 @@ export declare class FetchHttpApi<O extends IHttpOpts> {
*
* @param body - The HTTP JSON body.
*
* @param paramOpts - additional options.
* When `paramOpts.doNotAttemptTokenRefresh` is true, token refresh will not be attempted
* when an expired token is encountered. Used to only attempt token refresh once.
* @param opts - additional options. If a number is specified,
* this is treated as `opts.localTimeoutMs`.
*
* @returns The parsed response.
* @throws Error if a problem occurred. This includes network problems and Matrix-specific error JSON.
* @returns Promise which resolves to
* ```
* {
* data: {Object},
* headers: {Object},
* code: {Number},
* }
* ```
* If `onlyData` is set, this will resolve to the `data` object only.
* @returns Rejects with an error if a problem occurred.
* This includes network problems and Matrix-specific error JSON.
*/
authedRequest<T>(method: Method, path: string, queryParams?: QueryDict, body?: Body, paramOpts?: IRequestOpts): Promise<T>;
private doAuthedRequest;
authedRequest<T>(method: Method, path: string, queryParams?: QueryDict, body?: Body, opts?: IRequestOpts): Promise<ResponseType<T, O>>;
/**
* Perform a request to the homeserver without any credentials.
* @param method - The HTTP method e.g. "GET".
@@ -49,10 +61,20 @@ export declare class FetchHttpApi<O extends IHttpOpts> {
*
* @param opts - additional options
*
* @returns The parsed response.
* @throws Error if a problem occurred. This includes network problems and Matrix-specific error JSON.
* @returns Promise which resolves to
* ```
* {
* data: {Object},
* headers: {Object},
* code: {Number},
* }
* ```
* If `onlyData</code> is set, this will resolve to the <code>data`
* object only.
* @returns Rejects with an error if a problem
* occurred. This includes network problems and Matrix-specific error JSON.
*/
request<T = unknown>(method: Method, path: string, queryParams?: QueryDict, body?: Body, opts?: IRequestOpts): Promise<T>;
request<T>(method: Method, path: string, queryParams?: QueryDict, body?: Body, opts?: IRequestOpts): Promise<ResponseType<T, O>>;
/**
* Perform a request to an arbitrary URL.
* @param method - The HTTP method e.g. "GET".
@@ -62,10 +84,12 @@ export declare class FetchHttpApi<O extends IHttpOpts> {
*
* @param opts - additional options
*
* @returns The parsed response.
* @throws Error if a problem occurred. This includes network problems and Matrix-specific error JSON.
* @returns Promise which resolves to data unless `onlyData` is specified as false,
* where the resolved value will be a fetch Response object.
* @returns Rejects with an error if a problem
* occurred. This includes network problems and Matrix-specific error JSON.
*/
requestOtherUrl<T>(method: Method, url: URL | string, body?: Body, opts?: BaseRequestOpts): Promise<T>;
requestOtherUrl<T>(method: Method, url: URL | string, body?: Body, opts?: Pick<IRequestOpts, "headers" | "json" | "localTimeoutMs" | "keepAlive" | "abortSignal" | "priority">): Promise<ResponseType<T, O>>;
private sanitizeUrlForLogs;
/**
* Form and return a homeserver request URL based on the given path params and prefix.
@@ -77,4 +101,5 @@ export declare class FetchHttpApi<O extends IHttpOpts> {
*/
getUrl(path: string, queryParams?: QueryDict, prefix?: string, baseUrl?: string): URL;
}
export {};
//# sourceMappingURL=fetch.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/http-api/fetch.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EACH,KAAK,eAAe,EACpB,YAAY,EACZ,KAAK,sBAAsB,EAC3B,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,IAAI,EACZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,qBAAa,YAAY,CAAC,CAAC,SAAS,SAAS;IAKrC,OAAO,CAAC,YAAY;aACJ,IAAI,EAAE,CAAC;IAL3B,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAEhD,YACY,YAAY,EAAE,iBAAiB,CAAC,YAAY,EAAE,sBAAsB,CAAC,EAC7D,IAAI,EAAE,CAAC,EAS1B;IAEM,KAAK,IAAI,IAAI,CAGnB;IAEM,KAAK,CAAC,QAAQ,EAAE,GAAG,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,KAAK,CAAC,CAK/F;IAED;;;OAGG;IACI,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAEtC;IAEM,eAAe,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7D,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,SAAS,EACrD,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,CAAC,CAAC,CAwBZ;IAED;;;;;;;;;;;;;;;;;OAiBG;IACI,aAAa,CAAC,CAAC,EAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,WAAW,GAAE,SAAc,EAC3B,IAAI,CAAC,EAAE,IAAI,EACX,SAAS,GAAE,YAAiB,GAC7B,OAAO,CAAC,CAAC,CAAC,CAEZ;YAGa,eAAe;IA4D7B;;;;;;;;;;;;;;;OAeG;IACI,OAAO,CAAC,CAAC,GAAG,OAAO,EACtB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,SAAS,EACvB,IAAI,CAAC,EAAE,IAAI,EACX,IAAI,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC,CAAC,CAAC,CAGZ;IAED;;;;;;;;;;;OAWG;IACU,eAAe,CAAC,CAAC,EAC1B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,GAAG,GAAG,MAAM,EACjB,IAAI,CAAC,EAAE,IAAI,EACX,IAAI,GAAE,eAAoB,GAC3B,OAAO,CAAC,CAAC,CAAC,CA4FZ;IAED,OAAO,CAAC,kBAAkB;IAsB1B;;;;;;;OAOG;IACI,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG,CAa3F;CACJ"}
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/http-api/fetch.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,SAAS,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAElG,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAGrC,UAAU,aAAa,CAAC,CAAC,CAAE,SAAQ,QAAQ;IACvC,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;CACtB;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,SAAS,IAAI,CAAC,SAAS,SAAS,GAChE,CAAC,GACD,CAAC,SAAS;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,GAC5B,CAAC,GACD,aAAa,CAAC,CAAC,CAAC,CAAC;AAEvB,qBAAa,YAAY,CAAC,CAAC,SAAS,SAAS;IAIrC,OAAO,CAAC,YAAY;aACJ,IAAI,EAAE,CAAC;IAJ3B,OAAO,CAAC,eAAe,CAAyB;gBAGpC,YAAY,EAAE,iBAAiB,CAAC,YAAY,EAAE,sBAAsB,CAAC,EAC7D,IAAI,EAAE,CAAC;IAOpB,KAAK,IAAI,IAAI;IAKb,KAAK,CAAC,QAAQ,EAAE,GAAG,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,KAAK,CAAC;IAO5F;;;OAGG;IACI,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;IAIhC,eAAe,CAAC,CAAC,SAAS,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzD,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,SAAS,EACrD,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IA0B9B;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACI,aAAa,CAAC,CAAC,EAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,SAAS,EACvB,IAAI,CAAC,EAAE,IAAI,EACX,IAAI,GAAE,YAAiB,GACxB,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAkC9B;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACI,OAAO,CAAC,CAAC,EACZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,SAAS,EACvB,IAAI,CAAC,EAAE,IAAI,EACX,IAAI,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAK9B;;;;;;;;;;;;;OAaG;IACU,eAAe,CAAC,CAAC,EAC1B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,GAAG,GAAG,MAAM,EACjB,IAAI,CAAC,EAAE,IAAI,EACX,IAAI,GAAE,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,MAAM,GAAG,gBAAgB,GAAG,WAAW,GAAG,aAAa,GAAG,UAAU,CAAM,GAChH,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IA6E9B,OAAO,CAAC,kBAAkB;IAsB1B;;;;;;;OAOG;IACI,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG;CAW/F"}

View File

@@ -1,5 +1,17 @@
import _objectSpread from "@babel/runtime/helpers/objectSpread2";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.FetchHttpApi = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _utils = require("../utils");
var _method = require("./method");
var _errors = require("./errors");
var _interface = require("./interface");
var _utils2 = require("./utils");
var _logger = require("../logger");
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
@@ -20,24 +32,15 @@ limitations under the License.
* This is an internal module. See {@link MatrixHttpApi} for the public class.
*/
import { checkObjectHasKeys, deepCopy, encodeParams } from "../utils.js";
import { Method } from "./method.js";
import { ConnectionError, MatrixError, TokenRefreshError } from "./errors.js";
import { HttpApiEvent } from "./interface.js";
import { anySignal, parseErrorResponse, timeoutSignal } from "./utils.js";
import { TokenRefresher, TokenRefreshOutcome } from "./refresh.js";
export class FetchHttpApi {
class FetchHttpApi {
constructor(eventEmitter, opts) {
_defineProperty(this, "abortController", new AbortController());
_defineProperty(this, "tokenRefresher", void 0);
var _opts$useAuthorizatio;
this.eventEmitter = eventEmitter;
this.opts = opts;
checkObjectHasKeys(opts, ["baseUrl", "prefix"]);
if (!opts.onlyData) {
throw new Error("Constructing FetchHttpApi without `onlyData=true` is no longer supported.");
}
opts.useAuthorizationHeader = opts.useAuthorizationHeader ?? true;
this.tokenRefresher = new TokenRefresher(opts);
(0, _defineProperty2.default)(this, "abortController", new AbortController());
(0, _utils.checkObjectHasKeys)(opts, ["baseUrl", "prefix"]);
opts.onlyData = !!opts.onlyData;
opts.useAuthorizationHeader = (_opts$useAuthorizatio = opts.useAuthorizationHeader) !== null && _opts$useAuthorizatio !== void 0 ? _opts$useAuthorizatio : true;
}
abort() {
this.abortController.abort();
@@ -47,7 +50,7 @@ export class FetchHttpApi {
if (this.opts.fetchFn) {
return this.opts.fetchFn(resource, options);
}
return globalThis.fetch(resource, options);
return global.fetch(resource, options);
}
/**
@@ -63,7 +66,7 @@ export class FetchHttpApi {
}
let queryParams = undefined;
let body = undefined;
if (method === Method.Get) {
if (method === _method.Method.Get) {
queryParams = params;
} else {
body = params;
@@ -90,65 +93,50 @@ export class FetchHttpApi {
*
* @param body - The HTTP JSON body.
*
* @param paramOpts - additional options.
* When `paramOpts.doNotAttemptTokenRefresh` is true, token refresh will not be attempted
* when an expired token is encountered. Used to only attempt token refresh once.
* @param opts - additional options. If a number is specified,
* this is treated as `opts.localTimeoutMs`.
*
* @returns The parsed response.
* @throws Error if a problem occurred. This includes network problems and Matrix-specific error JSON.
* @returns Promise which resolves to
* ```
* {
* data: {Object},
* headers: {Object},
* code: {Number},
* }
* ```
* If `onlyData` is set, this will resolve to the `data` object only.
* @returns Rejects with an error if a problem occurred.
* This includes network problems and Matrix-specific error JSON.
*/
authedRequest(method, path, queryParams = {}, body, paramOpts = {}) {
return this.doAuthedRequest(1, method, path, queryParams, body, paramOpts);
}
// Wrapper around public method authedRequest to allow for tracking retry attempt counts
async doAuthedRequest(attempt, method, path, queryParams, body, paramOpts = {}) {
// avoid mutating paramOpts so they can be used on retry
const opts = deepCopy(paramOpts);
// we have to manually copy the abortSignal over as it is not a plain object
opts.abortSignal = paramOpts.abortSignal;
// Take a snapshot of the current token state before we start the request so we can reference it if we error
const requestSnapshot = await this.tokenRefresher.prepareForRequest();
if (requestSnapshot.accessToken) {
authedRequest(method, path, queryParams, body, opts = {}) {
if (!queryParams) queryParams = {};
if (this.opts.accessToken) {
if (this.opts.useAuthorizationHeader) {
if (!opts.headers) {
opts.headers = {};
}
if (!opts.headers.Authorization) {
opts.headers.Authorization = `Bearer ${requestSnapshot.accessToken}`;
opts.headers.Authorization = "Bearer " + this.opts.accessToken;
}
if (queryParams.access_token) {
delete queryParams.access_token;
}
} else if (!queryParams.access_token) {
queryParams.access_token = requestSnapshot.accessToken;
queryParams.access_token = this.opts.accessToken;
}
}
try {
const response = await this.request(method, path, queryParams, body, opts);
return response;
} catch (error) {
if (!(error instanceof MatrixError)) {
throw error;
const requestPromise = this.request(method, path, queryParams, body, opts);
requestPromise.catch(err => {
if (err.errcode == "M_UNKNOWN_TOKEN" && !(opts !== null && opts !== void 0 && opts.inhibitLogoutEmit)) {
this.eventEmitter.emit(_interface.HttpApiEvent.SessionLoggedOut, err);
} else if (err.errcode == "M_CONSENT_NOT_GIVEN") {
this.eventEmitter.emit(_interface.HttpApiEvent.NoConsent, err.message, err.data.consent_uri);
}
if (error.errcode === "M_UNKNOWN_TOKEN") {
const outcome = await this.tokenRefresher.handleUnknownToken(requestSnapshot, attempt);
if (outcome === TokenRefreshOutcome.Success) {
// if we got a new token retry the request
return this.doAuthedRequest(attempt + 1, method, path, queryParams, body, paramOpts);
}
if (outcome === TokenRefreshOutcome.Failure) {
throw new TokenRefreshError(error);
}
if (!opts?.inhibitLogoutEmit) {
this.eventEmitter.emit(HttpApiEvent.SessionLoggedOut, error);
}
} else if (error.errcode == "M_CONSENT_NOT_GIVEN") {
this.eventEmitter.emit(HttpApiEvent.NoConsent, error.message, error.data.consent_uri);
}
throw error;
}
});
// return the original promise, otherwise tests break due to it having to
// go around the event loop one more time to process the result of the request
return requestPromise;
}
/**
@@ -164,11 +152,21 @@ export class FetchHttpApi {
*
* @param opts - additional options
*
* @returns The parsed response.
* @throws Error if a problem occurred. This includes network problems and Matrix-specific error JSON.
* @returns Promise which resolves to
* ```
* {
* data: {Object},
* headers: {Object},
* code: {Number},
* }
* ```
* If `onlyData</code> is set, this will resolve to the <code>data`
* object only.
* @returns Rejects with an error if a problem
* occurred. This includes network problems and Matrix-specific error JSON.
*/
request(method, path, queryParams, body, opts) {
const fullUri = this.getUrl(path, queryParams, opts?.prefix, opts?.baseUrl);
const fullUri = this.getUrl(path, queryParams, opts === null || opts === void 0 ? void 0 : opts.prefix, opts === null || opts === void 0 ? void 0 : opts.baseUrl);
return this.requestOtherUrl(method, fullUri, body, opts);
}
@@ -181,55 +179,46 @@ export class FetchHttpApi {
*
* @param opts - additional options
*
* @returns The parsed response.
* @throws Error if a problem occurred. This includes network problems and Matrix-specific error JSON.
* @returns Promise which resolves to data unless `onlyData` is specified as false,
* where the resolved value will be a fetch Response object.
* @returns Rejects with an error if a problem
* occurred. This includes network problems and Matrix-specific error JSON.
*/
async requestOtherUrl(method, url, body, opts = {}) {
if (opts.json !== undefined && opts.rawResponseBody !== undefined) {
throw new Error("Invalid call to `FetchHttpApi` sets both `opts.json` and `opts.rawResponseBody`");
}
var _opts$json, _body$constructor, _opts$localTimeoutMs, _opts$keepAlive;
const urlForLogs = this.sanitizeUrlForLogs(url);
this.opts.logger?.debug(`FetchHttpApi: --> ${method} ${urlForLogs}`);
_logger.logger.debug(`FetchHttpApi: --> ${method} ${urlForLogs}`);
const headers = Object.assign({}, opts.headers || {});
const jsonResponse = !opts.rawResponseBody && opts.json !== false;
if (jsonResponse) {
const json = (_opts$json = opts.json) !== null && _opts$json !== void 0 ? _opts$json : true;
// We can't use getPrototypeOf here as objects made in other contexts e.g. over postMessage won't have same ref
const jsonBody = json && (body === null || body === void 0 || (_body$constructor = body.constructor) === null || _body$constructor === void 0 ? void 0 : _body$constructor.name) === Object.name;
if (json) {
if (jsonBody && !headers["Content-Type"]) {
headers["Content-Type"] = "application/json";
}
if (!headers["Accept"]) {
headers["Accept"] = "application/json";
}
}
const timeout = opts.localTimeoutMs ?? this.opts.localTimeoutMs;
const keepAlive = opts.keepAlive ?? false;
const timeout = (_opts$localTimeoutMs = opts.localTimeoutMs) !== null && _opts$localTimeoutMs !== void 0 ? _opts$localTimeoutMs : this.opts.localTimeoutMs;
const keepAlive = (_opts$keepAlive = opts.keepAlive) !== null && _opts$keepAlive !== void 0 ? _opts$keepAlive : false;
const signals = [this.abortController.signal];
if (timeout !== undefined) {
signals.push(timeoutSignal(timeout));
signals.push((0, _utils2.timeoutSignal)(timeout));
}
if (opts.abortSignal) {
signals.push(opts.abortSignal);
}
// If the body is an object, encode it as JSON and set the `Content-Type` header,
// unless that has been explicitly inhibited by setting `opts.json: false`.
// We can't use getPrototypeOf here as objects made in other contexts e.g. over postMessage won't have same ref
let data;
if (opts.json !== false && body?.constructor?.name === Object.name) {
if (jsonBody) {
data = JSON.stringify(body);
if (!headers["Content-Type"]) {
headers["Content-Type"] = "application/json";
}
} else {
data = body;
}
const {
signal,
cleanup
} = anySignal(signals);
// Set cache mode based on presence of Authorization header.
// Browsers/proxies do not cache responses to requests with Authorization headers.
// So specifying "no-cache" is redundant, and actually prevents caching
// of preflight requests in CORS scenarios. As such, we only set "no-cache"
// when there is no Authorization header.
const cacheMode = "Authorization" in headers ? undefined : "no-cache";
} = (0, _utils2.anySignal)(signals);
let res;
const start = Date.now();
try {
@@ -242,32 +231,29 @@ export class FetchHttpApi {
redirect: "follow",
referrer: "",
referrerPolicy: "no-referrer",
cache: cacheMode,
cache: "no-cache",
credentials: "omit",
// we send credentials via headers
keepalive: keepAlive,
priority: opts.priority
});
this.opts.logger?.debug(`FetchHttpApi: <-- ${method} ${urlForLogs} [${Date.now() - start}ms ${res.status}]`);
_logger.logger.debug(`FetchHttpApi: <-- ${method} ${urlForLogs} [${Date.now() - start}ms ${res.status}]`);
} catch (e) {
this.opts.logger?.debug(`FetchHttpApi: <-- ${method} ${urlForLogs} [${Date.now() - start}ms ${e}]`);
_logger.logger.debug(`FetchHttpApi: <-- ${method} ${urlForLogs} [${Date.now() - start}ms ${e}]`);
if (e.name === "AbortError") {
throw e;
}
throw new ConnectionError("fetch failed", e);
throw new _errors.ConnectionError("fetch failed", e);
} finally {
cleanup();
}
if (!res.ok) {
throw parseErrorResponse(res, await res.text());
throw (0, _utils2.parseErrorResponse)(res, await res.text());
}
if (opts.rawResponseBody) {
return await res.blob();
} else if (jsonResponse) {
return await res.json();
} else {
return await res.text();
if (this.opts.onlyData) {
return json ? res.json() : res.text();
}
return res;
}
sanitizeUrlForLogs(url) {
try {
@@ -285,7 +271,7 @@ export class FetchHttpApi {
const sanitizedQsString = sanitizedQs.toString();
const sanitizedQsUrlPiece = sanitizedQsString ? `?${sanitizedQsString}` : "";
return asUrl.origin + asUrl.pathname + sanitizedQsUrlPiece;
} catch {
} catch (error) {
// defensive coding for malformed url
return "??";
}
@@ -299,15 +285,14 @@ export class FetchHttpApi {
* @returns URL
*/
getUrl(path, queryParams, prefix, baseUrl) {
const baseUrlWithFallback = baseUrl ?? this.opts.baseUrl;
const baseUrlWithFallback = baseUrl !== null && baseUrl !== void 0 ? baseUrl : this.opts.baseUrl;
const baseUrlWithoutTrailingSlash = baseUrlWithFallback.endsWith("/") ? baseUrlWithFallback.slice(0, -1) : baseUrlWithFallback;
const url = new URL(baseUrlWithoutTrailingSlash + (prefix ?? this.opts.prefix) + path);
// If there are any params, encode and append them to the URL.
if (this.opts.extraParams || queryParams) {
const mergedParams = _objectSpread(_objectSpread({}, this.opts.extraParams), queryParams);
encodeParams(mergedParams, url.searchParams);
const url = new URL(baseUrlWithoutTrailingSlash + (prefix !== null && prefix !== void 0 ? prefix : this.opts.prefix) + path);
if (queryParams) {
(0, _utils.encodeParams)(queryParams, url.searchParams);
}
return url;
}
}
exports.FetchHttpApi = FetchHttpApi;
//# sourceMappingURL=fetch.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,10 +1,10 @@
import { FetchHttpApi } from "./fetch.ts";
import { type FileType, type IHttpOpts, type Upload, type UploadOpts, type UploadResponse } from "./interface.ts";
export * from "./interface.ts";
export * from "./prefix.ts";
export * from "./errors.ts";
export * from "./method.ts";
export * from "./utils.ts";
import { FetchHttpApi } from "./fetch";
import { FileType, IContentUri, IHttpOpts, Upload, UploadOpts, UploadResponse } from "./interface";
export * from "./interface";
export * from "./prefix";
export * from "./errors";
export * from "./method";
export * from "./utils";
export declare class MatrixHttpApi<O extends IHttpOpts> extends FetchHttpApi<O> {
private uploads;
/**
@@ -16,12 +16,18 @@ export declare class MatrixHttpApi<O extends IHttpOpts> extends FetchHttpApi<O>
*
* @param opts - options object
*
* @returns Promise which resolves to response object, or rejects with an error (usually a MatrixError).
* @throws May throw a `MatrixSafetyError` if content is deemed unsafe.
* @see MatrixSafetyError
* @returns Promise which resolves to response object, as
* determined by this.opts.onlyData, opts.rawResponse, and
* opts.onlyContentUri. Rejects with an error (usually a MatrixError).
*/
uploadContent(file: FileType, opts?: UploadOpts): Promise<UploadResponse>;
cancelUpload(promise: Promise<UploadResponse>): boolean;
getCurrentUploads(): Upload[];
/**
* Get the content repository url with query parameters.
* @returns An object with a 'base', 'path' and 'params' for base URL,
* path and query parameters respectively.
*/
getContentUri(): IContentUri;
}
//# sourceMappingURL=index.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http-api/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAQlH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAE3B,qBAAa,aAAa,CAAC,CAAC,SAAS,SAAS,CAAE,SAAQ,YAAY,CAAC,CAAC,CAAC;IACnE,OAAO,CAAC,OAAO,CAAgB;IAE/B;;;;;;;;;;;;OAYG;IACI,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CA8GnF;IAEM,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,OAAO,CAO7D;IAEM,iBAAiB,IAAI,MAAM,EAAE,CAEnC;CACJ"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http-api/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAQnG,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AAExB,qBAAa,aAAa,CAAC,CAAC,SAAS,SAAS,CAAE,SAAQ,YAAY,CAAC,CAAC,CAAC;IACnE,OAAO,CAAC,OAAO,CAAgB;IAE/B;;;;;;;;;;;;OAYG;IACI,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,cAAc,CAAC;IAoH7E,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,OAAO;IASvD,iBAAiB,IAAI,MAAM,EAAE;IAIpC;;;;OAIG;IACI,aAAa,IAAI,WAAW;CAStC"}

View File

@@ -1,4 +1,79 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
var _exportNames = {
MatrixHttpApi: true
};
exports.MatrixHttpApi = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _fetch = require("./fetch");
var _prefix = require("./prefix");
Object.keys(_prefix).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _prefix[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _prefix[key];
}
});
});
var _utils = require("../utils");
var callbacks = _interopRequireWildcard(require("../realtime-callbacks"));
var _method = require("./method");
Object.keys(_method).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _method[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _method[key];
}
});
});
var _errors = require("./errors");
Object.keys(_errors).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _errors[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _errors[key];
}
});
});
var _utils2 = require("./utils");
Object.keys(_utils2).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _utils2[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _utils2[key];
}
});
});
var _interface = require("./interface");
Object.keys(_interface).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _interface[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _interface[key];
}
});
});
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
@@ -15,22 +90,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { FetchHttpApi } from "./fetch.js";
import { MediaPrefix } from "./prefix.js";
import { removeElement } from "../utils.js";
import * as callbacks from "../realtime-callbacks.js";
import { Method } from "./method.js";
import { ConnectionError } from "./errors.js";
import { parseErrorResponse } from "./utils.js";
export * from "./interface.js";
export * from "./prefix.js";
export * from "./errors.js";
export * from "./method.js";
export * from "./utils.js";
export class MatrixHttpApi extends FetchHttpApi {
class MatrixHttpApi extends _fetch.FetchHttpApi {
constructor(...args) {
super(...args);
_defineProperty(this, "uploads", []);
(0, _defineProperty2.default)(this, "uploads", []);
}
/**
* Upload content to the homeserver
@@ -41,76 +104,79 @@ export class MatrixHttpApi extends FetchHttpApi {
*
* @param opts - options object
*
* @returns Promise which resolves to response object, or rejects with an error (usually a MatrixError).
* @throws May throw a `MatrixSafetyError` if content is deemed unsafe.
* @see MatrixSafetyError
* @returns Promise which resolves to response object, as
* determined by this.opts.onlyData, opts.rawResponse, and
* opts.onlyContentUri. Rejects with an error (usually a MatrixError).
*/
uploadContent(file, opts = {}) {
const includeFilename = opts.includeFilename ?? true;
const abortController = opts.abortController ?? new AbortController();
var _opts$includeFilename, _opts$abortController, _ref, _opts$type, _opts$name;
const includeFilename = (_opts$includeFilename = opts.includeFilename) !== null && _opts$includeFilename !== void 0 ? _opts$includeFilename : true;
const abortController = (_opts$abortController = opts.abortController) !== null && _opts$abortController !== void 0 ? _opts$abortController : new AbortController();
// If the file doesn't have a mime type, use a default since the HS errors if we don't supply one.
const contentType = (opts.type ?? file.type) || "application/octet-stream";
const fileName = opts.name ?? file.name;
const contentType = (_ref = (_opts$type = opts.type) !== null && _opts$type !== void 0 ? _opts$type : file.type) !== null && _ref !== void 0 ? _ref : "application/octet-stream";
const fileName = (_opts$name = opts.name) !== null && _opts$name !== void 0 ? _opts$name : file.name;
const upload = {
loaded: 0,
total: 0,
abortController
};
const uploadResolvers = Promise.withResolvers();
if (globalThis.XMLHttpRequest) {
const xhr = new globalThis.XMLHttpRequest();
const deferred = (0, _utils.defer)();
if (global.XMLHttpRequest) {
const xhr = new global.XMLHttpRequest();
const timeoutFn = function () {
xhr.abort();
uploadResolvers.reject(new Error("Timeout"));
deferred.reject(new Error("Timeout"));
};
// set an initial timeout of 30s; we'll advance it each time we get a progress notification
let timeoutTimer = callbacks.setTimeout(timeoutFn, 30000);
xhr.onreadystatechange = function () {
switch (xhr.readyState) {
case globalThis.XMLHttpRequest.DONE:
case global.XMLHttpRequest.DONE:
callbacks.clearTimeout(timeoutTimer);
try {
if (xhr.status === 0) {
throw new DOMException(xhr.statusText, "AbortError"); // mimic fetch API
}
if (!xhr.responseText) {
throw new Error("No response body.");
}
if (xhr.status >= 400) {
uploadResolvers.reject(parseErrorResponse(xhr, xhr.responseText));
deferred.reject((0, _utils2.parseErrorResponse)(xhr, xhr.responseText));
} else {
uploadResolvers.resolve(JSON.parse(xhr.responseText));
deferred.resolve(JSON.parse(xhr.responseText));
}
} catch (err) {
if (err.name === "AbortError") {
uploadResolvers.reject(err);
deferred.reject(err);
return;
}
uploadResolvers.reject(new ConnectionError("request failed", err));
deferred.reject(new _errors.ConnectionError("request failed", err));
}
break;
}
};
xhr.upload.onprogress = ev => {
var _opts$progressHandler;
callbacks.clearTimeout(timeoutTimer);
upload.loaded = ev.loaded;
upload.total = ev.total;
timeoutTimer = callbacks.setTimeout(timeoutFn, 30000);
opts.progressHandler?.({
(_opts$progressHandler = opts.progressHandler) === null || _opts$progressHandler === void 0 ? void 0 : _opts$progressHandler.call(opts, {
loaded: ev.loaded,
total: ev.total
});
};
const url = this.getUrl("/upload", undefined, MediaPrefix.V3);
const url = this.getUrl("/upload", undefined, _prefix.MediaPrefix.R0);
if (includeFilename && fileName) {
url.searchParams.set("filename", encodeURIComponent(fileName));
}
if (!this.opts.useAuthorizationHeader && this.opts.accessToken) {
url.searchParams.set("access_token", encodeURIComponent(this.opts.accessToken));
}
xhr.open(Method.Post, url.href);
xhr.open(_method.Method.Post, url.href);
if (this.opts.useAuthorizationHeader && this.opts.accessToken) {
xhr.setRequestHeader("Authorization", "Bearer " + this.opts.accessToken);
}
@@ -127,20 +193,22 @@ export class MatrixHttpApi extends FetchHttpApi {
const headers = {
"Content-Type": contentType
};
this.authedRequest(Method.Post, "/upload", queryParams, file, {
prefix: MediaPrefix.V3,
this.authedRequest(_method.Method.Post, "/upload", queryParams, file, {
prefix: _prefix.MediaPrefix.R0,
headers,
abortSignal: abortController.signal
}).then(uploadResolvers.resolve, uploadResolvers.reject);
}).then(response => {
return this.opts.onlyData ? response : response.json();
}).then(deferred.resolve, deferred.reject);
}
// remove the upload from the list on completion
upload.promise = uploadResolvers.promise.finally(() => {
removeElement(this.uploads, elem => elem === upload);
upload.promise = deferred.promise.finally(() => {
(0, _utils.removeElement)(this.uploads, elem => elem === upload);
});
abortController.signal.addEventListener("abort", () => {
removeElement(this.uploads, elem => elem === upload);
uploadResolvers.reject(new DOMException("Aborted", "AbortError"));
(0, _utils.removeElement)(this.uploads, elem => elem === upload);
deferred.reject(new DOMException("Aborted", "AbortError"));
});
this.uploads.push(upload);
return upload.promise;
@@ -156,5 +224,21 @@ export class MatrixHttpApi extends FetchHttpApi {
getCurrentUploads() {
return this.uploads;
}
/**
* Get the content repository url with query parameters.
* @returns An object with a 'base', 'path' and 'params' for base URL,
* path and query parameters respectively.
*/
getContentUri() {
return {
base: this.opts.baseUrl,
path: _prefix.MediaPrefix.R0 + "/upload",
params: {
access_token: this.opts.accessToken
}
};
}
}
exports.MatrixHttpApi = MatrixHttpApi;
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,61 +1,27 @@
import { type MatrixError } from "./errors.ts";
import { type Logger } from "../logger.ts";
import { type QueryDict } from "../utils.ts";
import { MatrixError } from "./errors";
export type Body = Record<string, any> | BodyInit;
/**
* Unencrypted access and (optional) refresh token
*/
export type AccessTokens = {
/**
* The new access token to use for authenticated requests
*/
accessToken: string;
/**
* The new refresh token to use for refreshing tokens, optional
*/
refreshToken?: string;
/**
* Approximate date when the access token will expire, optional
*/
expiry?: Date;
};
/**
* Function that performs token refresh using the given refreshToken.
* Returns a promise that resolves to the refreshed access and (optional) refresh tokens.
*
* Can be passed to HttpApi instance as {@link IHttpOpts.tokenRefreshFunction} during client creation {@link ICreateClientOpts}
*/
export type TokenRefreshFunction = (refreshToken: string) => Promise<AccessTokens>;
/** Options object for `FetchHttpApi` and {@link MatrixHttpApi}. */
export interface IHttpOpts {
fetchFn?: typeof globalThis.fetch;
fetchFn?: typeof global.fetch;
baseUrl: string;
idBaseUrl?: string;
prefix: string;
extraParams?: QueryDict;
extraParams?: Record<string, string>;
accessToken?: string;
/**
* Used in conjunction with tokenRefreshFunction to attempt token refresh
*/
refreshToken?: string;
/**
* Function to attempt token refresh when a possibly expired token is encountered
* Optional, only called when a refreshToken is present
*/
tokenRefreshFunction?: TokenRefreshFunction;
/**
* Whether to use the HTTP Authorization header over the `access_token` query parameter
* @deprecated as of v1.11 in https://spec.matrix.org/v1.17/client-server-api/#using-access-tokens
*/
useAuthorizationHeader?: boolean;
/** For historical reasons, must be set to `true`. Will eventually be removed. */
onlyData?: boolean;
localTimeoutMs?: number;
/** Optional logger instance. If provided, requests and responses will be logged. */
logger?: Logger;
}
/** Options object for `FetchHttpApi.requestOtherUrl`. */
export interface BaseRequestOpts extends Pick<RequestInit, "priority"> {
export interface IRequestOpts extends Pick<RequestInit, "priority"> {
/**
* The alternative base url to use.
* If not specified, uses this.opts.baseUrl
*/
baseUrl?: string;
/**
* The full prefix to use e.g.
* "/_matrix/client/v2_alpha". If not specified, uses this.opts.prefix.
*/
prefix?: string;
/**
* map of additional request headers
*/
@@ -67,45 +33,16 @@ export interface BaseRequestOpts extends Pick<RequestInit, "priority"> {
*/
localTimeoutMs?: number;
keepAlive?: boolean;
/**
* By default, we will:
*
* * If the `body` is an object, JSON-encode it and set `Content-Type: application/json` in the
* request headers (unless overridden by {@link headers}).
*
* * Set `Accept: application/json` in the request headers (again, unless overridden by {@link headers}).
*
* * Parse the response as JSON and return the parsed response.
*
* Setting this to `false` inhibits all three behaviors, and the response is instead parsed as a UTF-8 string. It
* defaults to `true`, unless {@link rawResponseBody} is set.
*
* @deprecated Instead of setting this to `false`, set {@link rawResponseBody} to `true`.
*/
json?: boolean;
/**
* Setting this to `true` does two things:
*
* * Inhibits the automatic addition of `Accept: application/json` in the request headers.
*
* * Causes the raw response to be returned as a {@link https://developer.mozilla.org/en-US/docs/Web/API/Blob|Blob}
* instead of parsing it as JSON.
*/
rawResponseBody?: boolean;
}
export interface IRequestOpts extends BaseRequestOpts {
/**
* The alternative base url to use.
* If not specified, uses this.opts.baseUrl
*/
baseUrl?: string;
/**
* The full prefix to use e.g.
* "/_matrix/client/v2_alpha". If not specified, uses this.opts.prefix.
*/
prefix?: string;
inhibitLogoutEmit?: boolean;
}
export interface IContentUri {
base: string;
path: string;
params: {
access_token: string;
};
}
export declare enum HttpApiEvent {
SessionLoggedOut = "Session.logged_out",
NoConsent = "no_consent"

View File

@@ -1 +1 @@
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/http-api/interface.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC;AAElD;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACvB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,MAAM,CAAC,EAAE,IAAI,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAEnF,mEAAmE;AACnE,MAAM,WAAW,SAAS;IACtB,OAAO,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAElC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,SAAS,CAAC;IAExB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAE5C;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,yDAAyD;AACzD,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC;IAClE;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;;;;;;;;;;;OAcG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,YAAa,SAAQ,eAAe;IACjD;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAKhB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,oBAAY,YAAY;IACpB,gBAAgB,uBAAuB;IACvC,SAAS,eAAe;CAC3B;AAED,MAAM,MAAM,sBAAsB,GAAG;IACjC;;;;;;;;;;;OAWG;IACH,CAAC,YAAY,CAAC,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,CAAC;IAC5D;;;;;;;;;OASG;IACH,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3E,CAAC;AAEF,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACvB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,eAAe,CAAC,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IACjD,eAAe,CAAC,EAAE,eAAe,CAAC;CACrC;AAED,MAAM,WAAW,MAAM;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACjC,eAAe,EAAE,eAAe,CAAC;CACpC;AAED,MAAM,WAAW,cAAc;IAC3B,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,QAAQ,GAAG,sBAAsB,CAAC"}
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/http-api/interface.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC;AAElD,MAAM,WAAW,SAAS;IACtB,OAAO,CAAC,EAAE,OAAO,MAAM,CAAC,KAAK,CAAC;IAE9B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAErC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC;IAC/D;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IAKf,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAEJ,YAAY,EAAE,MAAM,CAAC;KACxB,CAAC;CACL;AAED,oBAAY,YAAY;IACpB,gBAAgB,uBAAuB;IACvC,SAAS,eAAe;CAC3B;AAED,MAAM,MAAM,sBAAsB,GAAG;IACjC;;;;;;;;;;;OAWG;IACH,CAAC,YAAY,CAAC,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,CAAC;IAC5D;;;;;;;;;OASG;IACH,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3E,CAAC;AAEF,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACvB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,eAAe,CAAC,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IACjD,eAAe,CAAC,EAAE,eAAe,CAAC;CACrC;AAED,MAAM,WAAW,MAAM;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACjC,eAAe,EAAE,eAAe,CAAC;CACpC;AAED,MAAM,WAAW,cAAc;IAE3B,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,QAAQ,GAAG,sBAAsB,CAAC"}

View File

@@ -1,3 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.HttpApiEvent = void 0;
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
@@ -13,25 +19,10 @@ 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.
*/
/**
* Unencrypted access and (optional) refresh token
*/
/**
* Function that performs token refresh using the given refreshToken.
* Returns a promise that resolves to the refreshed access and (optional) refresh tokens.
*
* Can be passed to HttpApi instance as {@link IHttpOpts.tokenRefreshFunction} during client creation {@link ICreateClientOpts}
*/
/** Options object for `FetchHttpApi` and {@link MatrixHttpApi}. */
/** Options object for `FetchHttpApi.requestOtherUrl`. */
export let HttpApiEvent = /*#__PURE__*/function (HttpApiEvent) {
let HttpApiEvent = /*#__PURE__*/function (HttpApiEvent) {
HttpApiEvent["SessionLoggedOut"] = "Session.logged_out";
HttpApiEvent["NoConsent"] = "no_consent";
return HttpApiEvent;
}({});
exports.HttpApiEvent = HttpApiEvent;
//# sourceMappingURL=interface.js.map

File diff suppressed because one or more lines are too long

View File

@@ -2,9 +2,6 @@ export declare enum Method {
Get = "GET",
Put = "PUT",
Post = "POST",
Delete = "DELETE",
Options = "OPTIONS",
Head = "HEAD",
Patch = "PATCH"
Delete = "DELETE"
}
//# sourceMappingURL=method.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"method.d.ts","sourceRoot":"","sources":["../../src/http-api/method.ts"],"names":[],"mappings":"AAgBA,oBAAY,MAAM;IACd,GAAG,QAAQ;IACX,GAAG,QAAQ;IACX,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,IAAI,SAAS;IACb,KAAK,UAAU;CAClB"}
{"version":3,"file":"method.d.ts","sourceRoot":"","sources":["../../src/http-api/method.ts"],"names":[],"mappings":"AAgBA,oBAAY,MAAM;IACd,GAAG,QAAQ;IACX,GAAG,QAAQ;IACX,IAAI,SAAS;IACb,MAAM,WAAW;CACpB"}

View File

@@ -1,3 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Method = void 0;
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
@@ -13,15 +19,12 @@ 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.
*/
export let Method = /*#__PURE__*/function (Method) {
let Method = /*#__PURE__*/function (Method) {
Method["Get"] = "GET";
Method["Put"] = "PUT";
Method["Post"] = "POST";
Method["Delete"] = "DELETE";
Method["Options"] = "OPTIONS";
Method["Head"] = "HEAD";
Method["Patch"] = "PATCH";
return Method;
}({});
exports.Method = Method;
//# sourceMappingURL=method.js.map

View File

@@ -1 +1 @@
{"version":3,"file":"method.js","names":[],"sources":["../../src/http-api/method.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport enum Method {\n Get = \"GET\",\n Put = \"PUT\",\n Post = \"POST\",\n Delete = \"DELETE\",\n Options = \"OPTIONS\",\n Head = \"HEAD\",\n Patch = \"PATCH\",\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,WAAY,MAAM,0BAAN,MAAM;EAAN,MAAM;EAAN,MAAM;EAAN,MAAM;EAAN,MAAM;EAAN,MAAM;EAAN,MAAM;EAAN,MAAM;EAAA,OAAN,MAAM;AAAA","ignoreList":[]}
{"version":3,"file":"method.js","names":["Method","exports"],"sources":["../../src/http-api/method.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport enum Method {\n Get = \"GET\",\n Put = \"PUT\",\n Post = \"POST\",\n Delete = \"DELETE\",\n}\n"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAdA,IAgBYA,MAAM,0BAANA,MAAM;EAANA,MAAM;EAANA,MAAM;EAANA,MAAM;EAANA,MAAM;EAAA,OAANA,MAAM;AAAA;AAAAC,OAAA,CAAAD,MAAA,GAAAA,MAAA"}

View File

@@ -1,6 +1,10 @@
export declare enum ClientPrefix {
/**
* A constant representing the URI path for Client-Server API endpoints versioned at v1.
* A constant representing the URI path for release 0 of the Client-Server HTTP API.
*/
R0 = "/_matrix/client/r0",
/**
* A constant representing the URI path for the legacy release v1 of the Client-Server HTTP API.
*/
V1 = "/_matrix/client/v1",
/**
@@ -20,12 +24,8 @@ export declare enum IdentityPrefix {
}
export declare enum MediaPrefix {
/**
* A constant representing the URI path for Client-Server API Media endpoints versioned at v1.
* URI path for the media repo API
*/
V1 = "/_matrix/media/v1",
/**
* A constant representing the URI path for Client-Server API Media endpoints versioned at v3.
*/
V3 = "/_matrix/media/v3"
R0 = "/_matrix/media/r0"
}
//# sourceMappingURL=prefix.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"prefix.d.ts","sourceRoot":"","sources":["../../src/http-api/prefix.ts"],"names":[],"mappings":"AAgBA,oBAAY,YAAY;IACpB;;OAEG;IACH,EAAE,uBAAuB;IACzB;;OAEG;IACH,EAAE,uBAAuB;IACzB;;OAEG;IACH,QAAQ,6BAA6B;CACxC;AAED,oBAAY,cAAc;IACtB;;OAEG;IACH,EAAE,yBAAyB;CAC9B;AAED,oBAAY,WAAW;IACnB;;OAEG;IACH,EAAE,sBAAsB;IACxB;;OAEG;IACH,EAAE,sBAAsB;CAC3B"}
{"version":3,"file":"prefix.d.ts","sourceRoot":"","sources":["../../src/http-api/prefix.ts"],"names":[],"mappings":"AAgBA,oBAAY,YAAY;IACpB;;OAEG;IACH,EAAE,uBAAuB;IACzB;;OAEG;IACH,EAAE,uBAAuB;IACzB;;OAEG;IACH,EAAE,uBAAuB;IACzB;;OAEG;IACH,QAAQ,6BAA6B;CACxC;AAED,oBAAY,cAAc;IACtB;;OAEG;IACH,EAAE,yBAAyB;CAC9B;AAED,oBAAY,WAAW;IACnB;;OAEG;IACH,EAAE,sBAAsB;CAC3B"}

View File

@@ -1,3 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.MediaPrefix = exports.IdentityPrefix = exports.ClientPrefix = void 0;
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
@@ -13,38 +19,22 @@ 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.
*/
export let ClientPrefix = /*#__PURE__*/function (ClientPrefix) {
/**
* A constant representing the URI path for Client-Server API endpoints versioned at v1.
*/
let ClientPrefix = /*#__PURE__*/function (ClientPrefix) {
ClientPrefix["R0"] = "/_matrix/client/r0";
ClientPrefix["V1"] = "/_matrix/client/v1";
/**
* A constant representing the URI path for Client-Server API endpoints versioned at v3.
*/
ClientPrefix["V3"] = "/_matrix/client/v3";
/**
* A constant representing the URI path for as-yet unspecified Client-Server HTTP APIs.
*/
ClientPrefix["Unstable"] = "/_matrix/client/unstable";
return ClientPrefix;
}({});
export let IdentityPrefix = /*#__PURE__*/function (IdentityPrefix) {
/**
* URI path for the v2 identity API
*/
exports.ClientPrefix = ClientPrefix;
let IdentityPrefix = /*#__PURE__*/function (IdentityPrefix) {
IdentityPrefix["V2"] = "/_matrix/identity/v2";
return IdentityPrefix;
}({});
export let MediaPrefix = /*#__PURE__*/function (MediaPrefix) {
/**
* A constant representing the URI path for Client-Server API Media endpoints versioned at v1.
*/
MediaPrefix["V1"] = "/_matrix/media/v1";
/**
* A constant representing the URI path for Client-Server API Media endpoints versioned at v3.
*/
MediaPrefix["V3"] = "/_matrix/media/v3";
exports.IdentityPrefix = IdentityPrefix;
let MediaPrefix = /*#__PURE__*/function (MediaPrefix) {
MediaPrefix["R0"] = "/_matrix/media/r0";
return MediaPrefix;
}({});
exports.MediaPrefix = MediaPrefix;
//# sourceMappingURL=prefix.js.map

View File

@@ -1 +1 @@
{"version":3,"file":"prefix.js","names":[],"sources":["../../src/http-api/prefix.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport enum ClientPrefix {\n /**\n * A constant representing the URI path for Client-Server API endpoints versioned at v1.\n */\n V1 = \"/_matrix/client/v1\",\n /**\n * A constant representing the URI path for Client-Server API endpoints versioned at v3.\n */\n V3 = \"/_matrix/client/v3\",\n /**\n * A constant representing the URI path for as-yet unspecified Client-Server HTTP APIs.\n */\n Unstable = \"/_matrix/client/unstable\",\n}\n\nexport enum IdentityPrefix {\n /**\n * URI path for the v2 identity API\n */\n V2 = \"/_matrix/identity/v2\",\n}\n\nexport enum MediaPrefix {\n /**\n * A constant representing the URI path for Client-Server API Media endpoints versioned at v1.\n */\n V1 = \"/_matrix/media/v1\",\n /**\n * A constant representing the URI path for Client-Server API Media endpoints versioned at v3.\n */\n V3 = \"/_matrix/media/v3\",\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,WAAY,YAAY,0BAAZ,YAAY;EACpB;AACJ;AACA;EAHY,YAAY;EAKpB;AACJ;AACA;EAPY,YAAY;EASpB;AACJ;AACA;EAXY,YAAY;EAAA,OAAZ,YAAY;AAAA;AAexB,WAAY,cAAc,0BAAd,cAAc;EACtB;AACJ;AACA;EAHY,cAAc;EAAA,OAAd,cAAc;AAAA;AAO1B,WAAY,WAAW,0BAAX,WAAW;EACnB;AACJ;AACA;EAHY,WAAW;EAKnB;AACJ;AACA;EAPY,WAAW;EAAA,OAAX,WAAW;AAAA","ignoreList":[]}
{"version":3,"file":"prefix.js","names":["ClientPrefix","exports","IdentityPrefix","MediaPrefix"],"sources":["../../src/http-api/prefix.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport enum ClientPrefix {\n /**\n * A constant representing the URI path for release 0 of the Client-Server HTTP API.\n */\n R0 = \"/_matrix/client/r0\",\n /**\n * A constant representing the URI path for the legacy release v1 of the Client-Server HTTP API.\n */\n V1 = \"/_matrix/client/v1\",\n /**\n * A constant representing the URI path for Client-Server API endpoints versioned at v3.\n */\n V3 = \"/_matrix/client/v3\",\n /**\n * A constant representing the URI path for as-yet unspecified Client-Server HTTP APIs.\n */\n Unstable = \"/_matrix/client/unstable\",\n}\n\nexport enum IdentityPrefix {\n /**\n * URI path for the v2 identity API\n */\n V2 = \"/_matrix/identity/v2\",\n}\n\nexport enum MediaPrefix {\n /**\n * URI path for the media repo API\n */\n R0 = \"/_matrix/media/r0\",\n}\n"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAdA,IAgBYA,YAAY,0BAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAA,OAAZA,YAAY;AAAA;AAAAC,OAAA,CAAAD,YAAA,GAAAA,YAAA;AAAA,IAmBZE,cAAc,0BAAdA,cAAc;EAAdA,cAAc;EAAA,OAAdA,cAAc;AAAA;AAAAD,OAAA,CAAAC,cAAA,GAAAA,cAAA;AAAA,IAOdC,WAAW,0BAAXA,WAAW;EAAXA,WAAW;EAAA,OAAXA,WAAW;AAAA;AAAAF,OAAA,CAAAE,WAAA,GAAAA,WAAA"}

View File

@@ -1,53 +0,0 @@
import { type IHttpOpts } from "./interface.ts";
/**
* This is an internal module. See {@link MatrixHttpApi} for the public class.
*/
export declare const enum TokenRefreshOutcome {
Success = "success",
Failure = "failure",
Logout = "logout"
}
interface Snapshot {
accessToken: string;
refreshToken?: string;
expiry?: Date;
}
type Opts = Pick<IHttpOpts, "tokenRefreshFunction" | "logger" | "refreshToken" | "accessToken">;
/**
* This class is responsible for managing the access token and refresh token for authenticated requests.
* It will automatically refresh the access token when it is about to expire, and will handle unknown token errors.
*/
export declare class TokenRefresher {
private readonly opts;
constructor(opts: Opts);
/**
* Promise used to block authenticated requests during a token refresh to avoid repeated expected errors.
* @private
*/
private tokenRefreshPromise?;
private latestTokenRefreshExpiry?;
/**
* This function is called before every request to ensure that the access token is valid.
* @returns a snapshot containing the access token and other properties which must be passed to the handleUnknownToken
* handler if an M_UNKNOWN_TOKEN error is encountered.
*/
prepareForRequest(): Promise<Snapshot>;
private refreshIfNeeded;
/**
* This function is called when an M_UNKNOWN_TOKEN error is encountered.
* It will attempt to refresh the access token if it is unknown, and will return a TokenRefreshOutcome.
* @param snapshot - the snapshot returned by prepareForRequest
* @param attempt - the number of attempts made for this request so far
* @returns a TokenRefreshOutcome indicating the result of the refresh attempt
*/
handleUnknownToken(snapshot: Snapshot, attempt: number): Promise<TokenRefreshOutcome>;
private _handleUnknownToken;
/**
* Attempt to refresh access tokens.
* On success, sets new access and refresh tokens in opts.
* @returns Promise that resolves to a boolean - true when token was refreshed successfully
*/
private doTokenRefresh;
}
export {};
//# sourceMappingURL=refresh.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../src/http-api/refresh.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhD;;GAEG;AAEH,0BAAkB,mBAAmB;IACjC,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,MAAM,WAAW;CACpB;AAED,UAAU,QAAQ;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,IAAI,CAAC;CACjB;AAQD,KAAK,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,sBAAsB,GAAG,QAAQ,GAAG,cAAc,GAAG,aAAa,CAAC,CAAC;AAEhG;;;GAGG;AACH,qBAAa,cAAc;IACJ,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAxC,YAAoC,IAAI,EAAE,IAAI,EAAI;IAElD;;;OAGG;IACH,OAAO,CAAC,mBAAmB,CAAC,CAA+B;IAE3D,OAAO,CAAC,wBAAwB,CAAC,CAAO;IAExC;;;;OAIG;IACU,iBAAiB,IAAI,OAAO,CAAC,QAAQ,CAAC,CASlD;YAEa,eAAe;IAa7B;;;;;;OAMG;IACU,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAEjG;YAEa,mBAAmB;IA6BjC;;;;OAIG;YACW,cAAc;CAgC/B"}

View File

@@ -1,150 +0,0 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
/*
Copyright 2025 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 { MatrixError, TokenRefreshLogoutError } from "./errors.js";
import { sleep } from "../utils.js";
/**
* This is an internal module. See {@link MatrixHttpApi} for the public class.
*/
export let TokenRefreshOutcome = /*#__PURE__*/function (TokenRefreshOutcome) {
TokenRefreshOutcome["Success"] = "success";
TokenRefreshOutcome["Failure"] = "failure";
TokenRefreshOutcome["Logout"] = "logout";
return TokenRefreshOutcome;
}({});
// If the token expires in less than this time amount of time, we will eagerly refresh it before making the intended request.
const REFRESH_IF_TOKEN_EXPIRES_WITHIN_MS = 500;
// If we get an unknown token error and the token expires in less than this time amount of time, we will refresh it before making the intended request.
// Otherwise, we will error as the token should not have expired yet and we need to avoid retrying indefinitely.
const REFRESH_ON_ERROR_IF_TOKEN_EXPIRES_WITHIN_MS = 60 * 1000;
/**
* This class is responsible for managing the access token and refresh token for authenticated requests.
* It will automatically refresh the access token when it is about to expire, and will handle unknown token errors.
*/
export class TokenRefresher {
constructor(opts) {
/**
* Promise used to block authenticated requests during a token refresh to avoid repeated expected errors.
* @private
*/
_defineProperty(this, "tokenRefreshPromise", void 0);
_defineProperty(this, "latestTokenRefreshExpiry", void 0);
this.opts = opts;
}
/**
* This function is called before every request to ensure that the access token is valid.
* @returns a snapshot containing the access token and other properties which must be passed to the handleUnknownToken
* handler if an M_UNKNOWN_TOKEN error is encountered.
*/
async prepareForRequest() {
// Ensure our token is refreshed before we build the headers/params
await this.refreshIfNeeded();
return {
accessToken: this.opts.accessToken,
refreshToken: this.opts.refreshToken,
expiry: this.latestTokenRefreshExpiry
};
}
async refreshIfNeeded() {
if (this.tokenRefreshPromise) {
return this.tokenRefreshPromise;
}
// If we don't know the token expiry, we can't eagerly refresh
if (!this.latestTokenRefreshExpiry) return;
const expiresIn = this.latestTokenRefreshExpiry.getTime() - Date.now();
if (expiresIn <= REFRESH_IF_TOKEN_EXPIRES_WITHIN_MS) {
await this._handleUnknownToken();
}
}
/**
* This function is called when an M_UNKNOWN_TOKEN error is encountered.
* It will attempt to refresh the access token if it is unknown, and will return a TokenRefreshOutcome.
* @param snapshot - the snapshot returned by prepareForRequest
* @param attempt - the number of attempts made for this request so far
* @returns a TokenRefreshOutcome indicating the result of the refresh attempt
*/
async handleUnknownToken(snapshot, attempt) {
return this._handleUnknownToken(snapshot, attempt);
}
async _handleUnknownToken(snapshot, attempt) {
if (snapshot?.expiry) {
// If our token is unknown, but it should not have expired yet, then we should not refresh
const expiresIn = snapshot.expiry.getTime() - Date.now();
// If it still has plenty of time left on the clock, we assume something else must be wrong and
// do not refresh. Otherwise if it's expired, or will soon, we try refreshing.
if (expiresIn >= REFRESH_ON_ERROR_IF_TOKEN_EXPIRES_WITHIN_MS) {
return TokenRefreshOutcome.Logout;
}
}
if (!snapshot || snapshot?.accessToken === this.opts.accessToken) {
// If we have a snapshot, but the access token is the same as the current one then a refresh
// did not happen behind us but one may be ongoing anyway
this.tokenRefreshPromise ??= this.doTokenRefresh(attempt);
try {
return await this.tokenRefreshPromise;
} finally {
this.tokenRefreshPromise = undefined;
}
}
// We may end up here if the token was refreshed in the background due to another request
return TokenRefreshOutcome.Success;
}
/**
* Attempt to refresh access tokens.
* On success, sets new access and refresh tokens in opts.
* @returns Promise that resolves to a boolean - true when token was refreshed successfully
*/
async doTokenRefresh(attempt) {
if (!this.opts.refreshToken || !this.opts.tokenRefreshFunction) {
this.opts.logger?.error("Unable to refresh token - no refresh token or refresh function");
return TokenRefreshOutcome.Logout;
}
if (attempt && attempt > 1) {
// Exponential backoff to ensure we don't trash the server, up to 2^5 seconds
await sleep(1000 * Math.min(32, 2 ** attempt));
}
try {
this.opts.logger?.debug("Attempting to refresh token");
const {
accessToken,
refreshToken,
expiry
} = await this.opts.tokenRefreshFunction(this.opts.refreshToken);
this.opts.accessToken = accessToken;
this.opts.refreshToken = refreshToken;
this.latestTokenRefreshExpiry = expiry;
this.opts.logger?.debug("... token refresh complete, new token expiry:", expiry);
// successfully got new tokens
return TokenRefreshOutcome.Success;
} catch (error) {
// If we get a TokenError or MatrixError, we should log out, otherwise assume transient
if (error instanceof TokenRefreshLogoutError || error instanceof MatrixError) {
this.opts.logger?.error("Failed to refresh token", error);
return TokenRefreshOutcome.Logout;
}
this.opts.logger?.warn("Failed to refresh token", error);
return TokenRefreshOutcome.Failure;
}
}
}
//# sourceMappingURL=refresh.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
export declare function timeoutSignal(ms: number): AbortSignal;
export declare function anySignal(signals: AbortSignal[]): {
signal: AbortSignal;
cleanup(this: void): void;
cleanup(): void;
};
/**
* Attempt to turn an HTTP error response into a Javascript Error.
@@ -22,16 +22,4 @@ export declare function parseErrorResponse(response: XMLHttpRequest | Response,
* @throws {@link ConnectionError} If after maxAttempts the callback still throws ConnectionError
*/
export declare function retryNetworkOperation<T>(maxAttempts: number, callback: () => Promise<T>): Promise<T>;
/**
* Calculate the backoff time for a request retry attempt.
* This produces wait times of 2, 4, 8, and 16 seconds (30s total) after which we give up. If the
* failure was due to a rate limited request, the time specified in the error is returned.
*
* Returns -1 if the error is not retryable, or if we reach the maximum number of attempts.
*
* @param err - The error thrown by the http call
* @param attempts - The number of attempts made so far, including the one that just failed.
* @param retryConnectionError - Whether to retry on {@link ConnectionError} (CORS, connection is down, etc.)
*/
export declare function calculateRetryBackoff(err: any, attempts: number, retryConnectionError: boolean): number;
//# sourceMappingURL=utils.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/http-api/utils.ts"],"names":[],"mappings":"AA8BA,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,CAOrD;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG;IAC/C,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;CAC7B,CA0BA;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc,GAAG,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CA2C5F;AAqBD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAqB1G;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,oBAAoB,EAAE,OAAO,GAAG,MAAM,CA0BvG"}
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/http-api/utils.ts"],"names":[],"mappings":"AAuBA,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,CAOrD;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG;IAC/C,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,IAAI,IAAI,CAAC;CACnB,CA0BA;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc,GAAG,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAmB5F;AAgCD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAqB1G"}

View File

@@ -1,5 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.anySignal = anySignal;
exports.parseErrorResponse = parseErrorResponse;
exports.retryNetworkOperation = retryNetworkOperation;
exports.timeoutSignal = timeoutSignal;
var _contentType3 = require("content-type");
var _logger = require("../logger");
var _utils = require("../utils");
var _errors = require("./errors");
/*
Copyright 2022 - 2024 The Matrix.org Foundation C.I.C.
Copyright 2022 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.
@@ -14,20 +27,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { parse as parseContentType } from "content-type";
import { logger } from "../logger.js";
import { sleep } from "../utils.js";
import { ConnectionError, HTTPError, MatrixError, MatrixSafetyError, MatrixSafetyErrorCode, safeGetRetryAfterMs } from "./errors.js";
// Ponyfill for https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout
export function timeoutSignal(ms) {
function timeoutSignal(ms) {
const controller = new AbortController();
setTimeout(() => {
controller.abort();
}, ms);
return controller.signal;
}
export function anySignal(signals) {
function anySignal(signals) {
const controller = new AbortController();
function cleanup() {
for (const signal of signals) {
@@ -61,35 +69,28 @@ export function anySignal(signals) {
* @param body - raw body of the response
* @returns
*/
export function parseErrorResponse(response, body) {
const httpHeaders = isXhr(response) ? new Headers(response.getAllResponseHeaders().trim().split(/[\r\n]+/).map(header => {
const colonIdx = header.indexOf(":");
return [header.substring(0, colonIdx), header.substring(colonIdx + 1)];
})) : response.headers;
function parseErrorResponse(response, body) {
var _contentType, _contentType2;
let contentType;
try {
contentType = getResponseContentType(httpHeaders);
contentType = getResponseContentType(response);
} catch (e) {
return e;
}
if (contentType?.type === "application/json" && body) {
const errorBody = JSON.parse(body);
if (errorBody.errcode && MatrixSafetyErrorCode.matches(errorBody.errcode)) {
return new MatrixSafetyError(errorBody, response.status, isXhr(response) ? response.responseURL : response.url, undefined, httpHeaders);
}
return new MatrixError(errorBody, response.status, isXhr(response) ? response.responseURL : response.url, undefined, httpHeaders);
if (((_contentType = contentType) === null || _contentType === void 0 ? void 0 : _contentType.type) === "application/json" && body) {
return new _errors.MatrixError(JSON.parse(body), response.status, isXhr(response) ? response.responseURL : response.url);
}
if (contentType?.type === "text/plain") {
return new HTTPError(`Server returned ${response.status} error: ${body}`, response.status, httpHeaders);
if (((_contentType2 = contentType) === null || _contentType2 === void 0 ? void 0 : _contentType2.type) === "text/plain") {
return new _errors.HTTPError(`Server returned ${response.status} error: ${body}`, response.status);
}
return new HTTPError(`Server returned ${response.status} error`, response.status, httpHeaders);
return new _errors.HTTPError(`Server returned ${response.status} error`, response.status);
}
function isXhr(response) {
return "getResponseHeader" in response;
}
/**
* extract the Content-Type header from response headers, and
* extract the Content-Type header from the response object, and
* parse it to a `{type, parameters}` object.
*
* returns null if no content-type header could be found.
@@ -97,10 +98,19 @@ function isXhr(response) {
* @param response - response object
* @returns parsed content-type header, or null if not found
*/
function getResponseContentType(headers) {
const contentType = headers.get("Content-Type");
if (contentType === null) return null;
return parseContentType(contentType);
function getResponseContentType(response) {
let contentType;
if (isXhr(response)) {
contentType = response.getResponseHeader("Content-Type");
} else {
contentType = response.headers.get("Content-Type");
}
if (!contentType) return null;
try {
return (0, _contentType3.parse)(contentType);
} catch (e) {
throw new Error(`Error parsing Content-Type '${contentType}': ${e}`);
}
}
/**
@@ -110,19 +120,19 @@ function getResponseContentType(headers) {
* @returns the result of the network operation
* @throws {@link ConnectionError} If after maxAttempts the callback still throws ConnectionError
*/
export async function retryNetworkOperation(maxAttempts, callback) {
async function retryNetworkOperation(maxAttempts, callback) {
let attempts = 0;
let lastConnectionError = null;
while (attempts < maxAttempts) {
try {
if (attempts > 0) {
const timeout = 1000 * Math.pow(2, attempts);
logger.log(`network operation failed ${attempts} times, retrying in ${timeout}ms...`);
await sleep(timeout);
_logger.logger.log(`network operation failed ${attempts} times, retrying in ${timeout}ms...`);
await (0, _utils.sleep)(timeout);
}
return await callback();
} catch (err) {
if (err instanceof ConnectionError) {
if (err instanceof _errors.ConnectionError) {
attempts += 1;
lastConnectionError = err;
} else {
@@ -132,39 +142,4 @@ export async function retryNetworkOperation(maxAttempts, callback) {
}
throw lastConnectionError;
}
/**
* Calculate the backoff time for a request retry attempt.
* This produces wait times of 2, 4, 8, and 16 seconds (30s total) after which we give up. If the
* failure was due to a rate limited request, the time specified in the error is returned.
*
* Returns -1 if the error is not retryable, or if we reach the maximum number of attempts.
*
* @param err - The error thrown by the http call
* @param attempts - The number of attempts made so far, including the one that just failed.
* @param retryConnectionError - Whether to retry on {@link ConnectionError} (CORS, connection is down, etc.)
*/
export function calculateRetryBackoff(err, attempts, retryConnectionError) {
if (attempts > 4) {
return -1; // give up
}
if (err instanceof ConnectionError && !retryConnectionError) {
return -1;
}
if (err.httpStatus && Math.floor(err.httpStatus / 100) === 4 && err.httpStatus !== 429) {
// client error; no amount of retrying will save you now (except for rate limiting which is handled below)
return -1;
}
if (err.name === "AbortError") {
// this is a client timeout, that is already very high 60s/80s
// we don't want to retry, as it could do it for very long
return -1;
}
// If we are trying to send an event (or similar) that is too large in any way, then retrying won't help
if (err.name === "M_TOO_LARGE") {
return -1;
}
return safeGetRetryAfterMs(err, 1000 * Math.pow(2, attempts));
}
//# sourceMappingURL=utils.js.map

File diff suppressed because one or more lines are too long