refactor: 重写checkToken V2模块

This commit is contained in:
ElyPrism 2026-08-07 11:52:54 +08:00
parent 7753b5de41
commit c4a3d83abe
No known key found for this signature in database
7 changed files with 598 additions and 65 deletions

View File

@ -239,7 +239,7 @@ module.exports = async (query, request) => {
const res = await request( const res = await request(
`/api/ad/listening/rights/gain`, `/api/ad/listening/rights/gain`,
data, data,
createOption(query, 'xeapi', 'v2'), createOption(query, 'eapi', 'v2'),
) )
return { return {

View File

@ -19,6 +19,6 @@ module.exports = (query, request) => {
return request( return request(
`/api/middle/play/do/lottery`, `/api/middle/play/do/lottery`,
data, data,
createOption(query, 'eapi', 'v3'), createOption(query, 'eapi', 'v2'),
) )
} }

View File

@ -1,33 +1,126 @@
// 易盾反作弊 Token 注册端点 // 易盾反作弊 Token 注册端点
// 调用后获取实时 token 并存入共享存储,供后续带 checkToken 的请求使用 // 通过易盾官方 Watchman SDKWeb 版,跑在 jsdom 模拟的浏览器环境里)
// 实时调用 getToken(businessId) 获取反作弊 token供后续带 checkToken 的请求使用
// //
// GET /register/checktoken → 实时获取新 token不缓存 // GET /register/checktoken/v2 → 实时获取新 token不缓存
// POST /register/checktoken → 实时获取新 token // POST /register/checktoken/v2 → 实时获取新 token
//
// 原理:加载 tool.min.js 初始化 Watchman
// 再调用 wm.getToken(businessId) 拿 token。watchman 内部会 POST /v3/d + /v3/b
// 并携带加密的设备指纹与签名,无法用普通 HTTP 复刻,故走完整 SDK。
// //
// 注意:每次获取都不缓存,模拟真实客户端每次请求使用新鲜 token // 注意:每次获取都不缓存,模拟真实客户端每次请求使用新鲜 token
// 避免反作弊 token 复用触发风控。 // 避免反作弊 token 复用触发风控。
// //
const { JSDOM, VirtualConsole } = require('jsdom')
const { default: axios } = require('axios') const { default: axios } = require('axios')
const { APP_CONF } = require('../util/config.json') const { APP_CONF } = require('../util/config.json')
const logger = require('../util/logger')
const URL = APP_CONF.dunDomainV2 + '/v2/config/js?pn=YD00000558929251' // 网易云音乐在易盾的 productNumber 与 businessId
const PRODUCT_NUMBER = 'YD00000558929251'
const BUSINESS_ID = 'bd5d2f973ef74cd2a61325a412ae54d9'
const TOOL_JS_URL = `${APP_CONF.dunStaticDomain}/tool.min.js`
async function fetch() { // 最小 HTML 外壳,模拟网页环境
const res = await axios.get(URL, { timeout: 10000 }) const HTML =
const data = res.data '<!doctype html><html><head><meta charset="UTF-8"></head><body></body></html>'
if (data && data.code === 200 && data.result && data.result.conf) {
return data.result.conf let toolJs = ''
let wm = null // Watchman 实例(进程内复用,可反复 getToken
let initPromise = null
// 获取 tool.min.js内存缓存避免每次初始化重复下载
async function getToolJs() {
if (toolJs) return toolJs
const res = await axios.get(TOOL_JS_URL, { timeout: 10000 })
toolJs = String(res.data)
return toolJs
}
// 初始化 Watchman进程内只初始化一次实例可反复 getToken
async function ensureWatchman() {
if (wm) return wm
if (initPromise) return initPromise
initPromise = (async () => {
const js = await getToolJs()
const virtualConsole = new VirtualConsole()
virtualConsole.on('jsdomError', () => {})
const dom = new JSDOM(HTML, {
url: 'https://music.163.com/',
referrer: 'https://music.163.com/',
contentType: 'text/html',
runScripts: 'dangerously',
resources: 'usable', // 允许动态加载 watchman.min.js / JSONP
pretendToBeVisual: true,
virtualConsole,
beforeParse(window) {
// 抹掉 headless 特征,避免易盾风控误判
Object.defineProperty(window.navigator, 'webdriver', {
get: () => undefined,
})
window.chrome = { runtime: {} }
window.navigator.languages = ['zh-CN', 'zh']
window.navigator.plugins = [1, 2, 3, 4, 5]
},
})
const script = dom.window.document.createElement('script')
script.textContent = js
dom.window.document.body.appendChild(script)
return new Promise((resolve, reject) => {
const timer = setTimeout(
() => reject(new Error('watchman 初始化超时')),
15000,
)
dom.window.initWatchman({
auto: true,
productNumber: PRODUCT_NUMBER,
onload(instance) {
clearTimeout(timer)
wm = instance
resolve(instance)
},
onerror(...args) {
clearTimeout(timer)
reject(new Error('watchman 初始化失败'))
},
})
})
})()
try {
return await initPromise
} catch (e) {
// 初始化失败后允许下次重试
initPromise = null
wm = null
throw e
} }
throw new Error('易盾返回异常: ' + JSON.stringify(data).substring(0, 200)) }
// 获取新 token
async function fetchToken() {
const instance = await ensureWatchman()
return new Promise((resolve) => {
const timer = setTimeout(() => resolve(''), 15000)
instance.getToken(BUSINESS_ID, (tk) => {
clearTimeout(timer)
resolve(typeof tk === 'string' ? tk : '')
})
})
} }
// 端点处理:每次实时获取新 token // 端点处理:每次实时获取新 token
module.exports = async () => { module.exports = async () => {
let token = '' let token = ''
try { try {
token = await fetch() token = await fetchToken()
} catch (e) { } catch (e) {
// token 获取失败时返回空,由调用方决定是否重试 // token 获取失败时返回空,由调用方决定是否重试
logger.warn('[checkToken v2]', e.message)
} }
return { return {
status: 200, status: 200,
@ -38,8 +131,9 @@ module.exports = async () => {
// 给 request.js 读取用:每次调用实时获取新 token不缓存 // 给 request.js 读取用:每次调用实时获取新 token不缓存
module.exports.getToken = async () => { module.exports.getToken = async () => {
try { try {
return await fetch() return await fetchToken()
} catch (e) { } catch (e) {
logger.warn('[checkToken v2]', e.message)
return '' return ''
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@neteasecloudmusicapienhanced/api", "name": "@neteasecloudmusicapienhanced/api",
"version": "4.39.0", "version": "4.40.0",
"description": "全网最全的网易云音乐API接口 || A revival project for NeteaseCloudMusicApi Node.js Services (Half Refactor & Enhanced) || 网易云音乐 API 备份 + 增强 || 本项目自原版v4.28.0版本后开始自行维护", "description": "全网最全的网易云音乐API接口 || A revival project for NeteaseCloudMusicApi Node.js Services (Half Refactor & Enhanced) || 网易云音乐 API 备份 + 增强 || 本项目自原版v4.28.0版本后开始自行维护",
"scripts": { "scripts": {
"dev": "nodemon app.js", "dev": "nodemon app.js",
@ -82,6 +82,7 @@
"express": "^5.2.1", "express": "^5.2.1",
"express-fileupload": "^1.5.2", "express-fileupload": "^1.5.2",
"gzip": "^0.1.0", "gzip": "^0.1.0",
"jsdom": "^30.0.1",
"music-metadata": "^11.14.0", "music-metadata": "^11.14.0",
"node-forge": "^1.4.0", "node-forge": "^1.4.0",
"pac-proxy-agent": "^7.2.0", "pac-proxy-agent": "^7.2.0",

326
pnpm-lock.yaml generated
View File

@ -29,6 +29,9 @@ importers:
gzip: gzip:
specifier: ^0.1.0 specifier: ^0.1.0
version: 0.1.0 version: 0.1.0
jsdom:
specifier: ^30.0.1
version: 30.0.1
music-metadata: music-metadata:
specifier: ^11.14.0 specifier: ^11.14.0
version: 11.14.0 version: 11.14.0
@ -123,6 +126,14 @@ importers:
packages: packages:
'@asamuzakjp/css-color@6.0.5':
resolution: {integrity: sha512-mbhpPMmnw/kwW19aRNmSUl1QzLbdGo1SCuE49BT98MNwqF6zaHb3o2owssFc/PEO/4t2UjqtCNwocuDtJornzA==}
engines: {node: ^22.13.0 || >=24.0.0}
'@asamuzakjp/dom-selector@8.3.2':
resolution: {integrity: sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q==}
engines: {node: ^22.13.0 || >=24.0.0}
'@babel/generator@7.18.2': '@babel/generator@7.18.2':
resolution: {integrity: sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==} resolution: {integrity: sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
@ -147,6 +158,46 @@ packages:
'@borewit/text-codec@0.2.2': '@borewit/text-codec@0.2.2':
resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==}
'@bramus/specificity@2.4.2':
resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==}
hasBin: true
'@csstools/color-helpers@6.1.0':
resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==}
engines: {node: '>=20.19.0'}
'@csstools/css-calc@3.3.0':
resolution: {integrity: sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==}
engines: {node: '>=20.19.0'}
peerDependencies:
'@csstools/css-parser-algorithms': ^4.0.0
'@csstools/css-tokenizer': ^4.0.0
'@csstools/css-color-parser@4.1.10':
resolution: {integrity: sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==}
engines: {node: '>=20.19.0'}
peerDependencies:
'@csstools/css-parser-algorithms': ^4.0.0
'@csstools/css-tokenizer': ^4.0.0
'@csstools/css-parser-algorithms@4.0.0':
resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==}
engines: {node: '>=20.19.0'}
peerDependencies:
'@csstools/css-tokenizer': ^4.0.0
'@csstools/css-syntax-patches-for-csstree@1.1.7':
resolution: {integrity: sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==}
peerDependencies:
css-tree: ^3.2.1
peerDependenciesMeta:
css-tree:
optional: true
'@csstools/css-tokenizer@4.0.0':
resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==}
engines: {node: '>=20.19.0'}
'@eslint-community/eslint-utils@4.10.1': '@eslint-community/eslint-utils@4.10.1':
resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -185,6 +236,15 @@ packages:
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@exodus/bytes@1.15.1':
resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
peerDependencies:
'@noble/hashes': ^1.8.0 || ^2.0.0
peerDependenciesMeta:
'@noble/hashes':
optional: true
'@humanfs/core@0.19.2': '@humanfs/core@0.19.2':
resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==}
engines: {node: '>=18.18.0'} engines: {node: '>=18.18.0'}
@ -502,6 +562,9 @@ packages:
resolution: {integrity: sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==} resolution: {integrity: sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
bidi-js@1.0.3:
resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
binary-extensions@2.3.0: binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -692,6 +755,10 @@ packages:
crypto-js@4.2.0: crypto-js@4.2.0:
resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
css-tree@3.2.1:
resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
d@1.0.2: d@1.0.2:
resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==}
engines: {node: '>=0.12'} engines: {node: '>=0.12'}
@ -700,6 +767,10 @@ packages:
resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==}
engines: {node: '>= 14'} engines: {node: '>= 14'}
data-urls@7.0.0:
resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
data-view-buffer@1.0.2: data-view-buffer@1.0.2:
resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -740,6 +811,9 @@ packages:
resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
decimal.js@10.6.0:
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
decompress-response@6.0.0: decompress-response@6.0.0:
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -860,6 +934,10 @@ packages:
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
engines: {node: '>=0.12'} engines: {node: '>=0.12'}
entities@8.0.0:
resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==}
engines: {node: '>=20.19.0'}
environment@1.1.0: environment@1.1.0:
resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -1323,6 +1401,10 @@ packages:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true hasBin: true
html-encoding-sniffer@6.0.0:
resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
htmlparser2@10.1.0: htmlparser2@10.1.0:
resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==}
@ -1503,6 +1585,9 @@ packages:
resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==}
engines: {node: '>=8'} engines: {node: '>=8'}
is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
is-promise@4.0.0: is-promise@4.0.0:
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
@ -1569,6 +1654,15 @@ packages:
resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==}
hasBin: true hasBin: true
jsdom@30.0.1:
resolution: {integrity: sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==}
engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0}
peerDependencies:
canvas: ^3.2.3
peerDependenciesMeta:
canvas:
optional: true
jsesc@2.5.2: jsesc@2.5.2:
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -1632,10 +1726,17 @@ packages:
lru-cache@10.4.3: lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
lru-cache@11.5.2:
resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==}
engines: {node: 20 || >=22}
math-intrinsics@1.1.0: math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
mdn-data@2.27.1:
resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
media-typer@0.3.0: media-typer@0.3.0:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
@ -1876,6 +1977,9 @@ packages:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'} engines: {node: '>=6'}
parse5@8.0.1:
resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==}
parseurl@1.3.3: parseurl@1.3.3:
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
@ -2104,6 +2208,10 @@ packages:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
require-from-string@2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
require-main-filename@2.0.0: require-main-filename@2.0.0:
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
@ -2162,6 +2270,10 @@ packages:
resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==} resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==}
engines: {node: '>=11.0.0'} engines: {node: '>=11.0.0'}
saxes@6.0.0:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
engines: {node: '>=v12.22.7'}
secure-json-parse@2.7.0: secure-json-parse@2.7.0:
resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
@ -2275,6 +2387,10 @@ packages:
sonic-boom@2.8.0: sonic-boom@2.8.0:
resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==} resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
source-map-support@0.4.18: source-map-support@0.4.18:
resolution: {integrity: sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==} resolution: {integrity: sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==}
@ -2389,6 +2505,9 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
synckit@0.11.13: synckit@0.11.13:
resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==}
engines: {node: ^14.18.0 || >=16.0.0} engines: {node: ^14.18.0 || >=16.0.0}
@ -2408,6 +2527,13 @@ packages:
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
tldts-core@7.4.10:
resolution: {integrity: sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw==}
tldts@7.4.10:
resolution: {integrity: sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==}
hasBin: true
to-fast-properties@2.0.0: to-fast-properties@2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -2428,9 +2554,17 @@ packages:
resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
hasBin: true hasBin: true
tough-cookie@6.0.2:
resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==}
engines: {node: '>=16'}
tr46@0.0.3: tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
tr46@6.0.0:
resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
engines: {node: '>=20'}
traverse@0.6.11: traverse@0.6.11:
resolution: {integrity: sha512-vxXDZg8/+p3gblxB6BhhG5yWVn1kGRlaL8O78UDXc3wRnPizB5g83dcvWV1jpDMIPnjZjOFuxlMmE82XJ4407w==} resolution: {integrity: sha512-vxXDZg8/+p3gblxB6BhhG5yWVn1kGRlaL8O78UDXc3wRnPizB5g83dcvWV1jpDMIPnjZjOFuxlMmE82XJ4407w==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -2512,6 +2646,10 @@ packages:
undici-types@7.24.6: undici-types@7.24.6:
resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
undici@8.10.0:
resolution: {integrity: sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==}
engines: {node: '>=22.19.0'}
universal-deep-strict-equal@1.2.2: universal-deep-strict-equal@1.2.2:
resolution: {integrity: sha512-UpnFi3/IF3jZHIHTdQXTHLCqpBP3805OFFRPHgvCS7k0oob2YVXxMTjS0U0g9qJTzqFRMwEnFFSlFLqt6zwjTQ==} resolution: {integrity: sha512-UpnFi3/IF3jZHIHTdQXTHLCqpBP3805OFFRPHgvCS7k0oob2YVXxMTjS0U0g9qJTzqFRMwEnFFSlFLqt6zwjTQ==}
@ -2537,9 +2675,29 @@ packages:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
w3c-xmlserializer@5.0.0:
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
engines: {node: '>=18'}
webidl-conversions@3.0.1: webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
webidl-conversions@8.0.1:
resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
engines: {node: '>=20'}
whatwg-mimetype@5.0.0:
resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==}
engines: {node: '>=20'}
whatwg-url@16.0.1:
resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
whatwg-url@17.1.0:
resolution: {integrity: sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw==}
engines: {node: ^22.14.0 || >=24.0.0}
whatwg-url@5.0.0: whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
@ -2596,6 +2754,10 @@ packages:
wrappy@1.0.2: wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
xml-name-validator@5.0.0:
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
engines: {node: '>=18'}
xml2js@0.6.2: xml2js@0.6.2:
resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==}
engines: {node: '>=4.0.0'} engines: {node: '>=4.0.0'}
@ -2607,6 +2769,9 @@ packages:
resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
engines: {node: '>=4.0'} engines: {node: '>=4.0'}
xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
xtend@4.0.2: xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'} engines: {node: '>=0.4'}
@ -2665,6 +2830,21 @@ packages:
snapshots: snapshots:
'@asamuzakjp/css-color@6.0.5':
dependencies:
'@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
'@csstools/css-color-parser': 4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
'@csstools/css-tokenizer': 4.0.0
lru-cache: 11.5.2
'@asamuzakjp/dom-selector@8.3.2':
dependencies:
bidi-js: 1.0.3
css-tree: 3.2.1
is-potential-custom-element-name: 1.0.1
lru-cache: 11.5.2
'@babel/generator@7.18.2': '@babel/generator@7.18.2':
dependencies: dependencies:
'@babel/types': 7.19.0 '@babel/types': 7.19.0
@ -2687,6 +2867,34 @@ snapshots:
'@borewit/text-codec@0.2.2': {} '@borewit/text-codec@0.2.2': {}
'@bramus/specificity@2.4.2':
dependencies:
css-tree: 3.2.1
'@csstools/color-helpers@6.1.0': {}
'@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
dependencies:
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
'@csstools/css-tokenizer': 4.0.0
'@csstools/css-color-parser@4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
dependencies:
'@csstools/color-helpers': 6.1.0
'@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
'@csstools/css-tokenizer': 4.0.0
'@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)':
dependencies:
'@csstools/css-tokenizer': 4.0.0
'@csstools/css-syntax-patches-for-csstree@1.1.7(css-tree@3.2.1)':
optionalDependencies:
css-tree: 3.2.1
'@csstools/css-tokenizer@4.0.0': {}
'@eslint-community/eslint-utils@4.10.1(eslint@9.39.5)': '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5)':
dependencies: dependencies:
eslint: 9.39.5 eslint: 9.39.5
@ -2733,6 +2941,8 @@ snapshots:
'@eslint/core': 0.17.0 '@eslint/core': 0.17.0
levn: 0.4.1 levn: 0.4.1
'@exodus/bytes@1.15.1': {}
'@humanfs/core@0.19.2': '@humanfs/core@0.19.2':
dependencies: dependencies:
'@humanfs/types': 0.15.0 '@humanfs/types': 0.15.0
@ -3090,6 +3300,10 @@ snapshots:
basic-ftp@5.3.1: {} basic-ftp@5.3.1: {}
bidi-js@1.0.3:
dependencies:
require-from-string: 2.0.2
binary-extensions@2.3.0: {} binary-extensions@2.3.0: {}
bl@4.1.0: bl@4.1.0:
@ -3307,6 +3521,11 @@ snapshots:
crypto-js@4.2.0: {} crypto-js@4.2.0: {}
css-tree@3.2.1:
dependencies:
mdn-data: 2.27.1
source-map-js: 1.2.1
d@1.0.2: d@1.0.2:
dependencies: dependencies:
es5-ext: 0.10.64 es5-ext: 0.10.64
@ -3314,6 +3533,13 @@ snapshots:
data-uri-to-buffer@6.0.2: {} data-uri-to-buffer@6.0.2: {}
data-urls@7.0.0:
dependencies:
whatwg-mimetype: 5.0.0
whatwg-url: 16.0.1
transitivePeerDependencies:
- '@noble/hashes'
data-view-buffer@1.0.2: data-view-buffer@1.0.2:
dependencies: dependencies:
call-bound: 1.0.4 call-bound: 1.0.4
@ -3358,6 +3584,8 @@ snapshots:
decamelize@4.0.0: {} decamelize@4.0.0: {}
decimal.js@10.6.0: {}
decompress-response@6.0.0: decompress-response@6.0.0:
dependencies: dependencies:
mimic-response: 3.1.0 mimic-response: 3.1.0
@ -3478,6 +3706,8 @@ snapshots:
entities@7.0.1: {} entities@7.0.1: {}
entities@8.0.0: {}
environment@1.1.0: {} environment@1.1.0: {}
es-abstract-get@1.0.0: es-abstract-get@1.0.0:
@ -4135,6 +4365,12 @@ snapshots:
he@1.2.0: {} he@1.2.0: {}
html-encoding-sniffer@6.0.0:
dependencies:
'@exodus/bytes': 1.15.1
transitivePeerDependencies:
- '@noble/hashes'
htmlparser2@10.1.0: htmlparser2@10.1.0:
dependencies: dependencies:
domelementtype: 2.3.0 domelementtype: 2.3.0
@ -4319,6 +4555,8 @@ snapshots:
is-plain-obj@2.1.0: {} is-plain-obj@2.1.0: {}
is-potential-custom-element-name@1.0.1: {}
is-promise@4.0.0: {} is-promise@4.0.0: {}
is-regex@1.2.1: is-regex@1.2.1:
@ -4382,6 +4620,32 @@ snapshots:
dependencies: dependencies:
argparse: 2.0.1 argparse: 2.0.1
jsdom@30.0.1:
dependencies:
'@asamuzakjp/css-color': 6.0.5
'@asamuzakjp/dom-selector': 8.3.2
'@bramus/specificity': 2.4.2
'@csstools/css-syntax-patches-for-csstree': 1.1.7(css-tree@3.2.1)
'@exodus/bytes': 1.15.1
css-tree: 3.2.1
data-urls: 7.0.0
decimal.js: 10.6.0
html-encoding-sniffer: 6.0.0
is-potential-custom-element-name: 1.0.1
lru-cache: 11.5.2
parse5: 8.0.1
saxes: 6.0.0
symbol-tree: 3.2.4
tough-cookie: 6.0.2
undici: 8.10.0
w3c-xmlserializer: 5.0.0
webidl-conversions: 8.0.1
whatwg-mimetype: 5.0.0
whatwg-url: 17.1.0
xml-name-validator: 5.0.0
transitivePeerDependencies:
- '@noble/hashes'
jsesc@2.5.2: {} jsesc@2.5.2: {}
json-buffer@3.0.1: {} json-buffer@3.0.1: {}
@ -4455,8 +4719,12 @@ snapshots:
lru-cache@10.4.3: {} lru-cache@10.4.3: {}
lru-cache@11.5.2: {}
math-intrinsics@1.1.0: {} math-intrinsics@1.1.0: {}
mdn-data@2.27.1: {}
media-typer@0.3.0: {} media-typer@0.3.0: {}
media-typer@1.1.1: {} media-typer@1.1.1: {}
@ -4713,6 +4981,10 @@ snapshots:
dependencies: dependencies:
callsites: 3.1.0 callsites: 3.1.0
parse5@8.0.1:
dependencies:
entities: 8.0.0
parseurl@1.3.3: {} parseurl@1.3.3: {}
path-exists@4.0.0: {} path-exists@4.0.0: {}
@ -5012,6 +5284,8 @@ snapshots:
require-directory@2.1.1: {} require-directory@2.1.1: {}
require-from-string@2.0.2: {}
require-main-filename@2.0.0: {} require-main-filename@2.0.0: {}
resolve-from@4.0.0: {} resolve-from@4.0.0: {}
@ -5075,6 +5349,10 @@ snapshots:
sax@1.6.1: {} sax@1.6.1: {}
saxes@6.0.0:
dependencies:
xmlchars: 2.2.0
secure-json-parse@2.7.0: {} secure-json-parse@2.7.0: {}
semver@7.8.5: {} semver@7.8.5: {}
@ -5245,6 +5523,8 @@ snapshots:
dependencies: dependencies:
atomic-sleep: 1.0.0 atomic-sleep: 1.0.0
source-map-js@1.2.1: {}
source-map-support@0.4.18: source-map-support@0.4.18:
dependencies: dependencies:
source-map: 0.5.7 source-map: 0.5.7
@ -5368,6 +5648,8 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {} supports-preserve-symlinks-flag@1.0.0: {}
symbol-tree@3.2.4: {}
synckit@0.11.13: synckit@0.11.13:
dependencies: dependencies:
'@pkgr/core': 0.3.6 '@pkgr/core': 0.3.6
@ -5394,6 +5676,12 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.5) fdir: 6.5.0(picomatch@4.0.5)
picomatch: 4.0.5 picomatch: 4.0.5
tldts-core@7.4.10: {}
tldts@7.4.10:
dependencies:
tldts-core: 7.4.10
to-fast-properties@2.0.0: {} to-fast-properties@2.0.0: {}
to-regex-range@5.0.1: to-regex-range@5.0.1:
@ -5410,8 +5698,16 @@ snapshots:
touch@3.1.1: {} touch@3.1.1: {}
tough-cookie@6.0.2:
dependencies:
tldts: 7.4.10
tr46@0.0.3: {} tr46@0.0.3: {}
tr46@6.0.0:
dependencies:
punycode: 2.3.1
traverse@0.6.11: traverse@0.6.11:
dependencies: dependencies:
gopd: 1.2.0 gopd: 1.2.0
@ -5512,6 +5808,8 @@ snapshots:
undici-types@7.24.6: {} undici-types@7.24.6: {}
undici@8.10.0: {}
universal-deep-strict-equal@1.2.2: universal-deep-strict-equal@1.2.2:
dependencies: dependencies:
array-filter: 1.0.0 array-filter: 1.0.0
@ -5532,8 +5830,32 @@ snapshots:
vary@1.1.2: {} vary@1.1.2: {}
w3c-xmlserializer@5.0.0:
dependencies:
xml-name-validator: 5.0.0
webidl-conversions@3.0.1: {} webidl-conversions@3.0.1: {}
webidl-conversions@8.0.1: {}
whatwg-mimetype@5.0.0: {}
whatwg-url@16.0.1:
dependencies:
'@exodus/bytes': 1.15.1
tr46: 6.0.0
webidl-conversions: 8.0.1
transitivePeerDependencies:
- '@noble/hashes'
whatwg-url@17.1.0:
dependencies:
'@exodus/bytes': 1.15.1
tr46: 6.0.0
webidl-conversions: 8.0.1
transitivePeerDependencies:
- '@noble/hashes'
whatwg-url@5.0.0: whatwg-url@5.0.0:
dependencies: dependencies:
tr46: 0.0.3 tr46: 0.0.3
@ -5618,6 +5940,8 @@ snapshots:
wrappy@1.0.2: {} wrappy@1.0.2: {}
xml-name-validator@5.0.0: {}
xml2js@0.6.2: xml2js@0.6.2:
dependencies: dependencies:
sax: 1.6.1 sax: 1.6.1
@ -5627,6 +5951,8 @@ snapshots:
xmlbuilder@11.0.1: {} xmlbuilder@11.0.1: {}
xmlchars@2.2.0: {}
xtend@4.0.2: {} xtend@4.0.2: {}
y18n@4.0.3: {} y18n@4.0.3: {}

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Token 注册器 — 网易云音乐 API Enhanced</title> <title>易盾 Watchman 调试器 — 网易云音乐 API Enhanced</title>
<style> <style>
:root { :root {
--fg: #333; --fg: #333;
@ -17,7 +17,7 @@
* { box-sizing: border-box; } * { box-sizing: border-box; }
html, body { height: 100%; } html, body { height: 100%; }
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: var(--fg); background: var(--bg); line-height: 1.6; } body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: var(--fg); background: var(--bg); line-height: 1.6; }
.container { max-width: 720px; margin: 40px auto; padding: 0 20px; } .container { max-width: 760px; margin: 40px auto; padding: 0 20px; }
@media (max-width: 480px) { @media (max-width: 480px) {
.container { margin: 20px auto; padding: 0 16px; } .container { margin: 20px auto; padding: 0 16px; }
header.site-header h1 { font-size: 22px; } header.site-header h1 { font-size: 22px; }
@ -28,8 +28,12 @@
.sub { margin-top: 4px; color: var(--muted); font-size: 14px; } .sub { margin-top: 4px; color: var(--muted); font-size: 14px; }
.block { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 20px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); } .block { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 20px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); }
.block h3 { margin: 0 0 12px; font-size: 15px; font-weight: 600; } .block h3 { margin: 0 0 12px; font-size: 15px; font-weight: 600; }
pre { margin: 0; background: #f9f9f9; border: 1px solid var(--border); border-radius: 6px; padding: 12px; overflow-x: auto; white-space: pre-wrap; word-break: break-all; font-family: 'Courier New', monospace; font-size: 13px; max-height: 160px; overflow: auto; } .field { margin-bottom: 10px; }
.row { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; } .field label { display: block; font-size: 13px; color: var(--muted); margin-bottom: 4px; }
input[type="text"] { width: 100%; padding: 8px 12px; border: 1px solid var(--border); border-radius: 8px; font-size: 13px; font-family: 'Courier New', monospace; color: var(--fg); background: #fafafa; }
input[type="text"]:focus { outline: none; border-color: #888; background: #fff; }
pre { margin: 0; background: #f9f9f9; border: 1px solid var(--border); border-radius: 6px; padding: 12px; overflow-x: auto; white-space: pre-wrap; word-break: break-all; font-family: 'Courier New', monospace; font-size: 13px; max-height: 220px; overflow: auto; }
.row { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; align-items: center; }
button { padding: 8px 18px; border-radius: 8px; border: 1px solid var(--border); font-size: 14px; font-weight: 500; cursor: pointer; background: var(--panel); color: var(--fg); transition: all .15s; } button { padding: 8px 18px; border-radius: 8px; border: 1px solid var(--border); font-size: 14px; font-weight: 500; cursor: pointer; background: var(--panel); color: var(--fg); transition: all .15s; }
button:hover { background: #eee; } button:hover { background: #eee; }
button.primary { background: var(--fg); color: var(--panel); border-color: var(--fg); } button.primary { background: var(--fg); color: var(--panel); border-color: var(--fg); }
@ -41,32 +45,65 @@
.tag.busy { background: #fff3cd; color: #856404; border-color: #ffeeba; } .tag.busy { background: #fff3cd; color: #856404; border-color: #ffeeba; }
.flex { display: flex; align-items: center; gap: 10px; } .flex { display: flex; align-items: center; gap: 10px; }
.flex.sb { justify-content: space-between; } .flex.sb { justify-content: space-between; }
.mt-2 { margin-top: 14px; } .hint { font-size: 12px; color: var(--muted); margin-top: 8px; }
footer.site-footer { margin-top: 24px; padding-top: 12px; border-top: 1px solid var(--border); color: var(--muted); text-align: center; font-size: 13px; } footer.site-footer { margin-top: 24px; padding-top: 12px; border-top: 1px solid var(--border); color: var(--muted); text-align: center; font-size: 13px; }
</style> </style>
</head> </head>
<body> <body>
<main class="container"> <main class="container">
<header class="site-header"> <header class="site-header">
<h1>🔑 Token 注册器</h1> <h1>🛡️ 易盾 Watchman 调试器</h1>
<p class="sub">从易盾获取反作弊 token 并注册到服务器,供广告等需要 checkToken 的接口使用</p> <p class="sub">
直接调用易盾官方 Web SDKtool.min.js获取反作弊 token。
<code>module/register_checktoken_v2.js</code> 内部jsdom 模拟同一套 SDK逻辑一致可用于对照验证。
</p>
</header> </header>
<!-- Watchman 初始化 -->
<div class="block"> <div class="block">
<div class="flex sb"> <div class="flex sb">
<h3>当前 Token</h3> <h3>① 初始化 Watchman</h3>
<span id="statusTag" class="tag no">未注册</span> <span id="initTag" class="tag no">未初始化</span>
</div>
<div class="field">
<label for="pnInput">productNumber易盾产品号</label>
<input type="text" id="pnInput" value="YD00000558929251" spellcheck="false">
</div> </div>
<pre id="tokenDisplay">(空)</pre>
<div class="row"> <div class="row">
<button id="btnGet" class="primary">获取 Token</button> <button id="btnInit" class="primary">初始化</button>
<button id="btnRefresh">强制刷新</button> <button id="btnReset">重置</button>
</div> </div>
</div> </div>
<!-- 获取 Token -->
<div class="block"> <div class="block">
<h3>服务器响应</h3> <div class="flex sb">
<pre id="respDisplay">{}</pre> <h3>② 获取 Token</h3>
<span id="getTag" class="tag no"></span>
</div>
<div class="field">
<label for="bidInput">businessId业务 ID来自网易 clientcfg 下发)</label>
<input type="text" id="bidInput" value="bd5d2f973ef74cd2a61325a412ae54d9" spellcheck="false">
</div>
<div class="row">
<button id="btnGet" class="primary" disabled>获取 Token</button>
<button id="btnCopy" disabled>复制</button>
<span class="hint">覆盖 /api/playlist/subscribe、/api/song/like、/api/user/follow 等接口</span>
</div>
<pre id="tokenDisplay">(尚未获取)</pre>
</div>
<!-- 服务器端对照 -->
<div class="block">
<div class="flex sb">
<h3>③ 服务器端对照(/register/checktoken/v2</h3>
<span id="srvTag" class="tag no"></span>
</div>
<div class="row">
<button id="btnSrv">获取服务器端 Token</button>
<span class="hint">node + jsdom 模拟同一套 SDK 的实时结果</span>
</div>
<pre id="srvDisplay">(尚未获取)</pre>
</div> </div>
<footer class="site-footer"> <footer class="site-footer">
@ -74,46 +111,121 @@
</footer> </footer>
</main> </main>
<script src="https://acstatic-dun.126.net/tool.min.js"></script>
<script> <script>
const $ = (id) => document.getElementById(id) const $ = (id) => document.getElementById(id)
const base = location.origin
async function call(refresh) { const DEFAULTS = {
const btn = $('btnGet') pn: 'YD00000558929251',
const btnR = $('btnRefresh') bid: 'bd5d2f973ef74cd2a61325a412ae54d9',
btn.disabled = btnR.disabled = true
const tag = $('statusTag')
tag.className = 'tag busy'
tag.textContent = '获取中…'
try {
const url = base + '/register/checktoken/v2' + (refresh ? '?refresh=1' : '')
const res = await fetch(url)
const data = await res.json()
$('respDisplay').textContent = JSON.stringify(data, null, 2)
if (data.token) {
$('tokenDisplay').textContent = data.token
tag.className = 'tag ok'
tag.textContent = '已注册'
} else {
$('tokenDisplay').textContent = '(获取失败)'
tag.className = 'tag no'
tag.textContent = '失败'
}
} catch (e) {
$('respDisplay').textContent = JSON.stringify({ error: e.message }, null, 2)
tag.className = 'tag no'
tag.textContent = e.message
}
btn.disabled = btnR.disabled = false
} }
$('btnGet').onclick = () => call(false) let wm = null // Watchman 实例
$('btnRefresh').onclick = () => call(true)
call(false) const setTag = (id, text, state) => {
const tag = $(id)
tag.textContent = text
tag.className = 'tag ' + state // ok / no / busy
}
// ① 初始化
function initShield() {
if (typeof window.initWatchman !== 'function') {
alert('未找到 initWatchman请确认 tool.min.js 已加载CDN 是否可访问)')
return
}
const pn = $('pnInput').value.trim() || DEFAULTS.pn
setTag('initTag', '初始化中…', 'busy')
$('btnInit').disabled = true
window.initWatchman({
auto: true,
productNumber: pn,
onload(instance) {
wm = instance
setTag('initTag', '已就绪', 'ok')
$('btnInit').disabled = false
$('btnGet').disabled = false
},
onerror() {
wm = null
setTag('initTag', '初始化失败', 'no')
$('btnInit').disabled = false
$('btnGet').disabled = true
},
})
}
function resetShield() {
wm = null
setTag('initTag', '未初始化', 'no')
setTag('getTag', '—', 'no')
$('btnGet').disabled = true
$('btnCopy').disabled = true
$('tokenDisplay').textContent = '(尚未获取)'
}
// ② 获取 token
function getToken() {
if (!wm) {
alert('请先完成初始化')
return
}
const bid = $('bidInput').value.trim() || DEFAULTS.bid
setTag('getTag', '获取中…', 'busy')
$('btnGet').disabled = true
wm.getToken(bid, (tk) => {
if (typeof tk === 'string' && tk) {
$('tokenDisplay').textContent = tk
setTag('getTag', 'OK', 'ok')
$('btnCopy').disabled = false
} else {
$('tokenDisplay').textContent = '(获取失败)'
setTag('getTag', '失败', 'no')
$('btnCopy').disabled = true
}
$('btnGet').disabled = false
})
}
// ③ 服务器端对照
async function fetchServerToken() {
setTag('srvTag', '获取中…', 'busy')
$('btnSrv').disabled = true
try {
const res = await fetch(
location.origin + '/register/checktoken/v2?t=' + Date.now(),
)
const data = await res.json()
$('srvDisplay').textContent = data.token
? data.token
: JSON.stringify(data, null, 2)
setTag('srvTag', data.token ? 'OK' : '失败', data.token ? 'ok' : 'no')
} catch (e) {
$('srvDisplay').textContent = JSON.stringify({ error: e.message }, null, 2)
setTag('srvTag', e.message, 'no')
}
$('btnSrv').disabled = false
}
// 复制
async function copyToken() {
const txt = $('tokenDisplay').textContent
try {
await navigator.clipboard.writeText(txt)
setTag('getTag', '已复制', 'ok')
} catch (e) {
setTag('getTag', '复制失败', 'no')
}
}
$('btnInit').onclick = initShield
$('btnReset').onclick = resetShield
$('btnGet').onclick = getToken
$('btnCopy').onclick = copyToken
$('btnSrv').onclick = fetchServerToken
// 自动初始化
initShield()
</script> </script>
</body> </body>
</html> </html>

View File

@ -17,7 +17,7 @@
"clDomian": "https://clientlog.music.163.com", "clDomian": "https://clientlog.music.163.com",
"clDomian3": "https://clientlog3.music.163.com", "clDomian3": "https://clientlog3.music.163.com",
"dunDomainV3": "https://ac.dun.163yun.com", "dunDomainV3": "https://ac.dun.163yun.com",
"dunDomainV2": "https://ac.dun.163.com", "dunStaticDomain": "https://acstatic-dun.126.net",
"encrypt": true, "encrypt": true,
"encryptResponse": false, "encryptResponse": false,
"clientSign": "18:C0:4D:B9:8F:FE@@@453832335F384641365F424635335F303030315F303031425F343434415F343643365F333638332@@@@@@6ff673ef74955b38bce2fa8562d95c976ed4758b1227c4e9ee345987cee17bc9", "clientSign": "18:C0:4D:B9:8F:FE@@@453832335F384641365F424635335F303030315F303031425F343434415F343643365F333638332@@@@@@6ff673ef74955b38bce2fa8562d95c976ed4758b1227c4e9ee345987cee17bc9",