From 788534b9810f8cf6400ccb5f48a276183ffb27c5 Mon Sep 17 00:00:00 2001 From: richarjiang Date: Sun, 7 Dec 2025 14:41:55 +0800 Subject: [PATCH] =?UTF-8?q?refactor(expo-updates):=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E8=B5=84=E4=BA=A7=E5=85=83=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84?= =?UTF-8?q?=EF=BC=8C=E6=94=AF=E6=8C=81=E5=AE=8C=E6=95=B4=E7=9A=84hash?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=E5=92=8C=E5=AE=8C=E6=95=B4=E6=80=A7=E6=A3=80?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/expo-updates/expo-updates.controller.ts | 7 +- src/expo-updates/expo-updates.service.ts | 79 ++++++++++++++++----- src/users/users.service.ts | 2 +- yarn.lock | 48 ++++++------- 4 files changed, 92 insertions(+), 44 deletions(-) diff --git a/src/expo-updates/expo-updates.controller.ts b/src/expo-updates/expo-updates.controller.ts index 04ef515..80e1d46 100644 --- a/src/expo-updates/expo-updates.controller.ts +++ b/src/expo-updates/expo-updates.controller.ts @@ -62,7 +62,7 @@ export class ExpoUpdatesController { // 使用 form-data 构建正确的 multipart 响应 const form = new FormData(); - + form.append('manifest', JSON.stringify(manifest), { contentType: 'application/json', header: { @@ -70,7 +70,10 @@ export class ExpoUpdatesController { }, }); - form.append('extensions', JSON.stringify({ assetRequestHeaders: {} }), { + // protocol v1 recommends including integrity headers for every asset request + const extensions = this.expoUpdatesService.buildExtensions(manifest); + + form.append('extensions', JSON.stringify(extensions), { contentType: 'application/json', }); diff --git a/src/expo-updates/expo-updates.service.ts b/src/expo-updates/expo-updates.service.ts index 00942b7..7e2034d 100644 --- a/src/expo-updates/expo-updates.service.ts +++ b/src/expo-updates/expo-updates.service.ts @@ -6,10 +6,13 @@ import * as crypto from 'crypto'; export interface AssetMetadata { hash: string; - key: string; + // expo-updates protocol v1 uses fileSHA256 for integrity checks; keep hash for backward compatibility. + fileSHA256: string; + key: string; // md5 hash of file contents contentType: string; fileExtension?: string; url: string; + metadata?: Record; } export interface UpdateManifest { @@ -23,6 +26,9 @@ export interface UpdateManifest { expoClient?: Record; }; } +export interface ManifestExtensions { + assetRequestHeaders: Record>; +} export interface NoUpdateAvailableDirective { type: 'noUpdateAvailable'; @@ -31,6 +37,7 @@ export interface NoUpdateAvailableDirective { interface MetadataFileAsset { path: string; ext: string; + metadata?: Record; } interface MetadataFile { @@ -56,9 +63,14 @@ interface MetadataCache { // 缓存 hash 数据 interface HashCache { - hash: string; + sha256: string; + md5: string; timestamp: number; } +interface FileHashes { + sha256: string; + md5: string; +} @Injectable() export class ExpoUpdatesService { @@ -108,23 +120,25 @@ export class ExpoUpdatesService { // 构建 bundle URL 并计算真实 hash const bundleUrl = baseUrl + platformMetadata.bundle; - const bundleHash = await this.calculateFileHash(bundleUrl); + const bundleHashes = await this.calculateFileHashes(bundleUrl); // 构建 assets(需要计算每个文件的真实 hash) const assets = await this.buildAssetsWithHash(platformMetadata.assets, baseUrl); // ID 基于 bundle hash 生成,确保内容不变时 ID 固定 const updateId = this.configService.get('EXPO_UPDATE_ID') - || this.convertSHA256HashToUUID(bundleHash); + || this.convertSHA256HashToUUID(bundleHashes.sha256); return { id: updateId, createdAt: new Date().toISOString(), runtimeVersion: configRuntimeVersion || runtimeVersion, launchAsset: { - hash: bundleHash, - key: 'bundle', + hash: bundleHashes.sha256, + fileSHA256: bundleHashes.sha256, + key: bundleHashes.md5, contentType: 'application/javascript', + fileExtension: '.bundle', url: bundleUrl, }, assets, @@ -162,14 +176,14 @@ export class ExpoUpdatesService { } /** - * 计算文件的 SHA-256 hash(Base64URL 编码) + * 计算文件的 hash(sha256 base64url + md5 hex) */ - private async calculateFileHash(url: string): Promise { + private async calculateFileHashes(url: string): Promise { // 检查缓存 const cacheKey = `hash:${url}`; const cached = this.hashCache.get(cacheKey); if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) { - return cached.hash; + return { sha256: cached.sha256, md5: cached.md5 }; } try { @@ -177,13 +191,15 @@ export class ExpoUpdatesService { responseType: 'arraybuffer', timeout: 30000, }); - const hash = crypto.createHash('sha256').update(Buffer.from(response.data)).digest('base64url'); + const buffer = Buffer.from(response.data); + const sha256 = crypto.createHash('sha256').update(buffer).digest('base64url'); + const md5 = crypto.createHash('md5').update(buffer).digest('hex'); // 缓存 hash - this.hashCache.set(cacheKey, { hash, timestamp: Date.now() }); + this.hashCache.set(cacheKey, { sha256, md5, timestamp: Date.now() }); - logger.debug(`Calculated hash for ${url}: ${hash}`); - return hash; + logger.debug(`Calculated hashes for ${url}: sha256=${sha256}, md5=${md5}`); + return { sha256, md5 }; } catch (error) { logger.error(`Failed to calculate hash for ${url}: ${error.message}`); throw new BadRequestException(`Failed to fetch asset: ${url}`); @@ -214,15 +230,16 @@ export class ExpoUpdatesService { const batchResults = await Promise.all( batch.map(async (asset) => { const url = baseUrl + asset.path; - const key = asset.path.split('/').pop() || ''; // 使用文件名作为 key - const hash = await this.calculateFileHash(url); + const { sha256, md5 } = await this.calculateFileHashes(url); return { - hash, - key, + hash: sha256, + fileSHA256: sha256, + key: md5, contentType: this.getContentType(asset.ext), fileExtension: `.${asset.ext}`, url, + metadata: asset.metadata || {}, }; }) ); @@ -260,6 +277,24 @@ export class ExpoUpdatesService { return { type: 'noUpdateAvailable' }; } + /** + * 构建 extensions(主要提供每个资源的 integrity header) + * 参考 expo-updates v1 协议,将 sha256 的 base64 形式用于 integrity。 + */ + buildExtensions(manifest: UpdateManifest): ManifestExtensions { + const headers: Record> = {}; + + const allAssets = [manifest.launchAsset, ...manifest.assets]; + for (const asset of allAssets) { + const b64 = this.base64FromBase64Url(asset.fileSHA256); + headers[asset.key] = { + integrity: `sha256-${b64}`, + }; + } + + return { assetRequestHeaders: headers }; + } + /** * 将 SHA-256 hash 转换为 UUID 格式 */ @@ -268,4 +303,14 @@ export class ExpoUpdatesService { const hex = Buffer.from(hash, 'base64url').toString('hex').slice(0, 32); return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`; } + + /** + * base64url -> base64(用于 integrity header) + */ + private base64FromBase64Url(value: string): string { + // Replace URL-safe chars and pad + const base64 = value.replace(/-/g, '+').replace(/_/g, '/'); + const padding = (4 - (base64.length % 4)) % 4; + return base64.padEnd(base64.length + padding, '='); + } } diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 8457e0a..b07aac8 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -2971,7 +2971,7 @@ export class UsersService { private getReleaseNotes(version: string): string { // 这里可以从数据库或配置文件中获取版本发布说明 // 暂时返回示例数据 - return '1. 优化多语言配置\n2. 锻炼通知点击直接查看锻炼详情\n3. 修复已知问题'; + return '1. 支持健康档案管理\n2. 支持每日手绘健康图片生成\n3. 优化体重管理 UI'; } /** diff --git a/yarn.lock b/yarn.lock index 1933c9a..b0a197f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -889,12 +889,12 @@ "@napi-rs/nice-android-arm-eabi@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.0.1.tgz#9a0cba12706ff56500df127d6f4caf28ddb94936" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.0.1.tgz#9a0cba12706ff56500df127d6f4caf28ddb94936" integrity sha512-5qpvOu5IGwDo7MEKVqqyAxF90I6aLj4n07OzpARdgDRfz8UbBztTByBp0RC59r3J1Ij8uzYi6jI7r5Lws7nn6w== "@napi-rs/nice-android-arm64@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.0.1.tgz#32fc32e9649bd759d2a39ad745e95766f6759d2f" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.0.1.tgz#32fc32e9649bd759d2a39ad745e95766f6759d2f" integrity sha512-GqvXL0P8fZ+mQqG1g0o4AO9hJjQaeYG84FRfZaYjyJtZZZcMjXW5TwkL8Y8UApheJgyE13TQ4YNUssQaTgTyvA== "@napi-rs/nice-darwin-arm64@1.0.1": @@ -904,67 +904,67 @@ "@napi-rs/nice-darwin-x64@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.0.1.tgz#f1b1365a8370c6a6957e90085a9b4873d0e6a957" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.0.1.tgz#f1b1365a8370c6a6957e90085a9b4873d0e6a957" integrity sha512-jXnMleYSIR/+TAN/p5u+NkCA7yidgswx5ftqzXdD5wgy/hNR92oerTXHc0jrlBisbd7DpzoaGY4cFD7Sm5GlgQ== "@napi-rs/nice-freebsd-x64@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.0.1.tgz#4280f081efbe0b46c5165fdaea8b286e55a8f89e" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.0.1.tgz#4280f081efbe0b46c5165fdaea8b286e55a8f89e" integrity sha512-j+iJ/ezONXRQsVIB/FJfwjeQXX7A2tf3gEXs4WUGFrJjpe/z2KB7sOv6zpkm08PofF36C9S7wTNuzHZ/Iiccfw== "@napi-rs/nice-linux-arm-gnueabihf@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.0.1.tgz#07aec23a9467ed35eb7602af5e63d42c5d7bd473" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.0.1.tgz#07aec23a9467ed35eb7602af5e63d42c5d7bd473" integrity sha512-G8RgJ8FYXYkkSGQwywAUh84m946UTn6l03/vmEXBYNJxQJcD+I3B3k5jmjFG/OPiU8DfvxutOP8bi+F89MCV7Q== "@napi-rs/nice-linux-arm64-gnu@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.0.1.tgz#038a77134cc6df3c48059d5a5e199d6f50fb9a90" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.0.1.tgz#038a77134cc6df3c48059d5a5e199d6f50fb9a90" integrity sha512-IMDak59/W5JSab1oZvmNbrms3mHqcreaCeClUjwlwDr0m3BoR09ZiN8cKFBzuSlXgRdZ4PNqCYNeGQv7YMTjuA== "@napi-rs/nice-linux-arm64-musl@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.0.1.tgz#715d0906582ba0cff025109f42e5b84ea68c2bcc" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.0.1.tgz#715d0906582ba0cff025109f42e5b84ea68c2bcc" integrity sha512-wG8fa2VKuWM4CfjOjjRX9YLIbysSVV1S3Kgm2Fnc67ap/soHBeYZa6AGMeR5BJAylYRjnoVOzV19Cmkco3QEPw== "@napi-rs/nice-linux-ppc64-gnu@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.0.1.tgz#ac1c8f781c67b0559fa7a1cd4ae3ca2299dc3d06" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.0.1.tgz#ac1c8f781c67b0559fa7a1cd4ae3ca2299dc3d06" integrity sha512-lxQ9WrBf0IlNTCA9oS2jg/iAjQyTI6JHzABV664LLrLA/SIdD+I1i3Mjf7TsnoUbgopBcCuDztVLfJ0q9ubf6Q== "@napi-rs/nice-linux-riscv64-gnu@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.0.1.tgz#b0a430549acfd3920ffd28ce544e2fe17833d263" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.0.1.tgz#b0a430549acfd3920ffd28ce544e2fe17833d263" integrity sha512-3xs69dO8WSWBb13KBVex+yvxmUeEsdWexxibqskzoKaWx9AIqkMbWmE2npkazJoopPKX2ULKd8Fm9veEn0g4Ig== "@napi-rs/nice-linux-s390x-gnu@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.0.1.tgz#5b95caf411ad72a965885217db378c4d09733e97" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.0.1.tgz#5b95caf411ad72a965885217db378c4d09733e97" integrity sha512-lMFI3i9rlW7hgToyAzTaEybQYGbQHDrpRkg+1gJWEpH0PLAQoZ8jiY0IzakLfNWnVda1eTYYlxxFYzW8Rqczkg== "@napi-rs/nice-linux-x64-gnu@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.0.1.tgz#a98cdef517549f8c17a83f0236a69418a90e77b7" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.0.1.tgz#a98cdef517549f8c17a83f0236a69418a90e77b7" integrity sha512-XQAJs7DRN2GpLN6Fb+ZdGFeYZDdGl2Fn3TmFlqEL5JorgWKrQGRUrpGKbgZ25UeZPILuTKJ+OowG2avN8mThBA== "@napi-rs/nice-linux-x64-musl@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.0.1.tgz#5e26843eafa940138aed437c870cca751c8a8957" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.0.1.tgz#5e26843eafa940138aed437c870cca751c8a8957" integrity sha512-/rodHpRSgiI9o1faq9SZOp/o2QkKQg7T+DK0R5AkbnI/YxvAIEHf2cngjYzLMQSQgUhxym+LFr+UGZx4vK4QdQ== "@napi-rs/nice-win32-arm64-msvc@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.0.1.tgz#bd62617d02f04aa30ab1e9081363856715f84cd8" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.0.1.tgz#bd62617d02f04aa30ab1e9081363856715f84cd8" integrity sha512-rEcz9vZymaCB3OqEXoHnp9YViLct8ugF+6uO5McifTedjq4QMQs3DHz35xBEGhH3gJWEsXMUbzazkz5KNM5YUg== "@napi-rs/nice-win32-ia32-msvc@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.0.1.tgz#b8b7aad552a24836027473d9b9f16edaeabecf18" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.0.1.tgz#b8b7aad552a24836027473d9b9f16edaeabecf18" integrity sha512-t7eBAyPUrWL8su3gDxw9xxxqNwZzAqKo0Szv3IjVQd1GpXXVkb6vBBQUuxfIYaXMzZLwlxRQ7uzM2vdUE9ULGw== "@napi-rs/nice-win32-x64-msvc@1.0.1": version "1.0.1" - resolved "https://mirrors.tencent.com/npm/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.0.1.tgz#37d8718b8f722f49067713e9f1e85540c9a3dd09" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.0.1.tgz#37d8718b8f722f49067713e9f1e85540c9a3dd09" integrity sha512-JlF+uDcatt3St2ntBG8H02F1mM45i5SF9W+bIKiReVE6wiy3o16oBP/yxt+RZ+N6LbCImJXJ6bXNO2kn9AXicg== "@napi-rs/nice@^1.0.1": @@ -1271,47 +1271,47 @@ "@swc/core-darwin-x64@1.11.13": version "1.11.13" - resolved "https://mirrors.tencent.com/npm/@swc/core-darwin-x64/-/core-darwin-x64-1.11.13.tgz#9cad870d48ebff805e8946ddcbe3d8312182f70b" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.11.13.tgz#9cad870d48ebff805e8946ddcbe3d8312182f70b" integrity sha512-uSA4UwgsDCIysUPfPS8OrQTH2h9spO7IYFd+1NB6dJlVGUuR6jLKuMBOP1IeLeax4cGHayvkcwSJ3OvxHwgcZQ== "@swc/core-linux-arm-gnueabihf@1.11.13": version "1.11.13" - resolved "https://mirrors.tencent.com/npm/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.13.tgz#51839e5a850bfa300e2c838fee8379e4dba1de78" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.13.tgz#51839e5a850bfa300e2c838fee8379e4dba1de78" integrity sha512-boVtyJzS8g30iQfe8Q46W5QE/cmhKRln/7NMz/5sBP/am2Lce9NL0d05NnFwEWJp1e2AMGHFOdRr3Xg1cDiPKw== "@swc/core-linux-arm64-gnu@1.11.13": version "1.11.13" - resolved "https://mirrors.tencent.com/npm/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.13.tgz#4145f1e504bdfa92604aee883d777bc8c4fba5d7" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.13.tgz#4145f1e504bdfa92604aee883d777bc8c4fba5d7" integrity sha512-+IK0jZ84zHUaKtwpV+T+wT0qIUBnK9v2xXD03vARubKF+eUqCsIvcVHXmLpFuap62dClMrhCiwW10X3RbXNlHw== "@swc/core-linux-arm64-musl@1.11.13": version "1.11.13" - resolved "https://mirrors.tencent.com/npm/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.13.tgz#b1813ae2e99e386ca16fff5af6601ac45ef57c5b" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.13.tgz#b1813ae2e99e386ca16fff5af6601ac45ef57c5b" integrity sha512-+ukuB8RHD5BHPCUjQwuLP98z+VRfu+NkKQVBcLJGgp0/+w7y0IkaxLY/aKmrAS5ofCNEGqKL+AOVyRpX1aw+XA== "@swc/core-linux-x64-gnu@1.11.13": version "1.11.13" - resolved "https://mirrors.tencent.com/npm/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.13.tgz#13b89a0194c4033c01400e9c65d9c21c56a4a6cd" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.13.tgz#13b89a0194c4033c01400e9c65d9c21c56a4a6cd" integrity sha512-q9H3WI3U3dfJ34tdv60zc8oTuWvSd5fOxytyAO9Pc5M82Hic3jjWaf2xBekUg07ubnMZpyfnv+MlD+EbUI3Llw== "@swc/core-linux-x64-musl@1.11.13": version "1.11.13" - resolved "https://mirrors.tencent.com/npm/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.13.tgz#0d0e5aa889dd4da69723e2287c3c1714d9bfd8aa" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.13.tgz#0d0e5aa889dd4da69723e2287c3c1714d9bfd8aa" integrity sha512-9aaZnnq2pLdTbAzTSzy/q8dr7Woy3aYIcQISmw1+Q2/xHJg5y80ZzbWSWKYca/hKonDMjIbGR6dp299I5J0aeA== "@swc/core-win32-arm64-msvc@1.11.13": version "1.11.13" - resolved "https://mirrors.tencent.com/npm/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.13.tgz#ad7281f9467e3de09f52615afe2276a8ef738a9d" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.13.tgz#ad7281f9467e3de09f52615afe2276a8ef738a9d" integrity sha512-n3QZmDewkHANcoHvtwvA6yJbmS4XJf0MBMmwLZoKDZ2dOnC9D/jHiXw7JOohEuzYcpLoL5tgbqmjxa3XNo9Oow== "@swc/core-win32-ia32-msvc@1.11.13": version "1.11.13" - resolved "https://mirrors.tencent.com/npm/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.13.tgz#046f6dbddb5b69a29bbaa98de104090a46088b74" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.13.tgz#046f6dbddb5b69a29bbaa98de104090a46088b74" integrity sha512-wM+Nt4lc6YSJFthCx3W2dz0EwFNf++j0/2TQ0Js9QLJuIxUQAgukhNDVCDdq8TNcT0zuA399ALYbvj5lfIqG6g== "@swc/core-win32-x64-msvc@1.11.13": version "1.11.13" - resolved "https://mirrors.tencent.com/npm/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.13.tgz#0412620d8594a7d3e482d3e79d9e89d80f9a14c0" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.13.tgz#0412620d8594a7d3e482d3e79d9e89d80f9a14c0" integrity sha512-+X5/uW3s1L5gK7wAo0E27YaAoidJDo51dnfKSfU7gF3mlEUuWH8H1bAy5OTt2mU4eXtfsdUMEVXSwhDlLtQkuA== "@swc/core@^1.10.7":