{"version":3,"file":"autocapture.cjs","sources":["../src/react-native/autocapture.ts"],"sourcesContent":["type Sender = {\n  send: (event: string, data: Record<string, unknown>) => void\n  trackScreenView: (screenName: string) => void\n}\n\n// Store the sender on globalThis so both the react-native bundle (which inlines this module)\n// and the separately-loaded autocapture bundle (imported by the Babel plugin) share one instance.\n// Module-level variables don't work here because Metro/Rollup can create two separate copies.\nconst _g = globalThis as unknown as Record<string, unknown>\nconst _KEY = '__nohmo_sender__'\n\nexport function setAutoCaptureTracker(sender: Sender): void {\n  _g[_KEY] = sender\n}\n\nfunction _getSender(): Sender | null {\n  return (_g[_KEY] as Sender | undefined) ?? null\n}\n\n// ── Screen tracking ────────────────────────────────────────────────────────\n\nfunction getActiveRouteName(state: any): string | undefined {\n  if (!state?.routes) return undefined\n  const route = state.routes[state.index ?? state.routes.length - 1]\n  if (!route) return undefined\n  // Nested navigators — recurse into the active child state\n  if (route.state) return getActiveRouteName(route.state)\n  return route.name as string\n}\n\n/**\n * Pass directly to NavigationContainer's onStateChange prop.\n * Fires SCREEN_VIEW + TIME_SPENT automatically on every navigation.\n *\n * @example\n * import { onNohmoStateChange } from 'nohmo/react-native/autocapture'\n * <NavigationContainer onStateChange={onNohmoStateChange}>\n */\nexport function onNohmoStateChange(state: any): void {\n  const s = _getSender()\n  if (!s) return\n  const name = getActiveRouteName(state)\n  if (name) s.trackScreenView(name)\n}\n\n/**\n * Pass to NavigationContainer's onReady prop to also capture the initial screen.\n * Requires passing your navigationRef so the current route can be read.\n *\n * @example\n * import { onNohmoStateChange, makeNohmoReadyHandler } from 'nohmo/react-native/autocapture'\n * <NavigationContainer\n *   onStateChange={onNohmoStateChange}\n *   onReady={makeNohmoReadyHandler(navigationRef)}\n * >\n */\nexport function makeNohmoReadyHandler(\n  navigationRef: { current: { getCurrentRoute: () => { name: string } | undefined } | null }\n): () => void {\n  return () => {\n    const s = _getSender()\n    if (!s) return\n    const route = navigationRef.current?.getCurrentRoute()\n    if (route?.name) s.trackScreenView(route.name)\n  }\n}\n\n// ── Press autocapture (injected by babel-plugin) ───────────────────────────\n\n/**\n * Coerce a captured label to clean text. A dynamic label expression can resolve\n * at runtime to a React element or object (e.g. `label={icon}`, or a template\n * fragment that is itself an element) — `String()` would turn that into the\n * useless \"[object Object]\". So: drop non-string/object values entirely, and\n * strip any \"[object Object]\" that leaked into an otherwise-good string before\n * trimming and capping. Returns null when nothing meaningful remains.\n */\nfunction _coerceLabel(v: unknown): string | null {\n  let s: string\n  if (typeof v === 'string') s = v\n  else if (typeof v === 'number' || typeof v === 'boolean') s = String(v)\n  else return null // null/undefined, objects, React elements, arrays, functions\n  s = s.replace(/\\[object Object\\]/g, '').replace(/\\s+/g, ' ').trim().slice(0, 80)\n  return s || null\n}\n\n// Marks a function as already wrapped, so nested wraps can de-duplicate.\nconst _WRAPPED = '__nohmoWrapped__'\n\n// ── Rage presses ───────────────────────────────────────────────────────────\n// The web SDK emits RAGE_CLICK when someone jabs the same spot three times in a\n// second. Mobile users do exactly the same thing to an unresponsive button, and\n// without this the whole signal was web-only. Keyed by the press target rather than\n// by coordinates — RN gives us the component identity, which is more precise than a\n// screen position and survives scrolling.\nconst RAGE_WINDOW_MS = 1000\nconst RAGE_THRESHOLD = 3\nconst _rageCounts = new Map<string, { count: number; ts: number }>()\n\nfunction _trackRage(sender: Sender, key: string, payload: Record<string, unknown>): void {\n  const now = Date.now()\n  const prev = _rageCounts.get(key)\n  if (prev && now - prev.ts < RAGE_WINDOW_MS) {\n    prev.count++\n    prev.ts = now\n    // Fire once on crossing the threshold, not on every press after it.\n    if (prev.count === RAGE_THRESHOLD) sender.send('RAGE_CLICK', payload)\n    return\n  }\n  _rageCounts.set(key, { count: 1, ts: now })\n  // A long session touching many controls would otherwise grow this map without\n  // bound; the entries are only meaningful for a second anyway.\n  if (_rageCounts.size > 100) {\n    for (const [k, v] of _rageCounts) {\n      if (now - v.ts > RAGE_WINDOW_MS) _rageCounts.delete(k)\n    }\n  }\n}\n\n/**\n * Injected by the Nohmo Babel plugin around every onPress / onLongPress.\n * Fires a PRESS or LONG_PRESS event then calls the original handler.\n *\n * De-duplication: design-system buttons (e.g. <Button> → internal <Pressable\n * onPress={onPress}/>) thread the app's handler down, so the plugin wraps the\n * SAME handler at two levels. To avoid a double press (and the inner component's\n * \"[object Object]\" label), a wrapper whose handler is ITSELF a wrapper stays\n * silent — the app-level wrapper it delegates to carries the real label.\n */\nexport function __nohmoWrap<T extends ((...args: unknown[]) => unknown) | null | undefined>(\n  handler: T,\n  meta: {\n    c?: string | null  // component name\n    p?: string         // prop name (onPress | onLongPress)\n    t?: unknown        // text — static (children/prop) or a runtime prop value\n    f?: string | null  // filename (without extension)\n    l?: number         // line number\n  }\n): (...args: unknown[]) => unknown {\n  const wrapped = (...args: unknown[]) => {\n    const s = _getSender()\n    const handlerIsWrapped =\n      typeof handler === 'function' && (handler as unknown as Record<string, unknown>)[_WRAPPED] === true\n    if (s && !handlerIsWrapped) {\n      const isLong = meta.p === 'onLongPress'\n      const payload = {\n        component: meta.c ?? null,\n        text: _coerceLabel(meta.t),\n        file: meta.f ?? null,\n        line: meta.l ?? null,\n      }\n      s.send(isLong ? 'LONG_PRESS' : 'PRESS', payload)\n      // Repeated jabs at the same control are the mobile equivalent of a rage click.\n      // Long-presses are excluded: holding a control is intentional, not frustration.\n      if (!isLong) {\n        _trackRage(s, `${meta.f ?? ''}:${meta.l ?? ''}:${meta.c ?? ''}`, payload)\n      }\n    }\n    return (handler as ((...a: unknown[]) => unknown) | null | undefined)?.(...args)\n  }\n  ;(wrapped as unknown as Record<string, unknown>)[_WRAPPED] = true\n  return wrapped\n}\n"],"names":[],"mappings":"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"}