PageSourceSearch

https://refr.com.au/__l5e/events.js

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