import hash from 'object-hash' export function isAbsoluteURL(url: string) { // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL). // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed // by any combination of letters, digits, plus, period, or hyphen. return /^([a-z][a-z\d+-.]*:)?\/\//i.test(url) } export function combineURLs(baseURL: string, relativeURL?: string) { return relativeURL ? `${baseURL.replace(/\/+$/, '')}/${relativeURL.replace(/^\/+/, '')}` : baseURL } export function buildFullPath(baseURL: string, requestedURL: string): string { if (baseURL && !isAbsoluteURL(requestedURL)) { return combineURLs(baseURL, requestedURL) } return requestedURL } type DeepCopy = T extends object ? T extends Array ? DeepCopy[] : { [K in keyof T]: T[K] extends object ? DeepCopy : T[K] } : T export function deepCopy(obj: T): DeepCopy { let target: any if (typeof obj === 'object' && obj !== null) { if (Array.isArray(obj)) { target = [] obj.forEach((elem) => target.push(deepCopy(elem))) } else { target = {} Object.keys(obj).forEach((key) => { target[key] = deepCopy((obj as Record)[key]) }) } } else { target = obj } return target } export function getType(obj: any): string { const type = typeof obj if (type !== 'object') return type return Object.prototype.toString .call(obj) .replace(/^\[object\s+(\S+)\]$/, '$1') .toLowerCase() } type NormalizedConfig = { params?: Record data?: Record url?: string baseURL?: string method?: string } /** * 获取请求的标识,相同的请求配置有相同的标识 * @param config 经过标准化的ajax请求配置 * @returns 该请求的hash key */ export function generateRequestHash(config: NormalizedConfig) { const { url = '', method = '', params = {}, data = {}, baseURL = '' } = config // object-hash 在小程序环境下处理 emoji 会有问题 return hash({ params: encodeURIComponent(JSON.stringify(toObj(params))), data: encodeURIComponent(JSON.stringify(toObj(data))), baseURL, url, method, }) } // 小程序环境下没有 FormData 和 File const hasFormData = !!globalThis.FormData; const hasFile = !!globalThis.File; function toObj(obj: Record | null | undefined | FormData) { const encoded: Record = {} if (obj === null || obj === undefined) return encoded if (hasFormData && obj instanceof FormData) { obj.forEach((value, key) => { if (!encoded[key]) encoded[key] = [] if (hasFile && value instanceof File) { encoded[key].push(`[Blob:${value.type}:${value.size}:${value.name}:${value.lastModified}]`) } else { encoded[key].push(value) } }) } else if (typeof obj === 'object') { return obj } return encoded }