Files
agency/.pnpm-store/v3/files/47/f773f0e16b17920402b0a3be0531d887b49071c3dc4f3b6b3d298d1b9a55634f746ce874d93486304a73875ee3d352cb5a6a26dc05ae67b0eb96d1da106b64
hanzo-agent 5af6960b87 run project
Run: sess_f37640604ae254cb4d11dc6217f7b721
2026-09-30 00:04:56 +00:00

1 line
133 KiB
Plaintext

{"version":3,"sources":["../src/attribution.ts","../src/dsn.ts","../src/org.ts","../src/scrub.ts","../src/anon.js","../src/uid.ts","../package.json","../src/version.ts","../src/sentry.ts","../src/exception.ts","../src/storage.ts","../src/core.ts","../src/goals.ts"],"names":["truncate","normalizeError"],"mappings":"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","file":"index.mjs","sourcesContent":["// Pure attribution helpers: parse first-touch UTM/referrer/refCode from a URL,\n// derive the acquisition channel, and compute the ISO week for the signup cohort.\n// No I/O, no globals — trivially testable.\n\nimport type { Attribution } from './types'\n\nconst SOCIAL_HOSTS = [\n 'facebook.', 'instagram.', 'twitter.', 'x.com', 't.co', 'linkedin.',\n 'reddit.', 'youtube.', 'tiktok.', 'pinterest.', 'news.ycombinator.com',\n]\nconst SEARCH_HOSTS = ['google.', 'bing.', 'duckduckgo.', 'yahoo.', 'baidu.', 'ecosia.']\n\n/** parseAttribution reads UTM params + ref/refCode from a query string and pairs\n * them with the referrer. `search` is a location.search value (\"?utm_source=…\"). */\nexport function parseAttribution(search: string, referrer: string): Attribution {\n const q = new URLSearchParams(search || '')\n const get = (k: string) => {\n const v = q.get(k)\n return v ? v.trim() : undefined\n }\n const a: Attribution = {\n utm: {\n source: get('utm_source'),\n medium: get('utm_medium'),\n campaign: get('utm_campaign'),\n term: get('utm_term'),\n content: get('utm_content'),\n },\n referrer: referrer ? referrer.trim() : undefined,\n refCode: get('ref') || get('refCode') || get('ref_code') || undefined,\n }\n a.channel = deriveChannel(a)\n return a\n}\n\n/** deriveChannel classifies the visit: paid | referral | social | organic | direct. */\nexport function deriveChannel(a: Attribution): string {\n const medium = (a.utm.medium || '').toLowerCase()\n if (/(cpc|ppc|paid|paidsearch|display|cpm)/.test(medium)) return 'paid'\n if (a.utm.source || a.utm.campaign) return 'campaign'\n if (a.refCode) return 'referral'\n const host = hostOf(a.referrer)\n if (!host) return 'direct'\n if (SOCIAL_HOSTS.some((h) => host.includes(h))) return 'social'\n if (SEARCH_HOSTS.some((h) => host.includes(h))) return 'organic'\n return 'referral'\n}\n\n/** hostOf extracts a bare lowercase host from a URL; \"\" when unparseable. */\nexport function hostOf(raw?: string): string {\n if (!raw) return ''\n let s = raw.trim()\n const scheme = s.indexOf('://')\n if (scheme >= 0) s = s.slice(scheme + 3)\n const cut = s.search(/[/?#]/)\n if (cut >= 0) s = s.slice(0, cut)\n const at = s.indexOf('@')\n if (at >= 0) s = s.slice(at + 1)\n const colon = s.indexOf(':')\n if (colon >= 0) s = s.slice(0, colon)\n return s.toLowerCase().trim()\n}\n\n/** hasAttribution reports whether anything was captured (so we don't persist an\n * empty first-touch that would shadow a later real one). */\nexport function hasAttribution(a: Attribution): boolean {\n return Boolean(\n a.utm.source || a.utm.medium || a.utm.campaign || a.utm.term ||\n a.utm.content || a.refCode || (a.referrer && hostOf(a.referrer)),\n )\n}\n\n/** isoWeek returns the ISO-8601 week label, e.g. \"2026-W28\". */\nexport function isoWeek(d: Date): string {\n // Copy to UTC midnight; ISO week: Thursday-anchored.\n const date = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()))\n const day = date.getUTCDay() || 7\n date.setUTCDate(date.getUTCDate() + 4 - day)\n const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1))\n const week = Math.ceil(((date.getTime() - yearStart.getTime()) / 86400000 + 1) / 7)\n return `${date.getUTCFullYear()}-W${String(week).padStart(2, '0')}`\n}\n","/**\n * The product → Sentinel DSN registry.\n *\n * An app declares WHAT it is (`product: 'console'`); this module knows WHERE its\n * errors go. That split is the whole point: no surface has to learn a DSN, carry\n * a build argument, or grow a config file to report errors — declaring the\n * product it already declares is enough.\n *\n * A Sentinel DSN is PUBLIC by construction. It ships inside the client bundle and\n * is readable in devtools on any deployed page, and it grants exactly one\n * capability: submitting new events. It cannot read issues, projects, or any\n * other data. So committing it is not leaking a secret — it is recording a public\n * identifier next to the code that needs it. (Contrast the server-side collector\n * DSN in the `team-analytics-sentry` Secret, which is HMAC-derived and revocable\n * precisely because a server-side credential is NOT public.)\n *\n * Why a literal map instead of deriving `hanzo-${product}`: the projects predate\n * this registry and do not derive cleanly — `site` lives in `hanzo-ai`, not\n * `hanzo-site`. An explicit map is honest about that; a derivation rule plus an\n * exception table is the same data with a trap in it.\n *\n * Projects are org-scoped and named `<org>-<app>`. To add one: create the project\n * (POST /v1/sentinel/projects with X-Org-Id), then add its `dsn` here keyed by the\n * product name the app passes to `createAnalytics`.\n */\n\n/** PRODUCT_PROJECT maps a `product` to its Sentinel project id. The DSN's KEY is no\n * longer a per-project secret — it is the ONE org publishable key (below), so a\n * surface's errors ride the SAME key its events do. The id only names WHICH\n * project the errors group under, and cloud auto-provisions that project on first\n * keyed ingest, so a new id needs nothing minted. `site` lives in the `hanzo-ai`\n * project — an explicit map, because the projects predate this and do not derive\n * cleanly from the product name. */\nexport const PRODUCT_PROJECT: Readonly<Record<string, string>> = Object.freeze({\n console: '019fa40b-94ae-7f1d-8f7b-e92f123fad42', // console.hanzo.ai\n app: '019f9b1e-57eb-7171-9d92-72c0b85e4b4b', // hanzo.app\n site: '019f9b1e-5785-7359-ad0b-f75db8e58c99', // hanzo.ai (marketing; product `site`)\n})\n\n/** dsnForProduct builds the product's Sentinel DSN from the caller's resolved\n * publishable `key` and the product's project — the SAME key the event stream\n * carries, at the product's envelope endpoint. Returns undefined when there is no\n * key or no project for the product, leaving the error plane inert rather than\n * posting into the wrong one. The key is NOT baked here: it is the value the\n * surface resolved (an explicit `ingestKey`, or the KMS-sourced\n * NEXT_PUBLIC_PUBLISHABLE_KEY the build inlines) — the ONE live source, never a\n * literal committed beside the code. */\nexport function dsnForProduct(\n product: string | undefined,\n key: string | undefined\n): string | undefined {\n if (!product || !key) return undefined\n const projectId = PRODUCT_PROJECT[product]\n if (!projectId) return undefined\n return `https://${key}@api.hanzo.ai/v1/event/${projectId}`\n}\n","/**\n * Which org owns a host's telemetry.\n *\n * A surface should not have to LEARN its key. It already knows the domain it is\n * served from, and a domain belongs to exactly one brand, so the key is derivable\n * and asking a surface to carry one is asking it to restate something already\n * true. This module answers `orgOf(location.hostname)` and hands back that org's\n * publishable key, which is what lets a static export report correctly while\n * configuring nothing.\n *\n * This is the SAME question `hanzo.id` answers at runtime for the identity hosts\n * (`pkgs/shared/src/ingest.ts`): one image serves every brand, so the key cannot\n * be a property of the build. It was, and the bill is on record — Lux's, Zoo's,\n * Osage's and Pars' visitors were all filed in HANZO's project, because one\n * build-time key was inlined for every brand. That is the white-label boundary\n * crossed in the direction that shows up latest. The marketing sites carry the\n * identical defect in its other form: each commits its OWN key literal, so the\n * fleet holds N copies of a value with one source, and a site added tomorrow\n * reports nothing until somebody remembers to paste one in.\n *\n * A `pk-` is PUBLISHABLE: it authorizes a write into one org and mints no reading\n * principal. It is readable in devtools on every deployed page that sends an\n * event, so stating it here exposes nothing that serving the page did not. What\n * it does buy is that correct attribution becomes the DEFAULT rather than\n * something each surface opts into.\n *\n * Keyed by ORG, never by host: a brand's facts repeat on every alias it owns\n * (`lux.id`, `id.lux.network`), and a key stated per host would have to be\n * repeated too — so the day someone adds an alias and forgets the key, that host\n * silently stops reporting. Stated once per org, a new alias inherits its brand's\n * key by naming its brand's domain, which is all an alias ever says.\n */\n\n/** One publishable key per org. */\nexport type Keyring = Readonly<Record<string, string>>\n\n/**\n * Each org's registrable domains. A host matches a domain when it IS that domain\n * or a subdomain of it, so `explore.lux.network` needs no entry of its own.\n *\n * An org absent from this table resolves to undefined, and that is deliberate:\n * `osage`, `pars` and `bootnode` have no project of their own yet, and reporting\n * NOTHING is the honest answer. Filing them under a brand that is not theirs is\n * the defect this module exists to prevent — it is silent, it reads as working,\n * and it is only visible later in someone else's warehouse.\n */\nexport const ORG_DOMAIN: Readonly<Record<string, readonly string[]>> = Object.freeze({\n // A brand's domains are not a pattern — `hanzoskills.com` is Hanzo's and does\n // not end in `hanzo.ai`, while `hanzo.works` is a registrable domain of its own\n // rather than a subdomain of anything here. Each is stated because each is a\n // separate fact, which is the whole reason this is a table.\n hanzo: Object.freeze([\n 'hanzo.ai',\n 'hanzo.app',\n 'hanzo.bot',\n 'hanzo.chat',\n 'hanzo.codes',\n 'hanzo.id',\n 'hanzo.sh',\n 'hanzo.team',\n 'hanzo.ventures',\n 'hanzo.works',\n 'hanzoskills.com',\n ]),\n lux: Object.freeze([\n 'lux.network',\n 'lux.exchange',\n 'lux.market',\n 'lux.finance',\n 'lux.financial',\n 'lux.credit',\n 'lux.fund',\n 'lux.id',\n ]),\n zoo: Object.freeze(['zoo.ngo', 'zoo.network', 'zoolabs.id', 'zoolabs.io']),\n})\n\n/**\n * Each org's publishable key, and a copy: the values live in universe at\n * infra/k8s/id/configmap.yaml as SPA_INGEST_KEYRING, which serves them to the\n * identity hosts, and this table is what a statically exported page carries when\n * it cannot ask. The two must say the same thing. Add an org the day its project\n * exists, never before.\n *\n * A key is only as real as the project it names, and these had stopped naming\n * one: all three answered `403 ingest_key_unknown` against /v1/event, so every\n * page that fell back to this table reported nowhere while looking fine. The\n * copy drifted silently because nothing compares it to the keyring. If you\n * recreate a project, move BOTH.\n */\nexport const ORG_KEY: Keyring = Object.freeze({\n hanzo: 'pk-rM_CdaF2MQckGCrla113SrR1oH4zvqN8xh2I95Z9tY8',\n lux: 'pk-gUZp6ZVfhJzSwK-rb4oLbVkpCnMBx5uSCpxf_5yEhQk',\n zoo: 'pk-3TKpKnERV9AQSsBUERWkZejC1O1mUxc1jRzsP3MPbs4',\n})\n\n/** `pk-` is publishable; `sk-` is not, and there is no third thing. Checked at the\n * one point a key becomes something a page will send, so a mistyped entry fails\n * closed rather than putting a secret in every visitor's tab. */\nconst PUBLISHABLE = 'pk-'\n\n/** Strip the port and case a hostname the way a domain compares. */\nconst normalize = (host: string): string =>\n host.trim().toLowerCase().replace(/\\.$/, '').split(':')[0] ?? ''\n\n/**\n * The org that owns a host, or undefined when no brand claims it.\n *\n * Longest match wins, so a brand owning both a domain and a subdomain of another\n * brand's cannot be decided by table order. Pure and total.\n */\nexport function orgOf(host: string): string | undefined {\n const h = normalize(host)\n if (!h) return undefined\n let best: string | undefined\n let bestLen = 0\n for (const [org, domains] of Object.entries(ORG_DOMAIN)) {\n for (const d of domains) {\n if ((h === d || h.endsWith(`.${d}`)) && d.length > bestLen) {\n best = org\n bestLen = d.length\n }\n }\n }\n return best\n}\n\n/**\n * The publishable key for a host, or undefined when there is not exactly one to\n * give.\n *\n * Deliberately WITHOUT a fallback: returning Hanzo's key for an unrecognised host\n * is precisely the defect described above. Undefined is the honest answer and the\n * caller reports nothing.\n *\n * `keyring` is a parameter so the identity runtime — which receives its keyring\n * from `/config.json` because one image serves every brand — resolves through\n * this SAME function rather than a second copy of it.\n */\nexport function keyFor(host: string, keyring: Keyring = ORG_KEY): string | undefined {\n const org = orgOf(host)\n if (!org) return undefined\n const key = keyring[org]\n if (typeof key !== 'string') return undefined\n const trimmed = key.trim()\n return trimmed.startsWith(PUBLISHABLE) ? trimmed : undefined\n}\n\n/** The key for the page this code is running on; undefined off a browser. */\nexport function keyForPage(keyring: Keyring = ORG_KEY): string | undefined {\n if (typeof location === 'undefined') return undefined\n return keyFor(location.hostname, keyring)\n}\n","// Client-side redaction for error text — a faithful port of the server's\n// errortracking scrub (o11y pkg/modules/errortracking/implerrortracking/scrub.go),\n// applied BEFORE anything leaves the browser. Two layers, default-secure:\n//\n// - Secret shapes are ALWAYS redacted (sk-… keys, bearer/JWT tokens, DB DSNs,\n// PANs, cloud keys). There is no mode that ships a secret off-device.\n// - PII (email/IP) is scrubbed UNLESS capturePII is explicitly enabled.\n//\n// The server scrubs again — this is defense in depth, not a substitute — but the\n// point is that a Hanzo browser never emits a raw secret/email/IP in the first\n// place. Pure, no I/O.\n\nconst REDACTED = '[redacted]'\nconst EMAIL_MARK = '[email]'\nconst IP_MARK = '[ip]'\n\n// Secret patterns mirror scrub.go's secretPatterns. Order matters (broad DSN/PAN\n// rules run last). All are applied unconditionally.\nconst SECRET_PATTERNS: RegExp[] = [\n /-----BEGIN[ A-Z]*PRIVATE KEY-----[\\s\\S]*?-----END[ A-Z]*PRIVATE KEY-----/g,\n /eyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{6,}\\.[A-Za-z0-9_-]{6,}/g, // JWT\n /\\bbearer\\s+[A-Za-z0-9._~+/-]{12,}=*/gi, // bearer token\n /\\b(?:sk|pk|rk)-[A-Za-z0-9]{2,}-?[A-Za-z0-9]{12,}/g, // openai-style\n /\\b(?:sk|pk)_(?:live|test)_[A-Za-z0-9]{16,}/g, // stripe\n /\\bhk-[A-Za-z0-9]{16,}/g, // hanzo key\n /\\bAKIA[0-9A-Z]{16}\\b/g, // aws access key id\n /\\bASIA[0-9A-Z]{16}\\b/g, // aws sts key id\n /\\bAIza[0-9A-Za-z_-]{20,}/g, // google api key\n /\\bgh[posru]_[A-Za-z0-9]{20,}/g, // github token\n /\\bxox[baprs]-[A-Za-z0-9-]{10,}/g, // slack token\n // Creds in a URL/DSN. The repetition is BOUNDED on purpose: the unbounded form\n // ([^\\s:@/]+:[^\\s@/]+@) backtracks quadratically on colon-rich text with no\n // terminating '@' — an HTML error page pasted into an error message took 4.9s\n // at 32KB and >60s at 128KB, freezing the main thread from inside captureError.\n // Real userinfo is far below these caps, so bounding costs nothing.\n /[a-zA-Z][a-zA-Z0-9+.-]{0,32}:\\/\\/[^\\s:@/]{1,256}:[^\\s@/]{1,256}@/g,\n]\n\n/** Candidate card numbers. Gated by Luhn below — the bare digit-run pattern is a\n * false-positive cannon that redacts every millisecond epoch, order id and\n * phone number it sees (\"request 1753468800000 timed out\" became\n * \"request [redacted]timed out\"), which destroys the readability of the very\n * error messages this client exists to deliver. */\nconst RE_PAN = /\\b(?:\\d[ -]?){13,19}\\b/g\n\n/** luhn reports whether a digit string satisfies the Luhn checksum. Every real\n * card number does, so gating redaction on it removes the false positives\n * WITHOUT introducing a false negative — the safe direction for a redactor. */\nfunction luhn(digits: string): boolean {\n let sum = 0\n let alt = false\n for (let i = digits.length - 1; i >= 0; i--) {\n let d = digits.charCodeAt(i) - 48\n if (alt) {\n d *= 2\n if (d > 9) d -= 9\n }\n sum += d\n alt = !alt\n }\n return sum % 10 === 0\n}\n\n/** redactPAN removes digit runs that actually check out as card numbers. */\nfunction redactPAN(s: string): string {\n return s.replace(RE_PAN, (m) => {\n const digits = m.replace(/[ -]/g, '')\n if (digits.length < 13 || digits.length > 19) return m\n return luhn(digits) ? REDACTED : m\n })\n}\n\n// Bounded to the RFC 5321 maxima (local-part 64, domain 255) for the same reason\n// as the creds-in-URL rule: the unbounded form backtracks quadratically across a\n// long run of local-part-legal characters that never reaches an '@' — measured at\n// 20ms on an 8KB worst case, on the main thread, per captured error. No real\n// address is excluded by these caps.\nconst RE_EMAIL = /[A-Za-z0-9._%+-]{1,64}@[A-Za-z0-9.-]{1,255}\\.[A-Za-z]{2,24}/g\nconst RE_IPV4 = /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g\nconst RE_IPV6 = /\\b(?:[0-9A-Fa-f]{1,4}:){2,7}[0-9A-Fa-f]{1,4}\\b/g\n\n// A CREDENTIAL IN A QUERY PARAM HAS NO SHAPE TO MATCH.\n//\n// Every pattern above recognises a secret by what it LOOKS like — a JWT's three\n// dots, `sk-`, `AKIA`, `ghp_`. An OAuth `code`, a `state`, a password-reset\n// nonce and an invite token are opaque random strings, indistinguishable from a\n// page id, so none of them match and all of them survive.\n//\n// That matters because the client stamps `url: window.location.href` on EVERY\n// event, not just pageviews. So one visit to `/callback?code=…&state=…` puts a\n// live authorization code on the wire, once per event, in cleartext — and the\n// code is exchangeable until it is redeemed.\n//\n// The fix is to redact by the NAME the value is filed under rather than by the\n// value's shape, which is the only signal available for an opaque token. The\n// name half is bounded (`{1,32}`) and the value half stops at the first\n// separator, so neither half can backtrack across a long string — the same\n// discipline the creds-in-URL pattern above documents.\nconst CREDENTIAL_PARAMS = [\n 'code', 'state', 'token', 'access_token', 'id_token', 'refresh_token',\n 'auth', 'authorization', 'secret', 'client_secret', 'password', 'passwd', 'pwd',\n 'api_key', 'apikey', 'key', 'session', 'session_id', 'sid',\n 'sig', 'signature', 'nonce', 'otp', 'invite', 'invitation',\n 'reset_token', 'confirmation_token', 'magic', 'ticket', 'assertion',\n]\nconst RE_CREDENTIAL_PARAM = new RegExp(\n '([?&#;](?:' + CREDENTIAL_PARAMS.join('|') + ')=)[^&#;\\\\s]{1,4096}',\n 'gi',\n)\n\n/** redactCredentialParams removes the VALUE of any query parameter whose NAME\n * says it carries a credential, leaving the name so the URL still reads. */\nexport function redactCredentialParams(s: string): string {\n return s.replace(RE_CREDENTIAL_PARAM, (_m, prefix: string) => prefix + REDACTED)\n}\n\n/** redactSecrets removes known secret shapes. Always applied. */\nexport function redactSecrets(s: string): string {\n s = redactCredentialParams(s)\n for (const re of SECRET_PATTERNS) s = s.replace(re, REDACTED)\n return redactPAN(s)\n}\n\n/** scrubPII masks emails and IPs. Applied unless PII capture is enabled. */\nexport function scrubPII(s: string): string {\n s = s.replace(RE_EMAIL, EMAIL_MARK)\n s = s.replace(RE_IPV6, IP_MARK)\n s = s.replace(RE_IPV4, IP_MARK)\n return s\n}\n\n/** Longest free-text field this module will scrub. Every pattern here is a regex\n * run synchronously on the main thread, and the input is attacker-influenced —\n * `throw new Error(await res.text())` against an HTML error page is a one-line\n * way to hand us 128KB. Bounding the INPUT bounds the work regardless of which\n * pattern is pathological. 8KB is far beyond any real error message. */\nexport const MAX_SCRUB_LEN = 8192\n\n/** truncate caps a string at MAX_SCRUB_LEN, marking that it was cut so a reader\n * never mistakes a truncated message for the whole one. */\nexport function truncate(s: string, max = MAX_SCRUB_LEN): string {\n return s.length > max ? s.slice(0, max) + '… [truncated]' : s\n}\n\n/** scrubText applies the redaction policy to a free-text field. Input is capped\n * first: unbounded text is a denial-of-service surface, not just a size problem. */\nexport function scrubText(s: string | undefined, capturePII = false): string {\n if (!s) return s ?? ''\n s = truncate(s)\n s = redactSecrets(s)\n if (!capturePII) s = scrubPII(s)\n return s\n}\n","/*! anon.js — THE anonymous-identity chain. ONE implementation, two distributions.\n *\n * One browser is ONE person on every Hanzo surface, whichever client a page\n * happens to have loaded. There were once three implementations writing TWO keys —\n * `hz_anon_id` and `hz_id` — so the same visitor was several people depending on\n * which snippet the surface shipped.\n *\n * The two call sites:\n * 1. src/storage.ts — the bundled npm client; IMPORTS this file.\n * 2. hanzoai/cloud apps/analytics/tag.js — the tag served at\n * /v1/event.js; vendors this file and its tag.go serves the marked region\n * with the tag as one asset, so cloud holds no second copy either.\n *\n * (2) has no bundler and cannot import anything, which is why the chain lives in a\n * file that is plain ES5 rather than in a .ts: the region between the BEGIN and END\n * markers is COPIED VERBATIM. Keep it ES5, dependency-free, `hz`-prefixed (it is\n * spliced into other people's scopes) and unformatted — a reformat here is a diff\n * against the vendored copy. After editing, resync that copy:\n *\n * curl -fsSL https://unpkg.com/@hanzo/event/src/anon.js -o apps/analytics/anon.js\n */\n\n/* ── BEGIN hz anon chain — copied VERBATIM into hanzoai/cloud ──────────────── */\n\n/** The ONE anonymous-id key, on every surface and in every distribution.\n *\n * It names what it is, not who ships it: this value is visible to anyone who opens\n * their own cookie jar, and a vendor's name has no business there. Identity is IAM's\n * concern whether or not the visitor has said who they are. */\nvar HZ_ANON_KEY = 'iam-anon-id'\n\n/** Keys an id may already be living under. READ, never written.\n *\n * Each was canonical once — `hz_anon_id` most recently, and `hz_id` before it, when a\n * no-build tag wrote a second identity space and one page could hold two people.\n * Adopting an id already in the wild is what makes this a rename rather than an\n * erasure: minting over one detaches a returning visitor from their own history.\n * They are never written, so they leave with the cookies that hold them. */\nvar HZ_ANON_ADOPT = ['hz_anon_id', 'hz_id']\n\n/** The registrable domain the cookie is scoped to, so docs, cloud, console,\n * studio, pay, id and www all read the ONE id. localStorage cannot do this: it is\n * ORIGIN-scoped, which is what made one journey arrive as several strangers. */\nvar HZ_ANON_DOMAIN = 'hanzo.ai'\n\n/** Two years, rewritten on every read, so the cookie rolls forward with the\n * visitor instead of expiring two years after first touch. Safari caps a\n * SCRIPT-written cookie at 7 days no matter what this says, so the rewrite is\n * what keeps a returning Safari visitor: each read re-arms the 7-day window. */\nvar HZ_ANON_MAX_AGE = 2 * 365 * 24 * 60 * 60\n\n/** Last resort for a browser that refuses cookies AND localStorage: without it\n * every event in a page load would mint an id of its own. */\nvar hzAnonMemo\n\n/**\n * hzUuidv7 mints a time-ordered UUIDv7 (RFC 9562 §5.7) for `now` in epoch ms.\n *\n * It has to be v7, and this is the only minter any distribution may use. The\n * session rollups on the event plane derive a session's start instant FROM THE ID\n * and admit only ids whose version nibble is 7, so a crypto.randomUUID() (v4) id\n * is not merely unordered there — it is DISCARDED, silently, and the rollup stays\n * empty. Without crypto only the ENTROPY degrades; the shape is always a valid v7.\n */\nfunction hzUuidv7(now) {\n var b = new Uint8Array(16)\n var i\n var c = typeof crypto !== 'undefined' ? crypto : undefined\n if (c && typeof c.getRandomValues === 'function') c.getRandomValues(b)\n else for (i = 0; i < 16; i++) b[i] = (Math.random() * 256) | 0\n var t = Math.floor(now === undefined ? Date.now() : now)\n for (i = 5; i >= 0; i--) {\n b[i] = t % 256\n t = Math.floor(t / 256)\n }\n b[6] = 0x70 | (b[6] & 0x0f) // version 7\n b[8] = 0x80 | (b[8] & 0x3f) // variant 0b10\n var h = ''\n for (i = 0; i < 16; i++) {\n h += (b[i] + 0x100).toString(16).slice(1)\n if (i === 3 || i === 5 || i === 7 || i === 9) h += '-'\n }\n return h\n}\n\n/** The cookie jar, or null wherever there is no document to read one from. */\nfunction hzAnonJar() {\n try {\n if (typeof document === 'undefined' || typeof document.cookie !== 'string') return null\n return document\n } catch (e) {\n return null // sandboxed frame with an opaque origin\n }\n}\n\n/** localStorage, or null when the browser refuses it (Safari private mode). */\nfunction hzAnonStore() {\n try {\n if (typeof window === 'undefined' || !window.localStorage) return null\n return window.localStorage\n } catch (e) {\n return null\n }\n}\n\n/** One stored value, or '' — a jar can read as well as refuse to. */\nfunction hzAnonItem(store, name) {\n try {\n return (store && store.getItem(name)) || ''\n } catch (e) {\n return ''\n }\n}\n\n/** The value of cookie `name`, or ''. */\nfunction hzAnonCookie(name) {\n var d = hzAnonJar()\n if (!d) return ''\n var parts = d.cookie.split(';')\n for (var i = 0; i < parts.length; i++) {\n var eq = parts[i].indexOf('=')\n if (eq < 0 || parts[i].slice(0, eq).trim() !== name) continue\n var v = parts[i].slice(eq + 1).trim()\n if (!v) continue\n try {\n return decodeURIComponent(v)\n } catch (e) {\n return v // not percent-encoded — take it as written\n }\n }\n return ''\n}\n\n/** Writes `name` on the registrable domain, for as long as the browser allows. */\nfunction hzAnonWrite(name, value) {\n var d = hzAnonJar()\n if (!d) return\n var host = ''\n var secure = false\n try {\n if (typeof window !== 'undefined' && window.location) {\n host = window.location.hostname || ''\n // A Secure cookie is refused outright by a non-secure origin, which would\n // strand http://localhost dev on the localStorage path.\n secure = window.location.protocol === 'https:'\n }\n } catch (e) {\n /* location unreachable — write a host-only, non-secure cookie */\n }\n // encodeURIComponent leaves a UUID byte-identical while making any value that is\n // not one unable to forge a `;` and inject an attribute.\n var c = name + '=' + encodeURIComponent(value)\n c += '; Path=/; Max-Age=' + HZ_ANON_MAX_AGE + '; SameSite=Lax'\n // Off hanzo.ai (localhost, previews, other registrable domains) the attribute\n // would be rejected and the whole cookie dropped, so it stays host-only there.\n // Prefixing both sides with '.' matches the domain itself and its subdomains\n // while refusing a suffix that merely ends in the same letters (evilhanzo.ai).\n if (('.' + host).slice(-(HZ_ANON_DOMAIN.length + 1)) === '.' + HZ_ANON_DOMAIN) {\n c += '; Domain=' + HZ_ANON_DOMAIN\n }\n if (secure) c += '; Secure'\n try {\n d.cookie = c\n } catch (e) {\n /* cookies refused — localStorage still carries the id */\n }\n}\n\n/**\n * hzAnonId returns the stable anonymous id for this browser, '' during SSR.\n *\n * Resolution is strictly ADDITIVE — every id that already exists is ADOPTED, and\n * only a browser holding none of them is given a new one:\n *\n * cookie · localStorage iam-anon-id · adopted keys · in-memory · mint\n *\n * Minting over an id resets a returning visitor and detaches them from their own\n * history, so the order is the migration: the cookie is the shared home, the two\n * localStorage keys are what the three implementations wrote before it existed,\n * and each is read until nothing is left to adopt.\n *\n * localStorage keeps being written, so a rollback finds everyone where it left\n * them, and a browser that refuses cookies still holds one id per origin.\n */\n/**\n * hzAnonAdopted returns an id already living under a key that used to be canonical,\n * from the cookie jar first — that is the copy shared across subdomains, so a visitor\n * who arrived on one host is the same person on the next.\n */\nfunction hzAnonAdopted(s) {\n for (var i = 0; i < HZ_ANON_ADOPT.length; i++) {\n var id = hzAnonCookie(HZ_ANON_ADOPT[i]) || hzAnonItem(s, HZ_ANON_ADOPT[i])\n if (id) return id\n }\n return ''\n}\n\nfunction hzAnonId() {\n if (typeof window === 'undefined') return '' // SSR / prerender: no browser to identify\n var s = hzAnonStore()\n var id =\n hzAnonCookie(HZ_ANON_KEY) ||\n hzAnonItem(s, HZ_ANON_KEY) ||\n hzAnonAdopted(s) ||\n hzAnonMemo ||\n hzUuidv7()\n hzAnonMemo = id\n hzAnonWrite(HZ_ANON_KEY, id)\n try {\n if (s && s.getItem(HZ_ANON_KEY) !== id) s.setItem(HZ_ANON_KEY, id)\n } catch (e) {\n /* quota exhausted, or a private-mode jar that reads but refuses writes */\n }\n return id\n}\n\n/* ── END hz anon chain ─────────────────────────────────────────────────────── */\n\nexport { hzAnonId, hzUuidv7 }\n","// The ONE id minter for this client — UUIDv7 (RFC 9562 §5.7).\n//\n// The implementation is `hzUuidv7` in ./anon.js and this file only re-exports it.\n// It lives there because the anonymous-id chain has to mint too, and that chain is\n// inlined verbatim by the distribution that has no bundler (the tag\n// served at /v1/event.js) — a minter here as well would be a second implementation, and\n// the version nibble it produces is exactly the thing that must never diverge.\n//\n// WHY NOT crypto.randomUUID(): it mints v4, whose 122 bits are pure entropy and\n// carry no time. The session rollups on the event plane derive a session's start\n// instant FROM THE ID — their PARTITION BY and ORDER BY are\n// `fromUnixTimestamp(intDiv(toUInt64(bitShiftRight(session_id_v7, 80)), 1000))` —\n// so they admit only ids whose version nibble is 7\n// (`bitAnd(bitShiftRight(toUInt128(session_id), 76), 15) = 7`). A v4 session id is\n// not merely unordered there: it is DISCARDED by that predicate, silently, and the\n// rollup stays empty forever. Minting v7 is the whole reason those rollups can\n// exist; it also clusters index writes by time instead of scattering them across\n// the keyspace.\n//\n// Layout, 16 bytes big-endian:\n// 0..5 unix_ts_ms — 48-bit millisecond timestamp\n// 6 0111 (version 7) in the high nibble | rand_a\n// 7 rand_a\n// 8 10 (variant) in the high 2 bits | rand_b\n// 9..15 rand_b\n//\n// Never returns a non-UUID shape. The old minters fell back to `'a-' + base36`\n// when crypto was absent, and the plane casts a session id with\n// accurateCastOrNull(…, 'UUID') — a shape that does not parse becomes NULL and is\n// dropped by the same gate, so the fallback failed exactly like v4 did. Only the\n// ENTROPY degrades without crypto; the shape is always a valid v7 UUID.\n\n/**\n * uuidv7 mints a time-ordered UUIDv7 for `now` (epoch milliseconds).\n *\n * Two ids minted in the same millisecond sort arbitrarily between themselves; ids\n * from different milliseconds sort by time, lexically and numerically alike.\n */\nexport { hzUuidv7 as uuidv7 } from './anon.js'\n\n/** The millisecond timestamp a v7 id was minted at — the inverse of uuidv7. */\nexport function uuidv7Time(id: string): number {\n return parseInt(id.slice(0, 8) + id.slice(9, 13), 16)\n}\n","{\n \"name\": \"@hanzo/event\",\n \"version\": \"0.3.41\",\n \"description\": \"Hanzo Event \\u2014 the ONE telemetry client. Emits pageview/event/identify/group to the Hanzo Cloud event stream (POST /v1/event), AND reports errors to Sentry as real Sentry envelopes \\u2014 the error plane needs a DSN, without one nothing reaches Sentry. First-touch attribution, beacon-on-unload, auto error capture, client-side secret/PII scrubbing, a shared event + goal vocabulary. Subsumes @sentry.\",\n \"publishConfig\": {\n \"registry\": \"https://registry.npmjs.org/\",\n \"access\": \"public\",\n \"scope\": \"@hanzo\"\n },\n \"author\": \"Hanzo AI <[email protected]>\",\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.mjs\",\n \"types\": \"./dist/index.d.ts\",\n \"sideEffects\": false,\n \"files\": [\n \"dist\",\n \"src\",\n \"README.md\",\n \"TAXONOMY.md\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/hanzoai/ui.git\",\n \"directory\": \"pkgs/event\"\n },\n \"keywords\": [\n \"analytics\",\n \"product-analytics\",\n \"telemetry\",\n \"events\",\n \"error-tracking\",\n \"sentry\",\n \"attribution\",\n \"funnel\",\n \"hanzo\"\n ],\n \"scripts\": {\n \"prepublishOnly\": \"node ../../scripts/guard-publish.mjs\",\n \"build\": \"tsup && tsc --emitDeclarationOnly --declaration --outDir dist && node ../../scripts/dts.mjs\",\n \"dev\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"typecheck\": \"tsc --noEmit -p tsconfig.json\",\n \"clean\": \"rm -rf dist\"\n },\n \"exports\": {\n \".\": {\n \"import\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.mjs\"\n },\n \"require\": {\n \"types\": \"./dist/index.d.cts\",\n \"default\": \"./dist/index.cjs\"\n }\n },\n \"./react\": {\n \"import\": {\n \"types\": \"./dist/react.d.ts\",\n \"default\": \"./dist/react.mjs\"\n },\n \"require\": {\n \"types\": \"./dist/react.d.cts\",\n \"default\": \"./dist/react.cjs\"\n }\n }\n },\n \"peerDependencies\": {\n \"react\": \">=19.2.8\"\n },\n \"peerDependenciesMeta\": {\n \"react\": {\n \"optional\": true\n }\n },\n \"devDependencies\": {\n \"@types/node\": \"catalog:\",\n \"@types/react\": \"catalog:\",\n \"react\": \"catalog:\",\n \"tsup\": \"catalog:\",\n \"typescript\": \"catalog:\",\n \"vitest\": \"catalog:\"\n },\n \"dependencies\": {\n \"@hanzo/events\": \"catalog:\"\n }\n}\n","// The library version, stamped on every event (`libraryVersion`) and on the\n// Sentry `sdk` block. It lives alone so `sentry.ts` can read it without importing\n// `core.ts` — core imports sentry, so the reverse would be an import cycle.\n//\n// Read from package.json rather than restated, because a version restated is a\n// version that drifts: 0.3.38 and 0.3.39 both shipped while this file still said\n// 0.3.37, so every event those builds sent named a library that was two releases\n// behind. `resolveJsonModule` inlines the literal at build time, and tsup shakes\n// the rest of the manifest out.\nimport { version } from '../package.json'\n\nexport const VERSION: string = version\n","// Pure builders for the error path: parse a Hanzo-minted DSN, turn a JS Error into\n// a Sentry `event`, and frame it into a Sentry envelope. No I/O, no globals — the\n// Analytics client (core.ts) wires these to identity + transport.\n//\n// The wire shapes are a from-scratch model of the PUBLIC, documented Sentry ingest\n// protocol (develop.sentry.dev), verified against the Hanzo /v1/event ingest\n// (o11y pkg/modules/errortracking/implerrortracking: parseEnvelope, SentryEvent,\n// normalizeEvent, computeFingerprint). No upstream (FSL) code is used.\n\nimport { scrubText } from './scrub'\nimport { uuidv7 } from './uid'\nimport { VERSION } from './version'\nimport type {\n CaptureErrorOptions,\n Dsn,\n SentryEvent,\n SentryFrame,\n SentryLevel,\n} from './types'\n\n/** Max stack frames kept — well under the server's 250 cap, plenty for grouping. */\nconst MAX_FRAMES = 50\n/** Max stack lines examined, and max length of a line worth examining. Guards the\n * frame regexes against a hostile `stack` string (see framesFromStack). */\nconst MAX_LINES = 500\nconst MAX_LINE_LEN = 2048\n/** Max tag string length, so one huge property can't bloat the envelope. */\nconst MAX_TAG_LEN = 1024\n/** Max tags copied from properties. */\nconst MAX_TAGS = 50\n\n/** eventId mints a 32-hex-char id (no dashes) — the Sentry event_id shape. Same\n * minter as everything else, just formatted for Sentry's wire. */\nexport function eventId(): string {\n return uuidv7().replace(/-/g, '')\n}\n\n/** byteLen returns the UTF-8 byte length used for envelope item framing. */\nfunction byteLen(s: string): number {\n if (typeof TextEncoder !== 'undefined') return new TextEncoder().encode(s).length\n // Node fallback.\n if (typeof Buffer !== 'undefined') return Buffer.byteLength(s, 'utf8')\n return s.length\n}\n\n/**\n * parseDsn parses \"https://<version>:<hmac>@<host>/v1/event/<projectId>\" into its\n * public key + the derived envelope ingest URL. The key (which itself contains a\n * ':') is taken verbatim as the userinfo — we do NOT split it as user:pass. The\n * key rides ?sentry_key= (not the DSN in the body) because that is the credential\n * channel the server trusts AND the only one sendBeacon can carry on unload.\n * Returns null for anything malformed (fail-safe: the caller then stays inert).\n */\nexport function parseDsn(dsn: string | undefined | null): Dsn | null {\n if (!dsn) return null\n const m = /^(https?):\\/\\/([^@]+)@([^/]+)(\\/.*)?$/.exec(dsn.trim())\n if (!m) return null\n const scheme = m[1]\n const publicKey = m[2]\n const host = m[3]\n const path = m[4] ?? ''\n if (!publicKey || !host) return null\n const segs = path.split('/').filter(Boolean)\n const projectId = segs.length > 0 ? segs[segs.length - 1] : ''\n if (!projectId) return null\n const origin = `${scheme}://${host}`\n // THE DSN'S OWN PATH IS THE ADDRESS — derived here, never restated. It used to\n // be rebuilt as a literal, which threw away the path the DSN carried and wrote\n // the ingest address a second time; buildEnvelope wrote it a third. Three copies\n // of one fact. dsnForProduct (dsn.ts) is the ONE place it is spelled; this\n // follows it.\n const base = `${origin}/${segs.map(encodeURIComponent).join('/')}`\n const ingestUrl = `${base}/envelope/?sentry_key=${encodeURIComponent(publicKey)}`\n return { publicKey, origin, projectId, base, ingestUrl }\n}\n\nconst V8_FRAME = /^\\s*at\\s+(?:(.+?)\\s+\\()?(?:(.+?):(\\d+):(\\d+)|([^)]+))\\)?\\s*$/\nconst MOZ_FRAME = /^\\s*(?:(.*?)@)?(.+?):(\\d+):(\\d+)\\s*$/\n\n/** inApp marks a frame as the app's own code (vs vendor/runtime) for grouping. */\nfunction inApp(file: string): boolean {\n if (!file) return false\n return !(\n file.includes('node_modules') ||\n file.startsWith('webpack-internal') ||\n file.startsWith('webpack://') ||\n file.startsWith('chrome-extension://') ||\n file.startsWith('moz-extension://')\n )\n}\n\n/**\n * framesFromStack parses a browser Error.stack into Sentry frames, OLDEST-FIRST\n * (Sentry orders caller->callee; the crash site is last — matching the server's\n * pickCrashFrame). Handles both V8 (\"at fn (file:li:co)\") and\n * Firefox/Safari (\"fn@file:li:co\"). Unparseable lines are skipped.\n */\nexport function framesFromStack(stack: string | undefined): SentryFrame[] {\n if (!stack) return []\n // Both frame regexes use lazy nested quantifiers, which backtrack badly on a\n // long line that never matches. A stack is attacker-influenced (a thrown value\n // can carry any `stack` string), so bound the work: skip absurd lines and stop\n // after MAX_LINES. Only the innermost MAX_FRAMES are kept anyway.\n const lines = stack.split('\\n', MAX_LINES)\n const frames: SentryFrame[] = []\n for (const raw of lines) {\n if (raw.length > MAX_LINE_LEN) continue\n const line = raw.trimEnd()\n if (!line) continue\n // Header lines like \"TypeError: x is not a function\" match neither frame\n // regex (no \"at \" prefix, no trailing :line:col) and are skipped naturally.\n let fn: string | undefined\n let file = ''\n let lineno = 0\n let colno = 0\n const v = V8_FRAME.exec(line)\n if (v) {\n fn = v[1]\n if (v[2]) {\n file = v[2]\n lineno = Number(v[3]) || 0\n colno = Number(v[4]) || 0\n } else {\n file = (v[5] || '').trim()\n }\n } else {\n const f = MOZ_FRAME.exec(line)\n if (!f) continue\n fn = f[1]\n file = f[2]\n lineno = Number(f[3]) || 0\n colno = Number(f[4]) || 0\n }\n if (!file && !fn) continue\n frames.push({\n function: fn || '<anonymous>',\n filename: file,\n abs_path: file,\n lineno,\n colno,\n in_app: inApp(file),\n })\n }\n // Reverse to oldest-first and cap to the innermost MAX_FRAMES.\n frames.reverse()\n if (frames.length > MAX_FRAMES) return frames.slice(frames.length - MAX_FRAMES)\n return frames\n}\n\n/** normalizeError coerces an unknown throwable into {name, message, stack}. */\nexport function normalizeError(err: unknown): { name: string; message: string; stack?: string } {\n if (err instanceof Error) {\n // `name`, `message` and `stack` are ordinary getters, and the thrown object is\n // the least trustworthy input this library handles — any of them may throw.\n // Losing the whole report to a hostile getter is not acceptable.\n const name = read(err, 'name')\n const message = read(err, 'message')\n const stack = read(err, 'stack')\n return {\n name: typeof name === 'string' && name ? name : 'Error',\n message: typeof message === 'string' && message ? message : str(err),\n stack: typeof stack === 'string' ? stack : undefined,\n }\n }\n if (typeof err === 'string') return { name: 'Error', message: err }\n try {\n return { name: 'Error', message: JSON.stringify(err) ?? str(err) }\n } catch {\n return { name: 'Error', message: str(err) }\n }\n}\n\n/** read pulls a property off a possibly-hostile value without letting a throwing\n * getter escape. */\nfunction read(o: unknown, k: string): unknown {\n try {\n return (o as Record<string, unknown>)[k]\n } catch {\n return undefined\n }\n}\n\n/** str coerces to a string without letting a throwing toString/Symbol.toPrimitive\n * escape. */\nfunction str(v: unknown): string {\n try {\n return String(v)\n } catch {\n return '[unstringifiable]'\n }\n}\n\n/** Identity carried onto every error event — the SAME ids analytics uses. */\nexport interface ErrorIdentity {\n /** OIDC sub (post-identify) or anon id. NEVER email/PII. */\n userId?: string\n sessionId?: string\n product?: string\n release?: string\n environment?: string\n}\n\nexport interface BuildEventInput {\n error: unknown\n options?: CaptureErrorOptions\n identity: ErrorIdentity\n capturePII?: boolean\n /** Injectable for deterministic tests. */\n now?: number\n id?: string\n}\n\nfunction coerceTag(v: unknown): string {\n const s = typeof v === 'string' ? v : (() => {\n try {\n return JSON.stringify(v) ?? String(v)\n } catch {\n try {\n return String(v) // circular — fall back to the primitive coercion\n } catch {\n return '[unstringifiable]' // ...which a throwing toString can also refuse\n }\n }\n })()\n return s.length > MAX_TAG_LEN ? s.slice(0, MAX_TAG_LEN) : s\n}\n\n/**\n * buildSentryEvent turns a throwable + identity into a Sentry `event`. The message\n * (the leak surface) is scrubbed client-side; the user is ONLY the stable subject\n * id — never email/username/ip. Level defaults to error, or fatal for uncaught.\n */\nexport function buildSentryEvent(input: BuildEventInput): SentryEvent {\n const { error, options = {}, identity, capturePII = false } = input\n const norm = normalizeError(error)\n const handled = options.handled !== false\n const level: SentryLevel = options.level ?? (handled ? 'error' : 'fatal')\n\n const tags: Record<string, string> = { handled: String(handled) }\n if (identity.product) tags.product = identity.product\n if (identity.sessionId) tags.session = identity.sessionId\n // Tags come from arbitrary caller `properties`. Read each key in isolation:\n // Object.entries() invokes every getter at once, so one throwing getter would\n // cost us the entire envelope — losing the stack trace to save a tag. Keys are\n // optional; the exception is not.\n try {\n const props = (options.properties ?? {}) as Record<string, unknown>\n let n = 0\n for (const k of Object.keys(props)) {\n if (n >= MAX_TAGS) break\n try {\n const val = props[k]\n if (val === undefined || val === null) continue\n tags[k] = scrubText(coerceTag(val), capturePII)\n n++\n } catch {\n continue // throwing getter or unstringifiable value — skip this key only\n }\n }\n } catch {\n /* unenumerable/exotic properties object — ship the exception without tags */\n }\n\n const event: SentryEvent = {\n event_id: input.id ?? eventId(),\n timestamp: (input.now ?? Date.now()) / 1000,\n platform: 'javascript',\n level,\n logger: identity.product,\n environment: identity.environment,\n release: identity.release,\n exception: {\n values: [\n {\n type: norm.name,\n value: scrubText(norm.message, capturePII),\n stacktrace: { frames: framesFromStack(norm.stack) },\n },\n ],\n },\n tags,\n sdk: { name: '@hanzo/event', version: VERSION },\n }\n if (identity.userId) event.user = { id: identity.userId }\n return event\n}\n\n/**\n * buildEnvelope frames a Sentry event into a newline-delimited envelope:\n *\n * {\"event_id\",\"dsn\",\"sent_at\"}\\n\n * {\"type\":\"event\",\"content_type\":\"application/json\",\"length\":N}\\n\n * <event json>\\n\n *\n * The item is length-delimited (N = UTF-8 byte length) — the framing the server's\n * parseEnvelope reads first (falling back to newline-delimited otherwise).\n */\nexport function buildEnvelope(event: SentryEvent, dsn: Dsn, sentAt?: string): string {\n const payload = JSON.stringify(event)\n const header = JSON.stringify({\n event_id: event.event_id,\n dsn: dsn.base,\n sent_at: sentAt ?? new Date().toISOString(),\n })\n const itemHeader = JSON.stringify({\n type: 'event',\n content_type: 'application/json',\n length: byteLen(payload),\n })\n return `${header}\\n${itemHeader}\\n${payload}\\n`\n}\n","// The `$exception` product-event shape — pure builders, no I/O.\n//\n// An exception reaches TWO destinations, and they read different shapes:\n//\n// 1. The ERROR PLANE — a Sentry envelope to the DSN host (sentry.ts). Untouched\n// by this module.\n// 2. ERROR TRACKING in Hanzo Insights — reads the PRODUCT-EVENT plane\n// (`insights.events WHERE event = '$exception'`) and expects the PostHog\n// exception vocabulary: `$exception_list` plus the denormalized `$exception_*`\n// properties. This module builds that.\n//\n// WHY THE FULLY-DERIVED SHAPE. Upstream, a server stage (Cymbal) symbolicates\n// frames and computes `$exception_fingerprint`, `$exception_types`, `_values`,\n// `_sources`, `_functions`, then REPLACES the property bag. That stage is not in\n// Hanzo's path: the ingest endpoint writes `event.fact` and a materialized view projects it\n// into `insights.events`, so nothing between the client and the warehouse derives\n// anything. Whatever the product reads, the client has to have sent. Two\n// consequences are load-bearing rather than cosmetic:\n//\n// • The issue query drops any event whose `$exception_fingerprint` is null, so an\n// event without one is invisible no matter how well-formed the rest is.\n// • `stacktrace.type` must be the literal 'resolved'; on any other value the\n// renderer falls through both match arms and draws nothing under the header.\n//\n// If that server stage is ever put in front of this plane it replaces these\n// properties wholesale, so sending them stays correct either way.\n//\n// FRAME ORDER is bottom-up: frames[0] is the entry point and the LAST frame is the\n// throw site. framesFromStack (sentry.ts) already returns that order, which is why\n// this module reuses it rather than re-parsing.\n\nimport { framesFromStack, normalizeError } from './sentry'\nimport type {\n ExceptionEntry,\n ExceptionFrame,\n ExceptionProperties,\n SentryFrame,\n SentryLevel,\n} from './types'\n\n/** Cap on the frames carried per exception, matching the Sentry path's budget. */\nconst MAX_FRAMES = 50\n/** Cap on `value`, so one enormous message cannot dominate a batch. The product\n * truncates for display anyway; this bounds the wire. */\nconst MAX_VALUE = 4096\n\n/**\n * digest is a stable 32-hex-char (128-bit) content hash, computed synchronously.\n *\n * Grouping keys are needed on the capture path, which is synchronous and may be\n * running inside an unload handler — SubtleCrypto is async and unavailable on\n * insecure origins, so it cannot be used here. This is FNV-1a run over four seeds\n * and concatenated. It is a GROUPING key, never a security boundary: it is not\n * collision-resistant against a chosen-input adversary, and nothing authorizes or\n * authenticates on it. The product treats the value as opaque.\n */\nexport function digest(s: string): string {\n let out = ''\n for (const seed of [0x811c9dc5, 0x01000193, 0x9e3779b9, 0x85ebca6b]) {\n let h = seed >>> 0\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i)\n // FNV prime 16777619, via shifts to stay in 32-bit integer math.\n h = (h + (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24)) >>> 0\n }\n out += h.toString(16).padStart(8, '0')\n }\n return out\n}\n\n/**\n * frameOf converts one parsed stack frame into the shape the product renders.\n *\n * The product's frame vocabulary is the POST-symbolication one (`mangled_name`,\n * `source`, `line`, `column`), so that is what is emitted. `resolved` is false and\n * honest: a browser bundle is minified and no symbol set has been uploaded, so the\n * original name and line are genuinely unknown. Claiming `resolved: true` would\n * trade an accurate \"upload your symbol sets\" hint for a misleading \"source code is\n * not available\" error.\n */\nfunction frameOf(f: SentryFrame): ExceptionFrame {\n const source = f.filename ?? ''\n const name = f.function ?? '<anonymous>'\n const line = f.lineno ?? 0\n const column = f.colno ?? 0\n return {\n // Stable per code location, so the same frame groups and re-renders under one\n // key. The '/part' suffix is the product's format for one raw frame expanding\n // into several resolved ones; a browser frame is always part 0.\n raw_id: `${digest(`${source}|${name}|${line}|${column}`)}/0`,\n mangled_name: name,\n source,\n line,\n column,\n in_app: f.in_app ?? false,\n lang: 'javascript',\n resolved: false,\n resolve_failure: 'no symbol set uploaded for this release',\n resolved_name: null,\n module: null,\n }\n}\n\n/** truncate bounds a free-text field without splitting a surrogate pair. */\nfunction truncate(s: string, max: number): string {\n if (s.length <= max) return s\n let end = max\n const c = s.charCodeAt(end - 1)\n // A high surrogate at the cut point would leave a lone half.\n if (c >= 0xd800 && c <= 0xdbff) end--\n return s.slice(0, end)\n}\n\n/**\n * exceptionEntry builds the single `$exception_list` entry for a throwable.\n *\n * One entry, not a chain: `Error.cause` chaining is a distinct fact with its own\n * ordering rules, and emitting it wrongly is worse than not emitting it.\n */\nexport function exceptionEntry(\n err: unknown,\n opts: { handled: boolean; id: string },\n): ExceptionEntry {\n const n = normalizeError(err)\n const frames = framesFromStack(n.stack).slice(-MAX_FRAMES)\n const entry: ExceptionEntry = {\n id: opts.id,\n type: n.name,\n value: truncate(n.message, MAX_VALUE),\n mechanism: {\n type: 'generic',\n handled: opts.handled,\n // An exception the app reported by hand was constructed, not thrown by the\n // runtime at a real call site.\n synthetic: !(err instanceof Error),\n },\n }\n if (frames.length > 0) {\n // 'resolved' names the SHAPE of the frame list, not the symbolication state of\n // any frame — the renderer only walks frames on this exact value.\n entry.stacktrace = { type: 'resolved', frames: frames.map(frameOf) }\n }\n return entry\n}\n\n/**\n * fingerprint is the issue grouping key.\n *\n * Keyed on exception type plus each in-app frame's function and source — the same\n * pieces the server-side grouper records (\"Exception Type\", \"Resolved function\n * name\", \"Source file name\"). Deliberately NOT the message: `Loading chunk 3324\n * failed` and `Loading chunk 998 failed` are one bug, and grouping on message is\n * precisely the mistake that made every distinct error string its own event name.\n *\n * Falls back to the type alone when no in-app frame survived, which keeps\n * stackless errors (`Script error.`, cross-origin) in one issue instead of\n * scattering them.\n */\nexport function fingerprint(entry: ExceptionEntry): string {\n const frames = entry.stacktrace?.frames ?? []\n const pieces = frames\n .filter((f) => f.in_app)\n .map((f) => `${f.mangled_name}@${f.source}`)\n return digest([entry.type, ...pieces].join('\\n'))\n}\n\n/**\n * exceptionProperties builds the full `$exception_*` property bag for one captured\n * throwable — everything Error Tracking reads off the event.\n *\n * The denormalized arrays are ordered like `frames`: last element is the throw\n * site, which is the element the issue list indexes at -1 for its source/function\n * columns.\n */\nexport function exceptionProperties(\n err: unknown,\n opts: { handled: boolean; id: string; level?: SentryLevel },\n): ExceptionProperties {\n const entry = exceptionEntry(err, opts)\n const frames = entry.stacktrace?.frames ?? []\n const fp = fingerprint(entry)\n return {\n $exception_list: [entry],\n $exception_fingerprint: fp,\n // Records WHY this fingerprint holds. 'manual' is the honest value: the client\n // supplied the key rather than a server grouper deriving one.\n $exception_fingerprint_record: [{ type: 'manual' }],\n $exception_type: entry.type,\n $exception_message: entry.value,\n $exception_level: opts.level ?? 'error',\n $exception_handled: opts.handled,\n $exception_synthetic: entry.mechanism?.synthetic ?? false,\n $exception_types: [entry.type],\n $exception_values: [entry.value],\n $exception_sources: frames.map((f) => f.source).filter((s): s is string => !!s),\n $exception_functions: frames\n .map((f) => f.resolved_name ?? f.mangled_name)\n .filter((s): s is string => !!s),\n }\n}\n","// SSR-safe browser storage for stable identifiers and first-touch state. Every\n// accessor no-ops (returns undefined) when there is no window/localStorage, so the\n// client imports cleanly in a Next.js server component.\n//\n// The anonymous id is the one value that must outlive an ORIGIN, and it is also\n// the one value the OTHER two distributions must agree with, so its chain lives in\n// ./anon.js and this file only calls it. Everything else here stays origin-local.\n\nimport { hzAnonId } from './anon.js'\nimport type { Attribution, Cohort } from './types'\nimport { uuidv7 } from './uid'\n\nconst KEY = {\n session: 'hz_session',\n firstTouch: 'hz_first_touch',\n cohort: 'hz_cohort',\n} as const\n\n/** 30-minute inactivity window defines a session (PostHog/GA convention). */\nconst SESSION_TTL_MS = 30 * 60 * 1000\n\nfunction ls(): Storage | undefined {\n try {\n if (typeof window === 'undefined' || !window.localStorage) return undefined\n return window.localStorage\n } catch {\n return undefined // Safari private mode / blocked storage\n }\n}\n\n/**\n * Stable anonymous id, shared by every *.hanzo.ai surface AND by every Hanzo\n * client on the page — the npm client and the hosted tag both run the one\n * chain in ./anon.js, so which snippet a surface loaded no longer decides who the\n * visitor is.\n *\n * It lives in a first-party cookie on the registrable domain because localStorage\n * is ORIGIN-scoped: docs, cloud and console each minted their own id for the same\n * person, so one marketing → docs → signup → checkout journey arrived as several\n * strangers — 463 anonymous identities carried 545 events in a week, about 1.2\n * events each.\n */\nexport function anonId(): string | undefined {\n return hzAnonId() || undefined // '' during SSR / prerender\n}\n\ninterface SessionState {\n id: string\n last: number\n}\n\n/**\n * Current session id, rotated after SESSION_TTL_MS of inactivity.\n *\n * Minted at `now`, so the v7 timestamp the id carries IS the session's start\n * instant — which is what the session rollups partition and order on. Passing the\n * caller's clock rather than reading Date.now() again keeps the id's embedded time\n * and the recorded `last` from disagreeing.\n */\nexport function sessionId(now = Date.now()): string | undefined {\n const s = ls()\n if (!s) return undefined\n let state: SessionState | null = null\n try {\n state = JSON.parse(s.getItem(KEY.session) || 'null')\n } catch {\n state = null\n }\n if (!state || now - state.last > SESSION_TTL_MS) {\n state = { id: uuidv7(now), last: now }\n } else {\n state.last = now\n }\n s.setItem(KEY.session, JSON.stringify(state))\n return state.id\n}\n\n/** Read the persisted first-touch attribution. */\nexport function getFirstTouch(): Attribution | undefined {\n const s = ls()\n if (!s) return undefined\n try {\n const v = s.getItem(KEY.firstTouch)\n return v ? (JSON.parse(v) as Attribution) : undefined\n } catch {\n return undefined\n }\n}\n\n/** Persist first-touch attribution ONCE — never overwrite an existing record. */\nexport function setFirstTouchOnce(a: Attribution): Attribution {\n const s = ls()\n const existing = getFirstTouch()\n if (existing) return existing\n if (s) s.setItem(KEY.firstTouch, JSON.stringify(a))\n return a\n}\n\n/** Read persisted cohort dimensions. */\nexport function getCohort(): Cohort | undefined {\n const s = ls()\n if (!s) return undefined\n try {\n const v = s.getItem(KEY.cohort)\n return v ? (JSON.parse(v) as Cohort) : undefined\n } catch {\n return undefined\n }\n}\n\n/** Merge + persist cohort dimensions (signupWeek set once). */\nexport function mergeCohort(patch: Cohort): Cohort {\n const s = ls()\n const cur = getCohort() || {}\n const next: Cohort = {\n signupWeek: cur.signupWeek || patch.signupWeek,\n channel: patch.channel || cur.channel,\n refCode: cur.refCode || patch.refCode,\n }\n if (s) s.setItem(KEY.cohort, JSON.stringify(next))\n return next\n}\n","// The framework-agnostic event client. ONE API surface over TWO orthogonal\n// planes, sharing one session and one identity:\n//\n// 1. EVENT STREAM — buffered pageview/event/identify/group, flushed as ONE\n// batch to the Hanzo Cloud entry point:\n// POST {host}/v1/event body: { batch: [Event, …] } -> { accepted, dropped }\n// Cloud resolves the tenant server-side (validated session, or the signed\n// publishable key) and stamps it; the client NEVER sends the org.\n//\n// 2. ERROR PLANE — every captured exception is ALSO framed as a real Sentry\n// envelope and POSTed to the error host named by the DSN:\n// POST {dsn.base}/envelope/?sentry_key=… ({base} = /v1/event/{projectId})\n// This is what reaches sentry.hanzo.ai (issues, grouping, stack frames).\n//\n// These are NOT the same pipe and one does NOT feed the other. The event stream\n// stores a `type:'error'` row in the cloud event warehouse (readable via\n// GET /v1/errors) — that is product signal, not error tracking. There is no\n// server-side fan-out from /v1/event into Sentry; without the envelope below,\n// nothing ever reaches sentry.hanzo.ai. An earlier revision of this file claimed\n// the one entry point was \"lensed server-side into … error tracking (sentry)\". It was\n// wrong, and it silently cost the fleet all of its error telemetry.\n//\n// The error plane is inert (fail-safe) when no DSN is configured: nothing is\n// sent, nothing throws, and the event stream is unaffected.\n//\n// Auth is orthogonal — the SAME body to the SAME endpoint, differing only in how the\n// caller proves its tenant:\n//\n// • cookie/session app (host:'') — same-origin credentials ride the request.\n// • bearer app (getToken) — Authorization: Bearer <jwt>.\n// • publishable-key app (ingestKey: 'pk-…') — ?ingest_key=pk-… on the query, on\n// the fetch and the beacon alike, with a text/plain body. Write-only and safe\n// to ship in a bundle; the endpoint resolves it to an org server-side. The query\n// is the carrier neither send needs a header for, which is what keeps both\n// CORS-simple and therefore sendable from a customer's own origin.\n//\n// The wire is the canonical `Event` (== the cloud CaptureEvent): its `type` field\n// is what Cloud folds to event_type='error', which is how the event WAREHOUSE\n// classifies the row (GET /v1/errors). That is the extent of it — the fold does\n// not forward anything to Sentry. The error dashboard is fed only by the envelope\n// in plane 2 above, and only when a DSN is set.\n\nimport {\n parseAttribution,\n hasAttribution,\n deriveChannel,\n} from './attribution'\nimport { dsnForProduct } from './dsn'\nimport { keyForPage } from './org'\nimport { EXCEPTION, PAGEVIEW } from './events'\nimport { exceptionProperties } from './exception'\nimport { scrubText } from './scrub'\nimport {\n buildEnvelope,\n buildSentryEvent,\n normalizeError as normalizeThrowable,\n parseDsn,\n type ErrorIdentity,\n} from './sentry'\nimport {\n anonId,\n sessionId,\n getFirstTouch,\n setFirstTouchOnce,\n getCohort,\n mergeCohort,\n} from './storage'\nimport { uuidv7 } from './uid'\nimport type {\n AnalyticsConfig,\n Attribution,\n CaptureErrorOptions,\n Cohort,\n Dsn,\n EventKind,\n Exception,\n Transport,\n WireEvent,\n} from './types'\nimport { VERSION } from './version'\n\nexport { VERSION }\n\nconst EVENT_PATH = '/v1/event' // the ONE canonical ingestion entry point\nconst DEFAULT_HOST = 'https://api.hanzo.ai' // the one edge; cookie apps pass host:''\nconst ENVELOPE_CONTENT_TYPE = 'application/x-sentry-envelope'\n// The beacon body's type. text/plain is CORS-SAFELISTED, which is the whole\n// property: a safelisted type makes the POST a SIMPLE request, and a simple\n// request needs no preflight. An unloading document does not get a second round\n// trip, so cross-origin a preflighted beacon is never sent at all. The endpoint reads\n// the raw body and dispatches on its first non-space byte, so the type names the\n// CORS class and nothing else.\nconst BEACON_CONTENT_TYPE = 'text/plain'\n\n/** readEnvDsn resolves a DSN from the public env when config omits one, so an app\n * gets the error plane by setting ONE build-time variable and nothing else.\n * Next/Vite inline these at build; the access is guarded so it is safe in a bare\n * browser and during SSR/prerender where `process` may not exist. */\nfunction readEnvDsn(): string | undefined {\n try {\n if (typeof process !== 'undefined' && process.env) {\n return process.env.NEXT_PUBLIC_HANZO_EVENT_DSN || process.env.HANZO_EVENT_DSN || undefined\n }\n } catch {\n /* no process — browser without inlined env */\n }\n return undefined\n}\n\n/** readEnv reads an inlined build-time variable, guarded like readEnvDsn. */\nfunction readEnv(name: string): string | undefined {\n try {\n if (typeof process !== 'undefined' && process.env) return process.env[name] || undefined\n } catch {\n /* no process */\n }\n return undefined\n}\n\n/** appendQuery adds a single query param to a URL string — used to carry a\n * publishable key on either send (?ingest_key=…). */\nfunction appendQuery(url: string, key: string, value: string): string {\n return url + (url.includes('?') ? '&' : '?') + key + '=' + encodeURIComponent(value)\n}\n\n/** Normalize anything thrown (Error | string | unknown) into an Exception. */\n/** normalizeError adapts the shared, hostile-input-safe normalizer (sentry.ts) to\n * the event stream's Exception shape. ONE normalizer serves both planes: a thrown\n * object may define `name`/`message`/`stack` as throwing getters, and when each\n * plane rolled its own reader the stream still lost the report that the error\n * plane had already survived. */\nfunction normalizeError(err: unknown): Exception {\n const n = normalizeThrowable(err)\n return { type: n.name, message: n.message, stack: n.stack }\n}\n\n// React Native defines a `window` but no `document` (and no `window.location`),\n// so `typeof window !== 'undefined'` alone took the browser path there and\n// `init()` threw on `window.location.search`. Requiring `document` makes RN read\n// as non-browser: `init()` returns early (skips attribution + the visibility/\n// unload listeners RN doesn't have), while `capture()`/`flush()` — which are NOT\n// gated on this and post through plain `fetch` — still emit. So the client is\n// inert-safe under SSR/Node and live on RN, without a second transport.\nconst isBrowser = () => typeof window !== 'undefined' && typeof document !== 'undefined'\n\n/** serializeBatch stringifies a batch, salvaging what it can. `properties` is\n * arbitrary caller data — a DOM node, a React synthetic event, an axios error are\n * all circular, and a getter on one can throw — so a whole batch must never be\n * lost to a single poisoned event. Falls back to per-event serialization, then to\n * keeping the event and dropping its properties. Returns null only when nothing\n * at all survives. */\nfunction serializeBatch(batch: WireEvent[]): string | null {\n try {\n return JSON.stringify({ batch })\n } catch {\n /* one bad event — salvage the rest below */\n }\n const parts: string[] = []\n for (const e of batch) {\n try {\n parts.push(JSON.stringify(e))\n } catch {\n try {\n // Keep the event (identity, session, type all matter); drop the payload\n // that could not be serialized, and say so rather than lying by omission.\n parts.push(JSON.stringify({ ...e, properties: { $unserializable: true } }))\n } catch {\n /* unsalvageable — drop this ONE event, never the batch */\n }\n }\n }\n return parts.length > 0 ? '{\"batch\":[' + parts.join(',') + ']}' : null\n}\n\n/** DefaultTransport: fetch(keepalive) for authenticated/normal sends;\n * navigator.sendBeacon for page-unload beacons.\n *\n * A publishable key rides the ?ingest_key query on BOTH, which is what keeps\n * both CORS-simple: the query is the one carrier neither send needs a header\n * for, and a simple request leaves the browser from any origin — permission is\n * asked to READ a cross-origin response, never to send one. That is what lets a\n * bundled client run on a customer's own page. A JWT is first-party by\n * construction, so it keeps Authorization and the session it travels with.\n *\n * `contentType` names the FETCH request's Content-Type (the error plane sets the\n * envelope type there, and keeps the header carrier with it). The beacon body is\n * always BEACON_CONTENT_TYPE: its type is a CORS class, not a payload\n * description, and a beacon that is not a simple request is never sent. */\nclass DefaultTransport implements Transport {\n send(\n url: string,\n body: string,\n opts: {\n beacon: boolean\n token?: string\n ingestKey?: string\n contentType?: string\n debug?: boolean\n },\n ): void {\n if (opts.beacon && isBrowser() && typeof navigator.sendBeacon === 'function') {\n const beaconUrl = opts.ingestKey ? appendQuery(url, 'ingest_key', opts.ingestKey) : url\n try {\n // The answer is whether the agent QUEUED the batch — false for a body past\n // the beacon size limit, or a full queue. Only a queued batch ends the\n // send; a refused one falls through to the keepalive fetch below.\n const queued = navigator.sendBeacon(\n beaconUrl,\n new Blob([body], { type: BEACON_CONTENT_TYPE }),\n )\n if (queued) return\n if (opts.debug) console.warn('[event] beacon refused, falling back to fetch')\n } catch {\n /* fall through to fetch */\n }\n }\n if (typeof fetch !== 'function') return\n // A publishable key makes the send a CORS SIMPLE request: the key rides the\n // query, the body is text/plain, and nothing asks for credentials. That is what\n // lets a bundled client run on a customer's own page — every send from there is\n // cross-origin, and an Authorization header or a JSON type preflights the POST\n // for an origin that cannot pass one. A JWT is first-party by construction, so\n // it keeps the header and the session it travels with, and so does the error\n // plane, which names its own envelope type.\n const simple = opts.ingestKey !== undefined && opts.contentType === undefined\n const headers: Record<string, string> = {\n 'Content-Type': simple ? BEACON_CONTENT_TYPE : (opts.contentType ?? 'application/json'),\n }\n const bearer = simple ? undefined : (opts.ingestKey ?? opts.token)\n if (bearer) headers.Authorization = `Bearer ${bearer}`\n void fetch(simple ? appendQuery(url, 'ingest_key', opts.ingestKey!) : url, {\n method: 'POST',\n headers,\n body,\n keepalive: true,\n credentials: simple ? 'omit' : 'include',\n })\n .then((res) => {\n // Telemetry loss never throws into the app — but silence is how this\n // client lost the fleet's errors in the first place. Under `debug`, say\n // so. A rejected ingest (the CORS allowlist 403s non-production origins,\n // so local dev NEVER reports) is otherwise indistinguishable from success.\n if (!res.ok && opts.debug) {\n console.warn('[event] ingest rejected', res.status, url.split('?')[0])\n }\n })\n .catch((e: unknown) => {\n if (opts.debug) console.warn('[event] ingest failed', url.split('?')[0], e)\n })\n }\n}\n\nexport class Analytics {\n private cfg: Required<\n Pick<AnalyticsConfig, 'product' | 'batchSize' | 'flushIntervalMs' | 'enabled' | 'captureErrors'>\n > &\n AnalyticsConfig\n private transport: Transport\n private queue: WireEvent[] = []\n private timer: ReturnType<typeof setTimeout> | null = null\n private personId?: string\n private attribution: Attribution = { utm: {} }\n private cohort: Cohort = {}\n private started = false\n /** The view pageview() last counted — path + location. */\n private counted?: string\n /** Parsed error-plane DSN, or null when the plane is inert. */\n private dsn: Dsn | null\n /** Guards against an error thrown *inside* the error path re-entering it. */\n private reentrant = false\n\n constructor(config: AnalyticsConfig) {\n this.cfg = {\n host: DEFAULT_HOST,\n batchSize: 20,\n flushIntervalMs: 5000,\n enabled: true,\n captureErrors: true,\n ...config,\n // The publishable key resolves from ONE live source, never a literal baked\n // beside the code. Most specific first:\n // 1. an explicit `ingestKey` in config;\n // 2. NEXT_PUBLIC_PUBLISHABLE_KEY, the inlined build-time env the fleet\n // carries end to end — KMS `deploy/PUBLISHABLE_KEY` -> the PUBLISHABLE_KEY\n // build-arg -> the NEXT_PUBLIC_ prefix Next inlines.\n //\n // 3. the org that owns the HOST this page is served from (`org.ts`).\n //\n // Step 3 fires only when a surface configured NOTHING, so it never overrides\n // the KMS chain above — it replaces going dark with reporting to the right\n // brand. That is what a static export needs: `output: export` inlines no env,\n // so before this the only way to report was to commit a key literal, and the\n // fleet grew one copy per site of a value with a single source.\n //\n // A surface no brand claims still has no key, and the edge REFUSES an\n // unattributed event (401 `ingest_key_required`) — the reserved `$public`\n // tenant that once caught keyless beacons is retired, and anonymous ingest is\n // refused at every edge on every brand host. So there is no quiet fallback to\n // rely on: an event lands in the org a credential names, or it does not land.\n ingestKey:\n config.ingestKey ?? readEnv('NEXT_PUBLIC_PUBLISHABLE_KEY') ?? keyForPage(),\n }\n this.transport = config.transport ?? new DefaultTransport()\n // Error plane, most specific source first: an explicit DSN wins, then the\n // inlined build-time env (a per-deploy override), then the product registry —\n // whose DSN carries the SAME resolved key (not a baked one), so declaring\n // `product` + providing the key is enough to report errors. Malformed or\n // absent => null => inert, never throwing into the host app.\n this.dsn = parseDsn(\n config.dsn ?? readEnvDsn() ?? dsnForProduct(this.cfg.product, this.cfg.ingestKey)\n )\n }\n\n /** adopt gives this client a credential it does not have. The key belongs to\n * the stream, not to whichever caller happened to ask for the handle first,\n * so a later caller carrying one hands it over. The error plane derives from\n * the key, so it comes up here too when it was inert for want of one.\n * Present fields are never overwritten: the first caller's key stays. */\n adopt(config: AnalyticsConfig): void {\n if (config.ingestKey && !this.cfg.ingestKey) this.cfg.ingestKey = config.ingestKey\n if (config.getToken && !this.cfg.getToken) this.cfg.getToken = config.getToken\n if (!this.dsn) {\n this.dsn = parseDsn(\n config.dsn ?? readEnvDsn() ?? dsnForProduct(this.cfg.product, this.cfg.ingestKey),\n )\n }\n }\n\n /** errorPlaneEnabled reports whether captured exceptions can actually reach the\n * error host. False means a DSN was never configured — the documented\n * fail-safe. Exposed so an app (or a test) can assert its wiring instead of\n * discovering months later that nothing was ever reported. */\n get errorPlaneEnabled(): boolean {\n return this.dsn !== null\n }\n\n /** errorIngestUrl is the fully-derived envelope endpoint, or undefined when the\n * plane is inert. Diagnostics only. */\n get errorIngestUrl(): string | undefined {\n return this.dsn?.ingestUrl\n }\n\n /** init is idempotent and browser-only for its side effects: capture first-touch\n * attribution, hydrate cohort, register the unload flush, and (unless opted out)\n * auto-capture unhandled errors. Safe to call from a React effect on every\n * render. */\n init(): void {\n if (this.started || !this.cfg.enabled) return\n this.started = true\n if (!isBrowser()) return\n\n const parsed = parseAttribution(window.location.search, document.referrer)\n this.attribution = hasAttribution(parsed)\n ? setFirstTouchOnce(parsed)\n : getFirstTouch() ?? parsed\n this.cohort = mergeCohort({\n channel: this.attribution.channel ?? deriveChannel(this.attribution),\n refCode: this.attribution.refCode,\n })\n\n const flushHidden = () => {\n if (document.visibilityState === 'hidden') this.flush(true)\n }\n window.addEventListener('visibilitychange', flushHidden)\n window.addEventListener('pagehide', () => this.flush(true))\n\n // Auto error capture — the drop-in @sentry replacement. Unhandled errors and\n // rejected promises are reported on BOTH planes: a Sentry envelope to the DSN\n // host (what reaches the error dashboard — requires a DSN) and a type:'error'\n // event on the stream (product signal in the warehouse).\n if (this.cfg.captureErrors) {\n window.addEventListener('error', (e: ErrorEvent) => {\n this.captureError(e.error ?? e.message, { handled: false })\n })\n window.addEventListener('unhandledrejection', (e: PromiseRejectionEvent) => {\n this.captureError(e.reason, { handled: false })\n })\n }\n }\n\n /** identify binds the current visitor to a stable person id (post-login). */\n identify(personId: string, traits?: Record<string, unknown>): void {\n this.personId = personId\n this.enqueue('identify', undefined, { properties: traits })\n }\n\n /** group associates the visitor with an org/team (analytics grouping, not the\n * server tenant — the server still derives tenant from the session). */\n group(groupId: string, traits?: Record<string, unknown>): void {\n this.enqueue('group', undefined, { groupId, properties: traits })\n }\n\n /** pageview records a $pageview for the current (or given) location, once per\n * view. A view is the path plus the full location, so a query or hash change\n * is a new one and a repeat call for the same place is not. */\n pageview(path?: string, properties?: Record<string, unknown>): void {\n // No `url` here: build() reads window.location.href for every event, in the\n // same tick, so this recomputed it to the identical value. Only `path` is\n // passed, because a route change fires before window.location has caught up\n // and the caller's value has to win.\n const p = path ?? (isBrowser() ? window.location.pathname : undefined)\n // Counting a page once is the client's rule, not each caller's. A page holds\n // several emitters that each correctly believe they count the first view —\n // the provider's autoPageview, a router hook, a nav autocapture — and per\n // emitter state cannot see the others. On the stream they are one page.\n const view = (p ?? '') + '\\0' + (isBrowser() ? window.location.href : '')\n if (view === this.counted) return\n this.counted = view\n this.enqueue('pageview', PAGEVIEW, { path: p, properties })\n }\n\n /** capture records a named product event with optional properties. Commerce\n * fields (productId/quantity/revenue/currency) may be passed for order events. */\n capture(\n event: string,\n properties?: Record<string, unknown>,\n commerce?: Pick<WireEvent, 'productId' | 'quantity' | 'revenue' | 'currency'>,\n ): void {\n this.enqueue('event', event, { properties, ...commerce })\n }\n\n /** captureError reports a caught error, an unhandled rejection, a React render\n * error, or a manual report to BOTH planes, from one call:\n *\n * - the ERROR PLANE — a real Sentry envelope to the DSN host. This is the one\n * that produces an issue in sentry.hanzo.ai (grouping, stack frames, AST).\n * Inert when no DSN is configured.\n * - the EVENT STREAM — a `type:'error'` row in the cloud event warehouse, so\n * an error stays correlated with the session's pageviews for product\n * analysis (readable via GET /v1/errors).\n *\n * Both carry the SAME session and subject id, so an error and the pageview\n * before it join up. Never throws back into the app; errors are higher-signal\n * than pageviews, so both planes flush promptly (a crash may unload the page\n * moments later). */\n captureError(err: unknown, context?: CaptureErrorOptions): void {\n // A failure inside the error path must not recurse through the global handlers.\n if (this.reentrant) return\n this.reentrant = true\n try {\n // ERROR PLANE FIRST, in its own try. The planes are independent, so neither\n // may be able to starve the other: `properties` is arbitrary caller data\n // (a DOM node, a React synthetic event, an axios error — all circular and\n // all common), and serializing it on the event stream can throw. When the\n // stream ran first, that throw escaped to the outer catch and the crash\n // report was never sent — silently losing exactly the signal this client\n // exists to deliver. Order and isolation are the fix.\n try {\n this.sendError(err, context)\n } catch {\n /* the error plane must never take the event stream down with it */\n }\n\n try {\n const handled = context?.handled ?? true\n const ex = normalizeError(err)\n ex.handled = handled\n // NAME: the reserved '$exception', never the message. The message was the\n // name until 0.3.20, which put every distinct error string — one per failed\n // chunk id, per ResizeObserver notification — permanently into the event\n // taxonomy, and left Error Tracking (which reads this exact name) at zero.\n //\n // TYPE 'event', not 'error'. `type` alone picks the storage plane: 'error'\n // routes to the error plane, which the product-event projection does not\n // read, so an exception filed there is invisible to Error Tracking however\n // well-formed it is. The full error record still reaches the error plane as\n // a Sentry envelope above — this row is the product-analytics breadcrumb,\n // which is what keeps a crash correlated with the session's pageviews.\n //\n // `error` is still carried: the server folds it into properties.$exception\n // (scrubbing message and stack on the way), which is the shape existing\n // readers bind to.\n this.enqueue('event', EXCEPTION, {\n error: ex,\n properties: {\n ...context?.properties,\n ...exceptionProperties(err, {\n handled,\n id: uuidv7(),\n level: context?.level,\n }),\n },\n })\n this.flush()\n } catch {\n /* nor the reverse */\n }\n } finally {\n this.reentrant = false\n }\n }\n\n /** setCohort persists cohort dimensions (e.g. signupWeek at signup) so they ride\n * every subsequent event. */\n setCohort(patch: Cohort): void {\n this.cohort = mergeCohort(patch)\n }\n\n /** flush drains the buffer to the server as ONE batch through the ONE ingest\n * endpoint POST /v1/event, body { batch: [Event…] }. beacon=true selects the\n * unload-safe transport. Auth is orthogonal to the wire:\n *\n * • publishable key set → rides ?ingest_key=pk-… on both sends, keeping each\n * a CORS-simple request that any origin may send.\n * • else a bearer JWT rides Authorization (fetch only — sendBeacon cannot\n * carry a header, so token apps fall back to keepalive fetch on unload).\n * • else a cookie app rides same-origin credentials (beacon carries the\n * cookie fine).\n */\n flush(beacon = false): void {\n if (!this.cfg.enabled || this.queue.length === 0) return\n const batch = this.queue\n this.queue = []\n this.clearTimer()\n\n // A publishable key and a bearer JWT are mutually exclusive credentials, and the\n // BEARER WINS. It names a real principal and resolves to THAT person's org;\n // a pk- names one org for everybody holding it. So the key is what attributes\n // a visitor nobody has vouched for, and it must never displace someone who\n // has been.\n //\n // The precedence used to run the other way, which was survivable only while\n // the key had to be passed in code. Once it also resolves from the build env,\n // key-wins means setting one variable silently blanks every signed-in user's\n // token and re-files their events under whichever org minted the key — on a\n // console served to several brands from one bundle, that is a cross-tenant\n // leak introduced by an env var.\n const token = this.cfg.getToken?.() ?? undefined\n const key = token ? undefined : this.cfg.ingestKey?.trim() || undefined\n // Only a bearer JWT blocks the beacon: sendBeacon cannot set an Authorization\n // header. A pk- rides ?ingest_key; a cookie rides credentials.\n const useBeacon = beacon && !token\n const body = serializeBatch(batch)\n if (body === null) {\n if (this.cfg.debug) console.debug('[event] flush → dropped, batch unserializable')\n return\n }\n if (this.cfg.debug) console.debug('[event] flush →', EVENT_PATH, batch.length)\n this.transport.send(this.cfg.host + EVENT_PATH, body, {\n beacon: useBeacon,\n token,\n ingestKey: key,\n debug: this.cfg.debug,\n })\n }\n\n // ── internals ────────────────────────────────────────────────────────────\n\n /** sendError frames one exception as a Sentry envelope and posts it to the DSN's\n * ingest URL. The DSN's own key rides ?sentry_key= (the credential channel the\n * server trusts, and the only one a headerless beacon can carry), so NO bearer\n * or publishable key is attached here — the two planes authenticate\n * independently. Errors are sent one envelope per event, immediately: batching\n * a crash report is how you lose it. */\n private sendError(err: unknown, options?: CaptureErrorOptions): void {\n if (!this.cfg.enabled || !this.dsn) return\n const event = buildSentryEvent({\n error: err,\n options,\n identity: this.errorIdentity(),\n capturePII: this.cfg.capturePII ?? false,\n })\n const body = buildEnvelope(event, this.dsn)\n if (this.cfg.debug) console.debug('[event] error →', this.dsn.ingestUrl, event.event_id)\n this.transport.send(this.dsn.ingestUrl, body, {\n beacon: false,\n contentType: ENVELOPE_CONTENT_TYPE,\n debug: this.cfg.debug,\n })\n }\n\n /** errorIdentity is the SAME identity the event stream stamps — the OIDC subject\n * once identify() has run, else the anon id. Never email/PII. */\n private errorIdentity(): ErrorIdentity {\n return {\n userId: this.personId ?? anonId(),\n sessionId: sessionId(),\n product: this.cfg.product,\n release: this.cfg.release ?? readEnv('NEXT_PUBLIC_HANZO_RELEASE'),\n environment: this.cfg.environment ?? readEnv('NODE_ENV'),\n }\n }\n\n private enqueue(kind: EventKind, event: string | undefined, extra: Partial<WireEvent>): void {\n if (!this.cfg.enabled) return\n if (!this.started) this.init()\n this.queue.push(this.build(kind, event, extra))\n if (this.queue.length >= this.cfg.batchSize) this.flush()\n else this.schedule()\n }\n\n private build(kind: EventKind, event: string | undefined, extra: Partial<WireEvent>): WireEvent {\n const anon = anonId()\n const wire: WireEvent = {\n messageId: uuidv7(),\n type: kind,\n event,\n timestamp: new Date().toISOString(),\n distinctId: this.personId ?? anon,\n anonymousId: anon,\n personId: this.personId,\n sessionId: sessionId(),\n product: this.cfg.product,\n referrer: this.attribution.referrer,\n utm: this.attribution.utm,\n refCode: this.cohort.refCode ?? this.attribution.refCode,\n channel: this.cohort.channel ?? this.attribution.channel,\n signupWeek: this.cohort.signupWeek,\n // Where the event happened, stamped for every kind rather than only for\n // pageviews. An interaction is reported against the page it happened on,\n // and autocapture ($click/$input/$change) reaches the wire through\n // capture(), which supplies no location — so every click arrived with an\n // empty url and path, and `host`, derived from url, was empty with them.\n // That is unattributable to a page, which is the one thing a heatmap\n // needs. Read here, at the single point every event is built, so it\n // cannot go missing from a call site again.\n //\n // Before `...extra`: pageview() passes an explicit path for route\n // changes that fire before window.location has caught up, and that value\n // has to win over the one read here.\n url: isBrowser() ? window.location.href : undefined,\n path: isBrowser() ? window.location.pathname : undefined,\n library: '@hanzo/event',\n libraryVersion: VERSION,\n ...extra,\n }\n\n // A location is free text an attacker (or an ordinary product flow) controls,\n // and it is now stamped on EVERY event rather than only pageviews — so the\n // one field that is always a URL gets the same policy the error plane has\n // always applied to error text. A password-reset, invite or magic link puts\n // a JWT in the query and an address in `?email=`; without this, one click on\n // that page ships both to the warehouse in cleartext, and every later click\n // repeats it.\n //\n // AFTER `...extra`, deliberately. A call site can pass its own location —\n // pageview() passes `path`, and until this commit it passed `url` too — and\n // `extra` merges over the fields read above, so scrubbing at the read would\n // have left the highest-volume event emitting a raw location while looking\n // scrubbed. Applied to the ASSEMBLED record, the guarantee holds for every\n // call site, including ones not written yet.\n //\n // `scrubText` is the SAME policy as the error plane (secrets always,\n // PII unless capturePII) rather than a second URL-specific redactor: one\n // definition of \"must not leave the browser\", already tested, mirroring the\n // server's. Guarded on presence so an absent field stays absent instead of\n // becoming the empty string that `host` derivation reads as a page.\n const capturePII = this.cfg.capturePII ?? false\n if (wire.url) wire.url = scrubText(wire.url, capturePII)\n if (wire.path) wire.path = scrubText(wire.path, capturePII)\n if (wire.referrer) wire.referrer = scrubText(wire.referrer, capturePII)\n return wire\n }\n\n private schedule(): void {\n if (this.timer || !this.cfg.enabled) return\n this.timer = setTimeout(() => {\n this.timer = null\n this.flush()\n }, this.cfg.flushIntervalMs)\n }\n\n private clearTimer(): void {\n if (this.timer) {\n clearTimeout(this.timer)\n this.timer = null\n }\n }\n}\n\n// ── one stream, one client ──────────────────────────────────────────────────\n//\n// A page has ONE event stream per (host, product): one queue, one anon id, one\n// credential. Two clients on it split the batch and double the pageview, and\n// the half built by the caller that had no key is refused at the edge — so the\n// same page is both over- and under-counted, and neither number says so.\n//\n// Two callers ask for that stream on any real page and both are right to: the\n// app mounts a provider, and the component library mounts its own telemetry.\n// Neither can see the other, so the fix cannot live in either. It lives here,\n// where the stream is: asking twice returns the same client.\n//\n// The registry is on the PAGE, under a `Symbol.for` slot, not in module scope.\n// `@hanzo/event` and `@hanzo/event/react` are separate bundles that each carry\n// a copy of this module, so a module-scope map is per-copy and the two copies\n// never see each other's — which is exactly how the duplicate arises. A\n// well-known symbol is the one slot every copy resolves to the same value.\n//\n// Browser only. A server process serves many visitors, and one client shared\n// across requests would carry one visitor's personId into the next.\nconst CLIENTS = Symbol.for('hanzo.event.clients')\n\nfunction registry(): Map<string, Analytics> {\n const g = globalThis as unknown as Record<symbol, Map<string, Analytics> | undefined>\n const existing = g[CLIENTS]\n if (existing) return existing\n const fresh = new Map<string, Analytics>()\n g[CLIENTS] = fresh\n return fresh\n}\n\n/** resetClients drops the page's clients, so the next `createAnalytics` for a\n * stream builds a fresh one.\n *\n * A real page loads once and never needs this. A TEST RUNNER is what it exists\n * for: one document, many mounts, each meant to be its own page. The second one\n * gets a client that already counted this location, so its pageview is\n * correctly suppressed and the suite reads it as \"nothing was emitted\".\n *\n * It is exported so a suite calls this instead of reaching for the slot itself.\n * A consumer that deletes the registry from its own code path takes the\n * guarantee with it: `<Provider/>` unmount/remount is not a new page, and the\n * fresh client it would get counts the same view again. */\nexport function resetClients(): void {\n const g = globalThis as unknown as Record<symbol, Map<string, Analytics> | undefined>\n delete g[CLIENTS]\n}\n\n/** createAnalytics returns the client for a stream, building it on first ask.\n * This is the ONE way to get a client: `new Analytics` bypasses the registry\n * and is for tests and for a deliberately separate instance. */\nexport function createAnalytics(config: AnalyticsConfig): Analytics {\n // A disabled client emits nothing, so it can double nothing and does not take\n // the stream. Callers reach their answer at different moments — a provider\n // whose ownership resolves in an effect renders once saying `false` before it\n // has decided anything — and the client built in that moment must not become\n // the one the page keeps. Registering it would silence the page, and adopting\n // `enabled` from a later caller would let one surface overrule another's\n // reading of consent. Neither: it is simply not the stream's client.\n if (!isBrowser() || config.enabled === false) return new Analytics(config)\n const key = (config.host ?? DEFAULT_HOST) + '\\0' + config.product\n const clients = registry()\n const existing = clients.get(key)\n if (existing) {\n existing.adopt(config)\n return existing\n }\n const fresh = new Analytics(config)\n clients.set(key, fresh)\n return fresh\n}\n\n// Re-export the hydrate helpers so consumers can read persisted cohort/attribution\n// (e.g. to send refCode to the referrals API) without reaching into storage.\nexport { getCohort, getFirstTouch }\n","// Insights goals + cohorts, defined once as data so the console/insights UI and\n// every product agree on what \"a Signup\", \"a Sale\", and \"upgrade intent\" mean.\n// This is the machine-readable spec — the shared source of truth a sync step can\n// push into Insights, and what TAXONOMY.md documents.\n//\n// A goal answers \"what counts as a conversion\". A funnel answers \"by what path\".\n// The paths live in funnels.ts and a goal REFERENCES one by id: the steps are\n// written down exactly once.\n\nimport { EVENTS } from './events'\nimport { eventsOf, type FunnelId } from './funnels'\n\nexport interface GoalDef {\n /** Human label shown in Insights. */\n label: string\n /** The event whose occurrence counts as the goal conversion. */\n event: string\n /** The funnel leading to the goal — an id into FUNNELS (see funnels.ts). */\n funnelId?: FunnelId\n /** The ordered event names of `funnelId`, derived — never hand-written. */\n funnel?: string[]\n /** Optional property equality filter that qualifies the conversion. */\n filter?: { property: string; equals: string }\n}\n\nexport const GOALS: Record<'signup' | 'sale' | 'upgradeIntent' | 'activation', GoalDef> = {\n // Signup: the conversion is signup_completed, along the site signup funnel.\n signup: {\n label: 'Signup',\n event: EVENTS.SIGNUP_COMPLETED,\n funnelId: 'signup',\n funnel: eventsOf('signup'),\n },\n // Sale: a completed order qualified as a plan purchase (kind=plan).\n sale: {\n label: 'Sale',\n event: EVENTS.ORDER_COMPLETED,\n funnelId: 'upgrade',\n funnel: eventsOf('upgrade'),\n filter: { property: 'kind', equals: 'plan' },\n },\n // Upgrade intent: a plan click; pricing_viewed is the top of its funnel.\n upgradeIntent: {\n label: 'Upgrade Intent',\n event: EVENTS.PLAN_CLICKED,\n funnelId: 'upgrade',\n funnel: eventsOf('upgrade'),\n },\n // Activation: the ONE north-star conversion — an account that did the first\n // valuable thing (a successful API call, a live app, a chat answer).\n activation: {\n label: 'Activation',\n event: EVENTS.FIRST_ACTION,\n funnelId: 'apiActivation',\n funnel: eventsOf('apiActivation'),\n },\n}\n\nexport interface CohortDef {\n /** The hanzo.events column the cohort dimension maps to. */\n field: string\n label: string\n}\n\nexport const COHORTS: Record<'signupWeek' | 'channel' | 'refCode', CohortDef> = {\n signupWeek: { field: 'signup_week', label: 'Signup week' },\n channel: { field: 'channel', label: 'Acquisition channel' },\n refCode: { field: 'ref_code', label: 'Referral code' },\n}\n"]}