UNPKG

minigame-std

Version:

Cross-platform standard library for WeChat minigame and web browsers with unified APIs for crypto, fs, fetch, storage, and more.

1 lines 12.2 kB
{"version":3,"file":"fetch.mjs","names":[],"sources":["../src/macros/env.ts","../src/std/fetch/mina_fetch.ts","../src/std/fetch/mod.ts"],"sourcesContent":["/**\n * @internal\n * 小游戏环境宏模块。\n */\n\n/**\n * 小游戏环境宏。\n *\n * 可通过打包工具在 build 时修改,如 esbuild webpack vite 等。\n */\ndeclare const __MINIGAME_STD_MINA__: boolean;\n\n/**\n * 如果在小游戏环境中返回 true,否则返回 false。\n */\nexport const IS_MINA = __MINIGAME_STD_MINA__;\n","/**\n * @internal\n * 小游戏平台的 HTTP 请求实现。\n */\n\nimport { ABORT_ERROR, FetchError, TIMEOUT_ERROR, type FetchResult, type FetchTask } from '@happy-ts/fetch-t';\nimport { Err, Ok, type IOResult } from 'happy-rusty';\nimport { Future } from 'tiny-future';\nimport { createFailedFetchTask, miniGameFailureToError, validateSafeUrl } from '../internal/mod.ts';\nimport type { MinaFetchInit } from './fetch_defines.ts';\n\n/**\n * 发起一个可中断的 ArrayBuffer 类型响应的网络请求。\n * @param url - 请求的 URL 地址。\n * @param init - 请求的初始化配置,指定响应类型为 ArrayBuffer 且请求可中断。\n * @returns 返回一个 ArrayBuffer 类型的 FetchTask。\n */\nexport function minaFetch(url: string, init: MinaFetchInit & {\n responseType: 'arraybuffer';\n}): FetchTask<ArrayBuffer>;\n\n/**\n * 发起一个可中断的 JSON 类型响应的网络请求。\n * @typeParam T - 预期的 JSON 响应数据类型。\n * @param url - 请求的 URL 地址。\n * @param init - 请求的初始化配置,指定响应类型为 JSON 且请求可中断。\n * @returns 返回一个 JSON 类型的 FetchTask。\n */\nexport function minaFetch<T>(url: string, init: MinaFetchInit & {\n responseType: 'json';\n}): FetchTask<T>;\n\n/**\n * 发起一个可中断的文本类型响应的网络请求。\n * @param url - 请求的 URL 地址。\n * @param init - 请求的初始化配置,指定响应类型为文本且请求可中断。\n * @returns 返回一个文本类型的 FetchTask。\n */\nexport function minaFetch(url: string, init?: MinaFetchInit & {\n responseType: 'text';\n}): FetchTask<string>;\n\n/**\n * 发起一个可中断的网络请求,默认返回文本类型响应。\n * @param url - 请求的 URL 地址。\n * @param init - 请求的初始化配置,指定请求可中断。\n * @returns {FetchTask<string>} 返回一个文本类型的 FetchTask。\n */\nexport function minaFetch(url: string, init: MinaFetchInit): FetchTask<string>;\n\n/**\n * 发起一个网络请求,根据初始化配置返回对应类型的 FetchTask。\n * @typeParam T - 预期的响应数据类型。\n * @param url - 请求的 URL 地址。\n * @param init - 请求的初始化配置。\n * @returns 根据配置返回 FetchTask。\n */\nexport function minaFetch<T>(url: string, init?: MinaFetchInit): FetchTask<T> {\n const urlRes = validateSafeUrl(url);\n if (urlRes.isErr()) return createFailedFetchTask(urlRes);\n\n const {\n responseType,\n onChunk,\n ...rest\n } = init ?? {};\n\n let aborted = false;\n\n const future = new Future<IOResult<T>>();\n\n const options: WechatMinigame.RequestOption = {\n ...rest,\n url,\n success(res) {\n const { statusCode } = res;\n\n if (statusCode >= 200 && statusCode < 300) {\n future.resolve(Ok(res.data as T));\n } else {\n future.resolve(Err(new FetchError(res.errMsg, statusCode)));\n }\n },\n fail(err) {\n const error = miniGameFailureToError(err);\n const { errMsg } = err;\n\n if (errMsg.includes('abort')) {\n error.name = ABORT_ERROR;\n } else if (errMsg.includes('timeout')) {\n error.name = TIMEOUT_ERROR;\n }\n\n future.resolve(Err(error));\n },\n };\n\n if (responseType === 'arraybuffer') {\n options.responseType = responseType;\n } else if (responseType === 'json') {\n options.dataType = responseType;\n } else {\n // 默认 responseType 是 text\n options.responseType = responseType;\n // responseType设置为text还不够,否则返回类型还是json\n options.dataType = '其他';\n }\n\n const task = wx.request(options);\n\n if (typeof onChunk === 'function') {\n task.onChunkReceived(res => {\n onChunk(new Uint8Array(res.data));\n });\n }\n\n return {\n abort(): void {\n aborted = true;\n task.abort();\n },\n\n get aborted(): boolean {\n return aborted;\n },\n\n get result(): FetchResult<T> {\n return future.promise;\n },\n };\n}","/**\n * 网络请求模块,提供可中断的 fetch 请求功能,支持 text、JSON、ArrayBuffer 等响应类型。\n * @module fetch\n */\nimport { fetchT as webFetch, type FetchInit, type FetchTask } from '@happy-ts/fetch-t';\nimport { IS_MINA } from '../../macros/env.ts';\nimport { bufferSourceToAb } from '../internal/mod.ts';\nimport type { MinaFetchInit, UnionFetchInit } from './fetch_defines.ts';\nimport { minaFetch } from './mina_fetch.ts';\n\nexport { ABORT_ERROR, FetchError, TIMEOUT_ERROR, type FetchTask } from '@happy-ts/fetch-t';\nexport type { UnionFetchInit } from './fetch_defines.ts';\n\n/**\n * 发起一个可中断的文本类型响应的网络请求。\n * @param url - 请求的 URL 地址。\n * @param init - 请求的初始化配置,指定响应类型为文本且请求可中断。\n * @returns 返回一个文本类型的 FetchTask。\n * @since 1.0.0\n * @example\n * ```ts\n * const task = fetchT('https://api.example.com/data', { responseType: 'text' });\n * const result = await task.result;\n * if (result.isOk()) {\n * console.log(result.unwrap()); // 文本内容\n * }\n * // 如需中断请求\n * task.abort();\n * ```\n */\nexport function fetchT(url: string, init: UnionFetchInit & {\n responseType: 'text';\n}): FetchTask<string>;\n\n/**\n * 发起一个可中断的 ArrayBuffer 类型响应的网络请求。\n * @param url - 请求的 URL 地址。\n * @param init - 请求的初始化配置,指定响应类型为 ArrayBuffer 且请求可中断。\n * @returns 返回一个 ArrayBuffer 类型的 FetchTask。\n * @since 1.0.0\n * @example\n * ```ts\n * const task = fetchT('https://api.example.com/file', { responseType: 'arraybuffer' });\n * const result = await task.result;\n * if (result.isOk()) {\n * const buffer = result.unwrap();\n * console.log('文件大小:', buffer.byteLength);\n * }\n * ```\n */\nexport function fetchT(url: string, init: UnionFetchInit & {\n responseType: 'arraybuffer';\n}): FetchTask<ArrayBuffer>;\n\n/**\n * 发起一个可中断的 JSON 类型响应的网络请求。\n * @typeParam T - 预期的 JSON 响应数据类型。\n * @param url - 请求的 URL 地址。\n * @param init - 请求的初始化配置,指定响应类型为 JSON 且请求可中断。\n * @returns 返回一个 JSON 类型的 FetchTask。\n * @since 1.0.0\n * @example\n * ```ts\n * interface User {\n * id: number;\n * name: string;\n * }\n * const task = fetchT<User>('https://api.example.com/user/1', { responseType: 'json' });\n * const result = await task.result;\n * if (result.isOk()) {\n * const user = result.unwrap();\n * console.log(user.name);\n * }\n * ```\n */\nexport function fetchT<T>(url: string, init: UnionFetchInit & {\n responseType: 'json';\n}): FetchTask<T>;\n\n/**\n * 发起一个可中断的网络请求,默认返回文本类型响应。\n * @typeParam T - 预期的响应数据类型。\n * @param url - 请求的 URL 地址。\n * @param init - 请求的初始化配置,指定请求可中断。\n * @returns FetchTask。\n * @since 1.0.0\n * @example\n * ```ts\n * const task = fetchT('https://api.example.com/data');\n * const result = await task.result;\n * if (result.isOk()) {\n * console.log(result.unwrap());\n * }\n * ```\n */\nexport function fetchT(url: string, init?: UnionFetchInit): FetchTask<string | Response>;\n\n/**\n * 发起一个网络请求,根据初始化配置返回对应类型的 FetchTask。\n * @typeParam T - 预期的响应数据类型。\n * @param url - 请求的 URL 地址。\n * @param init - 请求的初始化配置。\n * @returns FetchTask。\n * @since 1.0.0\n * @example\n * ```ts\n * // 发起 POST 请求\n * const task = fetchT('https://api.example.com/submit', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ key: 'value' }),\n * responseType: 'json',\n * });\n * const result = await task.result;\n * ```\n */\nexport function fetchT<T>(url: string, init?: UnionFetchInit): FetchTask<T> {\n const defaultInit = init ?? {};\n // 默认是 text 类型\n defaultInit.responseType ??= 'text';\n\n if (IS_MINA) {\n // Map body → data, headers → header for mini-game\n const { body, headers, ...rest } = defaultInit;\n if (body != null) {\n // wx.request data only accepts string | IAnyObject | ArrayBuffer\n // BufferSource needs conversion to handle potential byteOffset\n (rest as MinaFetchInit).data = typeof body === 'string' || isPlainObject(body)\n ? body\n : bufferSourceToAb(body as BufferSource);\n }\n if (headers !== undefined) {\n (rest as MinaFetchInit).header = headers;\n }\n return minaFetch(url, rest) as FetchTask<T>;\n }\n\n // Auto-serialize object body for web\n const { body, ...rest } = defaultInit;\n const webInit: FetchInit & { abortable: true; } = { ...rest, body: body as BodyInit | null | undefined, abortable: true };\n\n if (isPlainObject(body)) {\n webInit.body = JSON.stringify(body);\n // Object body is always serialized as JSON\n const headers = new Headers(webInit.headers);\n headers.set('Content-Type', 'application/json');\n webInit.headers = headers;\n }\n\n return webFetch(url, webInit) as FetchTask<T>;\n}\n\n/**\n * 判断值是否为普通对象(非 string、非 BufferSource)。\n */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return value != null\n && typeof value === 'object'\n && !ArrayBuffer.isView(value)\n && !(value instanceof ArrayBuffer);\n}\n"],"mappings":";;;;;;;;AAeA,MAAa,UAAU;;;;;;;;;;;;;;AC0CvB,SAAgB,UAAa,KAAa,MAAoC;CAC1E,MAAM,SAAS,gBAAgB,GAAG;CAClC,IAAI,OAAO,MAAM,GAAG,OAAO,sBAAsB,MAAM;CAEvD,MAAM,EACF,cACA,SACA,GAAG,SACH,QAAQ,CAAC;CAEb,IAAI,UAAU;CAEd,MAAM,SAAS,IAAI,OAAoB;CAEvC,MAAM,UAAwC;EAC1C,GAAG;EACH;EACA,QAAQ,KAAK;GACT,MAAM,EAAE,eAAe;GAEvB,IAAI,cAAc,OAAO,aAAa,KAClC,OAAO,QAAQ,GAAG,IAAI,IAAS,CAAC;QAEhC,OAAO,QAAQ,IAAI,IAAI,aAAW,IAAI,QAAQ,UAAU,CAAC,CAAC;EAElE;EACA,KAAK,KAAK;GACN,MAAM,QAAQ,uBAAuB,GAAG;GACxC,MAAM,EAAE,WAAW;GAEnB,IAAI,OAAO,SAAS,OAAO,GACvB,MAAM,OAAO;QACV,IAAI,OAAO,SAAS,SAAS,GAChC,MAAM,OAAO;GAGjB,OAAO,QAAQ,IAAI,KAAK,CAAC;EAC7B;CACJ;CAEA,IAAI,iBAAiB,eACjB,QAAQ,eAAe;MACpB,IAAI,iBAAiB,QACxB,QAAQ,WAAW;MAChB;EAEH,QAAQ,eAAe;EAEvB,QAAQ,WAAW;CACvB;CAEA,MAAM,OAAO,GAAG,QAAQ,OAAO;CAE/B,IAAI,OAAO,YAAY,YACnB,KAAK,iBAAgB,QAAO;EACxB,QAAQ,IAAI,WAAW,IAAI,IAAI,CAAC;CACpC,CAAC;CAGL,OAAO;EACH,QAAc;GACV,UAAU;GACV,KAAK,MAAM;EACf;EAEA,IAAI,UAAmB;GACnB,OAAO;EACX;EAEA,IAAI,SAAyB;GACzB,OAAO,OAAO;EAClB;CACJ;AACJ;;;;;;;;;;;;;;;;;;;;;;;;;;ACdA,SAAgB,OAAU,KAAa,MAAqC;CACxE,MAAM,cAAc,QAAQ,CAAC;CAE7B,YAAY,iBAAiB;CAE7B,IAAI,SAAS;EAET,MAAM,EAAE,MAAM,SAAS,GAAG,SAAS;EACnC,IAAI,QAAQ,MAGR,KAAwB,OAAO,OAAO,SAAS,YAAY,cAAc,IAAI,IACvE,OACA,iBAAiB,IAAoB;EAE/C,IAAI,YAAY,KAAA,GACZ,KAAwB,SAAS;EAErC,OAAO,UAAU,KAAK,IAAI;CAC9B;CAGA,MAAM,EAAE,MAAM,GAAG,SAAS;CAC1B,MAAM,UAA4C;EAAE,GAAG;EAAY;EAAqC,WAAW;CAAK;CAExH,IAAI,cAAc,IAAI,GAAG;EACrB,QAAQ,OAAO,KAAK,UAAU,IAAI;EAElC,MAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;EAC3C,QAAQ,IAAI,gBAAgB,kBAAkB;EAC9C,QAAQ,UAAU;CACtB;CAEA,OAAO,SAAS,KAAK,OAAO;AAChC;;;;AAKA,SAAS,cAAc,OAAkD;CACrE,OAAO,SAAS,QACT,OAAO,UAAU,YACjB,CAAC,YAAY,OAAO,KAAK,KACzB,EAAE,iBAAiB;AAC9B"}