PageSourceSearch

https://maximilian-schimanzik.de/__l5e/events.js

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