mcp-scraper 0.65.1 → 0.66.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/README.md +11 -6
  3. package/dist/{analytics-repository-IKE53S3E.js → analytics-repository-RUXBFSFX.js} +1 -2
  4. package/dist/bin/api-server.cjs +1844 -71
  5. package/dist/bin/api-server.js +4 -5
  6. package/dist/bin/mcp-scraper-cli.cjs +1 -2
  7. package/dist/bin/mcp-scraper-cli.js +6 -7
  8. package/dist/bin/mcp-scraper-install.cjs +7 -3
  9. package/dist/bin/mcp-scraper-install.js +2 -3
  10. package/dist/bin/mcp-stdio-server.cjs +5150 -3467
  11. package/dist/bin/mcp-stdio-server.js +11 -11
  12. package/dist/bin/paa-harvest.cjs +8 -3
  13. package/dist/bin/paa-harvest.js +4 -5
  14. package/dist/{chunk-5PIO7QBG.js → chunk-3PIXKLGZ.js} +3 -4
  15. package/dist/{chunk-VIWT35S6.js → chunk-4OT7BAIE.js} +1 -2
  16. package/dist/{chunk-ORKXE7VX.js → chunk-4P3PHFPQ.js} +11 -6
  17. package/dist/{chunk-AMLFKPLL.js → chunk-75VW7CWE.js} +4 -7
  18. package/dist/{chunk-FG6LJGJ7.js → chunk-77LQV73B.js} +0 -1
  19. package/dist/{chunk-DKIG2TEE.js → chunk-AQKJOSIB.js} +2 -3
  20. package/dist/{chunk-2XUXVKT4.js → chunk-AYJHI2FK.js} +0 -1
  21. package/dist/{chunk-5BEPOFKG.js → chunk-CNLLTD5B.js} +0 -1
  22. package/dist/{chunk-EKJ65E3Z.js → chunk-DNM65UCK.js} +0 -1
  23. package/dist/{chunk-PDAKCYHZ.js → chunk-E4LMY5OJ.js} +6258 -3747
  24. package/dist/{chunk-MQGBCWZD.js → chunk-GGZEC22A.js} +64 -1
  25. package/dist/{chunk-4VG3NUUY.js → chunk-GTGSD4YS.js} +2 -2
  26. package/dist/{chunk-MQQS7ONH.js → chunk-KBTUPRTF.js} +8 -296
  27. package/dist/{chunk-XPEN6OG5.js → chunk-NS754CRS.js} +15 -3
  28. package/dist/{chunk-UN4LZVCB.js → chunk-OZJMVCDK.js} +1 -2
  29. package/dist/{chunk-342OUCSG.js → chunk-PGJQDMC2.js} +1 -2
  30. package/dist/{chunk-G3P3ZDB4.js → chunk-RJ7JVYKU.js} +0 -1
  31. package/dist/{chunk-N54JS5WP.js → chunk-TNJIPV74.js} +1 -2
  32. package/dist/{chunk-4LINSJNI.js → chunk-TNLGHNLX.js} +1 -2
  33. package/dist/chunk-VRJ7CO6L.js +6 -0
  34. package/dist/chunk-VXLU74YZ.js +293 -0
  35. package/dist/{chunk-DCWXVAQT.js → chunk-WID3MFRS.js} +6 -2
  36. package/dist/{db-BRI465XQ.js → db-PO7N7ZP3.js} +1 -2
  37. package/dist/{extract-bundle-UWKJT4MU.js → extract-bundle-SIBFUA6I.js} +8 -10
  38. package/dist/index.cjs +8 -3
  39. package/dist/index.js +4 -5
  40. package/dist/{lead-list-enrichment-repository-ODWGRVCQ.js → lead-list-enrichment-repository-OTAUQONJ.js} +2 -3
  41. package/dist/{location-data-repository-67MMEVZQ.js → location-data-repository-RM2KJ3H5.js} +3 -4
  42. package/dist/{server-RSHSZBUZ.js → server-ELEJNRRE.js} +875 -1629
  43. package/dist/{site-extract-repository-J3RH3MOS.js → site-extract-repository-ASYEJO7Z.js} +4 -5
  44. package/dist/{worker-YKRIK5LS.js → worker-MAVYYWM7.js} +8 -9
  45. package/package.json +3 -1
  46. package/dist/analytics-repository-IKE53S3E.js.map +0 -1
  47. package/dist/bin/api-server.cjs.map +0 -1
  48. package/dist/bin/api-server.js.map +0 -1
  49. package/dist/bin/mcp-scraper-cli.cjs.map +0 -1
  50. package/dist/bin/mcp-scraper-cli.js.map +0 -1
  51. package/dist/bin/mcp-scraper-install.cjs.map +0 -1
  52. package/dist/bin/mcp-scraper-install.js.map +0 -1
  53. package/dist/bin/mcp-stdio-server.cjs.map +0 -1
  54. package/dist/bin/mcp-stdio-server.js.map +0 -1
  55. package/dist/bin/paa-harvest.cjs.map +0 -1
  56. package/dist/bin/paa-harvest.js.map +0 -1
  57. package/dist/chunk-2XUXVKT4.js.map +0 -1
  58. package/dist/chunk-342OUCSG.js.map +0 -1
  59. package/dist/chunk-4LINSJNI.js.map +0 -1
  60. package/dist/chunk-4VG3NUUY.js.map +0 -1
  61. package/dist/chunk-5BEPOFKG.js.map +0 -1
  62. package/dist/chunk-5PIO7QBG.js.map +0 -1
  63. package/dist/chunk-AMLFKPLL.js.map +0 -1
  64. package/dist/chunk-CGKWNXJR.js +0 -7
  65. package/dist/chunk-CGKWNXJR.js.map +0 -1
  66. package/dist/chunk-DCWXVAQT.js.map +0 -1
  67. package/dist/chunk-DKIG2TEE.js.map +0 -1
  68. package/dist/chunk-EKJ65E3Z.js.map +0 -1
  69. package/dist/chunk-FG6LJGJ7.js.map +0 -1
  70. package/dist/chunk-G3P3ZDB4.js.map +0 -1
  71. package/dist/chunk-MQGBCWZD.js.map +0 -1
  72. package/dist/chunk-MQQS7ONH.js.map +0 -1
  73. package/dist/chunk-N54JS5WP.js.map +0 -1
  74. package/dist/chunk-ORKXE7VX.js.map +0 -1
  75. package/dist/chunk-PDAKCYHZ.js.map +0 -1
  76. package/dist/chunk-UN4LZVCB.js.map +0 -1
  77. package/dist/chunk-UVFB24RZ.js +0 -67
  78. package/dist/chunk-UVFB24RZ.js.map +0 -1
  79. package/dist/chunk-VIWT35S6.js.map +0 -1
  80. package/dist/chunk-XPEN6OG5.js.map +0 -1
  81. package/dist/db-BRI465XQ.js.map +0 -1
  82. package/dist/extract-bundle-UWKJT4MU.js.map +0 -1
  83. package/dist/index.cjs.map +0 -1
  84. package/dist/index.js.map +0 -1
  85. package/dist/lead-list-enrichment-repository-ODWGRVCQ.js.map +0 -1
  86. package/dist/location-data-repository-67MMEVZQ.js.map +0 -1
  87. package/dist/server-RSHSZBUZ.js.map +0 -1
  88. package/dist/site-extract-repository-J3RH3MOS.js.map +0 -1
  89. package/dist/worker-YKRIK5LS.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/media-extractor.ts","../src/api/extraction-problems.ts","../src/api/commons-embeddings.ts"],"sourcesContent":["import { createWriteStream, mkdirSync, rmSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join, extname, basename } from 'node:path'\nimport { pipeline } from 'node:stream/promises'\nimport { Readable, Transform } from 'node:stream'\nimport { validatePublicHttpUrl } from '../api/url-utils.js'\nimport type { RenderedMediaDiscovery } from './rendered-media.js'\nimport { loadHtmlDocument, type HtmlDocument } from './html-document.js'\n\nconst AD_PATTERNS: string[] = [\n 'doubleclick.net', 'googlesyndication.com', 'googletagmanager.com',\n 'google-analytics.com', 'googletagservices.com', 'adservice.google',\n 'googletag.', 'pagead2.googlesyndication',\n 'facebook.net/tr', 'connect.facebook.net', 'fbcdn.net/rsrc',\n 'analytics.twitter.com', 'static.ads-twitter.com', 'ads.twitter.com',\n 't.co/i/adsct', 'pixel.advertising.com',\n 'hotjar.com', 'clarity.ms', 'quantserve.com', 'scorecardresearch.com',\n 'newrelic.com', 'nr-data.net', 'segment.io', 'segment.com',\n 'amplitude.com', 'mixpanel.com', 'heap.io', 'fullstory.com',\n 'moatads.com', 'criteo.com', 'adsrvr.org', 'rubiconproject.com',\n 'pubmatic.com', 'openx.net', 'appnexus.com', 'amazon-adsystem.com',\n 'media.net', 'yieldmo.com', 'triplelift.com', 'sharethrough.com',\n 'prebid.', 'smaato.net', 'indexworm.com', 'casalemedia.com',\n 'outbrain.com', 'taboola.com', 'revcontent.com', 'mgid.com',\n 'tawk.to', 'intercom.io', 'drift.com', 'hs-scripts.com',\n 'zopim.com', 'livechatinc.com', 'userlike.com',\n 'onetrust.com', 'cookielaw.org', 'cookieinformation.com', 'trustarc.com',\n '/ads/', '/ad/', '/banner/', '/banners/', '/pixel/', '/beacon/',\n '/tracking/', '/tracker/', '/remarketing/', '/conversion/',\n '1x1.gif', 'spacer.gif', 'blank.gif', 'transparent.gif',\n]\n\nconst IMAGE_EXTS = new Set(['.jpg', '.jpeg', '.png', '.webp', '.gif', '.avif', '.svg', '.tiff'])\nconst VIDEO_EXTS = new Set(['.mp4', '.webm', '.mov', '.avi', '.m4v', '.ogv', '.mkv'])\nconst AUDIO_EXTS = new Set(['.mp3', '.wav', '.ogg', '.aac', '.m4a', '.flac', '.opus'])\n\nexport type MediaType = 'image' | 'video' | 'audio'\n\nexport interface MediaAsset {\n url: string\n type: MediaType\n mimeType: string | null\n filename: string\n savedPath: string | null\n sizeBytes: number | null\n discoveryMethods: string[]\n altTexts: string[]\n contexts: string[]\n width: number | null\n height: number | null\n variants: string[]\n finalUrl?: string | null\n duplicateOf?: string | null\n sha256?: string | null\n downloadStatus?: 'downloaded' | 'failed' | 'not_attempted'\n downloadError?: string | null\n inlinePreview?: { data: string; mimeType: string } | null\n}\n\nexport interface MediaManifest {\n pageUrl: string\n outputDir: string | null\n assets: MediaAsset[]\n filteredCount: number\n totalFound: number\n staticFound: number\n renderedFound: number\n completeness: 'complete' | 'partial'\n exhausted: boolean\n stopReason: 'page_exhausted' | 'asset_limit' | 'scroll_round_limit' | 'render_unavailable'\n scrollRounds: number\n warnings: string[]\n artifact: {\n artifactId: string\n filename: string\n contentType: string\n bytes: number\n sha256: string\n expiresAt: string\n downloadUrl: string | null\n downloadUrlExpiresAt: string | null\n localPath: string | null\n } | null\n}\n\nexport interface MediaExtractOptions {\n types?: MediaType[]\n outputDir?: string | null\n rendered?: RenderedMediaDiscovery | null\n maxAssets?: number\n}\n\nexport interface DiscoveredMediaCandidate {\n url: string\n type: MediaType | null\n discoveryMethods: string[]\n altTexts: string[]\n contexts: string[]\n width: number | null\n height: number | null\n}\n\nfunction isAdUrl(url: string): boolean {\n const lower = url.toLowerCase()\n return AD_PATTERNS.some(p => lower.includes(p))\n}\n\nfunction isDataUri(url: string): boolean {\n return url.startsWith('data:')\n}\n\nexport function typeFromUrl(url: string): MediaType | null {\n try {\n const ext = extname(new URL(url).pathname).toLowerCase()\n if (IMAGE_EXTS.has(ext)) return 'image'\n if (VIDEO_EXTS.has(ext)) return 'video'\n if (AUDIO_EXTS.has(ext)) return 'audio'\n } catch { /* non-parseable URL */ }\n return null\n}\n\nexport function typeFromMime(mime: string): MediaType | null {\n const lower = mime.toLowerCase()\n if (lower.startsWith('image/')) return 'image'\n if (lower.startsWith('video/')) return 'video'\n if (lower.startsWith('audio/')) return 'audio'\n return null\n}\n\nfunction resolveUrl(raw: string, base: string): string | null {\n if (!raw || isDataUri(raw)) return null\n try { return new URL(raw, base).href } catch { return null }\n}\n\nfunction safeFilename(url: string, index: number): string {\n try {\n const u = new URL(url)\n const base = basename(u.pathname).replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 80)\n return base || `asset-${index}`\n } catch {\n return `asset-${index}`\n }\n}\n\nfunction boundedText(value: string | undefined | null, max: number): string | null {\n const normalized = value?.replace(/&(?:amp|#0?38|#x26);/gi, '&').replace(/\\s+/g, ' ').trim()\n return normalized ? normalized.slice(0, max) : null\n}\n\nfunction dimensionsFromUrl(rawUrl: string): { width: number; height: number } | null {\n const match = rawUrl.match(/[-_/](\\d{2,5})x(\\d{2,5})(?=[._/?#-]|$)/i)\n return match ? { width: Number(match[1]), height: Number(match[2]) } : null\n}\n\nexport function mediaFamilyKey(rawUrl: string): string {\n try {\n const url = new URL(rawUrl)\n url.hash = ''\n url.pathname = url.pathname.replace(/-(?:\\d{2,5}x\\d{2,5}|scaled|e\\d{6,})(?=\\.[a-z0-9]{2,6}$)/i, '')\n for (const key of ['w', 'width', 'h', 'height', 'resize']) url.searchParams.delete(key)\n url.searchParams.sort()\n return url.href\n } catch {\n return rawUrl\n }\n}\n\nfunction candidateScore(candidate: DiscoveredMediaCandidate): number {\n const area = (candidate.width ?? 0) * (candidate.height ?? 0)\n const methodBoost = candidate.discoveryMethods.some(method => method.includes('lightbox')) ? 1_000_000_000_000 : 0\n const originalBoost = /-(?:\\d{2,5}x\\d{2,5}|scaled|e\\d{6,})(?=\\.[a-z0-9]{2,6}(?:[?#]|$))/i.test(candidate.url) ? 0 : 100_000_000_000\n return methodBoost + originalBoost + area\n}\n\nfunction mergeCandidate(target: Map<string, DiscoveredMediaCandidate>, incoming: DiscoveredMediaCandidate): void {\n const existing = target.get(incoming.url)\n if (!existing) {\n target.set(incoming.url, incoming)\n return\n }\n existing.discoveryMethods = [...new Set([...existing.discoveryMethods, ...incoming.discoveryMethods])]\n existing.altTexts = [...new Set([...existing.altTexts, ...incoming.altTexts])]\n existing.contexts = [...new Set([...existing.contexts, ...incoming.contexts])]\n if (!existing.type && incoming.type) existing.type = incoming.type\n if ((incoming.width ?? 0) * (incoming.height ?? 0) > (existing.width ?? 0) * (existing.height ?? 0)) {\n existing.width = incoming.width\n existing.height = incoming.height\n }\n}\n\nexport function discoverStaticMedia(html: string | HtmlDocument, baseUrl: string): DiscoveredMediaCandidate[] {\n const found = new Map<string, DiscoveredMediaCandidate>()\n const add = (\n raw: string | undefined,\n method: string,\n explicitType: MediaType | null = null,\n alt?: string | null,\n context?: string | null,\n dimensions?: { width: number; height: number } | null,\n ) => {\n const resolved = raw ? resolveUrl(raw.trim().replace(/&amp;/g, '&'), baseUrl) : null\n if (!resolved || isAdUrl(resolved)) return\n const inferredDimensions = dimensions ?? dimensionsFromUrl(resolved)\n mergeCandidate(found, {\n url: resolved,\n type: explicitType ?? typeFromUrl(resolved),\n discoveryMethods: [method],\n altTexts: alt ? [alt] : [],\n contexts: context ? [context] : [],\n width: inferredDimensions?.width ?? null,\n height: inferredDimensions?.height ?? null,\n })\n }\n const scanSrcset = (value: string | undefined, method: string, type: MediaType, alt?: string | null) => {\n for (const part of (value ?? '').split(',')) {\n const [url, descriptor] = part.trim().split(/\\s+/)\n if (!url) continue\n const width = descriptor?.match(/^(\\d+)w$/)?.[1]\n add(url, method, type, alt, null, width ? { width: Number(width), height: 0 } : null)\n }\n }\n const scan = (document: HtmlDocument, suffix = '') => {\n const $ = document.$\n for (const image of document.images) {\n const attrs = image.attributes\n const alt = boundedText(image.alt, 500)\n const dimensions = image.width && image.height ? { width: image.width, height: image.height } : null\n for (const attr of ['src', 'data-src', 'data-lazy-src', 'data-original', 'data-bg', 'data-background', 'data-bg-url', 'data-lazy-bg', 'data-echo']) {\n const value = attrs[attr]\n add(value, `static-${attr}${suffix}`, 'image', alt, null, dimensions)\n }\n scanSrcset(attrs.srcset, `static-srcset${suffix}`, 'image', alt)\n scanSrcset(attrs['data-srcset'], `static-data-srcset${suffix}`, 'image', alt)\n }\n $('source').each((_index, element) => {\n const node = $(element)\n const declared = node.attr('type') ?? ''\n const type: MediaType = declared.startsWith('video/') ? 'video' : declared.startsWith('audio/') ? 'audio' : 'image'\n add(node.attr('src'), `static-source${suffix}`, type)\n scanSrcset(node.attr('srcset'), `static-source-srcset${suffix}`, type)\n })\n $('video,audio').each((_index, element) => {\n const node = $(element)\n const type = element.tagName.toLowerCase() as 'video' | 'audio'\n add(node.attr('src'), `static-${type}${suffix}`, type)\n if (type === 'video') add(node.attr('poster'), `static-video-poster${suffix}`, 'image')\n })\n $('meta').each((_index, element) => {\n const node = $(element)\n const prop = (node.attr('property') ?? node.attr('name') ?? '').toLowerCase()\n if (prop === 'og:image' || prop === 'og:image:url' || prop === 'twitter:image' || prop === 'twitter:image:src') {\n add(node.attr('content'), `static-${prop}${suffix}`, 'image')\n }\n })\n $('link[href]').each((_index, element) => {\n const node = $(element)\n const rel = (node.attr('rel') ?? '').toLowerCase().split(/\\s+/)\n if (rel.some(value => value === 'icon' || value === 'apple-touch-icon')) {\n add(node.attr('href'), `static-icon${suffix}`, 'image')\n }\n })\n $('svg image').each((_index, element) => {\n const node = $(element)\n add(node.attr('href') ?? node.attr('xlink:href'), `static-svg-image${suffix}`, 'image')\n })\n $('a[href]').has('img').each((_index, element) => {\n const node = $(element)\n const href = node.attr('href')\n if (!href) return\n if (\n /lightbox|gallery|fancybox|glightbox|swipebox|elementor-open-lightbox/i.test(`${node.attr('class') ?? ''} ${node.attr('data-elementor-open-lightbox') ?? ''}`)\n || /\\.(?:avif|gif|jpe?g|png|svg|webp)(?:[?#]|$)/i.test(href)\n ) {\n const alt = boundedText(node.find('img').first().attr('alt'), 500)\n add(href, `static-lightbox-target${suffix}`, 'image', alt)\n }\n })\n const cssSources = $('style').toArray().map(element => $(element).text())\n $('[style]').each((_index, element) => { cssSources.push($(element).attr('style') ?? '') })\n for (const match of cssSources.join('\\n').matchAll(/(?:background(?:-image)?\\s*:\\s*)?url\\(\\s*(?:[\"']|&quot;|&#0?34;|&apos;|&#0?39;)?([^\"')&]+)(?:[\"']|&quot;|&#0?34;|&apos;|&#0?39;)?\\s*\\)/gi)) {\n add(match[1], `static-css-url${suffix}`, 'image')\n }\n for (const match of document.sourceHtml.matchAll(/[\"'](https?:\\/\\/[^\"'\\s<>]+\\.(?:avif|gif|jpe?g|png|svg|webp|mp4|webm|mp3|ogg)(?:\\?[^\"'\\s<>]*)?)[\"']/gi)) {\n add(match[1], `static-bare-url${suffix}`)\n }\n }\n\n const document = typeof html === 'string' ? loadHtmlDocument(html) : html\n scan(document)\n document.$('script').each((_index, element) => {\n const source = document.$(element).text()\n const unescaped = source\n .replace(/\\\\u003[cC]/g, '<').replace(/\\\\u003[eE]/g, '>').replace(/\\\\u0026/gi, '&')\n .replace(/\\\\[\"']/g, match => match.slice(1)).replace(/\\\\\\//g, '/')\n if (unescaped !== source && /<(?:img|source|video|audio)\\b/i.test(unescaped)) {\n scan(loadHtmlDocument(unescaped), '-json-unescaped')\n }\n })\n return [...found.values()]\n}\n\nexport function mergeMediaDiscovery(\n staticAssets: DiscoveredMediaCandidate[],\n rendered: RenderedMediaDiscovery | null | undefined,\n maxAssets = 100,\n): Array<DiscoveredMediaCandidate & { variants: string[] }> {\n const exact = new Map<string, DiscoveredMediaCandidate>()\n for (const asset of staticAssets) mergeCandidate(exact, asset)\n for (const asset of rendered?.assets ?? []) mergeCandidate(exact, { ...asset, type: asset.type })\n\n const families = new Map<string, DiscoveredMediaCandidate[]>()\n for (const asset of exact.values()) {\n const key = mediaFamilyKey(asset.url)\n const family = families.get(key) ?? []\n family.push(asset)\n families.set(key, family)\n }\n return [...families.values()]\n .map(family => {\n const ordered = [...family].sort((a, b) => candidateScore(b) - candidateScore(a) || a.url.localeCompare(b.url))\n const preferred = { ...ordered[0] }\n preferred.discoveryMethods = [...new Set(family.flatMap(asset => asset.discoveryMethods))]\n preferred.altTexts = [...new Set(family.flatMap(asset => asset.altTexts))]\n preferred.contexts = [...new Set(family.flatMap(asset => asset.contexts))]\n return { ...preferred, variants: ordered.slice(1).map(asset => asset.url) }\n })\n .sort((a, b) => candidateScore(b) - candidateScore(a) || a.url.localeCompare(b.url))\n .slice(0, Math.max(1, maxAssets))\n}\n\nexport function extractMediaUrls(html: string, baseUrl: string): string[] {\n return mergeMediaDiscovery(discoverStaticMedia(html, baseUrl), null, 2_000).map(asset => asset.url)\n}\n\nexport async function downloadAsset(\n url: string,\n destDir: string,\n filename: string,\n options: {\n expectedType?: MediaType\n maxBytes?: number\n consumeBytes?: (bytes: number) => boolean\n } = {},\n): Promise<{ savedPath: string; sizeBytes: number; mimeType: string | null }> {\n const maxBytes = Math.max(1, Math.min(options.maxBytes ?? 50 * 1024 * 1024, 100 * 1024 * 1024))\n let target = url\n let res: Response | null = null\n for (let redirects = 0; redirects <= 5; redirects++) {\n const checked = await validatePublicHttpUrl(target, { field: 'media URL' })\n if (checked.error || !checked.parsed) throw new Error(checked.error ?? 'Media URL was rejected')\n res = await fetch(checked.parsed.href, {\n signal: AbortSignal.timeout(15_000),\n redirect: 'manual',\n })\n if (res.status >= 300 && res.status < 400) {\n const location = res.headers.get('location')\n if (!location) throw new Error(`HTTP ${res.status} redirect did not include Location`)\n target = new URL(location, checked.parsed.href).href\n res = null\n continue\n }\n break\n }\n if (!res) throw new Error('Media download exceeded five redirects')\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n if (!res.body) throw new Error('Empty response body')\n\n const mimeType = res.headers.get('content-type')?.split(';')[0].trim() ?? null\n if (options.expectedType && (!mimeType || typeFromMime(mimeType) !== options.expectedType)) {\n throw new Error(`Expected ${options.expectedType} content but received ${mimeType ?? 'no content-type'}`)\n }\n const declaredBytes = Number(res.headers.get('content-length') ?? 0)\n if (Number.isFinite(declaredBytes) && declaredBytes > maxBytes) {\n throw new Error(`Media asset exceeds ${maxBytes} byte limit`)\n }\n\n let dest = join(destDir, filename)\n if (mimeType && !extname(filename)) {\n const mimeExt: Record<string, string> = {\n 'image/jpeg': '.jpg', 'image/png': '.png', 'image/webp': '.webp',\n 'image/gif': '.gif', 'image/svg+xml': '.svg', 'image/avif': '.avif',\n 'video/mp4': '.mp4', 'video/webm': '.webm',\n 'audio/mpeg': '.mp3', 'audio/ogg': '.ogg', 'audio/wav': '.wav',\n }\n const ext = mimeExt[mimeType]\n if (ext) dest = dest + ext\n }\n\n const writer = createWriteStream(dest)\n await new Promise<void>((resolve, reject) => {\n writer.once('open', () => resolve())\n writer.once('error', reject)\n })\n let streamedBytes = 0\n const limiter = new Transform({\n transform(chunk: Buffer, _encoding, callback) {\n const bytes = chunk.length\n if (streamedBytes + bytes > maxBytes) {\n callback(new Error(`Media asset exceeds ${maxBytes} byte limit`))\n return\n }\n if (options.consumeBytes && !options.consumeBytes(bytes)) {\n callback(new Error('Media export total byte limit exceeded'))\n return\n }\n streamedBytes += bytes\n callback(null, chunk)\n },\n })\n try {\n await pipeline(Readable.fromWeb(res.body as import('stream/web').ReadableStream), limiter, writer)\n } catch (error) {\n writer.destroy()\n rmSync(dest, { force: true })\n throw error\n }\n\n const { statSync } = await import('node:fs')\n const sizeBytes = statSync(dest).size\n\n return { savedPath: dest, sizeBytes, mimeType }\n}\n\nexport async function harvestPageMedia(\n html: string,\n pageUrl: string,\n options: MediaExtractOptions = {},\n): Promise<MediaManifest> {\n const types = options.types ?? ['image', 'video', 'audio']\n const typesSet = new Set(types)\n const staticAssets = discoverStaticMedia(html, pageUrl)\n const maxAssets = Math.max(1, Math.min(options.maxAssets ?? 100, 250))\n const allDiscovered = mergeMediaDiscovery(staticAssets, options.rendered, 2_000)\n const discovered = allDiscovered.slice(0, maxAssets)\n const totalFound = new Set([\n ...staticAssets.map(asset => asset.url),\n ...(options.rendered?.assets ?? []).map(asset => asset.url),\n ]).size\n const kept = discovered.flatMap(asset => {\n const type = asset.type ?? typeFromUrl(asset.url)\n return type && typesSet.has(type) ? [{ ...asset, type }] : []\n })\n const filteredCount = totalFound - kept.length\n const retentionLimitReached = allDiscovered.length > maxAssets\n const exhausted = options.rendered?.exhausted === true && !retentionLimitReached\n const stopReason = retentionLimitReached ? 'asset_limit' : options.rendered?.stopReason ?? 'render_unavailable'\n const warnings = retentionLimitReached\n ? [`Media discovery found ${allDiscovered.length} responsive families; only the requested ${maxAssets} records were retained.`]\n : options.rendered\n ? options.rendered.exhausted ? [] : [`Rendered media discovery stopped with ${options.rendered.stopReason}; the inventory may be incomplete.`]\n : ['Rendered media discovery was unavailable; this manifest contains static-source evidence only.']\n\n const baseManifest: Omit<MediaManifest, 'outputDir' | 'assets'> = {\n pageUrl,\n staticFound: staticAssets.length,\n renderedFound: options.rendered?.assets.length ?? 0,\n filteredCount,\n totalFound,\n completeness: exhausted ? 'complete' as const : 'partial' as const,\n exhausted,\n stopReason,\n scrollRounds: options.rendered?.scrollRounds ?? 0,\n warnings,\n artifact: null,\n }\n\n if (options.outputDir === null) {\n return {\n ...baseManifest,\n outputDir: null,\n assets: kept.map((asset, i) => ({\n ...asset,\n mimeType: null, filename: safeFilename(asset.url, i), savedPath: null, sizeBytes: null,\n sha256: null, downloadStatus: 'not_attempted' as const, downloadError: null, inlinePreview: null,\n })),\n }\n }\n\n const domain = (() => { try { return new URL(pageUrl).hostname.replace(/^www\\./, '') } catch { return 'unknown' } })()\n const stamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)\n const outDir = options.outputDir ?? join(homedir(), 'Downloads', 'mcp-scraper', 'media', `${stamp}-${domain}`)\n mkdirSync(outDir, { recursive: true })\n\n const assets: MediaAsset[] = []\n\n await Promise.allSettled(\n kept.map(async (asset, i) => {\n const filename = safeFilename(asset.url, i)\n try {\n const { savedPath, sizeBytes, mimeType } = await downloadAsset(asset.url, outDir, filename)\n const resolvedType = mimeType ? (typeFromMime(mimeType) ?? asset.type) : asset.type\n assets.push({\n ...asset, type: resolvedType, mimeType, filename: basename(savedPath), savedPath, sizeBytes,\n sha256: null, downloadStatus: 'downloaded', downloadError: null, inlinePreview: null,\n })\n } catch (error) {\n assets.push({\n ...asset, mimeType: null, filename, savedPath: null, sizeBytes: null, sha256: null,\n downloadStatus: 'failed', downloadError: error instanceof Error ? error.message : 'media_download_failed', inlinePreview: null,\n })\n }\n })\n )\n\n assets.sort((a, b) => {\n if (a.savedPath && !b.savedPath) return -1\n if (!a.savedPath && b.savedPath) return 1\n return a.url.localeCompare(b.url)\n })\n\n return { ...baseManifest, outputDir: outDir, assets }\n}\n","import {\n buildPublicErrorEnvelope,\n type PublicChargeStatus,\n type PublicErrorDetails,\n type PublicErrorEnvelope,\n} from '../errors.js'\nimport { isVendorUnavailableError } from './vendor-errors.js'\n\nexport type ExtractionErrorCode =\n | 'vendor_unavailable'\n | 'page_not_found'\n | 'page_forbidden'\n | 'page_rate_limited'\n | 'page_server_error'\n | 'page_http_error'\n | 'bot_check_unresolved'\n | 'page_too_large'\n | 'page_unreachable'\n | 'browser_session_interrupted'\n | 'extraction_failed'\n\nexport interface ExtractionProblem {\n errorCode: ExtractionErrorCode\n httpStatus: number | null\n retryable: boolean\n}\n\nexport interface ExtractionErrorContext {\n chargeStatus?: PublicChargeStatus\n details?: PublicErrorDetails\n}\n\nexport type ExtractionErrorResponse = PublicErrorEnvelope & { error: string }\n\nconst UNREACHABLE_CODES = new Set([\n 'browser_navigation_blocked',\n 'browser_navigation_failed',\n 'browser_timeout',\n 'browser_session_failed',\n 'browser_result_missing',\n])\n\nfunction errorCode(err: unknown): string | undefined {\n return err instanceof Error ? (err as Error & { code?: string }).code : undefined\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n\nfunction httpStatusFromBrowserHttpCode(value: string | undefined): number | null {\n const match = value?.match(/^browser_http_(\\d+)$/)\n return match ? Number(match[1]) : null\n}\n\nfunction bucketHttpStatus(status: number): ExtractionErrorCode {\n if (status === 404) return 'page_not_found'\n if (status === 403) return 'page_forbidden'\n if (status === 429) return 'page_rate_limited'\n if (status >= 500 && status <= 599) return 'page_server_error'\n return 'page_http_error'\n}\n\nexport function classifyExtractionProblem(err: unknown): ExtractionProblem {\n const message = errorMessage(err)\n const code = errorCode(err)\n\n if (isVendorUnavailableError(err, message)) {\n return { errorCode: 'vendor_unavailable', httpStatus: null, retryable: true }\n }\n\n const httpStatus = httpStatusFromBrowserHttpCode(code) ?? httpStatusFromBrowserHttpCode(message.match(/^(browser_http_\\d+)/)?.[1])\n if (httpStatus != null) {\n return { errorCode: bucketHttpStatus(httpStatus), httpStatus, retryable: httpStatus === 429 || httpStatus >= 500 }\n }\n\n if (code === 'browser_challenge_unresolved' || /^browser_challenge_unresolved/.test(message)) {\n return { errorCode: 'bot_check_unresolved', httpStatus: null, retryable: true }\n }\n\n if (code === 'browser_response_too_large' || /exceeds \\d+ byte limit/i.test(message)) {\n return { errorCode: 'page_too_large', httpStatus: null, retryable: false }\n }\n\n if (/browser (?:has been )?closed|context closed|target page, context or browser has been closed|session closed/i.test(message)) {\n return { errorCode: 'browser_session_interrupted', httpStatus: null, retryable: true }\n }\n\n if (UNREACHABLE_CODES.has(code ?? '') || /timeout|navigation_failed|ERR_/i.test(message)) {\n return { errorCode: 'page_unreachable', httpStatus: null, retryable: true }\n }\n\n return { errorCode: 'extraction_failed', httpStatus: null, retryable: false }\n}\n\nexport function publicExtractionErrorMessage(code: ExtractionErrorCode, httpStatus: number | null): string {\n switch (code) {\n case 'page_not_found':\n return `The page could not be found (HTTP ${httpStatus ?? 404}). It may have been moved or deleted.`\n case 'page_forbidden':\n return `The site refused the request (HTTP ${httpStatus ?? 403}).`\n case 'page_rate_limited':\n return 'The site is rate-limiting requests right now (HTTP 429). Retrying after a short wait usually works.'\n case 'page_server_error':\n return `The page's own server returned an error (HTTP ${httpStatus}). This is on the target site's side, not something a retry here can fix.`\n case 'page_http_error':\n return `The page returned an unexpected HTTP ${httpStatus} response.`\n case 'bot_check_unresolved':\n return 'This site has automated-traffic protection (a bot/CAPTCHA check) that could not be resolved in time. Some sites are simply not extractable this way.'\n case 'page_too_large':\n return 'The page is larger than we can safely process.'\n case 'page_unreachable':\n return 'The page did not respond in time, or the connection could not be completed.'\n case 'browser_session_interrupted':\n return 'The browser session closed before extraction completed. A retry uses a fresh browser session.'\n case 'vendor_unavailable':\n return \"MCP Scraper's internal browser services require MCP Scraper team attention. Servers or IPs are down until this is fixed — this is not caused by your request. Please retry in a few minutes.\"\n case 'extraction_failed':\n return 'The page could not be extracted. Please retry, or contact support if this persists for the same URL.'\n }\n}\n\nexport function extractionProblemResponse(problem: ExtractionProblem, context: ExtractionErrorContext = {}): ExtractionErrorResponse {\n const message = publicExtractionErrorMessage(problem.errorCode, problem.httpStatus)\n const envelope = buildPublicErrorEnvelope({\n errorCode: problem.errorCode,\n retryable: problem.retryable,\n chargeStatus: context.chargeStatus,\n details: {\n ...(context.details ?? {}),\n ...(problem.httpStatus != null ? { http_status: problem.httpStatus } : {}),\n },\n })\n return { ...envelope, message, error: message }\n}\n\nexport function extractionWireStatus(problem: ExtractionProblem): 403 | 404 | 429 | 500 | 502 | 503 {\n if (problem.httpStatus === 404) return 404\n if (problem.httpStatus === 403) return 403\n if (problem.httpStatus === 429) return 429\n if (problem.httpStatus != null && problem.httpStatus >= 500 && problem.httpStatus <= 599) return 500\n if (problem.httpStatus != null) return 502\n if (problem.errorCode === 'vendor_unavailable') return 503\n return 502\n}\n\nconst WAYBACK_CAPTURE_MISSING = 'wayback_capture_missing'\n\nexport function publicizeExtractionFailure(\n failureCode: string | null | undefined,\n failureReason: string | null | undefined,\n): { failureCode: string | null; failureReason: string | null } {\n if (failureCode == null) return { failureCode: null, failureReason: null }\n if (failureCode === WAYBACK_CAPTURE_MISSING) return { failureCode, failureReason: failureReason ?? null }\n const err = new Error(failureReason ?? failureCode) as Error & { code?: string }\n err.code = failureCode\n const problem = classifyExtractionProblem(err)\n return { failureCode: problem.errorCode, failureReason: publicExtractionErrorMessage(problem.errorCode, problem.httpStatus) }\n}\n","import { neon, type NeonQueryFunction } from '@neondatabase/serverless'\nimport { getDb } from './db.js'\n\nexport const COMMONS_EMBED_PROVIDER = 'jina'\n\nlet _vectorSql: NeonQueryFunction<false, false> | null = null\nlet vectorSchemaReady = false\n\nexport function commonsEmbedModel(): string {\n return (process.env.JINA_EMBED_MODEL ?? 'jina-embeddings-v5-omni-small').trim()\n}\n\nexport function commonsEmbedDim(): number {\n return Number((process.env.JINA_EMBED_DIM ?? '1024').trim())\n}\n\nexport function commonsSemanticSearchConfigured(): boolean {\n return Boolean(process.env.JINA_API_KEY?.trim() && process.env.MEMORY_DATABASE_URL?.trim())\n}\n\nfunction vectorSql(): NeonQueryFunction<false, false> {\n if (_vectorSql) return _vectorSql\n const url = process.env.MEMORY_DATABASE_URL?.trim()\n if (!url) throw new Error('MEMORY_DATABASE_URL is not set; Commons semantic search needs the shared Postgres.')\n _vectorSql = neon(url)\n return _vectorSql\n}\n\nexport function resetCommonsVectorSchemaForTesting(): void {\n vectorSchemaReady = false\n _vectorSql = null\n}\n\nexport async function ensureCommonsVectorSchema(): Promise<void> {\n if (vectorSchemaReady) return\n const dimension = commonsEmbedDim()\n await vectorSql().query('CREATE EXTENSION IF NOT EXISTS vector')\n await vectorSql().query(`\n CREATE TABLE IF NOT EXISTS commons_index_vectors (\n document_id TEXT PRIMARY KEY,\n entity_id TEXT NOT NULL,\n document_type TEXT NOT NULL,\n title TEXT NOT NULL,\n embedding vector(${dimension}) NOT NULL,\n model TEXT NOT NULL,\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now()\n )\n `)\n await vectorSql().query('CREATE INDEX IF NOT EXISTS commons_index_vectors_entity ON commons_index_vectors(entity_id)')\n vectorSchemaReady = true\n}\n\nexport async function embedCommonsTexts(texts: string[]): Promise<number[][]> {\n const apiKey = process.env.JINA_API_KEY?.trim()\n if (!apiKey) throw new Error('JINA_API_KEY is not set; Commons semantic search cannot embed.')\n if (!texts.length) return []\n const response = await fetch('https://api.jina.ai/v1/embeddings', {\n method: 'POST',\n headers: { authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' },\n body: JSON.stringify({\n model: commonsEmbedModel(),\n dimensions: commonsEmbedDim(),\n input: texts.map(text => ({ text: text.slice(0, 8_000) })),\n }),\n signal: AbortSignal.timeout(60_000),\n })\n if (!response.ok) {\n throw new Error(`Jina embedding request failed with HTTP ${response.status}: ${(await response.text()).slice(0, 200)}`)\n }\n const payload = await response.json() as { data?: Array<{ embedding: number[] }> }\n const vectors = (payload.data ?? []).map(item => item.embedding)\n if (vectors.length !== texts.length) {\n throw new Error(`Jina returned ${vectors.length} embeddings for ${texts.length} inputs.`)\n }\n return vectors\n}\n\nexport async function embedQueuedCommonsDocuments(limit = 50): Promise<{\n claimed: number\n embedded: number\n failed: number\n remaining: number\n}> {\n if (!commonsSemanticSearchConfigured()) return { claimed: 0, embedded: 0, failed: 0, remaining: 0 }\n await ensureCommonsVectorSchema()\n const bounded = Math.max(1, Math.min(200, Math.floor(limit)))\n const queued = await getDb().execute({\n sql: `SELECT id, entity_id, document_type, title, text FROM commons_index_documents\n WHERE embedding_status IN ('queued', 'failed') ORDER BY updated_at ASC LIMIT ?`,\n args: [bounded],\n })\n const rows = queued.rows as unknown as Array<{ id: string; entity_id: string; document_type: string; title: string; text: string }>\n if (!rows.length) return { claimed: 0, embedded: 0, failed: 0, remaining: await queuedCommonsDocumentCount() }\n\n let embedded = 0\n let failed = 0\n const model = commonsEmbedModel()\n try {\n const vectors = await embedCommonsTexts(rows.map(row => `${row.title}\\n\\n${row.text}`))\n for (const [index, row] of rows.entries()) {\n const literal = `[${vectors[index].join(',')}]`\n await vectorSql().query(\n `INSERT INTO commons_index_vectors (document_id, entity_id, document_type, title, embedding, model, updated_at)\n VALUES ($1, $2, $3, $4, $5::vector, $6, now())\n ON CONFLICT (document_id) DO UPDATE SET entity_id = EXCLUDED.entity_id, document_type = EXCLUDED.document_type,\n title = EXCLUDED.title, embedding = EXCLUDED.embedding, model = EXCLUDED.model, updated_at = now()`,\n [row.id, row.entity_id, row.document_type, row.title, literal, model],\n )\n await getDb().execute({\n sql: `UPDATE commons_index_documents SET embedding_status = 'indexed', embedding_provider = ?, embedding_model = ?,\n vector_ref = ?, indexed_at = ?, error = NULL WHERE id = ?`,\n args: [COMMONS_EMBED_PROVIDER, model, row.id, new Date().toISOString(), row.id],\n })\n embedded += 1\n }\n } catch (error) {\n failed = rows.length - embedded\n const message = (error instanceof Error ? error.message : String(error)).slice(0, 500)\n for (const row of rows.slice(embedded)) {\n await getDb().execute({\n sql: `UPDATE commons_index_documents SET embedding_status = 'failed', error = ? WHERE id = ?`,\n args: [message, row.id],\n }).catch(() => undefined)\n }\n }\n return { claimed: rows.length, embedded, failed, remaining: await queuedCommonsDocumentCount() }\n}\n\nexport async function queuedCommonsDocumentCount(): Promise<number> {\n const result = await getDb().execute(`SELECT COUNT(*) AS n FROM commons_index_documents WHERE embedding_status IN ('queued', 'failed')`)\n return Number((result.rows[0] as unknown as { n: number })?.n ?? 0)\n}\n\nexport async function semanticCommonsEntityScores(query: string, limit = 40): Promise<Map<string, number>> {\n const scores = new Map<string, number>()\n if (!commonsSemanticSearchConfigured() || !query.trim()) return scores\n await ensureCommonsVectorSchema()\n const [vector] = await embedCommonsTexts([query])\n if (!vector) return scores\n const rows = await vectorSql().query(\n `SELECT entity_id, MAX(1 - (embedding <=> $1::vector)) AS score\n FROM commons_index_vectors GROUP BY entity_id ORDER BY score DESC LIMIT $2`,\n [`[${vector.join(',')}]`, Math.max(1, Math.min(100, limit))],\n ) as unknown as Array<{ entity_id: string; score: number }>\n for (const row of rows) scores.set(String(row.entity_id), Number(row.score))\n return scores\n}\n"],"mappings":"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","names":["document"]}
@@ -1,7 +0,0 @@
1
- // src/version.ts
2
- var PACKAGE_VERSION = "0.65.1";
3
-
4
- export {
5
- PACKAGE_VERSION
6
- };
7
- //# sourceMappingURL=chunk-CGKWNXJR.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/version.ts"],"sourcesContent":["export const PACKAGE_VERSION = '0.65.1'\n"],"mappings":";AAAO,IAAM,kBAAkB;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/install-terminal.ts"],"sourcesContent":["export interface InstallTerminalOptions {\n color?: boolean\n version: string\n apiKeyConfigured?: boolean\n}\n\ntype Tone = 'cyan' | 'lime' | 'amber' | 'red' | 'muted' | 'bold'\n\nconst CODES: Record<Tone | 'reset', string> = {\n reset: '\\x1b[0m',\n cyan: '\\x1b[36m',\n lime: '\\x1b[32m',\n amber: '\\x1b[33m',\n red: '\\x1b[31m',\n muted: '\\x1b[90m',\n bold: '\\x1b[1m',\n}\n\nfunction colorize(value: string, tone: Tone, enabled: boolean): string {\n if (!enabled) return value\n return `${CODES[tone]}${value}${CODES.reset}`\n}\n\nfunction toolRow(label: string, tools: string[], enabled: boolean): string {\n const padded = label.padEnd(9, ' ')\n return ` ${colorize(padded, 'muted', enabled)} ${tools.join(colorize(' . ', 'muted', enabled))}`\n}\n\nexport function renderInstallTerminal(options: InstallTerminalOptions): string {\n const color = options.color ?? true\n const apiKeyValue = options.apiKeyConfigured ? '$MCP_SCRAPER_API_KEY' : 'sk_live_your_key'\n const ascii = String.raw`\n __ __ ____ ____ \n| \\/ |/ ___| _ \\\n| |\\/| | | | |_) |\n| | | | |___| __/\n|_| |_|\\____|_|\n\n ____ ____ ____ _ ____ _____ ____\n/ ___| / ___| _ \\ / \\ | _ \\| ____| _ \\\n\\___ \\| | | |_) | / _ \\ | |_) | _| | |_) |\n ___) | |___| _ < / ___ \\| __/| |___| _ <\n|____/ \\____|_| \\_\\/_/ \\_\\_| |_____|_| \\_\\\n`\n\n const claudeCommand = [\n `MCP_SCRAPER_API_KEY=${apiKeyValue} npx -y -p mcp-scraper@latest \\\\`,\n ' mcp-scraper-cli agent install claude --apply',\n ].join('\\n')\n\n const codexConfig = [\n '[mcp_servers.mcp-scraper]',\n 'command = \"npx\"',\n 'args = [\"-y\", \"-p\", \"mcp-scraper@latest\", \"mcp-scraper\"]',\n `env = { MCP_SCRAPER_API_KEY = \"${apiKeyValue}\" }`,\n ].join('\\n')\n\n return [\n colorize(`mcp-scraper v${options.version}`, 'bold', color),\n colorize('> mcp-scraper-install', 'muted', color),\n colorize(ascii, 'amber', color),\n `${colorize('MCP Scraper Agent', 'cyan', color)} . v${options.version} . mcpscraper.dev`,\n '1/1 install surfaces ready',\n colorize('Newest: any approved connection read can become an indexed Memory snapshot in one call. OAuth stays tenant-isolated and provider content is redacted and marked untrusted.', 'lime', color),\n '',\n `${colorize('Tools', 'cyan', color)} ${colorize('(259 MCP tools)', 'muted', color)}`,\n toolRow('search', ['harvest_paa', 'search_serp', 'maps_search', 'maps_place_intel'], color),\n toolRow('extract', ['extract_url', 'map_site_urls', 'extract_site', 'audit_site', 'directory_workflow'], color),\n toolRow('build', ['create_editorial_reading_room', 'rank_tracker_workflow', 'portable HTML'], color),\n toolRow('media', ['youtube_harvest', 'youtube_transcribe', 'facebook_ad_search', 'facebook_page_intel', 'facebook_ad_transcribe', 'facebook_video_transcribe', 'instagram_profile_content', 'instagram_media_download', 'reddit_thread'], color),\n toolRow('browser', ['serp_identity_create', 'serp_identity_list', 'browser_open', 'browser_profile_connect', 'browser_profile_list', 'browser_close', 'browser_screenshot', 'browser_read', 'browser_locate', 'browser_replay_mark', 'browser_replay_annotate'], color),\n toolRow('connect', ['list_service_connections', 'describe_service_connection_tool', 'import_service_connection_to_memory', 'export_connected_service_data', 'renew_connected_data_download', 'read_service_connection', 'call_service_connection_action'], color),\n toolRow('commons', ['commons_search_entities', 'commons_get_entity_linkset', 'commons_prepare_entity', 'commons_submit_entity', 'commons_prepare_publication', 'commons_claim_publication', 'commons_publish_editorial', 'commons_get_publication'], color),\n toolRow('account', ['credits_info', 'reports', 'MCP resources'], color),\n toolRow('memory', ['memory-put', 'memory-get', 'memory-search', 'list-vaults', 'record-fact', 'list-scheduled-actions'], color),\n `${colorize('Workflows', 'cyan', color)} ${colorize('(MCP + CLI + API)', 'muted', color)}`,\n toolRow('route', ['workflow_list', 'workflow_suggest', 'workflow_run', 'workflow_step', 'workflow_status', 'workflow_artifact_read'], color),\n toolRow('seo', ['directory', 'agent-packet', 'competitive audit', 'map/serp comparison', 'PAA/AIO briefs', 'scheduled runs'], color),\n '',\n colorize('Usage tips:', 'amber', color),\n 'Run mcp-scraper-install for this visible card. Run mcp-scraper-cli for setup utilities and subcommands.',\n 'Run mcp-scraper in a human terminal to print this card; MCP clients get the same command as a silent stdio server.',\n 'Explicit card command: npx -y -p mcp-scraper@latest mcp-scraper-install',\n 'Hosted browser sessions use direct/no-proxy egress by default.',\n 'Customer auth setup: run browser_profile_connect, send the watch_url, let the user sign in, then call browser_profile_list until AUTHENTICATED.',\n 'Connected account ranges: call export_connected_service_data once. It handles Gmail, Calendar, Zoom, and Resend pagination; do not loop read_service_connection over individual records.',\n 'Connected account RAG: call import_service_connection_to_memory for one bounded approved read. It writes a redacted, untrusted snapshot to a stable Memory path and embeds it for search.',\n 'Stack logins / reconnect: run browser_profile_connect again with the same profile name and another domain to add accounts or refresh a login.',\n 'Start with workflow_suggest for broad jobs like market analysis, ICP research, CRO audits, brand briefs, content gaps, and AI visibility.',\n 'For MCP clients, use mcp-scraper so one install can mix SERP, Maps, browser, reports, and saved MCP resources.',\n 'If you hit the concurrency limit, add 2 browsers for $5/month with mcp-scraper-cli billing concurrency checkout.',\n '',\n `${colorize('Ready.', 'lime', color)} Install the combined MCP server with one command:`,\n '',\n colorize('Setup doctor', 'amber', color),\n 'npx -y -p mcp-scraper@latest mcp-scraper-cli doctor',\n '',\n colorize('Hosted profile setup', 'amber', color),\n 'In your MCP client, call browser_profile_connect with email=\"seo@example.com\" and domain=\"chatgpt.com\".',\n 'Give the returned watch_url to the user. After they sign in, call browser_profile_list, then browser_open with the returned profile. Add more logins by calling browser_profile_connect again with the same profile and a new domain.',\n '',\n colorize('Claude Code one-command setup', 'amber', color),\n claudeCommand,\n 'Then fully exit Claude Code and open a new Claude terminal. Check with: claude mcp list',\n '',\n colorize('Codex config', 'amber', color),\n codexConfig,\n '',\n colorize('Claude Desktop Extension', 'amber', color),\n 'Download: https://mcpscraper.dev/downloads/mcp-scraper.mcpb',\n '',\n colorize('Safety note:', 'muted', color),\n 'mcp-scraper prints this card only when stdin/stdout are an interactive TTY. In MCP clients it writes only JSON-RPC to stdout.',\n 'Use --stdio or MCP_SCRAPER_FORCE_STDIO=1 to force server mode from a terminal.',\n '',\n ].join('\\n')\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/api/location-data-repository.ts"],"sourcesContent":["import { createHash, randomUUID } from 'node:crypto'\nimport { getDb } from './db.js'\nimport { csvRecords } from '../directory/csv.js'\nimport type { DirectoryMarket, PopulationYear } from '../directory/location-db.js'\n\nexport const HOSTED_ZIP_DATASET_KIND = 'us_zip_groups' as const\nexport const HOSTED_CENSUS_DATASET_PREFIX = 'us_census_places' as const\nexport type HostedLocationDatasetKind = typeof HOSTED_ZIP_DATASET_KIND | `${typeof HOSTED_CENSUS_DATASET_PREFIX}:${string}`\nexport const MIN_NATIONWIDE_ZIP_COUNT = 25_000\n\nexport const REQUIRED_LOCATION_STATE_CODES = [\n 'AL', 'AK', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DE', 'DC', 'FL', 'GA', 'HI', 'ID', 'IL', 'IN', 'IA',\n 'KS', 'KY', 'LA', 'ME', 'MD', 'MA', 'MI', 'MN', 'MS', 'MO', 'MT', 'NE', 'NV', 'NH', 'NJ', 'NM',\n 'NY', 'NC', 'ND', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VT', 'VA', 'WA',\n 'WV', 'WI', 'WY',\n] as const\n\nconst STATE_FIPS_BY_ABBR: Record<typeof REQUIRED_LOCATION_STATE_CODES[number], string> = {\n AL: '01', AK: '02', AZ: '04', AR: '05', CA: '06', CO: '08', CT: '09', DE: '10', DC: '11',\n FL: '12', GA: '13', HI: '15', ID: '16', IL: '17', IN: '18', IA: '19', KS: '20', KY: '21',\n LA: '22', ME: '23', MD: '24', MA: '25', MI: '26', MN: '27', MS: '28', MO: '29', MT: '30',\n NE: '31', NV: '32', NH: '33', NJ: '34', NM: '35', NY: '36', NC: '37', ND: '38', OH: '39',\n OK: '40', OR: '41', PA: '42', RI: '44', SC: '45', SD: '46', TN: '47', TX: '48', UT: '49',\n VT: '50', VA: '51', WA: '53', WV: '54', WI: '55', WY: '56',\n}\nconst REQUIRED_LOCATION_STATE_SET = new Set<string>(REQUIRED_LOCATION_STATE_CODES)\n\nexport interface HostedZipGroup {\n city: string\n cityKey: string\n state: string\n zips: string[]\n counties: string[]\n}\n\nexport interface HostedLocationDataset {\n id: string\n kind: HostedLocationDatasetKind\n sourceUrl: string | null\n sha256: string\n rowCount: number\n groupCount: number\n stateCount: number\n sourceRowCount: number\n rejectedRowCount: number\n zipCount: number\n coveredStates: string[]\n coverageComplete: boolean\n status: 'loading' | 'ready' | 'failed'\n createdAt: string\n completedAt: string | null\n error: string | null\n}\n\nexport interface HostedZipGroupLookup {\n dataset: HostedLocationDataset | null\n groups: Map<string, HostedZipGroup>\n}\n\nexport interface HostedZipImportResult {\n dataset: HostedLocationDataset\n duplicate: boolean\n}\n\nexport interface HostedLocationMarketQuery {\n state: string\n minPopulation: number\n populationYear: PopulationYear\n maxResults: number\n includeZipGroups: boolean\n cityQuery?: string\n zip?: string\n}\n\nexport interface HostedLocationMarketResult {\n markets: DirectoryMarket[]\n populationDataset: HostedLocationDataset | null\n zipDataset: HostedLocationDataset | null\n warnings: string[]\n}\n\ninterface HostedCensusPlace {\n city: string\n cityKey: string\n censusName: string\n estimatesBase2020: number | null\n populations: Record<PopulationYear, number | null>\n}\n\ninterface ParsedCensusDataset {\n sourceRowCount: number\n places: HostedCensusPlace[]\n}\n\ninterface ParsedZipDataset {\n sourceRowCount: number\n rowCount: number\n rejectedRowCount: number\n zipCount: number\n stateCount: number\n coveredStates: string[]\n groups: HostedZipGroup[]\n}\n\nconst IMPORT_BATCH_SIZE = 200\nconst MAX_SOURCE_URL_LENGTH = 2_000\n\nlet schemaDb: ReturnType<typeof getDb> | null = null\nlet schemaPromise: Promise<void> | null = null\n\nfunction normalizeHostedCityKey(value: string): string {\n return value.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim()\n}\n\nfunction stringArray(value: unknown): string[] {\n if (value == null) return []\n try {\n const parsed = JSON.parse(String(value)) as unknown\n return Array.isArray(parsed) ? parsed.map(String) : []\n } catch {\n return []\n }\n}\n\nfunction rowToDataset(row: Record<string, unknown>): HostedLocationDataset {\n return {\n id: String(row.id),\n kind: String(row.dataset_kind) as HostedLocationDatasetKind,\n sourceUrl: row.source_url == null ? null : String(row.source_url),\n sha256: String(row.sha256),\n rowCount: Number(row.row_count ?? 0),\n groupCount: Number(row.group_count ?? 0),\n stateCount: Number(row.state_count ?? 0),\n sourceRowCount: Number(row.source_row_count ?? row.row_count ?? 0),\n rejectedRowCount: Number(row.rejected_row_count ?? 0),\n zipCount: Number(row.zip_count ?? 0),\n coveredStates: stringArray(row.covered_states_json),\n coverageComplete: Number(row.coverage_complete ?? 0) === 1,\n status: String(row.status) as HostedLocationDataset['status'],\n createdAt: String(row.created_at),\n completedAt: row.completed_at == null ? null : String(row.completed_at),\n error: row.error == null ? null : String(row.error),\n }\n}\n\nfunction censusDatasetKind(state: string): `${typeof HOSTED_CENSUS_DATASET_PREFIX}:${string}` {\n return `${HOSTED_CENSUS_DATASET_PREFIX}:${state}`\n}\n\nfunction normalizedSourceUrl(value: string | null | undefined): string | null {\n const trimmed = value?.trim()\n if (!trimmed) return null\n if (trimmed.length > MAX_SOURCE_URL_LENGTH) throw new Error(`sourceUrl must be ${MAX_SOURCE_URL_LENGTH} characters or fewer`)\n let parsed: URL\n try {\n parsed = new URL(trimmed)\n } catch {\n throw new Error('sourceUrl must be a valid URL')\n }\n parsed.username = ''\n parsed.password = ''\n parsed.search = ''\n parsed.hash = ''\n return parsed.toString()\n}\n\nfunction normalizedZip(value: string): string | null {\n const digits = value.trim()\n if (!/^\\d{1,5}$/.test(digits)) return null\n return digits.padStart(5, '0')\n}\n\nfunction normalizedState(value: string): string | null {\n const state = value.trim().toUpperCase()\n return REQUIRED_LOCATION_STATE_SET.has(state) ? state : null\n}\n\nfunction csvColumn(record: Record<string, string>, names: string[]): string {\n for (const name of names) {\n const value = record[name]\n if (value !== undefined) return value\n }\n return ''\n}\n\nfunction numberOrNull(value: string | undefined): number | null {\n if (value === undefined || value.trim() === '') return null\n const parsed = Number(value)\n return Number.isFinite(parsed) && parsed >= 0 ? Math.trunc(parsed) : null\n}\n\nfunction displayCityFromCensus(name: string): string {\n if (/^Nashville-Davidson metropolitan government/i.test(name)) return 'Nashville'\n return name.replace(/\\s+(city|town|village|municipality|borough)$/i, '').trim()\n}\n\nexport function parseHostedCensusPlacesCsv(csv: string, expectedStateFips?: string): ParsedCensusDataset {\n const records = csvRecords(csv.replace(/^\\uFEFF/, ''))\n if (records.length === 0) throw new Error('Census place CSV contains no data rows.')\n const headers = new Set(Object.keys(records[0] ?? {}).map(header => header.trim()))\n const required = ['SUMLEV', 'STATE', 'NAME', 'POPESTIMATE2020', 'POPESTIMATE2021', 'POPESTIMATE2022', 'POPESTIMATE2023', 'POPESTIMATE2024', 'POPESTIMATE2025']\n const missing = required.filter(header => !headers.has(header))\n if (missing.length > 0) throw new Error(`Census place CSV is missing required columns: ${missing.join(', ')}.`)\n\n const placeRecords = records.filter(record => record.SUMLEV?.trim() === '162')\n if (expectedStateFips) {\n const expected = expectedStateFips.padStart(2, '0')\n const mismatched = placeRecords.filter(record => record.STATE?.trim().padStart(2, '0') !== expected)\n if (mismatched.length > 0) {\n const received = [...new Set(mismatched.map(record => record.STATE?.trim() || '(blank)'))].slice(0, 5)\n throw new Error(`Census place CSV STATE does not match requested FIPS ${expected}; received ${received.join(', ')}.`)\n }\n }\n\n const candidates = placeRecords\n .map((record): HostedCensusPlace | null => {\n const censusName = record.NAME?.trim() ?? ''\n if (!censusName) return null\n const city = displayCityFromCensus(censusName)\n const cityKey = normalizeHostedCityKey(city)\n if (!cityKey) return null\n const populations = {\n 2020: numberOrNull(record.POPESTIMATE2020),\n 2021: numberOrNull(record.POPESTIMATE2021),\n 2022: numberOrNull(record.POPESTIMATE2022),\n 2023: numberOrNull(record.POPESTIMATE2023),\n 2024: numberOrNull(record.POPESTIMATE2024),\n 2025: numberOrNull(record.POPESTIMATE2025),\n } satisfies Record<PopulationYear, number | null>\n if (Object.values(populations).every(value => value === null)) return null\n return {\n city,\n cityKey,\n censusName,\n estimatesBase2020: numberOrNull(record.ESTIMATESBASE2020),\n populations,\n }\n })\n .filter((place): place is HostedCensusPlace => place !== null)\n\n // Census can contain separate legal entities with the same normalized place\n // name in one state (for example, both a small \"Wilmington village\" and a\n // much larger \"Wilmington city\" in Illinois). The hosted table and the ZIP\n // join intentionally use one row per state/city key, so retain the largest\n // current place deterministically instead of failing the whole state import.\n const placesByCityKey = new Map<string, HostedCensusPlace>()\n const populationRank = (place: HostedCensusPlace): number => (\n place.populations[2025]\n ?? place.populations[2024]\n ?? place.populations[2023]\n ?? place.populations[2022]\n ?? place.populations[2021]\n ?? place.populations[2020]\n ?? place.estimatesBase2020\n ?? -1\n )\n for (const candidate of candidates) {\n const current = placesByCityKey.get(candidate.cityKey)\n if (!current\n || populationRank(candidate) > populationRank(current)\n || (populationRank(candidate) === populationRank(current)\n && candidate.censusName.localeCompare(current.censusName) < 0)) {\n placesByCityKey.set(candidate.cityKey, candidate)\n }\n }\n const places = [...placesByCityKey.values()]\n .sort((a, b) => a.city.localeCompare(b.city))\n\n if (places.length === 0) throw new Error('Census place CSV did not contain any valid place population rows.')\n return { sourceRowCount: records.length, places }\n}\n\nexport function parseHostedZipGroupsCsv(csv: string): ParsedZipDataset {\n const records = csvRecords(csv.replace(/^\\uFEFF/, ''))\n if (records.length === 0) throw new Error('Location ZIP CSV contains no data rows.')\n\n const first = records[0] ?? {}\n const headers = new Set(Object.keys(first).map(header => header.trim().toLowerCase()))\n const hasState = ['state_abbr', 'state', 'state_id'].some(header => headers.has(header))\n const hasCity = ['city', 'primary_city'].some(header => headers.has(header))\n const hasZip = ['zipcode', 'zip', 'zip_code'].some(header => headers.has(header))\n if (!hasState || !hasCity || !hasZip) {\n throw new Error('Location ZIP CSV must include state_abbr/state, city, and zipcode/zip columns.')\n }\n\n const grouped = new Map<string, { city: string; cityKey: string; state: string; zips: Set<string>; counties: Set<string> }>()\n let rowCount = 0\n let rejectedRowCount = 0\n const states = new Set<string>()\n const uniqueZips = new Set<string>()\n\n for (const rawRecord of records) {\n const record = Object.fromEntries(Object.entries(rawRecord).map(([key, value]) => [key.trim().toLowerCase(), value]))\n const state = normalizedState(csvColumn(record, ['state_abbr', 'state', 'state_id']))\n const city = csvColumn(record, ['city', 'primary_city']).trim()\n const zip = normalizedZip(csvColumn(record, ['zipcode', 'zip', 'zip_code']))\n const county = csvColumn(record, ['county', 'county_name']).trim()\n if (!state || !REQUIRED_LOCATION_STATE_SET.has(state) || !city || !zip) {\n rejectedRowCount += 1\n continue\n }\n\n const cityKey = normalizeHostedCityKey(city)\n if (!cityKey) {\n rejectedRowCount += 1\n continue\n }\n const key = `${state}\\0${cityKey}`\n let group = grouped.get(key)\n if (!group) {\n group = { city, cityKey, state, zips: new Set(), counties: new Set() }\n grouped.set(key, group)\n }\n group.zips.add(zip)\n if (county) group.counties.add(county)\n states.add(state)\n uniqueZips.add(zip)\n rowCount += 1\n }\n\n if (grouped.size === 0) throw new Error('Location ZIP CSV did not contain any valid US city/ZIP rows.')\n return {\n sourceRowCount: records.length,\n rowCount,\n rejectedRowCount,\n zipCount: uniqueZips.size,\n stateCount: states.size,\n coveredStates: [...states].sort(),\n groups: [...grouped.values()]\n .map(group => ({\n city: group.city,\n cityKey: group.cityKey,\n state: group.state,\n zips: [...group.zips].sort(),\n counties: [...group.counties].sort(),\n }))\n .sort((a, b) => a.state.localeCompare(b.state) || a.city.localeCompare(b.city)),\n }\n}\n\nfunction assertNationwideZipCoverage(parsed: ParsedZipDataset): void {\n const covered = new Set(parsed.coveredStates)\n const missingStates = REQUIRED_LOCATION_STATE_CODES.filter(state => !covered.has(state))\n const errors: string[] = []\n if (missingStates.length > 0) errors.push(`missing required states: ${missingStates.join(', ')}`)\n if (parsed.zipCount < MIN_NATIONWIDE_ZIP_COUNT) {\n errors.push(`only ${parsed.zipCount} unique ZIPs; at least ${MIN_NATIONWIDE_ZIP_COUNT} are required`)\n }\n if (errors.length > 0) {\n throw new Error(`Location ZIP snapshot is not nationwide and was not activated (${errors.join('; ')}; ${parsed.rejectedRowCount} rejected rows).`)\n }\n}\n\nexport async function ensureHostedLocationDataSchema(): Promise<void> {\n const db = getDb()\n if (schemaDb !== db) {\n schemaDb = db\n schemaPromise = null\n }\n if (!schemaPromise) {\n schemaPromise = (async () => {\n await db.execute(`\n CREATE TABLE IF NOT EXISTS location_data_imports (\n id TEXT PRIMARY KEY,\n dataset_kind TEXT NOT NULL,\n source_url TEXT,\n sha256 TEXT NOT NULL,\n row_count INTEGER NOT NULL DEFAULT 0,\n group_count INTEGER NOT NULL DEFAULT 0,\n state_count INTEGER NOT NULL DEFAULT 0,\n source_row_count INTEGER NOT NULL DEFAULT 0,\n rejected_row_count INTEGER NOT NULL DEFAULT 0,\n zip_count INTEGER NOT NULL DEFAULT 0,\n covered_states_json TEXT NOT NULL DEFAULT '[]',\n coverage_complete INTEGER NOT NULL DEFAULT 0,\n status TEXT NOT NULL,\n error TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n completed_at TEXT\n )\n `)\n const importColumns = new Set((await db.execute(`PRAGMA table_info(location_data_imports)`)).rows.map(row => String(row.name)))\n const missingImportColumns = [\n ['source_row_count', `INTEGER NOT NULL DEFAULT 0`],\n ['rejected_row_count', `INTEGER NOT NULL DEFAULT 0`],\n ['zip_count', `INTEGER NOT NULL DEFAULT 0`],\n ['covered_states_json', `TEXT NOT NULL DEFAULT '[]'`],\n ['coverage_complete', `INTEGER NOT NULL DEFAULT 0`],\n ] as const\n for (const [column, definition] of missingImportColumns) {\n if (!importColumns.has(column)) await db.execute(`ALTER TABLE location_data_imports ADD COLUMN ${column} ${definition}`)\n }\n await db.execute(`CREATE INDEX IF NOT EXISTS location_data_imports_kind_created ON location_data_imports(dataset_kind, created_at DESC)`)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS location_zip_groups (\n import_id TEXT NOT NULL REFERENCES location_data_imports(id) ON DELETE CASCADE,\n state_abbr TEXT NOT NULL,\n city_key TEXT NOT NULL,\n city TEXT NOT NULL,\n zips_json TEXT NOT NULL,\n counties_json TEXT NOT NULL,\n PRIMARY KEY(import_id, state_abbr, city_key)\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS location_zip_groups_state ON location_zip_groups(import_id, state_abbr, city_key)`)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS location_census_places (\n import_id TEXT NOT NULL REFERENCES location_data_imports(id) ON DELETE CASCADE,\n state_abbr TEXT NOT NULL,\n city_key TEXT NOT NULL,\n city TEXT NOT NULL,\n census_name TEXT NOT NULL,\n estimates_base_2020 INTEGER,\n population_2020 INTEGER,\n population_2021 INTEGER,\n population_2022 INTEGER,\n population_2023 INTEGER,\n population_2024 INTEGER,\n population_2025 INTEGER,\n PRIMARY KEY(import_id, state_abbr, city_key)\n )\n `)\n await db.execute(`CREATE INDEX IF NOT EXISTS location_census_places_state ON location_census_places(import_id, state_abbr, city_key)`)\n await db.execute(`\n CREATE TABLE IF NOT EXISTS location_data_active (\n dataset_kind TEXT PRIMARY KEY,\n import_id TEXT NOT NULL REFERENCES location_data_imports(id),\n activated_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `)\n })().catch(error => {\n schemaPromise = null\n throw error\n })\n }\n await schemaPromise\n}\n\nexport async function getActiveHostedDataset(kind: HostedLocationDatasetKind): Promise<HostedLocationDataset | null> {\n await ensureHostedLocationDataSchema()\n const result = await getDb().execute({\n sql: `\n SELECT i.*\n FROM location_data_active a\n JOIN location_data_imports i ON i.id = a.import_id\n WHERE a.dataset_kind = ? AND i.status = 'ready'\n LIMIT 1\n `,\n args: [kind],\n })\n const row = result.rows[0]\n return row ? rowToDataset(row as unknown as Record<string, unknown>) : null\n}\n\nexport async function getActiveHostedLocationDataset(): Promise<HostedLocationDataset | null> {\n return getActiveHostedDataset(HOSTED_ZIP_DATASET_KIND)\n}\n\nexport async function listActiveHostedLocationDatasets(): Promise<HostedLocationDataset[]> {\n await ensureHostedLocationDataSchema()\n const result = await getDb().execute(`\n SELECT i.*\n FROM location_data_active a\n JOIN location_data_imports i ON i.id = a.import_id\n WHERE i.status = 'ready'\n ORDER BY i.dataset_kind ASC\n `)\n return result.rows.map(row => rowToDataset(row as unknown as Record<string, unknown>))\n}\n\nexport async function getHostedZipGroups(stateInput: string): Promise<HostedZipGroupLookup> {\n const state = normalizedState(stateInput)\n if (!state) throw new Error('state must be a two-letter US state abbreviation')\n const dataset = await getActiveHostedLocationDataset()\n if (!dataset) return { dataset: null, groups: new Map() }\n\n const result = await getDb().execute({\n sql: `\n SELECT city, city_key, state_abbr, zips_json, counties_json\n FROM location_zip_groups\n WHERE import_id = ? AND state_abbr = ?\n ORDER BY city COLLATE NOCASE ASC\n `,\n args: [dataset.id, state],\n })\n const groups = new Map<string, HostedZipGroup>()\n for (const row of result.rows) {\n const cityKey = String(row.city_key)\n groups.set(cityKey, {\n city: String(row.city),\n cityKey,\n state: String(row.state_abbr),\n zips: JSON.parse(String(row.zips_json)) as string[],\n counties: JSON.parse(String(row.counties_json)) as string[],\n })\n }\n return { dataset, groups }\n}\n\nexport async function importHostedZipGroupsCsv(input: {\n csv: string\n sourceUrl?: string | null\n}): Promise<HostedZipImportResult> {\n await ensureHostedLocationDataSchema()\n const sourceUrl = normalizedSourceUrl(input.sourceUrl)\n const sha256 = createHash('sha256').update(input.csv).digest('hex')\n const active = await getActiveHostedLocationDataset()\n if (active?.sha256 === sha256) return { dataset: active, duplicate: true }\n\n const parsed = parseHostedZipGroupsCsv(input.csv)\n assertNationwideZipCoverage(parsed)\n const id = `loc_${randomUUID().replaceAll('-', '')}`\n const db = getDb()\n await db.execute({\n sql: `\n INSERT INTO location_data_imports (\n id, dataset_kind, source_url, sha256, row_count, group_count, state_count,\n source_row_count, rejected_row_count, zip_count, covered_states_json, coverage_complete, status\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 'loading')\n `,\n args: [\n id, HOSTED_ZIP_DATASET_KIND, sourceUrl, sha256, parsed.rowCount, parsed.groups.length, parsed.stateCount,\n parsed.sourceRowCount, parsed.rejectedRowCount, parsed.zipCount, JSON.stringify(parsed.coveredStates),\n ],\n })\n\n try {\n for (let offset = 0; offset < parsed.groups.length; offset += IMPORT_BATCH_SIZE) {\n const batch = parsed.groups.slice(offset, offset + IMPORT_BATCH_SIZE).map(group => ({\n sql: `\n INSERT INTO location_zip_groups (\n import_id, state_abbr, city_key, city, zips_json, counties_json\n ) VALUES (?, ?, ?, ?, ?, ?)\n `,\n args: [id, group.state, group.cityKey, group.city, JSON.stringify(group.zips), JSON.stringify(group.counties)],\n }))\n await db.batch(batch, 'write')\n }\n\n await db.batch([\n {\n sql: `UPDATE location_data_imports SET status = 'ready', completed_at = datetime('now'), error = NULL WHERE id = ? AND status = 'loading'`,\n args: [id],\n },\n {\n sql: `\n INSERT INTO location_data_active (dataset_kind, import_id, activated_at)\n VALUES (?, ?, datetime('now'))\n ON CONFLICT(dataset_kind) DO UPDATE SET import_id = excluded.import_id, activated_at = excluded.activated_at\n `,\n args: [HOSTED_ZIP_DATASET_KIND, id],\n },\n ], 'write')\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n await db.execute({\n sql: `UPDATE location_data_imports SET status = 'failed', error = ?, completed_at = datetime('now') WHERE id = ?`,\n args: [message.slice(0, 1_000), id],\n }).catch(() => undefined)\n throw error\n }\n\n const result = await db.execute({ sql: 'SELECT * FROM location_data_imports WHERE id = ? LIMIT 1', args: [id] })\n const row = result.rows[0]\n if (!row) throw new Error('Hosted location import completed without a durable metadata row.')\n return { dataset: rowToDataset(row as unknown as Record<string, unknown>), duplicate: false }\n}\n\nexport async function importHostedCensusPlacesCsv(input: {\n state: string\n csv: string\n sourceUrl?: string | null\n}): Promise<HostedZipImportResult> {\n await ensureHostedLocationDataSchema()\n const state = normalizedState(input.state)\n if (!state) throw new Error('state must be a two-letter US state abbreviation')\n const kind = censusDatasetKind(state)\n const sourceUrl = normalizedSourceUrl(input.sourceUrl)\n const sha256 = createHash('sha256').update(input.csv).digest('hex')\n const active = await getActiveHostedDataset(kind)\n if (active?.sha256 === sha256) return { dataset: active, duplicate: true }\n\n const stateFips = STATE_FIPS_BY_ABBR[state as keyof typeof STATE_FIPS_BY_ABBR]\n const parsed = parseHostedCensusPlacesCsv(input.csv, stateFips)\n const id = `loc_${randomUUID().replaceAll('-', '')}`\n const db = getDb()\n await db.execute({\n sql: `\n INSERT INTO location_data_imports (\n id, dataset_kind, source_url, sha256, row_count, group_count, state_count,\n source_row_count, rejected_row_count, zip_count, covered_states_json, coverage_complete, status\n ) VALUES (?, ?, ?, ?, ?, ?, 1, ?, 0, 0, ?, 1, 'loading')\n `,\n args: [id, kind, sourceUrl, sha256, parsed.places.length, parsed.places.length, parsed.sourceRowCount, JSON.stringify([state])],\n })\n\n try {\n for (let offset = 0; offset < parsed.places.length; offset += IMPORT_BATCH_SIZE) {\n const batch = parsed.places.slice(offset, offset + IMPORT_BATCH_SIZE).map(place => ({\n sql: `\n INSERT INTO location_census_places (\n import_id, state_abbr, city_key, city, census_name, estimates_base_2020,\n population_2020, population_2021, population_2022, population_2023,\n population_2024, population_2025\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `,\n args: [\n id,\n state,\n place.cityKey,\n place.city,\n place.censusName,\n place.estimatesBase2020,\n place.populations[2020],\n place.populations[2021],\n place.populations[2022],\n place.populations[2023],\n place.populations[2024],\n place.populations[2025],\n ],\n }))\n await db.batch(batch, 'write')\n }\n\n await db.batch([\n {\n sql: `UPDATE location_data_imports SET status = 'ready', completed_at = datetime('now'), error = NULL WHERE id = ? AND status = 'loading'`,\n args: [id],\n },\n {\n sql: `\n INSERT INTO location_data_active (dataset_kind, import_id, activated_at)\n VALUES (?, ?, datetime('now'))\n ON CONFLICT(dataset_kind) DO UPDATE SET import_id = excluded.import_id, activated_at = excluded.activated_at\n `,\n args: [kind, id],\n },\n ], 'write')\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n await db.execute({\n sql: `UPDATE location_data_imports SET status = 'failed', error = ?, completed_at = datetime('now') WHERE id = ?`,\n args: [message.slice(0, 1_000), id],\n }).catch(() => undefined)\n throw error\n }\n\n const result = await db.execute({ sql: 'SELECT * FROM location_data_imports WHERE id = ? LIMIT 1', args: [id] })\n const row = result.rows[0]\n if (!row) throw new Error('Hosted Census import completed without a durable metadata row.')\n return { dataset: rowToDataset(row as unknown as Record<string, unknown>), duplicate: false }\n}\n\nexport async function queryHostedLocationMarkets(input: HostedLocationMarketQuery): Promise<HostedLocationMarketResult> {\n const state = normalizedState(input.state)\n if (!state) throw new Error('state must be a two-letter US state abbreviation')\n if (!Number.isSafeInteger(input.minPopulation) || input.minPopulation < 0) throw new Error('minPopulation must be a non-negative integer')\n if (!Number.isSafeInteger(input.maxResults) || input.maxResults < 1 || input.maxResults > 100) throw new Error('maxResults must be between 1 and 100')\n\n const populationDataset = await getActiveHostedDataset(censusDatasetKind(state))\n const needsZipDataset = input.includeZipGroups || Boolean(input.zip)\n const zipLookup = needsZipDataset\n ? await getHostedZipGroups(state)\n : { dataset: null, groups: new Map<string, HostedZipGroup>() }\n if (!populationDataset) {\n return { markets: [], populationDataset: null, zipDataset: zipLookup.dataset, warnings: [] }\n }\n\n const result = await getDb().execute({\n sql: `\n SELECT *\n FROM location_census_places\n WHERE import_id = ? AND state_abbr = ?\n ORDER BY city COLLATE NOCASE ASC\n `,\n args: [populationDataset.id, state],\n })\n const cityQuery = normalizeHostedCityKey(input.cityQuery ?? '')\n const zip = input.zip?.trim()\n const populationColumn = `population_${input.populationYear}`\n const markets = result.rows\n .map((row): DirectoryMarket | null => {\n const populationValue = row[populationColumn]\n if (populationValue == null) return null\n const population = Number(populationValue)\n if (!Number.isSafeInteger(population) || population < input.minPopulation) return null\n const city = String(row.city)\n const normalizedKey = String(row.city_key)\n if (cityQuery && !normalizedKey.includes(cityQuery)) return null\n const zipGroup = zipLookup.groups.get(normalizedKey)\n const zips = zipGroup?.zips ?? []\n if (zip && !zips.includes(zip)) return null\n return {\n city,\n state,\n location: `${city}, ${state}`,\n cityKey: `${city}|${state}`,\n censusName: String(row.census_name),\n population,\n populationYear: input.populationYear,\n estimatesBase2020: row.estimates_base_2020 == null ? null : Number(row.estimates_base_2020),\n zips: input.includeZipGroups ? zips : [],\n counties: input.includeZipGroups ? zipGroup?.counties ?? [] : [],\n }\n })\n .filter((market): market is DirectoryMarket => market !== null)\n .sort((a, b) => b.population - a.population || a.city.localeCompare(b.city))\n .slice(0, input.maxResults)\n\n const warnings: string[] = []\n if (input.includeZipGroups && zipLookup.dataset && markets.some(market => market.zips.length === 0)) {\n warnings.push('Some hosted Census places did not match the hosted ZIP city groups.')\n }\n return {\n markets,\n populationDataset,\n zipDataset: zipLookup.dataset,\n warnings,\n }\n}\n"],"mappings":";;;;;;;;AAAA,SAAS,YAAY,kBAAkB;AAKhC,IAAM,0BAA0B;AAChC,IAAM,+BAA+B;AAErC,IAAM,2BAA2B;AAEjC,IAAM,gCAAgC;AAAA,EAC3C;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AACd;AAEA,IAAM,qBAAmF;AAAA,EACvF,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EACpF,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EACpF,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EACpF,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EACpF,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EACpF,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AAAA,EAAM,IAAI;AACxD;AACA,IAAM,8BAA8B,IAAI,IAAY,6BAA6B;AA+EjF,IAAM,oBAAoB;AAC1B,IAAM,wBAAwB;AAE9B,IAAI,WAA4C;AAChD,IAAI,gBAAsC;AAE1C,SAAS,uBAAuB,OAAuB;AACrD,SAAO,MAAM,YAAY,EAAE,QAAQ,eAAe,GAAG,EAAE,KAAK;AAC9D;AAEA,SAAS,YAAY,OAA0B;AAC7C,MAAI,SAAS,KAAM,QAAO,CAAC;AAC3B,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,OAAO,KAAK,CAAC;AACvC,WAAO,MAAM,QAAQ,MAAM,IAAI,OAAO,IAAI,MAAM,IAAI,CAAC;AAAA,EACvD,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,aAAa,KAAqD;AACzE,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,MAAM,OAAO,IAAI,YAAY;AAAA,IAC7B,WAAW,IAAI,cAAc,OAAO,OAAO,OAAO,IAAI,UAAU;AAAA,IAChE,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,UAAU,OAAO,IAAI,aAAa,CAAC;AAAA,IACnC,YAAY,OAAO,IAAI,eAAe,CAAC;AAAA,IACvC,YAAY,OAAO,IAAI,eAAe,CAAC;AAAA,IACvC,gBAAgB,OAAO,IAAI,oBAAoB,IAAI,aAAa,CAAC;AAAA,IACjE,kBAAkB,OAAO,IAAI,sBAAsB,CAAC;AAAA,IACpD,UAAU,OAAO,IAAI,aAAa,CAAC;AAAA,IACnC,eAAe,YAAY,IAAI,mBAAmB;AAAA,IAClD,kBAAkB,OAAO,IAAI,qBAAqB,CAAC,MAAM;AAAA,IACzD,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,WAAW,OAAO,IAAI,UAAU;AAAA,IAChC,aAAa,IAAI,gBAAgB,OAAO,OAAO,OAAO,IAAI,YAAY;AAAA,IACtE,OAAO,IAAI,SAAS,OAAO,OAAO,OAAO,IAAI,KAAK;AAAA,EACpD;AACF;AAEA,SAAS,kBAAkB,OAAmE;AAC5F,SAAO,GAAG,4BAA4B,IAAI,KAAK;AACjD;AAEA,SAAS,oBAAoB,OAAiD;AAC5E,QAAM,UAAU,OAAO,KAAK;AAC5B,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,QAAQ,SAAS,sBAAuB,OAAM,IAAI,MAAM,qBAAqB,qBAAqB,sBAAsB;AAC5H,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,OAAO;AAAA,EAC1B,QAAQ;AACN,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AACA,SAAO,WAAW;AAClB,SAAO,WAAW;AAClB,SAAO,SAAS;AAChB,SAAO,OAAO;AACd,SAAO,OAAO,SAAS;AACzB;AAEA,SAAS,cAAc,OAA8B;AACnD,QAAM,SAAS,MAAM,KAAK;AAC1B,MAAI,CAAC,YAAY,KAAK,MAAM,EAAG,QAAO;AACtC,SAAO,OAAO,SAAS,GAAG,GAAG;AAC/B;AAEA,SAAS,gBAAgB,OAA8B;AACrD,QAAM,QAAQ,MAAM,KAAK,EAAE,YAAY;AACvC,SAAO,4BAA4B,IAAI,KAAK,IAAI,QAAQ;AAC1D;AAEA,SAAS,UAAU,QAAgC,OAAyB;AAC1E,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,OAAO,IAAI;AACzB,QAAI,UAAU,OAAW,QAAO;AAAA,EAClC;AACA,SAAO;AACT;AAEA,SAAS,aAAa,OAA0C;AAC9D,MAAI,UAAU,UAAa,MAAM,KAAK,MAAM,GAAI,QAAO;AACvD,QAAM,SAAS,OAAO,KAAK;AAC3B,SAAO,OAAO,SAAS,MAAM,KAAK,UAAU,IAAI,KAAK,MAAM,MAAM,IAAI;AACvE;AAEA,SAAS,sBAAsB,MAAsB;AACnD,MAAI,+CAA+C,KAAK,IAAI,EAAG,QAAO;AACtE,SAAO,KAAK,QAAQ,iDAAiD,EAAE,EAAE,KAAK;AAChF;AAEO,SAAS,2BAA2B,KAAa,mBAAiD;AACvG,QAAM,UAAU,WAAW,IAAI,QAAQ,WAAW,EAAE,CAAC;AACrD,MAAI,QAAQ,WAAW,EAAG,OAAM,IAAI,MAAM,yCAAyC;AACnF,QAAM,UAAU,IAAI,IAAI,OAAO,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,YAAU,OAAO,KAAK,CAAC,CAAC;AAClF,QAAM,WAAW,CAAC,UAAU,SAAS,QAAQ,mBAAmB,mBAAmB,mBAAmB,mBAAmB,mBAAmB,iBAAiB;AAC7J,QAAM,UAAU,SAAS,OAAO,YAAU,CAAC,QAAQ,IAAI,MAAM,CAAC;AAC9D,MAAI,QAAQ,SAAS,EAAG,OAAM,IAAI,MAAM,iDAAiD,QAAQ,KAAK,IAAI,CAAC,GAAG;AAE9G,QAAM,eAAe,QAAQ,OAAO,YAAU,OAAO,QAAQ,KAAK,MAAM,KAAK;AAC7E,MAAI,mBAAmB;AACrB,UAAM,WAAW,kBAAkB,SAAS,GAAG,GAAG;AAClD,UAAM,aAAa,aAAa,OAAO,YAAU,OAAO,OAAO,KAAK,EAAE,SAAS,GAAG,GAAG,MAAM,QAAQ;AACnG,QAAI,WAAW,SAAS,GAAG;AACzB,YAAM,WAAW,CAAC,GAAG,IAAI,IAAI,WAAW,IAAI,YAAU,OAAO,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC;AACrG,YAAM,IAAI,MAAM,wDAAwD,QAAQ,cAAc,SAAS,KAAK,IAAI,CAAC,GAAG;AAAA,IACtH;AAAA,EACF;AAEA,QAAM,aAAa,aAChB,IAAI,CAAC,WAAqC;AACzC,UAAM,aAAa,OAAO,MAAM,KAAK,KAAK;AAC1C,QAAI,CAAC,WAAY,QAAO;AACxB,UAAM,OAAO,sBAAsB,UAAU;AAC7C,UAAM,UAAU,uBAAuB,IAAI;AAC3C,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,cAAc;AAAA,MAClB,MAAM,aAAa,OAAO,eAAe;AAAA,MACzC,MAAM,aAAa,OAAO,eAAe;AAAA,MACzC,MAAM,aAAa,OAAO,eAAe;AAAA,MACzC,MAAM,aAAa,OAAO,eAAe;AAAA,MACzC,MAAM,aAAa,OAAO,eAAe;AAAA,MACzC,MAAM,aAAa,OAAO,eAAe;AAAA,IAC3C;AACA,QAAI,OAAO,OAAO,WAAW,EAAE,MAAM,WAAS,UAAU,IAAI,EAAG,QAAO;AACtE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,mBAAmB,aAAa,OAAO,iBAAiB;AAAA,MACxD;AAAA,IACF;AAAA,EACF,CAAC,EACA,OAAO,CAAC,UAAsC,UAAU,IAAI;AAO/D,QAAM,kBAAkB,oBAAI,IAA+B;AAC3D,QAAM,iBAAiB,CAAC,UACtB,MAAM,YAAY,IAAI,KACjB,MAAM,YAAY,IAAI,KACtB,MAAM,YAAY,IAAI,KACtB,MAAM,YAAY,IAAI,KACtB,MAAM,YAAY,IAAI,KACtB,MAAM,YAAY,IAAI,KACtB,MAAM,qBACN;AAEP,aAAW,aAAa,YAAY;AAClC,UAAM,UAAU,gBAAgB,IAAI,UAAU,OAAO;AACrD,QAAI,CAAC,WACA,eAAe,SAAS,IAAI,eAAe,OAAO,KACjD,eAAe,SAAS,MAAM,eAAe,OAAO,KACnD,UAAU,WAAW,cAAc,QAAQ,UAAU,IAAI,GAAI;AAClE,sBAAgB,IAAI,UAAU,SAAS,SAAS;AAAA,IAClD;AAAA,EACF;AACA,QAAM,SAAS,CAAC,GAAG,gBAAgB,OAAO,CAAC,EACxC,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAE9C,MAAI,OAAO,WAAW,EAAG,OAAM,IAAI,MAAM,mEAAmE;AAC5G,SAAO,EAAE,gBAAgB,QAAQ,QAAQ,OAAO;AAClD;AAEO,SAAS,wBAAwB,KAA+B;AACrE,QAAM,UAAU,WAAW,IAAI,QAAQ,WAAW,EAAE,CAAC;AACrD,MAAI,QAAQ,WAAW,EAAG,OAAM,IAAI,MAAM,yCAAyC;AAEnF,QAAM,QAAQ,QAAQ,CAAC,KAAK,CAAC;AAC7B,QAAM,UAAU,IAAI,IAAI,OAAO,KAAK,KAAK,EAAE,IAAI,YAAU,OAAO,KAAK,EAAE,YAAY,CAAC,CAAC;AACrF,QAAM,WAAW,CAAC,cAAc,SAAS,UAAU,EAAE,KAAK,YAAU,QAAQ,IAAI,MAAM,CAAC;AACvF,QAAM,UAAU,CAAC,QAAQ,cAAc,EAAE,KAAK,YAAU,QAAQ,IAAI,MAAM,CAAC;AAC3E,QAAM,SAAS,CAAC,WAAW,OAAO,UAAU,EAAE,KAAK,YAAU,QAAQ,IAAI,MAAM,CAAC;AAChF,MAAI,CAAC,YAAY,CAAC,WAAW,CAAC,QAAQ;AACpC,UAAM,IAAI,MAAM,gFAAgF;AAAA,EAClG;AAEA,QAAM,UAAU,oBAAI,IAAwG;AAC5H,MAAI,WAAW;AACf,MAAI,mBAAmB;AACvB,QAAM,SAAS,oBAAI,IAAY;AAC/B,QAAM,aAAa,oBAAI,IAAY;AAEnC,aAAW,aAAa,SAAS;AAC/B,UAAM,SAAS,OAAO,YAAY,OAAO,QAAQ,SAAS,EAAE,IAAI,CAAC,CAACA,MAAK,KAAK,MAAM,CAACA,KAAI,KAAK,EAAE,YAAY,GAAG,KAAK,CAAC,CAAC;AACpH,UAAM,QAAQ,gBAAgB,UAAU,QAAQ,CAAC,cAAc,SAAS,UAAU,CAAC,CAAC;AACpF,UAAM,OAAO,UAAU,QAAQ,CAAC,QAAQ,cAAc,CAAC,EAAE,KAAK;AAC9D,UAAM,MAAM,cAAc,UAAU,QAAQ,CAAC,WAAW,OAAO,UAAU,CAAC,CAAC;AAC3E,UAAM,SAAS,UAAU,QAAQ,CAAC,UAAU,aAAa,CAAC,EAAE,KAAK;AACjE,QAAI,CAAC,SAAS,CAAC,4BAA4B,IAAI,KAAK,KAAK,CAAC,QAAQ,CAAC,KAAK;AACtE,0BAAoB;AACpB;AAAA,IACF;AAEA,UAAM,UAAU,uBAAuB,IAAI;AAC3C,QAAI,CAAC,SAAS;AACZ,0BAAoB;AACpB;AAAA,IACF;AACA,UAAM,MAAM,GAAG,KAAK,KAAK,OAAO;AAChC,QAAI,QAAQ,QAAQ,IAAI,GAAG;AAC3B,QAAI,CAAC,OAAO;AACV,cAAQ,EAAE,MAAM,SAAS,OAAO,MAAM,oBAAI,IAAI,GAAG,UAAU,oBAAI,IAAI,EAAE;AACrE,cAAQ,IAAI,KAAK,KAAK;AAAA,IACxB;AACA,UAAM,KAAK,IAAI,GAAG;AAClB,QAAI,OAAQ,OAAM,SAAS,IAAI,MAAM;AACrC,WAAO,IAAI,KAAK;AAChB,eAAW,IAAI,GAAG;AAClB,gBAAY;AAAA,EACd;AAEA,MAAI,QAAQ,SAAS,EAAG,OAAM,IAAI,MAAM,8DAA8D;AACtG,SAAO;AAAA,IACL,gBAAgB,QAAQ;AAAA,IACxB;AAAA,IACA;AAAA,IACA,UAAU,WAAW;AAAA,IACrB,YAAY,OAAO;AAAA,IACnB,eAAe,CAAC,GAAG,MAAM,EAAE,KAAK;AAAA,IAChC,QAAQ,CAAC,GAAG,QAAQ,OAAO,CAAC,EACzB,IAAI,YAAU;AAAA,MACb,MAAM,MAAM;AAAA,MACZ,SAAS,MAAM;AAAA,MACf,OAAO,MAAM;AAAA,MACb,MAAM,CAAC,GAAG,MAAM,IAAI,EAAE,KAAK;AAAA,MAC3B,UAAU,CAAC,GAAG,MAAM,QAAQ,EAAE,KAAK;AAAA,IACrC,EAAE,EACD,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,KAAK,KAAK,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAAA,EAClF;AACF;AAEA,SAAS,4BAA4B,QAAgC;AACnE,QAAM,UAAU,IAAI,IAAI,OAAO,aAAa;AAC5C,QAAM,gBAAgB,8BAA8B,OAAO,WAAS,CAAC,QAAQ,IAAI,KAAK,CAAC;AACvF,QAAM,SAAmB,CAAC;AAC1B,MAAI,cAAc,SAAS,EAAG,QAAO,KAAK,4BAA4B,cAAc,KAAK,IAAI,CAAC,EAAE;AAChG,MAAI,OAAO,WAAW,0BAA0B;AAC9C,WAAO,KAAK,QAAQ,OAAO,QAAQ,0BAA0B,wBAAwB,eAAe;AAAA,EACtG;AACA,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,IAAI,MAAM,kEAAkE,OAAO,KAAK,IAAI,CAAC,KAAK,OAAO,gBAAgB,kBAAkB;AAAA,EACnJ;AACF;AAEA,eAAsB,iCAAgD;AACpE,QAAM,KAAK,MAAM;AACjB,MAAI,aAAa,IAAI;AACnB,eAAW;AACX,oBAAgB;AAAA,EAClB;AACA,MAAI,CAAC,eAAe;AAClB,qBAAiB,YAAY;AAC3B,YAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAmBhB;AACD,YAAM,gBAAgB,IAAI,KAAK,MAAM,GAAG,QAAQ,0CAA0C,GAAG,KAAK,IAAI,SAAO,OAAO,IAAI,IAAI,CAAC,CAAC;AAC9H,YAAM,uBAAuB;AAAA,QAC3B,CAAC,oBAAoB,4BAA4B;AAAA,QACjD,CAAC,sBAAsB,4BAA4B;AAAA,QACnD,CAAC,aAAa,4BAA4B;AAAA,QAC1C,CAAC,uBAAuB,4BAA4B;AAAA,QACpD,CAAC,qBAAqB,4BAA4B;AAAA,MACpD;AACA,iBAAW,CAAC,QAAQ,UAAU,KAAK,sBAAsB;AACvD,YAAI,CAAC,cAAc,IAAI,MAAM,EAAG,OAAM,GAAG,QAAQ,gDAAgD,MAAM,IAAI,UAAU,EAAE;AAAA,MACzH;AACA,YAAM,GAAG,QAAQ,uHAAuH;AACxI,YAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAUhB;AACD,YAAM,GAAG,QAAQ,8GAA8G;AAC/H,YAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAgBhB;AACD,YAAM,GAAG,QAAQ,oHAAoH;AACrI,YAAM,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAMhB;AAAA,IACH,GAAG,EAAE,MAAM,WAAS;AAClB,sBAAgB;AAChB,YAAM;AAAA,IACR,CAAC;AAAA,EACH;AACA,QAAM;AACR;AAEA,eAAsB,uBAAuB,MAAwE;AACnH,QAAM,+BAA+B;AACrC,QAAM,SAAS,MAAM,MAAM,EAAE,QAAQ;AAAA,IACnC,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOL,MAAM,CAAC,IAAI;AAAA,EACb,CAAC;AACD,QAAM,MAAM,OAAO,KAAK,CAAC;AACzB,SAAO,MAAM,aAAa,GAAyC,IAAI;AACzE;AAEA,eAAsB,iCAAwE;AAC5F,SAAO,uBAAuB,uBAAuB;AACvD;AAEA,eAAsB,mCAAqE;AACzF,QAAM,+BAA+B;AACrC,QAAM,SAAS,MAAM,MAAM,EAAE,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAMpC;AACD,SAAO,OAAO,KAAK,IAAI,SAAO,aAAa,GAAyC,CAAC;AACvF;AAEA,eAAsB,mBAAmB,YAAmD;AAC1F,QAAM,QAAQ,gBAAgB,UAAU;AACxC,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,kDAAkD;AAC9E,QAAM,UAAU,MAAM,+BAA+B;AACrD,MAAI,CAAC,QAAS,QAAO,EAAE,SAAS,MAAM,QAAQ,oBAAI,IAAI,EAAE;AAExD,QAAM,SAAS,MAAM,MAAM,EAAE,QAAQ;AAAA,IACnC,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAML,MAAM,CAAC,QAAQ,IAAI,KAAK;AAAA,EAC1B,CAAC;AACD,QAAM,SAAS,oBAAI,IAA4B;AAC/C,aAAW,OAAO,OAAO,MAAM;AAC7B,UAAM,UAAU,OAAO,IAAI,QAAQ;AACnC,WAAO,IAAI,SAAS;AAAA,MAClB,MAAM,OAAO,IAAI,IAAI;AAAA,MACrB;AAAA,MACA,OAAO,OAAO,IAAI,UAAU;AAAA,MAC5B,MAAM,KAAK,MAAM,OAAO,IAAI,SAAS,CAAC;AAAA,MACtC,UAAU,KAAK,MAAM,OAAO,IAAI,aAAa,CAAC;AAAA,IAChD,CAAC;AAAA,EACH;AACA,SAAO,EAAE,SAAS,OAAO;AAC3B;AAEA,eAAsB,yBAAyB,OAGZ;AACjC,QAAM,+BAA+B;AACrC,QAAM,YAAY,oBAAoB,MAAM,SAAS;AACrD,QAAM,SAAS,WAAW,QAAQ,EAAE,OAAO,MAAM,GAAG,EAAE,OAAO,KAAK;AAClE,QAAM,SAAS,MAAM,+BAA+B;AACpD,MAAI,QAAQ,WAAW,OAAQ,QAAO,EAAE,SAAS,QAAQ,WAAW,KAAK;AAEzE,QAAM,SAAS,wBAAwB,MAAM,GAAG;AAChD,8BAA4B,MAAM;AAClC,QAAM,KAAK,OAAO,WAAW,EAAE,WAAW,KAAK,EAAE,CAAC;AAClD,QAAM,KAAK,MAAM;AACjB,QAAM,GAAG,QAAQ;AAAA,IACf,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAML,MAAM;AAAA,MACJ;AAAA,MAAI;AAAA,MAAyB;AAAA,MAAW;AAAA,MAAQ,OAAO;AAAA,MAAU,OAAO,OAAO;AAAA,MAAQ,OAAO;AAAA,MAC9F,OAAO;AAAA,MAAgB,OAAO;AAAA,MAAkB,OAAO;AAAA,MAAU,KAAK,UAAU,OAAO,aAAa;AAAA,IACtG;AAAA,EACF,CAAC;AAED,MAAI;AACF,aAAS,SAAS,GAAG,SAAS,OAAO,OAAO,QAAQ,UAAU,mBAAmB;AAC/E,YAAM,QAAQ,OAAO,OAAO,MAAM,QAAQ,SAAS,iBAAiB,EAAE,IAAI,YAAU;AAAA,QAClF,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,QAKL,MAAM,CAAC,IAAI,MAAM,OAAO,MAAM,SAAS,MAAM,MAAM,KAAK,UAAU,MAAM,IAAI,GAAG,KAAK,UAAU,MAAM,QAAQ,CAAC;AAAA,MAC/G,EAAE;AACF,YAAM,GAAG,MAAM,OAAO,OAAO;AAAA,IAC/B;AAEA,UAAM,GAAG,MAAM;AAAA,MACb;AAAA,QACE,KAAK;AAAA,QACL,MAAM,CAAC,EAAE;AAAA,MACX;AAAA,MACA;AAAA,QACE,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,QAKL,MAAM,CAAC,yBAAyB,EAAE;AAAA,MACpC;AAAA,IACF,GAAG,OAAO;AAAA,EACZ,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAM,GAAG,QAAQ;AAAA,MACf,KAAK;AAAA,MACL,MAAM,CAAC,QAAQ,MAAM,GAAG,GAAK,GAAG,EAAE;AAAA,IACpC,CAAC,EAAE,MAAM,MAAM,MAAS;AACxB,UAAM;AAAA,EACR;AAEA,QAAM,SAAS,MAAM,GAAG,QAAQ,EAAE,KAAK,4DAA4D,MAAM,CAAC,EAAE,EAAE,CAAC;AAC/G,QAAM,MAAM,OAAO,KAAK,CAAC;AACzB,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,kEAAkE;AAC5F,SAAO,EAAE,SAAS,aAAa,GAAyC,GAAG,WAAW,MAAM;AAC9F;AAEA,eAAsB,4BAA4B,OAIf;AACjC,QAAM,+BAA+B;AACrC,QAAM,QAAQ,gBAAgB,MAAM,KAAK;AACzC,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,kDAAkD;AAC9E,QAAM,OAAO,kBAAkB,KAAK;AACpC,QAAM,YAAY,oBAAoB,MAAM,SAAS;AACrD,QAAM,SAAS,WAAW,QAAQ,EAAE,OAAO,MAAM,GAAG,EAAE,OAAO,KAAK;AAClE,QAAM,SAAS,MAAM,uBAAuB,IAAI;AAChD,MAAI,QAAQ,WAAW,OAAQ,QAAO,EAAE,SAAS,QAAQ,WAAW,KAAK;AAEzE,QAAM,YAAY,mBAAmB,KAAwC;AAC7E,QAAM,SAAS,2BAA2B,MAAM,KAAK,SAAS;AAC9D,QAAM,KAAK,OAAO,WAAW,EAAE,WAAW,KAAK,EAAE,CAAC;AAClD,QAAM,KAAK,MAAM;AACjB,QAAM,GAAG,QAAQ;AAAA,IACf,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAML,MAAM,CAAC,IAAI,MAAM,WAAW,QAAQ,OAAO,OAAO,QAAQ,OAAO,OAAO,QAAQ,OAAO,gBAAgB,KAAK,UAAU,CAAC,KAAK,CAAC,CAAC;AAAA,EAChI,CAAC;AAED,MAAI;AACF,aAAS,SAAS,GAAG,SAAS,OAAO,OAAO,QAAQ,UAAU,mBAAmB;AAC/E,YAAM,QAAQ,OAAO,OAAO,MAAM,QAAQ,SAAS,iBAAiB,EAAE,IAAI,YAAU;AAAA,QAClF,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOL,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM,YAAY,IAAI;AAAA,UACtB,MAAM,YAAY,IAAI;AAAA,UACtB,MAAM,YAAY,IAAI;AAAA,UACtB,MAAM,YAAY,IAAI;AAAA,UACtB,MAAM,YAAY,IAAI;AAAA,UACtB,MAAM,YAAY,IAAI;AAAA,QACxB;AAAA,MACF,EAAE;AACF,YAAM,GAAG,MAAM,OAAO,OAAO;AAAA,IAC/B;AAEA,UAAM,GAAG,MAAM;AAAA,MACb;AAAA,QACE,KAAK;AAAA,QACL,MAAM,CAAC,EAAE;AAAA,MACX;AAAA,MACA;AAAA,QACE,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,QAKL,MAAM,CAAC,MAAM,EAAE;AAAA,MACjB;AAAA,IACF,GAAG,OAAO;AAAA,EACZ,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAM,GAAG,QAAQ;AAAA,MACf,KAAK;AAAA,MACL,MAAM,CAAC,QAAQ,MAAM,GAAG,GAAK,GAAG,EAAE;AAAA,IACpC,CAAC,EAAE,MAAM,MAAM,MAAS;AACxB,UAAM;AAAA,EACR;AAEA,QAAM,SAAS,MAAM,GAAG,QAAQ,EAAE,KAAK,4DAA4D,MAAM,CAAC,EAAE,EAAE,CAAC;AAC/G,QAAM,MAAM,OAAO,KAAK,CAAC;AACzB,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,gEAAgE;AAC1F,SAAO,EAAE,SAAS,aAAa,GAAyC,GAAG,WAAW,MAAM;AAC9F;AAEA,eAAsB,2BAA2B,OAAuE;AACtH,QAAM,QAAQ,gBAAgB,MAAM,KAAK;AACzC,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,kDAAkD;AAC9E,MAAI,CAAC,OAAO,cAAc,MAAM,aAAa,KAAK,MAAM,gBAAgB,EAAG,OAAM,IAAI,MAAM,8CAA8C;AACzI,MAAI,CAAC,OAAO,cAAc,MAAM,UAAU,KAAK,MAAM,aAAa,KAAK,MAAM,aAAa,IAAK,OAAM,IAAI,MAAM,sCAAsC;AAErJ,QAAM,oBAAoB,MAAM,uBAAuB,kBAAkB,KAAK,CAAC;AAC/E,QAAM,kBAAkB,MAAM,oBAAoB,QAAQ,MAAM,GAAG;AACnE,QAAM,YAAY,kBACd,MAAM,mBAAmB,KAAK,IAC9B,EAAE,SAAS,MAAM,QAAQ,oBAAI,IAA4B,EAAE;AAC/D,MAAI,CAAC,mBAAmB;AACtB,WAAO,EAAE,SAAS,CAAC,GAAG,mBAAmB,MAAM,YAAY,UAAU,SAAS,UAAU,CAAC,EAAE;AAAA,EAC7F;AAEA,QAAM,SAAS,MAAM,MAAM,EAAE,QAAQ;AAAA,IACnC,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAML,MAAM,CAAC,kBAAkB,IAAI,KAAK;AAAA,EACpC,CAAC;AACD,QAAM,YAAY,uBAAuB,MAAM,aAAa,EAAE;AAC9D,QAAM,MAAM,MAAM,KAAK,KAAK;AAC5B,QAAM,mBAAmB,cAAc,MAAM,cAAc;AAC3D,QAAM,UAAU,OAAO,KACpB,IAAI,CAAC,QAAgC;AACpC,UAAM,kBAAkB,IAAI,gBAAgB;AAC5C,QAAI,mBAAmB,KAAM,QAAO;AACpC,UAAM,aAAa,OAAO,eAAe;AACzC,QAAI,CAAC,OAAO,cAAc,UAAU,KAAK,aAAa,MAAM,cAAe,QAAO;AAClF,UAAM,OAAO,OAAO,IAAI,IAAI;AAC5B,UAAM,gBAAgB,OAAO,IAAI,QAAQ;AACzC,QAAI,aAAa,CAAC,cAAc,SAAS,SAAS,EAAG,QAAO;AAC5D,UAAM,WAAW,UAAU,OAAO,IAAI,aAAa;AACnD,UAAM,OAAO,UAAU,QAAQ,CAAC;AAChC,QAAI,OAAO,CAAC,KAAK,SAAS,GAAG,EAAG,QAAO;AACvC,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,UAAU,GAAG,IAAI,KAAK,KAAK;AAAA,MAC3B,SAAS,GAAG,IAAI,IAAI,KAAK;AAAA,MACzB,YAAY,OAAO,IAAI,WAAW;AAAA,MAClC;AAAA,MACA,gBAAgB,MAAM;AAAA,MACtB,mBAAmB,IAAI,uBAAuB,OAAO,OAAO,OAAO,IAAI,mBAAmB;AAAA,MAC1F,MAAM,MAAM,mBAAmB,OAAO,CAAC;AAAA,MACvC,UAAU,MAAM,mBAAmB,UAAU,YAAY,CAAC,IAAI,CAAC;AAAA,IACjE;AAAA,EACF,CAAC,EACA,OAAO,CAAC,WAAsC,WAAW,IAAI,EAC7D,KAAK,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,EAC1E,MAAM,GAAG,MAAM,UAAU;AAE5B,QAAM,WAAqB,CAAC;AAC5B,MAAI,MAAM,oBAAoB,UAAU,WAAW,QAAQ,KAAK,YAAU,OAAO,KAAK,WAAW,CAAC,GAAG;AACnG,aAAS,KAAK,qEAAqE;AAAA,EACrF;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,YAAY,UAAU;AAAA,IACtB;AAAA,EACF;AACF;","names":["key"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/api/private-artifacts.ts"],"sourcesContent":["import { createHash } from 'node:crypto'\nimport { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { createWriteStream } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { dirname, join } from 'node:path'\nimport { Readable, Transform } from 'node:stream'\nimport { pipeline } from 'node:stream/promises'\n\nexport interface PrivateArtifactPolicy {\n prefix: string\n artifactTtlMs: number | null\n downloadTtlMs: number\n token?: string | null\n localBaseDir?: string\n hosted?: boolean\n}\n\nexport interface PrivateArtifactPointer {\n artifactId: string\n filename: string\n contentType: string\n bytes: number\n sha256: string\n expiresAt: string\n downloadUrl: string | null\n downloadUrlExpiresAt: string | null\n}\n\nexport interface PrivateArtifactWindow {\n text: string\n totalBytes: number\n nextOffset: number | null\n}\n\nconst DURABLE_ARTIFACT_EXPIRES_AT = '9999-12-31T23:59:59.999Z'\n\nfunction hostedByEnvironment(): boolean {\n return process.env.VERCEL === '1' || process.env.NODE_ENV === 'production'\n}\n\nfunction policyHosted(policy: PrivateArtifactPolicy): boolean {\n return policy.hosted ?? hostedByEnvironment()\n}\n\nfunction policyLocalBaseDir(policy: PrivateArtifactPolicy): string {\n return policy.localBaseDir?.trim()\n || process.env.MCP_SCRAPER_OUTPUT_DIR?.trim()\n || join(homedir(), 'Downloads', 'mcp-scraper')\n}\n\nfunction normalizedPrefix(prefix: string): string {\n const normalized = prefix.replace(/^\\/+/, '').replace(/\\/+$/, '')\n if (!normalized || normalized.includes('..') || !/^[a-zA-Z0-9/_-]+$/.test(normalized)) {\n throw new Error('private artifact prefix is invalid')\n }\n return `${normalized}/`\n}\n\nfunction requiredSafeSegment(value: string, field: string): string {\n const trimmed = value.trim()\n if (!/^[a-zA-Z0-9_-]{1,160}$/.test(trimmed)) {\n throw new Error(`${field} must contain only letters, numbers, underscores, or hyphens`)\n }\n return trimmed\n}\n\nfunction requiredSafeArtifactKey(value: string): string {\n const trimmed = value.trim()\n if (!/^[a-zA-Z0-9_.-]{1,200}$/.test(trimmed) || trimmed === '.' || trimmed === '..') {\n throw new Error('private artifact key is invalid')\n }\n return trimmed\n}\n\nfunction safeDownloadFilename(value: string): string {\n const normalized = value.trim().replace(/[^a-zA-Z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '')\n return (normalized || 'artifact').slice(0, 160)\n}\n\nfunction createdAtMs(value: Date | string | number): number {\n const normalized = typeof value === 'string' && /^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/.test(value)\n ? `${value.replace(' ', 'T')}Z`\n : value\n const timestamp = normalized instanceof Date\n ? normalized.getTime()\n : typeof normalized === 'number'\n ? normalized\n : new Date(normalized).getTime()\n if (!Number.isSafeInteger(timestamp) || timestamp <= 0) throw new Error('private artifact createdAt is invalid')\n return timestamp\n}\n\nfunction validatePolicy(policy: PrivateArtifactPolicy): {\n prefix: string\n artifactTtlMs: number | null\n downloadTtlMs: number\n} {\n if (policy.artifactTtlMs !== null && (!Number.isSafeInteger(policy.artifactTtlMs) || policy.artifactTtlMs <= 0)) {\n throw new Error('private artifact TTL must be a positive integer')\n }\n if (!Number.isSafeInteger(policy.downloadTtlMs) || policy.downloadTtlMs <= 0) {\n throw new Error('private artifact download TTL must be a positive integer')\n }\n return {\n prefix: normalizedPrefix(policy.prefix),\n artifactTtlMs: policy.artifactTtlMs,\n downloadTtlMs: policy.downloadTtlMs,\n }\n}\n\nfunction artifactTimestamp(artifactId: string, prefix: string): number | null {\n const normalized = normalizedPrefix(prefix)\n if (!artifactId.startsWith(normalized)) return null\n const filename = artifactId.split('/').at(-1) ?? ''\n const match = filename.match(/^(\\d{13})-/)\n if (!match) return null\n const timestamp = Number(match[1])\n return Number.isSafeInteger(timestamp) && timestamp > 0 ? timestamp : null\n}\n\nexport function privateArtifactOwnerId(artifactId: string, prefix: string): string | null {\n const normalized = normalizedPrefix(prefix)\n if (!artifactId.startsWith(normalized) || artifactId.includes('..')) return null\n const rest = artifactId.slice(normalized.length)\n const segments = rest.split('/')\n if (segments.length < 2) return null\n try {\n return requiredSafeSegment(segments[0] ?? '', 'ownerId')\n } catch {\n return null\n }\n}\n\nexport function privateArtifactExpiresAt(artifactId: string, policy: PrivateArtifactPolicy): Date | null {\n const validated = validatePolicy(policy)\n if (privateArtifactOwnerId(artifactId, validated.prefix) === null) return null\n if (validated.artifactTtlMs === null) return null\n const timestamp = artifactTimestamp(artifactId, validated.prefix)\n return timestamp === null ? null : new Date(timestamp + validated.artifactTtlMs)\n}\n\nasync function signedDownloadUrl(pathname: string, expiresAt: Date | null, policy: PrivateArtifactPolicy): Promise<{\n url: string\n expiresAt: string\n} | null> {\n const token = policy.token?.trim()\n if (!token) return null\n const validUntil = expiresAt\n ? Math.min(Date.now() + policy.downloadTtlMs, expiresAt.getTime())\n : Date.now() + policy.downloadTtlMs\n if (validUntil <= Date.now()) return null\n const { issueSignedToken, presignUrl } = await import('@vercel/blob')\n const signedToken = await issueSignedToken({\n token,\n pathname,\n operations: ['get'],\n validUntil,\n })\n const { presignedUrl } = await presignUrl(signedToken, {\n access: 'private',\n operation: 'get',\n pathname,\n validUntil,\n })\n return { url: presignedUrl, expiresAt: new Date(validUntil).toISOString() }\n}\n\nexport async function createPrivateArtifact(args: {\n policy: PrivateArtifactPolicy\n ownerId: string\n artifactKey: string\n createdAt: Date | string | number\n filename: string\n contentType: string\n content: string | Buffer\n scopeSegments?: string[]\n}): Promise<PrivateArtifactPointer> {\n const policy = { ...args.policy, ...validatePolicy(args.policy) }\n const ownerId = requiredSafeSegment(args.ownerId, 'ownerId')\n const artifactKey = requiredSafeArtifactKey(args.artifactKey)\n const scope = (args.scopeSegments ?? []).map((segment, index) => requiredSafeSegment(segment, `scopeSegments[${index}]`))\n const timestamp = createdAtMs(args.createdAt)\n const requestedPathname = `${policy.prefix}${ownerId}/${scope.length ? `${scope.join('/')}/` : ''}${timestamp}-${artifactKey}`\n const body = Buffer.isBuffer(args.content) ? args.content : Buffer.from(args.content)\n const bytes = body.length\n const sha256 = createHash('sha256').update(body).digest('hex')\n const expiresAt = policy.artifactTtlMs === null ? null : new Date(timestamp + policy.artifactTtlMs)\n const token = policy.token?.trim() || null\n\n let artifactId = requestedPathname\n if (token) {\n const { put } = await import('@vercel/blob')\n const stored = await put(requestedPathname, body, {\n access: 'private',\n token,\n contentType: args.contentType,\n addRandomSuffix: false,\n allowOverwrite: true,\n cacheControlMaxAge: 60,\n multipart: bytes > 100 * 1024 * 1024,\n })\n artifactId = stored.pathname\n } else {\n if (policyHosted(policy)) throw new Error('private_artifact_blob_not_configured')\n const path = join(policyLocalBaseDir(policy), 'blobs', requestedPathname)\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, body)\n }\n\n const download = await signedDownloadUrl(artifactId, expiresAt, policy)\n return {\n artifactId,\n filename: safeDownloadFilename(args.filename),\n contentType: args.contentType,\n bytes,\n sha256,\n expiresAt: expiresAt?.toISOString() ?? DURABLE_ARTIFACT_EXPIRES_AT,\n downloadUrl: download?.url ?? null,\n downloadUrlExpiresAt: download?.expiresAt ?? null,\n }\n}\n\nexport async function createPrivateArtifactFromStream(args: {\n policy: PrivateArtifactPolicy\n ownerId: string\n artifactKey: string\n createdAt: Date | string | number\n filename: string\n contentType: string\n content: Readable\n scopeSegments?: string[]\n}): Promise<PrivateArtifactPointer> {\n const policy = { ...args.policy, ...validatePolicy(args.policy) }\n const ownerId = requiredSafeSegment(args.ownerId, 'ownerId')\n const artifactKey = requiredSafeArtifactKey(args.artifactKey)\n const scope = (args.scopeSegments ?? []).map((segment, index) => requiredSafeSegment(segment, `scopeSegments[${index}]`))\n const timestamp = createdAtMs(args.createdAt)\n const requestedPathname = `${policy.prefix}${ownerId}/${scope.length ? `${scope.join('/')}/` : ''}${timestamp}-${artifactKey}`\n const expiresAt = policy.artifactTtlMs === null ? null : new Date(timestamp + policy.artifactTtlMs)\n const token = policy.token?.trim() || null\n const hash = createHash('sha256')\n let bytes = 0\n const meter = new Transform({\n transform(chunk: Buffer, _encoding, callback) {\n bytes += chunk.length\n hash.update(chunk)\n callback(null, chunk)\n },\n })\n\n let artifactId = requestedPathname\n if (token) {\n const { put } = await import('@vercel/blob')\n const pumping = pipeline(args.content, meter)\n try {\n const stored = await put(requestedPathname, meter, {\n access: 'private',\n token,\n contentType: args.contentType,\n addRandomSuffix: false,\n allowOverwrite: true,\n cacheControlMaxAge: 60,\n multipart: true,\n })\n await pumping\n artifactId = stored.pathname\n } catch (error) {\n args.content.destroy(error instanceof Error ? error : new Error(String(error)))\n meter.destroy(error instanceof Error ? error : new Error(String(error)))\n await pumping.catch(() => undefined)\n throw error\n }\n } else {\n if (policyHosted(policy)) throw new Error('private_artifact_blob_not_configured')\n const path = join(policyLocalBaseDir(policy), 'blobs', requestedPathname)\n await mkdir(dirname(path), { recursive: true })\n await pipeline(args.content, meter, createWriteStream(path))\n }\n\n const sha256 = hash.digest('hex')\n const download = await signedDownloadUrl(artifactId, expiresAt, policy)\n return {\n artifactId,\n filename: safeDownloadFilename(args.filename),\n contentType: args.contentType,\n bytes,\n sha256,\n expiresAt: expiresAt?.toISOString() ?? DURABLE_ARTIFACT_EXPIRES_AT,\n downloadUrl: download?.url ?? null,\n downloadUrlExpiresAt: download?.expiresAt ?? null,\n }\n}\n\nexport async function renewPrivateArtifactDownload(args: {\n policy: PrivateArtifactPolicy\n artifactId: string\n ownerId: string\n}): Promise<{ downloadUrl: string; downloadUrlExpiresAt: string; expiresAt: string } | null> {\n if (privateArtifactOwnerId(args.artifactId, args.policy.prefix) !== args.ownerId) return null\n const expiresAt = privateArtifactExpiresAt(args.artifactId, args.policy)\n if (args.policy.artifactTtlMs !== null && (!expiresAt || expiresAt.getTime() <= Date.now())) return null\n const download = await signedDownloadUrl(args.artifactId, expiresAt, args.policy)\n if (!download) return null\n return {\n downloadUrl: download.url,\n downloadUrlExpiresAt: download.expiresAt,\n expiresAt: expiresAt?.toISOString() ?? DURABLE_ARTIFACT_EXPIRES_AT,\n }\n}\n\nasync function streamToBuffer(stream: ReadableStream<Uint8Array>): Promise<Buffer> {\n const reader = stream.getReader()\n const chunks: Buffer[] = []\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n if (value) chunks.push(Buffer.from(value))\n }\n } finally {\n reader.releaseLock()\n }\n return Buffer.concat(chunks)\n}\n\nexport async function readPrivateArtifactWindow(args: {\n policy: PrivateArtifactPolicy\n artifactId: string\n offset: number\n maxBytes: number\n}): Promise<PrivateArtifactWindow | null> {\n if (privateArtifactOwnerId(args.artifactId, args.policy.prefix) === null) return null\n const expiresAt = privateArtifactExpiresAt(args.artifactId, args.policy)\n if (args.policy.artifactTtlMs !== null && (!expiresAt || expiresAt.getTime() <= Date.now())) return null\n if (!Number.isSafeInteger(args.offset) || args.offset < 0) throw new Error('private artifact offset is invalid')\n if (!Number.isSafeInteger(args.maxBytes) || args.maxBytes <= 0) throw new Error('private artifact maxBytes is invalid')\n\n const token = args.policy.token?.trim() || null\n let buffer: Buffer\n if (token) {\n const { get } = await import('@vercel/blob')\n const result = await get(args.artifactId, { access: 'private', token, useCache: false })\n if (!result || result.statusCode !== 200) return null\n buffer = await streamToBuffer(result.stream)\n } else {\n if (policyHosted(args.policy)) return null\n try {\n buffer = await readFile(join(policyLocalBaseDir(args.policy), 'blobs', args.artifactId))\n } catch {\n return null\n }\n }\n\n const totalBytes = buffer.length\n const end = Math.min(totalBytes, args.offset + args.maxBytes)\n return {\n text: buffer.subarray(args.offset, end).toString('utf8'),\n totalBytes,\n nextOffset: end < totalBytes ? end : null,\n }\n}\n\nexport async function readPrivateArtifactBuffer(args: {\n policy: PrivateArtifactPolicy\n artifactId: string\n maxBytes: number\n}): Promise<Buffer | null> {\n if (privateArtifactOwnerId(args.artifactId, args.policy.prefix) === null) return null\n const expiresAt = privateArtifactExpiresAt(args.artifactId, args.policy)\n if (args.policy.artifactTtlMs !== null && (!expiresAt || expiresAt.getTime() <= Date.now())) return null\n if (!Number.isSafeInteger(args.maxBytes) || args.maxBytes <= 0) throw new Error('private artifact maxBytes is invalid')\n\n const token = args.policy.token?.trim() || null\n let buffer: Buffer\n if (token) {\n const { get } = await import('@vercel/blob')\n const result = await get(args.artifactId, { access: 'private', token, useCache: false })\n if (!result || result.statusCode !== 200) return null\n buffer = await streamToBuffer(result.stream)\n } else {\n if (policyHosted(args.policy)) return null\n try {\n buffer = await readFile(join(policyLocalBaseDir(args.policy), 'blobs', args.artifactId))\n } catch {\n return null\n }\n }\n if (buffer.length > args.maxBytes) throw new Error('private artifact exceeds read limit')\n return buffer\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/schemas.ts","../src/output/memory-library-sink.ts"],"sourcesContent":["import { z } from 'zod'\n\nexport const DEFAULT_PROXY_MODE = 'none' as const\nexport const DEFAULT_MAPS_PROXY_MODE = 'none' as const\n\nexport const HarvestOptionsSchema = z.object({\n query: z.string().min(1),\n location: z.string().optional(),\n gl: z.string().length(2).default('us'),\n hl: z.string().length(2).default('en'),\n device: z.enum(['desktop', 'mobile']).default('desktop'),\n proxyMode: z.enum(['location', 'configured', 'none']).default(DEFAULT_PROXY_MODE),\n proxyZip: z.string().regex(/^\\d{5}$/).optional(),\n keepDefaultProxy: z.boolean().optional(),\n requireUsEgress: z.boolean().optional(),\n maxAttempts: z.number().int().min(1).max(12).optional(),\n debug: z.boolean().default(false),\n depth: z.number().int().min(1).max(30).default(3),\n maxQuestions: z.number().int().min(1).max(1000).default(100),\n headless: z.boolean().default(false),\n profileDir: z.string().optional(),\n proxy: z.string().url().optional(),\n kernelApiKey: z.string().optional(),\n kernelProxyId: z.string().optional(),\n kernelProfileName: z.string().optional(),\n kernelProfileSaveChanges: z.boolean().optional(),\n kernelStealth: z.boolean().optional(),\n serpIdentity: z.string().regex(/^[a-z0-9][a-z0-9_-]{0,63}$/).optional(),\n kernelProxyResolution: z.unknown().optional(),\n outputDir: z.string().default('./paa-output'),\n format: z.enum(['json', 'csv', 'both']).default('both'),\n serpOnly: z.boolean().default(false),\n questionsOnly: z.boolean().default(false),\n questionGrowthRecoveryRounds: z.number().int().min(0).max(4).default(2),\n pages: z.number().int().min(1).max(2).default(1),\n includeAllSerpFeatures: z.boolean().default(false),\n includeLocalPack: z.boolean().default(false),\n includeForums: z.boolean().default(false),\n includeVideos: z.boolean().default(false),\n includeAiOverview: z.boolean().default(false),\n includeWhatPeopleSaying: z.boolean().default(false),\n includeShortVideos: z.boolean().default(false),\n recency: z.enum(['day', 'week', 'month', 'year']).optional(),\n softDeadlineMs: z.number().optional(),\n})\n\nexport function recencyToTbs(recency: 'day' | 'week' | 'month' | 'year' | undefined): string | undefined {\n switch (recency) {\n case 'day': return 'qdr:d'\n case 'week': return 'qdr:w'\n case 'month': return 'qdr:m'\n case 'year': return 'qdr:y'\n default: return undefined\n }\n}\n\nexport const MapsPlaceOptionsSchema = z.object({\n businessName: z.string().min(1),\n location: z.string().min(1),\n gl: z.string().length(2).default('us'),\n hl: z.string().length(2).default('en'),\n includeReviews: z.boolean().default(false),\n maxReviews: z.number().int().min(1).max(500).default(50),\n includeServices: z.boolean().default(false),\n includeImages: z.boolean().default(false),\n imageScope: z.enum(['owner', 'all']).default('all'),\n maxImages: z.number().int().min(1).max(250).default(100),\n maxInlineImages: z.number().int().min(0).max(5).default(3),\n kernelApiKey: z.string().optional(),\n kernelProxyId: z.string().optional(),\n headless: z.boolean().default(true),\n})\n\nexport const MapsSearchOptionsSchema = z.object({\n query: z.string().min(1),\n location: z.string().optional(),\n gl: z.string().length(2).default('us'),\n hl: z.string().length(2).default('en'),\n maxResults: z.number().int().min(1).max(50).default(10),\n includeServices: z.boolean().default(false),\n proxyMode: z.enum(['location', 'configured', 'none']).default(DEFAULT_MAPS_PROXY_MODE),\n proxyZip: z.string().regex(/^\\d{5}$/).optional(),\n serpRedirect: z.boolean().optional(),\n forceDirectEgress: z.boolean().optional(),\n debug: z.boolean().default(false),\n kernelApiKey: z.string().optional(),\n kernelProxyId: z.string().optional(),\n kernelProxyResolution: z.unknown().optional(),\n headless: z.boolean().default(true),\n})\n\nexport const RawPAAItemSchema = z.object({\n question: z.string().min(1),\n answer: z.string().optional(),\n sourceTitle: z.string().optional(),\n sourceSite: z.string().optional(),\n sourceCite: z.string().optional(),\n})\n\nexport const RawMapsOverviewSchema = z.object({\n name: z.string().nullable(),\n rating: z.string().nullable(),\n reviewCount: z.string().nullable(),\n category: z.string().nullable(),\n address: z.string().nullable(),\n hoursSummary: z.string().nullable(),\n phone: z.string().nullable(),\n phoneDisplay: z.string().nullable(),\n website: z.string().nullable(),\n plusCode: z.string().nullable(),\n bookingUrl: z.string().nullable(),\n})\n\nexport const RawMapsHoursRowSchema = z.object({\n day: z.string(),\n hours: z.string(),\n})\n\nexport const RawMapsReviewStatsSchema = z.object({\n reviewHistogram: z.array(z.object({\n stars: z.number(),\n count: z.string(),\n })),\n reviewTopics: z.array(z.object({\n label: z.string(),\n count: z.string(),\n })),\n})\n\nexport const RawMapsReviewCardSchema = z.object({\n reviewId: z.string(),\n author: z.string().nullable(),\n stars: z.string().nullable(),\n date: z.string().nullable(),\n text: z.string().nullable(),\n ownerResponse: z.string().nullable(),\n})\n\nexport const RawMapsAboutAttributeSchema = z.object({\n section: z.string(),\n attribute: z.string(),\n})\n","export async function postToMemoryLibrary(args: { title: string; content: string; source: string }): Promise<void> {\n const url = process.env.MCP_MEMORY_INGEST_URL\n const key = process.env.MCP_MEMORY_KEY\n if (!url || !key) return\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ data: { apiKey: key, title: args.title, content: args.content, source: args.source } }),\n })\n const body = (await res.json().catch(() => null)) as { result?: { error?: boolean; message?: string } } | null\n if (!res.ok || body?.result?.error) {\n console.warn('[memory-library-sink] ingest not accepted:', res.status, body?.result?.message ?? '')\n }\n } catch (err) {\n console.warn('[memory-library-sink] ingest failed:', (err as Error)?.message)\n }\n}\n\nexport function memoryLibrarySinkEnabled(): boolean {\n return Boolean(process.env.MCP_MEMORY_INGEST_URL && process.env.MCP_MEMORY_KEY)\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/directory/csv.ts"],"sourcesContent":["export function parseCsv(text: string): string[][] {\n const rows: string[][] = []\n let row: string[] = []\n let field = ''\n let quoted = false\n\n for (let i = 0; i < text.length; i += 1) {\n const ch = text[i]\n const next = text[i + 1]\n\n if (quoted) {\n if (ch === '\"' && next === '\"') {\n field += '\"'\n i += 1\n } else if (ch === '\"') {\n quoted = false\n } else {\n field += ch\n }\n continue\n }\n\n if (ch === '\"') {\n quoted = true\n } else if (ch === ',') {\n row.push(field)\n field = ''\n } else if (ch === '\\n') {\n row.push(field)\n rows.push(row)\n row = []\n field = ''\n } else if (ch !== '\\r') {\n field += ch\n }\n }\n\n if (field.length > 0 || row.length > 0) {\n row.push(field)\n rows.push(row)\n }\n\n return rows\n}\n\nexport function csvRecords(text: string): Array<Record<string, string>> {\n const rows = parseCsv(text).filter(row => row.some(cell => cell.trim() !== ''))\n const header = rows[0]?.map(cell => cell.trim()) ?? []\n return rows.slice(1).map((row) => {\n const record: Record<string, string> = {}\n for (let i = 0; i < header.length; i += 1) {\n record[header[i]] = row[i] ?? ''\n }\n return record\n })\n}\n\nexport function csvCell(value: unknown): string {\n if (value === null || value === undefined) return ''\n const text = String(value)\n return /[\",\\n\\r]/.test(text) ? `\"${text.replace(/\"/g, '\"\"')}\"` : text\n}\n\nexport function rowsToCsv(headers: string[], rows: Array<Record<string, unknown>>): string {\n return [\n headers.join(','),\n ...rows.map(row => headers.map(header => csvCell(row[header])).join(',')),\n ].join('\\n') + '\\n'\n}\n"],"mappings":";AAAO,SAAS,SAAS,MAA0B;AACjD,QAAM,OAAmB,CAAC;AAC1B,MAAI,MAAgB,CAAC;AACrB,MAAI,QAAQ;AACZ,MAAI,SAAS;AAEb,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,UAAM,KAAK,KAAK,CAAC;AACjB,UAAM,OAAO,KAAK,IAAI,CAAC;AAEvB,QAAI,QAAQ;AACV,UAAI,OAAO,OAAO,SAAS,KAAK;AAC9B,iBAAS;AACT,aAAK;AAAA,MACP,WAAW,OAAO,KAAK;AACrB,iBAAS;AAAA,MACX,OAAO;AACL,iBAAS;AAAA,MACX;AACA;AAAA,IACF;AAEA,QAAI,OAAO,KAAK;AACd,eAAS;AAAA,IACX,WAAW,OAAO,KAAK;AACrB,UAAI,KAAK,KAAK;AACd,cAAQ;AAAA,IACV,WAAW,OAAO,MAAM;AACtB,UAAI,KAAK,KAAK;AACd,WAAK,KAAK,GAAG;AACb,YAAM,CAAC;AACP,cAAQ;AAAA,IACV,WAAW,OAAO,MAAM;AACtB,eAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI,MAAM,SAAS,KAAK,IAAI,SAAS,GAAG;AACtC,QAAI,KAAK,KAAK;AACd,SAAK,KAAK,GAAG;AAAA,EACf;AAEA,SAAO;AACT;AAEO,SAAS,WAAW,MAA6C;AACtE,QAAM,OAAO,SAAS,IAAI,EAAE,OAAO,SAAO,IAAI,KAAK,UAAQ,KAAK,KAAK,MAAM,EAAE,CAAC;AAC9E,QAAM,SAAS,KAAK,CAAC,GAAG,IAAI,UAAQ,KAAK,KAAK,CAAC,KAAK,CAAC;AACrD,SAAO,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,QAAQ;AAChC,UAAM,SAAiC,CAAC;AACxC,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,GAAG;AACzC,aAAO,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK;AAAA,IAChC;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAEO,SAAS,QAAQ,OAAwB;AAC9C,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,QAAM,OAAO,OAAO,KAAK;AACzB,SAAO,WAAW,KAAK,IAAI,IAAI,IAAI,KAAK,QAAQ,MAAM,IAAI,CAAC,MAAM;AACnE;AAEO,SAAS,UAAU,SAAmB,MAA8C;AACzF,SAAO;AAAA,IACL,QAAQ,KAAK,GAAG;AAAA,IAChB,GAAG,KAAK,IAAI,SAAO,QAAQ,IAAI,YAAU,QAAQ,IAAI,MAAM,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC;AAAA,EAC1E,EAAE,KAAK,IAAI,IAAI;AACjB;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/html-document.ts"],"sourcesContent":["import { load, type Cheerio, type CheerioAPI } from 'cheerio'\nimport type { AnyNode, Element } from 'domhandler'\n\nexport interface ParsedHeading {\n level: number\n text: string\n}\n\nexport interface ParsedLink {\n href: string\n text: string\n rel: string | null\n}\n\nexport interface ParsedImage {\n src: string | null\n srcset: string | null\n alt: string | null\n width: number | null\n height: number | null\n attributes: Record<string, string>\n}\n\nexport interface HtmlDocument {\n $: CheerioAPI\n sourceHtml: string\n title: string | null\n meta: Record<string, string>\n headings: ParsedHeading[]\n schema: unknown[]\n schemaTypes: string[]\n microdataTypes: string[]\n canonicalUrl: string | null\n mainHtml: string\n mainText: string\n bodyClass: string\n links: ParsedLink[]\n images: ParsedImage[]\n}\n\nconst REMOVED_CONTENT = 'script,style,noscript,svg,iframe,canvas,nav,header,footer,aside'\nconst MAX_CONTENT_URL_ATTRIBUTE_CHARS = 4_096\n\nfunction normalizedText(value: string | null | undefined): string {\n return (value ?? '').replace(/\\s+/g, ' ').trim()\n}\n\nfunction parseJsonLd(raw: string): unknown | null {\n const clean = raw.replace(/[\\x00-\\x09\\x0b\\x0c\\x0e-\\x1f]/g, ' ').trim()\n if (!clean) return null\n try {\n return JSON.parse(clean)\n } catch (error) {\n const position = /at position (\\d+)/.exec(error instanceof Error ? error.message : '')?.[1]\n if (!position) return null\n try {\n return JSON.parse(clean.slice(0, Number(position)))\n } catch {\n return null\n }\n }\n}\n\nfunction collectSchemaTypes(value: unknown, output: Set<string>, remaining = { value: 2_000 }): void {\n if (remaining.value-- <= 0 || value == null) return\n if (Array.isArray(value)) {\n for (const item of value) collectSchemaTypes(item, output, remaining)\n return\n }\n if (typeof value !== 'object') return\n const record = value as Record<string, unknown>\n const type = record['@type']\n if (typeof type === 'string' && type) output.add(type)\n if (Array.isArray(type)) {\n for (const item of type) if (typeof item === 'string' && item) output.add(item)\n }\n if (Array.isArray(record['@graph'])) collectSchemaTypes(record['@graph'], output, remaining)\n}\n\nfunction contentRoot($: CheerioAPI): Cheerio<AnyNode> {\n const main = $('main').first()\n if (main.length) return main\n const article = $('article').first()\n if (article.length) return article\n const body = $('body').first()\n return body.length ? body : $.root()\n}\n\nfunction numericAttribute(element: Cheerio<Element>, name: string): number | null {\n const value = Number(element.attr(name) ?? 0)\n return Number.isFinite(value) && value > 0 ? value : null\n}\n\n/**\n * Parse acquired HTML once with Cheerio's standards-oriented parse5 loader.\n * All extraction surfaces consume this representation instead of matching HTML\n * tags or attributes with independent regular expressions.\n */\nexport function loadHtmlDocument(sourceHtml: string): HtmlDocument {\n const $ = load(sourceHtml)\n const meta: Record<string, string> = {}\n $('meta').each((_index, element) => {\n const node = $(element)\n const key = normalizedText(node.attr('name') ?? node.attr('property')).toLowerCase()\n const content = normalizedText(node.attr('content'))\n if (key && content) meta[key] = content\n })\n\n const headings: ParsedHeading[] = []\n $('h1,h2,h3,h4,h5,h6').each((_index, element) => {\n const node = $(element)\n const text = normalizedText(node.text())\n if (text) headings.push({ level: Number(element.tagName.slice(1)), text })\n })\n\n const schema: unknown[] = []\n const schemaTypeSet = new Set<string>()\n $('script[type=\"application/ld+json\"]').each((_index, element) => {\n const parsed = parseJsonLd($(element).text())\n if (parsed == null) return\n if (Array.isArray(parsed)) schema.push(...parsed)\n else schema.push(parsed)\n collectSchemaTypes(parsed, schemaTypeSet)\n })\n\n const microdataTypes = new Set<string>()\n $('[itemtype]').each((_index, element) => {\n const itemtype = $(element).attr('itemtype') ?? ''\n for (const value of itemtype.split(/\\s+/)) {\n try {\n const url = new URL(value)\n if (url.hostname === 'schema.org' || url.hostname === 'www.schema.org') {\n const type = url.pathname.replace(/^\\/+/, '')\n if (type) microdataTypes.add(type)\n }\n } catch { /* Ignore non-URL itemtype values. */ }\n }\n })\n\n const root = contentRoot($).clone()\n root.find(REMOVED_CONTENT).remove()\n root.find('[href],[src],[srcset],[data-src],[data-srcset]').each((_index, element) => {\n const node = $(element)\n for (const attribute of ['href', 'src', 'srcset', 'data-src', 'data-srcset']) {\n const value = node.attr(attribute)\n if (value && value.length > MAX_CONTENT_URL_ATTRIBUTE_CHARS) node.removeAttr(attribute)\n }\n })\n root.find('img').each((_index, element) => {\n const image = $(element)\n const src = image.attr('src')\n if (!src || src.startsWith('data:')) {\n const lazy = image.attr('data-src') ?? image.attr('data-lazy-src') ?? image.attr('data-original')\n if (lazy) image.attr('src', lazy)\n }\n })\n const mainHtml = root.html() ?? ''\n const mainText = normalizedText(root.text())\n\n const links: ParsedLink[] = []\n $('a[href]').each((_index, element) => {\n const node = $(element)\n const href = normalizedText(node.attr('href'))\n if (!href) return\n links.push({\n href,\n text: normalizedText(node.text()),\n rel: normalizedText(node.attr('rel')).toLowerCase() || null,\n })\n })\n\n const images: ParsedImage[] = []\n $('img').each((_index, element) => {\n const node = $(element)\n const attributes = Object.fromEntries(Object.entries(element.attribs ?? {}).map(([key, value]) => [key.toLowerCase(), value]))\n images.push({\n src: node.attr('src') ?? node.attr('data-src') ?? node.attr('data-lazy-src') ?? node.attr('data-original') ?? null,\n srcset: node.attr('srcset') ?? node.attr('data-srcset') ?? null,\n alt: node.attr('alt') ?? null,\n width: numericAttribute(node, 'width'),\n height: numericAttribute(node, 'height'),\n attributes,\n })\n })\n\n return {\n $,\n sourceHtml,\n title: normalizedText($('title').first().text()) || null,\n meta,\n headings,\n schema,\n schemaTypes: [...schemaTypeSet],\n microdataTypes: [...microdataTypes],\n canonicalUrl: normalizedText($('link[rel~=\"canonical\"]').first().attr('href')) || null,\n mainHtml,\n mainText,\n bodyClass: $('body').first().attr('class') ?? '',\n links,\n images,\n }\n}\n"],"mappings":"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","names":[]}