@lapwy/js 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,710 @@
1
+ // src/core.ts
2
+ var DEFAULT_ENDPOINT = "https://in.lapwy.com/e";
3
+ var noop = { track() {
4
+ }, page() {
5
+ }, consent() {
6
+ }, notFound() {
7
+ } };
8
+ function start(options) {
9
+ if (typeof window === "undefined" || typeof document === "undefined" || !options.site) return noop;
10
+ const w = window;
11
+ const d = document;
12
+ const loc = w.location;
13
+ const nav = w.navigator;
14
+ const key = options.site;
15
+ const endpoint = options.endpoint || DEFAULT_ENDPOINT;
16
+ const release = options.release || "";
17
+ const sdk = options.sdk || "web-0.1";
18
+ const cookieMode = options.cookie === "always" || options.cookie === "consent" ? options.cookie : "";
19
+ const ID = "lapwy_id";
20
+ let vid = "";
21
+ let queue = [];
22
+ let timer = 0;
23
+ let backoff = 0;
24
+ let pageId = "";
25
+ let pageUrl = "";
26
+ let rawUrl = "";
27
+ const patterns = (options.pages || []).map((p) => p.split("/"));
28
+ function grouped(url) {
29
+ try {
30
+ const u = new URL(url);
31
+ let path = u.pathname;
32
+ if (options.path) {
33
+ path = String(options.path(path) || path);
34
+ if (path[0] !== "/") path = "/" + path;
35
+ } else {
36
+ const parts = path.split("/");
37
+ if (parts.length > 2 && !parts[parts.length - 1]) parts.pop();
38
+ for (const p of patterns) {
39
+ const last = p.length - 1;
40
+ const star = p[last] === "*";
41
+ if (star ? parts.length < p.length : parts.length !== p.length) continue;
42
+ let fits = true;
43
+ for (let i = 0; fits && i <= last; i++) fits = star && i === last || (p[i][0] === ":" ? !!parts[i] : p[i] === parts[i]);
44
+ if (fits) {
45
+ path = p.join("/");
46
+ break;
47
+ }
48
+ }
49
+ }
50
+ return u.origin + path + u.search;
51
+ } catch {
52
+ return url;
53
+ }
54
+ }
55
+ const Q = "lapwy_q";
56
+ let kept = false;
57
+ function keep() {
58
+ try {
59
+ if (queue.length) w.localStorage.setItem(Q, JSON.stringify(queue.slice(0, 200)));
60
+ else w.localStorage.removeItem(Q);
61
+ } catch {
62
+ }
63
+ kept = queue.length > 0;
64
+ }
65
+ let shownAt = 0;
66
+ let engaged = 0;
67
+ let lastRef = d.referrer || "";
68
+ const rawFetch = w.fetch ? w.fetch.bind(w) : null;
69
+ function hex(n) {
70
+ const a = new Uint8Array(n);
71
+ let s = "";
72
+ (w.crypto || w.msCrypto).getRandomValues(a);
73
+ for (let i = 0; i < n; i++) s += (a[i] + 256).toString(16).slice(1);
74
+ return s;
75
+ }
76
+ function uuid() {
77
+ const h = hex(16);
78
+ return h.slice(0, 8) + "-" + h.slice(8, 12) + "-4" + h.slice(13, 16) + "-a" + h.slice(17, 20) + "-" + h.slice(20);
79
+ }
80
+ function add(kind, extra) {
81
+ const e = { id: uuid(), t: Date.now(), k: kind, u: pageUrl, p: pageId };
82
+ for (const k in extra) e[k] = extra[k];
83
+ queue.push(e);
84
+ if (!timer) timer = setTimeout(flush, 800);
85
+ }
86
+ function readId() {
87
+ const m = d.cookie.match(/(?:^|; )lapwy_id=([0-9a-f]{16,64})/);
88
+ let v = m ? m[1] : "";
89
+ try {
90
+ v = v || w.localStorage.getItem(ID) || "";
91
+ } catch {
92
+ }
93
+ return /^[0-9a-f]{16,64}$/.test(v) ? v : "";
94
+ }
95
+ function keepId() {
96
+ vid = readId() || hex(16);
97
+ d.cookie = ID + "=" + vid + "; Max-Age=34164000; Path=/; SameSite=Lax" + (loc.protocol === "https:" ? "; Secure" : "");
98
+ try {
99
+ w.localStorage.setItem(ID, vid);
100
+ } catch {
101
+ }
102
+ }
103
+ function dropId() {
104
+ vid = "";
105
+ d.cookie = ID + "=; Max-Age=0; Path=/; SameSite=Lax";
106
+ try {
107
+ w.localStorage.removeItem(ID);
108
+ } catch {
109
+ }
110
+ }
111
+ let input = 128;
112
+ [
113
+ ["pointermove", 1],
114
+ ["mousemove", 1],
115
+ ["pointerdown", 2],
116
+ ["click", 2],
117
+ ["keydown", 4],
118
+ ["wheel", 8],
119
+ ["touchstart", 16]
120
+ ].forEach(
121
+ ([type, bit]) => w.addEventListener(
122
+ type,
123
+ (ev) => {
124
+ if (ev.isTrusted) input |= bit;
125
+ },
126
+ { capture: true, passive: true }
127
+ )
128
+ );
129
+ let moves = 0;
130
+ let press = 0;
131
+ let downAt = 0;
132
+ w.addEventListener(
133
+ "pointermove",
134
+ (ev) => {
135
+ if (ev.isTrusted && ev.pointerType !== "touch" && moves < 255) moves++;
136
+ },
137
+ { capture: true, passive: true }
138
+ );
139
+ w.addEventListener(
140
+ "pointerdown",
141
+ (ev) => {
142
+ downAt = ev.isTrusted && ev.pointerType === "mouse" ? w.performance.now() : 0;
143
+ },
144
+ { capture: true, passive: true }
145
+ );
146
+ w.addEventListener(
147
+ "pointerup",
148
+ (ev) => {
149
+ if (!downAt || !ev.isTrusted || ev.pointerType !== "mouse") return;
150
+ const held = Math.min(Math.max(Math.round(w.performance.now() - downAt), 1), 6e4);
151
+ if (!press || held < press) press = held;
152
+ downAt = 0;
153
+ },
154
+ { capture: true, passive: true }
155
+ );
156
+ function odd() {
157
+ let b = 0;
158
+ try {
159
+ const g = w;
160
+ if (!nav.languages || !nav.languages.length) b |= 1;
161
+ if (/Chrome\//.test(nav.userAgent) && !/Edg|OPR/.test(nav.userAgent) && !g.chrome) b |= 2;
162
+ if (!g.outerWidth || !g.outerHeight) b |= 4;
163
+ } catch {
164
+ }
165
+ return b;
166
+ }
167
+ function automated() {
168
+ let b = 0;
169
+ try {
170
+ const g = w;
171
+ if (nav.webdriver) b |= 1;
172
+ if (g.callPhantom || g._phantom || g.__nightmare || g.__playwright__binding__ || g.__pwInitScripts || g.domAutomation || g.domAutomationController || d.documentElement.getAttribute("webdriver") !== null) b |= 2;
173
+ } catch {
174
+ }
175
+ return b;
176
+ }
177
+ let sg = {};
178
+ let sgStart = 0;
179
+ let sgOn = false;
180
+ let sgTimer = 0;
181
+ let sgX = -1;
182
+ let sgY = 0;
183
+ let sgSlot = -1;
184
+ let sgKeyAt = 0;
185
+ let sgScrollY = 0;
186
+ let sgMoved = false;
187
+ let sgDownAt = 0;
188
+ const clock = () => w.performance.now();
189
+ const bump = (k, by = 1) => {
190
+ sg[k] = (sg[k] || 0) + by;
191
+ };
192
+ const lowest = (k, v) => {
193
+ if (!(k in sg) || v < sg[k]) sg[k] = v;
194
+ };
195
+ const firstInput = () => {
196
+ if (!("fi" in sg)) sg.fi = clock() - sgStart;
197
+ };
198
+ const watch = (type, fn) => w.addEventListener(
199
+ type,
200
+ (ev) => {
201
+ try {
202
+ if (ev.isTrusted) fn(ev);
203
+ } catch {
204
+ }
205
+ },
206
+ { capture: true, passive: true }
207
+ );
208
+ watch("pointermove", (ev) => {
209
+ if (ev.pointerType === "touch") return;
210
+ bump("mv");
211
+ if (sgX >= 0) bump("md", Math.hypot(ev.clientX - sgX, ev.clientY - sgY));
212
+ sgX = ev.clientX;
213
+ sgY = ev.clientY;
214
+ const slot = Math.floor(clock() / 100);
215
+ if (slot !== sgSlot) bump("mb");
216
+ sgSlot = slot;
217
+ sgMoved = true;
218
+ firstInput();
219
+ });
220
+ watch("pointerdown", (ev) => {
221
+ bump("ck");
222
+ if (ev.pointerType === "mouse") {
223
+ if (!sgMoved) bump("cj");
224
+ sgMoved = false;
225
+ sgDownAt = clock();
226
+ }
227
+ firstInput();
228
+ });
229
+ watch("pointerup", (ev) => {
230
+ if (ev.pointerType !== "mouse" || !sgDownAt) return;
231
+ const held = clock() - sgDownAt;
232
+ sgDownAt = 0;
233
+ bump("pn");
234
+ bump("ps", held);
235
+ lowest("pmin", held);
236
+ });
237
+ watch("keydown", () => {
238
+ bump("kd");
239
+ const at = clock();
240
+ if (sgKeyAt && at - sgKeyAt < 5e3) {
241
+ bump("kn");
242
+ bump("ks", at - sgKeyAt);
243
+ lowest("kmin", at - sgKeyAt);
244
+ }
245
+ sgKeyAt = at;
246
+ firstInput();
247
+ });
248
+ watch("wheel", () => {
249
+ bump("wh");
250
+ firstInput();
251
+ });
252
+ watch("touchstart", () => {
253
+ bump("tc");
254
+ firstInput();
255
+ });
256
+ watch("touchmove", () => bump("tm"));
257
+ w.addEventListener(
258
+ "scroll",
259
+ () => {
260
+ try {
261
+ bump("sc");
262
+ bump("sd", Math.abs(w.scrollY - sgScrollY));
263
+ sgScrollY = w.scrollY;
264
+ const reached = (w.scrollY + w.innerHeight) / Math.max(d.documentElement.scrollHeight, 1) * 100;
265
+ if (!(sg.sp >= reached)) sg.sp = Math.min(reached, 100);
266
+ } catch {
267
+ }
268
+ },
269
+ { capture: true, passive: true }
270
+ );
271
+ function sendSignals(final) {
272
+ if (!pageId || !sgOn) return;
273
+ const n = nav;
274
+ const p = {
275
+ final,
276
+ age: Math.round(clock() - sgStart),
277
+ vis: engaged + (shownAt ? Date.now() - shownAt : 0),
278
+ wd: automated(),
279
+ odd: odd(),
280
+ hw: n.hardwareConcurrency || 0,
281
+ mem: n.deviceMemory || 0,
282
+ tp: n.maxTouchPoints || 0,
283
+ dpr: w.devicePixelRatio || 0,
284
+ ow: w.outerWidth || 0,
285
+ oh: w.outerHeight || 0,
286
+ pl: n.plugins && n.plugins.length || 0,
287
+ lg: n.languages && n.languages.length || 0,
288
+ cd: w.screen.colorDepth || 0,
289
+ tzo: (/* @__PURE__ */ new Date()).getTimezoneOffset()
290
+ };
291
+ for (const k in sg) p[k] = Math.round(sg[k]);
292
+ add("signals", { props: p });
293
+ }
294
+ function startSignals() {
295
+ sg = {};
296
+ sgOn = true;
297
+ sgStart = clock();
298
+ sgX = -1;
299
+ sgSlot = -1;
300
+ sgKeyAt = 0;
301
+ sgMoved = false;
302
+ sgScrollY = w.scrollY || 0;
303
+ if (sgTimer) clearTimeout(sgTimer);
304
+ sgTimer = setTimeout(() => sendSignals(false), 5e3);
305
+ }
306
+ function body(events) {
307
+ return JSON.stringify({
308
+ k: key,
309
+ v: vid || void 0,
310
+ sdk,
311
+ now: Date.now(),
312
+ c: { sw: w.screen.width, sh: w.screen.height, vw: w.innerWidth, vh: w.innerHeight, l: nav.language || "", rel: release, in: input, bot: automated() || void 0, mv: moves || void 0, pr: press || void 0, odd: odd() || void 0 },
313
+ e: events
314
+ });
315
+ }
316
+ function flush(leaving2) {
317
+ drainCalls();
318
+ if (timer) clearTimeout(timer);
319
+ timer = 0;
320
+ if (!queue.length || !rawFetch) return;
321
+ for (const e of queue) {
322
+ if (e.k !== "pageview" || e.p !== pageId) continue;
323
+ e.ti = d.title;
324
+ const props = e.props || {};
325
+ if (!props.status && /(^|\W)404(\W|$)|not found/i.test(d.title)) e.props = { ...props, status: 404, soft: true };
326
+ }
327
+ if (leaving2 === true && backoff) {
328
+ keep();
329
+ return;
330
+ }
331
+ const events = queue.splice(0, 50);
332
+ const payload = body(events);
333
+ if (leaving2 === true && nav.sendBeacon && nav.sendBeacon(endpoint, new Blob([payload], { type: "text/plain" }))) {
334
+ if (kept) keep();
335
+ return;
336
+ }
337
+ rawFetch(endpoint, { method: "POST", body: payload, headers: { "Content-Type": "text/plain" }, keepalive: true, credentials: "omit" }).then((r) => {
338
+ if (r.status === 429 || r.status >= 500) throw 0;
339
+ backoff = 0;
340
+ if (kept) keep();
341
+ if (queue.length && !timer) timer = setTimeout(flush, 200);
342
+ }).catch(() => {
343
+ queue = events.concat(queue).slice(0, 200);
344
+ keep();
345
+ if (timer) clearTimeout(timer);
346
+ timer = setTimeout(flush, Math.min(2e3 * Math.pow(2, backoff++), 3e5) + Math.random() * 1e3);
347
+ });
348
+ }
349
+ const visible = () => d.visibilityState !== "hidden";
350
+ function sendEngagement() {
351
+ if (shownAt) {
352
+ engaged += Date.now() - shownAt;
353
+ shownAt = 0;
354
+ }
355
+ if (pageId && engaged >= 1e3) add("engagement", { v: engaged });
356
+ engaged = 0;
357
+ }
358
+ function page2(props) {
359
+ const raw = loc.href.split("#")[0];
360
+ if (raw === rawUrl && !props) return;
361
+ const url = grouped(raw);
362
+ sendSignals(true);
363
+ sendEngagement();
364
+ if (pageUrl) timeNav(pageUrl);
365
+ pageId = hex(8);
366
+ startSignals();
367
+ pageUrl = url;
368
+ rawUrl = raw;
369
+ shownAt = visible() ? Date.now() : 0;
370
+ add("pageview", { r: lastRef, ti: d.title, props });
371
+ if (timer) clearTimeout(timer);
372
+ timer = setTimeout(flush, 800);
373
+ lastRef = url;
374
+ }
375
+ function notFound2(status) {
376
+ const code = status && status >= 400 && status < 600 ? Math.floor(status) : 404;
377
+ for (const e of queue) {
378
+ if (e.k === "pageview" && e.p === pageId) {
379
+ e.props = { ...e.props || {}, status: code, soft: false };
380
+ return;
381
+ }
382
+ }
383
+ add("deadend", { v: code });
384
+ }
385
+ function track2(name, props) {
386
+ if (typeof name === "string" && name) add("custom", { n: name.slice(0, 100), props });
387
+ }
388
+ ["pushState", "replaceState"].forEach((m) => {
389
+ const orig = w.history[m];
390
+ w.history[m] = function(...args) {
391
+ const r = orig.apply(this, args);
392
+ setTimeout(page2, 0);
393
+ return r;
394
+ };
395
+ });
396
+ w.addEventListener("popstate", () => setTimeout(page2, 0));
397
+ d.addEventListener("visibilitychange", () => {
398
+ if (visible()) shownAt = Date.now();
399
+ else {
400
+ sendVitals();
401
+ sendSignals(true);
402
+ sendEngagement();
403
+ flush(true);
404
+ }
405
+ });
406
+ w.addEventListener("pagehide", () => {
407
+ sendVitals();
408
+ sendSignals(true);
409
+ sendEngagement();
410
+ flush(true);
411
+ });
412
+ d.addEventListener(
413
+ "click",
414
+ (ev) => {
415
+ const target = ev.target;
416
+ const el = target && target.closest ? target.closest("[data-lapwy-event]") : null;
417
+ if (!el) return;
418
+ const props = {};
419
+ for (const at of Array.from(el.attributes)) {
420
+ if (at.name.indexOf("data-lapwy-") === 0 && at.name !== "data-lapwy-event") props[at.name.slice(11)] = at.value;
421
+ }
422
+ track2(el.getAttribute("data-lapwy-event") || "", props);
423
+ },
424
+ true
425
+ );
426
+ let loadUrl = "";
427
+ let loadPage = "";
428
+ const sent = {};
429
+ function vital(name, value) {
430
+ if (sent[name] || !loadPage || !(value >= 0)) return;
431
+ sent[name] = 1;
432
+ add("vital", { n: name, v: Math.round(value * 1e3) / 1e3, u: loadUrl, p: loadPage });
433
+ }
434
+ function observe(type, fn, opts) {
435
+ try {
436
+ new PerformanceObserver((l) => l.getEntries().forEach(fn)).observe({ type, buffered: true, ...opts });
437
+ } catch {
438
+ }
439
+ }
440
+ let lcp = -1;
441
+ let cls = 0;
442
+ let clsWin = 0;
443
+ let clsStart = 0;
444
+ let clsLast = 0;
445
+ const inter = {};
446
+ let hiddenAt = visible() ? Infinity : 0;
447
+ d.addEventListener(
448
+ "visibilitychange",
449
+ () => {
450
+ if (!visible() && hiddenAt === Infinity) hiddenAt = w.performance.now();
451
+ },
452
+ true
453
+ );
454
+ observe("largest-contentful-paint", (e) => {
455
+ if (e.startTime < hiddenAt) lcp = e.startTime;
456
+ });
457
+ observe("layout-shift", (e) => {
458
+ if (e.hadRecentInput) return;
459
+ if (clsWin && e.startTime - clsLast < 1e3 && e.startTime - clsStart < 5e3) clsWin += e.value;
460
+ else {
461
+ clsWin = e.value;
462
+ clsStart = e.startTime;
463
+ }
464
+ clsLast = e.startTime;
465
+ if (clsWin > cls) cls = clsWin;
466
+ });
467
+ observe(
468
+ "event",
469
+ (e) => {
470
+ if (!e.interactionId) return;
471
+ const was = inter[e.interactionId];
472
+ if (was === void 0 || e.duration > was) inter[e.interactionId] = e.duration;
473
+ },
474
+ { durationThreshold: 40 }
475
+ );
476
+ function sendVitals() {
477
+ try {
478
+ const timing = w.performance.getEntriesByType("navigation")[0];
479
+ if (timing && timing.responseStart > 0) vital("TTFB", timing.responseStart);
480
+ } catch {
481
+ }
482
+ if (lcp >= 0) vital("LCP", lcp);
483
+ const worst = Object.keys(inter).map((k) => inter[Number(k)]);
484
+ if (worst.length) {
485
+ worst.sort((a, b) => b - a);
486
+ vital("INP", worst[Math.min(worst.length - 1, Math.floor(worst.length / 50))]);
487
+ }
488
+ if (w.LayoutShift) vital("CLS", cls);
489
+ }
490
+ let navBusy = false;
491
+ function timeNav(from) {
492
+ if (navBusy || typeof MutationObserver === "undefined" || !d.body) return;
493
+ navBusy = true;
494
+ const t0 = Date.now();
495
+ let last = t0;
496
+ const mo = new MutationObserver(() => {
497
+ last = Date.now();
498
+ });
499
+ mo.observe(d.body, { childList: true, subtree: true, attributes: true, characterData: true });
500
+ const iv = setInterval(() => {
501
+ const now = Date.now();
502
+ if (now - last < 150 && now - t0 < 1e4) return;
503
+ mo.disconnect();
504
+ clearInterval(iv);
505
+ navBusy = false;
506
+ let path = "";
507
+ try {
508
+ path = new URL(from).pathname;
509
+ } catch {
510
+ }
511
+ add("nav", { v: Math.max(last - t0, 1), props: { from: path } });
512
+ }, 50);
513
+ }
514
+ let errs = 0;
515
+ const seenErr = {};
516
+ function report(rawType, rawMsg, src, line) {
517
+ const type = String(rawType || "Error").slice(0, 60);
518
+ let msg = String(rawMsg || "");
519
+ if (msg.indexOf(type + ": ") === 0) msg = msg.slice(type.length + 2);
520
+ msg = msg.slice(0, 300);
521
+ if (!msg || seenErr[msg] || errs >= 10) return;
522
+ seenErr[msg] = 1;
523
+ errs++;
524
+ const props = { msg };
525
+ if (src) {
526
+ try {
527
+ props.src = new URL(src).pathname.slice(-120);
528
+ } catch {
529
+ }
530
+ }
531
+ if (line) props.line = line;
532
+ add("error", { n: type, props });
533
+ }
534
+ w.addEventListener("error", (ev) => {
535
+ if (ev.message) report(ev.error && ev.error.name, ev.message.replace(/^Uncaught /, ""), ev.filename, ev.lineno);
536
+ });
537
+ w.addEventListener("unhandledrejection", (ev) => {
538
+ const r = ev.reason;
539
+ report(r && r.name || "UnhandledRejection", r && r.message || r);
540
+ });
541
+ let calls = {};
542
+ let endpoints = 0;
543
+ let leaving = false;
544
+ const apiHosts = options.api ? options.api.map((h) => String(h).toLowerCase()) : [];
545
+ let ownUrl = "";
546
+ try {
547
+ ownUrl = new URL(endpoint, loc.href).href;
548
+ } catch {
549
+ }
550
+ function tidy(path) {
551
+ return path.split("/").map((s) => /^\d+$/.test(s) || /^[0-9a-f]{8}-[0-9a-f]{4}-/i.test(s) || /^[0-9a-f]{16,}$/i.test(s) || s.length >= 20 && /\d/.test(s) ? ":id" : s).join("/").slice(0, 120);
552
+ }
553
+ function called(method, rawUrl2, status, ms) {
554
+ try {
555
+ const u = new URL(String(rawUrl2), loc.href);
556
+ const same = u.host === loc.host;
557
+ if (u.protocol !== "http:" && u.protocol !== "https:" || u.href.split("?")[0] === ownUrl) return;
558
+ if (!same && apiHosts.indexOf(u.hostname.toLowerCase()) < 0) return;
559
+ if (/\.(js|mjs|css|map|png|jpe?g|gif|svg|webp|avif|ico|woff2?|ttf|html?|txt)$/i.test(u.pathname)) return;
560
+ const failed = status >= 500 || status === 0 && !leaving && visible();
561
+ if (status === 0 && !failed) return;
562
+ const name = (String(method || "GET").toUpperCase().slice(0, 7) + " " + (same ? "" : u.hostname) + tidy(u.pathname)).slice(0, 100);
563
+ let c = calls[name];
564
+ if (!c) {
565
+ if (endpoints >= 20) return;
566
+ endpoints++;
567
+ c = calls[name] = { n: 0, fail: 0, ms: 0 };
568
+ }
569
+ c.n++;
570
+ c.ms += Math.max(ms, 0);
571
+ if (failed) c.fail++;
572
+ if (!timer) timer = setTimeout(flush, 3e3);
573
+ } catch {
574
+ }
575
+ }
576
+ function drainCalls() {
577
+ const waiting = calls;
578
+ calls = {};
579
+ endpoints = 0;
580
+ for (const name in waiting) {
581
+ const c = waiting[name];
582
+ add("api", { n: name, v: Math.round(c.ms), props: { calls: c.n, failed: c.fail } });
583
+ }
584
+ }
585
+ if (options.api !== false) {
586
+ try {
587
+ if (rawFetch) {
588
+ const orig = w.fetch;
589
+ w.fetch = function(input2, init2) {
590
+ const t0 = Date.now();
591
+ const p = orig.apply(this, [input2, init2]);
592
+ try {
593
+ const req = input2;
594
+ const url = typeof input2 === "string" ? input2 : input2 instanceof URL ? input2.href : req.url;
595
+ const method = init2 && init2.method || typeof input2 === "object" && "method" in input2 && req.method || "GET";
596
+ p.then(
597
+ (r) => {
598
+ if (r.type !== "opaque") called(method, url, r.status, Date.now() - t0);
599
+ },
600
+ (e) => {
601
+ if (!e || e.name !== "AbortError") called(method, url, 0, Date.now() - t0);
602
+ }
603
+ );
604
+ } catch {
605
+ }
606
+ return p;
607
+ };
608
+ }
609
+ const X = w.XMLHttpRequest;
610
+ if (X) {
611
+ const open = X.prototype.open;
612
+ const send = X.prototype.send;
613
+ X.prototype.open = function(method, url, ...rest) {
614
+ this.__lapwy = [method, String(url)];
615
+ return open.apply(this, [method, url, ...rest]);
616
+ };
617
+ X.prototype.send = function(...args) {
618
+ const m = this.__lapwy;
619
+ if (m) {
620
+ const t0 = Date.now();
621
+ let aborted = false;
622
+ this.addEventListener("abort", () => aborted = true);
623
+ this.addEventListener("loadend", () => {
624
+ if (!aborted) called(m[0], m[1], this.status, Date.now() - t0);
625
+ });
626
+ }
627
+ return send.apply(this, args);
628
+ };
629
+ }
630
+ } catch {
631
+ }
632
+ }
633
+ w.addEventListener("pagehide", () => leaving = true);
634
+ w.addEventListener("pageshow", () => leaving = false);
635
+ function consent2(granted) {
636
+ if (!cookieMode) return;
637
+ if (granted) keepId();
638
+ else dropId();
639
+ }
640
+ if (cookieMode === "always") keepId();
641
+ else if (cookieMode === "consent" && readId()) keepId();
642
+ try {
643
+ const old = JSON.parse(w.localStorage.getItem(Q) || "[]");
644
+ const from = Date.now() - 6048e5;
645
+ queue = old.filter((e) => e && e.id && e.k && e.t > from).slice(0, 200);
646
+ kept = old.length > 0;
647
+ if (queue.length) timer = setTimeout(flush, 800);
648
+ } catch {
649
+ }
650
+ let loaded;
651
+ try {
652
+ const status = w.performance.getEntriesByType("navigation")[0].responseStatus;
653
+ if (status && status >= 400) loaded = { status, soft: false };
654
+ } catch {
655
+ }
656
+ page2(loaded);
657
+ loadUrl = pageUrl;
658
+ loadPage = pageId;
659
+ return { track: track2, page: page2, consent: consent2, notFound: notFound2 };
660
+ }
661
+
662
+ // src/index.ts
663
+ var shared = () => window;
664
+ function init(options) {
665
+ var _a;
666
+ if (typeof window === "undefined") return api;
667
+ const w = shared();
668
+ if (!w.lapwy) w.lapwy = start(options);
669
+ const waiting = (_a = w.__lapwyEarly) != null ? _a : [];
670
+ w.__lapwyEarly = [];
671
+ waiting.forEach((run) => run(w.lapwy));
672
+ return api;
673
+ }
674
+ function later(run) {
675
+ var _a;
676
+ if (typeof window === "undefined") return;
677
+ const w = shared();
678
+ if (w.lapwy) run(w.lapwy);
679
+ else {
680
+ const q = (_a = w.__lapwyEarly) != null ? _a : w.__lapwyEarly = [];
681
+ if (q.length < 100) q.push(run);
682
+ }
683
+ }
684
+ function track(name, props) {
685
+ later((t) => t.track(name, props));
686
+ }
687
+ function page(props) {
688
+ later((t) => t.page(props));
689
+ }
690
+ function consent(granted) {
691
+ later((t) => t.consent(granted));
692
+ }
693
+ function notFound(status) {
694
+ later((t) => t.notFound(status));
695
+ }
696
+ var api = { track, page, consent, notFound };
697
+ function reset() {
698
+ if (typeof window === "undefined") return;
699
+ delete shared().lapwy;
700
+ delete shared().__lapwyEarly;
701
+ }
702
+ export {
703
+ DEFAULT_ENDPOINT,
704
+ consent,
705
+ init,
706
+ notFound,
707
+ page,
708
+ reset,
709
+ track
710
+ };