@hanzo/event 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/attribution.ts","../src/events.ts","../src/scrub.ts","../src/version.ts","../src/sentry.ts","../src/storage.ts","../src/core.ts","../src/goals.ts"],"names":["uid","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","// The ONE product-analytics vocabulary. Every Hanzo surface emits these exact\n// names so funnels, goals, and cohorts line up across console/chat/app/site/admin.\n// Pageviews use the reserved \"$pageview\" name (emitted by analytics.pageview()),\n// matching the server read lens.\n\nexport const EVENTS = {\n // Signup funnel: view -> submit -> verify -> completed -> first action.\n SIGNUP_VIEWED: 'signup_viewed',\n SIGNUP_SUBMITTED: 'signup_submitted',\n SIGNUP_VERIFIED: 'signup_verified',\n SIGNUP_COMPLETED: 'signup_completed',\n FIRST_ACTION: 'first_action',\n\n // Waitlist + referral.\n WAITLIST_JOINED: 'waitlist_joined',\n WAITLIST_SHARED: 'waitlist_shared',\n REFERRAL_USED: 'referral_used',\n REFERRAL_CLAIMED: 'referral_claimed',\n\n // Upgrade-intent + purchase.\n PRICING_VIEWED: 'pricing_viewed',\n PLAN_CLICKED: 'plan_clicked',\n CHECKOUT_STARTED: 'checkout_started',\n ORDER_COMPLETED: 'order_completed',\n\n // Feature usage — generic + the common key surfaces across products.\n FEATURE_USED: 'feature_used',\n API_KEY_CREATED: 'api_key_created',\n APP_CREATED: 'app_created',\n DEPLOY_STARTED: 'deploy_started',\n PROJECT_CREATED: 'project_created',\n AGENT_CREATED: 'agent_created',\n CHAT_STARTED: 'chat_started',\n CHAT_MESSAGE_SENT: 'chat_message_sent',\n TASK_STARTED: 'task_started',\n TASK_COMPLETED: 'task_completed',\n} as const\n\nexport type EventName = (typeof EVENTS)[keyof typeof EVENTS]\n\n/** The reserved event name a pageview is stored under (server + read lens). */\nexport const PAGEVIEW = '$pageview'\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\nconst RE_EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/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/** redactSecrets removes known secret shapes. Always applied. */\nexport function redactSecrets(s: string): string {\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","// 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.\nexport const VERSION = '0.3.2'\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/sentry ingest\n// (o11y pkg/modules/errortracking/implerrortracking: parseEnvelope, SentryEvent,\n// normalizeEvent, computeFingerprint). No upstream (FSL) code is used.\n\nimport { scrubText } from './scrub'\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. */\nexport function eventId(): string {\n const c = typeof crypto !== 'undefined' ? crypto : undefined\n if (c && 'randomUUID' in c) return c.randomUUID().replace(/-/g, '')\n let s = ''\n for (let i = 0; i < 32; i++) s += Math.floor(Math.random() * 16).toString(16)\n return s\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/sentry/<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 const ingestUrl =\n `${origin}/v1/sentry/${encodeURIComponent(projectId)}/envelope/` +\n `?sentry_key=${encodeURIComponent(publicKey)}`\n return { publicKey, origin, projectId, 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 return { name: err.name || 'Error', message: err.message || String(err), stack: err.stack }\n }\n if (typeof err === 'string') return { name: 'Error', message: err }\n try {\n return { name: 'Error', message: JSON.stringify(err) }\n } catch {\n return { name: 'Error', message: String(err) }\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.origin}/v1/sentry/${dsn.projectId}`,\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","// 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\nimport type { Attribution, Cohort } from './types'\n\nconst KEY = {\n anon: 'hz_anon_id',\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\nfunction uid(): string {\n const c = typeof crypto !== 'undefined' ? crypto : undefined\n if (c && 'randomUUID' in c) return c.randomUUID()\n return 'a-' + Date.now().toString(36) + Math.random().toString(36).slice(2, 10)\n}\n\n/** Stable anonymous id, minted once per browser and reused across sessions. */\nexport function anonId(): string | undefined {\n const s = ls()\n if (!s) return undefined\n let v = s.getItem(KEY.anon)\n if (!v) {\n v = uid()\n s.setItem(KEY.anon, v)\n }\n return v\n}\n\ninterface SessionState {\n id: string\n last: number\n}\n\n/** Current session id, rotated after SESSION_TTL_MS of inactivity. */\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: uid(), 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 front door:\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.origin}/v1/sentry/{projectId}/envelope/?sentry_key=…\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 door 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 door, 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_…') — Authorization: Bearer pk_… on\n// fetch, ?ingest_key=pk_… on a headerless page-unload beacon. Write-only and\n// safe to ship in a bundle; the door HMAC-verifies it to an org server-side.\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 { PAGEVIEW } from './events'\nimport { buildEnvelope, buildSentryEvent, parseDsn, type ErrorIdentity } from './sentry'\nimport {\n anonId,\n sessionId,\n getFirstTouch,\n setFirstTouchOnce,\n getCohort,\n mergeCohort,\n} from './storage'\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 front door\nconst DEFAULT_HOST = 'https://api.hanzo.ai' // the one edge; cookie apps pass host:''\nconst ENVELOPE_CONTENT_TYPE = 'application/x-sentry-envelope'\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 a headerless sendBeacon (?ingest_key=…). */\nfunction appendQuery(url: string, key: string, value: string): string {\n return url + (url.includes('?') ? '&' : '?') + key + '=' + encodeURIComponent(value)\n}\n\nfunction uid(): string {\n const c = typeof crypto !== 'undefined' ? crypto : undefined\n if (c && 'randomUUID' in c) return c.randomUUID()\n return 'm-' + Date.now().toString(36) + Math.random().toString(36).slice(2, 10)\n}\n\n/** Normalize anything thrown (Error | string | unknown) into an Exception. */\nfunction normalizeError(err: unknown): Exception {\n if (err instanceof Error) {\n return { type: err.name, message: err.message, stack: err.stack }\n }\n if (typeof err === 'string') return { message: err }\n try {\n return { message: JSON.stringify(err) }\n } catch {\n return { message: String(err) }\n }\n}\n\nconst isBrowser = () => typeof window !== '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 headerless page-unload beacons. A bearer (a JWT or a\n * publishable pk_ key) rides Authorization on fetch; on a beacon — which cannot\n * set headers — a publishable key rides the ?ingest_key query instead. */\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 const contentType = opts.contentType ?? 'application/json'\n if (opts.beacon && isBrowser() && typeof navigator.sendBeacon === 'function') {\n const beaconUrl = opts.ingestKey ? appendQuery(url, 'ingest_key', opts.ingestKey) : url\n try {\n navigator.sendBeacon(beaconUrl, new Blob([body], { type: contentType }))\n return\n } catch {\n /* fall through to fetch */\n }\n }\n if (typeof fetch !== 'function') return\n const headers: Record<string, string> = { 'Content-Type': contentType }\n const bearer = opts.ingestKey ?? opts.token\n if (bearer) headers.Authorization = `Bearer ${bearer}`\n void fetch(url, {\n method: 'POST',\n headers,\n body,\n keepalive: true,\n credentials: '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 /** 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 }\n this.transport = config.transport ?? new DefaultTransport()\n // Error plane: explicit DSN wins, else the inlined build-time env. Malformed\n // or absent => null => inert, never throwing into the host app.\n this.dsn = parseDsn(config.dsn ?? readEnvDsn())\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. */\n pageview(path?: string, properties?: Record<string, unknown>): void {\n const url = isBrowser() ? window.location.href : undefined\n const p = path ?? (isBrowser() ? window.location.pathname : undefined)\n this.enqueue('pageview', PAGEVIEW, { url, 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 /** track is an alias of capture (Segment familiarity). */\n track = this.capture.bind(this)\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 ex = normalizeError(err)\n ex.handled = context?.handled ?? true\n this.enqueue('error', ex.message, { error: ex, properties: context?.properties })\n this.flush()\n } catch {\n /* nor the reverse */\n }\n } finally {\n this.reentrant = false\n }\n }\n\n /** captureException — @sentry-familiar alias of captureError. */\n captureException = this.captureError.bind(this)\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 * front door 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 Authorization: Bearer pk_… (fetch) or\n * ?ingest_key=pk_… (beacon), so unload beacons work anonymously.\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 const key = this.cfg.ingestKey?.trim() || undefined\n // A publishable key and a bearer JWT are mutually exclusive doors; the key wins.\n const token = key ? undefined : this.cfg.getToken?.() ?? undefined\n // Only a headerful bearer JWT blocks the beacon: sendBeacon cannot set an\n // Authorization 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 return {\n messageId: uid(),\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 library: '@hanzo/event',\n libraryVersion: VERSION,\n ...extra,\n }\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/** createAnalytics builds a client instance. Most apps use one shared instance. */\nexport function createAnalytics(config: AnalyticsConfig): Analytics {\n return new Analytics(config)\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 the guide documents.\n\nimport { EVENTS } from './events'\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 /** Optional ordered funnel leading to the goal (for funnel insights). */\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', GoalDef> = {\n // Signup: the conversion is signup_completed; the funnel is the four steps.\n signup: {\n label: 'Signup',\n event: EVENTS.SIGNUP_COMPLETED,\n funnel: [\n EVENTS.SIGNUP_VIEWED,\n EVENTS.SIGNUP_SUBMITTED,\n EVENTS.SIGNUP_VERIFIED,\n EVENTS.FIRST_ACTION,\n ],\n },\n // Sale: a completed order qualified as a plan purchase (kind=plan).\n sale: {\n label: 'Sale',\n event: EVENTS.ORDER_COMPLETED,\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 funnel: [EVENTS.PRICING_VIEWED, EVENTS.PLAN_CLICKED, EVENTS.CHECKOUT_STARTED],\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"]}
1
+ {"version":3,"sources":["../src/attribution.ts","../src/events.ts","../src/scrub.ts","../src/version.ts","../src/sentry.ts","../src/storage.ts","../src/core.ts","../src/funnels.ts","../src/goals.ts"],"names":["s","uid","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","// The ONE product-analytics vocabulary. Every Hanzo surface emits these exact\n// names so funnels, goals, and cohorts line up across console/chat/app/site/admin.\n// Pageviews use the reserved \"$pageview\" name (emitted by analytics.pageview()),\n// matching the server read lens.\n//\n// Naming is a closed convention, not a style preference (see TAXONOMY.md):\n// • snake_case, `<object>_<verb-in-past-tense>` — signup_completed, plan_clicked.\n// • Names are CONSTANTS, never interpolated. A dimension (framework, model,\n// plan, source) is a PROPERTY, never part of the name. `deploy_succeeded`\n// with {framework:'static'} — never `deploy_static_succeeded`.\n// • One name per user-visible moment, shared by every surface. The surface is\n// already on the wire as `product`, so a name is never product-prefixed.\n\nexport const EVENTS = {\n // Signup funnel: view -> submit -> verify -> completed -> first action.\n SIGNUP_VIEWED: 'signup_viewed',\n SIGNUP_SUBMITTED: 'signup_submitted',\n SIGNUP_VERIFIED: 'signup_verified',\n SIGNUP_COMPLETED: 'signup_completed',\n /** A RETURNING user authenticated — the non-signup half of the IAM callback.\n * Keeping it distinct is what stops returning logins from inflating signups. */\n LOGIN_COMPLETED: 'login_completed',\n /** Activation: the first moment of real value. ONE event for every product —\n * the product-specific moment is the `action` property (api_call, app_live,\n * chat_reply), never a new event name. */\n FIRST_ACTION: 'first_action',\n\n // Waitlist + referral.\n WAITLIST_JOINED: 'waitlist_joined',\n WAITLIST_SHARED: 'waitlist_shared',\n REFERRAL_USED: 'referral_used',\n REFERRAL_CLAIMED: 'referral_claimed',\n\n // Upgrade-intent + purchase.\n PRICING_VIEWED: 'pricing_viewed',\n PLAN_CLICKED: 'plan_clicked',\n CHECKOUT_STARTED: 'checkout_started',\n ORDER_COMPLETED: 'order_completed',\n\n // Feature usage — generic + the common key surfaces across products.\n FEATURE_USED: 'feature_used',\n API_KEY_CREATED: 'api_key_created',\n APP_CREATED: 'app_created',\n PROJECT_CREATED: 'project_created',\n AGENT_CREATED: 'agent_created',\n CHAT_STARTED: 'chat_started',\n CHAT_MESSAGE_SENT: 'chat_message_sent',\n /** The user switched model/endpoint — the single strongest quality signal a\n * chat surface emits (a switch usually follows a bad answer). */\n MODEL_SWITCHED: 'model_switched',\n TASK_STARTED: 'task_started',\n TASK_COMPLETED: 'task_completed',\n\n // Build → ship. `build_*` is a MODEL producing an artifact; `deploy_*` is that\n // artifact going live. Intent (build_started) is never the same event as the\n // artifact existing (app_created) — conflating them makes the funnel lie.\n BUILD_STARTED: 'build_started',\n /** A model finished producing an artifact (an app build, a chat reply, an agent\n * run). Carries `durationMs` — the outcome event owns its own duration, so no\n * paired start event is needed. */\n GENERATION_COMPLETED: 'generation_completed',\n GENERATION_FAILED: 'generation_failed',\n DEPLOY_STARTED: 'deploy_started',\n DEPLOY_SUCCEEDED: 'deploy_succeeded',\n DEPLOY_FAILED: 'deploy_failed',\n} as const\n\nexport type EventName = (typeof EVENTS)[keyof typeof EVENTS]\n\n/** The reserved event name a pageview is stored under (server + read lens). */\nexport const PAGEVIEW = '$pageview'\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/** redactSecrets removes known secret shapes. Always applied. */\nexport function redactSecrets(s: string): string {\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","// 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.\nexport const VERSION = '0.3.3'\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/sentry ingest\n// (o11y pkg/modules/errortracking/implerrortracking: parseEnvelope, SentryEvent,\n// normalizeEvent, computeFingerprint). No upstream (FSL) code is used.\n\nimport { scrubText } from './scrub'\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. */\nexport function eventId(): string {\n const c = typeof crypto !== 'undefined' ? crypto : undefined\n if (c && 'randomUUID' in c) return c.randomUUID().replace(/-/g, '')\n let s = ''\n for (let i = 0; i < 32; i++) s += Math.floor(Math.random() * 16).toString(16)\n return s\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/sentry/<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 const ingestUrl =\n `${origin}/v1/sentry/${encodeURIComponent(projectId)}/envelope/` +\n `?sentry_key=${encodeURIComponent(publicKey)}`\n return { publicKey, origin, projectId, 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.origin}/v1/sentry/${dsn.projectId}`,\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","// 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\nimport type { Attribution, Cohort } from './types'\n\nconst KEY = {\n anon: 'hz_anon_id',\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\nfunction uid(): string {\n const c = typeof crypto !== 'undefined' ? crypto : undefined\n if (c && 'randomUUID' in c) return c.randomUUID()\n return 'a-' + Date.now().toString(36) + Math.random().toString(36).slice(2, 10)\n}\n\n/** Stable anonymous id, minted once per browser and reused across sessions. */\nexport function anonId(): string | undefined {\n const s = ls()\n if (!s) return undefined\n let v = s.getItem(KEY.anon)\n if (!v) {\n v = uid()\n s.setItem(KEY.anon, v)\n }\n return v\n}\n\ninterface SessionState {\n id: string\n last: number\n}\n\n/** Current session id, rotated after SESSION_TTL_MS of inactivity. */\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: uid(), 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 front door:\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.origin}/v1/sentry/{projectId}/envelope/?sentry_key=…\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 door 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 door, 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_…') — Authorization: Bearer pk_… on\n// fetch, ?ingest_key=pk_… on a headerless page-unload beacon. Write-only and\n// safe to ship in a bundle; the door HMAC-verifies it to an org server-side.\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 { PAGEVIEW } from './events'\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 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 front door\nconst DEFAULT_HOST = 'https://api.hanzo.ai' // the one edge; cookie apps pass host:''\nconst ENVELOPE_CONTENT_TYPE = 'application/x-sentry-envelope'\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 a headerless sendBeacon (?ingest_key=…). */\nfunction appendQuery(url: string, key: string, value: string): string {\n return url + (url.includes('?') ? '&' : '?') + key + '=' + encodeURIComponent(value)\n}\n\nfunction uid(): string {\n const c = typeof crypto !== 'undefined' ? crypto : undefined\n if (c && 'randomUUID' in c) return c.randomUUID()\n return 'm-' + Date.now().toString(36) + Math.random().toString(36).slice(2, 10)\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\nconst isBrowser = () => typeof window !== '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 headerless page-unload beacons. A bearer (a JWT or a\n * publishable pk_ key) rides Authorization on fetch; on a beacon — which cannot\n * set headers — a publishable key rides the ?ingest_key query instead. */\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 const contentType = opts.contentType ?? 'application/json'\n if (opts.beacon && isBrowser() && typeof navigator.sendBeacon === 'function') {\n const beaconUrl = opts.ingestKey ? appendQuery(url, 'ingest_key', opts.ingestKey) : url\n try {\n navigator.sendBeacon(beaconUrl, new Blob([body], { type: contentType }))\n return\n } catch {\n /* fall through to fetch */\n }\n }\n if (typeof fetch !== 'function') return\n const headers: Record<string, string> = { 'Content-Type': contentType }\n const bearer = opts.ingestKey ?? opts.token\n if (bearer) headers.Authorization = `Bearer ${bearer}`\n void fetch(url, {\n method: 'POST',\n headers,\n body,\n keepalive: true,\n credentials: '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 /** 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 }\n this.transport = config.transport ?? new DefaultTransport()\n // Error plane: explicit DSN wins, else the inlined build-time env. Malformed\n // or absent => null => inert, never throwing into the host app.\n this.dsn = parseDsn(config.dsn ?? readEnvDsn())\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. */\n pageview(path?: string, properties?: Record<string, unknown>): void {\n const url = isBrowser() ? window.location.href : undefined\n const p = path ?? (isBrowser() ? window.location.pathname : undefined)\n this.enqueue('pageview', PAGEVIEW, { url, 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 /** track is an alias of capture (Segment familiarity). */\n track = this.capture.bind(this)\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 ex = normalizeError(err)\n ex.handled = context?.handled ?? true\n this.enqueue('error', ex.message, { error: ex, properties: context?.properties })\n this.flush()\n } catch {\n /* nor the reverse */\n }\n } finally {\n this.reentrant = false\n }\n }\n\n /** captureException — @sentry-familiar alias of captureError. */\n captureException = this.captureError.bind(this)\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 * front door 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 Authorization: Bearer pk_… (fetch) or\n * ?ingest_key=pk_… (beacon), so unload beacons work anonymously.\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 const key = this.cfg.ingestKey?.trim() || undefined\n // A publishable key and a bearer JWT are mutually exclusive doors; the key wins.\n const token = key ? undefined : this.cfg.getToken?.() ?? undefined\n // Only a headerful bearer JWT blocks the beacon: sendBeacon cannot set an\n // Authorization 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 return {\n messageId: uid(),\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 library: '@hanzo/event',\n libraryVersion: VERSION,\n ...extra,\n }\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/** createAnalytics builds a client instance. Most apps use one shared instance. */\nexport function createAnalytics(config: AnalyticsConfig): Analytics {\n return new Analytics(config)\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","// The ONE funnel registry. A funnel is DATA — an ordered list of event names —\n// so the products, the Insights UI, and the docs can never drift: every step is\n// a name from EVENTS (funnels.test.ts fails the build otherwise), and a GOAL that\n// has a funnel points AT one of these rather than restating the steps.\n//\n// Why here and not in Insights: the taxonomy is code that all three surfaces\n// already import (`@hanzo/event`). A funnel defined in the read lens could name\n// an event no surface emits; a funnel defined next to EVENTS cannot.\n\nimport { EVENTS, PAGEVIEW } from './events'\n\n/** The emitting surfaces — the closed set of `AnalyticsConfig.product` values.\n * `product` is on every event, so a funnel scopes by product instead of every\n * surface prefixing its event names. */\nexport const PRODUCTS = ['site', 'app', 'chat', 'console', 'admin', 'cloud'] as const\nexport type ProductId = (typeof PRODUCTS)[number]\n\nexport interface FunnelStep {\n /** An EVENTS value (or PAGEVIEW). */\n event: string\n /** Human label for the Insights step. */\n label: string\n /** Property equality that qualifies the step, e.g. first_action{action:'api_call'}. */\n where?: { property: string; equals: string }\n}\n\nexport interface FunnelDef {\n label: string\n /** Surface(s) the steps are emitted from — matched against the `product` field. */\n products: ProductId[]\n /**\n * How steps are joined:\n * • 'person' — steps join on distinctId (one browser, or one logged-in\n * person across surfaces). The normal case.\n * • 'aggregate' — steps are emitted on DIFFERENT origins by a LOGGED-OUT\n * visitor, so there is no shared id: hanzo.ai, hanzo.app and\n * hanzo.chat each mint their own anonymousId in their own\n * storage. Read these as step-over-step COUNTS, never as a\n * per-person conversion. Honest by construction.\n */\n join: 'person' | 'aggregate'\n steps: FunnelStep[]\n}\n\nconst s = (event: string, label: string, where?: FunnelStep['where']): FunnelStep => ({\n event,\n label,\n ...(where ? { where } : {}),\n})\n\nexport const FUNNELS = {\n /** hanzo.ai: land → sign up. IAM hosts the form, so `signup_submitted` is the\n * redirect INTO IAM and `signup_completed` is the return at /auth/callback. */\n signup: {\n label: 'Signup',\n products: ['site'],\n join: 'person',\n steps: [\n s(PAGEVIEW, 'Landed'),\n s(EVENTS.SIGNUP_VIEWED, 'Opened signup'),\n s(EVENTS.SIGNUP_SUBMITTED, 'Redirected to Hanzo ID'),\n s(EVENTS.SIGNUP_COMPLETED, 'Account created'),\n s(EVENTS.FIRST_ACTION, 'First action'),\n ],\n },\n\n /** The developer activation path: an account is worth nothing until a key has\n * made a call. `first_action{action:'api_call'}` is emitted SERVER-SIDE by\n * Cloud on an org's first successful /v1 request — a browser cannot see it. */\n apiActivation: {\n label: 'API activation',\n products: ['site', 'cloud'],\n join: 'person',\n steps: [\n s(EVENTS.SIGNUP_COMPLETED, 'Account created'),\n s(EVENTS.API_KEY_CREATED, 'Key minted'),\n s(EVENTS.FIRST_ACTION, 'First successful API call', {\n property: 'action',\n equals: 'api_call',\n }),\n ],\n },\n\n /** Upgrade intent → revenue. `order_completed{kind:'plan'}` is the Sale goal. */\n upgrade: {\n label: 'Upgrade',\n products: ['site', 'app', 'console'],\n join: 'person',\n steps: [\n s(EVENTS.PRICING_VIEWED, 'Viewed pricing'),\n s(EVENTS.PLAN_CLICKED, 'Chose a plan'),\n s(EVENTS.CHECKOUT_STARTED, 'Started checkout'),\n s(EVENTS.ORDER_COMPLETED, 'Paid'),\n ],\n },\n\n /** hanzo.app: describe → build → deploy → live URL. The whole product thesis\n * in five steps; `deploy_succeeded` is the moment a live URL exists. */\n appShip: {\n label: 'Describe → ship',\n products: ['app'],\n join: 'person',\n steps: [\n s(PAGEVIEW, 'Landed'),\n s(EVENTS.BUILD_STARTED, 'Described an app'),\n s(EVENTS.GENERATION_COMPLETED, 'Got a working build'),\n s(EVENTS.DEPLOY_STARTED, 'Hit publish'),\n s(EVENTS.DEPLOY_SUCCEEDED, 'Live URL'),\n ],\n },\n\n /** hanzo.chat: visit → first message → answer. `generation_completed` is what\n * separates \"typed something\" from \"got value\". */\n chatEngage: {\n label: 'Chat engagement',\n products: ['chat'],\n join: 'person',\n steps: [\n s(PAGEVIEW, 'Landed'),\n s(EVENTS.CHAT_STARTED, 'Started a conversation'),\n s(EVENTS.CHAT_MESSAGE_SENT, 'Sent a message'),\n s(EVENTS.GENERATION_COMPLETED, 'Got an answer'),\n ],\n },\n\n /** The cross-surface handoff: the hanzo.ai composer forwards its prompt to\n * hanzo.chat. Two origins, two anonymousIds — so this is an AGGREGATE funnel.\n * The join is the `referrerProduct` property hanzo.chat reads off `?hz_ref=`,\n * which makes the drop-off measurable without any cross-domain identity. */\n siteToChat: {\n label: 'Site → Chat handoff',\n products: ['site', 'chat'],\n join: 'aggregate',\n steps: [\n s(EVENTS.CHAT_STARTED, 'Submitted the hanzo.ai composer', {\n property: 'source',\n equals: 'composer',\n }),\n s(EVENTS.CHAT_STARTED, 'Landed in hanzo.chat', {\n property: 'referrerProduct',\n equals: 'site',\n }),\n s(EVENTS.GENERATION_COMPLETED, 'Got an answer'),\n ],\n },\n} as const satisfies Record<string, FunnelDef>\n\nexport type FunnelId = keyof typeof FUNNELS\n\n/** eventsOf flattens a funnel to its ordered event names — what a goal's `funnel`\n * field carries, so the steps are defined exactly once (here). */\nexport function eventsOf(id: FunnelId): string[] {\n return FUNNELS[id].steps.map((step) => step.event)\n}\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"]}
package/dist/react.cjs CHANGED
@@ -127,7 +127,7 @@ function redactPAN(s) {
127
127
  return luhn(digits) ? REDACTED : m;
128
128
  });
