interface FeedTargetInput { feed_id?: string; xsec_token?: string; url?: string; } interface UserTargetInput { user_id?: string; xsec_token?: string; url?: string; } interface FeedTargetResolved { feedId: string; xsecToken: string; } interface UserTargetResolved { userId: string; xsecToken: string; } function parseXhsUrl(rawUrl: string): URL { const trimmed = rawUrl.trim(); if (!trimmed) { throw new Error('url cannot be empty'); } if (/^https?:\/\//i.test(trimmed)) { return new URL(trimmed); } if (trimmed.startsWith('/')) { return new URL(`https://www.xiaohongshu.com${trimmed}`); } return new URL(`https://${trimmed}`); } function extractFeedIdFromPath(pathname: string): string | undefined { const patterns = [ /\/explore\/([a-zA-Z0-9_-]+)/, /\/discovery\/item\/([a-zA-Z0-9_-]+)/, /\/note\/([a-zA-Z0-9_-]+)/, ]; for (const pattern of patterns) { const match = pathname.match(pattern); if (match?.[1]) return match[1]; } return undefined; } function extractUserIdFromPath(pathname: string): string | undefined { const match = pathname.match(/\/user\/profile\/([a-zA-Z0-9_-]+)/); return match?.[1]; } function extractXsecToken(url: URL): string | undefined { return url.searchParams.get('xsec_token') ?? url.searchParams.get('xsecToken') ?? undefined; } export function resolveFeedTarget(input: FeedTargetInput): FeedTargetResolved { let feedId = input.feed_id?.trim(); let xsecToken = input.xsec_token?.trim(); if (input.url) { const parsed = parseXhsUrl(input.url); feedId = feedId || extractFeedIdFromPath(parsed.pathname); xsecToken = xsecToken || extractXsecToken(parsed); } if (!feedId || !xsecToken) { throw new Error('xhs_get_feed_detail requires either url with feed_id/xsec_token, or both feed_id and xsec_token'); } return { feedId, xsecToken }; } export function resolveUserTarget(input: UserTargetInput): UserTargetResolved { let userId = input.user_id?.trim(); let xsecToken = input.xsec_token?.trim(); if (input.url) { const parsed = parseXhsUrl(input.url); userId = userId || extractUserIdFromPath(parsed.pathname); xsecToken = xsecToken || extractXsecToken(parsed); } if (!userId || !xsecToken) { throw new Error('xhs_get_user_profile requires either url with user_id/xsec_token, or both user_id and xsec_token'); } return { userId, xsecToken }; }