mcp-scraper 0.95.2 → 0.96.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -1
- package/README.md +21 -3
- package/dist/actions-R4EJDLRO.js +1 -0
- package/dist/artifacts-4ZDNP2JW.js +1 -0
- package/dist/artifacts-N62OPIT5.js +1 -0
- package/dist/artifacts-PSADD5OC.js +1 -0
- package/dist/artifacts-RUR3OHAK.js +1 -0
- package/dist/bin/api-server.js +1 -1
- package/dist/bin/mcp-scraper-cli.js +22 -22
- package/dist/bin/mcp-scraper-core.js +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.js +1 -1
- package/dist/bin/paa-harvest.js +1 -1
- package/dist/blob-store-GIH26EDO.js +1 -0
- package/dist/bundle-FCXZMIRB.js +1 -0
- package/dist/chunk-24YDQ3EO.js +5 -0
- package/dist/chunk-3F7AFM53.js +1 -0
- package/dist/chunk-4HZXATHB.js +27 -0
- package/dist/chunk-55ZANTHL.js +3 -0
- package/dist/chunk-5BKSKLHX.js +1 -0
- package/dist/chunk-5LYINW7Z.js +8 -0
- package/dist/chunk-63VWBMYR.js +3 -0
- package/dist/chunk-6BD75VKS.js +218 -0
- package/dist/chunk-6N5EC7ZO.js +1 -0
- package/dist/chunk-7POHNTVW.js +1 -0
- package/dist/chunk-7SR7MEU5.js +62 -0
- package/dist/chunk-7WPCGRD7.js +182 -0
- package/dist/chunk-B3JRL45T.js +1 -0
- package/dist/chunk-B4ZKPLSP.js +1 -0
- package/dist/chunk-BPKTUCV7.js +12 -0
- package/dist/chunk-C7MGJUTU.js +1 -0
- package/dist/{chunk-W2BVJ7S2.js → chunk-CNSVZTTT.js} +4 -4
- package/dist/chunk-CORWWDHL.js +1 -0
- package/dist/chunk-CVQUHVHX.js +1 -0
- package/dist/chunk-FB4KBB5W.js +16 -0
- package/dist/{chunk-Y3Q4FIKK.js → chunk-H6MODA7W.js} +199 -84
- package/dist/chunk-HCK6PLM3.js +3 -0
- package/dist/chunk-HNNYXMSI.js +1 -0
- package/dist/chunk-HUNSEGUN.js +57 -0
- package/dist/{chunk-MC4W6TZT.js → chunk-HVQZRQRJ.js} +41 -8
- package/dist/chunk-IFPION5D.js +121 -0
- package/dist/chunk-IY6ZUUY7.js +173 -0
- package/dist/chunk-JHT6A4U6.js +11 -0
- package/dist/chunk-JMZIDHYQ.js +2 -0
- package/dist/chunk-KXJCGV3H.js +1 -0
- package/dist/chunk-MVMWPKRC.js +2 -0
- package/dist/chunk-NALXKTUU.js +1 -0
- package/dist/chunk-NNOX3FYG.js +198 -0
- package/dist/chunk-OILOHQNL.js +2 -0
- package/dist/chunk-OLYPLX5C.js +1 -0
- package/dist/chunk-QER2ELZP.js +1 -0
- package/dist/chunk-RDHGQ2WZ.js +1 -0
- package/dist/chunk-RIHI3KH7.js +955 -0
- package/dist/chunk-RN4VG3VH.js +4 -0
- package/dist/chunk-RYX4FJVH.js +1 -0
- package/dist/chunk-SDQW7IJN.js +1 -0
- package/dist/{chunk-ENZUJEIM.js → chunk-SE4VKLEC.js} +1 -1
- package/dist/chunk-SEZETSB3.js +1 -0
- package/dist/chunk-SFUOSJ3A.js +159 -0
- package/dist/chunk-UIOSADPB.js +1718 -0
- package/dist/{chunk-ZH75KWBI.js → chunk-UNEJYZ4T.js} +1 -1
- package/dist/chunk-UXNR3K6I.js +1 -0
- package/dist/{chunk-VREXSMC6.js → chunk-VVSHZV5S.js} +5 -5
- package/dist/chunk-WNUXMPVX.js +1 -0
- package/dist/chunk-WP3QS4CJ.js +1 -0
- package/dist/chunk-ZTMQEDIB.js +41 -0
- package/dist/contracts-GUXGFCVV.js +1 -0
- package/dist/core-schema-423BSTE6.js +936 -0
- package/dist/dispatch-P7IY75LH.js +1 -0
- package/dist/index.cjs +103 -213
- package/dist/index.d.cts +184 -169
- package/dist/index.d.ts +184 -169
- package/dist/index.js +1 -1
- package/dist/media-artifacts-67CJWI7O.js +1 -0
- package/dist/metering-ZH2P62IC.js +1 -0
- package/dist/migrations-3AKEC5QE.js +1 -0
- package/dist/queue-AC4XKD6Z.js +1 -0
- package/dist/reconciliation-UZUZ6B74.js +1 -0
- package/dist/report-artifact-offload-6OOY2PTM.js +1 -0
- package/dist/repository-4EC5EXVN.js +1 -0
- package/dist/{location-data-repository-A2QZVP7O.js → repository-CYXVMYAI.js} +1 -1
- package/dist/server-DXZ27KQO.js +5825 -0
- package/dist/settlement-6CS4JZTY.js +1 -0
- package/dist/worker-ATRD6VKM.js +1 -0
- package/package.json +2 -2
- package/dist/analytics-repository-DZZKDZL6.js +0 -1
- package/dist/chunk-6DRKDSXY.js +0 -182
- package/dist/chunk-6ND5AFV5.js +0 -27
- package/dist/chunk-7XDBGQDY.js +0 -6
- package/dist/chunk-7YOXSMHG.js +0 -1565
- package/dist/chunk-BLVBUMH5.js +0 -21
- package/dist/chunk-EDQ57S4P.js +0 -1
- package/dist/chunk-GLTECY3U.js +0 -1282
- package/dist/chunk-GPSPSJBJ.js +0 -1
- package/dist/chunk-HE45FFBU.js +0 -1
- package/dist/chunk-HUV2WTRW.js +0 -1
- package/dist/chunk-IXJQHXDC.js +0 -2595
- package/dist/chunk-K3D2S36Q.js +0 -16
- package/dist/chunk-KFOFU3LA.js +0 -1
- package/dist/chunk-LMWU7VPZ.js +0 -63
- package/dist/chunk-O4LUUIYG.js +0 -4
- package/dist/chunk-OLSW4AN4.js +0 -19
- package/dist/chunk-RLFCJGHA.js +0 -100
- package/dist/chunk-T4VTJNDZ.js +0 -84
- package/dist/chunk-TMB56NCA.js +0 -1
- package/dist/chunk-VXOXB3IB.js +0 -1
- package/dist/chunk-WO3N5FH2.js +0 -5
- package/dist/chunk-WO46LTBW.js +0 -1
- package/dist/chunk-XPEJB4BZ.js +0 -1
- package/dist/db-CDEZHBRN.js +0 -1
- package/dist/extract-bundle-SXC6EWJR.js +0 -26
- package/dist/gmail-service-EWR76XE7.js +0 -1
- package/dist/lead-list-enrichment-repository-SIWGXWQV.js +0 -1
- package/dist/operation-metering-UJ53YX7S.js +0 -1
- package/dist/server-QVEKDIDQ.js +0 -5014
- package/dist/site-extract-repository-23ND4N77.js +0 -1
- package/dist/stripe-event-worker-LPXTITVC.js +0 -1
- package/dist/worker-EDN2TJH3.js +0 -1
- /package/dist/{chunk-YGBTTW5D.js → chunk-2IATUJO6.js} +0 -0
- /package/dist/{chunk-6DTXIZY2.js → chunk-4ZVO6GX4.js} +0 -0
- /package/dist/{chunk-XG6GCEUE.js → chunk-U4WIJZCX.js} +0 -0
|
@@ -1,5 +1,59 @@
|
|
|
1
|
-
import{
|
|
2
|
-
|
|
1
|
+
import{H as sn,_ as an,c as Yt,j as ut,k as je,m as nn,z as rn}from"./chunk-SFUOSJ3A.js";import{B as gn,a as f,b as v,c as ue,d as Oe,e as yt,f as ln,g as un,h as U,i as $,l as _n,m as mn,n as pn,p as gt,u as B,v as Q,z as yn}from"./chunk-NNOX3FYG.js";import{a as qe}from"./chunk-NALXKTUU.js";import{a as Kt,b as Jt,f as zt}from"./chunk-6BD75VKS.js";import{d as on,g as He,i as We,k as _t,m as mt,o as cn,r as Be,t as dn,u as pt}from"./chunk-2IATUJO6.js";import{D as L,E as Qt,N as Zt,O as en,P as Re,Q as dt,R as tn,S as lt,U as te,W as le,p as Fe,q as Gt,v as Xt,z as ct}from"./chunk-CORWWDHL.js";import{f as Ue}from"./chunk-5LYINW7Z.js";import{a as I}from"./chunk-WNUXMPVX.js";import{randomUUID as Fi}from"crypto";async function fn(e,t,n){return(await I().execute({sql:`INSERT OR IGNORE INTO stripe_events
|
|
2
|
+
(event_id, event_type, stripe_created_at, status, attempt_count, received_at, next_attempt_at, processed_at, updated_at)
|
|
3
|
+
VALUES (?, ?, ?, 'pending', 0, datetime('now'), datetime('now'), datetime('now'), datetime('now'))`,args:[e,t,n]})).rowsAffected===1?"queued":"duplicate"}async function ra(e=50,t=300){let n=Math.max(1,Math.min(500,Math.trunc(e))),i=await I().execute({sql:`SELECT event_id
|
|
4
|
+
FROM stripe_events
|
|
5
|
+
WHERE (status IN ('pending', 'retryable') AND COALESCE(next_attempt_at, received_at) <= datetime('now'))
|
|
6
|
+
OR (status = 'processing' AND claimed_at <= datetime('now', '-' || ? || ' seconds'))
|
|
7
|
+
ORDER BY COALESCE(next_attempt_at, received_at), received_at
|
|
8
|
+
LIMIT ?`,args:[t,n]}),r=[];for(let s of i.rows){let a=String(s.event_id),o=Fi();if((await I().execute({sql:`UPDATE stripe_events
|
|
9
|
+
SET status = 'processing', attempt_count = attempt_count + 1,
|
|
10
|
+
claim_token = ?, claimed_at = datetime('now'), last_error = NULL,
|
|
11
|
+
last_error_code = NULL, updated_at = datetime('now')
|
|
12
|
+
WHERE event_id = ?
|
|
13
|
+
AND ((status IN ('pending', 'retryable') AND COALESCE(next_attempt_at, received_at) <= datetime('now'))
|
|
14
|
+
OR (status = 'processing' AND claimed_at <= datetime('now', '-' || ? || ' seconds')))`,args:[o,a,t]})).rowsAffected!==1)continue;let l=await I().execute({sql:"SELECT event_type, attempt_count FROM stripe_events WHERE event_id = ? LIMIT 1",args:[a]});r.push({eventId:a,eventType:l.rows[0]?.event_type!=null?String(l.rows[0].event_type):null,attemptCount:Number(l.rows[0]?.attempt_count??1),token:o})}return r}async function sa(e,t){return(await I().execute({sql:`UPDATE stripe_events
|
|
15
|
+
SET status = 'processed',
|
|
16
|
+
claim_token = NULL,
|
|
17
|
+
claimed_at = NULL,
|
|
18
|
+
next_attempt_at = NULL,
|
|
19
|
+
completed_at = datetime('now'),
|
|
20
|
+
last_error = NULL,
|
|
21
|
+
processed_at = datetime('now'),
|
|
22
|
+
updated_at = datetime('now')
|
|
23
|
+
WHERE event_id = ? AND status = 'processing' AND claim_token = ?`,args:[e,t]})).rowsAffected===1}async function aa(e,t,n,i,r){return(await I().execute({sql:`UPDATE stripe_events
|
|
24
|
+
SET status = 'retryable',
|
|
25
|
+
claim_token = NULL,
|
|
26
|
+
claimed_at = NULL,
|
|
27
|
+
last_error = ?,
|
|
28
|
+
last_error_code = ?,
|
|
29
|
+
next_attempt_at = datetime('now', '+' || ? || ' seconds'),
|
|
30
|
+
updated_at = datetime('now')
|
|
31
|
+
WHERE event_id = ? AND status = 'processing' AND claim_token = ?`,args:[n.slice(0,500),i.slice(0,100),Math.max(0,Math.trunc(r)),e,t]})).rowsAffected===1}async function oa(e,t,n,i){return(await I().execute({sql:`UPDATE stripe_events
|
|
32
|
+
SET status = 'dead_letter', claim_token = NULL, claimed_at = NULL,
|
|
33
|
+
last_error = ?, last_error_code = ?, next_attempt_at = NULL, updated_at = datetime('now')
|
|
34
|
+
WHERE event_id = ? AND status = 'processing' AND claim_token = ?`,args:[n.slice(0,500),i.slice(0,100),e,t]})).rowsAffected===1}async function ca(){let t=(await I().execute(`
|
|
35
|
+
SELECT
|
|
36
|
+
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS pending,
|
|
37
|
+
SUM(CASE WHEN status = 'processing' THEN 1 ELSE 0 END) AS processing,
|
|
38
|
+
SUM(CASE WHEN status = 'retryable' THEN 1 ELSE 0 END) AS retryable,
|
|
39
|
+
SUM(CASE WHEN status = 'dead_letter' THEN 1 ELSE 0 END) AS dead_letter,
|
|
40
|
+
MIN(CASE WHEN status IN ('pending', 'retryable') THEN COALESCE(next_attempt_at, received_at) END) AS oldest_due_at
|
|
41
|
+
FROM stripe_events
|
|
42
|
+
`)).rows[0];return{pending:Number(t?.pending??0),processing:Number(t?.processing??0),retryable:Number(t?.retryable??0),dead_letter:Number(t?.dead_letter??0),oldest_due_at:t?.oldest_due_at!=null?String(t.oldest_due_at):null}}import zs from"stripe";import{Hono as Ys}from"hono";import{waitUntil as Gs}from"@vercel/functions";async function P(e){let t=await I().execute({sql:"SELECT * FROM users WHERE stripe_customer_id = ? AND active = 1",args:[e]});return t.rows[0]?ut(t.rows[0]):void 0}async function Ve(e,t){await I().execute({sql:"UPDATE users SET stripe_customer_id = ? WHERE id = ?",args:[t,e]})}async function ft(e,t){if(!Number.isSafeInteger(t)||t<0)throw new Error("concurrency pack quantity must be a non-negative safe integer");await I().execute({sql:"UPDATE users SET concurrency_pack_quantity = ?, extra_concurrency_slots = ? WHERE id = ?",args:[t,t*2,e]})}async function vt(e,t){await I().execute({sql:"UPDATE users SET concurrency_stripe_sub_id = ? WHERE id = ?",args:[t,e]})}async function Et(e,t,n,i){await I().execute({sql:"UPDATE users SET subscription_tier = ?, subscription_concurrency = ?, subscription_id = ? WHERE id = ?",args:[t,Math.max(0,Math.round(n)),i,e]})}async function vn(e){if(!e.invoiceId||e.invoiceId.length>500)throw new Error("invoice id is required and must be bounded");if(!Number.isSafeInteger(e.creditsMc)||e.creditsMc<=0)throw new Error("subscription credit amount must be a positive integer");await Jt(e.userId);let t=I(),n=e.neverExpires?"9999-12-31 23:59:59":null,i=await t.batch([{sql:`INSERT OR IGNORE INTO ledger (user_id, amount_mc, operation, description, stripe_pi)
|
|
43
|
+
VALUES (?, ?, 'subscription', ?, ?)`,args:[e.userId,e.creditsMc,e.description,e.invoiceId]},{sql:`INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, stripe_pi, expires_at, never_expires)
|
|
44
|
+
SELECT ?, ?, ?, 'subscription', ?, COALESCE(?, datetime('now', ?)), ?
|
|
45
|
+
WHERE changes() = 1`,args:[e.userId,e.creditsMc,e.creditsMc,e.invoiceId,n,Kt,e.neverExpires?1:0]},{sql:"UPDATE users SET balance_mc = balance_mc + ? WHERE id = ? AND changes() = 1",args:[e.creditsMc,e.userId]},{sql:`UPDATE users
|
|
46
|
+
SET subscription_tier = ?, subscription_concurrency = ?, subscription_id = ?,
|
|
47
|
+
memory_plan = CASE WHEN ? IS NOT NULL THEN ? ELSE memory_plan END,
|
|
48
|
+
memory_subscription_id = CASE WHEN ? IS NOT NULL THEN NULL ELSE memory_subscription_id END
|
|
49
|
+
WHERE id = ?
|
|
50
|
+
AND EXISTS (
|
|
51
|
+
SELECT 1 FROM ledger
|
|
52
|
+
WHERE stripe_pi = ? AND user_id = ? AND amount_mc = ? AND operation = 'subscription'
|
|
53
|
+
)`,args:[e.tier,Math.max(0,Math.round(e.concurrency)),e.subscriptionId,e.includedMemoryPlan,e.includedMemoryPlan,e.includedMemoryPlan,e.userId,e.invoiceId,e.userId,e.creditsMc]}],"write"),s=(await t.execute({sql:`SELECT l.user_id, l.amount_mc, l.operation, u.balance_mc
|
|
54
|
+
FROM ledger l JOIN users u ON u.id = l.user_id
|
|
55
|
+
WHERE l.stripe_pi = ? LIMIT 1`,args:[e.invoiceId]})).rows[0];if(!s||Number(s.user_id)!==e.userId||Number(s.amount_mc)!==e.creditsMc||String(s.operation)!=="subscription")throw new Error("invoice idempotency key conflicts with an existing grant");return{granted:i[0]?.rowsAffected===1,balance_mc:Number(s.balance_mc??0)}}async function _e(e,t,n){await I().execute({sql:"UPDATE users SET memory_plan = ?, memory_subscription_id = ? WHERE id = ?",args:[t,n,e]})}async function ma(e){let n=(await I().execute({sql:"SELECT memory_provisioned FROM users WHERE id = ?",args:[e]})).rows[0];return Number(n?.memory_provisioned??0)===1}async function En(e,t){await I().execute({sql:"UPDATE users SET memory_provisioned = ? WHERE id = ?",args:[t?1:0,e]})}async function bt(e){let t=await I().execute({sql:"SELECT * FROM users WHERE memory_subscription_id = ? AND active = 1",args:[e]});return t.rows[0]?ut(t.rows[0]):void 0}import{createCipheriv as Ji,createDecipheriv as zi,randomBytes as Yi,scryptSync as Gi}from"crypto";import{setScheduleEntitlementTool as Xi}from"mcpscraper-memory-tools/tools/schedule/set-schedule-entitlement";import{createHash as ji,randomUUID as qi}from"crypto";import{exportJWK as Hi,importPKCS8 as Wi,SignJWT as Bi}from"jose";function Vi(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 Ki=Vi(),Ke=null;async function bn(){if(Ke)return Ke;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 Wi(t,"RS256",{extractable:!0}),i=await Hi(n),r={kty:i.kty,n:i.n,e:i.e},s=ji("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",Ke={privateKey:n,publicJwk:r,kid:s},Ke}async function fa(){return(await bn()).publicJwk}async function In(e){let{privateKey:t,kid:n}=await bn();return new Bi({scope:e.scope,plan:e.plan}).setProtectedHeader({alg:"RS256",kid:n}).setIssuer(Ki).setSubject(e.identity).setAudience(e.audience).setIssuedAt().setJti(qi()).setExpirationTime(`${e.ttlSeconds}s`).sign(t)}var Qi=()=>(process.env.MCP_MEMORY_BASE_URL??"https://memory.mcpscraper.dev").replace(/\/$/,""),Zi=()=>(process.env.MCP_MEMORY_RESOURCE_URL??"https://memory.mcpscraper.dev/mcp").replace(/\/$/,""),er=()=>process.env.MCP_MEMORY_ADMIN_KEY?.trim()??"",tr=300;function nr(e){let t=e.split(`
|
|
56
|
+
`).find(i=>i.startsWith("data:")),n=t?t.slice(5).trim():e;return JSON.parse(n)}function It(e){return e?(process.env.MEMORY_OPERATOR_IDENTITIES??"").split(",").map(n=>n.trim().toLowerCase()).filter(Boolean).includes(e.trim().toLowerCase()):!1}function An(){return Gi(qe(),"mcp-memory-key-v1",32)}function Na(e){let t=Yi(12),n=Ji("aes-256-gcm",An(),t),i=Buffer.concat([n.update(e,"utf8"),n.final()]),r=n.getAuthTag();return`${t.toString("base64")}:${r.toString("base64")}:${i.toString("base64")}`}function Ca(e){try{let[t,n,i]=e.split(":"),r=zi("aes-256-gcm",An(),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 Sn(e,t,n){try{let i=await fetch(`${Qi()}/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=nr(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 hn(e){return e.email}function ir(e){return hn(e)}function wa(e){return It(e.email)?"unlimited":e.memory_plan==="pro"||e.memory_plan==="team"?e.memory_plan:"free"}async function Ra(e){try{let{key:t}=await Nn(e);if(!t)return;(await Sn("listVaultsTool",{},t)).ok&&await En(e.id,!0)}catch{}}function rr(e){return e?!!Qt[e]?.includes_memory:!1}function V(e){return e.memory_plan==="team"?"team":rr(e.subscription_tier)||e.memory_plan==="pro"?"pro":"free"}async function Nn(e,t){try{let n=It(e.email),i=n?"unlimited":t??V(e),r=n?"memory.read memory.write memory.admin":"memory.read memory.write";return{key:await In({identity:hn(e),scope:r,plan:i,audience:Zi(),ttlSeconds:tr})}}catch(n){return{key:null,error:n instanceof Error?n.message:"memory service authorization unavailable"}}}async function Z(e,t){let n=It(e.email)?"unlimited":t,{key:i,error:r}=await Nn(e,n);if(!i)return{ok:!1,error:r??"memory unavailable"};let s=await Sn("listVaultsTool",{},i);return{ok:s.ok,error:s.error}}async function Je(e){let t=er();if(!t)return{ok:!1,error:"memory not configured (MCP_MEMORY_ADMIN_KEY missing)"};let n=ir(e),i=await Xi.execute({apiKey:t,granteeIdentity:n,mcpScraperApiKey:e.api_key},{});return{ok:i.ok,error:i.error}}var Cn=["active","trialing","past_due"];function De(e){return Cn.includes(e)}function wn(e,t){for(let n of Cn){let i=e.find(r=>r.status===n&&t(r));if(i)return i}}var sr="price_1TtYPAS8aAcsk3TGHOgrDZiQ";function ar(e=new Date){return`${e.getUTCFullYear()}-${String(e.getUTCMonth()+1).padStart(2,"0")}`}var or=new Set(["connected","needs_reauth"]);function cr(e){return or.has(String(e.lifecycleStatus??""))}async function dr(e,t){let n=await I().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 lr(e,t,n){await sn();let i=Number(e),r=I();return await r.batch([{sql:`
|
|
3
57
|
INSERT OR IGNORE INTO connected_account_charge_transfers (
|
|
4
58
|
user_id, period_start, from_connection_id, to_connection_id, provider_config_key
|
|
5
59
|
)
|
|
@@ -50,7 +104,7 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
50
104
|
AND existing.connection_id = ?
|
|
51
105
|
AND existing.period_start = ?
|
|
52
106
|
)
|
|
53
|
-
`,args:[t,i,n,i,n,t,i,t,n]}],"write"),(await r.execute({sql:"SELECT 1 FROM connected_account_charges WHERE user_id = ? AND connection_id = ? AND period_start = ? LIMIT 1",args:[i,t,n]})).rows.length>0}async function
|
|
107
|
+
`,args:[t,i,n,i,n,t,i,t,n]}],"write"),(await r.execute({sql:"SELECT 1 FROM connected_account_charges WHERE user_id = ? AND connection_id = ? AND period_start = ? LIMIT 1",args:[i,t,n]})).rows.length>0}async function ht(e){let t=await I().execute({sql:"SELECT * FROM connected_account_billing WHERE user_id = ? LIMIT 1",args:[Number(e)]});return t.rows[0]?t.rows[0]:null}async function At(e){await I().execute({sql:`INSERT INTO connected_account_billing
|
|
54
108
|
(user_id, stripe_subscription_id, stripe_subscription_item_id, price_id, quantity, status, last_error_code, checked_at, synced_at, updated_at)
|
|
55
109
|
VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'), CASE WHEN ? THEN datetime('now') ELSE NULL END, datetime('now'))
|
|
56
110
|
ON CONFLICT(user_id) DO UPDATE SET
|
|
@@ -62,36 +116,36 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
62
116
|
last_error_code = excluded.last_error_code,
|
|
63
117
|
checked_at = datetime('now'),
|
|
64
118
|
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
|
|
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
|
|
119
|
+
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 ht(e.userId);if(!t)throw new Error("connected account billing state was not persisted");return t}var K=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 Nt(){let e=process.env.CONNECTED_ACCOUNT_PRICE_ID?.trim();return e||sr||null}function St(e,t){let n=Math.max(0,Math.round(e));return{provider:"nango",billingMode:"flat_recurring_credits",priceId:Nt(),unitAmountCredits:Fe,interval:"month",quantity:n,projectedMonthlyCredits:n*Fe,status:t?.status??"pending",lastErrorCode:t?.last_error_code??null,syncedAt:t?.synced_at??null,usage:Xt.usage}}function me(e){return e.items.data.find(t=>!!t.price?.id&&t.price.id in L)}function Ma(e){return wn(e,t=>!!me(t))}function ur(e){return e instanceof K?e.code:"stripe_connection_billing_failed"}async function ze(e,t,n){await At({userId:e.id,stripeSubscriptionId:e.subscription_id,stripeSubscriptionItemId:null,priceId:Nt(),quantity:t,status:n instanceof K&&n.code!=="stripe_connection_billing_failed"?"blocked":"error",lastErrorCode:ur(n),synced:!1})}async function Ua(e,t){let n=[...new Set(t.filter(cr).map(c=>c.connectionId))],i=n.length,r=Nt();if(!r){let c=new K("Connected-account billing is not configured.","connected_account_price_not_configured",503);throw await ze(e,i,c),c}if(!e.subscription_id||!(e.subscription_tier??"")||!Object.values(L).some(c=>c.tier===e.subscription_tier)){if(i===0){let l=await At({userId:e.id,stripeSubscriptionId:null,stripeSubscriptionItemId:null,priceId:r,quantity:0,status:"synced",lastErrorCode:null,synced:!0});return St(0,l)}let c=new K("An active paid plan is required before connected accounts can be billed.","paid_plan_required",403);throw await ze(e,i,c),c}let s=await ht(e.id),a=Math.max(0,s?.quantity??0),o=ar();try{let c=await dr(e.id,o),l=n.filter(d=>!c.has(d));for(let d of l){if(await lr(e.id,d,o))continue;let p=Gt,E=await zt(e.id,p,Ue.CONNECTED_ACCOUNT_FEE,`1 connected account at ${Fe} credits/month (${o})`,`connection-fee:${d}:${o}`);if(!E.ok){let b=new K(`Insufficient credits to connect. Balance: ${te(E.balance_mc)} credits. Connecting requires ${te(p)} credits (${te(p-E.balance_mc)} short). Upgrade your plan or wait for your credits to refresh.`,"insufficient_credits",402,{balanceCredits:te(E.balance_mc),requiredCredits:te(p),shortByCredits:te(Math.max(0,p-E.balance_mc))});throw await ze(e,a,b),b}await I().execute({sql:`INSERT INTO connected_account_charges (user_id, connection_id, period_start, amount_mc)
|
|
120
|
+
VALUES (?, ?, ?, ?) ON CONFLICT(user_id, connection_id, period_start) DO NOTHING`,args:[Number(e.id),d,o,p]}),E.duplicate||await rn(e.id,1)}let _=await At({userId:e.id,stripeSubscriptionId:e.subscription_id,stripeSubscriptionItemId:null,priceId:r,quantity:i,status:"synced",lastErrorCode:null,synced:!0});return St(i,_)}catch(c){throw c instanceof K?c:(await ze(e,a,c),new K("Unable to update connected-account billing.","stripe_connection_billing_failed",503))}}async function Fa(e,t){return St(t,await ht(e))}import{createHash as Bc,createHmac as jt,generateKeyPairSync as Ss,randomUUID as de,sign as hs}from"crypto";import Ns from"twilio";import{z as u}from"zod";import{z as R}from"zod";var _r=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"]),Te=R.string().trim().length(3).transform(e=>e.toUpperCase()).refine(e=>_r.has(e),"Provide a valid ISO-4217 currency."),Ye=R.number().int("Money must be represented as integer minor units.").min(0,"Money cannot be negative.").max(Number.MAX_SAFE_INTEGER),Ge=R.string().trim().min(1).max(80).default("1"),mr=R.enum(["semantic_markup","source_event","crm_field","transaction"]),pr=R.object({mode:R.literal("none"),ruleVersion:Ge}).strict(),yr=R.object({mode:R.literal("fixed"),valueMinor:Ye,currency:Te,ruleVersion:Ge}).strict(),gr=R.object({mode:R.literal("dynamic"),source:mr,currency:Te,fallbackValueMinor:Ye.nullable().default(null),ruleVersion:Ge}).strict(),Rn=R.discriminatedUnion("mode",[pr,yr,gr]),fr=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:Ye,currency:Te}).strict(),Xe=R.object({targetEventName:R.string().trim().regex(/^[a-z][a-z0-9_]{1,79}$/),base:Rn,conditionalOverride:fr.nullable().default(null),ruleVersion:Ge}).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 vr(e,t){let n=Ye.parse(e.valueMinor),i=On(t,e.currency);return ne(e.observedAt,"observedAt"),{valueMinor:n,currency:i,observedAt:new Date(e.observedAt).toISOString()}}function Er(e){return Rn.parse(e)}function On(e,t){let i=(Array.isArray(e)?e:[e,t]).filter(r=>typeof r=="string"&&r.trim().length>0).map(r=>Te.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 br(e,t={}){let n=Er(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=vr(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 Ir(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 Dn(e,t){let n=Xe.parse(e);if(t.targetEvent.eventName!==n.targetEventName)throw new Error(`Value rule targets ${n.targetEventName}, not ${t.targetEvent.eventName}.`);let i=br({...n.base,ruleVersion:n.ruleVersion},t);if(!n.conditionalOverride)return i;let r=Ir(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?On(i.currency,n.conditionalOverride.currency):Te.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 Tn=u.object({gclid:u.string().trim().max(500).optional(),gbraid:u.string().trim().max(500).optional(),wbraid:u.string().trim().max(500).optional(),fbclid:u.string().trim().max(500).optional(),fbp:u.string().trim().max(500).optional(),fbc:u.string().trim().max(500).optional(),ttclid:u.string().trim().max(500).optional(),rdt_cid:u.string().trim().max(500).optional(),msclkid:u.string().trim().max(500).optional(),li_fat_id:u.string().trim().max(500).optional(),snapclid:u.string().trim().max(500).optional()}).strict(),Ar=u.object({site_analytics:u.enum(["granted","denied","unknown"]),enhanced_matching:u.enum(["granted","denied","unknown"]),advertising_measurement:u.enum(["granted","denied","unknown"])}).strict(),Sr=u.object({analytics:u.enum(["granted","denied","unknown"]).default("unknown"),adUserData:u.enum(["granted","denied","unknown"]).default("unknown"),adPersonalization:u.enum(["granted","denied","unknown"]).default("unknown"),globalPrivacyControl:u.boolean().default(!1),occurredAt:u.string().datetime(),receiptId:u.string().uuid().optional(),policyVersion:u.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}})),xn=u.enum(["browser","form","call","crm","transaction","custom_server","conversion","delivery"]),hr=u.object({schemaVersion:u.literal(1).default(1),eventId:u.string().trim().min(8).max(200),sourceEventId:u.string().trim().min(1).max(300).optional(),source:u.string().trim().min(1).max(80).default("xray"),sourceAccountRef:u.string().trim().max(240).optional(),eventKind:xn,eventName:u.string().trim().regex(/^[a-z][a-z0-9_]{1,79}$/),occurredAt:u.string().datetime(),visitorId:u.string().trim().max(160).optional(),sessionId:u.string().trim().max(160).optional(),personRef:u.string().trim().max(240).optional(),orderId:u.string().trim().max(240).optional(),callId:u.string().trim().max(240).optional(),dealId:u.string().trim().max(240).optional(),path:u.string().trim().max(2e3).optional(),canonicalUrl:u.string().url().max(3e3).optional(),sourceName:u.string().trim().max(180).optional(),medium:u.string().trim().max(180).optional(),campaign:u.string().trim().max(240).optional(),clickIds:Tn.optional(),consent:Sr.optional(),valueMinor:u.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),currency:u.string().trim().regex(/^[A-Za-z]{3}$/).optional(),properties:u.record(u.string().max(80),u.unknown()).default({})}).strict(),Qe=hr.extend({sourceEventId:u.string().trim().min(1).max(300),source:u.string().trim().min(1).max(80),sourceAccountRef:u.string().trim().min(1).max(240)}),Va=u.object({sourceEventId:u.string().trim().min(1).max(300),source:u.string().trim().min(1).max(80),sourceAccountRef:u.string().trim().min(1).max(240),contactId:u.string().trim().max(240).optional(),dealId:u.string().trim().min(1).max(240),pipelineId:u.string().trim().max(240).optional(),stageId:u.string().trim().min(1).max(240),stageName:u.string().trim().max(240).optional(),occurredAt:u.string().datetime(),valueMinor:u.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),currency:u.string().trim().regex(/^[A-Za-z]{3}$/).optional(),visitorId:u.string().trim().max(160).optional(),sessionId:u.string().trim().max(160).optional(),clickIds:Tn.optional(),email:u.string().email().max(320).optional(),phone:u.string().trim().max(80).optional(),orderId:u.string().trim().max(240).optional(),status:u.enum(["open","won","lost","spam","unknown"]).default("unknown")}).strict(),Nr=u.object({eventName:u.string().regex(/^[a-z][a-z0-9_]{1,79}$/).optional(),eventKind:xn.optional(),source:u.string().trim().max(80).optional(),stageId:u.string().trim().max(240).optional(),status:u.enum(["open","won","lost","spam","unknown"]).optional(),answered:u.boolean().optional(),minimumCallDurationSeconds:u.number().int().min(0).max(86400).optional(),excludedTags:u.array(u.string().trim().max(80)).max(30).default([]),requiredTags:u.array(u.string().trim().max(80)).max(30).default([])}).strict(),Ln=u.object({name:u.string().trim().min(1).max(120),conversionKind:u.string().trim().regex(/^[a-z][a-z0-9_]{1,79}$/),condition:Nr,defaultValueMinor:u.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),defaultCurrency:u.string().trim().regex(/^[A-Za-z]{3}$/).default("USD"),valueRule:Xe.optional(),enabled:u.boolean().default(!0)}).strict();function Cr(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 wr=u.string().trim().max(253).transform(Cr).refine(Boolean,"Provide a valid bare hostname."),xe=u.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=u.object({value:u.number().finite().min(0).max(1e12).optional(),currency:u.string().trim().regex(/^[A-Za-z]{3}$/).transform(e=>e.toUpperCase()).optional(),orderId:u.string().trim().min(1).max(200).optional(),contentId:u.string().trim().min(1).max(200).optional(),contentType:u.string().trim().min(1).max(80).optional(),placement:xe.optional(),component:xe.optional(),action:xe.optional(),destination:xe.optional(),plan:xe.optional()}).strict(),Ct=u.object({name:u.string().trim().min(1).max(120),eventName:u.string().trim().regex(/^[a-z][a-z0-9_]{1,79}$/),triggerKind:u.enum(["page","click","form","semantic"]),path:u.string().trim().startsWith("/").max(2e3).optional(),pathMatch:u.enum(["exact","prefix"]).default("exact"),selector:u.string().trim().min(1).max(500).optional(),hostnames:u.array(wr).max(25).transform(e=>[...new Set(e)]).default([]),metadata:Rr.optional(),valueRule:Xe.optional(),oncePerSession:u.boolean().default(!1),enabled:u.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 Or,randomUUID as wt}from"crypto";import io from"ipaddr.js";import{randomUUID as $n}from"crypto";async function Ga(e){return(await v().query(`SELECT id, site_id, name, status, version, approved_at
|
|
67
121
|
FROM analytics_identity_namespaces
|
|
68
122
|
WHERE site_id=$1 AND status IN ('draft','approved','disabled')
|
|
69
123
|
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
|
|
124
|
+
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 kn={domain:"host",application:"server",phone:"call",crm:"crm"},Pn={host:"domain",server:"application",call:"phone",crm:"crm"};function Mn(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 Xa(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
125
|
FROM analytics_identity_namespaces
|
|
72
126
|
WHERE site_id=$1 AND status IN ('draft','approved','disabled')
|
|
73
127
|
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
128
|
id, site_id, name, status, approved_hosts, approved_sources, version, approved_by_user_id
|
|
75
129
|
) VALUES ($1,$2,$3,'draft','[]'::jsonb,'[]'::jsonb,1,$4)
|
|
76
|
-
RETURNING id, site_id, name, status, version, approved_at`,[
|
|
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=
|
|
130
|
+
RETURNING id, site_id, name, status, version, approved_at`,[$n(),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 Qa(e){let t=Mn(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
|
|
131
|
+
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=kn[e.sourceKind],s=await n.query(`INSERT INTO analytics_identity_sources(id, namespace_id, site_id, source_kind, source_ref, status)
|
|
78
132
|
VALUES ($1,$2,$3,$4,$5,'approved')
|
|
79
133
|
ON CONFLICT(namespace_id, source_kind, source_ref) DO UPDATE
|
|
80
134
|
SET status='approved', revoked_at=NULL
|
|
81
|
-
RETURNING id, namespace_id, site_id, source_kind, source_ref, status, created_at, revoked_at`,[
|
|
135
|
+
RETURNING id, namespace_id, site_id, source_kind, source_ref, status, created_at, revoked_at`,[$n(),e.namespaceId,e.siteId,r,t]);await n.query(`UPDATE analytics_identity_namespaces
|
|
82
136
|
SET status='approved', approved_by_user_id=$3, approved_at=COALESCE(approved_at, now()),
|
|
83
137
|
approved_hosts=CASE WHEN $4='host'
|
|
84
138
|
THEN (SELECT jsonb_agg(DISTINCT value) FROM jsonb_array_elements_text(approved_hosts || to_jsonb($5::text)) value)
|
|
85
139
|
ELSE approved_hosts END,
|
|
86
140
|
approved_sources=(SELECT jsonb_agg(DISTINCT value) FROM jsonb_array_elements_text(approved_sources || to_jsonb(($4 || ':' || $5)::text)) value),
|
|
87
141
|
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:
|
|
142
|
+
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:Pn[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 Ze(e){let t=v();if(!(await t.query(`SELECT id FROM analytics_identity_namespaces
|
|
89
143
|
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=
|
|
91
|
-
WHERE namespace_id=$1 AND site_id=$2 AND source_kind=$3 AND source_ref=$4 AND status='approved'`,[e.namespaceId,e.siteId,
|
|
144
|
+
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=Mn(e.source.kind,e.source.ref);if(!(await t.query(`SELECT id FROM analytics_identity_sources
|
|
145
|
+
WHERE namespace_id=$1 AND site_id=$2 AND source_kind=$3 AND source_ref=$4 AND status='approved'`,[e.namespaceId,e.siteId,kn[e.source.kind],r])).rows[0])throw new f("analytics_namespace_source_unapproved","This identity source is not approved.",403)}}async function Za(e){return await Ze(e),(await v().query(`SELECT id, namespace_id, site_id, source_kind, source_ref, status, created_at, revoked_at
|
|
92
146
|
FROM analytics_identity_sources
|
|
93
147
|
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:
|
|
148
|
+
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:Pn[n.source_kind],sourceRef:n.source_ref,status:n.status,createdAt:n.created_at.toISOString(),revokedAt:n.revoked_at?.toISOString()??null}))}function Dr(){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 Rt(e,t){return Or("sha256",Dr()).update(`xray-candidate:v1:${e}:${t}`).digest("hex")}async function oo(e){await Ze(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
149
|
array_remove(array_agg(DISTINCT e.feature_kind) FILTER (WHERE e.contribution > 0), NULL) supporting,
|
|
96
150
|
array_remove(array_agg(DISTINCT e.feature_kind) FILTER (WHERE e.contribution < 0), NULL) conflicting
|
|
97
151
|
FROM analytics_candidate_associations a
|
|
@@ -99,7 +153,7 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
99
153
|
WHERE a.site_id=$1 AND a.namespace_id=$2 AND a.status='active' AND a.confidence_band='medium'
|
|
100
154
|
AND a.expires_at > $3::timestamptz
|
|
101
155
|
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
|
|
156
|
+
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 Un(e){await Ze({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=Rt("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
157
|
SET status='promoted', promoted_person_id=$4, promoted_at=$5, updated_at=now()
|
|
104
158
|
WHERE id=$1 AND site_id=$2 AND namespace_id=$3
|
|
105
159
|
AND (status IN ('active','expired') OR (status='promoted' AND promoted_person_id=$4))
|
|
@@ -107,41 +161,41 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
107
161
|
id, site_id, namespace_id, association_id, person_id, evidence_kind,
|
|
108
162
|
evidence_ref_hmac, promoted_by, promoted_by_user_id, promoted_at
|
|
109
163
|
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)
|
|
110
|
-
ON CONFLICT(association_id, person_id) DO NOTHING`,[
|
|
164
|
+
ON CONFLICT(association_id, person_id) DO NOTHING`,[wt(),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
165
|
id, site_id, namespace_ref, subject_ref, action, idempotency_key,
|
|
112
166
|
actor_user_id, result_code, affected_counts, completed_at
|
|
113
167
|
) VALUES ($1,$2,$3,$4,'candidate_promoted',$5,$6,'promoted',$7::jsonb,$8)
|
|
114
|
-
ON CONFLICT(site_id, idempotency_key) DO NOTHING`,[
|
|
168
|
+
ON CONFLICT(site_id, idempotency_key) DO NOTHING`,[wt(),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
169
|
id,site_id,namespace_id,canonical_person_id,canonical_person_ref_hmac,
|
|
116
170
|
evidence_kind,evidence_ref_hmac,prior_person_ids,prior_identity_node_ids,
|
|
117
171
|
candidate_association_ids,affected_history_counts,schema_version,idempotency_key,promoted_at
|
|
118
172
|
) VALUES($1,$2,$3,$4,$5,$6,$7,'{}'::uuid[],$8::uuid[],$9::uuid[],$10::jsonb,
|
|
119
173
|
'xray.identity-promotion.v1',$11,$12)
|
|
120
|
-
ON CONFLICT(site_id,idempotency_key) DO NOTHING`,[
|
|
174
|
+
ON CONFLICT(site_id,idempotency_key) DO NOTHING`,[wt(),e.siteId,e.namespaceId,e.personId,Rt("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}),Rt("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 zr}from"crypto";import{z}from"zod";import{createHash as Tr,randomUUID as ie}from"crypto";function S(e){return e??v()}function et(e){return Tr("sha256").update(JSON.stringify(e)).digest("hex")}async function re(e){let t=await S(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 po(e){let t=on.parse(e.capability),n=S(e.db),i=await re({siteId:e.siteId,connectionId:e.connectionId,db:n});await n.query(`INSERT INTO analytics_crm_connection_capabilities(
|
|
121
175
|
site_id,connection_id,provider,contract_version,mode,schema_fingerprint,allowed_operations,evidence,verified_at,updated_at
|
|
122
176
|
) VALUES($1,$2,$3,1,$4,$5,$6::jsonb,$7::jsonb,$8,now())
|
|
123
177
|
ON CONFLICT(connection_id,contract_version) DO UPDATE SET
|
|
124
178
|
mode=EXCLUDED.mode,schema_fingerprint=EXCLUDED.schema_fingerprint,
|
|
125
179
|
allowed_operations=EXCLUDED.allowed_operations,evidence=EXCLUDED.evidence,
|
|
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
|
|
180
|
+
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 yo(e){let t=He.parse(e.plan),n=S(e.db),i=await re({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:He.parse(r.rows[0].plan),reused:!0}}return await n.query(`INSERT INTO analytics_crm_provisioning_plans(
|
|
127
181
|
id,site_id,connection_id,provider,contract_version,tenant_schema_version,plan,status,
|
|
128
182
|
created_by_user_id,public_plan_id,schema_fingerprint,request_fingerprint,created_at,updated_at
|
|
129
|
-
) VALUES($1,$2,$3,$4,1,$5,$6::jsonb,'preview',$7,$8,$9,$10,$11,$11)`,[
|
|
183
|
+
) VALUES($1,$2,$3,$4,1,$5,$6::jsonb,'preview',$7,$8,$9,$10,$11,$11)`,[ie(),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 go(e){let t=await S(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 He.parse(t.rows[0].plan)}async function fo(e){let t=We.parse(e.receipt),n=S(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
|
|
130
184
|
JOIN analytics_crm_provisioning_plans p ON p.id=r.plan_id
|
|
131
|
-
WHERE p.site_id=$1 AND r.public_receipt_id=$2`,[e.siteId,t.receiptId]);if(r.rowCount&&
|
|
185
|
+
WHERE p.site_id=$1 AND r.public_receipt_id=$2`,[e.siteId,t.receiptId]);if(r.rowCount&&We.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(
|
|
132
186
|
id,plan_id,operation,status,safe_error_code,warnings,created_at,public_receipt_id,receipt
|
|
133
187
|
) VALUES($1,$2,$3,$4,$5,'[]'::jsonb,$6,$7,$8::jsonb)
|
|
134
188
|
ON CONFLICT (public_receipt_id) WHERE public_receipt_id IS NOT NULL
|
|
135
|
-
DO UPDATE SET operation=EXCLUDED.operation,status=EXCLUDED.status,safe_error_code=EXCLUDED.safe_error_code,receipt=EXCLUDED.receipt`,[
|
|
189
|
+
DO UPDATE SET operation=EXCLUDED.operation,status=EXCLUDED.status,safe_error_code=EXCLUDED.safe_error_code,receipt=EXCLUDED.receipt`,[ie(),i.rows[0].id,s,a,t.errorCode??null,t.occurredAt,t.receiptId,JSON.stringify(t)]),await n.query(`UPDATE analytics_crm_provisioning_plans
|
|
136
190
|
SET status=$3,approved_by_user_id=$4,approved_at=COALESCE(approved_at,$5),
|
|
137
191
|
approved_field_ids=$6::jsonb,updated_at=$5
|
|
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
|
|
192
|
+
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 vo(e){let t=await S(e.db).query(`SELECT r.receipt FROM analytics_crm_provisioning_receipts r
|
|
139
193
|
JOIN analytics_crm_provisioning_plans p ON p.id=r.plan_id
|
|
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
|
|
194
|
+
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 We.parse(t.rows[0].receipt)}var Ot=e=>e instanceof Date?e.toISOString():e;function Dt(e){return cn.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:Ot(e.last_started_at),lastSuccessAt:Ot(e.last_success_at),nextRunAt:Ot(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 Eo(e){let t=S(e.db),n=await re({siteId:e.siteId,connectionId:e.connectionId,db:t}),i=et({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:Dt(r.rows[0])}}let s=await t.query(`INSERT INTO analytics_crm_sync_runs(
|
|
141
195
|
sync_id,site_id,connection_id,provider,mode,state,mapping,mapping_version,capability_evidence,idempotency_key,request_fingerprint,last_started_at
|
|
142
196
|
) 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)
|
|
143
197
|
VALUES($1,'pending',now(),now()) ON CONFLICT(connection_id)
|
|
144
|
-
DO UPDATE SET status='pending',last_started_at=now(),updated_at=now()`,[n]),{reused:!1,status:
|
|
198
|
+
DO UPDATE SET status='pending',last_started_at=now(),updated_at=now()`,[n]),{reused:!1,status:Dt(s.rows[0])}}async function bo(e){let t=await S(e.db).query("SELECT * FROM analytics_crm_sync_runs WHERE site_id=$1 AND sync_id=$2",[e.siteId,e.syncId]);return t.rowCount?Dt(t.rows[0]):null}async function Io(e){let t=S(e.db);return(await t.query(`UPDATE analytics_crm_sync_runs SET next_run_at=$4,interval_seconds=$5,updated_at=now()
|
|
145
199
|
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
|
|
146
200
|
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)
|
|
147
201
|
VALUES($1,$2,'pending',$3)
|
|
@@ -149,15 +203,15 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
149
203
|
status=CASE WHEN analytics_reconciliation_jobs.status='leased' THEN 'leased' ELSE 'pending' END,
|
|
150
204
|
next_attempt_at=CASE WHEN analytics_reconciliation_jobs.status='leased'
|
|
151
205
|
THEN analytics_reconciliation_jobs.next_attempt_at ELSE EXCLUDED.next_attempt_at END,
|
|
152
|
-
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
|
|
206
|
+
updated_at=now()`,[ie(),e.connectionId,e.nextRunAt]),!0):!1}async function Ao(e){let t=S(e.db),n=await t.query(`UPDATE analytics_crm_sync_runs SET state='cancelled',next_run_at=NULL,updated_at=now()
|
|
207
|
+
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 So(e){let t=S(e.db);await re({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)
|
|
154
208
|
VALUES($1,'inbound','running',now(),now())
|
|
155
209
|
ON CONFLICT(connection_id) DO UPDATE SET
|
|
156
210
|
direction=CASE WHEN analytics_crm_sync_state.direction='outbound' THEN 'bidirectional' ELSE analytics_crm_sync_state.direction END,
|
|
157
211
|
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()
|
|
158
212
|
WHERE sync_id=(SELECT sync_id FROM analytics_crm_sync_runs
|
|
159
213
|
WHERE site_id=$1 AND connection_id::text=$2 AND state NOT IN ('cancelled','webhook_only')
|
|
160
|
-
ORDER BY created_at DESC LIMIT 1)`,[e.siteId,e.connectionId])}async function
|
|
214
|
+
ORDER BY created_at DESC LIMIT 1)`,[e.siteId,e.connectionId])}async function ho(e){let t=S(e.db);await re({siteId:e.siteId,connectionId:e.connectionId,db:t}),await t.query(`INSERT INTO analytics_crm_sync_state(
|
|
161
215
|
connection_id,cursor,direction,status,last_started_at,last_completed_at,last_success_at,next_sync_at,updated_at
|
|
162
216
|
) VALUES($1,$2::jsonb,'inbound','healthy',now(),now(),now(),$3,now())
|
|
163
217
|
ON CONFLICT(connection_id) DO UPDATE SET
|
|
@@ -172,7 +226,7 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
172
226
|
WHERE site_id=$1 AND connection_id::text=$2 AND state <> 'cancelled'
|
|
173
227
|
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(
|
|
174
228
|
id,site_id,connection_id,direction,operation,subject_ref,external_receipt_id,status,occurred_at
|
|
175
|
-
) VALUES($1,$2,$3,'inbound','historical_pull',NULL,$4,'accepted',now())`,[
|
|
229
|
+
) VALUES($1,$2,$3,'inbound','historical_pull',NULL,$4,'accepted',now())`,[ie(),e.siteId,e.connectionId,e.receiptFingerprint])}async function No(e){let t=S(e.db);await re({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)
|
|
176
230
|
VALUES($1,'inbound','degraded',now(),$2,$3,now())
|
|
177
231
|
ON CONFLICT(connection_id) DO UPDATE SET
|
|
178
232
|
direction=CASE WHEN analytics_crm_sync_state.direction='outbound' THEN 'bidirectional' ELSE analytics_crm_sync_state.direction END,
|
|
@@ -183,7 +237,7 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
183
237
|
WHERE site_id=$1 AND connection_id::text=$2 AND state <> 'cancelled'
|
|
184
238
|
ORDER BY created_at DESC LIMIT 1)`,[e.siteId,e.connectionId,e.safeErrorCode,e.nextRunAt]),await t.query(`INSERT INTO analytics_crm_sync_receipts(
|
|
185
239
|
id,site_id,connection_id,direction,operation,subject_ref,external_receipt_id,status,safe_error_code,occurred_at
|
|
186
|
-
) VALUES($1,$2,$3,'inbound','historical_pull',NULL,$4,$5,$6,now())`,[
|
|
240
|
+
) VALUES($1,$2,$3,'inbound','historical_pull',NULL,$4,$5,$6,now())`,[ie(),e.siteId,e.connectionId,e.receiptFingerprint,n,e.safeErrorCode])}function Fn(e){return Be.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 Co(e){let t=Be.parse(e.policy),n=S(e.db),i=await re({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,
|
|
187
241
|
c.schema_fingerprint current_schema_fingerprint
|
|
188
242
|
FROM analytics_crm_provisioning_receipts r
|
|
189
243
|
JOIN analytics_crm_provisioning_plans p ON p.id=r.plan_id
|
|
@@ -201,14 +255,14 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
201
255
|
AND analytics_crm_outbound_policies.connection_id=EXCLUDED.connection_id
|
|
202
256
|
AND analytics_crm_outbound_policies.kind=EXCLUDED.kind
|
|
203
257
|
AND analytics_crm_outbound_policies.version=EXCLUDED.version
|
|
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
|
|
258
|
+
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 Fn(r.rows[0])}async function jn(e){return(await S(e.db).query("SELECT * FROM analytics_crm_outbound_policies WHERE site_id=$1 ORDER BY updated_at DESC,policy_id",[e.siteId])).rows.map(Fn)}function Tt(e){return dn.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 qn(e){let t=Be.parse(e.policy);if(!t.enabled)throw new f("analytics_crm_outbound_policy_disabled","The CRM outbound policy is disabled.",409);let n=S(e.db);if(!(await n.query(`SELECT p.id::text id FROM analytics_people p
|
|
205
259
|
WHERE p.id=$1 AND p.site_id=$2 AND
|
|
206
260
|
(p.crm_person_ref IS NOT NULL OR EXISTS (
|
|
207
261
|
SELECT 1 FROM analytics_identity_edges e JOIN analytics_identity_nodes n ON n.id=e.identity_node_id
|
|
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:
|
|
262
|
+
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:Tt(r.rows[0]),reused:!0}}let s=await n.query(`INSERT INTO analytics_crm_outbound_jobs(
|
|
209
263
|
job_id,policy_id,site_id,person_id,kind,policy_version,mapping_version,source_event_id,
|
|
210
264
|
payload_ciphertext,idempotency_key,request_fingerprint,next_attempt_at
|
|
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:
|
|
265
|
+
) 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:Tt(s.rows[0]),reused:!1}}async function Hn(e){let t=await S(e.db).query(`WITH due AS (
|
|
212
266
|
SELECT j.job_id FROM analytics_crm_outbound_jobs j
|
|
213
267
|
JOIN analytics_crm_outbound_policies p ON p.policy_id=j.policy_id
|
|
214
268
|
WHERE p.enabled=true AND p.provider NOT IN ('salesforce','pipedrive')
|
|
@@ -222,8 +276,8 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
222
276
|
WHERE j.job_id=due.job_id AND p.policy_id=j.policy_id AND s.id=j.site_id
|
|
223
277
|
RETURNING j.*,p.provider,p.connection_id::text,p.mapping,p.allowed_fields,p.purpose,s.owner_user_id,
|
|
224
278
|
EXISTS(SELECT 1 FROM analytics_crm_connection_capabilities c
|
|
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
|
|
226
|
-
VALUES($1,$2,$3,'started',now()) ON CONFLICT(job_id,attempt) DO NOTHING`,[
|
|
279
|
+
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 S(e.db).query(`INSERT INTO analytics_crm_outbound_attempts(id,job_id,attempt,status,started_at)
|
|
280
|
+
VALUES($1,$2,$3,'started',now()) ON CONFLICT(job_id,attempt) DO NOTHING`,[ie(),n.job_id,Number(n.attempts)]);return t.rows.map(n=>({job:Tt(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 Wn(e){return(await S(e.db).query(`SELECT EXISTS(
|
|
227
281
|
SELECT 1
|
|
228
282
|
FROM analytics_crm_outbound_jobs j
|
|
229
283
|
JOIN analytics_crm_outbound_policies p ON p.policy_id=j.policy_id
|
|
@@ -266,27 +320,88 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
266
320
|
AND (($3='site_analytics' AND consent.site_analytics=true) OR
|
|
267
321
|
($3='advertising_measurement' AND consent.advertising_measurement=true))
|
|
268
322
|
))
|
|
269
|
-
) authorized`,[e.jobId,e.attempt,e.purpose,e.consentReceiptRef])).rows[0]?.authorized===!0}async function
|
|
323
|
+
) authorized`,[e.jobId,e.attempt,e.purpose,e.consentReceiptRef])).rows[0]?.authorized===!0}async function Bn(e){let t=S(e.db);return(await t.query(`UPDATE analytics_crm_outbound_jobs
|
|
270
324
|
SET state='cancelled',payload_ciphertext='',next_attempt_at=NULL,
|
|
271
325
|
lease_expires_at=NULL,cancelled_at=now(),safe_error_code=$3,updated_at=now()
|
|
272
326
|
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
|
|
273
327
|
SET status='cancelled',safe_error_code=$3,completed_at=now()
|
|
274
|
-
WHERE job_id=$1 AND attempt=$2 AND status='started'`,[e.jobId,e.attempt,e.safeErrorCode.slice(0,80)]),!0):!1}async function
|
|
328
|
+
WHERE job_id=$1 AND attempt=$2 AND status='started'`,[e.jobId,e.attempt,e.safeErrorCode.slice(0,80)]),!0):!1}async function tt(e){let t=S(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_${et({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,
|
|
275
329
|
lease_expires_at=NULL,delivered_at=CASE WHEN $2='delivered' THEN now() ELSE delivered_at END,updated_at=now()
|
|
276
330
|
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)
|
|
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()`,[
|
|
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
|
|
331
|
+
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()`,[ie(),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)
|
|
332
|
+
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 wo(e){return(await S(e.db).query(`SELECT r.* FROM analytics_crm_outbound_receipts r
|
|
279
333
|
JOIN analytics_crm_outbound_jobs j ON j.job_id=r.job_id
|
|
280
334
|
JOIN analytics_crm_outbound_policies p ON p.policy_id=j.policy_id
|
|
281
335
|
WHERE j.site_id=$1 AND p.provider NOT IN ('salesforce','pipedrive')
|
|
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,
|
|
336
|
+
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 Kn}from"crypto";var Lr=["hubspot","salesforce","highlevel","zoho","pipedrive","keap"],Ro=["hubspot","highlevel","zoho","keap"];var J=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)},pe=["describe","discover_schema","incremental_read","webhook","upsert_person","append_activity","test_write","list_outcomes","pipeline_write"],Vn=Object.freeze({hubspot:{provider:"hubspot",displayName:"HubSpot",providerConfigKeys:["hubspot"],auth:"oauth2",operations:J([...pe,"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:J([...pe,"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:J([...pe,"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:J([...pe,"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:J([...pe,"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:J([...pe,"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:J(["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:J(["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:J(["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"}}),$r=Object.freeze({gohighlevel:"highlevel",ghl:"highlevel","high-level":"highlevel",infusionsoft:"keap","zoho-crm":"zoho",calltrackingmetrics:"ctm","call-tracking-metrics":"ctm"});function se(e){let t=e.trim().toLowerCase().replace(/[ _]+/g,"-"),n=$r[t]??t;if(!(n in Vn))throw new Error("analytics_provider_unsupported");return n}function nt(e){return Vn[se(e)]}function ye(e,t){let n=nt(e);if(!n.operations[t])throw new Error(`analytics_provider_operation_unsupported:${n.provider}:${t}`);return n}var F=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"}),kr=Object.freeze({hubspot:["crm.objects.deals.read"],salesforce:["api"],highlevel:["opportunities.readonly"],zoho:["ZohoCRM.modules.deals.READ"],pipedrive:["deals:read"],keap:["full"]});function Oo(e){let t=se(e.provider);if(!Lr.includes(t))throw new Error("analytics_crm_provider_required");let n=t,i=nt(n),r=new Set(e.compiledActions??[]),s=new Set(e.advertisedTools??[]),a=new Set(e.grantedScopes??[]),o=e.documented===!0,c=e.normalizedAdapter===!0,l=F.listOutcomes,_=c&&r.has(l),d=s.has(l),p=kr[n],E=p.every(D=>a.has(D)),b=new Set(e.tenantObjects??[]),k=!!(i.pipelineObject&&b.has(i.pipelineObject)),x=o&&_&&d&&E&&k,h=i.operations.webhook&&e.webhookAdvertised===!0,C=[...o?[]:["official_documentation"],...c?[]:["normalized_adapter"],...r.has(l)?[]:[`compiled_action:${l}`],...d?[]:[`connection_tool:${l}`],...p.filter(D=>!a.has(D)).map(D=>`scope:${D}`),...!k&&i.pipelineObject?[`tenant_object:${i.pipelineObject}`]:[]],m=x&&h?"incremental_and_webhook":x?"incremental_only":h?"webhook_only":e.manualBackfillAvailable===!0?"manual_backfill":"unavailable",X=(D,A)=>i.operations[D]&&r.has(A)&&s.has(A);return{provider:n,mode:m,canSyncNow:x,canSchedule:x,canProvisionFields:X("create_fields",F.createFields),canUpsertPerson:X("upsert_person",F.upsertPerson),canApplyPipelineEvent:X("pipeline_write",F.pipelineEvent),supportedObjects:e.tenantObjects?i.supportedObjects.filter(D=>b.has(D)):[],evidence:{documented:o,normalizedAdapter:c,advertisedTools:[...s].sort(),grantedScopes:[...a].sort(),missingRequirements:C,tenantSchemaFingerprint:e.tenantSchemaFingerprint??null,evaluatedAt:e.evaluatedAt??new Date().toISOString()},caveats:i.caveats}}var Jn=[{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 ae(e,t){return`${e}_${Kn("sha256").update(JSON.stringify(t)).digest("hex").slice(0,24)}`}function Lt(e){return Kn("sha256").update(JSON.stringify(e)).digest("hex")}function Pr(e){return Lt([...e].sort((t,n)=>`${t.object}:${t.internalName}`.localeCompare(`${n.object}:${n.internalName}`)))}function w(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function O(e,t=300){return typeof e=="string"&&e.trim()?e.trim().slice(0,t):void 0}function Mr(e){let t=O(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 ge(e){let t=O(e.object,100),n=O(e.internalName,160),i=O(e.label,160);return!t||!n||!i?null:{object:t,internalName:n,label:i,type:Mr(e.type),writable:e.writable,...e.required?{required:!0}:{}}}function xt(e){return Array.isArray(e)?e.flatMap(t=>{if(!w(t))return[];let n=O(t.id,240),i=O(t.name??t.label,240);if(!n||!i)return[];let r=Array.isArray(t.stages)?t.stages.flatMap(s=>{if(!w(s))return[];let a=O(s.id,240),o=O(s.name??s.label,240);return a&&o?[{id:a,name:o}]:[]}):[];return[{id:n,name:i,stages:r}]}):[]}function Ur(e,t){let n=w(t)?t:{},i=[],r=[],s=xt(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 l of n[o])w(l)&&a(ge({object:c,internalName:l.name,label:l.label,type:l.type,writable:l.archived!==!0&&l.providerDefined!==!0}))}}else if(e==="salesforce"){if(Array.isArray(n.objects))for(let o of n.objects){if(!w(o))continue;let c=O(o.object,100);if(!c)continue;r.push(c);let l=w(o.permissions)?o.permissions:{};if(Array.isArray(o.fields))for(let _ of o.fields)w(_)&&a(ge({object:c,internalName:_.name,label:_.label,type:_.type,writable:l.update===!0&&_.updateable===!0,required:_.required===!0}))}}else if(e==="highlevel"){if(Array.isArray(n.customFields)){for(let o of n.customFields)if(w(o)){let c=o.model==="opportunity"?"opportunities":"contacts";r.push(c),a(ge({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(!w(c))continue;let l=O(c.module,100);if(l&&(r.push(l),Array.isArray(c.fields)))for(let _ of c.fields)w(_)&&a(ge({object:l,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(w(c)){let l=c.objectType==="deal"?"deals":"persons";r.push(l),a(ge({object:l,internalName:c.code,label:c.name,type:c.fieldType,writable:c.writable===!0}))}}let o=Array.isArray(n.stages)?n.stages:[];s=xt(Array.isArray(n.pipelines)?n.pipelines.map(c=>w(c)?{...c,id:String(c.id??""),stages:o.filter(l=>w(l)&&String(l.pipelineId)===String(c.id)).map(l=>({...l,id:String(l.id??"")}))}:c):[])}else if(e==="keap"){if(r.push("contacts"),Array.isArray(n.contactFields))for(let c of n.contactFields)w(c)&&a(ge({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:xt([{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 Fr(e){return!w(e)||!Array.isArray(e.fields)?[]:e.fields.flatMap(t=>{if(!w(t))return[];let n=O(t.object,100),i=O(t.internalName??t.internal_name,160);if(!n||!i)return[];let r=O(t.id??t.providerFieldId,240);return[{object:n,internalName:i,...r?{providerFieldId:r}:{}}]})}function it(e){let t=se(e);if(t==="callrail"||t==="twilio"||t==="ctm")throw new Error("analytics_crm_provider_required");return t}function Lo(e){let t=it(e.provider),n=ye(t,"discover_schema");if(!n.personObject||!e.connectionId.trim())throw new Error("analytics_crm_provisioning_invalid_scope");let i={},r=[];for(let l of Jn){let _=e.fields.find(d=>d.object===n.personObject&&(d.internalName.toLowerCase()===l.internalName||d.label.toLowerCase()===l.label.toLowerCase()));_?_.writable&&(i[l.internalName]=_.internalName):r.push({...l,object:n.personObject})}let s=e.now??new Date().toISOString(),a=Pr(e.fields),o={provider:t,connectionId:e.connectionId,existingMappings:i,proposedFields:r,schemaFingerprint:a},c=Lt(o);return{planId:ae("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 $o(e){let t=ye(e.plan.provider,"create_fields"),n=ae("crmplan",{provider:e.plan.provider,connectionId:e.plan.connectionId,existingMappings:e.plan.existingMappings,proposedFields:e.plan.proposedFields,schemaFingerprint:e.plan.schemaFingerprint}),i=Lt({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(Jn.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(l=>l.internalName===c)))throw new Error("analytics_crm_provisioning_field_not_in_plan");let a=e.plan.proposedFields.filter(c=>s.has(c.internalName)),o=ae("crmreceipt",{planId:e.plan.planId,fields:[...s].sort()});try{let c=a.length?await e.dependencies.callAction({connectionId:e.plan.connectionId,action:F.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:Fr(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:O(c instanceof Error?c.message:c,80)?.replace(/[^a-zA-Z0-9_:-]/g,"_")||"analytics_crm_provisioning_failed"}}}async function ko(e){if(e.receipt.status!=="approved")throw new Error("analytics_crm_provisioning_not_approved");ye(e.receipt.provider,"test_write");let t=ae("crmtest",e.receipt.receiptId),n=await e.dependencies.callAction({connectionId:e.receipt.connectionId,action:F.testWrite,payload:{provider:e.receipt.provider,marker:"X-Ray reversible connection test",cleanupRequested:!0},idempotencyKey:t});if(!w(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 zn(e){let t=it(e.provider);if(ye(t,"upsert_person"),e.person.identityTier!=="confirmed"||!e.person.deterministicIdentity.value.trim())throw new Error("analytics_crm_person_requires_confirmed_identity");let n=ae("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:F.upsertPerson,payload:i,idempotencyKey:n}),s=w(r)?O(r.recordRef??r.id,240):void 0;return{receiptId:n,provider:t,operation:"person_upsert",dealCreated:!1,...s?{providerRecordRef:s}:{}}}async function Yn(e){let t=it(e.provider);if(ye(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=ae("crmpipeline",{provider:t,connectionId:e.connectionId,eventId:e.event.eventId,mappingVersion:e.mapping.version});return await e.dependencies.callAction({connectionId:e.connectionId,action:F.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 Po(e){let t=it(e.provider),n=nt(t),i=t==="highlevel"?{locationId:e.discoveryContext?.locationId}:t==="zoho"?{module:e.discoveryContext?.zohoModules?.[0]??"Contacts"}:{};if(t==="highlevel"&&!O(i.locationId,128))throw new Error("analytics_crm_highlevel_location_required");let r=(p,E)=>e.dependencies.callAction({connectionId:e.connectionId,action:F.discoverSchema,payload:p,idempotencyKey:ae("crmdiscover",{provider:t,connectionId:e.connectionId,actionPayload:p,suffix:E})}),s=await r(i);if(t==="zoho"){let p=[...new Set(e.discoveryContext?.zohoModules??["Contacts","Deals"])],E=[s];for(let b of p.slice(1))E.push(await r({module:b},b));s={moduleResults:E}}let a=Ur(t,s),o=a.fields,c=Object.entries(n.operations).filter(([,p])=>p).map(([p])=>p),l=a.allowedOperations?new Set(a.allowedOperations.map(p=>O(p,80)).filter(p=>!!p)):null,_=l?c.filter(p=>l.has(p)):c,d=new Set(n.supportedObjects);return{provider:t,publicProfile:n,connectionId:e.connectionId,fields:o,objects:a.objects.length?a.objects.filter(p=>d.has(p)):n.supportedObjects,pipelines:a.pipelines,allowedOperations:_,discoveredAt:e.now??new Date().toISOString()}}import{createHash as qr,randomUUID as q}from"crypto";var jr=[[/perplexity/i,"llm","Perplexity"],[/chatgpt|chat\.openai|openai/i,"llm","ChatGPT"],[/claude|anthropic/i,"llm","Claude"],[/\bgrok\b|xai/i,"llm","Grok"],[/ai[ _-]?overview|google[ _-]?ai|\bsge\b|gemini/i,"llm","Google AI Overview"],[/instagram/i,"social","Instagram"],[/facebook|\bfb\b|meta/i,"social","Facebook"],[/linkedin/i,"social","LinkedIn"],[/tiktok/i,"social","TikTok"],[/youtube|youtu\.be/i,"social","YouTube"],[/twitter|x\.com/i,"social","X"],[/\bg2\b|g2\.com/i,"review","G2"],[/trustpilot/i,"review","Trustpilot"],[/\bbbb\b|better business bureau/i,"review","BBB"],[/capterra/i,"review","Capterra"],[/yelp/i,"review","Yelp"],[/google|bing|duckduckgo|yahoo/i,"search","Search"]];function j(e,t=240){return typeof e=="string"&&e.trim()?e.trim().slice(0,t):null}function Gn(e){try{return e?new URL(e).hostname.replace(/^www\./,""):""}catch{return""}}function Xn(e){let t=e.properties??{},n=j(e.source,180)||Gn(e.referrer)||"(direct)",i=j(e.medium,180)||(n==="(direct)"?"(none)":"referral"),r=e.clickIds??{},s=`${n} ${i} ${Gn(e.referrer)}`,a=n==="(direct)"?"direct":/email|newsletter/i.test(i)?"email":"referral",o=n==="(direct)"?"Direct":n;for(let[c,l,_]of jr)if(c.test(s)){a=l,o=_;break}return r.fbclid?(a="social",o=/instagram/i.test(s)?"Instagram":"Facebook"):r.ttclid?(a="social",o="TikTok"):r.rdt_cid?(a="social",o="Reddit"):r.gclid||r.gbraid||r.wbraid?(a="search",o="Google"):r.msclkid&&(a="search",o="Microsoft Ads"),{channelFamily:a,platform:o,source:n,medium:i,campaign:j(e.campaign),campaignId:j(t.campaign_id??t.utm_id),adSetId:j(t.adset_id??t.ad_set_id??t.adgroup_id??t.ad_group_id),adId:j(t.ad_id),creativeId:j(t.creative_id),placement:j(t.placement),term:j(t.utm_term),content:j(t.utm_content)}}import{randomBytes as Qn}from"crypto";function jo(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||`site-${Qn(4).toString("hex")}`}function Zn(e){if(!e||e==="null")return null;try{let t=new URL(e);return t.protocol!=="http:"&&t.protocol!=="https:"?null:t.hostname.toLowerCase().replace(/\.$/,"")}catch{return null}}function qo(){return`px_${Qn(18).toString("base64url")}`}var Hr=1800*1e3,Xo=1e4;function Wr(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0?Math.min(Math.trunc(e),Hr):null}function Br(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0?Math.min(Math.trunc(e),100):null}var Vr=/(email|phone|password|token|secret|authorization|cookie|name|address|form|body)/i;function ei(e){if(!e)return"/";try{return new URL(e,"https://analytics.invalid").pathname.slice(0,2e3)||"/"}catch{return"/"}}function fe(e){if(!e)return null;try{let t=new URL(e);return t.protocol!=="http:"&&t.protocol!=="https:"?null:(t.search="",t.hash="",t.toString().slice(0,3e3))}catch{return null}}function Kr(e){let t={};for(let[n,i]of Object.entries(e??{}).slice(0,24))!/^[a-z][a-z0-9_]{0,63}$/i.test(n)||Vr.test(n)||(i===null||typeof i=="boolean"||typeof i=="number"?t[n]=i:typeof i=="string"&&(t[n]=i.slice(0,300)));return t}async function Jr(e){if(!e.event.visitorId||!e.event.sessionId)return;let t=$("visitor_id",e.event.visitorId),n=$("session_id",e.event.sessionId),i=new Date(e.event.occurredAt);if(!Number.isFinite(i.getTime()))return;let r=ei(e.event.path||e.event.canonicalUrl),s=await e.client.query(`INSERT INTO analytics_sessions(
|
|
337
|
+
id, site_id, pixel_id, visitor_hmac, session_hmac, person_id,
|
|
338
|
+
started_at, last_active_at, landing_path, exit_path, pageview_count, event_count
|
|
339
|
+
) VALUES ($1,$2,$3,$4,$5,$6,$7,$7,$8,$8,$9,1)
|
|
340
|
+
ON CONFLICT(site_id, session_hmac) DO UPDATE SET
|
|
341
|
+
person_id = COALESCE(analytics_sessions.person_id, EXCLUDED.person_id),
|
|
342
|
+
last_active_at = GREATEST(analytics_sessions.last_active_at, EXCLUDED.last_active_at),
|
|
343
|
+
ended_at = GREATEST(COALESCE(analytics_sessions.ended_at, EXCLUDED.last_active_at), EXCLUDED.last_active_at),
|
|
344
|
+
duration_ms = GREATEST(
|
|
345
|
+
analytics_sessions.duration_ms,
|
|
346
|
+
LEAST(1800000, (EXTRACT(EPOCH FROM (GREATEST(analytics_sessions.last_active_at, EXCLUDED.last_active_at) - analytics_sessions.started_at)) * 1000)::bigint)
|
|
347
|
+
),
|
|
348
|
+
exit_path = EXCLUDED.exit_path,
|
|
349
|
+
pageview_count = analytics_sessions.pageview_count + EXCLUDED.pageview_count,
|
|
350
|
+
event_count = analytics_sessions.event_count + 1,
|
|
351
|
+
updated_at = now()
|
|
352
|
+
RETURNING id`,[q(),e.siteId,e.pixelId,t,n,e.personId??null,e.event.occurredAt,r,e.event.eventName==="page_view"?1:0]),a=U(e.event.clickIds);if(!!!(e.event.source||e.event.medium||e.event.campaign||e.event.referrer||Object.keys(a).length))return;let c=qr("sha256").update(JSON.stringify({siteId:e.siteId,sessionHmac:n,source:e.event.source??null,medium:e.event.medium??null,campaign:e.event.campaign??null,referrer:fe(e.event.referrer),clickIds:a,path:r})).digest("hex"),l=await e.client.query(`INSERT INTO analytics_attribution_touches(
|
|
353
|
+
id, site_id, session_id, person_id, visitor_hmac, occurred_at,
|
|
354
|
+
source, medium, campaign, referrer, click_ids, landing_path, touch_hash
|
|
355
|
+
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11::jsonb,$12,$13)
|
|
356
|
+
ON CONFLICT(site_id, touch_hash) DO UPDATE SET
|
|
357
|
+
person_id = COALESCE(analytics_attribution_touches.person_id, EXCLUDED.person_id)
|
|
358
|
+
RETURNING id`,[q(),e.siteId,s.rows[0].id,e.personId??null,t,e.event.occurredAt,e.event.source?.slice(0,180)??null,e.event.medium?.slice(0,180)??null,e.event.campaign?.slice(0,240)??null,fe(e.event.referrer),JSON.stringify(a),r,c]);await e.client.query(`UPDATE analytics_sessions
|
|
359
|
+
SET first_touch_id = COALESCE(first_touch_id, $2), last_touch_id = $2, updated_at = now()
|
|
360
|
+
WHERE id = $1`,[s.rows[0].id,l.rows[0].id])}async function Qo(e){let t=v(),n=`air_${q()}`,i=Zn(e.origin),s=(await t.query(`SELECT p.id, p.site_id, p.status,
|
|
361
|
+
COALESCE(
|
|
362
|
+
(e.eligible AND e.access_expires_at > now()) OR e.trial_ends_at > now(),
|
|
363
|
+
false
|
|
364
|
+
) AS entitled
|
|
365
|
+
FROM analytics_pixels p
|
|
366
|
+
JOIN analytics_sites s ON s.id = p.site_id
|
|
367
|
+
LEFT JOIN analytics_thorbit_entitlements e ON e.user_id = s.owner_user_id
|
|
368
|
+
WHERE p.public_id = $1`,[e.publicPixelId])).rows[0];if(!s||s.status!=="active"){let d=[s?"pixel_disabled":"pixel_unknown"];return await t.query(`INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
|
|
369
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,[q(),n,s?.site_id??null,s?.id??null,i,e.events.length,JSON.stringify(d)]),{requestId:n,accepted:0,rejected:e.events.length,reasonCodes:d,hostname:i,siteId:s?.site_id??null,pixelId:s?.id??null}}if(!s.entitled){let d=["thorbit_entitlement_required"];return await t.query(`INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
|
|
370
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,[q(),n,s.site_id,s.id,i,e.events.length,JSON.stringify(d)]),{requestId:n,accepted:0,rejected:e.events.length,reasonCodes:d,hostname:i,siteId:s.site_id,pixelId:s.id}}if(!i){let d=["origin_missing_or_invalid"];return await t.query(`INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, rejected_count, reason_codes)
|
|
371
|
+
VALUES ($1, $2, $3, $4, $5, $6::jsonb)`,[q(),n,s.site_id,s.id,e.events.length,JSON.stringify(d)]),{requestId:n,accepted:0,rejected:e.events.length,reasonCodes:d,hostname:i,siteId:s.site_id,pixelId:s.id}}if((await t.query(`INSERT INTO analytics_pixel_domains(id, pixel_id, hostname, state, rejected_count)
|
|
372
|
+
VALUES ($1, $2, $3, 'observed_unverified', 0)
|
|
373
|
+
ON CONFLICT(pixel_id, hostname) DO UPDATE
|
|
374
|
+
SET last_seen_at = now(), updated_at = now()
|
|
375
|
+
RETURNING state`,[q(),s.id,i])).rows[0]?.state!=="approved"){await t.query(`UPDATE analytics_pixel_domains
|
|
376
|
+
SET rejected_count = rejected_count + $3, last_seen_at = now(), updated_at = now()
|
|
377
|
+
WHERE pixel_id = $1 AND hostname = $2`,[s.id,i,e.events.length]);let d=["domain_unapproved"];return await t.query(`INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
|
|
378
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,[q(),n,s.site_id,s.id,i,e.events.length,JSON.stringify(d)]),{requestId:n,accepted:0,rejected:e.events.length,reasonCodes:d,hostname:i,siteId:s.site_id,pixelId:s.id}}let o=await t.connect(),c=0,l=0,_=new Set;try{await o.query("BEGIN");for(let d of e.events)try{await pn(o,{siteId:s.site_id,pixelId:s.id,event:d});let p=Xn(d),E=d.identity,b=null,k=null;if(d.eventDefinitionId&&d.eventDefinitionVersion){let C=await o.query(`SELECT id, version FROM analytics_event_definitions
|
|
379
|
+
WHERE id=$1 AND site_id=$2 AND version=$3 AND event_name=$4 AND status='active'
|
|
380
|
+
LIMIT 1`,[d.eventDefinitionId,s.site_id,d.eventDefinitionVersion,d.eventName]);C.rows[0]&&(b=C.rows[0].id,k=C.rows[0].version)}let x=_n(d)&&(d.visitorId||d.sessionId)?await o.query(`SELECT e.person_id FROM analytics_identity_nodes n JOIN analytics_identity_edges e ON e.identity_node_id = n.id
|
|
381
|
+
WHERE n.site_id = $1 AND ((n.kind = 'visitor_id' AND n.value_hmac = $2) OR (n.kind = 'session_id' AND n.value_hmac = $3))
|
|
382
|
+
ORDER BY e.confidence DESC, e.last_seen_at DESC LIMIT 1`,[s.site_id,d.visitorId?$("visitor_id",d.visitorId):"",d.sessionId?$("session_id",d.sessionId):""]):{rows:[]};if((await o.query(`INSERT INTO analytics_events(
|
|
383
|
+
id, event_id, site_id, pixel_id, hostname, visitor_id, session_id, event_name,
|
|
384
|
+
occurred_at, path, canonical_url, title, referrer, source, medium, campaign,
|
|
385
|
+
device_class, properties, country_code, region_code, click_ids, person_id,
|
|
386
|
+
channel_family, platform, campaign_id, ad_set_id, ad_id, creative_id, placement, utm_term, utm_content,
|
|
387
|
+
engaged_ms, scroll_depth, visitor_hmac, session_hmac, event_definition_id, event_definition_version
|
|
388
|
+
) VALUES (
|
|
389
|
+
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18::jsonb, $19, $20, $21::jsonb, $22,
|
|
390
|
+
$23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37
|
|
391
|
+
) ON CONFLICT(site_id, event_id) DO NOTHING`,[q(),d.eventId,s.site_id,s.id,i,d.visitorId??null,d.sessionId??null,d.eventName,d.occurredAt,ei(d.path||d.canonicalUrl),fe(d.canonicalUrl),d.title?.slice(0,500)??null,fe(d.referrer),d.source?.slice(0,180)??null,d.medium?.slice(0,180)??null,d.campaign?.slice(0,240)??null,d.deviceClass??null,JSON.stringify(Kr(d.properties)),gt(e.geography?.countryCode,2),gt(e.geography?.regionCode,8),JSON.stringify(U(d.clickIds)),x.rows[0]?.person_id??null,p.channelFamily,p.platform,p.campaignId,p.adSetId,p.adId,p.creativeId,p.placement,p.term,p.content,Wr(d.engagedMs),Br(d.scrollDepth),d.visitorId?$("visitor_id",d.visitorId):null,d.sessionId?$("session_id",d.sessionId):null,b,k])).rowCount){c+=1;let C=x.rows[0]?.person_id;await Jr({client:o,siteId:s.site_id,pixelId:s.id,personId:C,event:d}),C&&d.eventName==="identify"&&await yn(o,{siteId:s.site_id,personId:C,visitorId:d.visitorId,sessionId:d.sessionId}),E&&mn(d)&&await gn(o,{siteId:s.site_id,visitorId:d.visitorId,sessionId:d.sessionId,email:E.email,phone:E.phone,customerId:E.customerId,orderId:E.orderId,clickIds:d.clickIds,occurredAt:d.occurredAt})}else l+=1,_.add("duplicate_event")}catch{l+=1,_.add("event_insert_failed")}await o.query(`UPDATE analytics_pixel_domains
|
|
392
|
+
SET accepted_count = accepted_count + $3,
|
|
393
|
+
rejected_count = rejected_count + $4,
|
|
394
|
+
last_seen_at = now(), updated_at = now()
|
|
395
|
+
WHERE pixel_id = $1 AND hostname = $2`,[s.id,i,c,l]),await o.query(`INSERT INTO analytics_ingestion_receipts(
|
|
396
|
+
id, request_id, site_id, pixel_id, hostname, accepted_count, rejected_count, reason_codes
|
|
397
|
+
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb)`,[q(),n,s.site_id,s.id,i,c,l,JSON.stringify([..._])]),await o.query("COMMIT")}catch(d){throw await o.query("ROLLBACK"),d}finally{o.release()}return{requestId:n,accepted:c,rejected:l,reasonCodes:[..._],hostname:i,siteId:s.site_id,pixelId:s.id}}var Yr=25,Gr=25e3,ti=z.object({person:_t,event:mt.nullable(),mapping:z.object({version:z.number().int().positive(),eventName:z.string(),pipelineId:z.string(),stageId:z.string(),operation:z.enum(["create_or_update_deal","update_stage"]),valueSource:z.enum(["none","verified_revenue"]),activationEligible:z.boolean()}).strict().nullable()}).strict();function Xr(e,t){return`${e}_${zr("sha256").update(JSON.stringify(t)).digest("hex").slice(0,24)}`}function rt(e){return fe(e)??void 0}function Qr(e,t,n){return e.purpose==="necessary_form_fulfillment"?t.eventKind==="form":e.purpose==="site_analytics"?n.siteAnalytics:n.advertisingMeasurement}function H(e,t){return e.has(t)}function Zr(e){let t=new Set(e.policy.allowedFields),n=e.contact?{...H(t,"contact.email")&&e.contact.email?{email:e.contact.email}:{},...H(t,"contact.phone")&&e.contact.phone?{phone:e.contact.phone}:{},...H(t,"contact.firstName")&&e.contact.firstName?{firstName:e.contact.firstName}:{},...H(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?U(e.event.clickIds):{},s=Object.entries(r).filter(([a])=>H(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}:{},...H(t,"attribution.firstTouch")&&i?{firstTouch:i}:{},...H(t,"attribution.lastNonDirectTouch")&&i?{lastNonDirectTouch:i}:{},...H(t,"attribution.convertingTouch")&&i?{convertingTouch:i}:{},...H(t,"attribution.landingPage")&&rt(e.event.canonicalUrl)?{landingPage:rt(e.event.canonicalUrl)}:{},...H(t,"attribution.conversionPage")&&rt(e.event.canonicalUrl)?{conversionPage:rt(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 ni(e){let t=(await jn({siteId:e.siteId,db:e.db})).filter(s=>s.enabled&&Qr(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,
|
|
283
398
|
(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,
|
|
284
399
|
((SELECT count(*) FROM analytics_events e WHERE e.site_id=p.site_id AND e.person_id=p.id)+
|
|
285
400
|
(SELECT count(*) FROM analytics_external_events x WHERE x.site_id=p.site_id AND x.person_id=p.id))::text event_count
|
|
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=
|
|
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
|
|
401
|
+
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=Zr({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=ti.parse({person:a,event:o,mapping:s.mapping}),l=`${s.policyId}:${s.version}:${e.canonicalEventId}`,_=et({policy:s,personId:e.personId,event:o,projection:a});(await qn({jobId:Xr("crmjob",{idempotencyKey:l}),policy:s,personId:e.personId,sourceEventId:e.canonicalEventId,payloadCiphertext:ue(JSON.stringify(c),"crm-outbound-payload"),idempotencyKey:l,requestFingerprint:_,db:e.db})).reused?r+=1:i+=1}return{queued:i,reused:r}}var st={async resolveIdentity(e){return(await nn(e))?.email??null},async callAction(e){return an({identity:e.identity,connectionId:e.connectionId,tool:e.action,input:e.payload,classification:"action",operationKind:"sync",requestId:e.idempotencyKey})},authorizeSend:e=>Wn(e),cancelUnauthorized:e=>Bn(e)};function es(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 uc(e=10,t=Date.now()+Gr,n=st){let i=Math.max(1,Math.min(Yr,e)),r=v();await r.query(`INSERT INTO analytics_crm_outbound_worker_state(worker_key,last_started_at,last_error_code,updated_at)
|
|
402
|
+
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 Hn({limit:i,leaseSeconds:90}),a=0,o=0,c=0;for(let d of s){if(Date.now()>=t){await tt({jobId:d.job.jobId,status:"retrying",safeErrorCode:"crm_outbound_tick_deadline",retryAt:new Date(Date.now()+6e4).toISOString()}),o+=1;continue}try{if(!d.schemaCurrent)throw new Error("analytics_crm_schema_changed");let p=Oe(d.payloadCiphertext,"crm-outbound-payload");if(!p)throw new Error("crm_outbound_payload_unreadable");let E=ti.parse(JSON.parse(p)),b=await n.resolveIdentity(d.ownerUserId);if(!b)throw new Error("crm_outbound_owner_identity_unavailable");if(!await(n.authorizeSend??(n===st?st.authorizeSend:async()=>!0))({jobId:d.job.jobId,attempt:d.job.attempts,purpose:d.purpose,consentReceiptRef:E.person.consentReceiptRef})){await(n.cancelUnauthorized??st.cancelUnauthorized)({jobId:d.job.jobId,attempt:d.job.attempts,safeErrorCode:"crm_outbound_privacy_fence_closed"}),c+=1;continue}let h={callAction:({connectionId:m,action:X,payload:D,idempotencyKey:A})=>n.callAction({identity:b,connectionId:m,action:X,payload:D,idempotencyKey:A})},C=d.job.kind==="person_summary"?await zn({provider:d.provider,connectionId:d.connectionId,person:E.person,dependencies:h}):await Yn({provider:d.provider,connectionId:d.connectionId,person:E.person,event:E.event,mapping:E.mapping,dependencies:h});await tt({jobId:d.job.jobId,status:"accepted",externalReceiptId:C.receiptId}),a+=1}catch(p){let E=es(p),b=new Date(Date.now()+(E.rateLimited?30*6e4:Math.min(360*6e4,3e4*2**d.job.attempts))).toISOString();await tt({jobId:d.job.jobId,status:E.retryable?"retrying":"failed",safeErrorCode:E.code,...E.retryable?{retryAt:b}:{}}),E.retryable&&d.job.attempts<5?o+=1:c+=1}}let l=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(l.rows[0]?.due??0)};return await r.query(`UPDATE analytics_crm_outbound_worker_state SET last_completed_at=now(),last_success_at=now(),
|
|
288
403
|
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()
|
|
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 */
|
|
404
|
+
WHERE worker_key='default'`).catch(()=>{}),s}}import{createHash as ts,randomUUID as ii}from"crypto";import{z as g}from"zod";var oe=g.string().trim().min(1).max(160).regex(/^[a-zA-Z0-9_.:-]+$/),$t=g.string().trim().min(1).max(160).regex(/^[a-zA-Z][a-zA-Z0-9_.:-]*$/),ns=g.discriminatedUnion("kind",[g.object({kind:g.literal("event_completed"),eventName:oe,withinDays:g.number().int().min(1).max(3650).optional()}).strict(),g.object({kind:g.literal("event_frequency"),eventName:oe,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:oe,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:oe,withinDays:g.number().int().min(1).max(3650).optional()}).strict(),g.object({kind:g.literal("crm_stage"),stage:oe}).strict(),g.object({kind:g.literal("property_equals"),property:$t,value:g.union([g.string().max(500),g.number().finite(),g.boolean()])}).strict(),g.object({kind:g.literal("property_present"),property:$t}).strict()]),is=g.object({id:oe,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(ns).min(1).max(50)}).strict(),ri=g.object({namespaceId:oe,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(is).min(1).max(100),permittedProperties:g.array($t).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 si(){return{definitions:new Map,evaluations:new Map,evaluationHistory:[]}}var ee=class extends Error{constructor(n,i){super(i);this.code=n;this.name="AnalyticsLeadScoringError"}code};function kt(e){let t=e instanceof Date?e.toISOString():e??new Date().toISOString();if(!Number.isFinite(Date.parse(t)))throw new ee("analytics_score_invalid_time","A valid evaluation time is required.");return new Date(t).toISOString()}function Le(e,t){return`${e}:${t}`}function Pt(e){return{...e,groups:e.groups.map(t=>({...t,predicates:t.predicates.map(n=>({...n}))})),permittedProperties:[...e.permittedProperties]}}function rs(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 ss(e,t,n){let i=(r,s,a)=>(r??[]).filter(o=>o.name===s&&rs(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 as(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 ts("sha256").update(JSON.stringify(t(e))).digest("hex")}function ai(e){let t=ri.parse(e.definition),n=e.id??ii();if(e.tables.definitions.has(Le(t.namespaceId,n)))throw new ee("analytics_score_exists","A lead score with this ID already exists.");let i=kt(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(Le(t.namespaceId,n),r),Pt(r)}function yc(e){let t=e.tables.definitions.get(Le(e.namespaceId,e.scoreId));if(!t)throw new ee("analytics_score_not_found","Lead score not found.");if(t.version!==e.expectedVersion)throw new ee("analytics_score_version_conflict","The lead score changed; reload before updating.");let n=ri.parse(e.definition);if(n.namespaceId!==e.namespaceId)throw new ee("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:kt(e.now)};return e.tables.definitions.set(Le(e.namespaceId,e.scoreId),i),Pt(i)}function gc(e){return[...e.tables.definitions.values()].filter(t=>t.namespaceId===e.namespaceId).sort((t,n)=>t.name.localeCompare(n.name)).map(Pt)}function oi(e){let t=e.tables.definitions.get(Le(e.namespaceId,e.scoreId));if(!t||!t.enabled)throw new ee("analytics_score_unavailable","Lead score is missing or disabled.");let n=kt(e.now),i=t.groups.map(_=>{let d=_.predicates.map(b=>ss(b,e.facts,n)),p=d.every(b=>b!==null),E=p?Math.min(_.points,_.cap??_.points):0;return{groupId:_.id,operation:_.operation,matched:p,points:_.operation==="subtract"?-E:E,evidenceRefs:p?[...new Set(d.flatMap(b=>b??[]))].sort():[]}}),r=i.reduce((_,d)=>_+d.points,0),s=Math.max(t.minimum,Math.min(t.maximum,r)),a=[...new Set(i.flatMap(_=>_.evidenceRefs))].sort(),o=as({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 l={id:ii(),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,l),e.tables.evaluationHistory.push(l),{...l,components:l.components.map(_=>({..._,evidenceRefs:[..._.evidenceRefs]})),evidenceRefs:[...l.evidenceRefs]}}import{createHmac as os,timingSafeEqual as cs}from"crypto";function y(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 Y(e){let t=Number(e);return Number.isFinite(t)?t:void 0}function ce(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 $e(e){if(Array.isArray(e))return e.map(n=>y(n,80)).filter(n=>!!n).slice(0,30);let t=y(e,1e3);return t?t.split(",").map(n=>n.trim()).filter(Boolean).slice(0,30):[]}function G(...e){for(let t of e){let n=y(t,100);if(!n)continue;let i=new Date(n);if(Number.isFinite(i.getTime()))return i.toISOString()}return new Date().toISOString()}function ve(e,t,n){return os(e,t).update(n).digest("base64")}function Ee(e,t){if(!e||e.length!==t.length)return!1;try{return cs(Buffer.from(e),Buffer.from(t))}catch{return!1}}function be(e,t=Date.now(),n=300*1e3){let i=y(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 Ec(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 ci(e){return be(e.payloadTimestamp,e.now)&&Ee(e.signature,ve("sha1",e.secret,e.rawBody))}function di(e){let t=e.payload,n=y(t.resource_id??t.id,240)||"unknown",i=G(t.timestamp,t.start_time,t.created_at),r=$e(t.tags??t.tag),s=y(t.lead_status,80),a=s==="not_a_lead"||r.some(c=>c.toLowerCase()==="spam")||ce(t.spam)===!0,o={...y(t.gclid)?{gclid:y(t.gclid)}:{},...y(t.gbraid)?{gbraid:y(t.gbraid)}:{},...y(t.wbraid)?{wbraid:y(t.wbraid)}:{},...y(t.fbclid)?{fbclid:y(t.fbclid)}:{},...y(t.msclkid)?{msclkid:y(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:y(t.person_resource_id,240),visitorId:y(t.visitor_id??t.browser_id??t.ga,160),canonicalUrl:y(t.landing_page_url,3e3),sourceName:y(t.utm_source??t.source_name,180),medium:y(t.utm_medium??t.medium,180),campaign:y(t.utm_campaign??t.campaign,240),clickIds:o,properties:{answered:ce(t.answered)??!1,durationSeconds:Y(t.duration)??0,status:a?"spam":s||"unknown",tags:r,direction:y(t.direction,40)||"unknown",trackingNumber:y(t.tracking_phone_number,80)||null,changes:$e(t.changes)}}}function li(e){return be(e.requestTime,e.now)&&Ee(e.signature.trim(),ve("sha1",e.secret,`${e.requestTime}${e.rawBody}`))}function ui(e){let t=e.payload,n=y(t.activity_id??t.call_id??t.id,240)||"unknown",i=G(t.timestamp,t.event_time,t.started_at,t.created_at),r=$e(t.tags??t.tag_list),s=y(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:y(t.contact_id??t.visitor_id,240),visitorId:y(t.visitor_id??t.cookie_id,160),sessionId:y(t.session_id,160),canonicalUrl:y(t.landing_page_url??t.landing_url,3e3),sourceName:y(t.source??t.utm_source,180),medium:y(t.medium??t.utm_medium,180),campaign:y(t.campaign??t.utm_campaign,240),clickIds:{...y(t.gclid)?{gclid:y(t.gclid)}:{},...y(t.fbclid)?{fbclid:y(t.fbclid)}:{},...y(t.msclkid)?{msclkid:y(t.msclkid)}:{}},properties:{answered:ce(t.answered)??e.phase.includes("answer"),durationSeconds:Y(t.duration??t.talk_time)??0,status:a?"spam":s,tags:r,direction:y(t.direction,40)||"unknown",trackingNumber:y(t.tracking_number??t.dialed_number,80)||null,score:Y(t.score)??null}}}import ds from"twilio";function _i(e){return ds.validateRequest(e.authToken,e.signature,e.url,e.params)}function mi(e){let t=e.params,n=y(t.CallSid,240)||"unknown",i=(y(t.CallStatus,80)||"unknown").toLowerCase(),r=G(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:y(t.XRayVisitorId,160),sessionId:y(t.XRaySessionId,160),personRef:y(t.ContactId,240),properties:{answered:ce(t.Answered)??a,durationSeconds:Y(t.CallDuration??t.Duration)??0,status:i,direction:y(t.Direction,40)||"unknown",trackingNumber:y(t.To,80)||null,attributionLink:t.XRaySessionId?"explicit_session":"unattributed"}}}import{createCipheriv as ls,createDecipheriv as us,createHash as _s,createHmac as ms}from"crypto";var ke={session:"xrs_",visitor:"xrv_",person:"xrp_",cursor:"xrc_",candidate:"xri_"};function Ut(e){return _s("sha256").update(`xray-report:${e}:`).update(qe()).digest()}function T(e,t,n){let i=Ut("reference"),r=Buffer.from(`v1:${e}:${t}`),s=Buffer.from(n),a=ms("sha256",Ut("reference-iv")).update(r).update(s).digest().subarray(0,12),o=ls("aes-256-gcm",i,a);o.setAAD(r);let c=Buffer.concat([o.update(s),o.final()]);return`${ke[e]}${Buffer.concat([a,c,o.getAuthTag()]).toString("base64url")}`}function at(e,t,n){let i=ke[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=us("aes-256-gcm",Ut("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 ps(e,t){return T("cursor",e,JSON.stringify(t))}function Ae(e,t){if(!t)return null;try{let n=JSON.parse(at("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 Se(e){if(e===void 0)return 50;if(!Number.isInteger(e)||e<1||e>100)throw new Error("analytics_limit_invalid");return e}function Mt(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 Ie(e){if(e==null)return null;let t=Number(e);return Number.isFinite(t)?t:null}function N(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 pi(e){return e===null?null:N(e)}function he(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?ps(e,{at:a._cursorAt,id:a._cursorId}):null}}}async function Ne(e,t){await B(t,e.siteId,e.userId)}async function Lc(e,t=v()){await Ne(e,t);let n=Se(e.limit),i=Ae(e.siteId,e.cursor),s=(await t.query(`/* analyticsSessions:keyset */
|
|
290
405
|
WITH ranked AS (
|
|
291
406
|
SELECT s.*,
|
|
292
407
|
row_number() OVER (PARTITION BY s.visitor_hmac ORDER BY s.started_at, s.id)::int AS session_number
|
|
@@ -322,7 +437,7 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
322
437
|
SELECT 1 FROM analytics_events ee
|
|
323
438
|
WHERE ee.site_id = $1 AND ee.session_hmac = r.session_hmac AND ee.event_name = $8
|
|
324
439
|
))
|
|
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:
|
|
440
|
+
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:N(a.started_at),_cursorId:a.internal_id}));return he(e.siteId,s,n,a=>({sessionRef:T("session",e.siteId,a.internal_id),visitorRef:T("visitor",e.siteId,`visitor:${a.visitor_hmac}`),personRef:a.person_id?T("person",e.siteId,a.person_id):null,returning:a.session_number>1,sessionNumber:a.session_number,startedAt:N(a.started_at),lastActiveAt:N(a.last_active_at),endedAt:pi(a.ended_at),durationMs:Ie(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 $c(e,t=v()){await Ne(e,t);let n=Se(e.limit),i=Ae(e.siteId,e.cursor),s=(await t.query(`/* analyticsVisitors:keyset */
|
|
326
441
|
WITH visitor_sessions AS (
|
|
327
442
|
SELECT COALESCE(s.person_id::text, s.visitor_hmac) AS identity_key, s.person_id,
|
|
328
443
|
min(s.started_at) AS first_seen_at, max(s.last_active_at) AS last_seen_at,
|
|
@@ -344,19 +459,19 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
344
459
|
WHERE ($2::timestamptz IS NULL OR (v.last_seen_at, v.last_session_id) < ($2::timestamptz, $3::uuid))
|
|
345
460
|
AND ($4::boolean IS NULL OR (v.session_count > 1) = $4)
|
|
346
461
|
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:
|
|
462
|
+
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:N(a.last_seen_at),_cursorId:a.last_session_id}));return he(e.siteId,s,n,a=>({visitorRef:T("visitor",e.siteId,a.person_id?`person:${a.person_id}`:`visitor:${a.identity_key}`),personRef:a.person_id?T("person",e.siteId,a.person_id):null,known:!!a.person_id,returning:a.session_count>1,sessions:a.session_count,firstSeenAt:N(a.first_seen_at),lastSeenAt:N(a.last_seen_at),durationMs:Ie(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 ys(e,t){if(t.startsWith(ke.person))return{kind:"person",personId:at("person",e,t),visitorHmac:null,sessionId:null};if(t.startsWith(ke.visitor)){let n=at("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(ke.session))return{kind:"session",personId:null,visitorHmac:null,sessionId:at("session",e,t)};throw new Error("analytics_reference_invalid")}var gs=2160*60*60*1e3,fs=366*24*60*60*1e3;function vs(e,t){let n=t===void 0?Date.now():Date.parse(t),i=e===void 0?n-gs:Date.parse(e);if(!Number.isFinite(i)||!Number.isFinite(n)||i>=n||n-i>fs)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 kc(e,t=v()){await Ne(e,t);let n=Se(e.limit),i=Ae(e.siteId,e.cursor),r;try{r=await ys(e.siteId,e.reference)}catch{throw new f("analytics_journey_not_found","Journey not found.",404)}if(!(await t.query(`/* analyticsEnhancedJourney:subject */
|
|
348
463
|
SELECT 1 WHERE
|
|
349
464
|
($2::uuid IS NOT NULL AND EXISTS (SELECT 1 FROM analytics_people p WHERE p.site_id=$1 AND p.id=$2))
|
|
350
465
|
OR ($3::text IS NOT NULL AND EXISTS (SELECT 1 FROM analytics_sessions s WHERE s.site_id=$1 AND s.visitor_hmac=$3))
|
|
351
466
|
OR ($4::uuid IS NOT NULL AND EXISTS (SELECT 1 FROM analytics_sessions s WHERE s.site_id=$1 AND s.id=$4))
|
|
352
|
-
LIMIT 1`,[e.siteId,r.personId,r.visitorHmac,r.sessionId])).rowCount)throw new f("analytics_journey_not_found","Journey not found.",404);let a=
|
|
467
|
+
LIMIT 1`,[e.siteId,r.personId,r.visitorHmac,r.sessionId])).rowCount)throw new f("analytics_journey_not_found","Journey not found.",404);let a=vs(e.from,e.to),o=[e.siteId,r.personId,r.visitorHmac,r.sessionId,a.from,a.to],c=`
|
|
353
468
|
SELECT s.id, s.session_hmac, s.visitor_hmac, s.person_id, s.started_at,
|
|
354
469
|
s.last_active_at, s.ended_at, s.duration_ms, s.landing_path, s.exit_path
|
|
355
470
|
FROM analytics_sessions s
|
|
356
471
|
WHERE s.site_id=$1
|
|
357
472
|
AND ($2::uuid IS NULL OR s.person_id=$2)
|
|
358
473
|
AND ($3::text IS NULL OR s.visitor_hmac=$3 OR s.person_id=(SELECT person_id FROM analytics_sessions WHERE site_id=$1 AND visitor_hmac=$3 AND person_id IS NOT NULL LIMIT 1))
|
|
359
|
-
AND ($4::uuid IS NULL OR s.id=$4)`,[
|
|
474
|
+
AND ($4::uuid IS NULL OR s.id=$4)`,[l,_,d,p,E,b]=await Promise.all([t.query(`/* analyticsSessionTimeline:keyset */
|
|
360
475
|
WITH subject_sessions AS (
|
|
361
476
|
${c}
|
|
362
477
|
), entries AS (
|
|
@@ -515,7 +630,7 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
515
630
|
f.source,f.medium,f.campaign,f.referrer
|
|
516
631
|
FROM ranged_sessions s LEFT JOIN event_stats e ON e.session_hmac=s.session_hmac
|
|
517
632
|
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])]),
|
|
633
|
+
ORDER BY s.started_at DESC,s.id DESC LIMIT 50`,[...o])]),k=l.rows.map(m=>({...m,_cursorAt:N(m.occurred_at),_cursorId:m.stable_id})),x=he(e.siteId,k,n,m=>({occurredAt:N(m.occurred_at),type:m.entry_type,name:m.name,path:m.path,referrer:Mt(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},C=h.resolved_person_id??r.personId;return{subject:{kind:C?"person":r.kind,reference:e.reference,personRef:C?T("person",e.siteId,C):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:Ie(h.total_engagement_ms),averageEngagementMs:Ie(h.average_engagement_ms)},commonPages:d.rows.map(m=>({path:m.path,visits:Number(m.visits),uniqueSessions:Number(m.unique_sessions),engagedMs:Ie(m.engaged_ms),conversions:Number(m.conversions)})),acquisition:p.rows.map(m=>({source:m.source,medium:m.medium,campaign:m.campaign,referrer:Mt(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:T("session",e.siteId,m.internal_id),startedAt:N(m.started_at),endedAt:pi(m.ended_at),durationMs:Ie(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:Mt(m.referrer)}:null})),...x}}async function Pc(e,t=v()){await Ne(e,t);let n=Se(e.limit),i=Ae(e.siteId,e.cursor),s=(await t.query(`/* analyticsConfirmedJourneys:keyset */
|
|
519
634
|
SELECT p.id AS person_id, p.namespace_id, sessions.first_seen_at,
|
|
520
635
|
sessions.last_seen_at, COALESCE(sessions.session_count,0)::int AS session_count,
|
|
521
636
|
COALESCE(events.event_count,0)::int AS event_count,
|
|
@@ -539,7 +654,7 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
539
654
|
) conversions ON true
|
|
540
655
|
WHERE p.site_id=$1 AND n.status='approved'
|
|
541
656
|
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:
|
|
657
|
+
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:N(o.last_seen_at),_cursorId:o.person_id}));return{journeyTier:"confirmed",...he(e.siteId,s,n,o=>({journeyTier:"confirmed",personRef:T("person",e.siteId,o.person_id),firstSeenAt:N(o.first_seen_at),lastSeenAt:N(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 Mc(e,t=v()){await Ne(e,t);let n=Se(e.limit),i=Ae(e.siteId,e.cursor),s=(await t.query(`/* analyticsBestGuessJourneys:keyset */
|
|
543
658
|
SELECT ca.id AS association_id, ca.namespace_id, ca.model_version, ca.expires_at, ca.updated_at,
|
|
544
659
|
ca.left_identity_node_id AS left_node_id, ca.right_identity_node_id AS right_node_id,
|
|
545
660
|
COALESCE(stats.confirmed_sessions,0)::int AS confirmed_sessions,
|
|
@@ -593,7 +708,7 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
593
708
|
WHERE ca.site_id=$1 AND n.status='approved' AND ca.status='active'
|
|
594
709
|
AND ca.confidence_band='high' AND ca.expires_at > now()
|
|
595
710
|
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:
|
|
711
|
+
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:N(o.updated_at),_cursorId:o.association_id}));return{journeyTier:"candidate_assisted",totalsAreSeparated:!0,...he(e.siteId,s,n,o=>({journeyTier:"candidate_assisted",inferenceRef:T("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:N(o.expires_at),provenance:"consented_high_confidence_candidate_association"}))}}async function Uc(e,t=v()){await Ne(e,t);let n=Se(e.limit),i=Ae(e.siteId,e.cursor),s=(await t.query(`/* analyticsInferenceReview:keyset */
|
|
597
712
|
SELECT ca.id AS association_id, ca.updated_at, ca.first_observed_at, ca.last_observed_at,
|
|
598
713
|
ca.expires_at, ca.model_version, ca.confidence_band,
|
|
599
714
|
COALESCE(array_agg(DISTINCT ce.feature_kind ORDER BY ce.feature_kind)
|
|
@@ -606,32 +721,32 @@ import{a as Zt,s as tn}from"./chunk-OLSW4AN4.js";import{T as cn,a as de,b as Ne,
|
|
|
606
721
|
AND ca.confidence_band='medium' AND ca.expires_at > now()
|
|
607
722
|
AND ($2::timestamptz IS NULL OR (ca.updated_at,ca.id) < ($2::timestamptz,$3::uuid))
|
|
608
723
|
GROUP BY ca.id
|
|
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:
|
|
724
|
+
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:N(a.updated_at),_cursorId:a.association_id}));return he(e.siteId,s,n,a=>({inferenceRef:T("candidate",e.siteId,a.association_id),confidenceBand:a.confidence_band,modelVersion:a.model_version,featureClasses:a.feature_kinds,firstObservedAt:N(a.first_observed_at),lastObservedAt:N(a.last_observed_at),expiresAt:N(a.expires_at),action:"diagnostic_only",eligibleForConfirmedMetrics:!1,eligibleForCrmOrActivation:!1}))}import{createHash as Es,createVerify as bs,verify as Is}from"crypto";function yi(e){if(!be(e.timestamp,e.now))return!1;let t=`${e.method.toUpperCase()}${e.url}${e.rawBody}${e.timestamp}`;return Ee(e.signature,ve("sha256",e.clientSecret,t))}var As=`-----BEGIN PUBLIC KEY-----
|
|
610
725
|
MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
611
|
-
-----END PUBLIC KEY-----`;function
|
|
726
|
+
-----END PUBLIC KEY-----`;function gi(e){try{if(e.ghlSignature&&e.ghlSignature!=="N/A")return Is(null,Buffer.from(e.rawBody,"utf8"),e.ed25519PublicKey||As,Buffer.from(e.ghlSignature,"base64"));if(e.legacySignature&&e.legacySignature!=="N/A"&&e.legacyRsaPublicKey){let t=bs("SHA256");return t.update(e.rawBody),t.verify(e.legacyRsaPublicKey,e.legacySignature,"base64")}return!1}catch{return!1}}function Ft(e){return{schemaVersion:1,eventId:`crm_${Es("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 fi(e){let t=e.item,n=y(t.objectId,240)||"unknown",i=y(t.propertyValue,240)||"unknown";return{sourceEventId:`deal:${n}:stage:${i}:${y(t.occurredAt,100)||"unknown"}`,source:"hubspot",sourceAccountRef:e.sourceAccountRef,dealId:n,stageId:i,stageName:e.stageName,occurredAt:G(t.occurredAt),valueMinor:e.valueMinor,currency:e.currency,status:/closed.?won/i.test(i)?"won":/closed.?lost/i.test(i)?"lost":"open"}}function vi(e){let t=e.payload,n=t.opportunity&&typeof t.opportunity=="object"?t.opportunity:t,i=y(n.id??t.id,240)||"unknown",r=y(n.pipelineStageId??t.pipelineStageId,240)||"unknown",s=(y(n.status??t.status,80)||"open").toLowerCase();return{sourceEventId:`opportunity:${i}:stage:${r}:${y(t.timestamp??t.dateAdded,100)||"unknown"}`,source:"highlevel",sourceAccountRef:e.sourceAccountRef,contactId:y(n.contactId??t.contactId,240),dealId:i,pipelineId:y(n.pipelineId??t.pipelineId,240),stageId:r,stageName:y(n.pipelineStageName??t.pipelineStageName,240),occurredAt:G(t.timestamp,t.dateAdded,n.updatedAt),valueMinor:Math.round((Y(n.monetaryValue??t.monetaryValue)??0)*100),currency:y(n.currency??t.currency,3)?.toUpperCase(),status:s.includes("won")?"won":s.includes("lost")?"lost":s.includes("spam")?"spam":"open"}}var Cs=90;function bi(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 ws(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 Rs={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}},Os=/(^|_)(api_?key|access_?key|api_?token|auth_?token|token|password|private_?key|secret|signing_?key)($|_)/i;function Ds(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(Os.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 cd(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 qt(e){let t=e.provider,n=Rs[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 dd(e,t){let n=String(e.id),i=String(e.provider),r=qt({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,l=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:l?"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 ld(e){let t=Ct.parse(e.definition),n=v();await Q(n,e.siteId,e.userId);let i=t.enabled?"active":"disabled";return(await n.query(`INSERT INTO analytics_event_definitions(
|
|
612
727
|
id, site_id, name, event_name, trigger_kind, definition, status, created_by_user_id
|
|
613
728
|
) VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7,$8)
|
|
614
729
|
ON CONFLICT(site_id, name) DO UPDATE SET event_name=EXCLUDED.event_name,
|
|
615
730
|
trigger_kind=EXCLUDED.trigger_kind, definition=EXCLUDED.definition,
|
|
616
731
|
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`,[
|
|
732
|
+
RETURNING id, name, event_name, trigger_kind, definition, version, status, created_at, updated_at`,[de(),e.siteId,t.name,t.eventName,t.triggerKind,JSON.stringify(t),i,e.userId])).rows[0]}async function ud(e,t){let n=v();return await B(n,e,t),(await n.query(`SELECT id, name, event_name, trigger_kind, definition, version, status, created_at, updated_at
|
|
618
733
|
FROM analytics_event_definitions WHERE site_id=$1 AND status <> 'archived'
|
|
619
|
-
ORDER BY created_at DESC`,[e])).rows}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
|
|
734
|
+
ORDER BY created_at DESC`,[e])).rows}async function Ht(e){let t=v();e.requireEditor?await Q(t,e.siteId,e.userId):await B(t,e.siteId,e.userId);let i=(await t.query(`SELECT id, definition, version, status FROM analytics_event_definitions
|
|
735
|
+
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 _d(e){let{db:t,row:n}=await Ht({...e,requireEditor:!0}),i=Ct.parse({...n.definition,...e.changes}),r=i.enabled?"active":"disabled";return(await t.query(`UPDATE analytics_event_definitions SET
|
|
621
736
|
name=$3, event_name=$4, trigger_kind=$5, definition=$6::jsonb,
|
|
622
737
|
status=$7, version=version+1, updated_at=now()
|
|
623
738
|
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
|
|
739
|
+
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 md(e){let t=v();await Q(t,e.siteId,e.userId);let n=await t.query(`UPDATE analytics_event_definitions SET status='archived', updated_at=now()
|
|
625
740
|
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
|
|
741
|
+
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 Ts(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 pd(e){let{row:t}=await Ht({...e,requireEditor:!1});return Ts(e.definitionId,t.definition,e.preview)}async function yd(e){let{db:t,row:n}=await Ht({...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
742
|
FROM analytics_events
|
|
628
743
|
WHERE site_id=$1 AND event_definition_id=$2 AND event_definition_version=$3
|
|
629
744
|
AND received_at >= now() - ($4 * interval '1 second')
|
|
630
745
|
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(
|
|
746
|
+
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(l=>setTimeout(l,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 gd(e){let t=await v().query(`SELECT s.owner_user_id, s.id AS site_id, d.id, d.version, d.definition
|
|
632
747
|
FROM analytics_pixels p JOIN analytics_sites s ON s.id=p.site_id
|
|
633
748
|
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
|
|
749
|
+
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 fd(e){Ds(e.config??{});let t=v();await Q(t,e.siteId,e.userId);let n=e.webhookSecret?ue(e.webhookSecret,`connection-secret:${e.provider}:${e.sourceAccountRef}`):null;return(await t.query(`INSERT INTO analytics_connections(
|
|
635
750
|
id, site_id, provider, name, source_account_ref, service_connection_ref,
|
|
636
751
|
secret_ciphertext, config, created_by_user_id
|
|
637
752
|
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb,$9)
|
|
@@ -646,17 +761,17 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
646
761
|
readiness, last_verified_at, last_event_at, last_reconciled_at, last_error_code,
|
|
647
762
|
verification_evidence, last_verified_receipt_id,
|
|
648
763
|
(secret_ciphertext IS NOT NULL) AS webhook_secret_configured,
|
|
649
|
-
created_at, updated_at`,[
|
|
764
|
+
created_at, updated_at`,[de(),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 vd(e,t){let n=v();return await B(n,e,t),(await n.query(`SELECT id, site_id, provider, name, source_account_ref, service_connection_ref,
|
|
650
765
|
readiness, last_verified_at, last_event_at, last_reconciled_at,
|
|
651
766
|
last_error_code, verification_evidence, last_verified_receipt_id,
|
|
652
767
|
created_at, updated_at,
|
|
653
768
|
(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
|
|
769
|
+
FROM analytics_connections WHERE site_id = $1 ORDER BY created_at`,[e])).rows}async function Ed(e){let n=(await v().query(`SELECT id, site_id, provider, source_account_ref, secret_ciphertext,
|
|
655
770
|
readiness, config
|
|
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?
|
|
771
|
+
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?Oe(n.secret_ciphertext,i):null,readiness:n.readiness,config:n.config||{}}}async function Ii(e){let t=v();await Q(t,e.siteId,e.userId);let i=(await t.query(`SELECT id, site_id, provider, source_account_ref, service_connection_ref,
|
|
657
772
|
secret_ciphertext, readiness, config
|
|
658
773
|
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
|
|
774
|
+
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 xs(e){return e.secret_ciphertext?Oe(e.secret_ciphertext,`connection-secret:${e.provider}:${e.source_account_ref}`):null}function Ls(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},l=JSON.stringify(c);return{signatureVerified:ci({rawBody:l,signature:e.tamperSignature?"invalid":jt("sha1",i).update(l).digest("base64"),secret:i,payloadTimestamp:n,now:t}),event:di({payload:c,sourceAccountRef:e.sourceAccountRef,phase:"post_call"})}}if(e.provider==="calltrackingmetrics"){let c={activity_id:"xray-test-call",timestamp:n,status:"completed"},l=JSON.stringify(c),_=String(Math.floor(t/1e3));return{signatureVerified:li({rawBody:l,requestTime:_,signature:e.tamperSignature?"invalid":jt("sha1",i).update(_+l).digest("base64"),secret:i,now:t}),event:ui({payload:c,sourceAccountRef:e.sourceAccountRef,phase:"call_complete"})}}if(e.provider==="twilio"){let c={CallSid:"CA_XRAY_TEST",CallStatus:"completed",Timestamp:n},l=e.tamperSignature?"invalid":Ns.getExpectedTwilioSignature(i,e.callbackUrl,c);return{signatureVerified:_i({authToken:i,signature:l,url:e.callbackUrl,params:c}),event:mi({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)},l=JSON.stringify([c]),_=String(t),d=jt("sha256",i).update(`POST${e.callbackUrl}${l}${_}`).digest("base64"),p=e.tamperSignature?"invalid":d;return{signatureVerified:yi({clientSecret:i,signature:p,timestamp:_,method:"POST",url:e.callbackUrl,rawBody:l,now:t}),event:Ft(fi({item:c,sourceAccountRef:e.sourceAccountRef}))}}if(e.provider==="highlevel"){let c={timestamp:n,opportunity:{id:"xray-test-opportunity",pipelineStageId:"qualified",status:"open"}},l=JSON.stringify(c),{privateKey:_,publicKey:d}=Ss("ed25519"),p=e.tamperSignature?Buffer.alloc(64).toString("base64"):hs(null,Buffer.from(l),_).toString("base64");return{signatureVerified:gi({rawBody:l,ghlSignature:p,ed25519PublicKey:d.export({type:"spki",format:"pem"}).toString()}),event:Ft(vi({payload:c,sourceAccountRef:e.sourceAccountRef}))}}let r=Qe.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=ln(e.provider,a,s,i);return{signatureVerified:un(e.tamperSignature?`${o.startsWith("0")?"1":"0"}${o.slice(1)}`:o,o),event:r}}async function bd(e){let t=await Ii(e),n=xs(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=qt({provider:t.provider,connectionId:t.id,serviceConnectionRef:t.service_connection_ref}),r=new URL(i.callbackPath,`${e.requestOrigin.replace(/\/$/,"")}/`).toString(),s=Ls({provider:t.provider,sourceAccountRef:t.source_account_ref,callbackUrl:r,secret:n});if(!s.signatureVerified)throw await Ei({connectionId:t.id,readiness:"degraded",errorCode:"synthetic_signature_test_failed"}),new f("analytics_connection_test_failed","Connection signature test failed.",409);let a=await Ei({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 Id(e){let t=await Ii(e),n=qt({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 Ad(e){let t=de(),n=e.rawBody?ue(e.rawBody,"webhook-payload"):null,i=await v().query(`INSERT INTO analytics_webhook_receipts(
|
|
660
775
|
id, site_id, connection_id, provider, source_account_ref, source_event_id,
|
|
661
776
|
signature_status, payload_ciphertext, payload_expires_at
|
|
662
777
|
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,now() + interval '7 days')
|
|
@@ -665,11 +780,11 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
665
780
|
WHEN analytics_webhook_receipts.normalization_status = 'accepted' THEN 'replayed'
|
|
666
781
|
ELSE analytics_webhook_receipts.normalization_status
|
|
667
782
|
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
|
|
783
|
+
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 Sd(e){await v().query(`UPDATE analytics_webhook_receipts SET
|
|
669
784
|
normalization_status = $2,
|
|
670
785
|
error_code = $3,
|
|
671
786
|
normalized_at = now()
|
|
672
|
-
WHERE id = $1`,[e.receiptId,e.accepted?"accepted":"rejected",e.errorCode??null])}async function
|
|
787
|
+
WHERE id = $1`,[e.receiptId,e.accepted?"accepted":"rejected",e.errorCode??null])}async function Ei(e){return(await v().query(`UPDATE analytics_connections SET
|
|
673
788
|
readiness = CASE WHEN readiness='live' AND $2='verified' THEN 'live' ELSE $2 END,
|
|
674
789
|
last_error_code = $3,
|
|
675
790
|
last_verified_at = CASE WHEN $2 IN ('verified','live') THEN now() ELSE last_verified_at END,
|
|
@@ -683,7 +798,7 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
683
798
|
updated_at = now()
|
|
684
799
|
WHERE id = $1
|
|
685
800
|
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
|
|
801
|
+
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 hd(e){let t=Ln.parse(e.rule),n=v();return await Q(n,e.siteId,e.userId),(await n.query(`INSERT INTO analytics_conversion_rules(
|
|
687
802
|
id, site_id, name, conversion_kind, condition, default_value_minor,
|
|
688
803
|
default_currency, status, created_by_user_id
|
|
689
804
|
) VALUES ($1,$2,$3,$4,$5::jsonb,$6,$7,$8,$9)
|
|
@@ -695,15 +810,15 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
695
810
|
status = EXCLUDED.status,
|
|
696
811
|
version = analytics_conversion_rules.version + 1,
|
|
697
812
|
updated_at = now()
|
|
698
|
-
RETURNING *`,[
|
|
813
|
+
RETURNING *`,[de(),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 Nd(e,t){let n=v();return await B(n,e,t),(await n.query(`SELECT id, name, conversion_kind, condition, default_value_minor,
|
|
699
814
|
default_currency, version, status, created_at, updated_at
|
|
700
815
|
FROM analytics_conversion_rules WHERE site_id = $1 AND status <> 'archived'
|
|
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
|
|
702
|
-
WHERE site_id=$1 AND status='approved' ORDER BY version DESC LIMIT 1`,[t])).rows[0]?.id??null,a=[];n.visitorId&&a.push(["visitor_id",n.visitorId]),n.sessionId&&a.push(["session_id",n.sessionId]),n.personRef&&a.push(["customer_id",n.personRef]);for(let[o,c]of Object.entries(
|
|
816
|
+
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 $s(e){let t=e.properties.tags;return Array.isArray(t)?t.filter(n=>typeof n=="string"):[]}function ks(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=$s(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 Ps(e,t,n){if(!bi(n).enhancedMatching)return{personId:null,namespaceId:null,identityTier:"unlinked"};let s=(await e.query(`SELECT id FROM analytics_identity_namespaces
|
|
817
|
+
WHERE site_id=$1 AND status='approved' ORDER BY version DESC LIMIT 1`,[t])).rows[0]?.id??null,a=[];n.visitorId&&a.push(["visitor_id",n.visitorId]),n.sessionId&&a.push(["session_id",n.sessionId]),n.personRef&&a.push(["customer_id",n.personRef]);for(let[o,c]of Object.entries(U(n.clickIds)))a.push(["click_id",`${o}:${c}`]);for(let[o,c]of a){let l=await e.query(`SELECT e.person_id FROM analytics_identity_nodes n
|
|
703
818
|
JOIN analytics_identity_edges e ON e.identity_node_id = n.id
|
|
704
819
|
WHERE n.site_id = $1 AND n.kind = $2 AND n.value_hmac = $3
|
|
705
820
|
AND ($4::uuid IS NULL OR (n.namespace_id=$4 AND e.namespace_id=$4))
|
|
706
|
-
ORDER BY e.confidence DESC, e.last_seen_at DESC LIMIT 1`,[t,o
|
|
821
|
+
ORDER BY e.confidence DESC, e.last_seen_at DESC LIMIT 1`,[t,o,$(o,c),s]);if(l.rows[0])return{personId:l.rows[0].person_id,namespaceId:s,identityTier:"confirmed"}}return{personId:null,namespaceId:s,identityTier:"unlinked"}}async function Ms(e,t){let i=(await e.query(`SELECT m.person_id::text, m.namespace_id::text,
|
|
707
822
|
c.provider AS connection_provider,
|
|
708
823
|
s.latest_receipt_id::text AS receipt_id, s.policy_version,
|
|
709
824
|
s.site_analytics, s.enhanced_matching, s.advertising_measurement,
|
|
@@ -718,20 +833,20 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
718
833
|
ON r.id=s.latest_receipt_id AND r.site_id=s.site_id
|
|
719
834
|
WHERE m.site_id=$1 AND m.connection_id::text=$2
|
|
720
835
|
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=
|
|
836
|
+
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=se(t.context.provider),s=se(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 Us(e,t){let n=U(t.eventClickIds),i=await e.query(`SELECT click_ids FROM analytics_attribution_touches
|
|
722
837
|
WHERE site_id=$1 AND person_id=$2
|
|
723
838
|
AND occurred_at <= $3::timestamptz
|
|
724
839
|
AND occurred_at >= $3::timestamptz - ($4::text || ' days')::interval
|
|
725
840
|
AND click_ids <> '{}'::jsonb
|
|
726
|
-
ORDER BY occurred_at DESC, id DESC`,[t.siteId,t.personId,t.occurredAt,
|
|
841
|
+
ORDER BY occurred_at DESC, id DESC`,[t.siteId,t.personId,t.occurredAt,Cs]),r={};for(let s of i.rows)for(let[a,o]of Object.entries(U(s.click_ids)))a in r||(r[a]=o);return{...r,...n}}function Fs(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 js(e,t){if(!t.rule.value_rule)return Fs(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
842
|
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
|
|
843
|
+
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 Dn(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 qs(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
844
|
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
845
|
FROM analytics_external_events WHERE site_id=$1 AND person_id=$2
|
|
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(
|
|
846
|
+
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(l=>typeof l=="string"):[];try{let l=si();ai({tables:l,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 _=oi({tables:l,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(
|
|
732
847
|
id, site_id, namespace_id, definition_id, definition_version,
|
|
733
848
|
person_id, score, contributions, evaluated_at
|
|
734
|
-
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb,$9)`,[_.id,e.siteId,e.namespaceId,s.id,s.version,e.personId,_.value,JSON.stringify(_.components),_.evaluatedAt]),r.push(_.id)}catch{}}return r}async function
|
|
849
|
+
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb,$9)`,[_.id,e.siteId,e.namespaceId,s.id,s.version,e.personId,_.value,JSON.stringify(_.components),_.evaluatedAt]),r.push(_.id)}catch{}}return r}async function Hs(e){return e.namespaceId?(await e.client.query(`SELECT t.id, d.event_name, t.minimum_confirmed_people, t.minimum_positive_outcomes,
|
|
735
850
|
(SELECT count(DISTINCT p.id)::text FROM analytics_people p
|
|
736
851
|
WHERE p.site_id=t.site_id AND p.namespace_id=t.namespace_id) AS confirmed_people,
|
|
737
852
|
(SELECT count(DISTINCT e.person_id)::text FROM analytics_external_events e
|
|
@@ -741,26 +856,26 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
741
856
|
WHERE m.target_id=t.id AND m.status IN ('validated','active')) AS validated_manifest
|
|
742
857
|
FROM analytics_prediction_targets t
|
|
743
858
|
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=>
|
|
859
|
+
WHERE t.site_id=$1 AND t.namespace_id=$2 AND t.status<>'disabled'`,[e.siteId,e.namespaceId])).rows.map(n=>ws({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 Ai(e){let t=Qe.parse(e.event),n=e.db??v();e.userId!==void 0&&await Q(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 Ms(r,{siteId:e.siteId,context:e.trustedCrmContext}):null,a=e.trustedCrmContext?Qe.parse({...t,personRef:void 0,consent:s?.consent,properties:{...t.properties,_xrayCrmLinkReason:s?.reason??"mapping_unavailable"}}):t,o=bi(a),c=s?.personId&&s.namespaceId?{personId:s.personId,namespaceId:s.namespaceId,identityTier:"confirmed"}:e.trustedCrmContext?{personId:null,namespaceId:null,identityTier:"unlinked"}:await Ps(r,e.siteId,a),l=c.personId,_=await r.query(`INSERT INTO analytics_external_events(
|
|
745
860
|
id, site_id, receipt_id, person_id, source, source_account_ref,
|
|
746
861
|
source_event_id, event_kind, event_name, occurred_at, visitor_hmac,
|
|
747
862
|
session_hmac, call_id, deal_id, order_id, value_minor, currency,
|
|
748
863
|
click_ids, properties
|
|
749
864
|
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18::jsonb,$19::jsonb)
|
|
750
865
|
ON CONFLICT(site_id, source, source_account_ref, source_event_id) DO NOTHING
|
|
751
|
-
RETURNING id`,[
|
|
752
|
-
WHERE site_id=$1 AND source=$2 AND source_account_ref=$3 AND source_event_id=$4`,[e.siteId,a.source,a.sourceAccountRef,a.sourceEventId])).rows[0]?.id;if(!
|
|
866
|
+
RETURNING id`,[de(),e.siteId,e.receiptId??null,l,a.source,a.sourceAccountRef,a.sourceEventId,a.eventKind,a.eventName,a.occurredAt,a.visitorId?yt("visitor_id",a.visitorId):null,a.sessionId?yt("session_id",a.sessionId):null,a.callId??null,a.dealId??null,a.orderId??null,a.valueMinor??null,a.currency?.toUpperCase()??null,JSON.stringify(U(a.clickIds)),JSON.stringify({...a.properties,_xrayConsentEligibility:o,_xrayIdentityTier:c.identityTier,...c.namespaceId?{_xrayNamespaceId:c.namespaceId}:{}})]),d=_.rows[0]?.id??(await r.query(`SELECT id FROM analytics_external_events
|
|
867
|
+
WHERE site_id=$1 AND source=$2 AND source_account_ref=$3 AND source_event_id=$4`,[e.siteId,a.source,a.sourceAccountRef,a.sourceEventId])).rows[0]?.id;if(!d)throw new f("analytics_external_event_conflict","The canonical event could not be resolved.",409);let E=(await r.query(`SELECT id, name, conversion_kind, condition, default_value_minor,
|
|
753
868
|
default_currency, version
|
|
754
|
-
FROM analytics_conversion_rules WHERE site_id = $1 AND status = 'active'`,[e.siteId])).rows.filter(A=>
|
|
869
|
+
FROM analytics_conversion_rules WHERE site_id = $1 AND status = 'active'`,[e.siteId])).rows.filter(A=>ks({name:A.name,conversionKind:A.conversion_kind,condition:(({valueRule:W,...Me})=>Me)(A.condition),defaultValueMinor:A.default_value_minor?Number(A.default_value_minor):void 0,defaultCurrency:A.default_currency,valueRule:A.condition.valueRule,enabled:!0},a).matches),b=[],k={};for(let A of E){let W=await js(r,{siteId:e.siteId,personId:l,insertedEventId:d,event:a,rule:{value_rule:A.condition.valueRule,default_value_minor:A.default_value_minor,default_currency:A.default_currency,version:A.version}}),Me=W.valueMinor??0,Vt=(W.currency||A.default_currency||"USD").toUpperCase(),Li=W.provenance.resolution==="absent"?"absent":W.provenance.resolution==="fallback"?"dynamic":W.provenance.resolution,$i=de(),we=await r.query(`INSERT INTO analytics_conversions(
|
|
755
870
|
id, source_event_id, site_id, session_id, conversion_kind,
|
|
756
871
|
value_minor, currency, occurred_at, person_id, rule_id, rule_version, order_id,
|
|
757
872
|
value_source, value_provenance, session_hmac
|
|
758
873
|
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14::jsonb,$15)
|
|
759
|
-
ON CONFLICT(site_id, source_event_id) DO NOTHING RETURNING id`,[
|
|
760
|
-
WHERE site_id = $1 AND status = 'active' AND readiness IN ('verified','live')`,[e.siteId]),
|
|
874
|
+
ON CONFLICT(site_id, source_event_id) DO NOTHING RETURNING id`,[$i,`rule:${A.id}:${a.source}:${a.sourceEventId}`,e.siteId,a.sessionId??null,A.conversion_kind,Me,Vt,a.occurredAt,l,A.id,A.version,a.orderId??null,Li,JSON.stringify(W.provenance),a.sessionId?$("session_id",a.sessionId):null]);if(!we.rowCount||(b.push(we.rows[0].id),k[we.rows[0].id]=W.provenance,!l||!o.advertisingMeasurement))continue;let ki=await r.query(`SELECT id FROM analytics_activation_destinations
|
|
875
|
+
WHERE site_id = $1 AND status = 'active' AND readiness IN ('verified','live')`,[e.siteId]),Pi=await Us(r,{siteId:e.siteId,personId:l,occurredAt:a.occurredAt,eventClickIds:a.clickIds}),Mi=ue(JSON.stringify({eventId:we.rows[0].id,eventName:A.conversion_kind,eventTime:a.occurredAt,valueMinor:Me,currency:Vt,valueProvenance:W.provenance,attributionModelProvenance:{model:typeof a.properties.attributionModel=="string"?a.properties.attributionModel:"unassigned",evidenceTier:"confirmed"},orderId:a.orderId,clickIds:Pi,source:a.source,personId:l,identityTier:"confirmed",namespaceId:c.namespaceId,consent:{receiptId:o.receiptId,advertisingMeasurement:!0}}),"activation-payload");for(let Ui of ki.rows)await r.query(`INSERT INTO analytics_activation_jobs(
|
|
761
876
|
id, destination_id, conversion_id, person_id, payload_ciphertext
|
|
762
877
|
) VALUES ($1,$2,$3,$4,$5)
|
|
763
|
-
ON CONFLICT(destination_id, conversion_id) DO NOTHING`,[
|
|
878
|
+
ON CONFLICT(destination_id, conversion_id) DO NOTHING`,[de(),Ui.id,we.rows[0].id,l,Mi])}let x=await qs({client:r,siteId:e.siteId,namespaceId:c.namespaceId,personId:l,occurredAt:a.occurredAt}),h=await Hs({client:r,siteId:e.siteId,namespaceId:c.namespaceId});l&&await ni({siteId:e.siteId,personId:l,canonicalEventId:d,event:a,consent:{...o.receiptId?{receiptId:o.receiptId}:{},siteAnalytics:o.siteAnalytics,advertisingMeasurement:o.advertisingMeasurement},db:r}),i&&await r.query("COMMIT");let C=null,m=typeof a.properties.candidateAssociationId=="string"?a.properties.candidateAssociationId:null,X=typeof a.properties.deterministicEvidenceRef=="string"?a.properties.deterministicEvidenceRef:null,D=a.properties.deterministicEvidenceKind;return l&&c.namespaceId&&m&&X&&["verified_email","verified_phone","crm_contact","signed_linker","authenticated_user"].includes(String(D))&&(C=await Un({siteId:e.siteId,namespaceId:c.namespaceId,associationId:m,personId:l,evidenceKind:D,evidenceRef:X,occurredAt:a.occurredAt}).catch(()=>null)),{id:_.rows[0]?.id??null,created:!!_.rowCount,personId:l,identityTier:c.identityTier,namespaceId:c.namespaceId,conversionIds:b,consentEligibility:o,valueProvenance:k,scoreEvaluationIds:x,predictionEvidence:h,candidatePromotion:C}}catch(s){throw i&&await r.query("ROLLBACK"),s}finally{i&&r.release()}}async function Cd(e,t,n){let i=v();if(await B(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,l]=await Promise.all([i.query(`SELECT id, started_at, last_active_at, ended_at, duration_ms, landing_path,
|
|
764
879
|
exit_path, pageview_count, event_count
|
|
765
880
|
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
881
|
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 +884,7 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
769
884
|
d.platform, d.name AS destination_name
|
|
770
885
|
FROM analytics_activation_jobs j
|
|
771
886
|
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])]),_=
|
|
887
|
+
WHERE d.site_id = $1 AND j.person_id = $2 ORDER BY j.created_at`,[e,n])]),_=d=>{if(typeof d!="string"||!d)return null;try{let p=new URL(d);return`${p.origin}${p.pathname}`.slice(0,2e3)}catch{return null}};return{personRef:T("person",e,n),sessions:s.rows.map(d=>({sessionRef:T("session",e,String(d.id)),started_at:d.started_at,last_active_at:d.last_active_at,ended_at:d.ended_at,duration_ms:d.duration_ms,landing_path:d.landing_path,exit_path:d.exit_path,pageview_count:d.pageview_count,event_count:d.event_count})),touches:a.rows.map(d=>({occurred_at:d.occurred_at,source:d.source,medium:d.medium,campaign:d.campaign,referrer:_(d.referrer),landing_path:d.landing_path})),externalEvents:o.rows.map(d=>({source:d.source,event_kind:d.event_kind,event_name:d.event_name,occurred_at:d.occurred_at,value_minor:d.value_minor,currency:d.currency})),conversions:c.rows.map(d=>({conversion_kind:d.conversion_kind,value_minor:d.value_minor,currency:d.currency,occurred_at:d.occurred_at,rule_version:d.rule_version})),deliveries:l.rows.map(d=>({status:d.status,attempts:d.attempts,response_category:d.response_category,created_at:d.created_at,delivered_at:d.delivered_at,platform:d.platform,destination_name:d.destination_name}))}}async function wd(e,t){let n=v();await B(n,e,t);let r=(await n.query(`SELECT
|
|
773
888
|
count(*)::int AS total_events,
|
|
774
889
|
count(*) FILTER (WHERE person_id IS NOT NULL)::int AS identified_events,
|
|
775
890
|
count(*) FILTER (WHERE click_ids <> '{}'::jsonb)::int AS events_with_click_id,
|
|
@@ -778,7 +893,7 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
778
893
|
count(*) FILTER (WHERE event_kind = 'crm')::int AS crm_events,
|
|
779
894
|
count(*) FILTER (WHERE event_kind = 'crm' AND person_id IS NOT NULL)::int AS joined_crm_events,
|
|
780
895
|
count(*) FILTER (WHERE properties->>'status' = 'spam' OR properties->'tags' ? 'spam')::int AS spam_events
|
|
781
|
-
FROM analytics_external_events WHERE site_id = $1`,[e])).rows[0],s=(a,o)=>o?a/o:null;return{...r,identifiedRate:s(r.identified_events,r.total_events),clickIdCaptureRate:s(r.events_with_click_id,r.total_events),phoneJoinRate:s(r.joined_call_events,r.call_events),crmStageJoinRate:s(r.joined_crm_events,r.crm_events)}}var
|
|
896
|
+
FROM analytics_external_events WHERE site_id = $1`,[e])).rows[0],s=(a,o)=>o?a/o:null;return{...r,identifiedRate:s(r.identified_events,r.total_events),clickIdCaptureRate:s(r.events_with_click_id,r.total_events),phoneJoinRate:s(r.joined_call_events,r.call_events),crmStageJoinRate:s(r.joined_crm_events,r.crm_events)}}var Pe="mcp_scraper_subscription_paid";function Ws(e){return/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e)}async function Bs(e,t){return(await e.query(`SELECT p.id::text AS person_id, p.namespace_id::text AS namespace_id,
|
|
782
897
|
cs.latest_receipt_id::text AS receipt_id, cs.policy_version,
|
|
783
898
|
cs.site_analytics, cs.enhanced_matching, cs.advertising_measurement,
|
|
784
899
|
cs.effective_at
|
|
@@ -817,4 +932,4 @@ MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
|
817
932
|
)
|
|
818
933
|
ORDER BY edge.confidence DESC, edge.last_seen_at DESC
|
|
819
934
|
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(!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-LPXTITVC.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};
|
|
935
|
+
FOR SHARE OF p, ns, cs, receipt`,[t.siteId,$("customer_id",t.stableUserId)])).rows[0]??null}async function Wt(e,t){let n=process.env.MCP_SCRAPER_XRAY_SITE_ID?.trim();if(!n)return{status:"skipped",eventName:Pe,reason:"site_not_configured"};if(!Ws(n))return{status:"skipped",eventName:Pe,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)=>Ai({...a,transactionClient:o})},s=await r.db.connect();try{await s.query("BEGIN");let a=await Bs(s,{siteId:n,stableUserId:i});if(!a)return await s.query("ROLLBACK"),{status:"skipped",eventName:Pe,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:Pe,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:Pe,reason:"recorded",created:c.created,conversionIds:c.conversionIds}}catch(a){throw await s.query("ROLLBACK").catch(()=>{}),a}finally{s.release()}}function Si(e,t){return e&&(t??0)>0}function hi(e){let t=e;return t?.price?.id??t?.pricing?.price_details?.price??t?.plan?.id}function Ce(e){if(typeof e=="string")return e;if(e&&typeof e=="object"&&"id"in e)return String(e.id)}function Vs(e){let t=e.subscription;if(t)return Ce(t);let n=e.parent;return Ce(n?.subscription_details?.subscription)}async function Ks(e,t){let n=await P(e);return n||!t||(n=await je(t),n&&await Ve(n.id,e)),n}async function Js(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 Bt(e,t,n){if(!t.id||t.status!=="paid")return{status:"not_applicable",invoiceId:t.id??null};let i=t.lines.data.some(p=>{let E=hi(p);return!!E&&E in L});if(!Si(i,t.total))return{status:"not_applicable",invoiceId:t.id};let r=Vs(t),a=(r?me(await e.subscriptions.retrieve(r))?.price?.id:void 0)??t.lines.data.map(hi).find(p=>!!p&&p in L),o=a?L[a]:void 0;if(!o)return{status:"not_applicable",invoiceId:t.id};let c=Ce(t.customer);if(!c)throw new Error("Paid subscription invoice has no Stripe customer");let l=await Ks(c,t.customer_email);if(!l)throw new Error("Paid subscription invoice does not map to an MCP Scraper account");let _=await vn({invoiceId:t.id,userId:l.id,subscriptionId:r??l.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 Js(e,l,r??l.subscription_id),await Z(l,V({...l,memory_plan:"pro",subscription_tier:o.tier})));let d=await Je(l);d.ok||console.warn("[stripe] scheduled-action credential sync failed:",d.error??"unknown error");try{await Wt({userId:l.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(p){console.error("[stripe] X-Ray paid outcome failed",{invoiceId:t.id,error:p instanceof Error?p.message:String(p)})}return{status:_.granted?"fulfilled":"already_fulfilled",invoiceId:t.id,userId:l.id,tier:o.tier}}async function Ni(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:Ce(i.invoice)??null};let r=Ce(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(Ce(i.invoice)??"");if(!s?.id)throw new Error("Paid subscription checkout has no invoice");return Bt(e,s,i.created)}var Ci="scale_credit_pack";function ot(e){if(typeof e=="string")return e;if(e&&typeof e=="object"&&"id"in e)return String(e.id)}function wi(e){let t=Number(e);if(!Number.isSafeInteger(t)||t<1||t>lt)throw new Error(`quantity must be a whole number from 1 to ${lt}`);return t}function Vd(e){return e.subscription_tier==="scale"}function Kd(e,t){let n=wi(t);return{purchase_kind:Ci,account_user_id:String(e),eligible_tier:"scale",increment_quantity:String(n),increment_usd:String(Re),credits_per_increment:String(dt)}}async function Ri(e,t,n){let i=await e.checkout.sessions.retrieve(t);if(i.mode!=="payment"||i.metadata?.purchase_kind!==Ci)return{status:"not_applicable",paymentIntentId:ot(i.payment_intent)??null};if(i.payment_status!=="paid")return{status:"not_applicable",paymentIntentId:ot(i.payment_intent)??null};let r=ot(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=wi(i.metadata.increment_quantity),a=s*Re*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!==Re*100)throw new Error("Scale credit-pack checkout line item does not match the product contract");let l=await P(r);if(!l)throw new Error("Paid Scale credit-pack checkout does not map to an MCP Scraper account");if(i.metadata.account_user_id!==String(l.id)||i.metadata.eligible_tier!=="scale")throw new Error("Scale credit-pack checkout account metadata does not match the billing account");let _=ot(i.payment_intent);if(!_)throw new Error("Paid Scale credit-pack checkout has no payment intent");let d=s*dt;return{status:(await Yt(l.id,s*tn,Ue.TOPUP,`$${s*Re} Scale credit pack (${d.toLocaleString("en-US")} credits)`,`stripe-payment-intent:${_}`,{neverExpires:!1})).duplicate?"already_fulfilled":"fulfilled",paymentIntentId:_,userId:l.id,quantity:s,credits:d}}var M=new zs(process.env.STRIPE_SECRET_KEY,{apiVersion:"2026-02-25.clover"});async function Xs(e,t){if(!(!e||e===t))try{let n=await M.subscriptions.retrieve(e),i=n.items.data[0]?.price?.id,r=n.status!=="canceled"&&!n.cancel_at_period_end;i&&i in le&&r&&await M.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 Qs=new Ys;Qs.post("/webhooks",async e=>{let t=e.req.header("stripe-signature"),n=await e.req.text(),i;try{i=M.webhooks.constructEvent(n,t,process.env.STRIPE_WEBHOOK_SECRET)}catch{return e.json({error:"Invalid signature"},400)}try{let r=await fn(i.id,i.type,i.created);return r==="queued"&&Zs(),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 Zs(){let e=import("./queue-AC4XKD6Z.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))});Gs(e)}async function ol(e){if(e.type==="checkout.session.completed"||e.type==="checkout.session.async_payment_succeeded"){let t=e.data.object;t.mode==="payment"?await Ri(M,t.id):await Ni(M,t.id);return}if(e.type==="invoice.created"){let t=e.data.object;Oi(t)&&t.status==="draft"&&await M.invoices.update(t.id,{description:Zt,footer:en,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(Oi(t)){let i=t.customer_email?void 0:await Ti(t.customer,void 0),r=t.customer_email??i?.email;if(!r)throw new Error("Paid concurrency invoice has no customer receipt email");await ea(t.id,r);return}if(t.lines.data.map(xi).find(i=>i&&i in le))return;await Bt(M,t,e.created)}if(e.type==="customer.subscription.created"||e.type==="customer.subscription.updated"){let t=e.data.object,n=Di(t);if(n){let s=await P(t.customer);if(!s)return;let a=De(t.status);await ft(s.id,a?ta(n):0),await vt(s.id,a?t.id:null);return}let i=me(t)?.price?.id??t.items.data[0]?.price?.id;if(i&&i in le){let s=le[i],a=await Ti(t.customer,t.customer_email);if(a){let o=De(t.status),c=o?s.plan:"free";await _e(a.id,c,o?t.id:null),await Z(a,V({...a,memory_plan:c}))}return}let r=await P(t.customer);if(!r)return;if(i&&i in L){let s=L[i],a=De(t.status),o=a?s.tier:null;if(await Et(r.id,o,a?s.concurrency:0,a?t.id:null),s.includes_memory){let c=a?"pro":"free";a&&await Xs(r.memory_subscription_id,t.id),await _e(r.id,c,null),await Z(r,V({...r,memory_plan:c}))}else r.memory_subscription_id===t.id&&(await _e(r.id,"free",null),await Z(r,V({...r,memory_plan:"free"})));if(a){let c=await Je(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(Di(t)){let i=await P(t.customer);i&&(await ft(i.id,0),await vt(i.id,null));return}let n=me(t)?.price?.id??t.items.data[0]?.price?.id;if(n&&n in L){let i=L[n],r=await P(t.customer);r&&(await Et(r.id,null,0,null),(i.includes_memory||r.memory_subscription_id===t.id)&&(await _e(r.id,"free",null),await Z(r,V({...r,memory_plan:"free"}))));return}if(n&&n in le||await bt(t.id)){let i=await bt(t.id)??await P(t.customer);i&&(await _e(i.id,"free",null),await Z(i,V({...i,memory_plan:"free"})));return}}}function Oi(e){return e.lines.data.some(t=>xi(t)===ct)}async function ea(e,t){let i=(await M.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 M.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 M.charges.retrieve(r):r;s.receipt_email!==t&&await M.charges.update(s.id,{receipt_email:t})}function Di(e){return e.items.data.find(t=>t.price?.id===ct)}function ta(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 xi(e){let t=e;return t?.price?.id??t?.pricing?.price_details?.price??t?.plan?.id}async function Ti(e,t){let n=await P(e);if(n)return n;if(t)return n=await je(t),n&&await Ve(n.id,e),n}export{Ve as a,ft as b,vt as c,Et as d,_e as e,ma as f,Ki as g,fa as h,In as i,It as j,Na as k,Ca as l,Sn as m,ir as n,wa as o,Ra as p,rr as q,Nn as r,Je as s,ra as t,sa as u,aa as v,oa as w,ca as x,ht as y,K as z,me as A,Ma as B,Ua as C,Fa as D,Xe as E,Qe as F,Ln as G,Cr as H,Rr as I,Ct as J,Ga as K,Xa as L,Qa as M,Za as N,oo as O,Un as P,po as Q,yo as R,go as S,fo as T,vo as U,Eo as V,bo as W,Io as X,Ao as Y,So as Z,ho as _,No as $,Co as aa,jn as ba,wo as ca,Lr as da,Ro as ea,se as fa,nt as ga,F as ha,Oo as ia,Pr as ja,Lo as ka,$o as la,ko as ma,zn as na,Yn as oa,Po as pa,jo as qa,Zn as ra,qo as sa,Hr as ta,Xo as ua,fe as va,Qo as wa,uc as xa,si as ya,ai as za,yc as Aa,gc as Ba,oi as Ca,Ec as Da,ci as Ea,di as Fa,li as Ga,ui as Ha,_i as Ia,mi as Ja,at as Ka,Lc as La,$c as Ma,kc as Na,Pc as Oa,Mc as Pa,Uc as Qa,yi as Ra,gi as Sa,Ft as Ta,fi as Ua,vi as Va,Ds as Wa,cd as Xa,dd as Ya,ld as Za,ud as _a,_d as $a,md as ab,pd as bb,yd as cb,gd as db,fd as eb,vd as fb,Ed as gb,bd as hb,Id as ib,Ad as jb,Sd as kb,Ei as lb,hd as mb,Nd as nb,Ai as ob,Cd as pb,wd as qb,Bt as rb,Ni as sb,wi as tb,Vd as ub,Kd as vb,Ri as wb,M as xb,Qs as yb,ol as zb};
|