129
129
  }
130
- var RE_EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
130
+ var RE_EMAIL = /[A-Za-z0-9._%+-]{1,64}@[A-Za-z0-9.-]{1,255}\.[A-Za-z]{2,24}/g;
131
131
  var RE_IPV4 = /\b(?:\d{1,3}\.){3}\d{1,3}\b/g;
132
132
  var RE_IPV6 = /\b(?:[0-9A-Fa-f]{1,4}:){2,7}[0-9A-Fa-f]{1,4}\b/g;
133
133
  function redactSecrets(s) {
@@ -153,7 +153,7 @@ function scrubText(s, capturePII = false) {
153
153
  }
154
154
 
155
155
  // src/version.ts
156
- var VERSION = "0.3.2";
156
+ var VERSION = "0.3.3";
157
157
 
158
158
  // src/sentry.ts
159
159
  var MAX_FRAMES = 50;
@@ -241,13 +241,34 @@ function framesFromStack(stack) {
241
241
  }
242
242
  function normalizeError(err) {
243
243
  if (err instanceof Error) {
244
- return { name: err.name || "Error", message: err.message || String(err), stack: err.stack };
244
+ const name = read(err, "name");
245
+ const message = read(err, "message");
246
+ const stack = read(err, "stack");
247
+ return {
248
+ name: typeof name === "string" && name ? name : "Error",
249
+ message: typeof message === "string" && message ? message : str(err),
250
+ stack: typeof stack === "string" ? stack : void 0
251
+ };
245
252
  }
246
253
  if (typeof err === "string") return { name: "Error", message: err };
247
254
  try {
248
- return { name: "Error", message: JSON.stringify(err) };
255
+ return { name: "Error", message: JSON.stringify(err) ?? str(err) };
256
+ } catch {
257
+ return { name: "Error", message: str(err) };
258
+ }
259
+ }
260
+ function read(o, k) {
261
+ try {
262
+ return o[k];
263
+ } catch {
264
+ return void 0;
265
+ }
266
+ }
267
+ function str(v) {
268
+ try {
269
+ return String(v);
249
270
  } catch {
250
- return { name: "Error", message: String(err) };
271
+ return "[unstringifiable]";
251
272
  }
252
273
  }
253
274
  function coerceTag(v) {
@@ -445,15 +466,8 @@ function uid2() {
445
466
  return "m-" + Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
446
467
  }
447
468
  function normalizeError2(err) {
448
- if (err instanceof Error) {
449
- return { type: err.name, message: err.message, stack: err.stack };
450
- }
451
- if (typeof err === "string") return { message: err };
452
- try {
453
- return { message: JSON.stringify(err) };
454
- } catch {
455
- return { message: String(err) };
456
- }
469
+ const n = normalizeError(err);
470
+ return { type: n.name, message: n.message, stack: n.stack };
457
471
  }
458
472
  var isBrowser = () => typeof window !== "undefined";
459
473
  function serializeBatch(batch) {