UNPKG

@building-block/xhr-fetch

Version:

XHR-powered fetch implementation with upload and download updates

1 lines 6.15 kB
{"version":3,"file":"xhrFetch.mjs","sources":["../src/xhrFetch.js"],"sourcesContent":["const XHR_READY_STATE = {\n // Client has been created. open() not called yet.\n UNSENT: 0,\n // open() has been called.\n OPENED: 1,\n // send() has been called, and headers and status are available.\n HEADERS_RECEIVED: 2,\n // Downloading; responseText holds partial data.\n LOADING: 3,\n // The operation is complete.\n DONE: 4,\n};\n\nconst noop = () => {};\n\nconst isInRange = (num, begin, end) => num >= begin && num <= end;\n\n// https://github.com/github/fetch/blob/master/fetch.js#L368-L382\nconst parseHeaders = (rawHeaders = '') => {\n const headers = new Headers();\n // Replace instances of \\r\\n and \\n followed by at least one space or horizontal tab with a space\n // https://tools.ietf.org/html/rfc7230#section-3.2\n const preProcessedHeaders = rawHeaders.replace(/\\r?\\n[\\t ]+/g, ' ');\n preProcessedHeaders.split(/\\r?\\n/).forEach((line) => {\n const parts = line.split(':');\n const key = parts.shift().trim();\n if (key) {\n const value = parts.join(':').trim();\n headers.append(key, value);\n }\n });\n return headers;\n};\n\nconst DEFAULT_CONFIG = {\n method: 'GET',\n headers: {},\n body: null,\n onDownloadProgress: noop,\n onUploadProgress: noop,\n signal: null,\n credentials: null,\n};\n\nconst createFetchishResponse = (xhr) => {\n const {\n response,\n responseText,\n responseURL,\n status,\n statusText,\n } = xhr;\n\n const blob = () => Promise.resolve(new Blob([response]));\n const json = () => Promise.resolve(JSON.parse(responseText));\n const text = () => Promise.resolve(responseText);\n\n const clone = () => createFetchishResponse(xhr);\n\n const headers = parseHeaders(xhr.getAllResponseHeaders());\n const url = responseURL || headers.get('X-Request-URL');\n\n return {\n blob,\n clone,\n headers,\n json,\n ok: isInRange(status, 200, 299),\n status,\n statusText,\n text,\n url,\n };\n};\n\nconst xhrFetch = (endpoint, {\n method = 'GET',\n headers = {},\n body = null,\n onDownloadProgress = noop,\n onUploadProgress = noop,\n signal,\n credentials = null,\n} = DEFAULT_CONFIG) => new Promise((resolve, reject) => {\n const xmlHttpRequest = new XMLHttpRequest();\n\n const abortXhr = () => {\n xmlHttpRequest.abort();\n };\n\n if (signal) {\n signal.addEventListener('abort', abortXhr);\n }\n\n xmlHttpRequest.open(method.toLowerCase(), endpoint, true);\n\n // IE10 assumes `withCredentials` is set after `open()` is called.\n if (credentials === 'include') {\n xmlHttpRequest.withCredentials = true;\n }\n\n if (credentials === 'omit') {\n xmlHttpRequest.withCredentials = false;\n }\n\n Object.keys(headers).forEach(key => xmlHttpRequest.setRequestHeader(key, headers[key]));\n\n // TODO switch from DOM Level 2 events to using Level 0 events for browser compatibility\n xmlHttpRequest.addEventListener('readystatechange', () => {\n if (xmlHttpRequest.readyState === XHR_READY_STATE.DONE) {\n // https://xhr.spec.whatwg.org/#the-abort()-method\n if (signal) {\n signal.removeEventListener('abort', abortXhr);\n }\n }\n });\n\n xmlHttpRequest.addEventListener('error', () => {\n reject(new TypeError('Network request failed'));\n });\n\n xmlHttpRequest.addEventListener('timeout', () => {\n reject(new TypeError('Network request failed'));\n });\n\n xmlHttpRequest.addEventListener('abort', () => {\n reject(new DOMException('Aborted', 'AbortError'));\n });\n\n xmlHttpRequest.addEventListener('load', () => {\n // Handle errors just like `fetch` does. So we resolve all upstream errors, and only throw\n // network errors which are handled in the error event listener.\n resolve(createFetchishResponse(xmlHttpRequest));\n });\n\n /* download */\n xmlHttpRequest.addEventListener('progress', xhrEvent => onDownloadProgress(xhrEvent));\n\n /* upload */\n xmlHttpRequest.upload.addEventListener('progress', xhrEvent => onUploadProgress(xhrEvent));\n\n xmlHttpRequest.send(body);\n});\n\nexport default xhrFetch;\n"],"names":["const","noop","DEFAULT_CONFIG","method","headers","body","onDownloadProgress","onUploadProgress","signal","credentials","createFetchishResponse","xhr","num","rawHeaders","Headers","replace","split","forEach","line","parts","key","shift","trim","value","join","append","parseHeaders","getAllResponseHeaders","url","responseURL","get","Promise","resolve","Blob","response","JSON","parse","responseText","ok","status","statusText","endpoint","ref","reject","xmlHttpRequest","XMLHttpRequest","abortXhr","abort","addEventListener","open","toLowerCase","withCredentials","Object","keys","setRequestHeader","readyState","removeEventListener","TypeError","DOMException","xhrEvent","upload","send"],"mappings":"AAAAA,IAaMC,eAqBAC,EAAiB,CACrBC,OAAQ,MACRC,QAAS,GACTC,KAAM,KACNC,mBAAoBL,EACpBM,iBAAkBN,EAClBO,OAAQ,KACRC,YAAa,MAGTC,WAA0BC,OA7BbC,0EA4CXR,WAzCcS,kBAAa,QAC3BT,EAAU,IAAIU,eAGQD,EAAWE,QAAQ,eAAgB,KAC3CC,MAAM,SAASC,iBAASC,OACpCC,EAAQD,EAAKF,MAAM,KACnBI,EAAMD,EAAME,QAAQC,UACtBF,EAAK,KACDG,EAAQJ,EAAMK,KAAK,KAAKF,OAC9BlB,EAAQqB,OAAOL,EAAKG,MAGjBnB,EA4BSsB,CAAaf,EAAIgB,yBAC3BC,EAAMC,GAAezB,EAAQ0B,IAAI,uBAEhC,wBATYC,QAAQC,QAAQ,IAAIC,KAAK,CAACC,8BAIzBxB,EAAuBC,YAQzCP,yBAXiB2B,QAAQC,QAAQG,KAAKC,MAAMC,KAa5CC,IApDe1B,EAoDD2B,EApDqB3B,GAoDb,KApD6BA,GAoDxB,YAC3B2B,aACAC,yBAdiBT,QAAQC,QAAQK,QAgBjCT,4BAIca,EAAUC,kBAQxBxC,iCAPO,sCACC,gCACH,gDACcD,2CACFA,wDAEL,MACO,IAAI8B,iBAASC,EAASW,OACrCC,EAAiB,IAAIC,eAErBC,aACJF,EAAeG,SAGbvC,GACFA,EAAOwC,iBAAiB,QAASF,GAGnCF,EAAeK,KAAK9C,EAAO+C,cAAeT,GAAU,GAGhC,YAAhBhC,IACFmC,EAAeO,iBAAkB,GAGf,SAAhB1C,IACFmC,EAAeO,iBAAkB,GAGnCC,OAAOC,KAAKjD,GAASa,iBAAQG,UAAOwB,EAAeU,iBAAiBlC,EAAKhB,EAAQgB,MAGjFwB,EAAeI,iBAAiB,8BAlG1B,IAmGAJ,EAAeW,YAEb/C,GACFA,EAAOgD,oBAAoB,QAASV,KAK1CF,EAAeI,iBAAiB,mBAC9BL,EAAO,IAAIc,UAAU,6BAGvBb,EAAeI,iBAAiB,qBAC9BL,EAAO,IAAIc,UAAU,6BAGvBb,EAAeI,iBAAiB,mBAC9BL,EAAO,IAAIe,aAAa,UAAW,iBAGrCd,EAAeI,iBAAiB,kBAG9BhB,EAAQtB,EAAuBkC,MAIjCA,EAAeI,iBAAiB,oBAAYW,UAAYrD,EAAmBqD,KAG3Ef,EAAegB,OAAOZ,iBAAiB,oBAAYW,UAAYpD,EAAiBoD,KAEhFf,EAAeiB,KAAKxD"}