@wevu/web-apis
Version:
Web API polyfills and global installers for mini-program runtimes
396 lines (395 loc) • 16.6 kB
JavaScript
import { a as decodeTextFallback, c as installRequestGlobalBinding, d as resolveRequestGlobalsHosts, g as getMiniProgramRuntimeGlobalKeys, s as encodeTextFallback, t as RequestGlobalsEventTarget, u as resolveRequestGlobalsHost } from "./shared-pgiOoBm7.mjs";
import { URLPolyfill, URLSearchParamsPolyfill } from "./url.mjs";
import { AbortControllerPolyfill, AbortSignalPolyfill } from "./abort.mjs";
import { HeadersPolyfill, RequestPolyfill, ResponsePolyfill } from "./http.mjs";
import { getMiniProgramNetworkDefaults, resetMiniProgramNetworkDefaults, setMiniProgramNetworkDefaults } from "./networkDefaults.mjs";
import { fetch } from "./fetch.mjs";
import { BlobPolyfill, FormDataPolyfill } from "./web.mjs";
import { WebSocketPolyfill } from "./websocket.mjs";
import { XMLHttpRequestPolyfill } from "./xhr.mjs";
import { REQUEST_GLOBAL_ACTUALS_KEY, REQUEST_GLOBAL_PLACEHOLDER_KEY } from "@weapp-core/constants";
//#region src/base64.ts
const BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const BASE64_LOOKUP = new Uint8Array(256);
const BASE64_INVALID_INPUT_RE = /[^A-Za-z\d+/=]/u;
for (let index = 0; index < BASE64_LOOKUP.length; index++) BASE64_LOOKUP[index] = 255;
for (let index = 0; index < 64; index++) BASE64_LOOKUP[BASE64_CHARS.charCodeAt(index)] = index;
function toLatin1Byte(value, index) {
const codePoint = value.charCodeAt(index);
if (codePoint > 255) throw new TypeError(`Failed to execute 'btoa': character outside Latin1 range at index ${index}`);
return codePoint;
}
function btoaPolyfill(input = "") {
const source = String(input);
let output = "";
for (let index = 0; index < source.length; index += 3) {
const byte0 = toLatin1Byte(source, index);
const byte1 = index + 1 < source.length ? toLatin1Byte(source, index + 1) : NaN;
const byte2 = index + 2 < source.length ? toLatin1Byte(source, index + 2) : NaN;
const triplet = byte0 << 16 | (Number.isNaN(byte1) ? 0 : byte1) << 8 | (Number.isNaN(byte2) ? 0 : byte2);
output += BASE64_CHARS[triplet >> 18 & 63];
output += BASE64_CHARS[triplet >> 12 & 63];
output += Number.isNaN(byte1) ? "=" : BASE64_CHARS[triplet >> 6 & 63];
output += Number.isNaN(byte2) ? "=" : BASE64_CHARS[triplet & 63];
}
return output;
}
function atobPolyfill(input = "") {
const source = String(input).replace(/[\t\n\f\r ]+/g, "");
if (source.length % 4 === 1 || BASE64_INVALID_INPUT_RE.test(source)) throw new TypeError("Failed to execute 'atob': the input is not correctly encoded");
let output = "";
for (let index = 0; index < source.length; index += 4) {
const char0 = source.charCodeAt(index);
const char1 = source.charCodeAt(index + 1);
const char2 = source.charAt(index + 2);
const char3 = source.charAt(index + 3);
const sextet0 = BASE64_LOOKUP[char0] ?? 255;
const sextet1 = BASE64_LOOKUP[char1] ?? 255;
const sextet2 = char2 === "=" ? 64 : BASE64_LOOKUP[source.charCodeAt(index + 2)] ?? 255;
const sextet3 = char3 === "=" ? 64 : BASE64_LOOKUP[source.charCodeAt(index + 3)] ?? 255;
if (sextet0 > 63 || sextet1 > 63 || sextet2 > 64 || sextet3 > 64) throw new TypeError("Failed to execute 'atob': the input is not correctly encoded");
const triplet = sextet0 << 18 | sextet1 << 12 | (sextet2 & 63) << 6 | sextet3 & 63;
output += String.fromCharCode(triplet >> 16 & 255);
if (char2 !== "=") output += String.fromCharCode(triplet >> 8 & 255);
if (char3 !== "=") output += String.fromCharCode(triplet & 255);
}
return output;
}
//#endregion
//#region src/crypto.ts
const MAX_RANDOM_VALUES_BYTE_LENGTH = 65536;
function isIntegerTypedArray(value) {
return value instanceof Int8Array || value instanceof Uint8Array || value instanceof Uint8ClampedArray || value instanceof Int16Array || value instanceof Uint16Array || value instanceof Int32Array || value instanceof Uint32Array || value instanceof BigInt64Array || value instanceof BigUint64Array;
}
function getNativeCrypto() {
const candidate = globalThis.crypto;
if (candidate && typeof candidate.getRandomValues === "function") return candidate;
}
function fillArrayWithMathRandom(target) {
const bytes = new Uint8Array(target.buffer, target.byteOffset, target.byteLength);
for (let index = 0; index < bytes.length; index++) bytes[index] = Math.floor(Math.random() * 256);
}
function fillArrayWithHostRandomValues(target) {
for (const hostKey of getMiniProgramRuntimeGlobalKeys()) {
const host = globalThis[hostKey];
if (!host || typeof host.getRandomValues !== "function") continue;
try {
host.getRandomValues(target);
return true;
} catch {}
}
return false;
}
function getRandomValuesPolyfill(typedArray) {
if (!isIntegerTypedArray(typedArray)) throw new TypeError("Failed to execute 'getRandomValues': input must be an integer TypedArray");
if (typedArray.byteLength > MAX_RANDOM_VALUES_BYTE_LENGTH) throw new TypeError("Failed to execute 'getRandomValues': byteLength exceeds 65536");
const nativeCrypto = getNativeCrypto();
if (nativeCrypto && nativeCrypto.getRandomValues !== getRandomValuesPolyfill) return nativeCrypto.getRandomValues(typedArray);
if (!fillArrayWithHostRandomValues(typedArray)) fillArrayWithMathRandom(typedArray);
return typedArray;
}
const cryptoPolyfill = { getRandomValues: getRandomValuesPolyfill };
//#endregion
//#region src/events.ts
var EventPolyfill = class {
bubbles;
cancelable;
composed;
isTrusted = false;
timeStamp = Date.now();
type;
currentTarget = null;
target = null;
defaultPrevented = false;
cancelBubble = false;
constructor(type, init = {}) {
this.type = String(type);
this.bubbles = init.bubbles === true;
this.cancelable = init.cancelable === true;
this.composed = init.composed === true;
}
composedPath() {
return this.target == null ? [] : [this.target];
}
preventDefault() {
if (this.cancelable) this.defaultPrevented = true;
}
stopImmediatePropagation() {
this.cancelBubble = true;
}
stopPropagation() {
this.cancelBubble = true;
}
};
var CustomEventPolyfill = class extends EventPolyfill {
detail;
constructor(type, init = {}) {
super(type, init);
this.detail = init.detail ?? null;
}
};
//#endregion
//#region src/performance.ts
const performanceTimeOrigin = Date.now();
const PERFORMANCE_POLYFILL_MARKER = "__weappVitePerformancePolyfill";
function resolveNativePerformance() {
const candidate = globalThis.performance;
if (candidate && candidate?.[PERFORMANCE_POLYFILL_MARKER] !== true && typeof candidate.now === "function") return candidate;
for (const hostKey of getMiniProgramRuntimeGlobalKeys()) {
const host = globalThis[hostKey];
if (!host || typeof host.getPerformance !== "function") continue;
try {
const performance = host.getPerformance();
if (performance && typeof performance.now === "function") return performance;
} catch {}
}
}
const performancePolyfill = {
[PERFORMANCE_POLYFILL_MARKER]: true,
now() {
const nativePerformance = resolveNativePerformance();
if (nativePerformance && nativePerformance !== performancePolyfill) try {
return Number(nativePerformance.now());
} catch {}
return Date.now() - performanceTimeOrigin;
},
timeOrigin: performanceTimeOrigin
};
//#endregion
//#region src/task.ts
function queueMicrotaskPolyfill(callback) {
if (typeof callback !== "function") throw new TypeError("Failed to execute 'queueMicrotask': callback must be a function");
const nativeQueueMicrotask = globalThis.queueMicrotask;
if (typeof nativeQueueMicrotask === "function" && nativeQueueMicrotask !== queueMicrotaskPolyfill) {
nativeQueueMicrotask(callback);
return;
}
Promise.resolve().then(callback).catch((error) => {
setTimeout(() => {
throw error;
}, 0);
});
}
//#endregion
//#region src/textCodec.ts
function normalizeTextDecoderInput(input) {
if (input == null) return /* @__PURE__ */ new ArrayBuffer(0);
if (input instanceof ArrayBuffer) return input.slice(0);
if (ArrayBuffer.isView(input)) {
const copied = new Uint8Array(input.byteLength);
copied.set(new Uint8Array(input.buffer, input.byteOffset, input.byteLength));
return copied.buffer;
}
return /* @__PURE__ */ new ArrayBuffer(0);
}
var TextEncoderPolyfill = class {
encoding = "utf-8";
encode(input = "") {
return new Uint8Array(encodeTextFallback(String(input)));
}
};
var TextDecoderPolyfill = class {
encoding = "utf-8";
fatal = false;
ignoreBOM = false;
decode(input) {
return decodeTextFallback(normalizeTextDecoderInput(input));
}
};
//#endregion
//#region src/index.ts
function hasRequestRuntimeBinaryUsage(targets) {
return targets.some((target) => target === "fetch" || target === "Request" || target === "Response" || target === "XMLHttpRequest" || target === "WebSocket");
}
function resolveInstallTargets(targets) {
const installTargets = [...targets];
if (hasRequestRuntimeBinaryUsage(targets)) installTargets.push("TextEncoder", "TextDecoder");
if (targets.includes("CustomEvent")) installTargets.push("Event");
return [...new Set(installTargets)];
}
function resolveActualBindingTargets(targets) {
const bindingTargets = [...targets];
if (hasRequestRuntimeBinaryUsage(targets)) {
bindingTargets.push("TextEncoder", "TextDecoder");
bindingTargets.push("URL", "URLSearchParams", "Blob", "FormData");
}
return [...new Set(bindingTargets)];
}
function isPlaceholderRequestGlobal(value) {
return Boolean(value && typeof value === "function" && value[REQUEST_GLOBAL_PLACEHOLDER_KEY] === true);
}
function hasUsableConstructor(value, args = []) {
if (typeof value !== "function" || isPlaceholderRequestGlobal(value)) return false;
try {
Reflect.construct(value, args);
return true;
} catch {
return false;
}
}
function assignHostGlobal(host, key, value) {
try {
host[key] = value;
return true;
} catch {
return false;
}
}
function installSingleTarget(host, target) {
if (target === "fetch") {
if (typeof host.fetch !== "function" || isPlaceholderRequestGlobal(host.fetch)) assignHostGlobal(host, "fetch", fetch);
return;
}
if (target === "Headers") {
if (typeof host.Headers !== "function" || isPlaceholderRequestGlobal(host.Headers)) assignHostGlobal(host, "Headers", HeadersPolyfill);
return;
}
if (target === "Request") {
if (typeof host.Request !== "function" || isPlaceholderRequestGlobal(host.Request)) assignHostGlobal(host, "Request", RequestPolyfill);
return;
}
if (target === "Response") {
if (typeof host.Response !== "function" || isPlaceholderRequestGlobal(host.Response)) assignHostGlobal(host, "Response", ResponsePolyfill);
return;
}
if (target === "TextEncoder") {
if (typeof host.TextEncoder !== "function" || isPlaceholderRequestGlobal(host.TextEncoder)) assignHostGlobal(host, "TextEncoder", TextEncoderPolyfill);
return;
}
if (target === "TextDecoder") {
if (typeof host.TextDecoder !== "function" || isPlaceholderRequestGlobal(host.TextDecoder)) assignHostGlobal(host, "TextDecoder", TextDecoderPolyfill);
return;
}
if (target === "AbortController") {
if (typeof host.AbortController !== "function" || isPlaceholderRequestGlobal(host.AbortController)) assignHostGlobal(host, "AbortController", AbortControllerPolyfill);
return;
}
if (target === "AbortSignal") {
if (typeof host.AbortSignal !== "function" || isPlaceholderRequestGlobal(host.AbortSignal)) assignHostGlobal(host, "AbortSignal", AbortSignalPolyfill);
return;
}
if (target === "WebSocket") {
if (typeof host.WebSocket !== "function" || isPlaceholderRequestGlobal(host.WebSocket)) assignHostGlobal(host, "WebSocket", WebSocketPolyfill);
return;
}
if (target === "atob") {
if (typeof host.atob !== "function" || isPlaceholderRequestGlobal(host.atob)) assignHostGlobal(host, "atob", atobPolyfill);
return;
}
if (target === "btoa") {
if (typeof host.btoa !== "function" || isPlaceholderRequestGlobal(host.btoa)) assignHostGlobal(host, "btoa", btoaPolyfill);
return;
}
if (target === "queueMicrotask") {
if (typeof host.queueMicrotask !== "function" || isPlaceholderRequestGlobal(host.queueMicrotask)) assignHostGlobal(host, "queueMicrotask", queueMicrotaskPolyfill);
return;
}
if (target === "performance") {
if (!host.performance || typeof host.performance.now !== "function") assignHostGlobal(host, "performance", performancePolyfill);
return;
}
if (target === "crypto") {
if (!host.crypto || typeof host.crypto.getRandomValues !== "function") assignHostGlobal(host, "crypto", cryptoPolyfill);
return;
}
if (target === "Event") {
if (typeof host.Event !== "function" || isPlaceholderRequestGlobal(host.Event)) assignHostGlobal(host, "Event", EventPolyfill);
return;
}
if (target === "CustomEvent") {
if (typeof host.CustomEvent !== "function" || isPlaceholderRequestGlobal(host.CustomEvent)) assignHostGlobal(host, "CustomEvent", CustomEventPolyfill);
return;
}
if (target === "XMLHttpRequest" && (typeof host.XMLHttpRequest !== "function" || isPlaceholderRequestGlobal(host.XMLHttpRequest))) assignHostGlobal(host, "XMLHttpRequest", XMLHttpRequestPolyfill);
}
function installUrlGlobals(host) {
if (!hasUsableConstructor(host.URL, ["https://request-globals.invalid"])) assignHostGlobal(host, "URL", URLPolyfill);
if (!hasUsableConstructor(host.URLSearchParams, ["client=graphql-request"])) assignHostGlobal(host, "URLSearchParams", URLSearchParamsPolyfill);
if (!hasUsableConstructor(host.Blob)) assignHostGlobal(host, "Blob", BlobPolyfill);
if (!hasUsableConstructor(host.FormData)) assignHostGlobal(host, "FormData", FormDataPolyfill);
}
function installGlobalBindingIfNeeded(host, target) {
const value = host[target];
if (value == null) return;
installRequestGlobalBinding(target, value);
}
function ensureRuntimeHostAliases(host) {
for (const alias of [
"global",
"self",
"window"
]) {
try {
host[alias] = host;
} catch {}
installRequestGlobalBinding(alias, host);
}
}
function syncWeappViteRequestGlobalsActuals(host, targets) {
const globalObject = host != null && (typeof host === "object" || typeof host === "function") ? host : resolveRequestGlobalsHost();
let actuals = globalObject[REQUEST_GLOBAL_ACTUALS_KEY];
if (!actuals || typeof actuals !== "object") {
actuals = Object.create(null);
assignHostGlobal(globalObject, REQUEST_GLOBAL_ACTUALS_KEY, actuals);
}
for (const target of resolveActualBindingTargets(targets)) {
const value = host[target];
if (value != null) actuals[target] = value;
}
}
/**
* @description 按需向小程序全局环境注入缺失的 Web Runtime 对象。
*/
function installWebRuntimeGlobals(options = {}) {
if (options.networkDefaults !== void 0) setMiniProgramNetworkDefaults(options.networkDefaults);
const targets = resolveInstallTargets(options.targets ?? [
"fetch",
"Headers",
"Request",
"Response",
"TextEncoder",
"TextDecoder",
"AbortController",
"AbortSignal",
"XMLHttpRequest",
"WebSocket",
"atob",
"btoa",
"queueMicrotask",
"performance",
"crypto",
"Event",
"CustomEvent"
]);
const hosts = resolveRequestGlobalsHosts();
const primaryHost = resolveRequestGlobalsHost();
const needsUrlGlobals = hasRequestRuntimeBinaryUsage(targets);
ensureRuntimeHostAliases(primaryHost);
for (const host of hosts) {
if (needsUrlGlobals) installUrlGlobals(host);
for (const target of targets) installSingleTarget(host, target);
}
if (needsUrlGlobals) {
installGlobalBindingIfNeeded(primaryHost, "URL");
installGlobalBindingIfNeeded(primaryHost, "URLSearchParams");
installGlobalBindingIfNeeded(primaryHost, "Blob");
installGlobalBindingIfNeeded(primaryHost, "FormData");
}
for (const target of targets) installGlobalBindingIfNeeded(primaryHost, target);
syncWeappViteRequestGlobalsActuals(primaryHost, targets);
return hosts[0];
}
/**
* @description 已废弃,请迁移到 `installWebRuntimeGlobals`。
*/
function installRequestGlobals(options = {}) {
return installWebRuntimeGlobals(options);
}
/**
* @description 仅安装 AbortController 与 AbortSignal 兼容层。
*/
function installAbortGlobals() {
return installWebRuntimeGlobals({ targets: ["AbortController", "AbortSignal"] });
}
//#endregion
export { AbortControllerPolyfill, AbortSignalPolyfill, BlobPolyfill, CustomEventPolyfill, EventPolyfill, FormDataPolyfill, HeadersPolyfill, RequestGlobalsEventTarget, RequestPolyfill, ResponsePolyfill, TextDecoderPolyfill, TextEncoderPolyfill, URLPolyfill, URLSearchParamsPolyfill, WebSocketPolyfill, XMLHttpRequestPolyfill, atobPolyfill, btoaPolyfill, cryptoPolyfill, fetch, getMiniProgramNetworkDefaults, installAbortGlobals, installRequestGlobals, installWebRuntimeGlobals, performancePolyfill, queueMicrotaskPolyfill, resetMiniProgramNetworkDefaults, setMiniProgramNetworkDefaults };