From d9253bf9305dadf7d59bd89ac83f5bd9207e66fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2026 12:47:33 +0000 Subject: [PATCH 01/19] build(deps): bump actions/stale from 10 to 11 Bumps [actions/stale](https://github.com/actions/stale) from 10 to 11. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v10...v11) --- updated-dependencies: - dependency-name: actions/stale dependency-version: '11' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/issue-manage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue-manage.yml b/.github/workflows/issue-manage.yml index 0467fd6..fd983bc 100644 --- a/.github/workflows/issue-manage.yml +++ b/.github/workflows/issue-manage.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Mark stale issues - uses: actions/stale@v10 + uses: actions/stale@v11 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: | From 9a16c897145e81c6a2e9cad22b426effdf0ce4f6 Mon Sep 17 00:00:00 2001 From: cmachsocket Date: Mon, 14 Sep 2026 21:13:16 +0800 Subject: [PATCH 02/19] compatibility fix of rumtime and legacy node --- util/crypto.js | 5 +++-- util/request.js | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/util/crypto.js b/util/crypto.js index 6838fe1..0fa1695 100644 --- a/util/crypto.js +++ b/util/crypto.js @@ -151,7 +151,8 @@ const decrypt = (cipher) => { } const aesEcbEncrypt = (key, plaintext) => { - const cipher = crypto.createCipheriv(`aes-${key.length * 8}-ecb`, key, null) + // 某些运行时不允许使用 null 作为 IV,因此使用 Buffer.alloc(0) ,同时与上下文deriveX25519AesKey等函数保持一致 + const cipher = crypto.createCipheriv(`aes-${key.length * 8}-ecb`, key, Buffer.alloc(0)) return Buffer.concat([cipher.update(Buffer.from(plaintext)), cipher.final()]) } @@ -159,7 +160,7 @@ const aesEcbDecrypt = (key, ciphertext) => { const decipher = crypto.createDecipheriv( `aes-${key.length * 8}-ecb`, key, - null, + Buffer.alloc(0), ) return Buffer.concat([decipher.update(ciphertext), decipher.final()]) } diff --git a/util/request.js b/util/request.js index 55b6d2b..b6c9481 100644 --- a/util/request.js +++ b/util/request.js @@ -440,7 +440,11 @@ const createRequest = async (uri, data, options) => { !cookie.NMTID ) { NMTID_RETRIES_LEFT-- - answer.cookie = setCookies.map((x) => { + answer.cookie = ( + typeof res.headers['set-cookie'] === 'string' // node 20 版本以前,可能解析为String + ? [res.headers['set-cookie']] + : res.headers['set-cookie'] || [] + ).map((x) => { const cleaned = cleanCookie(x) const match = x.match(/(?:^|;\s*)NMTID=([^;]+)/) if (match) { From 77a9269828a3266efe49a300aeed236b05efb31a Mon Sep 17 00:00:00 2001 From: cmachsocket Date: Sat, 19 Sep 2026 21:31:39 +0800 Subject: [PATCH 03/19] fix lint --- util/crypto.js | 6 +++++- util/request.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/util/crypto.js b/util/crypto.js index 0fa1695..39f5feb 100644 --- a/util/crypto.js +++ b/util/crypto.js @@ -152,7 +152,11 @@ const decrypt = (cipher) => { const aesEcbEncrypt = (key, plaintext) => { // 某些运行时不允许使用 null 作为 IV,因此使用 Buffer.alloc(0) ,同时与上下文deriveX25519AesKey等函数保持一致 - const cipher = crypto.createCipheriv(`aes-${key.length * 8}-ecb`, key, Buffer.alloc(0)) + const cipher = crypto.createCipheriv( + `aes-${key.length * 8}-ecb`, + key, + Buffer.alloc(0), + ) return Buffer.concat([cipher.update(Buffer.from(plaintext)), cipher.final()]) } diff --git a/util/request.js b/util/request.js index b6c9481..11960ed 100644 --- a/util/request.js +++ b/util/request.js @@ -441,7 +441,7 @@ const createRequest = async (uri, data, options) => { ) { NMTID_RETRIES_LEFT-- answer.cookie = ( - typeof res.headers['set-cookie'] === 'string' // node 20 版本以前,可能解析为String + typeof res.headers['set-cookie'] === 'string' ? [res.headers['set-cookie']] : res.headers['set-cookie'] || [] ).map((x) => { From c4d448641ab65154a4ef7dfe6e2926e28fda8d2d Mon Sep 17 00:00:00 2001 From: 1254qwer Date: Sun, 27 Sep 2026 14:53:29 +0800 Subject: [PATCH 04/19] feat: Add neapi support --- generateConfig.js | 14 ++ interface.d.ts | 4 + module/register_neapikey.js | 48 +++++++ package.json | 1 + pnpm-lock.yaml | 8 ++ public/api.html | 1 + public/docs/home.md | 2 +- util/config.json | 1 + util/crypto.js | 77 +++++++++++ util/neapiConfig.js | 258 ++++++++++++++++++++++++++++++++++++ util/neapiKey.js | 49 +++++++ util/request.js | 102 +++++++++++++- util/zstd.js | 41 ++++++ 13 files changed, 600 insertions(+), 6 deletions(-) create mode 100644 module/register_neapikey.js create mode 100644 util/neapiConfig.js create mode 100644 util/neapiKey.js create mode 100644 util/zstd.js diff --git a/generateConfig.js b/generateConfig.js index 7640065..a9f4b8e 100644 --- a/generateConfig.js +++ b/generateConfig.js @@ -3,6 +3,8 @@ const path = require('path') const { register_anonimous } = require('./main') const { cookieToJson, generateRandomChineseIP } = require('./util/index') const { getXeapiPublicKey } = require('./util/xeapiKey') +const { loadNeapiKey } = require('./util/neapiKey') +const registerNeapikey = require('./module/register_neapikey') const tmpPath = require('os').tmpdir() async function generateConfig() { @@ -37,5 +39,17 @@ async function generateConfig() { } catch (error) { console.log(error) } + try { + const current = loadNeapiKey() + await registerNeapikey( + { + version: current ? current.version : 0, + behavior: current ? 'update' : 'open', + }, + null, + ) + } catch (error) { + console.log(error) + } } module.exports = generateConfig diff --git a/interface.d.ts b/interface.d.ts index 0952dc8..f81b8e5 100644 --- a/interface.d.ts +++ b/interface.d.ts @@ -1087,6 +1087,10 @@ export function register_checktoken_v3( params: { refresh?: boolean | string } & RequestBaseConfig, ): Promise +export function register_neapikey( + params: { version?: string | number; behavior?: string } & RequestBaseConfig, +): Promise + export function register_xeapikey( params: { deviceId?: string; currentKeyVersion?: string } & RequestBaseConfig, ): Promise diff --git a/module/register_neapikey.js b/module/register_neapikey.js new file mode 100644 index 0000000..42c0afb --- /dev/null +++ b/module/register_neapikey.js @@ -0,0 +1,48 @@ +const { default: axios } = require('axios') +const { APP_CONF } = require('../util/config.json') +const { saveNeapiKey } = require('../util/neapiKey') +const { decodeConfig } = require('../util/neapiConfig') + +const USER_AGENT = + 'NeteaseMusic/9.5.61.260802021928(9005061);Dalvik/2.1.0 (Linux; U; Android 12; HBN-AL00 Build/cd737a2.0)' + +module.exports = async (query, request) => { + const version = Number(query.version) || 0 + const behavior = query.behavior === 'update' ? 'update' : 'open' + + const res = await axios({ + method: 'GET', + url: APP_CONF.apiDomain + '/api/gorilla/algorithm/record', + params: { version, behavior }, + headers: { 'User-Agent': USER_AGENT }, + proxy: false, + }) + + if (!res.data || res.data.code !== 200 || !res.data.data) { + throw new Error('neapi config request failed') + } + + // file 为空表示本地已是最新,无需更新 + const { file, circleTime } = res.data.data + const config = file + ? saveNeapiKey(decodeConfig(Buffer.from(file, 'base64')), circleTime) + : null + + // 只回版本与密钥偏移,密钥本身留在本地缓存 + const keyOffsets = {} + if (config) { + keyOffsets.encrypt = config.keyBlob.indexOf(config.encryptKey) + keyOffsets.decrypt = config.keyBlob.indexOf(config.decryptKey) + } + + return { + status: 200, + body: { + version: config ? config.version : res.data.data.version, + updated: Boolean(config), + circleTime, + keyOffsets, + }, + cookie: [], + } +} diff --git a/package.json b/package.json index 8643f7c..43f2247 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "dotenv": "^17.4.2", "express": "^5.2.1", "express-fileupload": "^1.5.2", + "fzstd": "^0.1.1", "gzip": "^0.1.0", "jsdom": "^24.1.3", "music-metadata": "^11.15.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a9f6e66..f46d131 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,9 @@ importers: express-fileupload: specifier: ^1.5.2 version: 1.5.2 + fzstd: + specifier: ^0.1.1 + version: 0.1.1 gzip: specifier: ^0.1.0 version: 0.1.0 @@ -1265,6 +1268,9 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + fzstd@0.1.1: + resolution: {integrity: sha512-dkuVSOKKwh3eas5VkJy1AW1vFpet8TA/fGmVA5krThl8YcOVE/8ZIoEA1+U1vEn5ckxxhLirSdY837azmbaNHA==} + generator-function@2.0.1: resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} engines: {node: '>= 0.4'} @@ -4226,6 +4232,8 @@ snapshots: functions-have-names@1.2.3: {} + fzstd@0.1.1: {} + generator-function@2.0.1: {} get-caller-file@2.0.5: {} diff --git a/public/api.html b/public/api.html index e62411c..c860761 100644 --- a/public/api.html +++ b/public/api.html @@ -319,6 +319,7 @@ + diff --git a/public/docs/home.md b/public/docs/home.md index 36ad52a..166a836 100644 --- a/public/docs/home.md +++ b/public/docs/home.md @@ -216,7 +216,7 @@ $ sudo docker run -d -p 3000:3000 netease-music-api - 大部分请求参数或返回内容可在 `/api_decrypt.html` 里解析 - 请求参数模式下, 解密结果可直接带到 `/api.html` 继续调试 - 需要返回值加密时, 可传 `e_r=1`, `weapi` 和 `eapi` 都支持 -- 目前支持算法 有 `weapi`, `eapi`, `linuxapi` 和 `xeapi` (xeapi 是一种不加密的特殊算法, 主要用于调试加密前的原始请求参数) +- 目前支持算法 有 `weapi`, `eapi`, `linuxapi`, `xeapi` 和 `neapi` (xeapi 是一种不加密的特殊算法, 主要用于调试加密前的原始请求参数; neapi 走独立的压缩加密通道, 配置由 `/register/neapikey` 自动获取与刷新) ## 接口文档 diff --git a/util/config.json b/util/config.json index 654f898..2bbdc3c 100644 --- a/util/config.json +++ b/util/config.json @@ -13,6 +13,7 @@ "apiDomain": "https://interface.music.163.com", "eapiDomain": "https://interfacepc.music.163.com", "xeapiDomain": "https://interface3.music.163.com", + "neapiDomain": "https://interface3.music.163.com", "domain": "https://music.163.com", "clDomian": "https://clientlog.music.163.com", "clDomian3": "https://clientlog3.music.163.com", diff --git a/util/crypto.js b/util/crypto.js index 6838fe1..419336d 100644 --- a/util/crypto.js +++ b/util/crypto.js @@ -2,6 +2,7 @@ const CryptoJS = require('crypto-js') const crypto = require('crypto') const forge = require('node-forge') const zlib = require('zlib') +const zstd = require('./zstd') const iv = '0102030405060708' const presetKey = '0CoJUm6Qyw8W8jud' const linuxapiKey = 'rFgB&h#%2?^eDg:Q' @@ -305,11 +306,86 @@ const xeapiDecryptPublicKey = (encryptedData) => { ) } +const NEAPI_MAGIC = Buffer.from('CSJM') +const NEAPI_NONCE_LEN = 12 +const NEAPI_AD_LEN = 16 +const NEAPI_TAG_LEN = 16 +const NEAPI_HEADER_LEN = 4 + 1 + 1 + 2 + NEAPI_NONCE_LEN + 1 + NEAPI_AD_LEN + 4 + +// Header: magic, the config version as two big-endian bytes, then the +// nonce / associated data / sealed length. +const neapi = (data, config) => { + const nonce = crypto.randomBytes(NEAPI_NONCE_LEN) + const ad = crypto.randomBytes(NEAPI_AD_LEN) + const cipher = crypto.createCipheriv( + 'chacha20-poly1305', + config.encryptKey, + nonce, + { authTagLength: NEAPI_TAG_LEN }, + ) + cipher.setAAD(ad) + + const sealed = Buffer.concat([ + cipher.update(zstd.compress(data)), + cipher.final(), + cipher.getAuthTag(), + ]) + + const header = Buffer.alloc(NEAPI_HEADER_LEN) + NEAPI_MAGIC.copy(header, 0) + header[4] = config.version >> 8 + header[5] = config.version & 0xff + header.writeUInt16LE(nonce.length, 6) + nonce.copy(header, 8) + header[8 + nonce.length] = ad.length + ad.copy(header, 9 + nonce.length) + header.writeUInt32LE(sealed.length, 9 + nonce.length + ad.length) + + return Buffer.concat([header, sealed]).toString('base64') +} + +// Some error responses repeat the same document back to back. +const parseBody = (text) => { + try { + return JSON.parse(text) + } catch (error) { + const boundary = text.indexOf('}{') + if (boundary < 0) throw error + return JSON.parse(text.slice(0, boundary + 1)) + } +} + +const neapiResDecrypt = (body, config) => { + const raw = Buffer.from(String(body).trim(), 'base64') + const nonceLen = raw.readUInt16LE(6) + const nonce = raw.subarray(8, 8 + nonceLen) + const adLen = raw[8 + nonceLen] + const ad = raw.subarray(9 + nonceLen, 9 + nonceLen + adLen) + const offset = 9 + nonceLen + adLen + const sealed = raw.subarray(offset + 4, offset + 4 + raw.readUInt32LE(offset)) + + const decipher = crypto.createDecipheriv( + 'chacha20-poly1305', + config.decryptKey, + nonce, + { authTagLength: NEAPI_TAG_LEN }, + ) + decipher.setAAD(ad) + decipher.setAuthTag(sealed.subarray(sealed.length - NEAPI_TAG_LEN)) + + const compressed = Buffer.concat([ + decipher.update(sealed.subarray(0, sealed.length - NEAPI_TAG_LEN)), + decipher.final(), + ]) + return parseBody(zstd.decompress(compressed).toString()) +} + module.exports = { weapi, linuxapi, eapi, xeapi, + neapi, decrypt, aesEncrypt, aesDecrypt, @@ -318,4 +394,5 @@ module.exports = { xeapiSign, xeapiResDecrypt, xeapiDecryptPublicKey, + neapiResDecrypt, } diff --git a/util/neapiConfig.js b/util/neapiConfig.js new file mode 100644 index 0000000..498ce82 --- /dev/null +++ b/util/neapiConfig.js @@ -0,0 +1,258 @@ +const crypto = require('crypto') +const zstd = require('./zstd') + +// gorilla/algorithm/record 返回的 base64 解码后: +// [0 .. 64) RSA 块 -> RSA_PKCS1 去填充得到 RC4 密钥 +// [64 .. 68) u32 LE 载荷长度 +// [68 .. ) RC4 载荷 -> zstd 解压得到 ECFG 容器 +const CONFIG_PUBLIC_KEY = Buffer.from( + '3048024100e4e79c1cb27a9fab7d2c740e99e923ec51c009169ec3a380c60404' + + 'e01b060f0160cdc631d9fb7fe53b228a5ff11bee42b965d28c6c2036193f4ccd' + + 'c7ed853d110203010001', + 'hex', +) + +// 容器里的 Lua 区块对指令和字符串做了逐字节异或混淆 +const CODE_XOR = [ + 0x94b4f825, 0x977c6795, 0x327de32b, 0x16da5ba1, 0xcf55a2eb, 0x080bb445, + 0x021af055, 0xfc56b8bd, +] +const STR_XOR = Buffer.from('aefda2d1832b518a7741c325fb2687f0', 'hex') +const CHUNK_SIGNATURE = Buffer.from('7f454c46', 'hex') + +// 脚本用这两个标记区分请求方向与响应方向 +const DIRECTION_ENCRYPT = 251 +const DIRECTION_DECRYPT = 503 +const KEY_LEN = 32 + +const T_NUMFLT = 3 +const T_NUMINT = 19 +const OP_LOADK = 1 +const OP_GETUPVAL = 5 +const OP_EQ = 31 + +const op = (ins) => ins & 0x3f +const argA = (ins) => (ins >>> 6) & 0xff +const argB = (ins) => (ins >>> 23) & 0x1ff +const argC = (ins) => (ins >>> 14) & 0x1ff +const argBx = (ins) => (ins >>> 14) & 0x3ffff + +const rc4 = (key, data) => { + const s = Buffer.alloc(256) + for (let i = 0; i < 256; i++) s[i] = i + + let j = 0 + for (let i = 0; i < 256; i++) { + j = (j + s[i] + key[i % key.length]) & 0xff + ;[s[i], s[j]] = [s[j], s[i]] + } + + const out = Buffer.allocUnsafe(data.length) + let i = 0 + j = 0 + for (let n = 0; n < data.length; n++) { + i = (i + 1) & 0xff + j = (j + s[i]) & 0xff + ;[s[i], s[j]] = [s[j], s[i]] + out[n] = data[n] ^ s[(s[i] + s[j]) & 0xff] + } + return out +} + +const unwrapContainer = (raw) => { + const key = crypto.publicDecrypt( + crypto.createPublicKey({ + key: CONFIG_PUBLIC_KEY, + format: 'der', + type: 'pkcs1', + }), + raw.subarray(0, 64), + ) + const size = raw.readUInt32LE(64) + return zstd.decompress(rc4(key, raw.subarray(68, 68 + size))) +} + +class ChunkReader { + constructor(buf, pos = 0) { + this.buf = buf + this.pos = pos + } + + u8() { + return this.buf[this.pos++] + } + + u32() { + const value = this.buf.readUInt32LE(this.pos) + this.pos += 4 + return value + } + + i64() { + const value = this.buf.readBigInt64LE(this.pos) + this.pos += 8 + return Number(value) + } + + f64() { + const value = this.buf.readDoubleLE(this.pos) + this.pos += 8 + return value + } + + string() { + let size = this.u8() + if (size === 0) return null + if (size === 0xff) size = this.u32() + // 长度比实际字节数多 1(标准 luac 的写法) + const body = this.buf.subarray(this.pos, this.pos + size - 1) + this.pos += size - 1 + const out = Buffer.allocUnsafe(body.length) + for (let i = 0; i < body.length; i++) { + out[i] = body[i] ^ STR_XOR[i & 0x0f] + } + return out.toString('utf8') + } +} + +const loadProto = (r) => { + const proto = { + source: r.string(), + linedefined: r.u32(), + lastlinedefined: r.u32(), + numparams: r.u8(), + isvararg: r.u8(), + maxstack: r.u8(), + } + + let count = r.u32() + proto.code = new Array(count) + for (let i = 0; i < count; i++) { + proto.code[i] = (r.u32() ^ CODE_XOR[i & 7]) >>> 0 + } + + // 常量标签:0 nil / 1 boolean / 3 float / 19 integer / 4,20 string + // 只保留用得到的整数与字符串 + count = r.u32() + proto.consts = new Array(count) + for (let i = 0; i < count; i++) { + const tag = r.u8() + if (tag === 0) proto.consts[i] = null + else if (tag === 1) { + r.u8() + proto.consts[i] = null + } else if (tag === T_NUMFLT) { + r.f64() + proto.consts[i] = null + } else if (tag === T_NUMINT) proto.consts[i] = r.i64() + else proto.consts[i] = r.string() + } + + count = r.u32() + proto.upvals = new Array(count) + for (let i = 0; i < count; i++) { + proto.upvals[i] = [r.u8(), r.u8()] + } + + count = r.u32() + proto.protos = new Array(count) + for (let i = 0; i < count; i++) { + proto.protos[i] = loadProto(r) + } + + // 调试段跳过即可 + count = r.u32() + r.pos += 4 * count + count = r.u32() + for (let i = 0; i < count; i++) { + r.string() + r.u32() + r.u32() + } + count = r.u32() + for (let i = 0; i < count; i++) { + r.string() + } + return proto +} + +const loadChunk = (buf) => { + if (!buf.subarray(0, 4).equals(CHUNK_SIGNATURE)) { + throw new Error('neapi config is not a chunk') + } + const r = new ChunkReader(buf, 33) + r.u8() // 主闭包 upvalue 数量 + return loadProto(r) +} + +// 主函数用一连串 LOADK 装载所有标量,据此可以把 upvalue 追回常量值 +const mainRegisters = (main) => { + const regs = new Map() + for (const ins of main.code) { + if (op(ins) === OP_LOADK) regs.set(argA(ins), main.consts[argBx(ins)]) + } + return regs +} + +// create_ctx 里方向 -> 密钥偏移的分支: +// if direction == then offset = +// elseif direction == then offset = +const extractKeyOffsets = (script) => { + const createCtx = script.protos[0] + const regs = mainRegisters(script) + const offsets = {} + let pending = null + + for (const ins of createCtx.code) { + const code = op(ins) + if (code === OP_EQ) { + const c = argC(ins) + const constant = c & 0x100 && createCtx.consts[c & 0xff] + if (Number.isInteger(constant)) pending = constant + } else if (code === OP_GETUPVAL && pending !== null) { + const up = createCtx.upvals[argB(ins)] + const constant = up && up[0] === 1 && regs.get(up[1]) + if (Number.isInteger(constant)) { + offsets[pending] = constant + pending = null + } + } + } + + if (!(DIRECTION_ENCRYPT in offsets) || !(DIRECTION_DECRYPT in offsets)) { + throw new Error('neapi config script has an unexpected direction table') + } + return offsets +} + +const extractKeyBlob = (script) => { + const blob = script.consts + .filter( + (c) => typeof c === 'string' && /^[A-Za-z0-9+/]{64,}={0,2}$/.test(c), + ) + .sort((a, b) => b.length - a.length)[0] + return Buffer.from(blob, 'base64') +} + +const decodeConfig = (raw) => { + const ecfg = unwrapContainer(raw) + // ECFG 头 4 字节 + 大端 u16 版本,之后是 Lua 区块 + const version = ecfg.readUInt16BE(4) + const script = loadChunk(ecfg.subarray(6)) + + const offsets = extractKeyOffsets(script) + const keyBlob = extractKeyBlob(script) + const slice = (direction) => + keyBlob.subarray(offsets[direction], offsets[direction] + KEY_LEN) + + return { + version, + keyBlob, + encryptKey: slice(DIRECTION_ENCRYPT), + decryptKey: slice(DIRECTION_DECRYPT), + } +} + +module.exports = { + decodeConfig, +} diff --git a/util/neapiKey.js b/util/neapiKey.js new file mode 100644 index 0000000..8119ad3 --- /dev/null +++ b/util/neapiKey.js @@ -0,0 +1,49 @@ +const fs = require('fs') +const path = require('path') + +// 配置会轮换,启动时由 /register/neapikey 拉取后缓存到这里 +const neapiKeyPath = path.resolve(require('os').tmpdir(), 'neapi_key') + +let neapi_key = null + +const loadNeapiKey = () => { + if (!neapi_key && fs.existsSync(neapiKeyPath)) { + try { + const stored = JSON.parse(fs.readFileSync(neapiKeyPath, 'utf-8')) + neapi_key = { + version: stored.version, + circleTime: stored.circleTime, + fetchedAt: stored.fetchedAt, + keyBlob: Buffer.from(stored.keyBlob, 'base64'), + encryptKey: Buffer.from(stored.encryptKey, 'base64'), + decryptKey: Buffer.from(stored.decryptKey, 'base64'), + } + } catch (error) { + console.log('[ERR]', error) + } + } + return neapi_key +} + +const saveNeapiKey = (config, circleTime) => { + const fetchedAt = Date.now() + fs.writeFileSync( + neapiKeyPath, + JSON.stringify({ + version: config.version, + circleTime, + fetchedAt, + keyBlob: config.keyBlob.toString('base64'), + encryptKey: config.encryptKey.toString('base64'), + decryptKey: config.decryptKey.toString('base64'), + }), + 'utf-8', + ) + neapi_key = { ...config, circleTime, fetchedAt } + return neapi_key +} + +module.exports = { + loadNeapiKey, + saveNeapiKey, +} diff --git a/util/request.js b/util/request.js index 55b6d2b..9b277e9 100644 --- a/util/request.js +++ b/util/request.js @@ -18,12 +18,14 @@ const { } = require('./index') const { URLSearchParams, URL } = require('url') const { APP_CONF } = require('./config.json') +const { loadNeapiKey } = require('./neapiKey') const { getToken: antiCheatTokenV2, } = require('../module/register_checktoken_v2') const { getToken: antiCheatTokenV3, } = require('../module/register_checktoken_v3') +const registerNeapikey = require('../module/register_neapikey') // 预先读取匿名token并缓存 const anonymous_token = fs.readFileSync( @@ -71,6 +73,20 @@ const WNMCID = (function () { let NMTID = '' let NMTID_RETRIES_LEFT = 3 +// neapi 配置过期时只让一个请求去刷新,并发的其余请求共用同一次结果 +let neapiUserStatus = '' +let neapiRefreshing = null +const refreshNeapiKey = (version) => { + if (!neapiRefreshing) { + neapiRefreshing = registerNeapikey({ version, behavior: 'update' }, null) + .catch((error) => console.log('[ERR]', error)) + .finally(() => { + neapiRefreshing = null + }) + } + return neapiRefreshing +} + // 预先定义osMap const osMap = { pc: { @@ -127,6 +143,7 @@ const DOMAIN = APP_CONF.domain const API_DOMAIN = APP_CONF.apiDomain const EAPI_DOMAIN = APP_CONF.eapiDomain const XEAPI_DOMAIN = APP_CONF.xeapiDomain +const NEAPI_DOMAIN = APP_CONF.neapiDomain const ENCRYPT_RESPONSE = APP_CONF.encryptResponse const SPECIAL_STATUS_CODES = new Set([201, 302, 400, 502, 800, 801, 802, 803]) @@ -321,6 +338,47 @@ const createRequest = async (uri, data, options) => { }) break + case 'neapi': + const neapiKey = loadNeapiKey() + if (!neapiKey) { + throw new Error('neapi config is missing') + } + // 超过 circleTime 就后台刷新,本次仍用手上的配置 + if (now() - neapiKey.fetchedAt >= neapiKey.circleTime) { + refreshNeapiKey(neapiKey.version) + } + const neapiCookie = { + osver: cookie.osver, + deviceId: cookie.deviceId, + os: cookie.os, + appver: cookie.appver, + versioncode: cookie.versioncode || '140', + mobilename: cookie.mobilename || '', + buildver: cookie.buildver || now().toString().substr(0, 10), + resolution: cookie.resolution || '1920x1080', + __csrf: csrfToken, + channel: cookie.channel, + requestId: generateRequestId(), + } + if (cookie.MUSIC_U) neapiCookie['MUSIC_U'] = cookie.MUSIC_U + if (cookie.MUSIC_A) neapiCookie['MUSIC_A'] = cookie.MUSIC_A + headers['Cookie'] = createHeaderCookie(neapiCookie) + headers['User-Agent'] = options.ua || chooseUserAgent('api', 'android') + headers['content-type'] = + 'application/x-www-form-urlencoded;charset=utf-8' + // 服务端按 X-ER 选密钥,配置过期时由下面的 777/999 分支重拉并重试 + headers['X-ER'] = String(neapiKey.version) + if (neapiUserStatus) headers['X-USER-STATUS'] = neapiUserStatus + + const neapiBody = { ...data } + delete neapiBody.e_r + encryptData = encrypt.neapi( + new URLSearchParams(neapiBody).toString(), + neapiKey, + ) + url = (options.domain || NEAPI_DOMAIN) + '/neapi/' + uri.substr(5) + break + case 'eapi': case 'api': // header创建 @@ -367,12 +425,19 @@ const createRequest = async (uri, data, options) => { console.log('[ERR]', 'Unknown Crypto:', crypto) break } + const use_e_r = (crypto === 'eapi' || crypto === 'weapi') && data.e_r + const use_xeapi = crypto === 'xeapi' + const use_neapi = crypto === 'neapi' + // settings创建 let settings = { method: 'POST', url: url, headers: headers, - data: new URLSearchParams(encryptData).toString(), + // neapi 的请求体就是容器本身,不是表单字段 + data: use_neapi + ? encryptData + : new URLSearchParams(encryptData).toString(), httpAgent: createHttpAgent(), httpsAgent: createHttpsAgent(), } @@ -383,9 +448,7 @@ const createRequest = async (uri, data, options) => { } // 使用返回值加密 - const use_e_r = (crypto === 'eapi' || crypto === 'weapi') && data.e_r - const use_xeapi = crypto === 'xeapi' - if (use_e_r || use_xeapi) { + if (use_e_r || use_xeapi || use_neapi) { settings.encoding = null settings.responseType = 'arraybuffer' } @@ -426,7 +489,7 @@ const createRequest = async (uri, data, options) => { } // console.log(settings.headers); axios(settings) - .then((res) => { + .then(async (res) => { const body = res.data const setCookies = res.headers['set-cookie'] || [] @@ -453,6 +516,30 @@ const createRequest = async (uri, data, options) => { answer.cookie = setCookies.map(cleanCookie) } + // 空值同样要记下来,否则状态恢复后会一直带着上一次的值 + const userStatus = res.headers['x-user-status'] || '' + if (use_neapi) { + neapiUserStatus = userStatus + if ( + (userStatus === '777' || userStatus === '999') && + !options.neapiRetried + ) { + // 配置已轮换:刷新后重试一次 + await refreshNeapiKey(loadNeapiKey().version) + try { + resolve( + await createRequest(uri, data, { + ...options, + neapiRetried: true, + }), + ) + } catch (error) { + reject(error) + } + return + } + } + // debug: 统一注释块,需要时取消注释查看请求/返回的原始密文 // logger.debug(`[${crypto}]`, uri) @@ -471,6 +558,11 @@ const createRequest = async (uri, data, options) => { xeapiSessionKey = res.headers['x-encr-sskey'] } answer.body = encrypt.xeapiResDecrypt(Buffer.from(body)) + } else if (use_neapi) { + answer.body = encrypt.neapiResDecrypt( + Buffer.from(body).toString(), + loadNeapiKey(), + ) } else if (use_e_r) { answer.body = encrypt.eapiResDecrypt( body.toString('hex').toUpperCase(), diff --git a/util/zstd.js b/util/zstd.js new file mode 100644 index 0000000..f5ba3f6 --- /dev/null +++ b/util/zstd.js @@ -0,0 +1,41 @@ +const zlib = require('zlib') +const { decompress: fzstdDecompress } = require('fzstd') + +// Node 22.15+ 自带 zstd。更早的版本(CI 含 18)用 fzstd 解压,压缩则退回到 +// raw block 帧:内容原样存储,任何 zstd 解码器都能读,只是没有压缩率。 +const hasNative = typeof zlib.zstdCompressSync === 'function' + +const MAGIC = Buffer.from([0x28, 0xb5, 0x2f, 0xfd]) +const WINDOW_DESCRIPTOR = 0x50 // windowLog 20 +const BLOCK_MAX = 0x20000 // zstd 单块上限 128 KiB + +const compressRawFrame = (input) => { + const parts = [MAGIC, Buffer.from([0x00, WINDOW_DESCRIPTOR])] + let offset = 0 + do { + const block = input.subarray(offset, offset + BLOCK_MAX) + const last = offset + BLOCK_MAX >= input.length + const header = Buffer.alloc(3) + header.writeUIntLE((block.length << 3) | (last ? 1 : 0), 0, 3) + parts.push(header, block) + offset += BLOCK_MAX + } while (offset < input.length) + return Buffer.concat(parts) +} + +const compress = (input) => { + const buf = Buffer.isBuffer(input) ? input : Buffer.from(input) + return hasNative ? zlib.zstdCompressSync(buf) : compressRawFrame(buf) +} + +const decompress = (input) => { + const buf = Buffer.isBuffer(input) ? input : Buffer.from(input) + return hasNative + ? zlib.zstdDecompressSync(buf) + : Buffer.from(fzstdDecompress(buf)) +} + +module.exports = { + compress, + decompress, +} From 17c07960f9c3a10cc47d3ecb0b7957787d21b95c Mon Sep 17 00:00:00 2001 From: Takanashi Hoshino Date: Wed, 30 Sep 2026 19:56:15 +0800 Subject: [PATCH 05/19] remove ci --- .github/workflows/opencode-auto.yml | 67 ----------------------------- AGENTS.md | 10 ++++- 2 files changed, 9 insertions(+), 68 deletions(-) delete mode 100644 .github/workflows/opencode-auto.yml diff --git a/.github/workflows/opencode-auto.yml b/.github/workflows/opencode-auto.yml deleted file mode 100644 index f090658..0000000 --- a/.github/workflows/opencode-auto.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: OpenCode Automation - -on: - pull_request: - types: [opened, synchronize, reopened, ready_for_review] - -permissions: - contents: write - issues: write - pull-requests: write - -concurrency: - group: auto-${{ github.event.issue.number || github.event.pull_request.number }} - cancel-in-progress: false - -jobs: - automate: - # 排除其他机器人触发,防止自我循环; - # 但允许 takanashi-hoshino-agent 创建的 PR,这样 /oc 生成的测试 PR 也能被自动评审 - if: | - github.actor != 'github-actions[bot]' && - github.actor != 'opencode-agent[bot]' - runs-on: ubuntu-latest - steps: - - name: Fetch config from CF Worker - id: cfg - uses: MoeFurina/pull-oc-config@v1 - with: - url: https://bot-storage.interface.focalors.ltd/config - - - name: Generate bot token - id: token - uses: actions/create-github-app-token@v1 - with: - app-id: ${{ steps.cfg.outputs.app_id }} - private-key: ${{ steps.cfg.outputs.app_private_key }} - owner: ${{ github.repository_owner }} - - - name: Checkout - uses: actions/checkout@v6 - with: - token: ${{ steps.token.outputs.token }} - fetch-depth: 0 - persist-credentials: true - - - name: Setup Node.js - uses: actions/setup-node@v6 - with: - node-version: 22 - - - name: Install opencode - run: npm i -g opencode-ai@latest - - - name: Run automation - env: - GITHUB_TOKEN: ${{ steps.token.outputs.token }} - MODEL: opencode/muse-spark-1.3-contributor-free - OPENCODE_API_KEY: ${{ steps.cfg.outputs.opencode_api_key }} - ISSUE_PROMPT: ${{ steps.cfg.outputs.opencode_prompt }} - PR_PROMPT: | - ${{ steps.cfg.outputs.opencode_prompt }} - ${{ steps.cfg.outputs.pr_review_prompt }} - run: | - mkdir -p scripts - curl -fsSL "https://raw.githubusercontent.com/MoeFurina/pull-oc-config/v1/scripts/opencode-automation.mjs" \ - -o scripts/opencode-automation.mjs - node scripts/opencode-automation.mjs diff --git a/AGENTS.md b/AGENTS.md index d0eefb2..7e20086 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,6 +13,12 @@ - 文档格式化检查/修复:`pnpm docs:check` / `pnpm docs:format` - 打包独立二进制:`pnpm pkgwin` / `pkglinux` / `pkgmacos` +## 目录内非路由文件 +- `plugins/`——内部上传辅助(`upload.js`、`songUpload.js`),被 `module/*` require,本身不是路由。 +- `module_example/`——接口模板与 `main.js` 库调用示例(含 `test.js` 演示 `login_cellphone`→`song_url` 链路),不是运行时代码。 +- `examples/get_static_moddef.js`——用 `getModulesDefinitions(..., false)`(`doRequire=false`)把全部路由导出为 `moddef.json`。改路由注册逻辑时可跑它核对。 +- `module_types/` 为空目录;类型声明在根目录 `interface.d.ts`(`types` 字段)。 + ## 架构 - `app.js`(也是 `bin`)——服务入口。先确保 `os.tmpdir()` 里存在 `anonymous_token`,执行 `generateConfig()` 刷新匿名 cookie 与 xeapi 公钥,再调用 `server.serveNcmApi()`。 - `server.js`——Express 工厂。`constructServer()` 自动扫描 `module/*.js`,每个文件注册一条路由(文件名 `_` 转 `/`,如 `album_new.js` → `/album/new`;特例 `daily_signin`/`fm_trash`/`personal_fm` 硬编码在 `server.js` 的 `special` 对象里)。`serveNcmApi()` 监听 `PORT`(默认 3000)/`HOST`。 @@ -40,4 +46,6 @@ - **没有实际 git hooks**:`package.json` 里配了 `lint-staged` 和 husky,但 `.husky/` 下只有 `_` 脚手架目录、没有真正的 hook 文件,commit 时不会自动跑任何检查,自己记得 `pnpm lint-fix`。 - **代理环境变量已失效**:README 里关于 `http_proxy`/`https_proxy` 的警告来自旧 `request` 库时代;现在 `util/request.js` 用 axios + 自定义 keep-alive agent,且显式 `proxy: false`,环境变量代理不会生效。按请求走 `query.proxy` 参数(支持 PAC 和 http 隧道)。 - **启动令牌在系统临时目录**:`anonymous_token`、`xeapi_public_key` 存放在 `os.tmpdir()`,`util/request.js` 在 require 时同步读取。文件过期或被清空就重启服务(或调用 `generateConfig()`);首次启动先写空文件再刷新。 -- **ESLint 9 flat config**:`eslint.config.js`,风格由 `eslint-plugin-prettier` 强制(2 空格缩进、单引号、分号、`endOfLine: auto`)。 +- **`docs:check` 校验的是 `public/docs/home.md`(docsify 站点)**,不是 README;脚本默认只处理该文件,换行会被统一为 `\n`、连续空行压缩到 2 行,PR 改动它后记得跑 `pnpm docs:format`。 +- **ESLint 9 flat config**:`eslint.config.js`,风格由 `eslint-plugin-prettier` 强制(2 空格缩进、单引号、分号、`endOfLine: auto`),并 `globalIgnores(['**/public/'])`——`public/` 下的前端页面不参与 lint。 +- **`pnpm-workspace.yaml` 是单包工作区**(`packages: ['.']`),并带 `allowBuilds`(core-js/es5-ext)与 `minimumReleaseAgeExclude` 白名单;改依赖安装行为时先看它。 From 548f6c170af78e7ecd4965d83d6bfabf2d0b8b97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Sep 2026 11:57:54 +0000 Subject: [PATCH 06/19] build(deps): bump actions/create-github-app-token from 1 to 3 Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1 to 3. - [Release notes](https://github.com/actions/create-github-app-token/releases) - [Changelog](https://github.com/actions/create-github-app-token/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/create-github-app-token/compare/v1...v3) --- updated-dependencies: - dependency-name: actions/create-github-app-token dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/opencode.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/opencode.yml b/.github/workflows/opencode.yml index ec8b2a4..ba76735 100644 --- a/.github/workflows/opencode.yml +++ b/.github/workflows/opencode.yml @@ -33,7 +33,7 @@ jobs: - name: Generate bot token id: token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v3 with: app-id: ${{ steps.cfg.outputs.app_id }} private-key: ${{ steps.cfg.outputs.app_private_key }} From 8ad30b591db60ce0fc6c4bd18460ce70ecd3d969 Mon Sep 17 00:00:00 2001 From: Takanashi Hoshino Date: Thu, 1 Oct 2026 13:49:53 +0800 Subject: [PATCH 07/19] build: update dependences --- .github/workflows/opencode.yml | 9 +- package.json | 14 +- pnpm-lock.yaml | 350 ++++++++++++++++----------------- 3 files changed, 183 insertions(+), 190 deletions(-) diff --git a/.github/workflows/opencode.yml b/.github/workflows/opencode.yml index ba76735..bb9fa47 100644 --- a/.github/workflows/opencode.yml +++ b/.github/workflows/opencode.yml @@ -19,7 +19,7 @@ jobs: contains(github.event.comment.body, ' /opencode') || startsWith(github.event.comment.body, '/opencode')) && github.actor != 'opencode-agent[bot]' && - github.actor != 'takanashi-hoshino-agent[bot]' && + github.actor != 'yukka-agent[bot]' && github.actor != 'github-actions[bot]' runs-on: ubuntu-latest permissions: @@ -35,7 +35,7 @@ jobs: id: token uses: actions/create-github-app-token@v3 with: - app-id: ${{ steps.cfg.outputs.app_id }} + client-id: ${{ steps.cfg.outputs.app_id }} private-key: ${{ steps.cfg.outputs.app_private_key }} owner: ${{ github.repository_owner }} @@ -63,4 +63,7 @@ jobs: ${{ steps.cfg.outputs.opencode_prompt }} ## 用户指令(来自触发评论,请严格按此执行) - ${{ github.event.comment.body }} \ No newline at end of file + ${{ github.event.comment.body }} + + ## 用户要求Review时, 严格执行下面指令 + ${{ steps.cfg.outputs.pr_review_prompt }} \ No newline at end of file diff --git a/package.json b/package.json index 43f2247..b73a5a2 100644 --- a/package.json +++ b/package.json @@ -84,14 +84,14 @@ "fzstd": "^0.1.1", "gzip": "^0.1.0", "jsdom": "^24.1.3", - "music-metadata": "^11.15.0", + "music-metadata": "^11.16.1", "node-forge": "^1.4.0", "pac-proxy-agent": "^7.2.0", "qrcode": "^1.5.4", "safe-decode-uri-component": "^1.2.1", "tunnel": "^0.0.6", "xml2js": "^0.6.2", - "yargs": "^18.1.0" + "yargs": "^18.2.0" }, "devDependencies": { "@eslint/eslintrc": "^3.3.7", @@ -100,13 +100,13 @@ "@types/express-fileupload": "^1.5.1", "@types/mocha": "^10.0.10", "@types/node": "25.9.2", - "@typescript-eslint/eslint-plugin": "^8.69.0", - "@typescript-eslint/parser": "^8.69.0", + "@typescript-eslint/eslint-plugin": "^8.71.0", + "@typescript-eslint/parser": "^8.71.0", "eslint": "^9.39.5", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-html": "^8.2.0", + "eslint-plugin-html": "^8.2.1", "eslint-plugin-prettier": "^5.5.6", - "globals": "^17.12.0", + "globals": "^17.13.0", "husky": "^9.1.7", "intelli-espower-loader": "^1.1.0", "lint-staged": "^16.4.0", @@ -114,7 +114,7 @@ "nodemon": "^3.1.14", "pkg": "^5.8.1", "power-assert": "^1.6.1", - "prettier": "^3.9.6", + "prettier": "^3.9.9", "typescript": "^5.9.3" }, "publishConfig": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f46d131..e7c44f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,8 +36,8 @@ importers: specifier: ^24.1.3 version: 24.1.3 music-metadata: - specifier: ^11.15.0 - version: 11.15.0 + specifier: ^11.16.1 + version: 11.16.1 node-forge: specifier: ^1.4.0 version: 1.4.0 @@ -57,8 +57,8 @@ importers: specifier: ^0.6.2 version: 0.6.2 yargs: - specifier: ^18.1.0 - version: 18.1.0 + specifier: ^18.2.0 + version: 18.2.0 devDependencies: '@eslint/eslintrc': specifier: ^3.3.7 @@ -79,11 +79,11 @@ importers: specifier: 25.9.2 version: 25.9.2 '@typescript-eslint/eslint-plugin': - specifier: ^8.69.0 - version: 8.69.0(@typescript-eslint/parser@8.69.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3) + specifier: ^8.71.0 + version: 8.71.0(@typescript-eslint/parser@8.71.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3) '@typescript-eslint/parser': - specifier: ^8.69.0 - version: 8.69.0(eslint@9.39.5)(typescript@5.9.3) + specifier: ^8.71.0 + version: 8.71.0(eslint@9.39.5)(typescript@5.9.3) eslint: specifier: ^9.39.5 version: 9.39.5 @@ -91,14 +91,14 @@ importers: specifier: ^10.1.8 version: 10.1.8(eslint@9.39.5) eslint-plugin-html: - specifier: ^8.2.0 - version: 8.2.0 + specifier: ^8.2.1 + version: 8.2.1 eslint-plugin-prettier: specifier: ^5.5.6 - version: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.6) + version: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.9) globals: - specifier: ^17.12.0 - version: 17.12.0 + specifier: ^17.13.0 + version: 17.13.0 husky: specifier: ^9.1.7 version: 9.1.7 @@ -121,8 +121,8 @@ importers: specifier: ^1.6.1 version: 1.6.1 prettier: - specifier: ^3.9.6 - version: 3.9.6 + specifier: ^3.9.9 + version: 3.9.9 typescript: specifier: ^5.9.3 version: 5.9.3 @@ -338,63 +338,63 @@ packages: '@types/serve-static@2.2.0': resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} - '@typescript-eslint/eslint-plugin@8.69.0': - resolution: {integrity: sha512-t5jQTKPIgVW1PE6dR6H6Qz5gm8zjMlX5/2gRaOGd9eO6V7J+tQc6iWKukEe7dY8u9HyYasQ0yfF0/FSSTEO2gA==} + '@typescript-eslint/eslint-plugin@8.71.0': + resolution: {integrity: sha512-pqcS9c1HxZTHt7End4nXqd0s5lJrrFzrgCkKFJrsbUnaL6M3+6oBFZaslg6Gjsl3argl2DDRFROnXARaZ2e4Nw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.69.0 + '@typescript-eslint/parser': ^8.71.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.69.0': - resolution: {integrity: sha512-l4b0DhWioGg6Gt2ebGlvfkFMOjRsauxtsnDRwUSRX1qHq3HdTfQHV8wW9zEXeciai6HfeaKOedQn2Zoofx3WBw==} + '@typescript-eslint/parser@8.71.0': + resolution: {integrity: sha512-CG4nPk1f2zc8yw4pALqHsFYH2hdo+h1T9daSp21+Hnxi9LOE3GT9hAfTKJCBXVNM2GmYs1eMEP615wPoeOgk3A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.69.0': - resolution: {integrity: sha512-yi4obFrHMmnsesWehHbkg9zMA7Jt8cXT+mKM08G999pH1yT6nqgsHx7MYm0uY1wAj8CqiBXYRJ7WAT0QdQHQXg==} + '@typescript-eslint/project-service@8.71.0': + resolution: {integrity: sha512-aABjw5rjBacYONVPaPiWOCjJu0vEF4a25iQuodlmQYL1trtLZ0X/y+2Vzl3BKI1odM4LnwLE1oUDXYp1wzx1TQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.69.0': - resolution: {integrity: sha512-ewfspqWvSxKSOaplqAUNbaSFO0eB6w1EtQ+esfYFRm3614Ty4uNtExkcbgd6nWsXphbqKyf9ZYdbZdv2xEoWEQ==} + '@typescript-eslint/scope-manager@8.71.0': + resolution: {integrity: sha512-gWF0BhUcnjZxSpLE8ngS/59n2SB0J3YqRxvX1+2aoRJk9hNtHSLOV+TcarFiOr5ipXm3yc1QrI4c9YZc8zyCxw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.69.0': - resolution: {integrity: sha512-xNqK7YTDZsLniQMV/4rpFR8Z5JlqeRvVjuG1YgF/mdPVH84HSD19L8CczMA0qg2RfwEV231GHH3VnToJDo4MfQ==} + '@typescript-eslint/tsconfig-utils@8.71.0': + resolution: {integrity: sha512-Z1UlWHADEK2Mlb9NpWfDeSjqoZ5EyrOv4R3eQpbkzqn/EwaIdOpXXupEA1+0ZIOSJSZZDBHG0BrQyN8zUG6Pwg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.69.0': - resolution: {integrity: sha512-ZfoJAVg3JZndQEpEl9petVlxau3lRuElc4HRMuAlLCf8to04/iHz692RUSNmXKDjEuJmIL+KZ2/BsOcBc16dsA==} + '@typescript-eslint/type-utils@8.71.0': + resolution: {integrity: sha512-i8uO1qbdxeKgRnS5sCRt6On3/nfo2d2DwQe3Yvjx543zLy7r8ySqRuPPiIIXAhS03U0v5NfAFx+rUgxFzKKwNw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.69.0': - resolution: {integrity: sha512-K3VrubUPhlo9VDBS6QdI8YB5j7ClpqLRdefcz6PFrhnwicehBweqQ9Evhl4l+FYz0HdDmMqIiSX0aldGRYtDCA==} + '@typescript-eslint/types@8.71.0': + resolution: {integrity: sha512-cJ4OoxPGWvFnBTnSZyaU+qJzGTqPTGJY+gDchj6cRyLRdmIdt4rcsE4twj+zPfrNiWuVi38wijHzShL++Z9atQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.69.0': - resolution: {integrity: sha512-AdFkgqck3Vudb/kWnxlyafU/4aBhHrbQ9locP2N4psXTy5mOBg0SHJumnLvx7r6g1gV4DKvUFwV2nJZBoqOD8w==} + '@typescript-eslint/typescript-estree@8.71.0': + resolution: {integrity: sha512-PEEF4G5sLLWAS5BpPrUvms4ySZkiBQQZM4z+3ReI46axK5Vqr/vXBQatJQIZZOYdGyPUAKTtsrWzpqKuU+3DEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.69.0': - resolution: {integrity: sha512-tUbx60BBqQa31kXF5MCsOOLL5E/WzUuxIn7YpAvq+eaUlqvk8/NXnXMBNAdLCr0icjkzem7iUA5QqWHe/hJ1aw==} + '@typescript-eslint/utils@8.71.0': + resolution: {integrity: sha512-pKR/tEMVrXZG23UFKUn5BQf3zfmfk7KQceI2cGzywZ5nxM5Eu3hEJU1utjWzydtzBbcJAQhHN8iPCxobHpPcZQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.69.0': - resolution: {integrity: sha512-+rmdgPA+EXkNgKYvHvFfhrs35utXbwaC5PGpDquSXcoXQDKUA5UjV0LmTucG/4JXkM31BTu4TilHtrN8IVBe8w==} + '@typescript-eslint/visitor-keys@8.71.0': + resolution: {integrity: sha512-8eQ9R218XORK+KLosnf4bu/QsUXvUyVwTbArg7/0NMB1Pu87OJKvj4nhFblkYE8gQV73mW1dx1ptlPCkwRGa7A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@unblockneteasemusic/server@0.28.0': @@ -451,8 +451,8 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.3.0: - resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==} + ansi-regex@6.4.0: + resolution: {integrity: sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==} engines: {node: '>=12'} ansi-styles@3.2.1: @@ -546,22 +546,22 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - body-parser@1.20.6: - resolution: {integrity: sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==} + body-parser@1.20.8: + resolution: {integrity: sha512-JNcyFQ64OiijEkPzUBTCe+hyPXUD/3LEldGQ6iF5LR1w00mx9o7xtDWHXBY2iItjdCFGoilOLNQbH943ut7pHA==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} body-parser@2.3.0: resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} engines: {node: '>=18'} - brace-expansion@1.1.18: - resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==} + brace-expansion@1.1.21: + resolution: {integrity: sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==} - brace-expansion@2.1.4: - resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==} + brace-expansion@2.1.7: + resolution: {integrity: sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==} - brace-expansion@5.0.9: - resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} engines: {node: 20 || >=22} braces@3.0.3: @@ -1004,8 +1004,8 @@ packages: peerDependencies: eslint: '>=7.0.0' - eslint-plugin-html@8.2.0: - resolution: {integrity: sha512-L6F37uij4uq3cZah46BawXeZlZYW9IfFeep7BwkuxBZA++BbihvEpoJPhq0NpCruPRzVkG/tpgvQYM53Re4pVA==} + eslint-plugin-html@8.2.1: + resolution: {integrity: sha512-Xn6QOifC3niiqDojZ/hgGRCj+YOefDHtT6ShwM65/+goFCNFcTTXUoXcjht+gvuds2I6r3tmM5XWQuTOOF5lhw==} engines: {node: '>=16.0.0'} eslint-plugin-prettier@5.5.6: @@ -1122,8 +1122,8 @@ packages: resolution: {integrity: sha512-wxUJn2vTHvj/kZCVmc5/bJO15C7aSMyHeuXYY3geKpeKibaAoQGcEv5+sM6nHS2T7VF+QHS4hTWPiY2mKofEdg==} engines: {node: '>=12.0.0'} - express@4.22.2: - resolution: {integrity: sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==} + express@4.22.3: + resolution: {integrity: sha512-Bdcs4+3qlpVlx2NRn6fgX2Ue2/gGRaPeawebgclM0ERSCqDpA+owF1fdPwjJUTAJWMTuAaxjDf+hzb0/4eKvvw==} engines: {node: '>= 0.10.0'} express@5.2.1: @@ -1279,8 +1279,8 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-east-asian-width@1.6.0: - resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + get-east-asian-width@1.7.0: + resolution: {integrity: sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==} engines: {node: '>=18'} get-intrinsic@1.3.0: @@ -1319,8 +1319,8 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@17.12.0: - resolution: {integrity: sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==} + globals@17.13.0: + resolution: {integrity: sha512-RwMTC61u7hrG3ZJMkZQOK4JLJrKS8QtoTii63EmWvFXHqycY9FObBJQCbsLxSO8kjKhWE5kV1GC+Vb1g3RI0Zg==} engines: {node: '>=18'} globalthis@1.0.4: @@ -1433,8 +1433,8 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - ignore@7.0.8: - resolution: {integrity: sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==} + ignore@7.0.11: + resolution: {integrity: sha512-YChdK5txDjwGUvgR7oCJcLGkwi3LDh8Zx8tS7ndYciLEg/oOCL26VUBSUEtgS7EjiybcBqFxh5j0rAdDX7/bbg==} engines: {node: '>= 4'} import-fresh@3.3.1: @@ -1465,8 +1465,8 @@ packages: resolution: {integrity: sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==} engines: {node: '>=10'} - ip-address@10.7.0: - resolution: {integrity: sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==} + ip-address@10.7.2: + resolution: {integrity: sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==} engines: {node: '>= 12'} ipaddr.js@1.9.1: @@ -1501,8 +1501,8 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.16.2: - resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + is-core-module@2.17.0: + resolution: {integrity: sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==} engines: {node: '>= 0.4'} is-core-module@2.9.0: @@ -1818,8 +1818,8 @@ packages: multistream@4.1.0: resolution: {integrity: sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw==} - music-metadata@11.15.0: - resolution: {integrity: sha512-TN+kO1/oOc8UzDW5N3vSDncBcv9WyNnQQe/NFMcFWq6G1+zVeYUkGvkYpQ/S8wb8vKtUD7i78dIaY0cv+cmPRA==} + music-metadata@11.16.1: + resolution: {integrity: sha512-uR/mHK6eyfl8h3jVCobF5b38Mfg1IDJMiBxIVbmm5F+G1OXEFcUpQHveO84tJBAqvY93GCRY2R4kDyx7s06Rug==} engines: {node: '>=18'} napi-build-utils@1.0.2: @@ -1872,8 +1872,8 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - nwsapi@2.2.27: - resolution: {integrity: sha512-gQPNF78qebCQ6tvVFBYrvJdBNOrYZm90ZlXgpIFm06p6qHDHq/XC4TnJftN6OMbxVE0UTBAoRgcsDeJBBooITw==} + nwsapi@2.2.28: + resolution: {integrity: sha512-IlVB7OS7qrOsVYlpnFIkETjMwT9jwvmocJmmM+GZU/PAB3uGi9Ezd7vcWhWBUnSc0ya4ppmQITOyP1ez9gg8cg==} object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} @@ -2089,8 +2089,8 @@ packages: resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} engines: {node: '>=6.0.0'} - prettier@3.9.6: - resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} + prettier@3.9.9: + resolution: {integrity: sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==} engines: {node: '>=14'} hasBin: true @@ -2104,8 +2104,8 @@ packages: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + proxy-addr@2.0.8: + resolution: {integrity: sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==} engines: {node: '>= 0.10'} proxy-from-env@2.1.0: @@ -2130,10 +2130,6 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - qs@6.15.3: - resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} - engines: {node: '>=0.6'} - qs@6.16.0: resolution: {integrity: sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==} engines: {node: '>=0.6'} @@ -2434,8 +2430,8 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} - string-width@8.2.2: - resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} + string-width@8.3.0: + resolution: {integrity: sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==} engines: {node: '>=20'} string.prototype.trim@1.2.11: @@ -2598,8 +2594,8 @@ packages: resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} engines: {node: '>= 0.4'} - typed-array-byte-offset@1.0.4: - resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + typed-array-byte-offset@1.0.5: + resolution: {integrity: sha512-0FHJvLPqZ7KJzp17O13jfsAjsqazgrxBu2zEK95PmUz8lv2+GjRuxUInCr2Rk9Dms3ihN21zJ929ZO43yJ95QQ==} engines: {node: '>= 0.4'} typed-array-length@1.0.8: @@ -2615,8 +2611,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - uint8array-extras@1.5.0: - resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} + uint8array-extras@1.6.0: + resolution: {integrity: sha512-8iAasVS4wUx0gPLjH8Xtz2PeDzTSiy8QiLGu2DT3X5GU+egFEQhpnbtkehbAwjvDcxIERmCYcysiODFmE3Ud0Q==} engines: {node: '>=18'} unbox-primitive@1.1.0: @@ -2703,8 +2699,8 @@ packages: which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - which-typed-array@1.1.22: - resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} + which-typed-array@1.1.24: + resolution: {integrity: sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ==} engines: {node: '>= 0.4'} which@2.0.2: @@ -2741,8 +2737,8 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.21.3: - resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} + ws@8.22.0: + resolution: {integrity: sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -2782,8 +2778,8 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yaml@2.9.0: - resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + yaml@2.9.1: + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} engines: {node: '>= 14.6'} hasBin: true @@ -2819,8 +2815,8 @@ packages: resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} engines: {node: '>=12'} - yargs@18.1.0: - resolution: {integrity: sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==} + yargs@18.2.0: + resolution: {integrity: sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} yocto-queue@0.1.0: @@ -2969,7 +2965,7 @@ snapshots: '@unblockneteasemusic/server': 0.28.0 axios: 1.20.0 dotenv: 17.4.2 - express: 4.22.2 + express: 4.22.3 https: 1.0.0 transitivePeerDependencies: - debug @@ -3059,57 +3055,57 @@ snapshots: '@types/http-errors': 2.0.5 '@types/node': 25.9.2 - '@typescript-eslint/eslint-plugin@8.69.0(@typescript-eslint/parser@8.69.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.71.0(@typescript-eslint/parser@8.71.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.69.0(eslint@9.39.5)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.69.0 - '@typescript-eslint/type-utils': 8.69.0(eslint@9.39.5)(typescript@5.9.3) - '@typescript-eslint/utils': 8.69.0(eslint@9.39.5)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.69.0 + '@typescript-eslint/parser': 8.71.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.71.0 + '@typescript-eslint/type-utils': 8.71.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/utils': 8.71.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.71.0 eslint: 9.39.5 - ignore: 7.0.8 + ignore: 7.0.11 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.69.0(eslint@9.39.5)(typescript@5.9.3)': + '@typescript-eslint/parser@8.71.0(eslint@9.39.5)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.69.0 - '@typescript-eslint/types': 8.69.0 - '@typescript-eslint/typescript-estree': 8.69.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.69.0 + '@typescript-eslint/scope-manager': 8.71.0 + '@typescript-eslint/types': 8.71.0 + '@typescript-eslint/typescript-estree': 8.71.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.71.0 debug: 4.4.3 eslint: 9.39.5 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.69.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.71.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.69.0(typescript@5.9.3) - '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/tsconfig-utils': 8.71.0(typescript@5.9.3) + '@typescript-eslint/types': 8.71.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.69.0': + '@typescript-eslint/scope-manager@8.71.0': dependencies: - '@typescript-eslint/types': 8.69.0 - '@typescript-eslint/visitor-keys': 8.69.0 + '@typescript-eslint/types': 8.71.0 + '@typescript-eslint/visitor-keys': 8.71.0 - '@typescript-eslint/tsconfig-utils@8.69.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.71.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.69.0(eslint@9.39.5)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.71.0(eslint@9.39.5)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.69.0 - '@typescript-eslint/typescript-estree': 8.69.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.69.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/types': 8.71.0 + '@typescript-eslint/typescript-estree': 8.71.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.71.0(eslint@9.39.5)(typescript@5.9.3) debug: 4.4.3 eslint: 9.39.5 ts-api-utils: 2.5.0(typescript@5.9.3) @@ -3117,14 +3113,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.69.0': {} + '@typescript-eslint/types@8.71.0': {} - '@typescript-eslint/typescript-estree@8.69.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.71.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.69.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.69.0(typescript@5.9.3) - '@typescript-eslint/types': 8.69.0 - '@typescript-eslint/visitor-keys': 8.69.0 + '@typescript-eslint/project-service': 8.71.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.71.0(typescript@5.9.3) + '@typescript-eslint/types': 8.71.0 + '@typescript-eslint/visitor-keys': 8.71.0 debug: 4.4.3 minimatch: 10.2.6 semver: 7.8.5 @@ -3134,20 +3130,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.69.0(eslint@9.39.5)(typescript@5.9.3)': + '@typescript-eslint/utils@8.71.0(eslint@9.39.5)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5) - '@typescript-eslint/scope-manager': 8.69.0 - '@typescript-eslint/types': 8.69.0 - '@typescript-eslint/typescript-estree': 8.69.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.71.0 + '@typescript-eslint/types': 8.71.0 + '@typescript-eslint/typescript-estree': 8.71.0(typescript@5.9.3) eslint: 9.39.5 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.69.0': + '@typescript-eslint/visitor-keys@8.71.0': dependencies: - '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/types': 8.71.0 eslint-visitor-keys: 5.0.1 '@unblockneteasemusic/server@0.28.0': @@ -3199,7 +3195,7 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.3.0: {} + ansi-regex@6.4.0: {} ansi-styles@3.2.1: dependencies: @@ -3290,7 +3286,7 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - body-parser@1.20.6: + body-parser@1.20.8: dependencies: bytes: 3.1.2 content-type: 1.0.5 @@ -3300,7 +3296,7 @@ snapshots: http-errors: 2.0.1 iconv-lite: 0.4.24 on-finished: 2.4.1 - qs: 6.15.3 + qs: 6.16.0 raw-body: 2.5.3 type-is: 1.6.18 unpipe: 1.0.0 @@ -3321,16 +3317,16 @@ snapshots: transitivePeerDependencies: - supports-color - brace-expansion@1.1.18: + brace-expansion@1.1.21: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.1.4: + brace-expansion@2.1.7: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.9: + brace-expansion@5.0.12: dependencies: balanced-match: 4.0.4 @@ -3421,7 +3417,7 @@ snapshots: cli-truncate@5.2.0: dependencies: slice-ansi: 8.0.0 - string-width: 8.2.2 + string-width: 8.3.0 cliui@6.0.0: dependencies: @@ -3745,10 +3741,10 @@ snapshots: string.prototype.trimstart: 1.0.8 typed-array-buffer: 1.0.3 typed-array-byte-length: 1.0.3 - typed-array-byte-offset: 1.0.4 + typed-array-byte-offset: 1.0.5 typed-array-length: 1.0.8 unbox-primitive: 1.1.0 - which-typed-array: 1.1.22 + which-typed-array: 1.1.24 es-define-property@1.0.1: {} @@ -3858,14 +3854,14 @@ snapshots: dependencies: eslint: 9.39.5 - eslint-plugin-html@8.2.0: + eslint-plugin-html@8.2.1: dependencies: htmlparser2: 10.1.0 - eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.6): + eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.9): dependencies: eslint: 9.39.5 - prettier: 3.9.6 + prettier: 3.9.9 prettier-linter-helpers: 1.0.1 synckit: 0.11.13 optionalDependencies: @@ -4012,11 +4008,11 @@ snapshots: dependencies: busboy: 1.6.0 - express@4.22.2: + express@4.22.3: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.6 + body-parser: 1.20.8 content-disposition: 0.5.4 content-type: 1.0.5 cookie: 0.7.2 @@ -4034,8 +4030,8 @@ snapshots: on-finished: 2.4.1 parseurl: 1.3.3 path-to-regexp: 0.1.13 - proxy-addr: 2.0.7 - qs: 6.15.3 + proxy-addr: 2.0.8 + qs: 6.16.0 range-parser: 1.2.1 safe-buffer: 5.2.1 send: 0.19.2 @@ -4069,7 +4065,7 @@ snapshots: on-finished: 2.4.1 once: 1.4.0 parseurl: 1.3.3 - proxy-addr: 2.0.7 + proxy-addr: 2.0.8 qs: 6.16.0 range-parser: 1.3.0 router: 2.2.0 @@ -4122,7 +4118,7 @@ snapshots: '@tokenizer/inflate': 0.4.1 strtok3: 10.3.5 token-types: 6.1.2 - uint8array-extras: 1.5.0 + uint8array-extras: 1.6.0 transitivePeerDependencies: - supports-color @@ -4238,7 +4234,7 @@ snapshots: get-caller-file@2.0.5: {} - get-east-asian-width@1.6.0: {} + get-east-asian-width@1.7.0: {} get-intrinsic@1.3.0: dependencies: @@ -4293,7 +4289,7 @@ snapshots: globals@14.0.0: {} - globals@17.12.0: {} + globals@17.13.0: {} globalthis@1.0.4: dependencies: @@ -4405,7 +4401,7 @@ snapshots: ignore@5.3.2: {} - ignore@7.0.8: {} + ignore@7.0.11: {} import-fresh@3.3.1: dependencies: @@ -4435,7 +4431,7 @@ snapshots: from2: 2.3.0 p-is-promise: 3.0.0 - ip-address@10.7.0: {} + ip-address@10.7.2: {} ipaddr.js@1.9.1: {} @@ -4473,7 +4469,7 @@ snapshots: is-callable@1.2.7: {} - is-core-module@2.16.2: + is-core-module@2.17.0: dependencies: hasown: 2.0.4 @@ -4506,7 +4502,7 @@ snapshots: is-fullwidth-code-point@5.1.0: dependencies: - get-east-asian-width: 1.6.0 + get-east-asian-width: 1.7.0 is-generator-function@1.1.2: dependencies: @@ -4565,7 +4561,7 @@ snapshots: is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.22 + which-typed-array: 1.1.24 is-unicode-supported@0.1.0: {} @@ -4610,7 +4606,7 @@ snapshots: http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.27 + nwsapi: 2.2.28 parse5: 7.3.0 rrweb-cssom: 0.7.1 saxes: 6.0.0 @@ -4621,7 +4617,7 @@ snapshots: whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - ws: 8.21.3 + ws: 8.22.0 xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -4665,7 +4661,7 @@ snapshots: picomatch: 4.0.7 string-argv: 0.3.2 tinyexec: 1.3.1 - yaml: 2.9.0 + yaml: 2.9.1 listr2@9.0.5: dependencies: @@ -4746,15 +4742,15 @@ snapshots: minimatch@10.2.6: dependencies: - brace-expansion: 5.0.9 + brace-expansion: 5.0.12 minimatch@3.1.5: dependencies: - brace-expansion: 1.1.18 + brace-expansion: 1.1.21 minimatch@9.0.9: dependencies: - brace-expansion: 2.1.4 + brace-expansion: 2.1.7 minimist@1.2.8: {} @@ -4801,7 +4797,7 @@ snapshots: once: 1.4.0 readable-stream: 3.6.2 - music-metadata@11.15.0: + music-metadata@11.16.1: dependencies: '@borewit/text-codec': 0.2.2 '@tokenizer/token': 0.3.0 @@ -4811,7 +4807,7 @@ snapshots: media-typer: 2.0.0 strtok3: 10.3.5 token-types: 6.1.2 - uint8array-extras: 1.5.0 + uint8array-extras: 1.6.0 win-guid: 0.2.1 transitivePeerDependencies: - supports-color @@ -4860,7 +4856,7 @@ snapshots: normalize-path@3.0.0: {} - nwsapi@2.2.27: {} + nwsapi@2.2.28: {} object-inspect@1.13.4: {} @@ -5154,7 +5150,7 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier@3.9.6: {} + prettier@3.9.9: {} process-nextick-args@2.0.1: {} @@ -5162,7 +5158,7 @@ snapshots: progress@2.0.3: {} - proxy-addr@2.0.7: + proxy-addr@2.0.8: dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 @@ -5188,11 +5184,6 @@ snapshots: pngjs: 5.0.0 yargs: 15.4.1 - qs@6.15.3: - dependencies: - es-define-property: 1.0.1 - side-channel: 1.1.1 - qs@6.16.0: dependencies: es-define-property: 1.0.1 @@ -5286,7 +5277,7 @@ snapshots: resolve@1.22.12: dependencies: es-errors: 1.3.0 - is-core-module: 2.16.2 + is-core-module: 2.17.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -5508,7 +5499,7 @@ snapshots: socks@2.8.10: dependencies: - ip-address: 10.7.0 + ip-address: 10.7.2 smart-buffer: 4.2.0 sonic-boom@1.4.1: @@ -5567,12 +5558,12 @@ snapshots: string-width@7.2.0: dependencies: emoji-regex: 10.6.0 - get-east-asian-width: 1.6.0 + get-east-asian-width: 1.7.0 strip-ansi: 7.2.0 - string-width@8.2.2: + string-width@8.3.0: dependencies: - get-east-asian-width: 1.6.0 + get-east-asian-width: 1.7.0 strip-ansi: 7.2.0 string.prototype.trim@1.2.11: @@ -5619,7 +5610,7 @@ snapshots: strip-ansi@7.2.0: dependencies: - ansi-regex: 6.3.0 + ansi-regex: 6.4.0 strip-json-comments@2.0.1: {} @@ -5704,7 +5695,7 @@ snapshots: dependencies: gopd: 1.2.0 typedarray.prototype.slice: 1.0.5 - which-typed-array: 1.1.22 + which-typed-array: 1.1.24 ts-api-utils@2.5.0(typescript@5.9.3): dependencies: @@ -5755,13 +5746,12 @@ snapshots: has-proto: 1.2.0 is-typed-array: 1.1.15 - typed-array-byte-offset@1.0.4: + typed-array-byte-offset@1.0.5: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 - has-proto: 1.2.0 is-typed-array: 1.1.15 reflect.getprototypeof: 1.0.10 @@ -5783,11 +5773,11 @@ snapshots: get-proto: 1.0.1 math-intrinsics: 1.1.0 typed-array-buffer: 1.0.3 - typed-array-byte-offset: 1.0.4 + typed-array-byte-offset: 1.0.5 typescript@5.9.3: {} - uint8array-extras@1.5.0: {} + uint8array-extras@1.6.0: {} unbox-primitive@1.1.0: dependencies: @@ -5873,7 +5863,7 @@ snapshots: isarray: 2.0.5 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.22 + which-typed-array: 1.1.24 which-collection@1.0.2: dependencies: @@ -5884,7 +5874,7 @@ snapshots: which-module@2.0.1: {} - which-typed-array@1.1.22: + which-typed-array@1.1.24: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.9 @@ -5930,7 +5920,7 @@ snapshots: wrappy@1.0.2: {} - ws@8.21.3: {} + ws@8.22.0: {} xml-name-validator@5.0.0: {} @@ -5951,7 +5941,7 @@ snapshots: y18n@5.0.8: {} - yaml@2.9.0: {} + yaml@2.9.1: {} yargs-parser@18.1.3: dependencies: @@ -6005,12 +5995,12 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yargs@18.1.0: + yargs@18.2.0: dependencies: cliui: 9.0.1 escalade: 3.2.0 get-caller-file: 2.0.5 - string-width: 8.2.2 + string-width: 8.3.0 y18n: 5.0.8 yargs-parser: 22.0.0 From 4a45a14aa7e035e22b824600409ab1a07b748a0e Mon Sep 17 00:00:00 2001 From: Takanashi Hoshino Date: Thu, 1 Oct 2026 18:19:20 +0800 Subject: [PATCH 08/19] build: update denpendences 8ad30b591db60ce0fc6c4bd18460ce70ecd3d969 --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index b73a5a2..01b7245 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "data" ], "dependencies": { - "@neteasecloudmusicapienhanced/unblockmusic-utils": "^0.4.4", + "@neteasecloudmusicapienhanced/unblockmusic-utils": "^0.4.5", "axios": "^1.20.0", "crypto-js": "^4.2.0", "dotenv": "^17.4.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e7c44f4..48a79e7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@neteasecloudmusicapienhanced/unblockmusic-utils': - specifier: ^0.4.4 - version: 0.4.4 + specifier: ^0.4.5 + version: 0.4.5 axios: specifier: ^1.20.0 version: 1.20.0 @@ -259,8 +259,8 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@neteasecloudmusicapienhanced/unblockmusic-utils@0.4.4': - resolution: {integrity: sha512-ErQMlbG9LK2TuHr1ZVVsaHAyOkmY3MEDQ5/thtnEtX68YADkmcYt7ddgfnEVXGfGNCdvCV4k8viG2IdX9Qhoeg==} + '@neteasecloudmusicapienhanced/unblockmusic-utils@0.4.5': + resolution: {integrity: sha512-8BVbf80pKLpVdK6M+TJYMYB8V3hzIrBy0iVY1SP4hxZTqZAVTowC/lVn0gMHV6uc6e6BVXzFYUL0H4CTLdNmKQ==} hasBin: true '@nodelib/fs.scandir@2.1.5': @@ -2960,7 +2960,7 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.6.0 - '@neteasecloudmusicapienhanced/unblockmusic-utils@0.4.4': + '@neteasecloudmusicapienhanced/unblockmusic-utils@0.4.5': dependencies: '@unblockneteasemusic/server': 0.28.0 axios: 1.20.0 From bd407cee817f2159e6ee6658a018b0b7e3a46327 Mon Sep 17 00:00:00 2001 From: 1254qwer Date: Sat, 3 Oct 2026 17:21:32 +0800 Subject: [PATCH 09/19] fix: xeapi from BSR to CSR --- module/register_xeapikey.js | 2 +- util/crypto.js | 28 ++++++++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/module/register_xeapikey.js b/module/register_xeapikey.js index 48e0953..0602e87 100644 --- a/module/register_xeapikey.js +++ b/module/register_xeapikey.js @@ -32,7 +32,7 @@ module.exports = async (query, request) => { const res = await axios({ method: 'POST', - url: APP_CONF.apiDomain + '/api/gorilla/anti/crawler/security/key/get', + url: APP_CONF.apiDomain + '/api/bsr/sk/get', headers: { 'User-Agent': 'NeteaseMusic/9.5.61.260802021928(9005061);Dalvik/2.1.0 (Linux; U; Android 12; HBN-AL00 Build/cd737a2.0)', diff --git a/util/crypto.js b/util/crypto.js index e34699f..cc882e2 100644 --- a/util/crypto.js +++ b/util/crypto.js @@ -206,9 +206,19 @@ const xeapiMidTransform = (ciphertext) => { for (let i = 0; i < ciphertext.length; i++) { xored[i] = ciphertext[i] ^ random[i & 0x0f] } - const b64 = Buffer.from(xored.toString('base64')) - const rot = b64.length ? (random[0] & 0x0f) % b64.length : 0 - return Buffer.concat([random, b64.subarray(rot), b64.subarray(0, rot)]) + const rot = xored.length ? (random[0] & 0x0f) % xored.length : 0 + return Buffer.concat([random, xored.subarray(rot), xored.subarray(0, rot)]) +} + +const decoder = new TextDecoder('utf-8', { fatal: true }) + +const isUtf8 = (buf) => { + try { + decoder.decode(buf) + return true + } catch { + return false + } } const xeapiEncryptS = (dynamicKey, publicKeyState, os) => { @@ -251,7 +261,13 @@ const buildXeapiPlaintext = (uri, data, options = {}) => { const bodyData = { ...data } delete bodyData.e_r const body = Buffer.from(new URLSearchParams(bodyData).toString()) - fields.body = body.toString('base64') + if (body.length) { + if (mediaType === 'application/x-www-form-urlencoded' && isUtf8(body)) { + fields.content = body.toString('utf8') + } else { + fields.body = body.toString('base64') + } + } } if (fields.queryString) { @@ -274,7 +290,7 @@ const xeapi = (uri, data, options = {}) => { const dynamicKey = activeSessionKey || crypto.randomBytes(16) const plaintext = Buffer.from(buildXeapiPlaintext(uri, data, options)) - const b = aesEcbEncrypt( + const c = aesEcbEncrypt( dynamicKey, xeapiMidTransform(aesEcbEncrypt(xeapiStaticKey, plaintext)), ) @@ -287,7 +303,7 @@ const xeapi = (uri, data, options = {}) => { ) return { - B: b.toString('base64'), + C: c.toString('base64url'), S: s.toString('base64'), R: r.toString('base64'), } From 37b64ab01e0ddb52d234f40f8cd394a96ca00569 Mon Sep 17 00:00:00 2001 From: MT-SUPER-POWER Date: Sat, 3 Oct 2026 18:42:36 +0800 Subject: [PATCH 10/19] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=BA=86'?= =?UTF-8?q?=E4=B9=90=E8=BF=B7=E5=9B=A2'=E7=9A=84=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- interface.d.ts | 25 ++++++++++++++ module/fans_group_detail.js | 15 +++++++++ module/fans_group_feed_recommend.js | 17 ++++++++++ module/fans_group_user_group_detail.js | 14 ++++++++ module/fans_group_user_groups.js | 11 ++++++ public/docs/home.md | 46 +++++++++++++++++++++++++- 6 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 module/fans_group_detail.js create mode 100644 module/fans_group_feed_recommend.js create mode 100644 module/fans_group_user_group_detail.js create mode 100644 module/fans_group_user_groups.js diff --git a/interface.d.ts b/interface.d.ts index f81b8e5..28c8226 100644 --- a/interface.d.ts +++ b/interface.d.ts @@ -2890,3 +2890,28 @@ export function voicelist_trans( } & MultiPageConfig & RequestBaseConfig, ): Promise + +export function fans_group_detail( + params: { + groupId: string + scene?: string + } & RequestBaseConfig, +): Promise + +export function fans_group_feed_recommend( + params: { + fansGroupId: string + cursor?: string + size?: string | number + } & RequestBaseConfig, +): Promise + +export function fans_group_user_groups( + params?: RequestBaseConfig, +): Promise + +export function fans_group_user_group_detail( + params: { + groupId: string | number + } & RequestBaseConfig, +): Promise diff --git a/module/fans_group_detail.js b/module/fans_group_detail.js new file mode 100644 index 0000000..d7134ee --- /dev/null +++ b/module/fans_group_detail.js @@ -0,0 +1,15 @@ +// 乐迷团详情 + +const createOption = require('../util/option.js') + +module.exports = (query, request) => { + const data = { + groupId: query.groupId || query.id || '', + scene: query.scene || '', + } + return request( + '/api/social/fansgroup/bff/detail/get', + data, + createOption(query, 'eapi'), + ) +} diff --git a/module/fans_group_feed_recommend.js b/module/fans_group_feed_recommend.js new file mode 100644 index 0000000..be8cd2f --- /dev/null +++ b/module/fans_group_feed_recommend.js @@ -0,0 +1,17 @@ +// 乐迷团推荐笔记 + +const createOption = require('../util/option.js') + +module.exports = (query, request) => { + const data = { + artistSelf: query.artistSelf || '0', + fansGroupId: query.fansGroupId || query.groupId || '', + cursor: query.cursor || '0', + size: String(query.size || 10), + } + return request( + '/api/fans/group/feed/recommend/get', + data, + createOption(query, 'eapi'), + ) +} diff --git a/module/fans_group_user_group_detail.js b/module/fans_group_user_group_detail.js new file mode 100644 index 0000000..8d046b7 --- /dev/null +++ b/module/fans_group_user_group_detail.js @@ -0,0 +1,14 @@ +// 用户所处乐迷团详情 + +const createOption = require('../util/option.js') + +module.exports = (query, request) => { + const data = { + groupId: query.groupId || query.id || '', + } + return request( + '/api/social/fansgroup/bff/user/group/detail/get', + data, + createOption(query, 'eapi'), + ) +} diff --git a/module/fans_group_user_groups.js b/module/fans_group_user_groups.js new file mode 100644 index 0000000..c9db381 --- /dev/null +++ b/module/fans_group_user_groups.js @@ -0,0 +1,11 @@ +// 获取当前登录用户加入的全部歌手乐迷团列表 + +const createOption = require('../util/option.js') + +module.exports = (query, request) => { + return request( + '/api/social/fansgroup/bff/user/groups/get', + {}, + createOption(query, 'eapi'), + ) +} diff --git a/public/docs/home.md b/public/docs/home.md index 166a836..e1346f7 100644 --- a/public/docs/home.md +++ b/public/docs/home.md @@ -1307,7 +1307,7 @@ tags: 歌单标签 `lossless`=>`无损`, `hires`=>`Hi-Res`, `jyeffect` => `高清臻音`, `dolby` => `杜比全景声`, `vivid` => `臻音全景声`, `jymaster` => `超清母带`, `sky` => `沉浸环绕声` `unblock`: 是否使用使用歌曲解锁, 分为`true`和`false` -**可选参数 :** `immerseType`: 沉浸声环绕声类型, 分为`c512` => `新版c51类型`, `ste2` => `新版环绕立体声类型`, `aac2` => `新版aac类型`, `c51` => `c51类型`, `ste` => `环绕立体声类型`, `aac` => `aac类型`, 仅在 `level=sky` 时生效, 默认为 `c51` +**可选参数 :** `immerseType`: 沉浸声环绕声类型, 分为`c512` => `新版c51类型`, `ste2` => `新版环绕立体声类型`, `aac2` => `新版aac类型`, `c51` => `c51类型`, `ste` => `环绕立体声类型`, `aac` => `aac类型`, 仅在 `level=sky` 时生效, 默认为 `c51` **接口地址 :** `/song/url/v1` @@ -5878,6 +5878,50 @@ let data = encodeURIComponent( **调用例子 :** `/song/simi/get?id=39227633` +### 获取用户乐迷团列表 + +说明 : 登录后调用此接口 , 可获取当前登录用户已加入的全部歌手乐迷团列表 + +**接口地址 :** `/fans/group/user/groups` + +**调用例子 :** `/fans/group/user/groups` + +### 获取乐迷团详情 + +说明 : 调用此接口 , 可获取乐迷团详情 + +**必选参数 :** `groupId` : 乐迷团 ID + +**可选参数 :** `scene` : 场景标识 + +**接口地址 :** `/fans/group/detail` + +**调用例子 :** `/fans/group/detail?groupId=1872529203038486609` + +### 获取用户在乐迷团的详情 + +说明 : 登录后调用此接口 , 可获取当前用户在指定乐迷团的详细成员信息及铭牌等级 + +**必选参数 :** `groupId` : 乐迷团 ID + +**接口地址 :** `/fans/group/user/group/detail` + +**调用例子 :** `/fans/group/user/group/detail?groupId=1755777360858267711` + +### 获取乐迷团推荐笔记 + +说明 : 调用此接口 , 可获取指定乐迷团的推荐笔记 + +**必选参数 :** `fansGroupId` : 乐迷团 ID + +**可选参数 :** `cursor` : 游标 , 默认为 0 + +`size` : 返回数量 , 默认为 10 + +**接口地址 :** `/fans/group/feed/recommend` + +**调用例子 :** `/fans/group/feed/recommend?fansGroupId=1872529203038486609&size=10&cursor=0` + ## 离线访问此文档 此文档同时也是 Progressive Web Apps(PWA), 加入了 serviceWorker, 可离线访问 From 2aab9957dfd5231e5b192aecdb177f93ace4c92e Mon Sep 17 00:00:00 2001 From: Takanashi Hoshino Date: Sat, 3 Oct 2026 20:50:35 +0800 Subject: [PATCH 11/19] bump 4.41.0 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 01b7245..8f20b98 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@neteasecloudmusicapienhanced/api", - "version": "4.40.1", - "description": "全网最全的网易云音乐API接口 || A revival project for NeteaseCloudMusicApi Node.js Services (Half Refactor & Enhanced) || 网易云音乐 API 备份 + 增强 || 本项目自原版v4.28.0版本后开始自行维护", + "version": "4.41.0", + "description": "全网最全的网易云音乐API接口 || A revival project for NeteaseCloudMusicApi Node.js Services (Half Refactor & Enhanced)", "scripts": { "dev": "nodemon app.js", "start": "node app.js", From 90de64140fedecc0cfc1c0320d9e673d71834aab Mon Sep 17 00:00:00 2001 From: Takanashi Hoshino Date: Mon, 5 Oct 2026 22:38:41 +0800 Subject: [PATCH 12/19] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=97=A5?= =?UTF-8?q?=E6=8E=A8=E9=A3=8E=E6=A0=BC=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/opencode.yml | 2 ++ module/recommend_category_configs.js | 11 ++++++++ module/recommend_category_set.js | 17 +++++++++++ module/recommend_category_songs.js | 11 ++++++++ module/recommend_songs_v1.js | 13 +++++++++ package.json | 2 +- public/docs/home.md | 42 ++++++++++++++++++++++++++++ 7 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 module/recommend_category_configs.js create mode 100644 module/recommend_category_set.js create mode 100644 module/recommend_category_songs.js create mode 100644 module/recommend_songs_v1.js diff --git a/.github/workflows/opencode.yml b/.github/workflows/opencode.yml index bb9fa47..da5ef7c 100644 --- a/.github/workflows/opencode.yml +++ b/.github/workflows/opencode.yml @@ -22,6 +22,7 @@ jobs: github.actor != 'yukka-agent[bot]' && github.actor != 'github-actions[bot]' runs-on: ubuntu-latest + timeout-minutes: 15 permissions: contents: read steps: @@ -56,6 +57,7 @@ jobs: env: OPENCODE_API_KEY: ${{ steps.cfg.outputs.opencode_api_key }} GITHUB_TOKEN: ${{ steps.token.outputs.token }} + OPENCODE_PERMISSION: '{"permission":{"*":"allow","question":"deny"}}' with: model: opencode/muse-spark-1.3-contributor-free use_github_token: true diff --git a/module/recommend_category_configs.js b/module/recommend_category_configs.js new file mode 100644 index 0000000..4218637 --- /dev/null +++ b/module/recommend_category_configs.js @@ -0,0 +1,11 @@ +// 每日风格推荐场景配置列表获取 + +const createOption = require('../util/option.js') +module.exports = (query, request) => { + const data = {} + return request( + `/api/homepage/daily/song/config/get`, + data, + createOption(query, 'xeapi'), + ) +} diff --git a/module/recommend_category_set.js b/module/recommend_category_set.js new file mode 100644 index 0000000..936fd54 --- /dev/null +++ b/module/recommend_category_set.js @@ -0,0 +1,17 @@ +// 每日风格推荐场景配置设置 + +const createOption = require('../util/option.js') +module.exports = (query, request) => { + const data = { + tags: JSON.stringify({ + tagIds: String(query.tags).split(',').map(Number), + categoryId: Number(query.category), + }), + } + console.log(data) + return request( + `/api/homepage/daily/song/tag/save`, + data, + createOption(query, 'xeapi'), + ) +} diff --git a/module/recommend_category_songs.js b/module/recommend_category_songs.js new file mode 100644 index 0000000..70a125b --- /dev/null +++ b/module/recommend_category_songs.js @@ -0,0 +1,11 @@ +// 每日推荐风格歌曲 + +const createOption = require('../util/option.js') +module.exports = (query, request) => { + const data = {} + return request( + `/api/homepage/category/daily/song/list`, + data, + createOption(query, 'xeapi'), + ) +} diff --git a/module/recommend_songs_v1.js b/module/recommend_songs_v1.js new file mode 100644 index 0000000..9657cb7 --- /dev/null +++ b/module/recommend_songs_v1.js @@ -0,0 +1,13 @@ +// 每日推荐歌曲 - v1 + +const createOption = require('../util/option.js') +module.exports = (query, request) => { + const data = { + ispush: query.ispush || false, + } + return request( + `/api/v3/discovery/recommend/songs`, + data, + createOption(query, 'xeapi'), + ) +} diff --git a/package.json b/package.json index 8f20b98..4b04a3a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@neteasecloudmusicapienhanced/api", - "version": "4.41.0", + "version": "4.41.1", "description": "全网最全的网易云音乐API接口 || A revival project for NeteaseCloudMusicApi Node.js Services (Half Refactor & Enhanced)", "scripts": { "dev": "nodemon app.js", diff --git a/public/docs/home.md b/public/docs/home.md index e1346f7..592711e 100644 --- a/public/docs/home.md +++ b/public/docs/home.md @@ -2391,6 +2391,16 @@ privilege:权限相关信息 **调用例子 :** `/recommend/songs` +### 获取每日推荐歌曲 - v1 + +说明 : 调用此接口 , 可获得每日推荐歌曲 ( 需要登录 ) + +**可选参数 :** `ispush`: 是否刷新日推 , 默认为 false + +**接口地址 :** `/recommend/songs/v1` + +**调用例子 :** `/recommend/songs/v1` + ### 每日推荐歌曲-不感兴趣 说明 : 日推歌曲标记为不感兴趣( 同时会返回一个新推荐歌曲, 需要登录 ) @@ -5922,6 +5932,38 @@ let data = encodeURIComponent( **调用例子 :** `/fans/group/feed/recommend?fansGroupId=1872529203038486609&size=10&cursor=0` +## 每日指定风格歌曲推荐 + +说明 : 调用此接口 , 可获取每日指定风格歌曲推荐 + +**接口地址 :** `/recommend/category/songs` + +**调用例子 :** `/recommend/category/songs` + +## 获取每日指定风格歌曲推荐的风格列表 + +说明 : 调用此接口 , 可获取每日指定风格歌曲推荐的风格列表 + +**接口地址 :** `/recommend/category/configs` + +**调用例子 :** `/recommend/category/configs` + +## 设置每日指定风格歌曲推荐的风格 + +说明 : 调用此接口 , 可设置每日指定风格歌曲推荐的风格 + +**必选参数 :** + +`tags`: 风格标签, 可通过 `/recommend/category/configs` 获取 + +`category`: 风格标签的父ID, 可通过 `/recommend/category/configs` 获取 + +!> 只能设置当前CategoryId下的tags, 不能设置其他CategoryId下的tags, 否则会报错 + +**接口地址 :** `/recommend/category/config` + +**调用例子 :** `/recommend/category/config?tags=10004,10015&categoryId=1000` + ## 离线访问此文档 此文档同时也是 Progressive Web Apps(PWA), 加入了 serviceWorker, 可离线访问 From 2db6684453297ee86dec0492842f2320b08182d9 Mon Sep 17 00:00:00 2001 From: Takanashi Hoshino Date: Mon, 5 Oct 2026 22:41:37 +0800 Subject: [PATCH 13/19] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=97=A5?= =?UTF-8?q?=E6=8E=A8=E9=A3=8E=E6=A0=BC=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=20(90de64140fedecc0cfc1c0320d9e673d71834aab)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/docs/home.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/docs/home.md b/public/docs/home.md index 592711e..5c3d1b0 100644 --- a/public/docs/home.md +++ b/public/docs/home.md @@ -5952,7 +5952,7 @@ let data = encodeURIComponent( 说明 : 调用此接口 , 可设置每日指定风格歌曲推荐的风格 -**必选参数 :** +**必选参数 :** `tags`: 风格标签, 可通过 `/recommend/category/configs` 获取 From 19c7dad6db7d660954c7a615a686ff0acb030f91 Mon Sep 17 00:00:00 2001 From: Takanashi Hoshino Date: Sat, 17 Oct 2026 19:29:09 +0800 Subject: [PATCH 14/19] =?UTF-8?q?docs:=20=E4=BF=AE=E6=AD=A3=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/docs/home.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/docs/home.md b/public/docs/home.md index 5c3d1b0..87ef977 100644 --- a/public/docs/home.md +++ b/public/docs/home.md @@ -5932,7 +5932,7 @@ let data = encodeURIComponent( **调用例子 :** `/fans/group/feed/recommend?fansGroupId=1872529203038486609&size=10&cursor=0` -## 每日指定风格歌曲推荐 +### 每日指定风格歌曲推荐 说明 : 调用此接口 , 可获取每日指定风格歌曲推荐 @@ -5940,7 +5940,7 @@ let data = encodeURIComponent( **调用例子 :** `/recommend/category/songs` -## 获取每日指定风格歌曲推荐的风格列表 +### 获取每日指定风格歌曲推荐的风格列表 说明 : 调用此接口 , 可获取每日指定风格歌曲推荐的风格列表 @@ -5948,7 +5948,7 @@ let data = encodeURIComponent( **调用例子 :** `/recommend/category/configs` -## 设置每日指定风格歌曲推荐的风格 +### 设置每日指定风格歌曲推荐的风格 说明 : 调用此接口 , 可设置每日指定风格歌曲推荐的风格 From 896a62ec960f9d68aa5b4b4f88c99101819626bb Mon Sep 17 00:00:00 2001 From: 70CentsApple <70centsapple@gmail.com> Date: Mon, 19 Oct 2026 14:04:57 +0800 Subject: [PATCH 15/19] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=A9=BA=20cook?= =?UTF-8?q?ie=20=E5=AF=B9=E8=B1=A1=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.js | 2 +- module/login_qr_create.js | 4 ++-- module/scrobble.js | 9 +++++++-- module/scrobble_v1.js | 5 ++++- util/index.js | 11 +++++++++++ util/option.js | 6 +++++- 6 files changed, 30 insertions(+), 7 deletions(-) diff --git a/main.js b/main.js index 6c7634b..bb5505c 100644 --- a/main.js +++ b/main.js @@ -27,7 +27,7 @@ moduleFiles.forEach((file) => { const cookie = typeof data.cookie === 'string' ? cookieToJson(data.cookie) - : data.cookie || {} + : data.cookie return fileModule( { diff --git a/module/login_qr_create.js b/module/login_qr_create.js index d94cbec..5562099 100644 --- a/module/login_qr_create.js +++ b/module/login_qr_create.js @@ -1,10 +1,10 @@ const QRCode = require('qrcode') -const { generateChainId } = require('../util/index') +const { generateChainId, hasCookie } = require('../util/index') module.exports = (query) => { return new Promise(async (resolve) => { const platform = query.platform || 'pc' - const cookie = query.cookie || '' + const cookie = hasCookie(query.cookie) ? query.cookie : '' // 构建基础URL let url = `https://music.163.com/login?codekey=${query.key}` diff --git a/module/scrobble.js b/module/scrobble.js index 7adf91e..e2ed226 100644 --- a/module/scrobble.js +++ b/module/scrobble.js @@ -1,18 +1,23 @@ // 听歌打卡 const createOption = require('../util/option.js') +const { hasCookie } = require('../util/index') const { APP_CONF } = require('../util/config.json') const DOMAIN = APP_CONF.clDomian module.exports = async (query, request) => { // 注入 os=osx 的 cookie - let cookie = query.cookie || '' + let cookie = hasCookie(query.cookie) + ? query.cookie + : process.env.NETEASE_COOKIE || '' if (typeof cookie === 'object') { cookie = Object.assign({ os: 'osx' }, cookie) } else if (typeof cookie === 'string') { if (cookie.indexOf('os=') > -1) { cookie = cookie.replace(/os=[^;]+/g, 'os=osx') - } else { + } else if (cookie) { cookie = cookie + '; os=osx' + } else { + cookie = 'os=osx' } } else { cookie = 'os=osx' diff --git a/module/scrobble_v1.js b/module/scrobble_v1.js index 84c3658..0351baa 100644 --- a/module/scrobble_v1.js +++ b/module/scrobble_v1.js @@ -13,6 +13,7 @@ const { buildMetaJson, doUpload, } = require('../util/ncbl') +const { hasCookie } = require('../util/index') module.exports = async (query, request) => { // --- 参数校验 --- @@ -32,7 +33,9 @@ module.exports = async (query, request) => { const sourceName = query.source || 'list' // --- 解析认证上下文 --- - const rawCookie = query.cookie || '' + const rawCookie = hasCookie(query.cookie) + ? query.cookie + : process.env.NETEASE_COOKIE || '' const cookieObj = parseCookie(rawCookie) cookieObj.os = 'pc' const ctx = extractContext(cookieObj) diff --git a/util/index.js b/util/index.js index e76d665..e253912 100644 --- a/util/index.js +++ b/util/index.js @@ -103,6 +103,15 @@ module.exports = { return obj }, + hasCookie(cookie) { + if (typeof cookie === 'string') return cookie.length > 0 + return ( + cookie !== null && + typeof cookie === 'object' && + Object.keys(cookie).length > 0 + ) + }, + cookieObjToString(cookie) { // 优化:使用预绑定的keys函数和for循环 const cookieKeys = keys(cookie) @@ -226,6 +235,8 @@ const cookieToJsonOptimized = (function() { function getCookieValue(cookieStr, name) { if (!cookieStr) return '' + if (typeof cookieStr === 'object') return cookieStr[name] || '' + const cookies = '; ' + cookieStr const parts = cookies.split('; ' + name + '=') if (parts.length === 2) return parts.pop().split(';').shift() diff --git a/util/option.js b/util/option.js index 9796234..e10078f 100644 --- a/util/option.js +++ b/util/option.js @@ -1,7 +1,11 @@ +const { hasCookie } = require('./index') + const createOption = (query, crypto = '', checkToken = false) => { return { crypto: query.crypto || crypto || '', - cookie: query.cookie || process.env.NETEASE_COOKIE, + cookie: hasCookie(query.cookie) + ? query.cookie + : process.env.NETEASE_COOKIE, ua: query.ua || '', proxy: query.proxy, realIP: query.realIP, From 29c48e2ef98eb0a61e91a8d0fa41f65cf7ebb97c Mon Sep 17 00:00:00 2001 From: 70CentsApple <70centsapple@gmail.com> Date: Mon, 19 Oct 2026 14:23:57 +0800 Subject: [PATCH 16/19] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20cookie=20?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E5=99=A8=E5=B9=B6=E4=B9=9F=E5=B0=9D=E8=AF=95?= =?UTF-8?q?=E4=BB=8E=E8=AF=B7=E6=B1=82=E5=A4=B4=E8=AF=BB=20cookie?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- interface.d.ts | 1 + module/login_qr_create.js | 13 +++++++++++-- module/scrobble.js | 7 +++++-- module/scrobble_v1.js | 7 +++++-- server.js | 39 +++++++++++++++++++++++---------------- util/index.js | 28 ++++++++++++++++++++++++---- util/option.js | 8 ++++++-- util/request.js | 10 +++++++++- 8 files changed, 84 insertions(+), 29 deletions(-) diff --git a/interface.d.ts b/interface.d.ts index 28c8226..bb67d7b 100644 --- a/interface.d.ts +++ b/interface.d.ts @@ -1,5 +1,6 @@ export interface RequestBaseConfig { cookie?: string + headers?: Record realIP?: string // IPv4/IPv6 filled in X-Real-IP proxy?: string // HTTP proxy randomCNIP?: boolean // Whether to use a random Chinese IP address filled in X-Real-IP diff --git a/module/login_qr_create.js b/module/login_qr_create.js index 5562099..5678430 100644 --- a/module/login_qr_create.js +++ b/module/login_qr_create.js @@ -1,10 +1,19 @@ const QRCode = require('qrcode') -const { generateChainId, hasCookie } = require('../util/index') +const { + generateChainId, + getCookieHeader, + hasCookie, +} = require('../util/index') module.exports = (query) => { return new Promise(async (resolve) => { const platform = query.platform || 'pc' - const cookie = hasCookie(query.cookie) ? query.cookie : '' + const headerCookie = getCookieHeader(query.headers) + const cookie = hasCookie(query.cookie) + ? query.cookie + : hasCookie(headerCookie) + ? headerCookie + : '' // 构建基础URL let url = `https://music.163.com/login?codekey=${query.key}` diff --git a/module/scrobble.js b/module/scrobble.js index e2ed226..2f19ffe 100644 --- a/module/scrobble.js +++ b/module/scrobble.js @@ -1,14 +1,17 @@ // 听歌打卡 const createOption = require('../util/option.js') -const { hasCookie } = require('../util/index') +const { getCookieHeader, hasCookie } = require('../util/index') const { APP_CONF } = require('../util/config.json') const DOMAIN = APP_CONF.clDomian module.exports = async (query, request) => { // 注入 os=osx 的 cookie + const headerCookie = getCookieHeader(query.headers) let cookie = hasCookie(query.cookie) ? query.cookie - : process.env.NETEASE_COOKIE || '' + : hasCookie(headerCookie) + ? headerCookie + : process.env.NETEASE_COOKIE || '' if (typeof cookie === 'object') { cookie = Object.assign({ os: 'osx' }, cookie) } else if (typeof cookie === 'string') { diff --git a/module/scrobble_v1.js b/module/scrobble_v1.js index 0351baa..5c2f60a 100644 --- a/module/scrobble_v1.js +++ b/module/scrobble_v1.js @@ -13,7 +13,7 @@ const { buildMetaJson, doUpload, } = require('../util/ncbl') -const { hasCookie } = require('../util/index') +const { getCookieHeader, hasCookie } = require('../util/index') module.exports = async (query, request) => { // --- 参数校验 --- @@ -33,9 +33,12 @@ module.exports = async (query, request) => { const sourceName = query.source || 'list' // --- 解析认证上下文 --- + const headerCookie = getCookieHeader(query.headers) const rawCookie = hasCookie(query.cookie) ? query.cookie - : process.env.NETEASE_COOKIE || '' + : hasCookie(headerCookie) + ? headerCookie + : process.env.NETEASE_COOKIE || '' const cookieObj = parseCookie(rawCookie) cookieObj.os = 'pc' const ctx = extractContext(cookieObj) diff --git a/server.js b/server.js index 1c72cf8..7af7f6a 100644 --- a/server.js +++ b/server.js @@ -6,7 +6,7 @@ const request = require('./util/request') const packageJSON = require('./package.json') const exec = require('child_process').exec const cache = require('./util/apicache').middleware -const { cookieToJson } = require('./util/index') +const { cookieToJson, getCookieHeader, hasCookie } = require('./util/index') const fileUpload = require('express-fileupload') const decode = require('safe-decode-uri-component') const logger = require('./util/logger.js') @@ -225,14 +225,15 @@ async function constructServer(moduleDefs) { */ app.use((req, _, next) => { req.cookies = {} - //;(req.headers.cookie || '').split(/\s*;\s*/).forEach((pair) => { // Polynomial regular expression // - ;(req.headers.cookie || '').split(/;\s+|(? { - let crack = pair.indexOf('=') - if (crack < 1 || crack == pair.length - 1) return - req.cookies[decode(pair.slice(0, crack)).trim()] = decode( - pair.slice(crack + 1), - ).trim() - }) + getCookieHeader(req.headers) + .split(';') + .forEach((pair) => { + let crack = pair.indexOf('=') + if (crack < 1 || crack == pair.length - 1) return + req.cookies[decode(pair.slice(0, crack)).trim()] = decode( + pair.slice(crack + 1), + ).trim() + }) next() }) @@ -291,13 +292,19 @@ async function constructServer(moduleDefs) { } }) - let query = Object.assign( - {}, - { cookie: req.cookies }, - req.query, - req.body, - req.files, - ) + const queryCookie = req.query && req.query.cookie + const bodyCookie = req.body && req.body.cookie + const fileCookie = req.files && req.files.cookie + const cookie = hasCookie(fileCookie) + ? fileCookie + : hasCookie(bodyCookie) + ? bodyCookie + : hasCookie(queryCookie) + ? queryCookie + : req.cookies + const query = Object.assign({}, req.query, req.body, req.files, { + cookie, + }) try { let usedCrypto = '' diff --git a/util/index.js b/util/index.js index e253912..11e91cd 100644 --- a/util/index.js +++ b/util/index.js @@ -94,10 +94,12 @@ module.exports = { // 优化:使用for循环替代forEach,性能更好 for (let i = 0, len = cookieArr.length; i < len; i++) { let item = cookieArr[i] - let arr = item.split('=') - // 优化:使用严格等于 - if (arr.length === 2) { - obj[arr[0].trim()] = arr[1].trim() + let separator = item.indexOf('=') + // 只按第一个等号拆分,保留 Cookie 值中的等号 + if (separator > 0) { + obj[item.slice(0, separator).trim()] = item + .slice(separator + 1) + .trim() } } return obj @@ -112,6 +114,24 @@ module.exports = { ) }, + getCookieHeader(headers) { + if (!headers || typeof headers !== 'object') return '' + + if (typeof headers.get === 'function') { + const cookie = headers.get('cookie') + if (typeof cookie === 'string') return cookie + } + + const headerName = Object.keys(headers).find( + (name) => name.toLowerCase() === 'cookie', + ) + if (!headerName) return '' + + const cookie = headers[headerName] + if (Array.isArray(cookie)) return cookie.join('; ') + return typeof cookie === 'string' ? cookie : '' + }, + cookieObjToString(cookie) { // 优化:使用预绑定的keys函数和for循环 const cookieKeys = keys(cookie) diff --git a/util/option.js b/util/option.js index e10078f..317e40d 100644 --- a/util/option.js +++ b/util/option.js @@ -1,11 +1,15 @@ -const { hasCookie } = require('./index') +const { getCookieHeader, hasCookie } = require('./index') const createOption = (query, crypto = '', checkToken = false) => { + const headerCookie = getCookieHeader(query.headers) + return { crypto: query.crypto || crypto || '', cookie: hasCookie(query.cookie) ? query.cookie - : process.env.NETEASE_COOKIE, + : hasCookie(headerCookie) + ? headerCookie + : process.env.NETEASE_COOKIE, ua: query.ua || '', proxy: query.proxy, realIP: query.realIP, diff --git a/util/request.js b/util/request.js index a0c9020..2bfd35d 100644 --- a/util/request.js +++ b/util/request.js @@ -13,6 +13,8 @@ const tmpPath = require('os').tmpdir() const { cookieToJson, cookieObjToString, + getCookieHeader, + hasCookie, toBoolean, generateRandomChineseIP, } = require('./index') @@ -243,13 +245,19 @@ const createRequest = async (uri, data, options) => { headers['X-Forwarded-For'] = ip } - let cookie = options.cookie || {} + let cookie = hasCookie(options.cookie) + ? options.cookie + : getCookieHeader(options.headers) + if (!hasCookie(cookie)) cookie = {} if (typeof cookie === 'string') { cookie = cookieToJson(cookie) } if (typeof cookie === 'object') { cookie = processCookieObject(cookie, crypto) + Object.keys(headers).forEach((name) => { + if (name.toLowerCase() === 'cookie') delete headers[name] + }) headers['Cookie'] = cookieObjToString(cookie) } let url = '' From f1acc2fda3a06750944483e2093b8d69dc767f68 Mon Sep 17 00:00:00 2001 From: 70CentsApple <70centsapple@gmail.com> Date: Fri, 23 Oct 2026 19:40:11 +0800 Subject: [PATCH 17/19] fix: prettier --- main.js | 4 +--- module/login_qr_create.js | 6 +----- util/index.js | 4 +--- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/main.js b/main.js index bb5505c..dacceb6 100644 --- a/main.js +++ b/main.js @@ -25,9 +25,7 @@ moduleFiles.forEach((file) => { obj[fn] = function (data = {}) { const cookie = - typeof data.cookie === 'string' - ? cookieToJson(data.cookie) - : data.cookie + typeof data.cookie === 'string' ? cookieToJson(data.cookie) : data.cookie return fileModule( { diff --git a/module/login_qr_create.js b/module/login_qr_create.js index 5678430..709d5ee 100644 --- a/module/login_qr_create.js +++ b/module/login_qr_create.js @@ -1,9 +1,5 @@ const QRCode = require('qrcode') -const { - generateChainId, - getCookieHeader, - hasCookie, -} = require('../util/index') +const { generateChainId, getCookieHeader, hasCookie } = require('../util/index') module.exports = (query) => { return new Promise(async (resolve) => { diff --git a/util/index.js b/util/index.js index 11e91cd..d87dd94 100644 --- a/util/index.js +++ b/util/index.js @@ -97,9 +97,7 @@ module.exports = { let separator = item.indexOf('=') // 只按第一个等号拆分,保留 Cookie 值中的等号 if (separator > 0) { - obj[item.slice(0, separator).trim()] = item - .slice(separator + 1) - .trim() + obj[item.slice(0, separator).trim()] = item.slice(separator + 1).trim() } } return obj From 5b677a1f799a6d78c2152c479b682aae56c2cd2d Mon Sep 17 00:00:00 2001 From: Ming-QWQ520 Date: Sat, 24 Oct 2026 22:08:47 +0800 Subject: [PATCH 18/19] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20v9.6.13=20?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E9=80=86=E5=90=91=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=EF=BC=887=20=E4=B8=AA=E6=A8=A1=E5=9D=97=EF=BC=8C=E5=9D=87?= =?UTF-8?q?=E7=BB=8F=E5=AE=98=E6=96=B9=E5=AE=9E=E6=B5=8B=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - search_suggest_mobile: 搜索联想词 App 移动端版(/api/search/suggest/keyword/get) * 参数名为 keyword(s/keywords 会返回 HTTP 400 空体) * 返回直 hit 歌曲卡(entity_card + simpleSongData)与热搜/原唱标签联想词 - search_song_list_page: 新版单曲搜索分页(keyword/limit/offset/scene) - search_lyric: 按歌词搜索(返回 lyric 匹配行与 highlightText) - event_search: 动态搜索(/api/event/search/list/get/v1) - comment_default_text: 评论输入框默认文案(threadId) - comment_hotwall_square_info: 热评墙广场信息 - banner_v3: 首页轮播 v3(clientType) - 文档: public/docs/home.md 同步 7 个接口的说明/参数/调用例子 全部接口使用 v9.6.13 APK 逆向路径, 经官方 weapi 实测 code=200 --- module/banner_v3.js | 9 +++ module/comment_default_text.js | 13 ++++ module/comment_hotwall_square_info.js | 11 +++ module/event_search.js | 15 +++++ module/search_lyric.js | 15 +++++ module/search_song_list_page.js | 16 +++++ module/search_suggest_mobile.js | 15 +++++ public/docs/home.md | 96 +++++++++++++++++++++++++++ 8 files changed, 190 insertions(+) create mode 100644 module/banner_v3.js create mode 100644 module/comment_default_text.js create mode 100644 module/comment_hotwall_square_info.js create mode 100644 module/event_search.js create mode 100644 module/search_lyric.js create mode 100644 module/search_song_list_page.js create mode 100644 module/search_suggest_mobile.js diff --git a/module/banner_v3.js b/module/banner_v3.js new file mode 100644 index 0000000..77583d7 --- /dev/null +++ b/module/banner_v3.js @@ -0,0 +1,9 @@ +// 首页轮播(v3) + +const createOption = require('../util/option.js') +module.exports = (query, request) => { + const data = { + clientType: query.clientType || 'android', + } + return request(`/api/banner/get/v3`, data, createOption(query, 'weapi')) +} diff --git a/module/comment_default_text.js b/module/comment_default_text.js new file mode 100644 index 0000000..37107fc --- /dev/null +++ b/module/comment_default_text.js @@ -0,0 +1,13 @@ +// 评论默认文案 + +const createOption = require('../util/option.js') +module.exports = (query, request) => { + const data = { + threadId: query.threadId, + } + return request( + `/api/comment/default/text`, + data, + createOption(query, 'weapi'), + ) +} diff --git a/module/comment_hotwall_square_info.js b/module/comment_hotwall_square_info.js new file mode 100644 index 0000000..8c1d8f9 --- /dev/null +++ b/module/comment_hotwall_square_info.js @@ -0,0 +1,11 @@ +// 热评墙广场信息 + +const createOption = require('../util/option.js') +module.exports = (query, request) => { + const data = {} + return request( + `/api/comment/hotwall/today/squareinfo/get`, + data, + createOption(query, 'weapi'), + ) +} diff --git a/module/event_search.js b/module/event_search.js new file mode 100644 index 0000000..b196b04 --- /dev/null +++ b/module/event_search.js @@ -0,0 +1,15 @@ +// 动态搜索 + +const createOption = require('../util/option.js') +module.exports = (query, request) => { + const data = { + keyword: query.keyword || query.keywords || '', + limit: query.limit || 30, + offset: query.offset || 0, + } + return request( + `/api/event/search/list/get/v1`, + data, + createOption(query, 'weapi'), + ) +} diff --git a/module/search_lyric.js b/module/search_lyric.js new file mode 100644 index 0000000..a49477d --- /dev/null +++ b/module/search_lyric.js @@ -0,0 +1,15 @@ +// 按歌词搜索 + +const createOption = require('../util/option.js') +module.exports = (query, request) => { + const data = { + keyword: query.keyword || query.keywords || '', + limit: query.limit || 30, + offset: query.offset || 0, + } + return request( + `/api/search/resource/lyric`, + data, + createOption(query, 'weapi'), + ) +} diff --git a/module/search_song_list_page.js b/module/search_song_list_page.js new file mode 100644 index 0000000..4a8def6 --- /dev/null +++ b/module/search_song_list_page.js @@ -0,0 +1,16 @@ +// 新版单曲搜索(分页) + +const createOption = require('../util/option.js') +module.exports = (query, request) => { + const data = { + keyword: query.keyword || query.keywords || '', + limit: query.limit || 30, + offset: query.offset || 0, + scene: query.scene || 'normal', + } + return request( + `/api/search/song/list/page`, + data, + createOption(query, 'weapi'), + ) +} diff --git a/module/search_suggest_mobile.js b/module/search_suggest_mobile.js new file mode 100644 index 0000000..b046c1b --- /dev/null +++ b/module/search_suggest_mobile.js @@ -0,0 +1,15 @@ +// 搜索联想(App 移动端版:直hit歌曲卡 + 联想词 + 热搜/原唱标签) +// 注意:参数名为 keyword(传 s 会返回 HTTP 400 空体), +// 与 search_suggest 的旧版 /api/search/suggest/keyword 不同。 + +const createOption = require('../util/option.js') +module.exports = (query, request) => { + const data = { + keyword: query.keyword || query.keywords || '', + } + return request( + `/api/search/suggest/keyword/get`, + data, + createOption(query, 'weapi'), + ) +} diff --git a/public/docs/home.md b/public/docs/home.md index 87ef977..b5b6f34 100644 --- a/public/docs/home.md +++ b/public/docs/home.md @@ -5291,6 +5291,102 @@ let data = encodeURIComponent( **调用例子 :** `/user/playlist/collect?uid=32953014` +### 搜索联想词(App 移动端版) + +说明 : 调用此接口 , 传入搜索关键词可获得 App 搜索框下拉的联想结果:第 0 条为直hit歌曲卡(含完整歌曲数据 simpleSongData), 其后为联想词列表(首条带"热搜"标签, 另有"原唱"标签卡), 每条带 highLightInfo 高亮分段。与 v9.6.13 客户端截图逐字段对齐。 + +**必选参数 :** + +`keyword` : 搜索关键词(注意: 参数名必须是 keyword, 传 keywords/s 会返回 HTTP 400 空体) + +**接口地址 :** `/search/suggest/mobile` + +**调用例子 :** `/search/suggest/mobile?keyword=如果呢` + +### 新版单曲搜索(分页) + +说明 : 调用此接口 , 使用 v9.6.13 客户端新版单曲搜索接口 , 返回 resources 数组(含歌曲信息、 royaltyInfo、 高亮词 hlWords)与 hasMore 分页标记 + +**必选参数 :** + +`keyword` : 搜索关键词 + +**可选参数 :** + +`limit` : 返回数量, 默认 30 +`offset` : 偏移量 +`scene` : 场景, 默认 normal + +**接口地址 :** `/search/song/list/page` + +**调用例子 :** `/search/song/list/page?keyword=如果呢&limit=30` + +### 按歌词搜索 + +说明 : 调用此接口 , 传入歌词片段可搜索包含该歌词的歌曲, 返回 resources 数组(含 lyric 匹配行、 highlightText 高亮文本、 歌曲详情) + +**必选参数 :** + +`keyword` : 歌词片段 + +**可选参数 :** + +`limit` : 返回数量, 默认 30 +`offset` : 偏移量 + +**接口地址 :** `/search/lyric` + +**调用例子 :** `/search/lyric?keyword=天青色等烟雨` + +### 动态搜索 + +说明 : 调用此接口 , 传入关键词可搜索用户发布的动态 + +**必选参数 :** + +`keyword` : 关键词 + +**可选参数 :** + +`limit` : 返回数量, 默认 30 +`offset` : 偏移量 + +**接口地址 :** `/event/search` + +**调用例子 :** `/event/search?keyword=周杰伦` + +### 评论默认文案 + +说明 : 调用此接口 , 传入评论区 threadId 可获取该评论区输入框的默认提示文案数组 + +**必选参数 :** + +`threadId` : 评论区 ID(如 `A_EV_2_6559519868_32953014`) + +**接口地址 :** `/comment/default/text` + +**调用例子 :** `/comment/default/text?threadId=A_EV_2_6559519868_32953014` + +### 热评墙广场信息 + +说明 : 调用此接口 , 可获取今天热评墙广场的配置信息 + +**接口地址 :** `/comment/hotwall/square/info` + +**调用例子 :** `/comment/hotwall/square/info` + +### 首页轮播(v3) + +说明 : 调用此接口 , 获取 v3 版首页轮播图(v9.6.13 客户端使用版本) + +**可选参数 :** + +`clientType` : 客户端类型, 默认 android + +**接口地址 :** `/banner/v3` + +**调用例子 :** `/banner/v3?clientType=android` + ### 搜索建议 - PC端 说明 : 调用此接口, 传入搜索关键词, 获取搜索建议 From f01f99eece7252ba6d4496f0082eb9429d5f06ea Mon Sep 17 00:00:00 2001 From: Takanashi Hoshino Date: Sat, 24 Oct 2026 22:22:43 +0800 Subject: [PATCH 19/19] build(package): update dependences --- .github/workflows/opencode.yml | 2 +- package.json | 10 +- pnpm-lock.yaml | 324 +++++++++++++++++---------------- 3 files changed, 172 insertions(+), 164 deletions(-) diff --git a/.github/workflows/opencode.yml b/.github/workflows/opencode.yml index da5ef7c..b9c3c06 100644 --- a/.github/workflows/opencode.yml +++ b/.github/workflows/opencode.yml @@ -59,7 +59,7 @@ jobs: GITHUB_TOKEN: ${{ steps.token.outputs.token }} OPENCODE_PERMISSION: '{"permission":{"*":"allow","question":"deny"}}' with: - model: opencode/muse-spark-1.3-contributor-free + model: opencode/glm-5.3-flash-free use_github_token: true prompt: | ${{ steps.cfg.outputs.opencode_prompt }} diff --git a/package.json b/package.json index 4b04a3a..ec19b6e 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "axios": "^1.20.0", "crypto-js": "^4.2.0", "dotenv": "^17.4.2", - "express": "^5.2.1", + "express": "^5.3.0", "express-fileupload": "^1.5.2", "fzstd": "^0.1.1", "gzip": "^0.1.0", @@ -100,12 +100,12 @@ "@types/express-fileupload": "^1.5.1", "@types/mocha": "^10.0.10", "@types/node": "25.9.2", - "@typescript-eslint/eslint-plugin": "^8.71.0", - "@typescript-eslint/parser": "^8.71.0", + "@typescript-eslint/eslint-plugin": "^8.72.0", + "@typescript-eslint/parser": "^8.72.0", "eslint": "^9.39.5", "eslint-config-prettier": "^10.1.8", "eslint-plugin-html": "^8.2.1", - "eslint-plugin-prettier": "^5.5.6", + "eslint-plugin-prettier": "^5.5.7", "globals": "^17.13.0", "husky": "^9.1.7", "intelli-espower-loader": "^1.1.0", @@ -114,7 +114,7 @@ "nodemon": "^3.1.14", "pkg": "^5.8.1", "power-assert": "^1.6.1", - "prettier": "^3.9.9", + "prettier": "^3.9.10", "typescript": "^5.9.3" }, "publishConfig": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 48a79e7..16f73a6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,8 +21,8 @@ importers: specifier: ^17.4.2 version: 17.4.2 express: - specifier: ^5.2.1 - version: 5.2.1 + specifier: ^5.3.0 + version: 5.3.0 express-fileupload: specifier: ^1.5.2 version: 1.5.2 @@ -79,11 +79,11 @@ importers: specifier: 25.9.2 version: 25.9.2 '@typescript-eslint/eslint-plugin': - specifier: ^8.71.0 - version: 8.71.0(@typescript-eslint/parser@8.71.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3) + specifier: ^8.72.0 + version: 8.72.0(@typescript-eslint/parser@8.72.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3) '@typescript-eslint/parser': - specifier: ^8.71.0 - version: 8.71.0(eslint@9.39.5)(typescript@5.9.3) + specifier: ^8.72.0 + version: 8.72.0(eslint@9.39.5)(typescript@5.9.3) eslint: specifier: ^9.39.5 version: 9.39.5 @@ -94,8 +94,8 @@ importers: specifier: ^8.2.1 version: 8.2.1 eslint-plugin-prettier: - specifier: ^5.5.6 - version: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.9) + specifier: ^5.5.7 + version: 5.5.7(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.10) globals: specifier: ^17.13.0 version: 17.13.0 @@ -121,8 +121,8 @@ importers: specifier: ^1.6.1 version: 1.6.1 prettier: - specifier: ^3.9.9 - version: 3.9.9 + specifier: ^3.9.10 + version: 3.9.10 typescript: specifier: ^5.9.3 version: 5.9.3 @@ -136,8 +136,8 @@ packages: resolution: {integrity: sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.29.7': - resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + '@babel/helper-string-parser@7.29.11': + resolution: {integrity: sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==} engines: {node: '>=6.9.0'} '@babel/helper-validator-identifier@7.29.7': @@ -184,6 +184,9 @@ packages: resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} + '@dual-bundle/import-meta-resolve@4.2.1': + resolution: {integrity: sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==} + '@eslint-community/eslint-utils@4.10.1': resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -296,8 +299,8 @@ packages: '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} - '@types/busboy@1.5.4': - resolution: {integrity: sha512-kG7WrUuAKK0NoyxfQHsVE6j1m01s6kMma64E+OZenQABMQyTJop1DumUWcLwAQ2JzpefU7PDYoRDKl8uZosFjw==} + '@types/busboy@1.5.5': + resolution: {integrity: sha512-IwqrfVFxWa23c4WeE7uZswssCqGfvnl5Q7b1pdshbT3HaQDa3UIbJx5BAxtcj9nZYX9tVDArM9TUv9SpvORbpQ==} '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -338,63 +341,63 @@ packages: '@types/serve-static@2.2.0': resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} - '@typescript-eslint/eslint-plugin@8.71.0': - resolution: {integrity: sha512-pqcS9c1HxZTHt7End4nXqd0s5lJrrFzrgCkKFJrsbUnaL6M3+6oBFZaslg6Gjsl3argl2DDRFROnXARaZ2e4Nw==} + '@typescript-eslint/eslint-plugin@8.72.0': + resolution: {integrity: sha512-aysKgysPX9XOv/dk95UkvUDe++Q3n3dJPG9CEWT3RUccxfkPJ+Qn6nhKPDhZXU6Ofo9kgwGWwKyQCnZIXTgngg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.71.0 + '@typescript-eslint/parser': ^8.72.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.71.0': - resolution: {integrity: sha512-CG4nPk1f2zc8yw4pALqHsFYH2hdo+h1T9daSp21+Hnxi9LOE3GT9hAfTKJCBXVNM2GmYs1eMEP615wPoeOgk3A==} + '@typescript-eslint/parser@8.72.0': + resolution: {integrity: sha512-0O+x3ynciD6zuXb1CKSu165haQV6ey53DKhHHUnujP7U+XlltJG+/keT0Xhd1HXCze+IHf7Tvt39ovyepX/LNw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.71.0': - resolution: {integrity: sha512-aABjw5rjBacYONVPaPiWOCjJu0vEF4a25iQuodlmQYL1trtLZ0X/y+2Vzl3BKI1odM4LnwLE1oUDXYp1wzx1TQ==} + '@typescript-eslint/project-service@8.72.0': + resolution: {integrity: sha512-bzHngBbmIgzj6TsTjVUCntGx6FFLgE7mPjpO0PzHycOaZHXqS43XnrkFIHRS16LLr/xO6FHSXG3GJFo/F4bSBQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.71.0': - resolution: {integrity: sha512-gWF0BhUcnjZxSpLE8ngS/59n2SB0J3YqRxvX1+2aoRJk9hNtHSLOV+TcarFiOr5ipXm3yc1QrI4c9YZc8zyCxw==} + '@typescript-eslint/scope-manager@8.72.0': + resolution: {integrity: sha512-JewPyJOYcXBSAS7PIgufscHOwZqhTGyBRiQJhhuDln66tMchxvvt7nJ90JKKIoOelmdHA3kAQbm9cEUHc4JSEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.71.0': - resolution: {integrity: sha512-Z1UlWHADEK2Mlb9NpWfDeSjqoZ5EyrOv4R3eQpbkzqn/EwaIdOpXXupEA1+0ZIOSJSZZDBHG0BrQyN8zUG6Pwg==} + '@typescript-eslint/tsconfig-utils@8.72.0': + resolution: {integrity: sha512-0vbxCf7xLVPSjcOdm6eQbgViH8EDJ8xnl1laLT9yoqSz2YmcSfSg5Vdl0JUvZf6rcuJ0ZivJIuMD4edTg8obaA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.71.0': - resolution: {integrity: sha512-i8uO1qbdxeKgRnS5sCRt6On3/nfo2d2DwQe3Yvjx543zLy7r8ySqRuPPiIIXAhS03U0v5NfAFx+rUgxFzKKwNw==} + '@typescript-eslint/type-utils@8.72.0': + resolution: {integrity: sha512-gEXxmmbSTSOVTC37IYYvremOM31v+UN2W772bJxmVYPhAOPSUxtacyb7wkm1EhkYKU6KzFJsdU44DCV4to0LWw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.71.0': - resolution: {integrity: sha512-cJ4OoxPGWvFnBTnSZyaU+qJzGTqPTGJY+gDchj6cRyLRdmIdt4rcsE4twj+zPfrNiWuVi38wijHzShL++Z9atQ==} + '@typescript-eslint/types@8.72.0': + resolution: {integrity: sha512-Adfoy59kxNEjT5E5fnyrp7GpHYuWs8ONbdohu4ez6IU46qDq8LltG90tLmkucZB2uhYL8mqBjtXqGd8yhfjXqQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.71.0': - resolution: {integrity: sha512-PEEF4G5sLLWAS5BpPrUvms4ySZkiBQQZM4z+3ReI46axK5Vqr/vXBQatJQIZZOYdGyPUAKTtsrWzpqKuU+3DEw==} + '@typescript-eslint/typescript-estree@8.72.0': + resolution: {integrity: sha512-gwLfFgWcw15tN2KXDCJvxEOQjPn+P3j/n3eSqkZ5sX4VpW9Cosodr+tWwBZRvJT3TcKx6HnPRGZMCNAiP1Xhug==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.71.0': - resolution: {integrity: sha512-pKR/tEMVrXZG23UFKUn5BQf3zfmfk7KQceI2cGzywZ5nxM5Eu3hEJU1utjWzydtzBbcJAQhHN8iPCxobHpPcZQ==} + '@typescript-eslint/utils@8.72.0': + resolution: {integrity: sha512-0up3oArJZBYnzOaEXKo8DmI43QvMZyPoFEt9LWzRJcyl/RcJjfKKPebknN1VbTgXBf3M/KggUh5uUMdURLnseg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.71.0': - resolution: {integrity: sha512-8eQ9R218XORK+KLosnf4bu/QsUXvUyVwTbArg7/0NMB1Pu87OJKvj4nhFblkYE8gQV73mW1dx1ptlPCkwRGa7A==} + '@typescript-eslint/visitor-keys@8.72.0': + resolution: {integrity: sha512-E2mroviasaWecDzTGiGMtuEguuLVOeRggnrIn93CJuxYJzQBHbyymlT08aVQydbBal5538o+JkBKXh2sRuCyIA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@unblockneteasemusic/server@0.28.0': @@ -423,8 +426,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - acorn@8.18.0: - resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + acorn@8.19.0: + resolution: {integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==} engines: {node: '>=0.4.0'} hasBin: true @@ -546,12 +549,12 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - body-parser@1.20.8: - resolution: {integrity: sha512-JNcyFQ64OiijEkPzUBTCe+hyPXUD/3LEldGQ6iF5LR1w00mx9o7xtDWHXBY2iItjdCFGoilOLNQbH943ut7pHA==} + body-parser@1.20.9: + resolution: {integrity: sha512-FISIQxV5u91Y9gvuFq3q/tA/qTqLJn3AhRcEvyozyhJTh5OBFFtv827AdHU7Rbyct4r+Ba1/99oFa1Y3yuFsdw==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@2.3.0: - resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} + body-parser@2.4.0: + resolution: {integrity: sha512-Ifk1KcLrxaGt1occQagQNFNNOqRt1LFuehEnHHzB8Z6cRgIxRU1TBaiIsUiCdA+gy5PL7Y1IX31DNKkRmxtXjQ==} engines: {node: '>=18'} brace-expansion@1.1.21: @@ -689,8 +692,8 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} - content-disposition@1.1.0: - resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + content-disposition@2.0.1: + resolution: {integrity: sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==} engines: {node: '>=18'} content-type@1.0.5: @@ -1008,8 +1011,8 @@ packages: resolution: {integrity: sha512-Xn6QOifC3niiqDojZ/hgGRCj+YOefDHtT6ShwM65/+goFCNFcTTXUoXcjht+gvuds2I6r3tmM5XWQuTOOF5lhw==} engines: {node: '>=16.0.0'} - eslint-plugin-prettier@5.5.6: - resolution: {integrity: sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==} + eslint-plugin-prettier@5.5.7: + resolution: {integrity: sha512-WjHyn3GrFd+ZR0rzsLiM4zJZ35PZMOE0SUIxzGmnWNG0126yNfJKJzObmmkYDwHJfGlkz3PSlzyG9ez/LJntwg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@types/eslint': '>=8.0.0' @@ -1126,8 +1129,8 @@ packages: resolution: {integrity: sha512-Bdcs4+3qlpVlx2NRn6fgX2Ue2/gGRaPeawebgclM0ERSCqDpA+owF1fdPwjJUTAJWMTuAaxjDf+hzb0/4eKvvw==} engines: {node: '>= 0.10.0'} - express@5.2.1: - resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + express@5.3.0: + resolution: {integrity: sha512-qCqy1xSoaugxOD4IVHb1ZWdkvCF2W3aE2okKouWj8VwJ+ztskSiXoBnCSJqfKT+cVPJhuJqe9YLbY425+Zh00g==} engines: {node: '>= 18'} ext@1.7.0: @@ -1210,8 +1213,8 @@ packages: flatted@3.4.4: resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} - follow-redirects@1.16.0: - resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + follow-redirects@1.16.1: + resolution: {integrity: sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -1433,8 +1436,8 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - ignore@7.0.11: - resolution: {integrity: sha512-YChdK5txDjwGUvgR7oCJcLGkwi3LDh8Zx8tS7ndYciLEg/oOCL26VUBSUEtgS7EjiybcBqFxh5j0rAdDX7/bbg==} + ignore@7.0.14: + resolution: {integrity: sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==} engines: {node: '>= 4'} import-fresh@3.3.1: @@ -1465,8 +1468,8 @@ packages: resolution: {integrity: sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==} engines: {node: '>=10'} - ip-address@10.7.2: - resolution: {integrity: sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==} + ip-address@10.7.4: + resolution: {integrity: sha512-DWBMKyeM25D70nrIiTkgQth59/G+naKOTep6huF9j+GmeK3DMD60Zvxg5d5qQnz+38AHgou6ean2LECX+lrKDg==} engines: {node: '>= 12'} ipaddr.js@1.9.1: @@ -1843,8 +1846,8 @@ packages: next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} - node-abi@3.96.0: - resolution: {integrity: sha512-rebQ/lz7i0EkoLzUVSrKRzA69zMkwLp95kKMWoMDkkM00Suxz0D7zEQPwRml5fQum24mj7bPvmlgLAmu2JCiYg==} + node-abi@3.101.0: + resolution: {integrity: sha512-bkb/RkuWCE64eWu1PCh3Fu2pGc9mKRs+kOE+FU2bGHRYlQa86/D321DSATV54AsCQMKz3/xSt9sRgraZTpqdhQ==} engines: {node: '>=10'} node-fetch@2.7.0: @@ -1894,8 +1897,8 @@ packages: on-exit-leak-free@0.2.0: resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==} - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + on-finished@2.4.2: + resolution: {integrity: sha512-ATWUv387i+nLD49EiDoOft5scf32Vsdz3ktrQJG9bi7jRFmaXTssvyvj1EmeKVVgvesbqUtvQQvaS2TpTukAZQ==} engines: {node: '>= 0.8'} once@1.4.0: @@ -2089,8 +2092,8 @@ packages: resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} engines: {node: '>=6.0.0'} - prettier@3.9.9: - resolution: {integrity: sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==} + prettier@3.9.10: + resolution: {integrity: sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==} engines: {node: '>=14'} hasBin: true @@ -2130,8 +2133,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - qs@6.16.0: - resolution: {integrity: sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==} + qs@6.16.1: + resolution: {integrity: sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==} engines: {node: '>=0.6'} querystringify@2.2.0: @@ -2203,8 +2206,8 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve@1.22.12: - resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + resolve@1.22.13: + resolution: {integrity: sha512-Dj/cW9zBV2aRj6XzCxuGIs3ESZKv02jixne3q6jFZYZ3/Tg09gHHviSMDL4kObh9pqSahdRHEnDoaP2gn8YLhA==} engines: {node: '>= 0.4'} hasBin: true @@ -2471,8 +2474,8 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - strtok3@10.3.5: - resolution: {integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==} + strtok3@10.6.2: + resolution: {integrity: sha512-BkdixFFUt7r7LsX6aGNPVyYMLjW+M1USLlKPNbZsrZEmLFh5tGqhdfckKo6xgsZIJqdDD0CZGkeCJJwNWQIKQA==} engines: {node: '>=18'} supports-color@5.5.0: @@ -2494,8 +2497,8 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - synckit@0.11.13: - resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} + synckit@0.12.3: + resolution: {integrity: sha512-U+/AXT6LWsxnj2vz9HeUuPWBNgdWxjyIEjEPdbJARC9txmgPzo2VEdqnFo4hHaNyMPtycyVJrSaQYeQv/PoyyA==} engines: {node: ^14.18.0 || >=16.0.0} tar-fs@2.1.5: @@ -2544,8 +2547,8 @@ packages: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} - traverse@0.6.11: - resolution: {integrity: sha512-vxXDZg8/+p3gblxB6BhhG5yWVn1kGRlaL8O78UDXc3wRnPizB5g83dcvWV1jpDMIPnjZjOFuxlMmE82XJ4407w==} + traverse@0.6.14: + resolution: {integrity: sha512-CYRAVGfnSSe+4Wlof62oUH6y9fGwo0NNS84hMq76MTZtwXQU7Oqna9SgWfrCGj5JPUtHQudnIEt1BP7PInPkPQ==} engines: {node: '>= 0.4'} ts-api-utils@2.5.0: @@ -2602,8 +2605,8 @@ packages: resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} engines: {node: '>= 0.4'} - typedarray.prototype.slice@1.0.5: - resolution: {integrity: sha512-q7QNVDGTdl702bVFiI5eY4l/HkgCM6at9KhcFbgUAzezHFbOVy4+0O/lCjsABEQwbZPravVfBIiBVGo89yzHFg==} + typedarray.prototype.slice@1.0.6: + resolution: {integrity: sha512-r+7YvGfM5TkTiDQ5EqWi/Z4L4KP3jUI+YCmTtWg1tmTHUw6xladLJqzR2heMaHBIM0TTFLZmn/EkIaEHYs/bIA==} engines: {node: '>= 0.4'} typescript@5.9.3: @@ -2839,7 +2842,7 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 jsesc: 2.5.2 - '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-string-parser@7.29.11': {} '@babel/helper-validator-identifier@7.29.7': {} @@ -2849,7 +2852,7 @@ snapshots: '@babel/types@7.19.0': dependencies: - '@babel/helper-string-parser': 7.29.7 + '@babel/helper-string-parser': 7.29.11 '@babel/helper-validator-identifier': 7.29.7 to-fast-properties: 2.0.0 @@ -2875,6 +2878,8 @@ snapshots: '@csstools/css-tokenizer@3.0.4': {} + '@dual-bundle/import-meta-resolve@4.2.1': {} + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5)': dependencies: eslint: 9.39.5 @@ -3004,7 +3009,7 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 25.9.2 - '@types/busboy@1.5.4': + '@types/busboy@1.5.5': dependencies: '@types/node': 25.9.2 @@ -3016,7 +3021,7 @@ snapshots: '@types/express-fileupload@1.5.1': dependencies: - '@types/busboy': 1.5.4 + '@types/busboy': 1.5.5 '@types/express': 5.0.6 '@types/express-serve-static-core@5.1.3': @@ -3055,57 +3060,57 @@ snapshots: '@types/http-errors': 2.0.5 '@types/node': 25.9.2 - '@typescript-eslint/eslint-plugin@8.71.0(@typescript-eslint/parser@8.71.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.72.0(@typescript-eslint/parser@8.72.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.71.0(eslint@9.39.5)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.71.0 - '@typescript-eslint/type-utils': 8.71.0(eslint@9.39.5)(typescript@5.9.3) - '@typescript-eslint/utils': 8.71.0(eslint@9.39.5)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.71.0 + '@typescript-eslint/parser': 8.72.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.72.0 + '@typescript-eslint/type-utils': 8.72.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/utils': 8.72.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.72.0 eslint: 9.39.5 - ignore: 7.0.11 + ignore: 7.0.14 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.71.0(eslint@9.39.5)(typescript@5.9.3)': + '@typescript-eslint/parser@8.72.0(eslint@9.39.5)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.71.0 - '@typescript-eslint/types': 8.71.0 - '@typescript-eslint/typescript-estree': 8.71.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.71.0 + '@typescript-eslint/scope-manager': 8.72.0 + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/typescript-estree': 8.72.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.72.0 debug: 4.4.3 eslint: 9.39.5 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.71.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.72.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.71.0(typescript@5.9.3) - '@typescript-eslint/types': 8.71.0 + '@typescript-eslint/tsconfig-utils': 8.72.0(typescript@5.9.3) + '@typescript-eslint/types': 8.72.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.71.0': + '@typescript-eslint/scope-manager@8.72.0': dependencies: - '@typescript-eslint/types': 8.71.0 - '@typescript-eslint/visitor-keys': 8.71.0 + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/visitor-keys': 8.72.0 - '@typescript-eslint/tsconfig-utils@8.71.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.72.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.71.0(eslint@9.39.5)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.72.0(eslint@9.39.5)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.71.0 - '@typescript-eslint/typescript-estree': 8.71.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.71.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/typescript-estree': 8.72.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.72.0(eslint@9.39.5)(typescript@5.9.3) debug: 4.4.3 eslint: 9.39.5 ts-api-utils: 2.5.0(typescript@5.9.3) @@ -3113,14 +3118,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.71.0': {} + '@typescript-eslint/types@8.72.0': {} - '@typescript-eslint/typescript-estree@8.71.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.72.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.71.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.71.0(typescript@5.9.3) - '@typescript-eslint/types': 8.71.0 - '@typescript-eslint/visitor-keys': 8.71.0 + '@typescript-eslint/project-service': 8.72.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.72.0(typescript@5.9.3) + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/visitor-keys': 8.72.0 debug: 4.4.3 minimatch: 10.2.6 semver: 7.8.5 @@ -3130,20 +3135,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.71.0(eslint@9.39.5)(typescript@5.9.3)': + '@typescript-eslint/utils@8.72.0(eslint@9.39.5)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5) - '@typescript-eslint/scope-manager': 8.71.0 - '@typescript-eslint/types': 8.71.0 - '@typescript-eslint/typescript-estree': 8.71.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.72.0 + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/typescript-estree': 8.72.0(typescript@5.9.3) eslint: 9.39.5 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.71.0': + '@typescript-eslint/visitor-keys@8.72.0': dependencies: - '@typescript-eslint/types': 8.71.0 + '@typescript-eslint/types': 8.72.0 eslint-visitor-keys: 5.0.1 '@unblockneteasemusic/server@0.28.0': @@ -3164,13 +3169,13 @@ snapshots: acorn-es7-plugin@1.1.7: {} - acorn-jsx@5.3.2(acorn@8.18.0): + acorn-jsx@5.3.2(acorn@8.19.0): dependencies: - acorn: 8.18.0 + acorn: 8.19.0 acorn@5.7.4: {} - acorn@8.18.0: {} + acorn@8.19.0: {} agent-base@6.0.2: dependencies: @@ -3262,7 +3267,7 @@ snapshots: axios@1.20.0: dependencies: - follow-redirects: 1.16.0 + follow-redirects: 1.16.1 form-data: 4.0.6 https-proxy-agent: 5.0.1 proxy-from-env: 2.1.0 @@ -3286,7 +3291,7 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - body-parser@1.20.8: + body-parser@1.20.9: dependencies: bytes: 3.1.2 content-type: 1.0.5 @@ -3295,23 +3300,23 @@ snapshots: destroy: 1.2.0 http-errors: 2.0.1 iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.16.0 + on-finished: 2.4.2 + qs: 6.16.1 raw-body: 2.5.3 type-is: 1.6.18 unpipe: 1.0.0 transitivePeerDependencies: - supports-color - body-parser@2.3.0: + body-parser@2.4.0: dependencies: bytes: 3.1.2 content-type: 2.1.0 debug: 4.4.3 http-errors: 2.0.1 iconv-lite: 0.7.3 - on-finished: 2.4.1 - qs: 6.16.0 + on-finished: 2.4.2 + qs: 6.16.1 raw-body: 3.0.2 type-is: 2.1.0 transitivePeerDependencies: @@ -3469,7 +3474,7 @@ snapshots: dependencies: safe-buffer: 5.2.1 - content-disposition@1.1.0: {} + content-disposition@2.0.1: {} content-type@1.0.5: {} @@ -3858,12 +3863,12 @@ snapshots: dependencies: htmlparser2: 10.1.0 - eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.9): + eslint-plugin-prettier@5.5.7(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.10): dependencies: eslint: 9.39.5 - prettier: 3.9.9 + prettier: 3.9.10 prettier-linter-helpers: 1.0.1 - synckit: 0.11.13 + synckit: 0.12.3 optionalDependencies: eslint-config-prettier: 10.1.8(eslint@9.39.5) @@ -3967,8 +3972,8 @@ snapshots: espree@10.4.0: dependencies: - acorn: 8.18.0 - acorn-jsx: 5.3.2(acorn@8.18.0) + acorn: 8.19.0 + acorn-jsx: 5.3.2(acorn@8.19.0) eslint-visitor-keys: 4.2.1 esprima@2.7.3: {} @@ -4012,7 +4017,7 @@ snapshots: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.8 + body-parser: 1.20.9 content-disposition: 0.5.4 content-type: 1.0.5 cookie: 0.7.2 @@ -4027,11 +4032,11 @@ snapshots: http-errors: 2.0.1 merge-descriptors: 1.0.3 methods: 1.1.2 - on-finished: 2.4.1 + on-finished: 2.4.2 parseurl: 1.3.3 path-to-regexp: 0.1.13 proxy-addr: 2.0.8 - qs: 6.16.0 + qs: 6.16.1 range-parser: 1.2.1 safe-buffer: 5.2.1 send: 0.19.2 @@ -4044,12 +4049,12 @@ snapshots: transitivePeerDependencies: - supports-color - express@5.2.1: + express@5.3.0: dependencies: accepts: 2.0.0 - body-parser: 2.3.0 - content-disposition: 1.1.0 - content-type: 1.0.5 + body-parser: 2.4.0 + content-disposition: 2.0.1 + content-type: 2.1.0 cookie: 0.7.2 cookie-signature: 1.2.2 debug: 4.4.3 @@ -4062,11 +4067,11 @@ snapshots: http-errors: 2.0.1 merge-descriptors: 2.0.0 mime-types: 3.0.2 - on-finished: 2.4.1 + on-finished: 2.4.2 once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.8 - qs: 6.16.0 + qs: 6.16.1 range-parser: 1.3.0 router: 2.2.0 send: 1.2.1 @@ -4116,7 +4121,7 @@ snapshots: file-type@21.3.4: dependencies: '@tokenizer/inflate': 0.4.1 - strtok3: 10.3.5 + strtok3: 10.6.2 token-types: 6.1.2 uint8array-extras: 1.6.0 transitivePeerDependencies: @@ -4131,7 +4136,7 @@ snapshots: debug: 2.6.9 encodeurl: 2.0.0 escape-html: 1.0.3 - on-finished: 2.4.1 + on-finished: 2.4.2 parseurl: 1.3.3 statuses: 2.0.2 unpipe: 1.0.0 @@ -4143,7 +4148,7 @@ snapshots: debug: 4.4.3 encodeurl: 2.0.0 escape-html: 1.0.3 - on-finished: 2.4.1 + on-finished: 2.4.2 parseurl: 1.3.3 statuses: 2.0.2 transitivePeerDependencies: @@ -4170,7 +4175,7 @@ snapshots: flatted@3.4.4: {} - follow-redirects@1.16.0: {} + follow-redirects@1.16.1: {} for-each@0.3.5: dependencies: @@ -4401,7 +4406,7 @@ snapshots: ignore@5.3.2: {} - ignore@7.0.11: {} + ignore@7.0.14: {} import-fresh@3.3.1: dependencies: @@ -4431,7 +4436,7 @@ snapshots: from2: 2.3.0 p-is-promise: 3.0.0 - ip-address@10.7.2: {} + ip-address@10.7.4: {} ipaddr.js@1.9.1: {} @@ -4805,7 +4810,7 @@ snapshots: debug: 4.4.3 file-type: 21.3.4 media-typer: 2.0.0 - strtok3: 10.3.5 + strtok3: 10.6.2 token-types: 6.1.2 uint8array-extras: 1.6.0 win-guid: 0.2.1 @@ -4826,7 +4831,7 @@ snapshots: next-tick@1.1.0: {} - node-abi@3.96.0: + node-abi@3.101.0: dependencies: semver: 7.8.5 @@ -4878,7 +4883,7 @@ snapshots: on-exit-leak-free@0.2.0: {} - on-finished@2.4.1: + on-finished@2.4.2: dependencies: ee-first: 1.1.1 @@ -5051,7 +5056,7 @@ snapshots: multistream: 4.1.0 pkg-fetch: 3.4.2 prebuild-install: 7.1.1 - resolve: 1.22.12 + resolve: 1.22.13 stream-meter: 1.0.4 transitivePeerDependencies: - encoding @@ -5135,7 +5140,7 @@ snapshots: minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 1.0.2 - node-abi: 3.96.0 + node-abi: 3.101.0 pump: 3.0.4 rc: 1.2.8 simple-get: 4.0.1 @@ -5150,7 +5155,7 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier@3.9.9: {} + prettier@3.9.10: {} process-nextick-args@2.0.1: {} @@ -5184,7 +5189,7 @@ snapshots: pngjs: 5.0.0 yargs: 15.4.1 - qs@6.16.0: + qs@6.16.1: dependencies: es-define-property: 1.0.1 side-channel: 1.1.1 @@ -5274,7 +5279,7 @@ snapshots: resolve-from@4.0.0: {} - resolve@1.22.12: + resolve@1.22.13: dependencies: es-errors: 1.3.0 is-core-module: 2.17.0 @@ -5357,7 +5362,7 @@ snapshots: http-errors: 2.0.1 mime: 1.6.0 ms: 2.1.3 - on-finished: 2.4.1 + on-finished: 2.4.2 range-parser: 1.2.1 statuses: 2.0.2 transitivePeerDependencies: @@ -5373,7 +5378,7 @@ snapshots: http-errors: 2.0.1 mime-types: 3.0.2 ms: 2.1.3 - on-finished: 2.4.1 + on-finished: 2.4.2 range-parser: 1.3.0 statuses: 2.0.2 transitivePeerDependencies: @@ -5499,7 +5504,7 @@ snapshots: socks@2.8.10: dependencies: - ip-address: 10.7.2 + ip-address: 10.7.4 smart-buffer: 4.2.0 sonic-boom@1.4.1: @@ -5601,7 +5606,7 @@ snapshots: stringifier@1.4.1: dependencies: core-js: 2.6.12 - traverse: 0.6.11 + traverse: 0.6.14 type-name: 2.0.2 strip-ansi@6.0.1: @@ -5616,7 +5621,7 @@ snapshots: strip-json-comments@3.1.1: {} - strtok3@10.3.5: + strtok3@10.6.2: dependencies: '@tokenizer/token': 0.3.0 @@ -5636,8 +5641,9 @@ snapshots: symbol-tree@3.2.4: {} - synckit@0.11.13: + synckit@0.12.3: dependencies: + '@dual-bundle/import-meta-resolve': 4.2.1 '@pkgr/core': 0.3.6 tar-fs@2.1.5: @@ -5691,10 +5697,12 @@ snapshots: dependencies: punycode: 2.3.1 - traverse@0.6.11: + traverse@0.6.14: dependencies: + define-data-property: 1.1.4 + es-to-primitive: 1.3.4 gopd: 1.2.0 - typedarray.prototype.slice: 1.0.5 + typedarray.prototype.slice: 1.0.6 which-typed-array: 1.1.24 ts-api-utils@2.5.0(typescript@5.9.3): @@ -5764,7 +5772,7 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typedarray.prototype.slice@1.0.5: + typedarray.prototype.slice@1.0.6: dependencies: call-bind: 1.0.9 define-properties: 1.2.1