Support streaming drivers in XMLHTTPRequest-less platforms (nodejs, serviceworker) (#1364)

* xmlhttpreq partial support pt1

* XMLHTTPRequest shim cleanup

* fix check for x-ndjson

* change credit header at top of xhr shim
This commit is contained in:
Neal Shah
2025-07-18 14:35:08 -07:00
committed by GitHub
parent f9995a219d
commit 0858f9932b
+59 -15
View File
@@ -1,4 +1,4 @@
// https://www.npmjs.com/package/xhr-shim under MIT
// Originally from https://www.npmjs.com/package/xhr-shim under MIT, heavily modified since
/* global module */
/* global EventTarget, AbortController, DOMException */
@@ -16,12 +16,51 @@ const sTimeout = Symbol("timeout");
const sTimedOut = Symbol("timedOut");
const sIsResponseText = Symbol("isResponseText");
// SO: https://stackoverflow.com/questions/49129643/how-do-i-merge-an-array-of-uint8arrays
function mergeUint8Arrays(...arrays) {
const totalSize = arrays.reduce((acc, e) => acc + e.length, 0);
const merged = new Uint8Array(totalSize);
arrays.forEach((array, i, arrays) => {
const offset = arrays.slice(0, i).reduce((acc, e) => acc + e.length, 0);
merged.set(array, offset);
});
return merged;
}
/**
* Exposes incoming data
* @this {XMLHttpRequest}
* @param {Uint8Array} bytes
*/
async function parseBody(bytes) {
const responseType = this.responseType || "text";
const textde = new TextDecoder();
const finalMIME = this[sMIME] || this[sRespHeaders].get("content-type") || "text/plain";
switch (responseType) {
case "text":
this.response = textde.decode(bytes)
break;
case "blob":
this.response = new Blob([bytes], { type: finalMIME });
break;
case "arraybuffer":
this.response = bytes.buffer;
break;
case "json":
this.response = JSON.parse(textde.decode(bytes));
break;
}
}
const XMLHttpRequestShim = class XMLHttpRequest extends EventTarget {
onreadystatechange() {
}
set readyState(value) {
if (this[sReadyState] === value) return; // dont do anything if "value" is already the internal value
this[sReadyState] = value;
this.dispatchEvent(new Event("readystatechange"));
this.onreadystatechange(new Event("readystatechange"));
@@ -146,21 +185,26 @@ const XMLHttpRequestShim = class XMLHttpRequest extends EventTarget {
this.status = resp.status;
this.statusText = resp.statusText;
this[sRespHeaders] = resp.headers;
const finalMIME = this[sMIME] || this[sRespHeaders].get("content-type") || "text/plain";
switch (responseType) {
case "text":
this.response = await resp.text();
break;
case "blob":
this.response = new Blob([await resp.arrayBuffer()], { type: finalMIME });
break;
case "arraybuffer":
this.response = await resp.arrayBuffer();
break;
case "json":
this.response = await resp.json();
break;
this.readyState = this.constructor.HEADERS_RECEIVED;
if (resp.headers.get("content-type").includes("application/x-ndjson") || this.streamRequestBadForPerformance) {
let bytes = new Uint8Array();
for await (const chunk of resp.body) {
this.readyState = this.constructor.LOADING;
bytes = mergeUint8Arrays(bytes, chunk);
parseBody.call(this, bytes);
this[sDispatch](new CustomEvent("progress"));
}
} else {
const bytesChunks = [];
for await (const chunk of resp.body) {
bytesChunks.push(chunk)
}
parseBody.call(this, mergeUint8Arrays(...bytesChunks));
}
this.readyState = this.constructor.DONE;
this[sDispatch](new CustomEvent("load"));
}, err => {