mcp-scraper 0.95.2 → 0.96.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.
- package/CHANGELOG.md +67 -1
- package/README.md +21 -3
- package/dist/actions-R4EJDLRO.js +1 -0
- package/dist/artifacts-4ZDNP2JW.js +1 -0
- package/dist/artifacts-N62OPIT5.js +1 -0
- package/dist/artifacts-PSADD5OC.js +1 -0
- package/dist/artifacts-RUR3OHAK.js +1 -0
- package/dist/bin/api-server.js +1 -1
- package/dist/bin/mcp-scraper-cli.js +22 -22
- package/dist/bin/mcp-scraper-core.js +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.js +1 -1
- package/dist/bin/paa-harvest.js +1 -1
- package/dist/blob-store-GIH26EDO.js +1 -0
- package/dist/bundle-FCXZMIRB.js +1 -0
- package/dist/chunk-24YDQ3EO.js +5 -0
- package/dist/chunk-3F7AFM53.js +1 -0
- package/dist/chunk-4HZXATHB.js +27 -0
- package/dist/chunk-55ZANTHL.js +3 -0
- package/dist/chunk-5BKSKLHX.js +1 -0
- package/dist/chunk-5LYINW7Z.js +8 -0
- package/dist/chunk-63VWBMYR.js +3 -0
- package/dist/chunk-6BD75VKS.js +218 -0
- package/dist/chunk-6N5EC7ZO.js +1 -0
- package/dist/chunk-7POHNTVW.js +1 -0
- package/dist/chunk-7SR7MEU5.js +62 -0
- package/dist/chunk-7WPCGRD7.js +182 -0
- package/dist/chunk-B3JRL45T.js +1 -0
- package/dist/chunk-B4ZKPLSP.js +1 -0
- package/dist/chunk-BPKTUCV7.js +12 -0
- package/dist/chunk-C7MGJUTU.js +1 -0
- package/dist/{chunk-W2BVJ7S2.js → chunk-CNSVZTTT.js} +4 -4
- package/dist/chunk-CORWWDHL.js +1 -0
- package/dist/chunk-CVQUHVHX.js +1 -0
- package/dist/chunk-FB4KBB5W.js +16 -0
- package/dist/{chunk-Y3Q4FIKK.js → chunk-H6MODA7W.js} +199 -84
- package/dist/chunk-HCK6PLM3.js +3 -0
- package/dist/chunk-HNNYXMSI.js +1 -0
- package/dist/chunk-HUNSEGUN.js +57 -0
- package/dist/{chunk-MC4W6TZT.js → chunk-HVQZRQRJ.js} +41 -8
- package/dist/chunk-IFPION5D.js +121 -0
- package/dist/chunk-IY6ZUUY7.js +173 -0
- package/dist/chunk-JHT6A4U6.js +11 -0
- package/dist/chunk-JMZIDHYQ.js +2 -0
- package/dist/chunk-KXJCGV3H.js +1 -0
- package/dist/chunk-MVMWPKRC.js +2 -0
- package/dist/chunk-NALXKTUU.js +1 -0
- package/dist/chunk-NNOX3FYG.js +198 -0
- package/dist/chunk-OILOHQNL.js +2 -0
- package/dist/chunk-OLYPLX5C.js +1 -0
- package/dist/chunk-QER2ELZP.js +1 -0
- package/dist/chunk-RDHGQ2WZ.js +1 -0
- package/dist/chunk-RIHI3KH7.js +955 -0
- package/dist/chunk-RN4VG3VH.js +4 -0
- package/dist/chunk-RYX4FJVH.js +1 -0
- package/dist/chunk-SDQW7IJN.js +1 -0
- package/dist/{chunk-ENZUJEIM.js → chunk-SE4VKLEC.js} +1 -1
- package/dist/chunk-SEZETSB3.js +1 -0
- package/dist/chunk-SFUOSJ3A.js +159 -0
- package/dist/chunk-UIOSADPB.js +1718 -0
- package/dist/{chunk-ZH75KWBI.js → chunk-UNEJYZ4T.js} +1 -1
- package/dist/chunk-UXNR3K6I.js +1 -0
- package/dist/{chunk-VREXSMC6.js → chunk-VVSHZV5S.js} +5 -5
- package/dist/chunk-WNUXMPVX.js +1 -0
- package/dist/chunk-WP3QS4CJ.js +1 -0
- package/dist/chunk-ZTMQEDIB.js +41 -0
- package/dist/contracts-GUXGFCVV.js +1 -0
- package/dist/core-schema-423BSTE6.js +936 -0
- package/dist/dispatch-P7IY75LH.js +1 -0
- package/dist/index.cjs +103 -213
- package/dist/index.d.cts +184 -169
- package/dist/index.d.ts +184 -169
- package/dist/index.js +1 -1
- package/dist/media-artifacts-67CJWI7O.js +1 -0
- package/dist/metering-ZH2P62IC.js +1 -0
- package/dist/migrations-3AKEC5QE.js +1 -0
- package/dist/queue-AC4XKD6Z.js +1 -0
- package/dist/reconciliation-UZUZ6B74.js +1 -0
- package/dist/report-artifact-offload-6OOY2PTM.js +1 -0
- package/dist/repository-4EC5EXVN.js +1 -0
- package/dist/{location-data-repository-A2QZVP7O.js → repository-CYXVMYAI.js} +1 -1
- package/dist/server-DXZ27KQO.js +5825 -0
- package/dist/settlement-6CS4JZTY.js +1 -0
- package/dist/worker-ATRD6VKM.js +1 -0
- package/package.json +2 -2
- package/dist/analytics-repository-DZZKDZL6.js +0 -1
- package/dist/chunk-6DRKDSXY.js +0 -182
- package/dist/chunk-6ND5AFV5.js +0 -27
- package/dist/chunk-7XDBGQDY.js +0 -6
- package/dist/chunk-7YOXSMHG.js +0 -1565
- package/dist/chunk-BLVBUMH5.js +0 -21
- package/dist/chunk-EDQ57S4P.js +0 -1
- package/dist/chunk-GLTECY3U.js +0 -1282
- package/dist/chunk-GPSPSJBJ.js +0 -1
- package/dist/chunk-HE45FFBU.js +0 -1
- package/dist/chunk-HUV2WTRW.js +0 -1
- package/dist/chunk-IXJQHXDC.js +0 -2595
- package/dist/chunk-K3D2S36Q.js +0 -16
- package/dist/chunk-KFOFU3LA.js +0 -1
- package/dist/chunk-LMWU7VPZ.js +0 -63
- package/dist/chunk-O4LUUIYG.js +0 -4
- package/dist/chunk-OLSW4AN4.js +0 -19
- package/dist/chunk-RLFCJGHA.js +0 -100
- package/dist/chunk-T4VTJNDZ.js +0 -84
- package/dist/chunk-TMB56NCA.js +0 -1
- package/dist/chunk-VXOXB3IB.js +0 -1
- package/dist/chunk-WO3N5FH2.js +0 -5
- package/dist/chunk-WO46LTBW.js +0 -1
- package/dist/chunk-XPEJB4BZ.js +0 -1
- package/dist/db-CDEZHBRN.js +0 -1
- package/dist/extract-bundle-SXC6EWJR.js +0 -26
- package/dist/gmail-service-EWR76XE7.js +0 -1
- package/dist/lead-list-enrichment-repository-SIWGXWQV.js +0 -1
- package/dist/operation-metering-UJ53YX7S.js +0 -1
- package/dist/server-QVEKDIDQ.js +0 -5014
- package/dist/site-extract-repository-23ND4N77.js +0 -1
- package/dist/stripe-event-worker-LPXTITVC.js +0 -1
- package/dist/worker-EDN2TJH3.js +0 -1
- /package/dist/{chunk-YGBTTW5D.js → chunk-2IATUJO6.js} +0 -0
- /package/dist/{chunk-6DTXIZY2.js → chunk-4ZVO6GX4.js} +0 -0
- /package/dist/{chunk-XG6GCEUE.js → chunk-U4WIJZCX.js} +0 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as D}from"./chunk-OLYPLX5C.js";import{b as R}from"./chunk-B3JRL45T.js";import{c as P}from"./chunk-OILOHQNL.js";import{createWriteStream as H,mkdirSync as W,rmSync as N}from"fs";import{join as A,extname as B,basename as z}from"path";import{pipeline as O}from"stream/promises";import{Readable as I,Transform as q}from"stream";var X=["doubleclick.net","googlesyndication.com","googletagmanager.com","google-analytics.com","googletagservices.com","adservice.google","googletag.","pagead2.googlesyndication","facebook.net/tr","connect.facebook.net","fbcdn.net/rsrc","analytics.twitter.com","static.ads-twitter.com","ads.twitter.com","t.co/i/adsct","pixel.advertising.com","hotjar.com","clarity.ms","quantserve.com","scorecardresearch.com","newrelic.com","nr-data.net","segment.io","segment.com","amplitude.com","mixpanel.com","heap.io","fullstory.com","moatads.com","criteo.com","adsrvr.org","rubiconproject.com","pubmatic.com","openx.net","appnexus.com","amazon-adsystem.com","media.net","yieldmo.com","triplelift.com","sharethrough.com","prebid.","smaato.net","indexworm.com","casalemedia.com","outbrain.com","taboola.com","revcontent.com","mgid.com","tawk.to","intercom.io","drift.com","hs-scripts.com","zopim.com","livechatinc.com","userlike.com","onetrust.com","cookielaw.org","cookieinformation.com","trustarc.com","/ads/","/ad/","/banner/","/banners/","/pixel/","/beacon/","/tracking/","/tracker/","/remarketing/","/conversion/","1x1.gif","spacer.gif","blank.gif","transparent.gif"],G=new Set([".jpg",".jpeg",".png",".webp",".gif",".avif",".svg",".tiff"]),K=new Set([".mp4",".webm",".mov",".avi",".m4v",".ogv",".mkv"]),V=new Set([".mp3",".wav",".ogg",".aac",".m4a",".flac",".opus"]);function Z(r){let e=r.toLowerCase();return X.some(t=>e.includes(t))}function J(r){return r.startsWith("data:")}function U(r){try{let e=B(new URL(r).pathname).toLowerCase();if(G.has(e))return"image";if(K.has(e))return"video";if(V.has(e))return"audio"}catch{}return null}function L(r){let e=r.toLowerCase();return e.startsWith("image/")?"image":e.startsWith("video/")?"video":e.startsWith("audio/")?"audio":null}function Q(r,e){if(!r||J(r))return null;try{return new URL(r,e).href}catch{return null}}function C(r,e){try{let t=new URL(r);return z(t.pathname).replace(/[^a-zA-Z0-9._-]/g,"_").slice(0,80)||`asset-${e}`}catch{return`asset-${e}`}}function k(r,e){let t=r?.replace(/&(?:amp|#0?38|#x26);/gi,"&").replace(/\s+/g," ").trim();return t?t.slice(0,e):null}function Y(r){let e=r.match(/[-_/](\d{2,5})x(\d{2,5})(?=[._/?#-]|$)/i);return e?{width:Number(e[1]),height:Number(e[2])}:null}function ee(r){try{let e=new URL(r);e.hash="",e.pathname=e.pathname.replace(/-(?:\d{2,5}x\d{2,5}|scaled|e\d{6,})(?=\.[a-z0-9]{2,6}$)/i,"");for(let t of["w","width","h","height","resize"])e.searchParams.delete(t);return e.searchParams.sort(),e.href}catch{return r}}function T(r){let e=(r.width??0)*(r.height??0),t=r.discoveryMethods.some(f=>f.includes("lightbox"))?1e12:0,u=/-(?:\d{2,5}x\d{2,5}|scaled|e\d{6,})(?=\.[a-z0-9]{2,6}(?:[?#]|$))/i.test(r.url)?0:1e11;return t+u+e}function S(r,e){let t=r.get(e.url);if(!t){r.set(e.url,e);return}t.discoveryMethods=[...new Set([...t.discoveryMethods,...e.discoveryMethods])],t.altTexts=[...new Set([...t.altTexts,...e.altTexts])],t.contexts=[...new Set([...t.contexts,...e.contexts])],!t.type&&e.type&&(t.type=e.type),(e.width??0)*(e.height??0)>(t.width??0)*(t.height??0)&&(t.width=e.width,t.height=e.height)}function te(r,e){let t=new Map,u=(l,s,a=null,h,i,c)=>{let n=l?Q(l.trim().replace(/&/g,"&"),e):null;if(!n||Z(n))return;let d=c??Y(n);S(t,{url:n,type:a??U(n),discoveryMethods:[s],altTexts:h?[h]:[],contexts:i?[i]:[],width:d?.width??null,height:d?.height??null})},f=(l,s,a,h)=>{for(let i of(l??"").split(",")){let[c,n]=i.trim().split(/\s+/);if(!c)continue;let d=n?.match(/^(\d+)w$/)?.[1];u(c,s,a,h,null,d?{width:Number(d),height:0}:null)}},m=(l,s="")=>{let a=l.$;for(let i of l.images){let c=i.attributes,n=k(i.alt,500),d=i.width&&i.height?{width:i.width,height:i.height}:null;for(let g of["src","data-src","data-lazy-src","data-original","data-bg","data-background","data-bg-url","data-lazy-bg","data-echo"]){let y=c[g];u(y,`static-${g}${s}`,"image",n,null,d)}f(c.srcset,`static-srcset${s}`,"image",n),f(c["data-srcset"],`static-data-srcset${s}`,"image",n)}a("source").each((i,c)=>{let n=a(c),d=n.attr("type")??"",g=d.startsWith("video/")?"video":d.startsWith("audio/")?"audio":"image";u(n.attr("src"),`static-source${s}`,g),f(n.attr("srcset"),`static-source-srcset${s}`,g)}),a("video,audio").each((i,c)=>{let n=a(c),d=c.tagName.toLowerCase();u(n.attr("src"),`static-${d}${s}`,d),d==="video"&&u(n.attr("poster"),`static-video-poster${s}`,"image")}),a("meta").each((i,c)=>{let n=a(c),d=(n.attr("property")??n.attr("name")??"").toLowerCase();(d==="og:image"||d==="og:image:url"||d==="twitter:image"||d==="twitter:image:src")&&u(n.attr("content"),`static-${d}${s}`,"image")}),a("link[href]").each((i,c)=>{let n=a(c);(n.attr("rel")??"").toLowerCase().split(/\s+/).some(g=>g==="icon"||g==="apple-touch-icon")&&u(n.attr("href"),`static-icon${s}`,"image")}),a("svg image").each((i,c)=>{let n=a(c);u(n.attr("href")??n.attr("xlink:href"),`static-svg-image${s}`,"image")}),a("a[href]").has("img").each((i,c)=>{let n=a(c),d=n.attr("href");if(d&&(/lightbox|gallery|fancybox|glightbox|swipebox|elementor-open-lightbox/i.test(`${n.attr("class")??""} ${n.attr("data-elementor-open-lightbox")??""}`)||/\.(?:avif|gif|jpe?g|png|svg|webp)(?:[?#]|$)/i.test(d))){let g=k(n.find("img").first().attr("alt"),500);u(d,`static-lightbox-target${s}`,"image",g)}});let h=a("style").toArray().map(i=>a(i).text());a("[style]").each((i,c)=>{h.push(a(c).attr("style")??"")});for(let i of h.join(`
|
|
2
|
+
`).matchAll(/(?:background(?:-image)?\s*:\s*)?url\(\s*(?:["']|"|�?34;|'|�?39;)?([^"')&]+)(?:["']|"|�?34;|'|�?39;)?\s*\)/gi))u(i[1],`static-css-url${s}`,"image");for(let i of l.sourceHtml.matchAll(/["'](https?:\/\/[^"'\s<>]+\.(?:avif|gif|jpe?g|png|svg|webp|mp4|webm|mp3|ogg)(?:\?[^"'\s<>]*)?)["']/gi))u(i[1],`static-bare-url${s}`)},o=typeof r=="string"?D(r):r;return m(o),o.$("script").each((l,s)=>{let a=o.$(s).text(),h=a.replace(/\\u003[cC]/g,"<").replace(/\\u003[eE]/g,">").replace(/\\u0026/gi,"&").replace(/\\["']/g,i=>i.slice(1)).replace(/\\\//g,"/");h!==a&&/<(?:img|source|video|audio)\b/i.test(h)&&m(D(h),"-json-unescaped")}),[...t.values()]}function re(r,e,t=100){let u=new Map;for(let m of r)S(u,m);for(let m of e?.assets??[])S(u,{...m,type:m.type});let f=new Map;for(let m of u.values()){let o=ee(m.url),l=f.get(o)??[];l.push(m),f.set(o,l)}return[...f.values()].map(m=>{let o=[...m].sort((s,a)=>T(a)-T(s)||s.url.localeCompare(a.url)),l={...o[0]};return l.discoveryMethods=[...new Set(m.flatMap(s=>s.discoveryMethods))],l.altTexts=[...new Set(m.flatMap(s=>s.altTexts))],l.contexts=[...new Set(m.flatMap(s=>s.contexts))],{...l,variants:o.slice(1).map(s=>s.url)}}).sort((m,o)=>T(o)-T(m)||m.url.localeCompare(o.url)).slice(0,Math.max(1,t))}async function ne(r,e,t,u={}){let f=Math.max(1,Math.min(u.maxBytes??52428800,104857600)),m=r,o=null;for(let g=0;g<=5;g++){let y=await R(m,{field:"media URL"});if(y.error||!y.parsed)throw new Error(y.error??"Media URL was rejected");if(o=await fetch(y.parsed.href,{signal:AbortSignal.timeout(15e3),redirect:"manual"}),o.status>=300&&o.status<400){let v=o.headers.get("location");if(!v)throw new Error(`HTTP ${o.status} redirect did not include Location`);m=new URL(v,y.parsed.href).href,o=null;continue}break}if(!o)throw new Error("Media download exceeded five redirects");if(!o.ok)throw new Error(`HTTP ${o.status}`);if(!o.body)throw new Error("Empty response body");let l=o.headers.get("content-type")?.split(";")[0].trim()??null;if(u.expectedType&&(!l||L(l)!==u.expectedType))throw new Error(`Expected ${u.expectedType} content but received ${l??"no content-type"}`);let s=Number(o.headers.get("content-length")??0);if(Number.isFinite(s)&&s>f)throw new Error(`Media asset exceeds ${f} byte limit`);let a=A(e,t);if(l&&!B(t)){let y={"image/jpeg":".jpg","image/png":".png","image/webp":".webp","image/gif":".gif","image/svg+xml":".svg","image/avif":".avif","video/mp4":".mp4","video/webm":".webm","audio/mpeg":".mp3","audio/ogg":".ogg","audio/wav":".wav"}[l];y&&(a=a+y)}let h=H(a);await new Promise((g,y)=>{h.once("open",()=>g()),h.once("error",y)});let i=0,c=new q({transform(g,y,v){let b=g.length;if(i+b>f){v(new Error(`Media asset exceeds ${f} byte limit`));return}if(u.consumeBytes&&!u.consumeBytes(b)){v(new Error("Media export total byte limit exceeded"));return}i+=b,v(null,g)}});try{await O(I.fromWeb(o.body),c,h)}catch(g){throw h.destroy(),N(a,{force:!0}),g}let{statSync:n}=await import("fs"),d=n(a).size;return{savedPath:a,sizeBytes:d,mimeType:l}}async function ue(r,e,t={}){let u=t.types??["image","video","audio"],f=new Set(u),m=te(r,e),o=Math.max(1,Math.min(t.maxAssets??100,250)),l=re(m,t.rendered,2e3),s=l.slice(0,o),a=new Set([...m.map(p=>p.url),...(t.rendered?.assets??[]).map(p=>p.url)]).size,h=s.flatMap(p=>{let w=p.type??U(p.url);return w&&f.has(w)?[{...p,type:w}]:[]}),i=a-h.length,c=l.length>o,n=t.rendered?.exhausted===!0&&!c,d=c?"asset_limit":t.rendered?.stopReason??"render_unavailable",g=c?[`Media discovery found ${l.length} responsive families; only the requested ${o} records were retained.`]:t.rendered?t.rendered.exhausted?[]:[`Rendered media discovery stopped with ${t.rendered.stopReason}; the inventory may be incomplete.`]:["Rendered media discovery was unavailable; this manifest contains static-source evidence only."],y={pageUrl:e,staticFound:m.length,renderedFound:t.rendered?.assets.length??0,filteredCount:i,totalFound:a,completeness:n?"complete":"partial",exhausted:n,stopReason:d,scrollRounds:t.rendered?.scrollRounds??0,warnings:g,artifact:null};if(t.outputDir===null)return{...y,outputDir:null,assets:h.map((p,w)=>({...p,mimeType:null,filename:C(p.url,w),savedPath:null,sizeBytes:null,sha256:null,downloadStatus:"not_attempted",downloadError:null,inlinePreview:null}))};let v=(()=>{try{return new URL(e).hostname.replace(/^www\./,"")}catch{return"unknown"}})(),b=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19),$=t.outputDir??A(P(),"media",`${b}-${v}`);W($,{recursive:!0});let M=[];return await Promise.allSettled(h.map(async(p,w)=>{let E=C(p.url,w);try{let{savedPath:x,sizeBytes:j,mimeType:_}=await ne(p.url,$,E),F=_?L(_)??p.type:p.type;M.push({...p,type:F,mimeType:_,filename:z(x),savedPath:x,sizeBytes:j,sha256:null,downloadStatus:"downloaded",downloadError:null,inlinePreview:null})}catch(x){M.push({...p,mimeType:null,filename:E,savedPath:null,sizeBytes:null,sha256:null,downloadStatus:"failed",downloadError:x instanceof Error?x.message:"media_download_failed",inlinePreview:null})}})),M.sort((p,w)=>p.savedPath&&!w.savedPath?-1:!p.savedPath&&w.savedPath?1:p.url.localeCompare(w.url)),{...y,outputDir:$,assets:M}}export{L as a,te as b,ne as c,ue as d};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{e as i,f as I,h as m,j as o}from"./chunk-SEZETSB3.js";import{i as A}from"./chunk-SDQW7IJN.js";var s="site-extracts/",y=10080*60*1e3,b=900*1e3;function x(){return process.env.SITE_EXTRACT_ARTIFACT_READ_WRITE_TOKEN?.trim()||process.env.PRIVATE_ARTIFACT_READ_WRITE_TOKEN?.trim()||process.env.CONNECTED_DATA_READ_WRITE_TOKEN?.trim()||process.env.CONNECTED_DATA_BLOB_READ_WRITE_TOKEN?.trim()||null}function _(){return process.env.VERCEL==="1"||process.env.NODE_ENV==="production"}function r(){return{prefix:s,artifactTtlMs:y,downloadTtlMs:b,token:x()}}function l(t){return A(t,s)}async function v(t){let e=await i({policy:r(),ownerId:t.ownerId,artifactKey:`${t.jobId}.zip`,createdAt:t.createdAt,filename:`${t.jobId}-site-export.zip`,contentType:"application/zip",content:t.content});return{key:e.artifactId,url:e.downloadUrl??"",bytes:e.bytes,contentType:e.contentType,filename:e.filename,sha256:e.sha256,expiresAt:e.expiresAt,downloadUrlExpiresAt:e.downloadUrlExpiresAt,kind:"bundle"}}async function R(t){let e=await I({policy:r(),ownerId:t.ownerId,artifactKey:`${t.jobId}.zip`,createdAt:t.createdAt,filename:`${t.jobId}-site-export.zip`,contentType:"application/zip",content:t.content});return{key:e.artifactId,url:e.downloadUrl??"",bytes:e.bytes,contentType:e.contentType,filename:e.filename,sha256:e.sha256,expiresAt:e.expiresAt,downloadUrlExpiresAt:e.downloadUrlExpiresAt,kind:"bundle"}}async function D(t){let e=await i({policy:r(),ownerId:t.ownerId,scopeSegments:[t.jobId,"images"],artifactKey:`${t.imageId}.bin`,createdAt:new Date,filename:t.filename,contentType:t.contentType,content:t.content});return{key:e.artifactId,url:e.downloadUrl??"",bytes:e.bytes,contentType:e.contentType,filename:e.filename,sha256:e.sha256,expiresAt:e.expiresAt,downloadUrlExpiresAt:e.downloadUrlExpiresAt,kind:"image",imageId:t.imageId,sourceUrl:t.sourceUrl,sourcePage:t.sourcePage}}async function S(t){return i({policy:r(),ownerId:t.ownerId,scopeSegments:[t.jobId,"content"],artifactKey:`${t.chunkKey}.json.gz`,createdAt:t.createdAt,filename:`${t.chunkKey}.json.gz`,contentType:"application/gzip",content:t.content})}async function C(t){return m({policy:r(),artifactId:t.artifactId,ownerId:t.ownerId})}async function P(t){return o({policy:r(),artifactId:t,maxBytes:50*1024*1024})}async function B(t){return l(t.artifactId)!==t.ownerId?null:P(t.artifactId)}async function k(t){return l(t.artifactId)!==t.ownerId?null:o({policy:r(),artifactId:t.artifactId,maxBytes:25*1024*1024})}async function h(t){return l(t.artifactId)!==t.ownerId?null:o({policy:r(),artifactId:t.artifactId,maxBytes:10*1024*1024})}async function U(t={}){let e=t.now??new Date,a=x();if(!a)return{deleted:0,store:_()?"none":"local"};if(!t.force&&!(e.getUTCHours()===3&&e.getUTCMinutes()===21))return{deleted:0,store:"private-vercel-blob",skipped:!0};let E=e.getTime()-y,{list:T,del:w}=await import("@vercel/blob"),f,p=0;for(let u=0;u<20;u+=1){let n=await T({prefix:s,token:a,limit:1e3,cursor:f}),c=n.blobs.filter(d=>new Date(d.uploadedAt).getTime()<=E);if(c.length>0&&(await w(c.map(d=>d.pathname),{token:a}),p+=c.length),!n.hasMore||!n.cursor)break;f=n.cursor}return{deleted:p,store:"private-vercel-blob"}}export{s as a,y as b,b as c,r as d,l as e,v as f,R as g,D as h,S as i,C as j,P as k,B as l,k as m,h as n,U as o};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{c as _}from"./chunk-63VWBMYR.js";import{a as p,f as w,i as x}from"./chunk-3F7AFM53.js";import{a as d,e as E,k as g}from"./chunk-RDHGQ2WZ.js";import{a as T,b as R,e as c,i as u}from"./chunk-SEZETSB3.js";import{i as l,k as m}from"./chunk-SDQW7IJN.js";import{createHash as h,randomBytes as C}from"crypto";var D=1440*60*1e3,y="mcp-reports/",F="extract-url-job-results/",O=2e3;function P(){return process.env.PRIVATE_ARTIFACT_READ_WRITE_TOKEN?.trim()||process.env.CONNECTED_DATA_READ_WRITE_TOKEN?.trim()||process.env.CONNECTED_DATA_BLOB_READ_WRITE_TOKEN?.trim()||process.env.BLOB_READ_WRITE_TOKEN?.trim()||void 0}var A={prefix:y,artifactTtlMs:D,downloadTtlMs:900*1e3,token:P()},b={...A,hosted:!1,token:void 0},a="mcp-results/";function v(t){return{prefix:a,artifactTtlMs:T,downloadTtlMs:R,hosted:!t,token:t?void 0:P()}}var M=process.env.MCP_SCRAPER_ARTIFACT_OFFLOAD!=="false";async function j(t,e,r){let i=Date.now(),n=C(6).toString("hex"),o=await c({policy:A,ownerId:e,artifactKey:`${n}.md`,createdAt:i,filename:`${t}-${i}.md`,contentType:"text/markdown",content:r,scopeSegments:[t]});return{artifactId:o.artifactId,mimeType:o.contentType,sizeBytes:o.bytes,bytes:o.bytes,sha256:o.sha256,expiresAt:o.expiresAt,downloadUrl:o.downloadUrl,downloadUrlExpiresAt:o.downloadUrlExpiresAt,preview:r.slice(0,O)}}async function k(t,e,r){let i=JSON.stringify(r),n=await c({policy:b,ownerId:t,artifactKey:`${e}.json`,createdAt:Date.now(),filename:`${e}.json`,contentType:"application/json",content:i,scopeSegments:["extract_url"]});return{artifactId:n.artifactId,mimeType:n.contentType,sizeBytes:n.bytes,bytes:n.bytes,sha256:n.sha256,expiresAt:n.expiresAt,downloadUrl:n.downloadUrl,downloadUrlExpiresAt:n.downloadUrlExpiresAt,preview:i.slice(0,O)}}function J(t){if(t.startsWith(a))return l(t,a);let e=f(t)?.ownerId;return e||(t.startsWith(p)?w(t):t.startsWith(d)?E(t):l(t,y))}function f(t){let e=t.match(/^extract-url-job-results\/([a-f0-9]{24})\/([1-9][0-9]*)\/(exurl-[a-f0-9]{24})\.json$/);if(!e)return null;let r=Number(e[2]);return Number.isSafeInteger(r)?{ownerId:e[1],userId:r,jobId:e[3]}:null}async function I(t,e){let r=await _(t,e);if(!r||r.status!=="done"||r.options.jobKind!=="extract_url"||!r.result)return null;let i=Date.parse(`${r.created_at.replace(" ","T")}Z`);if(!Number.isFinite(i))return null;let n=new Date(i+1080*60*60*1e3);return n.getTime()<=Date.now()?null:{content:Buffer.from(JSON.stringify(r.result)),expiresAt:n.toISOString()}}async function K(t,e,r){let i=`${F}${e}/${r}/${t}.json`;if(!f(i))return null;let n=await I(t,r);return n?{artifactId:i,mimeType:"application/json",sizeBytes:n.content.length,bytes:n.content.length,sha256:h("sha256").update(n.content).digest("hex"),expiresAt:n.expiresAt,downloadUrl:null,downloadUrlExpiresAt:null,preview:n.content.toString("utf8",0,Math.min(2e3,n.content.length))}:null}async function z(t,e,r,i,n=!1){let o=f(t);if(o){if(o.userId!==i||!Number.isSafeInteger(e)||e<0||!Number.isSafeInteger(r)||r<=0)return null;let s=await I(o.jobId,o.userId);return s?{...m(s.content.subarray(e,e+r+1),e,r,s.content.length),totalBytes:s.content.length}:null}return t.startsWith(a)?u({policy:v(n),artifactId:t,offset:e,maxBytes:r}):t.startsWith(p)?x(t,e,r):t.startsWith(d)?g(t,e,r):u({policy:n?b:A,artifactId:t,offset:e,maxBytes:r})}function X(t){return t.downloadUrl?{type:"resource_link",name:"Full MCP Scraper report",title:"Download full report",uri:t.downloadUrl,mimeType:t.mimeType,size:t.sizeBytes}:null}function Y(t,e){return[t.trim(),"","--- Full report stored as artifact ---",`artifactId: ${e.artifactId} \xB7 ${e.bytes} bytes \xB7 expires ${e.expiresAt}`,"Read it with report_artifact_read (supports offset/maxBytes windowing)."].join(`
|
|
2
|
+
`)}export{D as a,y as b,F as c,O as d,A as e,a as f,v as g,M as h,j as i,k as j,J as k,K as l,z as m,X as n,Y as o};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHmac as s,timingSafeEqual as u}from"crypto";var a=()=>process.env.NODE_ENV==="production"||process.env.VERCEL==="1";function f(){let e=process.env.SESSION_SECRET?.trim();if(e)return e;if(a())throw new Error("SESSION_SECRET is not set \u2014 add it to your Vercel env vars (see src/api/env.ts)");return"dev-secret-change-me"}var i=()=>f();function l(e,t){if(e.length!==t.length)return!1;try{return u(Buffer.from(e,"hex"),Buffer.from(t,"hex"))}catch{return!1}}function p(e){let t=String(e),n=s("sha256",i()).update(t).digest("hex");return`${t}.${n}`}function S(e){let t=e.lastIndexOf(".");if(t===-1)return null;let n=e.slice(0,t),o=e.slice(t+1),c=s("sha256",i()).update(n).digest("hex");if(!l(o,c))return null;let r=parseInt(n);return isNaN(r)?null:r}export{f as a,p as b,S as c};
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import{a as g}from"./chunk-NALXKTUU.js";import{Pool as B}from"pg";var u=class extends Error{constructor(t,s,n=400){super(s);this.code=t;this.status=n;this.name="AnalyticsRepositoryError"}code;status};var D=null;function K(){let i=process.env.ANALYTICS_DATABASE_URL?.trim();if(!i)throw new u("analytics_database_unavailable","Site Analytics is not configured.",503);return i}function E(){return D||(D=new B({connectionString:K(),max:8})),D}import{randomBytes as _e,randomUUID as p}from"crypto";import{createHash as Z,randomUUID as v}from"crypto";import{createCipheriv as X,createDecipheriv as J,createHmac as x,randomBytes as Y,scryptSync as z,timingSafeEqual as V}from"crypto";var N="xray1";function G(){return process.env.ANALYTICS_RESTRICTED_DATA_SECRET?.trim()||g()}function T(i){return z(G(),`xray-restricted:${i}:v1`,32)}function k(i,e){let t=e.trim().slice(0,80);if(!t)throw new Error("analytics encryption purpose required");let s=Y(12),n=X("aes-256-gcm",T(t),s);n.setAAD(Buffer.from(`${N}:${t}`,"utf8"));let r=Buffer.concat([n.update(i,"utf8"),n.final()]),o=n.getAuthTag();return[N,Buffer.from(t,"utf8").toString("base64url"),s.toString("base64url"),o.toString("base64url"),r.toString("base64url")].join(".")}function U(i,e){try{let[t,s,n,r,o]=i.split(".");if(t!==N||!s||!n||!r||!o)return null;let a=Buffer.from(s,"base64url").toString("utf8");if(a!==e)return null;let d=J("aes-256-gcm",T(a),Buffer.from(n,"base64url"));return d.setAAD(Buffer.from(`${N}:${a}`,"utf8")),d.setAuthTag(Buffer.from(r,"base64url")),Buffer.concat([d.update(Buffer.from(o,"base64url")),d.final()]).toString("utf8")}catch{return null}}function Ne(i,e){return x("sha256",T(`identity:${i}`)).update(e.trim().toLowerCase()).digest("hex")}function ve(i,e,t,s){return x("sha256",s).update(`${i}.${e}.${t}`).digest("hex")}function Ae(i,e){if(!i||i.length!==e.length)return!1;try{return V(Buffer.from(i),Buffer.from(e))}catch{return!1}}var M="enhanced_matching",Q=90,ee=10,f=class extends Error{constructor(t,s,n){super(s);this.code=t;this.status=n;this.name="AnalyticsIdentityProfileError"}code;status};function A(i,e){return i?.trim().replace(/\s+/g," ").slice(0,e)||null}function ie(i){if(!i)return null;let[e,t]=i.split("@");return!e||!t?null:`${e.slice(0,1)}${"*".repeat(Math.min(6,Math.max(2,e.length-1)))}@${t}`}function te(i){if(!i)return null;let e=i.replace(/\D/g,"");return e.length<4?null:`***-***-${e.slice(-4)}`}function O(i){return Z("sha256").update(i).digest("hex")}async function h(i,e){if(!e.signals.length)return;let t=e.signals.map(r=>r.kind),s=e.signals.map(r=>r.valueHmac);if((await i.query(`SELECT 1 FROM analytics_identity_tombstones
|
|
2
|
+
WHERE site_id=$1 AND (kind,value_hmac) IN (
|
|
3
|
+
SELECT * FROM unnest($2::text[],$3::text[])
|
|
4
|
+
) LIMIT 1`,[e.siteId,t,s])).rowCount)throw new f("analytics_identity_deleted","This identity cannot be automatically recreated.",410)}async function $(i,e){let t=A(e.displayName,160),s=A(e.email,320)?.toLowerCase()??null,n=A(e.phone,40);if(!t&&!s&&!n)return!1;let o=(await i.query(`SELECT p.public_ref FROM analytics_people p
|
|
5
|
+
JOIN analytics_identity_namespaces n ON n.id=p.namespace_id AND n.site_id=p.site_id AND n.status='approved'
|
|
6
|
+
WHERE p.id=$1 AND p.site_id=$2`,[e.personId,e.siteId])).rows[0]?.public_ref;if(!o)return!1;let a=[...new Set([o,...e.consentSubjectRefs].filter(Boolean))],l=(await i.query(`SELECT s.latest_receipt_id::text FROM analytics_consent_current_state s
|
|
7
|
+
JOIN analytics_consent_receipts r ON r.id=s.latest_receipt_id AND r.site_id=s.site_id
|
|
8
|
+
WHERE s.site_id=$1 AND s.subject_ref=ANY($2::text[])
|
|
9
|
+
AND s.site_analytics=true AND s.enhanced_matching=true AND s.gpc=false
|
|
10
|
+
AND r.action NOT IN ('withdraw','gpc')
|
|
11
|
+
AND (s.expires_at IS NULL OR s.expires_at > now())
|
|
12
|
+
ORDER BY s.effective_at DESC LIMIT 1`,[e.siteId,a])).rows[0]?.latest_receipt_id;if(!l)return!1;let m=e.verifiedAt&&Number.isFinite(Date.parse(e.verifiedAt))?new Date(e.verifiedAt).toISOString():new Date().toISOString(),_=k(JSON.stringify({schemaVersion:1,displayName:t,email:s,phone:n,evidenceType:e.evidenceType,verifiedAt:m}),"identity-profile");return await i.query(`INSERT INTO analytics_restricted_data(id,site_id,person_id,purpose,subject_ref,payload_ciphertext,expires_at,consent_receipt_id)
|
|
13
|
+
VALUES($1,$2,$3,$4,$5,$6,now()+($7::int * interval '1 day'),$8)
|
|
14
|
+
ON CONFLICT(site_id,purpose,subject_ref) DO UPDATE SET
|
|
15
|
+
person_id=EXCLUDED.person_id,payload_ciphertext=EXCLUDED.payload_ciphertext,
|
|
16
|
+
expires_at=EXCLUDED.expires_at,consent_receipt_id=EXCLUDED.consent_receipt_id,created_at=now()`,[v(),e.siteId,e.personId,M,o,_,Q,l]),!0}async function Re(i,e){let t=e.now??new Date,s=await i.query("SELECT role FROM analytics_site_grants WHERE site_id=$1 AND user_id=$2",[e.siteId,e.actorUserId]);if(!s.rowCount)throw new f("analytics_site_not_found","Site not found.",404);if(!["owner","editor"].includes(s.rows[0].role))throw new f("analytics_forbidden","Editor access is required.",403);let n=await i.query(`SELECT count(*)::text AS count FROM analytics_identity_profile_access_audits
|
|
17
|
+
WHERE site_id=$1 AND actor_user_id=$2 AND created_at > $3::timestamptz - interval '1 minute'`,[e.siteId,e.actorUserId,t.toISOString()]);if(Number(n.rows[0]?.count??0)>=ee)throw await i.query(`INSERT INTO analytics_identity_profile_access_audits(id,site_id,actor_user_id,subject_ref_hmac,purpose,result,created_at)
|
|
18
|
+
VALUES($1,$2,$3,$4,'journey_identity_display','rate_limited',$5)`,[v(),e.siteId,e.actorUserId,O(e.requestedPersonRef),t.toISOString()]),new f("analytics_identity_profile_rate_limited","Identity profile rate limit exceeded.",429);let r=async _=>{throw await i.query(`INSERT INTO analytics_identity_profile_access_audits(id,site_id,actor_user_id,subject_ref_hmac,purpose,result,created_at)
|
|
19
|
+
VALUES($1,$2,$3,$4,'journey_identity_display',$5,$6)`,[v(),e.siteId,e.actorUserId,O(e.requestedPersonRef),_,t.toISOString()]),new f("analytics_identity_profile_not_found","Identity profile not found.",404)};if(!e.personId)return r("not_found");let o=await i.query(`SELECT p.public_ref,r.payload_ciphertext,r.expires_at
|
|
20
|
+
FROM analytics_people p
|
|
21
|
+
JOIN analytics_restricted_data r ON r.person_id=p.id AND r.site_id=p.site_id
|
|
22
|
+
AND r.purpose=$3 AND r.subject_ref=p.public_ref AND r.expires_at>$4
|
|
23
|
+
JOIN analytics_consent_current_state s ON s.site_id=p.site_id AND s.latest_receipt_id=r.consent_receipt_id
|
|
24
|
+
AND s.site_analytics=true AND s.enhanced_matching=true AND s.gpc=false
|
|
25
|
+
AND (s.expires_at IS NULL OR s.expires_at>$4)
|
|
26
|
+
JOIN analytics_consent_receipts c ON c.id=s.latest_receipt_id AND c.site_id=s.site_id
|
|
27
|
+
AND c.action NOT IN ('withdraw','gpc')
|
|
28
|
+
WHERE p.id=$1 AND p.site_id=$2
|
|
29
|
+
AND NOT EXISTS (SELECT 1 FROM analytics_identity_tombstones t WHERE t.site_id=p.site_id AND t.person_id=p.id)
|
|
30
|
+
LIMIT 1`,[e.personId,e.siteId,M,t.toISOString()]);if(!o.rowCount)return r("not_found");let a=U(o.rows[0].payload_ciphertext,"identity-profile");if(!a)return r("unavailable");let d;try{d=JSON.parse(a)}catch{return r("unavailable")}let l=d.evidenceType;if(!["first_party_form","authenticated_user","crm_contact"].includes(String(l)))return r("unavailable");let m={personRef:e.requestedPersonRef,displayName:A(typeof d.displayName=="string"?d.displayName:void 0,160),maskedEmail:ie(typeof d.email=="string"?d.email:null),maskedPhone:te(typeof d.phone=="string"?d.phone:null),evidenceType:l,verifiedAt:typeof d.verifiedAt=="string"&&Number.isFinite(Date.parse(d.verifiedAt))?new Date(d.verifiedAt).toISOString():t.toISOString()};return await i.query(`INSERT INTO analytics_identity_profile_access_audits(id,site_id,person_id,actor_user_id,subject_ref_hmac,purpose,result,created_at)
|
|
31
|
+
VALUES($1,$2,$3,$4,$5,'journey_identity_display','returned_masked',$6)`,[v(),e.siteId,e.personId,e.actorUserId,O(e.requestedPersonRef),t.toISOString()]),m}import{randomUUID as re}from"crypto";import{createHmac as se}from"crypto";var ne=new Set(["fbclid","gclid","gbraid","wbraid","ttclid","rdt_cid","msclkid","fbp","fbc","li_fat_id","snapclid"]);function w(i){let e={};for(let[t,s]of Object.entries(i??{})){if(!ne.has(t)||typeof s!="string")continue;let n=s.trim();n&&(e[t]=n.slice(0,500))}return e}function c(i,e){return se("sha256",g()).update(`${i}:${e.trim().toLowerCase()}`).digest("hex")}function oe(i){return i.consent?.analytics==="granted"&&(i.consent.purposes?.enhanced_matching==="granted"||i.consent.adUserData==="granted")&&i.consent.globalPrivacyControl!==!0}function be(i){return!!(i.identity&&oe(i))}async function Pe(i,e){let t=e.event.visitorId?.trim().slice(0,240),s=e.event.consent;if(!t||!s)return null;let n=Date.parse(e.event.occurredAt),r=Date.now();if(!Number.isFinite(n)||n>r+5*6e4||n<r-1440*6e4)return null;let o=s.purposes,a={siteAnalytics:o?o.site_analytics==="granted":s.analytics==="granted",enhancedMatching:o?o.enhanced_matching==="granted":s.adUserData==="granted",advertisingMeasurement:o?o.advertising_measurement==="granted":s.adPersonalization==="granted"},d=s.globalPrivacyControl===!0;d&&(a.siteAnalytics=!1,a.enhancedMatching=!1,a.advertisingMeasurement=!1);let l=d?"gpc":Object.values(a).every(Boolean)?"accept_all":Object.values(a).every(R=>!R)?"reject_all":"save_choices",m=new Date(n).toISOString(),y=(await i.query(`SELECT s.latest_receipt_id::text,s.site_analytics,s.enhanced_matching,s.advertising_measurement,s.gpc,r.action
|
|
32
|
+
FROM analytics_consent_current_state s JOIN analytics_consent_receipts r ON r.id=s.latest_receipt_id
|
|
33
|
+
WHERE s.site_id=$1 AND s.subject_ref=$2 FOR UPDATE`,[e.siteId,t])).rows[0];if(y&&y.site_analytics===a.siteAnalytics&&y.enhanced_matching===a.enhancedMatching&&y.advertising_measurement===a.advertisingMeasurement&&y.gpc===d&&y.action===l)return y.latest_receipt_id;let I=re();return await i.query(`INSERT INTO analytics_consent_receipts(id,site_id,pixel_id,subject_ref,policy_version,jurisdiction_profile,
|
|
34
|
+
necessary,site_analytics,enhanced_matching,advertising_measurement,gpc,action,source,presented_at,occurred_at,supersedes_id)
|
|
35
|
+
VALUES($1,$2,$3,$4,1,'pixel',true,$5,$6,$7,$8,$9,'gateway',$10,$10,$11)`,[I,e.siteId,e.pixelId,t,a.siteAnalytics,a.enhancedMatching,a.advertisingMeasurement,d,l,m,y?.latest_receipt_id??null]),await i.query(`INSERT INTO analytics_consent_current_state(site_id,subject_ref,latest_receipt_id,policy_version,jurisdiction_profile,
|
|
36
|
+
necessary,site_analytics,enhanced_matching,advertising_measurement,gpc,effective_at)
|
|
37
|
+
VALUES($1,$2,$3,1,'pixel',true,$4,$5,$6,$7,$8)
|
|
38
|
+
ON CONFLICT(site_id,subject_ref) DO UPDATE SET latest_receipt_id=EXCLUDED.latest_receipt_id,
|
|
39
|
+
policy_version=EXCLUDED.policy_version,jurisdiction_profile=EXCLUDED.jurisdiction_profile,
|
|
40
|
+
site_analytics=EXCLUDED.site_analytics,enhanced_matching=EXCLUDED.enhanced_matching,
|
|
41
|
+
advertising_measurement=EXCLUDED.advertising_measurement,gpc=EXCLUDED.gpc,
|
|
42
|
+
effective_at=EXCLUDED.effective_at,updated_at=now()
|
|
43
|
+
WHERE EXCLUDED.effective_at>=analytics_consent_current_state.effective_at`,[e.siteId,t,I,a.siteAnalytics,a.enhancedMatching,a.advertisingMeasurement,d,m]),I}async function xe(i,e){if(!e.subjectRef)return{personId:null,activationEligible:!1};let s=(await i.query(`SELECT p.id::text AS person_id,s.advertising_measurement
|
|
44
|
+
FROM analytics_people p
|
|
45
|
+
JOIN analytics_consent_current_state s ON s.site_id=p.site_id AND s.subject_ref=$2
|
|
46
|
+
JOIN analytics_consent_receipts r ON r.id=s.latest_receipt_id AND r.site_id=s.site_id
|
|
47
|
+
WHERE p.site_id=$1
|
|
48
|
+
AND (($3::uuid IS NOT NULL AND p.id=$3) OR ($4::text IS NOT NULL AND EXISTS (
|
|
49
|
+
SELECT 1 FROM analytics_events e WHERE e.site_id=p.site_id AND e.event_id=$4
|
|
50
|
+
AND e.person_id=p.id AND e.visitor_id=$2
|
|
51
|
+
)))
|
|
52
|
+
AND s.site_analytics=true AND s.gpc=false AND r.action NOT IN ('withdraw','gpc')
|
|
53
|
+
AND (s.expires_at IS NULL OR s.expires_at>now()) LIMIT 1`,[e.siteId,e.subjectRef,e.personId??null,e.eventId??null])).rows[0];return s?{personId:s.person_id,activationEligible:s.advertising_measurement===!0}:{personId:null,activationEligible:!1}}async function C(i,e,t,s){return!t?.trim()||!s?.trim()?!1:!!(await i.query(`SELECT 1 FROM analytics_consent_current_state s
|
|
54
|
+
JOIN analytics_consent_receipts r ON r.id=s.latest_receipt_id AND r.site_id=s.site_id
|
|
55
|
+
WHERE s.site_id=$1 AND s.subject_ref=$2
|
|
56
|
+
AND s.site_analytics=true AND s.enhanced_matching=true AND s.gpc=false
|
|
57
|
+
AND r.action NOT IN ('withdraw','gpc')
|
|
58
|
+
AND (s.expires_at IS NULL OR s.expires_at>now())
|
|
59
|
+
AND EXISTS (SELECT 1 FROM analytics_sessions x
|
|
60
|
+
WHERE x.site_id=s.site_id AND x.visitor_hmac=$3 AND x.session_hmac=$4)
|
|
61
|
+
ORDER BY s.effective_at DESC LIMIT 1`,[e,t,c("visitor_id",t),c("session_id",s)])).rowCount}async function H(i,e,t){return!!(await i.query(`SELECT 1 FROM analytics_consent_current_state s
|
|
62
|
+
JOIN analytics_consent_receipts r ON r.id=s.latest_receipt_id AND r.site_id=s.site_id
|
|
63
|
+
WHERE s.site_id=$1 AND s.subject_ref=$2
|
|
64
|
+
AND s.site_analytics=true AND s.enhanced_matching=true AND s.gpc=false
|
|
65
|
+
AND r.action NOT IN ('withdraw','gpc')
|
|
66
|
+
AND (s.expires_at IS NULL OR s.expires_at>now()) LIMIT 1`,[e,t])).rowCount}import{createHash as ae}from"crypto";function He(i,e){let t=i?.trim().toUpperCase();return t&&/^[A-Z0-9-]+$/.test(t)?t.slice(0,e):null}function q(i,e,t){return ae("sha256").update(JSON.stringify({scope:i,siteId:e,filters:t})).digest("base64url").slice(0,18)}function de(i,e){if(!i)return 0;try{let t=JSON.parse(Buffer.from(i,"base64url").toString("utf8"));if(t.v!==1||t.f!==e||!Number.isInteger(t.o)||Number(t.o)<0)throw new Error("invalid");return Number(t.o)}catch{throw new u("invalid_cursor","The pagination cursor is invalid for these filters.")}}function F(i,e,t){if(!e)return{items:i,pageInfo:{hasNextPage:!1,nextCursor:null}};let s=Math.min(100,Math.max(1,Math.trunc(e?.limit??25))),n=de(e?.cursor,t),r=i.slice(n,n+s),o=n+r.length<i.length;return{items:r,pageInfo:{hasNextPage:o,nextCursor:o?Buffer.from(JSON.stringify({v:1,f:t,o:n+r.length})).toString("base64url"):null}}}async function S(i,e,t){let n=(await i.query("SELECT role FROM analytics_site_grants WHERE site_id = $1 AND user_id = $2",[e,t])).rows[0]?.role;if(!n)throw new u("analytics_site_not_found","Site not found.",404);return n}async function ce(i,e,t){let s=await S(i,e,t);if(s==="viewer")throw new u("analytics_forbidden","Editor access is required.",403);return s}async function We(i,e,t){return S(i,e,t)}async function je(i,e,t){return ce(i,e,t)}async function Qe(i){for(;;){let t=await i.query(`SELECT id, visitor_id, session_id
|
|
67
|
+
FROM analytics_events
|
|
68
|
+
WHERE (visitor_id IS NOT NULL AND visitor_hmac IS NULL)
|
|
69
|
+
OR (session_id IS NOT NULL AND session_hmac IS NULL)
|
|
70
|
+
ORDER BY id
|
|
71
|
+
LIMIT $1`,[500]);if(!t.rowCount)break;await i.query(`UPDATE analytics_events e
|
|
72
|
+
SET visitor_hmac = COALESCE(e.visitor_hmac, patch.visitor_hmac),
|
|
73
|
+
session_hmac = COALESCE(e.session_hmac, patch.session_hmac)
|
|
74
|
+
FROM unnest($1::uuid[], $2::text[], $3::text[]) AS patch(id, visitor_hmac, session_hmac)
|
|
75
|
+
WHERE e.id = patch.id`,[t.rows.map(s=>s.id),t.rows.map(s=>s.visitor_id?c("visitor_id",s.visitor_id):null),t.rows.map(s=>s.session_id?c("session_id",s.session_id):null)])}for(;;){let t=await i.query(`SELECT id, session_id
|
|
76
|
+
FROM analytics_conversions
|
|
77
|
+
WHERE session_id IS NOT NULL AND session_hmac IS NULL
|
|
78
|
+
ORDER BY id
|
|
79
|
+
LIMIT $1`,[500]);if(!t.rowCount)break;await i.query(`UPDATE analytics_conversions c
|
|
80
|
+
SET session_hmac = patch.session_hmac
|
|
81
|
+
FROM unnest($1::uuid[], $2::text[]) AS patch(id, session_hmac)
|
|
82
|
+
WHERE c.id = patch.id AND c.session_hmac IS NULL`,[t.rows.map(s=>s.id),t.rows.map(s=>c("session_id",s.session_id))])}}function W(){return`xrp_${_e(18).toString("base64url")}`}function ei(i,e){let t=c("crm_entity_id",`${i}:${e}`).slice(0,32).split("");return t[12]="5",t[16]=["8","9","a","b"][Number.parseInt(t[16],16)%4],`${t.slice(0,8).join("")}-${t.slice(8,12).join("")}-${t.slice(12,16).join("")}-${t.slice(16,20).join("")}-${t.slice(20).join("")}`}async function j(i,e,t){let s=await i.query("SELECT id FROM analytics_identity_namespaces WHERE site_id=$1 AND status='approved' ORDER BY version DESC LIMIT 1",[e]);s.rows[0]||(await i.query(`INSERT INTO analytics_identity_namespaces(
|
|
83
|
+
id,site_id,name,status,approved_hosts,approved_sources,version,approved_by_user_id,approved_at
|
|
84
|
+
)
|
|
85
|
+
SELECT $1,s.id,'X-Ray primary','approved',
|
|
86
|
+
COALESCE((SELECT jsonb_agg(d.hostname ORDER BY d.hostname)
|
|
87
|
+
FROM analytics_pixels p
|
|
88
|
+
JOIN analytics_pixel_domains d ON d.pixel_id=p.id AND d.state='approved'
|
|
89
|
+
WHERE p.site_id=s.id),'[]'::jsonb),
|
|
90
|
+
$3::jsonb,1,s.owner_user_id,now()
|
|
91
|
+
FROM analytics_sites s WHERE s.id=$2
|
|
92
|
+
ON CONFLICT(site_id,name,version) DO NOTHING`,[p(),e,JSON.stringify([t])]),s=await i.query("SELECT id FROM analytics_identity_namespaces WHERE site_id=$1 AND status='approved' ORDER BY version DESC LIMIT 1",[e]));let n=s.rows[0]?.id;if(!n)throw new u("identity_namespace_unavailable","The Site identity graph could not be initialized.",409);return await i.query(`UPDATE analytics_identity_namespaces
|
|
93
|
+
SET approved_sources=(SELECT jsonb_agg(value ORDER BY value)
|
|
94
|
+
FROM (SELECT DISTINCT value
|
|
95
|
+
FROM jsonb_array_elements_text(approved_sources || $3::jsonb)) merged_sources),
|
|
96
|
+
approved_hosts=(SELECT COALESCE(jsonb_agg(hostname ORDER BY hostname),'[]'::jsonb)
|
|
97
|
+
FROM (SELECT DISTINCT hostname
|
|
98
|
+
FROM (SELECT jsonb_array_elements_text(approved_hosts) AS hostname
|
|
99
|
+
UNION ALL
|
|
100
|
+
SELECT d.hostname
|
|
101
|
+
FROM analytics_pixels p
|
|
102
|
+
JOIN analytics_pixel_domains d ON d.pixel_id=p.id AND d.state='approved'
|
|
103
|
+
WHERE p.site_id=$2) hosts) distinct_hosts),
|
|
104
|
+
updated_at=now()
|
|
105
|
+
WHERE id=$1 AND site_id=$2`,[n,e,JSON.stringify([t])]),n}async function le(i,e){if(!e.visitorId&&!e.sessionId)return{events:0,sessions:0,touches:0,conversions:0};let t=e.visitorId?c("visitor_id",e.visitorId):null,s=e.sessionId?c("session_id",e.sessionId):null,n=await i.query(`UPDATE analytics_events SET person_id = $1 WHERE site_id = $2 AND person_id IS NULL
|
|
106
|
+
AND (($3::text IS NOT NULL AND visitor_id = $3) OR ($4::text IS NOT NULL AND session_id = $4))`,[e.personId,e.siteId,e.visitorId??null,e.sessionId??null]),r=await i.query(`UPDATE analytics_conversions SET person_id = $1 WHERE site_id = $2 AND person_id IS NULL
|
|
107
|
+
AND $3::text IS NOT NULL AND session_id = $3`,[e.personId,e.siteId,e.sessionId??null]),o=await i.query(`UPDATE analytics_sessions SET person_id = $1
|
|
108
|
+
WHERE site_id = $2 AND person_id IS NULL
|
|
109
|
+
AND (($3::text IS NOT NULL AND visitor_hmac = $3)
|
|
110
|
+
OR ($4::text IS NOT NULL AND session_hmac = $4))`,[e.personId,e.siteId,t,s]),a=await i.query(`UPDATE analytics_attribution_touches SET person_id = $1
|
|
111
|
+
WHERE site_id = $2 AND person_id IS NULL
|
|
112
|
+
AND (($3::text IS NOT NULL AND visitor_hmac = $3)
|
|
113
|
+
OR ($4::text IS NOT NULL AND session_id IN (
|
|
114
|
+
SELECT id FROM analytics_sessions
|
|
115
|
+
WHERE site_id = $2 AND session_hmac = $4
|
|
116
|
+
)))`,[e.personId,e.siteId,t,s]);return{events:n.rowCount??0,sessions:o.rowCount??0,touches:a.rowCount??0,conversions:r.rowCount??0}}async function L(i,e){let s=(await i.query(`SELECT id FROM analytics_people
|
|
117
|
+
WHERE site_id=$1 AND (namespace_id=$2 OR namespace_id IS NULL)
|
|
118
|
+
AND (($3::uuid IS NOT NULL AND crm_entity_id=$3::uuid)
|
|
119
|
+
OR ($3::uuid IS NULL AND $4::text IS NOT NULL AND crm_person_ref=$4))
|
|
120
|
+
ORDER BY first_seen_at,id LIMIT 1 FOR UPDATE`,[e.siteId,e.namespaceId,e.crmEntityId??null,e.crmPersonRef??null])).rows[0]?.id;if(!s&&!e.crmEntityId&&!e.crmPersonRef&&e.signals.length&&(s=(await i.query(`SELECT assignment.person_id
|
|
121
|
+
FROM analytics_identity_nodes node
|
|
122
|
+
JOIN analytics_identity_assignments assignment
|
|
123
|
+
ON assignment.identity_node_id=node.id AND assignment.site_id=node.site_id
|
|
124
|
+
WHERE node.site_id=$1 AND EXISTS (
|
|
125
|
+
SELECT 1 FROM jsonb_to_recordset($2::jsonb) signal(kind text,value_hmac text)
|
|
126
|
+
WHERE signal.kind=node.kind AND signal.value_hmac=node.value_hmac
|
|
127
|
+
)
|
|
128
|
+
ORDER BY assignment.assigned_at,assignment.person_id LIMIT 1`,[e.siteId,JSON.stringify(e.signals.map(r=>({kind:r.kind,value_hmac:r.valueHmac})))])).rows[0]?.person_id),s)await i.query(`UPDATE analytics_people SET last_seen_at=now(),
|
|
129
|
+
namespace_id=COALESCE(namespace_id,$2),
|
|
130
|
+
crm_entity_id=COALESCE(crm_entity_id,$3::uuid),
|
|
131
|
+
public_ref=COALESCE(public_ref,$4)
|
|
132
|
+
WHERE id=$1 AND site_id=$5`,[s,e.namespaceId,e.crmEntityId??null,W(),e.siteId]);else{let n=e.crmEntityId??null,r=e.crmPersonRef??(n?`XRay::entity::${n}`:`XRay::anonymous::${p()}`);s=(await i.query(`INSERT INTO analytics_people(id,site_id,namespace_id,crm_person_ref,crm_entity_id,public_ref)
|
|
133
|
+
VALUES($1,$2,$3,$4,$5::uuid,$6)
|
|
134
|
+
ON CONFLICT(site_id,crm_person_ref) DO UPDATE SET last_seen_at=now(),
|
|
135
|
+
namespace_id=COALESCE(analytics_people.namespace_id,EXCLUDED.namespace_id),
|
|
136
|
+
crm_entity_id=COALESCE(analytics_people.crm_entity_id,EXCLUDED.crm_entity_id),
|
|
137
|
+
public_ref=COALESCE(analytics_people.public_ref,EXCLUDED.public_ref)
|
|
138
|
+
RETURNING id`,[p(),e.siteId,e.namespaceId,r,n,W()])).rows[0].id}return{personId:s,mergedPersonIds:[]}}async function b(i,e){let s=(await i.query(`SELECT person_id,crm_entity_id::text FROM analytics_identity_assignments
|
|
139
|
+
WHERE identity_node_id=$1 AND site_id=$2 FOR UPDATE`,[e.identityNodeId,e.siteId])).rows[0];if(!s){await i.query(`INSERT INTO analytics_identity_assignments(
|
|
140
|
+
identity_node_id,site_id,person_id,crm_entity_id,evidence_kind
|
|
141
|
+
) VALUES($1,$2,$3,$4::uuid,$5)
|
|
142
|
+
ON CONFLICT(identity_node_id) DO NOTHING`,[e.identityNodeId,e.siteId,e.personId,e.crmEntityId??null,e.evidenceKind]);let n=await i.query("SELECT person_id FROM analytics_identity_assignments WHERE identity_node_id=$1 AND site_id=$2",[e.identityNodeId,e.siteId]);if(n.rows[0]?.person_id===e.personId)return"assigned";if(!n.rows[0])throw new u("identity_assignment_unavailable","Identity assignment could not be recorded.",409);s={person_id:n.rows[0].person_id,crm_entity_id:null}}return s.person_id===e.personId?(await i.query(`UPDATE analytics_identity_assignments SET updated_at=now(),
|
|
143
|
+
crm_entity_id=COALESCE(crm_entity_id,$3::uuid),evidence_kind=$4
|
|
144
|
+
WHERE identity_node_id=$1 AND site_id=$2`,[e.identityNodeId,e.siteId,e.crmEntityId??null,e.evidenceKind]),"existing"):(await i.query(`INSERT INTO analytics_identity_assignment_conflicts(
|
|
145
|
+
id,site_id,identity_node_id,active_person_id,attempted_person_id,
|
|
146
|
+
active_crm_entity_id,attempted_crm_entity_id,evidence_kind
|
|
147
|
+
) VALUES($1,$2,$3,$4,$5,$6::uuid,$7::uuid,$8)
|
|
148
|
+
ON CONFLICT(identity_node_id,active_person_id,attempted_person_id)
|
|
149
|
+
WHERE status='pending' DO UPDATE SET observed_at=now(),evidence_kind=EXCLUDED.evidence_kind`,[p(),e.siteId,e.identityNodeId,s.person_id,e.personId,s.crm_entity_id,e.crmEntityId??null,e.evidenceKind]),"conflict")}async function me(i,e){let s=(await i.query(`WITH person_nodes AS (
|
|
150
|
+
SELECT DISTINCT identity_node_id
|
|
151
|
+
FROM analytics_identity_edges
|
|
152
|
+
WHERE site_id=$1 AND namespace_id=$2 AND person_id=$3
|
|
153
|
+
)
|
|
154
|
+
UPDATE analytics_candidate_associations a
|
|
155
|
+
SET status='promoted',promoted_person_id=$3,promoted_at=$4::timestamptz,updated_at=now()
|
|
156
|
+
WHERE a.site_id=$1 AND a.namespace_id=$2
|
|
157
|
+
AND (a.left_identity_node_id IN (SELECT identity_node_id FROM person_nodes)
|
|
158
|
+
OR a.right_identity_node_id IN (SELECT identity_node_id FROM person_nodes))
|
|
159
|
+
AND (a.status IN ('active','expired') OR (a.status='promoted' AND a.promoted_person_id=$3))
|
|
160
|
+
RETURNING a.id`,[e.siteId,e.namespaceId,e.personId,e.occurredAt])).rows.map(r=>r.id);if(!s.length)return{associationIds:[],touches:0};for(let r of s)await i.query(`INSERT INTO analytics_candidate_promotions(
|
|
161
|
+
id,site_id,namespace_id,association_id,person_id,evidence_kind,
|
|
162
|
+
evidence_ref_hmac,promoted_by,promoted_at
|
|
163
|
+
) VALUES($1,$2,$3,$4,$5,$6,$7,'system',$8::timestamptz)
|
|
164
|
+
ON CONFLICT(association_id,person_id) DO NOTHING`,[p(),e.siteId,e.namespaceId,r,e.personId,e.candidateEvidenceKind,e.evidenceRefHmac,e.occurredAt]);let n=await i.query(`UPDATE analytics_attribution_touches touch SET person_id=$3
|
|
165
|
+
FROM analytics_candidate_attribution_touches candidate_touch
|
|
166
|
+
WHERE candidate_touch.site_id=$1 AND candidate_touch.namespace_id=$2
|
|
167
|
+
AND candidate_touch.association_id=ANY($4::uuid[])
|
|
168
|
+
AND candidate_touch.touch_id=touch.id AND touch.site_id=$1 AND touch.person_id IS NULL`,[e.siteId,e.namespaceId,e.personId,s]);return{associationIds:s,touches:n.rowCount??0}}async function ye(i,e){let t=c("promotion_evidence",`${e.evidenceKind}:${e.evidenceRef}`),s=c("promotion_person",e.personId),n=c("promotion_receipt",`${e.siteId}:${e.personId}:${e.evidenceKind}:${e.evidenceRef}`);await i.query(`INSERT INTO analytics_identity_promotion_receipts(
|
|
169
|
+
id,site_id,namespace_id,canonical_person_id,canonical_person_ref_hmac,
|
|
170
|
+
evidence_kind,evidence_ref_hmac,prior_person_ids,prior_identity_node_ids,
|
|
171
|
+
candidate_association_ids,affected_history_counts,schema_version,idempotency_key,promoted_at
|
|
172
|
+
) VALUES($1,$2,$3,$4,$5,$6,$7,$8::uuid[],$9::uuid[],$10::uuid[],$11::jsonb,
|
|
173
|
+
'xray.identity-promotion.v1',$12,$13::timestamptz)
|
|
174
|
+
ON CONFLICT(site_id,idempotency_key) DO NOTHING`,[p(),e.siteId,e.namespaceId,e.personId,s,e.evidenceKind,t,e.priorPersonIds,e.priorIdentityNodeIds,e.candidateAssociationIds,JSON.stringify({...e.history,candidateTouches:e.candidateTouches}),n,e.occurredAt])}async function P(i,e){let t=e.occurredAt??new Date().toISOString(),s=await le(i,e),n=c("promotion_evidence",`${e.evidenceKind}:${e.evidenceRef}`),r=await me(i,{siteId:e.siteId,namespaceId:e.namespaceId,personId:e.personId,candidateEvidenceKind:e.candidateEvidenceKind,evidenceRefHmac:n,occurredAt:t});await ye(i,{...e,history:s,candidateAssociationIds:r.associationIds,candidateTouches:r.touches,occurredAt:t})}async function ii(i,e){let t=e.customerId||e.orderId||e.email||e.phone;if(!t||!await C(i,e.siteId,e.visitorId,e.sessionId))return null;let s=await j(i,e.siteId,"pixel"),n=[...e.email?[{kind:"email",valueHmac:c("email",e.email)}]:[],...e.phone?[{kind:"phone",valueHmac:c("phone",e.phone)}]:[],...e.customerId?[{kind:"customer_id",valueHmac:c("customer_id",e.customerId)}]:[],...e.orderId?[{kind:"customer_id",valueHmac:c("customer_id",`order:${e.orderId}`)}]:[]];await h(i,{siteId:e.siteId,signals:n});let r=`XRay::${c("person_ref",t).slice(0,24)}`,o=[...n,...e.visitorId?[{kind:"visitor_id",valueHmac:c("visitor_id",e.visitorId)}]:[],...e.sessionId?[{kind:"session_id",valueHmac:c("session_id",e.sessionId)}]:[]],a=await L(i,{siteId:e.siteId,namespaceId:s,crmPersonRef:r,signals:o}),d=a.personId,l=[];e.visitorId&&l.push({kind:"visitor_id",value:e.visitorId,evidence:"pixel_identify",confidence:.98}),e.sessionId&&l.push({kind:"session_id",value:e.sessionId,evidence:"pixel_identify",confidence:.98}),e.email&&l.push({kind:"email",value:e.email,evidence:"pixel_identify",confidence:1}),e.phone&&l.push({kind:"phone",value:e.phone,evidence:"pixel_identify",confidence:.99}),e.customerId&&l.push({kind:"customer_id",value:e.customerId,evidence:"customer_id",confidence:1}),e.orderId&&l.push({kind:"customer_id",value:`order:${e.orderId}`,evidence:"order_id",confidence:1});for(let[y,I]of Object.entries(w(e.clickIds)))l.push({kind:"click_id",value:`${y}:${I}`,evidence:y,confidence:.9});let m=[],_=!1;for(let y of l){let I=await i.query(`INSERT INTO analytics_identity_nodes(id, site_id, namespace_id, kind, value_hmac) VALUES ($1,$2,$3,$4,$5)
|
|
175
|
+
ON CONFLICT(site_id, kind, value_hmac) DO UPDATE SET last_seen_at = now(),
|
|
176
|
+
namespace_id=COALESCE(analytics_identity_nodes.namespace_id,EXCLUDED.namespace_id) RETURNING id`,[p(),e.siteId,s,y.kind,c(y.kind,y.value)]);m.push(I.rows[0].id),await i.query(`INSERT INTO analytics_identity_edges(id, site_id, namespace_id, person_id, identity_node_id, evidence_kind, confidence)
|
|
177
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7)
|
|
178
|
+
ON CONFLICT(person_id, identity_node_id, evidence_kind) DO UPDATE
|
|
179
|
+
SET last_seen_at = now(), confidence = greatest(analytics_identity_edges.confidence, EXCLUDED.confidence),
|
|
180
|
+
namespace_id=COALESCE(analytics_identity_edges.namespace_id,EXCLUDED.namespace_id)`,[p(),e.siteId,s,d,I.rows[0].id,y.evidence,y.confidence]);let R=await b(i,{siteId:e.siteId,personId:d,identityNodeId:I.rows[0].id,evidenceKind:y.evidence});_||=R==="conflict"}return _||await P(i,{siteId:e.siteId,namespaceId:s,personId:d,evidenceKind:e.customerId?"authenticated_login":"browser_identify",candidateEvidenceKind:e.email?"verified_email":e.phone?"verified_phone":"authenticated_user",evidenceRef:t,priorPersonIds:a.mergedPersonIds,priorIdentityNodeIds:m,visitorId:e.visitorId,sessionId:e.sessionId,occurredAt:e.occurredAt}),e.customerId&&!_&&await $(i,{siteId:e.siteId,personId:d,consentSubjectRefs:[e.visitorId??"",e.sessionId??""],evidenceType:"authenticated_user",email:e.email,phone:e.phone}),d}async function ti(i){let e=await E().connect();try{if(await e.query("BEGIN"),!await C(e,i.siteId,i.visitorId,i.sessionId))return await e.query("COMMIT"),{personId:null,linkedSignals:0,conflicts:0};let t=await j(e,i.siteId,"form"),s=[...i.email?[{kind:"email",valueHmac:c("email",i.email)}]:[],...i.phone?[{kind:"phone",valueHmac:c("phone",i.phone)}]:[]];await h(e,{siteId:i.siteId,signals:s});let n=[...s,...i.visitorId?[{kind:"visitor_id",valueHmac:c("visitor_id",i.visitorId)}]:[],...i.sessionId?[{kind:"session_id",valueHmac:c("session_id",i.sessionId)}]:[]],r=await L(e,{siteId:i.siteId,namespaceId:t,crmPersonRef:i.crmPersonRef,crmEntityId:i.crmEntityId,signals:n}),o=r.personId,a=[];i.visitorId&&a.push({kind:"visitor_id",value:i.visitorId,evidence:"first_party_form",confidence:.98}),i.sessionId&&a.push({kind:"session_id",value:i.sessionId,evidence:"first_party_form",confidence:.95}),i.email&&a.push({kind:"email",value:i.email,evidence:"submitted_email",confidence:1}),i.phone&&a.push({kind:"phone",value:i.phone,evidence:"submitted_phone",confidence:.99});for(let[m,_]of Object.entries(w(i.clickIds)))a.push({kind:"click_id",value:`${m}:${_}`,evidence:m,confidence:.9});let d=[],l=0;for(let m of a){let _=await e.query(`INSERT INTO analytics_identity_nodes(id, site_id, namespace_id, kind, value_hmac) VALUES ($1,$2,$3,$4,$5)
|
|
181
|
+
ON CONFLICT(site_id, kind, value_hmac) DO UPDATE SET last_seen_at = now(),
|
|
182
|
+
namespace_id=COALESCE(analytics_identity_nodes.namespace_id,EXCLUDED.namespace_id) RETURNING id`,[p(),i.siteId,t,m.kind,c(m.kind,m.value)]);d.push(_.rows[0].id),await e.query(`INSERT INTO analytics_identity_edges(id, site_id, namespace_id, person_id, identity_node_id, evidence_kind, confidence)
|
|
183
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7)
|
|
184
|
+
ON CONFLICT(person_id, identity_node_id, evidence_kind) DO UPDATE SET last_seen_at = now(),
|
|
185
|
+
confidence = greatest(analytics_identity_edges.confidence, EXCLUDED.confidence),
|
|
186
|
+
namespace_id=COALESCE(analytics_identity_edges.namespace_id,EXCLUDED.namespace_id)`,[p(),i.siteId,t,o,_.rows[0].id,m.evidence,m.confidence]),await b(e,{siteId:i.siteId,personId:o,crmEntityId:i.crmEntityId,identityNodeId:_.rows[0].id,evidenceKind:m.evidence})==="conflict"&&(l+=1)}return i.submissionId&&await e.query("UPDATE analytics_form_submissions SET person_id = $1 WHERE id = $2 AND site_id = $3",[o,i.submissionId,i.siteId]),l===0&&await P(e,{siteId:i.siteId,namespaceId:t,personId:o,evidenceKind:"form_fill",candidateEvidenceKind:i.email?"verified_email":i.phone?"verified_phone":"authenticated_user",evidenceRef:i.submissionId??i.crmEntityId??i.crmPersonRef??o,priorPersonIds:r.mergedPersonIds,priorIdentityNodeIds:d,visitorId:i.visitorId,sessionId:i.sessionId}),l===0&&await $(e,{siteId:i.siteId,personId:o,consentSubjectRefs:[i.visitorId??""],evidenceType:i.evidenceType??"first_party_form",displayName:i.displayName,email:i.email,phone:i.phone}),await e.query("COMMIT"),{personId:o,linkedSignals:a.length,conflicts:l}}catch(t){throw await e.query("ROLLBACK"),t}finally{e.release()}}async function si(i){let e=await E().connect();try{await e.query("BEGIN");let s=(await e.query(`SELECT p.id,p.public_ref,p.namespace_id FROM analytics_people p
|
|
187
|
+
JOIN analytics_identity_namespaces n ON n.id=p.namespace_id AND n.site_id=p.site_id AND n.status='approved'
|
|
188
|
+
WHERE p.site_id=$1 AND p.crm_person_ref=$2 LIMIT 1 FOR UPDATE OF p`,[i.siteId,i.crmPersonRef])).rows[0];if(!s||!await H(e,i.siteId,s.public_ref))return await e.query("COMMIT"),{personId:null,linkedSignals:0,conflicts:0};let n=[...i.email?[{kind:"email",value:i.email,confidence:1}]:[],...i.phone?[{kind:"phone",value:i.phone,confidence:.99}]:[]];await h(e,{siteId:i.siteId,signals:n.map(_=>({kind:_.kind,valueHmac:c(_.kind,_.value)}))});let r=await L(e,{siteId:i.siteId,namespaceId:s.namespace_id,crmPersonRef:i.crmPersonRef,crmEntityId:i.crmEntityId,signals:n.map(_=>({kind:_.kind,valueHmac:c(_.kind,_.value)}))}),o=r.personId,d=(await e.query("SELECT public_ref FROM analytics_people WHERE id=$1 AND site_id=$2",[o,i.siteId])).rows[0]?.public_ref??s.public_ref,l=[],m=0;for(let _ of n){let y=await e.query(`INSERT INTO analytics_identity_nodes(id,site_id,namespace_id,kind,value_hmac) VALUES($1,$2,$3,$4,$5)
|
|
189
|
+
ON CONFLICT(site_id,kind,value_hmac) DO UPDATE SET last_seen_at=now(),
|
|
190
|
+
namespace_id=COALESCE(analytics_identity_nodes.namespace_id,EXCLUDED.namespace_id) RETURNING id`,[p(),i.siteId,s.namespace_id,_.kind,c(_.kind,_.value)]);l.push(y.rows[0].id),await e.query(`INSERT INTO analytics_identity_edges(id,site_id,namespace_id,person_id,identity_node_id,evidence_kind,confidence)
|
|
191
|
+
VALUES($1,$2,$3,$4,$5,'crm_contact',$6)
|
|
192
|
+
ON CONFLICT(person_id,identity_node_id,evidence_kind) DO UPDATE SET last_seen_at=now(),
|
|
193
|
+
confidence=greatest(analytics_identity_edges.confidence,EXCLUDED.confidence)`,[p(),i.siteId,s.namespace_id,o,y.rows[0].id,_.confidence]),await b(e,{siteId:i.siteId,personId:o,crmEntityId:i.crmEntityId,identityNodeId:y.rows[0].id,evidenceKind:"crm_contact"})==="conflict"&&(m+=1)}return m===0&&await P(e,{siteId:i.siteId,namespaceId:s.namespace_id,personId:o,evidenceKind:"crm_identity",candidateEvidenceKind:"crm_contact",evidenceRef:i.crmPersonRef,priorPersonIds:r.mergedPersonIds,priorIdentityNodeIds:l}),m===0&&await $(e,{siteId:i.siteId,personId:o,consentSubjectRefs:[d],evidenceType:"crm_contact",displayName:i.displayName,email:i.email,phone:i.phone}),await e.query("COMMIT"),{personId:o,linkedSignals:n.length,conflicts:m}}catch(t){throw await e.query("ROLLBACK"),t}finally{e.release()}}async function ni(i,e,t){let s=E();await S(s,i,e);let n=await s.query(`SELECT p.id, p.first_seen_at, p.last_seen_at, true AS crm_linked,
|
|
194
|
+
(SELECT count(*)::int FROM analytics_identity_edges e WHERE e.person_id = p.id) AS signal_count,
|
|
195
|
+
(SELECT count(*)::int FROM analytics_form_submissions f WHERE f.person_id = p.id) AS form_submission_count,
|
|
196
|
+
(SELECT count(*)::int FROM analytics_conversions c WHERE c.person_id = p.id) AS conversion_count,
|
|
197
|
+
(SELECT COALESCE(sum(c.value_minor), 0)::bigint FROM analytics_conversions c WHERE c.person_id = p.id) AS revenue_minor
|
|
198
|
+
FROM analytics_people p WHERE p.site_id = $1 ORDER BY p.last_seen_at DESC, p.id DESC LIMIT 1000`,[i]),r=F(n.rows,t,q("people",i,{start:"all",end:"all"}));return{people:r.items,pageInfo:r.pageInfo}}export{u as a,E as b,k as c,U as d,Ne as e,ve as f,Ae as g,w as h,c as i,f as j,Re as k,oe as l,be as m,Pe as n,xe as o,He as p,q,F as r,S as s,ce as t,We as u,je as v,Qe as w,W as x,ei as y,le as z,b as A,ii as B,ti as C,si as D,ni as E};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{mkdirSync as p,openSync as f,writeFileSync as m,closeSync as c,unlinkSync as g}from"fs";import{homedir as S}from"os";import{join as s}from"path";var u=!0;function w(r){u=r}function d(r){return r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"mcp-scraper-report"}function R(r){return r.split(`
|
|
2
|
+
`).find(t=>t.startsWith("# "))?.replace(/^#\s+/,"").trim()||"MCP Scraper Report"}function v(r){return r?.trim()||process.env.MCP_SCRAPER_OUTPUT_DIR?.trim()||s(S(),"Downloads","mcp-scraper")}function C(r){if(!y())return null;let o=v(),t=null,e=null;try{p(o,{recursive:!0});let a=`${new Date().toISOString().replace(/[:.]/g,"-")}-${d(R(r))}`;for(let n=0;n<1e3;n++){let i=s(o,`${a}${n?"-"+n:""}.md`);try{t=f(i,"wx"),e=i;break}catch(l){if(l.code!=="EEXIST")throw l}}return t===null?null:(m(t,r,"utf8"),c(t),t=null,e)}catch{if(t!==null)try{c(t)}catch{}if(e!==null)try{g(e)}catch{}return null}}function y(){return u&&process.env.MCP_SCRAPER_SAVE_REPORTS!=="false"}export{w as a,d as b,v as c,C as d,y as e};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{load as w}from"cheerio";var T="script,style,noscript,svg,iframe,canvas,nav,header,footer,aside",_=4096;function l(r){return(r??"").replace(/\s+/g," ").trim()}function C(r){let e=r.replace(/[\x00-\x09\x0b\x0c\x0e-\x1f]/g," ").trim();if(!e)return null;try{return JSON.parse(e)}catch(a){let o=/at position (\d+)/.exec(a instanceof Error?a.message:"")?.[1];if(!o)return null;try{return JSON.parse(e.slice(0,Number(o)))}catch{return null}}}function f(r,e,a={value:2e3}){if(a.value--<=0||r==null)return;if(Array.isArray(r)){for(let d of r)f(d,e,a);return}if(typeof r!="object")return;let o=r,c=o["@type"];if(typeof c=="string"&&c&&e.add(c),Array.isArray(c))for(let d of c)typeof d=="string"&&d&&e.add(d);Array.isArray(o["@graph"])&&f(o["@graph"],e,a)}function N(r){let e=r("main").first();if(e.length)return e;let a=r("article").first();if(a.length)return a;let o=r("body").first();return o.length?o:r.root()}function x(r,e){let a=Number(r.attr(e)??0);return Number.isFinite(a)&&a>0?a:null}function P(r){let e=w(r),a={};e("meta").each((m,s)=>{let t=e(s),n=l(t.attr("name")??t.attr("property")).toLowerCase(),i=l(t.attr("content"));n&&i&&(a[n]=i)});let o=[];e("h1,h2,h3,h4,h5,h6").each((m,s)=>{let t=e(s),n=l(t.text());n&&o.push({level:Number(s.tagName.slice(1)),text:n})});let c=[],d=new Set;e('script[type="application/ld+json"]').each((m,s)=>{let t=C(e(s).text());t!=null&&(Array.isArray(t)?c.push(...t):c.push(t),f(t,d))});let g=new Set;e("[itemtype]").each((m,s)=>{let t=e(s).attr("itemtype")??"";for(let n of t.split(/\s+/))try{let i=new URL(n);if(i.hostname==="schema.org"||i.hostname==="www.schema.org"){let h=i.pathname.replace(/^\/+/,"");h&&g.add(h)}}catch{}});let u=N(e).clone();u.find(T).remove(),u.find("[href],[src],[srcset],[data-src],[data-srcset]").each((m,s)=>{let t=e(s);for(let n of["href","src","srcset","data-src","data-srcset"]){let i=t.attr(n);i&&i.length>_&&t.removeAttr(n)}}),u.find("img").each((m,s)=>{let t=e(s),n=t.attr("src");if(!n||n.startsWith("data:")){let i=t.attr("data-src")??t.attr("data-lazy-src")??t.attr("data-original");i&&t.attr("src",i)}});let b=u.html()??"",A=l(u.text()),p=[];e("a[href]").each((m,s)=>{let t=e(s),n=l(t.attr("href"));n&&p.push({href:n,text:l(t.text()),rel:l(t.attr("rel")).toLowerCase()||null})});let y=[];return e("img").each((m,s)=>{let t=e(s),n=Object.fromEntries(Object.entries(s.attribs??{}).map(([i,h])=>[i.toLowerCase(),h]));y.push({src:t.attr("src")??t.attr("data-src")??t.attr("data-lazy-src")??t.attr("data-original")??null,srcset:t.attr("srcset")??t.attr("data-srcset")??null,alt:t.attr("alt")??null,width:x(t,"width"),height:x(t,"height"),attributes:n})}),{$:e,sourceHtml:r,title:l(e("title").first().text())||null,meta:a,headings:o,schema:c,schemaTypes:[...d],microdataTypes:[...g],canonicalUrl:l(e('link[rel~="canonical"]').first().attr("href"))||null,mainHtml:b,mainText:A,bodyClass:e("body").first().attr("class")??"",links:p,images:y}}export{P as a};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function u(e){if(Date.now()>=e)throw new Error("Reddit request deadline exhausted")}var t=class extends Error{constructor(n){super(`${n} deadline exceeded`),this.name="DeadlineExceededError"}};async function w(e,n,r,s=Number.POSITIVE_INFINITY){let o=Math.min(e-Date.now(),s);if(o<=0)throw new t(r);let T=Date.now()+o,i,a=Promise.resolve().then(()=>{if(Date.now()>=e)throw new t(r);return n()});try{let E=await Promise.race([a,new Promise((_,D)=>{i=setTimeout(()=>D(new t(r)),o)})]);if(Date.now()>=Math.min(e,T))throw new t(r);return E}finally{i&&clearTimeout(i)}}export{u as a,t as b,w as c};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as f,d,e as p,h as u,i as A,j as m}from"./chunk-SEZETSB3.js";import{c as s,i as l}from"./chunk-SDQW7IJN.js";import{randomUUID as D}from"crypto";var x="connected-data-exports/",_=10080*60*1e3,b=900*1e3;function C(){return process.env.CONNECTED_DATA_READ_WRITE_TOKEN?.trim()||process.env.CONNECTED_DATA_BLOB_READ_WRITE_TOKEN?.trim()||null}function r(){return{prefix:x,artifactTtlMs:_,downloadTtlMs:b,token:C()}}function i(t){return(t.toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"connected-data-export").slice(0,120)}function N(t){return l(t,x)}function w(t){return f(t,r())}function O(t,e=Date.now()){let n=w(t);return!n||n.getTime()<=e}async function P(t){return g({...t,contentType:"application/x-ndjson"})}async function g(t){let e=t.stableCreatedAt?Date.parse(t.stableCreatedAt):NaN,n=Number.isFinite(e)?e:Date.now(),c=i(t.filename),y=t.stableKey?i(t.stableKey):D(),a;try{a=await p({policy:r(),ownerId:t.ownerId,artifactKey:`${i(t.exportId)}-${y}--${c}`,createdAt:n,filename:c,contentType:t.contentType,content:typeof t.content=="string"?t.content:Buffer.from(t.content)})}catch(o){throw o instanceof Error&&o.message==="private_artifact_blob_not_configured"?new Error("connected_data_private_blob_not_configured"):o}return{...a,...a.capabilities.includes("text")?{readback:{tool:"report_artifact_read",arguments:{artifactId:a.artifactId,offset:0,maxBytes:2e4},continuation:"Repeat with offset set to the previous result nextOffset until nextOffset is null."}}:{}}}async function B(t){return u({...t,policy:r()})}async function R(t,e,n){return A({policy:r(),artifactId:t,offset:e,maxBytes:n})}async function U(t){try{return await m({...t,policy:r(),maxBytes:t.maxBytes??25*1024*1024})}catch(e){if(e instanceof s&&e.code==="read_limit")return null;throw e}}async function h(t={}){let e=t.now??new Date,n=r();return n.token?!t.force&&!(e.getUTCHours()===3&&e.getUTCMinutes()===17)?{deleted:0,store:"private-vercel-blob",skipped:!0}:d({policy:n,now:e,includeLegacy:!0,maxObjects:2e4}):{deleted:0,store:process.env.VERCEL==="1"||process.env.NODE_ENV==="production"?"none":"local"}}export{x as a,_ as b,b as c,r as d,N as e,w as f,O as g,P as h,g as i,B as j,R as k,U as l,h as m};
|