Files
zeekayandHanzo Dev 4816750b8a rename EVENT_INGEST_KEY -> PUBLISHABLE_KEY
The value is a pk- prefixed PUBLISHABLE key (the Stripe vocabulary, and what
the build-time gate already tests for). EVENT_INGEST_KEY hid that.

One substitution covers all three spellings, since the framework prefixes wrap
the same token: EVENT_INGEST_KEY -> PUBLISHABLE_KEY, NEXT_PUBLIC_* and VITE_*
follow. KMS already carries deploy/PUBLISHABLE_KEY (env prod) with the same
value, written and read back first, so no build can reach a name that does not
exist yet.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-05 16:24:33 -07:00

55 lines
2.1 KiB
TypeScript

'use client'
import { usePathname } from 'next/navigation'
import { AnalyticsProvider, usePageview } from '@hanzo/event/react'
/** The ONE Hanzo Cloud telemetry front door — POST api.hanzo.ai/v1/event. Cloud
* fans the one batched stream out to the web (analytics), product (insights) and
* error (sentry) lenses; the client never sends the org — Cloud resolves the
* tenant server-side from the publishable ingest key. */
const HOST = 'https://api.hanzo.ai'
/** Publishable ingest key (pk_…), minted per org via POST /v1/ingest/keys. This is
* a marketing site: every visitor is logged out, so no bearer can ride the
* request and this write-only, bundle-safe key IS how anonymous pageviews and
* errors resolve to an org. Unset → events are best-effort and dropped at the edge. */
const INGEST_KEY = process.env.NEXT_PUBLIC_PUBLISHABLE_KEY?.trim() || undefined
/** Honor an explicit browser opt-out (Global Privacy Control, then legacy DNT).
* SSR (no navigator) defaults to consented; the browser reads the real signal on
* hydration. Opting out suppresses pageviews AND errors. */
function consented(): boolean {
if (typeof navigator === 'undefined') return true
const nav = navigator as Navigator & {
globalPrivacyControl?: boolean
doNotTrack?: string | null
}
if (nav.globalPrivacyControl === true) return false
const dnt = nav.doNotTrack
return dnt !== '1' && dnt !== 'yes'
}
function Pageview() {
usePageview(usePathname())
return null
}
/**
* Telemetry root. The provider owns the ONE @hanzo/event client: it fires the
* first pageview, registers auto error capture (window.onerror +
* unhandledrejection) and flushes on unload; <Pageview> adds one per client-side
* route change. Children render inside so any page can reach the same client via
* useAnalytics() — errors are just events on that one stream, so there is no
* separate DSN.
*/
export function Analytics({ children }: { children: React.ReactNode }) {
return (
<AnalyticsProvider
config={{ product: 'hanzo-industries', host: HOST, ingestKey: INGEST_KEY, enabled: consented() }}
>
<Pageview />
{children}
</AnalyticsProvider>
)
}