@mrrocket/sdk 0.23.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -10
- package/dist/browser/mrrocket.js +1 -1
- package/dist/client.js +34 -20
- package/dist/client.js.map +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/react-native/index.d.cts +1 -1
- package/dist/react-native/index.d.ts +1 -1
- package/dist/react-native/index.js +1 -1
- package/dist/react-native/index.js.map +1 -1
- package/package.json +1 -1
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.tsx"],"sourcesContent":["\"use client\";\n\nimport { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { usePathname } from \"next/navigation\";\nimport { Ctx, type CockpitContext, type CockpitUser, useCtx } from \"./context\";\nimport { Survey, forgetStoredDismissals } from \"./survey\";\nimport { RageDetector, clickInfo, seenDepth } from \"./capture\";\nimport type { ReplayHandle } from \"./replay\";\nimport { type ErrorCapture, startErrorCapture } from \"./errors\";\nimport {\n type AttributionMode,\n DEFAULT_REFERRAL_DAYS,\n captureReferral,\n carriesReferral,\n forgetStoredReferral,\n getReferral,\n resetReferral,\n storeReferral,\n} from \"./referral\";\nimport {\n type CockpitConfig,\n type CockpitEvent,\n type ConsentChoice,\n type ConsentMode,\n EMBED_PARAM,\n EXPOSURE_PARAM,\n ID_COOKIE,\n PREVIEW_PARAM,\n SDK_VERSION,\n SESSION_COOKIE,\n type VariantId,\n envKey,\n experimentsOfText,\n exposureFrom,\n flagFor,\n parsePreview,\n redirectFor,\n resolveHost,\n resolveVariant,\n textChanges,\n CONSENT_OFF_EVENT,\n} from \"./core\";\nimport type { PushResult, WebPushOptions } from \"./push-web\";\nimport { bodies } from \"./batch\";\nimport { YEAR_S, deleteCookie, enterPage, page, privacySignal, touchSession, visitorId, writeCookie } from \"./web-state\";\nimport { type CancelFlowOptions, type CancelFlowResult, type FlowsController, type OnboardingFlow, createFlows } from \"./flows\";\nimport type { OnboardingReport, OnboardingReportOptions } from \"./onboarding-report\";\nimport { type UseOnboarding, useOnboardingItem, useReportWith } from \"./onboarding-state\";\nimport { startPicker } from \"./pick\";\nimport type { FlowsTheme } from \"./flows-ui\";\nimport { forgetFlowsStorage, webFlowsEnv } from \"./flows-web\";\nimport { type PaywallHost, type PaywallOptions, type PaywallResult, createPaywallHost } from \"./paywall\";\nimport { type IdentifyOptions, type Signed, hashFor, nextSigned, warnUnsigned } from \"./identity\";\nimport { currentLocale, langParam, onLocale, onWords, setLocale, setWords } from \"./i18n\";\n\n/** Tells the developer once, in the browser console, why the SDK is not reaching MRRocket (a wrong host or key is the usual cause). */\nconst warned = new Set<string>();\nconst warnOnce = (message: string) => {\n if (warned.has(message)) return;\n warned.add(message);\n console.warn(`[MRRocket] ${message}`);\n};\n\nexport interface CockpitProviderProps {\n /** The product's public key from Cockpit › Settings › SDK. Safe in the browser. Default: `NEXT_PUBLIC_MRROCKET_KEY`. */\n apiKey?: string;\n /** Where MRRocket runs, like `https://app.example.com`. Default: `NEXT_PUBLIC_MRROCKET_HOST`, else the page's own origin. */\n host?: string;\n preview?: Record<string, VariantId>;\n embedded?: boolean;\n /** Config fetched on the server, to render the right variant on the first paint. */\n config?: CockpitConfig;\n /**\n * 0.21.0: the language of the SDK's words and of what the server answers, like \"de-AT\", for an app with its own\n * language switch. Default: the browser's. `setLocale` does the same from anywhere.\n */\n locale?: string;\n /** Your own id for this visitor, for example a signed-in user id. Keeps variants stable without cookies. */\n distinctId?: string;\n /**\n * The identity hash of `distinctId` (0.16.0): `await userHash(user.id)` from `@mrrocket/sdk/next/server`, made in\n * the server layout that knows the user. Sent with every request for that id, so MRRocket knows it is really them\n * (needed with MRRocket › Settings › Products › Sign-in › \"Only signed users\"). In the browser pass it to\n * `identify(id, props, { hash })` instead.\n */\n userHash?: string;\n /**\n * \"cookieless\" (default) stores nothing in the browser, \"cookies\" keeps a visitor cookie for a year, \"off\" sends\n * nothing. The SDK does not remember the choice: set it on every page load, from the prop or setConsent().\n */\n consent?: ConsentMode;\n /** Send nothing when the browser asks not to be tracked (Do Not Track or Global Privacy Control). */\n respectDnt?: boolean;\n /**\n * The visitor's consent to session recordings, separate from analytics (default false). Recording also needs the\n * product to record (Cockpit › Sessions) and analytics not \"off\". Without it nothing is recorded or buffered.\n * Change it later with `setConsent({ replay })`.\n */\n replay?: boolean;\n /**\n * Clicks and scroll depth for Heatmaps (default true): where a click landed, a short selector and a link's or\n * button's label, never typed text. Sent under the normal analytics consent as `$click`, `$rageclick` and `$scroll`.\n */\n heatmaps?: boolean;\n /** Ask visitors the surveys you run in Cockpit. Set false to switch them off. */\n surveys?: boolean;\n /**\n * Error tracking for Build › Errors (default true): uncaught errors and unhandled promise rejections, with the page\n * and the last pages and clicks, under the normal analytics consent. Report errors you catch with\n * `useCockpit().captureError(error)`.\n */\n errors?: boolean;\n /** Your release (a version or a commit), so MRRocket can tell which release an error came with. */\n release?: string;\n /**\n * Affiliate referrals (`?ref=` links and MRRocket's tracked links). \"consent\" (default): stored in the first-party\n * `mrr_ref` cookie only while consent is \"cookies\", in memory otherwise. \"always\": stored whatever the consent (only\n * where your legal basis allows it). \"off\": referrals are ignored.\n */\n attribution?: AttributionMode;\n /** The query parameter of direct partner links, default `ref` (`https://your.site/?ref=maya`). */\n referralParam?: string;\n /**\n * Onboarding flows start on their own when the visitor does their trigger (default true). False: only\n * `useCockpit().startOnboarding()` starts one. In-app messages and `openCancelFlow` work either way.\n */\n onboarding?: boolean;\n /**\n * The look of the cancel flow, onboarding and message dialogs: `{ theme: \"dark\", accent: \"#0f766e\", className }`.\n * Without an accent the onboarding dialogs take the product's brand color from MRRocket (0.18.0).\n */\n appearance?: FlowsTheme;\n /**\n * 0.18.0: opens what a checklist task's \"Opens: page or screen\" names, with your router (`(target) => router.push(target)`).\n * Without it a path or an http(s) address opens as a normal page load.\n */\n onNavigate?: (target: string) => void;\n /** 0.18.0: a checklist task set to \"Opens: your code\" was tapped: `(taskId, item) => …`. */\n onTask?: (taskId: string, item: OnboardingFlow) => void;\n /**\n * Form leads (0.12.0): a submitted form marked `data-mrrocket-form` becomes a contact in MRRocket (default true).\n * \"auto\" also captures any form with an email field (never one with a password field); false captures none. Only\n * the email, the name and the other fields a person typed go out, never passwords, card or hidden fields (forms.ts).\n */\n forms?: boolean | \"auto\";\n children: ReactNode;\n}\n\n/**\n * Put it once around your app, in `app/layout.tsx`. It loads your experiments, keeps each visitor in one variant,\n * records page views and sends your events to Cockpit in small batches.\n *\n * `preview` forces variants for this render, for example from the `ck_preview` search param on the server,\n * so a Cockpit preview never flickers. Without it the provider reads the param itself after the first render.\n */\nexport const CockpitProvider = ({\n apiKey: apiKeyProp,\n host: hostProp,\n preview: forced,\n embedded: embeddedProp,\n config: bootstrapConfig,\n locale,\n distinctId: bootstrapId,\n userHash: bootstrapHash,\n consent: consentProp = \"cookieless\",\n respectDnt = false,\n replay: replayProp = false,\n heatmaps = true,\n surveys = true,\n errors = true,\n release,\n attribution = \"consent\",\n referralParam = \"ref\",\n onboarding = true,\n appearance,\n onNavigate,\n onTask,\n forms = true,\n children,\n}: CockpitProviderProps) => {\n const pathname = usePathname();\n const apiKey = apiKeyProp ?? envKey() ?? \"\";\n const host = resolveHost(hostProp);\n const [config, setConfig] = useState<CockpitConfig | null>(bootstrapConfig ?? null);\n // The app's language before any child asks the server for words. Only in the browser: this module's state is shared\n // by all requests on a server.\n useState(() => {\n if (locale && typeof window !== \"undefined\") setLocale(locale);\n });\n const [localeTick, setLocaleTick] = useState(0);\n const [wordsTick, setWordsTick] = useState(0);\n const [consent, setConsentState] = useState<ConsentMode>(consentProp);\n const [replayConsent, setReplayConsent] = useState(replayProp);\n const [mounted, setMounted] = useState(false);\n const [dnt, setDnt] = useState(false);\n // The visitor id and the consent mode it was made for; null until the browser has been read.\n const [visitor, setVisitor] = useState<{ mode: ConsentMode; id: string } | null>(null);\n const [identified, setIdentified] = useState<string | undefined>();\n const [urlPreview, setUrlPreview] = useState<Record<string, VariantId>>({});\n const [urlEmbedded, setUrlEmbedded] = useState(false);\n const [user, setUser] = useState<CockpitUser | undefined>();\n const [ready, setReady] = useState(!!bootstrapConfig);\n // The id identify() signed and its hash (identity.ts); the ref answers callbacks at once, the state re-renders.\n const [signed, setSigned] = useState<Signed | null>(null);\n const signedRef = useRef<Signed | null>(null);\n const queue = useRef<CockpitEvent[]>([]);\n // Calls from before the visitor was known (a child's effect runs before ours); replayed once it is.\n const early = useRef<(() => void)[]>([]);\n const exposed = useRef(new Set<string>());\n const lastPage = useRef<string | null>(null);\n\n const bootstrap = bootstrapId && bootstrapId !== \"anonymous\" ? bootstrapId : undefined;\n // The `distinctId` prop signed by the server layout (`userHash` prop).\n const bootSigned = useMemo(() => (bootstrap && bootstrapHash ? nextSigned(null, bootstrap, bootstrapHash) : null), [bootstrap, bootstrapHash]);\n const bootSignedRef = useRef(bootSigned);\n bootSignedRef.current = bootSigned;\n /** The identity hash for `id`, only when the SDK holds one for exactly that id. */\n const hashOf = useCallback((id: string | undefined) => hashFor(signedRef.current, id) ?? hashFor(bootSignedRef.current, id), []);\n const preview = useMemo(() => ({ ...urlPreview, ...forced }), [urlPreview, forced]);\n const embedded = embeddedProp ?? urlEmbedded;\n const effectiveConsent: ConsentMode = respectDnt && dnt ? \"off\" : consent;\n const distinctId = identified ?? bootstrap ?? visitor?.id ?? \"anonymous\";\n const tracking = !embedded && effectiveConsent !== \"off\" && visitor?.mode === effectiveConsent;\n // Without cookies, events carry an id only after identify(); the server counts everyone else by its daily hash.\n const sendId = effectiveConsent === \"cookies\" ? distinctId : identified;\n const userHash = hashFor(signed, distinctId) ?? hashFor(bootSigned, distinctId);\n\n // Preview flags and the browser's privacy signal, once, in the browser.\n useEffect(() => {\n const params = new URLSearchParams(window.location.search);\n setUrlPreview(parsePreview(params.get(PREVIEW_PARAM)));\n setUrlEmbedded(params.get(EMBED_PARAM) === \"1\");\n setDnt(privacySignal());\n setMounted(true);\n // Opened by MRRocket's onboarding editor to pick an element for a tour step (./pick.ts); only then, only in a frame.\n return params.get(EMBED_PARAM) === \"1\" ? (startPicker() ?? undefined) : undefined;\n }, []);\n\n const setConsent = useCallback((choice: ConsentChoice) => {\n const { analytics: mode, replay } = typeof choice === \"string\" ? { analytics: choice, replay: undefined } : choice;\n if (mode === \"off\") {\n queue.current.length = 0;\n // The one thing that still goes out: the signed-in person withdrew, so lists stop sorting them by what they do.\n const now = live.current;\n if (now.tracking && now.identified && now.sendId) queue.current.push({ event: CONSENT_OFF_EVENT, distinctId: now.sendId, ts: Date.now() });\n }\n if (mode) setConsentState(mode);\n if (typeof replay === \"boolean\") setReplayConsent(replay);\n }, []);\n\n // Follow the `consent` and `replay` props when they change; setConsent() calls in between stay until they do.\n const lastProp = useRef(consentProp);\n useEffect(() => {\n if (lastProp.current === consentProp) return;\n lastProp.current = consentProp;\n setConsent(consentProp);\n }, [consentProp, setConsent]);\n const lastReplayProp = useRef(replayProp);\n useEffect(() => {\n if (lastReplayProp.current === replayProp) return;\n lastReplayProp.current = replayProp;\n setConsent({ replay: replayProp });\n }, [replayProp, setConsent]);\n\n // Storage follows consent. Leaving \"cookies\" deletes what the SDK stored; this page keeps its id in memory.\n const storedFor = useRef<ConsentMode | null>(null);\n useEffect(() => {\n if (!mounted) return;\n const before = storedFor.current;\n storedFor.current = effectiveConsent;\n if (before === \"cookies\" && effectiveConsent !== \"cookies\") {\n deleteCookie(ID_COOKIE);\n deleteCookie(SESSION_COOKIE);\n forgetStoredDismissals();\n forgetFlowsStorage();\n }\n if (effectiveConsent === \"off\") {\n queue.current.length = 0;\n page.session = undefined;\n }\n setVisitor({ mode: effectiveConsent, id: visitorId(effectiveConsent, !bootstrap) });\n }, [mounted, effectiveConsent, bootstrap]);\n\n useEffect(() => {\n if (locale) setLocale(locale);\n }, [locale]);\n useEffect(() => onLocale(() => setLocaleTick((n) => n + 1)), []);\n // New words: every widget under the provider draws again (the context changes with them).\n useEffect(() => onWords(() => setWordsTick((n) => n + 1)), []);\n\n // Experiments for this key. Without a key or when MRRocket is unreachable the page keeps control and carries on.\n useEffect(() => {\n if (!apiKey) warnOnce(\"No public key. Pass apiKey or set NEXT_PUBLIC_MRROCKET_KEY; nothing is tracked until then.\");\n // A config from the server stays unless the visitor reads another language than the one it brought. In an\n // effect, not in render: the first paint must match the server's, which has no visitor language.\n if (bootstrapConfig) setWords(bootstrapConfig.words, bootstrapConfig.lang);\n const reads = currentLocale()?.split(\"-\")[0].toLowerCase();\n if (!apiKey || (bootstrapConfig && (!reads || reads === (bootstrapConfig.lang ?? \"en\")))) {\n setReady(true);\n return;\n }\n let live = true;\n fetch(`${host}/api/sdk/config?key=${encodeURIComponent(apiKey)}${langParam()}`)\n .then((r) => {\n if (!r.ok) warnOnce(`Config request to ${host || \"this origin\"} answered ${r.status}. Check the public key and NEXT_PUBLIC_MRROCKET_HOST.`);\n return r.ok ? r.json() : null;\n })\n .then((c: CockpitConfig | null) => {\n if (!live) return;\n if (c && Array.isArray(c.experiments)) {\n setWords(c.words, c.lang);\n setConfig(c);\n }\n setReady(true);\n })\n .catch(() => {\n warnOnce(`Could not reach ${host || \"this origin\"}. Check NEXT_PUBLIC_MRROCKET_HOST. The page keeps working with control variants.`);\n if (live) setReady(true);\n });\n return () => {\n live = false;\n };\n }, [apiKey, host, bootstrapConfig, localeTick]);\n\n const flush = useCallback(\n (beacon = false) => {\n if (!queue.current.length) return;\n if (!apiKey) {\n queue.current.length = 0;\n return;\n }\n const url = `${host}/api/sdk/events`;\n for (const body of bodies(apiKey, queue.current.splice(0), { v: SDK_VERSION, errors }, hashOf(live.current.sendId))) {\n const sent = beacon && typeof navigator !== \"undefined\" && \"sendBeacon\" in navigator && navigator.sendBeacon(url, body);\n if (!sent)\n fetch(url, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body, keepalive: true })\n .then((r) => {\n if (r.status === 401) warnUnsigned(401);\n else if (!r.ok) warnOnce(`Events were rejected with ${r.status}. Check the public key (Settings › SDK) and NEXT_PUBLIC_MRROCKET_HOST.`);\n })\n .catch(() => warnOnce(`Could not reach ${host || \"this origin\"} to send events. Check NEXT_PUBLIC_MRROCKET_HOST.`));\n }\n },\n [apiKey, host, errors, hashOf],\n );\n\n useEffect(() => {\n const timer = window.setInterval(() => flush(), 2000);\n const leave = () => flush(true);\n window.addEventListener(\"pagehide\", leave);\n return () => {\n window.clearInterval(timer);\n window.removeEventListener(\"pagehide\", leave);\n flush(true);\n };\n }, [flush]);\n\n // The latest values for callbacks that must not change identity, so effects that use them run once.\n const live = useRef({ resolved: false, tracking, distinctId, sendId, persist: false, identified: false });\n live.current = { resolved: visitor !== null, tracking, distinctId, sendId, persist: visitor?.mode === \"cookies\", identified: !!identified };\n\n const record = useCallback((event: string, props: Record<string, unknown> | undefined, ts = Date.now(), url?: string) => {\n const now = live.current;\n if (!now.resolved) {\n early.current.push(() => record(event, props, ts, url));\n return;\n }\n // Previews in Cockpit never count, and nothing is sent without consent.\n if (!now.tracking) return;\n const referral = carriesReferral(event) ? getReferral() : null;\n queue.current.push({\n event,\n props: referral ? { ...props, $referral: { code: referral.code, clickId: referral.clickId } } : props,\n distinctId: now.sendId,\n sessionId: touchSession(now.persist),\n ts,\n url: url ?? window.location.href,\n referrer: page.referrer,\n ...page.utm,\n screen: { w: window.screen.width, h: window.screen.height },\n });\n }, []);\n\n // The running recorder, if this visit records (see the recording effect below).\n const replayer = useRef<ReplayHandle | null>(null);\n // Error tracking, while `errors` is on (see the effect below).\n const errorer = useRef<ErrorCapture | null>(null);\n\n // The cancel flow, onboarding and in-app messages (flows.ts): one controller for the provider's lifetime.\n const flowsRef = useRef<FlowsController | null>(null);\n // The latest handlers, read when a task is tapped.\n const handlers = useRef({ onNavigate, onTask });\n handlers.current = { onNavigate, onTask };\n const flows = (flowsRef.current ??= createFlows(\n webFlowsEnv({\n host,\n apiKey,\n track: (event, props) => record(event, props),\n personId: () => (live.current.tracking && live.current.identified ? live.current.distinctId : undefined),\n userHash: hashOf,\n canStore: () => live.current.persist,\n auto: onboarding,\n onNavigate: () => handlers.current.onNavigate,\n onTask: () => handlers.current.onTask,\n }),\n ));\n\n // Paywalls (paywall.ts): one controller too; its dialog loads with the first `open` (a dynamic import).\n const configNow = useRef(config);\n configNow.current = config;\n const paywallRef = useRef<{ host: PaywallHost; mounted?: Promise<unknown> } | null>(null);\n const paywall = (paywallRef.current ??= {\n host: createPaywallHost({ config: () => configNow.current, distinctId: () => live.current.distinctId, track: (event, props) => record(event, props) }),\n });\n\n const track = useCallback(\n (event: string, props?: Record<string, unknown>) => {\n record(event, props);\n if (live.current.tracking) replayer.current?.event(event);\n flows.onEvent(event);\n },\n [record, flows],\n );\n\n const identify = useCallback(\n (id: string, props?: Record<string, unknown>, options?: IdentifyOptions) => {\n const now = live.current;\n if (!now.resolved) {\n early.current.push(() => identify(id, props, options));\n return;\n }\n const previous = now.sendId;\n // The hash belongs to this id; another id without one drops it (identity.ts).\n signedRef.current = nextSigned(signedRef.current ?? bootSignedRef.current, id, options?.hash);\n setSigned(signedRef.current);\n // From here on events carry this id in every mode; only \"cookies\" keeps it for the next visit.\n live.current = { ...now, distinctId: id, sendId: id, identified: true };\n record(\"$identify\", { ...props, id, ...(previous && previous !== id ? { previousId: previous } : {}) });\n replayer.current?.identify(id, hashOf(id));\n if (now.persist) writeCookie(ID_COOKIE, id, YEAR_S);\n setIdentified(id);\n setUser({ id, ...(props as Omit<CockpitUser, \"id\">) });\n },\n [record, hashOf],\n );\n\n // Form leads (forms.ts, a dynamic import): one submit listener on the document while `forms` is on.\n const identifyRef = useRef(identify);\n identifyRef.current = identify;\n useEffect(() => {\n if (!forms || !apiKey || embedded || typeof window === \"undefined\") return;\n let off: (() => void) | undefined;\n let gone = false;\n void import(\"./forms\")\n .then((m) => {\n if (gone) return;\n off = m.watchForms({\n host,\n apiKey,\n auto: forms === \"auto\",\n distinctId: () => (live.current.tracking ? live.current.sendId : undefined),\n userHash: hashOf,\n identified: () => live.current.identified,\n identify: (id, props) => identifyRef.current(id, props),\n tracking: () => live.current.tracking,\n referrer: () => page.referrer,\n utm: () => page.utm,\n });\n })\n .catch(() => undefined);\n return () => {\n gone = true;\n off?.();\n };\n }, [forms, apiKey, host, embedded, hashOf]);\n\n // Replay what came in before the visitor was known: sent when tracking is on, dropped when it is not.\n useEffect(() => {\n if (!visitor) return;\n for (const run of early.current.splice(0)) run();\n }, [visitor]);\n\n // Affiliate referrals: read once the consent is known and the program's cookie days have loaded.\n const referralDays = config?.referral?.cookieDays ?? DEFAULT_REFERRAL_DAYS;\n const storeReferrals = attribution === \"always\" ? effectiveConsent !== \"off\" : attribution === \"consent\" && effectiveConsent === \"cookies\";\n const captured = useRef<string | null>(null);\n const storedReferral = useRef(false);\n useEffect(() => {\n if (!visitor || !ready || embedded) return;\n if (attribution === \"off\" || effectiveConsent === \"off\") {\n resetReferral();\n if (attribution === \"off\" || storedReferral.current) forgetStoredReferral();\n return;\n }\n const href = window.location.href;\n if (captured.current === href) return;\n captured.current = href;\n void captureReferral({\n search: window.location.search,\n param: referralParam,\n store: storeReferrals,\n days: referralDays,\n beacon: apiKey\n ? ({ code, clickId }) =>\n fetch(`${host}/api/sdk/affiliate/click`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ key: apiKey, code, clickId, url: href, referrer: document.referrer || undefined }),\n keepalive: true,\n })\n .then((r) => (r.ok ? (r.json() as Promise<{ clickId?: string | null }>) : null))\n .then((b) => b?.clickId ?? null)\n : undefined,\n });\n }, [visitor, ready, embedded, attribution, effectiveConsent, pathname, referralParam, storeReferrals, referralDays, apiKey, host]);\n\n // Consent given later keeps this page load's referral; consent withdrawn deletes what was stored.\n useEffect(() => {\n if (!mounted) return;\n const r = getReferral();\n if (storeReferrals && r) storeReferral(r, referralDays);\n else if (!storeReferrals && storedReferral.current) forgetStoredReferral();\n storedReferral.current = storeReferrals;\n }, [mounted, storeReferrals, referralDays]);\n\n // `tracking` is a dependency so that variants seen before consent are recorded once tracking starts.\n const expose = useCallback(\n (key: string, variant: VariantId) => {\n if (!tracking) return;\n const mark = `${pathname}|${key}`;\n if (exposed.current.has(mark)) return;\n exposed.current.add(mark);\n track(\"$exposure\", { experiment: key, variant });\n },\n [pathname, track, tracking],\n );\n\n // Redirects, once per page: a page redirect or a redirect test's variant sends the visitor on as soon as the config is\n // known (`experimentRedirect` in proxy.ts does it before the page renders, without a flash). A redirect test's\n // control stays and is recorded here; the page a variant lands on records it from `ck_x`, then drops the parameter.\n const decided = useRef<{ path: string; r: ReturnType<typeof redirectFor> } | null>(null);\n const arrived = useRef<string | null | undefined>(undefined);\n useEffect(() => {\n if (!ready || !visitor) return;\n if (decided.current?.path !== pathname) {\n const r = redirectFor(config, window.location.href, distinctId, preview);\n decided.current = { path: pathname, r };\n if (r?.to) {\n flush(true);\n window.location.replace(r.to);\n return;\n }\n }\n if (arrived.current === undefined) {\n const params = new URLSearchParams(window.location.search);\n arrived.current = params.get(EXPOSURE_PARAM);\n if (arrived.current !== null) {\n params.delete(EXPOSURE_PARAM);\n const q = params.toString();\n window.history.replaceState(window.history.state, \"\", `${window.location.pathname}${q ? `?${q}` : \"\"}${window.location.hash}`);\n }\n }\n if (!tracking) return;\n const r = decided.current.r;\n if (r?.experiment && !r.to) expose(r.experiment, r.variant!);\n const x = exposureFrom(config, arrived.current);\n if (x) expose(x.experiment, x.variant);\n arrived.current = null;\n }, [ready, visitor, pathname, config, distinctId, preview, tracking, expose, flush]);\n\n // The referrer and campaign of each page; before the page view below, so it already carries them.\n useEffect(() => {\n enterPage();\n replayer.current?.page(pathname);\n errorer.current?.crumb(\"navigation\", pathname);\n }, [pathname]);\n\n // Errors of the page for Build › Errors; sent only while tracking (consent, not a preview), like events.\n useEffect(() => {\n if (!errors || !apiKey || embedded) return;\n const capture = startErrorCapture({\n host,\n apiKey,\n release,\n context: () => ({ send: live.current.tracking, distinctId: live.current.sendId, sessionId: page.session?.id, userHash: hashOf(live.current.sendId) }),\n warn: warnOnce,\n });\n errorer.current = capture;\n capture.crumb(\"navigation\", window.location.pathname);\n return () => {\n capture.stop();\n if (errorer.current === capture) errorer.current = null;\n };\n }, [errors, apiKey, host, release, embedded, hashOf]);\n\n const captureError = useCallback((error: unknown) => errorer.current?.capture(error, \"manual\"), []);\n\n // One page view per route change while tracking, so a later setConsent() records the current page right away\n // (a ref keeps React's dev double-run from counting twice).\n useEffect(() => {\n if (!tracking || lastPage.current === pathname) return;\n lastPage.current = pathname;\n // Exposures are remembered per path (\"/pricing|test\"), so nothing needs clearing here.\n track(\"$pageview\", { path: pathname });\n }, [pathname, tracking, track]);\n\n // Clicks on anything marked `data-ck-goal=\"signup_started\"` count as that goal. Every click also feeds Heatmaps\n // (`$click`, `$rageclick` on the third quick click on one spot) and, when this visit records, the recording.\n const rage = useRef(new RageDetector());\n useEffect(() => {\n const onClick = (e: MouseEvent) => {\n const el = (e.target as HTMLElement | null)?.closest<HTMLElement>(\"[data-ck-goal]\");\n if (el?.dataset.ckGoal) track(el.dataset.ckGoal, { text: el.textContent?.trim().slice(0, 80) });\n if (!live.current.tracking) return;\n const c = clickInfo(e);\n if (!c) return;\n const raged = rage.current.push(c.x, c.y, Date.now());\n if (c.label || c.selector) errorer.current?.crumb(\"click\", c.label || c.selector);\n replayer.current?.click(c, raged);\n if (!heatmaps) return;\n const props = { x: c.x, y: c.y, vw: c.vw, vh: c.vh, docH: c.docH, selector: c.selector, label: c.label, dead: !c.interactive };\n record(\"$click\", props);\n if (raged) record(\"$rageclick\", props);\n };\n document.addEventListener(\"click\", onClick);\n return () => document.removeEventListener(\"click\", onClick);\n }, [track, record, heatmaps]);\n\n // Scroll depth for Heatmaps: the deepest point seen on each page, sent when the page changes or hides.\n const depth = useRef<{ url: string; max: number; docH: number } | null>(null);\n useEffect(() => {\n if (!heatmaps || !tracking || embedded) return;\n const send = () => {\n const d = depth.current;\n if (d && d.max > 0) record(\"$scroll\", { depth: d.max, docH: d.docH, vw: window.innerWidth, vh: window.innerHeight }, Date.now(), d.url);\n depth.current = null;\n };\n const measure = () => {\n const doc = document.documentElement;\n const docH = Math.max(doc.scrollHeight, document.body?.scrollHeight ?? 0);\n const max = seenDepth();\n const href = window.location.href;\n if (depth.current?.url !== href) depth.current = { url: href, max, docH };\n else if (max > depth.current.max) depth.current = { url: href, max, docH };\n };\n measure();\n let frame = 0;\n const onScroll = () => {\n if (frame) return;\n frame = window.requestAnimationFrame(() => {\n frame = 0;\n measure();\n });\n };\n const onHide = () => {\n send();\n flush(true);\n };\n window.addEventListener(\"scroll\", onScroll, { passive: true });\n window.addEventListener(\"pagehide\", onHide);\n return () => {\n window.removeEventListener(\"scroll\", onScroll);\n window.removeEventListener(\"pagehide\", onHide);\n window.cancelAnimationFrame(frame);\n send();\n };\n }, [heatmaps, tracking, embedded, pathname, record, flush]);\n\n // Session recording: only with the visitor's replay consent, while tracking, and when the product records. The\n // recorder and rrweb are loaded here, lazily; withdrawing consent stops it and drops what was not sent.\n const replayConfig = config?.replay;\n const recording = tracking && replayConsent && !!replayConfig && !!apiKey;\n useEffect(() => {\n if (!recording || !replayConfig) return;\n let active = true;\n let handle: ReplayHandle | null = null;\n // `typeof window` is a build-time constant in Next.js: on the server this folds to null and the import (with\n // rrweb) never enters the server bundle.\n void (typeof window === \"undefined\" ? Promise.resolve(null) : import(\"./replay\"))\n .then((m) =>\n m?.startReplay({\n host,\n apiKey,\n sessionId: touchSession(live.current.persist),\n distinctId: live.current.sendId,\n identified: live.current.identified,\n userHash: hashOf(live.current.sendId),\n sampleRate: replayConfig.sampleRate,\n warn: warnOnce,\n }),\n )\n .then((h) => {\n if (!active) h?.stop(true);\n else replayer.current = handle = h ?? null;\n })\n .catch(() => warnOnce(\"Session recording could not start (the recorder did not load).\"));\n return () => {\n active = false;\n handle?.stop(true);\n if (replayer.current === handle) replayer.current = null;\n };\n }, [recording, replayConfig, host, apiKey, hashOf]);\n\n // The flows follow the config and the person: their messages and onboarding progress load once both are known.\n useEffect(() => {\n if (!ready || !visitor || embedded) return;\n flows.setConfig(config);\n void flows.refresh();\n }, [flows, ready, visitor, embedded, config, identified, tracking, effectiveConsent]);\n\n // The dialogs load the first time something is about to show (a dynamic import), and stay mounted after.\n const look = useRef(appearance);\n look.current = appearance;\n useEffect(() => {\n if (embedded) return;\n let unmount: (() => void) | null = null;\n let loading = false;\n let gone = false;\n const show = () => {\n if (unmount || loading || !flows.view()) return;\n loading = true;\n void (typeof window === \"undefined\" ? Promise.resolve(null) : import(\"./flows-ui\"))\n .then((m) => {\n if (!m || gone) return;\n unmount = m.mountFlows(flows, look.current);\n })\n .catch(() => warnOnce(\"The cancel flow and onboarding dialogs did not load.\"))\n .finally(() => {\n loading = false;\n });\n };\n const off = flows.subscribe(show);\n show();\n return () => {\n gone = true;\n off();\n unmount?.();\n };\n }, [flows, embedded]);\n\n const openCancelFlow = useCallback((options?: CancelFlowOptions) => flows.openCancelFlow(options), [flows]);\n const startOnboarding = useCallback((id?: string) => flows.startOnboarding(id), [flows]);\n const openPaywall = useCallback(\n (placement: string, options?: PaywallOptions): Promise<PaywallResult> => {\n if (typeof window === \"undefined\") return Promise.resolve({ outcome: \"closed\", index: 0 });\n paywall.mounted ??= import(\"./flows-ui\").then((m) => m.mountPaywall(paywall.host, look.current));\n return paywall.mounted.then(() => paywall.host.open(placement, options));\n },\n [paywall],\n );\n\n // Web push (push-web.ts, a dynamic import on the first call). The permission is asked here, synchronously inside the\n // click that called it: Safari refuses one asked after an await.\n const subscribeToPush = useCallback(\n (options?: WebPushOptions): Promise<PushResult> => {\n if (typeof window === \"undefined\") return Promise.resolve({ status: \"unsupported\" });\n const now = live.current;\n const distinctId = now.tracking && now.identified ? now.distinctId : undefined;\n if (!distinctId) return Promise.resolve({ status: \"not-signed-in\" });\n const N = window.Notification;\n const permission = Promise.resolve(N && N.permission === \"default\" && options?.ask !== false ? N.requestPermission() : N?.permission);\n return import(\"./push-web\").then((m) => m.subscribeWebPush({ host, apiKey, distinctId, userHash: hashOf(distinctId), permission }, options));\n },\n [host, apiKey, hashOf],\n );\n const unsubscribeFromPush = useCallback(\n () => {\n if (typeof window === \"undefined\") return Promise.resolve(false);\n const distinctId = live.current.identified ? live.current.distinctId : undefined;\n return import(\"./push-web\").then((m) => m.unsubscribeWebPush({ host, apiKey, distinctId, userHash: hashOf(distinctId) }));\n },\n [host, apiKey, hashOf],\n );\n\n const texts = useMemo(() => textChanges(config, distinctId, preview), [config, distinctId, preview]);\n\n const value = useMemo<CockpitContext>(\n () => ({\n apiKey,\n host,\n user,\n ready,\n distinctId,\n userHash,\n config,\n preview,\n embedded,\n consent,\n effectiveConsent,\n replayConsent,\n setConsent,\n texts,\n track,\n identify,\n expose,\n captureError,\n openCancelFlow,\n startOnboarding,\n flows,\n openPaywall,\n subscribeToPush,\n unsubscribeFromPush,\n }),\n [\n wordsTick,\n flows,\n apiKey,\n host,\n user,\n ready,\n distinctId,\n userHash,\n config,\n preview,\n embedded,\n consent,\n effectiveConsent,\n replayConsent,\n setConsent,\n texts,\n track,\n identify,\n expose,\n captureError,\n openCancelFlow,\n startOnboarding,\n openPaywall,\n subscribeToPush,\n unsubscribeFromPush,\n ],\n );\n return (\n <Ctx.Provider value={value}>\n {children}\n {surveys ? <Survey /> : null}\n </Ctx.Provider>\n );\n};\n\n/**\n * `track`, `identify`, `captureError`, the consent mode, the replay consent and their setter, the visitor id and whether\n * the experiments and flags have loaded. `setConsent(\"cookies\")` sets the mode, `setConsent({ replay: true })` the\n * replay consent.\n */\nexport const useCockpit = () => {\n const { track, identify, captureError, distinctId, ready, embedded, consent, replayConsent, setConsent, openCancelFlow, startOnboarding } = useCtx();\n return { track, identify, captureError, distinctId, ready, embedded, consent, replay: replayConsent, setConsent, openCancelFlow, startOnboarding };\n};\n\n/**\n * Push notifications from your Lifecycle journeys for the signed-in person (`identify` first). Call `subscribe()` from\n * a click: it asks the browser's permission and signs this browser up (your site hosts `/mrrocket-sw.js`). Call\n * `unsubscribe()` on sign-out. Resolves with `{ status: \"subscribed\" | \"denied\" | \"not-asked\" | \"unsupported\" |\n * \"not-signed-in\" | \"error\" }`.\n *\n * const push = usePush();\n * <button onClick={() => push.subscribe()}>Turn on notifications</button>\n */\nexport const usePush = (): { subscribe: (options?: WebPushOptions) => Promise<PushResult>; unsubscribe: () => Promise<boolean> } => {\n const { subscribeToPush, unsubscribeFromPush } = useCtx();\n return useMemo(() => ({ subscribe: subscribeToPush, unsubscribe: unsubscribeFromPush }), [subscribeToPush, unsubscribeFromPush]);\n};\n\n/**\n * A feature flag from MRRocket › Build › Feature flags: true while it is on for this visitor (its audience and rollout\n * in Production), false while it is off, unknown, or the flags have not loaded yet (`useCockpit().ready`). The person's\n * properties from `identify(id, props)` feed the audience conditions (\"Email\", \"Plan\" …).\n *\n * const newCheckout = useFlag(\"new-checkout\");\n */\nexport const useFlag = (key: string): boolean => useFlagValue(key) !== false;\n\n/** The variant of a multivariate flag for this visitor (\"control\", \"test\" …), or null while the flag is off. */\nexport const useFlagVariant = (key: string): string | null => {\n const v = useFlagValue(key);\n return typeof v === \"string\" ? v : null;\n};\n\nconst useFlagValue = (key: string) => {\n const { config, distinctId, user } = useCtx();\n return useMemo(() => flagFor(key, config, distinctId, { ...user }), [key, config, distinctId, user]);\n};\n\n/** The variant this visitor sees, for example `\"control\"` or `\"a\"`. Records the exposure. */\nexport const useVariant = (key: string): VariantId => {\n const { config, distinctId, preview, expose } = useCtx();\n const variant = resolveVariant(key, config, distinctId, preview);\n useEffect(() => {\n if (config || preview[key]) expose(key, variant);\n }, [config, preview, key, variant, expose]);\n return variant;\n};\n\n/** Renders the element for the visitor's variant; control when the variant has no element. */\nexport const Experiment = ({ name, variants }: { name: string; variants: Record<VariantId, ReactNode> & { control: ReactNode } }) => {\n const variant = useVariant(name);\n return <>{variants[variant] ?? variants.control}</>;\n};\n\n/**\n * A text that experiments may change without a deploy: Cockpit sends the new copy for `id` and this renders it.\n * Write your default as the children.\n */\nexport const Text = ({ id, children }: { id: string; children: string }) => {\n const { texts, config, distinctId, preview, expose } = useCtx();\n // Showing a text a running test changes is an exposure to that test, whichever variant the visitor has.\n useEffect(() => {\n for (const key of experimentsOfText(config, id)) expose(key, resolveVariant(key, config, distinctId, preview));\n }, [config, id, distinctId, preview, expose]);\n return <>{texts[id] ?? children}</>;\n};\n\n/**\n * The cancel flow you built in MRRocket › Churn › Cancel flow. Call `open` from your Cancel button: it shows the flow\n * and resolves with what the customer did. MRRocket never changes the subscription; your `onCancel` and `onSave` do.\n *\n * const cancelFlow = useCancelFlow();\n * <button onClick={() => cancelFlow.open({ customer: \"business\", onCancel: cancelSubscription, onSave: applyOffer })}>\n */\nexport const useCancelFlow = (): { open: (options?: CancelFlowOptions) => Promise<CancelFlowResult> } => {\n const { openCancelFlow } = useCtx();\n return useMemo(() => ({ open: openCancelFlow }), [openCancelFlow]);\n};\n\n/**\n * 0.18.0: an onboarding item you draw yourself (\"Shown by: your code\" in MRRocket): its content, the person's progress\n * and the calls that report what they did; MRRocket still counts it and keeps the answers on the contact.\n *\n * const { item, steps, done, complete, dismiss } = useOnboarding(\"flow_123\");\n * if (!item || done) return null;\n * return <MyChecklist title={item.name} tasks={steps} onTick={complete} onClose={dismiss} />;\n */\nexport const useOnboarding = (id?: string): UseOnboarding => {\n const { flows, startOnboarding } = useCtx();\n return useOnboardingItem(flows, id, startOnboarding);\n};\n\n/**\n * 0.22.0: for an onboarding that lives in your own code. Nothing to set up in MRRocket: the first report makes the item\n * under Onboarding, and MRRocket shows who saw each step and what they answered. It follows the visitor's consent.\n *\n * const report = useOnboardingReport(\"Welcome questions\", { order: [\"goal\", \"role\"] });\n * report.saw(\"goal\", \"What brings you here?\");\n * report.answered(\"goal\", \"dream\", \"Dream game\");\n * report.closed(\"goal\");\n */\nexport const useOnboardingReport = (name: string, options?: OnboardingReportOptions): OnboardingReport => useReportWith(useCtx().track, name, options);\n\n/**\n * The paywalls you built in MRRocket › Paywalls, with their pages (benefits, social proof, plans). `open` shows the\n * placement's paywall page by page; on its last page the button hands the chosen plan to `onPurchase`, where your code\n * buys it (Stripe Checkout). MRRocket never takes a payment. A running test gives each visitor their variant's pages.\n *\n * const paywall = usePaywall();\n * <button onClick={() => paywall.open(\"feature_gate\", { onPurchase: (plan) => startCheckout(plan.id) })}>\n */\nexport const usePaywall = (): { open: (placement: string, options?: PaywallOptions) => Promise<PaywallResult> } => {\n const { openPaywall } = useCtx();\n return useMemo(() => ({ open: openPaywall }), [openPaywall]);\n};\n"],"mappings":"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","names":["live","r","distinctId"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.tsx"],"sourcesContent":["\"use client\";\n\nimport { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { usePathname } from \"next/navigation\";\nimport { Ctx, type CockpitContext, type CockpitUser, useCtx } from \"./context\";\nimport { Survey, forgetStoredDismissals } from \"./survey\";\nimport { RageDetector, clickInfo, seenDepth } from \"./capture\";\nimport type { ReplayHandle } from \"./replay\";\nimport { type ErrorCapture, startErrorCapture } from \"./errors\";\nimport {\n type AttributionMode,\n DEFAULT_REFERRAL_DAYS,\n captureReferral,\n carriesReferral,\n forgetStoredReferral,\n getReferral,\n resetReferral,\n storeReferral,\n} from \"./referral\";\nimport {\n type CockpitConfig,\n type CockpitEvent,\n type ConsentChoice,\n type ConsentMode,\n EMBED_PARAM,\n EXPOSURE_PARAM,\n ID_COOKIE,\n PREVIEW_PARAM,\n SDK_VERSION,\n SESSION_COOKIE,\n type VariantId,\n envKey,\n experimentsOfText,\n exposureFrom,\n flagFor,\n parsePreview,\n redirectFor,\n resolveHost,\n resolveVariant,\n textChanges,\n CONSENT_OFF_EVENT,\n} from \"./core\";\nimport type { PushResult, WebPushOptions } from \"./push-web\";\nimport { bodies } from \"./batch\";\nimport { YEAR_S, deleteCookie, enterPage, page, privacySignal, touchSession, visitorId, writeCookie } from \"./web-state\";\nimport { type CancelFlowOptions, type CancelFlowResult, type FlowsController, type OnboardingFlow, createFlows } from \"./flows\";\nimport type { OnboardingReport, OnboardingReportOptions } from \"./onboarding-report\";\nimport { type UseOnboarding, useOnboardingItem, useReportWith } from \"./onboarding-state\";\nimport { startPicker } from \"./pick\";\nimport type { FlowsTheme } from \"./flows-ui\";\nimport { forgetFlowsStorage, webFlowsEnv } from \"./flows-web\";\nimport { type PaywallHost, type PaywallOptions, type PaywallResult, createPaywallHost } from \"./paywall\";\nimport { type IdentifyOptions, type Signed, hashFor, nextSigned, warnUnsigned } from \"./identity\";\nimport { currentLocale, langParam, onLocale, onWords, setLocale, setWords } from \"./i18n\";\n\n/** Tells the developer once, in the browser console, why the SDK is not reaching MRRocket (a wrong host or key is the usual cause). */\nconst warned = new Set<string>();\nconst warnOnce = (message: string) => {\n if (warned.has(message)) return;\n warned.add(message);\n console.warn(`[MRRocket] ${message}`);\n};\n\nexport interface CockpitProviderProps {\n /** The product's public key from Cockpit › Settings › SDK. Safe in the browser. Default: `NEXT_PUBLIC_MRROCKET_KEY`. */\n apiKey?: string;\n /** Where MRRocket runs, like `https://app.example.com`. Default: `NEXT_PUBLIC_MRROCKET_HOST`, else the page's own origin. */\n host?: string;\n preview?: Record<string, VariantId>;\n embedded?: boolean;\n /** Config fetched on the server, to render the right variant on the first paint. */\n config?: CockpitConfig;\n /**\n * 0.21.0: the language of the SDK's words and of what the server answers, like \"de-AT\", for an app with its own\n * language switch. Default: the browser's. `setLocale` does the same from anywhere.\n */\n locale?: string;\n /** Your own id for this visitor, for example a signed-in user id. Keeps variants stable without cookies. */\n distinctId?: string;\n /**\n * The identity hash of `distinctId` (0.16.0): `await userHash(user.id)` from `@mrrocket/sdk/next/server`, made in\n * the server layout that knows the user. Sent with every request for that id, so MRRocket knows it is really them\n * (needed with MRRocket › Settings › Products › Sign-in › \"Only signed users\"). In the browser pass it to\n * `identify(id, props, { hash })` instead.\n */\n userHash?: string;\n /**\n * \"cookieless\" (default) stores nothing in the browser, \"cookies\" keeps a visitor cookie for a year, \"off\" sends\n * nothing. The SDK does not remember the choice: set it on every page load, from the prop or setConsent().\n */\n consent?: ConsentMode;\n /** Send nothing when the browser asks not to be tracked (Do Not Track or Global Privacy Control). */\n respectDnt?: boolean;\n /**\n * The visitor's consent to session recordings, separate from analytics (default false). Recording also needs the\n * product to record (Cockpit › Sessions) and analytics not \"off\". Without it nothing is recorded or buffered.\n * Change it later with `setConsent({ replay })`.\n */\n replay?: boolean;\n /**\n * Clicks and scroll depth for Heatmaps (default true): where a click landed, a short selector and a link's or\n * button's label, never typed text. Sent under the normal analytics consent as `$click`, `$rageclick` and `$scroll`.\n */\n heatmaps?: boolean;\n /** Ask visitors the surveys you run in Cockpit. Set false to switch them off. */\n surveys?: boolean;\n /**\n * Error tracking for Build › Errors (default true): uncaught errors and unhandled promise rejections, with the page\n * and the last pages and clicks, under the normal analytics consent. Report errors you catch with\n * `useCockpit().captureError(error)`.\n */\n errors?: boolean;\n /** Your release (a version or a commit), so MRRocket can tell which release an error came with. */\n release?: string;\n /**\n * Affiliate referrals (`?ref=` links and MRRocket's tracked links). \"consent\" (default): stored in the first-party\n * `mrr_ref` cookie only while consent is \"cookies\", in memory otherwise. \"always\": stored whatever the consent (only\n * where your legal basis allows it). \"off\": referrals are ignored.\n */\n attribution?: AttributionMode;\n /** The query parameter of direct partner links, default `ref` (`https://your.site/?ref=maya`). */\n referralParam?: string;\n /**\n * Onboarding flows start on their own when the visitor does their trigger (default true). False: only\n * `useCockpit().startOnboarding()` starts one. In-app messages and `openCancelFlow` work either way.\n */\n onboarding?: boolean;\n /**\n * The look of the cancel flow, onboarding and message dialogs: `{ theme: \"dark\", accent: \"#0f766e\", className }`.\n * Without an accent the onboarding dialogs take the product's brand color from MRRocket (0.18.0).\n */\n appearance?: FlowsTheme;\n /**\n * 0.18.0: opens what a checklist task's \"Opens: page or screen\" names, with your router (`(target) => router.push(target)`).\n * Without it a path or an http(s) address opens as a normal page load.\n */\n onNavigate?: (target: string) => void;\n /** 0.18.0: a checklist task set to \"Opens: your code\" was tapped: `(taskId, item) => …`. */\n onTask?: (taskId: string, item: OnboardingFlow) => void;\n /**\n * Form leads (0.12.0): a submitted form marked `data-mrrocket-form` becomes a contact in MRRocket (default true).\n * \"auto\" also captures any form with an email field (never one with a password field); false captures none. Only\n * the email, the name and the other fields a person typed go out, never passwords, card or hidden fields (forms.ts).\n */\n forms?: boolean | \"auto\";\n children: ReactNode;\n}\n\n/**\n * Put it once around your app, in `app/layout.tsx`. It loads your experiments, keeps each visitor in one variant,\n * records page views and sends your events to Cockpit in small batches.\n *\n * `preview` forces variants for this render, for example from the `ck_preview` search param on the server,\n * so a Cockpit preview never flickers. Without it the provider reads the param itself after the first render.\n */\nexport const CockpitProvider = ({\n apiKey: apiKeyProp,\n host: hostProp,\n preview: forced,\n embedded: embeddedProp,\n config: bootstrapConfig,\n locale,\n distinctId: bootstrapId,\n userHash: bootstrapHash,\n consent: consentProp = \"cookieless\",\n respectDnt = false,\n replay: replayProp = false,\n heatmaps = true,\n surveys = true,\n errors = true,\n release,\n attribution = \"consent\",\n referralParam = \"ref\",\n onboarding = true,\n appearance,\n onNavigate,\n onTask,\n forms = true,\n children,\n}: CockpitProviderProps) => {\n const pathname = usePathname();\n const apiKey = apiKeyProp ?? envKey() ?? \"\";\n const host = resolveHost(hostProp);\n const [config, setConfig] = useState<CockpitConfig | null>(bootstrapConfig ?? null);\n // The app's language before any child asks the server for words. Only in the browser: this module's state is shared\n // by all requests on a server.\n useState(() => {\n if (locale && typeof window !== \"undefined\") setLocale(locale);\n });\n const [localeTick, setLocaleTick] = useState(0);\n const [wordsTick, setWordsTick] = useState(0);\n const [consent, setConsentState] = useState<ConsentMode>(consentProp);\n const [replayConsent, setReplayConsent] = useState(replayProp);\n const [mounted, setMounted] = useState(false);\n const [dnt, setDnt] = useState(false);\n // The visitor id and the consent mode it was made for; null until the browser has been read.\n const [visitor, setVisitor] = useState<{ mode: ConsentMode; id: string } | null>(null);\n const [identified, setIdentified] = useState<string | undefined>();\n const [urlPreview, setUrlPreview] = useState<Record<string, VariantId>>({});\n const [urlEmbedded, setUrlEmbedded] = useState(false);\n const [user, setUser] = useState<CockpitUser | undefined>();\n const [ready, setReady] = useState(!!bootstrapConfig);\n // The id identify() signed and its hash (identity.ts); the ref answers callbacks at once, the state re-renders.\n const [signed, setSigned] = useState<Signed | null>(null);\n const signedRef = useRef<Signed | null>(null);\n const queue = useRef<CockpitEvent[]>([]);\n // Calls from before the visitor was known (a child's effect runs before ours); replayed once it is.\n const early = useRef<(() => void)[]>([]);\n const exposed = useRef(new Set<string>());\n const lastPage = useRef<string | null>(null);\n\n const bootstrap = bootstrapId && bootstrapId !== \"anonymous\" ? bootstrapId : undefined;\n // The `distinctId` prop signed by the server layout (`userHash` prop).\n const bootSigned = useMemo(() => (bootstrap && bootstrapHash ? nextSigned(null, bootstrap, bootstrapHash) : null), [bootstrap, bootstrapHash]);\n const bootSignedRef = useRef(bootSigned);\n bootSignedRef.current = bootSigned;\n /** The identity hash for `id`, only when the SDK holds one for exactly that id. */\n const hashOf = useCallback((id: string | undefined) => hashFor(signedRef.current, id) ?? hashFor(bootSignedRef.current, id), []);\n const preview = useMemo(() => ({ ...urlPreview, ...forced }), [urlPreview, forced]);\n const embedded = embeddedProp ?? urlEmbedded;\n const effectiveConsent: ConsentMode = respectDnt && dnt ? \"off\" : consent;\n const distinctId = identified ?? bootstrap ?? visitor?.id ?? \"anonymous\";\n const tracking = !embedded && effectiveConsent !== \"off\" && visitor?.mode === effectiveConsent;\n // Without cookies, events carry an id only after identify(); the server counts everyone else by its daily hash.\n const sendId = effectiveConsent === \"cookies\" ? distinctId : identified;\n const userHash = hashFor(signed, distinctId) ?? hashFor(bootSigned, distinctId);\n\n // Preview flags and the browser's privacy signal, once, in the browser.\n useEffect(() => {\n const params = new URLSearchParams(window.location.search);\n setUrlPreview(parsePreview(params.get(PREVIEW_PARAM)));\n setUrlEmbedded(params.get(EMBED_PARAM) === \"1\");\n setDnt(privacySignal());\n setMounted(true);\n // Opened by MRRocket's onboarding editor to pick an element for a tour step (./pick.ts); only then, only in a frame.\n return params.get(EMBED_PARAM) === \"1\" ? (startPicker() ?? undefined) : undefined;\n }, []);\n\n /** Sends these events now, past the queue; a beacon outlives the page. */\n const sendNow = useCallback(\n (events: CockpitEvent[], beacon: boolean, hash: string | undefined) => {\n if (!apiKey) return;\n const url = `${host}/api/sdk/events`;\n for (const body of bodies(apiKey, events, { v: SDK_VERSION, errors }, hash)) {\n const sent = beacon && typeof navigator !== \"undefined\" && \"sendBeacon\" in navigator && navigator.sendBeacon(url, body);\n if (!sent)\n fetch(url, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body, keepalive: true })\n .then((r) => {\n if (r.status === 401) warnUnsigned(401);\n else if (!r.ok) warnOnce(`Events were rejected with ${r.status}. Check the public key (Settings › SDK) and NEXT_PUBLIC_MRROCKET_HOST.`);\n })\n .catch(() => warnOnce(`Could not reach ${host || \"this origin\"} to send events. Check NEXT_PUBLIC_MRROCKET_HOST.`));\n }\n },\n [apiKey, host, errors],\n );\n\n const setConsent = useCallback(\n (choice: ConsentChoice) => {\n const { analytics: mode, replay } = typeof choice === \"string\" ? { analytics: choice, replay: undefined } : choice;\n if (mode === \"off\") {\n queue.current.length = 0;\n // The one thing that still goes out: the signed-in person withdrew, so lists stop sorting them by what they do.\n // Sent here, not queued: the effect below empties the queue when consent turns off. Only a change made here\n // says it: a browser's privacy signal or a page that starts off never tracked anyone.\n const now = live.current;\n const who = now.identified ? now.sendId : bootstrap;\n if (now.tracking && who) {\n live.current = { ...now, tracking: false };\n sendNow([{ event: CONSENT_OFF_EVENT, distinctId: who, ts: Date.now() }], true, hashOf(who));\n }\n }\n if (mode) setConsentState(mode);\n if (typeof replay === \"boolean\") setReplayConsent(replay);\n },\n [bootstrap, hashOf, sendNow],\n );\n\n // Follow the `consent` and `replay` props when they change; setConsent() calls in between stay until they do.\n const lastProp = useRef(consentProp);\n useEffect(() => {\n if (lastProp.current === consentProp) return;\n lastProp.current = consentProp;\n setConsent(consentProp);\n }, [consentProp, setConsent]);\n const lastReplayProp = useRef(replayProp);\n useEffect(() => {\n if (lastReplayProp.current === replayProp) return;\n lastReplayProp.current = replayProp;\n setConsent({ replay: replayProp });\n }, [replayProp, setConsent]);\n\n // Storage follows consent. Leaving \"cookies\" deletes what the SDK stored; this page keeps its id in memory.\n const storedFor = useRef<ConsentMode | null>(null);\n useEffect(() => {\n if (!mounted) return;\n const before = storedFor.current;\n storedFor.current = effectiveConsent;\n if (before === \"cookies\" && effectiveConsent !== \"cookies\") {\n deleteCookie(ID_COOKIE);\n deleteCookie(SESSION_COOKIE);\n forgetStoredDismissals();\n forgetFlowsStorage();\n }\n if (effectiveConsent === \"off\") {\n queue.current.length = 0;\n page.session = undefined;\n }\n setVisitor({ mode: effectiveConsent, id: visitorId(effectiveConsent, !bootstrap) });\n }, [mounted, effectiveConsent, bootstrap]);\n\n useEffect(() => {\n if (locale) setLocale(locale);\n }, [locale]);\n useEffect(() => onLocale(() => setLocaleTick((n) => n + 1)), []);\n // New words: every widget under the provider draws again (the context changes with them).\n useEffect(() => onWords(() => setWordsTick((n) => n + 1)), []);\n\n // Experiments for this key. Without a key or when MRRocket is unreachable the page keeps control and carries on.\n useEffect(() => {\n if (!apiKey) warnOnce(\"No public key. Pass apiKey or set NEXT_PUBLIC_MRROCKET_KEY; nothing is tracked until then.\");\n // A config from the server stays unless the visitor reads another language than the one it brought. In an\n // effect, not in render: the first paint must match the server's, which has no visitor language.\n if (bootstrapConfig) setWords(bootstrapConfig.words, bootstrapConfig.lang);\n const reads = currentLocale()?.split(\"-\")[0].toLowerCase();\n if (!apiKey || (bootstrapConfig && (!reads || reads === (bootstrapConfig.lang ?? \"en\")))) {\n setReady(true);\n return;\n }\n let live = true;\n fetch(`${host}/api/sdk/config?key=${encodeURIComponent(apiKey)}${langParam()}`)\n .then((r) => {\n if (!r.ok) warnOnce(`Config request to ${host || \"this origin\"} answered ${r.status}. Check the public key and NEXT_PUBLIC_MRROCKET_HOST.`);\n return r.ok ? r.json() : null;\n })\n .then((c: CockpitConfig | null) => {\n if (!live) return;\n if (c && Array.isArray(c.experiments)) {\n setWords(c.words, c.lang);\n setConfig(c);\n }\n setReady(true);\n })\n .catch(() => {\n warnOnce(`Could not reach ${host || \"this origin\"}. Check NEXT_PUBLIC_MRROCKET_HOST. The page keeps working with control variants.`);\n if (live) setReady(true);\n });\n return () => {\n live = false;\n };\n }, [apiKey, host, bootstrapConfig, localeTick]);\n\n const flush = useCallback(\n (beacon = false) => {\n if (!queue.current.length) return;\n if (!apiKey) {\n queue.current.length = 0;\n return;\n }\n sendNow(queue.current.splice(0), beacon, hashOf(live.current.sendId));\n },\n [apiKey, hashOf, sendNow],\n );\n\n useEffect(() => {\n const timer = window.setInterval(() => flush(), 2000);\n const leave = () => flush(true);\n window.addEventListener(\"pagehide\", leave);\n return () => {\n window.clearInterval(timer);\n window.removeEventListener(\"pagehide\", leave);\n flush(true);\n };\n }, [flush]);\n\n // The latest values for callbacks that must not change identity, so effects that use them run once.\n const live = useRef({ resolved: false, tracking, distinctId, sendId, persist: false, identified: false });\n live.current = { resolved: visitor !== null, tracking, distinctId, sendId, persist: visitor?.mode === \"cookies\", identified: !!identified };\n\n const record = useCallback((event: string, props: Record<string, unknown> | undefined, ts = Date.now(), url?: string) => {\n const now = live.current;\n if (!now.resolved) {\n early.current.push(() => record(event, props, ts, url));\n return;\n }\n // Previews in Cockpit never count, and nothing is sent without consent.\n if (!now.tracking) return;\n const referral = carriesReferral(event) ? getReferral() : null;\n queue.current.push({\n event,\n props: referral ? { ...props, $referral: { code: referral.code, clickId: referral.clickId } } : props,\n distinctId: now.sendId,\n sessionId: touchSession(now.persist),\n ts,\n url: url ?? window.location.href,\n referrer: page.referrer,\n ...page.utm,\n screen: { w: window.screen.width, h: window.screen.height },\n });\n }, []);\n\n // The running recorder, if this visit records (see the recording effect below).\n const replayer = useRef<ReplayHandle | null>(null);\n // Error tracking, while `errors` is on (see the effect below).\n const errorer = useRef<ErrorCapture | null>(null);\n\n // The cancel flow, onboarding and in-app messages (flows.ts): one controller for the provider's lifetime.\n const flowsRef = useRef<FlowsController | null>(null);\n // The latest handlers, read when a task is tapped.\n const handlers = useRef({ onNavigate, onTask });\n handlers.current = { onNavigate, onTask };\n const flows = (flowsRef.current ??= createFlows(\n webFlowsEnv({\n host,\n apiKey,\n track: (event, props) => record(event, props),\n personId: () => (live.current.tracking && live.current.identified ? live.current.distinctId : undefined),\n userHash: hashOf,\n canStore: () => live.current.persist,\n auto: onboarding,\n onNavigate: () => handlers.current.onNavigate,\n onTask: () => handlers.current.onTask,\n }),\n ));\n\n // Paywalls (paywall.ts): one controller too; its dialog loads with the first `open` (a dynamic import).\n const configNow = useRef(config);\n configNow.current = config;\n const paywallRef = useRef<{ host: PaywallHost; mounted?: Promise<unknown> } | null>(null);\n const paywall = (paywallRef.current ??= {\n host: createPaywallHost({ config: () => configNow.current, distinctId: () => live.current.distinctId, track: (event, props) => record(event, props) }),\n });\n\n const track = useCallback(\n (event: string, props?: Record<string, unknown>) => {\n record(event, props);\n if (live.current.tracking) replayer.current?.event(event);\n flows.onEvent(event);\n },\n [record, flows],\n );\n\n const identify = useCallback(\n (id: string, props?: Record<string, unknown>, options?: IdentifyOptions) => {\n const now = live.current;\n if (!now.resolved) {\n early.current.push(() => identify(id, props, options));\n return;\n }\n const previous = now.sendId;\n // The hash belongs to this id; another id without one drops it (identity.ts).\n signedRef.current = nextSigned(signedRef.current ?? bootSignedRef.current, id, options?.hash);\n setSigned(signedRef.current);\n // From here on events carry this id in every mode; only \"cookies\" keeps it for the next visit.\n live.current = { ...now, distinctId: id, sendId: id, identified: true };\n record(\"$identify\", { ...props, id, ...(previous && previous !== id ? { previousId: previous } : {}) });\n replayer.current?.identify(id, hashOf(id));\n if (now.persist) writeCookie(ID_COOKIE, id, YEAR_S);\n setIdentified(id);\n setUser({ id, ...(props as Omit<CockpitUser, \"id\">) });\n },\n [record, hashOf],\n );\n\n // Form leads (forms.ts, a dynamic import): one submit listener on the document while `forms` is on.\n const identifyRef = useRef(identify);\n identifyRef.current = identify;\n useEffect(() => {\n if (!forms || !apiKey || embedded || typeof window === \"undefined\") return;\n let off: (() => void) | undefined;\n let gone = false;\n void import(\"./forms\")\n .then((m) => {\n if (gone) return;\n off = m.watchForms({\n host,\n apiKey,\n auto: forms === \"auto\",\n distinctId: () => (live.current.tracking ? live.current.sendId : undefined),\n userHash: hashOf,\n identified: () => live.current.identified,\n identify: (id, props) => identifyRef.current(id, props),\n tracking: () => live.current.tracking,\n referrer: () => page.referrer,\n utm: () => page.utm,\n });\n })\n .catch(() => undefined);\n return () => {\n gone = true;\n off?.();\n };\n }, [forms, apiKey, host, embedded, hashOf]);\n\n // Replay what came in before the visitor was known: sent when tracking is on, dropped when it is not.\n useEffect(() => {\n if (!visitor) return;\n for (const run of early.current.splice(0)) run();\n }, [visitor]);\n\n // Affiliate referrals: read once the consent is known and the program's cookie days have loaded.\n const referralDays = config?.referral?.cookieDays ?? DEFAULT_REFERRAL_DAYS;\n const storeReferrals = attribution === \"always\" ? effectiveConsent !== \"off\" : attribution === \"consent\" && effectiveConsent === \"cookies\";\n const captured = useRef<string | null>(null);\n const storedReferral = useRef(false);\n useEffect(() => {\n if (!visitor || !ready || embedded) return;\n if (attribution === \"off\" || effectiveConsent === \"off\") {\n resetReferral();\n if (attribution === \"off\" || storedReferral.current) forgetStoredReferral();\n return;\n }\n const href = window.location.href;\n if (captured.current === href) return;\n captured.current = href;\n void captureReferral({\n search: window.location.search,\n param: referralParam,\n store: storeReferrals,\n days: referralDays,\n beacon: apiKey\n ? ({ code, clickId }) =>\n fetch(`${host}/api/sdk/affiliate/click`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ key: apiKey, code, clickId, url: href, referrer: document.referrer || undefined }),\n keepalive: true,\n })\n .then((r) => (r.ok ? (r.json() as Promise<{ clickId?: string | null }>) : null))\n .then((b) => b?.clickId ?? null)\n : undefined,\n });\n }, [visitor, ready, embedded, attribution, effectiveConsent, pathname, referralParam, storeReferrals, referralDays, apiKey, host]);\n\n // Consent given later keeps this page load's referral; consent withdrawn deletes what was stored.\n useEffect(() => {\n if (!mounted) return;\n const r = getReferral();\n if (storeReferrals && r) storeReferral(r, referralDays);\n else if (!storeReferrals && storedReferral.current) forgetStoredReferral();\n storedReferral.current = storeReferrals;\n }, [mounted, storeReferrals, referralDays]);\n\n // `tracking` is a dependency so that variants seen before consent are recorded once tracking starts.\n const expose = useCallback(\n (key: string, variant: VariantId) => {\n if (!tracking) return;\n const mark = `${pathname}|${key}`;\n if (exposed.current.has(mark)) return;\n exposed.current.add(mark);\n track(\"$exposure\", { experiment: key, variant });\n },\n [pathname, track, tracking],\n );\n\n // Redirects, once per page: a page redirect or a redirect test's variant sends the visitor on as soon as the config is\n // known (`experimentRedirect` in proxy.ts does it before the page renders, without a flash). A redirect test's\n // control stays and is recorded here; the page a variant lands on records it from `ck_x`, then drops the parameter.\n const decided = useRef<{ path: string; r: ReturnType<typeof redirectFor> } | null>(null);\n const arrived = useRef<string | null | undefined>(undefined);\n useEffect(() => {\n if (!ready || !visitor) return;\n if (decided.current?.path !== pathname) {\n const r = redirectFor(config, window.location.href, distinctId, preview);\n decided.current = { path: pathname, r };\n if (r?.to) {\n flush(true);\n window.location.replace(r.to);\n return;\n }\n }\n if (arrived.current === undefined) {\n const params = new URLSearchParams(window.location.search);\n arrived.current = params.get(EXPOSURE_PARAM);\n if (arrived.current !== null) {\n params.delete(EXPOSURE_PARAM);\n const q = params.toString();\n window.history.replaceState(window.history.state, \"\", `${window.location.pathname}${q ? `?${q}` : \"\"}${window.location.hash}`);\n }\n }\n if (!tracking) return;\n const r = decided.current.r;\n if (r?.experiment && !r.to) expose(r.experiment, r.variant!);\n const x = exposureFrom(config, arrived.current);\n if (x) expose(x.experiment, x.variant);\n arrived.current = null;\n }, [ready, visitor, pathname, config, distinctId, preview, tracking, expose, flush]);\n\n // The referrer and campaign of each page; before the page view below, so it already carries them.\n useEffect(() => {\n enterPage();\n replayer.current?.page(pathname);\n errorer.current?.crumb(\"navigation\", pathname);\n }, [pathname]);\n\n // Errors of the page for Build › Errors; sent only while tracking (consent, not a preview), like events.\n useEffect(() => {\n if (!errors || !apiKey || embedded) return;\n const capture = startErrorCapture({\n host,\n apiKey,\n release,\n context: () => ({ send: live.current.tracking, distinctId: live.current.sendId, sessionId: page.session?.id, userHash: hashOf(live.current.sendId) }),\n warn: warnOnce,\n });\n errorer.current = capture;\n capture.crumb(\"navigation\", window.location.pathname);\n return () => {\n capture.stop();\n if (errorer.current === capture) errorer.current = null;\n };\n }, [errors, apiKey, host, release, embedded, hashOf]);\n\n const captureError = useCallback((error: unknown) => errorer.current?.capture(error, \"manual\"), []);\n\n // One page view per route change while tracking, so a later setConsent() records the current page right away\n // (a ref keeps React's dev double-run from counting twice).\n useEffect(() => {\n if (!tracking || lastPage.current === pathname) return;\n lastPage.current = pathname;\n // Exposures are remembered per path (\"/pricing|test\"), so nothing needs clearing here.\n track(\"$pageview\", { path: pathname });\n }, [pathname, tracking, track]);\n\n // Clicks on anything marked `data-ck-goal=\"signup_started\"` count as that goal. Every click also feeds Heatmaps\n // (`$click`, `$rageclick` on the third quick click on one spot) and, when this visit records, the recording.\n const rage = useRef(new RageDetector());\n useEffect(() => {\n const onClick = (e: MouseEvent) => {\n const el = (e.target as HTMLElement | null)?.closest<HTMLElement>(\"[data-ck-goal]\");\n if (el?.dataset.ckGoal) track(el.dataset.ckGoal, { text: el.textContent?.trim().slice(0, 80) });\n if (!live.current.tracking) return;\n const c = clickInfo(e);\n if (!c) return;\n const raged = rage.current.push(c.x, c.y, Date.now());\n if (c.label || c.selector) errorer.current?.crumb(\"click\", c.label || c.selector);\n replayer.current?.click(c, raged);\n if (!heatmaps) return;\n const props = { x: c.x, y: c.y, vw: c.vw, vh: c.vh, docH: c.docH, selector: c.selector, label: c.label, dead: !c.interactive };\n record(\"$click\", props);\n if (raged) record(\"$rageclick\", props);\n };\n document.addEventListener(\"click\", onClick);\n return () => document.removeEventListener(\"click\", onClick);\n }, [track, record, heatmaps]);\n\n // Scroll depth for Heatmaps: the deepest point seen on each page, sent when the page changes or hides.\n const depth = useRef<{ url: string; max: number; docH: number } | null>(null);\n useEffect(() => {\n if (!heatmaps || !tracking || embedded) return;\n const send = () => {\n const d = depth.current;\n if (d && d.max > 0) record(\"$scroll\", { depth: d.max, docH: d.docH, vw: window.innerWidth, vh: window.innerHeight }, Date.now(), d.url);\n depth.current = null;\n };\n const measure = () => {\n const doc = document.documentElement;\n const docH = Math.max(doc.scrollHeight, document.body?.scrollHeight ?? 0);\n const max = seenDepth();\n const href = window.location.href;\n if (depth.current?.url !== href) depth.current = { url: href, max, docH };\n else if (max > depth.current.max) depth.current = { url: href, max, docH };\n };\n measure();\n let frame = 0;\n const onScroll = () => {\n if (frame) return;\n frame = window.requestAnimationFrame(() => {\n frame = 0;\n measure();\n });\n };\n const onHide = () => {\n send();\n flush(true);\n };\n window.addEventListener(\"scroll\", onScroll, { passive: true });\n window.addEventListener(\"pagehide\", onHide);\n return () => {\n window.removeEventListener(\"scroll\", onScroll);\n window.removeEventListener(\"pagehide\", onHide);\n window.cancelAnimationFrame(frame);\n send();\n };\n }, [heatmaps, tracking, embedded, pathname, record, flush]);\n\n // Session recording: only with the visitor's replay consent, while tracking, and when the product records. The\n // recorder and rrweb are loaded here, lazily; withdrawing consent stops it and drops what was not sent.\n const replayConfig = config?.replay;\n const recording = tracking && replayConsent && !!replayConfig && !!apiKey;\n useEffect(() => {\n if (!recording || !replayConfig) return;\n let active = true;\n let handle: ReplayHandle | null = null;\n // `typeof window` is a build-time constant in Next.js: on the server this folds to null and the import (with\n // rrweb) never enters the server bundle.\n void (typeof window === \"undefined\" ? Promise.resolve(null) : import(\"./replay\"))\n .then((m) =>\n m?.startReplay({\n host,\n apiKey,\n sessionId: touchSession(live.current.persist),\n distinctId: live.current.sendId,\n identified: live.current.identified,\n userHash: hashOf(live.current.sendId),\n sampleRate: replayConfig.sampleRate,\n warn: warnOnce,\n }),\n )\n .then((h) => {\n if (!active) h?.stop(true);\n else replayer.current = handle = h ?? null;\n })\n .catch(() => warnOnce(\"Session recording could not start (the recorder did not load).\"));\n return () => {\n active = false;\n handle?.stop(true);\n if (replayer.current === handle) replayer.current = null;\n };\n }, [recording, replayConfig, host, apiKey, hashOf]);\n\n // The flows follow the config and the person: their messages and onboarding progress load once both are known.\n useEffect(() => {\n if (!ready || !visitor || embedded) return;\n flows.setConfig(config);\n void flows.refresh();\n }, [flows, ready, visitor, embedded, config, identified, tracking, effectiveConsent]);\n\n // The dialogs load the first time something is about to show (a dynamic import), and stay mounted after.\n const look = useRef(appearance);\n look.current = appearance;\n useEffect(() => {\n if (embedded) return;\n let unmount: (() => void) | null = null;\n let loading = false;\n let gone = false;\n const show = () => {\n if (unmount || loading || !flows.view()) return;\n loading = true;\n void (typeof window === \"undefined\" ? Promise.resolve(null) : import(\"./flows-ui\"))\n .then((m) => {\n if (!m || gone) return;\n unmount = m.mountFlows(flows, look.current);\n })\n .catch(() => warnOnce(\"The cancel flow and onboarding dialogs did not load.\"))\n .finally(() => {\n loading = false;\n });\n };\n const off = flows.subscribe(show);\n show();\n return () => {\n gone = true;\n off();\n unmount?.();\n };\n }, [flows, embedded]);\n\n const openCancelFlow = useCallback((options?: CancelFlowOptions) => flows.openCancelFlow(options), [flows]);\n const startOnboarding = useCallback((id?: string) => flows.startOnboarding(id), [flows]);\n const openPaywall = useCallback(\n (placement: string, options?: PaywallOptions): Promise<PaywallResult> => {\n if (typeof window === \"undefined\") return Promise.resolve({ outcome: \"closed\", index: 0 });\n paywall.mounted ??= import(\"./flows-ui\").then((m) => m.mountPaywall(paywall.host, look.current));\n return paywall.mounted.then(() => paywall.host.open(placement, options));\n },\n [paywall],\n );\n\n // Web push (push-web.ts, a dynamic import on the first call). The permission is asked here, synchronously inside the\n // click that called it: Safari refuses one asked after an await.\n const subscribeToPush = useCallback(\n (options?: WebPushOptions): Promise<PushResult> => {\n if (typeof window === \"undefined\") return Promise.resolve({ status: \"unsupported\" });\n const now = live.current;\n const distinctId = now.tracking && now.identified ? now.distinctId : undefined;\n if (!distinctId) return Promise.resolve({ status: \"not-signed-in\" });\n const N = window.Notification;\n const permission = Promise.resolve(N && N.permission === \"default\" && options?.ask !== false ? N.requestPermission() : N?.permission);\n return import(\"./push-web\").then((m) => m.subscribeWebPush({ host, apiKey, distinctId, userHash: hashOf(distinctId), permission }, options));\n },\n [host, apiKey, hashOf],\n );\n const unsubscribeFromPush = useCallback(\n () => {\n if (typeof window === \"undefined\") return Promise.resolve(false);\n const distinctId = live.current.identified ? live.current.distinctId : undefined;\n return import(\"./push-web\").then((m) => m.unsubscribeWebPush({ host, apiKey, distinctId, userHash: hashOf(distinctId) }));\n },\n [host, apiKey, hashOf],\n );\n\n const texts = useMemo(() => textChanges(config, distinctId, preview), [config, distinctId, preview]);\n\n const value = useMemo<CockpitContext>(\n () => ({\n apiKey,\n host,\n user,\n ready,\n distinctId,\n userHash,\n config,\n preview,\n embedded,\n consent,\n effectiveConsent,\n replayConsent,\n setConsent,\n texts,\n track,\n identify,\n expose,\n captureError,\n openCancelFlow,\n startOnboarding,\n flows,\n openPaywall,\n subscribeToPush,\n unsubscribeFromPush,\n }),\n [\n wordsTick,\n flows,\n apiKey,\n host,\n user,\n ready,\n distinctId,\n userHash,\n config,\n preview,\n embedded,\n consent,\n effectiveConsent,\n replayConsent,\n setConsent,\n texts,\n track,\n identify,\n expose,\n captureError,\n openCancelFlow,\n startOnboarding,\n openPaywall,\n subscribeToPush,\n unsubscribeFromPush,\n ],\n );\n return (\n <Ctx.Provider value={value}>\n {children}\n {surveys ? <Survey /> : null}\n </Ctx.Provider>\n );\n};\n\n/**\n * `track`, `identify`, `captureError`, the consent mode, the replay consent and their setter, the visitor id and whether\n * the experiments and flags have loaded. `setConsent(\"cookies\")` sets the mode, `setConsent({ replay: true })` the\n * replay consent.\n */\nexport const useCockpit = () => {\n const { track, identify, captureError, distinctId, ready, embedded, consent, replayConsent, setConsent, openCancelFlow, startOnboarding } = useCtx();\n return { track, identify, captureError, distinctId, ready, embedded, consent, replay: replayConsent, setConsent, openCancelFlow, startOnboarding };\n};\n\n/**\n * Push notifications from your Lifecycle journeys for the signed-in person (`identify` first). Call `subscribe()` from\n * a click: it asks the browser's permission and signs this browser up (your site hosts `/mrrocket-sw.js`). Call\n * `unsubscribe()` on sign-out. Resolves with `{ status: \"subscribed\" | \"denied\" | \"not-asked\" | \"unsupported\" |\n * \"not-signed-in\" | \"error\" }`.\n *\n * const push = usePush();\n * <button onClick={() => push.subscribe()}>Turn on notifications</button>\n */\nexport const usePush = (): { subscribe: (options?: WebPushOptions) => Promise<PushResult>; unsubscribe: () => Promise<boolean> } => {\n const { subscribeToPush, unsubscribeFromPush } = useCtx();\n return useMemo(() => ({ subscribe: subscribeToPush, unsubscribe: unsubscribeFromPush }), [subscribeToPush, unsubscribeFromPush]);\n};\n\n/**\n * A feature flag from MRRocket › Build › Feature flags: true while it is on for this visitor (its audience and rollout\n * in Production), false while it is off, unknown, or the flags have not loaded yet (`useCockpit().ready`). The person's\n * properties from `identify(id, props)` feed the audience conditions (\"Email\", \"Plan\" …).\n *\n * const newCheckout = useFlag(\"new-checkout\");\n */\nexport const useFlag = (key: string): boolean => useFlagValue(key) !== false;\n\n/** The variant of a multivariate flag for this visitor (\"control\", \"test\" …), or null while the flag is off. */\nexport const useFlagVariant = (key: string): string | null => {\n const v = useFlagValue(key);\n return typeof v === \"string\" ? v : null;\n};\n\nconst useFlagValue = (key: string) => {\n const { config, distinctId, user } = useCtx();\n return useMemo(() => flagFor(key, config, distinctId, { ...user }), [key, config, distinctId, user]);\n};\n\n/** The variant this visitor sees, for example `\"control\"` or `\"a\"`. Records the exposure. */\nexport const useVariant = (key: string): VariantId => {\n const { config, distinctId, preview, expose } = useCtx();\n const variant = resolveVariant(key, config, distinctId, preview);\n useEffect(() => {\n if (config || preview[key]) expose(key, variant);\n }, [config, preview, key, variant, expose]);\n return variant;\n};\n\n/** Renders the element for the visitor's variant; control when the variant has no element. */\nexport const Experiment = ({ name, variants }: { name: string; variants: Record<VariantId, ReactNode> & { control: ReactNode } }) => {\n const variant = useVariant(name);\n return <>{variants[variant] ?? variants.control}</>;\n};\n\n/**\n * A text that experiments may change without a deploy: Cockpit sends the new copy for `id` and this renders it.\n * Write your default as the children.\n */\nexport const Text = ({ id, children }: { id: string; children: string }) => {\n const { texts, config, distinctId, preview, expose } = useCtx();\n // Showing a text a running test changes is an exposure to that test, whichever variant the visitor has.\n useEffect(() => {\n for (const key of experimentsOfText(config, id)) expose(key, resolveVariant(key, config, distinctId, preview));\n }, [config, id, distinctId, preview, expose]);\n return <>{texts[id] ?? children}</>;\n};\n\n/**\n * The cancel flow you built in MRRocket › Churn › Cancel flow. Call `open` from your Cancel button: it shows the flow\n * and resolves with what the customer did. MRRocket never changes the subscription; your `onCancel` and `onSave` do.\n *\n * const cancelFlow = useCancelFlow();\n * <button onClick={() => cancelFlow.open({ customer: \"business\", onCancel: cancelSubscription, onSave: applyOffer })}>\n */\nexport const useCancelFlow = (): { open: (options?: CancelFlowOptions) => Promise<CancelFlowResult> } => {\n const { openCancelFlow } = useCtx();\n return useMemo(() => ({ open: openCancelFlow }), [openCancelFlow]);\n};\n\n/**\n * 0.18.0: an onboarding item you draw yourself (\"Shown by: your code\" in MRRocket): its content, the person's progress\n * and the calls that report what they did; MRRocket still counts it and keeps the answers on the contact.\n *\n * const { item, steps, done, complete, dismiss } = useOnboarding(\"flow_123\");\n * if (!item || done) return null;\n * return <MyChecklist title={item.name} tasks={steps} onTick={complete} onClose={dismiss} />;\n */\nexport const useOnboarding = (id?: string): UseOnboarding => {\n const { flows, startOnboarding } = useCtx();\n return useOnboardingItem(flows, id, startOnboarding);\n};\n\n/**\n * 0.22.0: for an onboarding that lives in your own code. Nothing to set up in MRRocket: the first report makes the item\n * under Onboarding, and MRRocket shows who saw each step and what they answered. It follows the visitor's consent.\n *\n * const report = useOnboardingReport(\"Welcome questions\", { order: [\"goal\", \"role\"] });\n * report.saw(\"goal\", \"What brings you here?\");\n * report.answered(\"goal\", \"dream\", \"Dream game\");\n * report.closed(\"goal\");\n */\nexport const useOnboardingReport = (name: string, options?: OnboardingReportOptions): OnboardingReport => useReportWith(useCtx().track, name, options);\n\n/**\n * The paywalls you built in MRRocket › Paywalls, with their pages (benefits, social proof, plans). `open` shows the\n * placement's paywall page by page; on its last page the button hands the chosen plan to `onPurchase`, where your code\n * buys it (Stripe Checkout). MRRocket never takes a payment. A running test gives each visitor their variant's pages.\n *\n * const paywall = usePaywall();\n * <button onClick={() => paywall.open(\"feature_gate\", { onPurchase: (plan) => startCheckout(plan.id) })}>\n */\nexport const usePaywall = (): { open: (placement: string, options?: PaywallOptions) => Promise<PaywallResult> } => {\n const { openPaywall } = useCtx();\n return useMemo(() => ({ open: openPaywall }), [openPaywall]);\n};\n"],"mappings":"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","names":["live","r","distinctId"]}
|
package/dist/core.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { CancelFlowConfig, OnboardingFlow } from "./flows.js";
|
|
|
6
6
|
import type { PaywallConfig } from "./paywall.js";
|
|
7
7
|
export type VariantId = string;
|
|
8
8
|
/** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */
|
|
9
|
-
export declare const SDK_VERSION = "0.23.
|
|
9
|
+
export declare const SDK_VERSION = "0.23.1";
|
|
10
10
|
/** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */
|
|
11
11
|
export interface VariantConfig {
|
|
12
12
|
id: VariantId;
|
package/dist/core.js
CHANGED
package/dist/core.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core.ts"],"sourcesContent":["/**\n * The part of the SDK that runs anywhere: experiment config, variant assignment, preview links and event shapes.\n * No React, no browser APIs, so the server helper, the proxy helper and the client share it.\n */\nimport type { CancelFlowConfig, OnboardingFlow } from \"./flows\";\nimport type { PaywallConfig } from \"./paywall\";\n\nexport type VariantId = string;\n\n/** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */\nexport const SDK_VERSION = \"0.23.0\";\n\n/** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */\nexport interface VariantConfig {\n id: VariantId;\n /** Share of traffic, 0 to 1. The weights of an experiment add up to 1. */\n weight: number;\n changes?: Record<string, string>;\n /**\n * A redirect test: where this variant sends the visitor instead of the test's page (a path like \"/pricing-b\" or a\n * full address). Control has none and stays. SDK 0.7.0+.\n */\n url?: string;\n}\n\nexport interface ExperimentConfig {\n /** The experiment's key, the same as in Cockpit. */\n key: string;\n status: \"running\" | \"draft\" | \"stopped\";\n /** Control first. */\n variants: VariantConfig[];\n /** A winner that shipped: everyone gets it, and nobody is assigned anymore. */\n winner?: VariantId;\n /** A redirect test: the path of the page it runs on (\"/pricing\"). Visitors there are sent to their variant's `url`. */\n path?: string;\n}\n\n/** A page redirect set in MRRocket (Conversion › Landing pages › Redirect): everyone on `from` goes to `to`. SDK 0.7.0+. */\nexport interface RedirectConfig {\n /** A path of the site, like \"/old-pricing\". */\n from: string;\n /** A path or a full address. */\n to: string;\n}\n\nexport interface CockpitConfig {\n /** The product the public key belongs to. */\n project: string;\n /** The language the server chose for the words below, like \"de\" (`lang` on the request). SDK 0.21.0+. */\n lang?: string;\n /** The SDK's own words: English source word to translation (see words.ts). Absent for English. SDK 0.21.0+. */\n words?: Record<string, string>;\n experiments: ExperimentConfig[];\n /** The product's affiliate program: how many days a partner's referral is kept. Missing without a program. */\n referral?: { cookieDays: number };\n /** Session recordings: present only while the product records (Cockpit › Sessions). See `ReplayConfig`. */\n replay?: ReplayConfig;\n /** Feature flags of the product in Production (MRRocket › Build › Feature flags), evaluated by `useFlag` and `getFlag`. */\n flags?: FlagConfig[];\n /**\n * Published copy for texts the site marks (`<Text id>`, `data-ck-text`): a landing page's headline, subheadline and\n * button (`pricing.headline` …) and a paywall's (`paywall.<placement>.headline` …) as published in MRRocket. A\n * running test's variant changes them on top. Missing when nothing is published. SDK 0.5.0+.\n */\n texts?: Record<string, string>;\n /** The product's live cancel flow (MRRocket › Churn › Cancel flow), for `cancelFlow.open()`. SDK 0.7.0+. */\n cancelFlow?: CancelFlowConfig;\n /** The in-app steps of live onboarding flows (tours, checklists, modals). SDK 0.7.0+. */\n onboarding?: OnboardingFlow[];\n /** The product's brand color (\"#rrggbb\") for the onboarding dialogs when the app passes no accent. SDK 0.18.0+. */\n onboardingAccent?: string;\n /** A live Lifecycle journey has in-app messages: signed-in people ask `/api/sdk/inapp` for theirs. SDK 0.7.0+. */\n messages?: boolean;\n /** Page redirects set in MRRocket; the SDK sends visitors on before the page shows (`redirectFor`). SDK 0.7.0+. */\n redirects?: RedirectConfig[];\n /** Published paywalls with their pages, per placement (MRRocket › Conversion › Paywalls), for `paywall.open()`. SDK 0.14.0+. */\n paywalls?: PaywallConfig[];\n}\n\n/** One condition of a flag's audience, on a property of the person (`identify(id, props)`), the id or the page. */\nexport interface FlagCondition {\n /** \"Email\", \"Plan\", \"Country\", \"id\", \"path\" … Matched against the person's properties without regard to case. */\n property: string;\n op: \"is\" | \"is not\" | \"in\" | \"not in\" | \"≥\" | \"in cohort\";\n values: string[];\n}\n\n/** Users who match every condition, of whom `rollout` percent get the flag. No conditions: everyone. */\nexport interface FlagConditionSet {\n conditions: FlagCondition[];\n /** 0 to 100. */\n rollout: number;\n}\n\nexport interface FlagConfig {\n key: string;\n /** Off: everyone gets the off side. */\n on: boolean;\n /** The first set a person matches decides; none matched: off. */\n sets: FlagConditionSet[];\n /** Multivariate flags: the variants and their split in percent (control included). Missing for an on/off flag. */\n variants?: { key: string; split: number; payload?: string }[];\n}\n\n/** What a person is for flag conditions: their properties from identify(), their id and the page they are on. */\nexport type FlagPerson = Record<string, unknown>;\n\nconst propOf = (person: FlagPerson, name: string) => {\n const want = name.trim().toLowerCase();\n for (const [k, v] of Object.entries(person)) if (k.toLowerCase() === want) return v;\n return undefined;\n};\n\n/** `\"@acme.com\"` matches every address at that domain; anything else matches the whole value, without regard to case. */\nconst same = (actual: string, value: string) => {\n const a = actual.trim().toLowerCase();\n const v = value.trim().toLowerCase();\n return v.startsWith(\"@\") ? a.endsWith(v) : a === v;\n};\n\nconst matchesCondition = (c: FlagCondition, person: FlagPerson) => {\n // Cohorts live in MRRocket; a browser cannot know them, so a cohort condition never matches here.\n if (c.op === \"in cohort\") return false;\n const raw = propOf(person, c.property);\n const actual = raw === undefined || raw === null ? \"\" : String(raw);\n if (c.op === \"≥\") {\n const a = Number(actual);\n const b = Number(c.values[0]);\n return actual !== \"\" && Number.isFinite(a) && Number.isFinite(b) && a >= b;\n }\n const hit = actual !== \"\" && c.values.some((v) => same(actual, v));\n return c.op === \"is\" || c.op === \"in\" ? hit : !hit;\n};\n\n/**\n * The flag for one person: `false` (off), `true` (on, an on/off flag) or the variant's key (a multivariate flag). The\n * bucket is a stable hash of flag and person, so a person keeps their side as the rollout grows.\n */\nexport const evaluateFlag = (flag: FlagConfig, distinctId: string, person: FlagPerson = {}): boolean | string => {\n if (!flag.on) return false;\n const subject = { id: distinctId, ...person };\n const set = flag.sets.length ? flag.sets.find((s) => s.conditions.every((c) => matchesCondition(c, subject))) : { conditions: [], rollout: 100 };\n if (!set) return false;\n if (flagUnit(`${flag.key}:rollout:${distinctId}`) * 100 >= set.rollout) return false;\n if (!flag.variants?.length) return true;\n const u = flagUnit(`${flag.key}:variant:${distinctId}`) * 100;\n let acc = 0;\n for (const v of flag.variants) {\n acc += v.split;\n if (u < acc) return v.key;\n }\n return flag.variants[flag.variants.length - 1].key;\n};\n\n/** A flag of the config for this person; an unknown flag (or no config) is off. */\nexport const flagFor = (key: string, config: CockpitConfig | null, distinctId: string, person: FlagPerson = {}) => {\n const flag = config?.flags?.find((f) => f.key === key);\n return flag ? evaluateFlag(flag, distinctId, person) : false;\n};\n\n/** One JavaScript error as it goes over the wire to `POST /api/sdk/errors`. */\nexport interface ErrorReport {\n /** \"TypeError\", \"Error\" … */\n type: string;\n message: string;\n /** The raw stack, at most 8 KB. MRRocket parses it into frames. */\n stack?: string;\n /** \"onerror\" (uncaught), \"onunhandledrejection\" (a rejected promise nobody caught) or \"manual\" (captureError). */\n mechanism: \"onerror\" | \"onunhandledrejection\" | \"manual\";\n handled: boolean;\n url: string;\n ts: number;\n /** What happened just before: pages and clicks, newest last, at most 20. */\n crumbs?: { ts: number; type: \"navigation\" | \"click\" | \"lifecycle\"; message: string }[];\n /** Your release (a version or commit), from the provider's `release` prop. */\n release?: string;\n distinctId?: string;\n sessionId?: string;\n}\n\n/**\n * Session recordings of a product, from the config. Recording also needs the visitor's own replay consent\n * (`<CockpitProvider replay>` or `setConsent({ replay: true })`); without it nothing is recorded or buffered.\n */\nexport interface ReplayConfig {\n /**\n * Share of sessions uploaded from their start, 0 to 1 (default 0.1). The others keep only the last minute in\n * memory and upload it when something goes wrong: a JS error, a rage click, a `purchase…` or `checkout…` event.\n */\n sampleRate: number;\n}\n\n/**\n * What the SDK may send and store.\n * - \"cookieless\" (default): no cookie, no localStorage. Events go without a visitor id; the server counts visitors\n * with a hash of key, date, IP and user agent that changes every day.\n * - \"cookies\": a persistent `ck_id` cookie identifies the visitor across visits, `ck_sid` holds the session.\n * - \"off\": nothing is sent and nothing is stored.\n */\nexport type ConsentMode = \"cookieless\" | \"cookies\" | \"off\";\n\n/** Sent once when a signed-in person withdraws analytics consent, and stored as nothing but that fact on their record. */\nexport const CONSENT_OFF_EVENT = \"$consent_off\";\n\n/**\n * What `setConsent` takes: a mode as before, or the parts to change. `replay` is the separate consent for session\n * recordings (default false); it applies only while analytics is not \"off\".\n *\n * setConsent(\"cookies\");\n * setConsent({ replay: true });\n * setConsent({ analytics: \"cookieless\", replay: false });\n */\nexport type ConsentChoice = ConsentMode | { analytics?: ConsentMode; replay?: boolean };\n\n/** The campaign parameters the SDK reads from the landing URL. */\nexport const UTM_KEYS = [\"utm_source\", \"utm_medium\", \"utm_campaign\", \"utm_term\", \"utm_content\"] as const;\nexport type UtmKey = (typeof UTM_KEYS)[number];\n\n/** One event as it goes over the wire to `POST /api/sdk/events`. */\nexport interface CockpitEvent {\n event: string;\n /** The visitor. Missing in \"cookieless\" mode until identify(): the server then uses its daily visitor hash. */\n distinctId?: string;\n /** Ends after 30 minutes without an event. */\n sessionId?: string;\n props?: Record<string, unknown>;\n /** Milliseconds since the epoch. */\n ts: number;\n url?: string;\n /** `document.referrer` on the first page of a page load, the previous in-app URL after client-side navigation. */\n referrer?: string;\n utm_source?: string;\n utm_medium?: string;\n utm_campaign?: string;\n utm_term?: string;\n utm_content?: string;\n /** The screen size in CSS pixels (points on a phone). */\n screen?: { w: number; h: number };\n /** The app that sent it (React Native only): MRRocket stores it with the event and fills OS and device from it. */\n app?: AppContext;\n}\n\n/** Where a React Native event comes from. Every field is optional except the platform; nothing identifies the person. */\nexport interface AppContext {\n /** \"ios\" or \"android\" (React Native's `Platform.OS`). */\n platform: string;\n /** The app's version, like \"1.4.0\" (`expo-application`'s `nativeApplicationVersion`). */\n appVersion?: string;\n /** The build number, like \"42\". */\n build?: string;\n /** The OS version, like \"17.4\". */\n osVersion?: string;\n /** The device model, like \"iPhone 15 Pro\" (`expo-device`). */\n deviceModel?: string;\n /** \"phone\", \"tablet\", \"desktop\" or \"tv\" when `expo-device` knows it. */\n deviceType?: string;\n /** The first preferred locale, like \"de-DE\". */\n locale?: string;\n}\n\n/** The query parameter that forces variants, for previews in Cockpit: `?ck_preview=pricing-annual:a,home-hero:control`. */\nexport const PREVIEW_PARAM = \"ck_preview\";\n/** Set when Cockpit embeds the page: nothing is tracked, so previews never count as visits. */\nexport const EMBED_PARAM = \"ck_embed\";\n/** The cookie that keeps a visitor in the same variant across visits. Only written in \"cookies\" mode. */\nexport const ID_COOKIE = \"ck_id\";\n/** The cookie that holds the session id in \"cookies\" mode. */\nexport const SESSION_COOKIE = \"ck_sid\";\n/** A session ends after this long without an event. */\nexport const SESSION_TIMEOUT_MS = 30 * 60 * 1000;\n\n/** The UTM parameters from a parameter lookup (`URLSearchParams.get` in a browser). Empty values are left out. */\nexport const utmFrom = (get: (name: string) => string | null | undefined): Partial<Record<UtmKey, string>> => {\n const out: Partial<Record<UtmKey, string>> = {};\n for (const key of UTM_KEYS) {\n const value = get(key)?.trim();\n if (value) out[key] = value.slice(0, 150);\n }\n return out;\n};\n\n/** The UTM parameters of a query string, like `\"?utm_source=x\"`. Empty values are left out. */\nexport const readUtm = (search: string): Partial<Record<UtmKey, string>> => {\n const params = new URLSearchParams(search);\n return utmFrom((k) => params.get(k));\n};\n\n/**\n * The query parameters of a URL or query string, without `URLSearchParams` (React Native's is incomplete): `+` is a\n * space, values are percent-decoded, the first of a repeated name wins.\n */\nexport const parseQuery = (url: string): Record<string, string> => {\n const out: Record<string, string> = {};\n const q = url.split(\"#\")[0];\n const at = q.indexOf(\"?\");\n if (at < 0) return out;\n const dec = (s: string) => {\n try {\n return decodeURIComponent(s.replace(/\\+/g, \" \"));\n } catch {\n return s;\n }\n };\n for (const pair of q.slice(at + 1).split(\"&\")) {\n if (!pair) continue;\n const eq = pair.indexOf(\"=\");\n const k = dec(eq < 0 ? pair : pair.slice(0, eq));\n if (k && !(k in out)) out[k] = eq < 0 ? \"\" : dec(pair.slice(eq + 1));\n }\n return out;\n};\n\n/** FNV-1a, 32 bit, mapped to [0, 1). Stable across server and browser. */\nexport const hashUnit = (input: string) => {\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0) / 4294967296;\n};\n\n/**\n * FNV-1a with murmur3's finalizer, mapped to [0, 1): ids that differ only at the end (\"user-1\", \"user-2\") still spread\n * evenly, so a 30 % rollout reaches about 30 % of people. Flags only; experiments keep `hashUnit` so nobody switches.\n */\nexport const flagUnit = (input: string) => {\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n h ^= h >>> 16;\n h = Math.imul(h, 0x85ebca6b);\n h ^= h >>> 13;\n h = Math.imul(h, 0xc2b2ae35);\n h ^= h >>> 16;\n return (h >>> 0) / 4294967296;\n};\n\n/** The variant a visitor sees: a shipped winner, control while the experiment does not run, else a stable bucket by visitor. */\nexport const assign = (experiment: ExperimentConfig, distinctId: string): VariantId => {\n const control = experiment.variants[0]?.id ?? \"control\";\n if (experiment.winner) return experiment.winner;\n if (experiment.status !== \"running\") return control;\n const u = hashUnit(`${experiment.key}:${distinctId}`);\n let acc = 0;\n for (const v of experiment.variants) {\n acc += v.weight;\n if (u < acc) return v.id;\n }\n return experiment.variants[experiment.variants.length - 1]?.id ?? control;\n};\n\n/** `\"a:b,c:d\"` → `{ a: \"b\", c: \"d\" }`. Ignores anything malformed. */\nexport const parsePreview = (value: string | string[] | null | undefined): Record<string, VariantId> => {\n const raw = Array.isArray(value) ? value[0] : value;\n if (!raw) return {};\n const out: Record<string, VariantId> = {};\n for (const pair of raw.split(\",\")) {\n const [key, variant] = pair.split(\":\");\n if (key && variant) out[key.trim()] = variant.trim();\n }\n return out;\n};\n\n/** A link that shows one variant of a page, for example in Cockpit's preview frames. */\nexport const previewHref = (url: string, forced: Record<string, VariantId>, embed = true) => {\n const [path, query = \"\"] = url.split(\"?\");\n const params = new URLSearchParams(query);\n const value = Object.entries(forced)\n .map(([k, v]) => `${k}:${v}`)\n .join(\",\");\n if (value) params.set(PREVIEW_PARAM, value);\n if (embed) params.set(EMBED_PARAM, \"1\");\n return `${path}?${params.toString()}`;\n};\n\n/** The variant for one experiment, from a forced preview first, then the config. Unknown experiments get control. */\n/** The experiments whose variants change the text `id`: showing that text is an exposure to them. */\nexport const experimentsOfText = (config: CockpitConfig | null, id: string) =>\n (config?.experiments ?? []).filter((e) => e.variants.some((v) => v.changes && id in v.changes)).map((e) => e.key);\n\nexport const resolveVariant = (key: string, config: CockpitConfig | null, distinctId: string, preview: Record<string, VariantId>): VariantId => {\n if (preview[key]) return preview[key];\n const experiment = config?.experiments.find((e) => e.key === key);\n return experiment ? assign(experiment, distinctId) : \"control\";\n};\n\n/**\n * Every text change that applies to this visitor: the published texts first, then each experiment's in config order.\n * Control keeps the published (or the page's own) words, so only a non-control variant (assigned, forced in a preview,\n * or a shipped winner) changes them further.\n */\nexport const textChanges = (config: CockpitConfig | null, distinctId: string, preview: Record<string, VariantId>) => {\n const out: Record<string, string> = { ...(config?.texts ?? {}) };\n for (const experiment of config?.experiments ?? []) {\n const id = resolveVariant(experiment.key, config, distinctId, preview);\n if (id === (experiment.variants[0]?.id ?? \"control\")) continue;\n Object.assign(out, experiment.variants.find((v) => v.id === id)?.changes ?? {});\n }\n return out;\n};\n\n/**\n * The query parameter a redirect test adds to the variant's address (`ck_x=<test>:<variant>`): the page there records\n * the exposure and takes it out of the address bar. It also keeps that page from redirecting again.\n */\nexport const EXPOSURE_PARAM = \"ck_x\";\n\n/** What `redirectFor` decided: go to `to`, and/or the redirect test this page belongs to and the visitor's variant. */\nexport interface RedirectDecision {\n /** Where to send the visitor now. Missing for control, which stays on the page. */\n to?: string;\n /** The redirect test, while it runs: record `$exposure` for it (on this page for control, on `to` otherwise). */\n experiment?: string;\n variant?: VariantId;\n}\n\nconst pathOf = (p: string) => p.replace(/\\/+$/, \"\") || \"/\";\n\n/**\n * Where a page sends the visitor, before it shows: a page redirect set in MRRocket first, else a redirect test that runs\n * on this path (the visitor's variant, by the same stable assignment as every test). Null when nothing applies.\n *\n * - The new address keeps the page's query (UTM parameters) and its hash; a running test adds `ck_x=<test>:<variant>`,\n * so the page it lands on records the exposure (and never redirects again).\n * - `distinctId` null (no stable id, as on a server without the `ck_id` cookie): running tests are left to the browser,\n * page redirects and shipped winners still apply.\n * - A redirect to the page itself is ignored.\n */\nexport const redirectFor = (config: CockpitConfig | null, href: string, distinctId: string | null, preview: Record<string, VariantId> = {}): RedirectDecision | null => {\n if (!config) return null;\n let here: URL;\n try {\n here = new URL(href);\n } catch {\n return null;\n }\n if (here.searchParams.has(EXPOSURE_PARAM)) return null;\n const path = pathOf(here.pathname);\n const go = (to: string, mark?: string) => {\n let t: URL;\n try {\n t = new URL(to, here);\n } catch {\n return undefined;\n }\n if (!/^https?:$/.test(t.protocol) || (t.origin === here.origin && pathOf(t.pathname) === path)) return undefined;\n here.searchParams.forEach((v, k) => {\n if (!t.searchParams.has(k)) t.searchParams.set(k, v);\n });\n if (mark) t.searchParams.set(EXPOSURE_PARAM, mark);\n if (!t.hash) t.hash = here.hash;\n return t.href;\n };\n for (const r of config.redirects ?? []) {\n const to = r.from && pathOf(r.from) === path ? go(r.to) : undefined;\n if (to) return { to };\n }\n for (const e of config.experiments) {\n if (!e.path || pathOf(e.path) !== path) continue;\n const running = e.status === \"running\" && !e.winner;\n if (running && distinctId === null && !preview[e.key]) return null;\n const variant = resolveVariant(e.key, config, distinctId ?? \"\", preview);\n const url = e.variants.find((v) => v.id === variant)?.url;\n const to = url ? go(url, running ? `${e.key}:${variant}` : undefined) : undefined;\n return running ? { to, experiment: e.key, variant } : to ? { to } : null;\n }\n return null;\n};\n\n/** The test and variant a redirect test's `ck_x` names, when the config has them; null otherwise. */\nexport const exposureFrom = (config: CockpitConfig | null, value: string | null | undefined) => {\n const [key, variant] = (value ?? \"\").split(\":\");\n const e = config?.experiments.find((x) => x.key === key);\n return e && e.variants.some((v) => v.id === variant) ? { experiment: key, variant } : null;\n};\n\n/**\n * The MRRocket API host from `NEXT_PUBLIC_MRROCKET_HOST`. Written as a literal property access on purpose: Next.js\n * inlines `process.env.NEXT_PUBLIC_*` at build time, in the browser bundle and in the server bundle (also on\n * Cloudflare Workers), and only replaces the literal form. Without a bundler (or a `process`) it is undefined.\n */\nexport const envHost = (): string | undefined => {\n try {\n return process.env.NEXT_PUBLIC_MRROCKET_HOST || undefined;\n } catch {\n return undefined;\n }\n};\n\n/** The public key from `NEXT_PUBLIC_MRROCKET_KEY`, the default for the `apiKey` prop. Same literal-access rule as `envHost`. */\nexport const envKey = (): string | undefined => {\n try {\n return process.env.NEXT_PUBLIC_MRROCKET_KEY || undefined;\n } catch {\n return undefined;\n }\n};\n\n/**\n * Where the API lives: the `host` prop, else `NEXT_PUBLIC_MRROCKET_HOST`, else \"\" (the page's own origin, which is how\n * MRRocket's own demo pages call it). Without a trailing slash.\n */\nexport const resolveHost = (host?: string) => (host ?? envHost() ?? \"\").trim().replace(/\\/+$/, \"\");\n\n/** A random visitor id. */\nexport const newDistinctId = () =>\n typeof crypto !== \"undefined\" && \"randomUUID\" in crypto ? crypto.randomUUID() : `ck_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/core.ts"],"sourcesContent":["/**\n * The part of the SDK that runs anywhere: experiment config, variant assignment, preview links and event shapes.\n * No React, no browser APIs, so the server helper, the proxy helper and the client share it.\n */\nimport type { CancelFlowConfig, OnboardingFlow } from \"./flows\";\nimport type { PaywallConfig } from \"./paywall\";\n\nexport type VariantId = string;\n\n/** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */\nexport const SDK_VERSION = \"0.23.1\";\n\n/** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */\nexport interface VariantConfig {\n id: VariantId;\n /** Share of traffic, 0 to 1. The weights of an experiment add up to 1. */\n weight: number;\n changes?: Record<string, string>;\n /**\n * A redirect test: where this variant sends the visitor instead of the test's page (a path like \"/pricing-b\" or a\n * full address). Control has none and stays. SDK 0.7.0+.\n */\n url?: string;\n}\n\nexport interface ExperimentConfig {\n /** The experiment's key, the same as in Cockpit. */\n key: string;\n status: \"running\" | \"draft\" | \"stopped\";\n /** Control first. */\n variants: VariantConfig[];\n /** A winner that shipped: everyone gets it, and nobody is assigned anymore. */\n winner?: VariantId;\n /** A redirect test: the path of the page it runs on (\"/pricing\"). Visitors there are sent to their variant's `url`. */\n path?: string;\n}\n\n/** A page redirect set in MRRocket (Conversion › Landing pages › Redirect): everyone on `from` goes to `to`. SDK 0.7.0+. */\nexport interface RedirectConfig {\n /** A path of the site, like \"/old-pricing\". */\n from: string;\n /** A path or a full address. */\n to: string;\n}\n\nexport interface CockpitConfig {\n /** The product the public key belongs to. */\n project: string;\n /** The language the server chose for the words below, like \"de\" (`lang` on the request). SDK 0.21.0+. */\n lang?: string;\n /** The SDK's own words: English source word to translation (see words.ts). Absent for English. SDK 0.21.0+. */\n words?: Record<string, string>;\n experiments: ExperimentConfig[];\n /** The product's affiliate program: how many days a partner's referral is kept. Missing without a program. */\n referral?: { cookieDays: number };\n /** Session recordings: present only while the product records (Cockpit › Sessions). See `ReplayConfig`. */\n replay?: ReplayConfig;\n /** Feature flags of the product in Production (MRRocket › Build › Feature flags), evaluated by `useFlag` and `getFlag`. */\n flags?: FlagConfig[];\n /**\n * Published copy for texts the site marks (`<Text id>`, `data-ck-text`): a landing page's headline, subheadline and\n * button (`pricing.headline` …) and a paywall's (`paywall.<placement>.headline` …) as published in MRRocket. A\n * running test's variant changes them on top. Missing when nothing is published. SDK 0.5.0+.\n */\n texts?: Record<string, string>;\n /** The product's live cancel flow (MRRocket › Churn › Cancel flow), for `cancelFlow.open()`. SDK 0.7.0+. */\n cancelFlow?: CancelFlowConfig;\n /** The in-app steps of live onboarding flows (tours, checklists, modals). SDK 0.7.0+. */\n onboarding?: OnboardingFlow[];\n /** The product's brand color (\"#rrggbb\") for the onboarding dialogs when the app passes no accent. SDK 0.18.0+. */\n onboardingAccent?: string;\n /** A live Lifecycle journey has in-app messages: signed-in people ask `/api/sdk/inapp` for theirs. SDK 0.7.0+. */\n messages?: boolean;\n /** Page redirects set in MRRocket; the SDK sends visitors on before the page shows (`redirectFor`). SDK 0.7.0+. */\n redirects?: RedirectConfig[];\n /** Published paywalls with their pages, per placement (MRRocket › Conversion › Paywalls), for `paywall.open()`. SDK 0.14.0+. */\n paywalls?: PaywallConfig[];\n}\n\n/** One condition of a flag's audience, on a property of the person (`identify(id, props)`), the id or the page. */\nexport interface FlagCondition {\n /** \"Email\", \"Plan\", \"Country\", \"id\", \"path\" … Matched against the person's properties without regard to case. */\n property: string;\n op: \"is\" | \"is not\" | \"in\" | \"not in\" | \"≥\" | \"in cohort\";\n values: string[];\n}\n\n/** Users who match every condition, of whom `rollout` percent get the flag. No conditions: everyone. */\nexport interface FlagConditionSet {\n conditions: FlagCondition[];\n /** 0 to 100. */\n rollout: number;\n}\n\nexport interface FlagConfig {\n key: string;\n /** Off: everyone gets the off side. */\n on: boolean;\n /** The first set a person matches decides; none matched: off. */\n sets: FlagConditionSet[];\n /** Multivariate flags: the variants and their split in percent (control included). Missing for an on/off flag. */\n variants?: { key: string; split: number; payload?: string }[];\n}\n\n/** What a person is for flag conditions: their properties from identify(), their id and the page they are on. */\nexport type FlagPerson = Record<string, unknown>;\n\nconst propOf = (person: FlagPerson, name: string) => {\n const want = name.trim().toLowerCase();\n for (const [k, v] of Object.entries(person)) if (k.toLowerCase() === want) return v;\n return undefined;\n};\n\n/** `\"@acme.com\"` matches every address at that domain; anything else matches the whole value, without regard to case. */\nconst same = (actual: string, value: string) => {\n const a = actual.trim().toLowerCase();\n const v = value.trim().toLowerCase();\n return v.startsWith(\"@\") ? a.endsWith(v) : a === v;\n};\n\nconst matchesCondition = (c: FlagCondition, person: FlagPerson) => {\n // Cohorts live in MRRocket; a browser cannot know them, so a cohort condition never matches here.\n if (c.op === \"in cohort\") return false;\n const raw = propOf(person, c.property);\n const actual = raw === undefined || raw === null ? \"\" : String(raw);\n if (c.op === \"≥\") {\n const a = Number(actual);\n const b = Number(c.values[0]);\n return actual !== \"\" && Number.isFinite(a) && Number.isFinite(b) && a >= b;\n }\n const hit = actual !== \"\" && c.values.some((v) => same(actual, v));\n return c.op === \"is\" || c.op === \"in\" ? hit : !hit;\n};\n\n/**\n * The flag for one person: `false` (off), `true` (on, an on/off flag) or the variant's key (a multivariate flag). The\n * bucket is a stable hash of flag and person, so a person keeps their side as the rollout grows.\n */\nexport const evaluateFlag = (flag: FlagConfig, distinctId: string, person: FlagPerson = {}): boolean | string => {\n if (!flag.on) return false;\n const subject = { id: distinctId, ...person };\n const set = flag.sets.length ? flag.sets.find((s) => s.conditions.every((c) => matchesCondition(c, subject))) : { conditions: [], rollout: 100 };\n if (!set) return false;\n if (flagUnit(`${flag.key}:rollout:${distinctId}`) * 100 >= set.rollout) return false;\n if (!flag.variants?.length) return true;\n const u = flagUnit(`${flag.key}:variant:${distinctId}`) * 100;\n let acc = 0;\n for (const v of flag.variants) {\n acc += v.split;\n if (u < acc) return v.key;\n }\n return flag.variants[flag.variants.length - 1].key;\n};\n\n/** A flag of the config for this person; an unknown flag (or no config) is off. */\nexport const flagFor = (key: string, config: CockpitConfig | null, distinctId: string, person: FlagPerson = {}) => {\n const flag = config?.flags?.find((f) => f.key === key);\n return flag ? evaluateFlag(flag, distinctId, person) : false;\n};\n\n/** One JavaScript error as it goes over the wire to `POST /api/sdk/errors`. */\nexport interface ErrorReport {\n /** \"TypeError\", \"Error\" … */\n type: string;\n message: string;\n /** The raw stack, at most 8 KB. MRRocket parses it into frames. */\n stack?: string;\n /** \"onerror\" (uncaught), \"onunhandledrejection\" (a rejected promise nobody caught) or \"manual\" (captureError). */\n mechanism: \"onerror\" | \"onunhandledrejection\" | \"manual\";\n handled: boolean;\n url: string;\n ts: number;\n /** What happened just before: pages and clicks, newest last, at most 20. */\n crumbs?: { ts: number; type: \"navigation\" | \"click\" | \"lifecycle\"; message: string }[];\n /** Your release (a version or commit), from the provider's `release` prop. */\n release?: string;\n distinctId?: string;\n sessionId?: string;\n}\n\n/**\n * Session recordings of a product, from the config. Recording also needs the visitor's own replay consent\n * (`<CockpitProvider replay>` or `setConsent({ replay: true })`); without it nothing is recorded or buffered.\n */\nexport interface ReplayConfig {\n /**\n * Share of sessions uploaded from their start, 0 to 1 (default 0.1). The others keep only the last minute in\n * memory and upload it when something goes wrong: a JS error, a rage click, a `purchase…` or `checkout…` event.\n */\n sampleRate: number;\n}\n\n/**\n * What the SDK may send and store.\n * - \"cookieless\" (default): no cookie, no localStorage. Events go without a visitor id; the server counts visitors\n * with a hash of key, date, IP and user agent that changes every day.\n * - \"cookies\": a persistent `ck_id` cookie identifies the visitor across visits, `ck_sid` holds the session.\n * - \"off\": nothing is sent and nothing is stored.\n */\nexport type ConsentMode = \"cookieless\" | \"cookies\" | \"off\";\n\n/** Sent once when a signed-in person withdraws analytics consent, and stored as nothing but that fact on their record. */\nexport const CONSENT_OFF_EVENT = \"$consent_off\";\n\n/**\n * What `setConsent` takes: a mode as before, or the parts to change. `replay` is the separate consent for session\n * recordings (default false); it applies only while analytics is not \"off\".\n *\n * setConsent(\"cookies\");\n * setConsent({ replay: true });\n * setConsent({ analytics: \"cookieless\", replay: false });\n */\nexport type ConsentChoice = ConsentMode | { analytics?: ConsentMode; replay?: boolean };\n\n/** The campaign parameters the SDK reads from the landing URL. */\nexport const UTM_KEYS = [\"utm_source\", \"utm_medium\", \"utm_campaign\", \"utm_term\", \"utm_content\"] as const;\nexport type UtmKey = (typeof UTM_KEYS)[number];\n\n/** One event as it goes over the wire to `POST /api/sdk/events`. */\nexport interface CockpitEvent {\n event: string;\n /** The visitor. Missing in \"cookieless\" mode until identify(): the server then uses its daily visitor hash. */\n distinctId?: string;\n /** Ends after 30 minutes without an event. */\n sessionId?: string;\n props?: Record<string, unknown>;\n /** Milliseconds since the epoch. */\n ts: number;\n url?: string;\n /** `document.referrer` on the first page of a page load, the previous in-app URL after client-side navigation. */\n referrer?: string;\n utm_source?: string;\n utm_medium?: string;\n utm_campaign?: string;\n utm_term?: string;\n utm_content?: string;\n /** The screen size in CSS pixels (points on a phone). */\n screen?: { w: number; h: number };\n /** The app that sent it (React Native only): MRRocket stores it with the event and fills OS and device from it. */\n app?: AppContext;\n}\n\n/** Where a React Native event comes from. Every field is optional except the platform; nothing identifies the person. */\nexport interface AppContext {\n /** \"ios\" or \"android\" (React Native's `Platform.OS`). */\n platform: string;\n /** The app's version, like \"1.4.0\" (`expo-application`'s `nativeApplicationVersion`). */\n appVersion?: string;\n /** The build number, like \"42\". */\n build?: string;\n /** The OS version, like \"17.4\". */\n osVersion?: string;\n /** The device model, like \"iPhone 15 Pro\" (`expo-device`). */\n deviceModel?: string;\n /** \"phone\", \"tablet\", \"desktop\" or \"tv\" when `expo-device` knows it. */\n deviceType?: string;\n /** The first preferred locale, like \"de-DE\". */\n locale?: string;\n}\n\n/** The query parameter that forces variants, for previews in Cockpit: `?ck_preview=pricing-annual:a,home-hero:control`. */\nexport const PREVIEW_PARAM = \"ck_preview\";\n/** Set when Cockpit embeds the page: nothing is tracked, so previews never count as visits. */\nexport const EMBED_PARAM = \"ck_embed\";\n/** The cookie that keeps a visitor in the same variant across visits. Only written in \"cookies\" mode. */\nexport const ID_COOKIE = \"ck_id\";\n/** The cookie that holds the session id in \"cookies\" mode. */\nexport const SESSION_COOKIE = \"ck_sid\";\n/** A session ends after this long without an event. */\nexport const SESSION_TIMEOUT_MS = 30 * 60 * 1000;\n\n/** The UTM parameters from a parameter lookup (`URLSearchParams.get` in a browser). Empty values are left out. */\nexport const utmFrom = (get: (name: string) => string | null | undefined): Partial<Record<UtmKey, string>> => {\n const out: Partial<Record<UtmKey, string>> = {};\n for (const key of UTM_KEYS) {\n const value = get(key)?.trim();\n if (value) out[key] = value.slice(0, 150);\n }\n return out;\n};\n\n/** The UTM parameters of a query string, like `\"?utm_source=x\"`. Empty values are left out. */\nexport const readUtm = (search: string): Partial<Record<UtmKey, string>> => {\n const params = new URLSearchParams(search);\n return utmFrom((k) => params.get(k));\n};\n\n/**\n * The query parameters of a URL or query string, without `URLSearchParams` (React Native's is incomplete): `+` is a\n * space, values are percent-decoded, the first of a repeated name wins.\n */\nexport const parseQuery = (url: string): Record<string, string> => {\n const out: Record<string, string> = {};\n const q = url.split(\"#\")[0];\n const at = q.indexOf(\"?\");\n if (at < 0) return out;\n const dec = (s: string) => {\n try {\n return decodeURIComponent(s.replace(/\\+/g, \" \"));\n } catch {\n return s;\n }\n };\n for (const pair of q.slice(at + 1).split(\"&\")) {\n if (!pair) continue;\n const eq = pair.indexOf(\"=\");\n const k = dec(eq < 0 ? pair : pair.slice(0, eq));\n if (k && !(k in out)) out[k] = eq < 0 ? \"\" : dec(pair.slice(eq + 1));\n }\n return out;\n};\n\n/** FNV-1a, 32 bit, mapped to [0, 1). Stable across server and browser. */\nexport const hashUnit = (input: string) => {\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0) / 4294967296;\n};\n\n/**\n * FNV-1a with murmur3's finalizer, mapped to [0, 1): ids that differ only at the end (\"user-1\", \"user-2\") still spread\n * evenly, so a 30 % rollout reaches about 30 % of people. Flags only; experiments keep `hashUnit` so nobody switches.\n */\nexport const flagUnit = (input: string) => {\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n h ^= h >>> 16;\n h = Math.imul(h, 0x85ebca6b);\n h ^= h >>> 13;\n h = Math.imul(h, 0xc2b2ae35);\n h ^= h >>> 16;\n return (h >>> 0) / 4294967296;\n};\n\n/** The variant a visitor sees: a shipped winner, control while the experiment does not run, else a stable bucket by visitor. */\nexport const assign = (experiment: ExperimentConfig, distinctId: string): VariantId => {\n const control = experiment.variants[0]?.id ?? \"control\";\n if (experiment.winner) return experiment.winner;\n if (experiment.status !== \"running\") return control;\n const u = hashUnit(`${experiment.key}:${distinctId}`);\n let acc = 0;\n for (const v of experiment.variants) {\n acc += v.weight;\n if (u < acc) return v.id;\n }\n return experiment.variants[experiment.variants.length - 1]?.id ?? control;\n};\n\n/** `\"a:b,c:d\"` → `{ a: \"b\", c: \"d\" }`. Ignores anything malformed. */\nexport const parsePreview = (value: string | string[] | null | undefined): Record<string, VariantId> => {\n const raw = Array.isArray(value) ? value[0] : value;\n if (!raw) return {};\n const out: Record<string, VariantId> = {};\n for (const pair of raw.split(\",\")) {\n const [key, variant] = pair.split(\":\");\n if (key && variant) out[key.trim()] = variant.trim();\n }\n return out;\n};\n\n/** A link that shows one variant of a page, for example in Cockpit's preview frames. */\nexport const previewHref = (url: string, forced: Record<string, VariantId>, embed = true) => {\n const [path, query = \"\"] = url.split(\"?\");\n const params = new URLSearchParams(query);\n const value = Object.entries(forced)\n .map(([k, v]) => `${k}:${v}`)\n .join(\",\");\n if (value) params.set(PREVIEW_PARAM, value);\n if (embed) params.set(EMBED_PARAM, \"1\");\n return `${path}?${params.toString()}`;\n};\n\n/** The variant for one experiment, from a forced preview first, then the config. Unknown experiments get control. */\n/** The experiments whose variants change the text `id`: showing that text is an exposure to them. */\nexport const experimentsOfText = (config: CockpitConfig | null, id: string) =>\n (config?.experiments ?? []).filter((e) => e.variants.some((v) => v.changes && id in v.changes)).map((e) => e.key);\n\nexport const resolveVariant = (key: string, config: CockpitConfig | null, distinctId: string, preview: Record<string, VariantId>): VariantId => {\n if (preview[key]) return preview[key];\n const experiment = config?.experiments.find((e) => e.key === key);\n return experiment ? assign(experiment, distinctId) : \"control\";\n};\n\n/**\n * Every text change that applies to this visitor: the published texts first, then each experiment's in config order.\n * Control keeps the published (or the page's own) words, so only a non-control variant (assigned, forced in a preview,\n * or a shipped winner) changes them further.\n */\nexport const textChanges = (config: CockpitConfig | null, distinctId: string, preview: Record<string, VariantId>) => {\n const out: Record<string, string> = { ...(config?.texts ?? {}) };\n for (const experiment of config?.experiments ?? []) {\n const id = resolveVariant(experiment.key, config, distinctId, preview);\n if (id === (experiment.variants[0]?.id ?? \"control\")) continue;\n Object.assign(out, experiment.variants.find((v) => v.id === id)?.changes ?? {});\n }\n return out;\n};\n\n/**\n * The query parameter a redirect test adds to the variant's address (`ck_x=<test>:<variant>`): the page there records\n * the exposure and takes it out of the address bar. It also keeps that page from redirecting again.\n */\nexport const EXPOSURE_PARAM = \"ck_x\";\n\n/** What `redirectFor` decided: go to `to`, and/or the redirect test this page belongs to and the visitor's variant. */\nexport interface RedirectDecision {\n /** Where to send the visitor now. Missing for control, which stays on the page. */\n to?: string;\n /** The redirect test, while it runs: record `$exposure` for it (on this page for control, on `to` otherwise). */\n experiment?: string;\n variant?: VariantId;\n}\n\nconst pathOf = (p: string) => p.replace(/\\/+$/, \"\") || \"/\";\n\n/**\n * Where a page sends the visitor, before it shows: a page redirect set in MRRocket first, else a redirect test that runs\n * on this path (the visitor's variant, by the same stable assignment as every test). Null when nothing applies.\n *\n * - The new address keeps the page's query (UTM parameters) and its hash; a running test adds `ck_x=<test>:<variant>`,\n * so the page it lands on records the exposure (and never redirects again).\n * - `distinctId` null (no stable id, as on a server without the `ck_id` cookie): running tests are left to the browser,\n * page redirects and shipped winners still apply.\n * - A redirect to the page itself is ignored.\n */\nexport const redirectFor = (config: CockpitConfig | null, href: string, distinctId: string | null, preview: Record<string, VariantId> = {}): RedirectDecision | null => {\n if (!config) return null;\n let here: URL;\n try {\n here = new URL(href);\n } catch {\n return null;\n }\n if (here.searchParams.has(EXPOSURE_PARAM)) return null;\n const path = pathOf(here.pathname);\n const go = (to: string, mark?: string) => {\n let t: URL;\n try {\n t = new URL(to, here);\n } catch {\n return undefined;\n }\n if (!/^https?:$/.test(t.protocol) || (t.origin === here.origin && pathOf(t.pathname) === path)) return undefined;\n here.searchParams.forEach((v, k) => {\n if (!t.searchParams.has(k)) t.searchParams.set(k, v);\n });\n if (mark) t.searchParams.set(EXPOSURE_PARAM, mark);\n if (!t.hash) t.hash = here.hash;\n return t.href;\n };\n for (const r of config.redirects ?? []) {\n const to = r.from && pathOf(r.from) === path ? go(r.to) : undefined;\n if (to) return { to };\n }\n for (const e of config.experiments) {\n if (!e.path || pathOf(e.path) !== path) continue;\n const running = e.status === \"running\" && !e.winner;\n if (running && distinctId === null && !preview[e.key]) return null;\n const variant = resolveVariant(e.key, config, distinctId ?? \"\", preview);\n const url = e.variants.find((v) => v.id === variant)?.url;\n const to = url ? go(url, running ? `${e.key}:${variant}` : undefined) : undefined;\n return running ? { to, experiment: e.key, variant } : to ? { to } : null;\n }\n return null;\n};\n\n/** The test and variant a redirect test's `ck_x` names, when the config has them; null otherwise. */\nexport const exposureFrom = (config: CockpitConfig | null, value: string | null | undefined) => {\n const [key, variant] = (value ?? \"\").split(\":\");\n const e = config?.experiments.find((x) => x.key === key);\n return e && e.variants.some((v) => v.id === variant) ? { experiment: key, variant } : null;\n};\n\n/**\n * The MRRocket API host from `NEXT_PUBLIC_MRROCKET_HOST`. Written as a literal property access on purpose: Next.js\n * inlines `process.env.NEXT_PUBLIC_*` at build time, in the browser bundle and in the server bundle (also on\n * Cloudflare Workers), and only replaces the literal form. Without a bundler (or a `process`) it is undefined.\n */\nexport const envHost = (): string | undefined => {\n try {\n return process.env.NEXT_PUBLIC_MRROCKET_HOST || undefined;\n } catch {\n return undefined;\n }\n};\n\n/** The public key from `NEXT_PUBLIC_MRROCKET_KEY`, the default for the `apiKey` prop. Same literal-access rule as `envHost`. */\nexport const envKey = (): string | undefined => {\n try {\n return process.env.NEXT_PUBLIC_MRROCKET_KEY || undefined;\n } catch {\n return undefined;\n }\n};\n\n/**\n * Where the API lives: the `host` prop, else `NEXT_PUBLIC_MRROCKET_HOST`, else \"\" (the page's own origin, which is how\n * MRRocket's own demo pages call it). Without a trailing slash.\n */\nexport const resolveHost = (host?: string) => (host ?? envHost() ?? \"\").trim().replace(/\\/+$/, \"\");\n\n/** A random visitor id. */\nexport const newDistinctId = () =>\n typeof crypto !== \"undefined\" && \"randomUUID\" in crypto ? crypto.randomUUID() : `ck_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n"],"mappings":"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","names":[]}
|
|
@@ -829,7 +829,7 @@ interface PaywallHost {
|
|
|
829
829
|
|
|
830
830
|
type VariantId = string;
|
|
831
831
|
/** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */
|
|
832
|
-
declare const SDK_VERSION = "0.23.
|
|
832
|
+
declare const SDK_VERSION = "0.23.1";
|
|
833
833
|
/** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */
|
|
834
834
|
interface VariantConfig {
|
|
835
835
|
id: VariantId;
|
|
@@ -829,7 +829,7 @@ interface PaywallHost {
|
|
|
829
829
|
|
|
830
830
|
type VariantId = string;
|
|
831
831
|
/** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */
|
|
832
|
-
declare const SDK_VERSION = "0.23.
|
|
832
|
+
declare const SDK_VERSION = "0.23.1";
|
|
833
833
|
/** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */
|
|
834
834
|
interface VariantConfig {
|
|
835
835
|
id: VariantId;
|
|
@@ -114,7 +114,7 @@ var onLocale = (fn) => {
|
|
|
114
114
|
};
|
|
115
115
|
|
|
116
116
|
// src/core.ts
|
|
117
|
-
var SDK_VERSION = "0.23.
|
|
117
|
+
var SDK_VERSION = "0.23.1";
|
|
118
118
|
var propOf = (person, name) => {
|
|
119
119
|
const want = name.trim().toLowerCase();
|
|
120
120
|
for (const [k, v] of Object.entries(person)) if (k.toLowerCase() === want) return v;
|