UNPKG

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