mcp-scraper 0.96.3 → 0.97.0
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 +16 -1
- package/README.md +12 -18
- package/dist/{actions-FKAX2PYT.js → actions-NCZNPYEY.js} +1 -1
- package/dist/bin/api-server.js +1 -1
- package/dist/bin/mcp-scraper-cli.js +26 -27
- 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/{bundle-7JMACIIR.js → bundle-XR6XKYJM.js} +1 -1
- package/dist/chunk-4P3LZNVA.js +954 -0
- package/dist/chunk-6FAMBNHY.js +1 -0
- package/dist/{chunk-3HXMW2UJ.js → chunk-A4UASMEQ.js} +1 -1
- package/dist/chunk-AMYPXA2F.js +930 -0
- package/dist/chunk-B5Y26E3A.js +1 -0
- package/dist/{chunk-WP3QS4CJ.js → chunk-EYGA6LOU.js} +1 -1
- package/dist/{chunk-P55O4IDC.js → chunk-JNPM3U3L.js} +1 -1
- package/dist/chunk-ND4WIBXT.js +16 -0
- package/dist/{chunk-PANT6VEM.js → chunk-NULRGHJ4.js} +1 -1
- package/dist/{chunk-42SLBGDI.js → chunk-OOB3RKWD.js} +2 -2
- package/dist/{chunk-DJT2547J.js → chunk-OSKCHEVM.js} +1 -1
- package/dist/{chunk-U7BZ5KTE.js → chunk-PD7BI7TR.js} +1 -1
- package/dist/{chunk-WBJ6Y7WP.js → chunk-PQRT4T2G.js} +2 -2
- package/dist/chunk-U3NV72AL.js +8 -0
- package/dist/{chunk-4KHDV5E7.js → chunk-WHVK6FUB.js} +1 -1
- package/dist/{chunk-E7I3OQHQ.js → chunk-XYUJTIPE.js} +1 -1
- package/dist/{chunk-JREMJRSS.js → chunk-YJPFEL2E.js} +1 -1
- package/dist/{chunk-CFZXZKFT.js → chunk-Z4EHBBL4.js} +1 -1
- package/dist/{core-schema-423BSTE6.js → core-schema-Y47YFKH2.js} +7 -44
- package/dist/{dispatch-E6VTNHHS.js → dispatch-ILBBKOSW.js} +1 -1
- package/dist/{queue-4XHYUV7J.js → queue-VOGXQDPR.js} +1 -1
- package/dist/{reconciliation-43CSC3XD.js → reconciliation-OXBG7JA2.js} +1 -1
- package/dist/{server-74VGVXA4.js → server-6HUBN3OR.js} +700 -1016
- package/dist/settlement-3PS32LQO.js +1 -0
- package/dist/{worker-L5NISZQV.js → worker-NXRU3TT3.js} +1 -1
- package/package.json +2 -2
- package/dist/chunk-2IATUJO6.js +0 -1
- package/dist/chunk-7ZR5IO3B.js +0 -16
- package/dist/chunk-JGGQ3UX5.js +0 -1
- package/dist/chunk-MJOXIDKF.js +0 -935
- package/dist/chunk-OY24ERPV.js +0 -8
- package/dist/chunk-QYPSFZK3.js +0 -955
- package/dist/settlement-I5IBIK6U.js +0 -1
package/dist/chunk-MJOXIDKF.js
DELETED
|
@@ -1,935 +0,0 @@
|
|
|
1
|
-
import{H as sn,_ as an,b as ut,c as je,e as tn,t as nn,z as rn}from"./chunk-WBJ6Y7WP.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-DJT2547J.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{$ as en,A as Yt,F as Gt,J as ct,N as L,O as Xt,X as Qt,Y as Zt,Z as Re,_ as dt,aa as lt,ca as te,ea as le,f as Ue,z as Fe}from"./chunk-OY24ERPV.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?!!Xt[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:`
|
|
57
|
-
INSERT OR IGNORE INTO connected_account_charge_transfers (
|
|
58
|
-
user_id, period_start, from_connection_id, to_connection_id, provider_config_key
|
|
59
|
-
)
|
|
60
|
-
SELECT ?, ?, previous.id, replacement.id, replacement.provider_config_key
|
|
61
|
-
FROM service_connections AS replacement
|
|
62
|
-
JOIN service_connections AS previous
|
|
63
|
-
ON previous.user_id = replacement.user_id
|
|
64
|
-
AND previous.transport = 'nango'
|
|
65
|
-
AND previous.provider_config_key = replacement.provider_config_key
|
|
66
|
-
AND previous.lifecycle_status = 'disconnected'
|
|
67
|
-
JOIN connected_account_charges AS charge
|
|
68
|
-
ON charge.user_id = previous.user_id
|
|
69
|
-
AND charge.connection_id = previous.id
|
|
70
|
-
AND charge.period_start = ?
|
|
71
|
-
WHERE replacement.id = ?
|
|
72
|
-
AND replacement.user_id = ?
|
|
73
|
-
AND replacement.transport = 'nango'
|
|
74
|
-
AND replacement.lifecycle_status IN ('connected', 'needs_reauth')
|
|
75
|
-
AND NOT EXISTS (
|
|
76
|
-
SELECT 1 FROM connected_account_charges AS existing
|
|
77
|
-
WHERE existing.user_id = replacement.user_id
|
|
78
|
-
AND existing.connection_id = replacement.id
|
|
79
|
-
AND existing.period_start = ?
|
|
80
|
-
)
|
|
81
|
-
ORDER BY previous.updated_at DESC, previous.id ASC
|
|
82
|
-
LIMIT 1
|
|
83
|
-
`,args:[i,n,n,t,i,n]},{sql:`
|
|
84
|
-
UPDATE connected_account_charges
|
|
85
|
-
SET connection_id = ?
|
|
86
|
-
WHERE user_id = ?
|
|
87
|
-
AND period_start = ?
|
|
88
|
-
AND connection_id = (
|
|
89
|
-
SELECT transfer.from_connection_id
|
|
90
|
-
FROM connected_account_charge_transfers AS transfer
|
|
91
|
-
JOIN connected_account_charges AS current_charge
|
|
92
|
-
ON current_charge.user_id = transfer.user_id
|
|
93
|
-
AND current_charge.period_start = transfer.period_start
|
|
94
|
-
AND current_charge.connection_id = transfer.from_connection_id
|
|
95
|
-
WHERE transfer.user_id = ?
|
|
96
|
-
AND transfer.period_start = ?
|
|
97
|
-
AND transfer.to_connection_id = ?
|
|
98
|
-
ORDER BY transfer.transferred_at DESC, transfer.id DESC
|
|
99
|
-
LIMIT 1
|
|
100
|
-
)
|
|
101
|
-
AND NOT EXISTS (
|
|
102
|
-
SELECT 1 FROM connected_account_charges AS existing
|
|
103
|
-
WHERE existing.user_id = ?
|
|
104
|
-
AND existing.connection_id = ?
|
|
105
|
-
AND existing.period_start = ?
|
|
106
|
-
)
|
|
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
|
|
108
|
-
(user_id, stripe_subscription_id, stripe_subscription_item_id, price_id, quantity, status, last_error_code, checked_at, synced_at, updated_at)
|
|
109
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'), CASE WHEN ? THEN datetime('now') ELSE NULL END, datetime('now'))
|
|
110
|
-
ON CONFLICT(user_id) DO UPDATE SET
|
|
111
|
-
stripe_subscription_id = excluded.stripe_subscription_id,
|
|
112
|
-
stripe_subscription_item_id = excluded.stripe_subscription_item_id,
|
|
113
|
-
price_id = excluded.price_id,
|
|
114
|
-
quantity = excluded.quantity,
|
|
115
|
-
status = excluded.status,
|
|
116
|
-
last_error_code = excluded.last_error_code,
|
|
117
|
-
checked_at = datetime('now'),
|
|
118
|
-
synced_at = CASE WHEN ? THEN datetime('now') ELSE connected_account_billing.synced_at END,
|
|
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:Gt.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=Yt,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
|
|
121
|
-
FROM analytics_identity_namespaces
|
|
122
|
-
WHERE site_id=$1 AND status IN ('draft','approved','disabled')
|
|
123
|
-
ORDER BY CASE status WHEN 'approved' THEN 0 WHEN 'draft' THEN 1 ELSE 2 END,
|
|
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
|
|
125
|
-
FROM analytics_identity_namespaces
|
|
126
|
-
WHERE site_id=$1 AND status IN ('draft','approved','disabled')
|
|
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(
|
|
128
|
-
id, site_id, name, status, approved_hosts, approved_sources, version, approved_by_user_id
|
|
129
|
-
) VALUES ($1,$2,$3,'draft','[]'::jsonb,'[]'::jsonb,1,$4)
|
|
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)
|
|
132
|
-
VALUES ($1,$2,$3,$4,$5,'approved')
|
|
133
|
-
ON CONFLICT(namespace_id, source_kind, source_ref) DO UPDATE
|
|
134
|
-
SET status='approved', revoked_at=NULL
|
|
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
|
|
136
|
-
SET status='approved', approved_by_user_id=$3, approved_at=COALESCE(approved_at, now()),
|
|
137
|
-
approved_hosts=CASE WHEN $4='host'
|
|
138
|
-
THEN (SELECT jsonb_agg(DISTINCT value) FROM jsonb_array_elements_text(approved_hosts || to_jsonb($5::text)) value)
|
|
139
|
-
ELSE approved_hosts END,
|
|
140
|
-
approved_sources=(SELECT jsonb_agg(DISTINCT value) FROM jsonb_array_elements_text(approved_sources || to_jsonb(($4 || ':' || $5)::text)) value),
|
|
141
|
-
updated_at=now()
|
|
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
|
|
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
|
|
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
|
|
146
|
-
FROM analytics_identity_sources
|
|
147
|
-
WHERE namespace_id=$1 AND site_id=$2
|
|
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,
|
|
149
|
-
array_remove(array_agg(DISTINCT e.feature_kind) FILTER (WHERE e.contribution > 0), NULL) supporting,
|
|
150
|
-
array_remove(array_agg(DISTINCT e.feature_kind) FILTER (WHERE e.contribution < 0), NULL) conflicting
|
|
151
|
-
FROM analytics_candidate_associations a
|
|
152
|
-
LEFT JOIN analytics_candidate_evidence e ON e.association_id=a.id AND e.namespace_id=a.namespace_id
|
|
153
|
-
WHERE a.site_id=$1 AND a.namespace_id=$2 AND a.status='active' AND a.confidence_band='medium'
|
|
154
|
-
AND a.expires_at > $3::timestamptz
|
|
155
|
-
GROUP BY a.id
|
|
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
|
|
157
|
-
SET status='promoted', promoted_person_id=$4, promoted_at=$5, updated_at=now()
|
|
158
|
-
WHERE id=$1 AND site_id=$2 AND namespace_id=$3
|
|
159
|
-
AND (status IN ('active','expired') OR (status='promoted' AND promoted_person_id=$4))
|
|
160
|
-
RETURNING id,left_identity_node_id,right_identity_node_id`,[e.associationId,e.siteId,e.namespaceId,e.personId,t.toISOString()]);if(!a.rows[0])throw new f("analytics_candidate_not_found","Candidate association not found in this namespace.",404);return await s.query(`INSERT INTO analytics_candidate_promotions(
|
|
161
|
-
id, site_id, namespace_id, association_id, person_id, evidence_kind,
|
|
162
|
-
evidence_ref_hmac, promoted_by, promoted_by_user_id, promoted_at
|
|
163
|
-
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)
|
|
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(
|
|
165
|
-
id, site_id, namespace_ref, subject_ref, action, idempotency_key,
|
|
166
|
-
actor_user_id, result_code, affected_counts, completed_at
|
|
167
|
-
) VALUES ($1,$2,$3,$4,'candidate_promoted',$5,$6,'promoted',$7::jsonb,$8)
|
|
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(
|
|
169
|
-
id,site_id,namespace_id,canonical_person_id,canonical_person_ref_hmac,
|
|
170
|
-
evidence_kind,evidence_ref_hmac,prior_person_ids,prior_identity_node_ids,
|
|
171
|
-
candidate_association_ids,affected_history_counts,schema_version,idempotency_key,promoted_at
|
|
172
|
-
) VALUES($1,$2,$3,$4,$5,$6,$7,'{}'::uuid[],$8::uuid[],$9::uuid[],$10::jsonb,
|
|
173
|
-
'xray.identity-promotion.v1',$11,$12)
|
|
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(
|
|
175
|
-
site_id,connection_id,provider,contract_version,mode,schema_fingerprint,allowed_operations,evidence,verified_at,updated_at
|
|
176
|
-
) VALUES($1,$2,$3,1,$4,$5,$6::jsonb,$7::jsonb,$8,now())
|
|
177
|
-
ON CONFLICT(connection_id,contract_version) DO UPDATE SET
|
|
178
|
-
mode=EXCLUDED.mode,schema_fingerprint=EXCLUDED.schema_fingerprint,
|
|
179
|
-
allowed_operations=EXCLUDED.allowed_operations,evidence=EXCLUDED.evidence,
|
|
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(
|
|
181
|
-
id,site_id,connection_id,provider,contract_version,tenant_schema_version,plan,status,
|
|
182
|
-
created_by_user_id,public_plan_id,schema_fingerprint,request_fingerprint,created_at,updated_at
|
|
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
|
|
184
|
-
JOIN analytics_crm_provisioning_plans p ON p.id=r.plan_id
|
|
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(
|
|
186
|
-
id,plan_id,operation,status,safe_error_code,warnings,created_at,public_receipt_id,receipt
|
|
187
|
-
) VALUES($1,$2,$3,$4,$5,'[]'::jsonb,$6,$7,$8::jsonb)
|
|
188
|
-
ON CONFLICT (public_receipt_id) WHERE public_receipt_id IS NOT NULL
|
|
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
|
|
190
|
-
SET status=$3,approved_by_user_id=$4,approved_at=COALESCE(approved_at,$5),
|
|
191
|
-
approved_field_ids=$6::jsonb,updated_at=$5
|
|
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
|
|
193
|
-
JOIN analytics_crm_provisioning_plans p ON p.id=r.plan_id
|
|
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(
|
|
195
|
-
sync_id,site_id,connection_id,provider,mode,state,mapping,mapping_version,capability_evidence,idempotency_key,request_fingerprint,last_started_at
|
|
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)
|
|
197
|
-
VALUES($1,'pending',now(),now()) ON CONFLICT(connection_id)
|
|
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()
|
|
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
|
|
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)
|
|
201
|
-
VALUES($1,$2,'pending',$3)
|
|
202
|
-
ON CONFLICT(connection_id) DO UPDATE SET
|
|
203
|
-
status=CASE WHEN analytics_reconciliation_jobs.status='leased' THEN 'leased' ELSE 'pending' END,
|
|
204
|
-
next_attempt_at=CASE WHEN analytics_reconciliation_jobs.status='leased'
|
|
205
|
-
THEN analytics_reconciliation_jobs.next_attempt_at ELSE EXCLUDED.next_attempt_at END,
|
|
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)
|
|
208
|
-
VALUES($1,'inbound','running',now(),now())
|
|
209
|
-
ON CONFLICT(connection_id) DO UPDATE SET
|
|
210
|
-
direction=CASE WHEN analytics_crm_sync_state.direction='outbound' THEN 'bidirectional' ELSE analytics_crm_sync_state.direction END,
|
|
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()
|
|
212
|
-
WHERE sync_id=(SELECT sync_id FROM analytics_crm_sync_runs
|
|
213
|
-
WHERE site_id=$1 AND connection_id::text=$2 AND state NOT IN ('cancelled','webhook_only')
|
|
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(
|
|
215
|
-
connection_id,cursor,direction,status,last_started_at,last_completed_at,last_success_at,next_sync_at,updated_at
|
|
216
|
-
) VALUES($1,$2::jsonb,'inbound','healthy',now(),now(),now(),$3,now())
|
|
217
|
-
ON CONFLICT(connection_id) DO UPDATE SET
|
|
218
|
-
cursor=EXCLUDED.cursor,
|
|
219
|
-
direction=CASE WHEN analytics_crm_sync_state.direction='outbound' THEN 'bidirectional' ELSE analytics_crm_sync_state.direction END,
|
|
220
|
-
status='healthy',last_completed_at=now(),last_success_at=now(),last_error_code=NULL,
|
|
221
|
-
next_sync_at=EXCLUDED.next_sync_at,updated_at=now()`,[e.connectionId,JSON.stringify(e.cursor??null),e.nextRunAt]),await t.query(`UPDATE analytics_crm_sync_runs SET
|
|
222
|
-
state=$3,cursor=$4::jsonb,last_success_at=now(),next_run_at=$5,
|
|
223
|
-
imported=imported+$6,deduplicated=deduplicated+$7,rejected=rejected+$8,
|
|
224
|
-
last_error_code=NULL,updated_at=now()
|
|
225
|
-
WHERE sync_id=(SELECT sync_id FROM analytics_crm_sync_runs
|
|
226
|
-
WHERE site_id=$1 AND connection_id::text=$2 AND state <> 'cancelled'
|
|
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(
|
|
228
|
-
id,site_id,connection_id,direction,operation,subject_ref,external_receipt_id,status,occurred_at
|
|
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)
|
|
230
|
-
VALUES($1,'inbound','degraded',now(),$2,$3,now())
|
|
231
|
-
ON CONFLICT(connection_id) DO UPDATE SET
|
|
232
|
-
direction=CASE WHEN analytics_crm_sync_state.direction='outbound' THEN 'bidirectional' ELSE analytics_crm_sync_state.direction END,
|
|
233
|
-
status='degraded',last_completed_at=now(),last_error_code=EXCLUDED.last_error_code,
|
|
234
|
-
next_sync_at=EXCLUDED.next_sync_at,updated_at=now()`,[e.connectionId,e.safeErrorCode,e.nextRunAt]),await t.query(`UPDATE analytics_crm_sync_runs SET state='degraded',last_error_code=$3,next_run_at=$4,
|
|
235
|
-
rejected=rejected+1,updated_at=now()
|
|
236
|
-
WHERE sync_id=(SELECT sync_id FROM analytics_crm_sync_runs
|
|
237
|
-
WHERE site_id=$1 AND connection_id::text=$2 AND state <> 'cancelled'
|
|
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(
|
|
239
|
-
id,site_id,connection_id,direction,operation,subject_ref,external_receipt_id,status,safe_error_code,occurred_at
|
|
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,
|
|
241
|
-
c.schema_fingerprint current_schema_fingerprint
|
|
242
|
-
FROM analytics_crm_provisioning_receipts r
|
|
243
|
-
JOIN analytics_crm_provisioning_plans p ON p.id=r.plan_id
|
|
244
|
-
LEFT JOIN analytics_crm_connection_capabilities c ON c.connection_id=p.connection_id AND c.contract_version=1
|
|
245
|
-
WHERE p.site_id=$1 AND p.connection_id=$2 AND r.public_receipt_id=$3`,[t.siteId,i,t.provisioningReceiptId])).rows[0];if(!a||a.receipt_status!=="tested"||a.plan_status!=="verified")throw new f("analytics_crm_outbound_test_required","A successful provisioning test is required before outbound automation can be enabled.",409);if(a.plan_schema_fingerprint!==t.schemaFingerprint||a.current_schema_fingerprint!==t.schemaFingerprint)throw new f("analytics_crm_schema_changed","The CRM schema changed after testing. Test the exact schema again before enabling outbound automation.",409)}if(t.kind==="pipeline_event"&&(!t.mapping||t.mapping.version!==t.mappingVersion))throw new f("analytics_crm_mapping_version_mismatch","The pipeline mapping and mapping version must match exactly.",409);let r=await n.query(`INSERT INTO analytics_crm_outbound_policies(
|
|
246
|
-
policy_id,site_id,connection_id,provider,kind,enabled,version,provisioning_receipt_id,
|
|
247
|
-
schema_fingerprint,mapping_version,mapping,allowed_fields,selected_events,purpose,created_by_user_id,updated_at
|
|
248
|
-
) VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11::jsonb,$12::jsonb,$13::jsonb,$14,$15,now())
|
|
249
|
-
ON CONFLICT(policy_id) DO UPDATE SET
|
|
250
|
-
enabled=EXCLUDED.enabled,provisioning_receipt_id=EXCLUDED.provisioning_receipt_id,
|
|
251
|
-
schema_fingerprint=EXCLUDED.schema_fingerprint,mapping_version=EXCLUDED.mapping_version,
|
|
252
|
-
mapping=EXCLUDED.mapping,allowed_fields=EXCLUDED.allowed_fields,selected_events=EXCLUDED.selected_events,
|
|
253
|
-
purpose=EXCLUDED.purpose,updated_at=now()
|
|
254
|
-
WHERE analytics_crm_outbound_policies.site_id=EXCLUDED.site_id
|
|
255
|
-
AND analytics_crm_outbound_policies.connection_id=EXCLUDED.connection_id
|
|
256
|
-
AND analytics_crm_outbound_policies.kind=EXCLUDED.kind
|
|
257
|
-
AND analytics_crm_outbound_policies.version=EXCLUDED.version
|
|
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
|
|
259
|
-
WHERE p.id=$1 AND p.site_id=$2 AND
|
|
260
|
-
(p.crm_person_ref IS NOT NULL OR EXISTS (
|
|
261
|
-
SELECT 1 FROM analytics_identity_edges e JOIN analytics_identity_nodes n ON n.id=e.identity_node_id
|
|
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(
|
|
263
|
-
job_id,policy_id,site_id,person_id,kind,policy_version,mapping_version,source_event_id,
|
|
264
|
-
payload_ciphertext,idempotency_key,request_fingerprint,next_attempt_at
|
|
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 (
|
|
266
|
-
SELECT j.job_id FROM analytics_crm_outbound_jobs j
|
|
267
|
-
JOIN analytics_crm_outbound_policies p ON p.policy_id=j.policy_id
|
|
268
|
-
WHERE p.enabled=true AND p.provider NOT IN ('salesforce','pipedrive')
|
|
269
|
-
AND ((j.state IN ('pending','retrying') AND j.next_attempt_at <= now()) OR
|
|
270
|
-
(j.state='running' AND j.lease_expires_at < now()))
|
|
271
|
-
ORDER BY coalesce(j.next_attempt_at,j.lease_expires_at),j.created_at FOR UPDATE OF j SKIP LOCKED LIMIT $1
|
|
272
|
-
)
|
|
273
|
-
UPDATE analytics_crm_outbound_jobs j SET state='running',attempts=j.attempts+1,
|
|
274
|
-
lease_expires_at=now()+($2::text || ' seconds')::interval,updated_at=now()
|
|
275
|
-
FROM due,analytics_crm_outbound_policies p,analytics_sites s
|
|
276
|
-
WHERE j.job_id=due.job_id AND p.policy_id=j.policy_id AND s.id=j.site_id
|
|
277
|
-
RETURNING j.*,p.provider,p.connection_id::text,p.mapping,p.allowed_fields,p.purpose,s.owner_user_id,
|
|
278
|
-
EXISTS(SELECT 1 FROM analytics_crm_connection_capabilities c
|
|
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(
|
|
281
|
-
SELECT 1
|
|
282
|
-
FROM analytics_crm_outbound_jobs j
|
|
283
|
-
JOIN analytics_crm_outbound_policies p ON p.policy_id=j.policy_id
|
|
284
|
-
JOIN analytics_people person ON person.id=j.person_id AND person.site_id=j.site_id
|
|
285
|
-
WHERE j.job_id=$1 AND j.state='running' AND j.attempts=$2
|
|
286
|
-
AND j.lease_expires_at>now() AND j.payload_ciphertext<>''
|
|
287
|
-
AND p.enabled=true AND p.version=j.policy_version AND p.purpose=$3
|
|
288
|
-
AND EXISTS (
|
|
289
|
-
SELECT 1 FROM analytics_purpose_policies purpose_policy
|
|
290
|
-
WHERE purpose_policy.site_id=j.site_id
|
|
291
|
-
AND purpose_policy.purpose=CASE $3
|
|
292
|
-
WHEN 'necessary_form_fulfillment' THEN 'necessary'
|
|
293
|
-
WHEN 'site_analytics' THEN 'site_analytics'
|
|
294
|
-
ELSE 'advertising_measurement' END
|
|
295
|
-
AND purpose_policy.enabled=true AND purpose_policy.retired_at IS NULL
|
|
296
|
-
)
|
|
297
|
-
AND NOT EXISTS (
|
|
298
|
-
SELECT 1 FROM analytics_identity_tombstones tombstone
|
|
299
|
-
WHERE tombstone.site_id=j.site_id AND tombstone.person_id=j.person_id
|
|
300
|
-
)
|
|
301
|
-
AND ($3='necessary_form_fulfillment' OR EXISTS (
|
|
302
|
-
SELECT 1
|
|
303
|
-
FROM analytics_consent_current_state consent
|
|
304
|
-
JOIN analytics_consent_receipts receipt
|
|
305
|
-
ON receipt.id=consent.latest_receipt_id AND receipt.site_id=consent.site_id
|
|
306
|
-
WHERE consent.site_id=j.site_id
|
|
307
|
-
AND consent.latest_receipt_id::text=$4
|
|
308
|
-
AND consent.gpc=false
|
|
309
|
-
AND receipt.action NOT IN ('withdraw','reject_all','gpc')
|
|
310
|
-
AND (consent.expires_at IS NULL OR consent.expires_at>now())
|
|
311
|
-
AND EXISTS (
|
|
312
|
-
SELECT 1 FROM analytics_purpose_policies current_policy
|
|
313
|
-
WHERE current_policy.site_id=consent.site_id
|
|
314
|
-
AND current_policy.purpose=CASE $3
|
|
315
|
-
WHEN 'site_analytics' THEN 'site_analytics'
|
|
316
|
-
ELSE 'advertising_measurement' END
|
|
317
|
-
AND current_policy.jurisdiction_profile=consent.jurisdiction_profile
|
|
318
|
-
AND current_policy.enabled=true AND current_policy.retired_at IS NULL
|
|
319
|
-
)
|
|
320
|
-
AND (($3='site_analytics' AND consent.site_analytics=true) OR
|
|
321
|
-
($3='advertising_measurement' AND consent.advertising_measurement=true))
|
|
322
|
-
))
|
|
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
|
|
324
|
-
SET state='cancelled',payload_ciphertext='',next_attempt_at=NULL,
|
|
325
|
-
lease_expires_at=NULL,cancelled_at=now(),safe_error_code=$3,updated_at=now()
|
|
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
|
|
327
|
-
SET status='cancelled',safe_error_code=$3,completed_at=now()
|
|
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,
|
|
329
|
-
lease_expires_at=NULL,delivered_at=CASE WHEN $2='delivered' THEN now() ELSE delivered_at END,updated_at=now()
|
|
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)
|
|
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
|
|
333
|
-
JOIN analytics_crm_outbound_jobs j ON j.job_id=r.job_id
|
|
334
|
-
JOIN analytics_crm_outbound_policies p ON p.policy_id=j.policy_id
|
|
335
|
-
WHERE j.site_id=$1 AND p.provider NOT IN ('salesforce','pipedrive')
|
|
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,
|
|
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,
|
|
399
|
-
((SELECT count(*) FROM analytics_events e WHERE e.site_id=p.site_id AND e.person_id=p.id)+
|
|
400
|
-
(SELECT count(*) FROM analytics_external_events x WHERE x.site_id=p.site_id AND x.person_id=p.id))::text event_count
|
|
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 tn(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(),
|
|
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()
|
|
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 */
|
|
405
|
-
WITH ranked AS (
|
|
406
|
-
SELECT s.*,
|
|
407
|
-
row_number() OVER (PARTITION BY s.visitor_hmac ORDER BY s.started_at, s.id)::int AS session_number
|
|
408
|
-
FROM analytics_sessions s
|
|
409
|
-
WHERE s.site_id = $1
|
|
410
|
-
), facts AS (
|
|
411
|
-
SELECT DISTINCT ON (e.session_hmac)
|
|
412
|
-
e.session_hmac, e.country_code, e.region_code, e.device_class,
|
|
413
|
-
e.source, e.medium, e.campaign
|
|
414
|
-
FROM analytics_events e
|
|
415
|
-
WHERE e.site_id = $1
|
|
416
|
-
ORDER BY e.session_hmac, e.occurred_at
|
|
417
|
-
), conversions AS (
|
|
418
|
-
SELECT c.session_hmac, count(*)::int AS conversion_count,
|
|
419
|
-
sum(c.value_minor)::bigint AS revenue_minor,
|
|
420
|
-
CASE WHEN count(DISTINCT c.currency) = 1 THEN min(c.currency) END AS currency
|
|
421
|
-
FROM analytics_conversions c WHERE c.site_id = $1 GROUP BY c.session_hmac
|
|
422
|
-
)
|
|
423
|
-
SELECT r.id AS internal_id, r.visitor_hmac, r.person_id, r.started_at, r.last_active_at,
|
|
424
|
-
r.ended_at, r.duration_ms, r.landing_path, r.exit_path, r.pageview_count,
|
|
425
|
-
r.event_count, r.session_number, f.country_code, f.region_code, f.device_class,
|
|
426
|
-
f.source, f.medium, f.campaign, COALESCE(c.conversion_count, 0)::int AS conversion_count,
|
|
427
|
-
c.revenue_minor, c.currency
|
|
428
|
-
FROM ranked r
|
|
429
|
-
LEFT JOIN facts f ON f.session_hmac = r.session_hmac
|
|
430
|
-
LEFT JOIN conversions c ON c.session_hmac = r.session_hmac
|
|
431
|
-
WHERE ($2::timestamptz IS NULL OR (r.started_at, r.id) < ($2::timestamptz, $3::uuid))
|
|
432
|
-
AND ($4::boolean IS NULL OR (r.session_number > 1) = $4)
|
|
433
|
-
AND ($5::boolean IS NULL OR (COALESCE(c.conversion_count, 0) > 0) = $5)
|
|
434
|
-
AND ($6::bigint IS NULL OR r.duration_ms >= $6)
|
|
435
|
-
AND ($7::bigint IS NULL OR r.duration_ms <= $7)
|
|
436
|
-
AND ($8::text IS NULL OR EXISTS (
|
|
437
|
-
SELECT 1 FROM analytics_events ee
|
|
438
|
-
WHERE ee.site_id = $1 AND ee.session_hmac = r.session_hmac AND ee.event_name = $8
|
|
439
|
-
))
|
|
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 */
|
|
441
|
-
WITH visitor_sessions AS (
|
|
442
|
-
SELECT COALESCE(s.person_id::text, s.visitor_hmac) AS identity_key, s.person_id,
|
|
443
|
-
min(s.started_at) AS first_seen_at, max(s.last_active_at) AS last_seen_at,
|
|
444
|
-
count(*)::int AS session_count, sum(s.duration_ms)::bigint AS duration_ms,
|
|
445
|
-
sum(s.pageview_count)::int AS pageview_count, sum(s.event_count)::int AS event_count,
|
|
446
|
-
(array_agg(s.id ORDER BY s.last_active_at DESC, s.id DESC))[1] AS last_session_id
|
|
447
|
-
FROM analytics_sessions s WHERE s.site_id = $1
|
|
448
|
-
GROUP BY COALESCE(s.person_id::text, s.visitor_hmac), s.person_id
|
|
449
|
-
), conversions AS (
|
|
450
|
-
SELECT COALESCE(c.person_id::text, s.visitor_hmac) AS identity_key,
|
|
451
|
-
count(*)::int AS conversion_count, sum(c.value_minor)::bigint AS revenue_minor,
|
|
452
|
-
CASE WHEN count(DISTINCT c.currency) = 1 THEN min(c.currency) END AS currency
|
|
453
|
-
FROM analytics_conversions c
|
|
454
|
-
LEFT JOIN analytics_sessions s ON s.site_id=c.site_id AND s.session_hmac=c.session_hmac
|
|
455
|
-
WHERE c.site_id=$1 GROUP BY COALESCE(c.person_id::text, s.visitor_hmac)
|
|
456
|
-
)
|
|
457
|
-
SELECT v.*, COALESCE(c.conversion_count,0)::int AS conversion_count, c.revenue_minor, c.currency
|
|
458
|
-
FROM visitor_sessions v LEFT JOIN conversions c USING(identity_key)
|
|
459
|
-
WHERE ($2::timestamptz IS NULL OR (v.last_seen_at, v.last_session_id) < ($2::timestamptz, $3::uuid))
|
|
460
|
-
AND ($4::boolean IS NULL OR (v.session_count > 1) = $4)
|
|
461
|
-
AND ($5::boolean IS NULL OR (COALESCE(c.conversion_count,0) > 0) = $5)
|
|
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 */
|
|
463
|
-
SELECT 1 WHERE
|
|
464
|
-
($2::uuid IS NOT NULL AND EXISTS (SELECT 1 FROM analytics_people p WHERE p.site_id=$1 AND p.id=$2))
|
|
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))
|
|
466
|
-
OR ($4::uuid IS NOT NULL AND EXISTS (SELECT 1 FROM analytics_sessions s WHERE s.site_id=$1 AND s.id=$4))
|
|
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=`
|
|
468
|
-
SELECT s.id, s.session_hmac, s.visitor_hmac, s.person_id, s.started_at,
|
|
469
|
-
s.last_active_at, s.ended_at, s.duration_ms, s.landing_path, s.exit_path
|
|
470
|
-
FROM analytics_sessions s
|
|
471
|
-
WHERE s.site_id=$1
|
|
472
|
-
AND ($2::uuid IS NULL OR s.person_id=$2)
|
|
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))
|
|
474
|
-
AND ($4::uuid IS NULL OR s.id=$4)`,[l,_,d,p,E,b]=await Promise.all([t.query(`/* analyticsSessionTimeline:keyset */
|
|
475
|
-
WITH subject_sessions AS (
|
|
476
|
-
${c}
|
|
477
|
-
), entries AS (
|
|
478
|
-
SELECT s.started_at AS occurred_at, s.id::text||':start' AS stable_id,
|
|
479
|
-
'session' AS entry_type, 'session_started' AS name, s.landing_path AS path,
|
|
480
|
-
NULL::text AS referrer, NULL::text AS source, NULL::text AS medium,
|
|
481
|
-
NULL::text AS campaign, NULL::bigint AS value_minor, NULL::text AS currency,
|
|
482
|
-
'deterministic'::text AS evidence_class, 'persisted_session_bounds'::text AS provenance
|
|
483
|
-
FROM subject_sessions s
|
|
484
|
-
UNION ALL
|
|
485
|
-
SELECT COALESCE(s.ended_at,s.last_active_at), s.id::text||':end', 'session', 'session_ended',
|
|
486
|
-
s.exit_path, NULL, NULL, NULL, NULL, NULL, NULL, 'deterministic', 'persisted_session_bounds'
|
|
487
|
-
FROM analytics_sessions s JOIN subject_sessions ss ON ss.id=s.id
|
|
488
|
-
UNION ALL
|
|
489
|
-
SELECT e.occurred_at, e.id::text AS stable_id,
|
|
490
|
-
CASE WHEN e.event_name='page_view' THEN 'page' ELSE 'event' END AS entry_type,
|
|
491
|
-
e.event_name AS name, e.path, e.referrer, e.source, e.medium, e.campaign,
|
|
492
|
-
NULL::bigint AS value_minor, NULL::text AS currency,
|
|
493
|
-
'deterministic'::text AS evidence_class, 'pixel_event'::text AS provenance
|
|
494
|
-
FROM analytics_events e JOIN subject_sessions s ON s.session_hmac=e.session_hmac AND e.site_id=$1
|
|
495
|
-
UNION ALL
|
|
496
|
-
SELECT t.occurred_at, t.id::text, 'touch', COALESCE(t.touch_kind,'touch'), t.landing_path,
|
|
497
|
-
t.referrer, t.source, t.medium, t.campaign, NULL, NULL, 'deterministic', 'attribution_touch'
|
|
498
|
-
FROM analytics_attribution_touches t JOIN subject_sessions s ON s.id=t.session_id AND t.site_id=$1
|
|
499
|
-
UNION ALL
|
|
500
|
-
SELECT x.occurred_at, x.id::text, x.event_kind, x.event_name, NULL, NULL, x.source, NULL, NULL,
|
|
501
|
-
x.value_minor, x.currency, 'deterministic', 'verified_external_event'
|
|
502
|
-
FROM analytics_external_events x
|
|
503
|
-
WHERE x.site_id=$1 AND (x.person_id=$2 OR x.session_hmac IN (SELECT session_hmac FROM subject_sessions))
|
|
504
|
-
UNION ALL
|
|
505
|
-
SELECT c.occurred_at, c.id::text, 'conversion', c.conversion_kind, NULL, NULL, NULL, NULL, NULL,
|
|
506
|
-
c.value_minor, c.currency, 'deterministic', COALESCE(c.value_source,'conversion')
|
|
507
|
-
FROM analytics_conversions c
|
|
508
|
-
WHERE c.site_id=$1 AND (c.person_id=$2 OR c.session_hmac IN (SELECT session_hmac FROM subject_sessions))
|
|
509
|
-
UNION ALL
|
|
510
|
-
SELECT e.occurred_at, ve.id::text, 'value', e.event_name, e.path, NULL, e.source, e.medium,
|
|
511
|
-
e.campaign, ve.value_minor, ve.currency, 'deterministic', ve.value_source
|
|
512
|
-
FROM analytics_event_value_evaluations ve
|
|
513
|
-
JOIN analytics_events e ON e.id=ve.source_event_id AND e.site_id=ve.site_id
|
|
514
|
-
JOIN subject_sessions s ON s.session_hmac=e.session_hmac
|
|
515
|
-
WHERE ve.site_id=$1
|
|
516
|
-
UNION ALL
|
|
517
|
-
SELECT se.evaluated_at, se.id::text, 'score', sd.name, NULL, NULL, NULL, NULL, NULL,
|
|
518
|
-
se.score::bigint, NULL, 'deterministic', 'lead_score:'||sd.score_kind
|
|
519
|
-
FROM analytics_score_evaluations se JOIN analytics_score_definitions sd ON sd.id=se.definition_id
|
|
520
|
-
WHERE se.site_id=$1 AND se.person_id=$2
|
|
521
|
-
UNION ALL
|
|
522
|
-
SELECT j.created_at, j.id::text, 'receipt', j.status, NULL, NULL, d.platform, NULL, NULL,
|
|
523
|
-
NULL, NULL, 'deterministic', COALESCE(j.response_category,'delivery_receipt')
|
|
524
|
-
FROM analytics_activation_jobs j JOIN analytics_activation_destinations d ON d.id=j.destination_id
|
|
525
|
-
WHERE d.site_id=$1 AND j.person_id=$2
|
|
526
|
-
UNION ALL
|
|
527
|
-
SELECT ca.promoted_at, ca.id::text, 'identity_transition', 'candidate_promoted_to_confirmed', NULL, NULL,
|
|
528
|
-
NULL, NULL, NULL, NULL, NULL, 'deterministic', 'later_deterministic_evidence'
|
|
529
|
-
FROM analytics_candidate_associations ca
|
|
530
|
-
WHERE ca.site_id=$1 AND ca.promoted_person_id=$2 AND ca.status='promoted'
|
|
531
|
-
)
|
|
532
|
-
SELECT * FROM entries
|
|
533
|
-
WHERE occurred_at IS NOT NULL
|
|
534
|
-
AND occurred_at >= $5::timestamptz AND occurred_at < $6::timestamptz
|
|
535
|
-
AND ($7::timestamptz IS NULL OR (occurred_at, stable_id) > ($7::timestamptz, $8::text))
|
|
536
|
-
ORDER BY occurred_at, stable_id LIMIT $9`,[...o,i?.at??null,i?.id??null,n+1]),t.query(`/* analyticsEnhancedJourney:summary */
|
|
537
|
-
WITH subject_sessions AS (${c}), ranged_sessions AS (
|
|
538
|
-
SELECT * FROM subject_sessions WHERE started_at >= $5::timestamptz AND started_at < $6::timestamptz
|
|
539
|
-
), ranged_events AS (
|
|
540
|
-
SELECT e.* FROM analytics_events e JOIN subject_sessions s ON s.session_hmac=e.session_hmac
|
|
541
|
-
WHERE e.site_id=$1 AND e.occurred_at >= $5::timestamptz AND e.occurred_at < $6::timestamptz
|
|
542
|
-
), ranged_conversions AS (
|
|
543
|
-
SELECT c.* FROM analytics_conversions c
|
|
544
|
-
WHERE c.site_id=$1 AND c.occurred_at >= $5::timestamptz AND c.occurred_at < $6::timestamptz
|
|
545
|
-
AND (c.person_id=$2 OR c.session_hmac IN (SELECT session_hmac FROM subject_sessions))
|
|
546
|
-
)
|
|
547
|
-
SELECT (SELECT (array_agg(person_id ORDER BY started_at) FILTER (WHERE person_id IS NOT NULL))[1]::text FROM subject_sessions) AS resolved_person_id,
|
|
548
|
-
(SELECT count(*)::int FROM ranged_sessions) AS sessions,
|
|
549
|
-
(SELECT count(*)::int FROM ranged_events WHERE event_name='page_view') AS pageviews,
|
|
550
|
-
(SELECT count(*)::int FROM ranged_events) AS events,
|
|
551
|
-
(SELECT count(*)::int FROM ranged_conversions) AS conversions,
|
|
552
|
-
(SELECT sum(value_minor)::bigint FROM ranged_conversions) AS revenue_minor,
|
|
553
|
-
(SELECT CASE WHEN count(DISTINCT currency)=1 THEN min(currency) END FROM ranged_conversions WHERE value_minor IS NOT NULL) AS currency,
|
|
554
|
-
(SELECT sum(engaged_ms)::bigint FROM ranged_events WHERE event_name='page_engagement' AND engaged_ms IS NOT NULL) AS total_engagement_ms,
|
|
555
|
-
(SELECT round(avg(engaged_ms))::bigint FROM ranged_events WHERE event_name='page_engagement' AND engaged_ms IS NOT NULL) AS average_engagement_ms`,[...o]),t.query(`/* analyticsEnhancedJourney:pages */
|
|
556
|
-
WITH subject_sessions AS (${c}), pageviews AS (
|
|
557
|
-
SELECT e.path, e.session_id, count(*)::int AS visits
|
|
558
|
-
FROM analytics_events e JOIN subject_sessions s ON s.session_hmac=e.session_hmac
|
|
559
|
-
WHERE e.site_id=$1 AND e.event_name='page_view' AND e.path IS NOT NULL
|
|
560
|
-
AND e.occurred_at >= $5::timestamptz AND e.occurred_at < $6::timestamptz
|
|
561
|
-
GROUP BY e.path,e.session_id
|
|
562
|
-
), engagement AS (
|
|
563
|
-
SELECT e.path, sum(e.engaged_ms)::bigint AS engaged_ms
|
|
564
|
-
FROM analytics_events e JOIN subject_sessions s ON s.session_hmac=e.session_hmac
|
|
565
|
-
WHERE e.site_id=$1 AND e.event_name='page_engagement' AND e.path IS NOT NULL AND e.engaged_ms IS NOT NULL
|
|
566
|
-
AND e.occurred_at >= $5::timestamptz AND e.occurred_at < $6::timestamptz GROUP BY e.path
|
|
567
|
-
), conversion_pages AS (
|
|
568
|
-
SELECT latest.path, count(*)::int AS conversions FROM analytics_conversions c
|
|
569
|
-
JOIN LATERAL (
|
|
570
|
-
SELECT e.path FROM analytics_events e
|
|
571
|
-
WHERE e.site_id=$1 AND e.session_id=c.session_id AND e.event_name='page_view'
|
|
572
|
-
AND e.occurred_at <= c.occurred_at ORDER BY e.occurred_at DESC,e.id DESC LIMIT 1
|
|
573
|
-
) latest ON true
|
|
574
|
-
WHERE c.site_id=$1 AND c.occurred_at >= $5::timestamptz AND c.occurred_at < $6::timestamptz
|
|
575
|
-
AND (c.person_id=$2 OR c.session_hmac IN (SELECT session_hmac FROM subject_sessions))
|
|
576
|
-
GROUP BY latest.path
|
|
577
|
-
)
|
|
578
|
-
SELECT p.path,sum(p.visits)::int AS visits,count(*)::int AS unique_sessions,e.engaged_ms,
|
|
579
|
-
COALESCE(c.conversions,0)::int AS conversions
|
|
580
|
-
FROM pageviews p LEFT JOIN engagement e USING(path) LEFT JOIN conversion_pages c USING(path)
|
|
581
|
-
GROUP BY p.path,e.engaged_ms,c.conversions ORDER BY visits DESC,p.path LIMIT 20`,[...o]),t.query(`/* analyticsEnhancedJourney:acquisition */
|
|
582
|
-
WITH subject_sessions AS (${c}), first_touch AS (
|
|
583
|
-
SELECT DISTINCT ON (e.session_hmac) e.session_hmac,e.source,e.medium,e.campaign,e.referrer
|
|
584
|
-
FROM analytics_events e JOIN subject_sessions s ON s.session_hmac=e.session_hmac
|
|
585
|
-
WHERE e.site_id=$1 AND e.occurred_at >= $5::timestamptz AND e.occurred_at < $6::timestamptz
|
|
586
|
-
ORDER BY e.session_hmac,e.occurred_at,e.id
|
|
587
|
-
), event_counts AS (
|
|
588
|
-
SELECT e.session_hmac,count(*)::int AS events FROM analytics_events e JOIN subject_sessions s ON s.session_hmac=e.session_hmac
|
|
589
|
-
WHERE e.site_id=$1 AND e.occurred_at >= $5::timestamptz AND e.occurred_at < $6::timestamptz GROUP BY e.session_hmac
|
|
590
|
-
), conversion_counts AS (
|
|
591
|
-
SELECT c.session_hmac,count(*)::int AS conversions FROM analytics_conversions c
|
|
592
|
-
WHERE c.site_id=$1 AND c.occurred_at >= $5::timestamptz AND c.occurred_at < $6::timestamptz
|
|
593
|
-
AND (c.person_id=$2 OR c.session_hmac IN (SELECT session_hmac FROM subject_sessions)) GROUP BY c.session_hmac
|
|
594
|
-
)
|
|
595
|
-
SELECT f.source,f.medium,f.campaign,f.referrer,count(*)::int AS sessions,
|
|
596
|
-
sum(COALESCE(e.events,0))::int AS events,sum(COALESCE(c.conversions,0))::int AS conversions
|
|
597
|
-
FROM first_touch f LEFT JOIN event_counts e USING(session_hmac) LEFT JOIN conversion_counts c USING(session_hmac)
|
|
598
|
-
GROUP BY f.source,f.medium,f.campaign,f.referrer ORDER BY sessions DESC,f.source NULLS LAST LIMIT 20`,[...o]),t.query(`/* analyticsEnhancedJourney:events */
|
|
599
|
-
WITH subject_sessions AS (${c}), event_groups AS (
|
|
600
|
-
SELECT e.event_name AS name,count(*)::int AS count,count(DISTINCT e.session_id)::int AS unique_sessions,
|
|
601
|
-
array_agg(DISTINCT e.session_id) AS session_ids
|
|
602
|
-
FROM analytics_events e JOIN subject_sessions s ON s.session_hmac=e.session_hmac
|
|
603
|
-
WHERE e.site_id=$1 AND e.occurred_at >= $5::timestamptz AND e.occurred_at < $6::timestamptz
|
|
604
|
-
GROUP BY e.event_name
|
|
605
|
-
)
|
|
606
|
-
SELECT g.name,g.count,g.unique_sessions,
|
|
607
|
-
(SELECT count(*)::int FROM analytics_conversions c WHERE c.site_id=$1
|
|
608
|
-
AND c.occurred_at >= $5::timestamptz AND c.occurred_at < $6::timestamptz
|
|
609
|
-
AND c.session_id=ANY(g.session_ids)) AS conversions
|
|
610
|
-
FROM event_groups g ORDER BY g.count DESC,g.name LIMIT 50`,[...o]),t.query(`/* analyticsEnhancedJourney:sessions */
|
|
611
|
-
WITH subject_sessions AS (${c}), ranged_sessions AS (
|
|
612
|
-
SELECT * FROM subject_sessions WHERE started_at >= $5::timestamptz AND started_at < $6::timestamptz
|
|
613
|
-
), event_stats AS (
|
|
614
|
-
SELECT e.session_hmac,count(*) FILTER (WHERE e.event_name='page_view')::int AS pageviews,count(*)::int AS events
|
|
615
|
-
FROM analytics_events e JOIN ranged_sessions s ON s.session_hmac=e.session_hmac
|
|
616
|
-
WHERE e.site_id=$1 AND e.occurred_at >= $5::timestamptz AND e.occurred_at < $6::timestamptz GROUP BY e.session_hmac
|
|
617
|
-
), first_touch AS (
|
|
618
|
-
SELECT DISTINCT ON (e.session_hmac) e.session_hmac,e.source,e.medium,e.campaign,e.referrer
|
|
619
|
-
FROM analytics_events e JOIN ranged_sessions s ON s.session_hmac=e.session_hmac
|
|
620
|
-
WHERE e.site_id=$1 ORDER BY e.session_hmac,e.occurred_at,e.id
|
|
621
|
-
), conversion_stats AS (
|
|
622
|
-
SELECT c.session_hmac,count(*)::int AS conversions,sum(c.value_minor)::bigint AS revenue_minor,
|
|
623
|
-
CASE WHEN count(DISTINCT c.currency)=1 THEN min(c.currency) END AS currency
|
|
624
|
-
FROM analytics_conversions c JOIN ranged_sessions s ON s.session_hmac=c.session_hmac
|
|
625
|
-
WHERE c.site_id=$1 AND c.occurred_at >= $5::timestamptz AND c.occurred_at < $6::timestamptz GROUP BY c.session_hmac
|
|
626
|
-
)
|
|
627
|
-
SELECT s.id AS internal_id,s.started_at,s.ended_at,s.duration_ms,s.landing_path,s.exit_path,
|
|
628
|
-
COALESCE(e.pageviews,0)::int AS pageviews,COALESCE(e.events,0)::int AS events,
|
|
629
|
-
COALESCE(c.conversions,0)::int AS conversions,c.revenue_minor,c.currency,
|
|
630
|
-
f.source,f.medium,f.campaign,f.referrer
|
|
631
|
-
FROM ranged_sessions s LEFT JOIN event_stats e ON e.session_hmac=s.session_hmac
|
|
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
|
|
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 */
|
|
634
|
-
SELECT p.id AS person_id, p.namespace_id, sessions.first_seen_at,
|
|
635
|
-
sessions.last_seen_at, COALESCE(sessions.session_count,0)::int AS session_count,
|
|
636
|
-
COALESCE(events.event_count,0)::int AS event_count,
|
|
637
|
-
COALESCE(conversions.conversion_count,0)::int AS conversion_count,
|
|
638
|
-
conversions.revenue_minor, conversions.currency
|
|
639
|
-
FROM analytics_people p
|
|
640
|
-
JOIN analytics_identity_namespaces n ON n.id=p.namespace_id AND n.site_id=p.site_id
|
|
641
|
-
LEFT JOIN LATERAL (
|
|
642
|
-
SELECT min(s.started_at) AS first_seen_at, max(s.last_active_at) AS last_seen_at,
|
|
643
|
-
count(*)::int AS session_count
|
|
644
|
-
FROM analytics_sessions s WHERE s.site_id=p.site_id AND s.person_id=p.id
|
|
645
|
-
) sessions ON true
|
|
646
|
-
LEFT JOIN LATERAL (
|
|
647
|
-
SELECT count(*)::int AS event_count
|
|
648
|
-
FROM analytics_events e WHERE e.site_id=p.site_id AND e.person_id=p.id
|
|
649
|
-
) events ON true
|
|
650
|
-
LEFT JOIN LATERAL (
|
|
651
|
-
SELECT count(*)::int AS conversion_count, sum(c.value_minor)::bigint AS revenue_minor,
|
|
652
|
-
CASE WHEN count(DISTINCT c.currency)=1 THEN min(c.currency) END AS currency
|
|
653
|
-
FROM analytics_conversions c WHERE c.site_id=p.site_id AND c.person_id=p.id
|
|
654
|
-
) conversions ON true
|
|
655
|
-
WHERE p.site_id=$1 AND n.status='approved'
|
|
656
|
-
AND ($2::timestamptz IS NULL OR (sessions.last_seen_at,p.id) < ($2::timestamptz,$3::uuid))
|
|
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 */
|
|
658
|
-
SELECT ca.id AS association_id, ca.namespace_id, ca.model_version, ca.expires_at, ca.updated_at,
|
|
659
|
-
ca.left_identity_node_id AS left_node_id, ca.right_identity_node_id AS right_node_id,
|
|
660
|
-
COALESCE(stats.confirmed_sessions,0)::int AS confirmed_sessions,
|
|
661
|
-
COALESCE(stats.inferred_sessions,0)::int AS inferred_sessions,
|
|
662
|
-
COALESCE(stats.confirmed_events,0)::int AS confirmed_events,
|
|
663
|
-
COALESCE(stats.inferred_events,0)::int AS inferred_events,
|
|
664
|
-
COALESCE(stats.confirmed_conversions,0)::int AS confirmed_conversions,
|
|
665
|
-
COALESCE(stats.inferred_conversions,0)::int AS inferred_conversions,
|
|
666
|
-
stats.confirmed_revenue_minor, stats.inferred_revenue_minor, stats.currency,
|
|
667
|
-
COALESCE(ev.feature_kinds, ARRAY[]::text[]) AS feature_kinds
|
|
668
|
-
FROM analytics_candidate_associations ca
|
|
669
|
-
JOIN analytics_identity_namespaces n ON n.id=ca.namespace_id AND n.site_id=ca.site_id
|
|
670
|
-
LEFT JOIN LATERAL (
|
|
671
|
-
SELECT array_agg(DISTINCT ce.feature_kind ORDER BY ce.feature_kind) AS feature_kinds
|
|
672
|
-
FROM analytics_candidate_evidence ce
|
|
673
|
-
WHERE ce.association_id=ca.id AND ce.namespace_id=ca.namespace_id AND ce.expires_at > now()
|
|
674
|
-
) ev ON true
|
|
675
|
-
LEFT JOIN LATERAL (
|
|
676
|
-
SELECT
|
|
677
|
-
sum(node_stats.session_count) FILTER (WHERE node_stats.node_id=ca.left_identity_node_id)::int AS confirmed_sessions,
|
|
678
|
-
sum(node_stats.session_count) FILTER (WHERE node_stats.node_id=ca.right_identity_node_id)::int AS inferred_sessions,
|
|
679
|
-
sum(node_stats.event_count) FILTER (WHERE node_stats.node_id=ca.left_identity_node_id)::int AS confirmed_events,
|
|
680
|
-
sum(node_stats.event_count) FILTER (WHERE node_stats.node_id=ca.right_identity_node_id)::int AS inferred_events,
|
|
681
|
-
sum(node_stats.conversion_count) FILTER (WHERE node_stats.node_id=ca.left_identity_node_id)::int AS confirmed_conversions,
|
|
682
|
-
sum(node_stats.conversion_count) FILTER (WHERE node_stats.node_id=ca.right_identity_node_id)::int AS inferred_conversions,
|
|
683
|
-
sum(node_stats.revenue_minor) FILTER (WHERE node_stats.node_id=ca.left_identity_node_id)::bigint AS confirmed_revenue_minor,
|
|
684
|
-
sum(node_stats.revenue_minor) FILTER (WHERE node_stats.node_id=ca.right_identity_node_id)::bigint AS inferred_revenue_minor,
|
|
685
|
-
CASE WHEN count(DISTINCT node_stats.currency)=1 THEN min(node_stats.currency) END AS currency
|
|
686
|
-
FROM (
|
|
687
|
-
SELECT inode.id AS node_id,
|
|
688
|
-
(SELECT count(*) FROM analytics_sessions s
|
|
689
|
-
WHERE s.site_id=ca.site_id AND s.visitor_hmac=inode.value_hmac)::int AS session_count,
|
|
690
|
-
(SELECT count(*) FROM analytics_events e JOIN analytics_sessions s
|
|
691
|
-
ON s.site_id=e.site_id AND s.session_hmac=e.session_hmac
|
|
692
|
-
WHERE e.site_id=ca.site_id AND s.visitor_hmac=inode.value_hmac)::int AS event_count,
|
|
693
|
-
(SELECT count(*) FROM analytics_conversions c JOIN analytics_sessions s
|
|
694
|
-
ON s.site_id=c.site_id AND s.session_hmac=c.session_hmac
|
|
695
|
-
WHERE c.site_id=ca.site_id AND s.visitor_hmac=inode.value_hmac)::int AS conversion_count,
|
|
696
|
-
(SELECT sum(c.value_minor) FROM analytics_conversions c JOIN analytics_sessions s
|
|
697
|
-
ON s.site_id=c.site_id AND s.session_hmac=c.session_hmac
|
|
698
|
-
WHERE c.site_id=ca.site_id AND s.visitor_hmac=inode.value_hmac)::bigint AS revenue_minor,
|
|
699
|
-
(SELECT CASE WHEN count(DISTINCT c.currency)=1 THEN min(c.currency) END
|
|
700
|
-
FROM analytics_conversions c JOIN analytics_sessions s
|
|
701
|
-
ON s.site_id=c.site_id AND s.session_hmac=c.session_hmac
|
|
702
|
-
WHERE c.site_id=ca.site_id AND s.visitor_hmac=inode.value_hmac) AS currency
|
|
703
|
-
FROM analytics_identity_nodes inode
|
|
704
|
-
WHERE inode.site_id=ca.site_id AND inode.namespace_id=ca.namespace_id
|
|
705
|
-
AND inode.id IN (ca.left_identity_node_id,ca.right_identity_node_id)
|
|
706
|
-
) node_stats
|
|
707
|
-
) stats ON true
|
|
708
|
-
WHERE ca.site_id=$1 AND n.status='approved' AND ca.status='active'
|
|
709
|
-
AND ca.confidence_band='high' AND ca.expires_at > now()
|
|
710
|
-
AND ($2::timestamptz IS NULL OR (ca.updated_at,ca.id) < ($2::timestamptz,$3::uuid))
|
|
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 */
|
|
712
|
-
SELECT ca.id AS association_id, ca.updated_at, ca.first_observed_at, ca.last_observed_at,
|
|
713
|
-
ca.expires_at, ca.model_version, ca.confidence_band,
|
|
714
|
-
COALESCE(array_agg(DISTINCT ce.feature_kind ORDER BY ce.feature_kind)
|
|
715
|
-
FILTER (WHERE ce.id IS NOT NULL), ARRAY[]::text[]) AS feature_kinds
|
|
716
|
-
FROM analytics_candidate_associations ca
|
|
717
|
-
JOIN analytics_identity_namespaces n ON n.id=ca.namespace_id AND n.site_id=ca.site_id
|
|
718
|
-
LEFT JOIN analytics_candidate_evidence ce ON ce.association_id=ca.id
|
|
719
|
-
AND ce.namespace_id=ca.namespace_id AND ce.expires_at > now()
|
|
720
|
-
WHERE ca.site_id=$1 AND n.status='approved' AND ca.status='active'
|
|
721
|
-
AND ca.confidence_band='medium' AND ca.expires_at > now()
|
|
722
|
-
AND ($2::timestamptz IS NULL OR (ca.updated_at,ca.id) < ($2::timestamptz,$3::uuid))
|
|
723
|
-
GROUP BY ca.id
|
|
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-----
|
|
725
|
-
MCowBQYDK2VwAyEAi2HR1srL4o18O8BRa7gVJY7G7bupbN3H9AwJrHCDiOg=
|
|
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(
|
|
727
|
-
id, site_id, name, event_name, trigger_kind, definition, status, created_by_user_id
|
|
728
|
-
) VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7,$8)
|
|
729
|
-
ON CONFLICT(site_id, name) DO UPDATE SET event_name=EXCLUDED.event_name,
|
|
730
|
-
trigger_kind=EXCLUDED.trigger_kind, definition=EXCLUDED.definition,
|
|
731
|
-
status=EXCLUDED.status, version=analytics_event_definitions.version+1, updated_at=now()
|
|
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
|
|
733
|
-
FROM analytics_event_definitions WHERE site_id=$1 AND status <> 'archived'
|
|
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
|
|
736
|
-
name=$3, event_name=$4, trigger_kind=$5, definition=$6::jsonb,
|
|
737
|
-
status=$7, version=version+1, updated_at=now()
|
|
738
|
-
WHERE id=$1 AND site_id=$2
|
|
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()
|
|
740
|
-
WHERE id=$1 AND site_id=$2
|
|
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
|
|
742
|
-
FROM analytics_events
|
|
743
|
-
WHERE site_id=$1 AND event_definition_id=$2 AND event_definition_version=$3
|
|
744
|
-
AND received_at >= now() - ($4 * interval '1 second')
|
|
745
|
-
ORDER BY received_at DESC, id DESC
|
|
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
|
|
747
|
-
FROM analytics_pixels p JOIN analytics_sites s ON s.id=p.site_id
|
|
748
|
-
LEFT JOIN analytics_event_definitions d ON d.site_id=s.id AND d.status='active'
|
|
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(
|
|
750
|
-
id, site_id, provider, name, source_account_ref, service_connection_ref,
|
|
751
|
-
secret_ciphertext, config, created_by_user_id
|
|
752
|
-
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb,$9)
|
|
753
|
-
ON CONFLICT(site_id, provider, source_account_ref) DO UPDATE SET
|
|
754
|
-
name = EXCLUDED.name,
|
|
755
|
-
service_connection_ref = COALESCE(EXCLUDED.service_connection_ref, analytics_connections.service_connection_ref),
|
|
756
|
-
secret_ciphertext = COALESCE(EXCLUDED.secret_ciphertext, analytics_connections.secret_ciphertext),
|
|
757
|
-
config = EXCLUDED.config,
|
|
758
|
-
readiness = CASE WHEN analytics_connections.readiness = 'live' THEN 'live' ELSE 'configured_unverified' END,
|
|
759
|
-
updated_at = now()
|
|
760
|
-
RETURNING id, site_id, provider, name, source_account_ref, service_connection_ref,
|
|
761
|
-
readiness, last_verified_at, last_event_at, last_reconciled_at, last_error_code,
|
|
762
|
-
verification_evidence, last_verified_receipt_id,
|
|
763
|
-
(secret_ciphertext IS NOT NULL) AS webhook_secret_configured,
|
|
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,
|
|
765
|
-
readiness, last_verified_at, last_event_at, last_reconciled_at,
|
|
766
|
-
last_error_code, verification_evidence, last_verified_receipt_id,
|
|
767
|
-
created_at, updated_at,
|
|
768
|
-
(secret_ciphertext IS NOT NULL) AS webhook_secret_configured
|
|
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,
|
|
770
|
-
readiness, config
|
|
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,
|
|
772
|
-
secret_ciphertext, readiness, config
|
|
773
|
-
FROM analytics_connections
|
|
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(
|
|
775
|
-
id, site_id, connection_id, provider, source_account_ref, source_event_id,
|
|
776
|
-
signature_status, payload_ciphertext, payload_expires_at
|
|
777
|
-
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,now() + interval '7 days')
|
|
778
|
-
ON CONFLICT(site_id, provider, source_account_ref, source_event_id) DO UPDATE SET
|
|
779
|
-
normalization_status = CASE
|
|
780
|
-
WHEN analytics_webhook_receipts.normalization_status = 'accepted' THEN 'replayed'
|
|
781
|
-
ELSE analytics_webhook_receipts.normalization_status
|
|
782
|
-
END
|
|
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
|
|
784
|
-
normalization_status = $2,
|
|
785
|
-
error_code = $3,
|
|
786
|
-
normalized_at = now()
|
|
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
|
|
788
|
-
readiness = CASE WHEN readiness='live' AND $2='verified' THEN 'live' ELSE $2 END,
|
|
789
|
-
last_error_code = $3,
|
|
790
|
-
last_verified_at = CASE WHEN $2 IN ('verified','live') THEN now() ELSE last_verified_at END,
|
|
791
|
-
last_event_at = CASE WHEN $4 THEN now() ELSE last_event_at END,
|
|
792
|
-
last_reconciled_at = CASE WHEN $5 THEN now() ELSE last_reconciled_at END,
|
|
793
|
-
verification_evidence = CASE
|
|
794
|
-
WHEN verification_evidence='provider' THEN 'provider'
|
|
795
|
-
ELSE COALESCE($6, verification_evidence)
|
|
796
|
-
END,
|
|
797
|
-
last_verified_receipt_id = COALESCE($7::uuid, last_verified_receipt_id),
|
|
798
|
-
updated_at = now()
|
|
799
|
-
WHERE id = $1
|
|
800
|
-
RETURNING id, readiness, last_verified_at, last_event_at, last_reconciled_at,
|
|
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(
|
|
802
|
-
id, site_id, name, conversion_kind, condition, default_value_minor,
|
|
803
|
-
default_currency, status, created_by_user_id
|
|
804
|
-
) VALUES ($1,$2,$3,$4,$5::jsonb,$6,$7,$8,$9)
|
|
805
|
-
ON CONFLICT(site_id, name) DO UPDATE SET
|
|
806
|
-
conversion_kind = EXCLUDED.conversion_kind,
|
|
807
|
-
condition = EXCLUDED.condition,
|
|
808
|
-
default_value_minor = EXCLUDED.default_value_minor,
|
|
809
|
-
default_currency = EXCLUDED.default_currency,
|
|
810
|
-
status = EXCLUDED.status,
|
|
811
|
-
version = analytics_conversion_rules.version + 1,
|
|
812
|
-
updated_at = now()
|
|
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,
|
|
814
|
-
default_currency, version, status, created_at, updated_at
|
|
815
|
-
FROM analytics_conversion_rules WHERE site_id = $1 AND status <> 'archived'
|
|
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
|
|
818
|
-
JOIN analytics_identity_edges e ON e.identity_node_id = n.id
|
|
819
|
-
WHERE n.site_id = $1 AND n.kind = $2 AND n.value_hmac = $3
|
|
820
|
-
AND ($4::uuid IS NULL OR (n.namespace_id=$4 AND e.namespace_id=$4))
|
|
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,
|
|
822
|
-
c.provider AS connection_provider,
|
|
823
|
-
s.latest_receipt_id::text AS receipt_id, s.policy_version,
|
|
824
|
-
s.site_analytics, s.enhanced_matching, s.advertising_measurement,
|
|
825
|
-
s.gpc, r.action, s.effective_at, s.expires_at
|
|
826
|
-
FROM analytics_crm_person_mappings m
|
|
827
|
-
JOIN analytics_connections c ON c.id=m.connection_id AND c.site_id=m.site_id
|
|
828
|
-
JOIN analytics_people p ON p.id=m.person_id AND p.site_id=m.site_id AND p.namespace_id=m.namespace_id
|
|
829
|
-
JOIN analytics_identity_namespaces n ON n.id=m.namespace_id AND n.site_id=m.site_id AND n.status='approved'
|
|
830
|
-
LEFT JOIN analytics_consent_current_state s
|
|
831
|
-
ON s.site_id=m.site_id AND s.subject_ref=p.public_ref
|
|
832
|
-
LEFT JOIN analytics_consent_receipts r
|
|
833
|
-
ON r.id=s.latest_receipt_id AND r.site_id=s.site_id
|
|
834
|
-
WHERE m.site_id=$1 AND m.connection_id::text=$2
|
|
835
|
-
AND m.provider_person_ref=$3 AND m.person_id::text=$4
|
|
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
|
|
837
|
-
WHERE site_id=$1 AND person_id=$2
|
|
838
|
-
AND occurred_at <= $3::timestamptz
|
|
839
|
-
AND occurred_at >= $3::timestamptz - ($4::text || ' days')::interval
|
|
840
|
-
AND click_ids <> '{}'::jsonb
|
|
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
|
|
842
|
-
WHERE site_id=$1 AND person_id=$2 AND event_name=$3 AND id<>$4
|
|
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
|
|
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
|
|
845
|
-
FROM analytics_external_events WHERE site_id=$1 AND person_id=$2
|
|
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(
|
|
847
|
-
id, site_id, namespace_id, definition_id, definition_version,
|
|
848
|
-
person_id, score, contributions, evaluated_at
|
|
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,
|
|
850
|
-
(SELECT count(DISTINCT p.id)::text FROM analytics_people p
|
|
851
|
-
WHERE p.site_id=t.site_id AND p.namespace_id=t.namespace_id) AS confirmed_people,
|
|
852
|
-
(SELECT count(DISTINCT e.person_id)::text FROM analytics_external_events e
|
|
853
|
-
JOIN analytics_people p ON p.id=e.person_id AND p.site_id=e.site_id
|
|
854
|
-
WHERE e.site_id=t.site_id AND p.namespace_id=t.namespace_id AND e.event_name=d.event_name) AS positive_outcomes,
|
|
855
|
-
EXISTS(SELECT 1 FROM analytics_prediction_manifests m
|
|
856
|
-
WHERE m.target_id=t.id AND m.status IN ('validated','active')) AS validated_manifest
|
|
857
|
-
FROM analytics_prediction_targets t
|
|
858
|
-
JOIN analytics_event_definitions d ON d.id=t.target_event_definition_id
|
|
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(
|
|
860
|
-
id, site_id, receipt_id, person_id, source, source_account_ref,
|
|
861
|
-
source_event_id, event_kind, event_name, occurred_at, visitor_hmac,
|
|
862
|
-
session_hmac, call_id, deal_id, order_id, value_minor, currency,
|
|
863
|
-
click_ids, properties
|
|
864
|
-
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18::jsonb,$19::jsonb)
|
|
865
|
-
ON CONFLICT(site_id, source, source_account_ref, source_event_id) DO NOTHING
|
|
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,
|
|
868
|
-
default_currency, version
|
|
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(
|
|
870
|
-
id, source_event_id, site_id, session_id, conversion_kind,
|
|
871
|
-
value_minor, currency, occurred_at, person_id, rule_id, rule_version, order_id,
|
|
872
|
-
value_source, value_provenance, session_hmac
|
|
873
|
-
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14::jsonb,$15)
|
|
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(
|
|
876
|
-
id, destination_id, conversion_id, person_id, payload_ciphertext
|
|
877
|
-
) VALUES ($1,$2,$3,$4,$5)
|
|
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,
|
|
879
|
-
exit_path, pageview_count, event_count
|
|
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
|
|
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
|
|
882
|
-
FROM analytics_external_events WHERE site_id = $1 AND person_id = $2 ORDER BY occurred_at`,[e,n]),i.query(`SELECT conversion_kind, value_minor, currency, occurred_at, rule_version
|
|
883
|
-
FROM analytics_conversions WHERE site_id = $1 AND person_id = $2 ORDER BY occurred_at`,[e,n]),i.query(`SELECT j.status, j.attempts, j.response_category, j.created_at, j.delivered_at,
|
|
884
|
-
d.platform, d.name AS destination_name
|
|
885
|
-
FROM analytics_activation_jobs j
|
|
886
|
-
JOIN analytics_activation_destinations d ON d.id = j.destination_id
|
|
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
|
|
888
|
-
count(*)::int AS total_events,
|
|
889
|
-
count(*) FILTER (WHERE person_id IS NOT NULL)::int AS identified_events,
|
|
890
|
-
count(*) FILTER (WHERE click_ids <> '{}'::jsonb)::int AS events_with_click_id,
|
|
891
|
-
count(*) FILTER (WHERE event_kind = 'call')::int AS call_events,
|
|
892
|
-
count(*) FILTER (WHERE event_kind = 'call' AND person_id IS NOT NULL)::int AS joined_call_events,
|
|
893
|
-
count(*) FILTER (WHERE event_kind = 'crm')::int AS crm_events,
|
|
894
|
-
count(*) FILTER (WHERE event_kind = 'crm' AND person_id IS NOT NULL)::int AS joined_crm_events,
|
|
895
|
-
count(*) FILTER (WHERE properties->>'status' = 'spam' OR properties->'tags' ? 'spam')::int AS spam_events
|
|
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,
|
|
897
|
-
cs.latest_receipt_id::text AS receipt_id, cs.policy_version,
|
|
898
|
-
cs.site_analytics, cs.enhanced_matching, cs.advertising_measurement,
|
|
899
|
-
cs.effective_at
|
|
900
|
-
FROM analytics_identity_nodes node
|
|
901
|
-
JOIN analytics_identity_edges edge
|
|
902
|
-
ON edge.identity_node_id=node.id AND edge.site_id=node.site_id
|
|
903
|
-
JOIN analytics_people p
|
|
904
|
-
ON p.id=edge.person_id AND p.site_id=node.site_id
|
|
905
|
-
JOIN analytics_identity_namespaces ns
|
|
906
|
-
ON ns.id=p.namespace_id AND ns.site_id=p.site_id AND ns.status='approved'
|
|
907
|
-
JOIN analytics_sites site
|
|
908
|
-
ON site.id=p.site_id AND site.status='active'
|
|
909
|
-
JOIN analytics_restricted_data profile
|
|
910
|
-
ON profile.site_id=p.site_id AND profile.person_id=p.id
|
|
911
|
-
AND profile.purpose='enhanced_matching' AND profile.expires_at>now()
|
|
912
|
-
JOIN analytics_consent_receipts provenance
|
|
913
|
-
ON provenance.id=profile.consent_receipt_id AND provenance.site_id=profile.site_id
|
|
914
|
-
JOIN analytics_consent_current_state cs
|
|
915
|
-
ON cs.site_id=p.site_id AND cs.subject_ref=provenance.subject_ref
|
|
916
|
-
JOIN analytics_consent_receipts receipt
|
|
917
|
-
ON receipt.id=cs.latest_receipt_id AND receipt.site_id=cs.site_id
|
|
918
|
-
WHERE node.site_id=$1 AND node.kind='customer_id' AND node.value_hmac=$2
|
|
919
|
-
AND p.public_ref IS NOT NULL
|
|
920
|
-
AND cs.site_analytics=true AND cs.enhanced_matching=true
|
|
921
|
-
AND cs.gpc=false AND receipt.action NOT IN ('withdraw','gpc')
|
|
922
|
-
AND cs.effective_at <= now()
|
|
923
|
-
AND (cs.expires_at IS NULL OR cs.expires_at > now())
|
|
924
|
-
AND NOT EXISTS (
|
|
925
|
-
SELECT 1 FROM analytics_identity_tombstones tombstone
|
|
926
|
-
WHERE tombstone.site_id=p.site_id AND tombstone.person_id=p.id
|
|
927
|
-
)
|
|
928
|
-
AND NOT EXISTS (
|
|
929
|
-
SELECT 1 FROM analytics_privacy_audits audit
|
|
930
|
-
WHERE audit.site_id=p.site_id AND audit.subject_ref=p.public_ref
|
|
931
|
-
AND audit.action='deletion_requested' AND audit.result_code='running'
|
|
932
|
-
)
|
|
933
|
-
ORDER BY edge.confidence DESC, edge.last_seen_at DESC
|
|
934
|
-
LIMIT 1
|
|
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 nn(l.id,s*en,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-4XHYUV7J.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:Qt,footer:Zt,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};
|