PageSourceSearch

https://docs.sogni.ai/assets/sogni-attribution.js?v=d4dbb09873

js sogni.ai collected 2026-10-01 12:17:34 UTC 10,169 bytes, 315 lines download raw bytes

1(() => {
2  "use strict";
3
4  const COOKIE_NAME = "sogni_attribution_v1";
5  const CHANGE_EVENT = "sogni-attribution-change";
6  const HANDOFF_EVENT = "sogni_handoff";
7  const MAX_AGE_SECONDS = 60 * 60 * 24 * 90;
8  const VERSION = 1;
9  const AGENT_DISCOVERY_VERSION = "2026-09-10.1";
10  let agentEntryRecorded = false;
11
12  function agentPage() {
13    const path = window.location.pathname.replace(/\/$/, "") || "/";
14    if (["www.sogni.ai", "sogni.ai"].includes(window.location.hostname)) {
15      return ({ "/agent": "agent_landing", "/quickstart": "quickstart" })[path];
16    }
17    if (window.location.hostname !== "docs.sogni.ai") return undefined;
18    return ({
19      "/agents": "agent_hub",
20      "/agents/first-result": "first_result",
21      "/agents/authentication": "authentication",
22      "/agents/pricing": "pricing",
23      "/agents/jobs": "jobs",
24    })[path];
25  }
26
27  function trackAgentEntry() {
28    const page = agentPage();
29    if (!page || agentEntryRecorded || !analyticsAllowed() || typeof window.gtag !== "function") return;
30    try {
31      window.gtag("event", "agent_entry_viewed", {
32        schema_version: 1,
33        event_id: randomId(),
34        occurred_at: new Date().toISOString(),
35        environment: "production",
36        producer: "public_web",
37        producer_version: AGENT_DISCOVERY_VERSION,
38        agent_page: page,
39        exposure_version: AGENT_DISCOVERY_VERSION,
40        outcome: "viewed",
41        // This is a consented browser view, not authenticated runtime identity.
42        attribution_confidence: "unknown",
43        page_location: window.location.origin + window.location.pathname,
44        page_referrer: safeReferrer() || "",
45        transport_type: "beacon",
46      });
47      agentEntryRecorded = true;
48    } catch { /* Optional analytics must never interfere with the page. */ }
49  }
50  const OPTIONAL_TOUCH_FIELDS = [
51    "campaign",
52    "content",
53    "term",
54    "gclid",
55    "gbraid",
56    "wbraid",
57    "referrer",
58  ];
59
60  function isSogniDomain(hostname) {
61    return hostname === "sogni.ai" || hostname.endsWith(".sogni.ai");
62  }
63
64  function limit(value, length) {
65    if (typeof value !== "string") return undefined;
66    const trimmed = value.trim();
67    return trimmed ? trimmed.slice(0, length) : undefined;
68  }
69
70  function internalSource(value) {
71    const source = limit(value, 100);
72    return source && /^[a-z0-9][a-z0-9._-]*$/i.test(source) ? source : undefined;
73  }
74
75  function isTouch(value) {
76    if (!value || typeof value !== "object") return false;
77    if (
78      typeof value.source !== "string" ||
79      !value.source.trim() ||
80      typeof value.medium !== "string" ||
81      !value.medium.trim() ||
82      typeof value.landingPath !== "string" ||
83      !value.landingPath.trim() ||
84      typeof value.at !== "string" ||
85      !Number.isFinite(Date.parse(value.at))
86    ) {
87      return false;
88    }
89    return OPTIONAL_TOUCH_FIELDS.every((field) => {
90      const fieldValue = value[field];
91      return (
92        fieldValue === undefined || (typeof fieldValue === "string" && Boolean(fieldValue.trim()))
93      );
94    });
95  }
96
97  function readCookie() {
98    const prefix = `${COOKIE_NAME}=`;
99    const part = document.cookie.split(";").find((item) => item.trim().startsWith(prefix));
100    if (!part) return null;
101
102    try {
103      const parsed = JSON.parse(decodeURIComponent(part.trim().slice(prefix.length)));
104      if (
105        !parsed ||
106        parsed.version !== VERSION ||
107        typeof parsed.attributionId !== "string" ||
108        !parsed.attributionId.trim() ||
109        parsed.attributionId.length > 128 ||
110        !isTouch(parsed.firstTouch) ||
111        !isTouch(parsed.lastTouch)
112      ) {
113        return null;
114      }
115      return parsed;
116    } catch {
117      return null;
118    }
119  }
120
121  function cookieAttributes(maxAge) {
122    const attributes = ["Path=/", `Max-Age=${maxAge}`, "SameSite=Lax"];
123    if (isSogniDomain(window.location.hostname)) attributes.push("Domain=.sogni.ai");
124    if (window.location.protocol === "https:") attributes.push("Secure");
125    return attributes;
126  }
127
128  function writeCookie(value) {
129    const encoded = encodeURIComponent(JSON.stringify(value));
130    // Leave room for attributes and browser-dependent overhead. Normal visits
131    // are far smaller; reject oversized URL parameters instead of writing a
132    // silently truncated cross-domain cookie.
133    if (encoded.length > 3800) return;
134    document.cookie = [`${COOKIE_NAME}=${encoded}
134`, ...cookieAttributes(MAX_AGE_SECONDS)].join(
135      "; ",
136    );
137  }
138
139  function clearCookie() {
140    document.cookie = [`${COOKIE_NAME}=`, ...cookieAttributes(0)].join("; ");
141  }
142
143  function analyticsAllowed() {
144    const consent = window.SogniConsent?.read?.();
145    // Attribution is an optional analytics cookie. Do not create it until the
146    // visitor explicitly grants analytics storage.
147    return consent?.analytics_storage === "granted";
148  }
149
150  function randomId() {
151    if (typeof window.crypto?.randomUUID === "function") return window.crypto.randomUUID();
152    return `sat-${Date.now()}-${Math.random().toString(36).slice(2, 14)}`;
153  }
154
155  function safeReferrer() {
156    if (!document.referrer) return undefined;
157    try {
158      const url = new URL(document.referrer);
159      return limit(`${url.origin}${url.pathname}`, 512);
160    } catch {
161      return undefined;
162    }
163  }
164
165  function sourceFromReferrer(referrer) {
166    if (!referrer) return { source: "(direct)", medium: "(none)", external: false };
167
168    try {
169      const hostname = new URL(referrer).hostname.toLowerCase();
170      if (isSogniDomain(hostname)) return { source: "sogni", medium: "internal", external: false };
171      if (/^(?:www\.)?google\.[a-z.]+$/.test(hostname)) {
172        return { source: "google", medium: "organic", external: true };
173      }
174      if (/^(?:www\.)?bing\.com$/.test(hostname)) {
175        return { source: "bing", medium: "organic", external: true };
176      }
177      return { source: hostname, medium: "referral", external: true };
178    } catch {
179      return { source: "(direct)", medium: "(none)", external: false };
180    }
181  }
182
183  function trackHandoffClick(event) {
184    if (!analyticsAllowed()) return;
185    const target = event.target;
186    if (!(target instanceof Element)) return;
187
188    const anchor = target.closest("a[href]");
189    if (!(anchor instanceof HTMLAnchorElement)) return;
190
191    let destination;
192    try {
193      destination = new URL(anchor.href, window.location.href);
194    } catch {
195      return;
196    }
197
198    const source = internalSource(destination.searchParams.get("source"));
199    if (
200      !source ||
201      !isSogniDomain(destination.hostname.toLowerCase()) ||
202      destination.hostname === window.location.hostname
203    ) {
204      return;
205    }
206
207    // Keep the real acquisition source intact. This event records the internal
208    // www -> app/chat handoff without introducing an internal UTM campaign.
209    try { window.gtag?.("event", HANDOFF_EVENT, {
210      handoff_stage: "click",
211      internal_source: source,
212      origin_path: limit(window.location.pathname, 100) || "/",
213      destination_path: limit(destination.pathname, 100) || "/",
214      destination_host: limit(destination.hostname, 100),
215      ...(agentPage() ? { agent_page: agentPage(), exposure_version: AGENT_DISCOVERY_VERSION } : {}),
216      page_location: window.location.origin + window.location.pathname,
217      page_referrer: safeReferrer() || "",
218      transport_type: "beacon",
219    }); } catch { /* A telemetry failure must not block navigation. */ }
220  }
221
222  function currentTouch() {
223    const url = new URL(window.location.href);
224    const referrer = safeReferrer();
225    const derived = sourceFromReferrer(referrer);
226    const utmSource = limit(url.searchParams.get("utm_source"), 128);
227    const utmMedium = limit(url.searchParams.get("utm_medium"), 128);
228    const campaign = limit(url.searchParams.get("utm_campaign"), 256);
229    const content = limit(url.searchParams.get("utm_content"), 256);
230    const term = limit(url.searchParams.get("utm_term"), 256);
231    const gclid = limit(url.searchParams.get("gclid"), 256);
232    const gbraid = limit(url.searchParams.get("gbraid"), 256);
233    const wbraid = limit(url.searchParams.get("wbraid"), 256);
234    const hasGoogleClickId = Boolean(gclid || gbraid || wbraid);
235    const internalCampaign = derived.medium === "internal" && utmSource === "website";
236    const source = internalCampaign
237      ? derived.source
238      : utmSource || (hasGoogleClickId ? "google" : derived.source);
239    const medium = internalCampaign
240      ? derived.medium
241      : utmMedium || (hasGoogleClickId ? "cpc" : derived.medium);
242
243    const touch = {
244      source,
245      medium,
246      landingPath: limit(url.pathname, 512) || "/",
247      at: new Date().toISOString(),
248    };
249
250    const optional = {
251      campaign,
252      content,
253      term,
254      gclid,
255      gbraid,
256      wbraid,
257      referrer,
258    };
259    for (const [key, value] of Object.entries(optional)) {
260      if (value !== undefined) touch[key] = value;
261    }
262
263    return {
264      touch,
265      meaningful:
266        !internalCampaign &&
267        Boolean(
268          utmSource ||
269          utmMedium ||
270          campaign ||
271          content ||
272          term ||
273          hasGoogleClickId ||
274          derived.external,
275        ),
276    };
277  }
278
279  function capture() {
280    if (!analyticsAllowed()) {
281      clearCookie();
282      return null;
283    }
284
285    const existing = readCookie();
286    const { touch, meaningful } = currentTouch();
287    const next = existing
288      ? {
289          ...existing,
290          lastTouch: meaningful ? touch : existing.lastTouch,
291        }
292      : {
293          version: VERSION,
294          attributionId: randomId(),
295          firstTouch: touch,
296          lastTouch: touch,
297        };
298
299    writeCookie(next);
300    window.dispatchEvent(new CustomEvent(CHANGE_EVENT, { detail: next }));
301    trackAgentEntry();
302    return next;
303  }
304
305  window.SogniAttribution = Object.freeze({
306    cookieName: COOKIE_NAME,
307    changeEvent: CHANGE_EVENT,
308    read: readCookie,
309    capture,
310  });
311
312  capture();
313  window.addEventListener(window.SogniConsent?.changeEvent || "sogni-consent-change", capture);
314  document.addEventListener("click", trackHandoffClick, true);
315})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.