/** * 淘宝/天猫 SSR 状态提取 —— 通过 MAIN world 桥读取页面全局变量。 * * 候选键(selectors-taobao.md §5.3 列出的待评估项): * __ICE_APP_CONTEXT__ ICE 框架上下文(loaderData.home.data.res,v1 已知结构)★主 * __general_skupanel_cache_data 疑似完整 SKU 面板缓存(结构未知,容错深搜) * __ICE_DATA_LOADER__ ICE 框架数据层(容错深搜) * * 输出对齐 ssr.ts 的 SSRData,供 buildFromSSR 消费。 */ import type { SSRData } from './ssr'; /** 从 __ICE_APP_CONTEXT__ 结构映射(原 v1 extractSSRData 的对象版) */ function fromIceContext(ctx: unknown): SSRData | null { const res = (ctx as any)?.loaderData?.home?.data?.res; if (!res?.item?.title || !res?.item?.itemId) return null; const industryParams = res.plusViewVO?.industryParamVO; const extensionParams = res.componentsVO?.extensionInfoVO?.infos?.find( (i: any) => i.type === 'BASE_PROPS' ); return { item: { title: res.item.title, itemId: res.item.itemId, images: res.item.images || [], videos: res.item.videos, }, skuBase: res.skuBase, params: { basicParamList: industryParams?.basicParamList || extensionParams?.items || [], enhanceParamList: industryParams?.enhanceParamList || [], }, price: res.componentsVO?.priceVO?.price || res.componentsVO?.priceVO?.extraPrice, }; } /** 容错:未知结构里深搜「SKU props 数组」(元素含 name + values/props 嵰 name/imageUrl) */ function skuPropsFromUnknown(root: unknown): SSRData['skuBase'] | null { const candidates: any[] = []; const collect = (node: unknown, depth = 0) => { if (!node || typeof node !== 'object' || depth > 6 || candidates.length) return; if (Array.isArray(node)) { if ( node.length >= 1 && node.every((x: any) => x && typeof x === 'object' && typeof (x.prop ?? x.name) === 'string' && Array.isArray(x.values ?? x.props)) ) { candidates.push(node.map((x: any) => ({ pid: String(x.pid ?? ''), name: x.prop ?? x.name, values: (x.values ?? x.props).map((v: any) => ({ vid: String(v.vid ?? ''), name: v.name ?? v.valueName ?? '', image: v.image ?? v.imageUrl, })), }))); return; } node.forEach(n => collect(n, depth + 1)); return; } for (const v of Object.values(node as Record)) collect(v, depth + 1); }; collect(root); return candidates.length ? { props: candidates[0] } : null; } /** 容错:深搜参数数组(元素含 propertyName/valueName) */ function paramsFromUnknown(root: unknown): Array<{ propertyName: string; valueName: string }> { const out: Array<{ propertyName: string; valueName: string }> = []; const seen = new Set(); const walk = (node: unknown, depth = 0) => { if (!node || typeof node !== 'object' || depth > 6 || out.length > 60) return; if (Array.isArray(node)) { if (node.length >= 2 && node.every((x: any) => x && typeof x === 'object' && typeof x.propertyName === 'string' && typeof x.valueName === 'string')) { for (const p of node) { const k = `${p.propertyName}=${p.valueName}`; if (!seen.has(k)) { seen.add(k); out.push({ propertyName: p.propertyName, valueName: p.valueName }); } } } node.forEach(n => walk(n, depth + 1)); return; } for (const v of Object.values(node as Record)) walk(v, depth + 1); }; walk(root); return out; } export function taobaoStateFromBridge(keys: Record): SSRData | null { // 主路径:ICE 上下文 const ice = fromIceContext(keys['__ICE_APP_CONTEXT__']); if (ice) { // 主路径缺 SKU 时用面板缓存补 if (!ice.skuBase?.props?.length) { const fromCache = skuPropsFromUnknown(keys['__general_skupanel_cache_data'] ?? keys['__ICE_DATA_LOADER__']); if (fromCache?.props?.length) ice.skuBase = fromCache; } return ice; } // 降级:只有面板缓存/数据层 —— 尽力拼一个最小 SSRData(标题给空,DOM 会补) const cacheRoot = keys['__general_skupanel_cache_data'] ?? keys['__ICE_DATA_LOADER__']; if (cacheRoot) { const skuBase = skuPropsFromUnknown(cacheRoot); const params = paramsFromUnknown(cacheRoot); if (skuBase?.props?.length || params.length) { return { item: { title: '', itemId: '', images: [], videos: [] }, skuBase: skuBase ?? undefined, params: { basicParamList: params, enhanceParamList: [] }, price: undefined, }; } } return null; }