{"version":3,"sources":["../src/index.ts","../src/translation.ts","../src/types.ts","../src/utils/providers.ts","../src/utils/body.ts","../src/patches/patchFetch.ts","../src/patches/patchSendBeacon.ts","../src/patches/patchXhr.ts","../src/sanitise/forLLM.ts","../src/sanitise/url.ts","../src/engine/marketingParams.ts","../src/engine/deviceInfo.ts","../src/engine/eventLog.ts","../src/formatters/json.ts","../src/formatters/toon.ts","../src/formatters/index.ts","../src/tracking/scroll.ts","../src/tracking/rageClicks.ts","../src/tracking/index.ts","../src/session/navigation.ts","../src/session/ctSession.ts","../src/session/index.ts","../src/Contextune.ts"],"sourcesContent":["export { Contextune } from './Contextune.js';\nexport type {\n  ContextuneOptions,\n  ContextuneInstance,\n  ContextuneSource,\n  SubscriptionFilter,\n  SubscriptionHandler,\n} from './Contextune.js';\nexport type {\n  ContextuneSnapshot,\n  SnapshotFormat,\n  EventLogEntry,\n  MarketingParamsBlock,\n  DeviceInfoBlock,\n} from './types.js';\nexport type { Persistence } from './session/index.js';\n","import type { CapturedEventFromCallback, EventsExtractedFromBatch } from \"./types.js\";\n\nexport function getParsedQueryString(href: string) {\n  const url = new URL(href);\n  return Object.fromEntries(url.searchParams);\n}\n\n// Extracts ep.* (string) and epn.* (numeric) event params from a GA4 param map.\nfunction parseGa4Properties(params: Record<string, string>): Record<string, unknown> | undefined {\n  const props: Record<string, unknown> = {};\n  for (const [key, value] of Object.entries(params)) {\n    if (key.startsWith('ep.')) {\n      props[key.slice(3)] = value;\n    } else if (key.startsWith('epn.')) {\n      const num = Number(value);\n      props[key.slice(4)] = Number.isNaN(num) ? value : num;\n    }\n  }\n  return Object.keys(props).length > 0 ? props : undefined;\n}\n\nexport function extractEventsFromBatch(\n  event: CapturedEventFromCallback,\n): EventsExtractedFromBatch {\n  try {\n    const commonProps = {\n      href: event.href,\n      method: event.method,\n      transport: event.transport,\n      provider: event.provider,\n      sentAt: event.sentAt,\n    };\n    switch (event.provider) {\n      case 'googleanalytics': {\n        const parsedQS = getParsedQueryString(event.endpoint);\n        const isSingleEvent = Object.hasOwn(parsedQS, 'en');\n        if (isSingleEvent) {\n          return [\n            {\n              ...commonProps,\n              eventName: parsedQS.en ?? 'unknown',\n              body: null,\n              properties: parseGa4Properties(parsedQS),\n            },\n          ];\n        } else {\n          const eventBodiesInBatch = event.payload?.split(/\\s/).filter(Boolean);\n          if (!eventBodiesInBatch) {\n            return [];\n          }\n\n          return eventBodiesInBatch.map(ev => {\n            const params = Object.fromEntries(new URLSearchParams(ev));\n            return {\n              ...commonProps,\n              eventName: params.en ?? 'unknown',\n              body: ev,\n              properties: parseGa4Properties(params),\n            };\n          });\n        }\n      }\n      case 'mixpanel': {\n        if (!event.payload) return [];\n        const eventsInBatch = JSON.parse(\n          new URLSearchParams(event.payload).get('data') || '[]',\n        ) as Record<string, unknown>[];\n        return eventsInBatch.map(ev => ({\n          ...commonProps,\n          eventName: (ev.event as string) || 'unknown',\n          body: JSON.stringify(ev),\n          properties: ev.properties as Record<string, unknown> | undefined,\n        }));\n      }\n      case 'segment': {\n        const parsedPayload =\n          event.payload &&\n          (JSON.parse(event.payload) as Record<string, unknown>);\n        const isBatch =\n          typeof parsedPayload === 'object' &&\n          parsedPayload !== null &&\n          Object.hasOwn(parsedPayload, 'batch');\n        if (isBatch) {\n          const batchedEvents = parsedPayload.batch as Record<\n            string,\n            unknown\n          >[];\n          return batchedEvents.map(ev => ({\n            ...commonProps,\n            eventName:\n              ev.type === 'track' ? (ev.event as string) : (ev.type as string),\n            body: JSON.stringify(ev),\n            properties: ev.properties as Record<string, unknown> | undefined,\n          }));\n        } else if (parsedPayload) {\n          return [\n            {\n              ...commonProps,\n              body: event.payload || null,\n              eventName:\n                parsedPayload.type === 'track'\n                  ? (parsedPayload.event as string)\n                  : (parsedPayload.type as string),\n              properties: parsedPayload.properties as Record<string, unknown> | undefined,\n            },\n          ];\n        } else {\n          throw Error('something wrong with segment');\n        }\n      }\n      default:\n        return [];\n    }\n  } catch (error) {\n    console.error(\n      'something wrong in extraction',\n      event.endpoint,\n      event.provider,\n      event.payload,\n      error,\n    );\n    return [];\n  }\n}\n","/**\n * Public and internal types for `@contextune/sdk`.\n */\n\n// ---------------------------------------------------------------------------\n// Shared output types (used by both internal engine files and public surface)\n// ---------------------------------------------------------------------------\n\n/** Frozen-at-init marketing context. */\nexport interface MarketingParamsBlock {\n  utm_source: string | null;\n  utm_medium: string | null;\n  utm_campaign: string | null;\n  utm_term: string | null;\n  utm_content: string | null;\n  referrer: string | null;\n  landing_page: string | null;\n}\n\n/** Static device information, read at snapshot time from `navigator` + `window`. */\nexport interface DeviceInfoBlock {\n  user_agent: string;\n  viewport_w: number;\n  viewport_h: number;\n  language: string;\n  platform: string;\n  device_type: 'desktop' | 'tablet' | 'mobile';\n}\n\n/**\n * A single entry in the event log.\n *\n * `name` is the normalised event name (e.g. `'page_view'`, `'track'`).\n * `elapsed_s` is whole seconds since session start.\n */\nexport interface EventLogEntry {\n  name: string;\n  elapsed_s: number;\n  properties?: Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Public snapshot type\n// ---------------------------------------------------------------------------\n\nexport interface ContextuneSnapshot {\n  event_log: EventLogEntry[];\n  marketing_params?: MarketingParamsBlock;\n  device_info?: DeviceInfoBlock;\n}\n\nexport type SnapshotFormat = 'js' | 'json' | 'toon';\n\n// ---------------------------------------------------------------------------\n// Internal network-intercept types (not re-exported from index.ts)\n// ---------------------------------------------------------------------------\n\nexport const PROVIDER_REGEX = {\n  \"google-analytics.com\": \"googleanalytics\",\n  \"analytics.google.com\": \"googleanalytics\",\n  \"api.segment.io/v1/t\": \"segment\",\n  \"segmentapi\": \"segment\",\n  \"seg-api\": \"segment\",\n  \"segment-api\": \"segment\",\n  \"/.*api(-eu)?(-js)?.mixpanel.com.*/\": \"mixpanel\",\n} as const;\n\nexport type Providers = (typeof PROVIDER_REGEX)[keyof typeof PROVIDER_REGEX];\n\nexport type CapturedEvent = {\n  endpoint: string;\n  method: \"POST\" | \"GET\";\n  payload?: string | null;\n  transport: \"XHR\" | \"beacon\" | \"fetch\";\n  provider: Providers;\n};\n\nexport type CapturedEventFromCallback = CapturedEvent & {\n  href: string;\n  sentAt: Date;\n};\n\nexport type EventsExtractedFromBatch = {\n  eventName: string;\n  body: string | null;\n  href: string;\n  method: string;\n  transport: string;\n  provider: Providers;\n  sentAt: Date;\n  properties?: Record<string, unknown>;\n}[];\n\nexport type AddEventCallback = (event: CapturedEvent) => void;\n","import { PROVIDER_REGEX, type Providers } from \"../types.js\";\n\n// Pre-compiled at module load — avoids recompiling on every network request.\nconst STRING_PATTERNS: Array<[string, Providers]> = [];\nconst REGEX_PATTERNS: Array<[RegExp, Providers]> = [];\n\nfor (const [identifier, provider] of Object.entries(PROVIDER_REGEX)) {\n  if (identifier[0] === '/' && identifier[identifier.length - 1] === '/') {\n    REGEX_PATTERNS.push([new RegExp(identifier.slice(1, -1)), provider as Providers]);\n  } else {\n    STRING_PATTERNS.push([identifier, provider as Providers]);\n  }\n}\n\nexport function findEventProvider(endpoint: string): Providers | false {\n  for (const [pattern, provider] of STRING_PATTERNS) {\n    if (endpoint.includes(pattern)) return provider;\n  }\n  for (const [regex, provider] of REGEX_PATTERNS) {\n    if (regex.test(endpoint)) return provider;\n  }\n  return false;\n}\n","export function getBody(body: string | FormData): string | undefined {\n  try {\n    if (body instanceof FormData) {\n      return JSON.stringify(Object.fromEntries(body));\n    }\n    return body;\n  } catch {\n    return undefined;\n  }\n}\n","import { type AddEventCallback } from \"../types.js\";\nimport { findEventProvider } from \"../utils/providers.js\";\nimport { getBody } from \"../utils/body.js\";\n\nexport function patchFetch(callback: AddEventCallback): () => void {\n  if (typeof window.fetch !== 'function') return () => {};\n  const original = window.fetch;\n  window.fetch = function (input: RequestInfo | URL, init?: RequestInit) {\n    const provider = findEventProvider(input.toString());\n    if (provider) {\n      try {\n        callback({\n          endpoint: input.toString(),\n          // @ts-expect-error method may not be typed strictly\n          method: init?.method?.toUpperCase() || \"GET\",\n          transport: \"fetch\",\n          payload: init?.body && getBody(init?.body as string | FormData),\n          provider,\n        });\n      } catch {\n        // never abort client traffic\n      }\n    }\n\n    // @ts-expect-error arguments forwarding\n    return original.apply(this, arguments);\n  };\n  return () => { window.fetch = original; };\n}\n","import { type AddEventCallback } from \"../types.js\";\nimport { findEventProvider } from \"../utils/providers.js\";\nimport { getBody } from \"../utils/body.js\";\n\nexport function patchSendBeacon(callback: AddEventCallback): () => void {\n  if (typeof window.navigator?.sendBeacon !== 'function') return () => {};\n  const original = window.navigator.sendBeacon;\n  window.navigator.sendBeacon = function (\n    url: string | URL,\n    data?: string | FormData | null\n  ) {\n    const provider = findEventProvider(url.toString());\n    if (provider) {\n      try {\n        callback({\n          method: \"POST\",\n          transport: \"beacon\",\n          endpoint: url.toString(),\n          payload: data && getBody(data),\n          provider,\n        });\n      } catch {\n        // never abort client traffic\n      }\n    }\n    // @ts-expect-error arguments forwarding\n    return original.apply(this, arguments);\n  };\n  return () => { window.navigator.sendBeacon = original; };\n}\n","import { type AddEventCallback } from \"../types.js\";\nimport { findEventProvider } from \"../utils/providers.js\";\nimport { getBody } from \"../utils/body.js\";\n\nexport function patchXMLHTTPRequest(callback: AddEventCallback): () => void {\n  if (typeof window.XMLHttpRequest !== 'function') return () => {};\n  const origOpen = window.XMLHttpRequest.prototype.open;\n  const origSend = window.XMLHttpRequest.prototype.send;\n\n  window.XMLHttpRequest.prototype.open = function (\n    method: string,\n    url: string | URL\n  ) {\n    // @ts-expect-error ts stuff\n    this.__inspectorMethod = method;\n    // @ts-expect-error ts stuff\n    this.__inspectorEndpoint = url.toString();\n    // @ts-expect-error ts stuff\n    return origOpen.apply(this, [].slice.call(arguments));\n  };\n\n  window.XMLHttpRequest.prototype.send = function (\n    body: string | null | FormData\n  ) {\n    // @ts-expect-error ts stuff\n    if (this.__inspectorEndpoint) {\n      // @ts-expect-error ts stuff\n      const endpoint = this.__inspectorEndpoint as string;\n      const provider = findEventProvider(endpoint);\n\n      if (provider) {\n        // @ts-expect-error ts stuff\n        const method = ((this.__inspectorMethod as string) ?? '').toUpperCase();\n        if (method === 'POST' || method === 'GET') {\n          try {\n            callback({\n              endpoint,\n              method: method as 'POST' | 'GET',\n              transport: \"XHR\",\n              payload: body && getBody(body as string | FormData),\n              provider,\n            });\n          } catch {\n            // never abort client traffic\n          }\n        }\n      }\n    }\n\n    // @ts-expect-error ts stuff\n    return origSend.apply(this, [].slice.call(arguments));\n  };\n\n  return () => {\n    window.XMLHttpRequest.prototype.open = origOpen;\n    window.XMLHttpRequest.prototype.send = origSend;\n  };\n}\n","/**\n * `sanitiseForLLM` — defence-in-depth sanitiser applied at every string field\n * that lands in `getSnapshot()` output.\n *\n * Why: every snapshot field is intended to be templated into an LLM system\n * prompt. Multiple snapshot ingress points are attacker-influenceable:\n * URL query params, `document.referrer`, DOM `id` attributes, Segment event\n * names, etc. (see `.agents/security-review.md` F1 + F3). Without\n * sanitisation, an attacker can inject `</system>` markers or other chat-\n * template tokens and escape the host's prompt scaffold.\n *\n * The sanitiser is intentionally narrow:\n *   1. Length-cap (configurable per call; sensible defaults per field type).\n *   2. Strip C0/C1 control characters except `\\t` and `\\n`.\n *   3. Strip zero-width characters and bidi-override / format controls\n *      (these are otherwise invisible to a human reviewer and can be used\n *      to smuggle markers past visual inspection).\n *   4. Replace prompt-control markers (`</system>`, `<|im_end|>`, common\n *      chat-template tokens) with `?`. We don't attempt to be exhaustive —\n *      this is a deny-list to raise the bar, not a parser.\n *\n * Future M1.5++ can tighten further (e.g. fenced-block escaping at the\n * consumer boundary). For now, the README will also document that\n * downstream consumers should wrap snapshot output in a fenced JSON block.\n */\n\n/** Maximum total characters allowed in any single snapshot string field. */\nexport const SANITISE_DEFAULT_LIMIT = 256;\n\n/** Tighter cap for selector-like fields (CSS selectors, event names). */\nexport const SANITISE_SELECTOR_LIMIT = 64;\n\n/** Cap for URL-like fields (URLs are routinely longer than 256 chars). */\nexport const SANITISE_URL_LIMIT = 512;\n\n/**\n * Substrings replaced with `?` to neutralise chat-template injections.\n * Matched case-insensitively. Not a parser; a deny-list to raise the bar.\n *\n * The list covers:\n *   - Common HTML/markdown role tags an LLM may interpret as prompt scaffold\n *     (`</system>`, `</user>`, `</assistant>`).\n *   - ChatML-style special tokens (`<|im_start|>`, `<|im_end|>`).\n *   - Anthropic-style human/assistant turn markers.\n *   - Common context-fence tokens.\n */\nconst PROMPT_MARKERS: readonly RegExp[] = [\n  /<\\/?\\s*system\\s*>/gi,\n  /<\\/?\\s*user\\s*>/gi,\n  /<\\/?\\s*assistant\\s*>/gi,\n  /<\\/?\\s*context\\s*>/gi,\n  /<\\|im_start\\|>/gi,\n  /<\\|im_end\\|>/gi,\n  /<\\|endoftext\\|>/gi,\n  /\\[INST\\]/gi,\n  /\\[\\/INST\\]/gi,\n  /<<SYS>>/gi,\n  /<<\\/SYS>>/gi,\n  // Anthropic-style human/assistant markers (rare in real text; common in\n  // jailbreak attempts).\n  /\\n\\nHuman:\\s/gi,\n  /\\n\\nAssistant:\\s/gi,\n];\n\n/**\n * Strip control characters and zero-width / bidi-format characters.\n *\n * Keeps `\\t` (U+0009) and `\\n` (U+000A) for legibility — these don't help\n * an attacker escape a prompt scaffold any more than a space does.\n */\nfunction stripControls(input: string): string {\n  let out = '';\n  for (let i = 0; i < input.length; i += 1) {\n    const code = input.charCodeAt(i);\n    // Allow tab and newline.\n    if (code === 0x09 || code === 0x0a) {\n      out += input[i];\n      continue;\n    }\n    // C0 controls (U+0000–U+001F) and DEL (U+007F).\n    if (code < 0x20 || code === 0x7f) continue;\n    // C1 controls (U+0080–U+009F).\n    if (code >= 0x80 && code <= 0x9f) continue;\n    // Zero-width and bidi-format characters.\n    //   U+200B ZERO WIDTH SPACE\n    //   U+200C ZERO WIDTH NON-JOINER\n    //   U+200D ZERO WIDTH JOINER\n    //   U+200E LEFT-TO-RIGHT MARK\n    //   U+200F RIGHT-TO-LEFT MARK\n    //   U+202A–U+202E bidi embedding/override controls\n    //   U+2060 WORD JOINER\n    //   U+2066–U+2069 isolate controls\n    //   U+FEFF ZERO WIDTH NO-BREAK SPACE (BOM)\n    if (\n      code === 0x200b ||\n      code === 0x200c ||\n      code === 0x200d ||\n      code === 0x200e ||\n      code === 0x200f ||\n      (code >= 0x202a && code <= 0x202e) ||\n      code === 0x2060 ||\n      (code >= 0x2066 && code <= 0x2069) ||\n      code === 0xfeff\n    ) {\n      continue;\n    }\n    out += input[i];\n  }\n  return out;\n}\n\n/**\n * Sanitise an arbitrary string for safe inclusion in an LLM prompt context.\n *\n * Returns the sanitised string, length-capped at `limit`.\n * If the input is not a string, the input is returned unchanged (the caller\n * is expected to only invoke this for string-typed snapshot fields).\n */\nexport function sanitiseForLLM(\n  value: string,\n  limit: number = SANITISE_DEFAULT_LIMIT\n): string {\n  let out = stripControls(value);\n  for (const marker of PROMPT_MARKERS) {\n    out = out.replace(marker, '?');\n  }\n  if (out.length > limit) {\n    out = out.slice(0, limit);\n  }\n  return out;\n}\n\n","/**\n * URL sanitisation for snapshot fields.\n *\n * Strips credential-bearing query parameters by key name, and replaces UUID\n * path segments with `[id]`. Numeric IDs and other opaque identifiers are left\n * intact — the customer already sent them to their analytics provider, and they\n * carry useful page-context for an LLM (product IDs, order numbers, pagination).\n *\n * UUIDs are the exception: they are opaque by design, add no LLM context, and\n * are a near-universal pattern for user/session/resource identifiers.\n */\n\n/**\n * Query-parameter names whose values are always redacted. Lowercased; the\n * matcher is case-insensitive.\n */\nconst SENSITIVE_QUERY_KEYS = new Set<string>([\n  'token',\n  'access_token',\n  'id_token',\n  'refresh_token',\n  'auth_token',\n  'authtoken',\n  'jwt',\n  'session',\n  'sessionid',\n  'session_id',\n  'sid',\n  'sso',\n  'code',\n  'state',\n  'nonce',\n  'api_key',\n  'apikey',\n  'api-key',\n  'key',\n  'secret',\n  'password',\n  'passwd',\n  'pwd',\n  'auth',\n  'authorization',\n  'bearer',\n  'cookie',\n  'reset_password',\n  'reset_token',\n  'verify',\n  'verification',\n  'oauth_token',\n  'oauth_verifier',\n]);\n\n/** Standard UUID v1–v5 (8-4-4-4-12 hex groups). */\nconst UUID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;\n\n/**\n * Sanitise a URL for safe inclusion in an LLM snapshot.\n *\n * Returns the sanitised URL string, or the input truncated to `maxLength`\n * if parsing fails (some \"URLs\" we see are fragments or relative paths).\n */\nexport function redactUrl(input: string, maxLength = 512): string {\n  if (!input) return input;\n  let url: URL;\n  try {\n    url = new URL(input);\n  } catch {\n    try {\n      // Relative path — parse with stub base, emit path + query only.\n      url = new URL(input, 'http://placeholder.invalid/');\n      return redactPathAndQuery(url, maxLength);\n    } catch {\n      return input.replace(UUID_RE, '[id]').slice(0, maxLength);\n    }\n  }\n  return redactFullUrl(url, maxLength);\n}\n\nfunction redactFullUrl(url: URL, maxLength: number): string {\n  redactQueryParams(url);\n  url.pathname = url.pathname.replace(UUID_RE, '[id]');\n  return url.toString().slice(0, maxLength);\n}\n\nfunction redactPathAndQuery(url: URL, maxLength: number): string {\n  redactQueryParams(url);\n  const path = url.pathname.replace(UUID_RE, '[id]');\n  return `${path}${url.search}`.slice(0, maxLength);\n}\n\nfunction redactQueryParams(url: URL): void {\n  const keys = Array.from(new Set(Array.from(url.searchParams.keys())));\n  for (const key of keys) {\n    if (SENSITIVE_QUERY_KEYS.has(key.toLowerCase())) {\n      url.searchParams.set(key, '[REDACTED]');\n    }\n  }\n}\n","/**\n * `marketing_params` block. Captured once at `Contextune.init()` and reused —\n * even if the URL changes mid-session (SPA route change), the snapshot reports\n * the landing-page params.\n *\n * Every string field here lands in an LLM system prompt downstream, so\n * each is passed through:\n *   - `sanitiseForLLM` (strip prompt-control markers, control chars, length-cap)\n *   - `redactUrl` for URL-shaped fields (`landing_page`, `referrer`)\n *\n * See `.agents/security-review.md` F1 (prompt injection) and F2 (URL PII).\n */\n\nimport type { MarketingParamsBlock } from '../types.js';\nimport { sanitiseForLLM, SANITISE_URL_LIMIT } from '../sanitise/forLLM.js';\nimport { redactUrl } from '../sanitise/url.js';\n\nconst UTM_KEYS = [\n  'utm_source',\n  'utm_medium',\n  'utm_campaign',\n  'utm_term',\n  'utm_content',\n] as const;\n\n/** UTM values are short by spec; tighter cap reduces prompt-budget noise. */\nconst UTM_LIMIT = 128;\n\nexport function captureMarketingParams(): MarketingParamsBlock {\n  const loc = typeof window !== 'undefined' ? window.location : undefined;\n  const doc = typeof document !== 'undefined' ? document : undefined;\n\n  const search = loc?.search ?? '';\n  const params = new URLSearchParams(search);\n\n  const rawReferrer = doc?.referrer && doc.referrer.length > 0 ? doc.referrer : null;\n  const rawLandingPage = loc?.href ?? null;\n\n  const block: MarketingParamsBlock = {\n    utm_source: null,\n    utm_medium: null,\n    utm_campaign: null,\n    utm_term: null,\n    utm_content: null,\n    referrer:\n      rawReferrer === null\n        ? null\n        : sanitiseForLLM(redactUrl(rawReferrer, SANITISE_URL_LIMIT), SANITISE_URL_LIMIT),\n    landing_page:\n      rawLandingPage === null\n        ? null\n        : sanitiseForLLM(\n            redactUrl(rawLandingPage, SANITISE_URL_LIMIT),\n            SANITISE_URL_LIMIT\n          ),\n  };\n\n  for (const key of UTM_KEYS) {\n    const v = params.get(key);\n    if (v !== null) block[key] = sanitiseForLLM(v, UTM_LIMIT);\n  }\n\n  return block;\n}\n","/**\n * `device_info` block. Read at snapshot time, not at init time — keeps\n * viewport responsive to window resizes.\n */\n\nimport type { DeviceInfoBlock } from '../types.js';\n\nconst MOBILE_RE = /Mobi|Android.*Mobile|iPhone|iPod|Windows Phone|BlackBerry/i;\nconst TABLET_RE = /iPad|Tablet|Android(?!.*Mobile)/i;\n\nfunction inferDeviceType(\n  userAgent: string,\n  viewportWidth: number\n): 'desktop' | 'tablet' | 'mobile' {\n  if (TABLET_RE.test(userAgent)) return 'tablet';\n  if (MOBILE_RE.test(userAgent)) return 'mobile';\n  // Viewport fallback: if the UA didn't say but the screen is small.\n  if (viewportWidth > 0 && viewportWidth < 768) return 'mobile';\n  return 'desktop';\n}\n\nexport function readDeviceInfo(): DeviceInfoBlock {\n  // We're guaranteed to be in a browser-like env (jsdom in tests). Defensive\n  // reads only against truly missing globals; otherwise we fail loud per the\n  // SDK rules.\n  const nav = typeof navigator !== 'undefined' ? navigator : undefined;\n  const win = typeof window !== 'undefined' ? window : undefined;\n\n  const userAgent = nav?.userAgent ?? '';\n  const language = nav?.language ?? '';\n  const platform = nav?.platform ?? '';\n  const viewport_w = win?.innerWidth ?? 0;\n  const viewport_h = win?.innerHeight ?? 0;\n\n  return {\n    user_agent: userAgent,\n    viewport_w,\n    viewport_h,\n    language,\n    platform,\n    device_type: inferDeviceType(userAgent, viewport_w),\n  };\n}\n","/**\n * Sensitive-field redaction for event properties.\n *\n * Scrubs well-known sensitive key names, keys matching a credentials/secret/token\n * regex, and URL-shaped values (query params + path PII). Used by Contextune\n * before pushing any captured event into the ring buffer.\n */\n\nimport {\n  sanitiseForLLM,\n  SANITISE_DEFAULT_LIMIT,\n  SANITISE_URL_LIMIT,\n} from '../sanitise/forLLM.js';\nimport { redactUrl } from '../sanitise/url.js';\n\nconst SENSITIVE_NAME_KEYS = new Set<string>([\n  'password',\n  'passwd',\n  'pwd',\n  'email',\n  'credit_card',\n  'creditcard',\n  'cc_number',\n  'cvv',\n  'ssn',\n  'phone',\n  'phone_number',\n  'dob',\n  'date_of_birth',\n  'address',\n  'street',\n  'postcode',\n  'zip',\n  'auth',\n  'authorization',\n  'bearer',\n  'cookie',\n  'set_cookie',\n]);\n\nconst SENSITIVE_REGEX = /(password|secret|token|api[_-]?key|jwt|session)/i;\n\n/** Keys whose string values are treated as URLs for value-scrubbing. */\nconst URL_LIKE_KEYS = new Set<string>(['url', 'href', 'uri', 'referrer', 'referer']);\n\n/** Bound recursion to keep redaction work O(payload size). */\nconst MAX_REDACT_DEPTH = 4;\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction redactValue(value: unknown, key: string, depth: number): unknown {\n  if (typeof value === 'string') {\n    if (URL_LIKE_KEYS.has(key.toLowerCase())) {\n      return sanitiseForLLM(redactUrl(value, SANITISE_URL_LIMIT), SANITISE_URL_LIMIT);\n    }\n    return sanitiseForLLM(value, SANITISE_DEFAULT_LIMIT);\n  }\n  if (Array.isArray(value)) {\n    if (depth >= MAX_REDACT_DEPTH) return value;\n    return value.map((v) => redactValue(v, key, depth + 1));\n  }\n  if (isPlainObject(value)) {\n    if (depth >= MAX_REDACT_DEPTH) return value;\n    return redactObject(value, depth + 1);\n  }\n  return value;\n}\n\nfunction redactObject(\n  input: Record<string, unknown>,\n  depth: number\n): Record<string, unknown> {\n  const out: Record<string, unknown> = {};\n  for (const [k, v] of Object.entries(input)) {\n    if (k === '__proto__' || k === 'constructor' || k === 'prototype') {\n      continue;\n    }\n    if (SENSITIVE_NAME_KEYS.has(k.toLowerCase())) {\n      out[k] = '[REDACTED]';\n      continue;\n    }\n    if (SENSITIVE_REGEX.test(k) && typeof v === 'string') {\n      out[k] = '[REDACTED]';\n      continue;\n    }\n    out[k] = redactValue(v, k, depth);\n  }\n  return out;\n}\n\nexport function redactProperties(\n  input: Record<string, unknown> | undefined\n): Record<string, unknown> | undefined {\n  if (!input) return undefined;\n  return redactObject(input, 0);\n}\n","import type { ContextuneSnapshot } from \"../types.js\";\n\nexport function toJson(snap: ContextuneSnapshot): string {\n  return JSON.stringify(snap);\n}\n","import type { ContextuneSnapshot } from \"../types.js\";\n\n/**\n * Serialises a ContextuneSnapshot to TOON (Token-Oriented Object Notation).\n *\n * Flat objects (marketing_params, device_info) render as YAML-style key:value\n * blocks. event_log renders as a tabular section with [N]{fields} header and\n * one CSV row per entry — the most token-efficient form for uniform arrays.\n *\n * CSV quoting follows RFC 4180: cells that contain commas, double-quotes, or\n * newlines are wrapped in double-quotes, with internal double-quotes doubled.\n *\n * See: https://github.com/toon-format/toon\n */\n\nfunction toonScalar(v: unknown): string {\n  if (v === null || v === undefined) return 'null';\n  if (typeof v === 'string') return v;\n  return String(v);\n}\n\nfunction csvCell(v: string): string {\n  if (v.includes(',') || v.includes('\"') || v.includes('\\n')) {\n    return `\"${v.replace(/\"/g, '\"\"')}\"`;\n  }\n  return v;\n}\n\nfunction flatBlock(label: string, obj: object): string {\n  const lines = [`${label}:`];\n  for (const [k, v] of Object.entries(obj)) {\n    lines.push(`  ${k}: ${toonScalar(v)}`);\n  }\n  return lines.join('\\n');\n}\n\nexport function toToon(snap: ContextuneSnapshot): string {\n  const sections: string[] = [];\n\n  if (snap.marketing_params) {\n    sections.push(flatBlock('marketing_params', snap.marketing_params));\n  }\n\n  if (snap.device_info) {\n    sections.push(flatBlock('device_info', snap.device_info));\n  }\n\n  const log = snap.event_log;\n  const hasProps = log.some(e => e.properties && Object.keys(e.properties).length > 0);\n  const fields = hasProps ? '{name,elapsed_s,properties}' : '{name,elapsed_s}';\n  const header = `event_log[${log.length}]${fields}:`;\n\n  const rows = log.map(e => {\n    const name = csvCell(e.name);\n    const ts = String(e.elapsed_s);\n    if (!hasProps) return `  ${name},${ts}`;\n    const propsStr =\n      e.properties && Object.keys(e.properties).length > 0\n        ? csvCell(JSON.stringify(e.properties))\n        : '';\n    return `  ${name},${ts},${propsStr}`;\n  });\n\n  sections.push([header, ...rows].join('\\n'));\n\n  return sections.join('\\n');\n}\n","import type { ContextuneSnapshot } from \"../types.js\";\nimport { toJson } from \"./json.js\";\nimport { toToon } from \"./toon.js\";\n\nexport type { SnapshotFormat } from \"../types.js\";\n\nexport function formatSnapshot(snap: ContextuneSnapshot, format: 'json' | 'toon'): string {\n  if (format === 'json') return toJson(snap);\n  return toToon(snap);\n}\n","const MILESTONES = [25, 50, 75, 100] as const;\nconst DEBOUNCE_MS = 100;\n\ntype Push = (name: string, properties?: Record<string, unknown>) => void;\n\nexport function startScrollTracking(push: Push): () => void {\n  if (typeof window === 'undefined' || typeof document === 'undefined') {\n    return () => {};\n  }\n\n  let maxMilestone = 0;\n  let timer: ReturnType<typeof setTimeout> | null = null;\n\n  function check() {\n    const el = document.documentElement;\n    const scrollable = el.scrollHeight - el.clientHeight;\n    if (scrollable <= 0) return;\n\n    const pct = Math.min(100, Math.round((window.scrollY / scrollable) * 100));\n\n    for (const milestone of MILESTONES) {\n      if (pct >= milestone && maxMilestone < milestone) {\n        maxMilestone = milestone;\n        push('scroll', { scroll_pct: milestone });\n      }\n    }\n  }\n\n  function handler() {\n    if (timer !== null) clearTimeout(timer);\n    timer = setTimeout(check, DEBOUNCE_MS);\n  }\n\n  window.addEventListener('scroll', handler, { passive: true });\n\n  return () => {\n    window.removeEventListener('scroll', handler);\n    if (timer !== null) clearTimeout(timer);\n    timer = null;\n  };\n}\n","import { sanitiseForLLM, SANITISE_SELECTOR_LIMIT } from \"../sanitise/forLLM.js\";\n\nconst RAGE_CLICK_WINDOW_MS = 1000;\nconst RAGE_CLICK_THRESHOLD = 3;\nconst MAX_TRACKED_TARGETS = 64;\n\nfunction describeTarget(target: EventTarget | null): string {\n  if (!(target instanceof Element)) return 'unknown';\n  const tag = target.tagName.toLowerCase();\n  if (target.id) return `${tag}#${target.id}`.slice(0, 64);\n  const testId = target.getAttribute('data-testid');\n  if (testId) return `${tag}[data-testid=${testId}]`.slice(0, 64);\n  const cls = target.classList[0];\n  if (cls) return `${tag}.${cls}`.slice(0, 64);\n  return tag;\n}\n\ninterface TargetState {\n  /** Wall-clock timestamps of clicks within the current window. */\n  clicks: number[];\n  /**\n   * Debounce timer handle. Set when a cluster forms (≥ threshold clicks),\n   * reset on each additional click in the same cluster. When it fires the\n   * cluster is over and we emit once with the final count.\n   */\n  timer: ReturnType<typeof setTimeout> | null;\n}\n\ntype Push = (name: string, properties?: Record<string, unknown>) => void;\n\nexport function startRageClickTracking(push: Push): () => void {\n  if (typeof window === 'undefined') return () => {};\n\n  const targetMap = new Map<string, TargetState>();\n\n  function handler(e: MouseEvent): void {\n    const selector = sanitiseForLLM(describeTarget(e.target), SANITISE_SELECTOR_LIMIT);\n    const now = Date.now();\n\n    if (!targetMap.has(selector)) {\n      if (targetMap.size >= MAX_TRACKED_TARGETS) {\n        const [firstKey] = targetMap.keys();\n        if (firstKey !== undefined) targetMap.delete(firstKey);\n      }\n      targetMap.set(selector, { clicks: [], timer: null });\n    }\n\n    const state = targetMap.get(selector)!;\n\n    // Prune clicks that have fallen outside the window.\n    const windowStart = now - RAGE_CLICK_WINDOW_MS;\n    let i = 0;\n    while (i < state.clicks.length && (state.clicks[i] ?? 0) < windowStart) i++;\n    if (i > 0) state.clicks.splice(0, i);\n\n    state.clicks.push(now);\n\n    if (state.clicks.length < RAGE_CLICK_THRESHOLD) return;\n\n    // Cluster is active. Reset the debounce timer so we emit once the cluster\n    // ends — meaning no further click on this target for a full window period.\n    // The count at that point reflects every click in the cluster.\n    if (state.timer !== null) clearTimeout(state.timer);\n    state.timer = setTimeout(() => {\n      const count = state.clicks.length;\n      state.clicks = [];\n      state.timer = null;\n      push('rage_click', { target: selector, count });\n    }, RAGE_CLICK_WINDOW_MS);\n  }\n\n  window.addEventListener('click', handler, { capture: true });\n\n  return () => {\n    window.removeEventListener('click', handler, true);\n    for (const state of targetMap.values()) {\n      if (state.timer !== null) clearTimeout(state.timer);\n    }\n    targetMap.clear();\n  };\n}\n","import { startScrollTracking } from \"./scroll.js\";\nimport { startRageClickTracking } from \"./rageClicks.js\";\n\nexport interface TrackingExtrasOptions {\n  /** Fire a 'scroll' event each time the user crosses a 25/50/75/100% depth milestone. */\n  scroll?: boolean;\n  /** Fire a 'rage_click' event when ≥3 clicks on the same target occur within a 1-second window. */\n  rageClicks?: boolean;\n}\n\ntype Push = (name: string, properties?: Record<string, unknown>) => void;\n\nexport function startTrackingExtras(options: TrackingExtrasOptions, push: Push): () => void {\n  const disposers: Array<() => void> = [];\n\n  if (options.scroll) {\n    disposers.push(startScrollTracking(push));\n  }\n  if (options.rageClicks) {\n    disposers.push(startRageClickTracking(push));\n  }\n\n  return () => {\n    for (const dispose of disposers) dispose();\n    disposers.length = 0;\n  };\n}\n","import type { EventLogEntry } from \"../types.js\";\n\nexport function initNavigationSession() {\n  const sessionStartMs =\n    typeof performance !== 'undefined' && typeof performance.timeOrigin === 'number'\n      ? performance.timeOrigin\n      : Date.now();\n\n  return {\n    sessionStartMs,\n    loadEvents: (): EventLogEntry[] => [],\n    save: (_buffer: EventLogEntry[]): void => {},\n    destroy: (): void => {},\n  };\n}\n","import type { EventLogEntry } from \"../types.js\";\nimport { initNavigationSession } from \"./navigation.js\";\n\nconst COOKIE_NAME = 'contextune_session';\nconst STORAGE_PREFIX = 'ct_events:';\nconst MAX_AGE_S = 30 * 60;\n\nfunction generateId(): string {\n  if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n    return crypto.randomUUID().replace(/-/g, '').slice(0, 12);\n  }\n  return Math.random().toString(36).slice(2, 14);\n}\n\nfunction readCookie(): { id: string; startMs: number } | null {\n  try {\n    if (typeof document === 'undefined') return null;\n    const prefix = `${COOKIE_NAME}=`;\n    for (const part of document.cookie.split(';')) {\n      const trimmed = part.trim();\n      if (!trimmed.startsWith(prefix)) continue;\n      const raw = decodeURIComponent(trimmed.slice(prefix.length));\n      const sep = raw.indexOf(':');\n      if (sep === -1) return null;\n      const id = raw.slice(0, sep);\n      const startMs = Number(raw.slice(sep + 1));\n      if (!id || !Number.isFinite(startMs)) return null;\n      return { id, startMs };\n    }\n    return null;\n  } catch {\n    return null;\n  }\n}\n\nfunction writeCookie(id: string, startMs: number): void {\n  try {\n    if (typeof document === 'undefined') return;\n    const value = encodeURIComponent(`${id}:${startMs}`);\n    document.cookie = `${COOKIE_NAME}=${value}; Max-Age=${MAX_AGE_S}; SameSite=Lax; path=/`;\n  } catch {\n    // sandboxed iframe or restricted context — ignore\n  }\n}\n\nfunction expireCookie(): void {\n  try {\n    if (typeof document === 'undefined') return;\n    document.cookie = `${COOKIE_NAME}=; Max-Age=0; SameSite=Lax; path=/`;\n  } catch {\n    // sandboxed iframe or restricted context — ignore\n  }\n}\n\nfunction loadFromStorage(id: string): EventLogEntry[] {\n  try {\n    const raw = localStorage.getItem(`${STORAGE_PREFIX}${id}`);\n    if (!raw) return [];\n    return JSON.parse(raw) as EventLogEntry[];\n  } catch {\n    return [];\n  }\n}\n\nfunction saveToStorage(id: string, buffer: EventLogEntry[]): void {\n  try {\n    localStorage.setItem(`${STORAGE_PREFIX}${id}`, JSON.stringify(buffer));\n  } catch {\n    // localStorage full or disabled (private mode) — ignore\n  }\n}\n\nfunction cleanOldSessions(currentId: string): void {\n  try {\n    const toDelete: string[] = [];\n    for (let i = 0; i < localStorage.length; i++) {\n      const key = localStorage.key(i);\n      if (key?.startsWith(STORAGE_PREFIX) && key !== `${STORAGE_PREFIX}${currentId}`) {\n        toDelete.push(key);\n      }\n    }\n    for (const key of toDelete) localStorage.removeItem(key);\n  } catch {\n    // ignore\n  }\n}\n\nexport function initCtSession() {\n  if (typeof window === 'undefined') {\n    return initNavigationSession();\n  }\n\n  const existing = readCookie();\n  const id = existing?.id ?? generateId();\n  const startMs = existing?.startMs ?? Date.now();\n\n  writeCookie(id, startMs);\n  cleanOldSessions(id);\n\n  return {\n    sessionStartMs: startMs,\n    loadEvents: (): EventLogEntry[] => loadFromStorage(id),\n    save: (buffer: EventLogEntry[]): void => {\n      writeCookie(id, startMs);\n      saveToStorage(id, buffer);\n    },\n    // Full purge: remove the persisted event log and the session cookie so the\n    // profile cannot be recovered by a subsequent init() (see Contextune.destroy).\n    destroy: (): void => {\n      try {\n        localStorage.removeItem(`${STORAGE_PREFIX}${id}`);\n      } catch {\n        // localStorage disabled — nothing to remove\n      }\n      expireCookie();\n    },\n  };\n}\n","import type { EventLogEntry } from \"../types.js\";\nimport { initNavigationSession } from \"./navigation.js\";\nimport { initCtSession } from \"./ctSession.js\";\n\nexport type Persistence = 'navigation' | 'ct-session';\n\nexport interface SessionHandle {\n  sessionStartMs: number;\n  loadEvents(): EventLogEntry[];\n  save(buffer: EventLogEntry[]): void;\n  destroy(): void;\n}\n\nexport function initSession(persistence: Persistence = 'navigation'): SessionHandle {\n  if (persistence === 'ct-session') return initCtSession();\n  return initNavigationSession();\n}\n","import type {\n  CapturedEvent,\n  Providers,\n  AddEventCallback,\n  ContextuneSnapshot,\n  SnapshotFormat,\n  EventLogEntry,\n  MarketingParamsBlock,\n} from \"./types.js\";\nimport { extractEventsFromBatch } from \"./translation.js\";\nimport { patchFetch } from \"./patches/patchFetch.js\";\nimport { patchSendBeacon } from \"./patches/patchSendBeacon.js\";\nimport { patchXMLHTTPRequest } from \"./patches/patchXhr.js\";\nimport { captureMarketingParams } from \"./engine/marketingParams.js\";\nimport { readDeviceInfo } from \"./engine/deviceInfo.js\";\nimport { sanitiseForLLM, SANITISE_SELECTOR_LIMIT } from \"./sanitise/forLLM.js\";\nimport { redactProperties } from \"./engine/eventLog.js\";\nimport { formatSnapshot } from \"./formatters/index.js\";\nimport { startTrackingExtras, type TrackingExtrasOptions } from \"./tracking/index.js\";\nimport { initSession, type SessionHandle, type Persistence } from \"./session/index.js\";\n\nconst SOURCE_MAP = {\n  ga4: 'googleanalytics',\n  segment: 'segment',\n  mixpanel: 'mixpanel',\n} as const satisfies Record<string, Providers>;\n\nexport type ContextuneSource = keyof typeof SOURCE_MAP;\n\nexport interface ContextuneOptions {\n  /** Omit to use direct-call mode: only track()/page() calls are captured,\n   *  no network interception. Useful when you want Contextune itself as the\n   *  \"analytics\" layer. */\n  source?: ContextuneSource;\n  eventLogSize?: number;\n  /** Controls event log lifetime. 'navigation' (default): cleared on page reload.\n   *  'ct-session': survives reloads; scoped to 30 min of inactivity via cookie. */\n  persistence?: Persistence;\n  context?: {\n    extras?: {\n      /** Capture UTM params, referrer, and landing page. Default: true. */\n      marketing?: boolean;\n      /** Capture viewport, language, platform, and device type. Default: true. */\n      device?: boolean;\n    };\n  };\n  tracking?: TrackingExtrasOptions;\n}\n\n/**\n * Controls which events trigger a subscription handler.\n * - `'event'` — every entry added to the log\n * - predicate function — only entries for which the function returns `true`\n */\nexport type SubscriptionFilter =\n  | 'event'\n  | ((entry: EventLogEntry) => boolean);\n\n/**\n * Handler called when a matching event enters the log.\n * `entry` is the new event; `state` is the full snapshot captured immediately\n * before the entry was added (the log does not yet contain `entry`).\n */\nexport type SubscriptionHandler = (\n  entry: EventLogEntry,\n  state: ContextuneSnapshot,\n) => void;\n\nexport interface ContextuneInstance {\n  getSnapshot(): ContextuneSnapshot;\n  getSnapshot(options: { format: 'js' }): ContextuneSnapshot;\n  getSnapshot(options: { format: 'json' | 'toon' }): string;\n  track(name: string, properties?: Record<string, unknown>): void;\n  page(url?: string): void;\n  subscribe(filter: SubscriptionFilter, handler: SubscriptionHandler): () => void;\n  clear(): void;\n  destroy(): void;\n}\n\nconst DEFAULT_EVENT_LOG_SIZE = 50;\n\ntype Listener = { filter: SubscriptionFilter; handler: SubscriptionHandler };\n\nclass ContextuneImpl implements ContextuneInstance {\n  private readonly provider: Providers | null;\n  private readonly capacity: number;\n  private readonly sessionStartMs: number;\n  private readonly session: SessionHandle;\n  private readonly disposers: Array<() => void> = [];\n  private readonly buffer: EventLogEntry[] = [];\n  private readonly marketingParams: MarketingParamsBlock | null;\n  private readonly enableDevice: boolean;\n  private readonly listeners = new Set<Listener>();\n  private destroyed = false;\n\n  constructor(options: ContextuneOptions) {\n    this.provider = options.source ? SOURCE_MAP[options.source] : null;\n    this.capacity = Math.max(1, options.eventLogSize ?? DEFAULT_EVENT_LOG_SIZE);\n\n    this.session = initSession(options.persistence);\n    this.sessionStartMs = this.session.sessionStartMs;\n\n    const persisted = this.session.loadEvents();\n    const start = Math.max(0, persisted.length - this.capacity);\n    for (let i = start; i < persisted.length; i++) {\n      this.buffer.push(persisted[i]!);\n    }\n\n    const extras = options.context?.extras;\n    const enableMarketing = extras?.marketing !== false;\n    this.enableDevice = extras?.device !== false;\n    this.marketingParams = enableMarketing ? captureMarketingParams() : null;\n\n    if (typeof window === 'undefined') return;\n\n    if (this.provider) {\n      const callback: AddEventCallback = (event: CapturedEvent) => {\n        this.handleCapture(event);\n      };\n      this.disposers.push(patchFetch(callback));\n      this.disposers.push(patchSendBeacon(callback));\n      this.disposers.push(patchXMLHTTPRequest(callback));\n    }\n\n    if (options.tracking) {\n      this.disposers.push(startTrackingExtras(options.tracking, (name, props) => this.push(name, props)));\n    }\n  }\n\n  private buildSnapshot(): ContextuneSnapshot {\n    const snap: ContextuneSnapshot = {\n      event_log: this.buffer.map(e => ({ ...e })),\n    };\n    if (this.marketingParams) snap.marketing_params = { ...this.marketingParams };\n    if (this.enableDevice) snap.device_info = readDeviceInfo();\n    return snap;\n  }\n\n  private push(name: string, properties?: Record<string, unknown>): void {\n    if (this.destroyed) return;\n    const entry: EventLogEntry = {\n      name: sanitiseForLLM(name, SANITISE_SELECTOR_LIMIT),\n      elapsed_s: Math.max(0, Math.floor((Date.now() - this.sessionStartMs) / 1000)),\n    };\n    const redacted = redactProperties(properties);\n    if (redacted && Object.keys(redacted).length > 0) entry.properties = redacted;\n\n    if (this.listeners.size > 0) {\n      const state = this.buildSnapshot();\n      this.buffer.push(entry);\n      if (this.buffer.length > this.capacity) this.buffer.shift();\n      this.session.save(this.buffer);\n      for (const { filter, handler } of this.listeners) {\n        if (filter === 'event' || filter(entry)) {\n          try { handler({ ...entry }, state); } catch { /* isolate bad callbacks */ }\n        }\n      }\n    } else {\n      this.buffer.push(entry);\n      if (this.buffer.length > this.capacity) this.buffer.shift();\n      this.session.save(this.buffer);\n    }\n  }\n\n  private handleCapture(event: CapturedEvent): void {\n    if (this.destroyed) return;\n    if (!this.provider || event.provider !== this.provider) return;\n\n    const extracted = extractEventsFromBatch({\n      ...event,\n      href: window.location.href,\n      sentAt: new Date(),\n    });\n\n    for (const e of extracted) {\n      this.push(e.eventName, e.properties);\n    }\n  }\n\n  track(name: string, properties?: Record<string, unknown>): void {\n    this.push(name, properties);\n  }\n\n  page(url?: string): void {\n    this.push('page_view', url ? { url } : undefined);\n  }\n\n  subscribe(filter: SubscriptionFilter, handler: SubscriptionHandler): () => void {\n    const listener: Listener = { filter, handler };\n    this.listeners.add(listener);\n    return () => { this.listeners.delete(listener); };\n  }\n\n  getSnapshot(): ContextuneSnapshot;\n  getSnapshot(options: { format: 'js' }): ContextuneSnapshot;\n  getSnapshot(options: { format: 'json' | 'toon' }): string;\n  getSnapshot(options?: { format?: SnapshotFormat }): ContextuneSnapshot | string {\n    const snap = this.buildSnapshot();\n    const format = options?.format ?? 'js';\n    if (format === 'js') return snap;\n    return formatSnapshot(snap, format);\n  }\n\n  clear(): void {\n    if (this.destroyed) return;\n    // Reset the event log (in memory and its persisted copy) while keeping the\n    // SDK live: transport patches, subscribers, marketing/device capture, and\n    // the session clock (elapsed_s) all continue.\n    this.buffer.length = 0;\n    this.session.save(this.buffer);\n  }\n\n  destroy(): void {\n    if (this.destroyed) return;\n    this.destroyed = true;\n    for (const dispose of this.disposers) dispose();\n    this.disposers.length = 0;\n    // Full teardown: purges persisted state (localStorage + cookie for\n    // ct-session) so the profile cannot be recovered by a later init().\n    this.session.destroy();\n    this.buffer.length = 0;\n    this.listeners.clear();\n  }\n}\n\nlet singleton: ContextuneImpl | null = null;\n\nexport const Contextune = {\n  init(options: ContextuneOptions = {}): ContextuneInstance {\n    if (singleton) return singleton;\n    singleton = new ContextuneImpl(options);\n    return singleton;\n  },\n  getSnapshot(options?: { format?: SnapshotFormat }): ContextuneSnapshot | string | null {\n    if (!singleton) return null;\n    const format = options?.format;\n    if (!format || format === 'js') return singleton.getSnapshot();\n    return singleton.getSnapshot({ format });\n  },\n  track(name: string, properties?: Record<string, unknown>): void {\n    singleton?.track(name, properties);\n  },\n  page(url?: string): void {\n    singleton?.page(url);\n  },\n  subscribe(filter: SubscriptionFilter, handler: SubscriptionHandler): () => void {\n    if (!singleton) return () => {};\n    return singleton.subscribe(filter, handler);\n  },\n  clear(): void {\n    singleton?.clear();\n  },\n  destroy(): void {\n    if (!singleton) return;\n    singleton.destroy();\n    singleton = null;\n  },\n};\n"],"mappings":"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","names":[]}