@wevu/web-apis
Version:
Web API polyfills and global installers for mini-program runtimes
183 lines (182 loc) • 6.59 kB
JavaScript
import { f as isUrlInstance, l as normalizeHeaderName, n as cloneArrayBuffer, p as isUrlSearchParamsInstance, r as cloneArrayBufferView } from "./shared-BB491DgN.mjs";
import { HeadersPolyfill, ResponsePolyfill } from "./http.mjs";
import { resolveRequestMiniProgramOptions } from "./networkDefaults.mjs";
import { wpi } from "@wevu/api";
//#region src/fetch.ts
const REQUEST_METHODS = [
"GET",
"HEAD",
"OPTIONS",
"POST",
"PUT",
"DELETE",
"TRACE",
"CONNECT"
];
const hasOwn = Object.prototype.hasOwnProperty;
function isObject(value) {
return typeof value === "object" && value !== null;
}
function createAbortError() {
if (typeof DOMException === "function") return new DOMException("The operation was aborted.", "AbortError");
const error = /* @__PURE__ */ new Error("The operation was aborted.");
error.name = "AbortError";
return error;
}
function normalizeMethod(method) {
const normalized = (method ?? "GET").toUpperCase();
if (REQUEST_METHODS.includes(normalized)) return normalized;
return "GET";
}
function setHeader(target, key, value) {
const normalizedKey = normalizeHeaderName(key);
if (!normalizedKey) return;
const nextValue = String(value);
for (const currentKey of Object.keys(target)) if (normalizeHeaderName(currentKey) === normalizedKey) delete target[currentKey];
target[key] = nextValue;
}
function hasHeader(target, key) {
const normalizedKey = normalizeHeaderName(key);
return Object.keys(target).some((currentKey) => normalizeHeaderName(currentKey) === normalizedKey);
}
function mergeHeaderSource(target, source) {
if (!source) return;
if (typeof source.forEach === "function") {
source.forEach((value, key) => {
setHeader(target, key, value);
});
return;
}
if (typeof source[Symbol.iterator] === "function") {
for (const entry of source) {
if (!entry || entry.length < 2) continue;
setHeader(target, entry[0], entry[1]);
}
return;
}
if (!isObject(source)) return;
for (const [key, value] of Object.entries(source)) {
if (Array.isArray(value)) {
setHeader(target, key, value.join(", "));
continue;
}
setHeader(target, key, value);
}
}
function toHeaderMap(source) {
const headers = {};
mergeHeaderSource(headers, source);
return headers;
}
function isRequestLikeInput(input) {
return isObject(input) && typeof input.url === "string";
}
async function extractRequestBodyFromInput(input) {
if (!input || typeof input.clone !== "function") return;
if (input.bodyUsed) throw new TypeError("Failed to execute fetch: request body is already used");
const cloned = input.clone();
if (cloned?.arrayBuffer) return cloned.arrayBuffer();
if (cloned?.text) return cloned.text();
}
async function normalizeRequestBody(body, headers) {
if (body == null) return;
if (typeof body === "string") {
if (!hasHeader(headers, "content-type")) headers["content-type"] = "text/plain;charset=UTF-8";
return body;
}
if (isUrlSearchParamsInstance(body)) {
if (!hasHeader(headers, "content-type")) headers["content-type"] = "application/x-www-form-urlencoded;charset=UTF-8";
return body.toString();
}
if (body instanceof ArrayBuffer) return cloneArrayBuffer(body);
if (ArrayBuffer.isView(body)) return cloneArrayBufferView(body);
if (typeof Blob !== "undefined" && body instanceof Blob) {
if (body.type && !hasHeader(headers, "content-type")) headers["content-type"] = body.type;
return body.arrayBuffer();
}
if (typeof FormData !== "undefined" && body instanceof FormData) throw new TypeError("Failed to execute fetch: FormData body is not supported in request globals fetch");
return String(body);
}
async function resolveRequestMeta(input, init = {}) {
const requestInput = isRequestLikeInput(input) ? input : void 0;
const url = typeof input === "string" ? input : isUrlInstance(input) ? input.toString() : requestInput?.url;
if (!url) throw new TypeError("Failed to execute fetch: invalid request url");
const method = normalizeMethod(init.method ?? requestInput?.method);
const headers = toHeaderMap(requestInput?.headers);
mergeHeaderSource(headers, init.headers);
const hasBodyInInit = hasOwn.call(init, "body");
let rawBody = hasBodyInInit ? init.body : await extractRequestBodyFromInput(requestInput);
if (!hasBodyInInit && requestInput && (method === "GET" || method === "HEAD")) rawBody = void 0;
if ((method === "GET" || method === "HEAD") && rawBody != null) throw new TypeError("Failed to execute fetch: GET/HEAD request cannot have body");
return {
miniProgram: resolveRequestMiniProgramOptions(init.miniProgram, init.miniprogram),
url,
method,
headers,
body: await normalizeRequestBody(rawBody, headers),
signal: init.signal ?? requestInput?.signal ?? null
};
}
function createFetchResponse(data, status, headers, url) {
return new ResponsePolyfill(data instanceof Uint8Array ? cloneArrayBufferView(data) : data, {
status,
headers: new HeadersPolyfill(headers),
url
});
}
function isRequestTask(value) {
return isObject(value) && typeof value.abort === "function";
}
/**
* @description 使用 @wevu/api 的 request 能力实现 fetch 语义对齐。
*/
function fetch(input, init) {
return resolveRequestMeta(input, init).then((meta) => {
if (meta.signal?.aborted) return Promise.reject(createAbortError());
return new Promise((resolve, reject) => {
let settled = false;
let aborted = false;
let requestTask;
function onAbort() {
if (settled) return;
aborted = true;
requestTask?.abort();
settled = true;
if (meta.signal) meta.signal.removeEventListener("abort", onAbort);
reject(createAbortError());
}
function cleanup() {
if (meta.signal) meta.signal.removeEventListener("abort", onAbort);
}
if (meta.signal) meta.signal.addEventListener("abort", onAbort, { once: true });
const requestResult = wpi.request({
...meta.miniProgram,
url: meta.url,
method: meta.method,
header: meta.headers,
data: meta.body,
responseType: "arraybuffer",
success: (res) => {
if (settled) return;
settled = true;
cleanup();
resolve(createFetchResponse(res.data, res.statusCode, toHeaderMap(res.header), meta.url));
},
fail: (error) => {
if (settled) return;
settled = true;
cleanup();
if (aborted) {
reject(createAbortError());
return;
}
const message = isObject(error) && typeof error.errMsg === "string" ? error.errMsg : String(error);
reject(new TypeError(message));
}
});
requestTask = isRequestTask(requestResult) ? requestResult : void 0;
});
});
}
//#endregion
export { fetch };