@cross-nft-marketplace/auction-house-nft-hooks
Version:
Generic react hooks for fetching cross-nft-marketplace auctions, nfts, and data on arbitary 721s. Powers nft-components.
157 lines (156 loc) • 6.59 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchEx = exports.FetchStatusNotSuccessError = exports.FetchContentLengthExceededLimitError = exports.FetchDifferentContentTypeError = exports.FetchEror = exports.FetchWithTimeout = void 0;
const tslib_1 = require("tslib");
const cross_fetch_1 = tslib_1.__importDefault(require("cross-fetch"));
const node_abort_controller_1 = tslib_1.__importDefault(require("node-abort-controller"));
const RequestError_1 = require("./RequestError");
/**
* Simple Fetch wrapper that enables a timeout.
* Allows for showing an error state for slow-to-load IPFS files
*/
class FetchWithTimeout {
constructor(timeoutDuration = 5000, contentType = undefined) {
this.controller = new node_abort_controller_1.default();
this.expectedContentType = contentType;
this.timeoutDuration = timeoutDuration;
// Bind context to class
this.fetch = this.fetch.bind(this);
}
async fetch(url, options = {}) {
var _a;
const controller = this.controller;
const response = await cross_fetch_1.default(url, {
...options,
signal: this.controller.signal,
});
setTimeout(() => controller.abort(), this.timeoutDuration);
if (response.status !== 200) {
throw new RequestError_1.RequestError(`Request Status = ${response.status}`);
}
if (this.expectedContentType &&
!((_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.startsWith(this.expectedContentType))) {
throw new RequestError_1.RequestError('Reponse Content Type incorrect');
}
return response;
}
}
exports.FetchWithTimeout = FetchWithTimeout;
var XMLHttpRequest = require("@cross-nft-marketplace/xmlhttprequest").XMLHttpRequest;
class FetchEror extends Error {
}
exports.FetchEror = FetchEror;
class FetchDifferentContentTypeError extends FetchEror {
constructor(actualContentType_) {
super("Different Content-Type");
this.actualContentType = actualContentType_;
}
}
exports.FetchDifferentContentTypeError = FetchDifferentContentTypeError;
class FetchContentLengthExceededLimitError extends FetchEror {
constructor() {
super("Content-Length exceeded limit");
}
}
exports.FetchContentLengthExceededLimitError = FetchContentLengthExceededLimitError;
class FetchStatusNotSuccessError extends FetchEror {
constructor(status, statusText) {
super("Response status code does not indicate success");
this.status = status;
this.statusText = statusText;
}
}
exports.FetchStatusNotSuccessError = FetchStatusNotSuccessError;
const HTTP_OK = 200;
//bugs in XMLHttpRequest node.js lib:
//1) does not dispatch loadend on error
//2) does not support timeout option
//3) cve https://security.snyk.io/vuln/SNYK-JS-XMLHTTPREQUESTSSL-1082936
//4) "response" field missed or invalid (node-xhrstub)
//5) status set after HEADERS_RECEIVED emitted.
//6) response header case??
async function fetchEx(url, options_ = {}) {
let timeout = options_.timeoutMs || 10000;
return new Promise((resolve, reject) => {
let client = new XMLHttpRequest();
let completed = false;
let timeoutHandler = setTimeout(() => client.abort(), timeout);
let disposeTimeout = () => {
if (timeoutHandler) {
clearTimeout(timeoutHandler);
timeoutHandler = undefined;
}
};
let abortClient = () => {
client.abort();
disposeTimeout();
completed = true;
};
client.open(options_.method || 'GET', url, true);
//todo set responseType for browser?
client.onload = function () {
if (completed) {
return;
}
disposeTimeout();
if (client.status == HTTP_OK) {
resolve(client.responseText);
}
else {
reject(new FetchStatusNotSuccessError(client.status));
}
completed = true;
};
client.onerror = function () {
if (completed) {
return;
}
reject(new FetchStatusNotSuccessError(client.status, client.statusText));
disposeTimeout();
completed = true;
};
client.onreadystatechange = function () {
if (completed) {
return;
}
if (client.readyState == client.HEADERS_RECEIVED) {
if (client.status != 0 && client.status != HTTP_OK) {
let status = client.status;
abortClient();
reject(new FetchStatusNotSuccessError(status));
return;
}
if (options_.responseRequiredContentType != null) {
let contentType = client.getResponseHeader("Content-Type");
if (contentType == null || !contentType.startsWith(options_.responseRequiredContentType)) {
abortClient();
reject(new FetchDifferentContentTypeError(contentType || ""));
return;
}
}
if (options_.responseMaxLimitInBytes != null) {
let contentLength = client.getResponseHeader("Content-Length");
if (contentLength) {
let contentLengthNumber = Number(contentLength);
if (contentLengthNumber > options_.responseMaxLimitInBytes) {
abortClient();
reject(new FetchContentLengthExceededLimitError());
return;
}
}
}
}
else if (client.readyState == client.LOADING || client.readyState == client.DONE) {
//todo check in bytes
if (options_.responseMaxLimitInBytes
&& client.responseText
&& client.responseText.length > options_.responseMaxLimitInBytes) {
abortClient();
reject(new FetchContentLengthExceededLimitError());
}
}
};
client.send();
});
}
exports.fetchEx = fetchEx;