{"version":3,"sources":["../src/modules/replay.ts"],"sourcesContent":["import type { FeedbackApi, FeedbackApiInternal, ReplayBlob } from '../types'\n\nexport interface ReplayOptions {\n  durationMs?: number\n  maskAllText?: boolean\n  maskAllInputs?: boolean\n  maskTextSelector?: string\n  blockSelector?: string\n}\n\ntype RrwebEvent = { timestamp: number } & Record<string, unknown>\n\nexport function withReplay(fb: FeedbackApi, options: ReplayOptions = {}): FeedbackApi {\n  const internal = fb as FeedbackApiInternal\n  if (typeof internal._registerTransformer !== 'function') return fb\n\n  const duration = options.durationMs ?? 60_000\n  const events: RrwebEvent[] = []\n  let stopFn: (() => void) | null = null\n\n  function evictOld() {\n    const cutoff = Date.now() - duration\n    while (events.length > 0 && (events[0]!.timestamp ?? 0) < cutoff) events.shift()\n  }\n\n  void (async () => {\n    try {\n      const { record } = await import('rrweb')\n      // Privacy posture (v0.11): mask aggressively by default. Hosts can\n      // opt OUT explicitly by passing maskAllInputs:false / maskAllText:false,\n      // but the default should never ship sensitive text or input values.\n      // rrweb does not expose a maskAllText:boolean directly; mask-everything\n      // is achieved by passing a maskTextClass regex that matches all classes.\n      const maskAll = options.maskAllText !== false\n      stopFn = record({\n        emit(event: RrwebEvent) {\n          events.push(event)\n          evictOld()\n        },\n        maskAllInputs: options.maskAllInputs ?? true,\n        ...(maskAll && { maskTextClass: /.*/ }),\n        ...(options.maskTextSelector !== undefined && { maskTextSelector: options.maskTextSelector }),\n        // Block selector: elements matching this are wholly excluded from\n        // the replay (not even shape captured). The .mfb-mask class +\n        // data-mfb-mask attr line up with the screenshot mask defaults,\n        // so consumers tagging one tag both surfaces.\n        blockSelector: options.blockSelector ?? '[data-mfb-block],[data-mfb-mask],.mfb-mask',\n      }) ?? null\n    } catch { /* rrweb unavailable */ }\n  })()\n\n  internal._registerTransformer((payload) => {\n    if (events.length === 0) return payload\n    evictOld()\n    const blob: ReplayBlob = {\n      events: events.slice(),\n      durationMs: Math.min(duration, Date.now() - (events[0]?.timestamp ?? Date.now())),\n    }\n    return {\n      ...payload,\n      technical_context: { ...payload.technical_context, replay: blob },\n    }\n  })\n\n  const originalShutdown = fb.shutdown.bind(fb)\n  fb.shutdown = () => {\n    try { stopFn?.() } catch { /* noop */ }\n    originalShutdown()\n  }\n\n  return fb\n}\n"],"mappings":";AAYO,SAAS,WAAW,IAAiB,UAAyB,CAAC,GAAgB;AACpF,QAAM,WAAW;AACjB,MAAI,OAAO,SAAS,yBAAyB,WAAY,QAAO;AAEhE,QAAM,WAAW,QAAQ,cAAc;AACvC,QAAM,SAAuB,CAAC;AAC9B,MAAI,SAA8B;AAElC,WAAS,WAAW;AAClB,UAAM,SAAS,KAAK,IAAI,IAAI;AAC5B,WAAO,OAAO,SAAS,MAAM,OAAO,CAAC,EAAG,aAAa,KAAK,OAAQ,QAAO,MAAM;AAAA,EACjF;AAEA,QAAM,YAAY;AAChB,QAAI;AACF,YAAM,EAAE,OAAO,IAAI,MAAM,OAAO,OAAO;AAMvC,YAAM,UAAU,QAAQ,gBAAgB;AACxC,eAAS,OAAO;AAAA,QACd,KAAK,OAAmB;AACtB,iBAAO,KAAK,KAAK;AACjB,mBAAS;AAAA,QACX;AAAA,QACA,eAAe,QAAQ,iBAAiB;AAAA,QACxC,GAAI,WAAW,EAAE,eAAe,KAAK;AAAA,QACrC,GAAI,QAAQ,qBAAqB,UAAa,EAAE,kBAAkB,QAAQ,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,QAK3F,eAAe,QAAQ,iBAAiB;AAAA,MAC1C,CAAC,KAAK;AAAA,IACR,QAAQ;AAAA,IAA0B;AAAA,EACpC,GAAG;AAEH,WAAS,qBAAqB,CAAC,YAAY;AACzC,QAAI,OAAO,WAAW,EAAG,QAAO;AAChC,aAAS;AACT,UAAM,OAAmB;AAAA,MACvB,QAAQ,OAAO,MAAM;AAAA,MACrB,YAAY,KAAK,IAAI,UAAU,KAAK,IAAI,KAAK,OAAO,CAAC,GAAG,aAAa,KAAK,IAAI,EAAE;AAAA,IAClF;AACA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,mBAAmB,EAAE,GAAG,QAAQ,mBAAmB,QAAQ,KAAK;AAAA,IAClE;AAAA,EACF,CAAC;AAED,QAAM,mBAAmB,GAAG,SAAS,KAAK,EAAE;AAC5C,KAAG,WAAW,MAAM;AAClB,QAAI;AAAE,eAAS;AAAA,IAAE,QAAQ;AAAA,IAAa;AACtC,qBAAiB;AAAA,EACnB;AAEA,SAAO;AACT;","names":[]}