mcp-scraper 0.90.6 → 0.90.7
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 +18 -2
- package/README.md +1 -1
- package/dist/{analytics-repository-WNJESD4A.js → analytics-repository-FXS45ZTM.js} +1 -1
- package/dist/bin/api-server.js +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- 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/{chunk-MADTJXNI.js → chunk-5NQY2N7J.js} +105 -105
- package/dist/{chunk-COIA4RW4.js → chunk-74HHYSAZ.js} +1 -1
- package/dist/{chunk-BVSK6YHX.js → chunk-B2C7JU4O.js} +1 -1
- package/dist/{chunk-56UT5M5Y.js → chunk-BDLR4CCL.js} +3 -3
- package/dist/{chunk-5MMN27Q6.js → chunk-DSK6RKBS.js} +1 -1
- package/dist/{chunk-AAJG5OHF.js → chunk-I7IS7NTC.js} +1 -1
- package/dist/{chunk-Q5SUDDSQ.js → chunk-IGJKAECE.js} +1 -1
- package/dist/chunk-KDDU6BW3.js +48 -0
- package/dist/{chunk-NWRRVZKW.js → chunk-LPEIHMYH.js} +1 -1
- package/dist/{chunk-YDOBZMLS.js → chunk-OT2HUS2G.js} +1 -1
- package/dist/{chunk-AQSMJCTF.js → chunk-QUFR3KG2.js} +1 -1
- package/dist/{chunk-AQXYXVOP.js → chunk-R7LCD65B.js} +1 -1
- package/dist/{chunk-XXK5PLRB.js → chunk-TBTA7VVZ.js} +9 -8
- package/dist/{chunk-KKHTMYCD.js → chunk-TOJBPATJ.js} +1 -0
- package/dist/{chunk-4OTWTGKC.js → chunk-UKFNSXP3.js} +1 -1
- package/dist/chunk-WEPU72WA.js +1 -0
- package/dist/{chunk-D6BTAT5Y.js → chunk-YGBTTW5D.js} +1 -1
- package/dist/{chunk-WDB5CTRY.js → chunk-YTEAWZEN.js} +1 -1
- package/dist/{chunk-XARSC34L.js → chunk-ZKQRA6NQ.js} +5 -5
- package/dist/{db-XN7YPDGC.js → db-SVSD2AWJ.js} +1 -1
- package/dist/{extract-bundle-CP6WBOJI.js → extract-bundle-INT6JMJP.js} +1 -1
- package/dist/{gmail-service-5NH2GVQ5.js → gmail-service-YCPMGW3E.js} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{lead-list-enrichment-repository-AIMOP7YM.js → lead-list-enrichment-repository-B6Q4O4DG.js} +1 -1
- package/dist/{location-data-repository-25D3GHXE.js → location-data-repository-CBI5K7MR.js} +1 -1
- package/dist/{server-TQJ6Z3ZF.js → server-LYWW6XYE.js} +479 -454
- package/dist/{site-extract-repository-KG6FQU55.js → site-extract-repository-VPYOC3CP.js} +1 -1
- package/dist/{stripe-event-worker-ZNNQEVK4.js → stripe-event-worker-AQVRF24K.js} +1 -1
- package/dist/worker-U43KLOLN.js +1 -0
- package/package.json +1 -1
- package/dist/chunk-NOUHD54C.js +0 -48
- package/dist/chunk-UM4WSLKQ.js +0 -1
- package/dist/worker-4LFWAEX2.js +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{a as Zt,s as tn}from"./chunk-
|
|
2
|
-
`).find(i=>i.startsWith("data:")),n=t?t.slice(5).trim():e;return JSON.parse(n)}function gt(e){return e?(process.env.MEMORY_OPERATOR_IDENTITIES??"").split(",").map(n=>n.trim().toLowerCase()).filter(Boolean).includes(e.trim().toLowerCase()):!1}function un(){return Li(Fe(),"mcp-memory-key-v1",32)}function
|
|
1
|
+
import{a as Zt,s as tn}from"./chunk-YTEAWZEN.js";import{T as cn,a as de,b as Ne,c as yt,d as an,e as on,q as f,r as v,sa as G,ta as Ce,v as U,w as J}from"./chunk-UKFNSXP3.js";import{a as Fe,e as en}from"./chunk-IGJKAECE.js";import{d as nn,g as je,i as qe,k as _t,m as mt,o as rn,r as He,t as sn,u as pt}from"./chunk-YGBTTW5D.js";import{B as dt,F as D,G as Yt,P as Xt,Q as Gt,R as he,S as lt,T as Qt,U as ut,W as X,Y as ce,_ as Ue,r as Me,s as Jt,x as zt}from"./chunk-CTX3NMZQ.js";import{Ab as Wt,Eb as oe,G as qt,Hb as Bt,Ib as ct,Nb as Vt,Q as ke,Rb as Kt,S as Ht,t as ae,ub as T,vb as Pe,xb as st,yb as at,zb as ot}from"./chunk-TOJBPATJ.js";import{createCipheriv as Di,createDecipheriv as Ti,randomBytes as xi,scryptSync as Li}from"crypto";import{setScheduleEntitlementTool as $i}from"mcpscraper-memory-tools/tools/schedule/set-schedule-entitlement";import{createHash as Si,randomUUID as hi}from"crypto";import{exportJWK as Ni,importPKCS8 as Ci,SignJWT as Ri}from"jose";function wi(e=process.env.OAUTH_ISSUER){let t=e?.trim()||"https://mcpscraper.dev",n=new URL(t);if(n.protocol!=="https:"||n.username||n.password||n.search||n.hash||n.pathname!=="/"&&n.pathname!=="")throw new Error("OAUTH_ISSUER must be an HTTPS origin without credentials, path, query, or fragment");return n.origin}var Oi=wi(),We=null;async function dn(){if(We)return We;let e=process.env.OAUTH_SIGNING_KEY;if(!e?.trim())throw new Error("OAUTH_SIGNING_KEY is not set");let t=e.includes("BEGIN")?e:Buffer.from(e,"base64").toString("utf8"),n=await Ci(t,"RS256",{extractable:!0}),i=await Ni(n),r={kty:i.kty,n:i.n,e:i.e},s=Si("sha256").update(JSON.stringify({e:r.e,kty:r.kty,n:r.n})).digest("base64url").slice(0,16);return r.kid=s,r.alg="RS256",r.use="sig",We={privateKey:n,publicJwk:r,kid:s},We}async function Ls(){return(await dn()).publicJwk}async function ln(e){let{privateKey:t,kid:n}=await dn();return new Ri({scope:e.scope,plan:e.plan}).setProtectedHeader({alg:"RS256",kid:n}).setIssuer(Oi).setSubject(e.identity).setAudience(e.audience).setIssuedAt().setJti(hi()).setExpirationTime(`${e.ttlSeconds}s`).sign(t)}var ki=()=>(process.env.MCP_MEMORY_BASE_URL??"https://memory.mcpscraper.dev").replace(/\/$/,""),Pi=()=>(process.env.MCP_MEMORY_RESOURCE_URL??"https://memory.mcpscraper.dev/mcp").replace(/\/$/,""),Mi=()=>process.env.MCP_MEMORY_ADMIN_KEY?.trim()??"",Ui=300;function Fi(e){let t=e.split(`
|
|
2
|
+
`).find(i=>i.startsWith("data:")),n=t?t.slice(5).trim():e;return JSON.parse(n)}function gt(e){return e?(process.env.MEMORY_OPERATOR_IDENTITIES??"").split(",").map(n=>n.trim().toLowerCase()).filter(Boolean).includes(e.trim().toLowerCase()):!1}function un(){return Li(Fe(),"mcp-memory-key-v1",32)}function qs(e){let t=xi(12),n=Di("aes-256-gcm",un(),t),i=Buffer.concat([n.update(e,"utf8"),n.final()]),r=n.getAuthTag();return`${t.toString("base64")}:${r.toString("base64")}:${i.toString("base64")}`}function Hs(e){try{let[t,n,i]=e.split(":"),r=Ti("aes-256-gcm",un(),Buffer.from(t,"base64"));return r.setAuthTag(Buffer.from(n,"base64")),Buffer.concat([r.update(Buffer.from(i,"base64")),r.final()]).toString("utf8")}catch{return null}}async function _n(e,t,n){try{let i=await fetch(`${ki()}/mcp`,{method:"POST",headers:{"content-type":"application/json",accept:"application/json, text/event-stream",authorization:`Bearer ${n}`},body:JSON.stringify({jsonrpc:"2.0",id:1,method:"tools/call",params:{name:e,arguments:t}})}),r=await i.text();if(!i.ok)return{ok:!1,error:`memory ${e} failed (${i.status})`};let s=Fi(r);if(s.error)return{ok:!1,error:s.error.message??`memory ${e} failed`};if(s.result?.structuredContent)return s.result.structuredContent;let a=s.result?.content?.find(o=>o.type==="text")?.text;if(a)try{return JSON.parse(a)}catch{return{ok:!1,error:a}}return{ok:!1,error:`memory ${e} returned no result`}}catch(i){return{ok:!1,error:i?.message??"memory call failed"}}}function mn(e){return e.email}function ji(e){return mn(e)}function Ws(e){return gt(e.email)?"unlimited":e.memory_plan==="pro"||e.memory_plan==="team"?e.memory_plan:"free"}async function Bs(e){try{let{key:t}=await pn(e);if(!t)return;(await _n("listVaultsTool",{},t)).ok&&await Bt(e.id,!0)}catch{}}function qi(e){return e?!!Yt[e]?.includes_memory:!1}function F(e){return e.memory_plan==="team"?"team":qi(e.subscription_tier)||e.memory_plan==="pro"?"pro":"free"}async function pn(e,t){try{let n=gt(e.email),i=n?"unlimited":t??F(e),r=n?"memory.read memory.write memory.admin":"memory.read memory.write";return{key:await ln({identity:mn(e),scope:r,plan:i,audience:Pi(),ttlSeconds:Ui})}}catch(n){return{key:null,error:n instanceof Error?n.message:"memory service authorization unavailable"}}}async function z(e,t){let n=gt(e.email)?"unlimited":t,{key:i,error:r}=await pn(e,n);if(!i)return{ok:!1,error:r??"memory unavailable"};let s=await _n("listVaultsTool",{},i);return{ok:s.ok,error:s.error}}async function Be(e){let t=Mi();if(!t)return{ok:!1,error:"memory not configured (MCP_MEMORY_ADMIN_KEY missing)"};let n=ji(e),i=await $i.execute({apiKey:t,granteeIdentity:n,mcpScraperApiKey:e.api_key},{});return{ok:i.ok,error:i.error}}var yn=["active","trialing","past_due"];function Re(e){return yn.includes(e)}function gn(e,t){for(let n of yn){let i=e.find(r=>r.status===n&&t(r));if(i)return i}}var Hi="price_1TtYPAS8aAcsk3TGHOgrDZiQ";function Wi(e=new Date){return`${e.getUTCFullYear()}-${String(e.getUTCMonth()+1).padStart(2,"0")}`}var Bi=new Set(["connected","needs_reauth"]);function Vi(e){return Bi.has(String(e.lifecycleStatus??""))}async function Ki(e,t){let n=await ae().execute({sql:"SELECT connection_id FROM connected_account_charges WHERE user_id = ? AND period_start = ?",args:[Number(e),t]});return new Set(n.rows.map(i=>String(i.connection_id)))}async function Ji(e,t,n){await en();let i=Number(e),r=ae();return await r.batch([{sql:`
|
|
3
3
|
INSERT OR IGNORE INTO connected_account_charge_transfers (
|
|
4
4
|
user_id, period_start, from_connection_id, to_connection_id, provider_config_key
|
|
5
5
|
)
|
|
@@ -62,32 +62,86 @@ import{a as Zt,s as tn}from"./chunk-WDB5CTRY.js";import{T as cn,a as de,b as Ne,
|
|
|
62
62
|
last_error_code = excluded.last_error_code,
|
|
63
63
|
checked_at = datetime('now'),
|
|
64
64
|
synced_at = CASE WHEN ? THEN datetime('now') ELSE connected_account_billing.synced_at END,
|
|
65
|
-
updated_at = datetime('now')`,args:[Number(e.userId),e.stripeSubscriptionId,e.stripeSubscriptionItemId,e.priceId,e.quantity,e.status,e.lastErrorCode,e.synced?1:0,e.synced?1:0]});let t=await Et(e.userId);if(!t)throw new Error("connected account billing state was not persisted");return t}var j=class extends Error{constructor(n,i,r=409,s){super(n);this.code=i;this.status=r;this.details=s;this.name="ConnectedAccountBillingError"}code;status;details};function bt(){let e=process.env.CONNECTED_ACCOUNT_PRICE_ID?.trim();return e||Hi||null}function vt(e,t){let n=Math.max(0,Math.round(e));return{provider:"nango",billingMode:"flat_recurring_credits",priceId:bt(),unitAmountCredits:Me,interval:"month",quantity:n,projectedMonthlyCredits:n*Me,status:t?.status??"pending",lastErrorCode:t?.last_error_code??null,syncedAt:t?.synced_at??null,usage:zt.usage}}function le(e){return e.items.data.find(t=>!!t.price?.id&&t.price.id in D)}function
|
|
66
|
-
VALUES (?, ?, ?, ?) ON CONFLICT(user_id, connection_id, period_start) DO NOTHING`,args:[Number(e.id),u,o,y]}),E.duplicate||await Zt(e.id,1)}let _=await ft({userId:e.id,stripeSubscriptionId:e.subscription_id,stripeSubscriptionItemId:null,priceId:r,quantity:i,status:"synced",lastErrorCode:null,synced:!0});return vt(i,_)}catch(c){throw c instanceof j?c:(await Ve(e,a,c),new j("Unable to update connected-account billing.","stripe_connection_billing_failed",503))}}async function Qs(e,t){return vt(t,await Et(e))}import{createHash as rr}from"crypto";import{z as H}from"zod";import{createHash as Yi,randomUUID as Q}from"crypto";function I(e){return e??v()}function Ke(e){return Yi("sha256").update(JSON.stringify(e)).digest("hex")}async function Z(e){let t=await I(e.db).query("SELECT id::text AS id FROM analytics_connections WHERE id::text=$1 AND site_id=$2",[e.connectionId,e.siteId]);if(!t.rowCount)throw new f("analytics_crm_resource_not_found","CRM resource was not found.",404);return t.rows[0].id}async function ia(e){let t=nn.parse(e.capability),n=I(e.db),i=await Z({siteId:e.siteId,connectionId:e.connectionId,db:n});await n.query(`INSERT INTO analytics_crm_connection_capabilities(
|
|
65
|
+
updated_at = datetime('now')`,args:[Number(e.userId),e.stripeSubscriptionId,e.stripeSubscriptionItemId,e.priceId,e.quantity,e.status,e.lastErrorCode,e.synced?1:0,e.synced?1:0]});let t=await Et(e.userId);if(!t)throw new Error("connected account billing state was not persisted");return t}var j=class extends Error{constructor(n,i,r=409,s){super(n);this.code=i;this.status=r;this.details=s;this.name="ConnectedAccountBillingError"}code;status;details};function bt(){let e=process.env.CONNECTED_ACCOUNT_PRICE_ID?.trim();return e||Hi||null}function vt(e,t){let n=Math.max(0,Math.round(e));return{provider:"nango",billingMode:"flat_recurring_credits",priceId:bt(),unitAmountCredits:Me,interval:"month",quantity:n,projectedMonthlyCredits:n*Me,status:t?.status??"pending",lastErrorCode:t?.last_error_code??null,syncedAt:t?.synced_at??null,usage:zt.usage}}function le(e){return e.items.data.find(t=>!!t.price?.id&&t.price.id in D)}function Qs(e){return gn(e,t=>!!le(t))}function zi(e){return e instanceof j?e.code:"stripe_connection_billing_failed"}async function Ve(e,t,n){await ft({userId:e.id,stripeSubscriptionId:e.subscription_id,stripeSubscriptionItemId:null,priceId:bt(),quantity:t,status:n instanceof j&&n.code!=="stripe_connection_billing_failed"?"blocked":"error",lastErrorCode:zi(n),synced:!1})}async function Zs(e,t){let n=[...new Set(t.filter(Vi).map(c=>c.connectionId))],i=n.length,r=bt();if(!r){let c=new j("Connected-account billing is not configured.","connected_account_price_not_configured",503);throw await Ve(e,i,c),c}if(!e.subscription_id||!(e.subscription_tier??"")||!Object.values(D).some(c=>c.tier===e.subscription_tier)){if(i===0){let d=await ft({userId:e.id,stripeSubscriptionId:null,stripeSubscriptionItemId:null,priceId:r,quantity:0,status:"synced",lastErrorCode:null,synced:!0});return vt(0,d)}let c=new j("An active paid plan is required before connected accounts can be billed.","paid_plan_required",403);throw await Ve(e,i,c),c}let s=await Et(e.id),a=Math.max(0,s?.quantity??0),o=Wi();try{let c=await Ki(e.id,o),d=n.filter(u=>!c.has(u));for(let u of d){if(await Ji(e.id,u,o))continue;let y=Jt,E=await Kt(e.id,y,Ue.CONNECTED_ACCOUNT_FEE,`1 connected account at ${Me} credits/month (${o})`,`connection-fee:${u}:${o}`);if(!E.ok){let b=new j(`Insufficient credits to connect. Balance: ${X(E.balance_mc)} credits. Connecting requires ${X(y)} credits (${X(y-E.balance_mc)} short). Upgrade your plan or wait for your credits to refresh.`,"insufficient_credits",402,{balanceCredits:X(E.balance_mc),requiredCredits:X(y),shortByCredits:X(Math.max(0,y-E.balance_mc))});throw await Ve(e,a,b),b}await ae().execute({sql:`INSERT INTO connected_account_charges (user_id, connection_id, period_start, amount_mc)
|
|
66
|
+
VALUES (?, ?, ?, ?) ON CONFLICT(user_id, connection_id, period_start) DO NOTHING`,args:[Number(e.id),u,o,y]}),E.duplicate||await Zt(e.id,1)}let _=await ft({userId:e.id,stripeSubscriptionId:e.subscription_id,stripeSubscriptionItemId:null,priceId:r,quantity:i,status:"synced",lastErrorCode:null,synced:!0});return vt(i,_)}catch(c){throw c instanceof j?c:(await Ve(e,a,c),new j("Unable to update connected-account billing.","stripe_connection_billing_failed",503))}}async function ea(e,t){return vt(t,await Et(e))}function fn(e,t){return e&&(t??0)>0}import{createHash as Mo,createHmac as kt,generateKeyPairSync as Qr,randomUUID as se,sign as Zr}from"crypto";import es from"twilio";import{z as l}from"zod";import{z as C}from"zod";var Yi=new Set(["AED","AFN","ALL","AMD","ANG","AOA","ARS","AUD","AWG","AZN","BAM","BBD","BDT","BGN","BHD","BIF","BMD","BND","BOB","BOV","BRL","BSD","BTN","BWP","BYN","BZD","CAD","CDF","CHE","CHF","CHW","CLF","CLP","CNY","COP","COU","CRC","CUP","CVE","CZK","DJF","DKK","DOP","DZD","EGP","ERN","ETB","EUR","FJD","FKP","GBP","GEL","GHS","GIP","GMD","GNF","GTQ","GYD","HKD","HNL","HTG","HUF","IDR","ILS","INR","IQD","IRR","ISK","JMD","JOD","JPY","KES","KGS","KHR","KMF","KPW","KRW","KWD","KYD","KZT","LAK","LBP","LKR","LRD","LSL","LYD","MAD","MDL","MGA","MKD","MMK","MNT","MOP","MRU","MUR","MVR","MWK","MXN","MXV","MYR","MZN","NAD","NGN","NIO","NOK","NPR","NZD","OMR","PAB","PEN","PGK","PHP","PKR","PLN","PYG","QAR","RON","RSD","RUB","RWF","SAR","SBD","SCR","SDG","SEK","SGD","SHP","SLE","SOS","SRD","SSP","STN","SVC","SYP","SZL","THB","TJS","TMT","TND","TOP","TRY","TTD","TWD","TZS","UAH","UGX","USD","USN","UYI","UYU","UYW","UZS","VED","VES","VND","VUV","WST","XAF","XAG","XAU","XBA","XBB","XBC","XBD","XCD","XDR","XOF","XPD","XPF","XPT","XSU","XTS","XUA","XXX","YER","ZAR","ZMW","ZWG"]),we=C.string().trim().length(3).transform(e=>e.toUpperCase()).refine(e=>Yi.has(e),"Provide a valid ISO-4217 currency."),Ke=C.number().int("Money must be represented as integer minor units.").min(0,"Money cannot be negative.").max(Number.MAX_SAFE_INTEGER),Je=C.string().trim().min(1).max(80).default("1"),Xi=C.enum(["semantic_markup","source_event","crm_field","transaction"]),Gi=C.object({mode:C.literal("none"),ruleVersion:Je}).strict(),Qi=C.object({mode:C.literal("fixed"),valueMinor:Ke,currency:we,ruleVersion:Je}).strict(),Zi=C.object({mode:C.literal("dynamic"),source:Xi,currency:we,fallbackValueMinor:Ke.nullable().default(null),ruleVersion:Je}).strict(),vn=C.discriminatedUnion("mode",[Gi,Qi,Zi]),er=C.object({linkedEventName:C.string().trim().regex(/^[a-z][a-z0-9_]{1,79}$/),relation:C.enum(["before","after","either"]),intervalSeconds:C.number().int().min(0).max(10*365*24*60*60).nullable(),overrideValueMinor:Ke,currency:we}).strict(),ze=C.object({targetEventName:C.string().trim().regex(/^[a-z][a-z0-9_]{1,79}$/),base:vn,conditionalOverride:er.nullable().default(null),ruleVersion:Je}).strict().superRefine((e,t)=>{let n=e.conditionalOverride;n&&e.base.mode!=="none"&&n.currency!==e.base.currency&&t.addIssue({code:"custom",path:["conditionalOverride","currency"],message:"Conditional override currency must match the base value currency."})});function Q(e,t){let n=Date.parse(e);if(!Number.isFinite(n))throw new Error(`${t} must be an ISO timestamp.`);return n}function tr(e,t){let n=Ke.parse(e.valueMinor),i=En(t,e.currency);return Q(e.observedAt,"observedAt"),{valueMinor:n,currency:i,observedAt:new Date(e.observedAt).toISOString()}}function nr(e){return vn.parse(e)}function En(e,t){let i=(Array.isArray(e)?e:[e,t]).filter(r=>typeof r=="string"&&r.trim().length>0).map(r=>we.parse(r));if(i.length===0)throw new Error("A currency is required for a monetary value.");if(new Set(i).size!==1)throw new Error(`Mixed currencies are not compatible: ${[...new Set(i)].join(", ")}.`);return i[0]}function ir(e,t={}){let n=nr(e);if(n.mode==="none")return{valueMinor:null,currency:null,provenance:{mode:"none",source:null,observedAt:null,fallback:!1,ruleVersion:n.ruleVersion,resolution:"absent",baseValueMinor:null,resolvedValueMinor:null,currency:null,linkedEvent:null}};if(n.mode==="fixed"){let s=t.observedAt?new Date(Q(t.observedAt,"observedAt")).toISOString():null;return{valueMinor:n.valueMinor,currency:n.currency,provenance:{mode:"fixed",source:null,observedAt:s,fallback:!1,ruleVersion:n.ruleVersion,resolution:"fixed",baseValueMinor:n.valueMinor,resolvedValueMinor:n.valueMinor,currency:n.currency,linkedEvent:null}}}let i=t.observations?.[n.source];if(i){let s=tr(i,n.currency);return{valueMinor:s.valueMinor,currency:s.currency,provenance:{mode:"dynamic",source:n.source,observedAt:s.observedAt,fallback:!1,ruleVersion:n.ruleVersion,resolution:"dynamic",baseValueMinor:s.valueMinor,resolvedValueMinor:s.valueMinor,currency:s.currency,linkedEvent:null}}}let r=n.fallbackValueMinor;return{valueMinor:r,currency:r===null?null:n.currency,provenance:{mode:"dynamic",source:n.source,observedAt:null,fallback:r!==null,ruleVersion:n.ruleVersion,resolution:r===null?"absent":"fallback",baseValueMinor:r,resolvedValueMinor:r,currency:r===null?null:n.currency,linkedEvent:null}}}function rr(e,t,n){let i=Q(t.occurredAt,"targetEvent.occurredAt"),r=n.filter(s=>{if(s.eventName!==e.linkedEventName)return!1;let o=(Q(s.occurredAt,"linkedEvent.occurredAt")-i)/1e3;return e.relation==="before"&&o>0||e.relation==="after"&&o<0?!1:e.intervalSeconds===null||Math.abs(o)<=e.intervalSeconds});return r.sort((s,a)=>{let o=Math.abs(Q(s.occurredAt,"linkedEvent.occurredAt")-i),c=Math.abs(Q(a.occurredAt,"linkedEvent.occurredAt")-i);return o-c||s.eventId.localeCompare(a.eventId)}),r[0]??null}function bn(e,t){let n=ze.parse(e);if(t.targetEvent.eventName!==n.targetEventName)throw new Error(`Value rule targets ${n.targetEventName}, not ${t.targetEvent.eventName}.`);let i=ir({...n.base,ruleVersion:n.ruleVersion},t);if(!n.conditionalOverride)return i;let r=rr(n.conditionalOverride,t.targetEvent,t.linkedEvents);if(!r)return i;if(t.providerOperation==="restatement"&&r.identityTier!=="confirmed")throw new Error("Candidate-only evidence cannot be used for a provider restatement.");let s=i.currency?En(i.currency,n.conditionalOverride.currency):we.parse(n.conditionalOverride.currency);return{valueMinor:n.conditionalOverride.overrideValueMinor,currency:s,provenance:{...i.provenance,ruleVersion:n.ruleVersion,resolution:"conditional_override",resolvedValueMinor:n.conditionalOverride.overrideValueMinor,currency:s,linkedEvent:{eventId:r.eventId,eventName:r.eventName,occurredAt:new Date(Q(r.occurredAt,"linkedEvent.occurredAt")).toISOString(),identityTier:r.identityTier??"candidate"}}}}var An=l.object({gclid:l.string().trim().max(500).optional(),gbraid:l.string().trim().max(500).optional(),wbraid:l.string().trim().max(500).optional(),fbclid:l.string().trim().max(500).optional(),fbp:l.string().trim().max(500).optional(),fbc:l.string().trim().max(500).optional(),ttclid:l.string().trim().max(500).optional(),rdt_cid:l.string().trim().max(500).optional(),msclkid:l.string().trim().max(500).optional(),li_fat_id:l.string().trim().max(500).optional(),snapclid:l.string().trim().max(500).optional()}).strict(),sr=l.object({site_analytics:l.enum(["granted","denied","unknown"]),enhanced_matching:l.enum(["granted","denied","unknown"]),advertising_measurement:l.enum(["granted","denied","unknown"])}).strict(),ar=l.object({analytics:l.enum(["granted","denied","unknown"]).default("unknown"),adUserData:l.enum(["granted","denied","unknown"]).default("unknown"),adPersonalization:l.enum(["granted","denied","unknown"]).default("unknown"),globalPrivacyControl:l.boolean().default(!1),occurredAt:l.string().datetime(),receiptId:l.string().uuid().optional(),policyVersion:l.number().int().positive().optional(),purposes:sr.optional()}).strict().transform(e=>({...e,purposes:e.purposes??{site_analytics:e.analytics,enhanced_matching:e.adUserData,advertising_measurement:e.adPersonalization}})),In=l.enum(["browser","form","call","crm","transaction","custom_server","conversion","delivery"]),or=l.object({schemaVersion:l.literal(1).default(1),eventId:l.string().trim().min(8).max(200),sourceEventId:l.string().trim().min(1).max(300).optional(),source:l.string().trim().min(1).max(80).default("xray"),sourceAccountRef:l.string().trim().max(240).optional(),eventKind:In,eventName:l.string().trim().regex(/^[a-z][a-z0-9_]{1,79}$/),occurredAt:l.string().datetime(),visitorId:l.string().trim().max(160).optional(),sessionId:l.string().trim().max(160).optional(),personRef:l.string().trim().max(240).optional(),orderId:l.string().trim().max(240).optional(),callId:l.string().trim().max(240).optional(),dealId:l.string().trim().max(240).optional(),path:l.string().trim().max(2e3).optional(),canonicalUrl:l.string().url().max(3e3).optional(),sourceName:l.string().trim().max(180).optional(),medium:l.string().trim().max(180).optional(),campaign:l.string().trim().max(240).optional(),clickIds:An.optional(),consent:ar.optional(),valueMinor:l.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),currency:l.string().trim().regex(/^[A-Za-z]{3}$/).optional(),properties:l.record(l.string().max(80),l.unknown()).default({})}).strict(),Ye=or.extend({sourceEventId:l.string().trim().min(1).max(300),source:l.string().trim().min(1).max(80),sourceAccountRef:l.string().trim().min(1).max(240)}),oa=l.object({sourceEventId:l.string().trim().min(1).max(300),source:l.string().trim().min(1).max(80),sourceAccountRef:l.string().trim().min(1).max(240),contactId:l.string().trim().max(240).optional(),dealId:l.string().trim().min(1).max(240),pipelineId:l.string().trim().max(240).optional(),stageId:l.string().trim().min(1).max(240),stageName:l.string().trim().max(240).optional(),occurredAt:l.string().datetime(),valueMinor:l.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),currency:l.string().trim().regex(/^[A-Za-z]{3}$/).optional(),visitorId:l.string().trim().max(160).optional(),sessionId:l.string().trim().max(160).optional(),clickIds:An.optional(),email:l.string().email().max(320).optional(),phone:l.string().trim().max(80).optional(),orderId:l.string().trim().max(240).optional(),status:l.enum(["open","won","lost","spam","unknown"]).default("unknown")}).strict(),cr=l.object({eventName:l.string().regex(/^[a-z][a-z0-9_]{1,79}$/).optional(),eventKind:In.optional(),source:l.string().trim().max(80).optional(),stageId:l.string().trim().max(240).optional(),status:l.enum(["open","won","lost","spam","unknown"]).optional(),answered:l.boolean().optional(),minimumCallDurationSeconds:l.number().int().min(0).max(86400).optional(),excludedTags:l.array(l.string().trim().max(80)).max(30).default([]),requiredTags:l.array(l.string().trim().max(80)).max(30).default([])}).strict(),Sn=l.object({name:l.string().trim().min(1).max(120),conversionKind:l.string().trim().regex(/^[a-z][a-z0-9_]{1,79}$/),condition:cr,defaultValueMinor:l.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),defaultCurrency:l.string().trim().regex(/^[A-Za-z]{3}$/).default("USD"),valueRule:ze.optional(),enabled:l.boolean().default(!0)}).strict();function dr(e){let t=e.trim().toLowerCase().replace(/\.$/,"");if(!t||t.includes("://")||/[/?#]/.test(t))return"";try{let n=new URL(`https://${t}`).hostname.toLowerCase().replace(/\.$/,"");return!n||n.length>253||n!=="localhost"&&!n.includes(".")||n.split(".").some(i=>!i||i.length>63||!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(i))?"":n}catch{return""}}var lr=l.string().trim().max(253).transform(dr).refine(Boolean,"Provide a valid bare hostname."),Oe=l.string().trim().max(80).regex(/^[A-Za-z][A-Za-z0-9_-]{0,79}$/).refine(e=>(e.match(/\d/g)?.length??0)<7,"Semantic dimensions cannot contain phone-like values.").transform(e=>e.toLowerCase()),ur=l.object({value:l.number().finite().min(0).max(1e12).optional(),currency:l.string().trim().regex(/^[A-Za-z]{3}$/).transform(e=>e.toUpperCase()).optional(),orderId:l.string().trim().min(1).max(200).optional(),contentId:l.string().trim().min(1).max(200).optional(),contentType:l.string().trim().min(1).max(80).optional(),placement:Oe.optional(),component:Oe.optional(),action:Oe.optional(),destination:Oe.optional(),plan:Oe.optional()}).strict(),At=l.object({name:l.string().trim().min(1).max(120),eventName:l.string().trim().regex(/^[a-z][a-z0-9_]{1,79}$/),triggerKind:l.enum(["page","click","form","semantic"]),path:l.string().trim().startsWith("/").max(2e3).optional(),pathMatch:l.enum(["exact","prefix"]).default("exact"),selector:l.string().trim().min(1).max(500).optional(),hostnames:l.array(lr).max(25).transform(e=>[...new Set(e)]).default([]),metadata:ur.optional(),valueRule:ze.optional(),oncePerSession:l.boolean().default(!1),enabled:l.boolean().default(!0)}).strict().superRefine((e,t)=>{e.triggerKind==="page"&&!e.path&&t.addIssue({code:"custom",path:["path"],message:"Page triggers require a path."}),["click","form"].includes(e.triggerKind)&&!e.selector&&t.addIssue({code:"custom",path:["selector"],message:"Click and form triggers require a selector."}),e.triggerKind==="semantic"&&(e.path||e.selector)&&t.addIssue({code:"custom",path:["triggerKind"],message:"Semantic triggers use data-xray-event and cannot include a path or selector."})});import{createHmac as _r,randomUUID as It}from"crypto";import va from"ipaddr.js";import{randomUUID as hn}from"crypto";async function ua(e){return(await v().query(`SELECT id, site_id, name, status, version, approved_at
|
|
67
|
+
FROM analytics_identity_namespaces
|
|
68
|
+
WHERE site_id=$1 AND status IN ('draft','approved','disabled')
|
|
69
|
+
ORDER BY CASE status WHEN 'approved' THEN 0 WHEN 'draft' THEN 1 ELSE 2 END,
|
|
70
|
+
version DESC, name, id`,[e.siteId])).rows.map(n=>({id:n.id,siteId:n.site_id,name:n.name,status:n.status,version:n.version,approvedAt:n.approved_at===null?null:new Date(n.approved_at).toISOString()}))}var Nn={domain:"host",application:"server",phone:"call",crm:"crm"},Cn={host:"domain",server:"application",call:"phone",crm:"crm"};function Rn(e,t){let n=t.trim().toLowerCase();if(!n||n.length>500)throw new f("analytics_namespace_source_invalid","Provide a valid namespace source.",400);if(e==="domain"&&(n.includes("://")||/[/?#]/.test(n)||!n.includes(".")))throw new f("analytics_namespace_domain_invalid","Provide a bare approved domain.",400);return n}async function _a(e){let t=e.name.trim();if(!t||t.length>160)throw new f("analytics_namespace_name_invalid","Provide a namespace name.",400);let n=await v().connect();await n.query("BEGIN");try{await n.query("SELECT pg_advisory_xact_lock(hashtext($1))",[e.siteId]);let r=(await n.query(`SELECT id, site_id, name, status, version, approved_at
|
|
71
|
+
FROM analytics_identity_namespaces
|
|
72
|
+
WHERE site_id=$1 AND status IN ('draft','approved','disabled')
|
|
73
|
+
ORDER BY version DESC LIMIT 1`,[e.siteId])).rows[0];if(r){if(r.name!==t)throw new f("analytics_namespace_already_exists","This site already belongs to an identity namespace.",409);return await n.query("COMMIT"),{id:r.id,siteId:r.site_id,name:r.name,status:r.status,version:r.version,approvedAt:r.approved_at?.toISOString()??null}}let a=(await n.query(`INSERT INTO analytics_identity_namespaces(
|
|
74
|
+
id, site_id, name, status, approved_hosts, approved_sources, version, approved_by_user_id
|
|
75
|
+
) VALUES ($1,$2,$3,'draft','[]'::jsonb,'[]'::jsonb,1,$4)
|
|
76
|
+
RETURNING id, site_id, name, status, version, approved_at`,[hn(),e.siteId,t,e.createdByUserId])).rows[0];return await n.query("COMMIT"),{id:a.id,siteId:a.site_id,name:a.name,status:a.status,version:a.version,approvedAt:a.approved_at?.toISOString()??null}}catch(i){throw await n.query("ROLLBACK"),i}finally{n.release()}}async function ma(e){let t=Rn(e.sourceKind,e.sourceRef),n=await v().connect();await n.query("BEGIN");try{let i=await n.query(`SELECT id, status FROM analytics_identity_namespaces
|
|
77
|
+
WHERE id=$1 AND site_id=$2 FOR UPDATE`,[e.namespaceId,e.siteId]);if(!i.rows[0])throw new f("analytics_namespace_not_found","Identity namespace not found.",404);if(["archived","disabled"].includes(i.rows[0].status))throw new f("analytics_namespace_inactive","Identity namespace is not active.",409);let r=Nn[e.sourceKind],s=await n.query(`INSERT INTO analytics_identity_sources(id, namespace_id, site_id, source_kind, source_ref, status)
|
|
78
|
+
VALUES ($1,$2,$3,$4,$5,'approved')
|
|
79
|
+
ON CONFLICT(namespace_id, source_kind, source_ref) DO UPDATE
|
|
80
|
+
SET status='approved', revoked_at=NULL
|
|
81
|
+
RETURNING id, namespace_id, site_id, source_kind, source_ref, status, created_at, revoked_at`,[hn(),e.namespaceId,e.siteId,r,t]);await n.query(`UPDATE analytics_identity_namespaces
|
|
82
|
+
SET status='approved', approved_by_user_id=$3, approved_at=COALESCE(approved_at, now()),
|
|
83
|
+
approved_hosts=CASE WHEN $4='host'
|
|
84
|
+
THEN (SELECT jsonb_agg(DISTINCT value) FROM jsonb_array_elements_text(approved_hosts || to_jsonb($5::text)) value)
|
|
85
|
+
ELSE approved_hosts END,
|
|
86
|
+
approved_sources=(SELECT jsonb_agg(DISTINCT value) FROM jsonb_array_elements_text(approved_sources || to_jsonb(($4 || ':' || $5)::text)) value),
|
|
87
|
+
updated_at=now()
|
|
88
|
+
WHERE id=$1 AND site_id=$2`,[e.namespaceId,e.siteId,e.approvedByUserId,r,t]),await n.query("COMMIT");let a=s.rows[0];return{id:a.id,namespaceId:a.namespace_id,siteId:a.site_id,sourceKind:Cn[a.source_kind],sourceRef:a.source_ref,status:a.status,createdAt:a.created_at instanceof Date?a.created_at.toISOString():new Date(a.created_at).toISOString(),revokedAt:a.revoked_at==null?null:a.revoked_at instanceof Date?a.revoked_at.toISOString():new Date(a.revoked_at).toISOString()}}catch(i){throw await n.query("ROLLBACK"),i}finally{n.release()}}async function Xe(e){let t=v();if(!(await t.query(`SELECT id FROM analytics_identity_namespaces
|
|
89
|
+
WHERE id=$1 AND site_id=$2 AND status='approved'`,[e.namespaceId,e.siteId])).rows[0])throw new f("analytics_namespace_boundary","The identity namespace is not approved for this site.",403);let i=[...new Set(e.identityNodeIds??[])];if(i.length&&(await t.query(`SELECT id FROM analytics_identity_nodes
|
|
90
|
+
WHERE site_id=$1 AND namespace_id=$2 AND id = ANY($3::uuid[])`,[e.siteId,e.namespaceId,i])).rows.length!==i.length)throw new f("analytics_namespace_boundary","Identity evidence cannot cross namespaces.",403);if(e.personId&&!(await t.query("SELECT id FROM analytics_people WHERE id=$1 AND site_id=$2 AND namespace_id=$3",[e.personId,e.siteId,e.namespaceId])).rows[0])throw new f("analytics_namespace_boundary","The person does not belong to this namespace.",403);if(e.source){let r=Rn(e.source.kind,e.source.ref);if(!(await t.query(`SELECT id FROM analytics_identity_sources
|
|
91
|
+
WHERE namespace_id=$1 AND site_id=$2 AND source_kind=$3 AND source_ref=$4 AND status='approved'`,[e.namespaceId,e.siteId,Nn[e.source.kind],r])).rows[0])throw new f("analytics_namespace_source_unapproved","This identity source is not approved.",403)}}async function pa(e){return await Xe(e),(await v().query(`SELECT id, namespace_id, site_id, source_kind, source_ref, status, created_at, revoked_at
|
|
92
|
+
FROM analytics_identity_sources
|
|
93
|
+
WHERE namespace_id=$1 AND site_id=$2
|
|
94
|
+
ORDER BY source_kind, source_ref`,[e.namespaceId,e.siteId])).rows.map(n=>({id:n.id,namespaceId:n.namespace_id,siteId:n.site_id,sourceKind:Cn[n.source_kind],sourceRef:n.source_ref,status:n.status,createdAt:n.created_at.toISOString(),revokedAt:n.revoked_at?.toISOString()??null}))}function mr(){let e=process.env.ANALYTICS_IDENTITY_SECRET?.trim()||process.env.SESSION_SECRET?.trim();if(!e)throw new f("analytics_identity_secret_missing","Candidate identity derivation is not configured.",503);return e}function St(e,t){return _r("sha256",mr()).update(`xray-candidate:v1:${e}:${t}`).digest("hex")}async function Aa(e){await Xe(e);let t=Math.max(1,Math.min(200,Math.floor(e.limit??50)));return(await v().query(`SELECT a.id, a.model_version, a.first_observed_at, a.last_observed_at, a.expires_at,
|
|
95
|
+
array_remove(array_agg(DISTINCT e.feature_kind) FILTER (WHERE e.contribution > 0), NULL) supporting,
|
|
96
|
+
array_remove(array_agg(DISTINCT e.feature_kind) FILTER (WHERE e.contribution < 0), NULL) conflicting
|
|
97
|
+
FROM analytics_candidate_associations a
|
|
98
|
+
LEFT JOIN analytics_candidate_evidence e ON e.association_id=a.id AND e.namespace_id=a.namespace_id
|
|
99
|
+
WHERE a.site_id=$1 AND a.namespace_id=$2 AND a.status='active' AND a.confidence_band='medium'
|
|
100
|
+
AND a.expires_at > $3::timestamptz
|
|
101
|
+
GROUP BY a.id
|
|
102
|
+
ORDER BY a.updated_at DESC LIMIT $4`,[e.siteId,e.namespaceId,e.now??new Date().toISOString(),t])).rows.map(i=>({associationId:i.id,band:"medium",supportingFeatures:i.supporting??[],conflictingFeatures:i.conflicting??[],modelVersion:i.model_version,firstObservedAt:i.first_observed_at.toISOString(),lastObservedAt:i.last_observed_at.toISOString(),expiresAt:i.expires_at.toISOString()}))}async function wn(e){await Xe({siteId:e.siteId,namespaceId:e.namespaceId,personId:e.personId});let t=new Date(e.occurredAt??Date.now());if(!e.evidenceRef.trim())throw new f("analytics_candidate_promotion_evidence_required","Deterministic evidence is required.",400);let n=e.promotedBy??"system";if(n==="user"&&!e.promotedByUserId)throw new f("analytics_candidate_promotion_actor_required","User-confirmed promotion requires an actor.",400);let i=St("journey_pattern",`${e.evidenceKind}:${e.evidenceRef.trim().toLowerCase()}`),s=await v().connect();await s.query("BEGIN");try{let a=await s.query(`UPDATE analytics_candidate_associations
|
|
103
|
+
SET status='promoted', promoted_person_id=$4, promoted_at=$5, updated_at=now()
|
|
104
|
+
WHERE id=$1 AND site_id=$2 AND namespace_id=$3
|
|
105
|
+
AND (status IN ('active','expired') OR (status='promoted' AND promoted_person_id=$4))
|
|
106
|
+
RETURNING id,left_identity_node_id,right_identity_node_id`,[e.associationId,e.siteId,e.namespaceId,e.personId,t.toISOString()]);if(!a.rows[0])throw new f("analytics_candidate_not_found","Candidate association not found in this namespace.",404);return await s.query(`INSERT INTO analytics_candidate_promotions(
|
|
107
|
+
id, site_id, namespace_id, association_id, person_id, evidence_kind,
|
|
108
|
+
evidence_ref_hmac, promoted_by, promoted_by_user_id, promoted_at
|
|
109
|
+
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)
|
|
110
|
+
ON CONFLICT(association_id, person_id) DO NOTHING`,[It(),e.siteId,e.namespaceId,e.associationId,e.personId,e.evidenceKind,i,n,e.promotedByUserId??null,t.toISOString()]),await s.query(`INSERT INTO analytics_privacy_audits(
|
|
111
|
+
id, site_id, namespace_ref, subject_ref, action, idempotency_key,
|
|
112
|
+
actor_user_id, result_code, affected_counts, completed_at
|
|
113
|
+
) VALUES ($1,$2,$3,$4,'candidate_promoted',$5,$6,'promoted',$7::jsonb,$8)
|
|
114
|
+
ON CONFLICT(site_id, idempotency_key) DO NOTHING`,[It(),e.siteId,e.namespaceId,e.associationId,`candidate-promotion:${e.associationId}:${e.personId}`,e.promotedByUserId??null,JSON.stringify({candidateAssociations:1,deterministicPeople:1}),t.toISOString()]),await s.query(`INSERT INTO analytics_identity_promotion_receipts(
|
|
115
|
+
id,site_id,namespace_id,canonical_person_id,canonical_person_ref_hmac,
|
|
116
|
+
evidence_kind,evidence_ref_hmac,prior_person_ids,prior_identity_node_ids,
|
|
117
|
+
candidate_association_ids,affected_history_counts,schema_version,idempotency_key,promoted_at
|
|
118
|
+
) VALUES($1,$2,$3,$4,$5,$6,$7,'{}'::uuid[],$8::uuid[],$9::uuid[],$10::jsonb,
|
|
119
|
+
'xray.identity-promotion.v1',$11,$12)
|
|
120
|
+
ON CONFLICT(site_id,idempotency_key) DO NOTHING`,[It(),e.siteId,e.namespaceId,e.personId,St("journey_pattern",`person:${e.personId}`),e.evidenceKind,i,[a.rows[0].left_identity_node_id,a.rows[0].right_identity_node_id],[e.associationId],JSON.stringify({events:0,sessions:0,touches:0,conversions:0,candidateTouches:0}),St("journey_pattern",`promotion:${e.siteId}:${e.associationId}:${e.personId}:${e.evidenceKind}:${e.evidenceRef}`),t.toISOString()]),await s.query("COMMIT"),{associationId:e.associationId,personId:e.personId,promotedAt:t.toISOString(),reason:e.evidenceKind}}catch(a){throw await s.query("ROLLBACK"),a}finally{s.release()}}import{createHash as Sr}from"crypto";import{z as H}from"zod";import{createHash as pr,randomUUID as Z}from"crypto";function I(e){return e??v()}function Ge(e){return pr("sha256").update(JSON.stringify(e)).digest("hex")}async function ee(e){let t=await I(e.db).query("SELECT id::text AS id FROM analytics_connections WHERE id::text=$1 AND site_id=$2",[e.connectionId,e.siteId]);if(!t.rowCount)throw new f("analytics_crm_resource_not_found","CRM resource was not found.",404);return t.rows[0].id}async function Ca(e){let t=nn.parse(e.capability),n=I(e.db),i=await ee({siteId:e.siteId,connectionId:e.connectionId,db:n});await n.query(`INSERT INTO analytics_crm_connection_capabilities(
|
|
67
121
|
site_id,connection_id,provider,contract_version,mode,schema_fingerprint,allowed_operations,evidence,verified_at,updated_at
|
|
68
122
|
) VALUES($1,$2,$3,1,$4,$5,$6::jsonb,$7::jsonb,$8,now())
|
|
69
123
|
ON CONFLICT(connection_id,contract_version) DO UPDATE SET
|
|
70
124
|
mode=EXCLUDED.mode,schema_fingerprint=EXCLUDED.schema_fingerprint,
|
|
71
125
|
allowed_operations=EXCLUDED.allowed_operations,evidence=EXCLUDED.evidence,
|
|
72
|
-
verified_at=EXCLUDED.verified_at,updated_at=now()`,[e.siteId,i,t.provider,t.mode,t.evidence.tenantSchemaFingerprint,JSON.stringify([...new Set(e.allowedOperations)].sort()),JSON.stringify(t.evidence),t.evidence.evaluatedAt])}async function
|
|
126
|
+
verified_at=EXCLUDED.verified_at,updated_at=now()`,[e.siteId,i,t.provider,t.mode,t.evidence.tenantSchemaFingerprint,JSON.stringify([...new Set(e.allowedOperations)].sort()),JSON.stringify(t.evidence),t.evidence.evaluatedAt])}async function Ra(e){let t=je.parse(e.plan),n=I(e.db),i=await ee({siteId:e.siteId,connectionId:t.connectionId,db:n}),r=await n.query("SELECT plan,request_fingerprint FROM analytics_crm_provisioning_plans WHERE site_id=$1 AND public_plan_id=$2",[e.siteId,t.planId]);if(r.rowCount){if(r.rows[0].request_fingerprint!==t.requestFingerprint)throw new f("idempotency_conflict","This plan identifier was already used for different arguments.",409);return{plan:je.parse(r.rows[0].plan),reused:!0}}return await n.query(`INSERT INTO analytics_crm_provisioning_plans(
|
|
73
127
|
id,site_id,connection_id,provider,contract_version,tenant_schema_version,plan,status,
|
|
74
128
|
created_by_user_id,public_plan_id,schema_fingerprint,request_fingerprint,created_at,updated_at
|
|
75
|
-
) VALUES($1,$2,$3,$4,1,$5,$6::jsonb,'preview',$7,$8,$9,$10,$11,$11)`,[
|
|
129
|
+
) VALUES($1,$2,$3,$4,1,$5,$6::jsonb,'preview',$7,$8,$9,$10,$11,$11)`,[Z(),e.siteId,i,t.provider,t.schemaFingerprint,JSON.stringify(t),e.createdByUserId,t.planId,t.schemaFingerprint,t.requestFingerprint,t.createdAt]),{plan:t,reused:!1}}async function wa(e){let t=await I(e.db).query("SELECT plan FROM analytics_crm_provisioning_plans WHERE site_id=$1 AND public_plan_id=$2",[e.siteId,e.planId]);if(!t.rowCount)throw new f("analytics_crm_resource_not_found","CRM resource was not found.",404);return je.parse(t.rows[0].plan)}async function Oa(e){let t=qe.parse(e.receipt),n=I(e.db),i=await n.query("SELECT id::text AS id,request_fingerprint FROM analytics_crm_provisioning_plans WHERE site_id=$1 AND public_plan_id=$2",[e.siteId,t.planId]);if(!i.rowCount)throw new f("analytics_crm_resource_not_found","CRM resource was not found.",404);if(i.rows[0].request_fingerprint!==t.planFingerprint)throw new f("idempotency_conflict","The approved plan no longer matches the persisted plan.",409);let r=await n.query(`SELECT r.receipt FROM analytics_crm_provisioning_receipts r
|
|
76
130
|
JOIN analytics_crm_provisioning_plans p ON p.id=r.plan_id
|
|
77
131
|
WHERE p.site_id=$1 AND r.public_receipt_id=$2`,[e.siteId,t.receiptId]);if(r.rowCount&&qe.parse(r.rows[0].receipt).planFingerprint!==t.planFingerprint)throw new f("idempotency_conflict","This receipt identifier was already used for different arguments.",409);let s=t.status==="tested"?"test_write":"create_field",a=t.status==="failed"?"failed":"accepted";return await n.query(`INSERT INTO analytics_crm_provisioning_receipts(
|
|
78
132
|
id,plan_id,operation,status,safe_error_code,warnings,created_at,public_receipt_id,receipt
|
|
79
133
|
) VALUES($1,$2,$3,$4,$5,'[]'::jsonb,$6,$7,$8::jsonb)
|
|
80
134
|
ON CONFLICT (public_receipt_id) WHERE public_receipt_id IS NOT NULL
|
|
81
|
-
DO UPDATE SET operation=EXCLUDED.operation,status=EXCLUDED.status,safe_error_code=EXCLUDED.safe_error_code,receipt=EXCLUDED.receipt`,[
|
|
135
|
+
DO UPDATE SET operation=EXCLUDED.operation,status=EXCLUDED.status,safe_error_code=EXCLUDED.safe_error_code,receipt=EXCLUDED.receipt`,[Z(),i.rows[0].id,s,a,t.errorCode??null,t.occurredAt,t.receiptId,JSON.stringify(t)]),await n.query(`UPDATE analytics_crm_provisioning_plans
|
|
82
136
|
SET status=$3,approved_by_user_id=$4,approved_at=COALESCE(approved_at,$5),
|
|
83
137
|
approved_field_ids=$6::jsonb,updated_at=$5
|
|
84
|
-
WHERE site_id=$1 AND public_plan_id=$2`,[e.siteId,t.planId,t.status==="tested"?"verified":t.status==="failed"?"failed":"approved",e.approvedByUserId,t.occurredAt,JSON.stringify([...new Set(e.approvedFieldInternalNames)].sort())]),t}async function
|
|
138
|
+
WHERE site_id=$1 AND public_plan_id=$2`,[e.siteId,t.planId,t.status==="tested"?"verified":t.status==="failed"?"failed":"approved",e.approvedByUserId,t.occurredAt,JSON.stringify([...new Set(e.approvedFieldInternalNames)].sort())]),t}async function Da(e){let t=await I(e.db).query(`SELECT r.receipt FROM analytics_crm_provisioning_receipts r
|
|
85
139
|
JOIN analytics_crm_provisioning_plans p ON p.id=r.plan_id
|
|
86
|
-
WHERE p.site_id=$1 AND r.public_receipt_id=$2`,[e.siteId,e.receiptId]);if(!t.rowCount)throw new f("analytics_crm_resource_not_found","CRM resource was not found.",404);return qe.parse(t.rows[0].receipt)}var
|
|
140
|
+
WHERE p.site_id=$1 AND r.public_receipt_id=$2`,[e.siteId,e.receiptId]);if(!t.rowCount)throw new f("analytics_crm_resource_not_found","CRM resource was not found.",404);return qe.parse(t.rows[0].receipt)}var ht=e=>e instanceof Date?e.toISOString():e;function Nt(e){return rn.parse({syncId:e.sync_id,siteId:e.site_id,connectionId:e.connection_id,provider:e.provider,state:e.state,mode:e.mode,cursor:typeof e.cursor=="string"?e.cursor:e.cursor==null?null:JSON.stringify(e.cursor),lastStartedAt:ht(e.last_started_at),lastSuccessAt:ht(e.last_success_at),nextRunAt:ht(e.next_run_at),imported:Number(e.imported),deduplicated:Number(e.deduplicated),rejected:Number(e.rejected),lastErrorCode:e.last_error_code,requestFingerprint:e.request_fingerprint})}async function Ta(e){let t=I(e.db),n=await ee({siteId:e.siteId,connectionId:e.connectionId,db:t}),i=Ge({siteId:e.siteId,connectionId:n,provider:e.provider,mode:e.mode,mapping:e.mapping,mappingVersion:e.mappingVersion??1,capabilityEvidence:e.capabilityEvidence??{},backfillFrom:e.backfillFrom??null}),r=await t.query("SELECT * FROM analytics_crm_sync_runs WHERE site_id=$1 AND idempotency_key=$2",[e.siteId,e.idempotencyKey]);if(r.rowCount){if(r.rows[0].request_fingerprint!==i)throw new f("idempotency_conflict","This Idempotency-Key was already used for different CRM sync arguments.",409);return{reused:!0,status:Nt(r.rows[0])}}let s=await t.query(`INSERT INTO analytics_crm_sync_runs(
|
|
87
141
|
sync_id,site_id,connection_id,provider,mode,state,mapping,mapping_version,capability_evidence,idempotency_key,request_fingerprint,last_started_at
|
|
88
142
|
) VALUES($1,$2,$3,$4,$5,'queued',$6::jsonb,$7,$8::jsonb,$9,$10,now()) RETURNING *`,[e.syncId,e.siteId,n,e.provider,e.mode,JSON.stringify(e.mapping),e.mappingVersion??1,JSON.stringify(e.capabilityEvidence??{}),e.idempotencyKey,i]);return await t.query(`INSERT INTO analytics_crm_sync_state(connection_id,status,last_started_at,updated_at)
|
|
89
143
|
VALUES($1,'pending',now(),now()) ON CONFLICT(connection_id)
|
|
90
|
-
DO UPDATE SET status='pending',last_started_at=now(),updated_at=now()`,[n]),{reused:!1,status:
|
|
144
|
+
DO UPDATE SET status='pending',last_started_at=now(),updated_at=now()`,[n]),{reused:!1,status:Nt(s.rows[0])}}async function xa(e){let t=await I(e.db).query("SELECT * FROM analytics_crm_sync_runs WHERE site_id=$1 AND sync_id=$2",[e.siteId,e.syncId]);return t.rowCount?Nt(t.rows[0]):null}async function La(e){let t=I(e.db);return(await t.query(`UPDATE analytics_crm_sync_runs SET next_run_at=$4,interval_seconds=$5,updated_at=now()
|
|
91
145
|
WHERE site_id=$1 AND sync_id=$2 AND connection_id::text=$3 AND state <> 'cancelled'`,[e.siteId,e.syncId,e.connectionId,e.nextRunAt,e.intervalSeconds])).rowCount?(e.intervalSeconds===null?await t.query(`DELETE FROM analytics_reconciliation_jobs
|
|
92
146
|
WHERE connection_id::text=$1 AND status='pending'`,[e.connectionId]):await t.query(`INSERT INTO analytics_reconciliation_jobs(id,connection_id,status,next_attempt_at)
|
|
93
147
|
VALUES($1,$2,'pending',$3)
|
|
@@ -95,15 +149,15 @@ import{a as Zt,s as tn}from"./chunk-WDB5CTRY.js";import{T as cn,a as de,b as Ne,
|
|
|
95
149
|
status=CASE WHEN analytics_reconciliation_jobs.status='leased' THEN 'leased' ELSE 'pending' END,
|
|
96
150
|
next_attempt_at=CASE WHEN analytics_reconciliation_jobs.status='leased'
|
|
97
151
|
THEN analytics_reconciliation_jobs.next_attempt_at ELSE EXCLUDED.next_attempt_at END,
|
|
98
|
-
updated_at=now()`,[
|
|
99
|
-
WHERE site_id=$1 AND sync_id=$2 AND state <> 'cancelled' RETURNING connection_id::text AS connection_id`,[e.siteId,e.syncId]);return n.rowCount?(await t.query("UPDATE analytics_crm_sync_state SET status='disabled',next_sync_at=NULL,updated_at=now() WHERE connection_id::text=$1",[n.rows[0].connection_id]),!0):!1}async function
|
|
152
|
+
updated_at=now()`,[Z(),e.connectionId,e.nextRunAt]),!0):!1}async function $a(e){let t=I(e.db),n=await t.query(`UPDATE analytics_crm_sync_runs SET state='cancelled',next_run_at=NULL,updated_at=now()
|
|
153
|
+
WHERE site_id=$1 AND sync_id=$2 AND state <> 'cancelled' RETURNING connection_id::text AS connection_id`,[e.siteId,e.syncId]);return n.rowCount?(await t.query("UPDATE analytics_crm_sync_state SET status='disabled',next_sync_at=NULL,updated_at=now() WHERE connection_id::text=$1",[n.rows[0].connection_id]),!0):!1}async function ka(e){let t=I(e.db);await ee({siteId:e.siteId,connectionId:e.connectionId,db:t}),await t.query(`INSERT INTO analytics_crm_sync_state(connection_id,direction,status,last_started_at,updated_at)
|
|
100
154
|
VALUES($1,'inbound','running',now(),now())
|
|
101
155
|
ON CONFLICT(connection_id) DO UPDATE SET
|
|
102
156
|
direction=CASE WHEN analytics_crm_sync_state.direction='outbound' THEN 'bidirectional' ELSE analytics_crm_sync_state.direction END,
|
|
103
157
|
status='running',last_started_at=now(),last_error_code=NULL,updated_at=now()`,[e.connectionId]),await t.query(`UPDATE analytics_crm_sync_runs SET state='running',last_started_at=now(),last_error_code=NULL,updated_at=now()
|
|
104
158
|
WHERE sync_id=(SELECT sync_id FROM analytics_crm_sync_runs
|
|
105
159
|
WHERE site_id=$1 AND connection_id::text=$2 AND state NOT IN ('cancelled','webhook_only')
|
|
106
|
-
ORDER BY created_at DESC LIMIT 1)`,[e.siteId,e.connectionId])}async function
|
|
160
|
+
ORDER BY created_at DESC LIMIT 1)`,[e.siteId,e.connectionId])}async function Pa(e){let t=I(e.db);await ee({siteId:e.siteId,connectionId:e.connectionId,db:t}),await t.query(`INSERT INTO analytics_crm_sync_state(
|
|
107
161
|
connection_id,cursor,direction,status,last_started_at,last_completed_at,last_success_at,next_sync_at,updated_at
|
|
108
162
|
) VALUES($1,$2::jsonb,'inbound','healthy',now(),now(),now(),$3,now())
|
|
109
163
|
ON CONFLICT(connection_id) DO UPDATE SET
|
|
@@ -118,7 +172,7 @@ import{a as Zt,s as tn}from"./chunk-WDB5CTRY.js";import{T as cn,a as de,b as Ne,
|
|
|
118
172
|
WHERE site_id=$1 AND connection_id::text=$2 AND state <> 'cancelled'
|
|
119
173
|
ORDER BY created_at DESC LIMIT 1)`,[e.siteId,e.connectionId,e.hasMore?"queued":"completed",JSON.stringify(e.cursor??null),e.nextRunAt,e.imported,e.deduplicated,e.rejected]),await t.query(`INSERT INTO analytics_crm_sync_receipts(
|
|
120
174
|
id,site_id,connection_id,direction,operation,subject_ref,external_receipt_id,status,occurred_at
|
|
121
|
-
) VALUES($1,$2,$3,'inbound','historical_pull',NULL,$4,'accepted',now())`,[
|
|
175
|
+
) VALUES($1,$2,$3,'inbound','historical_pull',NULL,$4,'accepted',now())`,[Z(),e.siteId,e.connectionId,e.receiptFingerprint])}async function Ma(e){let t=I(e.db);await ee({siteId:e.siteId,connectionId:e.connectionId,db:t});let n=e.terminal?"failed":"retrying";await t.query(`INSERT INTO analytics_crm_sync_state(connection_id,direction,status,last_completed_at,last_error_code,next_sync_at,updated_at)
|
|
122
176
|
VALUES($1,'inbound','degraded',now(),$2,$3,now())
|
|
123
177
|
ON CONFLICT(connection_id) DO UPDATE SET
|
|
124
178
|
direction=CASE WHEN analytics_crm_sync_state.direction='outbound' THEN 'bidirectional' ELSE analytics_crm_sync_state.direction END,
|
|
@@ -129,7 +183,7 @@ import{a as Zt,s as tn}from"./chunk-WDB5CTRY.js";import{T as cn,a as de,b as Ne,
|
|
|
129
183
|
WHERE site_id=$1 AND connection_id::text=$2 AND state <> 'cancelled'
|
|
130
184
|
ORDER BY created_at DESC LIMIT 1)`,[e.siteId,e.connectionId,e.safeErrorCode,e.nextRunAt]),await t.query(`INSERT INTO analytics_crm_sync_receipts(
|
|
131
185
|
id,site_id,connection_id,direction,operation,subject_ref,external_receipt_id,status,safe_error_code,occurred_at
|
|
132
|
-
) VALUES($1,$2,$3,'inbound','historical_pull',NULL,$4,$5,$6,now())`,[
|
|
186
|
+
) VALUES($1,$2,$3,'inbound','historical_pull',NULL,$4,$5,$6,now())`,[Z(),e.siteId,e.connectionId,e.receiptFingerprint,n,e.safeErrorCode])}function On(e){return He.parse({policyId:e.policy_id,siteId:e.site_id,provider:e.provider,connectionId:e.connection_id,kind:e.kind,enabled:e.enabled,version:Number(e.version),provisioningReceiptId:e.provisioning_receipt_id,schemaFingerprint:e.schema_fingerprint,mappingVersion:e.mapping_version==null?null:Number(e.mapping_version),mapping:e.mapping??null,allowedFields:e.allowed_fields,selectedEvents:e.selected_events,purpose:e.purpose,updatedAt:e.updated_at instanceof Date?e.updated_at.toISOString():new Date(e.updated_at).toISOString()})}async function Ua(e){let t=He.parse(e.policy),n=I(e.db),i=await ee({siteId:t.siteId,connectionId:t.connectionId,db:n});if(t.enabled){let a=(await n.query(`SELECT r.receipt->>'status' receipt_status,p.status plan_status,p.schema_fingerprint plan_schema_fingerprint,
|
|
133
187
|
c.schema_fingerprint current_schema_fingerprint
|
|
134
188
|
FROM analytics_crm_provisioning_receipts r
|
|
135
189
|
JOIN analytics_crm_provisioning_plans p ON p.id=r.plan_id
|
|
@@ -147,14 +201,14 @@ import{a as Zt,s as tn}from"./chunk-WDB5CTRY.js";import{T as cn,a as de,b as Ne,
|
|
|
147
201
|
AND analytics_crm_outbound_policies.connection_id=EXCLUDED.connection_id
|
|
148
202
|
AND analytics_crm_outbound_policies.kind=EXCLUDED.kind
|
|
149
203
|
AND analytics_crm_outbound_policies.version=EXCLUDED.version
|
|
150
|
-
RETURNING *`,[t.policyId,t.siteId,i,t.provider,t.kind,t.enabled,t.version,t.provisioningReceiptId,t.schemaFingerprint,t.mappingVersion,JSON.stringify(t.mapping),JSON.stringify([...new Set(t.allowedFields)].sort()),JSON.stringify([...new Set(t.selectedEvents)].sort()),t.purpose,e.createdByUserId]);if(!r.rowCount)throw new f("idempotency_conflict","This policy identifier is already bound to another immutable policy version.",409);return
|
|
204
|
+
RETURNING *`,[t.policyId,t.siteId,i,t.provider,t.kind,t.enabled,t.version,t.provisioningReceiptId,t.schemaFingerprint,t.mappingVersion,JSON.stringify(t.mapping),JSON.stringify([...new Set(t.allowedFields)].sort()),JSON.stringify([...new Set(t.selectedEvents)].sort()),t.purpose,e.createdByUserId]);if(!r.rowCount)throw new f("idempotency_conflict","This policy identifier is already bound to another immutable policy version.",409);return On(r.rows[0])}async function Dn(e){return(await I(e.db).query("SELECT * FROM analytics_crm_outbound_policies WHERE site_id=$1 ORDER BY updated_at DESC,policy_id",[e.siteId])).rows.map(On)}function Ct(e){return sn.parse({jobId:e.job_id,policyId:e.policy_id,siteId:e.site_id,personId:e.person_id,kind:e.kind,state:e.state,policyVersion:Number(e.policy_version),mappingVersion:e.mapping_version==null?null:Number(e.mapping_version),attempts:Number(e.attempts),nextAttemptAt:e.next_attempt_at==null?null:e.next_attempt_at instanceof Date?e.next_attempt_at.toISOString():e.next_attempt_at,safeErrorCode:e.safe_error_code,requestFingerprint:e.request_fingerprint})}async function Tn(e){let t=He.parse(e.policy);if(!t.enabled)throw new f("analytics_crm_outbound_policy_disabled","The CRM outbound policy is disabled.",409);let n=I(e.db);if(!(await n.query(`SELECT p.id::text id FROM analytics_people p
|
|
151
205
|
WHERE p.id=$1 AND p.site_id=$2 AND
|
|
152
206
|
(p.crm_person_ref IS NOT NULL OR EXISTS (
|
|
153
207
|
SELECT 1 FROM analytics_identity_edges e JOIN analytics_identity_nodes n ON n.id=e.identity_node_id
|
|
154
|
-
WHERE e.person_id=p.id AND n.kind IN ('email','phone','customer_id') AND e.confidence >= 0.99))`,[e.personId,t.siteId])).rowCount)throw new f("analytics_crm_outbound_confirmed_person_required","CRM outbound delivery requires a confirmed person.",409);let r=await n.query("SELECT * FROM analytics_crm_outbound_jobs WHERE site_id=$1 AND idempotency_key=$2",[t.siteId,e.idempotencyKey]);if(r.rowCount){if(r.rows[0].request_fingerprint!==e.requestFingerprint)throw new f("idempotency_conflict","This idempotency key was already used for another CRM outbound request.",409);return{job:
|
|
208
|
+
WHERE e.person_id=p.id AND n.kind IN ('email','phone','customer_id') AND e.confidence >= 0.99))`,[e.personId,t.siteId])).rowCount)throw new f("analytics_crm_outbound_confirmed_person_required","CRM outbound delivery requires a confirmed person.",409);let r=await n.query("SELECT * FROM analytics_crm_outbound_jobs WHERE site_id=$1 AND idempotency_key=$2",[t.siteId,e.idempotencyKey]);if(r.rowCount){if(r.rows[0].request_fingerprint!==e.requestFingerprint)throw new f("idempotency_conflict","This idempotency key was already used for another CRM outbound request.",409);return{job:Ct(r.rows[0]),reused:!0}}let s=await n.query(`INSERT INTO analytics_crm_outbound_jobs(
|
|
155
209
|
job_id,policy_id,site_id,person_id,kind,policy_version,mapping_version,source_event_id,
|
|
156
210
|
payload_ciphertext,idempotency_key,request_fingerprint,next_attempt_at
|
|
157
|
-
) VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,now()) RETURNING *`,[e.jobId,t.policyId,t.siteId,e.personId,t.kind,t.version,t.mappingVersion,e.sourceEventId??null,e.payloadCiphertext,e.idempotencyKey,e.requestFingerprint]);return{job:
|
|
211
|
+
) VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,now()) RETURNING *`,[e.jobId,t.policyId,t.siteId,e.personId,t.kind,t.version,t.mappingVersion,e.sourceEventId??null,e.payloadCiphertext,e.idempotencyKey,e.requestFingerprint]);return{job:Ct(s.rows[0]),reused:!1}}async function xn(e){let t=await I(e.db).query(`WITH due AS (
|
|
158
212
|
SELECT j.job_id FROM analytics_crm_outbound_jobs j
|
|
159
213
|
JOIN analytics_crm_outbound_policies p ON p.policy_id=j.policy_id
|
|
160
214
|
WHERE p.enabled=true AND p.provider NOT IN ('salesforce','pipedrive')
|
|
@@ -169,7 +223,7 @@ import{a as Zt,s as tn}from"./chunk-WDB5CTRY.js";import{T as cn,a as de,b as Ne,
|
|
|
169
223
|
RETURNING j.*,p.provider,p.connection_id::text,p.mapping,p.allowed_fields,p.purpose,s.owner_user_id,
|
|
170
224
|
EXISTS(SELECT 1 FROM analytics_crm_connection_capabilities c
|
|
171
225
|
WHERE c.connection_id=p.connection_id AND c.contract_version=1 AND c.schema_fingerprint=p.schema_fingerprint) schema_current`,[Math.max(1,Math.min(50,e.limit)),e.leaseSeconds??90]);for(let n of t.rows)await I(e.db).query(`INSERT INTO analytics_crm_outbound_attempts(id,job_id,attempt,status,started_at)
|
|
172
|
-
VALUES($1,$2,$3,'started',now()) ON CONFLICT(job_id,attempt) DO NOTHING`,[
|
|
226
|
+
VALUES($1,$2,$3,'started',now()) ON CONFLICT(job_id,attempt) DO NOTHING`,[Z(),n.job_id,Number(n.attempts)]);return t.rows.map(n=>({job:Ct(n),provider:n.provider,connectionId:n.connection_id,ownerUserId:Number(n.owner_user_id),payloadCiphertext:n.payload_ciphertext,mapping:n.mapping,allowedFields:n.allowed_fields,purpose:n.purpose,schemaCurrent:n.schema_current}))}async function Ln(e){return(await I(e.db).query(`SELECT EXISTS(
|
|
173
227
|
SELECT 1
|
|
174
228
|
FROM analytics_crm_outbound_jobs j
|
|
175
229
|
JOIN analytics_crm_outbound_policies p ON p.policy_id=j.policy_id
|
|
@@ -212,81 +266,27 @@ import{a as Zt,s as tn}from"./chunk-WDB5CTRY.js";import{T as cn,a as de,b as Ne,
|
|
|
212
266
|
AND (($3='site_analytics' AND consent.site_analytics=true) OR
|
|
213
267
|
($3='advertising_measurement' AND consent.advertising_measurement=true))
|
|
214
268
|
))
|
|
215
|
-
) authorized`,[e.jobId,e.attempt,e.purpose,e.consentReceiptRef])).rows[0]?.authorized===!0}async function
|
|
269
|
+
) authorized`,[e.jobId,e.attempt,e.purpose,e.consentReceiptRef])).rows[0]?.authorized===!0}async function $n(e){let t=I(e.db);return(await t.query(`UPDATE analytics_crm_outbound_jobs
|
|
216
270
|
SET state='cancelled',payload_ciphertext='',next_attempt_at=NULL,
|
|
217
271
|
lease_expires_at=NULL,cancelled_at=now(),safe_error_code=$3,updated_at=now()
|
|
218
272
|
WHERE job_id=$1 AND state='running' AND attempts=$2`,[e.jobId,e.attempt,e.safeErrorCode.slice(0,80)])).rowCount?(await t.query(`UPDATE analytics_crm_outbound_attempts
|
|
219
273
|
SET status='cancelled',safe_error_code=$3,completed_at=now()
|
|
220
|
-
WHERE job_id=$1 AND attempt=$2 AND status='started'`,[e.jobId,e.attempt,e.safeErrorCode.slice(0,80)]),!0):!1}async function
|
|
274
|
+
WHERE job_id=$1 AND attempt=$2 AND status='started'`,[e.jobId,e.attempt,e.safeErrorCode.slice(0,80)]),!0):!1}async function Qe(e){let t=I(e.db),n=await t.query("SELECT attempts,max_attempts FROM analytics_crm_outbound_jobs WHERE job_id=$1",[e.jobId]);if(!n.rowCount)throw new f("analytics_crm_resource_not_found","CRM outbound job was not found.",404);let i=Number(n.rows[0].attempts),r=e.status!=="retrying"||i>=Number(n.rows[0].max_attempts),s=e.status==="accepted"?"delivered":e.status==="retrying"&&!r?"retrying":"dead_letter",a=`crmout_${Ge({jobId:e.jobId,attempt:i,status:e.status}).slice(0,24)}`;return await t.query(`UPDATE analytics_crm_outbound_jobs SET state=$2,safe_error_code=$3,next_attempt_at=$4,
|
|
221
275
|
lease_expires_at=NULL,delivered_at=CASE WHEN $2='delivered' THEN now() ELSE delivered_at END,updated_at=now()
|
|
222
276
|
WHERE job_id=$1 AND state='running'`,[e.jobId,s,e.safeErrorCode??null,s==="retrying"?e.retryAt??null:null]),await t.query(`INSERT INTO analytics_crm_outbound_attempts(id,job_id,attempt,status,safe_error_code,completed_at)
|
|
223
|
-
VALUES($1,$2,$3,$4,$5,now()) ON CONFLICT(job_id,attempt) DO UPDATE SET status=EXCLUDED.status,safe_error_code=EXCLUDED.safe_error_code,completed_at=now()`,[
|
|
224
|
-
VALUES($1,$2,$3,$4,$5) ON CONFLICT(receipt_id) DO NOTHING`,[a,e.jobId,s==="retrying"?"retrying":e.status,e.externalReceiptId??null,e.safeErrorCode??null]),pt.parse({receiptId:a,jobId:e.jobId,status:s==="retrying"?"retrying":e.status,...e.externalReceiptId?{externalReceiptId:e.externalReceiptId}:{},...e.safeErrorCode?{safeErrorCode:e.safeErrorCode}:{},occurredAt:new Date().toISOString()})}async function
|
|
277
|
+
VALUES($1,$2,$3,$4,$5,now()) ON CONFLICT(job_id,attempt) DO UPDATE SET status=EXCLUDED.status,safe_error_code=EXCLUDED.safe_error_code,completed_at=now()`,[Z(),e.jobId,i,e.status==="accepted"?"accepted":s==="retrying"?"retrying":"failed",e.safeErrorCode??null]),await t.query(`INSERT INTO analytics_crm_outbound_receipts(receipt_id,job_id,status,external_receipt_id,safe_error_code)
|
|
278
|
+
VALUES($1,$2,$3,$4,$5) ON CONFLICT(receipt_id) DO NOTHING`,[a,e.jobId,s==="retrying"?"retrying":e.status,e.externalReceiptId??null,e.safeErrorCode??null]),pt.parse({receiptId:a,jobId:e.jobId,status:s==="retrying"?"retrying":e.status,...e.externalReceiptId?{externalReceiptId:e.externalReceiptId}:{},...e.safeErrorCode?{safeErrorCode:e.safeErrorCode}:{},occurredAt:new Date().toISOString()})}async function Fa(e){return(await I(e.db).query(`SELECT r.* FROM analytics_crm_outbound_receipts r
|
|
225
279
|
JOIN analytics_crm_outbound_jobs j ON j.job_id=r.job_id
|
|
226
280
|
JOIN analytics_crm_outbound_policies p ON p.policy_id=j.policy_id
|
|
227
281
|
WHERE j.site_id=$1 AND p.provider NOT IN ('salesforce','pipedrive')
|
|
228
|
-
ORDER BY r.occurred_at DESC LIMIT $2`,[e.siteId,Math.max(1,Math.min(100,e.limit??25))])).rows.map(n=>pt.parse({receiptId:n.receipt_id,jobId:n.job_id,status:n.status,...n.external_receipt_id?{externalReceiptId:n.external_receipt_id}:{},...n.safe_error_code?{safeErrorCode:n.safe_error_code}:{},occurredAt:n.occurred_at instanceof Date?n.occurred_at.toISOString():n.occurred_at}))}import{createHash as hn}from"crypto";var Gi=["hubspot","salesforce","highlevel","zoho","pipedrive","keap"],fa=["hubspot","highlevel","zoho","keap"];var q=e=>{let t={describe:!1,discover_schema:!1,incremental_read:!1,webhook:!1,create_fields:!1,upsert_person:!1,append_activity:!1,test_write:!1,list_outcomes:!1,pipeline_write:!1};for(let n of e)t[n]=!0;return Object.freeze(t)},ue=["describe","discover_schema","incremental_read","webhook","upsert_person","append_activity","test_write","list_outcomes","pipeline_write"],Sn=Object.freeze({hubspot:{provider:"hubspot",displayName:"HubSpot",providerConfigKeys:["hubspot"],auth:"oauth2",operations:q([...ue,"create_fields"]),personObject:"contacts",pipelineObject:"deals",activityObject:"notes",supportedObjects:["contacts","companies","deals","notes"],metadataEndpoint:"/crm/v3/properties/{objectType}",customFieldEndpoint:"/crm/v3/properties/{objectType}",externalIdStrategy:"email or an approved unique contact property",writablePersonFields:["email","firstname","lastname","phone"],requiredPersonFields:[],stageField:"dealstage",valueField:"amount",associations:["contact_to_company","contact_to_deal","note_to_contact"],pagination:"cursor",batchLimit:100,rateLimitHeaders:["x-hubspot-ratelimit-remaining","retry-after"],caveats:["Effective properties and writable scopes are tenant-specific."],safeFallback:"note"},salesforce:{provider:"salesforce",displayName:"Salesforce",providerConfigKeys:["salesforce"],auth:"oauth2",operations:q([...ue,"create_fields"]),personObject:"Contact",pipelineObject:"Opportunity",activityObject:"Task",supportedObjects:["Lead","Contact","Account","Opportunity","Task"],metadataEndpoint:"/services/data/{version}/sobjects/{object}/describe",customFieldEndpoint:"Metadata API",externalIdStrategy:"approved External ID field",writablePersonFields:["Email","FirstName","LastName","Phone"],requiredPersonFields:["LastName"],stageField:"StageName",valueField:"Amount",associations:["Contact.AccountId","Opportunity.AccountId","OpportunityContactRole"],pagination:"cursor",batchLimit:200,rateLimitHeaders:["sforce-limit-info","retry-after"],caveats:["Required fields, validation rules, record types and field-level security are tenant-specific."],safeFallback:"activity"},highlevel:{provider:"highlevel",displayName:"HighLevel",providerConfigKeys:["highlevel","gohighlevel"],auth:"oauth2",operations:q([...ue,"create_fields"]),personObject:"contacts",pipelineObject:"opportunities",activityObject:"notes",supportedObjects:["contacts","companies","opportunities","notes"],metadataEndpoint:"/locations/{locationId}/customFields",customFieldEndpoint:"/locations/{locationId}/customFields",externalIdStrategy:"location-scoped email/phone or approved custom field",writablePersonFields:["email","firstName","lastName","phone"],requiredPersonFields:[],stageField:"pipelineStageId",valueField:"monetaryValue",associations:["contact.location","opportunity.contact"],pagination:"cursor",batchLimit:100,rateLimitHeaders:["x-ratelimit-remaining","retry-after"],caveats:["Location context and installed OAuth scopes determine effective access."],safeFallback:"note"},zoho:{provider:"zoho",displayName:"Zoho CRM",providerConfigKeys:["zoho","zoho-crm"],auth:"oauth2",operations:q([...ue,"create_fields"]),personObject:"Contacts",pipelineObject:"Deals",activityObject:"Notes",supportedObjects:["Leads","Contacts","Accounts","Deals","Notes"],metadataEndpoint:"/crm/v7/settings/fields",customFieldEndpoint:"/crm/v7/settings/fields",externalIdStrategy:"approved unique field and duplicate-check preference",writablePersonFields:["Email","First_Name","Last_Name","Phone"],requiredPersonFields:["Last_Name"],stageField:"Stage",valueField:"Amount",associations:["Contact_Name","Account_Name","Parent_Id"],pagination:"page",batchLimit:100,rateLimitHeaders:["x-api-credits-remaining","retry-after"],caveats:["Custom field APIs, editions and API credits vary by tenant."],safeFallback:"note"},pipedrive:{provider:"pipedrive",displayName:"Pipedrive",providerConfigKeys:["pipedrive"],auth:"oauth2",operations:q([...ue,"create_fields"]),personObject:"persons",pipelineObject:"deals",activityObject:"notes",supportedObjects:["persons","organizations","deals","activities","notes"],metadataEndpoint:"/v1/personFields",customFieldEndpoint:"/v1/personFields",externalIdStrategy:"approved namespaced custom field; custom-field keys are tenant-specific",writablePersonFields:["name","email","phone"],requiredPersonFields:["name"],stageField:"stage_id",valueField:"value",associations:["person_id","org_id","deal_id"],pagination:"cursor",batchLimit:100,rateLimitHeaders:["x-ratelimit-remaining","retry-after"],caveats:["Custom-field hashes must be discovered from the connected tenant."],safeFallback:"note"},keap:{provider:"keap",displayName:"Keap",providerConfigKeys:["keap","infusionsoft"],auth:"oauth2",operations:q([...ue,"create_fields"]),personObject:"contacts",pipelineObject:"opportunities",activityObject:"notes",supportedObjects:["contacts","companies","opportunities","notes","tags"],metadataEndpoint:"/v1/contactCustomFields",customFieldEndpoint:"/v1/contactCustomFields",externalIdStrategy:"email or approved custom field",writablePersonFields:["email_addresses","given_name","family_name","phone_numbers"],requiredPersonFields:[],stageField:"stage_id",valueField:"projected_revenue",associations:["contact.company","opportunity.contact"],pagination:"offset",batchLimit:100,rateLimitHeaders:["x-ratelimit-remaining","retry-after"],caveats:["Custom fields, opportunity access and throttling depend on account entitlements."],safeFallback:"note"},callrail:{provider:"callrail",displayName:"CallRail",providerConfigKeys:["callrail"],auth:"api_key",operations:q(["describe","incremental_read","webhook","list_outcomes"]),personObject:null,pipelineObject:null,activityObject:"calls",supportedObjects:["calls"],metadataEndpoint:null,customFieldEndpoint:null,externalIdStrategy:null,writablePersonFields:[],requiredPersonFields:[],stageField:null,valueField:null,associations:[],pagination:"cursor",batchLimit:100,rateLimitHeaders:["x-ratelimit-remaining","retry-after"],caveats:["Read and webhook call outcomes only; no CRM person or pipeline writes."],safeFallback:"webhook_only"},twilio:{provider:"twilio",displayName:"Twilio",providerConfigKeys:["twilio"],auth:"account_token",operations:q(["describe","incremental_read","webhook","list_outcomes"]),personObject:null,pipelineObject:null,activityObject:"calls",supportedObjects:["calls"],metadataEndpoint:null,customFieldEndpoint:null,externalIdStrategy:null,writablePersonFields:[],requiredPersonFields:[],stageField:null,valueField:null,associations:[],pagination:"page",batchLimit:100,rateLimitHeaders:["twilio-concurrent-requests","retry-after"],caveats:["Read and webhook call outcomes only; no CRM person or pipeline writes."],safeFallback:"webhook_only"},ctm:{provider:"ctm",displayName:"CallTrackingMetrics",providerConfigKeys:["ctm","calltrackingmetrics"],auth:"api_key",operations:q(["describe","webhook"]),personObject:null,pipelineObject:null,activityObject:"calls",supportedObjects:["calls"],metadataEndpoint:null,customFieldEndpoint:null,externalIdStrategy:null,writablePersonFields:[],requiredPersonFields:[],stageField:null,valueField:null,associations:[],pagination:"webhook_only",batchLimit:null,rateLimitHeaders:["retry-after"],caveats:["Webhook-only until incremental cursor semantics are documented and proven."],safeFallback:"webhook_only"}}),Qi=Object.freeze({gohighlevel:"highlevel",ghl:"highlevel","high-level":"highlevel",infusionsoft:"keap","zoho-crm":"zoho",calltrackingmetrics:"ctm","call-tracking-metrics":"ctm"});function ee(e){let t=e.trim().toLowerCase().replace(/[ _]+/g,"-"),n=Qi[t]??t;if(!(n in Sn))throw new Error("analytics_provider_unsupported");return n}function ze(e){return Sn[ee(e)]}function _e(e,t){let n=ze(e);if(!n.operations[t])throw new Error(`analytics_provider_operation_unsupported:${n.provider}:${t}`);return n}var $=Object.freeze({describe:"describe-xray-crm-capabilities",discoverSchema:"discover-xray-crm-schema",upsertPerson:"upsert-xray-person",appendActivity:"append-xray-journey-activity",testWrite:"test-xray-write",listOutcomes:"list-xray-outcomes",createFields:"create-xray-custom-fields",pipelineEvent:"apply-xray-pipeline-event"}),Zi=Object.freeze({hubspot:["crm.objects.deals.read"],salesforce:["api"],highlevel:["opportunities.readonly"],zoho:["ZohoCRM.modules.deals.READ"],pipedrive:["deals:read"],keap:["full"]});function va(e){let t=ee(e.provider);if(!Gi.includes(t))throw new Error("analytics_crm_provider_required");let n=t,i=ze(n),r=new Set(e.compiledActions??[]),s=new Set(e.advertisedTools??[]),a=new Set(e.grantedScopes??[]),o=e.documented===!0,c=e.normalizedAdapter===!0,d=$.listOutcomes,_=c&&r.has(d),u=s.has(d),y=Zi[n],E=y.every(w=>a.has(w)),b=new Set(e.tenantObjects??[]),V=!!(i.pipelineObject&&b.has(i.pipelineObject)),P=o&&_&&u&&E&&V,h=i.operations.webhook&&e.webhookAdvertised===!0,L=[...o?[]:["official_documentation"],...c?[]:["normalized_adapter"],...r.has(d)?[]:[`compiled_action:${d}`],...u?[]:[`connection_tool:${d}`],...y.filter(w=>!a.has(w)).map(w=>`scope:${w}`),...!V&&i.pipelineObject?[`tenant_object:${i.pipelineObject}`]:[]],m=P&&h?"incremental_and_webhook":P?"incremental_only":h?"webhook_only":e.manualBackfillAvailable===!0?"manual_backfill":"unavailable",K=(w,A)=>i.operations[w]&&r.has(A)&&s.has(A);return{provider:n,mode:m,canSyncNow:P,canSchedule:P,canProvisionFields:K("create_fields",$.createFields),canUpsertPerson:K("upsert_person",$.upsertPerson),canApplyPipelineEvent:K("pipeline_write",$.pipelineEvent),supportedObjects:e.tenantObjects?i.supportedObjects.filter(w=>b.has(w)):[],evidence:{documented:o,normalizedAdapter:c,advertisedTools:[...s].sort(),grantedScopes:[...a].sort(),missingRequirements:L,tenantSchemaFingerprint:e.tenantSchemaFingerprint??null,evaluatedAt:e.evaluatedAt??new Date().toISOString()},caveats:i.caveats}}var Nn=[{internalName:"xray_person_id",label:"X-Ray Person ID",type:"string",writable:!0},{internalName:"xray_journey_url",label:"X-Ray Detailed Journey",type:"url",writable:!0},{internalName:"xray_first_touch",label:"X-Ray First Touch",type:"string",writable:!0},{internalName:"xray_last_non_direct_touch",label:"X-Ray Last Non-Direct Touch",type:"string",writable:!0},{internalName:"xray_converting_touch",label:"X-Ray Converting Touch",type:"string",writable:!0},{internalName:"xray_landing_page",label:"X-Ray Landing Page",type:"url",writable:!0},{internalName:"xray_conversion_page",label:"X-Ray Conversion Page",type:"url",writable:!0},{internalName:"xray_session_count",label:"X-Ray Session Count",type:"number",writable:!0},{internalName:"xray_event_count",label:"X-Ray Event Count",type:"number",writable:!0},{internalName:"xray_consent_receipt_ref",label:"X-Ray Consent Receipt",type:"string",writable:!0}];function te(e,t){return`${e}_${hn("sha256").update(JSON.stringify(t)).digest("hex").slice(0,24)}`}function Nt(e){return hn("sha256").update(JSON.stringify(e)).digest("hex")}function er(e){return Nt([...e].sort((t,n)=>`${t.object}:${t.internalName}`.localeCompare(`${n.object}:${n.internalName}`)))}function N(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function C(e,t=300){return typeof e=="string"&&e.trim()?e.trim().slice(0,t):void 0}function tr(e){let t=C(e,80)?.toLowerCase()??"";return["number","integer","double","currency","decimal","percent","monetary"].some(n=>t.includes(n))?"number":["datetime","date","timestamp"].some(n=>t.includes(n))?"datetime":["bool","checkbox"].some(n=>t.includes(n))?"boolean":["url","uri"].some(n=>t.includes(n))?"url":"string"}function me(e){let t=C(e.object,100),n=C(e.internalName,160),i=C(e.label,160);return!t||!n||!i?null:{object:t,internalName:n,label:i,type:tr(e.type),writable:e.writable,...e.required?{required:!0}:{}}}function ht(e){return Array.isArray(e)?e.flatMap(t=>{if(!N(t))return[];let n=C(t.id,240),i=C(t.name??t.label,240);if(!n||!i)return[];let r=Array.isArray(t.stages)?t.stages.flatMap(s=>{if(!N(s))return[];let a=C(s.id,240),o=C(s.name??s.label,240);return a&&o?[{id:a,name:o}]:[]}):[];return[{id:n,name:i,stages:r}]}):[]}function nr(e,t){let n=N(t)?t:{},i=[],r=[],s=ht(n.pipelines),a=o=>{o&&i.push(o)};if(e==="hubspot"){for(let[o,c]of[["contacts","contacts"],["deals","deals"]])if(Array.isArray(n[o])){r.push(c);for(let d of n[o])N(d)&&a(me({object:c,internalName:d.name,label:d.label,type:d.type,writable:d.archived!==!0&&d.providerDefined!==!0}))}}else if(e==="salesforce"){if(Array.isArray(n.objects))for(let o of n.objects){if(!N(o))continue;let c=C(o.object,100);if(!c)continue;r.push(c);let d=N(o.permissions)?o.permissions:{};if(Array.isArray(o.fields))for(let _ of o.fields)N(_)&&a(me({object:c,internalName:_.name,label:_.label,type:_.type,writable:d.update===!0&&_.updateable===!0,required:_.required===!0}))}}else if(e==="highlevel"){if(Array.isArray(n.customFields)){for(let o of n.customFields)if(N(o)){let c=o.model==="opportunity"?"opportunities":"contacts";r.push(c),a(me({object:c,internalName:o.fieldKey??o.id,label:o.name,type:o.dataType,writable:!0}))}}}else if(e==="zoho"){let o=Array.isArray(n.moduleResults)?n.moduleResults:[n];for(let c of o){if(!N(c))continue;let d=C(c.module,100);if(d&&(r.push(d),Array.isArray(c.fields)))for(let _ of c.fields)N(_)&&a(me({object:d,internalName:_.apiName,label:_.label,type:_.dataType,writable:_.writable===!0,required:_.required===!0}))}}else if(e==="pipedrive"){if(Array.isArray(n.fields)){for(let c of n.fields)if(N(c)){let d=c.objectType==="deal"?"deals":"persons";r.push(d),a(me({object:d,internalName:c.code,label:c.name,type:c.fieldType,writable:c.writable===!0}))}}let o=Array.isArray(n.stages)?n.stages:[];s=ht(Array.isArray(n.pipelines)?n.pipelines.map(c=>N(c)?{...c,id:String(c.id??""),stages:o.filter(d=>N(d)&&String(d.pipelineId)===String(c.id)).map(d=>({...d,id:String(d.id??"")}))}:c):[])}else if(e==="keap"){if(r.push("contacts"),Array.isArray(n.contactFields))for(let c of n.contactFields)N(c)&&a(me({object:"contacts",internalName:c.fieldName??c.id,label:c.label,type:c.fieldType,writable:!0}));let o=Array.isArray(n.opportunityStages)?n.opportunityStages:[];o.length&&(s=[{id:"opportunities",name:"Opportunities",stages:ht([{id:"opportunities",name:"Opportunities",stages:o}])[0]?.stages??[]}])}return{fields:i,objects:[...new Set(r)],pipelines:s,...Array.isArray(n.allowedOperations)?{allowedOperations:n.allowedOperations.filter(o=>typeof o=="string")}:{}}}function ir(e){return!N(e)||!Array.isArray(e.fields)?[]:e.fields.flatMap(t=>{if(!N(t))return[];let n=C(t.object,100),i=C(t.internalName??t.internal_name,160);if(!n||!i)return[];let r=C(t.id??t.providerFieldId,240);return[{object:n,internalName:i,...r?{providerFieldId:r}:{}}]})}function Ye(e){let t=ee(e);if(t==="callrail"||t==="twilio"||t==="ctm")throw new Error("analytics_crm_provider_required");return t}function Ia(e){let t=Ye(e.provider),n=_e(t,"discover_schema");if(!n.personObject||!e.connectionId.trim())throw new Error("analytics_crm_provisioning_invalid_scope");let i={},r=[];for(let d of Nn){let _=e.fields.find(u=>u.object===n.personObject&&(u.internalName.toLowerCase()===d.internalName||u.label.toLowerCase()===d.label.toLowerCase()));_?_.writable&&(i[d.internalName]=_.internalName):r.push({...d,object:n.personObject})}let s=e.now??new Date().toISOString(),a=er(e.fields),o={provider:t,connectionId:e.connectionId,existingMappings:i,proposedFields:r,schemaFingerprint:a},c=Nt(o);return{planId:te("crmplan",o),provider:t,connectionId:e.connectionId,personObject:n.personObject,existingMappings:i,proposedFields:r,fallback:n.safeFallback==="activity"?"activity":"note",permissionRequirements:["schema:read",...r.length?["schema:write"]:[],"records:write"],schemaFingerprint:a,requestFingerprint:c,createdAt:s}}async function Sa(e){let t=_e(e.plan.provider,"create_fields"),n=te("crmplan",{provider:e.plan.provider,connectionId:e.plan.connectionId,existingMappings:e.plan.existingMappings,proposedFields:e.plan.proposedFields,schemaFingerprint:e.plan.schemaFingerprint}),i=Nt({provider:e.plan.provider,connectionId:e.plan.connectionId,existingMappings:e.plan.existingMappings,proposedFields:e.plan.proposedFields,schemaFingerprint:e.plan.schemaFingerprint});if(e.plan.planId!==n||e.plan.personObject!==t.personObject||e.plan.requestFingerprint!==i)throw new Error("analytics_crm_provisioning_plan_tampered");let r=new Set(Nn.map(c=>c.internalName));if(e.plan.proposedFields.some(c=>c.object!==t.personObject||!r.has(c.internalName)))throw new Error("analytics_crm_provisioning_plan_tampered");if(!Number.isInteger(e.approvedByUserId)||e.approvedByUserId<=0)throw new Error("analytics_crm_provisioning_approval_required");let s=new Set(e.approvedFieldInternalNames);if([...s].some(c=>!e.plan.proposedFields.some(d=>d.internalName===c)))throw new Error("analytics_crm_provisioning_field_not_in_plan");let a=e.plan.proposedFields.filter(c=>s.has(c.internalName)),o=te("crmreceipt",{planId:e.plan.planId,fields:[...s].sort()});try{let c=a.length?await e.dependencies.callAction({connectionId:e.plan.connectionId,action:$.createFields,payload:{provider:e.plan.provider,fields:a},idempotencyKey:o}):{fields:[]};return{receiptId:o,planId:e.plan.planId,provider:e.plan.provider,connectionId:e.plan.connectionId,status:"approved",createdFields:ir(c),existingMappings:e.plan.existingMappings,cleanup:{supported:!1,instructions:"Created empty X-Ray fields may be removed manually after confirming they are not populated."},planFingerprint:e.plan.requestFingerprint,occurredAt:e.now??new Date().toISOString()}}catch(c){return{receiptId:o,planId:e.plan.planId,provider:e.plan.provider,connectionId:e.plan.connectionId,status:"failed",createdFields:[],existingMappings:e.plan.existingMappings,cleanup:{supported:!1,instructions:"No cleanup was attempted."},planFingerprint:e.plan.requestFingerprint,occurredAt:e.now??new Date().toISOString(),errorCode:C(c instanceof Error?c.message:c,80)?.replace(/[^a-zA-Z0-9_:-]/g,"_")||"analytics_crm_provisioning_failed"}}}async function ha(e){if(e.receipt.status!=="approved")throw new Error("analytics_crm_provisioning_not_approved");_e(e.receipt.provider,"test_write");let t=te("crmtest",e.receipt.receiptId),n=await e.dependencies.callAction({connectionId:e.receipt.connectionId,action:$.testWrite,payload:{provider:e.receipt.provider,marker:"X-Ray reversible connection test",cleanupRequested:!0},idempotencyKey:t});if(!N(n)||n.ok!==!0)throw new Error("analytics_crm_test_write_not_verified");return{...e.receipt,status:"tested",occurredAt:e.now??new Date().toISOString()}}async function Cn(e){let t=Ye(e.provider);if(_e(t,"upsert_person"),e.person.identityTier!=="confirmed"||!e.person.deterministicIdentity.value.trim())throw new Error("analytics_crm_person_requires_confirmed_identity");let n=te("crmperson",{provider:t,connectionId:e.connectionId,personId:e.person.personId,identity:e.person.deterministicIdentity,consent:e.person.consentReceiptRef}),i={provider:t,identity:e.person.deterministicIdentity,contact:e.person.contact,journey:{personId:e.person.personId,firstTouch:e.person.firstTouch,lastNonDirectTouch:e.person.lastNonDirectTouch,convertingTouch:e.person.convertingTouch,landingPage:e.person.landingPage,conversionPage:e.person.conversionPage,sessionCount:e.person.sessionCount,eventCount:e.person.eventCount,campaignClickCoverage:e.person.campaignClickCoverage,consentReceiptRef:e.person.consentReceiptRef,detailedJourneyUrl:e.person.detailedJourneyUrl},mapping:e.mapping??{},createDeal:!1},r=await e.dependencies.callAction({connectionId:e.connectionId,action:$.upsertPerson,payload:i,idempotencyKey:n}),s=N(r)?C(r.recordRef??r.id,240):void 0;return{receiptId:n,provider:t,operation:"person_upsert",dealCreated:!1,...s?{providerRecordRef:s}:{}}}async function Rn(e){let t=Ye(e.provider);if(_e(t,"pipeline_write"),e.person.identityTier!=="confirmed")throw new Error("analytics_crm_pipeline_requires_confirmed_identity");if(e.event.eventName!==e.mapping.eventName)throw new Error("analytics_crm_pipeline_event_not_mapped");if(e.mapping.valueSource==="verified_revenue"&&(!Number.isSafeInteger(e.event.verifiedRevenueMinor)||e.event.verifiedRevenueMinor<0))throw new Error("analytics_crm_pipeline_verified_revenue_required");let n=te("crmpipeline",{provider:t,connectionId:e.connectionId,eventId:e.event.eventId,mappingVersion:e.mapping.version});return await e.dependencies.callAction({connectionId:e.connectionId,action:$.pipelineEvent,payload:{provider:t,command:"apply_pipeline_event",person:{personId:e.person.personId,identity:e.person.deterministicIdentity},event:e.event,mapping:e.mapping},idempotencyKey:n}),{receiptId:n,provider:t,operation:"pipeline_event",dealCreatedOrUpdated:!0}}async function Na(e){let t=Ye(e.provider),n=ze(t),i=t==="highlevel"?{locationId:e.discoveryContext?.locationId}:t==="zoho"?{module:e.discoveryContext?.zohoModules?.[0]??"Contacts"}:{};if(t==="highlevel"&&!C(i.locationId,128))throw new Error("analytics_crm_highlevel_location_required");let r=(y,E)=>e.dependencies.callAction({connectionId:e.connectionId,action:$.discoverSchema,payload:y,idempotencyKey:te("crmdiscover",{provider:t,connectionId:e.connectionId,actionPayload:y,suffix:E})}),s=await r(i);if(t==="zoho"){let y=[...new Set(e.discoveryContext?.zohoModules??["Contacts","Deals"])],E=[s];for(let b of y.slice(1))E.push(await r({module:b},b));s={moduleResults:E}}let a=nr(t,s),o=a.fields,c=Object.entries(n.operations).filter(([,y])=>y).map(([y])=>y),d=a.allowedOperations?new Set(a.allowedOperations.map(y=>C(y,80)).filter(y=>!!y)):null,_=d?c.filter(y=>d.has(y)):c,u=new Set(n.supportedObjects);return{provider:t,publicProfile:n,connectionId:e.connectionId,fields:o,objects:a.objects.length?a.objects.filter(y=>u.has(y)):n.supportedObjects,pipelines:a.pipelines,allowedOperations:_,discoveredAt:e.now??new Date().toISOString()}}var sr=25,ar=25e3,wn=H.object({person:_t,event:mt.nullable(),mapping:H.object({version:H.number().int().positive(),eventName:H.string(),pipelineId:H.string(),stageId:H.string(),operation:H.enum(["create_or_update_deal","update_stage"]),valueSource:H.enum(["none","verified_revenue"]),activationEligible:H.boolean()}).strict().nullable()}).strict();function or(e,t){return`${e}_${rr("sha256").update(JSON.stringify(t)).digest("hex").slice(0,24)}`}function Xe(e){return cn(e)??void 0}function cr(e,t,n){return e.purpose==="necessary_form_fulfillment"?t.eventKind==="form":e.purpose==="site_analytics"?n.siteAnalytics:n.advertisingMeasurement}function k(e,t){return e.has(t)}function dr(e){let t=new Set(e.policy.allowedFields),n=e.contact?{...k(t,"contact.email")&&e.contact.email?{email:e.contact.email}:{},...k(t,"contact.phone")&&e.contact.phone?{phone:e.contact.phone}:{},...k(t,"contact.firstName")&&e.contact.firstName?{firstName:e.contact.firstName}:{},...k(t,"contact.lastName")&&e.contact.lastName?{lastName:e.contact.lastName}:{}}:void 0,i=[e.event.sourceName??e.event.source,e.event.medium,e.event.campaign].filter(Boolean).map(a=>String(a).slice(0,240)).join(" / "),r=e.policy.purpose==="advertising_measurement"&&e.consent.advertisingMeasurement?G(e.event.clickIds):{},s=Object.entries(r).filter(([a])=>k(t,`clickIds.${a}`)).map(([a])=>a).sort().join(",");return _t.parse({identityTier:"confirmed",personId:e.personId,deterministicIdentity:{kind:"crm_id",value:e.crmPersonRef},...n&&Object.keys(n).length?{contact:n}:{},...k(t,"attribution.firstTouch")&&i?{firstTouch:i}:{},...k(t,"attribution.lastNonDirectTouch")&&i?{lastNonDirectTouch:i}:{},...k(t,"attribution.convertingTouch")&&i?{convertingTouch:i}:{},...k(t,"attribution.landingPage")&&Xe(e.event.canonicalUrl)?{landingPage:Xe(e.event.canonicalUrl)}:{},...k(t,"attribution.conversionPage")&&Xe(e.event.canonicalUrl)?{conversionPage:Xe(e.event.canonicalUrl)}:{},sessionCount:e.sessionCount,eventCount:e.eventCount,...s?{campaignClickCoverage:s}:{},consentReceiptRef:e.consent.receiptId??`purpose:${e.policy.purpose}`,detailedJourneyUrl:`https://mcpscraper.dev/dashboard/analytics/people/${encodeURIComponent(e.personId)}`})}async function On(e){let t=(await vn({siteId:e.siteId,db:e.db})).filter(s=>s.enabled&&cr(s,e.event,e.consent)).filter(s=>s.selectedEvents.length===0||s.selectedEvents.includes(e.event.eventName));if(!t.length)return{queued:0,reused:0};let n=await e.db.query(`SELECT p.crm_person_ref,
|
|
282
|
+
ORDER BY r.occurred_at DESC LIMIT $2`,[e.siteId,Math.max(1,Math.min(100,e.limit??25))])).rows.map(n=>pt.parse({receiptId:n.receipt_id,jobId:n.job_id,status:n.status,...n.external_receipt_id?{externalReceiptId:n.external_receipt_id}:{},...n.safe_error_code?{safeErrorCode:n.safe_error_code}:{},occurredAt:n.occurred_at instanceof Date?n.occurred_at.toISOString():n.occurred_at}))}import{createHash as Pn}from"crypto";var gr=["hubspot","salesforce","highlevel","zoho","pipedrive","keap"],ja=["hubspot","highlevel","zoho","keap"];var q=e=>{let t={describe:!1,discover_schema:!1,incremental_read:!1,webhook:!1,create_fields:!1,upsert_person:!1,append_activity:!1,test_write:!1,list_outcomes:!1,pipeline_write:!1};for(let n of e)t[n]=!0;return Object.freeze(t)},ue=["describe","discover_schema","incremental_read","webhook","upsert_person","append_activity","test_write","list_outcomes","pipeline_write"],kn=Object.freeze({hubspot:{provider:"hubspot",displayName:"HubSpot",providerConfigKeys:["hubspot"],auth:"oauth2",operations:q([...ue,"create_fields"]),personObject:"contacts",pipelineObject:"deals",activityObject:"notes",supportedObjects:["contacts","companies","deals","notes"],metadataEndpoint:"/crm/v3/properties/{objectType}",customFieldEndpoint:"/crm/v3/properties/{objectType}",externalIdStrategy:"email or an approved unique contact property",writablePersonFields:["email","firstname","lastname","phone"],requiredPersonFields:[],stageField:"dealstage",valueField:"amount",associations:["contact_to_company","contact_to_deal","note_to_contact"],pagination:"cursor",batchLimit:100,rateLimitHeaders:["x-hubspot-ratelimit-remaining","retry-after"],caveats:["Effective properties and writable scopes are tenant-specific."],safeFallback:"note"},salesforce:{provider:"salesforce",displayName:"Salesforce",providerConfigKeys:["salesforce"],auth:"oauth2",operations:q([...ue,"create_fields"]),personObject:"Contact",pipelineObject:"Opportunity",activityObject:"Task",supportedObjects:["Lead","Contact","Account","Opportunity","Task"],metadataEndpoint:"/services/data/{version}/sobjects/{object}/describe",customFieldEndpoint:"Metadata API",externalIdStrategy:"approved External ID field",writablePersonFields:["Email","FirstName","LastName","Phone"],requiredPersonFields:["LastName"],stageField:"StageName",valueField:"Amount",associations:["Contact.AccountId","Opportunity.AccountId","OpportunityContactRole"],pagination:"cursor",batchLimit:200,rateLimitHeaders:["sforce-limit-info","retry-after"],caveats:["Required fields, validation rules, record types and field-level security are tenant-specific."],safeFallback:"activity"},highlevel:{provider:"highlevel",displayName:"HighLevel",providerConfigKeys:["highlevel","gohighlevel"],auth:"oauth2",operations:q([...ue,"create_fields"]),personObject:"contacts",pipelineObject:"opportunities",activityObject:"notes",supportedObjects:["contacts","companies","opportunities","notes"],metadataEndpoint:"/locations/{locationId}/customFields",customFieldEndpoint:"/locations/{locationId}/customFields",externalIdStrategy:"location-scoped email/phone or approved custom field",writablePersonFields:["email","firstName","lastName","phone"],requiredPersonFields:[],stageField:"pipelineStageId",valueField:"monetaryValue",associations:["contact.location","opportunity.contact"],pagination:"cursor",batchLimit:100,rateLimitHeaders:["x-ratelimit-remaining","retry-after"],caveats:["Location context and installed OAuth scopes determine effective access."],safeFallback:"note"},zoho:{provider:"zoho",displayName:"Zoho CRM",providerConfigKeys:["zoho","zoho-crm"],auth:"oauth2",operations:q([...ue,"create_fields"]),personObject:"Contacts",pipelineObject:"Deals",activityObject:"Notes",supportedObjects:["Leads","Contacts","Accounts","Deals","Notes"],metadataEndpoint:"/crm/v7/settings/fields",customFieldEndpoint:"/crm/v7/settings/fields",externalIdStrategy:"approved unique field and duplicate-check preference",writablePersonFields:["Email","First_Name","Last_Name","Phone"],requiredPersonFields:["Last_Name"],stageField:"Stage",valueField:"Amount",associations:["Contact_Name","Account_Name","Parent_Id"],pagination:"page",batchLimit:100,rateLimitHeaders:["x-api-credits-remaining","retry-after"],caveats:["Custom field APIs, editions and API credits vary by tenant."],safeFallback:"note"},pipedrive:{provider:"pipedrive",displayName:"Pipedrive",providerConfigKeys:["pipedrive"],auth:"oauth2",operations:q([...ue,"create_fields"]),personObject:"persons",pipelineObject:"deals",activityObject:"notes",supportedObjects:["persons","organizations","deals","activities","notes"],metadataEndpoint:"/v1/personFields",customFieldEndpoint:"/v1/personFields",externalIdStrategy:"approved namespaced custom field; custom-field keys are tenant-specific",writablePersonFields:["name","email","phone"],requiredPersonFields:["name"],stageField:"stage_id",valueField:"value",associations:["person_id","org_id","deal_id"],pagination:"cursor",batchLimit:100,rateLimitHeaders:["x-ratelimit-remaining","retry-after"],caveats:["Custom-field hashes must be discovered from the connected tenant."],safeFallback:"note"},keap:{provider:"keap",displayName:"Keap",providerConfigKeys:["keap","infusionsoft"],auth:"oauth2",operations:q([...ue,"create_fields"]),personObject:"contacts",pipelineObject:"opportunities",activityObject:"notes",supportedObjects:["contacts","companies","opportunities","notes","tags"],metadataEndpoint:"/v1/contactCustomFields",customFieldEndpoint:"/v1/contactCustomFields",externalIdStrategy:"email or approved custom field",writablePersonFields:["email_addresses","given_name","family_name","phone_numbers"],requiredPersonFields:[],stageField:"stage_id",valueField:"projected_revenue",associations:["contact.company","opportunity.contact"],pagination:"offset",batchLimit:100,rateLimitHeaders:["x-ratelimit-remaining","retry-after"],caveats:["Custom fields, opportunity access and throttling depend on account entitlements."],safeFallback:"note"},callrail:{provider:"callrail",displayName:"CallRail",providerConfigKeys:["callrail"],auth:"api_key",operations:q(["describe","incremental_read","webhook","list_outcomes"]),personObject:null,pipelineObject:null,activityObject:"calls",supportedObjects:["calls"],metadataEndpoint:null,customFieldEndpoint:null,externalIdStrategy:null,writablePersonFields:[],requiredPersonFields:[],stageField:null,valueField:null,associations:[],pagination:"cursor",batchLimit:100,rateLimitHeaders:["x-ratelimit-remaining","retry-after"],caveats:["Read and webhook call outcomes only; no CRM person or pipeline writes."],safeFallback:"webhook_only"},twilio:{provider:"twilio",displayName:"Twilio",providerConfigKeys:["twilio"],auth:"account_token",operations:q(["describe","incremental_read","webhook","list_outcomes"]),personObject:null,pipelineObject:null,activityObject:"calls",supportedObjects:["calls"],metadataEndpoint:null,customFieldEndpoint:null,externalIdStrategy:null,writablePersonFields:[],requiredPersonFields:[],stageField:null,valueField:null,associations:[],pagination:"page",batchLimit:100,rateLimitHeaders:["twilio-concurrent-requests","retry-after"],caveats:["Read and webhook call outcomes only; no CRM person or pipeline writes."],safeFallback:"webhook_only"},ctm:{provider:"ctm",displayName:"CallTrackingMetrics",providerConfigKeys:["ctm","calltrackingmetrics"],auth:"api_key",operations:q(["describe","webhook"]),personObject:null,pipelineObject:null,activityObject:"calls",supportedObjects:["calls"],metadataEndpoint:null,customFieldEndpoint:null,externalIdStrategy:null,writablePersonFields:[],requiredPersonFields:[],stageField:null,valueField:null,associations:[],pagination:"webhook_only",batchLimit:null,rateLimitHeaders:["retry-after"],caveats:["Webhook-only until incremental cursor semantics are documented and proven."],safeFallback:"webhook_only"}}),fr=Object.freeze({gohighlevel:"highlevel",ghl:"highlevel","high-level":"highlevel",infusionsoft:"keap","zoho-crm":"zoho",calltrackingmetrics:"ctm","call-tracking-metrics":"ctm"});function te(e){let t=e.trim().toLowerCase().replace(/[ _]+/g,"-"),n=fr[t]??t;if(!(n in kn))throw new Error("analytics_provider_unsupported");return n}function Ze(e){return kn[te(e)]}function _e(e,t){let n=Ze(e);if(!n.operations[t])throw new Error(`analytics_provider_operation_unsupported:${n.provider}:${t}`);return n}var $=Object.freeze({describe:"describe-xray-crm-capabilities",discoverSchema:"discover-xray-crm-schema",upsertPerson:"upsert-xray-person",appendActivity:"append-xray-journey-activity",testWrite:"test-xray-write",listOutcomes:"list-xray-outcomes",createFields:"create-xray-custom-fields",pipelineEvent:"apply-xray-pipeline-event"}),vr=Object.freeze({hubspot:["crm.objects.deals.read"],salesforce:["api"],highlevel:["opportunities.readonly"],zoho:["ZohoCRM.modules.deals.READ"],pipedrive:["deals:read"],keap:["full"]});function qa(e){let t=te(e.provider);if(!gr.includes(t))throw new Error("analytics_crm_provider_required");let n=t,i=Ze(n),r=new Set(e.compiledActions??[]),s=new Set(e.advertisedTools??[]),a=new Set(e.grantedScopes??[]),o=e.documented===!0,c=e.normalizedAdapter===!0,d=$.listOutcomes,_=c&&r.has(d),u=s.has(d),y=vr[n],E=y.every(w=>a.has(w)),b=new Set(e.tenantObjects??[]),V=!!(i.pipelineObject&&b.has(i.pipelineObject)),P=o&&_&&u&&E&&V,h=i.operations.webhook&&e.webhookAdvertised===!0,L=[...o?[]:["official_documentation"],...c?[]:["normalized_adapter"],...r.has(d)?[]:[`compiled_action:${d}`],...u?[]:[`connection_tool:${d}`],...y.filter(w=>!a.has(w)).map(w=>`scope:${w}`),...!V&&i.pipelineObject?[`tenant_object:${i.pipelineObject}`]:[]],m=P&&h?"incremental_and_webhook":P?"incremental_only":h?"webhook_only":e.manualBackfillAvailable===!0?"manual_backfill":"unavailable",K=(w,A)=>i.operations[w]&&r.has(A)&&s.has(A);return{provider:n,mode:m,canSyncNow:P,canSchedule:P,canProvisionFields:K("create_fields",$.createFields),canUpsertPerson:K("upsert_person",$.upsertPerson),canApplyPipelineEvent:K("pipeline_write",$.pipelineEvent),supportedObjects:e.tenantObjects?i.supportedObjects.filter(w=>b.has(w)):[],evidence:{documented:o,normalizedAdapter:c,advertisedTools:[...s].sort(),grantedScopes:[...a].sort(),missingRequirements:L,tenantSchemaFingerprint:e.tenantSchemaFingerprint??null,evaluatedAt:e.evaluatedAt??new Date().toISOString()},caveats:i.caveats}}var Mn=[{internalName:"xray_person_id",label:"X-Ray Person ID",type:"string",writable:!0},{internalName:"xray_journey_url",label:"X-Ray Detailed Journey",type:"url",writable:!0},{internalName:"xray_first_touch",label:"X-Ray First Touch",type:"string",writable:!0},{internalName:"xray_last_non_direct_touch",label:"X-Ray Last Non-Direct Touch",type:"string",writable:!0},{internalName:"xray_converting_touch",label:"X-Ray Converting Touch",type:"string",writable:!0},{internalName:"xray_landing_page",label:"X-Ray Landing Page",type:"url",writable:!0},{internalName:"xray_conversion_page",label:"X-Ray Conversion Page",type:"url",writable:!0},{internalName:"xray_session_count",label:"X-Ray Session Count",type:"number",writable:!0},{internalName:"xray_event_count",label:"X-Ray Event Count",type:"number",writable:!0},{internalName:"xray_consent_receipt_ref",label:"X-Ray Consent Receipt",type:"string",writable:!0}];function ne(e,t){return`${e}_${Pn("sha256").update(JSON.stringify(t)).digest("hex").slice(0,24)}`}function wt(e){return Pn("sha256").update(JSON.stringify(e)).digest("hex")}function Er(e){return wt([...e].sort((t,n)=>`${t.object}:${t.internalName}`.localeCompare(`${n.object}:${n.internalName}`)))}function N(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function R(e,t=300){return typeof e=="string"&&e.trim()?e.trim().slice(0,t):void 0}function br(e){let t=R(e,80)?.toLowerCase()??"";return["number","integer","double","currency","decimal","percent","monetary"].some(n=>t.includes(n))?"number":["datetime","date","timestamp"].some(n=>t.includes(n))?"datetime":["bool","checkbox"].some(n=>t.includes(n))?"boolean":["url","uri"].some(n=>t.includes(n))?"url":"string"}function me(e){let t=R(e.object,100),n=R(e.internalName,160),i=R(e.label,160);return!t||!n||!i?null:{object:t,internalName:n,label:i,type:br(e.type),writable:e.writable,...e.required?{required:!0}:{}}}function Rt(e){return Array.isArray(e)?e.flatMap(t=>{if(!N(t))return[];let n=R(t.id,240),i=R(t.name??t.label,240);if(!n||!i)return[];let r=Array.isArray(t.stages)?t.stages.flatMap(s=>{if(!N(s))return[];let a=R(s.id,240),o=R(s.name??s.label,240);return a&&o?[{id:a,name:o}]:[]}):[];return[{id:n,name:i,stages:r}]}):[]}function Ar(e,t){let n=N(t)?t:{},i=[],r=[],s=Rt(n.pipelines),a=o=>{o&&i.push(o)};if(e==="hubspot"){for(let[o,c]of[["contacts","contacts"],["deals","deals"]])if(Array.isArray(n[o])){r.push(c);for(let d of n[o])N(d)&&a(me({object:c,internalName:d.name,label:d.label,type:d.type,writable:d.archived!==!0&&d.providerDefined!==!0}))}}else if(e==="salesforce"){if(Array.isArray(n.objects))for(let o of n.objects){if(!N(o))continue;let c=R(o.object,100);if(!c)continue;r.push(c);let d=N(o.permissions)?o.permissions:{};if(Array.isArray(o.fields))for(let _ of o.fields)N(_)&&a(me({object:c,internalName:_.name,label:_.label,type:_.type,writable:d.update===!0&&_.updateable===!0,required:_.required===!0}))}}else if(e==="highlevel"){if(Array.isArray(n.customFields)){for(let o of n.customFields)if(N(o)){let c=o.model==="opportunity"?"opportunities":"contacts";r.push(c),a(me({object:c,internalName:o.fieldKey??o.id,label:o.name,type:o.dataType,writable:!0}))}}}else if(e==="zoho"){let o=Array.isArray(n.moduleResults)?n.moduleResults:[n];for(let c of o){if(!N(c))continue;let d=R(c.module,100);if(d&&(r.push(d),Array.isArray(c.fields)))for(let _ of c.fields)N(_)&&a(me({object:d,internalName:_.apiName,label:_.label,type:_.dataType,writable:_.writable===!0,required:_.required===!0}))}}else if(e==="pipedrive"){if(Array.isArray(n.fields)){for(let c of n.fields)if(N(c)){let d=c.objectType==="deal"?"deals":"persons";r.push(d),a(me({object:d,internalName:c.code,label:c.name,type:c.fieldType,writable:c.writable===!0}))}}let o=Array.isArray(n.stages)?n.stages:[];s=Rt(Array.isArray(n.pipelines)?n.pipelines.map(c=>N(c)?{...c,id:String(c.id??""),stages:o.filter(d=>N(d)&&String(d.pipelineId)===String(c.id)).map(d=>({...d,id:String(d.id??"")}))}:c):[])}else if(e==="keap"){if(r.push("contacts"),Array.isArray(n.contactFields))for(let c of n.contactFields)N(c)&&a(me({object:"contacts",internalName:c.fieldName??c.id,label:c.label,type:c.fieldType,writable:!0}));let o=Array.isArray(n.opportunityStages)?n.opportunityStages:[];o.length&&(s=[{id:"opportunities",name:"Opportunities",stages:Rt([{id:"opportunities",name:"Opportunities",stages:o}])[0]?.stages??[]}])}return{fields:i,objects:[...new Set(r)],pipelines:s,...Array.isArray(n.allowedOperations)?{allowedOperations:n.allowedOperations.filter(o=>typeof o=="string")}:{}}}function Ir(e){return!N(e)||!Array.isArray(e.fields)?[]:e.fields.flatMap(t=>{if(!N(t))return[];let n=R(t.object,100),i=R(t.internalName??t.internal_name,160);if(!n||!i)return[];let r=R(t.id??t.providerFieldId,240);return[{object:n,internalName:i,...r?{providerFieldId:r}:{}}]})}function et(e){let t=te(e);if(t==="callrail"||t==="twilio"||t==="ctm")throw new Error("analytics_crm_provider_required");return t}function Va(e){let t=et(e.provider),n=_e(t,"discover_schema");if(!n.personObject||!e.connectionId.trim())throw new Error("analytics_crm_provisioning_invalid_scope");let i={},r=[];for(let d of Mn){let _=e.fields.find(u=>u.object===n.personObject&&(u.internalName.toLowerCase()===d.internalName||u.label.toLowerCase()===d.label.toLowerCase()));_?_.writable&&(i[d.internalName]=_.internalName):r.push({...d,object:n.personObject})}let s=e.now??new Date().toISOString(),a=Er(e.fields),o={provider:t,connectionId:e.connectionId,existingMappings:i,proposedFields:r,schemaFingerprint:a},c=wt(o);return{planId:ne("crmplan",o),provider:t,connectionId:e.connectionId,personObject:n.personObject,existingMappings:i,proposedFields:r,fallback:n.safeFallback==="activity"?"activity":"note",permissionRequirements:["schema:read",...r.length?["schema:write"]:[],"records:write"],schemaFingerprint:a,requestFingerprint:c,createdAt:s}}async function Ka(e){let t=_e(e.plan.provider,"create_fields"),n=ne("crmplan",{provider:e.plan.provider,connectionId:e.plan.connectionId,existingMappings:e.plan.existingMappings,proposedFields:e.plan.proposedFields,schemaFingerprint:e.plan.schemaFingerprint}),i=wt({provider:e.plan.provider,connectionId:e.plan.connectionId,existingMappings:e.plan.existingMappings,proposedFields:e.plan.proposedFields,schemaFingerprint:e.plan.schemaFingerprint});if(e.plan.planId!==n||e.plan.personObject!==t.personObject||e.plan.requestFingerprint!==i)throw new Error("analytics_crm_provisioning_plan_tampered");let r=new Set(Mn.map(c=>c.internalName));if(e.plan.proposedFields.some(c=>c.object!==t.personObject||!r.has(c.internalName)))throw new Error("analytics_crm_provisioning_plan_tampered");if(!Number.isInteger(e.approvedByUserId)||e.approvedByUserId<=0)throw new Error("analytics_crm_provisioning_approval_required");let s=new Set(e.approvedFieldInternalNames);if([...s].some(c=>!e.plan.proposedFields.some(d=>d.internalName===c)))throw new Error("analytics_crm_provisioning_field_not_in_plan");let a=e.plan.proposedFields.filter(c=>s.has(c.internalName)),o=ne("crmreceipt",{planId:e.plan.planId,fields:[...s].sort()});try{let c=a.length?await e.dependencies.callAction({connectionId:e.plan.connectionId,action:$.createFields,payload:{provider:e.plan.provider,fields:a},idempotencyKey:o}):{fields:[]};return{receiptId:o,planId:e.plan.planId,provider:e.plan.provider,connectionId:e.plan.connectionId,status:"approved",createdFields:Ir(c),existingMappings:e.plan.existingMappings,cleanup:{supported:!1,instructions:"Created empty X-Ray fields may be removed manually after confirming they are not populated."},planFingerprint:e.plan.requestFingerprint,occurredAt:e.now??new Date().toISOString()}}catch(c){return{receiptId:o,planId:e.plan.planId,provider:e.plan.provider,connectionId:e.plan.connectionId,status:"failed",createdFields:[],existingMappings:e.plan.existingMappings,cleanup:{supported:!1,instructions:"No cleanup was attempted."},planFingerprint:e.plan.requestFingerprint,occurredAt:e.now??new Date().toISOString(),errorCode:R(c instanceof Error?c.message:c,80)?.replace(/[^a-zA-Z0-9_:-]/g,"_")||"analytics_crm_provisioning_failed"}}}async function Ja(e){if(e.receipt.status!=="approved")throw new Error("analytics_crm_provisioning_not_approved");_e(e.receipt.provider,"test_write");let t=ne("crmtest",e.receipt.receiptId),n=await e.dependencies.callAction({connectionId:e.receipt.connectionId,action:$.testWrite,payload:{provider:e.receipt.provider,marker:"X-Ray reversible connection test",cleanupRequested:!0},idempotencyKey:t});if(!N(n)||n.ok!==!0)throw new Error("analytics_crm_test_write_not_verified");return{...e.receipt,status:"tested",occurredAt:e.now??new Date().toISOString()}}async function Un(e){let t=et(e.provider);if(_e(t,"upsert_person"),e.person.identityTier!=="confirmed"||!e.person.deterministicIdentity.value.trim())throw new Error("analytics_crm_person_requires_confirmed_identity");let n=ne("crmperson",{provider:t,connectionId:e.connectionId,personId:e.person.personId,identity:e.person.deterministicIdentity,consent:e.person.consentReceiptRef}),i={provider:t,identity:e.person.deterministicIdentity,contact:e.person.contact,journey:{personId:e.person.personId,firstTouch:e.person.firstTouch,lastNonDirectTouch:e.person.lastNonDirectTouch,convertingTouch:e.person.convertingTouch,landingPage:e.person.landingPage,conversionPage:e.person.conversionPage,sessionCount:e.person.sessionCount,eventCount:e.person.eventCount,campaignClickCoverage:e.person.campaignClickCoverage,consentReceiptRef:e.person.consentReceiptRef,detailedJourneyUrl:e.person.detailedJourneyUrl},mapping:e.mapping??{},createDeal:!1},r=await e.dependencies.callAction({connectionId:e.connectionId,action:$.upsertPerson,payload:i,idempotencyKey:n}),s=N(r)?R(r.recordRef??r.id,240):void 0;return{receiptId:n,provider:t,operation:"person_upsert",dealCreated:!1,...s?{providerRecordRef:s}:{}}}async function Fn(e){let t=et(e.provider);if(_e(t,"pipeline_write"),e.person.identityTier!=="confirmed")throw new Error("analytics_crm_pipeline_requires_confirmed_identity");if(e.event.eventName!==e.mapping.eventName)throw new Error("analytics_crm_pipeline_event_not_mapped");if(e.mapping.valueSource==="verified_revenue"&&(!Number.isSafeInteger(e.event.verifiedRevenueMinor)||e.event.verifiedRevenueMinor<0))throw new Error("analytics_crm_pipeline_verified_revenue_required");let n=ne("crmpipeline",{provider:t,connectionId:e.connectionId,eventId:e.event.eventId,mappingVersion:e.mapping.version});return await e.dependencies.callAction({connectionId:e.connectionId,action:$.pipelineEvent,payload:{provider:t,command:"apply_pipeline_event",person:{personId:e.person.personId,identity:e.person.deterministicIdentity},event:e.event,mapping:e.mapping},idempotencyKey:n}),{receiptId:n,provider:t,operation:"pipeline_event",dealCreatedOrUpdated:!0}}async function za(e){let t=et(e.provider),n=Ze(t),i=t==="highlevel"?{locationId:e.discoveryContext?.locationId}:t==="zoho"?{module:e.discoveryContext?.zohoModules?.[0]??"Contacts"}:{};if(t==="highlevel"&&!R(i.locationId,128))throw new Error("analytics_crm_highlevel_location_required");let r=(y,E)=>e.dependencies.callAction({connectionId:e.connectionId,action:$.discoverSchema,payload:y,idempotencyKey:ne("crmdiscover",{provider:t,connectionId:e.connectionId,actionPayload:y,suffix:E})}),s=await r(i);if(t==="zoho"){let y=[...new Set(e.discoveryContext?.zohoModules??["Contacts","Deals"])],E=[s];for(let b of y.slice(1))E.push(await r({module:b},b));s={moduleResults:E}}let a=Ar(t,s),o=a.fields,c=Object.entries(n.operations).filter(([,y])=>y).map(([y])=>y),d=a.allowedOperations?new Set(a.allowedOperations.map(y=>R(y,80)).filter(y=>!!y)):null,_=d?c.filter(y=>d.has(y)):c,u=new Set(n.supportedObjects);return{provider:t,publicProfile:n,connectionId:e.connectionId,fields:o,objects:a.objects.length?a.objects.filter(y=>u.has(y)):n.supportedObjects,pipelines:a.pipelines,allowedOperations:_,discoveredAt:e.now??new Date().toISOString()}}var hr=25,Nr=25e3,jn=H.object({person:_t,event:mt.nullable(),mapping:H.object({version:H.number().int().positive(),eventName:H.string(),pipelineId:H.string(),stageId:H.string(),operation:H.enum(["create_or_update_deal","update_stage"]),valueSource:H.enum(["none","verified_revenue"]),activationEligible:H.boolean()}).strict().nullable()}).strict();function Cr(e,t){return`${e}_${Sr("sha256").update(JSON.stringify(t)).digest("hex").slice(0,24)}`}function tt(e){return cn(e)??void 0}function Rr(e,t,n){return e.purpose==="necessary_form_fulfillment"?t.eventKind==="form":e.purpose==="site_analytics"?n.siteAnalytics:n.advertisingMeasurement}function k(e,t){return e.has(t)}function wr(e){let t=new Set(e.policy.allowedFields),n=e.contact?{...k(t,"contact.email")&&e.contact.email?{email:e.contact.email}:{},...k(t,"contact.phone")&&e.contact.phone?{phone:e.contact.phone}:{},...k(t,"contact.firstName")&&e.contact.firstName?{firstName:e.contact.firstName}:{},...k(t,"contact.lastName")&&e.contact.lastName?{lastName:e.contact.lastName}:{}}:void 0,i=[e.event.sourceName??e.event.source,e.event.medium,e.event.campaign].filter(Boolean).map(a=>String(a).slice(0,240)).join(" / "),r=e.policy.purpose==="advertising_measurement"&&e.consent.advertisingMeasurement?G(e.event.clickIds):{},s=Object.entries(r).filter(([a])=>k(t,`clickIds.${a}`)).map(([a])=>a).sort().join(",");return _t.parse({identityTier:"confirmed",personId:e.personId,deterministicIdentity:{kind:"crm_id",value:e.crmPersonRef},...n&&Object.keys(n).length?{contact:n}:{},...k(t,"attribution.firstTouch")&&i?{firstTouch:i}:{},...k(t,"attribution.lastNonDirectTouch")&&i?{lastNonDirectTouch:i}:{},...k(t,"attribution.convertingTouch")&&i?{convertingTouch:i}:{},...k(t,"attribution.landingPage")&&tt(e.event.canonicalUrl)?{landingPage:tt(e.event.canonicalUrl)}:{},...k(t,"attribution.conversionPage")&&tt(e.event.canonicalUrl)?{conversionPage:tt(e.event.canonicalUrl)}:{},sessionCount:e.sessionCount,eventCount:e.eventCount,...s?{campaignClickCoverage:s}:{},consentReceiptRef:e.consent.receiptId??`purpose:${e.policy.purpose}`,detailedJourneyUrl:`https://mcpscraper.dev/dashboard/analytics/people/${encodeURIComponent(e.personId)}`})}async function qn(e){let t=(await Dn({siteId:e.siteId,db:e.db})).filter(s=>s.enabled&&Rr(s,e.event,e.consent)).filter(s=>s.selectedEvents.length===0||s.selectedEvents.includes(e.event.eventName));if(!t.length)return{queued:0,reused:0};let n=await e.db.query(`SELECT p.crm_person_ref,
|
|
229
283
|
(SELECT count(DISTINCT s.id)::text FROM analytics_sessions s WHERE s.site_id=p.site_id AND s.person_id=p.id) session_count,
|
|
230
284
|
((SELECT count(*) FROM analytics_events e WHERE e.site_id=p.site_id AND e.person_id=p.id)+
|
|
231
285
|
(SELECT count(*) FROM analytics_external_events x WHERE x.site_id=p.site_id AND x.person_id=p.id))::text event_count
|
|
232
|
-
FROM analytics_people p WHERE p.site_id=$1 AND p.id=$2`,[e.siteId,e.personId]);if(!n.rows[0])return{queued:0,reused:0};let i=0,r=0;for(let s of t){if(s.kind==="pipeline_event"&&(!s.mapping||s.mapping.eventName!==e.event.eventName))continue;let a=
|
|
233
|
-
VALUES('default',now(),NULL,now()) ON CONFLICT(worker_key) DO UPDATE SET last_started_at=now(),last_error_code=NULL,updated_at=now()`);try{let s=await
|
|
286
|
+
FROM analytics_people p WHERE p.site_id=$1 AND p.id=$2`,[e.siteId,e.personId]);if(!n.rows[0])return{queued:0,reused:0};let i=0,r=0;for(let s of t){if(s.kind==="pipeline_event"&&(!s.mapping||s.mapping.eventName!==e.event.eventName))continue;let a=wr({personId:e.personId,crmPersonRef:n.rows[0].crm_person_ref,event:e.event,policy:s,consent:e.consent,sessionCount:Number(n.rows[0].session_count),eventCount:Number(n.rows[0].event_count)}),o=s.kind==="pipeline_event"?mt.parse({eventId:e.canonicalEventId,eventName:e.event.eventName,occurredAt:e.event.occurredAt,...s.mapping?.valueSource==="verified_revenue"&&e.event.valueMinor!==void 0?{verifiedRevenueMinor:e.event.valueMinor,currency:e.event.currency?.toUpperCase()}:{}}):null;if(s.kind==="pipeline_event"&&s.mapping?.valueSource==="verified_revenue"&&o?.verifiedRevenueMinor===void 0)continue;let c=jn.parse({person:a,event:o,mapping:s.mapping}),d=`${s.policyId}:${s.version}:${e.canonicalEventId}`,_=Ge({policy:s,personId:e.personId,event:o,projection:a});(await Tn({jobId:Cr("crmjob",{idempotencyKey:d}),policy:s,personId:e.personId,sourceEventId:e.canonicalEventId,payloadCiphertext:de(JSON.stringify(c),"crm-outbound-payload"),idempotencyKey:d,requestFingerprint:_,db:e.db})).reused?r+=1:i+=1}return{queued:i,reused:r}}var nt={async resolveIdentity(e){return(await Ht(e))?.email??null},async callAction(e){return tn({identity:e.identity,connectionId:e.connectionId,tool:e.action,input:e.payload,classification:"action",operationKind:"sync",requestId:e.idempotencyKey})},authorizeSend:e=>Ln(e),cancelUnauthorized:e=>$n(e)};function Or(e){let t=e instanceof Error?e.message:String(e);return/schema_changed|mapping_version|not_mapped|verified_revenue|required|disabled/i.test(t)?{code:"crm_outbound_contract_invalid",retryable:!1,rateLimited:!1}:/429|rate.?limit|too many/i.test(t)?{code:"crm_outbound_rate_limited",retryable:!0,rateLimited:!0}:/401|403|scope|permission|unauthorized|forbidden/i.test(t)?{code:"crm_outbound_authorization_required",retryable:!1,rateLimited:!1}:{code:"crm_outbound_provider_unavailable",retryable:!0,rateLimited:!1}}async function so(e=10,t=Date.now()+Nr,n=nt){let i=Math.max(1,Math.min(hr,e)),r=v();await r.query(`INSERT INTO analytics_crm_outbound_worker_state(worker_key,last_started_at,last_error_code,updated_at)
|
|
287
|
+
VALUES('default',now(),NULL,now()) ON CONFLICT(worker_key) DO UPDATE SET last_started_at=now(),last_error_code=NULL,updated_at=now()`);try{let s=await xn({limit:i,leaseSeconds:90}),a=0,o=0,c=0;for(let u of s){if(Date.now()>=t){await Qe({jobId:u.job.jobId,status:"retrying",safeErrorCode:"crm_outbound_tick_deadline",retryAt:new Date(Date.now()+6e4).toISOString()}),o+=1;continue}try{if(!u.schemaCurrent)throw new Error("analytics_crm_schema_changed");let y=Ne(u.payloadCiphertext,"crm-outbound-payload");if(!y)throw new Error("crm_outbound_payload_unreadable");let E=jn.parse(JSON.parse(y)),b=await n.resolveIdentity(u.ownerUserId);if(!b)throw new Error("crm_outbound_owner_identity_unavailable");if(!await(n.authorizeSend??(n===nt?nt.authorizeSend:async()=>!0))({jobId:u.job.jobId,attempt:u.job.attempts,purpose:u.purpose,consentReceiptRef:E.person.consentReceiptRef})){await(n.cancelUnauthorized??nt.cancelUnauthorized)({jobId:u.job.jobId,attempt:u.job.attempts,safeErrorCode:"crm_outbound_privacy_fence_closed"}),c+=1;continue}let h={callAction:({connectionId:m,action:K,payload:w,idempotencyKey:A})=>n.callAction({identity:b,connectionId:m,action:K,payload:w,idempotencyKey:A})},L=u.job.kind==="person_summary"?await Un({provider:u.provider,connectionId:u.connectionId,person:E.person,dependencies:h}):await Fn({provider:u.provider,connectionId:u.connectionId,person:E.person,event:E.event,mapping:E.mapping,dependencies:h});await Qe({jobId:u.job.jobId,status:"accepted",externalReceiptId:L.receiptId}),a+=1}catch(y){let E=Or(y),b=new Date(Date.now()+(E.rateLimited?30*6e4:Math.min(360*6e4,3e4*2**u.job.attempts))).toISOString();await Qe({jobId:u.job.jobId,status:E.retryable?"retrying":"failed",safeErrorCode:E.code,...E.retryable?{retryAt:b}:{}}),E.retryable&&u.job.attempts<5?o+=1:c+=1}}let d=await r.query("SELECT count(*)::int due FROM analytics_crm_outbound_jobs WHERE state IN ('pending','retrying') AND next_attempt_at <= now()"),_={claimed:s.length,delivered:a,retried:o,deadLettered:c,remainingDue:Number(d.rows[0]?.due??0)};return await r.query(`UPDATE analytics_crm_outbound_worker_state SET last_completed_at=now(),last_success_at=now(),
|
|
234
288
|
last_error_code=NULL,last_claimed=$1,last_remaining_due=$2,updated_at=now() WHERE worker_key='default'`,[_.claimed,_.remainingDue]),_}catch(s){throw await r.query(`UPDATE analytics_crm_outbound_worker_state SET last_completed_at=now(),last_error_code='crm_outbound_worker_failed',updated_at=now()
|
|
235
|
-
WHERE worker_key='default'`).catch(()=>{}),s}}function Dn(e,t){return e&&(t??0)>0}import{createHash as ko,createHmac as kt,generateKeyPairSync as Qr,randomUUID as se,sign as Zr}from"crypto";import es from"twilio";import{z as l}from"zod";import{z as R}from"zod";var ur=new Set(["AED","AFN","ALL","AMD","ANG","AOA","ARS","AUD","AWG","AZN","BAM","BBD","BDT","BGN","BHD","BIF","BMD","BND","BOB","BOV","BRL","BSD","BTN","BWP","BYN","BZD","CAD","CDF","CHE","CHF","CHW","CLF","CLP","CNY","COP","COU","CRC","CUP","CVE","CZK","DJF","DKK","DOP","DZD","EGP","ERN","ETB","EUR","FJD","FKP","GBP","GEL","GHS","GIP","GMD","GNF","GTQ","GYD","HKD","HNL","HTG","HUF","IDR","ILS","INR","IQD","IRR","ISK","JMD","JOD","JPY","KES","KGS","KHR","KMF","KPW","KRW","KWD","KYD","KZT","LAK","LBP","LKR","LRD","LSL","LYD","MAD","MDL","MGA","MKD","MMK","MNT","MOP","MRU","MUR","MVR","MWK","MXN","MXV","MYR","MZN","NAD","NGN","NIO","NOK","NPR","NZD","OMR","PAB","PEN","PGK","PHP","PKR","PLN","PYG","QAR","RON","RSD","RUB","RWF","SAR","SBD","SCR","SDG","SEK","SGD","SHP","SLE","SOS","SRD","SSP","STN","SVC","SYP","SZL","THB","TJS","TMT","TND","TOP","TRY","TTD","TWD","TZS","UAH","UGX","USD","USN","UYI","UYU","UYW","UZS","VED","VES","VND","VUV","WST","XAF","XAG","XAU","XBA","XBB","XBC","XBD","XCD","XDR","XOF","XPD","XPF","XPT","XSU","XTS","XUA","XXX","YER","ZAR","ZMW","ZWG"]),we=R.string().trim().length(3).transform(e=>e.toUpperCase()).refine(e=>ur.has(e),"Provide a valid ISO-4217 currency."),Qe=R.number().int("Money must be represented as integer minor units.").min(0,"Money cannot be negative.").max(Number.MAX_SAFE_INTEGER),Ze=R.string().trim().min(1).max(80).default("1"),_r=R.enum(["semantic_markup","source_event","crm_field","transaction"]),mr=R.object({mode:R.literal("none"),ruleVersion:Ze}).strict(),pr=R.object({mode:R.literal("fixed"),valueMinor:Qe,currency:we,ruleVersion:Ze}).strict(),yr=R.object({mode:R.literal("dynamic"),source:_r,currency:we,fallbackValueMinor:Qe.nullable().default(null),ruleVersion:Ze}).strict(),Tn=R.discriminatedUnion("mode",[mr,pr,yr]),gr=R.object({linkedEventName:R.string().trim().regex(/^[a-z][a-z0-9_]{1,79}$/),relation:R.enum(["before","after","either"]),intervalSeconds:R.number().int().min(0).max(10*365*24*60*60).nullable(),overrideValueMinor:Qe,currency:we}).strict(),et=R.object({targetEventName:R.string().trim().regex(/^[a-z][a-z0-9_]{1,79}$/),base:Tn,conditionalOverride:gr.nullable().default(null),ruleVersion:Ze}).strict().superRefine((e,t)=>{let n=e.conditionalOverride;n&&e.base.mode!=="none"&&n.currency!==e.base.currency&&t.addIssue({code:"custom",path:["conditionalOverride","currency"],message:"Conditional override currency must match the base value currency."})});function ne(e,t){let n=Date.parse(e);if(!Number.isFinite(n))throw new Error(`${t} must be an ISO timestamp.`);return n}function fr(e,t){let n=Qe.parse(e.valueMinor),i=xn(t,e.currency);return ne(e.observedAt,"observedAt"),{valueMinor:n,currency:i,observedAt:new Date(e.observedAt).toISOString()}}function vr(e){return Tn.parse(e)}function xn(e,t){let i=(Array.isArray(e)?e:[e,t]).filter(r=>typeof r=="string"&&r.trim().length>0).map(r=>we.parse(r));if(i.length===0)throw new Error("A currency is required for a monetary value.");if(new Set(i).size!==1)throw new Error(`Mixed currencies are not compatible: ${[...new Set(i)].join(", ")}.`);return i[0]}function Er(e,t={}){let n=vr(e);if(n.mode==="none")return{valueMinor:null,currency:null,provenance:{mode:"none",source:null,observedAt:null,fallback:!1,ruleVersion:n.ruleVersion,resolution:"absent",baseValueMinor:null,resolvedValueMinor:null,currency:null,linkedEvent:null}};if(n.mode==="fixed"){let s=t.observedAt?new Date(ne(t.observedAt,"observedAt")).toISOString():null;return{valueMinor:n.valueMinor,currency:n.currency,provenance:{mode:"fixed",source:null,observedAt:s,fallback:!1,ruleVersion:n.ruleVersion,resolution:"fixed",baseValueMinor:n.valueMinor,resolvedValueMinor:n.valueMinor,currency:n.currency,linkedEvent:null}}}let i=t.observations?.[n.source];if(i){let s=fr(i,n.currency);return{valueMinor:s.valueMinor,currency:s.currency,provenance:{mode:"dynamic",source:n.source,observedAt:s.observedAt,fallback:!1,ruleVersion:n.ruleVersion,resolution:"dynamic",baseValueMinor:s.valueMinor,resolvedValueMinor:s.valueMinor,currency:s.currency,linkedEvent:null}}}let r=n.fallbackValueMinor;return{valueMinor:r,currency:r===null?null:n.currency,provenance:{mode:"dynamic",source:n.source,observedAt:null,fallback:r!==null,ruleVersion:n.ruleVersion,resolution:r===null?"absent":"fallback",baseValueMinor:r,resolvedValueMinor:r,currency:r===null?null:n.currency,linkedEvent:null}}}function br(e,t,n){let i=ne(t.occurredAt,"targetEvent.occurredAt"),r=n.filter(s=>{if(s.eventName!==e.linkedEventName)return!1;let o=(ne(s.occurredAt,"linkedEvent.occurredAt")-i)/1e3;return e.relation==="before"&&o>0||e.relation==="after"&&o<0?!1:e.intervalSeconds===null||Math.abs(o)<=e.intervalSeconds});return r.sort((s,a)=>{let o=Math.abs(ne(s.occurredAt,"linkedEvent.occurredAt")-i),c=Math.abs(ne(a.occurredAt,"linkedEvent.occurredAt")-i);return o-c||s.eventId.localeCompare(a.eventId)}),r[0]??null}function Ln(e,t){let n=et.parse(e);if(t.targetEvent.eventName!==n.targetEventName)throw new Error(`Value rule targets ${n.targetEventName}, not ${t.targetEvent.eventName}.`);let i=Er({...n.base,ruleVersion:n.ruleVersion},t);if(!n.conditionalOverride)return i;let r=br(n.conditionalOverride,t.targetEvent,t.linkedEvents);if(!r)return i;if(t.providerOperation==="restatement"&&r.identityTier!=="confirmed")throw new Error("Candidate-only evidence cannot be used for a provider restatement.");let s=i.currency?xn(i.currency,n.conditionalOverride.currency):we.parse(n.conditionalOverride.currency);return{valueMinor:n.conditionalOverride.overrideValueMinor,currency:s,provenance:{...i.provenance,ruleVersion:n.ruleVersion,resolution:"conditional_override",resolvedValueMinor:n.conditionalOverride.overrideValueMinor,currency:s,linkedEvent:{eventId:r.eventId,eventName:r.eventName,occurredAt:new Date(ne(r.occurredAt,"linkedEvent.occurredAt")).toISOString(),identityTier:r.identityTier??"candidate"}}}}var $n=l.object({gclid:l.string().trim().max(500).optional(),gbraid:l.string().trim().max(500).optional(),wbraid:l.string().trim().max(500).optional(),fbclid:l.string().trim().max(500).optional(),fbp:l.string().trim().max(500).optional(),fbc:l.string().trim().max(500).optional(),ttclid:l.string().trim().max(500).optional(),rdt_cid:l.string().trim().max(500).optional(),msclkid:l.string().trim().max(500).optional(),li_fat_id:l.string().trim().max(500).optional(),snapclid:l.string().trim().max(500).optional()}).strict(),Ar=l.object({site_analytics:l.enum(["granted","denied","unknown"]),enhanced_matching:l.enum(["granted","denied","unknown"]),advertising_measurement:l.enum(["granted","denied","unknown"])}).strict(),Ir=l.object({analytics:l.enum(["granted","denied","unknown"]).default("unknown"),adUserData:l.enum(["granted","denied","unknown"]).default("unknown"),adPersonalization:l.enum(["granted","denied","unknown"]).default("unknown"),globalPrivacyControl:l.boolean().default(!1),occurredAt:l.string().datetime(),receiptId:l.string().uuid().optional(),policyVersion:l.number().int().positive().optional(),purposes:Ar.optional()}).strict().transform(e=>({...e,purposes:e.purposes??{site_analytics:e.analytics,enhanced_matching:e.adUserData,advertising_measurement:e.adPersonalization}})),kn=l.enum(["browser","form","call","crm","transaction","custom_server","conversion","delivery"]),Sr=l.object({schemaVersion:l.literal(1).default(1),eventId:l.string().trim().min(8).max(200),sourceEventId:l.string().trim().min(1).max(300).optional(),source:l.string().trim().min(1).max(80).default("xray"),sourceAccountRef:l.string().trim().max(240).optional(),eventKind:kn,eventName:l.string().trim().regex(/^[a-z][a-z0-9_]{1,79}$/),occurredAt:l.string().datetime(),visitorId:l.string().trim().max(160).optional(),sessionId:l.string().trim().max(160).optional(),personRef:l.string().trim().max(240).optional(),orderId:l.string().trim().max(240).optional(),callId:l.string().trim().max(240).optional(),dealId:l.string().trim().max(240).optional(),path:l.string().trim().max(2e3).optional(),canonicalUrl:l.string().url().max(3e3).optional(),sourceName:l.string().trim().max(180).optional(),medium:l.string().trim().max(180).optional(),campaign:l.string().trim().max(240).optional(),clickIds:$n.optional(),consent:Ir.optional(),valueMinor:l.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),currency:l.string().trim().regex(/^[A-Za-z]{3}$/).optional(),properties:l.record(l.string().max(80),l.unknown()).default({})}).strict(),tt=Sr.extend({sourceEventId:l.string().trim().min(1).max(300),source:l.string().trim().min(1).max(80),sourceAccountRef:l.string().trim().min(1).max(240)}),Wa=l.object({sourceEventId:l.string().trim().min(1).max(300),source:l.string().trim().min(1).max(80),sourceAccountRef:l.string().trim().min(1).max(240),contactId:l.string().trim().max(240).optional(),dealId:l.string().trim().min(1).max(240),pipelineId:l.string().trim().max(240).optional(),stageId:l.string().trim().min(1).max(240),stageName:l.string().trim().max(240).optional(),occurredAt:l.string().datetime(),valueMinor:l.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),currency:l.string().trim().regex(/^[A-Za-z]{3}$/).optional(),visitorId:l.string().trim().max(160).optional(),sessionId:l.string().trim().max(160).optional(),clickIds:$n.optional(),email:l.string().email().max(320).optional(),phone:l.string().trim().max(80).optional(),orderId:l.string().trim().max(240).optional(),status:l.enum(["open","won","lost","spam","unknown"]).default("unknown")}).strict(),hr=l.object({eventName:l.string().regex(/^[a-z][a-z0-9_]{1,79}$/).optional(),eventKind:kn.optional(),source:l.string().trim().max(80).optional(),stageId:l.string().trim().max(240).optional(),status:l.enum(["open","won","lost","spam","unknown"]).optional(),answered:l.boolean().optional(),minimumCallDurationSeconds:l.number().int().min(0).max(86400).optional(),excludedTags:l.array(l.string().trim().max(80)).max(30).default([]),requiredTags:l.array(l.string().trim().max(80)).max(30).default([])}).strict(),Pn=l.object({name:l.string().trim().min(1).max(120),conversionKind:l.string().trim().regex(/^[a-z][a-z0-9_]{1,79}$/),condition:hr,defaultValueMinor:l.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),defaultCurrency:l.string().trim().regex(/^[A-Za-z]{3}$/).default("USD"),valueRule:et.optional(),enabled:l.boolean().default(!0)}).strict();function Nr(e){let t=e.trim().toLowerCase().replace(/\.$/,"");if(!t||t.includes("://")||/[/?#]/.test(t))return"";try{let n=new URL(`https://${t}`).hostname.toLowerCase().replace(/\.$/,"");return!n||n.length>253||n!=="localhost"&&!n.includes(".")||n.split(".").some(i=>!i||i.length>63||!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(i))?"":n}catch{return""}}var Cr=l.string().trim().max(253).transform(Nr).refine(Boolean,"Provide a valid bare hostname."),Oe=l.string().trim().max(80).regex(/^[A-Za-z][A-Za-z0-9_-]{0,79}$/).refine(e=>(e.match(/\d/g)?.length??0)<7,"Semantic dimensions cannot contain phone-like values.").transform(e=>e.toLowerCase()),Rr=l.object({value:l.number().finite().min(0).max(1e12).optional(),currency:l.string().trim().regex(/^[A-Za-z]{3}$/).transform(e=>e.toUpperCase()).optional(),orderId:l.string().trim().min(1).max(200).optional(),contentId:l.string().trim().min(1).max(200).optional(),contentType:l.string().trim().min(1).max(80).optional(),placement:Oe.optional(),component:Oe.optional(),action:Oe.optional(),destination:Oe.optional(),plan:Oe.optional()}).strict(),Ct=l.object({name:l.string().trim().min(1).max(120),eventName:l.string().trim().regex(/^[a-z][a-z0-9_]{1,79}$/),triggerKind:l.enum(["page","click","form","semantic"]),path:l.string().trim().startsWith("/").max(2e3).optional(),pathMatch:l.enum(["exact","prefix"]).default("exact"),selector:l.string().trim().min(1).max(500).optional(),hostnames:l.array(Cr).max(25).transform(e=>[...new Set(e)]).default([]),metadata:Rr.optional(),valueRule:et.optional(),oncePerSession:l.boolean().default(!1),enabled:l.boolean().default(!0)}).strict().superRefine((e,t)=>{e.triggerKind==="page"&&!e.path&&t.addIssue({code:"custom",path:["path"],message:"Page triggers require a path."}),["click","form"].includes(e.triggerKind)&&!e.selector&&t.addIssue({code:"custom",path:["selector"],message:"Click and form triggers require a selector."}),e.triggerKind==="semantic"&&(e.path||e.selector)&&t.addIssue({code:"custom",path:["triggerKind"],message:"Semantic triggers use data-xray-event and cannot include a path or selector."})});import{createHmac as wr,randomUUID as Rt}from"crypto";import eo from"ipaddr.js";import{randomUUID as Mn}from"crypto";async function Ja(e){return(await v().query(`SELECT id, site_id, name, status, version, approved_at
|
|
236
|
-
FROM analytics_identity_namespaces
|
|
237
|
-
WHERE site_id=$1 AND status IN ('draft','approved','disabled')
|
|
238
|
-
ORDER BY CASE status WHEN 'approved' THEN 0 WHEN 'draft' THEN 1 ELSE 2 END,
|
|
239
|
-
version DESC, name, id`,[e.siteId])).rows.map(n=>({id:n.id,siteId:n.site_id,name:n.name,status:n.status,version:n.version,approvedAt:n.approved_at===null?null:new Date(n.approved_at).toISOString()}))}var Un={domain:"host",application:"server",phone:"call",crm:"crm"},Fn={host:"domain",server:"application",call:"phone",crm:"crm"};function jn(e,t){let n=t.trim().toLowerCase();if(!n||n.length>500)throw new f("analytics_namespace_source_invalid","Provide a valid namespace source.",400);if(e==="domain"&&(n.includes("://")||/[/?#]/.test(n)||!n.includes(".")))throw new f("analytics_namespace_domain_invalid","Provide a bare approved domain.",400);return n}async function za(e){let t=e.name.trim();if(!t||t.length>160)throw new f("analytics_namespace_name_invalid","Provide a namespace name.",400);let n=await v().connect();await n.query("BEGIN");try{await n.query("SELECT pg_advisory_xact_lock(hashtext($1))",[e.siteId]);let r=(await n.query(`SELECT id, site_id, name, status, version, approved_at
|
|
240
|
-
FROM analytics_identity_namespaces
|
|
241
|
-
WHERE site_id=$1 AND status IN ('draft','approved','disabled')
|
|
242
|
-
ORDER BY version DESC LIMIT 1`,[e.siteId])).rows[0];if(r){if(r.name!==t)throw new f("analytics_namespace_already_exists","This site already belongs to an identity namespace.",409);return await n.query("COMMIT"),{id:r.id,siteId:r.site_id,name:r.name,status:r.status,version:r.version,approvedAt:r.approved_at?.toISOString()??null}}let a=(await n.query(`INSERT INTO analytics_identity_namespaces(
|
|
243
|
-
id, site_id, name, status, approved_hosts, approved_sources, version, approved_by_user_id
|
|
244
|
-
) VALUES ($1,$2,$3,'draft','[]'::jsonb,'[]'::jsonb,1,$4)
|
|
245
|
-
RETURNING id, site_id, name, status, version, approved_at`,[Mn(),e.siteId,t,e.createdByUserId])).rows[0];return await n.query("COMMIT"),{id:a.id,siteId:a.site_id,name:a.name,status:a.status,version:a.version,approvedAt:a.approved_at?.toISOString()??null}}catch(i){throw await n.query("ROLLBACK"),i}finally{n.release()}}async function Ya(e){let t=jn(e.sourceKind,e.sourceRef),n=await v().connect();await n.query("BEGIN");try{let i=await n.query(`SELECT id, status FROM analytics_identity_namespaces
|
|
246
|
-
WHERE id=$1 AND site_id=$2 FOR UPDATE`,[e.namespaceId,e.siteId]);if(!i.rows[0])throw new f("analytics_namespace_not_found","Identity namespace not found.",404);if(["archived","disabled"].includes(i.rows[0].status))throw new f("analytics_namespace_inactive","Identity namespace is not active.",409);let r=Un[e.sourceKind],s=await n.query(`INSERT INTO analytics_identity_sources(id, namespace_id, site_id, source_kind, source_ref, status)
|
|
247
|
-
VALUES ($1,$2,$3,$4,$5,'approved')
|
|
248
|
-
ON CONFLICT(namespace_id, source_kind, source_ref) DO UPDATE
|
|
249
|
-
SET status='approved', revoked_at=NULL
|
|
250
|
-
RETURNING id, namespace_id, site_id, source_kind, source_ref, status, created_at, revoked_at`,[Mn(),e.namespaceId,e.siteId,r,t]);await n.query(`UPDATE analytics_identity_namespaces
|
|
251
|
-
SET status='approved', approved_by_user_id=$3, approved_at=COALESCE(approved_at, now()),
|
|
252
|
-
approved_hosts=CASE WHEN $4='host'
|
|
253
|
-
THEN (SELECT jsonb_agg(DISTINCT value) FROM jsonb_array_elements_text(approved_hosts || to_jsonb($5::text)) value)
|
|
254
|
-
ELSE approved_hosts END,
|
|
255
|
-
approved_sources=(SELECT jsonb_agg(DISTINCT value) FROM jsonb_array_elements_text(approved_sources || to_jsonb(($4 || ':' || $5)::text)) value),
|
|
256
|
-
updated_at=now()
|
|
257
|
-
WHERE id=$1 AND site_id=$2`,[e.namespaceId,e.siteId,e.approvedByUserId,r,t]),await n.query("COMMIT");let a=s.rows[0];return{id:a.id,namespaceId:a.namespace_id,siteId:a.site_id,sourceKind:Fn[a.source_kind],sourceRef:a.source_ref,status:a.status,createdAt:a.created_at instanceof Date?a.created_at.toISOString():new Date(a.created_at).toISOString(),revokedAt:a.revoked_at==null?null:a.revoked_at instanceof Date?a.revoked_at.toISOString():new Date(a.revoked_at).toISOString()}}catch(i){throw await n.query("ROLLBACK"),i}finally{n.release()}}async function nt(e){let t=v();if(!(await t.query(`SELECT id FROM analytics_identity_namespaces
|
|
258
|
-
WHERE id=$1 AND site_id=$2 AND status='approved'`,[e.namespaceId,e.siteId])).rows[0])throw new f("analytics_namespace_boundary","The identity namespace is not approved for this site.",403);let i=[...new Set(e.identityNodeIds??[])];if(i.length&&(await t.query(`SELECT id FROM analytics_identity_nodes
|
|
259
|
-
WHERE site_id=$1 AND namespace_id=$2 AND id = ANY($3::uuid[])`,[e.siteId,e.namespaceId,i])).rows.length!==i.length)throw new f("analytics_namespace_boundary","Identity evidence cannot cross namespaces.",403);if(e.personId&&!(await t.query("SELECT id FROM analytics_people WHERE id=$1 AND site_id=$2 AND namespace_id=$3",[e.personId,e.siteId,e.namespaceId])).rows[0])throw new f("analytics_namespace_boundary","The person does not belong to this namespace.",403);if(e.source){let r=jn(e.source.kind,e.source.ref);if(!(await t.query(`SELECT id FROM analytics_identity_sources
|
|
260
|
-
WHERE namespace_id=$1 AND site_id=$2 AND source_kind=$3 AND source_ref=$4 AND status='approved'`,[e.namespaceId,e.siteId,Un[e.source.kind],r])).rows[0])throw new f("analytics_namespace_source_unapproved","This identity source is not approved.",403)}}async function Xa(e){return await nt(e),(await v().query(`SELECT id, namespace_id, site_id, source_kind, source_ref, status, created_at, revoked_at
|
|
261
|
-
FROM analytics_identity_sources
|
|
262
|
-
WHERE namespace_id=$1 AND site_id=$2
|
|
263
|
-
ORDER BY source_kind, source_ref`,[e.namespaceId,e.siteId])).rows.map(n=>({id:n.id,namespaceId:n.namespace_id,siteId:n.site_id,sourceKind:Fn[n.source_kind],sourceRef:n.source_ref,status:n.status,createdAt:n.created_at.toISOString(),revokedAt:n.revoked_at?.toISOString()??null}))}function Or(){let e=process.env.ANALYTICS_IDENTITY_SECRET?.trim()||process.env.SESSION_SECRET?.trim();if(!e)throw new f("analytics_identity_secret_missing","Candidate identity derivation is not configured.",503);return e}function wt(e,t){return wr("sha256",Or()).update(`xray-candidate:v1:${e}:${t}`).digest("hex")}async function io(e){await nt(e);let t=Math.max(1,Math.min(200,Math.floor(e.limit??50)));return(await v().query(`SELECT a.id, a.model_version, a.first_observed_at, a.last_observed_at, a.expires_at,
|
|
264
|
-
array_remove(array_agg(DISTINCT e.feature_kind) FILTER (WHERE e.contribution > 0), NULL) supporting,
|
|
265
|
-
array_remove(array_agg(DISTINCT e.feature_kind) FILTER (WHERE e.contribution < 0), NULL) conflicting
|
|
266
|
-
FROM analytics_candidate_associations a
|
|
267
|
-
LEFT JOIN analytics_candidate_evidence e ON e.association_id=a.id AND e.namespace_id=a.namespace_id
|
|
268
|
-
WHERE a.site_id=$1 AND a.namespace_id=$2 AND a.status='active' AND a.confidence_band='medium'
|
|
269
|
-
AND a.expires_at > $3::timestamptz
|
|
270
|
-
GROUP BY a.id
|
|
271
|
-
ORDER BY a.updated_at DESC LIMIT $4`,[e.siteId,e.namespaceId,e.now??new Date().toISOString(),t])).rows.map(i=>({associationId:i.id,band:"medium",supportingFeatures:i.supporting??[],conflictingFeatures:i.conflicting??[],modelVersion:i.model_version,firstObservedAt:i.first_observed_at.toISOString(),lastObservedAt:i.last_observed_at.toISOString(),expiresAt:i.expires_at.toISOString()}))}async function qn(e){await nt({siteId:e.siteId,namespaceId:e.namespaceId,personId:e.personId});let t=new Date(e.occurredAt??Date.now());if(!e.evidenceRef.trim())throw new f("analytics_candidate_promotion_evidence_required","Deterministic evidence is required.",400);let n=e.promotedBy??"system";if(n==="user"&&!e.promotedByUserId)throw new f("analytics_candidate_promotion_actor_required","User-confirmed promotion requires an actor.",400);let i=wt("journey_pattern",`${e.evidenceKind}:${e.evidenceRef.trim().toLowerCase()}`),s=await v().connect();await s.query("BEGIN");try{let a=await s.query(`UPDATE analytics_candidate_associations
|
|
272
|
-
SET status='promoted', promoted_person_id=$4, promoted_at=$5, updated_at=now()
|
|
273
|
-
WHERE id=$1 AND site_id=$2 AND namespace_id=$3
|
|
274
|
-
AND (status IN ('active','expired') OR (status='promoted' AND promoted_person_id=$4))
|
|
275
|
-
RETURNING id,left_identity_node_id,right_identity_node_id`,[e.associationId,e.siteId,e.namespaceId,e.personId,t.toISOString()]);if(!a.rows[0])throw new f("analytics_candidate_not_found","Candidate association not found in this namespace.",404);return await s.query(`INSERT INTO analytics_candidate_promotions(
|
|
276
|
-
id, site_id, namespace_id, association_id, person_id, evidence_kind,
|
|
277
|
-
evidence_ref_hmac, promoted_by, promoted_by_user_id, promoted_at
|
|
278
|
-
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)
|
|
279
|
-
ON CONFLICT(association_id, person_id) DO NOTHING`,[Rt(),e.siteId,e.namespaceId,e.associationId,e.personId,e.evidenceKind,i,n,e.promotedByUserId??null,t.toISOString()]),await s.query(`INSERT INTO analytics_privacy_audits(
|
|
280
|
-
id, site_id, namespace_ref, subject_ref, action, idempotency_key,
|
|
281
|
-
actor_user_id, result_code, affected_counts, completed_at
|
|
282
|
-
) VALUES ($1,$2,$3,$4,'candidate_promoted',$5,$6,'promoted',$7::jsonb,$8)
|
|
283
|
-
ON CONFLICT(site_id, idempotency_key) DO NOTHING`,[Rt(),e.siteId,e.namespaceId,e.associationId,`candidate-promotion:${e.associationId}:${e.personId}`,e.promotedByUserId??null,JSON.stringify({candidateAssociations:1,deterministicPeople:1}),t.toISOString()]),await s.query(`INSERT INTO analytics_identity_promotion_receipts(
|
|
284
|
-
id,site_id,namespace_id,canonical_person_id,canonical_person_ref_hmac,
|
|
285
|
-
evidence_kind,evidence_ref_hmac,prior_person_ids,prior_identity_node_ids,
|
|
286
|
-
candidate_association_ids,affected_history_counts,schema_version,idempotency_key,promoted_at
|
|
287
|
-
) VALUES($1,$2,$3,$4,$5,$6,$7,'{}'::uuid[],$8::uuid[],$9::uuid[],$10::jsonb,
|
|
288
|
-
'xray.identity-promotion.v1',$11,$12)
|
|
289
|
-
ON CONFLICT(site_id,idempotency_key) DO NOTHING`,[Rt(),e.siteId,e.namespaceId,e.personId,wt("journey_pattern",`person:${e.personId}`),e.evidenceKind,i,[a.rows[0].left_identity_node_id,a.rows[0].right_identity_node_id],[e.associationId],JSON.stringify({events:0,sessions:0,touches:0,conversions:0,candidateTouches:0}),wt("journey_pattern",`promotion:${e.siteId}:${e.associationId}:${e.personId}:${e.evidenceKind}:${e.evidenceRef}`),t.toISOString()]),await s.query("COMMIT"),{associationId:e.associationId,personId:e.personId,promotedAt:t.toISOString(),reason:e.evidenceKind}}catch(a){throw await s.query("ROLLBACK"),a}finally{s.release()}}import{createHash as Dr,randomUUID as Hn}from"crypto";import{z as g}from"zod";var ie=g.string().trim().min(1).max(160).regex(/^[a-zA-Z0-9_.:-]+$/),Ot=g.string().trim().min(1).max(160).regex(/^[a-zA-Z][a-zA-Z0-9_.:-]*$/),Tr=g.discriminatedUnion("kind",[g.object({kind:g.literal("event_completed"),eventName:ie,withinDays:g.number().int().min(1).max(3650).optional()}).strict(),g.object({kind:g.literal("event_frequency"),eventName:ie,minimum:g.number().int().min(1).max(1e3),withinDays:g.number().int().min(1).max(3650).optional()}).strict(),g.object({kind:g.literal("event_recency"),eventName:ie,withinDays:g.number().int().min(1).max(3650)}).strict(),g.object({kind:g.literal("page_visited"),path:g.string().trim().startsWith("/").max(2e3),withinDays:g.number().int().min(1).max(3650).optional()}).strict(),g.object({kind:g.literal("referrer_equals"),value:g.string().trim().min(1).max(500)}).strict(),g.object({kind:g.literal("source_equals"),value:g.string().trim().min(1).max(180)}).strict(),g.object({kind:g.literal("call_outcome"),outcome:ie,withinDays:g.number().int().min(1).max(3650).optional()}).strict(),g.object({kind:g.literal("crm_stage"),stage:ie}).strict(),g.object({kind:g.literal("property_equals"),property:Ot,value:g.union([g.string().max(500),g.number().finite(),g.boolean()])}).strict(),g.object({kind:g.literal("property_present"),property:Ot}).strict()]),xr=g.object({id:ie,label:g.string().trim().min(1).max(160),operation:g.enum(["add","subtract"]),points:g.number().int().min(0).max(1e5),cap:g.number().int().min(0).max(1e5).optional(),predicates:g.array(Tr).min(1).max(50)}).strict(),Wn=g.object({namespaceId:ie,name:g.string().trim().min(1).max(160),kind:g.enum(["fit","engagement","combined"]),targetObject:g.enum(["person","visitor"]),minimum:g.number().int().min(-1e6).max(1e6),maximum:g.number().int().min(-1e6).max(1e6),groups:g.array(xr).min(1).max(100),permittedProperties:g.array(Ot).max(200).default([]),enabled:g.boolean().default(!0)}).strict().superRefine((e,t)=>{e.minimum>=e.maximum&&t.addIssue({code:"custom",path:["maximum"],message:"maximum must be greater than minimum"});let n=new Set(e.permittedProperties);for(let[i,r]of e.groups.entries())for(let[s,a]of r.predicates.entries())(a.kind==="property_equals"||a.kind==="property_present")&&!n.has(a.property)&&t.addIssue({code:"custom",path:["groups",i,"predicates",s,"property"],message:"property is not allowlisted"})});function Bn(){return{definitions:new Map,evaluations:new Map,evaluationHistory:[]}}var Y=class extends Error{constructor(n,i){super(i);this.code=n;this.name="AnalyticsLeadScoringError"}code};function Dt(e){let t=e instanceof Date?e.toISOString():e??new Date().toISOString();if(!Number.isFinite(Date.parse(t)))throw new Y("analytics_score_invalid_time","A valid evaluation time is required.");return new Date(t).toISOString()}function De(e,t){return`${e}:${t}`}function Tt(e){return{...e,groups:e.groups.map(t=>({...t,predicates:t.predicates.map(n=>({...n}))})),permittedProperties:[...e.permittedProperties]}}function Lr(e,t,n){let i=Date.parse(e),r=Date.parse(t);return!Number.isFinite(i)||i>r?!1:n===void 0||r-i<=n*864e5}function $r(e,t,n){let i=(r,s,a)=>(r??[]).filter(o=>o.name===s&&Lr(o.occurredAt,n,a));switch(e.kind){case"event_completed":{let r=i(t.events,e.eventName,e.withinDays);return r.length?r.map(s=>s.ref):null}case"event_frequency":{let r=i(t.events,e.eventName,e.withinDays);return r.length>=e.minimum?r.map(s=>s.ref):null}case"event_recency":{let r=i(t.events,e.eventName,e.withinDays);return r.length?r.map(s=>s.ref):null}case"page_visited":{let r=i(t.pages,e.path,e.withinDays);return r.length?r.map(s=>s.ref):null}case"referrer_equals":{let r=(t.referrers??[]).filter(s=>s.name===e.value);return r.length?r.map(s=>s.ref):null}case"source_equals":{let r=(t.sources??[]).filter(s=>s.name===e.value);return r.length?r.map(s=>s.ref):null}case"call_outcome":{let r=i(t.calls,e.outcome,e.withinDays);return r.length?r.map(s=>s.ref):null}case"crm_stage":{let r=i(t.crmStages,e.stage);return r.length?r.map(s=>s.ref):null}case"property_equals":return t.permittedProperties?.[e.property]===e.value?[`property:${e.property}`]:null;case"property_present":{let r=t.permittedProperties?.[e.property];return r!=null?[`property:${e.property}`]:null}}}function kr(e){let t=n=>Array.isArray(n)?n.map(t):n&&typeof n=="object"?Object.fromEntries(Object.entries(n).sort(([i],[r])=>i.localeCompare(r)).map(([i,r])=>[i,t(r)])):n;return Dr("sha256").update(JSON.stringify(t(e))).digest("hex")}function Vn(e){let t=Wn.parse(e.definition),n=e.id??Hn();if(e.tables.definitions.has(De(t.namespaceId,n)))throw new Y("analytics_score_exists","A lead score with this ID already exists.");let i=Dt(e.now),r={id:n,...t,groups:t.groups.map(s=>({...s,predicates:s.predicates.map(a=>({...a}))})),version:1,createdAt:i,updatedAt:i};return e.tables.definitions.set(De(t.namespaceId,n),r),Tt(r)}function oo(e){let t=e.tables.definitions.get(De(e.namespaceId,e.scoreId));if(!t)throw new Y("analytics_score_not_found","Lead score not found.");if(t.version!==e.expectedVersion)throw new Y("analytics_score_version_conflict","The lead score changed; reload before updating.");let n=Wn.parse(e.definition);if(n.namespaceId!==e.namespaceId)throw new Y("analytics_score_namespace_mismatch","Lead scores cannot move between namespaces.");let i={...t,...n,groups:n.groups.map(r=>({...r,predicates:r.predicates.map(s=>({...s}))})),version:t.version+1,updatedAt:Dt(e.now)};return e.tables.definitions.set(De(e.namespaceId,e.scoreId),i),Tt(i)}function co(e){return[...e.tables.definitions.values()].filter(t=>t.namespaceId===e.namespaceId).sort((t,n)=>t.name.localeCompare(n.name)).map(Tt)}function Kn(e){let t=e.tables.definitions.get(De(e.namespaceId,e.scoreId));if(!t||!t.enabled)throw new Y("analytics_score_unavailable","Lead score is missing or disabled.");let n=Dt(e.now),i=t.groups.map(_=>{let u=_.predicates.map(b=>$r(b,e.facts,n)),y=u.every(b=>b!==null),E=y?Math.min(_.points,_.cap??_.points):0;return{groupId:_.id,operation:_.operation,matched:y,points:_.operation==="subtract"?-E:E,evidenceRefs:y?[...new Set(u.flatMap(b=>b??[]))].sort():[]}}),r=i.reduce((_,u)=>_+u.points,0),s=Math.max(t.minimum,Math.min(t.maximum,r)),a=[...new Set(i.flatMap(_=>_.evidenceRefs))].sort(),o=kr({namespaceId:e.namespaceId,scoreId:e.scoreId,version:t.version,subjectRef:e.subjectRef,identityTier:e.identityTier,components:i,value:s}),c=e.tables.evaluations.get(o);if(c)return{...c,components:c.components.map(_=>({..._,evidenceRefs:[..._.evidenceRefs]})),evidenceRefs:[...c.evidenceRefs]};let d={id:Hn(),namespaceId:e.namespaceId,subjectRef:e.subjectRef,identityTier:e.identityTier,scoreId:t.id,scoreVersion:t.version,scoreKind:t.kind,value:s,components:i,evidenceRefs:a,evaluatedAt:n,idempotencyKey:o,providerEligible:e.identityTier==="confirmed"};return e.tables.evaluations.set(o,d),e.tables.evaluationHistory.push(d),{...d,components:d.components.map(_=>({..._,evidenceRefs:[..._.evidenceRefs]})),evidenceRefs:[...d.evidenceRefs]}}import{createHmac as Pr,timingSafeEqual as Mr}from"crypto";function p(e,t=500){if(typeof e!="string"&&typeof e!="number")return;let n=String(e).trim();return n?n.slice(0,t):void 0}function W(e){let t=Number(e);return Number.isFinite(t)?t:void 0}function re(e){if(typeof e=="boolean")return e;if(e==="true"||e==="1"||e===1)return!0;if(e==="false"||e==="0"||e===0)return!1}function Te(e){if(Array.isArray(e))return e.map(n=>p(n,80)).filter(n=>!!n).slice(0,30);let t=p(e,1e3);return t?t.split(",").map(n=>n.trim()).filter(Boolean).slice(0,30):[]}function B(...e){for(let t of e){let n=p(t,100);if(!n)continue;let i=new Date(n);if(Number.isFinite(i.getTime()))return i.toISOString()}return new Date().toISOString()}function pe(e,t,n){return Pr(e,t).update(n).digest("base64")}function ye(e,t){if(!e||e.length!==t.length)return!1;try{return Mr(Buffer.from(e),Buffer.from(t))}catch{return!1}}function ge(e,t=Date.now(),n=300*1e3){let i=p(e,100);if(!i)return!1;let r=Number(i),s=Number.isFinite(r)?r>1e10?r:r*1e3:Date.parse(i);return Number.isFinite(s)&&Math.abs(t-s)<=n}function _o(e){let t=new URL(e.requestUrl);for(let s of e.internalRewriteQueryKeys??[])t.searchParams.delete(s);let n=e.publicOrigin?.trim();if(n){let s=new URL(n);if(s.protocol!=="https:"||s.username||s.password)throw new Error("analytics webhook public origin must be an HTTPS origin");return s.pathname=t.pathname,s.search=t.search,s.hash="",s.toString()}let i=e.forwardedProto?.split(",")[0]?.trim(),r=e.forwardedHost?.split(",")[0]?.trim();return i&&(t.protocol=`${i}:`),r&&(t.host=r),t.toString()}function Jn(e){return ge(e.payloadTimestamp,e.now)&&ye(e.signature,pe("sha1",e.secret,e.rawBody))}function zn(e){let t=e.payload,n=p(t.resource_id??t.id,240)||"unknown",i=B(t.timestamp,t.start_time,t.created_at),r=Te(t.tags??t.tag),s=p(t.lead_status,80),a=s==="not_a_lead"||r.some(c=>c.toLowerCase()==="spam")||re(t.spam)===!0,o={...p(t.gclid)?{gclid:p(t.gclid)}:{},...p(t.gbraid)?{gbraid:p(t.gbraid)}:{},...p(t.wbraid)?{wbraid:p(t.wbraid)}:{},...p(t.fbclid)?{fbclid:p(t.fbclid)}:{},...p(t.msclkid)?{msclkid:p(t.msclkid)}:{}};return{schemaVersion:1,eventId:`callrail_${n}_${e.phase}`,sourceEventId:`${n}:${e.phase}:${i}`,source:"callrail",sourceAccountRef:e.sourceAccountRef,eventKind:"call",eventName:e.phase==="pre_call"?"call_started":e.phase==="routing_complete"?"call_routed":e.phase==="call_modified"?"call_modified":"call_completed",occurredAt:i,callId:n,personRef:p(t.person_resource_id,240),visitorId:p(t.visitor_id??t.browser_id??t.ga,160),canonicalUrl:p(t.landing_page_url,3e3),sourceName:p(t.utm_source??t.source_name,180),medium:p(t.utm_medium??t.medium,180),campaign:p(t.utm_campaign??t.campaign,240),clickIds:o,properties:{answered:re(t.answered)??!1,durationSeconds:W(t.duration)??0,status:a?"spam":s||"unknown",tags:r,direction:p(t.direction,40)||"unknown",trackingNumber:p(t.tracking_phone_number,80)||null,changes:Te(t.changes)}}}function Yn(e){return ge(e.requestTime,e.now)&&ye(e.signature.trim(),pe("sha1",e.secret,`${e.requestTime}${e.rawBody}`))}function Xn(e){let t=e.payload,n=p(t.activity_id??t.call_id??t.id,240)||"unknown",i=B(t.timestamp,t.event_time,t.started_at,t.created_at),r=Te(t.tags??t.tag_list),s=p(t.status??t.disposition,80)||"unknown",a=s.toLowerCase().includes("spam")||r.some(o=>o.toLowerCase()==="spam");return{schemaVersion:1,eventId:`ctm_${n}_${e.phase}`,sourceEventId:`${n}:${e.phase}:${i}`,source:"calltrackingmetrics",sourceAccountRef:e.sourceAccountRef,eventKind:"call",eventName:e.phase.includes("start")?"call_started":e.phase.includes("answer")?"call_answered":"call_completed",occurredAt:i,callId:n,personRef:p(t.contact_id??t.visitor_id,240),visitorId:p(t.visitor_id??t.cookie_id,160),sessionId:p(t.session_id,160),canonicalUrl:p(t.landing_page_url??t.landing_url,3e3),sourceName:p(t.source??t.utm_source,180),medium:p(t.medium??t.utm_medium,180),campaign:p(t.campaign??t.utm_campaign,240),clickIds:{...p(t.gclid)?{gclid:p(t.gclid)}:{},...p(t.fbclid)?{fbclid:p(t.fbclid)}:{},...p(t.msclkid)?{msclkid:p(t.msclkid)}:{}},properties:{answered:re(t.answered)??e.phase.includes("answer"),durationSeconds:W(t.duration??t.talk_time)??0,status:a?"spam":s,tags:r,direction:p(t.direction,40)||"unknown",trackingNumber:p(t.tracking_number??t.dialed_number,80)||null,score:W(t.score)??null}}}import Ur from"twilio";function Gn(e){return Ur.validateRequest(e.authToken,e.signature,e.url,e.params)}function Qn(e){let t=e.params,n=p(t.CallSid,240)||"unknown",i=(p(t.CallStatus,80)||"unknown").toLowerCase(),r=B(t.Timestamp,t.CallTimestamp,t.StartTime),s=["completed","busy","failed","no-answer","canceled"].includes(i),a=["in-progress","answered","completed"].includes(i);return{schemaVersion:1,eventId:`twilio_${n}_${i}`,sourceEventId:`${n}:${i}`,source:"twilio",sourceAccountRef:e.sourceAccountRef,eventKind:"call",eventName:s?"call_completed":a?"call_answered":"call_started",occurredAt:r,callId:n,visitorId:p(t.XRayVisitorId,160),sessionId:p(t.XRaySessionId,160),personRef:p(t.ContactId,240),properties:{answered:re(t.Answered)??a,durationSeconds:W(t.CallDuration??t.Duration)??0,status:i,direction:p(t.Direction,40)||"unknown",trackingNumber:p(t.To,80)||null,attributionLink:t.XRaySessionId?"explicit_session":"unattributed"}}}import{createCipheriv as Fr,createDecipheriv as jr,createHash as qr,createHmac as Hr}from"crypto";var xe={session:"xrs_",visitor:"xrv_",person:"xrp_",cursor:"xrc_",candidate:"xri_"};function Lt(e){return qr("sha256").update(`xray-report:${e}:`).update(Fe()).digest()}function O(e,t,n){let i=Lt("reference"),r=Buffer.from(`v1:${e}:${t}`),s=Buffer.from(n),a=Hr("sha256",Lt("reference-iv")).update(r).update(s).digest().subarray(0,12),o=Fr("aes-256-gcm",i,a);o.setAAD(r);let c=Buffer.concat([o.update(s),o.final()]);return`${xe[e]}${Buffer.concat([a,c,o.getAuthTag()]).toString("base64url")}`}function it(e,t,n){let i=xe[e];if(!n.startsWith(i))throw new Error("analytics_reference_invalid");try{let r=Buffer.from(n.slice(i.length),"base64url");if(r.length<29)throw new Error("short reference");let s=r.subarray(0,12),a=r.subarray(r.length-16),o=r.subarray(12,r.length-16),c=jr("aes-256-gcm",Lt("reference"),s);return c.setAAD(Buffer.from(`v1:${e}:${t}`)),c.setAuthTag(a),Buffer.concat([c.update(o),c.final()]).toString("utf8")}catch{throw new Error("analytics_reference_invalid")}}function Wr(e,t){return O("cursor",e,JSON.stringify(t))}function ve(e,t){if(!t)return null;try{let n=JSON.parse(it("cursor",e,t));if(!n.at||!n.id||Number.isNaN(Date.parse(n.at)))throw new Error("invalid");return n}catch{throw new Error("analytics_cursor_invalid")}}function Ee(e){if(e===void 0)return 50;if(!Number.isInteger(e)||e<1||e>100)throw new Error("analytics_limit_invalid");return e}function xt(e){if(typeof e!="string"||!e)return null;try{let t=new URL(e);return`${t.origin}${t.pathname}`.slice(0,2e3)}catch{return null}}function fe(e){if(e==null)return null;let t=Number(e);return Number.isFinite(t)?t:null}function S(e){let t=e instanceof Date?e:new Date(e);if(!Number.isFinite(t.getTime()))throw new Error("analytics_timestamp_invalid");return t.toISOString()}function Zn(e){return e===null?null:S(e)}function be(e,t,n,i){let r=t.length>n,s=t.slice(0,n),a=s.at(-1);return{items:s.map(i),pageInfo:{hasNextPage:r,nextCursor:r&&a?Wr(e,{at:a._cursorAt,id:a._cursorId}):null}}}async function Ae(e,t){await U(t,e.siteId,e.userId)}async function ho(e,t=v()){await Ae(e,t);let n=Ee(e.limit),i=ve(e.siteId,e.cursor),s=(await t.query(`/* analyticsSessions:keyset */
|
|
289
|
+
WHERE worker_key='default'`).catch(()=>{}),s}}import{createHash as Dr,randomUUID as Hn}from"crypto";import{z as g}from"zod";var ie=g.string().trim().min(1).max(160).regex(/^[a-zA-Z0-9_.:-]+$/),Ot=g.string().trim().min(1).max(160).regex(/^[a-zA-Z][a-zA-Z0-9_.:-]*$/),Tr=g.discriminatedUnion("kind",[g.object({kind:g.literal("event_completed"),eventName:ie,withinDays:g.number().int().min(1).max(3650).optional()}).strict(),g.object({kind:g.literal("event_frequency"),eventName:ie,minimum:g.number().int().min(1).max(1e3),withinDays:g.number().int().min(1).max(3650).optional()}).strict(),g.object({kind:g.literal("event_recency"),eventName:ie,withinDays:g.number().int().min(1).max(3650)}).strict(),g.object({kind:g.literal("page_visited"),path:g.string().trim().startsWith("/").max(2e3),withinDays:g.number().int().min(1).max(3650).optional()}).strict(),g.object({kind:g.literal("referrer_equals"),value:g.string().trim().min(1).max(500)}).strict(),g.object({kind:g.literal("source_equals"),value:g.string().trim().min(1).max(180)}).strict(),g.object({kind:g.literal("call_outcome"),outcome:ie,withinDays:g.number().int().min(1).max(3650).optional()}).strict(),g.object({kind:g.literal("crm_stage"),stage:ie}).strict(),g.object({kind:g.literal("property_equals"),property:Ot,value:g.union([g.string().max(500),g.number().finite(),g.boolean()])}).strict(),g.object({kind:g.literal("property_present"),property:Ot}).strict()]),xr=g.object({id:ie,label:g.string().trim().min(1).max(160),operation:g.enum(["add","subtract"]),points:g.number().int().min(0).max(1e5),cap:g.number().int().min(0).max(1e5).optional(),predicates:g.array(Tr).min(1).max(50)}).strict(),Wn=g.object({namespaceId:ie,name:g.string().trim().min(1).max(160),kind:g.enum(["fit","engagement","combined"]),targetObject:g.enum(["person","visitor"]),minimum:g.number().int().min(-1e6).max(1e6),maximum:g.number().int().min(-1e6).max(1e6),groups:g.array(xr).min(1).max(100),permittedProperties:g.array(Ot).max(200).default([]),enabled:g.boolean().default(!0)}).strict().superRefine((e,t)=>{e.minimum>=e.maximum&&t.addIssue({code:"custom",path:["maximum"],message:"maximum must be greater than minimum"});let n=new Set(e.permittedProperties);for(let[i,r]of e.groups.entries())for(let[s,a]of r.predicates.entries())(a.kind==="property_equals"||a.kind==="property_present")&&!n.has(a.property)&&t.addIssue({code:"custom",path:["groups",i,"predicates",s,"property"],message:"property is not allowlisted"})});function Bn(){return{definitions:new Map,evaluations:new Map,evaluationHistory:[]}}var Y=class extends Error{constructor(n,i){super(i);this.code=n;this.name="AnalyticsLeadScoringError"}code};function Dt(e){let t=e instanceof Date?e.toISOString():e??new Date().toISOString();if(!Number.isFinite(Date.parse(t)))throw new Y("analytics_score_invalid_time","A valid evaluation time is required.");return new Date(t).toISOString()}function De(e,t){return`${e}:${t}`}function Tt(e){return{...e,groups:e.groups.map(t=>({...t,predicates:t.predicates.map(n=>({...n}))})),permittedProperties:[...e.permittedProperties]}}function Lr(e,t,n){let i=Date.parse(e),r=Date.parse(t);return!Number.isFinite(i)||i>r?!1:n===void 0||r-i<=n*864e5}function $r(e,t,n){let i=(r,s,a)=>(r??[]).filter(o=>o.name===s&&Lr(o.occurredAt,n,a));switch(e.kind){case"event_completed":{let r=i(t.events,e.eventName,e.withinDays);return r.length?r.map(s=>s.ref):null}case"event_frequency":{let r=i(t.events,e.eventName,e.withinDays);return r.length>=e.minimum?r.map(s=>s.ref):null}case"event_recency":{let r=i(t.events,e.eventName,e.withinDays);return r.length?r.map(s=>s.ref):null}case"page_visited":{let r=i(t.pages,e.path,e.withinDays);return r.length?r.map(s=>s.ref):null}case"referrer_equals":{let r=(t.referrers??[]).filter(s=>s.name===e.value);return r.length?r.map(s=>s.ref):null}case"source_equals":{let r=(t.sources??[]).filter(s=>s.name===e.value);return r.length?r.map(s=>s.ref):null}case"call_outcome":{let r=i(t.calls,e.outcome,e.withinDays);return r.length?r.map(s=>s.ref):null}case"crm_stage":{let r=i(t.crmStages,e.stage);return r.length?r.map(s=>s.ref):null}case"property_equals":return t.permittedProperties?.[e.property]===e.value?[`property:${e.property}`]:null;case"property_present":{let r=t.permittedProperties?.[e.property];return r!=null?[`property:${e.property}`]:null}}}function kr(e){let t=n=>Array.isArray(n)?n.map(t):n&&typeof n=="object"?Object.fromEntries(Object.entries(n).sort(([i],[r])=>i.localeCompare(r)).map(([i,r])=>[i,t(r)])):n;return Dr("sha256").update(JSON.stringify(t(e))).digest("hex")}function Vn(e){let t=Wn.parse(e.definition),n=e.id??Hn();if(e.tables.definitions.has(De(t.namespaceId,n)))throw new Y("analytics_score_exists","A lead score with this ID already exists.");let i=Dt(e.now),r={id:n,...t,groups:t.groups.map(s=>({...s,predicates:s.predicates.map(a=>({...a}))})),version:1,createdAt:i,updatedAt:i};return e.tables.definitions.set(De(t.namespaceId,n),r),Tt(r)}function lo(e){let t=e.tables.definitions.get(De(e.namespaceId,e.scoreId));if(!t)throw new Y("analytics_score_not_found","Lead score not found.");if(t.version!==e.expectedVersion)throw new Y("analytics_score_version_conflict","The lead score changed; reload before updating.");let n=Wn.parse(e.definition);if(n.namespaceId!==e.namespaceId)throw new Y("analytics_score_namespace_mismatch","Lead scores cannot move between namespaces.");let i={...t,...n,groups:n.groups.map(r=>({...r,predicates:r.predicates.map(s=>({...s}))})),version:t.version+1,updatedAt:Dt(e.now)};return e.tables.definitions.set(De(e.namespaceId,e.scoreId),i),Tt(i)}function uo(e){return[...e.tables.definitions.values()].filter(t=>t.namespaceId===e.namespaceId).sort((t,n)=>t.name.localeCompare(n.name)).map(Tt)}function Kn(e){let t=e.tables.definitions.get(De(e.namespaceId,e.scoreId));if(!t||!t.enabled)throw new Y("analytics_score_unavailable","Lead score is missing or disabled.");let n=Dt(e.now),i=t.groups.map(_=>{let u=_.predicates.map(b=>$r(b,e.facts,n)),y=u.every(b=>b!==null),E=y?Math.min(_.points,_.cap??_.points):0;return{groupId:_.id,operation:_.operation,matched:y,points:_.operation==="subtract"?-E:E,evidenceRefs:y?[...new Set(u.flatMap(b=>b??[]))].sort():[]}}),r=i.reduce((_,u)=>_+u.points,0),s=Math.max(t.minimum,Math.min(t.maximum,r)),a=[...new Set(i.flatMap(_=>_.evidenceRefs))].sort(),o=kr({namespaceId:e.namespaceId,scoreId:e.scoreId,version:t.version,subjectRef:e.subjectRef,identityTier:e.identityTier,components:i,value:s}),c=e.tables.evaluations.get(o);if(c)return{...c,components:c.components.map(_=>({..._,evidenceRefs:[..._.evidenceRefs]})),evidenceRefs:[...c.evidenceRefs]};let d={id:Hn(),namespaceId:e.namespaceId,subjectRef:e.subjectRef,identityTier:e.identityTier,scoreId:t.id,scoreVersion:t.version,scoreKind:t.kind,value:s,components:i,evidenceRefs:a,evaluatedAt:n,idempotencyKey:o,providerEligible:e.identityTier==="confirmed"};return e.tables.evaluations.set(o,d),e.tables.evaluationHistory.push(d),{...d,components:d.components.map(_=>({..._,evidenceRefs:[..._.evidenceRefs]})),evidenceRefs:[...d.evidenceRefs]}}import{createHmac as Pr,timingSafeEqual as Mr}from"crypto";function p(e,t=500){if(typeof e!="string"&&typeof e!="number")return;let n=String(e).trim();return n?n.slice(0,t):void 0}function W(e){let t=Number(e);return Number.isFinite(t)?t:void 0}function re(e){if(typeof e=="boolean")return e;if(e==="true"||e==="1"||e===1)return!0;if(e==="false"||e==="0"||e===0)return!1}function Te(e){if(Array.isArray(e))return e.map(n=>p(n,80)).filter(n=>!!n).slice(0,30);let t=p(e,1e3);return t?t.split(",").map(n=>n.trim()).filter(Boolean).slice(0,30):[]}function B(...e){for(let t of e){let n=p(t,100);if(!n)continue;let i=new Date(n);if(Number.isFinite(i.getTime()))return i.toISOString()}return new Date().toISOString()}function pe(e,t,n){return Pr(e,t).update(n).digest("base64")}function ye(e,t){if(!e||e.length!==t.length)return!1;try{return Mr(Buffer.from(e),Buffer.from(t))}catch{return!1}}function ge(e,t=Date.now(),n=300*1e3){let i=p(e,100);if(!i)return!1;let r=Number(i),s=Number.isFinite(r)?r>1e10?r:r*1e3:Date.parse(i);return Number.isFinite(s)&&Math.abs(t-s)<=n}function po(e){let t=new URL(e.requestUrl);for(let s of e.internalRewriteQueryKeys??[])t.searchParams.delete(s);let n=e.publicOrigin?.trim();if(n){let s=new URL(n);if(s.protocol!=="https:"||s.username||s.password)throw new Error("analytics webhook public origin must be an HTTPS origin");return s.pathname=t.pathname,s.search=t.search,s.hash="",s.toString()}let i=e.forwardedProto?.split(",")[0]?.trim(),r=e.forwardedHost?.split(",")[0]?.trim();return i&&(t.protocol=`${i}:`),r&&(t.host=r),t.toString()}function Jn(e){return ge(e.payloadTimestamp,e.now)&&ye(e.signature,pe("sha1",e.secret,e.rawBody))}function zn(e){let t=e.payload,n=p(t.resource_id??t.id,240)||"unknown",i=B(t.timestamp,t.start_time,t.created_at),r=Te(t.tags??t.tag),s=p(t.lead_status,80),a=s==="not_a_lead"||r.some(c=>c.toLowerCase()==="spam")||re(t.spam)===!0,o={...p(t.gclid)?{gclid:p(t.gclid)}:{},...p(t.gbraid)?{gbraid:p(t.gbraid)}:{},...p(t.wbraid)?{wbraid:p(t.wbraid)}:{},...p(t.fbclid)?{fbclid:p(t.fbclid)}:{},...p(t.msclkid)?{msclkid:p(t.msclkid)}:{}};return{schemaVersion:1,eventId:`callrail_${n}_${e.phase}`,sourceEventId:`${n}:${e.phase}:${i}`,source:"callrail",sourceAccountRef:e.sourceAccountRef,eventKind:"call",eventName:e.phase==="pre_call"?"call_started":e.phase==="routing_complete"?"call_routed":e.phase==="call_modified"?"call_modified":"call_completed",occurredAt:i,callId:n,personRef:p(t.person_resource_id,240),visitorId:p(t.visitor_id??t.browser_id??t.ga,160),canonicalUrl:p(t.landing_page_url,3e3),sourceName:p(t.utm_source??t.source_name,180),medium:p(t.utm_medium??t.medium,180),campaign:p(t.utm_campaign??t.campaign,240),clickIds:o,properties:{answered:re(t.answered)??!1,durationSeconds:W(t.duration)??0,status:a?"spam":s||"unknown",tags:r,direction:p(t.direction,40)||"unknown",trackingNumber:p(t.tracking_phone_number,80)||null,changes:Te(t.changes)}}}function Yn(e){return ge(e.requestTime,e.now)&&ye(e.signature.trim(),pe("sha1",e.secret,`${e.requestTime}${e.rawBody}`))}function Xn(e){let t=e.payload,n=p(t.activity_id??t.call_id??t.id,240)||"unknown",i=B(t.timestamp,t.event_time,t.started_at,t.created_at),r=Te(t.tags??t.tag_list),s=p(t.status??t.disposition,80)||"unknown",a=s.toLowerCase().includes("spam")||r.some(o=>o.toLowerCase()==="spam");return{schemaVersion:1,eventId:`ctm_${n}_${e.phase}`,sourceEventId:`${n}:${e.phase}:${i}`,source:"calltrackingmetrics",sourceAccountRef:e.sourceAccountRef,eventKind:"call",eventName:e.phase.includes("start")?"call_started":e.phase.includes("answer")?"call_answered":"call_completed",occurredAt:i,callId:n,personRef:p(t.contact_id??t.visitor_id,240),visitorId:p(t.visitor_id??t.cookie_id,160),sessionId:p(t.session_id,160),canonicalUrl:p(t.landing_page_url??t.landing_url,3e3),sourceName:p(t.source??t.utm_source,180),medium:p(t.medium??t.utm_medium,180),campaign:p(t.campaign??t.utm_campaign,240),clickIds:{...p(t.gclid)?{gclid:p(t.gclid)}:{},...p(t.fbclid)?{fbclid:p(t.fbclid)}:{},...p(t.msclkid)?{msclkid:p(t.msclkid)}:{}},properties:{answered:re(t.answered)??e.phase.includes("answer"),durationSeconds:W(t.duration??t.talk_time)??0,status:a?"spam":s,tags:r,direction:p(t.direction,40)||"unknown",trackingNumber:p(t.tracking_number??t.dialed_number,80)||null,score:W(t.score)??null}}}import Ur from"twilio";function Gn(e){return Ur.validateRequest(e.authToken,e.signature,e.url,e.params)}function Qn(e){let t=e.params,n=p(t.CallSid,240)||"unknown",i=(p(t.CallStatus,80)||"unknown").toLowerCase(),r=B(t.Timestamp,t.CallTimestamp,t.StartTime),s=["completed","busy","failed","no-answer","canceled"].includes(i),a=["in-progress","answered","completed"].includes(i);return{schemaVersion:1,eventId:`twilio_${n}_${i}`,sourceEventId:`${n}:${i}`,source:"twilio",sourceAccountRef:e.sourceAccountRef,eventKind:"call",eventName:s?"call_completed":a?"call_answered":"call_started",occurredAt:r,callId:n,visitorId:p(t.XRayVisitorId,160),sessionId:p(t.XRaySessionId,160),personRef:p(t.ContactId,240),properties:{answered:re(t.Answered)??a,durationSeconds:W(t.CallDuration??t.Duration)??0,status:i,direction:p(t.Direction,40)||"unknown",trackingNumber:p(t.To,80)||null,attributionLink:t.XRaySessionId?"explicit_session":"unattributed"}}}import{createCipheriv as Fr,createDecipheriv as jr,createHash as qr,createHmac as Hr}from"crypto";var xe={session:"xrs_",visitor:"xrv_",person:"xrp_",cursor:"xrc_",candidate:"xri_"};function Lt(e){return qr("sha256").update(`xray-report:${e}:`).update(Fe()).digest()}function O(e,t,n){let i=Lt("reference"),r=Buffer.from(`v1:${e}:${t}`),s=Buffer.from(n),a=Hr("sha256",Lt("reference-iv")).update(r).update(s).digest().subarray(0,12),o=Fr("aes-256-gcm",i,a);o.setAAD(r);let c=Buffer.concat([o.update(s),o.final()]);return`${xe[e]}${Buffer.concat([a,c,o.getAuthTag()]).toString("base64url")}`}function it(e,t,n){let i=xe[e];if(!n.startsWith(i))throw new Error("analytics_reference_invalid");try{let r=Buffer.from(n.slice(i.length),"base64url");if(r.length<29)throw new Error("short reference");let s=r.subarray(0,12),a=r.subarray(r.length-16),o=r.subarray(12,r.length-16),c=jr("aes-256-gcm",Lt("reference"),s);return c.setAAD(Buffer.from(`v1:${e}:${t}`)),c.setAuthTag(a),Buffer.concat([c.update(o),c.final()]).toString("utf8")}catch{throw new Error("analytics_reference_invalid")}}function Wr(e,t){return O("cursor",e,JSON.stringify(t))}function ve(e,t){if(!t)return null;try{let n=JSON.parse(it("cursor",e,t));if(!n.at||!n.id||Number.isNaN(Date.parse(n.at)))throw new Error("invalid");return n}catch{throw new Error("analytics_cursor_invalid")}}function Ee(e){if(e===void 0)return 50;if(!Number.isInteger(e)||e<1||e>100)throw new Error("analytics_limit_invalid");return e}function xt(e){if(typeof e!="string"||!e)return null;try{let t=new URL(e);return`${t.origin}${t.pathname}`.slice(0,2e3)}catch{return null}}function fe(e){if(e==null)return null;let t=Number(e);return Number.isFinite(t)?t:null}function S(e){let t=e instanceof Date?e:new Date(e);if(!Number.isFinite(t.getTime()))throw new Error("analytics_timestamp_invalid");return t.toISOString()}function Zn(e){return e===null?null:S(e)}function be(e,t,n,i){let r=t.length>n,s=t.slice(0,n),a=s.at(-1);return{items:s.map(i),pageInfo:{hasNextPage:r,nextCursor:r&&a?Wr(e,{at:a._cursorAt,id:a._cursorId}):null}}}async function Ae(e,t){await U(t,e.siteId,e.userId)}async function Co(e,t=v()){await Ae(e,t);let n=Ee(e.limit),i=ve(e.siteId,e.cursor),s=(await t.query(`/* analyticsSessions:keyset */
|
|
290
290
|
WITH ranked AS (
|
|
291
291
|
SELECT s.*,
|
|
292
292
|
row_number() OVER (PARTITION BY s.visitor_hmac ORDER BY s.started_at, s.id)::int AS session_number
|
|
@@ -322,7 +322,7 @@ import{a as Zt,s as tn}from"./chunk-WDB5CTRY.js";import{T as cn,a as de,b as Ne,
|
|
|
322
322
|
SELECT 1 FROM analytics_events ee
|
|
323
323
|
WHERE ee.site_id = $1 AND ee.session_hmac = r.session_hmac AND ee.event_name = $8
|
|
324
324
|
))
|
|
325
|
-
ORDER BY r.started_at DESC, r.id DESC LIMIT $9`,[e.siteId,i?.at??null,i?.id??null,e.returning??null,e.converted??null,e.minimumDurationMs??null,e.maximumDurationMs??null,e.eventName?.slice(0,160)??null,n+1])).rows.map(a=>({...a,_cursorAt:S(a.started_at),_cursorId:a.internal_id}));return be(e.siteId,s,n,a=>({sessionRef:O("session",e.siteId,a.internal_id),visitorRef:O("visitor",e.siteId,`visitor:${a.visitor_hmac}`),personRef:a.person_id?O("person",e.siteId,a.person_id):null,returning:a.session_number>1,sessionNumber:a.session_number,startedAt:S(a.started_at),lastActiveAt:S(a.last_active_at),endedAt:Zn(a.ended_at),durationMs:fe(a.duration_ms),landingPath:a.landing_path,exitPath:a.exit_path,pages:a.pageview_count,events:a.event_count,device:a.device_class,location:{country:a.country_code,region:a.region_code},acquisition:{source:a.source,medium:a.medium,campaign:a.campaign},conversions:a.conversion_count,revenue:a.revenue_minor===null?null:{valueMinor:Number(a.revenue_minor),currency:a.currency}}))}async function
|
|
325
|
+
ORDER BY r.started_at DESC, r.id DESC LIMIT $9`,[e.siteId,i?.at??null,i?.id??null,e.returning??null,e.converted??null,e.minimumDurationMs??null,e.maximumDurationMs??null,e.eventName?.slice(0,160)??null,n+1])).rows.map(a=>({...a,_cursorAt:S(a.started_at),_cursorId:a.internal_id}));return be(e.siteId,s,n,a=>({sessionRef:O("session",e.siteId,a.internal_id),visitorRef:O("visitor",e.siteId,`visitor:${a.visitor_hmac}`),personRef:a.person_id?O("person",e.siteId,a.person_id):null,returning:a.session_number>1,sessionNumber:a.session_number,startedAt:S(a.started_at),lastActiveAt:S(a.last_active_at),endedAt:Zn(a.ended_at),durationMs:fe(a.duration_ms),landingPath:a.landing_path,exitPath:a.exit_path,pages:a.pageview_count,events:a.event_count,device:a.device_class,location:{country:a.country_code,region:a.region_code},acquisition:{source:a.source,medium:a.medium,campaign:a.campaign},conversions:a.conversion_count,revenue:a.revenue_minor===null?null:{valueMinor:Number(a.revenue_minor),currency:a.currency}}))}async function Ro(e,t=v()){await Ae(e,t);let n=Ee(e.limit),i=ve(e.siteId,e.cursor),s=(await t.query(`/* analyticsVisitors:keyset */
|
|
326
326
|
WITH visitor_sessions AS (
|
|
327
327
|
SELECT COALESCE(s.person_id::text, s.visitor_hmac) AS identity_key, s.person_id,
|
|
328
328
|
min(s.started_at) AS first_seen_at, max(s.last_active_at) AS last_seen_at,
|
|
@@ -344,7 +344,7 @@ import{a as Zt,s as tn}from"./chunk-WDB5CTRY.js";import{T as cn,a as de,b as Ne,
|
|
|
344
344
|
WHERE ($2::timestamptz IS NULL OR (v.last_seen_at, v.last_session_id) < ($2::timestamptz, $3::uuid))
|
|
345
345
|
AND ($4::boolean IS NULL OR (v.session_count > 1) = $4)
|
|
346
346
|
AND ($5::boolean IS NULL OR (COALESCE(c.conversion_count,0) > 0) = $5)
|
|
347
|
-
ORDER BY v.last_seen_at DESC, v.last_session_id DESC LIMIT $6`,[e.siteId,i?.at??null,i?.id??null,e.returning??null,e.converted??null,n+1])).rows.map(a=>({...a,_cursorAt:S(a.last_seen_at),_cursorId:a.last_session_id}));return be(e.siteId,s,n,a=>({visitorRef:O("visitor",e.siteId,a.person_id?`person:${a.person_id}`:`visitor:${a.identity_key}`),personRef:a.person_id?O("person",e.siteId,a.person_id):null,known:!!a.person_id,returning:a.session_count>1,sessions:a.session_count,firstSeenAt:S(a.first_seen_at),lastSeenAt:S(a.last_seen_at),durationMs:fe(a.duration_ms),pages:a.pageview_count,events:a.event_count,conversions:a.conversion_count,revenue:a.revenue_minor===null?null:{valueMinor:Number(a.revenue_minor),currency:a.currency}}))}async function Br(e,t){if(t.startsWith(xe.person))return{kind:"person",personId:it("person",e,t),visitorHmac:null,sessionId:null};if(t.startsWith(xe.visitor)){let n=it("visitor",e,t);if(n.startsWith("person:"))return{kind:"visitor",personId:n.slice(7),visitorHmac:null,sessionId:null};if(n.startsWith("visitor:"))return{kind:"visitor",personId:null,visitorHmac:n.slice(8),sessionId:null};throw new Error("analytics_reference_invalid")}if(t.startsWith(xe.session))return{kind:"session",personId:null,visitorHmac:null,sessionId:it("session",e,t)};throw new Error("analytics_reference_invalid")}var Vr=2160*60*60*1e3,Kr=366*24*60*60*1e3;function Jr(e,t){let n=t===void 0?Date.now():Date.parse(t),i=e===void 0?n-Vr:Date.parse(e);if(!Number.isFinite(i)||!Number.isFinite(n)||i>=n||n-i>Kr)throw new f("analytics_journey_date_range_invalid","Choose a valid from-inclusive, to-exclusive date range of at most 366 days.",400);return{from:new Date(i).toISOString(),to:new Date(n).toISOString()}}async function
|
|
347
|
+
ORDER BY v.last_seen_at DESC, v.last_session_id DESC LIMIT $6`,[e.siteId,i?.at??null,i?.id??null,e.returning??null,e.converted??null,n+1])).rows.map(a=>({...a,_cursorAt:S(a.last_seen_at),_cursorId:a.last_session_id}));return be(e.siteId,s,n,a=>({visitorRef:O("visitor",e.siteId,a.person_id?`person:${a.person_id}`:`visitor:${a.identity_key}`),personRef:a.person_id?O("person",e.siteId,a.person_id):null,known:!!a.person_id,returning:a.session_count>1,sessions:a.session_count,firstSeenAt:S(a.first_seen_at),lastSeenAt:S(a.last_seen_at),durationMs:fe(a.duration_ms),pages:a.pageview_count,events:a.event_count,conversions:a.conversion_count,revenue:a.revenue_minor===null?null:{valueMinor:Number(a.revenue_minor),currency:a.currency}}))}async function Br(e,t){if(t.startsWith(xe.person))return{kind:"person",personId:it("person",e,t),visitorHmac:null,sessionId:null};if(t.startsWith(xe.visitor)){let n=it("visitor",e,t);if(n.startsWith("person:"))return{kind:"visitor",personId:n.slice(7),visitorHmac:null,sessionId:null};if(n.startsWith("visitor:"))return{kind:"visitor",personId:null,visitorHmac:n.slice(8),sessionId:null};throw new Error("analytics_reference_invalid")}if(t.startsWith(xe.session))return{kind:"session",personId:null,visitorHmac:null,sessionId:it("session",e,t)};throw new Error("analytics_reference_invalid")}var Vr=2160*60*60*1e3,Kr=366*24*60*60*1e3;function Jr(e,t){let n=t===void 0?Date.now():Date.parse(t),i=e===void 0?n-Vr:Date.parse(e);if(!Number.isFinite(i)||!Number.isFinite(n)||i>=n||n-i>Kr)throw new f("analytics_journey_date_range_invalid","Choose a valid from-inclusive, to-exclusive date range of at most 366 days.",400);return{from:new Date(i).toISOString(),to:new Date(n).toISOString()}}async function wo(e,t=v()){await Ae(e,t);let n=Ee(e.limit),i=ve(e.siteId,e.cursor),r;try{r=await Br(e.siteId,e.reference)}catch{throw new f("analytics_journey_not_found","Journey not found.",404)}if(!(await t.query(`/* analyticsEnhancedJourney:subject */
|
|
348
348
|
SELECT 1 WHERE
|
|
349
349
|
($2::uuid IS NOT NULL AND EXISTS (SELECT 1 FROM analytics_people p WHERE p.site_id=$1 AND p.id=$2))
|
|
350
350
|
OR ($3::text IS NOT NULL AND EXISTS (SELECT 1 FROM analytics_sessions s WHERE s.site_id=$1 AND s.visitor_hmac=$3))
|
|
@@ -515,7 +515,7 @@ import{a as Zt,s as tn}from"./chunk-WDB5CTRY.js";import{T as cn,a as de,b as Ne,
|
|
|
515
515
|
f.source,f.medium,f.campaign,f.referrer
|
|
516
516
|
FROM ranged_sessions s LEFT JOIN event_stats e ON e.session_hmac=s.session_hmac
|
|
517
517
|
LEFT JOIN first_touch f ON f.session_hmac=s.session_hmac LEFT JOIN conversion_stats c ON c.session_hmac=s.session_hmac
|
|
518
|
-
ORDER BY s.started_at DESC,s.id DESC LIMIT 50`,[...o])]),V=d.rows.map(m=>({...m,_cursorAt:S(m.occurred_at),_cursorId:m.stable_id})),P=be(e.siteId,V,n,m=>({occurredAt:S(m.occurred_at),type:m.entry_type,name:m.name,path:m.path,referrer:xt(m.referrer),acquisition:m.source||m.medium||m.campaign?{source:m.source,medium:m.medium,campaign:m.campaign}:null,value:m.value_minor===null?null:{valueMinor:Number(m.value_minor),currency:m.currency},evidenceClass:m.evidence_class,provenance:m.provenance})),h=_.rows[0]??{resolved_person_id:null,sessions:0,pageviews:0,events:0,conversions:0,revenue_minor:null,currency:null,total_engagement_ms:null,average_engagement_ms:null},L=h.resolved_person_id??r.personId;return{subject:{kind:L?"person":r.kind,reference:e.reference,personRef:L?O("person",e.siteId,L):null},range:{...a,fromInclusive:!0,toExclusive:!0},journeyTier:"confirmed",candidateAssisted:null,summary:{sessions:Number(h.sessions),pageviews:Number(h.pageviews),events:Number(h.events),conversions:Number(h.conversions),revenue:h.revenue_minor===null?null:{valueMinor:Number(h.revenue_minor),currency:h.currency},totalEngagementMs:fe(h.total_engagement_ms),averageEngagementMs:fe(h.average_engagement_ms)},commonPages:u.rows.map(m=>({path:m.path,visits:Number(m.visits),uniqueSessions:Number(m.unique_sessions),engagedMs:fe(m.engaged_ms),conversions:Number(m.conversions)})),acquisition:y.rows.map(m=>({source:m.source,medium:m.medium,campaign:m.campaign,referrer:xt(m.referrer),sessions:Number(m.sessions),events:Number(m.events),conversions:Number(m.conversions)})),eventBreakdown:E.rows.map(m=>({name:m.name,count:Number(m.count),uniqueSessions:Number(m.unique_sessions),conversions:Number(m.conversions)})),sessions:b.rows.map(m=>({sessionRef:O("session",e.siteId,m.internal_id),startedAt:S(m.started_at),endedAt:Zn(m.ended_at),durationMs:fe(m.duration_ms),pageviews:Number(m.pageviews),events:Number(m.events),conversions:Number(m.conversions),revenue:m.revenue_minor===null?null:{valueMinor:Number(m.revenue_minor),currency:m.currency},landingPath:m.landing_path,exitPath:m.exit_path,acquisition:m.source||m.medium||m.campaign||m.referrer?{source:m.source,medium:m.medium,campaign:m.campaign,referrer:xt(m.referrer)}:null})),...P}}async function
|
|
518
|
+
ORDER BY s.started_at DESC,s.id DESC LIMIT 50`,[...o])]),V=d.rows.map(m=>({...m,_cursorAt:S(m.occurred_at),_cursorId:m.stable_id})),P=be(e.siteId,V,n,m=>({occurredAt:S(m.occurred_at),type:m.entry_type,name:m.name,path:m.path,referrer:xt(m.referrer),acquisition:m.source||m.medium||m.campaign?{source:m.source,medium:m.medium,campaign:m.campaign}:null,value:m.value_minor===null?null:{valueMinor:Number(m.value_minor),currency:m.currency},evidenceClass:m.evidence_class,provenance:m.provenance})),h=_.rows[0]??{resolved_person_id:null,sessions:0,pageviews:0,events:0,conversions:0,revenue_minor:null,currency:null,total_engagement_ms:null,average_engagement_ms:null},L=h.resolved_person_id??r.personId;return{subject:{kind:L?"person":r.kind,reference:e.reference,personRef:L?O("person",e.siteId,L):null},range:{...a,fromInclusive:!0,toExclusive:!0},journeyTier:"confirmed",candidateAssisted:null,summary:{sessions:Number(h.sessions),pageviews:Number(h.pageviews),events:Number(h.events),conversions:Number(h.conversions),revenue:h.revenue_minor===null?null:{valueMinor:Number(h.revenue_minor),currency:h.currency},totalEngagementMs:fe(h.total_engagement_ms),averageEngagementMs:fe(h.average_engagement_ms)},commonPages:u.rows.map(m=>({path:m.path,visits:Number(m.visits),uniqueSessions:Number(m.unique_sessions),engagedMs:fe(m.engaged_ms),conversions:Number(m.conversions)})),acquisition:y.rows.map(m=>({source:m.source,medium:m.medium,campaign:m.campaign,referrer:xt(m.referrer),sessions:Number(m.sessions),events:Number(m.events),conversions:Number(m.conversions)})),eventBreakdown:E.rows.map(m=>({name:m.name,count:Number(m.count),uniqueSessions:Number(m.unique_sessions),conversions:Number(m.conversions)})),sessions:b.rows.map(m=>({sessionRef:O("session",e.siteId,m.internal_id),startedAt:S(m.started_at),endedAt:Zn(m.ended_at),durationMs:fe(m.duration_ms),pageviews:Number(m.pageviews),events:Number(m.events),conversions:Number(m.conversions),revenue:m.revenue_minor===null?null:{valueMinor:Number(m.revenue_minor),currency:m.currency},landingPath:m.landing_path,exitPath:m.exit_path,acquisition:m.source||m.medium||m.campaign||m.referrer?{source:m.source,medium:m.medium,campaign:m.campaign,referrer:xt(m.referrer)}:null})),...P}}async function Oo(e,t=v()){await Ae(e,t);let n=Ee(e.limit),i=ve(e.siteId,e.cursor),s=(await t.query(`/* analyticsConfirmedJourneys:keyset */
|
|
519
519
|
SELECT p.id AS person_id, p.namespace_id, sessions.first_seen_at,
|
|
520
520
|
sessions.last_seen_at, COALESCE(sessions.session_count,0)::int AS session_count,
|
|
521
521
|
COALESCE(events.event_count,0)::int AS event_count,
|
|
@@ -539,7 +539,7 @@ import{a as Zt,s as tn}from"./chunk-WDB5CTRY.js";import{T as cn,a as de,b as Ne,
|
|
|
539
539
|
) conversions ON true
|
|
540
540
|
WHERE p.site_id=$1 AND n.status='approved'
|
|
541
541
|
AND ($2::timestamptz IS NULL OR (sessions.last_seen_at,p.id) < ($2::timestamptz,$3::uuid))
|
|
542
|
-
ORDER BY last_seen_at DESC, p.id DESC LIMIT $4`,[e.siteId,i?.at??null,i?.id??null,n+1])).rows.map(o=>({...o,_cursorAt:S(o.last_seen_at),_cursorId:o.person_id}));return{journeyTier:"confirmed",...be(e.siteId,s,n,o=>({journeyTier:"confirmed",personRef:O("person",e.siteId,o.person_id),firstSeenAt:S(o.first_seen_at),lastSeenAt:S(o.last_seen_at),sessions:o.session_count,events:o.event_count,conversions:o.conversion_count,revenue:o.revenue_minor===null?null:{valueMinor:Number(o.revenue_minor),currency:o.currency},evidenceClass:"deterministic"}))}}async function
|
|
542
|
+
ORDER BY last_seen_at DESC, p.id DESC LIMIT $4`,[e.siteId,i?.at??null,i?.id??null,n+1])).rows.map(o=>({...o,_cursorAt:S(o.last_seen_at),_cursorId:o.person_id}));return{journeyTier:"confirmed",...be(e.siteId,s,n,o=>({journeyTier:"confirmed",personRef:O("person",e.siteId,o.person_id),firstSeenAt:S(o.first_seen_at),lastSeenAt:S(o.last_seen_at),sessions:o.session_count,events:o.event_count,conversions:o.conversion_count,revenue:o.revenue_minor===null?null:{valueMinor:Number(o.revenue_minor),currency:o.currency},evidenceClass:"deterministic"}))}}async function Do(e,t=v()){await Ae(e,t);let n=Ee(e.limit),i=ve(e.siteId,e.cursor),s=(await t.query(`/* analyticsBestGuessJourneys:keyset */
|
|
543
543
|
SELECT ca.id AS association_id, ca.namespace_id, ca.model_version, ca.expires_at, ca.updated_at,
|
|
544
544
|
ca.left_identity_node_id AS left_node_id, ca.right_identity_node_id AS right_node_id,
|
|
545
545
|
COALESCE(stats.confirmed_sessions,0)::int AS confirmed_sessions,
|
|
@@ -593,7 +593,7 @@ import{a as Zt,s as tn}from"./chunk-WDB5CTRY.js";import{T as cn,a as de,b as Ne,
|
|
|
593
593
|
WHERE ca.site_id=$1 AND n.status='approved' AND ca.status='active'
|
|
594
594
|
AND ca.confidence_band='high' AND ca.expires_at > now()
|
|
595
595
|
AND ($2::timestamptz IS NULL OR (ca.updated_at,ca.id) < ($2::timestamptz,$3::uuid))
|
|
596
|
-
ORDER BY ca.updated_at DESC, ca.id DESC LIMIT $4`,[e.siteId,i?.at??null,i?.id??null,n+1])).rows.map(o=>({...o,_cursorAt:S(o.updated_at),_cursorId:o.association_id}));return{journeyTier:"candidate_assisted",totalsAreSeparated:!0,...be(e.siteId,s,n,o=>({journeyTier:"candidate_assisted",inferenceRef:O("candidate",e.siteId,o.association_id),confirmedTotals:{sessions:o.confirmed_sessions,events:o.confirmed_events,conversions:o.confirmed_conversions,revenue:o.confirmed_revenue_minor===null?null:{valueMinor:Number(o.confirmed_revenue_minor),currency:o.currency}},inferredIncrementalTotals:{sessions:o.inferred_sessions,events:o.inferred_events,conversions:o.inferred_conversions,revenue:o.inferred_revenue_minor===null?null:{valueMinor:Number(o.inferred_revenue_minor),currency:o.currency}},candidateCount:1,confidenceBand:"high",modelVersion:o.model_version,featureClasses:o.feature_kinds,coverage:o.inferred_sessions>0?"incremental_sessions_available":"no_incremental_sessions",expiresAt:S(o.expires_at),provenance:"consented_high_confidence_candidate_association"}))}}async function
|
|
596
|
+
ORDER BY ca.updated_at DESC, ca.id DESC LIMIT $4`,[e.siteId,i?.at??null,i?.id??null,n+1])).rows.map(o=>({...o,_cursorAt:S(o.updated_at),_cursorId:o.association_id}));return{journeyTier:"candidate_assisted",totalsAreSeparated:!0,...be(e.siteId,s,n,o=>({journeyTier:"candidate_assisted",inferenceRef:O("candidate",e.siteId,o.association_id),confirmedTotals:{sessions:o.confirmed_sessions,events:o.confirmed_events,conversions:o.confirmed_conversions,revenue:o.confirmed_revenue_minor===null?null:{valueMinor:Number(o.confirmed_revenue_minor),currency:o.currency}},inferredIncrementalTotals:{sessions:o.inferred_sessions,events:o.inferred_events,conversions:o.inferred_conversions,revenue:o.inferred_revenue_minor===null?null:{valueMinor:Number(o.inferred_revenue_minor),currency:o.currency}},candidateCount:1,confidenceBand:"high",modelVersion:o.model_version,featureClasses:o.feature_kinds,coverage:o.inferred_sessions>0?"incremental_sessions_available":"no_incremental_sessions",expiresAt:S(o.expires_at),provenance:"consented_high_confidence_candidate_association"}))}}async function To(e,t=v()){await Ae(e,t);let n=Ee(e.limit),i=ve(e.siteId,e.cursor),s=(await t.query(`/* analyticsInferenceReview:keyset */
|
|
597
597
|
SELECT ca.id AS association_id, ca.updated_at, ca.first_observed_at, ca.last_observed_at,
|
|
598
598
|
ca.expires_at, ca.model_version, ca.confidence_band,
|
|
599
599
|
COALESCE(array_agg(DISTINCT ce.feature_kind ORDER BY ce.feature_kind)
|
|
@@ -608,30 +608,30 @@ import{a as Zt,s as tn}from"./chunk-WDB5CTRY.js";import{T as cn,a as de,b as Ne,
|
|
|
608
608
|
GROUP BY ca.id
|
|
609
609
|
ORDER BY ca.updated_at DESC, ca.id DESC LIMIT $4`,[e.siteId,i?.at??null,i?.id??null,n+1])).rows.map(a=>({...a,_cursorAt:S(a.updated_at),_cursorId:a.association_id}));return be(e.siteId,s,n,a=>({inferenceRef:O("candidate",e.siteId,a.association_id),confidenceBand:a.confidence_band,modelVersion:a.model_version,featureClasses:a.feature_kinds,firstObservedAt:S(a.first_observed_at),lastObservedAt:S(a.last_observed_at),expiresAt:S(a.expires_at),action:"diagnostic_only",eligibleForConfirmedMetrics:!1,eligibleForCrmOrActivation:!1}))}import{createHash as zr,createVerify as Yr,verify as Xr}from"crypto";function ei(e){if(!ge(e.timestamp,e.now))return!1;let t=`${e.method.toUpperCase()}${e.url}${e.rawBody}${e.timestamp}`;return ye(e.signature,pe("sha256",e.clientSecret,t))}var Gr=`-----BEGIN PUBLIC KEY-----
|
|
610
610
|
MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
611
|
-
-----END PUBLIC KEY-----`;function ti(e){try{if(e.ghlSignature&&e.ghlSignature!=="N/A")return Xr(null,Buffer.from(e.rawBody,"utf8"),e.ed25519PublicKey||Gr,Buffer.from(e.ghlSignature,"base64"));if(e.legacySignature&&e.legacySignature!=="N/A"&&e.legacyRsaPublicKey){let t=Yr("SHA256");return t.update(e.rawBody),t.verify(e.legacyRsaPublicKey,e.legacySignature,"base64")}return!1}catch{return!1}}function $t(e){return{schemaVersion:1,eventId:`crm_${zr("sha256").update(`${e.source}:${e.sourceEventId}`).digest("hex").slice(0,24)}`,sourceEventId:e.sourceEventId,source:e.source,sourceAccountRef:e.sourceAccountRef,eventKind:"crm",eventName:"crm_stage_changed",occurredAt:e.occurredAt,visitorId:e.visitorId,sessionId:e.sessionId,personRef:e.contactId,dealId:e.dealId,orderId:e.orderId,valueMinor:e.valueMinor,currency:e.currency,clickIds:e.clickIds,properties:{pipelineId:e.pipelineId||null,stageId:e.stageId,stageName:e.stageName||null,status:e.status}}}function ni(e){let t=e.item,n=p(t.objectId,240)||"unknown",i=p(t.propertyValue,240)||"unknown";return{sourceEventId:`deal:${n}:stage:${i}:${p(t.occurredAt,100)||"unknown"}`,source:"hubspot",sourceAccountRef:e.sourceAccountRef,dealId:n,stageId:i,stageName:e.stageName,occurredAt:B(t.occurredAt),valueMinor:e.valueMinor,currency:e.currency,status:/closed.?won/i.test(i)?"won":/closed.?lost/i.test(i)?"lost":"open"}}function ii(e){let t=e.payload,n=t.opportunity&&typeof t.opportunity=="object"?t.opportunity:t,i=p(n.id??t.id,240)||"unknown",r=p(n.pipelineStageId??t.pipelineStageId,240)||"unknown",s=(p(n.status??t.status,80)||"open").toLowerCase();return{sourceEventId:`opportunity:${i}:stage:${r}:${p(t.timestamp??t.dateAdded,100)||"unknown"}`,source:"highlevel",sourceAccountRef:e.sourceAccountRef,contactId:p(n.contactId??t.contactId,240),dealId:i,pipelineId:p(n.pipelineId??t.pipelineId,240),stageId:r,stageName:p(n.pipelineStageName??t.pipelineStageName,240),occurredAt:B(t.timestamp,t.dateAdded,n.updatedAt),valueMinor:Math.round((W(n.monetaryValue??t.monetaryValue)??0)*100),currency:p(n.currency??t.currency,3)?.toUpperCase(),status:s.includes("won")?"won":s.includes("lost")?"lost":s.includes("spam")?"spam":"open"}}var ts=90;function si(e){let t=e.consent;if(!t)return{necessary:!0,siteAnalytics:!1,enhancedMatching:!1,advertisingMeasurement:!1,receiptId:null,reason:"missing"};if(t.globalPrivacyControl)return{necessary:!0,siteAnalytics:!1,enhancedMatching:!1,advertisingMeasurement:!1,receiptId:t.receiptId??null,reason:"gpc"};let n=t.purposes,i={necessary:!0,siteAnalytics:n.site_analytics==="granted",enhancedMatching:n.enhanced_matching==="granted",advertisingMeasurement:n.advertising_measurement==="granted",receiptId:t.receiptId??null};return{...i,reason:i.siteAnalytics||i.enhancedMatching||i.advertisingMeasurement?"granted":"denied"}}function ns(e){let t=[];return e.confirmedPeople<e.minimumConfirmedPeople&&t.push(`confirmed_people:${e.confirmedPeople}/${e.minimumConfirmedPeople}`),e.positiveOutcomes<e.minimumPositiveOutcomes&&t.push(`positive_outcomes:${e.positiveOutcomes}/${e.minimumPositiveOutcomes}`),e.validatedManifest||t.push("validated_calibrated_manifest"),{...e,eligible:t.length===0,missingEvidence:t}}var is={callrail:{expectedEventTypes:["call"],expectedEventPhases:["pre_call","routing_complete","post_call","call_modified"],signatureMode:"callrail_hmac_sha1_exact_body",freshnessSlaSeconds:86400,supportsReconciliation:!0},calltrackingmetrics:{expectedEventTypes:["call"],expectedEventPhases:["call_started","call_answered","call_complete","call_modified"],signatureMode:"ctm_hmac_sha1_time_and_exact_body",freshnessSlaSeconds:86400,supportsReconciliation:!1},twilio:{expectedEventTypes:["call_status"],expectedEventPhases:["initiated","ringing","in-progress","completed","busy","failed","no-answer","canceled"],signatureMode:"twilio_sdk_exact_url_and_form_body",freshnessSlaSeconds:86400,supportsReconciliation:!0},hubspot:{expectedEventTypes:["deal.propertyChange"],expectedEventPhases:["dealstage"],signatureMode:"hubspot_v3_hmac_sha256_exact_url_body_timestamp",freshnessSlaSeconds:86400,supportsReconciliation:!1},highlevel:{expectedEventTypes:["OpportunityStageUpdate"],expectedEventPhases:["open","won","lost","spam"],signatureMode:"highlevel_ed25519_or_legacy_rsa",freshnessSlaSeconds:86400,supportsReconciliation:!1},generic_crm:{expectedEventTypes:["crm_stage_changed","transaction","custom_server"],expectedEventPhases:["open","qualified","won","lost","spam"],signatureMode:"xray_hmac_sha256_timestamp_and_exact_body",freshnessSlaSeconds:86400,supportsReconciliation:!1}},rs=/(^|_)(api_?key|access_?key|api_?token|auth_?token|token|password|private_?key|secret|signing_?key)($|_)/i;function ss(e){let t=(n,i)=>{if(!(!n||typeof n!="object"))for(let[r,s]of Object.entries(n)){let a=i?`${i}.${r}`:r,o=r.replace(/([a-z0-9])([A-Z])/g,"$1_$2").toLowerCase();if(rs.test(o)&&!/public_?key/i.test(o))throw new f("analytics_connection_config_contains_credential",`Connection config cannot contain credentials (${a}). Use the encrypted webhook secret or service connection instead.`,400);t(s,a)}};t(e,"")}function
|
|
611
|
+
-----END PUBLIC KEY-----`;function ti(e){try{if(e.ghlSignature&&e.ghlSignature!=="N/A")return Xr(null,Buffer.from(e.rawBody,"utf8"),e.ed25519PublicKey||Gr,Buffer.from(e.ghlSignature,"base64"));if(e.legacySignature&&e.legacySignature!=="N/A"&&e.legacyRsaPublicKey){let t=Yr("SHA256");return t.update(e.rawBody),t.verify(e.legacyRsaPublicKey,e.legacySignature,"base64")}return!1}catch{return!1}}function $t(e){return{schemaVersion:1,eventId:`crm_${zr("sha256").update(`${e.source}:${e.sourceEventId}`).digest("hex").slice(0,24)}`,sourceEventId:e.sourceEventId,source:e.source,sourceAccountRef:e.sourceAccountRef,eventKind:"crm",eventName:"crm_stage_changed",occurredAt:e.occurredAt,visitorId:e.visitorId,sessionId:e.sessionId,personRef:e.contactId,dealId:e.dealId,orderId:e.orderId,valueMinor:e.valueMinor,currency:e.currency,clickIds:e.clickIds,properties:{pipelineId:e.pipelineId||null,stageId:e.stageId,stageName:e.stageName||null,status:e.status}}}function ni(e){let t=e.item,n=p(t.objectId,240)||"unknown",i=p(t.propertyValue,240)||"unknown";return{sourceEventId:`deal:${n}:stage:${i}:${p(t.occurredAt,100)||"unknown"}`,source:"hubspot",sourceAccountRef:e.sourceAccountRef,dealId:n,stageId:i,stageName:e.stageName,occurredAt:B(t.occurredAt),valueMinor:e.valueMinor,currency:e.currency,status:/closed.?won/i.test(i)?"won":/closed.?lost/i.test(i)?"lost":"open"}}function ii(e){let t=e.payload,n=t.opportunity&&typeof t.opportunity=="object"?t.opportunity:t,i=p(n.id??t.id,240)||"unknown",r=p(n.pipelineStageId??t.pipelineStageId,240)||"unknown",s=(p(n.status??t.status,80)||"open").toLowerCase();return{sourceEventId:`opportunity:${i}:stage:${r}:${p(t.timestamp??t.dateAdded,100)||"unknown"}`,source:"highlevel",sourceAccountRef:e.sourceAccountRef,contactId:p(n.contactId??t.contactId,240),dealId:i,pipelineId:p(n.pipelineId??t.pipelineId,240),stageId:r,stageName:p(n.pipelineStageName??t.pipelineStageName,240),occurredAt:B(t.timestamp,t.dateAdded,n.updatedAt),valueMinor:Math.round((W(n.monetaryValue??t.monetaryValue)??0)*100),currency:p(n.currency??t.currency,3)?.toUpperCase(),status:s.includes("won")?"won":s.includes("lost")?"lost":s.includes("spam")?"spam":"open"}}var ts=90;function si(e){let t=e.consent;if(!t)return{necessary:!0,siteAnalytics:!1,enhancedMatching:!1,advertisingMeasurement:!1,receiptId:null,reason:"missing"};if(t.globalPrivacyControl)return{necessary:!0,siteAnalytics:!1,enhancedMatching:!1,advertisingMeasurement:!1,receiptId:t.receiptId??null,reason:"gpc"};let n=t.purposes,i={necessary:!0,siteAnalytics:n.site_analytics==="granted",enhancedMatching:n.enhanced_matching==="granted",advertisingMeasurement:n.advertising_measurement==="granted",receiptId:t.receiptId??null};return{...i,reason:i.siteAnalytics||i.enhancedMatching||i.advertisingMeasurement?"granted":"denied"}}function ns(e){let t=[];return e.confirmedPeople<e.minimumConfirmedPeople&&t.push(`confirmed_people:${e.confirmedPeople}/${e.minimumConfirmedPeople}`),e.positiveOutcomes<e.minimumPositiveOutcomes&&t.push(`positive_outcomes:${e.positiveOutcomes}/${e.minimumPositiveOutcomes}`),e.validatedManifest||t.push("validated_calibrated_manifest"),{...e,eligible:t.length===0,missingEvidence:t}}var is={callrail:{expectedEventTypes:["call"],expectedEventPhases:["pre_call","routing_complete","post_call","call_modified"],signatureMode:"callrail_hmac_sha1_exact_body",freshnessSlaSeconds:86400,supportsReconciliation:!0},calltrackingmetrics:{expectedEventTypes:["call"],expectedEventPhases:["call_started","call_answered","call_complete","call_modified"],signatureMode:"ctm_hmac_sha1_time_and_exact_body",freshnessSlaSeconds:86400,supportsReconciliation:!1},twilio:{expectedEventTypes:["call_status"],expectedEventPhases:["initiated","ringing","in-progress","completed","busy","failed","no-answer","canceled"],signatureMode:"twilio_sdk_exact_url_and_form_body",freshnessSlaSeconds:86400,supportsReconciliation:!0},hubspot:{expectedEventTypes:["deal.propertyChange"],expectedEventPhases:["dealstage"],signatureMode:"hubspot_v3_hmac_sha256_exact_url_body_timestamp",freshnessSlaSeconds:86400,supportsReconciliation:!1},highlevel:{expectedEventTypes:["OpportunityStageUpdate"],expectedEventPhases:["open","won","lost","spam"],signatureMode:"highlevel_ed25519_or_legacy_rsa",freshnessSlaSeconds:86400,supportsReconciliation:!1},generic_crm:{expectedEventTypes:["crm_stage_changed","transaction","custom_server"],expectedEventPhases:["open","qualified","won","lost","spam"],signatureMode:"xray_hmac_sha256_timestamp_and_exact_body",freshnessSlaSeconds:86400,supportsReconciliation:!1}},rs=/(^|_)(api_?key|access_?key|api_?token|auth_?token|token|password|private_?key|secret|signing_?key)($|_)/i;function ss(e){let t=(n,i)=>{if(!(!n||typeof n!="object"))for(let[r,s]of Object.entries(n)){let a=i?`${i}.${r}`:r,o=r.replace(/([a-z0-9])([A-Z])/g,"$1_$2").toLowerCase();if(rs.test(o)&&!/public_?key/i.test(o))throw new f("analytics_connection_config_contains_credential",`Connection config cannot contain credentials (${a}). Use the encrypted webhook secret or service connection instead.`,400);t(s,a)}};t(e,"")}function Qo(e){return e instanceof Error&&e.message==="webhook_secret_unavailable"?"webhook_secret_unavailable":e instanceof f?e.code.slice(0,80):"invalid_webhook_payload"}function Pt(e){let t=e.provider,n=is[t];if(!n)throw new f("analytics_connection_provider_unavailable","This analytics connection is not available in the current public release.",404);return{callbackPath:`/analytics/v1/webhooks/${t}/${e.connectionId}`,expectedEventTypes:[...n.expectedEventTypes],expectedEventPhases:[...n.expectedEventPhases],signatureMode:n.signatureMode,freshnessSlaSeconds:n.freshnessSlaSeconds,reconciliationCapability:n.supportsReconciliation?e.serviceConnectionRef?"available":"connection_required":"webhook_only"}}function Zo(e,t){let n=String(e.id),i=String(e.provider),r=Pt({provider:i,connectionId:n,serviceConnectionRef:typeof e.service_connection_ref=="string"?e.service_connection_ref:null}),s=String(e.readiness??"configured_unverified"),a=e.verification_evidence==="provider"||e.verification_evidence==="synthetic"?e.verification_evidence:null,o=!!e.webhook_secret_configured,c=e.last_event_at?String(e.last_event_at):null,d=s==="live"&&c?Date.now()-Date.parse(c)>r.freshnessSlaSeconds*1e3:!1;return{...e,callbackUrl:new URL(r.callbackPath,`${t.replace(/\/$/,"")}/`).toString(),expectedEventTypes:r.expectedEventTypes,expectedEventPhases:r.expectedEventPhases,signatureMode:r.signatureMode,freshnessSlaSeconds:r.freshnessSlaSeconds,freshness:d?"stale":c?"fresh":"awaiting_first_event",reconciliationCapability:r.reconciliationCapability,lastVerifiedReceipt:e.last_verified_receipt_id?{receiptId:String(e.last_verified_receipt_id),evidence:a,verifiedAt:e.last_verified_at?String(e.last_verified_at):null}:a?{receiptId:null,evidence:a,verifiedAt:e.last_verified_at?String(e.last_verified_at):null}:null,safeError:e.last_error_code?{code:String(e.last_error_code)}:null,setupChecklist:{callbackUrlGenerated:!0,callbackConfigured:a==="provider",signatureConfigured:i==="highlevel"||o,syntheticTestPassed:a==="synthetic"||a==="provider",providerReceiptReceived:a==="provider"},evidenceStillMissing:a==="provider"?[]:["Send a real provider-signed event to the callback URL to mark this connection live."]}}async function ec(e){let t=At.parse(e.definition),n=v();await J(n,e.siteId,e.userId);let i=t.enabled?"active":"disabled";return(await n.query(`INSERT INTO analytics_event_definitions(
|
|
612
612
|
id, site_id, name, event_name, trigger_kind, definition, status, created_by_user_id
|
|
613
613
|
) VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7,$8)
|
|
614
614
|
ON CONFLICT(site_id, name) DO UPDATE SET event_name=EXCLUDED.event_name,
|
|
615
615
|
trigger_kind=EXCLUDED.trigger_kind, definition=EXCLUDED.definition,
|
|
616
616
|
status=EXCLUDED.status, version=analytics_event_definitions.version+1, updated_at=now()
|
|
617
|
-
RETURNING id, name, event_name, trigger_kind, definition, version, status, created_at, updated_at`,[se(),e.siteId,t.name,t.eventName,t.triggerKind,JSON.stringify(t),i,e.userId])).rows[0]}async function
|
|
617
|
+
RETURNING id, name, event_name, trigger_kind, definition, version, status, created_at, updated_at`,[se(),e.siteId,t.name,t.eventName,t.triggerKind,JSON.stringify(t),i,e.userId])).rows[0]}async function tc(e,t){let n=v();return await U(n,e,t),(await n.query(`SELECT id, name, event_name, trigger_kind, definition, version, status, created_at, updated_at
|
|
618
618
|
FROM analytics_event_definitions WHERE site_id=$1 AND status <> 'archived'
|
|
619
619
|
ORDER BY created_at DESC`,[e])).rows}async function Mt(e){let t=v();e.requireEditor?await J(t,e.siteId,e.userId):await U(t,e.siteId,e.userId);let i=(await t.query(`SELECT id, definition, version, status FROM analytics_event_definitions
|
|
620
|
-
WHERE id=$1 AND site_id=$2`,[e.definitionId,e.siteId])).rows[0];if(!i||i.status==="archived")throw new f("analytics_event_definition_not_found","Event definition not found.",404);return{db:t,row:i}}async function
|
|
620
|
+
WHERE id=$1 AND site_id=$2`,[e.definitionId,e.siteId])).rows[0];if(!i||i.status==="archived")throw new f("analytics_event_definition_not_found","Event definition not found.",404);return{db:t,row:i}}async function nc(e){let{db:t,row:n}=await Mt({...e,requireEditor:!0}),i=At.parse({...n.definition,...e.changes}),r=i.enabled?"active":"disabled";return(await t.query(`UPDATE analytics_event_definitions SET
|
|
621
621
|
name=$3, event_name=$4, trigger_kind=$5, definition=$6::jsonb,
|
|
622
622
|
status=$7, version=version+1, updated_at=now()
|
|
623
623
|
WHERE id=$1 AND site_id=$2
|
|
624
|
-
RETURNING id, name, event_name, trigger_kind, definition, version, status, created_at, updated_at`,[e.definitionId,e.siteId,i.name,i.eventName,i.triggerKind,JSON.stringify(i),r])).rows[0]}async function
|
|
624
|
+
RETURNING id, name, event_name, trigger_kind, definition, version, status, created_at, updated_at`,[e.definitionId,e.siteId,i.name,i.eventName,i.triggerKind,JSON.stringify(i),r])).rows[0]}async function ic(e){let t=v();await J(t,e.siteId,e.userId);let n=await t.query(`UPDATE analytics_event_definitions SET status='archived', updated_at=now()
|
|
625
625
|
WHERE id=$1 AND site_id=$2
|
|
626
|
-
RETURNING id, status`,[e.definitionId,e.siteId]);if(!n.rows[0])throw new f("analytics_event_definition_not_found","Event definition not found.",404);return n.rows[0]}function as(e,t,n){let i=t.hostnames.length===0||t.hostnames.includes(n.hostname),r=t.triggerKind===n.triggerKind,s=t.triggerKind==="page"?t.pathMatch==="prefix"?n.path.startsWith(t.path??"/"):n.path===t.path:null,a=t.triggerKind==="semantic"?n.tagEventName===t.eventName:null,o=t.triggerKind==="click"||t.triggerKind==="form"?n.selectorMatched===!0:null;return{definitionId:e,eventName:t.eventName,triggerKind:t.triggerKind,matched:t.enabled&&i&&r&&(s??a??o??!1),checks:{hostname:i,triggerKind:r,path:s,tag:a,selector:o}}}async function
|
|
626
|
+
RETURNING id, status`,[e.definitionId,e.siteId]);if(!n.rows[0])throw new f("analytics_event_definition_not_found","Event definition not found.",404);return n.rows[0]}function as(e,t,n){let i=t.hostnames.length===0||t.hostnames.includes(n.hostname),r=t.triggerKind===n.triggerKind,s=t.triggerKind==="page"?t.pathMatch==="prefix"?n.path.startsWith(t.path??"/"):n.path===t.path:null,a=t.triggerKind==="semantic"?n.tagEventName===t.eventName:null,o=t.triggerKind==="click"||t.triggerKind==="form"?n.selectorMatched===!0:null;return{definitionId:e,eventName:t.eventName,triggerKind:t.triggerKind,matched:t.enabled&&i&&r&&(s??a??o??!1),checks:{hostname:i,triggerKind:r,path:s,tag:a,selector:o}}}async function rc(e){let{row:t}=await Mt({...e,requireEditor:!1});return as(e.definitionId,t.definition,e.preview)}async function sc(e){let{db:t,row:n}=await Mt({...e,requireEditor:!1}),i=Math.max(0,Math.min(15e3,Math.trunc(e.timeoutMs??0))),r=Math.max(1,Math.min(86400,Math.trunc(e.maxAgeSeconds??300))),s=Date.now()+i;do{let o=(await t.query(`SELECT event_id, received_at
|
|
627
627
|
FROM analytics_events
|
|
628
628
|
WHERE site_id=$1 AND event_definition_id=$2 AND event_definition_version=$3
|
|
629
629
|
AND received_at >= now() - ($4 * interval '1 second')
|
|
630
630
|
ORDER BY received_at DESC, id DESC
|
|
631
|
-
LIMIT 1`,[e.siteId,e.definitionId,n.version,r])).rows[0];if(o)return{status:"observed_live_match",definitionId:e.definitionId,definitionVersion:n.version,eventId:o.event_id,observedAt:new Date(o.received_at).toISOString(),maxAgeSeconds:r};let c=s-Date.now();if(c<=0)break;await new Promise(d=>setTimeout(d,Math.min(500,c)))}while(Date.now()<=s);return{status:"not_observed",definitionId:e.definitionId,definitionVersion:n.version,eventId:null,observedAt:null,maxAgeSeconds:r}}async function
|
|
631
|
+
LIMIT 1`,[e.siteId,e.definitionId,n.version,r])).rows[0];if(o)return{status:"observed_live_match",definitionId:e.definitionId,definitionVersion:n.version,eventId:o.event_id,observedAt:new Date(o.received_at).toISOString(),maxAgeSeconds:r};let c=s-Date.now();if(c<=0)break;await new Promise(d=>setTimeout(d,Math.min(500,c)))}while(Date.now()<=s);return{status:"not_observed",definitionId:e.definitionId,definitionVersion:n.version,eventId:null,observedAt:null,maxAgeSeconds:r}}async function ac(e){let t=await v().query(`SELECT s.owner_user_id, s.id AS site_id, d.id, d.version, d.definition
|
|
632
632
|
FROM analytics_pixels p JOIN analytics_sites s ON s.id=p.site_id
|
|
633
633
|
LEFT JOIN analytics_event_definitions d ON d.site_id=s.id AND d.status='active'
|
|
634
|
-
WHERE p.public_id=$1 AND p.status='active'`,[e]);return{ownerUserId:t.rows[0]?Number(t.rows[0].owner_user_id):null,siteId:t.rows[0]?.site_id??null,definitions:t.rows.filter(n=>n.id&&n.version&&n.definition).map(n=>({...n.definition,id:n.id,version:n.version}))}}async function
|
|
634
|
+
WHERE p.public_id=$1 AND p.status='active'`,[e]);return{ownerUserId:t.rows[0]?Number(t.rows[0].owner_user_id):null,siteId:t.rows[0]?.site_id??null,definitions:t.rows.filter(n=>n.id&&n.version&&n.definition).map(n=>({...n.definition,id:n.id,version:n.version}))}}async function oc(e){ss(e.config??{});let t=v();await J(t,e.siteId,e.userId);let n=e.webhookSecret?de(e.webhookSecret,`connection-secret:${e.provider}:${e.sourceAccountRef}`):null;return(await t.query(`INSERT INTO analytics_connections(
|
|
635
635
|
id, site_id, provider, name, source_account_ref, service_connection_ref,
|
|
636
636
|
secret_ciphertext, config, created_by_user_id
|
|
637
637
|
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb,$9)
|
|
@@ -646,17 +646,17 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
646
646
|
readiness, last_verified_at, last_event_at, last_reconciled_at, last_error_code,
|
|
647
647
|
verification_evidence, last_verified_receipt_id,
|
|
648
648
|
(secret_ciphertext IS NOT NULL) AS webhook_secret_configured,
|
|
649
|
-
created_at, updated_at`,[se(),e.siteId,e.provider.trim().toLowerCase().slice(0,80),e.name.trim().slice(0,120),e.sourceAccountRef.trim().slice(0,240),e.serviceConnectionRef?.trim().slice(0,240)||null,n,JSON.stringify(e.config??{}),e.userId])).rows[0]}async function
|
|
649
|
+
created_at, updated_at`,[se(),e.siteId,e.provider.trim().toLowerCase().slice(0,80),e.name.trim().slice(0,120),e.sourceAccountRef.trim().slice(0,240),e.serviceConnectionRef?.trim().slice(0,240)||null,n,JSON.stringify(e.config??{}),e.userId])).rows[0]}async function cc(e,t){let n=v();return await U(n,e,t),(await n.query(`SELECT id, site_id, provider, name, source_account_ref, service_connection_ref,
|
|
650
650
|
readiness, last_verified_at, last_event_at, last_reconciled_at,
|
|
651
651
|
last_error_code, verification_evidence, last_verified_receipt_id,
|
|
652
652
|
created_at, updated_at,
|
|
653
653
|
(secret_ciphertext IS NOT NULL) AS webhook_secret_configured
|
|
654
|
-
FROM analytics_connections WHERE site_id = $1 ORDER BY created_at`,[e])).rows}async function
|
|
654
|
+
FROM analytics_connections WHERE site_id = $1 ORDER BY created_at`,[e])).rows}async function dc(e){let n=(await v().query(`SELECT id, site_id, provider, source_account_ref, secret_ciphertext,
|
|
655
655
|
readiness, config
|
|
656
656
|
FROM analytics_connections WHERE id = $1 AND readiness <> 'disabled'`,[e])).rows[0];if(!n)return null;let i=`connection-secret:${n.provider}:${n.source_account_ref}`;return{id:n.id,siteId:n.site_id,provider:n.provider,sourceAccountRef:n.source_account_ref,secret:n.secret_ciphertext?Ne(n.secret_ciphertext,i):null,readiness:n.readiness,config:n.config||{}}}async function ai(e){let t=v();await J(t,e.siteId,e.userId);let i=(await t.query(`SELECT id, site_id, provider, source_account_ref, service_connection_ref,
|
|
657
657
|
secret_ciphertext, readiness, config
|
|
658
658
|
FROM analytics_connections
|
|
659
|
-
WHERE id=$1 AND site_id=$2 AND readiness <> 'disabled'`,[e.connectionId,e.siteId])).rows[0];if(!i)throw new f("analytics_connection_not_found","Analytics connection not found.",404);return i}function os(e){return e.secret_ciphertext?Ne(e.secret_ciphertext,`connection-secret:${e.provider}:${e.source_account_ref}`):null}function cs(e){let t=e.now??Date.now(),n=new Date(t).toISOString(),i=e.secret||"xray-ephemeral-synthetic-fixture-secret";if(e.provider==="callrail"){let c={resource_id:"xray-test-call",timestamp:n,answered:!0,duration:30},d=JSON.stringify(c);return{signatureVerified:Jn({rawBody:d,signature:e.tamperSignature?"invalid":kt("sha1",i).update(d).digest("base64"),secret:i,payloadTimestamp:n,now:t}),event:zn({payload:c,sourceAccountRef:e.sourceAccountRef,phase:"post_call"})}}if(e.provider==="calltrackingmetrics"){let c={activity_id:"xray-test-call",timestamp:n,status:"completed"},d=JSON.stringify(c),_=String(Math.floor(t/1e3));return{signatureVerified:Yn({rawBody:d,requestTime:_,signature:e.tamperSignature?"invalid":kt("sha1",i).update(_+d).digest("base64"),secret:i,now:t}),event:Xn({payload:c,sourceAccountRef:e.sourceAccountRef,phase:"call_complete"})}}if(e.provider==="twilio"){let c={CallSid:"CA_XRAY_TEST",CallStatus:"completed",Timestamp:n},d=e.tamperSignature?"invalid":es.getExpectedTwilioSignature(i,e.callbackUrl,c);return{signatureVerified:Gn({authToken:i,signature:d,url:e.callbackUrl,params:c}),event:Qn({params:c,sourceAccountRef:e.sourceAccountRef})}}if(e.provider==="hubspot"){let c={subscriptionType:"deal.propertyChange",propertyName:"dealstage",objectId:"xray-test-deal",propertyValue:"qualified",occurredAt:String(t)},d=JSON.stringify([c]),_=String(t),u=kt("sha256",i).update(`POST${e.callbackUrl}${d}${_}`).digest("base64"),y=e.tamperSignature?"invalid":u;return{signatureVerified:ei({clientSecret:i,signature:y,timestamp:_,method:"POST",url:e.callbackUrl,rawBody:d,now:t}),event:$t(ni({item:c,sourceAccountRef:e.sourceAccountRef}))}}if(e.provider==="highlevel"){let c={timestamp:n,opportunity:{id:"xray-test-opportunity",pipelineStageId:"qualified",status:"open"}},d=JSON.stringify(c),{privateKey:_,publicKey:u}=Qr("ed25519"),y=e.tamperSignature?Buffer.alloc(64).toString("base64"):Zr(null,Buffer.from(d),_).toString("base64");return{signatureVerified:ti({rawBody:d,ghlSignature:y,ed25519PublicKey:u.export({type:"spki",format:"pem"}).toString()}),event:$t(ii({payload:c,sourceAccountRef:e.sourceAccountRef}))}}let r=
|
|
659
|
+
WHERE id=$1 AND site_id=$2 AND readiness <> 'disabled'`,[e.connectionId,e.siteId])).rows[0];if(!i)throw new f("analytics_connection_not_found","Analytics connection not found.",404);return i}function os(e){return e.secret_ciphertext?Ne(e.secret_ciphertext,`connection-secret:${e.provider}:${e.source_account_ref}`):null}function cs(e){let t=e.now??Date.now(),n=new Date(t).toISOString(),i=e.secret||"xray-ephemeral-synthetic-fixture-secret";if(e.provider==="callrail"){let c={resource_id:"xray-test-call",timestamp:n,answered:!0,duration:30},d=JSON.stringify(c);return{signatureVerified:Jn({rawBody:d,signature:e.tamperSignature?"invalid":kt("sha1",i).update(d).digest("base64"),secret:i,payloadTimestamp:n,now:t}),event:zn({payload:c,sourceAccountRef:e.sourceAccountRef,phase:"post_call"})}}if(e.provider==="calltrackingmetrics"){let c={activity_id:"xray-test-call",timestamp:n,status:"completed"},d=JSON.stringify(c),_=String(Math.floor(t/1e3));return{signatureVerified:Yn({rawBody:d,requestTime:_,signature:e.tamperSignature?"invalid":kt("sha1",i).update(_+d).digest("base64"),secret:i,now:t}),event:Xn({payload:c,sourceAccountRef:e.sourceAccountRef,phase:"call_complete"})}}if(e.provider==="twilio"){let c={CallSid:"CA_XRAY_TEST",CallStatus:"completed",Timestamp:n},d=e.tamperSignature?"invalid":es.getExpectedTwilioSignature(i,e.callbackUrl,c);return{signatureVerified:Gn({authToken:i,signature:d,url:e.callbackUrl,params:c}),event:Qn({params:c,sourceAccountRef:e.sourceAccountRef})}}if(e.provider==="hubspot"){let c={subscriptionType:"deal.propertyChange",propertyName:"dealstage",objectId:"xray-test-deal",propertyValue:"qualified",occurredAt:String(t)},d=JSON.stringify([c]),_=String(t),u=kt("sha256",i).update(`POST${e.callbackUrl}${d}${_}`).digest("base64"),y=e.tamperSignature?"invalid":u;return{signatureVerified:ei({clientSecret:i,signature:y,timestamp:_,method:"POST",url:e.callbackUrl,rawBody:d,now:t}),event:$t(ni({item:c,sourceAccountRef:e.sourceAccountRef}))}}if(e.provider==="highlevel"){let c={timestamp:n,opportunity:{id:"xray-test-opportunity",pipelineStageId:"qualified",status:"open"}},d=JSON.stringify(c),{privateKey:_,publicKey:u}=Qr("ed25519"),y=e.tamperSignature?Buffer.alloc(64).toString("base64"):Zr(null,Buffer.from(d),_).toString("base64");return{signatureVerified:ti({rawBody:d,ghlSignature:y,ed25519PublicKey:u.export({type:"spki",format:"pem"}).toString()}),event:$t(ii({payload:c,sourceAccountRef:e.sourceAccountRef}))}}let r=Ye.parse({schemaVersion:1,eventId:"generic_xray_test",sourceEventId:"xray-test-event",source:"generic_crm",sourceAccountRef:e.sourceAccountRef,eventKind:"crm",eventName:"crm_stage_changed",occurredAt:n,dealId:"xray-test-deal",properties:{stageId:"qualified",status:"open"}}),s=JSON.stringify(r),a=String(t),o=an(e.provider,a,s,i);return{signatureVerified:on(e.tamperSignature?`${o.startsWith("0")?"1":"0"}${o.slice(1)}`:o,o),event:r}}async function lc(e){let t=await ai(e),n=os(t);if(t.provider!=="highlevel"&&!n)throw new f("analytics_connection_secret_unavailable","Configure the provider webhook validation secret before running a connection test.",409);let i=Pt({provider:t.provider,connectionId:t.id,serviceConnectionRef:t.service_connection_ref}),r=new URL(i.callbackPath,`${e.requestOrigin.replace(/\/$/,"")}/`).toString(),s=cs({provider:t.provider,sourceAccountRef:t.source_account_ref,callbackUrl:r,secret:n});if(!s.signatureVerified)throw await ri({connectionId:t.id,readiness:"degraded",errorCode:"synthetic_signature_test_failed"}),new f("analytics_connection_test_failed","Connection signature test failed.",409);let a=await ri({connectionId:t.id,readiness:"verified",evidence:"synthetic"}),o=a?.verification_evidence??"synthetic";return{status:a?.readiness??"verified",evidence:o,provider:t.provider,eventType:s.event.eventKind,eventPhase:s.event.eventName,connection:a,evidenceStillMissing:o==="provider"?[]:["A real provider-signed receipt is required before this connection becomes live."]}}async function uc(e){let t=await ai(e),n=Pt({provider:t.provider,connectionId:t.id,serviceConnectionRef:t.service_connection_ref});return{capability:n.reconciliationCapability,started:!1,evidence:null,nextAction:n.reconciliationCapability==="available"?"Reconciliation is available; the reconciliation worker must claim and execute this request.":n.reconciliationCapability==="connection_required"?"Connect the provider read API before reconciliation can run.":"This provider is webhook only; use webhook retries or historical backfill."}}async function _c(e){let t=se(),n=e.rawBody?de(e.rawBody,"webhook-payload"):null,i=await v().query(`INSERT INTO analytics_webhook_receipts(
|
|
660
660
|
id, site_id, connection_id, provider, source_account_ref, source_event_id,
|
|
661
661
|
signature_status, payload_ciphertext, payload_expires_at
|
|
662
662
|
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,now() + interval '7 days')
|
|
@@ -665,7 +665,7 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
665
665
|
WHEN analytics_webhook_receipts.normalization_status = 'accepted' THEN 'replayed'
|
|
666
666
|
ELSE analytics_webhook_receipts.normalization_status
|
|
667
667
|
END
|
|
668
|
-
RETURNING id, normalization_status`,[t,e.siteId,e.connectionId,e.provider,e.sourceAccountRef,e.sourceEventId,e.signatureStatus,n]);return{id:i.rows[0].id,duplicate:i.rows[0].id!==t,status:i.rows[0].normalization_status}}async function
|
|
668
|
+
RETURNING id, normalization_status`,[t,e.siteId,e.connectionId,e.provider,e.sourceAccountRef,e.sourceEventId,e.signatureStatus,n]);return{id:i.rows[0].id,duplicate:i.rows[0].id!==t,status:i.rows[0].normalization_status}}async function mc(e){await v().query(`UPDATE analytics_webhook_receipts SET
|
|
669
669
|
normalization_status = $2,
|
|
670
670
|
error_code = $3,
|
|
671
671
|
normalized_at = now()
|
|
@@ -683,7 +683,7 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
683
683
|
updated_at = now()
|
|
684
684
|
WHERE id = $1
|
|
685
685
|
RETURNING id, readiness, last_verified_at, last_event_at, last_reconciled_at,
|
|
686
|
-
last_error_code, verification_evidence, last_verified_receipt_id`,[e.connectionId,e.readiness,e.errorCode??null,!!e.receipt,!!e.reconciled,e.evidence??null,e.receiptId??null])).rows[0]??null}async function
|
|
686
|
+
last_error_code, verification_evidence, last_verified_receipt_id`,[e.connectionId,e.readiness,e.errorCode??null,!!e.receipt,!!e.reconciled,e.evidence??null,e.receiptId??null])).rows[0]??null}async function pc(e){let t=Sn.parse(e.rule),n=v();return await J(n,e.siteId,e.userId),(await n.query(`INSERT INTO analytics_conversion_rules(
|
|
687
687
|
id, site_id, name, conversion_kind, condition, default_value_minor,
|
|
688
688
|
default_currency, status, created_by_user_id
|
|
689
689
|
) VALUES ($1,$2,$3,$4,$5::jsonb,$6,$7,$8,$9)
|
|
@@ -695,7 +695,7 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
695
695
|
status = EXCLUDED.status,
|
|
696
696
|
version = analytics_conversion_rules.version + 1,
|
|
697
697
|
updated_at = now()
|
|
698
|
-
RETURNING *`,[se(),e.siteId,t.name,t.conversionKind,JSON.stringify({...t.condition,...t.valueRule?{valueRule:t.valueRule}:{}}),t.defaultValueMinor??null,t.defaultCurrency.toUpperCase(),t.enabled?"active":"disabled",e.userId])).rows[0]}async function
|
|
698
|
+
RETURNING *`,[se(),e.siteId,t.name,t.conversionKind,JSON.stringify({...t.condition,...t.valueRule?{valueRule:t.valueRule}:{}}),t.defaultValueMinor??null,t.defaultCurrency.toUpperCase(),t.enabled?"active":"disabled",e.userId])).rows[0]}async function yc(e,t){let n=v();return await U(n,e,t),(await n.query(`SELECT id, name, conversion_kind, condition, default_value_minor,
|
|
699
699
|
default_currency, version, status, created_at, updated_at
|
|
700
700
|
FROM analytics_conversion_rules WHERE site_id = $1 AND status <> 'archived'
|
|
701
701
|
ORDER BY created_at`,[e])).rows.map(r=>{let s=r.condition&&typeof r.condition=="object"?r.condition:{},{valueRule:a,...o}=s;return{...r,condition:o,value_rule:a??null}})}function ds(e){let t=e.properties.tags;return Array.isArray(t)?t.filter(n=>typeof n=="string"):[]}function ls(e,t){let n=[],i=e.condition;i.eventName&&i.eventName!==t.eventName&&n.push("event_name_mismatch"),i.eventKind&&i.eventKind!==t.eventKind&&n.push("event_kind_mismatch"),i.source&&i.source!==t.source&&n.push("source_mismatch"),i.stageId&&i.stageId!==t.properties.stageId&&n.push("stage_mismatch"),i.status&&i.status!==t.properties.status&&n.push("status_mismatch"),i.answered!==void 0&&i.answered!==!!t.properties.answered&&n.push("answered_mismatch"),i.minimumCallDurationSeconds!==void 0&&Number(t.properties.durationSeconds??0)<i.minimumCallDurationSeconds&&n.push("call_too_short");let r=ds(t);return i.excludedTags.some(s=>r.includes(s))&&n.push("excluded_tag"),i.requiredTags.some(s=>!r.includes(s))&&n.push("required_tag_missing"),(t.properties.status==="spam"||r.includes("spam"))&&n.push("spam_excluded"),{matches:n.length===0,reasons:n}}async function us(e,t,n){if(!si(n).enhancedMatching)return{personId:null,namespaceId:null,identityTier:"unlinked"};let s=(await e.query(`SELECT id FROM analytics_identity_namespaces
|
|
@@ -718,14 +718,14 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
718
718
|
ON r.id=s.latest_receipt_id AND r.site_id=s.site_id
|
|
719
719
|
WHERE m.site_id=$1 AND m.connection_id::text=$2
|
|
720
720
|
AND m.provider_person_ref=$3 AND m.person_id::text=$4
|
|
721
|
-
LIMIT 1`,[t.siteId,t.context.connectionId,t.context.providerContactId,t.context.expectedPersonId])).rows[0];if(!i)return{personId:null,namespaceId:null,consent:void 0,reason:"mapping_unavailable"};let r,s;try{r=
|
|
721
|
+
LIMIT 1`,[t.siteId,t.context.connectionId,t.context.providerContactId,t.context.expectedPersonId])).rows[0];if(!i)return{personId:null,namespaceId:null,consent:void 0,reason:"mapping_unavailable"};let r,s;try{r=te(t.context.provider),s=te(i.connection_provider)}catch{return{personId:null,namespaceId:null,consent:void 0,reason:"provider_mismatch"}}if(r!==s)return{personId:null,namespaceId:null,consent:void 0,reason:"provider_mismatch"};if(!i.receipt_id||!i.effective_at)return{personId:null,namespaceId:null,consent:void 0,reason:"consent_missing"};if(i.action==="withdraw")return{personId:null,namespaceId:null,consent:void 0,reason:"consent_withdrawn"};if(i.gpc||i.action==="gpc")return{personId:null,namespaceId:null,consent:void 0,reason:"consent_gpc"};let a=new Date(t.at??Date.now()).getTime();if(i.expires_at&&new Date(i.expires_at).getTime()<=a)return{personId:null,namespaceId:null,consent:void 0,reason:"consent_expired"};if(!i.enhanced_matching)return{personId:null,namespaceId:null,consent:void 0,reason:"enhanced_matching_denied"};if(!i.advertising_measurement)return{personId:null,namespaceId:null,consent:void 0,reason:"advertising_measurement_denied"};let o=new Date(i.effective_at).toISOString();return{personId:i.person_id,namespaceId:i.namespace_id,reason:"eligible",consent:{analytics:i.site_analytics?"granted":"denied",adUserData:"granted",adPersonalization:"granted",globalPrivacyControl:!1,occurredAt:o,receiptId:i.receipt_id,policyVersion:i.policy_version??void 0,purposes:{site_analytics:i.site_analytics?"granted":"denied",enhanced_matching:"granted",advertising_measurement:"granted"}}}}async function ms(e,t){let n=G(t.eventClickIds),i=await e.query(`SELECT click_ids FROM analytics_attribution_touches
|
|
722
722
|
WHERE site_id=$1 AND person_id=$2
|
|
723
723
|
AND occurred_at <= $3::timestamptz
|
|
724
724
|
AND occurred_at >= $3::timestamptz - ($4::text || ' days')::interval
|
|
725
725
|
AND click_ids <> '{}'::jsonb
|
|
726
726
|
ORDER BY occurred_at DESC, id DESC`,[t.siteId,t.personId,t.occurredAt,ts]),r={};for(let s of i.rows)for(let[a,o]of Object.entries(G(s.click_ids)))a in r||(r[a]=o);return{...r,...n}}function ps(e,t){let n=e.valueMinor??Number(t.default_value_minor??0),i=e.valueMinor!==void 0?"dynamic":(t.default_value_minor!==null,"fixed"),r=e.valueMinor!==void 0?"source_event":null,s={mode:e.valueMinor!==void 0?"dynamic":"fixed",source:r,observedAt:e.valueMinor!==void 0?e.occurredAt:null,fallback:!1,ruleVersion:String(t.version),resolution:i,baseValueMinor:n,resolvedValueMinor:n,currency:(e.currency||t.default_currency||"USD").toUpperCase(),linkedEvent:null};return{valueMinor:n,currency:s.currency,provenance:s}}async function ys(e,t){if(!t.rule.value_rule)return ps(t.event,t.rule);let n=t.rule.value_rule.conditionalOverride?.linkedEventName,i=n&&t.personId?(await e.query(`SELECT id, event_name, occurred_at FROM analytics_external_events
|
|
727
727
|
WHERE site_id=$1 AND person_id=$2 AND event_name=$3 AND id<>$4
|
|
728
|
-
ORDER BY occurred_at DESC LIMIT 100`,[t.siteId,t.personId,n,t.insertedEventId])).rows.map(s=>({eventId:s.id,eventName:s.event_name,occurredAt:s.occurred_at.toISOString(),identityTier:"confirmed"})):[],r=t.event.valueMinor===void 0?void 0:{source_event:{valueMinor:t.event.valueMinor,currency:(t.event.currency||t.rule.default_currency||"USD").toUpperCase(),observedAt:t.event.occurredAt},crm_field:t.event.source.includes("crm")?{valueMinor:t.event.valueMinor,currency:(t.event.currency||t.rule.default_currency||"USD").toUpperCase(),observedAt:t.event.occurredAt}:void 0,transaction:t.event.eventKind==="transaction"?{valueMinor:t.event.valueMinor,currency:(t.event.currency||t.rule.default_currency||"USD").toUpperCase(),observedAt:t.event.occurredAt}:void 0};return
|
|
728
|
+
ORDER BY occurred_at DESC LIMIT 100`,[t.siteId,t.personId,n,t.insertedEventId])).rows.map(s=>({eventId:s.id,eventName:s.event_name,occurredAt:s.occurred_at.toISOString(),identityTier:"confirmed"})):[],r=t.event.valueMinor===void 0?void 0:{source_event:{valueMinor:t.event.valueMinor,currency:(t.event.currency||t.rule.default_currency||"USD").toUpperCase(),observedAt:t.event.occurredAt},crm_field:t.event.source.includes("crm")?{valueMinor:t.event.valueMinor,currency:(t.event.currency||t.rule.default_currency||"USD").toUpperCase(),observedAt:t.event.occurredAt}:void 0,transaction:t.event.eventKind==="transaction"?{valueMinor:t.event.valueMinor,currency:(t.event.currency||t.rule.default_currency||"USD").toUpperCase(),observedAt:t.event.occurredAt}:void 0};return bn(t.rule.value_rule,{targetEvent:{eventId:t.insertedEventId,eventName:t.event.eventName,occurredAt:t.event.occurredAt,identityTier:t.personId?"confirmed":"candidate"},linkedEvents:i,observations:r,observedAt:t.event.occurredAt,providerOperation:"new_conversion"})}async function gs(e){if(!e.namespaceId||!e.personId)return[];let t=await e.client.query(`SELECT id, name, score_kind, rules, minimum_score, maximum_score, version
|
|
729
729
|
FROM analytics_score_definitions WHERE site_id=$1 AND status='active'`,[e.siteId]);if(!t.rowCount)return[];let n=await e.client.query(`SELECT id, event_name, event_kind, occurred_at, source, properties
|
|
730
730
|
FROM analytics_external_events WHERE site_id=$1 AND person_id=$2
|
|
731
731
|
ORDER BY occurred_at DESC LIMIT 5000`,[e.siteId,e.personId]),i={events:n.rows.map(s=>({ref:s.id,name:s.event_name,occurredAt:s.occurred_at.toISOString()})),pages:n.rows.filter(s=>typeof s.properties.path=="string").map(s=>({ref:s.id,name:String(s.properties.path),occurredAt:s.occurred_at.toISOString()})),sources:n.rows.map(s=>({ref:s.id,name:s.source,occurredAt:s.occurred_at.toISOString()})),calls:n.rows.filter(s=>s.event_kind==="call").map(s=>({ref:s.id,name:String(s.properties.outcome??s.properties.status??s.event_name),occurredAt:s.occurred_at.toISOString()})),crmStages:n.rows.filter(s=>s.event_kind==="crm").map(s=>({ref:s.id,name:String(s.properties.stageId??s.properties.stageName??s.event_name),occurredAt:s.occurred_at.toISOString()}))},r=[];for(let s of t.rows){let a=s.rules&&typeof s.rules=="object"&&!Array.isArray(s.rules)?s.rules:{},o=Array.isArray(s.rules)?s.rules:a.groups;if(!Array.isArray(o)||o.length===0)continue;let c=Array.isArray(a.permittedProperties)?a.permittedProperties.filter(d=>typeof d=="string"):[];try{let d=Bn();Vn({tables:d,id:s.id,definition:{namespaceId:e.namespaceId,name:s.name,kind:s.score_kind,targetObject:"person",minimum:s.minimum_score,maximum:s.maximum_score,groups:o,permittedProperties:c,enabled:!0},now:e.occurredAt});let _=Kn({tables:d,namespaceId:e.namespaceId,scoreId:s.id,subjectRef:e.personId,identityTier:"confirmed",facts:i,now:e.occurredAt});await e.client.query(`INSERT INTO analytics_score_evaluations(
|
|
@@ -741,7 +741,7 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
741
741
|
WHERE m.target_id=t.id AND m.status IN ('validated','active')) AS validated_manifest
|
|
742
742
|
FROM analytics_prediction_targets t
|
|
743
743
|
JOIN analytics_event_definitions d ON d.id=t.target_event_definition_id
|
|
744
|
-
WHERE t.site_id=$1 AND t.namespace_id=$2 AND t.status<>'disabled'`,[e.siteId,e.namespaceId])).rows.map(n=>ns({targetId:n.id,confirmedPeople:Number(n.confirmed_people),positiveOutcomes:Number(n.positive_outcomes),minimumConfirmedPeople:n.minimum_confirmed_people,minimumPositiveOutcomes:n.minimum_positive_outcomes,validatedManifest:n.validated_manifest})):[]}async function oi(e){let t=
|
|
744
|
+
WHERE t.site_id=$1 AND t.namespace_id=$2 AND t.status<>'disabled'`,[e.siteId,e.namespaceId])).rows.map(n=>ns({targetId:n.id,confirmedPeople:Number(n.confirmed_people),positiveOutcomes:Number(n.positive_outcomes),minimumConfirmedPeople:n.minimum_confirmed_people,minimumPositiveOutcomes:n.minimum_positive_outcomes,validatedManifest:n.validated_manifest})):[]}async function oi(e){let t=Ye.parse(e.event),n=e.db??v();e.userId!==void 0&&await J(n,e.siteId,e.userId);let i=!e.transactionClient,r=e.transactionClient??await n.connect();try{i&&await r.query("BEGIN");let s=e.trustedCrmContext?await _s(r,{siteId:e.siteId,context:e.trustedCrmContext}):null,a=e.trustedCrmContext?Ye.parse({...t,personRef:void 0,consent:s?.consent,properties:{...t.properties,_xrayCrmLinkReason:s?.reason??"mapping_unavailable"}}):t,o=si(a),c=s?.personId&&s.namespaceId?{personId:s.personId,namespaceId:s.namespaceId,identityTier:"confirmed"}:e.trustedCrmContext?{personId:null,namespaceId:null,identityTier:"unlinked"}:await us(r,e.siteId,a),d=c.personId,_=await r.query(`INSERT INTO analytics_external_events(
|
|
745
745
|
id, site_id, receipt_id, person_id, source, source_account_ref,
|
|
746
746
|
source_event_id, event_kind, event_name, occurred_at, visitor_hmac,
|
|
747
747
|
session_hmac, call_id, deal_id, order_id, value_minor, currency,
|
|
@@ -760,7 +760,7 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
760
760
|
WHERE site_id = $1 AND status = 'active' AND readiness IN ('verified','live')`,[e.siteId]),bi=await ms(r,{siteId:e.siteId,personId:d,occurredAt:a.occurredAt,eventClickIds:a.clickIds}),Ai=de(JSON.stringify({eventId:Se.rows[0].id,eventName:A.conversion_kind,eventTime:a.occurredAt,valueMinor:$e,currency:jt,valueProvenance:M.provenance,attributionModelProvenance:{model:typeof a.properties.attributionModel=="string"?a.properties.attributionModel:"unassigned",evidenceTier:"confirmed"},orderId:a.orderId,clickIds:bi,source:a.source,personId:d,identityTier:"confirmed",namespaceId:c.namespaceId,consent:{receiptId:o.receiptId,advertisingMeasurement:!0}}),"activation-payload");for(let Ii of Ei.rows)await r.query(`INSERT INTO analytics_activation_jobs(
|
|
761
761
|
id, destination_id, conversion_id, person_id, payload_ciphertext
|
|
762
762
|
) VALUES ($1,$2,$3,$4,$5)
|
|
763
|
-
ON CONFLICT(destination_id, conversion_id) DO NOTHING`,[se(),Ii.id,Se.rows[0].id,d,Ai])}let P=await gs({client:r,siteId:e.siteId,namespaceId:c.namespaceId,personId:d,occurredAt:a.occurredAt}),h=await fs({client:r,siteId:e.siteId,namespaceId:c.namespaceId});d&&await
|
|
763
|
+
ON CONFLICT(destination_id, conversion_id) DO NOTHING`,[se(),Ii.id,Se.rows[0].id,d,Ai])}let P=await gs({client:r,siteId:e.siteId,namespaceId:c.namespaceId,personId:d,occurredAt:a.occurredAt}),h=await fs({client:r,siteId:e.siteId,namespaceId:c.namespaceId});d&&await qn({siteId:e.siteId,personId:d,canonicalEventId:u,event:a,consent:{...o.receiptId?{receiptId:o.receiptId}:{},siteAnalytics:o.siteAnalytics,advertisingMeasurement:o.advertisingMeasurement},db:r}),i&&await r.query("COMMIT");let L=null,m=typeof a.properties.candidateAssociationId=="string"?a.properties.candidateAssociationId:null,K=typeof a.properties.deterministicEvidenceRef=="string"?a.properties.deterministicEvidenceRef:null,w=a.properties.deterministicEvidenceKind;return d&&c.namespaceId&&m&&K&&["verified_email","verified_phone","crm_contact","signed_linker","authenticated_user"].includes(String(w))&&(L=await wn({siteId:e.siteId,namespaceId:c.namespaceId,associationId:m,personId:d,evidenceKind:w,evidenceRef:K,occurredAt:a.occurredAt}).catch(()=>null)),{id:_.rows[0]?.id??null,created:!!_.rowCount,personId:d,identityTier:c.identityTier,namespaceId:c.namespaceId,conversionIds:b,consentEligibility:o,valueProvenance:V,scoreEvaluationIds:P,predictionEvidence:h,candidatePromotion:L}}catch(s){throw i&&await r.query("ROLLBACK"),s}finally{i&&r.release()}}async function gc(e,t,n){let i=v();if(await U(i,e,t),!(await i.query("SELECT 1 FROM analytics_people WHERE id = $1 AND site_id = $2",[n,e])).rowCount)throw new f("analytics_person_not_found","Attributed person not found.",404);let[s,a,o,c,d]=await Promise.all([i.query(`SELECT id, started_at, last_active_at, ended_at, duration_ms, landing_path,
|
|
764
764
|
exit_path, pageview_count, event_count
|
|
765
765
|
FROM analytics_sessions WHERE site_id = $1 AND person_id = $2 ORDER BY started_at`,[e,n]),i.query(`SELECT occurred_at, source, medium, campaign, referrer, landing_path
|
|
766
766
|
FROM analytics_attribution_touches WHERE site_id = $1 AND person_id = $2 ORDER BY occurred_at`,[e,n]),i.query(`SELECT source, event_kind, event_name, occurred_at, value_minor, currency
|
|
@@ -769,7 +769,7 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
769
769
|
d.platform, d.name AS destination_name
|
|
770
770
|
FROM analytics_activation_jobs j
|
|
771
771
|
JOIN analytics_activation_destinations d ON d.id = j.destination_id
|
|
772
|
-
WHERE d.site_id = $1 AND j.person_id = $2 ORDER BY j.created_at`,[e,n])]),_=u=>{if(typeof u!="string"||!u)return null;try{let y=new URL(u);return`${y.origin}${y.pathname}`.slice(0,2e3)}catch{return null}};return{personRef:O("person",e,n),sessions:s.rows.map(u=>({sessionRef:O("session",e,String(u.id)),started_at:u.started_at,last_active_at:u.last_active_at,ended_at:u.ended_at,duration_ms:u.duration_ms,landing_path:u.landing_path,exit_path:u.exit_path,pageview_count:u.pageview_count,event_count:u.event_count})),touches:a.rows.map(u=>({occurred_at:u.occurred_at,source:u.source,medium:u.medium,campaign:u.campaign,referrer:_(u.referrer),landing_path:u.landing_path})),externalEvents:o.rows.map(u=>({source:u.source,event_kind:u.event_kind,event_name:u.event_name,occurred_at:u.occurred_at,value_minor:u.value_minor,currency:u.currency})),conversions:c.rows.map(u=>({conversion_kind:u.conversion_kind,value_minor:u.value_minor,currency:u.currency,occurred_at:u.occurred_at,rule_version:u.rule_version})),deliveries:d.rows.map(u=>({status:u.status,attempts:u.attempts,response_category:u.response_category,created_at:u.created_at,delivered_at:u.delivered_at,platform:u.platform,destination_name:u.destination_name}))}}async function
|
|
772
|
+
WHERE d.site_id = $1 AND j.person_id = $2 ORDER BY j.created_at`,[e,n])]),_=u=>{if(typeof u!="string"||!u)return null;try{let y=new URL(u);return`${y.origin}${y.pathname}`.slice(0,2e3)}catch{return null}};return{personRef:O("person",e,n),sessions:s.rows.map(u=>({sessionRef:O("session",e,String(u.id)),started_at:u.started_at,last_active_at:u.last_active_at,ended_at:u.ended_at,duration_ms:u.duration_ms,landing_path:u.landing_path,exit_path:u.exit_path,pageview_count:u.pageview_count,event_count:u.event_count})),touches:a.rows.map(u=>({occurred_at:u.occurred_at,source:u.source,medium:u.medium,campaign:u.campaign,referrer:_(u.referrer),landing_path:u.landing_path})),externalEvents:o.rows.map(u=>({source:u.source,event_kind:u.event_kind,event_name:u.event_name,occurred_at:u.occurred_at,value_minor:u.value_minor,currency:u.currency})),conversions:c.rows.map(u=>({conversion_kind:u.conversion_kind,value_minor:u.value_minor,currency:u.currency,occurred_at:u.occurred_at,rule_version:u.rule_version})),deliveries:d.rows.map(u=>({status:u.status,attempts:u.attempts,response_category:u.response_category,created_at:u.created_at,delivered_at:u.delivered_at,platform:u.platform,destination_name:u.destination_name}))}}async function fc(e,t){let n=v();await U(n,e,t);let r=(await n.query(`SELECT
|
|
773
773
|
count(*)::int AS total_events,
|
|
774
774
|
count(*) FILTER (WHERE person_id IS NOT NULL)::int AS identified_events,
|
|
775
775
|
count(*) FILTER (WHERE click_ids <> '{}'::jsonb)::int AS events_with_click_id,
|
|
@@ -817,4 +817,4 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
817
817
|
)
|
|
818
818
|
ORDER BY edge.confidence DESC, edge.last_seen_at DESC
|
|
819
819
|
LIMIT 1
|
|
820
|
-
FOR SHARE OF p, ns, cs, receipt`,[t.siteId,Ce("customer_id",t.stableUserId)])).rows[0]??null}async function Ut(e,t){let n=process.env.MCP_SCRAPER_XRAY_SITE_ID?.trim();if(!n)return{status:"skipped",eventName:Le,reason:"site_not_configured"};if(!vs(n))return{status:"skipped",eventName:Le,reason:"site_id_invalid"};let i=String(e.userId).trim();if(!i||!e.invoiceId.trim()||!Number.isSafeInteger(e.amountPaidMinor)||e.amountPaidMinor<0||!/^[a-z]{3}$/i.test(e.currency)||!Number.isFinite(new Date(e.occurredAt).getTime()))throw new Error("analytics_first_party_outcome_invalid");let r=t??{db:v(),record:(a,o)=>oi({...a,transactionClient:o})},s=await r.db.connect();try{await s.query("BEGIN");let a=await Es(s,{siteId:n,stableUserId:i});if(!a)return await s.query("ROLLBACK"),{status:"skipped",eventName:Le,reason:"identity_or_consent_unavailable"};let o={schemaVersion:1,eventId:`mcp_invoice_${e.invoiceId}`.slice(0,200),sourceEventId:`stripe:invoice:${e.invoiceId}`.slice(0,300),source:"mcp_scraper",sourceAccountRef:"first_party_billing",eventKind:"transaction",eventName:Le,occurredAt:e.occurredAt,personRef:i,orderId:e.invoiceId,valueMinor:e.amountPaidMinor,currency:e.currency.toUpperCase(),consent:{analytics:"granted",adUserData:"granted",adPersonalization:a.advertising_measurement?"granted":"denied",globalPrivacyControl:!1,occurredAt:new Date(a.effective_at).toISOString(),receiptId:a.receipt_id,policyVersion:a.policy_version,purposes:{site_analytics:"granted",enhanced_matching:"granted",advertising_measurement:a.advertising_measurement?"granted":"denied"}},properties:{product:"mcp_scraper",billingKind:"base_plan_paid_invoice",revenueProvenance:"stripe_paid_invoice"}},c=await r.record({siteId:n,event:o},s);return await s.query("COMMIT"),{status:"recorded",eventName:Le,reason:"recorded",created:c.created,conversionIds:c.conversionIds}}catch(a){throw await s.query("ROLLBACK").catch(()=>{}),a}finally{s.release()}}function ci(e){let t=e;return t?.price?.id??t?.pricing?.price_details?.price??t?.plan?.id}function Ie(e){if(typeof e=="string")return e;if(e&&typeof e=="object"&&"id"in e)return String(e.id)}function bs(e){let t=e.subscription;if(t)return Ie(t);let n=e.parent;return Ie(n?.subscription_details?.subscription)}async function As(e,t){let n=await T(e);return n||!t||(n=await ke(t),n&&await Pe(n.id,e)),n}async function Is(e,t,n){let i=t.memory_subscription_id;if(!(!i||i===n))try{let r=await e.subscriptions.retrieve(i);r.status!=="canceled"&&!r.cancel_at_period_end&&await e.subscriptions.update(i,{cancel_at_period_end:!0})}catch(r){console.error("[stripe] could not wind down standalone memory subscription",i,r instanceof Error?r.message:r)}}async function Ft(e,t,n){if(!t.id||t.status!=="paid")return{status:"not_applicable",invoiceId:t.id??null};let i=t.lines.data.some(y=>{let E=ci(y);return!!E&&E in D});if(!Dn(i,t.total))return{status:"not_applicable",invoiceId:t.id};let r=bs(t),a=(r?le(await e.subscriptions.retrieve(r))?.price?.id:void 0)??t.lines.data.map(ci).find(y=>!!y&&y in D),o=a?D[a]:void 0;if(!o)return{status:"not_applicable",invoiceId:t.id};let c=Ie(t.customer);if(!c)throw new Error("Paid subscription invoice has no Stripe customer");let d=await As(c,t.customer_email);if(!d)throw new Error("Paid subscription invoice does not map to an MCP Scraper account");let _=await Wt({invoiceId:t.id,userId:d.id,subscriptionId:r??d.subscription_id,tier:o.tier,concurrency:o.concurrency,creditsMc:o.credits_mc,description:`${o.label} subscription credits`,neverExpires:!!o.credits_never_expire,includedMemoryPlan:o.includes_memory?"pro":null});o.includes_memory&&(await Is(e,d,r??d.subscription_id),await z(d,F({...d,memory_plan:"pro",subscription_tier:o.tier})));let u=await Be(d);u.ok||console.warn("[stripe] scheduled-action credential sync failed:",u.error??"unknown error");try{await Ut({userId:d.id,invoiceId:t.id,amountPaidMinor:t.amount_paid,currency:t.currency,occurredAt:new Date(((t.status_transitions?.paid_at??n)||Math.floor(Date.now()/1e3))*1e3).toISOString()})}catch(y){console.error("[stripe] X-Ray paid outcome failed",{invoiceId:t.id,error:y instanceof Error?y.message:String(y)})}return{status:_.granted?"fulfilled":"already_fulfilled",invoiceId:t.id,userId:d.id,tier:o.tier}}async function di(e,t,n){let i=await e.checkout.sessions.retrieve(t,{expand:["invoice"]});if(i.mode!=="subscription")throw new Error("Checkout session is not a subscription purchase");if(i.payment_status!=="paid"&&i.payment_status!=="no_payment_required")return{status:"not_applicable",invoiceId:Ie(i.invoice)??null};let r=Ie(i.customer);if(n&&r!==n)throw new Error("Checkout session does not belong to this billing account");let s=typeof i.invoice=="object"&&i.invoice&&!("deleted"in i.invoice)?i.invoice:await e.invoices.retrieve(Ie(i.invoice)??"");if(!s?.id)throw new Error("Paid subscription checkout has no invoice");return Ft(e,s,i.created)}var li="scale_credit_pack";function rt(e){if(typeof e=="string")return e;if(e&&typeof e=="object"&&"id"in e)return String(e.id)}function ui(e){let t=Number(e);if(!Number.isSafeInteger(t)||t<1||t>ut)throw new Error(`quantity must be a whole number from 1 to ${ut}`);return t}function Oc(e){return e.subscription_tier==="scale"}function Dc(e,t){let n=ui(t);return{purchase_kind:li,account_user_id:String(e),eligible_tier:"scale",increment_quantity:String(n),increment_usd:String(he),credits_per_increment:String(lt)}}async function _i(e,t,n){let i=await e.checkout.sessions.retrieve(t);if(i.mode!=="payment"||i.metadata?.purchase_kind!==li)return{status:"not_applicable",paymentIntentId:rt(i.payment_intent)??null};if(i.payment_status!=="paid")return{status:"not_applicable",paymentIntentId:rt(i.payment_intent)??null};let r=rt(i.customer);if(!r)throw new Error("Paid Scale credit-pack checkout has no Stripe customer");if(n&&r!==n)throw new Error("Checkout session does not belong to this billing account");let s=ui(i.metadata.increment_quantity),a=s*he*100;if(i.currency!=="usd"||i.amount_subtotal!==a)throw new Error("Scale credit-pack checkout amount does not match the product contract");let o=await e.checkout.sessions.listLineItems(i.id,{limit:2}),c=o.data[0];if(o.data.length!==1||c?.quantity!==s||c.price?.currency!=="usd"||c.price?.unit_amount!==he*100)throw new Error("Scale credit-pack checkout line item does not match the product contract");let d=await T(r);if(!d)throw new Error("Paid Scale credit-pack checkout does not map to an MCP Scraper account");if(i.metadata.account_user_id!==String(d.id)||i.metadata.eligible_tier!=="scale")throw new Error("Scale credit-pack checkout account metadata does not match the billing account");let _=rt(i.payment_intent);if(!_)throw new Error("Paid Scale credit-pack checkout has no payment intent");let u=s*lt;return{status:(await Vt(d.id,s*Qt,Ue.TOPUP,`$${s*he} Scale credit pack (${u.toLocaleString("en-US")} credits)`,`stripe-payment-intent:${_}`,{neverExpires:!1})).duplicate?"already_fulfilled":"fulfilled",paymentIntentId:_,userId:d.id,quantity:s,credits:u}}import Ss from"stripe";import{Hono as hs}from"hono";var x=new Ss(process.env.STRIPE_SECRET_KEY,{apiVersion:"2026-02-25.clover"});async function Ns(e,t){if(!(!e||e===t))try{let n=await x.subscriptions.retrieve(e),i=n.items.data[0]?.price?.id,r=n.status!=="canceled"&&!n.cancel_at_period_end;i&&i in ce&&r&&await x.subscriptions.update(e,{cancel_at_period_end:!0})}catch(n){console.error("[stripe] could not wind down standalone memory subscription",e,n instanceof Error?n.message:n)}}var Cs=new hs;Cs.post("/webhooks",async e=>{let t=e.req.header("stripe-signature"),n=await e.req.text(),i;try{i=x.webhooks.constructEvent(n,t,process.env.STRIPE_WEBHOOK_SECRET)}catch{return e.json({error:"Invalid signature"},400)}try{let r=await qt(i.id,i.type,i.created);return e.json({received:!0,disposition:r})}catch(r){let s=r instanceof Error?r.message:String(r);return console.error("[stripe] webhook enqueue failed",i.id,s),e.json({error:"Webhook enqueue failed"},500)}});async function Wc(e){if(e.type==="checkout.session.completed"||e.type==="checkout.session.async_payment_succeeded"){let t=e.data.object;t.mode==="payment"?await _i(x,t.id):await di(x,t.id);return}if(e.type==="invoice.created"){let t=e.data.object;mi(t)&&t.status==="draft"&&await x.invoices.update(t.id,{description:Xt,footer:Gt,custom_fields:[{name:"Pack benefit",value:"2 browsers per $5 monthly pack"},{name:"Billing cadence",value:"Monthly"}]});return}if(e.type==="invoice.paid"||e.type==="invoice.payment_succeeded"){let t=e.data.object;if(mi(t)){let i=t.customer_email?void 0:await yi(t.customer,void 0),r=t.customer_email??i?.email;if(!r)throw new Error("Paid concurrency invoice has no customer receipt email");await Rs(t.id,r);return}if(t.lines.data.map(gi).find(i=>i&&i in ce))return;await Ft(x,t,e.created)}if(e.type==="customer.subscription.created"||e.type==="customer.subscription.updated"){let t=e.data.object,n=pi(t);if(n){let s=await T(t.customer);if(!s)return;let a=Re(t.status);await st(s.id,a?ws(n):0),await at(s.id,a?t.id:null);return}let i=le(t)?.price?.id??t.items.data[0]?.price?.id;if(i&&i in ce){let s=ce[i],a=await yi(t.customer,t.customer_email);if(a){let o=Re(t.status),c=o?s.plan:"free";await oe(a.id,c,o?t.id:null),await z(a,F({...a,memory_plan:c}))}return}let r=await T(t.customer);if(!r)return;if(i&&i in D){let s=D[i],a=Re(t.status),o=a?s.tier:null;if(await ot(r.id,o,a?s.concurrency:0,a?t.id:null),s.includes_memory){let c=a?"pro":"free";a&&await Ns(r.memory_subscription_id,t.id),await oe(r.id,c,null),await z(r,F({...r,memory_plan:c}))}else r.memory_subscription_id===t.id&&(await oe(r.id,"free",null),await z(r,F({...r,memory_plan:"free"})));if(a){let c=await Be(r);c.ok||console.warn("[stripe] scheduled-action credential sync failed:",c.error??"unknown error")}}}if(e.type==="customer.subscription.deleted"){let t=e.data.object;if(pi(t)){let i=await T(t.customer);i&&(await st(i.id,0),await at(i.id,null));return}let n=le(t)?.price?.id??t.items.data[0]?.price?.id;if(n&&n in D){let i=D[n],r=await T(t.customer);r&&(await ot(r.id,null,0,null),(i.includes_memory||r.memory_subscription_id===t.id)&&(await oe(r.id,"free",null),await z(r,F({...r,memory_plan:"free"}))));return}if(n&&n in ce||await ct(t.id)){let i=await ct(t.id)??await T(t.customer);i&&(await oe(i.id,"free",null),await z(i,F({...i,memory_plan:"free"})));return}}}function mi(e){return e.lines.data.some(t=>gi(t)===dt)}async function Rs(e,t){let i=(await x.invoicePayments.list({invoice:e,status:"paid",limit:10})).data.find(a=>a.status==="paid");if(!i)throw new Error("Paid concurrency invoice has no paid Stripe payment");let r;if(i.payment.type==="charge")r=i.payment.charge;else if(i.payment.type==="payment_intent"&&i.payment.payment_intent){let a=typeof i.payment.payment_intent=="string"?i.payment.payment_intent:i.payment.payment_intent.id;r=(await x.paymentIntents.retrieve(a,{expand:["latest_charge"]})).latest_charge}if(!r)throw new Error("Paid concurrency invoice has no receipt-capable charge");let s=typeof r=="string"?await x.charges.retrieve(r):r;s.receipt_email!==t&&await x.charges.update(s.id,{receipt_email:t})}function pi(e){return e.items.data.find(t=>t.price?.id===dt)}function ws(e){let t=e.quantity??1;if(!Number.isSafeInteger(t)||t<1)throw new Error("Stripe concurrency pack quantity must be a positive safe integer");return t}function gi(e){let t=e;return t?.price?.id??t?.pricing?.price_details?.price??t?.plan?.id}async function yi(e,t){let n=await T(e);if(n)return n;if(t)return n=await ke(t),n&&await Pe(n.id,e),n}export{Oi as a,Ts as b,ln as c,gt as d,Fs as e,js as f,_n as g,ji as h,qs as i,Hs as j,qi as k,pn as l,Be as m,Et as n,j as o,le as p,Xs as q,Gs as r,Qs as s,et as t,tt as u,Pn as v,Nr as w,Rr as x,Ct as y,Ja as z,za as A,Ya as B,Xa as C,io as D,qn as E,ia as F,ra as G,sa as H,aa as I,oa as J,ca as K,da as L,la as M,ua as N,_a as O,ma as P,pa as Q,ya as R,vn as S,ga as T,Gi as U,fa as V,ee as W,ze as X,$ as Y,va as Z,er as _,Ia as $,Sa as aa,ha as ba,Cn as ca,Rn as da,Na as ea,Pa as fa,Bn as ga,Vn as ha,oo as ia,co as ja,Kn as ka,_o as la,Jn as ma,zn as na,Yn as oa,Xn as pa,Gn as qa,Qn as ra,it as sa,ho as ta,No as ua,Co as va,Ro as wa,wo as xa,Oo as ya,ei as za,ti as Aa,$t as Ba,ni as Ca,ii as Da,ss as Ea,Xo as Fa,Go as Ga,Qo as Ha,Zo as Ia,ec as Ja,tc as Ka,nc as La,ic as Ma,rc as Na,sc as Oa,ac as Pa,oc as Qa,cc as Ra,dc as Sa,lc as Ta,uc as Ua,ri as Va,_c as Wa,mc as Xa,oi as Ya,pc as Za,yc as _a,Ft as $a,di as ab,ui as bb,Oc as cb,Dc as db,_i as eb,x as fb,Cs as gb,Wc as hb};
|
|
820
|
+
FOR SHARE OF p, ns, cs, receipt`,[t.siteId,Ce("customer_id",t.stableUserId)])).rows[0]??null}async function Ut(e,t){let n=process.env.MCP_SCRAPER_XRAY_SITE_ID?.trim();if(!n)return{status:"skipped",eventName:Le,reason:"site_not_configured"};if(!vs(n))return{status:"skipped",eventName:Le,reason:"site_id_invalid"};let i=String(e.userId).trim();if(!i||!e.invoiceId.trim()||!Number.isSafeInteger(e.amountPaidMinor)||e.amountPaidMinor<0||!/^[a-z]{3}$/i.test(e.currency)||!Number.isFinite(new Date(e.occurredAt).getTime()))throw new Error("analytics_first_party_outcome_invalid");let r=t??{db:v(),record:(a,o)=>oi({...a,transactionClient:o})},s=await r.db.connect();try{await s.query("BEGIN");let a=await Es(s,{siteId:n,stableUserId:i});if(!a)return await s.query("ROLLBACK"),{status:"skipped",eventName:Le,reason:"identity_or_consent_unavailable"};let o={schemaVersion:1,eventId:`mcp_invoice_${e.invoiceId}`.slice(0,200),sourceEventId:`stripe:invoice:${e.invoiceId}`.slice(0,300),source:"mcp_scraper",sourceAccountRef:"first_party_billing",eventKind:"transaction",eventName:Le,occurredAt:e.occurredAt,personRef:i,orderId:e.invoiceId,valueMinor:e.amountPaidMinor,currency:e.currency.toUpperCase(),consent:{analytics:"granted",adUserData:"granted",adPersonalization:a.advertising_measurement?"granted":"denied",globalPrivacyControl:!1,occurredAt:new Date(a.effective_at).toISOString(),receiptId:a.receipt_id,policyVersion:a.policy_version,purposes:{site_analytics:"granted",enhanced_matching:"granted",advertising_measurement:a.advertising_measurement?"granted":"denied"}},properties:{product:"mcp_scraper",billingKind:"base_plan_paid_invoice",revenueProvenance:"stripe_paid_invoice"}},c=await r.record({siteId:n,event:o},s);return await s.query("COMMIT"),{status:"recorded",eventName:Le,reason:"recorded",created:c.created,conversionIds:c.conversionIds}}catch(a){throw await s.query("ROLLBACK").catch(()=>{}),a}finally{s.release()}}function ci(e){let t=e;return t?.price?.id??t?.pricing?.price_details?.price??t?.plan?.id}function Ie(e){if(typeof e=="string")return e;if(e&&typeof e=="object"&&"id"in e)return String(e.id)}function bs(e){let t=e.subscription;if(t)return Ie(t);let n=e.parent;return Ie(n?.subscription_details?.subscription)}async function As(e,t){let n=await T(e);return n||!t||(n=await ke(t),n&&await Pe(n.id,e)),n}async function Is(e,t,n){let i=t.memory_subscription_id;if(!(!i||i===n))try{let r=await e.subscriptions.retrieve(i);r.status!=="canceled"&&!r.cancel_at_period_end&&await e.subscriptions.update(i,{cancel_at_period_end:!0})}catch(r){console.error("[stripe] could not wind down standalone memory subscription",i,r instanceof Error?r.message:r)}}async function Ft(e,t,n){if(!t.id||t.status!=="paid")return{status:"not_applicable",invoiceId:t.id??null};let i=t.lines.data.some(y=>{let E=ci(y);return!!E&&E in D});if(!fn(i,t.total))return{status:"not_applicable",invoiceId:t.id};let r=bs(t),a=(r?le(await e.subscriptions.retrieve(r))?.price?.id:void 0)??t.lines.data.map(ci).find(y=>!!y&&y in D),o=a?D[a]:void 0;if(!o)return{status:"not_applicable",invoiceId:t.id};let c=Ie(t.customer);if(!c)throw new Error("Paid subscription invoice has no Stripe customer");let d=await As(c,t.customer_email);if(!d)throw new Error("Paid subscription invoice does not map to an MCP Scraper account");let _=await Wt({invoiceId:t.id,userId:d.id,subscriptionId:r??d.subscription_id,tier:o.tier,concurrency:o.concurrency,creditsMc:o.credits_mc,description:`${o.label} subscription credits`,neverExpires:!!o.credits_never_expire,includedMemoryPlan:o.includes_memory?"pro":null});o.includes_memory&&(await Is(e,d,r??d.subscription_id),await z(d,F({...d,memory_plan:"pro",subscription_tier:o.tier})));let u=await Be(d);u.ok||console.warn("[stripe] scheduled-action credential sync failed:",u.error??"unknown error");try{await Ut({userId:d.id,invoiceId:t.id,amountPaidMinor:t.amount_paid,currency:t.currency,occurredAt:new Date(((t.status_transitions?.paid_at??n)||Math.floor(Date.now()/1e3))*1e3).toISOString()})}catch(y){console.error("[stripe] X-Ray paid outcome failed",{invoiceId:t.id,error:y instanceof Error?y.message:String(y)})}return{status:_.granted?"fulfilled":"already_fulfilled",invoiceId:t.id,userId:d.id,tier:o.tier}}async function di(e,t,n){let i=await e.checkout.sessions.retrieve(t,{expand:["invoice"]});if(i.mode!=="subscription")throw new Error("Checkout session is not a subscription purchase");if(i.payment_status!=="paid"&&i.payment_status!=="no_payment_required")return{status:"not_applicable",invoiceId:Ie(i.invoice)??null};let r=Ie(i.customer);if(n&&r!==n)throw new Error("Checkout session does not belong to this billing account");let s=typeof i.invoice=="object"&&i.invoice&&!("deleted"in i.invoice)?i.invoice:await e.invoices.retrieve(Ie(i.invoice)??"");if(!s?.id)throw new Error("Paid subscription checkout has no invoice");return Ft(e,s,i.created)}var li="scale_credit_pack";function rt(e){if(typeof e=="string")return e;if(e&&typeof e=="object"&&"id"in e)return String(e.id)}function ui(e){let t=Number(e);if(!Number.isSafeInteger(t)||t<1||t>ut)throw new Error(`quantity must be a whole number from 1 to ${ut}`);return t}function Tc(e){return e.subscription_tier==="scale"}function xc(e,t){let n=ui(t);return{purchase_kind:li,account_user_id:String(e),eligible_tier:"scale",increment_quantity:String(n),increment_usd:String(he),credits_per_increment:String(lt)}}async function _i(e,t,n){let i=await e.checkout.sessions.retrieve(t);if(i.mode!=="payment"||i.metadata?.purchase_kind!==li)return{status:"not_applicable",paymentIntentId:rt(i.payment_intent)??null};if(i.payment_status!=="paid")return{status:"not_applicable",paymentIntentId:rt(i.payment_intent)??null};let r=rt(i.customer);if(!r)throw new Error("Paid Scale credit-pack checkout has no Stripe customer");if(n&&r!==n)throw new Error("Checkout session does not belong to this billing account");let s=ui(i.metadata.increment_quantity),a=s*he*100;if(i.currency!=="usd"||i.amount_subtotal!==a)throw new Error("Scale credit-pack checkout amount does not match the product contract");let o=await e.checkout.sessions.listLineItems(i.id,{limit:2}),c=o.data[0];if(o.data.length!==1||c?.quantity!==s||c.price?.currency!=="usd"||c.price?.unit_amount!==he*100)throw new Error("Scale credit-pack checkout line item does not match the product contract");let d=await T(r);if(!d)throw new Error("Paid Scale credit-pack checkout does not map to an MCP Scraper account");if(i.metadata.account_user_id!==String(d.id)||i.metadata.eligible_tier!=="scale")throw new Error("Scale credit-pack checkout account metadata does not match the billing account");let _=rt(i.payment_intent);if(!_)throw new Error("Paid Scale credit-pack checkout has no payment intent");let u=s*lt;return{status:(await Vt(d.id,s*Qt,Ue.TOPUP,`$${s*he} Scale credit pack (${u.toLocaleString("en-US")} credits)`,`stripe-payment-intent:${_}`,{neverExpires:!1})).duplicate?"already_fulfilled":"fulfilled",paymentIntentId:_,userId:d.id,quantity:s,credits:u}}import Ss from"stripe";import{Hono as hs}from"hono";import{waitUntil as Ns}from"@vercel/functions";var x=new Ss(process.env.STRIPE_SECRET_KEY,{apiVersion:"2026-02-25.clover"});async function Cs(e,t){if(!(!e||e===t))try{let n=await x.subscriptions.retrieve(e),i=n.items.data[0]?.price?.id,r=n.status!=="canceled"&&!n.cancel_at_period_end;i&&i in ce&&r&&await x.subscriptions.update(e,{cancel_at_period_end:!0})}catch(n){console.error("[stripe] could not wind down standalone memory subscription",e,n instanceof Error?n.message:n)}}var Rs=new hs;Rs.post("/webhooks",async e=>{let t=e.req.header("stripe-signature"),n=await e.req.text(),i;try{i=x.webhooks.constructEvent(n,t,process.env.STRIPE_WEBHOOK_SECRET)}catch{return e.json({error:"Invalid signature"},400)}try{let r=await qt(i.id,i.type,i.created);return r==="queued"&&ws(),e.json({received:!0,disposition:r})}catch(r){let s=r instanceof Error?r.message:String(r);return console.error("[stripe] webhook enqueue failed",i.id,s),e.json({error:"Webhook enqueue failed"},500)}});function ws(){let e=import("./stripe-event-worker-AQVRF24K.js").then(({drainStripeEventQueue:t})=>t({limit:10,leaseSeconds:300})).catch(t=>{console.error("[stripe] immediate queue drain failed; the scheduled drain will retry",t instanceof Error?t.message:String(t))});Ns(e)}async function Kc(e){if(e.type==="checkout.session.completed"||e.type==="checkout.session.async_payment_succeeded"){let t=e.data.object;t.mode==="payment"?await _i(x,t.id):await di(x,t.id);return}if(e.type==="invoice.created"){let t=e.data.object;mi(t)&&t.status==="draft"&&await x.invoices.update(t.id,{description:Xt,footer:Gt,custom_fields:[{name:"Pack benefit",value:"2 browsers per $5 monthly pack"},{name:"Billing cadence",value:"Monthly"}]});return}if(e.type==="invoice.paid"||e.type==="invoice.payment_succeeded"){let t=e.data.object;if(mi(t)){let i=t.customer_email?void 0:await yi(t.customer,void 0),r=t.customer_email??i?.email;if(!r)throw new Error("Paid concurrency invoice has no customer receipt email");await Os(t.id,r);return}if(t.lines.data.map(gi).find(i=>i&&i in ce))return;await Ft(x,t,e.created)}if(e.type==="customer.subscription.created"||e.type==="customer.subscription.updated"){let t=e.data.object,n=pi(t);if(n){let s=await T(t.customer);if(!s)return;let a=Re(t.status);await st(s.id,a?Ds(n):0),await at(s.id,a?t.id:null);return}let i=le(t)?.price?.id??t.items.data[0]?.price?.id;if(i&&i in ce){let s=ce[i],a=await yi(t.customer,t.customer_email);if(a){let o=Re(t.status),c=o?s.plan:"free";await oe(a.id,c,o?t.id:null),await z(a,F({...a,memory_plan:c}))}return}let r=await T(t.customer);if(!r)return;if(i&&i in D){let s=D[i],a=Re(t.status),o=a?s.tier:null;if(await ot(r.id,o,a?s.concurrency:0,a?t.id:null),s.includes_memory){let c=a?"pro":"free";a&&await Cs(r.memory_subscription_id,t.id),await oe(r.id,c,null),await z(r,F({...r,memory_plan:c}))}else r.memory_subscription_id===t.id&&(await oe(r.id,"free",null),await z(r,F({...r,memory_plan:"free"})));if(a){let c=await Be(r);c.ok||console.warn("[stripe] scheduled-action credential sync failed:",c.error??"unknown error")}}}if(e.type==="customer.subscription.deleted"){let t=e.data.object;if(pi(t)){let i=await T(t.customer);i&&(await st(i.id,0),await at(i.id,null));return}let n=le(t)?.price?.id??t.items.data[0]?.price?.id;if(n&&n in D){let i=D[n],r=await T(t.customer);r&&(await ot(r.id,null,0,null),(i.includes_memory||r.memory_subscription_id===t.id)&&(await oe(r.id,"free",null),await z(r,F({...r,memory_plan:"free"}))));return}if(n&&n in ce||await ct(t.id)){let i=await ct(t.id)??await T(t.customer);i&&(await oe(i.id,"free",null),await z(i,F({...i,memory_plan:"free"})));return}}}function mi(e){return e.lines.data.some(t=>gi(t)===dt)}async function Os(e,t){let i=(await x.invoicePayments.list({invoice:e,status:"paid",limit:10})).data.find(a=>a.status==="paid");if(!i)throw new Error("Paid concurrency invoice has no paid Stripe payment");let r;if(i.payment.type==="charge")r=i.payment.charge;else if(i.payment.type==="payment_intent"&&i.payment.payment_intent){let a=typeof i.payment.payment_intent=="string"?i.payment.payment_intent:i.payment.payment_intent.id;r=(await x.paymentIntents.retrieve(a,{expand:["latest_charge"]})).latest_charge}if(!r)throw new Error("Paid concurrency invoice has no receipt-capable charge");let s=typeof r=="string"?await x.charges.retrieve(r):r;s.receipt_email!==t&&await x.charges.update(s.id,{receipt_email:t})}function pi(e){return e.items.data.find(t=>t.price?.id===dt)}function Ds(e){let t=e.quantity??1;if(!Number.isSafeInteger(t)||t<1)throw new Error("Stripe concurrency pack quantity must be a positive safe integer");return t}function gi(e){let t=e;return t?.price?.id??t?.pricing?.price_details?.price??t?.plan?.id}async function yi(e,t){let n=await T(e);if(n)return n;if(t)return n=await ke(t),n&&await Pe(n.id,e),n}export{Oi as a,Ls as b,ln as c,gt as d,qs as e,Hs as f,_n as g,ji as h,Ws as i,Bs as j,qi as k,pn as l,Be as m,Et as n,j as o,le as p,Qs as q,Zs as r,ea as s,ze as t,Ye as u,Sn as v,dr as w,ur as x,At as y,ua as z,_a as A,ma as B,pa as C,Aa as D,wn as E,Ca as F,Ra as G,wa as H,Oa as I,Da as J,Ta as K,xa as L,La as M,$a as N,ka as O,Pa as P,Ma as Q,Ua as R,Dn as S,Fa as T,gr as U,ja as V,te as W,Ze as X,$ as Y,qa as Z,Er as _,Va as $,Ka as aa,Ja as ba,Un as ca,Fn as da,za as ea,so as fa,Bn as ga,Vn as ha,lo as ia,uo as ja,Kn as ka,po as la,Jn as ma,zn as na,Yn as oa,Xn as pa,Gn as qa,Qn as ra,it as sa,Co as ta,Ro as ua,wo as va,Oo as wa,Do as xa,To as ya,ei as za,ti as Aa,$t as Ba,ni as Ca,ii as Da,ss as Ea,Qo as Fa,Zo as Ga,ec as Ha,tc as Ia,nc as Ja,ic as Ka,rc as La,sc as Ma,ac as Na,oc as Oa,cc as Pa,dc as Qa,lc as Ra,uc as Sa,_c as Ta,mc as Ua,ri as Va,pc as Wa,yc as Xa,oi as Ya,gc as Za,fc as _a,Ft as $a,di as ab,ui as bb,Tc as cb,xc as db,_i as eb,x as fb,Rs as gb,Kc as hb};
|