PageSourceSearch

https://deal-desk-demo.lovable.app/__l5e/events.js

js deal-desk-demo.lovable.app collected 2026-10-03 03:24:25 UTC 63,054 bytes, 1,458 lines download raw bytes

1(()=>{const __name=(target)=>target;((w, version, schemaVersions, maxEventsPerBatch = 50, maxBatchBodyBytes = 50 * 1024, maxReplayBodyBytes = 512 * 1024, maxReplayEventsPerChunk = 100, maxReplayTotalBytes = 5 * 1024 * 1024, maxReplayChunks = 50, maxReplayDurationMs = 10 * 60 * 1e3, replayFlushIntervalMs = 2e3) => {
2  const existing = w.__lovableEvents || {};
3  if (existing.__userAppEventsSdkVersion) return;
4  const d = w.document;
5  const nav = w.navigator || {};
6  const perf = w.performance || { now: /* @__PURE__ */ __name(() => Date.now(), "now") };
7  const loc = w.location || { pathname: "", href: "" };
8  const resolveSetTimer = /* @__PURE__ */ __name(() => w.setTimeout || (typeof setTimeout === "function" ? setTimeout : null), "resolveSetTimer");
9  const resolveClearTimer = /* @__PURE__ */ __name(() => w.clearTimeout || (typeof clearTimeout === "function" ? clearTimeout : null), "resolveClearTimer");
10  const isInternalPath = /* @__PURE__ */ __name((path) => path === "/__codex_token_check" || path.startsWith("/__l5e/"), "isInternalPath");
11  if (isInternalPath(loc.pathname)) return;
12  const script = d && d.currentScript;
13  const artifact = script ? {
14    kind: script.getAttribute("data-artifact-kind") || null,
15    id: script.getAttribute("data-artifact-id") || null,
16    commit_sha: script.getAttribute("data-commit-sha") || null,
17    context_token: script.getAttribute("data-context-token") || null
18  } : {};
19  const replayMode = script?.getAttribute("data-replay") || null;
20  const trackUrl = script?.getAttribute("data-track-url") || "/__l5e/trackevents";
21  const replayUrl = script?.getAttribute("data-replay-url") || "/__l5e/replay";
22  const replayScriptUrl = script?.getAttribute("data-replay-script-url") || "/__l5e/rrweb-record.js";
23  const parseSampleRate = /* @__PURE__ */ __name((value) => {
24    const rate = typeof value === "string" ? Number(value) : 0;
25    if (!Number.isFinite(rate) || rate <= 0) return 0;
26    return rate >= 1 ? 1 : rate;
27  }, "parseSampleRate");
28  const replaySampleRate = parseSampleRate(script?.getAttribute("data-replay-sample-rate") || null);
29  const replayCrashSampleRate = parseSampleRate(script?.getAttribute("data-replay-crash-sample-rate") || null);
30  const autocaptureEnabled = script?.getAttribute("data-autocapture") === "1";
31  const randomId = /* @__PURE__ */ __name(() => w.crypto && typeof w.crypto.randomUUID === "function" ? w.crypto.randomUUID() : String(Date.now()) + "-" + Math.random().toString(16).slice(2), "randomId");
32  const randomTraceHex = /* @__PURE__ */ __name((byteCount) => {
33    const bytes = new Uint8Array(byteCount);
34    if (w.crypto && typeof w.crypto.getRandomValues === "function") {
35      w.crypto.getRandomValues(bytes);
36    } else {
37      for (let index = 0; index < bytes.length; index++) {
38        bytes[index] = Math.floor(Math.random() * 256);
39      }
40    }
41    if (bytes.every((byte) => byte === 0)) bytes[bytes.length - 1] = 1;
42    return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
43  }, "randomTraceHex");
44  const createTraceContext = /* @__PURE__ */ __name(() => ({
45    traceId: randomTraceHex(16),
46    rootSpanId: randomTraceHex(8)
47  }), "createTraceContext");
48  const sessionId = randomId();
49  let appUserId = null;
50  let pageViewId = randomId();
51  let capturedClicks = 0;
52  let traceContext = createTraceContext();
53  let engagementAccruedMs = 0;
54  let engagementSegmentStart = null;
55  const ENGAGEMENT_EMIT_DELAYS_MS = [1e4, 3e4, 6e4, 3e5];
56  let engagementEmitStep = 0;
57  let engagementTimer = null;
58  const ENGAGEMENT_ACCRUAL_STOP_MS = 144e5;
59  let engagementReportedMs = 0;
60  let engagementCapped = false;
61  const ENGAGEMENT_UNFOCUSED_GRACE_MS = 18e5;
62  let engagementUnfocusedTimer = null;
63  let engagementUnfocusedSpentMs = 0;
64  let engagementUnfocusedArmedAt = 0;
65  let engagementStoppedUnfocused = false;
66  const unitIntervalFor = /* @__PURE__ */ __name((value) => {
67    let hash = 2166136261;
68    for (let index = 0; index < value.length; index++) {
69      hash ^= value.charCodeAt(index);
70      hash = Math.imul(hash, 16777619);
71    }
72    return (hash >>> 0) / 4294967296;
73  }, "unitIntervalFor");
74  const replaySamplingValue = unitIntervalFor(sessionId);
75  const isReplaySampledIn = replaySampleRate > 0 && replaySamplingValue < replaySampleRate;
76  const isReplayCrashSampledIn = replayCrashSampleRate > 0 && replaySamplingValue < replayCrashSampleRate;
77  const isReplayRecordingAllowed = isReplaySampledIn || isReplayCrashSampledIn;
78  const replayEffectiveSampleRate = isReplaySampledIn ? replaySampleRate : replayCrashSampleRate;
79  let replayId = null;
80  let replayEventCount = 0;
81  let replayStartedAt = 0;
82  let replayFinalized = false;
83  let stopReplayRecorder = null;
84  let replayChunkSequence = 0;
85  let replayThrottledChunks = 0;
86  let replayChunksInFlight = 0;
87  let replayBudgetStopReason = null;
88  let replayUploadedBytes = 0;
89  let replayFlushTimer = null;
90  let replayDurationTimer = null;
91  let replayRecorderActive = false;
92  let replayRecorderLoading = false;
93  let replayHasFullSnapshot = false;
94  let replayUploadEnabled = isReplaySampledIn;
95  let replayCaptureReason = isReplaySampledIn ? "sampled" : null;
96  let replayTriggerEventId = null;
97  let replayTriggerEventName = null;
98  let replayCaptureMetadataEmitted = false;
99  const replayQueue = [];
100  const maxReplayBufferedEvents = Math.max(maxReplayEventsPerChunk, maxReplayEventsPerChunk * maxReplayChunks);
101  const replayMaskTextSelector = [
102    "[data-lovable-mask]",
103    "[data-sensitive]",
104    "[data-private]",
105    "[data-auth]",
106    "[data-payment]",
107    "[contenteditable]",
108    "[autocomplete]",
109    "[name*='email' i]",
110    "[name*='phone' i]",
111    "[name*='address' i]",
112    "[name*='token' i]",
113    "[name*='secret' i]"
114  ].join(",");
115  const replayBlockSelector = [
116    "canvas",
117    "embed",
118    "iframe",
119    "object",
120    "video",
121    "[data-lovable-block]",
122    ".rr-block",
123    "[data-payment]",
124    "[data-auth]",
125    "[data-private]"
126  ].join(",");
127  const replayIgnoreSelector = "[data-lovable-ignore],.rrweb-ignore";
128  const maskReplayText = /* @__PURE__ */ __name((text) => text.replace(/[^\s]/gu, "*"), "maskReplayText");
129  const queue = [];
130  let flushTimer = null;
131  let sendingDisabled = false;
132  let sendRejectionReported = false;
133  let sendDisabledReported = false;
134  let unloadingPage = false;
135  let immediateErrorFlushes = 0;
136  let errorEventsSeen = 0;
137  const batchSize = Math.max(1, maxEventsPerBatch);
138  const maxImmediateErrorFlushes = 5;
139  const maxErrorEventsPerPageLoad = 200;
140  const projectEventNamePattern = /^project\.[a-z][a-z0-9_]{0,63}(\.[a-z][a-z0-9_]{0,63})*$/u;
141  const schemaVersionFor = /* @__PURE__ */ __name((eventName) => {
142    const schemaVersion = schemaVersions[eventName];
143    if (typeof schemaVersion === "number" && schemaVersion > 0) return schemaVersion;
144    return projectEventNamePattern.test(eventName) ? 1 : null;
145  }, "schemaVersionFor");
146  const textEncoder = typeof TextEncoder === "function" ? new TextEncoder() : null;
147  const byteLength = /* @__PURE__ */ __name((value) => textEncoder ? textEncoder.encode(value).length : value.length, "byteLength");
148  const createBatchBody = /* @__PURE__ */ __name((events) => JSON.stringify({
149    envelope_schema_version: 1,
150    batch_id: randomId(),
151    sent_at: (/* @__PURE__ */ new Date()).toISOString(),
152    sdk: { name: "lovable-user-app-events", version },
153    artifact,
154    events
155  }), "createBatchBody");
156  const report = /* @__PURE__ */ __name((message3) => {
157    const consoleRef = w.console;
158    if (consoleRef && typeof consoleRef.debug === "function") consoleRef.debug(`[lovable-analytics] ${message3}`);
159  }, "report");
160  const applyRejectedBatchStatus = /* @__PURE__ */ __name((status, routeMarker) => {
161    if ((status === 403 || status === 404) && routeMarker === "rejected") {
162      sendingDisabled = true;
163      queue.length = 0;
164      if (!sendDisabledReported) {
165        sendDisabledReported = true;
166        report(`events disabled for this page load (HTTP ${status})`);
167      }
168      return;
169    }
170    if (sendRejectionReported) return;
171    sendRejectionReported = true;
172    report(`event batch rejected (HTTP ${status})`);
173  }, "applyRejectedBatchStatus");
174  const keepalivePoolBytes = 64 * 1024;
175  const eventsKeepaliveReserveBytes = 16 * 1024;
176  let keepaliveBytesInFlight = 0;
177  let beaconBytesInFlight = 0;
178  const releaseBeaconBytes = /* @__PURE__ */ __name(() => {
179    keepaliveBytesInFlight -= beaconBytesInFlight;
180    beaconBytesInFlight = 0;
181  }, "releaseBeaconBytes");
182  const send = /* @__PURE__ */ __name((url, body, options) => {
183    const bodyBytes = byteLength(body);
184    const keepaliveLimitBytes = keepalivePoolBytes - (options.reserveBytes || 0);
185    const allowKeepalive = !!options.keepalive && keepaliveBytesInFlight + bodyBytes <= keepaliveLimitBytes;
186    if (allowKeepalive) keepaliveBytesInFlight += bodyBytes;
187    let charged = allowKeepalive;
188    const release = /* @__PURE__ */ __name(() => {
189      if (!charged) return;
190      charged = false;
191      keepaliveBytesInFlight -= bodyBytes;
192    }, "release");
193    const BlobCtor = w.Blob || (typeof Blob === "function" ? Blob : null);
194    if (allowKeepalive && options.beacon && nav.sendBeacon && BlobCtor) {
195      try {
196        const ok = nav.sendBeacon(url, new BlobCtor([body], { type: "application/json" }));
197        if (ok) {
198          beaconBytesInFlight += bodyBytes;
199          charged = false;
200          return;
201        }
202      } catch {
203      }
204    }
205    const fetchFn = w.fetch || (typeof fetch === "function" ? fetch : null);
206    if (!fetchFn) {
207      release();
208      options.onNetworkError?.();
209      return;
210    }
211    try {
212      Promise.resolve(
213        fetchFn(url, {
214          method: "POST",
215          headers: { "content-type": "application/json" },
216          body,
217          ...allowKeepalive ? { keepalive: true } : {}
218        })
219      ).then(
220        (response) => {
221          release();
222          options.onResponse?.(response);
223        },
224        () => {
225          release();
226          options.onNetworkError?.();
227        }
228      ).catch(() => {
229      });
230    } catch {
231      release();
232      options.onNetworkError?.();
233    }
234  }, "send");
235  const sendBatch = /* @__PURE__ */ __name((allEvents, unloading) => {
236    const events = allEvents.filter((event) => {
237      try {
238        JSON.stringify(event);
239        return true;
240      } catch {
241        return false;
242      }
243    });
244    let start = 0;
245    while (start < events.length) {
246      let end = events.length;
247      let body = createBatchBody(events.slice(start, end));
248      const remaining = keepalivePoolBytes - keepaliveBytesInFlight;
249      const splitLimit = unloading && remaining >= eventsKeepaliveReserveBytes ? Math.min(maxBatchBodyBytes, remaining) : maxBatchBodyBytes;
250      while (end - start > 1 && byteLength(body) > splitLimit) {
251        end = start + Math.max(1, Math.floor((end - start) / 2));
252        body = createBatchBody(events.slice(start, end));
253      }
254      send(trackUrl, body, {
255        keepalive: true,
256        beacon: unloading,
257        onResponse: /* @__PURE__ */ __name((response) => {
258          if (!response || response.ok !== false) return;
259          applyRejectedBatchStatus(response.status, response.headers?.get?.("x-lovable-events") ?? null);
260        }, "onResponse")
261      });
262      start = end;
263    }
264  }, "sendBatch");
265  const createReplayChunkBody = /* @__PURE__ */ __name((events, chunkID, chunkSequence) => {
266    if (!replayId) return null;
267    return JSON.stringify({
268      envelope_schema_version: 1,
269      replay_schema_version: 1,
270      replay_id: replayId,
271      chunk_id: chunkID,
272      chunk_sequence: chunkSequence,
273      sent_at: (/* @__PURE__ */ new Date()).toISOString(),
274      sdk: { name: "lovable-user-app-events", version },
275      artifact,
276      session_id: sessionId,
277      page_view_id: pageViewId,
278      events
279    });
280  }, "createReplayChunkBody");
281  const canSendReplayEvent = /* @__PURE__ */ __name((event) => {
282    const body = createReplayChunkBody([event], "00000000-0000-4000-8000-000000000000", replayChunkSequence);
283    return !!body && byteLength(body) <= maxReplayBodyBytes;
284  }, "canSendReplayEvent");
285  const replayEventType = /* @__PURE__ */ __name((event) => {
286    const record = event && typeof event === "object" ? event : null;
287    return typeof record?.type === "number" ? record.type : null;
288  }, "replayEventType");
289  const replayDurationMs = /* @__PURE__ */ __name(() => Math.max(0, perf.now() - replayStartedAt), "replayDurationMs");
290  const clearReplayFlushTimer = /* @__PURE__ */ __name(() => {
291    if (!replayFlushTimer) return;
292    const clearTimer = resolveClearTimer();
293    if (clearTimer) clearTimer(replayFlushTimer);
294    replayFlushTimer = null;
295  }, "clearReplayFlushTimer");
296  const clearReplayDurationTimer = /* @__PURE__ */ __name(() => {
297    if (!replayDurationTimer) return;
298    const clearTimer = resolveClearTimer();
299    if (clearTimer) clearTimer(replayDurationTimer);
300    replayDurationTimer = null;
301  }, "clearReplayDurationTimer");
302  const stopReplayRecorderSafely = /* @__PURE__ */ __name(() => {
303    try {
304      stopReplayRecorder?.();
305    } catch {
306    } finally {
307      stopReplayRecorder = null;
308      replayRecorderActive = false;
309    }
310  }, "stopReplayRecorderSafely");
311  const replayErrorStageFor = /* @__PURE__ */ __name((reason) => {
312    switch (reason) {
313      case "recorder_script_failed":
314        return "recorder_script";
315      case "recorder_start_failed":
316        return "recorder_start";
317      case "recorder_runtime_error":
318        return "recorder_runtime";
319      case "missing_full_snapshot":
320      case "full_snapshot_too_large":
321        return "snapshot";
322      case "upload_failed":
323        return "upload";
324      default:
325        return null;
326    }
327  }, "replayErrorStageFor");
328  const emitReplayErrorFor = /* @__PURE__ */ __name((targetReplayId, reason, eventCount, durationMs, chunkCount, uploadedBytes) => {
329    const stage = replayErrorStageFor(reason);
330    if (!stage) return;
331    enqueue("lovable.replay_error", {
332      replay_id: targetReplayId,
333      reason,
334      stage,
335      recorder: "rrweb",
336      sample_rate: replayEffectiveSampleRate,
337      event_count: eventCount,
338      duration_ms: durationMs,
339      chunk_count: chunkCount,
340      uploaded_bytes: uploadedBytes
341    });
342  }, "emitReplayErrorFor");
343  const replayCaptureProperties = /* @__PURE__ */ __name(() => !replayCaptureReason || replayCaptureReason === "sampled" ? {} : {
344    capture_reason: replayCaptureReason,
345    trigger_event_id: replayTriggerEventId,
346    trigger_event_name: replayTriggerEventName
347  }, "replayCaptureProperties");
348  const emitReplayMetadataFor = /* @__PURE__ */ __name((targetReplayId, status, reason, eventCount, durationMs, chunkCount, uploadedBytes) => {
349    enqueue("lovable.session_replay", {
350      replay_id: targetReplayId,
351      status,
352      reason,
353      recorder: "rrweb",
354      sample_rate: replayEffectiveSampleRate,
355      event_count: eventCount,
356      duration_ms: durationMs,
357      chunk_count: chunkCount,
358      uploaded_bytes: uploadedBytes,
359      ...replayCaptureProperties()
360    });
361    emitReplayErrorFor(targetReplayId, reason, eventCount, durationMs, chunkCount, uploadedBytes);
362  }, "emitReplayMetadataFor");
363  const emitReplayMetadata = /* @__PURE__ */ __name((status, reason) => {
364    if (!replayId) return;
365    emitReplayMetadataFor(
366      replayId,
367      status,
368      reason,
369      replayEventCount,
370      replayDurationMs(),
371      replayChunkSequence,
372      replayUploadedBytes
373    );
374  }, "emitReplayMetadata");
375  const shouldEmitReplayLifecycleMetadata = /* @__PURE__ */ __name(() => replayUploadEnabled || replayCaptureReason === "sampled", "shouldEmitReplayLifecycleMetadata");
376  const emitReplayMetadataOrErrorFor = /* @__PURE__ */ __name((targetReplayId, status, reason, eventCount, durationMs, chunkCount, uploadedBytes) => {
377    if (shouldEmitReplayLifecycleMetadata()) {
378      emitReplayMetadataFor(targetReplayId, status, reason, eventCount, durationMs, chunkCount, uploadedBytes);
379    } else {
380      emitReplayErrorFor(targetReplayId, reason, eventCount, durationMs, chunkCount, uploadedBytes);
381    }
382  }, "emitReplayMetadataOrErrorFor");
383  const stopReplayRecording = /* @__PURE__ */ __name((reason, dropQueuedEvents = false) => {
384    if (!replayId || replayFinalized) return;
385    stopReplayRecorderSafely();
386    clearReplayDurationTimer();
387    if (dropQueuedEvents) {
388      replayQueue.length = 0;
389      clearReplayFlushTimer();
390    }
391    if (shouldEmitReplayLifecycleMetadata()) {
392      emitReplayMetadata("stopped", reason);
393    }
394    replayFinalized = true;
395  }, "stopReplayRecording");
396  const stopReplayAtDurationLimit = /* @__PURE__ */ __name(() => {
397    if (!replayId || replayFinalized) return;
398    if (!replayUploadEnabled) {
399      stopReplayRecorderSafely();
400      clearReplayDurationTimer();
401      return;
402    }
403    stopReplayRecording("duration_limit", true);
404  }, "stopReplayAtDurationLimit");
405  const markReplayCapturedByClientError = /* @__PURE__ */ __name((triggerEventId) => {
406    if (!replayId || replayFinalized || replayCaptureReason === "client_error") return;
407    replayUploadEnabled = true;
408    replayCaptureReason = "client_error";
409    replayTriggerEventId = triggerEventId;
410    replayTriggerEventName = "lovable.client_error";
411    emitReplayCaptureMetadataIfReady();
412  }, "markReplayCapturedByClientError");
413  const hasReplayDataToCapture = /* @__PURE__ */ __name(() => replayQueue.length > 0 || replayChunkSequence > 0 || replayUploadedBytes > 0, "hasReplayDataToCapture");
414  const emitReplayCaptureMetadataIfReady = /* @__PURE__ */ __name(() => {
415    if (!replayId || replayFinalized || replayCaptureReason !== "client_error" || replayCaptureMetadataEmitted || !replayHasFullSnapshot || !hasReplayDataToCapture()) {
416      return false;
417    }
418    replayCaptureMetadataEmitted = true;
419    emitReplayMetadata("captured", "client_error");
420    flushReplay(true);
421    return true;
422  }, "emitReplayCaptureMetadataIfReady");
423  const endReplayOnSpentBudget = /* @__PURE__ */ __name((reason) => {
424    if (replayChunksInFlight > 0) {
425      replayBudgetStopReason = reason;
426      return;
427    }
428    stopReplayRecording(reason, true);
429  }, "endReplayOnSpentBudget");
430  const settleReplayChunk = /* @__PURE__ */ __name(() => {
431    replayChunksInFlight--;
432    if (replayFinalized || replayChunksInFlight > 0 || !replayBudgetStopReason) return;
433    const reason = replayBudgetStopReason;
434    replayBudgetStopReason = null;
435    if (replayThrottledChunks > 0) scheduleReplayFlush();
436    else stopReplayRecording(reason, true);
437  }, "settleReplayChunk");
438  const flushReplay = /* @__PURE__ */ __name((preferKeepalive = false) => {
439    clearReplayFlushTimer();
440    if (!replayUploadEnabled || !replayId || replayQueue.length === 0 || !replayHasFullSnapshot) return;
441    while (!replayFinalized && replayQueue.length > 0) {
442      let count2 = Math.min(replayQueue.length, maxReplayEventsPerChunk);
443      const chunkID = randomId();
444      const chunkSequence = replayChunkSequence;
445      let body = createReplayChunkBody(replayQueue.slice(0, count2), chunkID, chunkSequence);
446      while (body && count2 > 1 && byteLength(body) > maxReplayBodyBytes) {
447        count2 = Math.max(1, Math.floor(count2 / 2));
448        body = createReplayChunkBody(replayQueue.slice(0, count2), chunkID, chunkSequence);
449      }
450      if (!body) return;
451      const bodyBytes = byteLength(body);
452      if (bodyBytes > maxReplayBodyBytes) {
453        replayQueue.shift();
454        replayEventCount = Math.max(0, replayEventCount - 1);
455        continue;
456      }
457      const chunksStored = replayChunkSequence - replayThrottledChunks;
458      if (chunksStored >= maxReplayChunks || replayUploadedBytes + bodyBytes > maxReplayTotalBytes) {
459        endReplayOnSpentBudget(chunksStored >= maxReplayChunks ? "chunk_limit" : "size_limit");
460        return;
461      }
462      replayQueue.splice(0, count2);
463      replayChunkSequence++;
464      replayUploadedBytes += bodyBytes;
465      replayChunksInFlight++;
466      send(replayUrl, body, {
467        keepalive: preferKeepalive,
468        reserveBytes: eventsKeepaliveReserveBytes,
469        onResponse: /* @__PURE__ */ __name((response) => {
470          const throttled = response?.status === 429;
471          if (throttled) {
472            replayThrottledChunks++;
473            replayUploadedBytes -= bodyBytes;
474            replayEventCount = Math.max(0, replayEventCount - count2);
475          }
476          settleReplayChunk();
477          if (throttled || response && response.ok !== false) return;
478          const marker = response?.headers?.get?.("x-lovable-events");
479          const reason = marker === "unavailable" ? "upload_unavailable" : marker === "rejected" ? "upload_rejected" : "upload_failed";
480          stopReplayRecording(reason, true);
481        }, "onResponse"),
482        onNetworkError: /* @__PURE__ */ __name(() => {
483          stopReplayRecording("upload_failed", true);
484          settleReplayChunk();
485        }, "onNetworkError")
486      });
487      if (replayFinalized) return;
488      if (replayChunkSequence - replayThrottledChunks >= maxReplayChunks) {
489        endReplayOnSpentBudget("chunk_limit");
490        return;
491      }
492    }
493  }, "flushReplay");
494  const scheduleReplayFlush = /* @__PURE__ */ __name(() => {
495    if (replayFlushTimer) return;
496    const setTimer = resolveSetTimer();
497    if (!setTimer) {
498      flushReplay();
499      return;
500    }
501    replayFlushTimer = setTimer(flushReplay, replayFlushIntervalMs);
502  }, "scheduleReplayFlush");
503  const clearFlushTimer = /* @__PURE__ */ __name(() => {
504    if (!flushTimer) return;
505    const clearTimer = resolveClearTimer();
506    if (clearTimer) clearTimer(flushTimer);
507    flushTimer = null;
508  }, "clearFlushTimer");
509  const flush = /* @__PURE__ */ __name((unloading = false) => {
510    clearFlushTimer();
511    if (sendingDisabled || queue.length === 0) return;
512    const events = queue.splice(0, batchSize);
513    sendBatch(events, unloading || unloadingPage);
514    if (queue.length > 0) scheduleFlush();
515  }, "flush");
516  const scheduleFlush = /* @__PURE__ */ __name(() => {
517    if (flushTimer || sendingDisabled) return;
518    const setTimer = resolveSetTimer();
519    if (!setTimer) {
520      flush();
521      return;
522    }
523    flushTimer = setTimer(flush, 250);
524  }, "scheduleFlush");
525  const startEngagementSegment = /* @__PURE__ */ __name(() => {
526    if (engagementSegmentStart === null) engagementSegmentStart = perf.now();
527  }, "startEngagementSegment");
528  const stopEngagementSegment = /* @__PURE__ */ __name(() => {
529    engagementSegmentStart = null;
530  }, "stopEngagementSegment");
531  const clearEngagementTimer = /* @__PURE__ */ __name(() => {
532    const clearTimer = resolveClearTimer();
533    if (engagementTimer !== null && clearTimer) clearTimer(engagementTimer);
534    engagementTimer = null;
535  }, "clearEngagementTimer");
536  const takeEngagementDelta = /* @__PURE__ */ __name(() => {
537    if (engagementSegmentStart !== null) {
538      const nowMs = perf.now();
539      engagementAccruedMs += nowMs - engagementSegmentStart;
540      engagementSegmentStart = nowMs;
541    }
542    const delta = Math.round(engagementAccruedMs);
543    engagementAccruedMs = 0;
544    return delta;
545  }, "takeEngagementDelta");
546  const emitEngagement = /* @__PURE__ */ __name((reason) => {
547    const delta = takeEngagementDelta();
548    if (delta <= 0) return;
549    engagementReportedMs += delta;
550    const capping = engagementReportedMs >= ENGAGEMENT_ACCRUAL_STOP_MS;
551    if (capping) {
552      engagementCapped = true;
553      stopEngagementSegment();
554      clearEngagementTimer();
555    }
556    enqueue("lovable.page_engagement", { reason: capping ? "capped" : reason, engagement_time_ms: delta });
557  }, "emitEngagement");
558  const scheduleEngagementEmit = /* @__PURE__ */ __name(() => {
559    clearEngagementTimer();
560    const setTimer = resolveSetTimer();
561    if (!setTimer) return;
562    const delay = ENGAGEMENT_EMIT_DELAYS_MS[Math.min(engagementEmitStep, ENGAGEMENT_EMIT_DELAYS_MS.length - 1)];
563    engagementTimer = setTimer(() => {
564      engagementEmitStep++;
565      emitEngagement("threshold");
566      if (engagementCapped) return;
567      scheduleEngagementEmit();
568    }, delay);
569  }, "scheduleEngagementEmit");
570  const clearUnfocusedTimer = /* @__PURE__ */ __name(() => {
571    const clearTimer = resolveClearTimer();
572    if (engagementUnfocusedTimer !== null) {
573      if (clearTimer) clearTimer(engagementUnfocusedTimer);
574      engagementUnfocusedSpentMs += Math.max(0, perf.now() - engagementUnfocusedArmedAt);
575    }
576    engagementUnfocusedTimer = null;
577  }, "clearUnfocusedTimer");
578  const resetUnfocusedGrace = /* @__PURE__ */ __name(() => {
579    clearUnfocusedTimer();
580    engagementUnfocusedSpentMs = 0;
581  }, "resetUnfocusedGrace");
582  const scheduleUnfocusedStop = /* @__PURE__ */ __name(() => {
583    if (engagementUnfocusedTimer !== null) return;
584    if (!d || d.visibilityState === "hidden" || d.hasFocus?.() !== false) return;
585    const setTimer = resolveSetTimer();
586    if (!setTimer) return;
587    engagementUnfocusedArmedAt = perf.now();
588    engagementUnfocusedTimer = setTimer(
589      () => {
590        engagementUnfocusedTimer = null;
591        engagementUnfocusedSpentMs = ENGAGEMENT_UNFOCUSED_GRACE_MS;
592        engagementStoppedUnfocused = true;
593        emitEngagement("unfocused");
594        stopEngagementSegment();
595        clearEngagementTimer();
596      },
597      Math.max(0, ENGAGEMENT_UNFOCUSED_GRACE_MS - engagementUnfocusedSpentMs)
598    );
599  }, "scheduleUnfocusedStop");
600  const resumeEngagementMeasurement = /* @__PURE__ */ __name(() => {
601    if (d && d.visibilityState === "hidden") return;
602    if (engagementStoppedUnfocused) return;
603    if (engagementCapped) return;
604    startEngagementSegment();
605    engagementEmitStep = 0;
606    scheduleEngagementEmit();
607    scheduleUnfocusedStop();
608  }, "resumeEngagementMeasurement");
609  const rotatePageViewId = /* @__PURE__ */ __name(() => {
610    emitEngagement("navigation");
611    engagementReportedMs = 0;
612    engagementCapped = false;
613    resumeEngagementMeasurement();
614    pageViewId = randomId();
615    traceContext = createTraceContext();
616    capturedClicks = 0;
617    return pageViewId;
618  }, "rotatePageViewId");
619  const traceFieldsFor = /* @__PURE__ */ __name((eventName) => {
620    if (eventName === "lovable.page_viewed") {
621      return {
622        trace_id: traceContext.traceId,
623        span_id: traceContext.rootSpanId
624      };
625    }
626    return {
627      trace_id: traceContext.traceId,
628      span_id: randomTraceHex(8),
629      parent_span_id: traceContext.rootSpanId
630    };
631  }, "traceFieldsFor");
632  const isErrorEventName = /* @__PURE__ */ __name((eventName) => eventName === "lovable.client_error" || eventName === "lovable.resource_error" || eventName === "lovable.replay_error", "isErrorEventName");
633  const queueEvent = /* @__PURE__ */ __name((eventName, eventSchemaVersion, properties = {}) => {
634    if (sendingDisabled) return null;
635    if (isErrorEventName(eventName) && ++errorEventsSeen > maxErrorEventsPerPageLoad) {
636      if (eventName === "lovable.client_error") markReplayCapturedByClientError(null);
637      return null;
638    }
639    const flushNow = eventName === "lovable.client_error" && immediateErrorFlushes < maxImmediateErrorFlushes;
640    if (flushNow) immediateErrorFlushes++;
641    const eventId = randomId();
642    const activeReplayId = replayId && !replayFinalized && (replayUploadEnabled || eventName === "lovable.client_error") ? replayId : null;
643    const event = {
644      event_id: eventId,
645      event_name: eventName,
646      event_schema_version: eventSchemaVersion,
647      event_time: (/* @__PURE__ */ new Date()).toISOString(),
648      session_id: sessionId,
649      page_view_id: pageViewId,
650      ...traceFieldsFor(eventName),
651      properties
652    };
653    if (activeReplayId) event.replay_id = activeReplayId;
654    if (flushNow) {
655      queue.unshift(event);
656    } else {
657      queue.push(event);
658    }
659    if (eventName === "lovable.client_error") markReplayCapturedByClientError(eventId);
660    if (flushNow || queue.length >= batchSize) {
661      flush();
662    } else {
663      scheduleFlush();
664    }
665    return eventId;
666  }, "queueEvent");
667  const enqueue = /* @__PURE__ */ __name((eventName, properties = {}) => {
668    const eventSchemaVersion = typeof eventName === "string" ? schemaVersionFor(eventName) : null;
669    if (eventSchemaVersion === null) return null;
670    return queueEvent(eventName, eventSchemaVersion, properties);
671  }, "enqueue");
672  let lastPageViewPath = loc.pathname;
673  const track = /* @__PURE__ */ __name((eventName, properties = {}) => {
674    if (eventName === "lovable.page_viewed") {
675      rotatePageViewId();
676      if (typeof properties.url_path === "string") lastPageViewPath = properties.url_path;
677    }
678    return enqueue(eventName, properties);
679  }, "track");
680  const pathFromUrl = /* @__PURE__ */ __name((value) => {
681    try {
682      return new URL(value, loc.href).pathname;
683    } catch {
684      return null;
685    }
686  }, "pathFromUrl");
687  const hostFromUrl = /* @__PURE__ */ __name((value) => {
688    try {
689      return new URL(value, loc.href).host.replace(/^www\./iu, "");
690    } catch {
691      return null;
692    }
693  }, "hostFromUrl");
694  const truncateToJSONBytes = /* @__PURE__ */ __name((input, maxBytes) => {
695    let result = input;
696    while (result && byteLength(JSON.stringify(result)) > maxBytes) {
697      result = result.slice(0, Math.floor(result.length * 0.8));
698    }
699    return result;
700  }, "truncateToJSONBytes");
701  const serializeThrowable = /* @__PURE__ */ __name((value) => {
702    const throwable = value && typeof value === "object" ? value : null;
703    const stringifyPrimitive = /* @__PURE__ */ __name((input) => {
704      if (typeof input === "string") return input;
705      if (typeof input === "number" || typeof input === "boolean" || typeof input === "bigint") return String(input);
706      return null;
707    }, "stringifyPrimitive");
708    const stack = stringifyPrimitive(throwable?.stack);
709    const message3 = stringifyPrimitive(throwable?.message) ?? stringifyPrimitive(value) ?? "Unknown error";
710    return {
711      message: truncateToJSONBytes(message3.slice(0, 2e3), 2048),
712      name: stringifyPrimitive(throwable?.name),
713      stack: stack ? truncateToJSONBytes(stack.slice(0, 8e3), 8192) : null
714    };
715  }, "serializeThrowable");
716  const emitPageView = /* @__PURE__ */ __name((navigationType, rotatePageView = true) => {
717    if (rotatePageView) rotatePageViewId();
718    const pagePath = loc.pathname;
719    const properties = {
720      url_path: pagePath,
721      navigation_type: navigationType
722    };
723    if (typeof d?.referrer === "string" && d.referrer) {
724      properties.referrer_host = hostFromUrl(d.referrer) ?? "";
725      properties.referrer_path = pathFromUrl(d.referrer) ?? "";
726    }
727    lastPageViewPath = pagePath;
728    enqueue("lovable.page_viewed", properties);
729  }, "emitPageView");
730  const emitPageViewIfPathChanged = /* @__PURE__ */ __name((navigationType) => {
731    if (loc.pathname === lastPageViewPath) return;
732    emitPageView(navigationType);
733  }, "emitPageViewIfPathChanged");
734  const startReplayRecording = /* @__PURE__ */ __name(() => {
735    const record = replayMode === "rrweb" && isReplayRecordingAllowed && w.rrweb && typeof w.rrweb.record === "function" ? w.rrweb.record : null;
736    if (!record || replayId) return;
737    replayId = randomId();
738    replayStartedAt = perf.now();
739    replayFinalized = false;
740    replayChunkSequence = 0;
741    replayThrottledChunks = 0;
742    replayChunksInFlight = 0;
743    replayBudgetStopReason = null;
744    replayUploadedBytes = 0;
745    replayRecorderActive = false;
746    replayHasFullSnapshot = false;
747    replayUploadEnabled = isReplaySampledIn;
748    replayCaptureReason = isReplaySampledIn ? "sampled" : null;
749    replayTriggerEventId = null;
750    replayTriggerEventName = null;
751    replayCaptureMetadataEmitted = false;
752    try {
753      const stop = record({
754        emit(event) {
755          if (replayFinalized) return;
756          if (!event || typeof event !== "object") return;
757          if (replayDurationMs() >= maxReplayDurationMs) {
758            stopReplayAtDurationLimit();
759            return;
760          }
761          const eventType = replayEventType(event);
762          if (!canSendReplayEvent(event)) {
763            if (eventType === 2 && !replayHasFullSnapshot) {
764              stopReplayRecording("full_snapshot_too_large", true);
765            }
766            return;
767          }
768          if (eventType === 3 && !replayHasFullSnapshot) return;
769          if (eventType === 2) replayHasFullSnapshot = true;
770          replayEventCount += 1;
771          replayQueue.push(event);
772          while (!replayUploadEnabled && replayQueue.length > maxReplayBufferedEvents) {
773            const removableIndex = replayQueue.findIndex((queuedEvent) => replayEventType(queuedEvent) !== 2);
774            replayQueue.splice(removableIndex >= 0 ? removableIndex : 0, 1);
775            replayEventCount = Math.max(0, replayEventCount - 1);
776          }
777          if (emitReplayCaptureMetadataIfReady()) return;
778          if (!replayUploadEnabled) return;
779          if (replayQueue.length >= maxReplayEventsPerChunk) {
780            flushReplay();
781          } else {
782            scheduleReplayFlush();
783          }
784        },
785        blockClass: "rr-block",
786        blockSelector: replayBlockSelector,
787        collectFonts: false,
788        ignoreClass: "rrweb-ignore",
789        ignoreSelector: replayIgnoreSelector,
790        inlineImages: false,
791        maskAllInputs: true,
792        maskInputFn: maskReplayText,
793        maskInputOptions: {
794          checkbox: false,
795          color: false,
796          date: true,
797          "datetime-local": true,
798          email: true,
799          month: true,
800          number: true,
801          password: true,
802          radio: false,
803          range: false,
804          search: true,
805          select: true,
806          tel: true,
807          text: true,
808          textarea: true,
809          time: true,
810          url: true,
811          week: true
812        },
813        maskTextClass: "rr-mask",
814        maskTextFn: maskReplayText,
815        maskTextSelector: replayMaskTextSelector,
816        recordAfter: "load",
817        recordCanvas: false,
818        recordCrossOriginIframes: false
819      });
820      if (replayFinalized) {
821        if (typeof stop === "function") {
822          try {
823            stop();
824          } catch {
825          }
826        }
827        return;
828      }
829      replayRecorderActive = true;
830      if (typeof stop === "function") stopReplayRecorder = stop;
831      const setTimer = resolveSetTimer();
832      if (setTimer && typeof record.takeFullSnapshot === "function") {
833        setTimer(() => {
834          if (!replayId || replayFinalized) return;
835          try {
836            record.takeFullSnapshot?.(false);
837          } catch {
838          }
839        }, 1e3);
840      }
841      if (setTimer) {
842        replayDurationTimer = setTimer(stopReplayAtDurationLimit, maxReplayDurationMs);
843      }
844      if (shouldEmitReplayLifecycleMetadata()) {
845        emitReplayMetadata("started", "recorder_available");
846      }
847    } catch {
848      if (replayId) {
849        emitReplayMetadataOrErrorFor(
850          replayId,
851          "stopped",
852          "recorder_start_failed",
853          replayEventCount,
854          replayDurationMs(),
855          replayChunkSequence,
856          replayUploadedBytes
857        );
858      }
859      replayId = null;
860      replayEventCount = 0;
861      replayStartedAt = 0;
862      replayFinalized = false;
863      replayHasFullSnapshot = false;
864      replayUploadEnabled = isReplaySampledIn;
865      replayCaptureReason = isReplaySampledIn ? "sampled" : null;
866      replayTriggerEventId = null;
867      replayTriggerEventName = null;
868      stopReplayRecorder = null;
869      replayRecorderActive = false;
870      clearReplayDurationTimer();
871    }
872  }, "startReplayRecording");
873  const ensureReplayRecorder = /* @__PURE__ */ __name(() => {
874    if (replayMode !== "rrweb" || !isReplayRecordingAllowed || replayId || replayRecorderLoading) return;
875    if (w.rrweb && typeof w.rrweb.record === "function") {
876      startReplayRecording();
877      return;
878    }
879    const head = d?.head;
880    if (!d?.createElement || !head?.appendChild) return;
881    const recorderScript = d.createElement("script");
882    replayRecorderLoading = true;
883    recorderScript.async = true;
884    recorderScript.src = replayScriptUrl;
885    recorderScript.onload = () => {
886      replayRecorderLoading = false;
887      if (!w.rrweb || typeof w.rrweb.record !== "function") {
888        emitReplayMetadataOrErrorFor(randomId(), "stopped", "recorder_script_failed", 0, 0, 0, 0);
889        return;
890      }
891      startReplayRecording();
892    };
893    recorderScript.onerror = () => {
894      replayRecorderLoading = false;
895      emitReplayMetadataOrErrorFor(randomId(), "stopped", "recorder_script_failed", 0, 0, 0, 0);
896    };
897    head.appendChild(recorderScript);
898  }, "ensureReplayRecorder");
899  const finalizeReplayRecording = /* @__PURE__ */ __name((reason) => {
900    if (!replayId || replayFinalized) return;
901    stopReplayRecorderSafely();
902    clearReplayDurationTimer();
903    if (!replayHasFullSnapshot) {
904      stopReplayRecording("missing_full_snapshot", true);
905      return;
906    }
907    flushReplay(true);
908    if (!replayFinalized) stopReplayRecording(reason);
909  }, "finalizeReplayRecording");
910  const stopReplayRecordingOnRuntimeError = /* @__PURE__ */ __name((event) => {
911    if (!replayId || replayFinalized) return;
912    const errorEvent = event && typeof event === "object" ? event : null;
913    const errorRecord = errorEvent?.error && typeof errorEvent.error === "object" ? errorEvent.error : null;
914    const filename = typeof errorEvent?.filename === "string" ? errorEvent.filename : "";
915    const stack = typeof errorRecord?.stack === "string" ? errorRecord.stack : "";
916    const isRecorderError = filename.includes("/__l5e/rrweb-record") || stack.includes("/__l5e/rrweb-record");
917    if (isRecorderError) {
918      stopReplayRecording("recorder_runtime_error", true);
919    }
920  }, "stopReplayRecordingOnRuntimeError");
921  const webVitalRating = /* @__PURE__ */ __name((metric, value) => {
922    const thresholds = {
923      CLS: [0.1, 0.25],
924      FCP: [1800, 3e3],
925      INP: [200, 500],
926      LCP: [2500, 4e3],
927      TTFB: [800, 1800]
928    };
929    const threshold = thresholds[metric];
930    if (!threshold) return "good";
931    if (value <= threshold[0]) return "good";
932    return value <= threshold[1] ? "needs-improvement" : "poor";
933  }, "webVitalRating");
934  const webVitalUnit = /* @__PURE__ */ __name((metric) => metric === "CLS" ? "score" : "millisecond", "webVitalUnit");
935  const emitWebVital = /* @__PURE__ */ __name((metric, value, delta = value, id2 = randomId()) => {
936    if (!Number.isFinite(value) || value < 0) return;
937    enqueue("lovable.web_vital", {
938      metric,
939      value,
940      unit: webVitalUnit(metric),
941      delta,
942      rating: webVitalRating(metric, value),
943      id: id2,
944      navigation_type: "initial_load"
945    });
946  }, "emitWebVital");
947  const performanceEntryValue = /* @__PURE__ */ __name((entry, key) => {
948    const value = entry[key];
949    return typeof value === "number" && Number.isFinite(value) ? value : null;
950  }, "performanceEntryValue");
951  const observePerformanceEntries = /* @__PURE__ */ __name((entryType, callback) => {
952    const ObserverCtor = w.PerformanceObserver;
953    if (!ObserverCtor) return false;
954    const supportedEntryTypes = ObserverCtor.supportedEntryTypes;
955    if (Array.isArray(supportedEntryTypes) && !supportedEntryTypes.includes(entryType)) return false;
956    try {
957      const observer = new ObserverCtor((list) => {
958        const entries = typeof list.getEntries === "function" ? list.getEntries() : [];
959        for (const entry of entries) {
960          if (entry && typeof entry === "object") callback(entry);
961        }
962      });
963      observer.observe(
964        entryType === "event" ? { type: entryType, buffered: true, durationThreshold: 40 } : { type: entryType, buffered: true }
965      );
966      return true;
967    } catch {
968      return false;
969    }
970  }, "observePerformanceEntries");
971  const observeWebVitals = /* @__PURE__ */ __name(() => {
972    let fcpReported = false;
973    let latestLcp = null;
974    let clsValue = 0;
975    let maxInp = 0;
976    let finalized = false;
977    const metricIds = {};
978    const reportedValues = {};
979    const metricId = /* @__PURE__ */ __name((metric) => {
980      metricIds[metric] = metricIds[metric] || randomId();
981      return metricIds[metric];
982    }, "metricId");
983    const reportMetric = /* @__PURE__ */ __name((metric, value) => {
984      if (reportedValues[metric] === value) return;
985      const previousValue = reportedValues[metric];
986      reportedValues[metric] = value;
987      emitWebVital(metric, value, previousValue === void 0 ? value : value - previousValue, metricId(metric));
988    }, "reportMetric");
989    const navigationEntries = typeof perf.getEntriesByType === "function" ? perf.getEntriesByType("navigation") : [];
990    const navigationEntry = navigationEntries[0] && typeof navigationEntries[0] === "object" ? navigationEntries[0] : null;
991    const responseStart = navigationEntry ? performanceEntryValue(navigationEntry, "responseStart") : null;
992    const requestStart = navigationEntry ? performanceEntryValue(navigationEntry, "requestStart") : null;
993    const fetchStart = navigationEntry ? performanceEntryValue(navigationEntry, "fetchStart") : null;
994    const ttfbStart = requestStart ?? fetchStart;
995    if (responseStart !== null && ttfbStart !== null && responseStart >= ttfbStart) {
996      emitWebVital("TTFB", responseStart - ttfbStart);
997    }
998    observePerformanceEntries("paint", (entry) => {
999      if (fcpReported || entry.name !== "first-contentful-paint") return;
1000      const startTime = performanceEntryValue(entry, "startTime");
1001      if (startTime === null) return;
1002      fcpReported = true;
1003      emitWebVital("FCP", startTime);
1004    });
1005    observePerformanceEntries("largest-contentful-paint", (entry) => {
1006      latestLcp = performanceEntryValue(entry, "startTime") ?? performanceEntryValue(entry, "renderTime") ?? performanceEntryValue(entry, "loadTime") ?? latestLcp;
1007    });
1008    const clsObserverStarted = observePerformanceEntries("layout-shift", (entry) => {
1009      if (entry.hadRecentInput === true) return;
1010      const value = performanceEntryValue(entry, "value");
1011      if (value !== null) clsValue += value;
1012    });
1013    observePerformanceEntries("event", (entry) => {
1014      const interactionId = performanceEntryValue(entry, "interactionId");
1015      if (interactionId === null || interactionId <= 0) return;
1016      const duration = performanceEntryValue(entry, "duration");
1017      if (duration !== null && duration > maxInp) maxInp = duration;
1018    });
1019    return (final = false) => {
1020      if (finalized) return;
1021      if (latestLcp !== null) reportMetric("LCP", latestLcp);
1022      if (clsObserverStarted) reportMetric("CLS", clsValue);
1023      if (maxInp > 0) reportMetric("INP", maxInp);
1024      if (final) finalized = true;
1025    };
1026  }, "observeWebVitals");
1027  const patchHistory = /* @__PURE__ */ __name((method, navigationType) => {
1028    const historyObject = w.history;
1029    const original = historyObject && historyObject[method];
1030    if (!historyObject || typeof original !== "function") return;
1031    historyObject[method] = function(...args) {
1032      const result = original.apply(this, args);
1033      emitPageViewIfPathChanged(navigationType);
1034      return result;
1035    };
1036  }, "patchHistory");
1037  const normalizeCaptureExceptionOptions = /* @__PURE__ */ __name((options) => {
1038    const record = options && typeof options === "object" && !Array.isArray(options) ? options : {};
1039    const mechanism = typeof record.mechanism === "string" ? record.mechanism : "manual";
1040    const severity = typeof record.severity === "string" ? record.severity : "error";
1041    return {
1042      mechanism: ["manual", "onerror", "unhandledrejection", "react_error_boundary"].includes(mechanism) ? mechanism : "manual",
1043      handled: typeof record.handled === "boolean" ? record.handled : true,
1044      severity: ["error", "warning", "info"].includes(severity) ? severity : "error"
1045    };
1046  }, "normalizeCaptureExceptionOptions");
1047  w.__lovableEvents = Object.assign(existing, {
1048    __userAppEventsSdkVersion: version,
1049    track,
1050    flush: /* @__PURE__ */ __name(() => flush(), "flush"),
1051    identify(identity) {
1052      const identityRecord = identity && typeof identity === "object" ? identity : null;
1053      appUserId = typeof identityRecord?.user_id === "string" ? identityRecord.user_id : null;
1054    },
1055    getIdentity() {
1056      return { app_user_id_hash_available: !!appUserId };
1057    },
1058    getReplay() {
1059      return {
1060        replay_id: replayId,
1061        active: !!replayId && !replayFinalized && replayRecorderActive,
1062        event_count: replayEventCount
1063      };
1064    },
1065    resetIdentity() {
1066      appUserId = null;
1067    },
1068    captureException(error, context = {}, options = {}) {
1069      const throwable = serializeThrowable(error);
1070      const captureOptions = normalizeCaptureExceptionOptions(options);
1071      let captureContext = context && typeof context === "object" && !Array.isArray(context) ? context : {};
1072      try {
1073        if (byteLength(JSON.stringify(captureContext)) > 4096) captureContext = {};
1074      } catch {
1075        captureContext = {};
1076      }
1077      enqueue("lovable.client_error", {
1078        occurrence_id: randomId(),
1079        ...throwable,
1080        ...captureOptions,
1081        context: captureContext
1082      });
1083    },
1084    startSpan(name, attributes = {}) {
1085      const startTime = (/* @__PURE__ */ new Date()).toISOString();
1086      const start = perf.now();
1087      return {
1088        end(status = "ok") {
1089          enqueue("lovable.frontend_span", {
1090            name,
1091            start_time: startTime,
1092            status,
1093            duration_ms: Math.max(0, perf.now() - start),
1094            attributes
1095          });
1096        }
1097      };
1098    }
1099  });
1100  if (w.addEventListener) {
1101    w.addEventListener("error", stopReplayRecordingOnRuntimeError, true);
1102  }
1103  ensureReplayRecorder();
1104  const finalizeWebVitals = observeWebVitals();
1105  if (w.addEventListener) {
1106    w.addEventListener(
1107      "error",
1108      (event) => {
1109        const errorEvent = event && typeof event === "object" ? event : null;
1110        const target = errorEvent?.target && typeof errorEvent.target === "object" ? errorEvent.target : null;
1111        let resourceUrl = null;
1112        if (target && target !== w) {
1113          if (typeof target.src === "string") {
1114            resourceUrl = target.src;
1115          } else if (typeof target.href === "string") {
1116            resourceUrl = target.href;
1117          }
1118        }
1119        if (resourceUrl !== null) {
1120          const resourceType = typeof target?.tagName === "string" ? target.tagName.toLowerCase() : "unknown";
1121          enqueue("lovable.resource_error", {
1122            resource_type: resourceType,
1123            url_host: hostFromUrl(resourceUrl) ?? "",
1124            failure_type: "load_error",
1125            element: resourceType,
1126            url_path: pathFromUrl(resourceUrl),
1127            mechanism: "resource_load",
1128            handled: false,
1129            severity: "error"
1130          });
1131          return;
1132        }
1133        const throwable = serializeThrowable(errorEvent?.error || errorEvent?.message);
1134        enqueue("lovable.client_error", {
1135          occurrence_id: randomId(),
1136          ...throwable,
1137          filename: typeof errorEvent?.filename === "string" ? pathFromUrl(errorEvent.filename) : null,
1138          lineno: typeof errorEvent?.lineno === "number" ? errorEvent.lineno : null,
1139          colno: typeof errorEvent?.colno === "number" ? errorEvent.colno : null,
1140          mechanism: "onerror",
1141          handled: false,
1142          severity: "error"
1143        });
1144      },
1145      true
1146    );
1147    w.addEventListener("unhandledrejection", (event) => {
1148      const rejectionEvent = event && typeof event === "object" ? event : null;
1149      const throwable = serializeThrowable(rejectionEvent?.reason);
1150      enqueue("lovable.client_error", {
1151        occurrence_id: randomId(),
1152        ...throwable,
1153        mechanism: "unhandledrejection",
1154        handled: false,
1155        severity: "error"
1156      });
1157    });
1158    w.addEventListener("pagehide", () => {
1159      unloadingPage = true;
1160      finalizeWebVitals(true);
1161      finalizeReplayRecording("pagehide");
1162      emitEngagement("pagehide");
1163      stopEngagementSegment();
1164      clearEngagementTimer();
1165      clearUnfocusedTimer();
1166      flush(true);
1167    });
1168    w.addEventListener("blur", () => scheduleUnfocusedStop());
1169    w.addEventListener("focus", () => {
1170      resetUnfocusedGrace();
1171      if (!engagementStoppedUnfocused) return;
1172      engagementStoppedUnfocused = false;
1173      resumeEngagementMeasurement();
1174    });
1175    w.addEventListener("pageshow", (event) => {
1176      const persisted = event && typeof event === "object" ? event.persisted : false;
1177      if (persisted === true) {
1178        unloadingPage = false;
1179        releaseBeaconBytes();
1180        resetUnfocusedGrace();
1181        engagementStoppedUnfocused = false;
1182        resumeEngagementMeasurement();
1183      }
1184    });
1185    w.addEventListener("popstate", () => emitPageViewIfPathChanged("popstate"));
1186    w.addEventListener("hashchange", () => emitPageView("hashchange"));
1187    if (autocaptureEnabled) {
1188      const labelMaxLength = 64;
1189      const tagMaxLength = 64;
1190      const urlPathMaxLength = 2048;
1191      const ownTextMaxLength = 200;
1192      const chainMaxDepth = 32;
1193      const clickCapPerPageView = 200;
1194      const optOutMaxDepth = 512;
1195      const pathMaxDepth = 32;
1196      const pathMaxLength = 1024;
1197      const valueBearingTags = {
1198        select: true,
1199        textarea: true,
1200        input: true
1201      };
1202      const interactiveTags = {
1203        button: true,
1204        a: true,
1205        input: true,
1206        select: true,
1207        textarea: true,
1208        summary: true
1209      };
1210      const interactiveRoles = {
1211        button: true,
1212        link: true,
1213        menuitem: true,
1214        menuitemcheckbox: true,
1215        menuitemradio: true,
1216        tab: true,
1217        switch: true,
1218        checkbox: true,
1219        radio: true,
1220        option: true
1221      };
1222      const landmarkTags = {
1223        header: true,
1224        footer: true,
1225        main: true,
1226        nav: true,
1227        aside: true,
1228        section: true,
1229        article: true,
1230        form: true
1231      };
1232      const jsxSourceKey = typeof Symbol !== "undefined" && typeof Symbol.for === "function" ? /* @__P
1232URE__ */ Symbol.for("__jsxSource__") : null;
1233      const asNode = /* @__PURE__ */ __name((value) => value && typeof value === "object" && typeof value.tagName === "string" ? value : null, "asNode");
1234      const knownTags = {};
1235      for (const tag of "html body div span main header footer nav aside section article form p a button input select textarea label fieldset legend optgroup option datalist output progress meter ul ol li dl dt dd table thead tbody tfoot tr td th caption col colgroup h1 h2 h3 h4 h5 h6 br hr pre blockquote figure figcaption small strong em b i u s q cite abbr code kbd samp var mark sub sup time address details summary dialog menu picture source track canvas iframe object embed param map area img video audio template slot noscript script style link meta title base head ins del ruby rt rp bdi bdo wbr data hgroup search fencedframe svg path g circle rect line polyline polygon ellipse text textpath tspan defs use symbol clippath mask pattern lineargradient radialgradient stop filter foreignobject marker image view desc animate animatetransform animatemotion set fegaussianblur feoffset feblend fecolormatrix fecomposite feflood femerge femergenode femorphology fetile feturbulence fedropshadow fedisplacementmap".split(" ")) {
1236        knownTags[tag] = true;
1237      }
1238      const knownRoles = {};
1239      for (const role of "button link menuitem menuitemcheckbox menuitemradio tab switch checkbox radio option combobox listbox textbox searchbox slider spinbutton treeitem gridcell row columnheader rowheader tooltip dialog alertdialog navigation main banner contentinfo complementary region form search article list listitem table grid tree menu menubar toolbar tabpanel tablist progressbar status alert log timer none presentation".split(" ")) {
1240        knownRoles[role] = true;
1241      }
1242      const knownInputTypes = {};
1243      for (const type of "button submit reset checkbox radio text password email number tel url search date time datetime-local month week color file range hidden image".split(" ")) {
1244        knownInputTypes[type] = true;
1245      }
1246      const knownAttrValue = /* @__PURE__ */ __name((value, vocabulary) => value !== null && vocabulary[value.toLowerCase()] === true ? value.toLowerCase() : null, "knownAttrValue");
1247      const tagOf = /* @__PURE__ */ __name((node) => {
1248        const tag = String(node.tagName).toLowerCase();
1249        return knownTags[tag] === true ? tag : "x";
1250      }, "tagOf");
1251      const attrOf = /* @__PURE__ */ __name((node, name) => {
1252        if (typeof node.getAttribute !== "function") return null;
1253        const value = node.getAttribute(name);
1254        return typeof value === "string" && value !== "" ? value : null;
1255      }, "attrOf");
1256      const hasAttr = /* @__PURE__ */ __name((node, name) => typeof node.getAttribute === "function" && typeof node.getAttribute(name) === "string", "hasAttr");
1257      const truncateTo = /* @__PURE__ */ __name((value, maxChars) => {
1258        if (typeof value !== "string") return null;
1259        const collapsed = value.replace(/\s+/gu, " ").trim();
1260        if (!collapsed) return null;
1261        if (collapsed.length <= maxChars) return collapsed;
1262        const points = Array.from(collapsed);
1263        return points.length > maxChars ? points.slice(0, maxChars).join("") : collapsed;
1264      }, "truncateTo");
1265      const ownTextOf = /* @__PURE__ */ __name((node) => {
1266        if (valueBearingTags[tagOf(node)] === true) return null;
1267        const children = node.childNodes;
1268        if (!children || typeof children.length !== "number") return null;
1269        const list = children;
1270        let text = "";
1271        for (let index = 0; index < list.length; index++) {
1272          const child = list[index];
1273          if (child && child.nodeType === 3 && typeof child.nodeValue === "string") text += child.nodeValue;
1274          if (text.length > ownTextMaxLength) return null;
1275        }
1276        return text === "" ? null : text;
1277      }, "ownTextOf");
1278      const labelOf = /* @__PURE__ */ __name((node) => truncateTo(
1279        attrOf(node, "aria-label") || attrOf(node, "title") || attrOf(node, "alt") || ownTextOf(node),
1280        labelMaxLength
1281      ), "labelOf");
1282      const nthOfType = /* @__PURE__ */ __name((parent, node, tag) => {
1283        const children = parent.children;
1284        if (!children || typeof children.length !== "number") return "";
1285        const list = children;
1286        let position = 0;
1287        let total = 0;
1288        for (let index = 0; index < list.length; index++) {
1289          const sibling = asNode(list[index]);
1290          if (!sibling || tagOf(sibling) !== tag) continue;
1291          total++;
1292          if (sibling === node) position = total;
1293        }
1294        return position > 0 ? ":" + position : "";
1295      }, "nthOfType");
1296      const pathOf = /* @__PURE__ */ __name((node) => {
1297        const segments = [];
1298        let current = node;
1299        for (let depth = 0; current && depth < pathMaxDepth; depth++) {
1300          const tag = tagOf(current);
1301          if (!tag || tag === "html") break;
1302          const parent = asNode(current.parentElement);
1303          segments.unshift(parent && tag !== "body" ? tag + nthOfType(parent, current, tag) : tag);
1304          if (tag === "body" || !parent) break;
1305          current = parent;
1306        }
1307        if (segments.length === 0) return null;
1308        let path = segments.join(">");
1309        while (path.length > pathMaxLength && segments.length > 1) {
1310          segments.shift();
1311          path = segments.join(">");
1312        }
1313        return path.length > pathMaxLength ? path.slice(path.length - pathMaxLength) : path;
1314      }, "pathOf");
1315      const componentNameOf = /* @__PURE__ */ __name((node) => {
1316        if (jsxSourceKey === null) return null;
1317        const source = node[jsxSourceKey];
1318        if (!source || typeof source !== "object") return null;
1319        const name = source.displayName;
1320        return typeof name === "string" && name !== "" ? name : null;
1321      }, "componentNameOf");
1322      const isInteractive = /* @__PURE__ */ __name((node) => {
1323        if (interactiveTags[tagOf(node)] === true) return true;
1324        const role = attrOf(node, "role");
1325        return role !== null && interactiveRoles[role.toLowerCase()] === true;
1326      }, "isInteractive");
1327      const chainFrom = /* @__PURE__ */ __name((event, maxDepth) => {
1328        const composed = typeof event.composedPath === "function" ? event.composedPath() : null;
1329        if (Array.isArray(composed)) {
1330          const nodes2 = [];
1331          for (const entry of composed) {
1332            if (nodes2.length >= maxDepth) return { nodes: nodes2, complete: false };
1333            const node2 = asNode(entry);
1334            if (node2) nodes2.push(node2);
1335          }
1336          return { nodes: nodes2, complete: true };
1337        }
1338        const nodes = [];
1339        let node = asNode(event.target);
1340        while (node) {
1341          if (nodes.length >= maxDepth) return { nodes, complete: false };
1342          nodes.push(node);
1343          node = asNode(node.parentElement);
1344        }
1345        return { nodes, complete: true };
1346      }, "chainFrom");
1347      const isPointerType = /* @__PURE__ */ __name((value) => value === "mouse" || value === "touch" || value === "pen", "isPointerType");
1348      const pointerTypeMaxAgeMs = 2e3;
1349      const forwardedClickMaxAgeMs = 300;
1350      let lastPointerType = "";
1351      let lastPointerAt = 0;
1352      const recentPointerType = /* @__PURE__ */ __name((maxAgeMs) => lastPointerType !== "" && perf.now() - lastPointerAt <= maxAgeMs ? lastPointerType : "", "recentPointerType");
1353      w.addEventListener(
1354        "pointerdown",
1355        (event) => {
1356          try {
1357            const pointerEvent = event;
1358            if (pointerEvent.isTrusted !== true) return;
1359            const pointerType = typeof pointerEvent.pointerType === "string" ? pointerEvent.pointerType : "";
1360            if (isPointerType(pointerType)) {
1361              lastPointerType = pointerType;
1362              lastPointerAt = perf.now();
1363            }
1364          } catch {
1365          }
1366        },
1367        true
1368      );
1369      const roundUnit = /* @__PURE__ */ __name((value) => Math.round(value * 1e3) / 1e3, "roundUnit");
1370      const numberOr = /* @__PURE__ */ __name((value) => typeof value === "number" && isFinite(value) ? value : null, "numberOr");
1371      w.addEventListener(
1372        "click",
1373        (event) => {
1374          try {
1375            if (!event || typeof event !== "object") return;
1376            const clickEvent = event;
1377            if (clickEvent.isTrusted !== true) return;
1378            if (capturedClicks >= clickCapPerPageView) return;
1379            const chain = chainFrom(clickEvent, optOutMaxDepth);
1380            if (!chain.complete) return;
1381            let element = null;
1382            let section = null;
1383            for (let depth = 0; depth < chain.nodes.length; depth++) {
1384              const node = chain.nodes[depth];
1385              if (hasAttr(node, "data-lov-no-capture")) return;
1386              if (depth >= chainMaxDepth) continue;
1387              if (element === null) {
1388                if (isInteractive(node)) element = node;
1389                continue;
1390              }
1391              if (section === null && landmarkTags[tagOf(node)] === true) section = node;
1392            }
1393            if (element === null) return;
1394            const pointerType = typeof clickEvent.pointerType === "string" ? clickEvent.pointerType : "";
1395            const detail = numberOr(clickEvent.detail) ?? 0;
1396            const forwarded = detail === 0 ? recentPointerType(forwardedClickMaxAgeMs) : "";
1397            const interactionType = isPointerType(pointerType) ? pointerType : forwarded || (detail === 0 ? "keyboard" : recentPointerType(pointerTypeMaxAgeMs) || "unknown");
1398            let offsetX = null;
1399            let offsetY = null;
1400            const clientX = numberOr(clickEvent.clientX);
1401            const clientY = numberOr(clickEvent.clientY);
1402            const rect = typeof element.getBoundingClientRect === "function" ? element.getBoundingClientRect() : null;
1403            if (interactionType !== "keyboard" && rect && clientX !== null && clientY !== null && rect.width > 0 && rect.height > 0) {
1404              offsetX = roundUnit(Math.min(1, Math.max(0, (clientX - rect.left) / rect.width)));
1405              offsetY = roundUnit(Math.min(1, Math.max(0, (clientY - rect.top) / rect.height)));
1406            }
1407            const viewport = w;
1408            enqueue("lovable.element_clicked", {
1409              // pathname only, and named url_path so the collector's path sanitizer
1410              // selects it as a second line of defence.
1411              url_path: truncateTo(loc.pathname || "/", urlPathMaxLength) || "/",
1412              element_tag: tagOf(element),
1413              interaction_type: interactionType,
1414              element_role: knownAttrValue(attrOf(element, "role"), knownRoles),
1415              element_type: knownAttrValue(attrOf(element, "type"), knownInputTypes),
1416              element_label: labelOf(element),
1417              element_path: pathOf(element),
1418              component_name: truncateTo(componentNameOf(element), tagMaxLength),
1419              section_tag: section ? truncateTo(tagOf(section), tagMaxLength) : null,
1420              section_label: section ? truncateTo(attrOf(section, "aria-label"), labelMaxLength) : null,
1421              offset_x: offsetX,
1422              offset_y: offsetY,
1423              viewport_width: numberOr(viewport.innerWidth),
1424              viewport_height: numberOr(viewport.innerHeight)
1425            });
1426            capturedClicks++;
1427          } catch {
1428          }
1429        },
1430        true
1431      );
1432    }
1433  }
1434  if (d && typeof d.addEventListener === "function") {
1435    d.addEventListener("visibilitychange", () => {
1436      if (d.visibilityState === "hidden") {
1437        unloadingPage = true;
1438        finalizeWebVitals();
1439        flushReplay(true);
1440        emitEngagement("hidden");
1441        stopEngagementSegment();
1442        clearEngagementTimer();
1443        clearUnfocusedTimer();
1444        flush(true);
1445        unloadingPage = false;
1446        return;
1447      }
1448      unloadingPage = false;
1449      releaseBeaconBytes();
1450      resumeEngagementMeasurement();
1451    });
1452  }
1453  patchHistory("pushState", "push_state");
1454  patchHistory("replaceState", "replace_state");
1455  enqueue("lovable.session_started", { reason: "new_session" });
1456  emitPageView("initial_load", false);
1457  resumeEngagementMeasurement();
1458})(window, "0.2.0-no-device-storage", {"lovable.session_started":1,"lovable.page_viewed":1,"lovable.client_error":1,"lovable.frontend_span":1,"lovable.interaction":1,"lovable.element_clicked":1,"lovable.form_submitted":1,"lovable.resource_error":1,"lovable.session_replay":1,"lovable.replay_error":1,"lovable.web_vital":1,"lovable.page_engagement":1}, 50, 51200, 524288, 100, 5242880, 50, 600000, 2000);})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.