Files
atlas-seller-kit/apps/extension/entrypoints/source-page-state.content.ts
2026-09-08 16:19:38 +08:00

70 lines
3.1 KiB
TypeScript

/**
* A deliberately narrow MAIN-world bridge for the mature 1688/Taobao/Tmall
* structured collectors. It never exposes cookies, headers, arbitrary window
* keys, or a generic fetch primitive; only public product-state projections.
*/
export default defineContentScript({
matches: [
'*://detail.1688.com/*',
'*://item.taobao.com/*',
'*://detail.tmall.com/*',
],
world: 'MAIN',
runAt: 'document_idle',
main() {
window.addEventListener('message', (event) => {
if (event.source !== window || !isRequest(event.data)) return;
const data = event.data.platform === '1688'
? project1688((window as unknown as Record<string, unknown>).context)
: projectTaobao(window as unknown as Record<string, unknown>);
window.postMessage({
source: 'atlas-collector',
type: 'source-page-state.response',
requestId: event.data.requestId,
data,
}, location.origin);
});
},
});
type Request = { source: 'atlas-collector'; type: 'source-page-state.request'; requestId: string; platform: '1688' | 'taobao' | 'tmall' };
function isRequest(value: unknown): value is Request {
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
const data = value as Record<string, unknown>;
return data.source === 'atlas-collector' && data.type === 'source-page-state.request'
&& typeof data.requestId === 'string' && /^[a-zA-Z0-9-]{16,120}$/.test(data.requestId)
&& (data.platform === '1688' || data.platform === 'taobao' || data.platform === 'tmall');
}
const safeClone = (value: unknown) => { try { return JSON.parse(JSON.stringify(value)); } catch { return undefined; } };
function find(value: unknown, key: string): unknown {
const pending: unknown[] = [value];
const seen = new Set<object>();
let visited = 0;
while (pending.length && visited < 300_000) {
const current = pending.shift();
if (!current || typeof current !== 'object' || seen.has(current)) continue;
seen.add(current);
visited += 1;
const record = current as Record<string, unknown>;
if (key in record) return record[key];
pending.push(...Object.values(record));
}
return undefined;
}
function project1688(context: unknown) {
const projected: Record<string, unknown> = {};
for (const key of ['gallery', 'tempModel', 'skuModel', 'tradeModel', 'skuMapOriginal', 'pieceWeightScaleInfo', 'productAttributes']) {
const value = find(context, key);
if (value !== undefined) projected[key] = safeClone(value);
}
return projected;
}
function projectTaobao(windowState: Record<string, unknown>) {
const ice = windowState.__ICE_APP_CONTEXT__ as Record<string, unknown> | undefined;
const result = ice?.loaderData && typeof ice.loaderData === 'object'
? (((ice.loaderData as Record<string, unknown>).home as Record<string, unknown> | undefined)?.data as Record<string, unknown> | undefined)?.res
: undefined;
return { product: safeClone(result) };
}