@jsenv/fetch 1.1.32 → 1.1.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +4 -4
- package/src/browser/fetch_and_eval.js +9 -9
- package/src/browser/fetch_and_eval_using_script.js +29 -29
- package/src/browser/fetch_json.js +5 -5
- package/src/browser/fetch_url_browser.js +3 -3
- package/src/browser/fetch_using_fetch.js +8 -8
- package/src/browser/fetch_using_xhr.js +184 -184
- package/src/node/fetch_url_node.js +19 -19
package/README.md
CHANGED
|
@@ -3,19 +3,19 @@
|
|
|
3
3
|
`fetchUrl` is an async function requesting an url and returning a response object. It is a small wrapper on top of [node-fetch](https://github.com/node-fetch/node-fetch). It was created to provide a unified approach to request file and data urls.
|
|
4
4
|
|
|
5
5
|
```js
|
|
6
|
-
import { fetchUrl } from "@jsenv/fetch"
|
|
6
|
+
import { fetchUrl } from "@jsenv/fetch";
|
|
7
7
|
|
|
8
8
|
const responseForFile = await fetchUrl("file:///Users/you/index.html", {
|
|
9
9
|
method: "GET",
|
|
10
10
|
headers: {
|
|
11
11
|
"if-modified-since": "Wed, 21 Oct 2015 07:28:00 GMT",
|
|
12
12
|
},
|
|
13
|
-
})
|
|
13
|
+
});
|
|
14
14
|
|
|
15
15
|
const responseForHttp = await fetchUrl("http://example.com/file.js", {
|
|
16
16
|
method: "GET",
|
|
17
17
|
headers: {
|
|
18
18
|
"if-modified-since": "Wed, 21 Oct 2015 07:28:00 GMT",
|
|
19
19
|
},
|
|
20
|
-
})
|
|
20
|
+
});
|
|
21
21
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/fetch",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.34",
|
|
4
4
|
"description": "Unified url fetcher (http, http, file, data)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
},
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
13
|
-
"url": "https://github.com/jsenv/
|
|
14
|
-
"directory": "packages/fetch"
|
|
13
|
+
"url": "https://github.com/jsenv/core",
|
|
14
|
+
"directory": "packages/independent/fetch"
|
|
15
15
|
},
|
|
16
16
|
"engines": {
|
|
17
17
|
"node": ">=16.13.0"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@jsenv/urls": "2.0.0",
|
|
40
|
-
"@jsenv/server": "15.0.
|
|
40
|
+
"@jsenv/server": "15.0.4",
|
|
41
41
|
"node-fetch": "2.6.7"
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { fetchUrl } from "./fetch_url_browser.js"
|
|
1
|
+
import { fetchUrl } from "./fetch_url_browser.js";
|
|
2
2
|
|
|
3
3
|
export const fetchAndEval = async (url) => {
|
|
4
|
-
const response = await fetchUrl(url)
|
|
4
|
+
const response = await fetchUrl(url);
|
|
5
5
|
|
|
6
6
|
if (response.status >= 200 && response.status <= 299) {
|
|
7
|
-
const text = await response.text()
|
|
7
|
+
const text = await response.text();
|
|
8
8
|
// eslint-disable-next-line no-eval
|
|
9
|
-
window.eval(appendSourceURL(text, url))
|
|
9
|
+
window.eval(appendSourceURL(text, url));
|
|
10
10
|
} else {
|
|
11
|
-
const text = await response.text()
|
|
11
|
+
const text = await response.text();
|
|
12
12
|
throw new Error(
|
|
13
13
|
`Unexpected response for script.
|
|
14
14
|
--- script url ---
|
|
@@ -17,11 +17,11 @@ ${url}
|
|
|
17
17
|
${text}
|
|
18
18
|
--- response status ---
|
|
19
19
|
${response.status}`,
|
|
20
|
-
)
|
|
20
|
+
);
|
|
21
21
|
}
|
|
22
|
-
}
|
|
22
|
+
};
|
|
23
23
|
|
|
24
24
|
const appendSourceURL = (code, sourceURL) => {
|
|
25
25
|
return `${code}
|
|
26
|
-
${"//#"} sourceURL=${sourceURL}
|
|
27
|
-
}
|
|
26
|
+
${"//#"} sourceURL=${sourceURL}`;
|
|
27
|
+
};
|
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
export const fetchAndEvalUsingScript = async (src) => {
|
|
2
2
|
return new Promise((resolve, reject) => {
|
|
3
|
-
const script = document.createElement("script")
|
|
3
|
+
const script = document.createElement("script");
|
|
4
4
|
|
|
5
5
|
const onwindowerror = (errorEvent) => {
|
|
6
6
|
if (errorEvent.filename === src) {
|
|
7
|
-
cleanup()
|
|
8
|
-
reject(errorEvent.error)
|
|
7
|
+
cleanup();
|
|
8
|
+
reject(errorEvent.error);
|
|
9
9
|
}
|
|
10
|
-
}
|
|
10
|
+
};
|
|
11
11
|
|
|
12
12
|
const onscripterror = () => {
|
|
13
|
-
cleanup()
|
|
14
|
-
reject(new Error(`Error loading ${src}`))
|
|
15
|
-
}
|
|
13
|
+
cleanup();
|
|
14
|
+
reject(new Error(`Error loading ${src}`));
|
|
15
|
+
};
|
|
16
16
|
|
|
17
17
|
const onscriptload = () => {
|
|
18
|
-
cleanup()
|
|
19
|
-
resolve()
|
|
20
|
-
}
|
|
18
|
+
cleanup();
|
|
19
|
+
resolve();
|
|
20
|
+
};
|
|
21
21
|
|
|
22
22
|
const cleanup = () => {
|
|
23
|
-
removeOnWindowError()
|
|
24
|
-
removeOnScriptError()
|
|
25
|
-
removeOnScriptLoad()
|
|
26
|
-
document.head.removeChild(script)
|
|
27
|
-
}
|
|
23
|
+
removeOnWindowError();
|
|
24
|
+
removeOnScriptError();
|
|
25
|
+
removeOnScriptLoad();
|
|
26
|
+
document.head.removeChild(script);
|
|
27
|
+
};
|
|
28
28
|
|
|
29
29
|
const removeOnWindowError = () =>
|
|
30
|
-
window.removeEventListener("error", onwindowerror)
|
|
30
|
+
window.removeEventListener("error", onwindowerror);
|
|
31
31
|
|
|
32
32
|
const removeOnScriptError = () =>
|
|
33
|
-
script.removeEventListener("error", onscripterror)
|
|
33
|
+
script.removeEventListener("error", onscripterror);
|
|
34
34
|
|
|
35
35
|
const removeOnScriptLoad = () =>
|
|
36
|
-
script.removeEventListener("load", onscriptload)
|
|
37
|
-
|
|
38
|
-
window.addEventListener("error", onwindowerror)
|
|
39
|
-
script.addEventListener("error", onscripterror)
|
|
40
|
-
script.addEventListener("load", onscriptload)
|
|
41
|
-
script.charset = "utf-8"
|
|
42
|
-
script.crossOrigin = "anonymous"
|
|
43
|
-
script.src = src
|
|
44
|
-
|
|
45
|
-
document.head.appendChild(script)
|
|
46
|
-
})
|
|
47
|
-
}
|
|
36
|
+
script.removeEventListener("load", onscriptload);
|
|
37
|
+
|
|
38
|
+
window.addEventListener("error", onwindowerror);
|
|
39
|
+
script.addEventListener("error", onscripterror);
|
|
40
|
+
script.addEventListener("load", onscriptload);
|
|
41
|
+
script.charset = "utf-8";
|
|
42
|
+
script.crossOrigin = "anonymous";
|
|
43
|
+
script.src = src;
|
|
44
|
+
|
|
45
|
+
document.head.appendChild(script);
|
|
46
|
+
});
|
|
47
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { fetchUrl } from "./fetch_url_browser.js"
|
|
1
|
+
import { fetchUrl } from "./fetch_url_browser.js";
|
|
2
2
|
|
|
3
3
|
export const fetchJson = async (url, options = {}) => {
|
|
4
|
-
const response = await fetchUrl(url, options)
|
|
5
|
-
const object = await response.json()
|
|
6
|
-
return object
|
|
7
|
-
}
|
|
4
|
+
const response = await fetchUrl(url, options);
|
|
5
|
+
const object = await response.json();
|
|
6
|
+
return object;
|
|
7
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { fetchUsingXHR } from "./fetch_using_xhr.js"
|
|
2
|
-
import { fetchUsingFetch } from "./fetch_using_fetch.js"
|
|
1
|
+
import { fetchUsingXHR } from "./fetch_using_xhr.js";
|
|
2
|
+
import { fetchUsingFetch } from "./fetch_using_fetch.js";
|
|
3
3
|
|
|
4
4
|
export const fetchUrl =
|
|
5
5
|
typeof window.fetch === "function" &&
|
|
6
6
|
typeof window.AbortController === "function"
|
|
7
7
|
? fetchUsingFetch
|
|
8
|
-
: fetchUsingXHR
|
|
8
|
+
: fetchUsingXHR;
|
|
@@ -5,7 +5,7 @@ export const fetchUsingFetch = async (
|
|
|
5
5
|
const response = await window.fetch(url, {
|
|
6
6
|
mode,
|
|
7
7
|
...options,
|
|
8
|
-
})
|
|
8
|
+
});
|
|
9
9
|
return {
|
|
10
10
|
url: response.url,
|
|
11
11
|
status: response.status,
|
|
@@ -16,13 +16,13 @@ export const fetchUsingFetch = async (
|
|
|
16
16
|
blob: () => response.blob(),
|
|
17
17
|
arrayBuffer: () => response.arrayBuffer(),
|
|
18
18
|
formData: () => response.formData(),
|
|
19
|
-
}
|
|
20
|
-
}
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
21
|
|
|
22
22
|
const responseToHeaders = (response) => {
|
|
23
|
-
const headers = {}
|
|
23
|
+
const headers = {};
|
|
24
24
|
response.headers.forEach((value, name) => {
|
|
25
|
-
headers[name] = value
|
|
26
|
-
})
|
|
27
|
-
return headers
|
|
28
|
-
}
|
|
25
|
+
headers[name] = value;
|
|
26
|
+
});
|
|
27
|
+
return headers;
|
|
28
|
+
};
|
|
@@ -8,164 +8,164 @@ export const fetchUsingXHR = async (
|
|
|
8
8
|
body = null,
|
|
9
9
|
} = {},
|
|
10
10
|
) => {
|
|
11
|
-
const headersPromise = createPromiseAndHooks()
|
|
12
|
-
const bodyPromise = createPromiseAndHooks()
|
|
11
|
+
const headersPromise = createPromiseAndHooks();
|
|
12
|
+
const bodyPromise = createPromiseAndHooks();
|
|
13
13
|
|
|
14
|
-
const xhr = new XMLHttpRequest()
|
|
14
|
+
const xhr = new XMLHttpRequest();
|
|
15
15
|
|
|
16
16
|
const failure = (error) => {
|
|
17
17
|
// if it was already resolved, we must reject the body promise
|
|
18
18
|
if (headersPromise.settled) {
|
|
19
|
-
bodyPromise.reject(error)
|
|
19
|
+
bodyPromise.reject(error);
|
|
20
20
|
} else {
|
|
21
|
-
headersPromise.reject(error)
|
|
21
|
+
headersPromise.reject(error);
|
|
22
22
|
}
|
|
23
|
-
}
|
|
23
|
+
};
|
|
24
24
|
|
|
25
25
|
const cleanup = () => {
|
|
26
|
-
xhr.ontimeout = null
|
|
27
|
-
xhr.onerror = null
|
|
28
|
-
xhr.onload = null
|
|
29
|
-
xhr.onreadystatechange = null
|
|
30
|
-
}
|
|
26
|
+
xhr.ontimeout = null;
|
|
27
|
+
xhr.onerror = null;
|
|
28
|
+
xhr.onload = null;
|
|
29
|
+
xhr.onreadystatechange = null;
|
|
30
|
+
};
|
|
31
31
|
|
|
32
32
|
xhr.ontimeout = () => {
|
|
33
|
-
cleanup()
|
|
34
|
-
failure(new Error(`xhr request timeout on ${url}.`))
|
|
35
|
-
}
|
|
33
|
+
cleanup();
|
|
34
|
+
failure(new Error(`xhr request timeout on ${url}.`));
|
|
35
|
+
};
|
|
36
36
|
|
|
37
37
|
xhr.onerror = (error) => {
|
|
38
|
-
cleanup()
|
|
38
|
+
cleanup();
|
|
39
39
|
// unfortunately with have no clue why it fails
|
|
40
40
|
// might be cors for instance
|
|
41
|
-
failure(createRequestError(error, { url }))
|
|
42
|
-
}
|
|
41
|
+
failure(createRequestError(error, { url }));
|
|
42
|
+
};
|
|
43
43
|
|
|
44
44
|
xhr.onload = () => {
|
|
45
|
-
cleanup()
|
|
46
|
-
bodyPromise.resolve()
|
|
47
|
-
}
|
|
45
|
+
cleanup();
|
|
46
|
+
bodyPromise.resolve();
|
|
47
|
+
};
|
|
48
48
|
|
|
49
49
|
signal.addEventListener("abort", () => {
|
|
50
|
-
xhr.abort()
|
|
51
|
-
const abortError = new Error("aborted")
|
|
52
|
-
abortError.name = "AbortError"
|
|
53
|
-
failure(abortError)
|
|
54
|
-
})
|
|
50
|
+
xhr.abort();
|
|
51
|
+
const abortError = new Error("aborted");
|
|
52
|
+
abortError.name = "AbortError";
|
|
53
|
+
failure(abortError);
|
|
54
|
+
});
|
|
55
55
|
|
|
56
56
|
xhr.onreadystatechange = () => {
|
|
57
57
|
// https://developer.mozilla.org/fr/docs/Web/API/XMLHttpRequest/readyState
|
|
58
|
-
const { readyState } = xhr
|
|
58
|
+
const { readyState } = xhr;
|
|
59
59
|
|
|
60
60
|
if (readyState === 2) {
|
|
61
|
-
headersPromise.resolve()
|
|
61
|
+
headersPromise.resolve();
|
|
62
62
|
} else if (readyState === 4) {
|
|
63
|
-
cleanup()
|
|
64
|
-
bodyPromise.resolve()
|
|
63
|
+
cleanup();
|
|
64
|
+
bodyPromise.resolve();
|
|
65
65
|
}
|
|
66
|
-
}
|
|
66
|
+
};
|
|
67
67
|
|
|
68
|
-
xhr.open(method, url, true)
|
|
68
|
+
xhr.open(method, url, true);
|
|
69
69
|
Object.keys(headers).forEach((key) => {
|
|
70
|
-
xhr.setRequestHeader(key, headers[key])
|
|
71
|
-
})
|
|
72
|
-
xhr.withCredentials = computeWithCredentials({ credentials, url })
|
|
70
|
+
xhr.setRequestHeader(key, headers[key]);
|
|
71
|
+
});
|
|
72
|
+
xhr.withCredentials = computeWithCredentials({ credentials, url });
|
|
73
73
|
if ("responseType" in xhr && hasBlob) {
|
|
74
|
-
xhr.responseType = "blob"
|
|
74
|
+
xhr.responseType = "blob";
|
|
75
75
|
}
|
|
76
|
-
xhr.send(body)
|
|
76
|
+
xhr.send(body);
|
|
77
77
|
|
|
78
|
-
await headersPromise
|
|
78
|
+
await headersPromise;
|
|
79
79
|
|
|
80
80
|
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseURL
|
|
81
81
|
const responseUrl =
|
|
82
|
-
"responseURL" in xhr ? xhr.responseURL : headers["x-request-url"]
|
|
83
|
-
let responseStatus = xhr.status
|
|
84
|
-
const responseStatusText = xhr.statusText
|
|
85
|
-
const responseHeaders = getHeadersFromXHR(xhr)
|
|
82
|
+
"responseURL" in xhr ? xhr.responseURL : headers["x-request-url"];
|
|
83
|
+
let responseStatus = xhr.status;
|
|
84
|
+
const responseStatusText = xhr.statusText;
|
|
85
|
+
const responseHeaders = getHeadersFromXHR(xhr);
|
|
86
86
|
|
|
87
87
|
const readBody = async () => {
|
|
88
|
-
await bodyPromise
|
|
88
|
+
await bodyPromise;
|
|
89
89
|
|
|
90
|
-
const { status } = xhr
|
|
90
|
+
const { status } = xhr;
|
|
91
91
|
// in Chrome on file:/// URLs, status is 0
|
|
92
92
|
if (status === 0) {
|
|
93
|
-
responseStatus = 200
|
|
93
|
+
responseStatus = 200;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
const body = "response" in xhr ? xhr.response : xhr.responseText
|
|
96
|
+
const body = "response" in xhr ? xhr.response : xhr.responseText;
|
|
97
97
|
|
|
98
98
|
return {
|
|
99
99
|
responseBody: body,
|
|
100
100
|
responseBodyType: detectBodyType(body),
|
|
101
|
-
}
|
|
102
|
-
}
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
103
|
|
|
104
104
|
const text = async () => {
|
|
105
|
-
const { responseBody, responseBodyType } = await readBody()
|
|
105
|
+
const { responseBody, responseBodyType } = await readBody();
|
|
106
106
|
|
|
107
107
|
if (responseBodyType === "blob") {
|
|
108
|
-
return blobToText(responseBody)
|
|
108
|
+
return blobToText(responseBody);
|
|
109
109
|
}
|
|
110
110
|
if (responseBodyType === "formData") {
|
|
111
|
-
throw new Error("could not read FormData body as text")
|
|
111
|
+
throw new Error("could not read FormData body as text");
|
|
112
112
|
}
|
|
113
113
|
if (responseBodyType === "dataView") {
|
|
114
|
-
return arrayBufferToText(responseBody.buffer)
|
|
114
|
+
return arrayBufferToText(responseBody.buffer);
|
|
115
115
|
}
|
|
116
116
|
if (responseBodyType === "arrayBuffer") {
|
|
117
|
-
return arrayBufferToText(responseBody)
|
|
117
|
+
return arrayBufferToText(responseBody);
|
|
118
118
|
}
|
|
119
119
|
// if (responseBodyType === "text" || responseBodyType === 'searchParams') {
|
|
120
120
|
// return body
|
|
121
121
|
// }
|
|
122
|
-
return String(responseBody)
|
|
123
|
-
}
|
|
122
|
+
return String(responseBody);
|
|
123
|
+
};
|
|
124
124
|
|
|
125
125
|
const json = async () => {
|
|
126
|
-
const responseText = await text()
|
|
127
|
-
return JSON.parse(responseText)
|
|
128
|
-
}
|
|
126
|
+
const responseText = await text();
|
|
127
|
+
return JSON.parse(responseText);
|
|
128
|
+
};
|
|
129
129
|
|
|
130
130
|
const blob = async () => {
|
|
131
131
|
if (!hasBlob) {
|
|
132
|
-
throw new Error(`blob not supported`)
|
|
132
|
+
throw new Error(`blob not supported`);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
const { responseBody, responseBodyType } = await readBody()
|
|
135
|
+
const { responseBody, responseBodyType } = await readBody();
|
|
136
136
|
|
|
137
137
|
if (responseBodyType === "blob") {
|
|
138
|
-
return responseBody
|
|
138
|
+
return responseBody;
|
|
139
139
|
}
|
|
140
140
|
if (responseBodyType === "dataView") {
|
|
141
|
-
return new Blob([cloneBuffer(responseBody.buffer)])
|
|
141
|
+
return new Blob([cloneBuffer(responseBody.buffer)]);
|
|
142
142
|
}
|
|
143
143
|
if (responseBodyType === "arrayBuffer") {
|
|
144
|
-
return new Blob([cloneBuffer(responseBody)])
|
|
144
|
+
return new Blob([cloneBuffer(responseBody)]);
|
|
145
145
|
}
|
|
146
146
|
if (responseBodyType === "formData") {
|
|
147
|
-
throw new Error("could not read FormData body as blob")
|
|
147
|
+
throw new Error("could not read FormData body as blob");
|
|
148
148
|
}
|
|
149
|
-
return new Blob([String(responseBody)])
|
|
150
|
-
}
|
|
149
|
+
return new Blob([String(responseBody)]);
|
|
150
|
+
};
|
|
151
151
|
|
|
152
152
|
const arrayBuffer = async () => {
|
|
153
|
-
const { responseBody, responseBodyType } = await readBody()
|
|
153
|
+
const { responseBody, responseBodyType } = await readBody();
|
|
154
154
|
|
|
155
155
|
if (responseBodyType === "arrayBuffer") {
|
|
156
|
-
return cloneBuffer(responseBody)
|
|
156
|
+
return cloneBuffer(responseBody);
|
|
157
157
|
}
|
|
158
|
-
const responseBlob = await blob()
|
|
159
|
-
return blobToArrayBuffer(responseBlob)
|
|
160
|
-
}
|
|
158
|
+
const responseBlob = await blob();
|
|
159
|
+
return blobToArrayBuffer(responseBlob);
|
|
160
|
+
};
|
|
161
161
|
|
|
162
162
|
const formData = async () => {
|
|
163
163
|
if (!hasFormData) {
|
|
164
|
-
throw new Error(`formData not supported`)
|
|
164
|
+
throw new Error(`formData not supported`);
|
|
165
165
|
}
|
|
166
|
-
const responseText = await text()
|
|
167
|
-
return textToFormData(responseText)
|
|
168
|
-
}
|
|
166
|
+
const responseText = await text();
|
|
167
|
+
return textToFormData(responseText);
|
|
168
|
+
};
|
|
169
169
|
|
|
170
170
|
return {
|
|
171
171
|
url: responseUrl,
|
|
@@ -177,146 +177,146 @@ export const fetchUsingXHR = async (
|
|
|
177
177
|
blob,
|
|
178
178
|
arrayBuffer,
|
|
179
179
|
formData,
|
|
180
|
-
}
|
|
181
|
-
}
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
182
|
|
|
183
183
|
const canUseBlob = () => {
|
|
184
|
-
if (typeof window.FileReader !== "function") return false
|
|
184
|
+
if (typeof window.FileReader !== "function") return false;
|
|
185
185
|
|
|
186
|
-
if (typeof window.Blob !== "function") return false
|
|
186
|
+
if (typeof window.Blob !== "function") return false;
|
|
187
187
|
|
|
188
188
|
try {
|
|
189
189
|
// eslint-disable-next-line no-new
|
|
190
|
-
new Blob()
|
|
191
|
-
return true
|
|
190
|
+
new Blob();
|
|
191
|
+
return true;
|
|
192
192
|
} catch (e) {
|
|
193
|
-
return false
|
|
193
|
+
return false;
|
|
194
194
|
}
|
|
195
|
-
}
|
|
195
|
+
};
|
|
196
196
|
|
|
197
|
-
const hasBlob = canUseBlob()
|
|
197
|
+
const hasBlob = canUseBlob();
|
|
198
198
|
|
|
199
|
-
const hasFormData = typeof window.FormData === "function"
|
|
199
|
+
const hasFormData = typeof window.FormData === "function";
|
|
200
200
|
|
|
201
|
-
const hasArrayBuffer = typeof window.ArrayBuffer === "function"
|
|
201
|
+
const hasArrayBuffer = typeof window.ArrayBuffer === "function";
|
|
202
202
|
|
|
203
|
-
const hasSearchParams = typeof window.URLSearchParams === "function"
|
|
203
|
+
const hasSearchParams = typeof window.URLSearchParams === "function";
|
|
204
204
|
|
|
205
205
|
const createRequestError = (error, { url }) => {
|
|
206
206
|
return new Error(
|
|
207
207
|
`error during xhr request on ${url}.
|
|
208
208
|
--- error stack ---
|
|
209
209
|
${error.stack}`,
|
|
210
|
-
)
|
|
211
|
-
}
|
|
210
|
+
);
|
|
211
|
+
};
|
|
212
212
|
|
|
213
213
|
const createPromiseAndHooks = () => {
|
|
214
|
-
let resolve
|
|
215
|
-
let reject
|
|
214
|
+
let resolve;
|
|
215
|
+
let reject;
|
|
216
216
|
const promise = new Promise((res, rej) => {
|
|
217
217
|
resolve = (value) => {
|
|
218
|
-
promise.settled = true
|
|
219
|
-
res(value)
|
|
220
|
-
}
|
|
218
|
+
promise.settled = true;
|
|
219
|
+
res(value);
|
|
220
|
+
};
|
|
221
221
|
reject = (value) => {
|
|
222
|
-
promise.settled = true
|
|
223
|
-
rej(value)
|
|
224
|
-
}
|
|
225
|
-
})
|
|
226
|
-
promise.resolve = resolve
|
|
227
|
-
promise.reject = reject
|
|
228
|
-
return promise
|
|
229
|
-
}
|
|
222
|
+
promise.settled = true;
|
|
223
|
+
rej(value);
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
promise.resolve = resolve;
|
|
227
|
+
promise.reject = reject;
|
|
228
|
+
return promise;
|
|
229
|
+
};
|
|
230
230
|
|
|
231
231
|
// https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
|
|
232
232
|
const computeWithCredentials = ({ credentials, url }) => {
|
|
233
233
|
if (credentials === "same-origin") {
|
|
234
|
-
return originSameAsGlobalOrigin(url)
|
|
234
|
+
return originSameAsGlobalOrigin(url);
|
|
235
235
|
}
|
|
236
|
-
return credentials === "include"
|
|
237
|
-
}
|
|
236
|
+
return credentials === "include";
|
|
237
|
+
};
|
|
238
238
|
|
|
239
239
|
const originSameAsGlobalOrigin = (url) => {
|
|
240
240
|
// if we cannot read globalOrigin from window.location.origin, let's consider it's ok
|
|
241
|
-
if (typeof window !== "object") return true
|
|
242
|
-
if (typeof window.location !== "object") return true
|
|
243
|
-
const globalOrigin = window.location.origin
|
|
244
|
-
if (globalOrigin === "null") return true
|
|
245
|
-
return hrefToOrigin(url) === globalOrigin
|
|
246
|
-
}
|
|
241
|
+
if (typeof window !== "object") return true;
|
|
242
|
+
if (typeof window.location !== "object") return true;
|
|
243
|
+
const globalOrigin = window.location.origin;
|
|
244
|
+
if (globalOrigin === "null") return true;
|
|
245
|
+
return hrefToOrigin(url) === globalOrigin;
|
|
246
|
+
};
|
|
247
247
|
|
|
248
248
|
const detectBodyType = (body) => {
|
|
249
249
|
if (!body) {
|
|
250
|
-
return ""
|
|
250
|
+
return "";
|
|
251
251
|
}
|
|
252
252
|
if (typeof body === "string") {
|
|
253
|
-
return "text"
|
|
253
|
+
return "text";
|
|
254
254
|
}
|
|
255
255
|
if (hasBlob && Blob.prototype.isPrototypeOf(body)) {
|
|
256
|
-
return "blob"
|
|
256
|
+
return "blob";
|
|
257
257
|
}
|
|
258
258
|
if (hasFormData && FormData.prototype.isPrototypeOf(body)) {
|
|
259
|
-
return "formData"
|
|
259
|
+
return "formData";
|
|
260
260
|
}
|
|
261
261
|
if (hasArrayBuffer) {
|
|
262
262
|
if (hasBlob && isDataView(body)) {
|
|
263
|
-
return `dataView
|
|
263
|
+
return `dataView`;
|
|
264
264
|
}
|
|
265
265
|
if (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body)) {
|
|
266
|
-
return `arrayBuffer
|
|
266
|
+
return `arrayBuffer`;
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
269
|
if (hasSearchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
|
|
270
|
-
return "searchParams"
|
|
270
|
+
return "searchParams";
|
|
271
271
|
}
|
|
272
|
-
return ""
|
|
273
|
-
}
|
|
272
|
+
return "";
|
|
273
|
+
};
|
|
274
274
|
|
|
275
275
|
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/getAllResponseHeaders#Example
|
|
276
276
|
const getHeadersFromXHR = (xhr) => {
|
|
277
|
-
const headerMap = {}
|
|
277
|
+
const headerMap = {};
|
|
278
278
|
|
|
279
|
-
const headersString = xhr.getAllResponseHeaders()
|
|
280
|
-
if (headersString === "") return headerMap
|
|
279
|
+
const headersString = xhr.getAllResponseHeaders();
|
|
280
|
+
if (headersString === "") return headerMap;
|
|
281
281
|
|
|
282
|
-
const lines = headersString.trim().split(/[\r\n]+/)
|
|
282
|
+
const lines = headersString.trim().split(/[\r\n]+/);
|
|
283
283
|
lines.forEach((line) => {
|
|
284
|
-
const parts = line.split(": ")
|
|
285
|
-
const name = parts.shift()
|
|
286
|
-
const value = parts.join(": ")
|
|
287
|
-
headerMap[name.toLowerCase()] = value
|
|
288
|
-
})
|
|
284
|
+
const parts = line.split(": ");
|
|
285
|
+
const name = parts.shift();
|
|
286
|
+
const value = parts.join(": ");
|
|
287
|
+
headerMap[name.toLowerCase()] = value;
|
|
288
|
+
});
|
|
289
289
|
|
|
290
|
-
return headerMap
|
|
291
|
-
}
|
|
290
|
+
return headerMap;
|
|
291
|
+
};
|
|
292
292
|
|
|
293
293
|
const hrefToOrigin = (href) => {
|
|
294
|
-
const scheme = hrefToScheme(href)
|
|
294
|
+
const scheme = hrefToScheme(href);
|
|
295
295
|
|
|
296
296
|
if (scheme === "file") {
|
|
297
|
-
return "file://"
|
|
297
|
+
return "file://";
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
if (scheme === "http" || scheme === "https") {
|
|
301
|
-
const secondProtocolSlashIndex = scheme.length + "://".length
|
|
302
|
-
const pathnameSlashIndex = href.indexOf("/", secondProtocolSlashIndex)
|
|
301
|
+
const secondProtocolSlashIndex = scheme.length + "://".length;
|
|
302
|
+
const pathnameSlashIndex = href.indexOf("/", secondProtocolSlashIndex);
|
|
303
303
|
|
|
304
|
-
if (pathnameSlashIndex === -1) return href
|
|
305
|
-
return href.slice(0, pathnameSlashIndex)
|
|
304
|
+
if (pathnameSlashIndex === -1) return href;
|
|
305
|
+
return href.slice(0, pathnameSlashIndex);
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
return href.slice(0, scheme.length + 1)
|
|
309
|
-
}
|
|
308
|
+
return href.slice(0, scheme.length + 1);
|
|
309
|
+
};
|
|
310
310
|
|
|
311
311
|
const hrefToScheme = (href) => {
|
|
312
|
-
const colonIndex = href.indexOf(":")
|
|
313
|
-
if (colonIndex === -1) return ""
|
|
314
|
-
return href.slice(0, colonIndex)
|
|
315
|
-
}
|
|
312
|
+
const colonIndex = href.indexOf(":");
|
|
313
|
+
if (colonIndex === -1) return "";
|
|
314
|
+
return href.slice(0, colonIndex);
|
|
315
|
+
};
|
|
316
316
|
|
|
317
317
|
const isDataView = (obj) => {
|
|
318
|
-
return obj && DataView.prototype.isPrototypeOf(obj)
|
|
319
|
-
}
|
|
318
|
+
return obj && DataView.prototype.isPrototypeOf(obj);
|
|
319
|
+
};
|
|
320
320
|
|
|
321
321
|
const isArrayBufferView =
|
|
322
322
|
ArrayBuffer.isView ||
|
|
@@ -331,73 +331,73 @@ const isArrayBufferView =
|
|
|
331
331
|
"[object Uint32Array]",
|
|
332
332
|
"[object Float32Array]",
|
|
333
333
|
"[object Float64Array]",
|
|
334
|
-
]
|
|
334
|
+
];
|
|
335
335
|
|
|
336
336
|
return (value) => {
|
|
337
337
|
return (
|
|
338
338
|
value && viewClasses.includes(Object.prototype.toString.call(value))
|
|
339
|
-
)
|
|
340
|
-
}
|
|
341
|
-
})()
|
|
339
|
+
);
|
|
340
|
+
};
|
|
341
|
+
})();
|
|
342
342
|
|
|
343
343
|
const textToFormData = (text) => {
|
|
344
|
-
const form = new FormData()
|
|
344
|
+
const form = new FormData();
|
|
345
345
|
text
|
|
346
346
|
.trim()
|
|
347
347
|
.split("&")
|
|
348
348
|
.forEach(function (bytes) {
|
|
349
349
|
if (bytes) {
|
|
350
|
-
const split = bytes.split("=")
|
|
351
|
-
const name = split.shift().replace(/\+/g, " ")
|
|
352
|
-
const value = split.join("=").replace(/\+/g, " ")
|
|
353
|
-
form.append(decodeURIComponent(name), decodeURIComponent(value))
|
|
350
|
+
const split = bytes.split("=");
|
|
351
|
+
const name = split.shift().replace(/\+/g, " ");
|
|
352
|
+
const value = split.join("=").replace(/\+/g, " ");
|
|
353
|
+
form.append(decodeURIComponent(name), decodeURIComponent(value));
|
|
354
354
|
}
|
|
355
|
-
})
|
|
356
|
-
return form
|
|
357
|
-
}
|
|
355
|
+
});
|
|
356
|
+
return form;
|
|
357
|
+
};
|
|
358
358
|
|
|
359
359
|
const blobToArrayBuffer = async (blob) => {
|
|
360
|
-
const reader = new FileReader()
|
|
361
|
-
const promise = fileReaderReady(reader)
|
|
362
|
-
reader.readAsArrayBuffer(blob)
|
|
363
|
-
return promise
|
|
364
|
-
}
|
|
360
|
+
const reader = new FileReader();
|
|
361
|
+
const promise = fileReaderReady(reader);
|
|
362
|
+
reader.readAsArrayBuffer(blob);
|
|
363
|
+
return promise;
|
|
364
|
+
};
|
|
365
365
|
|
|
366
366
|
const blobToText = (blob) => {
|
|
367
|
-
const reader = new FileReader()
|
|
368
|
-
const promise = fileReaderReady(reader)
|
|
369
|
-
reader.readAsText(blob)
|
|
370
|
-
return promise
|
|
371
|
-
}
|
|
367
|
+
const reader = new FileReader();
|
|
368
|
+
const promise = fileReaderReady(reader);
|
|
369
|
+
reader.readAsText(blob);
|
|
370
|
+
return promise;
|
|
371
|
+
};
|
|
372
372
|
|
|
373
373
|
const arrayBufferToText = (arrayBuffer) => {
|
|
374
|
-
const view = new Uint8Array(arrayBuffer)
|
|
375
|
-
const chars = new Array(view.length)
|
|
376
|
-
let i = 0
|
|
374
|
+
const view = new Uint8Array(arrayBuffer);
|
|
375
|
+
const chars = new Array(view.length);
|
|
376
|
+
let i = 0;
|
|
377
377
|
while (i < view.length) {
|
|
378
|
-
chars[i] = String.fromCharCode(view[i])
|
|
378
|
+
chars[i] = String.fromCharCode(view[i]);
|
|
379
379
|
|
|
380
|
-
i
|
|
380
|
+
i++;
|
|
381
381
|
}
|
|
382
|
-
return chars.join("")
|
|
383
|
-
}
|
|
382
|
+
return chars.join("");
|
|
383
|
+
};
|
|
384
384
|
|
|
385
385
|
const fileReaderReady = (reader) => {
|
|
386
386
|
return new Promise(function (resolve, reject) {
|
|
387
387
|
reader.onload = function () {
|
|
388
|
-
resolve(reader.result)
|
|
389
|
-
}
|
|
388
|
+
resolve(reader.result);
|
|
389
|
+
};
|
|
390
390
|
reader.onerror = function () {
|
|
391
|
-
reject(reader.error)
|
|
392
|
-
}
|
|
393
|
-
})
|
|
394
|
-
}
|
|
391
|
+
reject(reader.error);
|
|
392
|
+
};
|
|
393
|
+
});
|
|
394
|
+
};
|
|
395
395
|
|
|
396
396
|
const cloneBuffer = (buffer) => {
|
|
397
397
|
if (buffer.slice) {
|
|
398
|
-
return buffer.slice(0)
|
|
398
|
+
return buffer.slice(0);
|
|
399
399
|
}
|
|
400
|
-
const view = new Uint8Array(buffer.byteLength)
|
|
401
|
-
view.set(new Uint8Array(buffer))
|
|
402
|
-
return view.buffer
|
|
403
|
-
}
|
|
400
|
+
const view = new Uint8Array(buffer.byteLength);
|
|
401
|
+
view.set(new Uint8Array(buffer));
|
|
402
|
+
return view.buffer;
|
|
403
|
+
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// https://github.com/node-fetch/node-fetch/blob/8c197f8982a238b3c345c64b17bfa92e16b4f7c4/src/response.js#L1
|
|
2
2
|
|
|
3
|
-
import { Agent } from "node:https"
|
|
4
|
-
import nodeFetch, { Response } from "node-fetch"
|
|
3
|
+
import { Agent } from "node:https";
|
|
4
|
+
import nodeFetch, { Response } from "node-fetch";
|
|
5
5
|
|
|
6
|
-
import { DATA_URL } from "@jsenv/urls"
|
|
7
|
-
import { fetchFileSystem } from "@jsenv/server"
|
|
6
|
+
import { DATA_URL } from "@jsenv/urls";
|
|
7
|
+
import { fetchFileSystem } from "@jsenv/server";
|
|
8
8
|
import {
|
|
9
9
|
isFileHandle,
|
|
10
10
|
fileHandleToReadableStream,
|
|
11
|
-
} from "@jsenv/server/src/interfacing_with_node/body.js"
|
|
11
|
+
} from "@jsenv/server/src/interfacing_with_node/body.js";
|
|
12
12
|
|
|
13
13
|
export const fetchUrl = async (
|
|
14
14
|
url,
|
|
@@ -24,11 +24,11 @@ export const fetchUrl = async (
|
|
|
24
24
|
} = {},
|
|
25
25
|
) => {
|
|
26
26
|
try {
|
|
27
|
-
url = String(new URL(url))
|
|
27
|
+
url = String(new URL(url));
|
|
28
28
|
} catch (e) {
|
|
29
29
|
throw new Error(
|
|
30
30
|
`fetchUrl first argument must be an absolute url, received ${url}`,
|
|
31
|
-
)
|
|
31
|
+
);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
if (url.startsWith("file://")) {
|
|
@@ -41,8 +41,8 @@ export const fetchUrl = async (
|
|
|
41
41
|
canReadDirectory,
|
|
42
42
|
contentTypeMap,
|
|
43
43
|
...rest,
|
|
44
|
-
})
|
|
45
|
-
const responseBody = responseProperties.body
|
|
44
|
+
});
|
|
45
|
+
const responseBody = responseProperties.body;
|
|
46
46
|
|
|
47
47
|
const response = new Response(
|
|
48
48
|
typeof responseBody === "string"
|
|
@@ -56,21 +56,21 @@ export const fetchUrl = async (
|
|
|
56
56
|
statusText: responseProperties.statusText,
|
|
57
57
|
headers: responseProperties.headers,
|
|
58
58
|
},
|
|
59
|
-
)
|
|
60
|
-
return response
|
|
59
|
+
);
|
|
60
|
+
return response;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
if (url.startsWith("data:")) {
|
|
64
|
-
const { contentType, base64Flag, data } = DATA_URL.parse(url)
|
|
65
|
-
const body = base64Flag ? Buffer.from(data, "base64") : Buffer.from(data)
|
|
64
|
+
const { contentType, base64Flag, data } = DATA_URL.parse(url);
|
|
65
|
+
const body = base64Flag ? Buffer.from(data, "base64") : Buffer.from(data);
|
|
66
66
|
const response = new Response(body, {
|
|
67
67
|
url,
|
|
68
68
|
status: 200,
|
|
69
69
|
headers: {
|
|
70
70
|
"content-type": contentType,
|
|
71
71
|
},
|
|
72
|
-
})
|
|
73
|
-
return response
|
|
72
|
+
});
|
|
73
|
+
return response;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
const response = await nodeFetch(url, {
|
|
@@ -82,12 +82,12 @@ export const fetchUrl = async (
|
|
|
82
82
|
agent: () => {
|
|
83
83
|
return new Agent({
|
|
84
84
|
rejectUnauthorized: false,
|
|
85
|
-
})
|
|
85
|
+
});
|
|
86
86
|
},
|
|
87
87
|
}
|
|
88
88
|
: {}),
|
|
89
89
|
...rest,
|
|
90
|
-
})
|
|
90
|
+
});
|
|
91
91
|
|
|
92
|
-
return response
|
|
93
|
-
}
|
|
92
|
+
return response;
|
|
93
|
+
};
|