@watchupltd/browser 0.1.2 → 0.2.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.d.mts CHANGED
@@ -115,6 +115,31 @@ interface WebAnalyticsPayload {
115
115
  /** ISO-8601 timestamp when the event occurred. */
116
116
  occurred_at: string;
117
117
  }
118
+ interface FlagVariant {
119
+ key: string;
120
+ weight: number;
121
+ }
122
+ interface FlagTargetingRule {
123
+ attribute: string;
124
+ operator: 'in' | 'not_in' | 'contains' | 'equals';
125
+ values: string[];
126
+ }
127
+ interface FeatureFlag {
128
+ id: string;
129
+ key: string;
130
+ name: string;
131
+ description?: string;
132
+ enabled: boolean;
133
+ rollout_percentage: number;
134
+ variants: FlagVariant[];
135
+ targeting_rules: FlagTargetingRule[];
136
+ }
137
+ interface FlagContext {
138
+ userId?: string | number;
139
+ email?: string;
140
+ plan?: string;
141
+ [key: string]: unknown;
142
+ }
118
143
 
119
144
  declare class Watchup {
120
145
  private readonly cfg;
@@ -137,7 +162,10 @@ declare class Watchup {
137
162
  * The server hashes this value before persisting.
138
163
  */
139
164
  private readonly webSessionId;
165
+ private _flags;
166
+ private _flagTimer;
140
167
  constructor(options: WatchupOptions);
168
+ private _fetchFlags;
141
169
  private _getOrCreateVisitorId;
142
170
  private _getOrCreateSessionId;
143
171
  /**
@@ -194,6 +222,26 @@ declare class Watchup {
194
222
  status?: TracePayload['status'];
195
223
  meta?: Record<string, unknown>;
196
224
  }) => void;
225
+ /**
226
+ * Check whether a feature flag is enabled. Evaluated locally — zero
227
+ * network latency. Flag rules refresh every 30 seconds in the background.
228
+ * Falls back to the identified user (via `setUser`) when no context is given.
229
+ *
230
+ * @example
231
+ * if (watchup.isEnabled('new-checkout')) {
232
+ * renderNewCheckout();
233
+ * }
234
+ */
235
+ isEnabled(key: string, ctx?: FlagContext): boolean;
236
+ /**
237
+ * Get the variant key for a multivariate (A/B) flag.
238
+ * Returns `"control"` if the flag is off or the visitor isn't in the rollout.
239
+ *
240
+ * @example
241
+ * const variant = watchup.getVariant('pricing-layout');
242
+ * // → "control" | "variant-a" | "variant-b"
243
+ */
244
+ getVariant(key: string, ctx?: FlagContext): string;
197
245
  /** Immediately flush all queued items (both telemetry and web analytics). */
198
246
  flush(): void;
199
247
  /** Stop the flush timer and release all listeners. */
@@ -203,4 +251,4 @@ declare class Watchup {
203
251
  private _timezone;
204
252
  }
205
253
 
206
- export { type ErrorPayload, type EventPayload, type IngestBatch, type TracePayload, Watchup, type WatchupOptions, type WatchupUser };
254
+ export { type ErrorPayload, type EventPayload, type FeatureFlag, type FlagContext, type FlagTargetingRule, type FlagVariant, type IngestBatch, type TracePayload, Watchup, type WatchupOptions, type WatchupUser };
package/dist/index.d.ts CHANGED
@@ -115,6 +115,31 @@ interface WebAnalyticsPayload {
115
115
  /** ISO-8601 timestamp when the event occurred. */
116
116
  occurred_at: string;
117
117
  }
118
+ interface FlagVariant {
119
+ key: string;
120
+ weight: number;
121
+ }
122
+ interface FlagTargetingRule {
123
+ attribute: string;
124
+ operator: 'in' | 'not_in' | 'contains' | 'equals';
125
+ values: string[];
126
+ }
127
+ interface FeatureFlag {
128
+ id: string;
129
+ key: string;
130
+ name: string;
131
+ description?: string;
132
+ enabled: boolean;
133
+ rollout_percentage: number;
134
+ variants: FlagVariant[];
135
+ targeting_rules: FlagTargetingRule[];
136
+ }
137
+ interface FlagContext {
138
+ userId?: string | number;
139
+ email?: string;
140
+ plan?: string;
141
+ [key: string]: unknown;
142
+ }
118
143
 
119
144
  declare class Watchup {
120
145
  private readonly cfg;
@@ -137,7 +162,10 @@ declare class Watchup {
137
162
  * The server hashes this value before persisting.
138
163
  */
139
164
  private readonly webSessionId;
165
+ private _flags;
166
+ private _flagTimer;
140
167
  constructor(options: WatchupOptions);
168
+ private _fetchFlags;
141
169
  private _getOrCreateVisitorId;
142
170
  private _getOrCreateSessionId;
143
171
  /**
@@ -194,6 +222,26 @@ declare class Watchup {
194
222
  status?: TracePayload['status'];
195
223
  meta?: Record<string, unknown>;
196
224
  }) => void;
225
+ /**
226
+ * Check whether a feature flag is enabled. Evaluated locally — zero
227
+ * network latency. Flag rules refresh every 30 seconds in the background.
228
+ * Falls back to the identified user (via `setUser`) when no context is given.
229
+ *
230
+ * @example
231
+ * if (watchup.isEnabled('new-checkout')) {
232
+ * renderNewCheckout();
233
+ * }
234
+ */
235
+ isEnabled(key: string, ctx?: FlagContext): boolean;
236
+ /**
237
+ * Get the variant key for a multivariate (A/B) flag.
238
+ * Returns `"control"` if the flag is off or the visitor isn't in the rollout.
239
+ *
240
+ * @example
241
+ * const variant = watchup.getVariant('pricing-layout');
242
+ * // → "control" | "variant-a" | "variant-b"
243
+ */
244
+ getVariant(key: string, ctx?: FlagContext): string;
197
245
  /** Immediately flush all queued items (both telemetry and web analytics). */
198
246
  flush(): void;
199
247
  /** Stop the flush timer and release all listeners. */
@@ -203,4 +251,4 @@ declare class Watchup {
203
251
  private _timezone;
204
252
  }
205
253
 
206
- export { type ErrorPayload, type EventPayload, type IngestBatch, type TracePayload, Watchup, type WatchupOptions, type WatchupUser };
254
+ export { type ErrorPayload, type EventPayload, type FeatureFlag, type FlagContext, type FlagTargetingRule, type FlagVariant, type IngestBatch, type TracePayload, Watchup, type WatchupOptions, type WatchupUser };
package/dist/index.js CHANGED
@@ -316,6 +316,34 @@ function capturePageLoad(onTrace, env) {
316
316
  }
317
317
 
318
318
  // src/watchup.ts
319
+ function flagBucket(flagKey, userId) {
320
+ const str = `${flagKey}:${userId}`;
321
+ let hash = 5381;
322
+ for (let i = 0; i < str.length; i++) {
323
+ hash = hash * 33 ^ str.charCodeAt(i) | 0;
324
+ }
325
+ return (hash >>> 0) % 100;
326
+ }
327
+ function matchesTargeting(flag, ctx) {
328
+ var _a;
329
+ if (!((_a = flag.targeting_rules) == null ? void 0 : _a.length)) return true;
330
+ return flag.targeting_rules.every((rule) => {
331
+ var _a2;
332
+ const val = String((_a2 = ctx[rule.attribute]) != null ? _a2 : "");
333
+ switch (rule.operator) {
334
+ case "in":
335
+ return rule.values.includes(val);
336
+ case "not_in":
337
+ return !rule.values.includes(val);
338
+ case "contains":
339
+ return rule.values.some((v) => val.includes(v));
340
+ case "equals":
341
+ return rule.values[0] === val;
342
+ default:
343
+ return true;
344
+ }
345
+ });
346
+ }
319
347
  var DEFAULTS = {
320
348
  baseUrl: "https://api.watchup.site",
321
349
  flushInterval: 5e3,
@@ -341,6 +369,9 @@ var Watchup = class {
341
369
  * useful for correlating all events from one user session.
342
370
  */
343
371
  this.sessionId = crypto.randomUUID();
372
+ // Feature flags
373
+ this._flags = /* @__PURE__ */ new Map();
374
+ this._flagTimer = null;
344
375
  if (!options.apiKey) {
345
376
  throw new Error("[watchup] apiKey is required.");
346
377
  }
@@ -355,6 +386,28 @@ var Watchup = class {
355
386
  this.visitorId = this._getOrCreateVisitorId();
356
387
  this.webSessionId = this._getOrCreateSessionId();
357
388
  this._setupAutoCapture();
389
+ this._fetchFlags();
390
+ this._flagTimer = setInterval(() => this._fetchFlags(), 3e4);
391
+ this.cleanup.push(() => {
392
+ if (this._flagTimer) clearInterval(this._flagTimer);
393
+ });
394
+ }
395
+ async _fetchFlags() {
396
+ var _a;
397
+ try {
398
+ const res = await fetch(`${this.cfg.baseUrl}/api/v1/flags`, {
399
+ headers: { "X-Api-Key": this.cfg.apiKey }
400
+ });
401
+ if (!res.ok) return;
402
+ const json = await res.json();
403
+ if (json.ok && ((_a = json.data) == null ? void 0 : _a.flags)) {
404
+ this._flags.clear();
405
+ for (const flag of json.data.flags) {
406
+ this._flags.set(flag.key, flag);
407
+ }
408
+ }
409
+ } catch {
410
+ }
358
411
  }
359
412
  // ── Visitor / session identity helpers ─────────────────────────────────────
360
413
  _getOrCreateVisitorId() {
@@ -505,6 +558,51 @@ var Watchup = class {
505
558
  });
506
559
  };
507
560
  }
561
+ // ── Feature flags ─────────────────────────────────────────────────────────
562
+ /**
563
+ * Check whether a feature flag is enabled. Evaluated locally — zero
564
+ * network latency. Flag rules refresh every 30 seconds in the background.
565
+ * Falls back to the identified user (via `setUser`) when no context is given.
566
+ *
567
+ * @example
568
+ * if (watchup.isEnabled('new-checkout')) {
569
+ * renderNewCheckout();
570
+ * }
571
+ */
572
+ isEnabled(key, ctx = {}) {
573
+ var _a, _b, _c, _d;
574
+ const flag = this._flags.get(key);
575
+ if (!flag || !flag.enabled) return false;
576
+ const mergedCtx = { userId: (_a = this._user) == null ? void 0 : _a.id, email: (_b = this._user) == null ? void 0 : _b.email, ...ctx };
577
+ if (!matchesTargeting(flag, mergedCtx)) return false;
578
+ if (flag.rollout_percentage >= 100) return true;
579
+ if (flag.rollout_percentage <= 0) return false;
580
+ const userId = String((_d = (_c = mergedCtx.userId) != null ? _c : mergedCtx.email) != null ? _d : this.visitorId);
581
+ return flagBucket(key, userId) < flag.rollout_percentage;
582
+ }
583
+ /**
584
+ * Get the variant key for a multivariate (A/B) flag.
585
+ * Returns `"control"` if the flag is off or the visitor isn't in the rollout.
586
+ *
587
+ * @example
588
+ * const variant = watchup.getVariant('pricing-layout');
589
+ * // → "control" | "variant-a" | "variant-b"
590
+ */
591
+ getVariant(key, ctx = {}) {
592
+ var _a, _b, _c, _d, _e;
593
+ if (!this.isEnabled(key, ctx)) return "control";
594
+ const flag = this._flags.get(key);
595
+ if (!((_a = flag.variants) == null ? void 0 : _a.length)) return "on";
596
+ const mergedCtx = { userId: (_b = this._user) == null ? void 0 : _b.id, email: (_c = this._user) == null ? void 0 : _c.email, ...ctx };
597
+ const userId = String((_e = (_d = mergedCtx.userId) != null ? _d : mergedCtx.email) != null ? _e : this.visitorId);
598
+ const bucket = flagBucket(key, userId);
599
+ let cumulative = 0;
600
+ for (const variant of flag.variants) {
601
+ cumulative += variant.weight;
602
+ if (bucket < cumulative) return variant.key;
603
+ }
604
+ return flag.variants[flag.variants.length - 1].key;
605
+ }
508
606
  /** Immediately flush all queued items (both telemetry and web analytics). */
509
607
  flush() {
510
608
  this.batcher.flush();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/transport.ts","../src/batcher.ts","../src/error-capture.ts","../src/perf.ts","../src/watchup.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · transport\n//\n// Two delivery strategies:\n// 1. fetch(keepalive: true) — for regular periodic flushes.\n// 2. navigator.sendBeacon — for page-hide/unload; survives tab close.\n//\n// Web analytics events are sent to a separate endpoint (/web-batch) that is\n// optimised for high-volume, low-latency browser hits.\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { IngestBatch, WebAnalyticsBatch } from './types.js';\n\nexport class Transport {\n private readonly url: string;\n private readonly webUrl: string;\n private readonly headers: Record<string, string>;\n private readonly debug: boolean;\n\n constructor(baseUrl: string, apiKey: string, debug = false) {\n const base = baseUrl.replace(/\\/$/, '');\n this.url = `${base}/api/v1/ingest/batch`;\n this.webUrl = `${base}/api/v1/ingest/web-batch`;\n this.headers = {\n 'Content-Type': 'application/json',\n 'X-Api-Key': apiKey,\n };\n this.debug = debug;\n }\n\n /**\n * Send via `fetch` with `keepalive: true`.\n * `keepalive` lets the request outlive the current page — it's the\n * browser equivalent of a \"fire and forget\" POST.\n * Never rejects.\n */\n async send(batch: IngestBatch): Promise<void> {\n try {\n const body = JSON.stringify(batch);\n\n // keepalive has a 64 KiB payload limit; fall back to beacon for large batches\n if (body.length > 60_000) {\n this.beacon(batch);\n return;\n }\n\n const res = await fetch(this.url, {\n method: 'POST',\n headers: this.headers,\n body,\n keepalive: true,\n });\n\n if (this.debug && !res.ok) {\n const text = await res.text().catch(() => '');\n console.warn(`[watchup] ingest ${res.status}: ${text}`);\n }\n } catch (err) {\n if (this.debug) console.warn('[watchup] send failed:', err);\n }\n }\n\n /**\n * Send web analytics batch to the dedicated /web-batch endpoint.\n * Never rejects.\n */\n async sendWeb(batch: WebAnalyticsBatch): Promise<void> {\n try {\n const body = JSON.stringify(batch);\n\n if (body.length > 60_000) {\n this.beaconWeb(batch);\n return;\n }\n\n const res = await fetch(this.webUrl, {\n method: 'POST',\n headers: this.headers,\n body,\n keepalive: true,\n });\n\n if (this.debug && !res.ok) {\n const text = await res.text().catch(() => '');\n console.warn(`[watchup] web-batch ${res.status}: ${text}`);\n }\n } catch (err) {\n if (this.debug) console.warn('[watchup] sendWeb failed:', err);\n }\n }\n\n /**\n * Send via `navigator.sendBeacon`.\n * Returns `true` if the browser accepted the request (doesn't guarantee delivery).\n * The server must accept `application/json` from sendBeacon via a Blob.\n */\n beacon(batch: IngestBatch): boolean {\n if (typeof navigator === 'undefined' || !navigator.sendBeacon) return false;\n try {\n const blob = new Blob([JSON.stringify(batch)], { type: 'application/json' });\n return navigator.sendBeacon(this.url, blob);\n } catch {\n return false;\n }\n }\n\n /**\n * sendBeacon variant for web analytics events.\n */\n beaconWeb(batch: WebAnalyticsBatch): boolean {\n if (typeof navigator === 'undefined' || !navigator.sendBeacon) return false;\n try {\n const blob = new Blob([JSON.stringify(batch)], { type: 'application/json' });\n return navigator.sendBeacon(this.webUrl, blob);\n } catch {\n return false;\n }\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · batcher\n//\n// Browser-specific flush strategy:\n// - Periodic interval flush via fetch(keepalive)\n// - visibilitychange 'hidden' + pagehide → sendBeacon for reliable exit delivery\n//\n// Two independent queues:\n// - telemetry queue (traces, errors, events) → /ingest/batch\n// - web queue (web page views) → /ingest/web-batch\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type {\n TracePayload,\n ErrorPayload,\n EventPayload,\n IngestBatch,\n WebAnalyticsPayload,\n WebAnalyticsBatch,\n} from './types.js';\nimport { Transport } from './transport.js';\n\nexport class Batcher {\n private traces: TracePayload[] = [];\n private errors: ErrorPayload[] = [];\n private events: EventPayload[] = [];\n private webViews: WebAnalyticsPayload[] = [];\n\n private readonly transport: Transport;\n private readonly flushInterval: number;\n private readonly maxBatchSize: number;\n\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n\n constructor(transport: Transport, flushInterval: number, maxBatchSize: number) {\n this.transport = transport;\n this.flushInterval = flushInterval;\n this.maxBatchSize = maxBatchSize;\n }\n\n start(): void {\n if (this.timer) return;\n\n // Periodic flush\n this.timer = setInterval(() => this.flush(), this.flushInterval);\n\n // Reliable delivery on tab hide — visibilitychange fires before the page\n // is destroyed, giving sendBeacon the best chance of succeeding.\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') this.beaconFlush();\n });\n\n // Belt-and-suspenders for browsers/environments that skip visibilitychange\n window.addEventListener('pagehide', () => this.beaconFlush(), { once: true });\n }\n\n stop(): void {\n if (this.timer) { clearInterval(this.timer); this.timer = null; }\n }\n\n // ── Telemetry queue ───────────────────────────────────────────────────────\n\n addTrace(t: TracePayload): void {\n this.traces.push(t);\n if (this.traces.length >= this.maxBatchSize) this.flush();\n }\n\n addError(e: ErrorPayload): void {\n this.errors.push(e);\n // Errors are high-priority — flush at half capacity\n if (this.errors.length >= Math.ceil(this.maxBatchSize / 2)) this.flush();\n }\n\n addEvent(e: EventPayload): void {\n this.events.push(e);\n if (this.events.length >= this.maxBatchSize) this.flush();\n }\n\n // ── Web analytics queue ───────────────────────────────────────────────────\n\n addWebView(payload: WebAnalyticsPayload): void {\n this.webViews.push(payload);\n if (this.webViews.length >= this.maxBatchSize) this.flushWeb();\n }\n\n // ── Drain helpers ─────────────────────────────────────────────────────────\n\n private drainTelemetry(): IngestBatch | null {\n const traces = this.traces.splice(0);\n const errors = this.errors.splice(0);\n const events = this.events.splice(0);\n if (!traces.length && !errors.length && !events.length) return null;\n return { traces, errors, events };\n }\n\n private drainWeb(): WebAnalyticsBatch | null {\n const web = this.webViews.splice(0);\n if (!web.length) return null;\n return { web };\n }\n\n // ── Flush ─────────────────────────────────────────────────────────────────\n\n flush(): void {\n if (!this.flushing) {\n const batch = this.drainTelemetry();\n if (batch) {\n this.flushing = true;\n this.transport.send(batch).finally(() => { this.flushing = false; });\n }\n }\n this.flushWeb();\n }\n\n flushWeb(): void {\n const batch = this.drainWeb();\n if (batch) this.transport.sendWeb(batch);\n }\n\n beaconFlush(): void {\n // Telemetry\n const batch = this.drainTelemetry();\n if (batch) {\n if (!this.transport.beacon(batch)) this.transport.send(batch);\n }\n // Web analytics\n const webBatch = this.drainWeb();\n if (webBatch) {\n if (!this.transport.beaconWeb(webBatch)) this.transport.sendWeb(webBatch);\n }\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · global error capture\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { ErrorPayload } from './types.js';\n\ntype ErrorCallback = (error: ErrorPayload) => void;\n\n/**\n * Attaches `window.onerror` and `window.addEventListener('unhandledrejection')`\n * listeners that forward caught errors to `onError`.\n *\n * Returns a cleanup function that removes both listeners.\n */\nexport function captureGlobalErrors(onError: ErrorCallback, env?: string): () => void {\n const handleError = (event: ErrorEvent) => {\n onError({\n message: event.message || 'Unknown error',\n level: 'error',\n route: window.location.pathname,\n stack: event.error?.stack,\n context: {\n url: window.location.href,\n source: event.filename ?? undefined,\n line: event.lineno ?? undefined,\n col: event.colno ?? undefined,\n },\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n };\n\n const handleRejection = (event: PromiseRejectionEvent) => {\n const reason = event.reason;\n const isErr = reason instanceof Error;\n onError({\n message: isErr ? reason.message : String(reason ?? 'Unhandled Promise rejection'),\n level: 'error',\n route: window.location.pathname,\n stack: isErr ? reason.stack : undefined,\n context: {\n url: window.location.href,\n type: 'unhandledrejection',\n },\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n };\n\n window.addEventListener('error', handleError);\n window.addEventListener('unhandledrejection', handleRejection);\n\n return () => {\n window.removeEventListener('error', handleError);\n window.removeEventListener('unhandledrejection', handleRejection);\n };\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · Web Vitals capture\n//\n// Captures FCP, LCP, and overall page-load time via PerformanceObserver and\n// PerformanceNavigationTiming. Forwarded as traces so they appear in the\n// Watchup dashboard alongside request spans.\n//\n// Thresholds come from Google's Core Web Vitals 2024 targets:\n// FCP: good ≤ 1800 ms, needs improvement ≤ 3000 ms, poor > 3000 ms\n// LCP: good ≤ 2500 ms, needs improvement ≤ 4000 ms, poor > 4000 ms\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { TracePayload } from './types.js';\n\ntype TraceCallback = (trace: TracePayload) => void;\n\nfunction rating(\n ms: number,\n good: number,\n needsImprovement: number,\n): TracePayload['status'] {\n if (ms <= good) return 'ok';\n if (ms <= needsImprovement) return 'warn';\n return 'err';\n}\n\nfunction statusCode(status: TracePayload['status']): number {\n return status === 'err' ? 500 : status === 'warn' ? 400 : 200;\n}\n\n// ── FCP — First Contentful Paint ─────────────────────────────────────────────\n\nexport function captureFCP(onTrace: TraceCallback, env?: string): void {\n if (typeof PerformanceObserver === 'undefined') return;\n try {\n const po = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n if (entry.name !== 'first-contentful-paint') continue;\n const ms = Math.round(entry.startTime);\n const status = rating(ms, 1800, 3000);\n onTrace({\n span: 'web-vital fcp',\n ms,\n status_code: statusCode(status),\n status,\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n po.disconnect();\n }\n });\n po.observe({ type: 'paint', buffered: true });\n } catch { /* PerformanceObserver 'paint' not supported */ }\n}\n\n// ── LCP — Largest Contentful Paint ───────────────────────────────────────────\n\nexport function captureLCP(onTrace: TraceCallback, env?: string): void {\n if (typeof PerformanceObserver === 'undefined') return;\n\n let last: PerformanceEntry | null = null;\n let reported = false;\n\n const report = () => {\n if (reported || !last) return;\n reported = true;\n try { po.disconnect(); } catch {}\n const ms = Math.round(last.startTime);\n const status = rating(ms, 2500, 4000);\n onTrace({\n span: 'web-vital lcp',\n ms,\n status_code: statusCode(status),\n status,\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n };\n\n let po: PerformanceObserver;\n try {\n po = new PerformanceObserver((list) => {\n const entries = list.getEntries();\n if (entries.length) last = entries[entries.length - 1] ?? null;\n });\n po.observe({ type: 'largest-contentful-paint', buffered: true });\n } catch {\n return; // 'largest-contentful-paint' not supported\n }\n\n // LCP is only finalised once the user interacts or the tab hides.\n document.addEventListener('visibilitychange', report, { once: true });\n document.addEventListener('keydown', report, { once: true, capture: true });\n document.addEventListener('pointerdown', report, { once: true, capture: true });\n}\n\n// ── Page load (overall) ───────────────────────────────────────────────────────\n\nexport function capturePageLoad(onTrace: TraceCallback, env?: string): void {\n const report = () => {\n const nav = performance.getEntriesByType('navigation')[0] as\n PerformanceNavigationTiming | undefined;\n if (!nav || nav.loadEventEnd <= 0) return;\n\n const ms = Math.round(nav.loadEventEnd - nav.startTime);\n const ttfb = Math.round(nav.responseStart - nav.requestStart);\n const status = rating(ms, 2000, 4000);\n\n onTrace({\n span: 'pageload',\n ms,\n status_code: statusCode(status),\n status,\n timestamp: new Date().toISOString(),\n meta: { ttfb },\n ...(env && { environment: env }),\n });\n };\n\n if (document.readyState === 'complete') {\n // PerformanceNavigationTiming might not be fully populated yet\n setTimeout(report, 0);\n } else {\n window.addEventListener('load', () => setTimeout(report, 100), { once: true });\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · Watchup client\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type {\n WatchupOptions,\n WatchupUser,\n TracePayload,\n ErrorPayload,\n EventPayload,\n WebAnalyticsPayload,\n} from './types.js';\nimport { Transport } from './transport.js';\nimport { Batcher } from './batcher.js';\nimport { captureGlobalErrors } from './error-capture.js';\nimport { captureFCP, captureLCP, capturePageLoad } from './perf.js';\n\nconst DEFAULTS = {\n baseUrl: 'https://api.watchup.site',\n flushInterval: 5_000,\n maxBatchSize: 100,\n debug: false,\n environment: 'production',\n release: '',\n sampleRate: 1,\n autoCapture: {\n errors: true,\n performance: true,\n pageViews: true,\n },\n} as const;\n\n// ── Storage keys ──────────────────────────────────────────────────────────────\n\nconst VISITOR_KEY = '__wup_vid';\nconst SESSION_KEY = '__wup_sid';\n\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class Watchup {\n private readonly cfg: Required<WatchupOptions>;\n private readonly batcher: Batcher;\n private readonly cleanup: Array<() => void> = [];\n private _user: WatchupUser | null = null;\n\n /**\n * A random UUID generated on init. Stable for the lifetime of the page —\n * useful for correlating all events from one user session.\n */\n readonly sessionId: string = crypto.randomUUID();\n\n // ── Visitor / session identity ─────────────────────────────────────────────\n\n /**\n * Persistent visitor ID. Stored in localStorage so it survives browser\n * sessions. Falls back to a per-session UUID when localStorage is blocked.\n * The server hashes this value with SHA-256 before persisting.\n */\n private readonly visitorId: string;\n\n /**\n * Per-session ID stored in sessionStorage. Resets on tab close.\n * The server hashes this value before persisting.\n */\n private readonly webSessionId: string;\n\n constructor(options: WatchupOptions) {\n if (!options.apiKey) {\n throw new Error('[watchup] apiKey is required.');\n }\n\n this.cfg = {\n ...DEFAULTS,\n autoCapture: { ...DEFAULTS.autoCapture, ...options.autoCapture },\n ...options,\n } as Required<WatchupOptions>;\n\n const transport = new Transport(this.cfg.baseUrl, this.cfg.apiKey, this.cfg.debug);\n this.batcher = new Batcher(transport, this.cfg.flushInterval, this.cfg.maxBatchSize);\n this.batcher.start();\n\n // Initialise visitor & session IDs\n this.visitorId = this._getOrCreateVisitorId();\n this.webSessionId = this._getOrCreateSessionId();\n\n this._setupAutoCapture();\n }\n\n // ── Visitor / session identity helpers ─────────────────────────────────────\n\n private _getOrCreateVisitorId(): string {\n try {\n let id = localStorage.getItem(VISITOR_KEY);\n if (!id) {\n id = crypto.randomUUID();\n localStorage.setItem(VISITOR_KEY, id);\n }\n return id;\n } catch {\n // localStorage blocked (private mode, etc.) — fall back to session scope\n return crypto.randomUUID();\n }\n }\n\n private _getOrCreateSessionId(): string {\n try {\n let id = sessionStorage.getItem(SESSION_KEY);\n if (!id) {\n id = crypto.randomUUID();\n sessionStorage.setItem(SESSION_KEY, id);\n }\n return id;\n } catch {\n return this.sessionId; // fallback: correlate with SDK sessionId\n }\n }\n\n // ── User identification ───────────────────────────────────────────────────\n\n /**\n * Attach a user to all subsequent errors, traces, and events.\n * Call this after login; the context persists until `clearUser()` or page reload.\n *\n * @example\n * watchup.setUser({ id: '42', email: 'ada@example.com', name: 'Ada Lovelace' });\n */\n setUser(user: WatchupUser): void {\n this._user = { ...user };\n }\n\n /**\n * Remove the current user context (e.g. after logout).\n */\n clearUser(): void {\n this._user = null;\n }\n\n // ── Public API ────────────────────────────────────────────────────────────\n\n /**\n * Track a custom analytics event.\n *\n * @example\n * watchup.track('button.clicked', { label: 'Sign Up', variant: 'A' });\n */\n track(name: string, properties?: Record<string, unknown>): void {\n if (!name) return;\n const event: EventPayload = {\n name,\n ...(properties && Object.keys(properties).length && { properties }),\n occurred_at: new Date().toISOString(),\n };\n this.batcher.addEvent(event);\n }\n\n /**\n * Track a web analytics page view (or custom web event).\n * Enriches the payload with visitor context, UTM params, and device info.\n *\n * Normally called automatically. Call manually when you need custom event_name.\n *\n * @example\n * watchup.trackWebView({ event_name: 'conversion', path: '/checkout/success' });\n */\n trackWebView(overrides: Partial<WebAnalyticsPayload> = {}): void {\n const url = new URL(window.location.href);\n const params = url.searchParams;\n\n const payload: WebAnalyticsPayload = {\n path: url.pathname + (url.search || ''),\n hostname: url.hostname,\n referrer: document.referrer || undefined,\n title: document.title || undefined,\n screen_w: window.screen?.width,\n screen_h: window.screen?.height,\n lang: navigator.language || undefined,\n timezone: this._timezone(),\n // UTM parameters\n utm_source: params.get('utm_source') || undefined,\n utm_medium: params.get('utm_medium') || undefined,\n utm_campaign: params.get('utm_campaign') || undefined,\n utm_term: params.get('utm_term') || undefined,\n utm_content: params.get('utm_content') || undefined,\n // Identity (raw; the server hashes before storing)\n visitor_id: this.visitorId,\n session_id: this.webSessionId,\n event_name: 'pageview',\n occurred_at: new Date().toISOString(),\n // Apply caller overrides last\n ...overrides,\n };\n\n this.batcher.addWebView(payload);\n }\n\n /**\n * Manually capture an error.\n *\n * @example\n * try { ... } catch (err) {\n * watchup.captureError(err, { component: 'CheckoutForm' });\n * }\n */\n captureError(\n error: Error | string | unknown,\n context?: Record<string, unknown> & { route?: string; level?: ErrorPayload['level'] },\n ): void {\n const { route, level = 'error', ...rest } = context ?? {};\n const err = error instanceof Error ? error : new Error(String(error));\n\n const payload: ErrorPayload = {\n message: err.message,\n level,\n ...(err.stack !== undefined && { stack: err.stack }),\n route: route ?? window.location.pathname,\n ...(Object.keys(rest).length && {\n context: { ...rest, url: window.location.href },\n }),\n timestamp: new Date().toISOString(),\n environment: this.cfg.environment,\n ...(this.cfg.release && { release: this.cfg.release }),\n ...(this._user && { user: this._user }),\n };\n\n this.batcher.addError(payload);\n }\n\n /**\n * Time any async operation and record it as a trace.\n * Returns an `end()` function — call it when the operation finishes.\n *\n * @example\n * const end = watchup.startTrace('fetch /api/cart');\n * const cart = await fetch('/api/cart');\n * end({ status: cart.ok ? 'ok' : 'err' });\n */\n startTrace(\n span: string,\n ): (opts?: { status?: TracePayload['status']; meta?: Record<string, unknown> }) => void {\n const start = Date.now();\n return (opts = {}) => {\n const status = opts.status ?? 'ok';\n this.batcher.addTrace({\n span,\n ms: Date.now() - start,\n status_code: status === 'err' ? 500 : status === 'warn' ? 400 : 200,\n status,\n timestamp: new Date().toISOString(),\n environment: this.cfg.environment,\n ...(this.cfg.release && { release: this.cfg.release }),\n ...(opts.meta && { meta: opts.meta }),\n ...(this._user && { user: this._user }),\n });\n };\n }\n\n /** Immediately flush all queued items (both telemetry and web analytics). */\n flush(): void { this.batcher.flush(); }\n\n /** Stop the flush timer and release all listeners. */\n shutdown(): void {\n this.batcher.stop();\n this.batcher.flush();\n this.cleanup.forEach((fn) => fn());\n }\n\n // ── Auto-capture setup ────────────────────────────────────────────────────\n\n private _setupAutoCapture(): void {\n const { autoCapture, environment } = this.cfg;\n\n if (autoCapture.errors) {\n this.cleanup.push(\n captureGlobalErrors((e) => this.batcher.addError(e), environment),\n );\n }\n\n if (autoCapture.performance) {\n captureFCP((t) => this.batcher.addTrace(t), environment);\n captureLCP((t) => this.batcher.addTrace(t), environment);\n capturePageLoad((t) => this.batcher.addTrace(t), environment);\n }\n\n if (autoCapture.pageViews) {\n this._setupPageViewTracking();\n }\n }\n\n private _setupPageViewTracking(): void {\n const trackView = () => {\n // Small delay so the page title has settled after navigation\n setTimeout(() => this.trackWebView(), 0);\n };\n\n // Initial view\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', trackView, { once: true });\n } else {\n setTimeout(() => this.trackWebView(), 0);\n }\n\n // SPA navigation — patch History API\n const origPush = history.pushState.bind(history);\n const origReplace = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n origPush(...args);\n trackView();\n };\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n origReplace(...args);\n // replaceState is often used for URL canonicalisation — don't track.\n };\n\n const onPopState = () => trackView();\n window.addEventListener('popstate', onPopState);\n\n this.cleanup.push(() => {\n history.pushState = origPush;\n history.replaceState = origReplace;\n window.removeEventListener('popstate', onPopState);\n });\n }\n\n // ── Helpers ───────────────────────────────────────────────────────────────\n\n private _timezone(): string | undefined {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n } catch {\n return undefined;\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/transport.ts","../src/batcher.ts","../src/error-capture.ts","../src/perf.ts","../src/watchup.ts"],"names":["_a"],"mappings":"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","file":"index.js","sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · transport\n//\n// Two delivery strategies:\n// 1. fetch(keepalive: true) — for regular periodic flushes.\n// 2. navigator.sendBeacon — for page-hide/unload; survives tab close.\n//\n// Web analytics events are sent to a separate endpoint (/web-batch) that is\n// optimised for high-volume, low-latency browser hits.\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { IngestBatch, WebAnalyticsBatch } from './types.js';\n\nexport class Transport {\n private readonly url: string;\n private readonly webUrl: string;\n private readonly headers: Record<string, string>;\n private readonly debug: boolean;\n\n constructor(baseUrl: string, apiKey: string, debug = false) {\n const base = baseUrl.replace(/\\/$/, '');\n this.url = `${base}/api/v1/ingest/batch`;\n this.webUrl = `${base}/api/v1/ingest/web-batch`;\n this.headers = {\n 'Content-Type': 'application/json',\n 'X-Api-Key': apiKey,\n };\n this.debug = debug;\n }\n\n /**\n * Send via `fetch` with `keepalive: true`.\n * `keepalive` lets the request outlive the current page — it's the\n * browser equivalent of a \"fire and forget\" POST.\n * Never rejects.\n */\n async send(batch: IngestBatch): Promise<void> {\n try {\n const body = JSON.stringify(batch);\n\n // keepalive has a 64 KiB payload limit; fall back to beacon for large batches\n if (body.length > 60_000) {\n this.beacon(batch);\n return;\n }\n\n const res = await fetch(this.url, {\n method: 'POST',\n headers: this.headers,\n body,\n keepalive: true,\n });\n\n if (this.debug && !res.ok) {\n const text = await res.text().catch(() => '');\n console.warn(`[watchup] ingest ${res.status}: ${text}`);\n }\n } catch (err) {\n if (this.debug) console.warn('[watchup] send failed:', err);\n }\n }\n\n /**\n * Send web analytics batch to the dedicated /web-batch endpoint.\n * Never rejects.\n */\n async sendWeb(batch: WebAnalyticsBatch): Promise<void> {\n try {\n const body = JSON.stringify(batch);\n\n if (body.length > 60_000) {\n this.beaconWeb(batch);\n return;\n }\n\n const res = await fetch(this.webUrl, {\n method: 'POST',\n headers: this.headers,\n body,\n keepalive: true,\n });\n\n if (this.debug && !res.ok) {\n const text = await res.text().catch(() => '');\n console.warn(`[watchup] web-batch ${res.status}: ${text}`);\n }\n } catch (err) {\n if (this.debug) console.warn('[watchup] sendWeb failed:', err);\n }\n }\n\n /**\n * Send via `navigator.sendBeacon`.\n * Returns `true` if the browser accepted the request (doesn't guarantee delivery).\n * The server must accept `application/json` from sendBeacon via a Blob.\n */\n beacon(batch: IngestBatch): boolean {\n if (typeof navigator === 'undefined' || !navigator.sendBeacon) return false;\n try {\n const blob = new Blob([JSON.stringify(batch)], { type: 'application/json' });\n return navigator.sendBeacon(this.url, blob);\n } catch {\n return false;\n }\n }\n\n /**\n * sendBeacon variant for web analytics events.\n */\n beaconWeb(batch: WebAnalyticsBatch): boolean {\n if (typeof navigator === 'undefined' || !navigator.sendBeacon) return false;\n try {\n const blob = new Blob([JSON.stringify(batch)], { type: 'application/json' });\n return navigator.sendBeacon(this.webUrl, blob);\n } catch {\n return false;\n }\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · batcher\n//\n// Browser-specific flush strategy:\n// - Periodic interval flush via fetch(keepalive)\n// - visibilitychange 'hidden' + pagehide → sendBeacon for reliable exit delivery\n//\n// Two independent queues:\n// - telemetry queue (traces, errors, events) → /ingest/batch\n// - web queue (web page views) → /ingest/web-batch\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type {\n TracePayload,\n ErrorPayload,\n EventPayload,\n IngestBatch,\n WebAnalyticsPayload,\n WebAnalyticsBatch,\n} from './types.js';\nimport { Transport } from './transport.js';\n\nexport class Batcher {\n private traces: TracePayload[] = [];\n private errors: ErrorPayload[] = [];\n private events: EventPayload[] = [];\n private webViews: WebAnalyticsPayload[] = [];\n\n private readonly transport: Transport;\n private readonly flushInterval: number;\n private readonly maxBatchSize: number;\n\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n\n constructor(transport: Transport, flushInterval: number, maxBatchSize: number) {\n this.transport = transport;\n this.flushInterval = flushInterval;\n this.maxBatchSize = maxBatchSize;\n }\n\n start(): void {\n if (this.timer) return;\n\n // Periodic flush\n this.timer = setInterval(() => this.flush(), this.flushInterval);\n\n // Reliable delivery on tab hide — visibilitychange fires before the page\n // is destroyed, giving sendBeacon the best chance of succeeding.\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') this.beaconFlush();\n });\n\n // Belt-and-suspenders for browsers/environments that skip visibilitychange\n window.addEventListener('pagehide', () => this.beaconFlush(), { once: true });\n }\n\n stop(): void {\n if (this.timer) { clearInterval(this.timer); this.timer = null; }\n }\n\n // ── Telemetry queue ───────────────────────────────────────────────────────\n\n addTrace(t: TracePayload): void {\n this.traces.push(t);\n if (this.traces.length >= this.maxBatchSize) this.flush();\n }\n\n addError(e: ErrorPayload): void {\n this.errors.push(e);\n // Errors are high-priority — flush at half capacity\n if (this.errors.length >= Math.ceil(this.maxBatchSize / 2)) this.flush();\n }\n\n addEvent(e: EventPayload): void {\n this.events.push(e);\n if (this.events.length >= this.maxBatchSize) this.flush();\n }\n\n // ── Web analytics queue ───────────────────────────────────────────────────\n\n addWebView(payload: WebAnalyticsPayload): void {\n this.webViews.push(payload);\n if (this.webViews.length >= this.maxBatchSize) this.flushWeb();\n }\n\n // ── Drain helpers ─────────────────────────────────────────────────────────\n\n private drainTelemetry(): IngestBatch | null {\n const traces = this.traces.splice(0);\n const errors = this.errors.splice(0);\n const events = this.events.splice(0);\n if (!traces.length && !errors.length && !events.length) return null;\n return { traces, errors, events };\n }\n\n private drainWeb(): WebAnalyticsBatch | null {\n const web = this.webViews.splice(0);\n if (!web.length) return null;\n return { web };\n }\n\n // ── Flush ─────────────────────────────────────────────────────────────────\n\n flush(): void {\n if (!this.flushing) {\n const batch = this.drainTelemetry();\n if (batch) {\n this.flushing = true;\n this.transport.send(batch).finally(() => { this.flushing = false; });\n }\n }\n this.flushWeb();\n }\n\n flushWeb(): void {\n const batch = this.drainWeb();\n if (batch) this.transport.sendWeb(batch);\n }\n\n beaconFlush(): void {\n // Telemetry\n const batch = this.drainTelemetry();\n if (batch) {\n if (!this.transport.beacon(batch)) this.transport.send(batch);\n }\n // Web analytics\n const webBatch = this.drainWeb();\n if (webBatch) {\n if (!this.transport.beaconWeb(webBatch)) this.transport.sendWeb(webBatch);\n }\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · global error capture\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { ErrorPayload } from './types.js';\n\ntype ErrorCallback = (error: ErrorPayload) => void;\n\n/**\n * Attaches `window.onerror` and `window.addEventListener('unhandledrejection')`\n * listeners that forward caught errors to `onError`.\n *\n * Returns a cleanup function that removes both listeners.\n */\nexport function captureGlobalErrors(onError: ErrorCallback, env?: string): () => void {\n const handleError = (event: ErrorEvent) => {\n onError({\n message: event.message || 'Unknown error',\n level: 'error',\n route: window.location.pathname,\n stack: event.error?.stack,\n context: {\n url: window.location.href,\n source: event.filename ?? undefined,\n line: event.lineno ?? undefined,\n col: event.colno ?? undefined,\n },\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n };\n\n const handleRejection = (event: PromiseRejectionEvent) => {\n const reason = event.reason;\n const isErr = reason instanceof Error;\n onError({\n message: isErr ? reason.message : String(reason ?? 'Unhandled Promise rejection'),\n level: 'error',\n route: window.location.pathname,\n stack: isErr ? reason.stack : undefined,\n context: {\n url: window.location.href,\n type: 'unhandledrejection',\n },\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n };\n\n window.addEventListener('error', handleError);\n window.addEventListener('unhandledrejection', handleRejection);\n\n return () => {\n window.removeEventListener('error', handleError);\n window.removeEventListener('unhandledrejection', handleRejection);\n };\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · Web Vitals capture\n//\n// Captures FCP, LCP, and overall page-load time via PerformanceObserver and\n// PerformanceNavigationTiming. Forwarded as traces so they appear in the\n// Watchup dashboard alongside request spans.\n//\n// Thresholds come from Google's Core Web Vitals 2024 targets:\n// FCP: good ≤ 1800 ms, needs improvement ≤ 3000 ms, poor > 3000 ms\n// LCP: good ≤ 2500 ms, needs improvement ≤ 4000 ms, poor > 4000 ms\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { TracePayload } from './types.js';\n\ntype TraceCallback = (trace: TracePayload) => void;\n\nfunction rating(\n ms: number,\n good: number,\n needsImprovement: number,\n): TracePayload['status'] {\n if (ms <= good) return 'ok';\n if (ms <= needsImprovement) return 'warn';\n return 'err';\n}\n\nfunction statusCode(status: TracePayload['status']): number {\n return status === 'err' ? 500 : status === 'warn' ? 400 : 200;\n}\n\n// ── FCP — First Contentful Paint ─────────────────────────────────────────────\n\nexport function captureFCP(onTrace: TraceCallback, env?: string): void {\n if (typeof PerformanceObserver === 'undefined') return;\n try {\n const po = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n if (entry.name !== 'first-contentful-paint') continue;\n const ms = Math.round(entry.startTime);\n const status = rating(ms, 1800, 3000);\n onTrace({\n span: 'web-vital fcp',\n ms,\n status_code: statusCode(status),\n status,\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n po.disconnect();\n }\n });\n po.observe({ type: 'paint', buffered: true });\n } catch { /* PerformanceObserver 'paint' not supported */ }\n}\n\n// ── LCP — Largest Contentful Paint ───────────────────────────────────────────\n\nexport function captureLCP(onTrace: TraceCallback, env?: string): void {\n if (typeof PerformanceObserver === 'undefined') return;\n\n let last: PerformanceEntry | null = null;\n let reported = false;\n\n const report = () => {\n if (reported || !last) return;\n reported = true;\n try { po.disconnect(); } catch {}\n const ms = Math.round(last.startTime);\n const status = rating(ms, 2500, 4000);\n onTrace({\n span: 'web-vital lcp',\n ms,\n status_code: statusCode(status),\n status,\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n };\n\n let po: PerformanceObserver;\n try {\n po = new PerformanceObserver((list) => {\n const entries = list.getEntries();\n if (entries.length) last = entries[entries.length - 1] ?? null;\n });\n po.observe({ type: 'largest-contentful-paint', buffered: true });\n } catch {\n return; // 'largest-contentful-paint' not supported\n }\n\n // LCP is only finalised once the user interacts or the tab hides.\n document.addEventListener('visibilitychange', report, { once: true });\n document.addEventListener('keydown', report, { once: true, capture: true });\n document.addEventListener('pointerdown', report, { once: true, capture: true });\n}\n\n// ── Page load (overall) ───────────────────────────────────────────────────────\n\nexport function capturePageLoad(onTrace: TraceCallback, env?: string): void {\n const report = () => {\n const nav = performance.getEntriesByType('navigation')[0] as\n PerformanceNavigationTiming | undefined;\n if (!nav || nav.loadEventEnd <= 0) return;\n\n const ms = Math.round(nav.loadEventEnd - nav.startTime);\n const ttfb = Math.round(nav.responseStart - nav.requestStart);\n const status = rating(ms, 2000, 4000);\n\n onTrace({\n span: 'pageload',\n ms,\n status_code: statusCode(status),\n status,\n timestamp: new Date().toISOString(),\n meta: { ttfb },\n ...(env && { environment: env }),\n });\n };\n\n if (document.readyState === 'complete') {\n // PerformanceNavigationTiming might not be fully populated yet\n setTimeout(report, 0);\n } else {\n window.addEventListener('load', () => setTimeout(report, 100), { once: true });\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · Watchup client\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type {\n WatchupOptions,\n WatchupUser,\n TracePayload,\n ErrorPayload,\n EventPayload,\n WebAnalyticsPayload,\n FeatureFlag,\n FlagContext,\n} from './types.js';\nimport { Transport } from './transport.js';\nimport { Batcher } from './batcher.js';\nimport { captureGlobalErrors } from './error-capture.js';\nimport { captureFCP, captureLCP, capturePageLoad } from './perf.js';\n\n// ── Flag evaluation helpers ───────────────────────────────────────────────────\n\nfunction flagBucket(flagKey: string, userId: string): number {\n const str = `${flagKey}:${userId}`;\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = ((hash * 33) ^ str.charCodeAt(i)) | 0;\n }\n return (hash >>> 0) % 100;\n}\n\nfunction matchesTargeting(flag: FeatureFlag, ctx: FlagContext): boolean {\n if (!flag.targeting_rules?.length) return true;\n return flag.targeting_rules.every(rule => {\n const val = String(ctx[rule.attribute] ?? '');\n switch (rule.operator) {\n case 'in': return rule.values.includes(val);\n case 'not_in': return !rule.values.includes(val);\n case 'contains': return rule.values.some(v => val.includes(v));\n case 'equals': return rule.values[0] === val;\n default: return true;\n }\n });\n}\n\nconst DEFAULTS = {\n baseUrl: 'https://api.watchup.site',\n flushInterval: 5_000,\n maxBatchSize: 100,\n debug: false,\n environment: 'production',\n release: '',\n sampleRate: 1,\n autoCapture: {\n errors: true,\n performance: true,\n pageViews: true,\n },\n} as const;\n\n// ── Storage keys ──────────────────────────────────────────────────────────────\n\nconst VISITOR_KEY = '__wup_vid';\nconst SESSION_KEY = '__wup_sid';\n\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class Watchup {\n private readonly cfg: Required<WatchupOptions>;\n private readonly batcher: Batcher;\n private readonly cleanup: Array<() => void> = [];\n private _user: WatchupUser | null = null;\n\n /**\n * A random UUID generated on init. Stable for the lifetime of the page —\n * useful for correlating all events from one user session.\n */\n readonly sessionId: string = crypto.randomUUID();\n\n // ── Visitor / session identity ─────────────────────────────────────────────\n\n /**\n * Persistent visitor ID. Stored in localStorage so it survives browser\n * sessions. Falls back to a per-session UUID when localStorage is blocked.\n * The server hashes this value with SHA-256 before persisting.\n */\n private readonly visitorId: string;\n\n /**\n * Per-session ID stored in sessionStorage. Resets on tab close.\n * The server hashes this value before persisting.\n */\n private readonly webSessionId: string;\n\n // Feature flags\n private _flags: Map<string, FeatureFlag> = new Map();\n private _flagTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: WatchupOptions) {\n if (!options.apiKey) {\n throw new Error('[watchup] apiKey is required.');\n }\n\n this.cfg = {\n ...DEFAULTS,\n autoCapture: { ...DEFAULTS.autoCapture, ...options.autoCapture },\n ...options,\n } as Required<WatchupOptions>;\n\n const transport = new Transport(this.cfg.baseUrl, this.cfg.apiKey, this.cfg.debug);\n this.batcher = new Batcher(transport, this.cfg.flushInterval, this.cfg.maxBatchSize);\n this.batcher.start();\n\n // Initialise visitor & session IDs\n this.visitorId = this._getOrCreateVisitorId();\n this.webSessionId = this._getOrCreateSessionId();\n\n this._setupAutoCapture();\n\n // Fetch flags immediately, then poll every 30 seconds\n this._fetchFlags();\n this._flagTimer = setInterval(() => this._fetchFlags(), 30_000);\n this.cleanup.push(() => {\n if (this._flagTimer) clearInterval(this._flagTimer);\n });\n }\n\n private async _fetchFlags(): Promise<void> {\n try {\n const res = await fetch(`${this.cfg.baseUrl}/api/v1/flags`, {\n headers: { 'X-Api-Key': this.cfg.apiKey },\n });\n if (!res.ok) return;\n const json = await res.json() as { ok: boolean; data?: { flags: FeatureFlag[] } };\n if (json.ok && json.data?.flags) {\n this._flags.clear();\n for (const flag of json.data.flags) {\n this._flags.set(flag.key, flag);\n }\n }\n } catch {\n // silently ignore — stale cache is fine\n }\n }\n\n // ── Visitor / session identity helpers ─────────────────────────────────────\n\n private _getOrCreateVisitorId(): string {\n try {\n let id = localStorage.getItem(VISITOR_KEY);\n if (!id) {\n id = crypto.randomUUID();\n localStorage.setItem(VISITOR_KEY, id);\n }\n return id;\n } catch {\n // localStorage blocked (private mode, etc.) — fall back to session scope\n return crypto.randomUUID();\n }\n }\n\n private _getOrCreateSessionId(): string {\n try {\n let id = sessionStorage.getItem(SESSION_KEY);\n if (!id) {\n id = crypto.randomUUID();\n sessionStorage.setItem(SESSION_KEY, id);\n }\n return id;\n } catch {\n return this.sessionId; // fallback: correlate with SDK sessionId\n }\n }\n\n // ── User identification ───────────────────────────────────────────────────\n\n /**\n * Attach a user to all subsequent errors, traces, and events.\n * Call this after login; the context persists until `clearUser()` or page reload.\n *\n * @example\n * watchup.setUser({ id: '42', email: 'ada@example.com', name: 'Ada Lovelace' });\n */\n setUser(user: WatchupUser): void {\n this._user = { ...user };\n }\n\n /**\n * Remove the current user context (e.g. after logout).\n */\n clearUser(): void {\n this._user = null;\n }\n\n // ── Public API ────────────────────────────────────────────────────────────\n\n /**\n * Track a custom analytics event.\n *\n * @example\n * watchup.track('button.clicked', { label: 'Sign Up', variant: 'A' });\n */\n track(name: string, properties?: Record<string, unknown>): void {\n if (!name) return;\n const event: EventPayload = {\n name,\n ...(properties && Object.keys(properties).length && { properties }),\n occurred_at: new Date().toISOString(),\n };\n this.batcher.addEvent(event);\n }\n\n /**\n * Track a web analytics page view (or custom web event).\n * Enriches the payload with visitor context, UTM params, and device info.\n *\n * Normally called automatically. Call manually when you need custom event_name.\n *\n * @example\n * watchup.trackWebView({ event_name: 'conversion', path: '/checkout/success' });\n */\n trackWebView(overrides: Partial<WebAnalyticsPayload> = {}): void {\n const url = new URL(window.location.href);\n const params = url.searchParams;\n\n const payload: WebAnalyticsPayload = {\n path: url.pathname + (url.search || ''),\n hostname: url.hostname,\n referrer: document.referrer || undefined,\n title: document.title || undefined,\n screen_w: window.screen?.width,\n screen_h: window.screen?.height,\n lang: navigator.language || undefined,\n timezone: this._timezone(),\n // UTM parameters\n utm_source: params.get('utm_source') || undefined,\n utm_medium: params.get('utm_medium') || undefined,\n utm_campaign: params.get('utm_campaign') || undefined,\n utm_term: params.get('utm_term') || undefined,\n utm_content: params.get('utm_content') || undefined,\n // Identity (raw; the server hashes before storing)\n visitor_id: this.visitorId,\n session_id: this.webSessionId,\n event_name: 'pageview',\n occurred_at: new Date().toISOString(),\n // Apply caller overrides last\n ...overrides,\n };\n\n this.batcher.addWebView(payload);\n }\n\n /**\n * Manually capture an error.\n *\n * @example\n * try { ... } catch (err) {\n * watchup.captureError(err, { component: 'CheckoutForm' });\n * }\n */\n captureError(\n error: Error | string | unknown,\n context?: Record<string, unknown> & { route?: string; level?: ErrorPayload['level'] },\n ): void {\n const { route, level = 'error', ...rest } = context ?? {};\n const err = error instanceof Error ? error : new Error(String(error));\n\n const payload: ErrorPayload = {\n message: err.message,\n level,\n ...(err.stack !== undefined && { stack: err.stack }),\n route: route ?? window.location.pathname,\n ...(Object.keys(rest).length && {\n context: { ...rest, url: window.location.href },\n }),\n timestamp: new Date().toISOString(),\n environment: this.cfg.environment,\n ...(this.cfg.release && { release: this.cfg.release }),\n ...(this._user && { user: this._user }),\n };\n\n this.batcher.addError(payload);\n }\n\n /**\n * Time any async operation and record it as a trace.\n * Returns an `end()` function — call it when the operation finishes.\n *\n * @example\n * const end = watchup.startTrace('fetch /api/cart');\n * const cart = await fetch('/api/cart');\n * end({ status: cart.ok ? 'ok' : 'err' });\n */\n startTrace(\n span: string,\n ): (opts?: { status?: TracePayload['status']; meta?: Record<string, unknown> }) => void {\n const start = Date.now();\n return (opts = {}) => {\n const status = opts.status ?? 'ok';\n this.batcher.addTrace({\n span,\n ms: Date.now() - start,\n status_code: status === 'err' ? 500 : status === 'warn' ? 400 : 200,\n status,\n timestamp: new Date().toISOString(),\n environment: this.cfg.environment,\n ...(this.cfg.release && { release: this.cfg.release }),\n ...(opts.meta && { meta: opts.meta }),\n ...(this._user && { user: this._user }),\n });\n };\n }\n\n // ── Feature flags ─────────────────────────────────────────────────────────\n\n /**\n * Check whether a feature flag is enabled. Evaluated locally — zero\n * network latency. Flag rules refresh every 30 seconds in the background.\n * Falls back to the identified user (via `setUser`) when no context is given.\n *\n * @example\n * if (watchup.isEnabled('new-checkout')) {\n * renderNewCheckout();\n * }\n */\n isEnabled(key: string, ctx: FlagContext = {}): boolean {\n const flag = this._flags.get(key);\n if (!flag || !flag.enabled) return false;\n const mergedCtx: FlagContext = { userId: this._user?.id, email: this._user?.email, ...ctx };\n if (!matchesTargeting(flag, mergedCtx)) return false;\n if (flag.rollout_percentage >= 100) return true;\n if (flag.rollout_percentage <= 0) return false;\n const userId = String(mergedCtx.userId ?? mergedCtx.email ?? this.visitorId);\n return flagBucket(key, userId) < flag.rollout_percentage;\n }\n\n /**\n * Get the variant key for a multivariate (A/B) flag.\n * Returns `\"control\"` if the flag is off or the visitor isn't in the rollout.\n *\n * @example\n * const variant = watchup.getVariant('pricing-layout');\n * // → \"control\" | \"variant-a\" | \"variant-b\"\n */\n getVariant(key: string, ctx: FlagContext = {}): string {\n if (!this.isEnabled(key, ctx)) return 'control';\n const flag = this._flags.get(key)!;\n if (!flag.variants?.length) return 'on';\n\n const mergedCtx: FlagContext = { userId: this._user?.id, email: this._user?.email, ...ctx };\n const userId = String(mergedCtx.userId ?? mergedCtx.email ?? this.visitorId);\n const bucket = flagBucket(key, userId);\n\n let cumulative = 0;\n for (const variant of flag.variants) {\n cumulative += variant.weight;\n if (bucket < cumulative) return variant.key;\n }\n return flag.variants[flag.variants.length - 1]!.key;\n }\n\n /** Immediately flush all queued items (both telemetry and web analytics). */\n flush(): void { this.batcher.flush(); }\n\n /** Stop the flush timer and release all listeners. */\n shutdown(): void {\n this.batcher.stop();\n this.batcher.flush();\n this.cleanup.forEach((fn) => fn());\n }\n\n // ── Auto-capture setup ────────────────────────────────────────────────────\n\n private _setupAutoCapture(): void {\n const { autoCapture, environment } = this.cfg;\n\n if (autoCapture.errors) {\n this.cleanup.push(\n captureGlobalErrors((e) => this.batcher.addError(e), environment),\n );\n }\n\n if (autoCapture.performance) {\n captureFCP((t) => this.batcher.addTrace(t), environment);\n captureLCP((t) => this.batcher.addTrace(t), environment);\n capturePageLoad((t) => this.batcher.addTrace(t), environment);\n }\n\n if (autoCapture.pageViews) {\n this._setupPageViewTracking();\n }\n }\n\n private _setupPageViewTracking(): void {\n const trackView = () => {\n // Small delay so the page title has settled after navigation\n setTimeout(() => this.trackWebView(), 0);\n };\n\n // Initial view\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', trackView, { once: true });\n } else {\n setTimeout(() => this.trackWebView(), 0);\n }\n\n // SPA navigation — patch History API\n const origPush = history.pushState.bind(history);\n const origReplace = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n origPush(...args);\n trackView();\n };\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n origReplace(...args);\n // replaceState is often used for URL canonicalisation — don't track.\n };\n\n const onPopState = () => trackView();\n window.addEventListener('popstate', onPopState);\n\n this.cleanup.push(() => {\n history.pushState = origPush;\n history.replaceState = origReplace;\n window.removeEventListener('popstate', onPopState);\n });\n }\n\n // ── Helpers ───────────────────────────────────────────────────────────────\n\n private _timezone(): string | undefined {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n } catch {\n return undefined;\n }\n }\n}\n"]}
package/dist/index.mjs CHANGED
@@ -314,6 +314,34 @@ function capturePageLoad(onTrace, env) {
314
314
  }
315
315
 
316
316
  // src/watchup.ts
317
+ function flagBucket(flagKey, userId) {
318
+ const str = `${flagKey}:${userId}`;
319
+ let hash = 5381;
320
+ for (let i = 0; i < str.length; i++) {
321
+ hash = hash * 33 ^ str.charCodeAt(i) | 0;
322
+ }
323
+ return (hash >>> 0) % 100;
324
+ }
325
+ function matchesTargeting(flag, ctx) {
326
+ var _a;
327
+ if (!((_a = flag.targeting_rules) == null ? void 0 : _a.length)) return true;
328
+ return flag.targeting_rules.every((rule) => {
329
+ var _a2;
330
+ const val = String((_a2 = ctx[rule.attribute]) != null ? _a2 : "");
331
+ switch (rule.operator) {
332
+ case "in":
333
+ return rule.values.includes(val);
334
+ case "not_in":
335
+ return !rule.values.includes(val);
336
+ case "contains":
337
+ return rule.values.some((v) => val.includes(v));
338
+ case "equals":
339
+ return rule.values[0] === val;
340
+ default:
341
+ return true;
342
+ }
343
+ });
344
+ }
317
345
  var DEFAULTS = {
318
346
  baseUrl: "https://api.watchup.site",
319
347
  flushInterval: 5e3,
@@ -339,6 +367,9 @@ var Watchup = class {
339
367
  * useful for correlating all events from one user session.
340
368
  */
341
369
  this.sessionId = crypto.randomUUID();
370
+ // Feature flags
371
+ this._flags = /* @__PURE__ */ new Map();
372
+ this._flagTimer = null;
342
373
  if (!options.apiKey) {
343
374
  throw new Error("[watchup] apiKey is required.");
344
375
  }
@@ -353,6 +384,28 @@ var Watchup = class {
353
384
  this.visitorId = this._getOrCreateVisitorId();
354
385
  this.webSessionId = this._getOrCreateSessionId();
355
386
  this._setupAutoCapture();
387
+ this._fetchFlags();
388
+ this._flagTimer = setInterval(() => this._fetchFlags(), 3e4);
389
+ this.cleanup.push(() => {
390
+ if (this._flagTimer) clearInterval(this._flagTimer);
391
+ });
392
+ }
393
+ async _fetchFlags() {
394
+ var _a;
395
+ try {
396
+ const res = await fetch(`${this.cfg.baseUrl}/api/v1/flags`, {
397
+ headers: { "X-Api-Key": this.cfg.apiKey }
398
+ });
399
+ if (!res.ok) return;
400
+ const json = await res.json();
401
+ if (json.ok && ((_a = json.data) == null ? void 0 : _a.flags)) {
402
+ this._flags.clear();
403
+ for (const flag of json.data.flags) {
404
+ this._flags.set(flag.key, flag);
405
+ }
406
+ }
407
+ } catch {
408
+ }
356
409
  }
357
410
  // ── Visitor / session identity helpers ─────────────────────────────────────
358
411
  _getOrCreateVisitorId() {
@@ -503,6 +556,51 @@ var Watchup = class {
503
556
  });
504
557
  };
505
558
  }
559
+ // ── Feature flags ─────────────────────────────────────────────────────────
560
+ /**
561
+ * Check whether a feature flag is enabled. Evaluated locally — zero
562
+ * network latency. Flag rules refresh every 30 seconds in the background.
563
+ * Falls back to the identified user (via `setUser`) when no context is given.
564
+ *
565
+ * @example
566
+ * if (watchup.isEnabled('new-checkout')) {
567
+ * renderNewCheckout();
568
+ * }
569
+ */
570
+ isEnabled(key, ctx = {}) {
571
+ var _a, _b, _c, _d;
572
+ const flag = this._flags.get(key);
573
+ if (!flag || !flag.enabled) return false;
574
+ const mergedCtx = { userId: (_a = this._user) == null ? void 0 : _a.id, email: (_b = this._user) == null ? void 0 : _b.email, ...ctx };
575
+ if (!matchesTargeting(flag, mergedCtx)) return false;
576
+ if (flag.rollout_percentage >= 100) return true;
577
+ if (flag.rollout_percentage <= 0) return false;
578
+ const userId = String((_d = (_c = mergedCtx.userId) != null ? _c : mergedCtx.email) != null ? _d : this.visitorId);
579
+ return flagBucket(key, userId) < flag.rollout_percentage;
580
+ }
581
+ /**
582
+ * Get the variant key for a multivariate (A/B) flag.
583
+ * Returns `"control"` if the flag is off or the visitor isn't in the rollout.
584
+ *
585
+ * @example
586
+ * const variant = watchup.getVariant('pricing-layout');
587
+ * // → "control" | "variant-a" | "variant-b"
588
+ */
589
+ getVariant(key, ctx = {}) {
590
+ var _a, _b, _c, _d, _e;
591
+ if (!this.isEnabled(key, ctx)) return "control";
592
+ const flag = this._flags.get(key);
593
+ if (!((_a = flag.variants) == null ? void 0 : _a.length)) return "on";
594
+ const mergedCtx = { userId: (_b = this._user) == null ? void 0 : _b.id, email: (_c = this._user) == null ? void 0 : _c.email, ...ctx };
595
+ const userId = String((_e = (_d = mergedCtx.userId) != null ? _d : mergedCtx.email) != null ? _e : this.visitorId);
596
+ const bucket = flagBucket(key, userId);
597
+ let cumulative = 0;
598
+ for (const variant of flag.variants) {
599
+ cumulative += variant.weight;
600
+ if (bucket < cumulative) return variant.key;
601
+ }
602
+ return flag.variants[flag.variants.length - 1].key;
603
+ }
506
604
  /** Immediately flush all queued items (both telemetry and web analytics). */
507
605
  flush() {
508
606
  this.batcher.flush();
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/transport.ts","../src/batcher.ts","../src/error-capture.ts","../src/perf.ts","../src/watchup.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · transport\n//\n// Two delivery strategies:\n// 1. fetch(keepalive: true) — for regular periodic flushes.\n// 2. navigator.sendBeacon — for page-hide/unload; survives tab close.\n//\n// Web analytics events are sent to a separate endpoint (/web-batch) that is\n// optimised for high-volume, low-latency browser hits.\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { IngestBatch, WebAnalyticsBatch } from './types.js';\n\nexport class Transport {\n private readonly url: string;\n private readonly webUrl: string;\n private readonly headers: Record<string, string>;\n private readonly debug: boolean;\n\n constructor(baseUrl: string, apiKey: string, debug = false) {\n const base = baseUrl.replace(/\\/$/, '');\n this.url = `${base}/api/v1/ingest/batch`;\n this.webUrl = `${base}/api/v1/ingest/web-batch`;\n this.headers = {\n 'Content-Type': 'application/json',\n 'X-Api-Key': apiKey,\n };\n this.debug = debug;\n }\n\n /**\n * Send via `fetch` with `keepalive: true`.\n * `keepalive` lets the request outlive the current page — it's the\n * browser equivalent of a \"fire and forget\" POST.\n * Never rejects.\n */\n async send(batch: IngestBatch): Promise<void> {\n try {\n const body = JSON.stringify(batch);\n\n // keepalive has a 64 KiB payload limit; fall back to beacon for large batches\n if (body.length > 60_000) {\n this.beacon(batch);\n return;\n }\n\n const res = await fetch(this.url, {\n method: 'POST',\n headers: this.headers,\n body,\n keepalive: true,\n });\n\n if (this.debug && !res.ok) {\n const text = await res.text().catch(() => '');\n console.warn(`[watchup] ingest ${res.status}: ${text}`);\n }\n } catch (err) {\n if (this.debug) console.warn('[watchup] send failed:', err);\n }\n }\n\n /**\n * Send web analytics batch to the dedicated /web-batch endpoint.\n * Never rejects.\n */\n async sendWeb(batch: WebAnalyticsBatch): Promise<void> {\n try {\n const body = JSON.stringify(batch);\n\n if (body.length > 60_000) {\n this.beaconWeb(batch);\n return;\n }\n\n const res = await fetch(this.webUrl, {\n method: 'POST',\n headers: this.headers,\n body,\n keepalive: true,\n });\n\n if (this.debug && !res.ok) {\n const text = await res.text().catch(() => '');\n console.warn(`[watchup] web-batch ${res.status}: ${text}`);\n }\n } catch (err) {\n if (this.debug) console.warn('[watchup] sendWeb failed:', err);\n }\n }\n\n /**\n * Send via `navigator.sendBeacon`.\n * Returns `true` if the browser accepted the request (doesn't guarantee delivery).\n * The server must accept `application/json` from sendBeacon via a Blob.\n */\n beacon(batch: IngestBatch): boolean {\n if (typeof navigator === 'undefined' || !navigator.sendBeacon) return false;\n try {\n const blob = new Blob([JSON.stringify(batch)], { type: 'application/json' });\n return navigator.sendBeacon(this.url, blob);\n } catch {\n return false;\n }\n }\n\n /**\n * sendBeacon variant for web analytics events.\n */\n beaconWeb(batch: WebAnalyticsBatch): boolean {\n if (typeof navigator === 'undefined' || !navigator.sendBeacon) return false;\n try {\n const blob = new Blob([JSON.stringify(batch)], { type: 'application/json' });\n return navigator.sendBeacon(this.webUrl, blob);\n } catch {\n return false;\n }\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · batcher\n//\n// Browser-specific flush strategy:\n// - Periodic interval flush via fetch(keepalive)\n// - visibilitychange 'hidden' + pagehide → sendBeacon for reliable exit delivery\n//\n// Two independent queues:\n// - telemetry queue (traces, errors, events) → /ingest/batch\n// - web queue (web page views) → /ingest/web-batch\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type {\n TracePayload,\n ErrorPayload,\n EventPayload,\n IngestBatch,\n WebAnalyticsPayload,\n WebAnalyticsBatch,\n} from './types.js';\nimport { Transport } from './transport.js';\n\nexport class Batcher {\n private traces: TracePayload[] = [];\n private errors: ErrorPayload[] = [];\n private events: EventPayload[] = [];\n private webViews: WebAnalyticsPayload[] = [];\n\n private readonly transport: Transport;\n private readonly flushInterval: number;\n private readonly maxBatchSize: number;\n\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n\n constructor(transport: Transport, flushInterval: number, maxBatchSize: number) {\n this.transport = transport;\n this.flushInterval = flushInterval;\n this.maxBatchSize = maxBatchSize;\n }\n\n start(): void {\n if (this.timer) return;\n\n // Periodic flush\n this.timer = setInterval(() => this.flush(), this.flushInterval);\n\n // Reliable delivery on tab hide — visibilitychange fires before the page\n // is destroyed, giving sendBeacon the best chance of succeeding.\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') this.beaconFlush();\n });\n\n // Belt-and-suspenders for browsers/environments that skip visibilitychange\n window.addEventListener('pagehide', () => this.beaconFlush(), { once: true });\n }\n\n stop(): void {\n if (this.timer) { clearInterval(this.timer); this.timer = null; }\n }\n\n // ── Telemetry queue ───────────────────────────────────────────────────────\n\n addTrace(t: TracePayload): void {\n this.traces.push(t);\n if (this.traces.length >= this.maxBatchSize) this.flush();\n }\n\n addError(e: ErrorPayload): void {\n this.errors.push(e);\n // Errors are high-priority — flush at half capacity\n if (this.errors.length >= Math.ceil(this.maxBatchSize / 2)) this.flush();\n }\n\n addEvent(e: EventPayload): void {\n this.events.push(e);\n if (this.events.length >= this.maxBatchSize) this.flush();\n }\n\n // ── Web analytics queue ───────────────────────────────────────────────────\n\n addWebView(payload: WebAnalyticsPayload): void {\n this.webViews.push(payload);\n if (this.webViews.length >= this.maxBatchSize) this.flushWeb();\n }\n\n // ── Drain helpers ─────────────────────────────────────────────────────────\n\n private drainTelemetry(): IngestBatch | null {\n const traces = this.traces.splice(0);\n const errors = this.errors.splice(0);\n const events = this.events.splice(0);\n if (!traces.length && !errors.length && !events.length) return null;\n return { traces, errors, events };\n }\n\n private drainWeb(): WebAnalyticsBatch | null {\n const web = this.webViews.splice(0);\n if (!web.length) return null;\n return { web };\n }\n\n // ── Flush ─────────────────────────────────────────────────────────────────\n\n flush(): void {\n if (!this.flushing) {\n const batch = this.drainTelemetry();\n if (batch) {\n this.flushing = true;\n this.transport.send(batch).finally(() => { this.flushing = false; });\n }\n }\n this.flushWeb();\n }\n\n flushWeb(): void {\n const batch = this.drainWeb();\n if (batch) this.transport.sendWeb(batch);\n }\n\n beaconFlush(): void {\n // Telemetry\n const batch = this.drainTelemetry();\n if (batch) {\n if (!this.transport.beacon(batch)) this.transport.send(batch);\n }\n // Web analytics\n const webBatch = this.drainWeb();\n if (webBatch) {\n if (!this.transport.beaconWeb(webBatch)) this.transport.sendWeb(webBatch);\n }\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · global error capture\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { ErrorPayload } from './types.js';\n\ntype ErrorCallback = (error: ErrorPayload) => void;\n\n/**\n * Attaches `window.onerror` and `window.addEventListener('unhandledrejection')`\n * listeners that forward caught errors to `onError`.\n *\n * Returns a cleanup function that removes both listeners.\n */\nexport function captureGlobalErrors(onError: ErrorCallback, env?: string): () => void {\n const handleError = (event: ErrorEvent) => {\n onError({\n message: event.message || 'Unknown error',\n level: 'error',\n route: window.location.pathname,\n stack: event.error?.stack,\n context: {\n url: window.location.href,\n source: event.filename ?? undefined,\n line: event.lineno ?? undefined,\n col: event.colno ?? undefined,\n },\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n };\n\n const handleRejection = (event: PromiseRejectionEvent) => {\n const reason = event.reason;\n const isErr = reason instanceof Error;\n onError({\n message: isErr ? reason.message : String(reason ?? 'Unhandled Promise rejection'),\n level: 'error',\n route: window.location.pathname,\n stack: isErr ? reason.stack : undefined,\n context: {\n url: window.location.href,\n type: 'unhandledrejection',\n },\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n };\n\n window.addEventListener('error', handleError);\n window.addEventListener('unhandledrejection', handleRejection);\n\n return () => {\n window.removeEventListener('error', handleError);\n window.removeEventListener('unhandledrejection', handleRejection);\n };\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · Web Vitals capture\n//\n// Captures FCP, LCP, and overall page-load time via PerformanceObserver and\n// PerformanceNavigationTiming. Forwarded as traces so they appear in the\n// Watchup dashboard alongside request spans.\n//\n// Thresholds come from Google's Core Web Vitals 2024 targets:\n// FCP: good ≤ 1800 ms, needs improvement ≤ 3000 ms, poor > 3000 ms\n// LCP: good ≤ 2500 ms, needs improvement ≤ 4000 ms, poor > 4000 ms\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { TracePayload } from './types.js';\n\ntype TraceCallback = (trace: TracePayload) => void;\n\nfunction rating(\n ms: number,\n good: number,\n needsImprovement: number,\n): TracePayload['status'] {\n if (ms <= good) return 'ok';\n if (ms <= needsImprovement) return 'warn';\n return 'err';\n}\n\nfunction statusCode(status: TracePayload['status']): number {\n return status === 'err' ? 500 : status === 'warn' ? 400 : 200;\n}\n\n// ── FCP — First Contentful Paint ─────────────────────────────────────────────\n\nexport function captureFCP(onTrace: TraceCallback, env?: string): void {\n if (typeof PerformanceObserver === 'undefined') return;\n try {\n const po = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n if (entry.name !== 'first-contentful-paint') continue;\n const ms = Math.round(entry.startTime);\n const status = rating(ms, 1800, 3000);\n onTrace({\n span: 'web-vital fcp',\n ms,\n status_code: statusCode(status),\n status,\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n po.disconnect();\n }\n });\n po.observe({ type: 'paint', buffered: true });\n } catch { /* PerformanceObserver 'paint' not supported */ }\n}\n\n// ── LCP — Largest Contentful Paint ───────────────────────────────────────────\n\nexport function captureLCP(onTrace: TraceCallback, env?: string): void {\n if (typeof PerformanceObserver === 'undefined') return;\n\n let last: PerformanceEntry | null = null;\n let reported = false;\n\n const report = () => {\n if (reported || !last) return;\n reported = true;\n try { po.disconnect(); } catch {}\n const ms = Math.round(last.startTime);\n const status = rating(ms, 2500, 4000);\n onTrace({\n span: 'web-vital lcp',\n ms,\n status_code: statusCode(status),\n status,\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n };\n\n let po: PerformanceObserver;\n try {\n po = new PerformanceObserver((list) => {\n const entries = list.getEntries();\n if (entries.length) last = entries[entries.length - 1] ?? null;\n });\n po.observe({ type: 'largest-contentful-paint', buffered: true });\n } catch {\n return; // 'largest-contentful-paint' not supported\n }\n\n // LCP is only finalised once the user interacts or the tab hides.\n document.addEventListener('visibilitychange', report, { once: true });\n document.addEventListener('keydown', report, { once: true, capture: true });\n document.addEventListener('pointerdown', report, { once: true, capture: true });\n}\n\n// ── Page load (overall) ───────────────────────────────────────────────────────\n\nexport function capturePageLoad(onTrace: TraceCallback, env?: string): void {\n const report = () => {\n const nav = performance.getEntriesByType('navigation')[0] as\n PerformanceNavigationTiming | undefined;\n if (!nav || nav.loadEventEnd <= 0) return;\n\n const ms = Math.round(nav.loadEventEnd - nav.startTime);\n const ttfb = Math.round(nav.responseStart - nav.requestStart);\n const status = rating(ms, 2000, 4000);\n\n onTrace({\n span: 'pageload',\n ms,\n status_code: statusCode(status),\n status,\n timestamp: new Date().toISOString(),\n meta: { ttfb },\n ...(env && { environment: env }),\n });\n };\n\n if (document.readyState === 'complete') {\n // PerformanceNavigationTiming might not be fully populated yet\n setTimeout(report, 0);\n } else {\n window.addEventListener('load', () => setTimeout(report, 100), { once: true });\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · Watchup client\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type {\n WatchupOptions,\n WatchupUser,\n TracePayload,\n ErrorPayload,\n EventPayload,\n WebAnalyticsPayload,\n} from './types.js';\nimport { Transport } from './transport.js';\nimport { Batcher } from './batcher.js';\nimport { captureGlobalErrors } from './error-capture.js';\nimport { captureFCP, captureLCP, capturePageLoad } from './perf.js';\n\nconst DEFAULTS = {\n baseUrl: 'https://api.watchup.site',\n flushInterval: 5_000,\n maxBatchSize: 100,\n debug: false,\n environment: 'production',\n release: '',\n sampleRate: 1,\n autoCapture: {\n errors: true,\n performance: true,\n pageViews: true,\n },\n} as const;\n\n// ── Storage keys ──────────────────────────────────────────────────────────────\n\nconst VISITOR_KEY = '__wup_vid';\nconst SESSION_KEY = '__wup_sid';\n\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class Watchup {\n private readonly cfg: Required<WatchupOptions>;\n private readonly batcher: Batcher;\n private readonly cleanup: Array<() => void> = [];\n private _user: WatchupUser | null = null;\n\n /**\n * A random UUID generated on init. Stable for the lifetime of the page —\n * useful for correlating all events from one user session.\n */\n readonly sessionId: string = crypto.randomUUID();\n\n // ── Visitor / session identity ─────────────────────────────────────────────\n\n /**\n * Persistent visitor ID. Stored in localStorage so it survives browser\n * sessions. Falls back to a per-session UUID when localStorage is blocked.\n * The server hashes this value with SHA-256 before persisting.\n */\n private readonly visitorId: string;\n\n /**\n * Per-session ID stored in sessionStorage. Resets on tab close.\n * The server hashes this value before persisting.\n */\n private readonly webSessionId: string;\n\n constructor(options: WatchupOptions) {\n if (!options.apiKey) {\n throw new Error('[watchup] apiKey is required.');\n }\n\n this.cfg = {\n ...DEFAULTS,\n autoCapture: { ...DEFAULTS.autoCapture, ...options.autoCapture },\n ...options,\n } as Required<WatchupOptions>;\n\n const transport = new Transport(this.cfg.baseUrl, this.cfg.apiKey, this.cfg.debug);\n this.batcher = new Batcher(transport, this.cfg.flushInterval, this.cfg.maxBatchSize);\n this.batcher.start();\n\n // Initialise visitor & session IDs\n this.visitorId = this._getOrCreateVisitorId();\n this.webSessionId = this._getOrCreateSessionId();\n\n this._setupAutoCapture();\n }\n\n // ── Visitor / session identity helpers ─────────────────────────────────────\n\n private _getOrCreateVisitorId(): string {\n try {\n let id = localStorage.getItem(VISITOR_KEY);\n if (!id) {\n id = crypto.randomUUID();\n localStorage.setItem(VISITOR_KEY, id);\n }\n return id;\n } catch {\n // localStorage blocked (private mode, etc.) — fall back to session scope\n return crypto.randomUUID();\n }\n }\n\n private _getOrCreateSessionId(): string {\n try {\n let id = sessionStorage.getItem(SESSION_KEY);\n if (!id) {\n id = crypto.randomUUID();\n sessionStorage.setItem(SESSION_KEY, id);\n }\n return id;\n } catch {\n return this.sessionId; // fallback: correlate with SDK sessionId\n }\n }\n\n // ── User identification ───────────────────────────────────────────────────\n\n /**\n * Attach a user to all subsequent errors, traces, and events.\n * Call this after login; the context persists until `clearUser()` or page reload.\n *\n * @example\n * watchup.setUser({ id: '42', email: 'ada@example.com', name: 'Ada Lovelace' });\n */\n setUser(user: WatchupUser): void {\n this._user = { ...user };\n }\n\n /**\n * Remove the current user context (e.g. after logout).\n */\n clearUser(): void {\n this._user = null;\n }\n\n // ── Public API ────────────────────────────────────────────────────────────\n\n /**\n * Track a custom analytics event.\n *\n * @example\n * watchup.track('button.clicked', { label: 'Sign Up', variant: 'A' });\n */\n track(name: string, properties?: Record<string, unknown>): void {\n if (!name) return;\n const event: EventPayload = {\n name,\n ...(properties && Object.keys(properties).length && { properties }),\n occurred_at: new Date().toISOString(),\n };\n this.batcher.addEvent(event);\n }\n\n /**\n * Track a web analytics page view (or custom web event).\n * Enriches the payload with visitor context, UTM params, and device info.\n *\n * Normally called automatically. Call manually when you need custom event_name.\n *\n * @example\n * watchup.trackWebView({ event_name: 'conversion', path: '/checkout/success' });\n */\n trackWebView(overrides: Partial<WebAnalyticsPayload> = {}): void {\n const url = new URL(window.location.href);\n const params = url.searchParams;\n\n const payload: WebAnalyticsPayload = {\n path: url.pathname + (url.search || ''),\n hostname: url.hostname,\n referrer: document.referrer || undefined,\n title: document.title || undefined,\n screen_w: window.screen?.width,\n screen_h: window.screen?.height,\n lang: navigator.language || undefined,\n timezone: this._timezone(),\n // UTM parameters\n utm_source: params.get('utm_source') || undefined,\n utm_medium: params.get('utm_medium') || undefined,\n utm_campaign: params.get('utm_campaign') || undefined,\n utm_term: params.get('utm_term') || undefined,\n utm_content: params.get('utm_content') || undefined,\n // Identity (raw; the server hashes before storing)\n visitor_id: this.visitorId,\n session_id: this.webSessionId,\n event_name: 'pageview',\n occurred_at: new Date().toISOString(),\n // Apply caller overrides last\n ...overrides,\n };\n\n this.batcher.addWebView(payload);\n }\n\n /**\n * Manually capture an error.\n *\n * @example\n * try { ... } catch (err) {\n * watchup.captureError(err, { component: 'CheckoutForm' });\n * }\n */\n captureError(\n error: Error | string | unknown,\n context?: Record<string, unknown> & { route?: string; level?: ErrorPayload['level'] },\n ): void {\n const { route, level = 'error', ...rest } = context ?? {};\n const err = error instanceof Error ? error : new Error(String(error));\n\n const payload: ErrorPayload = {\n message: err.message,\n level,\n ...(err.stack !== undefined && { stack: err.stack }),\n route: route ?? window.location.pathname,\n ...(Object.keys(rest).length && {\n context: { ...rest, url: window.location.href },\n }),\n timestamp: new Date().toISOString(),\n environment: this.cfg.environment,\n ...(this.cfg.release && { release: this.cfg.release }),\n ...(this._user && { user: this._user }),\n };\n\n this.batcher.addError(payload);\n }\n\n /**\n * Time any async operation and record it as a trace.\n * Returns an `end()` function — call it when the operation finishes.\n *\n * @example\n * const end = watchup.startTrace('fetch /api/cart');\n * const cart = await fetch('/api/cart');\n * end({ status: cart.ok ? 'ok' : 'err' });\n */\n startTrace(\n span: string,\n ): (opts?: { status?: TracePayload['status']; meta?: Record<string, unknown> }) => void {\n const start = Date.now();\n return (opts = {}) => {\n const status = opts.status ?? 'ok';\n this.batcher.addTrace({\n span,\n ms: Date.now() - start,\n status_code: status === 'err' ? 500 : status === 'warn' ? 400 : 200,\n status,\n timestamp: new Date().toISOString(),\n environment: this.cfg.environment,\n ...(this.cfg.release && { release: this.cfg.release }),\n ...(opts.meta && { meta: opts.meta }),\n ...(this._user && { user: this._user }),\n });\n };\n }\n\n /** Immediately flush all queued items (both telemetry and web analytics). */\n flush(): void { this.batcher.flush(); }\n\n /** Stop the flush timer and release all listeners. */\n shutdown(): void {\n this.batcher.stop();\n this.batcher.flush();\n this.cleanup.forEach((fn) => fn());\n }\n\n // ── Auto-capture setup ────────────────────────────────────────────────────\n\n private _setupAutoCapture(): void {\n const { autoCapture, environment } = this.cfg;\n\n if (autoCapture.errors) {\n this.cleanup.push(\n captureGlobalErrors((e) => this.batcher.addError(e), environment),\n );\n }\n\n if (autoCapture.performance) {\n captureFCP((t) => this.batcher.addTrace(t), environment);\n captureLCP((t) => this.batcher.addTrace(t), environment);\n capturePageLoad((t) => this.batcher.addTrace(t), environment);\n }\n\n if (autoCapture.pageViews) {\n this._setupPageViewTracking();\n }\n }\n\n private _setupPageViewTracking(): void {\n const trackView = () => {\n // Small delay so the page title has settled after navigation\n setTimeout(() => this.trackWebView(), 0);\n };\n\n // Initial view\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', trackView, { once: true });\n } else {\n setTimeout(() => this.trackWebView(), 0);\n }\n\n // SPA navigation — patch History API\n const origPush = history.pushState.bind(history);\n const origReplace = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n origPush(...args);\n trackView();\n };\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n origReplace(...args);\n // replaceState is often used for URL canonicalisation — don't track.\n };\n\n const onPopState = () => trackView();\n window.addEventListener('popstate', onPopState);\n\n this.cleanup.push(() => {\n history.pushState = origPush;\n history.replaceState = origReplace;\n window.removeEventListener('popstate', onPopState);\n });\n }\n\n // ── Helpers ───────────────────────────────────────────────────────────────\n\n private _timezone(): string | undefined {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n } catch {\n return undefined;\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/transport.ts","../src/batcher.ts","../src/error-capture.ts","../src/perf.ts","../src/watchup.ts"],"names":["_a"],"mappings":";AAaO,IAAM,YAAN,MAAgB;AAAA,EAMrB,WAAA,CAAY,OAAA,EAAiB,MAAA,EAAgB,KAAA,GAAQ,KAAA,EAAO;AAC1D,IAAA,MAAM,IAAA,GAAW,OAAA,CAAQ,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAC1C,IAAA,IAAA,CAAK,GAAA,GAAY,GAAG,IAAI,CAAA,oBAAA,CAAA;AACxB,IAAA,IAAA,CAAK,MAAA,GAAY,GAAG,IAAI,CAAA,wBAAA,CAAA;AACxB,IAAA,IAAA,CAAK,OAAA,GAAY;AAAA,MACf,cAAA,EAAgB,kBAAA;AAAA,MAChB,WAAA,EAAgB;AAAA,KAClB;AACA,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,KAAK,KAAA,EAAmC;AAC5C,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA;AAGjC,MAAA,IAAI,IAAA,CAAK,SAAS,GAAA,EAAQ;AACxB,QAAA,IAAA,CAAK,OAAO,KAAK,CAAA;AACjB,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,IAAA,CAAK,GAAA,EAAK;AAAA,QAChC,MAAA,EAAW,MAAA;AAAA,QACX,SAAW,IAAA,CAAK,OAAA;AAAA,QAChB,IAAA;AAAA,QACA,SAAA,EAAW;AAAA,OACZ,CAAA;AAED,MAAA,IAAI,IAAA,CAAK,KAAA,IAAS,CAAC,GAAA,CAAI,EAAA,EAAI;AACzB,QAAA,MAAM,OAAO,MAAM,GAAA,CAAI,MAAK,CAAE,KAAA,CAAM,MAAM,EAAE,CAAA;AAC5C,QAAA,OAAA,CAAQ,KAAK,CAAA,iBAAA,EAAoB,GAAA,CAAI,MAAM,CAAA,EAAA,EAAK,IAAI,CAAA,CAAE,CAAA;AAAA,MACxD;AAAA,IACF,SAAS,GAAA,EAAK;AACZ,MAAA,IAAI,IAAA,CAAK,KAAA,EAAO,OAAA,CAAQ,IAAA,CAAK,0BAA0B,GAAG,CAAA;AAAA,IAC5D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAQ,KAAA,EAAyC;AACrD,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA;AAEjC,MAAA,IAAI,IAAA,CAAK,SAAS,GAAA,EAAQ;AACxB,QAAA,IAAA,CAAK,UAAU,KAAK,CAAA;AACpB,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,IAAA,CAAK,MAAA,EAAQ;AAAA,QACnC,MAAA,EAAW,MAAA;AAAA,QACX,SAAW,IAAA,CAAK,OAAA;AAAA,QAChB,IAAA;AAAA,QACA,SAAA,EAAW;AAAA,OACZ,CAAA;AAED,MAAA,IAAI,IAAA,CAAK,KAAA,IAAS,CAAC,GAAA,CAAI,EAAA,EAAI;AACzB,QAAA,MAAM,OAAO,MAAM,GAAA,CAAI,MAAK,CAAE,KAAA,CAAM,MAAM,EAAE,CAAA;AAC5C,QAAA,OAAA,CAAQ,KAAK,CAAA,oBAAA,EAAuB,GAAA,CAAI,MAAM,CAAA,EAAA,EAAK,IAAI,CAAA,CAAE,CAAA;AAAA,MAC3D;AAAA,IACF,SAAS,GAAA,EAAK;AACZ,MAAA,IAAI,IAAA,CAAK,KAAA,EAAO,OAAA,CAAQ,IAAA,CAAK,6BAA6B,GAAG,CAAA;AAAA,IAC/D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,KAAA,EAA6B;AAClC,IAAA,IAAI,OAAO,SAAA,KAAc,WAAA,IAAe,CAAC,SAAA,CAAU,YAAY,OAAO,KAAA;AACtE,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,CAAC,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA,EAAG,EAAE,IAAA,EAAM,kBAAA,EAAoB,CAAA;AAC3E,MAAA,OAAO,SAAA,CAAU,UAAA,CAAW,IAAA,CAAK,GAAA,EAAK,IAAI,CAAA;AAAA,IAC5C,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,KAAA,EAAmC;AAC3C,IAAA,IAAI,OAAO,SAAA,KAAc,WAAA,IAAe,CAAC,SAAA,CAAU,YAAY,OAAO,KAAA;AACtE,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,CAAC,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA,EAAG,EAAE,IAAA,EAAM,kBAAA,EAAoB,CAAA;AAC3E,MAAA,OAAO,SAAA,CAAU,UAAA,CAAW,IAAA,CAAK,MAAA,EAAQ,IAAI,CAAA;AAAA,IAC/C,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,EACF;AACF,CAAA;;;AChGO,IAAM,UAAN,MAAc;AAAA,EAanB,WAAA,CAAY,SAAA,EAAsB,aAAA,EAAuB,YAAA,EAAsB;AAZ/E,IAAA,IAAA,CAAQ,SAAqC,EAAC;AAC9C,IAAA,IAAA,CAAQ,SAAqC,EAAC;AAC9C,IAAA,IAAA,CAAQ,SAAqC,EAAC;AAC9C,IAAA,IAAA,CAAQ,WAAqC,EAAC;AAM9C,IAAA,IAAA,CAAQ,KAAA,GAAkD,IAAA;AAC1D,IAAA,IAAA,CAAQ,QAAA,GAAW,KAAA;AAGjB,IAAA,IAAA,CAAK,SAAA,GAAgB,SAAA;AACrB,IAAA,IAAA,CAAK,aAAA,GAAgB,aAAA;AACrB,IAAA,IAAA,CAAK,YAAA,GAAgB,YAAA;AAAA,EACvB;AAAA,EAEA,KAAA,GAAc;AACZ,IAAA,IAAI,KAAK,KAAA,EAAO;AAGhB,IAAA,IAAA,CAAK,QAAQ,WAAA,CAAY,MAAM,KAAK,KAAA,EAAM,EAAG,KAAK,aAAa,CAAA;AAI/D,IAAA,QAAA,CAAS,gBAAA,CAAiB,oBAAoB,MAAM;AAClD,MAAA,IAAI,QAAA,CAAS,eAAA,KAAoB,QAAA,EAAU,IAAA,CAAK,WAAA,EAAY;AAAA,IAC9D,CAAC,CAAA;AAGD,IAAA,MAAA,CAAO,gBAAA,CAAiB,YAAY,MAAM,IAAA,CAAK,aAAY,EAAG,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AAAA,EAC9E;AAAA,EAEA,IAAA,GAAa;AACX,IAAA,IAAI,KAAK,KAAA,EAAO;AAAE,MAAA,aAAA,CAAc,KAAK,KAAK,CAAA;AAAG,MAAA,IAAA,CAAK,KAAA,GAAQ,IAAA;AAAA,IAAM;AAAA,EAClE;AAAA;AAAA,EAIA,SAAS,CAAA,EAAuB;AAC9B,IAAA,IAAA,CAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AAClB,IAAA,IAAI,KAAK,MAAA,CAAO,MAAA,IAAU,IAAA,CAAK,YAAA,OAAmB,KAAA,EAAM;AAAA,EAC1D;AAAA,EAEA,SAAS,CAAA,EAAuB;AAC9B,IAAA,IAAA,CAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AAElB,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,MAAA,IAAU,IAAA,CAAK,IAAA,CAAK,KAAK,YAAA,GAAe,CAAC,CAAA,EAAG,IAAA,CAAK,KAAA,EAAM;AAAA,EACzE;AAAA,EAEA,SAAS,CAAA,EAAuB;AAC9B,IAAA,IAAA,CAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AAClB,IAAA,IAAI,KAAK,MAAA,CAAO,MAAA,IAAU,IAAA,CAAK,YAAA,OAAmB,KAAA,EAAM;AAAA,EAC1D;AAAA;AAAA,EAIA,WAAW,OAAA,EAAoC;AAC7C,IAAA,IAAA,CAAK,QAAA,CAAS,KAAK,OAAO,CAAA;AAC1B,IAAA,IAAI,KAAK,QAAA,CAAS,MAAA,IAAU,IAAA,CAAK,YAAA,OAAmB,QAAA,EAAS;AAAA,EAC/D;AAAA;AAAA,EAIQ,cAAA,GAAqC;AAC3C,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA;AACnC,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA;AACnC,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA;AACnC,IAAA,IAAI,CAAC,OAAO,MAAA,IAAU,CAAC,OAAO,MAAA,IAAU,CAAC,MAAA,CAAO,MAAA,EAAQ,OAAO,IAAA;AAC/D,IAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAO;AAAA,EAClC;AAAA,EAEQ,QAAA,GAAqC;AAC3C,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,QAAA,CAAS,MAAA,CAAO,CAAC,CAAA;AAClC,IAAA,IAAI,CAAC,GAAA,CAAI,MAAA,EAAQ,OAAO,IAAA;AACxB,IAAA,OAAO,EAAE,GAAA,EAAI;AAAA,EACf;AAAA;AAAA,EAIA,KAAA,GAAc;AACZ,IAAA,IAAI,CAAC,KAAK,QAAA,EAAU;AAClB,MAAA,MAAM,KAAA,GAAQ,KAAK,cAAA,EAAe;AAClC,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAChB,QAAA,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,KAAK,CAAA,CAAE,QAAQ,MAAM;AAAE,UAAA,IAAA,CAAK,QAAA,GAAW,KAAA;AAAA,QAAO,CAAC,CAAA;AAAA,MACrE;AAAA,IACF;AACA,IAAA,IAAA,CAAK,QAAA,EAAS;AAAA,EAChB;AAAA,EAEA,QAAA,GAAiB;AACf,IAAA,MAAM,KAAA,GAAQ,KAAK,QAAA,EAAS;AAC5B,IAAA,IAAI,KAAA,EAAO,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,KAAK,CAAA;AAAA,EACzC;AAAA,EAEA,WAAA,GAAoB;AAElB,IAAA,MAAM,KAAA,GAAQ,KAAK,cAAA,EAAe;AAClC,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,IAAI,CAAC,KAAK,SAAA,CAAU,MAAA,CAAO,KAAK,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,KAAK,CAAA;AAAA,IAC9D;AAEA,IAAA,MAAM,QAAA,GAAW,KAAK,QAAA,EAAS;AAC/B,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,IAAI,CAAC,KAAK,SAAA,CAAU,SAAA,CAAU,QAAQ,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,QAAQ,CAAA;AAAA,IAC1E;AAAA,EACF;AACF,CAAA;;;ACtHO,SAAS,mBAAA,CAAoB,SAAwB,GAAA,EAA0B;AACpF,EAAA,MAAM,WAAA,GAAc,CAAC,KAAA,KAAsB;AAf7C,IAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAgBI,IAAA,OAAA,CAAQ;AAAA,MACN,OAAA,EAAW,MAAM,OAAA,IAAW,eAAA;AAAA,MAC5B,KAAA,EAAW,OAAA;AAAA,MACX,KAAA,EAAW,OAAO,QAAA,CAAS,QAAA;AAAA,MAC3B,KAAA,EAAA,CAAW,EAAA,GAAA,KAAA,CAAM,KAAA,KAAN,IAAA,GAAA,MAAA,GAAA,EAAA,CAAa,KAAA;AAAA,MACxB,OAAA,EAAS;AAAA,QACP,GAAA,EAAQ,OAAO,QAAA,CAAS,IAAA;AAAA,QACxB,MAAA,EAAA,CAAQ,EAAA,GAAA,KAAA,CAAM,QAAA,KAAN,IAAA,GAAA,EAAA,GAAkB,MAAA;AAAA,QAC1B,IAAA,EAAA,CAAQ,EAAA,GAAA,KAAA,CAAM,MAAA,KAAN,IAAA,GAAA,EAAA,GAAiB,MAAA;AAAA,QACzB,GAAA,EAAA,CAAQ,EAAA,GAAA,KAAA,CAAM,KAAA,KAAN,IAAA,GAAA,EAAA,GAAiB;AAAA,OAC3B;AAAA,MACA,SAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,MACpC,GAAI,GAAA,IAAO,EAAE,WAAA,EAAa,GAAA;AAAI,KAC/B,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,eAAA,GAAkB,CAAC,KAAA,KAAiC;AACxD,IAAA,MAAM,SAAS,KAAA,CAAM,MAAA;AACrB,IAAA,MAAM,QAAS,MAAA,YAAkB,KAAA;AACjC,IAAA,OAAA,CAAQ;AAAA,MACN,SAAW,KAAA,GAAQ,MAAA,CAAO,OAAA,GAAU,MAAA,CAAO,0BAAU,6BAA6B,CAAA;AAAA,MAClF,KAAA,EAAW,OAAA;AAAA,MACX,KAAA,EAAW,OAAO,QAAA,CAAS,QAAA;AAAA,MAC3B,KAAA,EAAW,KAAA,GAAQ,MAAA,CAAO,KAAA,GAAQ,MAAA;AAAA,MAClC,OAAA,EAAS;AAAA,QACP,GAAA,EAAM,OAAO,QAAA,CAAS,IAAA;AAAA,QACtB,IAAA,EAAM;AAAA,OACR;AAAA,MACA,SAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,MACpC,GAAI,GAAA,IAAO,EAAE,WAAA,EAAa,GAAA;AAAI,KAC/B,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAA,CAAO,gBAAA,CAAiB,SAAsB,WAAW,CAAA;AACzD,EAAA,MAAA,CAAO,gBAAA,CAAiB,sBAAsB,eAAe,CAAA;AAE7D,EAAA,OAAO,MAAM;AACX,IAAA,MAAA,CAAO,mBAAA,CAAoB,SAAsB,WAAW,CAAA;AAC5D,IAAA,MAAA,CAAO,mBAAA,CAAoB,sBAAsB,eAAe,CAAA;AAAA,EAClE,CAAA;AACF;;;ACxCA,SAAS,MAAA,CACP,EAAA,EACA,IAAA,EACA,gBAAA,EACwB;AACxB,EAAA,IAAI,EAAA,IAAM,MAAkB,OAAO,IAAA;AACnC,EAAA,IAAI,EAAA,IAAM,kBAAkB,OAAO,MAAA;AACnC,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,WAAW,MAAA,EAAwC;AAC1D,EAAA,OAAO,MAAA,KAAW,KAAA,GAAQ,GAAA,GAAM,MAAA,KAAW,SAAS,GAAA,GAAM,GAAA;AAC5D;AAIO,SAAS,UAAA,CAAW,SAAwB,GAAA,EAAoB;AACrE,EAAA,IAAI,OAAO,wBAAwB,WAAA,EAAa;AAChD,EAAA,IAAI;AACF,IAAA,MAAM,EAAA,GAAK,IAAI,mBAAA,CAAoB,CAAC,IAAA,KAAS;AAC3C,MAAA,KAAA,MAAW,KAAA,IAAS,IAAA,CAAK,UAAA,EAAW,EAAG;AACrC,QAAA,IAAI,KAAA,CAAM,SAAS,wBAAA,EAA0B;AAC7C,QAAA,MAAM,EAAA,GAAS,IAAA,CAAK,KAAA,CAAM,KAAA,CAAM,SAAS,CAAA;AACzC,QAAA,MAAM,MAAA,GAAS,MAAA,CAAO,EAAA,EAAI,IAAA,EAAM,GAAI,CAAA;AACpC,QAAA,OAAA,CAAQ;AAAA,UACN,IAAA,EAAa,eAAA;AAAA,UACb,EAAA;AAAA,UACA,WAAA,EAAa,WAAW,MAAM,CAAA;AAAA,UAC9B,MAAA;AAAA,UACA,SAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,UACpC,GAAI,GAAA,IAAO,EAAE,WAAA,EAAa,GAAA;AAAI,SAC/B,CAAA;AACD,QAAA,EAAA,CAAG,UAAA,EAAW;AAAA,MAChB;AAAA,IACF,CAAC,CAAA;AACD,IAAA,EAAA,CAAG,QAAQ,EAAE,IAAA,EAAM,OAAA,EAAS,QAAA,EAAU,MAAM,CAAA;AAAA,EAC9C,CAAA,CAAA,MAAQ;AAAA,EAAkD;AAC5D;AAIO,SAAS,UAAA,CAAW,SAAwB,GAAA,EAAoB;AACrE,EAAA,IAAI,OAAO,wBAAwB,WAAA,EAAa;AAEhD,EAAA,IAAI,IAAA,GAAgC,IAAA;AACpC,EAAA,IAAI,QAAA,GAAW,KAAA;AAEf,EAAA,MAAM,SAAS,MAAM;AACnB,IAAA,IAAI,QAAA,IAAY,CAAC,IAAA,EAAM;AACvB,IAAA,QAAA,GAAW,IAAA;AACX,IAAA,IAAI;AAAE,MAAA,EAAA,CAAG,UAAA,EAAW;AAAA,IAAG,CAAA,CAAA,MAAQ;AAAA,IAAC;AAChC,IAAA,MAAM,EAAA,GAAS,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,SAAS,CAAA;AACxC,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,EAAA,EAAI,IAAA,EAAM,GAAI,CAAA;AACpC,IAAA,OAAA,CAAQ;AAAA,MACN,IAAA,EAAa,eAAA;AAAA,MACb,EAAA;AAAA,MACA,WAAA,EAAa,WAAW,MAAM,CAAA;AAAA,MAC9B,MAAA;AAAA,MACA,SAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,MACpC,GAAI,GAAA,IAAO,EAAE,WAAA,EAAa,GAAA;AAAI,KAC/B,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,IAAI,EAAA;AACJ,EAAA,IAAI;AACF,IAAA,EAAA,GAAK,IAAI,mBAAA,CAAoB,CAAC,IAAA,KAAS;AAjF3C,MAAA,IAAA,EAAA;AAkFM,MAAA,MAAM,OAAA,GAAU,KAAK,UAAA,EAAW;AAChC,MAAA,IAAI,OAAA,CAAQ,QAAQ,IAAA,GAAA,CAAO,EAAA,GAAA,OAAA,CAAQ,QAAQ,MAAA,GAAS,CAAC,MAA1B,IAAA,GAAA,EAAA,GAA+B,IAAA;AAAA,IAC5D,CAAC,CAAA;AACD,IAAA,EAAA,CAAG,QAAQ,EAAE,IAAA,EAAM,0BAAA,EAA4B,QAAA,EAAU,MAAM,CAAA;AAAA,EACjE,CAAA,CAAA,MAAQ;AACN,IAAA;AAAA,EACF;AAGA,EAAA,QAAA,CAAS,iBAAiB,kBAAA,EAAoB,MAAA,EAAQ,EAAE,IAAA,EAAM,MAAM,CAAA;AACpE,EAAA,QAAA,CAAS,gBAAA,CAAiB,WAAoB,MAAA,EAAQ,EAAE,MAAM,IAAA,EAAM,OAAA,EAAS,MAAM,CAAA;AACnF,EAAA,QAAA,CAAS,gBAAA,CAAiB,eAAoB,MAAA,EAAQ,EAAE,MAAM,IAAA,EAAM,OAAA,EAAS,MAAM,CAAA;AACrF;AAIO,SAAS,eAAA,CAAgB,SAAwB,GAAA,EAAoB;AAC1E,EAAA,MAAM,SAAS,MAAM;AACnB,IAAA,MAAM,GAAA,GAAM,WAAA,CAAY,gBAAA,CAAiB,YAAY,EAAE,CAAC,CAAA;AAExD,IAAA,IAAI,CAAC,GAAA,IAAO,GAAA,CAAI,YAAA,IAAgB,CAAA,EAAG;AAEnC,IAAA,MAAM,KAAS,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,YAAA,GAAe,IAAI,SAAS,CAAA;AAC1D,IAAA,MAAM,OAAS,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,aAAA,GAAgB,IAAI,YAAY,CAAA;AAC9D,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,EAAA,EAAI,GAAA,EAAM,GAAI,CAAA;AAEpC,IAAA,OAAA,CAAQ;AAAA,MACN,IAAA,EAAa,UAAA;AAAA,MACb,EAAA;AAAA,MACA,WAAA,EAAa,WAAW,MAAM,CAAA;AAAA,MAC9B,MAAA;AAAA,MACA,SAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,MACpC,IAAA,EAAa,EAAE,IAAA,EAAK;AAAA,MACpB,GAAI,GAAA,IAAO,EAAE,WAAA,EAAa,GAAA;AAAI,KAC/B,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,IAAI,QAAA,CAAS,eAAe,UAAA,EAAY;AAEtC,IAAA,UAAA,CAAW,QAAQ,CAAC,CAAA;AAAA,EACtB,CAAA,MAAO;AACL,IAAA,MAAA,CAAO,gBAAA,CAAiB,MAAA,EAAQ,MAAM,UAAA,CAAW,MAAA,EAAQ,GAAG,CAAA,EAAG,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AAAA,EAC/E;AACF;;;ACxGA,SAAS,UAAA,CAAW,SAAiB,MAAA,EAAwB;AAC3D,EAAA,MAAM,GAAA,GAAM,CAAA,EAAG,OAAO,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAChC,EAAA,IAAI,IAAA,GAAO,IAAA;AACX,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,GAAA,CAAI,QAAQ,CAAA,EAAA,EAAK;AACnC,IAAA,IAAA,GAAS,IAAA,GAAO,EAAA,GAAM,GAAA,CAAI,UAAA,CAAW,CAAC,CAAA,GAAK,CAAA;AAAA,EAC7C;AACA,EAAA,OAAA,CAAQ,SAAS,CAAA,IAAK,GAAA;AACxB;AAEA,SAAS,gBAAA,CAAiB,MAAmB,GAAA,EAA2B;AA9BxE,EAAA,IAAA,EAAA;AA+BE,EAAA,IAAI,EAAA,CAAC,EAAA,GAAA,IAAA,CAAK,eAAA,KAAL,IAAA,GAAA,MAAA,GAAA,EAAA,CAAsB,SAAQ,OAAO,IAAA;AAC1C,EAAA,OAAO,IAAA,CAAK,eAAA,CAAgB,KAAA,CAAM,CAAA,IAAA,KAAQ;AAhC5C,IAAA,IAAAA,GAAAA;AAiCI,IAAA,MAAM,GAAA,GAAM,QAAOA,GAAAA,GAAA,GAAA,CAAI,KAAK,SAAS,CAAA,KAAlB,IAAA,GAAAA,GAAAA,GAAuB,EAAE,CAAA;AAC5C,IAAA,QAAQ,KAAK,QAAA;AAAU,MACrB,KAAK,IAAA;AAAY,QAAA,OAAO,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,GAAG,CAAA;AAAA,MAChD,KAAK,QAAA;AAAY,QAAA,OAAO,CAAC,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,GAAG,CAAA;AAAA,MACjD,KAAK,UAAA;AAAY,QAAA,OAAO,KAAK,MAAA,CAAO,IAAA,CAAK,OAAK,GAAA,CAAI,QAAA,CAAS,CAAC,CAAC,CAAA;AAAA,MAC7D,KAAK,QAAA;AAAY,QAAA,OAAO,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,KAAM,GAAA;AAAA,MAC3C;AAAiB,QAAA,OAAO,IAAA;AAAA;AAC1B,EACF,CAAC,CAAA;AACH;AAEA,IAAM,QAAA,GAAW;AAAA,EACf,OAAA,EAAe,0BAAA;AAAA,EACf,aAAA,EAAe,GAAA;AAAA,EACf,YAAA,EAAe,GAAA;AAAA,EACf,KAAA,EAAe,KAAA;AAAA,EACf,WAAA,EAAe,YAAA;AAAA,EACf,OAAA,EAAe,EAAA;AAAA,EACf,UAAA,EAAe,CAAA;AAAA,EACf,WAAA,EAAa;AAAA,IACX,MAAA,EAAa,IAAA;AAAA,IACb,WAAA,EAAa,IAAA;AAAA,IACb,SAAA,EAAa;AAAA;AAEjB,CAAA;AAIA,IAAM,WAAA,GAAc,WAAA;AACpB,IAAM,WAAA,GAAc,WAAA;AAIb,IAAM,UAAN,MAAc;AAAA,EA+BnB,YAAY,OAAA,EAAyB;AA5BrC,IAAA,IAAA,CAAiB,UAA6B,EAAC;AAC/C,IAAA,IAAA,CAAQ,KAAA,GAA4B,IAAA;AAMpC;AAAA;AAAA;AAAA;AAAA,IAAA,IAAA,CAAS,SAAA,GAAoB,OAAO,UAAA,EAAW;AAkB/C;AAAA,IAAA,IAAA,CAAQ,MAAA,uBAAuC,GAAA,EAAI;AACnD,IAAA,IAAA,CAAQ,UAAA,GAAoD,IAAA;AAG1D,IAAA,IAAI,CAAC,QAAQ,MAAA,EAAQ;AACnB,MAAA,MAAM,IAAI,MAAM,+BAA+B,CAAA;AAAA,IACjD;AAEA,IAAA,IAAA,CAAK,GAAA,GAAM;AAAA,MACT,GAAG,QAAA;AAAA,MACH,aAAa,EAAE,GAAG,SAAS,WAAA,EAAa,GAAG,QAAQ,WAAA,EAAY;AAAA,MAC/D,GAAG;AAAA,KACL;AAEA,IAAA,MAAM,SAAA,GAAY,IAAI,SAAA,CAAU,IAAA,CAAK,GAAA,CAAI,OAAA,EAAS,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA;AACjF,IAAA,IAAA,CAAK,OAAA,GAAa,IAAI,OAAA,CAAQ,SAAA,EAAW,KAAK,GAAA,CAAI,aAAA,EAAe,IAAA,CAAK,GAAA,CAAI,YAAY,CAAA;AACtF,IAAA,IAAA,CAAK,QAAQ,KAAA,EAAM;AAGnB,IAAA,IAAA,CAAK,SAAA,GAAc,KAAK,qBAAA,EAAsB;AAC9C,IAAA,IAAA,CAAK,YAAA,GAAe,KAAK,qBAAA,EAAsB;AAE/C,IAAA,IAAA,CAAK,iBAAA,EAAkB;AAGvB,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,IAAA,CAAK,aAAa,WAAA,CAAY,MAAM,IAAA,CAAK,WAAA,IAAe,GAAM,CAAA;AAC9D,IAAA,IAAA,CAAK,OAAA,CAAQ,KAAK,MAAM;AACtB,MAAA,IAAI,IAAA,CAAK,UAAA,EAAY,aAAA,CAAc,IAAA,CAAK,UAAU,CAAA;AAAA,IACpD,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,MAAc,WAAA,GAA6B;AA9H7C,IAAA,IAAA,EAAA;AA+HI,IAAA,IAAI;AACF,MAAA,MAAM,MAAM,MAAM,KAAA,CAAM,GAAG,IAAA,CAAK,GAAA,CAAI,OAAO,CAAA,aAAA,CAAA,EAAiB;AAAA,QAC1D,OAAA,EAAS,EAAE,WAAA,EAAa,IAAA,CAAK,IAAI,MAAA;AAAO,OACzC,CAAA;AACD,MAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACb,MAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,MAAA,IAAI,IAAA,CAAK,EAAA,KAAA,CAAM,EAAA,GAAA,IAAA,CAAK,IAAA,KAAL,mBAAW,KAAA,CAAA,EAAO;AAC/B,QAAA,IAAA,CAAK,OAAO,KAAA,EAAM;AAClB,QAAA,KAAA,MAAW,IAAA,IAAQ,IAAA,CAAK,IAAA,CAAK,KAAA,EAAO;AAClC,UAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,GAAA,EAAK,IAAI,CAAA;AAAA,QAChC;AAAA,MACF;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA,EAIQ,qBAAA,GAAgC;AACtC,IAAA,IAAI;AACF,MAAA,IAAI,EAAA,GAAK,YAAA,CAAa,OAAA,CAAQ,WAAW,CAAA;AACzC,MAAA,IAAI,CAAC,EAAA,EAAI;AACP,QAAA,EAAA,GAAK,OAAO,UAAA,EAAW;AACvB,QAAA,YAAA,CAAa,OAAA,CAAQ,aAAa,EAAE,CAAA;AAAA,MACtC;AACA,MAAA,OAAO,EAAA;AAAA,IACT,CAAA,CAAA,MAAQ;AAEN,MAAA,OAAO,OAAO,UAAA,EAAW;AAAA,IAC3B;AAAA,EACF;AAAA,EAEQ,qBAAA,GAAgC;AACtC,IAAA,IAAI;AACF,MAAA,IAAI,EAAA,GAAK,cAAA,CAAe,OAAA,CAAQ,WAAW,CAAA;AAC3C,MAAA,IAAI,CAAC,EAAA,EAAI;AACP,QAAA,EAAA,GAAK,OAAO,UAAA,EAAW;AACvB,QAAA,cAAA,CAAe,OAAA,CAAQ,aAAa,EAAE,CAAA;AAAA,MACxC;AACA,MAAA,OAAO,EAAA;AAAA,IACT,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAA,CAAK,SAAA;AAAA,IACd;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,QAAQ,IAAA,EAAyB;AAC/B,IAAA,IAAA,CAAK,KAAA,GAAQ,EAAE,GAAG,IAAA,EAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,SAAA,GAAkB;AAChB,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAA;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,KAAA,CAAM,MAAc,UAAA,EAA4C;AAC9D,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,MAAM,KAAA,GAAsB;AAAA,MAC1B,IAAA;AAAA,MACA,GAAI,cAAc,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA,CAAE,MAAA,IAAU,EAAE,UAAA,EAAW;AAAA,MACjE,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,KACtC;AACA,IAAA,IAAA,CAAK,OAAA,CAAQ,SAAS,KAAK,CAAA;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,YAAA,CAAa,SAAA,GAA0C,EAAC,EAAS;AA5NnE,IAAA,IAAA,EAAA,EAAA,EAAA;AA6NI,IAAA,MAAM,GAAA,GAAS,IAAI,GAAA,CAAI,MAAA,CAAO,SAAS,IAAI,CAAA;AAC3C,IAAA,MAAM,SAAS,GAAA,CAAI,YAAA;AAEnB,IAAA,MAAM,OAAA,GAA+B;AAAA,MACnC,IAAA,EAAa,GAAA,CAAI,QAAA,IAAY,GAAA,CAAI,MAAA,IAAU,EAAA,CAAA;AAAA,MAC3C,UAAa,GAAA,CAAI,QAAA;AAAA,MACjB,QAAA,EAAa,SAAS,QAAA,IAAY,MAAA;AAAA,MAClC,KAAA,EAAa,SAAS,KAAA,IAAW,MAAA;AAAA,MACjC,QAAA,EAAA,CAAa,EAAA,GAAA,MAAA,CAAO,MAAA,KAAP,IAAA,GAAA,MAAA,GAAA,EAAA,CAAe,KAAA;AAAA,MAC5B,QAAA,EAAA,CAAa,EAAA,GAAA,MAAA,CAAO,MAAA,KAAP,IAAA,GAAA,MAAA,GAAA,EAAA,CAAe,MAAA;AAAA,MAC5B,IAAA,EAAa,UAAU,QAAA,IAAY,MAAA;AAAA,MACnC,QAAA,EAAa,KAAK,SAAA,EAAU;AAAA;AAAA,MAE5B,UAAA,EAAc,MAAA,CAAO,GAAA,CAAI,YAAY,CAAA,IAAO,MAAA;AAAA,MAC5C,UAAA,EAAc,MAAA,CAAO,GAAA,CAAI,YAAY,CAAA,IAAO,MAAA;AAAA,MAC5C,YAAA,EAAc,MAAA,CAAO,GAAA,CAAI,cAAc,CAAA,IAAK,MAAA;AAAA,MAC5C,QAAA,EAAc,MAAA,CAAO,GAAA,CAAI,UAAU,CAAA,IAAS,MAAA;AAAA,MAC5C,WAAA,EAAc,MAAA,CAAO,GAAA,CAAI,aAAa,CAAA,IAAM,MAAA;AAAA;AAAA,MAE5C,YAAa,IAAA,CAAK,SAAA;AAAA,MAClB,YAAa,IAAA,CAAK,YAAA;AAAA,MAClB,UAAA,EAAa,UAAA;AAAA,MACb,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA;AAAA,MAEpC,GAAG;AAAA,KACL;AAEA,IAAA,IAAA,CAAK,OAAA,CAAQ,WAAW,OAAO,CAAA;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,YAAA,CACE,OACA,OAAA,EACM;AACN,IAAA,MAAM,EAAE,OAAO,KAAA,GAAQ,OAAA,EAAS,GAAG,IAAA,EAAK,GAAI,4BAAW,EAAC;AACxD,IAAA,MAAM,GAAA,GAAM,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAEpE,IAAA,MAAM,OAAA,GAAwB;AAAA,MAC5B,SAAS,GAAA,CAAI,OAAA;AAAA,MACb,KAAA;AAAA,MACA,GAAI,GAAA,CAAI,KAAA,KAAU,UAAa,EAAE,KAAA,EAAO,IAAI,KAAA,EAAM;AAAA,MAClD,KAAA,EAAS,KAAA,IAAA,IAAA,GAAA,KAAA,GAAS,MAAA,CAAO,QAAA,CAAS,QAAA;AAAA,MAClC,GAAI,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,MAAA,IAAU;AAAA,QAC9B,SAAS,EAAE,GAAG,MAAM,GAAA,EAAK,MAAA,CAAO,SAAS,IAAA;AAAK,OAChD;AAAA,MACA,SAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,MACpC,WAAA,EAAa,KAAK,GAAA,CAAI,WAAA;AAAA,MACtB,GAAI,KAAK,GAAA,CAAI,OAAA,IAAW,EAAE,OAAA,EAAS,IAAA,CAAK,IAAI,OAAA,EAAQ;AAAA,MACpD,GAAI,IAAA,CAAK,KAAA,IAAgB,EAAE,IAAA,EAAM,KAAK,KAAA;AAAM,KAC9C;AAEA,IAAA,IAAA,CAAK,OAAA,CAAQ,SAAS,OAAO,CAAA;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,WACE,IAAA,EACsF;AACtF,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,EAAI;AACvB,IAAA,OAAO,CAAC,IAAA,GAAO,EAAC,KAAM;AAxS1B,MAAA,IAAA,EAAA;AAySM,MAAA,MAAM,MAAA,GAAA,CAAS,EAAA,GAAA,IAAA,CAAK,MAAA,KAAL,IAAA,GAAA,EAAA,GAAe,IAAA;AAC9B,MAAA,IAAA,CAAK,QAAQ,QAAA,CAAS;AAAA,QACpB,IAAA;AAAA,QACA,EAAA,EAAa,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAAA,QAC1B,aAAa,MAAA,KAAW,KAAA,GAAQ,GAAA,GAAM,MAAA,KAAW,SAAS,GAAA,GAAM,GAAA;AAAA,QAChE,MAAA;AAAA,QACA,SAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,QACpC,WAAA,EAAa,KAAK,GAAA,CAAI,WAAA;AAAA,QACtB,GAAI,KAAK,GAAA,CAAI,OAAA,IAAW,EAAE,OAAA,EAAS,IAAA,CAAK,IAAI,OAAA,EAAQ;AAAA,QACpD,GAAI,IAAA,CAAK,IAAA,IAAe,EAAE,IAAA,EAAM,KAAK,IAAA,EAAK;AAAA,QAC1C,GAAI,IAAA,CAAK,KAAA,IAAe,EAAE,IAAA,EAAM,KAAK,KAAA;AAAM,OAC5C,CAAA;AAAA,IACH,CAAA;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,SAAA,CAAU,GAAA,EAAa,GAAA,GAAmB,EAAC,EAAY;AApUzD,IAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAqUI,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA;AAChC,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,IAAA,CAAK,SAAS,OAAO,KAAA;AACnC,IAAA,MAAM,SAAA,GAAyB,EAAE,MAAA,EAAA,CAAQ,EAAA,GAAA,IAAA,CAAK,KAAA,KAAL,IAAA,GAAA,MAAA,GAAA,EAAA,CAAY,EAAA,EAAI,KAAA,EAAA,CAAO,EAAA,GAAA,IAAA,CAAK,KAAA,KAAL,IAAA,GAAA,MAAA,GAAA,EAAA,CAAY,KAAA,EAAO,GAAG,GAAA,EAAI;AAC1F,IAAA,IAAI,CAAC,gBAAA,CAAiB,IAAA,EAAM,SAAS,GAAG,OAAO,KAAA;AAC/C,IAAA,IAAI,IAAA,CAAK,kBAAA,IAAsB,GAAA,EAAK,OAAO,IAAA;AAC3C,IAAA,IAAI,IAAA,CAAK,kBAAA,IAAsB,CAAA,EAAK,OAAO,KAAA;AAC3C,IAAA,MAAM,MAAA,GAAS,QAAO,EAAA,GAAA,CAAA,EAAA,GAAA,SAAA,CAAU,MAAA,KAAV,YAAoB,SAAA,CAAU,KAAA,KAA9B,IAAA,GAAA,EAAA,GAAuC,IAAA,CAAK,SAAS,CAAA;AAC3E,IAAA,OAAO,UAAA,CAAW,GAAA,EAAK,MAAM,CAAA,GAAI,IAAA,CAAK,kBAAA;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,UAAA,CAAW,GAAA,EAAa,GAAA,GAAmB,EAAC,EAAW;AAvVzD,IAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAwVI,IAAA,IAAI,CAAC,IAAA,CAAK,SAAA,CAAU,GAAA,EAAK,GAAG,GAAG,OAAO,SAAA;AACtC,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA;AAChC,IAAA,IAAI,EAAA,CAAC,EAAA,GAAA,IAAA,CAAK,QAAA,KAAL,IAAA,GAAA,MAAA,GAAA,EAAA,CAAe,SAAQ,OAAO,IAAA;AAEnC,IAAA,MAAM,SAAA,GAAyB,EAAE,MAAA,EAAA,CAAQ,EAAA,GAAA,IAAA,CAAK,KAAA,KAAL,IAAA,GAAA,MAAA,GAAA,EAAA,CAAY,EAAA,EAAI,KAAA,EAAA,CAAO,EAAA,GAAA,IAAA,CAAK,KAAA,KAAL,IAAA,GAAA,MAAA,GAAA,EAAA,CAAY,KAAA,EAAO,GAAG,GAAA,EAAI;AAC1F,IAAA,MAAM,MAAA,GAAS,QAAO,EAAA,GAAA,CAAA,EAAA,GAAA,SAAA,CAAU,MAAA,KAAV,YAAoB,SAAA,CAAU,KAAA,KAA9B,IAAA,GAAA,EAAA,GAAuC,IAAA,CAAK,SAAS,CAAA;AAC3E,IAAA,MAAM,MAAA,GAAS,UAAA,CAAW,GAAA,EAAK,MAAM,CAAA;AAErC,IAAA,IAAI,UAAA,GAAa,CAAA;AACjB,IAAA,KAAA,MAAW,OAAA,IAAW,KAAK,QAAA,EAAU;AACnC,MAAA,UAAA,IAAc,OAAA,CAAQ,MAAA;AACtB,MAAA,IAAI,MAAA,GAAS,UAAA,EAAY,OAAO,OAAA,CAAQ,GAAA;AAAA,IAC1C;AACA,IAAA,OAAO,KAAK,QAAA,CAAS,IAAA,CAAK,QAAA,CAAS,MAAA,GAAS,CAAC,CAAA,CAAG,GAAA;AAAA,EAClD;AAAA;AAAA,EAGA,KAAA,GAAc;AAAE,IAAA,IAAA,CAAK,QAAQ,KAAA,EAAM;AAAA,EAAG;AAAA;AAAA,EAGtC,QAAA,GAAiB;AACf,IAAA,IAAA,CAAK,QAAQ,IAAA,EAAK;AAClB,IAAA,IAAA,CAAK,QAAQ,KAAA,EAAM;AACnB,IAAA,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,CAAC,EAAA,KAAO,IAAI,CAAA;AAAA,EACnC;AAAA;AAAA,EAIQ,iBAAA,GAA0B;AAChC,IAAA,MAAM,EAAE,WAAA,EAAa,WAAA,EAAY,GAAI,IAAA,CAAK,GAAA;AAE1C,IAAA,IAAI,YAAY,MAAA,EAAQ;AACtB,MAAA,IAAA,CAAK,OAAA,CAAQ,IAAA;AAAA,QACX,mBAAA,CAAoB,CAAC,CAAA,KAAM,IAAA,CAAK,QAAQ,QAAA,CAAS,CAAC,GAAG,WAAW;AAAA,OAClE;AAAA,IACF;AAEA,IAAA,IAAI,YAAY,WAAA,EAAa;AAC3B,MAAA,UAAA,CAAW,CAAC,CAAA,KAAW,IAAA,CAAK,QAAQ,QAAA,CAAS,CAAC,GAAG,WAAW,CAAA;AAC5D,MAAA,UAAA,CAAW,CAAC,CAAA,KAAW,IAAA,CAAK,QAAQ,QAAA,CAAS,CAAC,GAAG,WAAW,CAAA;AAC5D,MAAA,eAAA,CAAgB,CAAC,CAAA,KAAM,IAAA,CAAK,QAAQ,QAAA,CAAS,CAAC,GAAG,WAAW,CAAA;AAAA,IAC9D;AAEA,IAAA,IAAI,YAAY,SAAA,EAAW;AACzB,MAAA,IAAA,CAAK,sBAAA,EAAuB;AAAA,IAC9B;AAAA,EACF;AAAA,EAEQ,sBAAA,GAA+B;AACrC,IAAA,MAAM,YAAY,MAAM;AAEtB,MAAA,UAAA,CAAW,MAAM,IAAA,CAAK,YAAA,EAAa,EAAG,CAAC,CAAA;AAAA,IACzC,CAAA;AAGA,IAAA,IAAI,QAAA,CAAS,eAAe,SAAA,EAAW;AACrC,MAAA,QAAA,CAAS,iBAAiB,kBAAA,EAAoB,SAAA,EAAW,EAAE,IAAA,EAAM,MAAM,CAAA;AAAA,IACzE,CAAA,MAAO;AACL,MAAA,UAAA,CAAW,MAAM,IAAA,CAAK,YAAA,EAAa,EAAG,CAAC,CAAA;AAAA,IACzC;AAGA,IAAA,MAAM,QAAA,GAAc,OAAA,CAAQ,SAAA,CAAU,IAAA,CAAK,OAAO,CAAA;AAClD,IAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,YAAA,CAAa,IAAA,CAAK,OAAO,CAAA;AAErD,IAAA,OAAA,CAAQ,SAAA,GAAY,IAAI,IAAA,KAA+C;AACrE,MAAA,QAAA,CAAS,GAAG,IAAI,CAAA;AAChB,MAAA,SAAA,EAAU;AAAA,IACZ,CAAA;AACA,IAAA,OAAA,CAAQ,YAAA,GAAe,IAAI,IAAA,KAAkD;AAC3E,MAAA,WAAA,CAAY,GAAG,IAAI,CAAA;AAAA,IAErB,CAAA;AAEA,IAAA,MAAM,UAAA,GAAa,MAAM,SAAA,EAAU;AACnC,IAAA,MAAA,CAAO,gBAAA,CAAiB,YAAY,UAAU,CAAA;AAE9C,IAAA,IAAA,CAAK,OAAA,CAAQ,KAAK,MAAM;AACtB,MAAA,OAAA,CAAQ,SAAA,GAAe,QAAA;AACvB,MAAA,OAAA,CAAQ,YAAA,GAAe,WAAA;AACvB,MAAA,MAAA,CAAO,mBAAA,CAAoB,YAAY,UAAU,CAAA;AAAA,IACnD,CAAC,CAAA;AAAA,EACH;AAAA;AAAA,EAIQ,SAAA,GAAgC;AACtC,IAAA,IAAI;AACF,MAAA,OAAO,IAAA,CAAK,cAAA,EAAe,CAAE,eAAA,GAAkB,QAAA,IAAY,KAAA,CAAA;AAAA,IAC7D,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF;AACF","file":"index.mjs","sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · transport\n//\n// Two delivery strategies:\n// 1. fetch(keepalive: true) — for regular periodic flushes.\n// 2. navigator.sendBeacon — for page-hide/unload; survives tab close.\n//\n// Web analytics events are sent to a separate endpoint (/web-batch) that is\n// optimised for high-volume, low-latency browser hits.\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { IngestBatch, WebAnalyticsBatch } from './types.js';\n\nexport class Transport {\n private readonly url: string;\n private readonly webUrl: string;\n private readonly headers: Record<string, string>;\n private readonly debug: boolean;\n\n constructor(baseUrl: string, apiKey: string, debug = false) {\n const base = baseUrl.replace(/\\/$/, '');\n this.url = `${base}/api/v1/ingest/batch`;\n this.webUrl = `${base}/api/v1/ingest/web-batch`;\n this.headers = {\n 'Content-Type': 'application/json',\n 'X-Api-Key': apiKey,\n };\n this.debug = debug;\n }\n\n /**\n * Send via `fetch` with `keepalive: true`.\n * `keepalive` lets the request outlive the current page — it's the\n * browser equivalent of a \"fire and forget\" POST.\n * Never rejects.\n */\n async send(batch: IngestBatch): Promise<void> {\n try {\n const body = JSON.stringify(batch);\n\n // keepalive has a 64 KiB payload limit; fall back to beacon for large batches\n if (body.length > 60_000) {\n this.beacon(batch);\n return;\n }\n\n const res = await fetch(this.url, {\n method: 'POST',\n headers: this.headers,\n body,\n keepalive: true,\n });\n\n if (this.debug && !res.ok) {\n const text = await res.text().catch(() => '');\n console.warn(`[watchup] ingest ${res.status}: ${text}`);\n }\n } catch (err) {\n if (this.debug) console.warn('[watchup] send failed:', err);\n }\n }\n\n /**\n * Send web analytics batch to the dedicated /web-batch endpoint.\n * Never rejects.\n */\n async sendWeb(batch: WebAnalyticsBatch): Promise<void> {\n try {\n const body = JSON.stringify(batch);\n\n if (body.length > 60_000) {\n this.beaconWeb(batch);\n return;\n }\n\n const res = await fetch(this.webUrl, {\n method: 'POST',\n headers: this.headers,\n body,\n keepalive: true,\n });\n\n if (this.debug && !res.ok) {\n const text = await res.text().catch(() => '');\n console.warn(`[watchup] web-batch ${res.status}: ${text}`);\n }\n } catch (err) {\n if (this.debug) console.warn('[watchup] sendWeb failed:', err);\n }\n }\n\n /**\n * Send via `navigator.sendBeacon`.\n * Returns `true` if the browser accepted the request (doesn't guarantee delivery).\n * The server must accept `application/json` from sendBeacon via a Blob.\n */\n beacon(batch: IngestBatch): boolean {\n if (typeof navigator === 'undefined' || !navigator.sendBeacon) return false;\n try {\n const blob = new Blob([JSON.stringify(batch)], { type: 'application/json' });\n return navigator.sendBeacon(this.url, blob);\n } catch {\n return false;\n }\n }\n\n /**\n * sendBeacon variant for web analytics events.\n */\n beaconWeb(batch: WebAnalyticsBatch): boolean {\n if (typeof navigator === 'undefined' || !navigator.sendBeacon) return false;\n try {\n const blob = new Blob([JSON.stringify(batch)], { type: 'application/json' });\n return navigator.sendBeacon(this.webUrl, blob);\n } catch {\n return false;\n }\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · batcher\n//\n// Browser-specific flush strategy:\n// - Periodic interval flush via fetch(keepalive)\n// - visibilitychange 'hidden' + pagehide → sendBeacon for reliable exit delivery\n//\n// Two independent queues:\n// - telemetry queue (traces, errors, events) → /ingest/batch\n// - web queue (web page views) → /ingest/web-batch\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type {\n TracePayload,\n ErrorPayload,\n EventPayload,\n IngestBatch,\n WebAnalyticsPayload,\n WebAnalyticsBatch,\n} from './types.js';\nimport { Transport } from './transport.js';\n\nexport class Batcher {\n private traces: TracePayload[] = [];\n private errors: ErrorPayload[] = [];\n private events: EventPayload[] = [];\n private webViews: WebAnalyticsPayload[] = [];\n\n private readonly transport: Transport;\n private readonly flushInterval: number;\n private readonly maxBatchSize: number;\n\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n\n constructor(transport: Transport, flushInterval: number, maxBatchSize: number) {\n this.transport = transport;\n this.flushInterval = flushInterval;\n this.maxBatchSize = maxBatchSize;\n }\n\n start(): void {\n if (this.timer) return;\n\n // Periodic flush\n this.timer = setInterval(() => this.flush(), this.flushInterval);\n\n // Reliable delivery on tab hide — visibilitychange fires before the page\n // is destroyed, giving sendBeacon the best chance of succeeding.\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') this.beaconFlush();\n });\n\n // Belt-and-suspenders for browsers/environments that skip visibilitychange\n window.addEventListener('pagehide', () => this.beaconFlush(), { once: true });\n }\n\n stop(): void {\n if (this.timer) { clearInterval(this.timer); this.timer = null; }\n }\n\n // ── Telemetry queue ───────────────────────────────────────────────────────\n\n addTrace(t: TracePayload): void {\n this.traces.push(t);\n if (this.traces.length >= this.maxBatchSize) this.flush();\n }\n\n addError(e: ErrorPayload): void {\n this.errors.push(e);\n // Errors are high-priority — flush at half capacity\n if (this.errors.length >= Math.ceil(this.maxBatchSize / 2)) this.flush();\n }\n\n addEvent(e: EventPayload): void {\n this.events.push(e);\n if (this.events.length >= this.maxBatchSize) this.flush();\n }\n\n // ── Web analytics queue ───────────────────────────────────────────────────\n\n addWebView(payload: WebAnalyticsPayload): void {\n this.webViews.push(payload);\n if (this.webViews.length >= this.maxBatchSize) this.flushWeb();\n }\n\n // ── Drain helpers ─────────────────────────────────────────────────────────\n\n private drainTelemetry(): IngestBatch | null {\n const traces = this.traces.splice(0);\n const errors = this.errors.splice(0);\n const events = this.events.splice(0);\n if (!traces.length && !errors.length && !events.length) return null;\n return { traces, errors, events };\n }\n\n private drainWeb(): WebAnalyticsBatch | null {\n const web = this.webViews.splice(0);\n if (!web.length) return null;\n return { web };\n }\n\n // ── Flush ─────────────────────────────────────────────────────────────────\n\n flush(): void {\n if (!this.flushing) {\n const batch = this.drainTelemetry();\n if (batch) {\n this.flushing = true;\n this.transport.send(batch).finally(() => { this.flushing = false; });\n }\n }\n this.flushWeb();\n }\n\n flushWeb(): void {\n const batch = this.drainWeb();\n if (batch) this.transport.sendWeb(batch);\n }\n\n beaconFlush(): void {\n // Telemetry\n const batch = this.drainTelemetry();\n if (batch) {\n if (!this.transport.beacon(batch)) this.transport.send(batch);\n }\n // Web analytics\n const webBatch = this.drainWeb();\n if (webBatch) {\n if (!this.transport.beaconWeb(webBatch)) this.transport.sendWeb(webBatch);\n }\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · global error capture\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { ErrorPayload } from './types.js';\n\ntype ErrorCallback = (error: ErrorPayload) => void;\n\n/**\n * Attaches `window.onerror` and `window.addEventListener('unhandledrejection')`\n * listeners that forward caught errors to `onError`.\n *\n * Returns a cleanup function that removes both listeners.\n */\nexport function captureGlobalErrors(onError: ErrorCallback, env?: string): () => void {\n const handleError = (event: ErrorEvent) => {\n onError({\n message: event.message || 'Unknown error',\n level: 'error',\n route: window.location.pathname,\n stack: event.error?.stack,\n context: {\n url: window.location.href,\n source: event.filename ?? undefined,\n line: event.lineno ?? undefined,\n col: event.colno ?? undefined,\n },\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n };\n\n const handleRejection = (event: PromiseRejectionEvent) => {\n const reason = event.reason;\n const isErr = reason instanceof Error;\n onError({\n message: isErr ? reason.message : String(reason ?? 'Unhandled Promise rejection'),\n level: 'error',\n route: window.location.pathname,\n stack: isErr ? reason.stack : undefined,\n context: {\n url: window.location.href,\n type: 'unhandledrejection',\n },\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n };\n\n window.addEventListener('error', handleError);\n window.addEventListener('unhandledrejection', handleRejection);\n\n return () => {\n window.removeEventListener('error', handleError);\n window.removeEventListener('unhandledrejection', handleRejection);\n };\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · Web Vitals capture\n//\n// Captures FCP, LCP, and overall page-load time via PerformanceObserver and\n// PerformanceNavigationTiming. Forwarded as traces so they appear in the\n// Watchup dashboard alongside request spans.\n//\n// Thresholds come from Google's Core Web Vitals 2024 targets:\n// FCP: good ≤ 1800 ms, needs improvement ≤ 3000 ms, poor > 3000 ms\n// LCP: good ≤ 2500 ms, needs improvement ≤ 4000 ms, poor > 4000 ms\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { TracePayload } from './types.js';\n\ntype TraceCallback = (trace: TracePayload) => void;\n\nfunction rating(\n ms: number,\n good: number,\n needsImprovement: number,\n): TracePayload['status'] {\n if (ms <= good) return 'ok';\n if (ms <= needsImprovement) return 'warn';\n return 'err';\n}\n\nfunction statusCode(status: TracePayload['status']): number {\n return status === 'err' ? 500 : status === 'warn' ? 400 : 200;\n}\n\n// ── FCP — First Contentful Paint ─────────────────────────────────────────────\n\nexport function captureFCP(onTrace: TraceCallback, env?: string): void {\n if (typeof PerformanceObserver === 'undefined') return;\n try {\n const po = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n if (entry.name !== 'first-contentful-paint') continue;\n const ms = Math.round(entry.startTime);\n const status = rating(ms, 1800, 3000);\n onTrace({\n span: 'web-vital fcp',\n ms,\n status_code: statusCode(status),\n status,\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n po.disconnect();\n }\n });\n po.observe({ type: 'paint', buffered: true });\n } catch { /* PerformanceObserver 'paint' not supported */ }\n}\n\n// ── LCP — Largest Contentful Paint ───────────────────────────────────────────\n\nexport function captureLCP(onTrace: TraceCallback, env?: string): void {\n if (typeof PerformanceObserver === 'undefined') return;\n\n let last: PerformanceEntry | null = null;\n let reported = false;\n\n const report = () => {\n if (reported || !last) return;\n reported = true;\n try { po.disconnect(); } catch {}\n const ms = Math.round(last.startTime);\n const status = rating(ms, 2500, 4000);\n onTrace({\n span: 'web-vital lcp',\n ms,\n status_code: statusCode(status),\n status,\n timestamp: new Date().toISOString(),\n ...(env && { environment: env }),\n });\n };\n\n let po: PerformanceObserver;\n try {\n po = new PerformanceObserver((list) => {\n const entries = list.getEntries();\n if (entries.length) last = entries[entries.length - 1] ?? null;\n });\n po.observe({ type: 'largest-contentful-paint', buffered: true });\n } catch {\n return; // 'largest-contentful-paint' not supported\n }\n\n // LCP is only finalised once the user interacts or the tab hides.\n document.addEventListener('visibilitychange', report, { once: true });\n document.addEventListener('keydown', report, { once: true, capture: true });\n document.addEventListener('pointerdown', report, { once: true, capture: true });\n}\n\n// ── Page load (overall) ───────────────────────────────────────────────────────\n\nexport function capturePageLoad(onTrace: TraceCallback, env?: string): void {\n const report = () => {\n const nav = performance.getEntriesByType('navigation')[0] as\n PerformanceNavigationTiming | undefined;\n if (!nav || nav.loadEventEnd <= 0) return;\n\n const ms = Math.round(nav.loadEventEnd - nav.startTime);\n const ttfb = Math.round(nav.responseStart - nav.requestStart);\n const status = rating(ms, 2000, 4000);\n\n onTrace({\n span: 'pageload',\n ms,\n status_code: statusCode(status),\n status,\n timestamp: new Date().toISOString(),\n meta: { ttfb },\n ...(env && { environment: env }),\n });\n };\n\n if (document.readyState === 'complete') {\n // PerformanceNavigationTiming might not be fully populated yet\n setTimeout(report, 0);\n } else {\n window.addEventListener('load', () => setTimeout(report, 100), { once: true });\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @watchupltd/browser · Watchup client\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type {\n WatchupOptions,\n WatchupUser,\n TracePayload,\n ErrorPayload,\n EventPayload,\n WebAnalyticsPayload,\n FeatureFlag,\n FlagContext,\n} from './types.js';\nimport { Transport } from './transport.js';\nimport { Batcher } from './batcher.js';\nimport { captureGlobalErrors } from './error-capture.js';\nimport { captureFCP, captureLCP, capturePageLoad } from './perf.js';\n\n// ── Flag evaluation helpers ───────────────────────────────────────────────────\n\nfunction flagBucket(flagKey: string, userId: string): number {\n const str = `${flagKey}:${userId}`;\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = ((hash * 33) ^ str.charCodeAt(i)) | 0;\n }\n return (hash >>> 0) % 100;\n}\n\nfunction matchesTargeting(flag: FeatureFlag, ctx: FlagContext): boolean {\n if (!flag.targeting_rules?.length) return true;\n return flag.targeting_rules.every(rule => {\n const val = String(ctx[rule.attribute] ?? '');\n switch (rule.operator) {\n case 'in': return rule.values.includes(val);\n case 'not_in': return !rule.values.includes(val);\n case 'contains': return rule.values.some(v => val.includes(v));\n case 'equals': return rule.values[0] === val;\n default: return true;\n }\n });\n}\n\nconst DEFAULTS = {\n baseUrl: 'https://api.watchup.site',\n flushInterval: 5_000,\n maxBatchSize: 100,\n debug: false,\n environment: 'production',\n release: '',\n sampleRate: 1,\n autoCapture: {\n errors: true,\n performance: true,\n pageViews: true,\n },\n} as const;\n\n// ── Storage keys ──────────────────────────────────────────────────────────────\n\nconst VISITOR_KEY = '__wup_vid';\nconst SESSION_KEY = '__wup_sid';\n\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class Watchup {\n private readonly cfg: Required<WatchupOptions>;\n private readonly batcher: Batcher;\n private readonly cleanup: Array<() => void> = [];\n private _user: WatchupUser | null = null;\n\n /**\n * A random UUID generated on init. Stable for the lifetime of the page —\n * useful for correlating all events from one user session.\n */\n readonly sessionId: string = crypto.randomUUID();\n\n // ── Visitor / session identity ─────────────────────────────────────────────\n\n /**\n * Persistent visitor ID. Stored in localStorage so it survives browser\n * sessions. Falls back to a per-session UUID when localStorage is blocked.\n * The server hashes this value with SHA-256 before persisting.\n */\n private readonly visitorId: string;\n\n /**\n * Per-session ID stored in sessionStorage. Resets on tab close.\n * The server hashes this value before persisting.\n */\n private readonly webSessionId: string;\n\n // Feature flags\n private _flags: Map<string, FeatureFlag> = new Map();\n private _flagTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: WatchupOptions) {\n if (!options.apiKey) {\n throw new Error('[watchup] apiKey is required.');\n }\n\n this.cfg = {\n ...DEFAULTS,\n autoCapture: { ...DEFAULTS.autoCapture, ...options.autoCapture },\n ...options,\n } as Required<WatchupOptions>;\n\n const transport = new Transport(this.cfg.baseUrl, this.cfg.apiKey, this.cfg.debug);\n this.batcher = new Batcher(transport, this.cfg.flushInterval, this.cfg.maxBatchSize);\n this.batcher.start();\n\n // Initialise visitor & session IDs\n this.visitorId = this._getOrCreateVisitorId();\n this.webSessionId = this._getOrCreateSessionId();\n\n this._setupAutoCapture();\n\n // Fetch flags immediately, then poll every 30 seconds\n this._fetchFlags();\n this._flagTimer = setInterval(() => this._fetchFlags(), 30_000);\n this.cleanup.push(() => {\n if (this._flagTimer) clearInterval(this._flagTimer);\n });\n }\n\n private async _fetchFlags(): Promise<void> {\n try {\n const res = await fetch(`${this.cfg.baseUrl}/api/v1/flags`, {\n headers: { 'X-Api-Key': this.cfg.apiKey },\n });\n if (!res.ok) return;\n const json = await res.json() as { ok: boolean; data?: { flags: FeatureFlag[] } };\n if (json.ok && json.data?.flags) {\n this._flags.clear();\n for (const flag of json.data.flags) {\n this._flags.set(flag.key, flag);\n }\n }\n } catch {\n // silently ignore — stale cache is fine\n }\n }\n\n // ── Visitor / session identity helpers ─────────────────────────────────────\n\n private _getOrCreateVisitorId(): string {\n try {\n let id = localStorage.getItem(VISITOR_KEY);\n if (!id) {\n id = crypto.randomUUID();\n localStorage.setItem(VISITOR_KEY, id);\n }\n return id;\n } catch {\n // localStorage blocked (private mode, etc.) — fall back to session scope\n return crypto.randomUUID();\n }\n }\n\n private _getOrCreateSessionId(): string {\n try {\n let id = sessionStorage.getItem(SESSION_KEY);\n if (!id) {\n id = crypto.randomUUID();\n sessionStorage.setItem(SESSION_KEY, id);\n }\n return id;\n } catch {\n return this.sessionId; // fallback: correlate with SDK sessionId\n }\n }\n\n // ── User identification ───────────────────────────────────────────────────\n\n /**\n * Attach a user to all subsequent errors, traces, and events.\n * Call this after login; the context persists until `clearUser()` or page reload.\n *\n * @example\n * watchup.setUser({ id: '42', email: 'ada@example.com', name: 'Ada Lovelace' });\n */\n setUser(user: WatchupUser): void {\n this._user = { ...user };\n }\n\n /**\n * Remove the current user context (e.g. after logout).\n */\n clearUser(): void {\n this._user = null;\n }\n\n // ── Public API ────────────────────────────────────────────────────────────\n\n /**\n * Track a custom analytics event.\n *\n * @example\n * watchup.track('button.clicked', { label: 'Sign Up', variant: 'A' });\n */\n track(name: string, properties?: Record<string, unknown>): void {\n if (!name) return;\n const event: EventPayload = {\n name,\n ...(properties && Object.keys(properties).length && { properties }),\n occurred_at: new Date().toISOString(),\n };\n this.batcher.addEvent(event);\n }\n\n /**\n * Track a web analytics page view (or custom web event).\n * Enriches the payload with visitor context, UTM params, and device info.\n *\n * Normally called automatically. Call manually when you need custom event_name.\n *\n * @example\n * watchup.trackWebView({ event_name: 'conversion', path: '/checkout/success' });\n */\n trackWebView(overrides: Partial<WebAnalyticsPayload> = {}): void {\n const url = new URL(window.location.href);\n const params = url.searchParams;\n\n const payload: WebAnalyticsPayload = {\n path: url.pathname + (url.search || ''),\n hostname: url.hostname,\n referrer: document.referrer || undefined,\n title: document.title || undefined,\n screen_w: window.screen?.width,\n screen_h: window.screen?.height,\n lang: navigator.language || undefined,\n timezone: this._timezone(),\n // UTM parameters\n utm_source: params.get('utm_source') || undefined,\n utm_medium: params.get('utm_medium') || undefined,\n utm_campaign: params.get('utm_campaign') || undefined,\n utm_term: params.get('utm_term') || undefined,\n utm_content: params.get('utm_content') || undefined,\n // Identity (raw; the server hashes before storing)\n visitor_id: this.visitorId,\n session_id: this.webSessionId,\n event_name: 'pageview',\n occurred_at: new Date().toISOString(),\n // Apply caller overrides last\n ...overrides,\n };\n\n this.batcher.addWebView(payload);\n }\n\n /**\n * Manually capture an error.\n *\n * @example\n * try { ... } catch (err) {\n * watchup.captureError(err, { component: 'CheckoutForm' });\n * }\n */\n captureError(\n error: Error | string | unknown,\n context?: Record<string, unknown> & { route?: string; level?: ErrorPayload['level'] },\n ): void {\n const { route, level = 'error', ...rest } = context ?? {};\n const err = error instanceof Error ? error : new Error(String(error));\n\n const payload: ErrorPayload = {\n message: err.message,\n level,\n ...(err.stack !== undefined && { stack: err.stack }),\n route: route ?? window.location.pathname,\n ...(Object.keys(rest).length && {\n context: { ...rest, url: window.location.href },\n }),\n timestamp: new Date().toISOString(),\n environment: this.cfg.environment,\n ...(this.cfg.release && { release: this.cfg.release }),\n ...(this._user && { user: this._user }),\n };\n\n this.batcher.addError(payload);\n }\n\n /**\n * Time any async operation and record it as a trace.\n * Returns an `end()` function — call it when the operation finishes.\n *\n * @example\n * const end = watchup.startTrace('fetch /api/cart');\n * const cart = await fetch('/api/cart');\n * end({ status: cart.ok ? 'ok' : 'err' });\n */\n startTrace(\n span: string,\n ): (opts?: { status?: TracePayload['status']; meta?: Record<string, unknown> }) => void {\n const start = Date.now();\n return (opts = {}) => {\n const status = opts.status ?? 'ok';\n this.batcher.addTrace({\n span,\n ms: Date.now() - start,\n status_code: status === 'err' ? 500 : status === 'warn' ? 400 : 200,\n status,\n timestamp: new Date().toISOString(),\n environment: this.cfg.environment,\n ...(this.cfg.release && { release: this.cfg.release }),\n ...(opts.meta && { meta: opts.meta }),\n ...(this._user && { user: this._user }),\n });\n };\n }\n\n // ── Feature flags ─────────────────────────────────────────────────────────\n\n /**\n * Check whether a feature flag is enabled. Evaluated locally — zero\n * network latency. Flag rules refresh every 30 seconds in the background.\n * Falls back to the identified user (via `setUser`) when no context is given.\n *\n * @example\n * if (watchup.isEnabled('new-checkout')) {\n * renderNewCheckout();\n * }\n */\n isEnabled(key: string, ctx: FlagContext = {}): boolean {\n const flag = this._flags.get(key);\n if (!flag || !flag.enabled) return false;\n const mergedCtx: FlagContext = { userId: this._user?.id, email: this._user?.email, ...ctx };\n if (!matchesTargeting(flag, mergedCtx)) return false;\n if (flag.rollout_percentage >= 100) return true;\n if (flag.rollout_percentage <= 0) return false;\n const userId = String(mergedCtx.userId ?? mergedCtx.email ?? this.visitorId);\n return flagBucket(key, userId) < flag.rollout_percentage;\n }\n\n /**\n * Get the variant key for a multivariate (A/B) flag.\n * Returns `\"control\"` if the flag is off or the visitor isn't in the rollout.\n *\n * @example\n * const variant = watchup.getVariant('pricing-layout');\n * // → \"control\" | \"variant-a\" | \"variant-b\"\n */\n getVariant(key: string, ctx: FlagContext = {}): string {\n if (!this.isEnabled(key, ctx)) return 'control';\n const flag = this._flags.get(key)!;\n if (!flag.variants?.length) return 'on';\n\n const mergedCtx: FlagContext = { userId: this._user?.id, email: this._user?.email, ...ctx };\n const userId = String(mergedCtx.userId ?? mergedCtx.email ?? this.visitorId);\n const bucket = flagBucket(key, userId);\n\n let cumulative = 0;\n for (const variant of flag.variants) {\n cumulative += variant.weight;\n if (bucket < cumulative) return variant.key;\n }\n return flag.variants[flag.variants.length - 1]!.key;\n }\n\n /** Immediately flush all queued items (both telemetry and web analytics). */\n flush(): void { this.batcher.flush(); }\n\n /** Stop the flush timer and release all listeners. */\n shutdown(): void {\n this.batcher.stop();\n this.batcher.flush();\n this.cleanup.forEach((fn) => fn());\n }\n\n // ── Auto-capture setup ────────────────────────────────────────────────────\n\n private _setupAutoCapture(): void {\n const { autoCapture, environment } = this.cfg;\n\n if (autoCapture.errors) {\n this.cleanup.push(\n captureGlobalErrors((e) => this.batcher.addError(e), environment),\n );\n }\n\n if (autoCapture.performance) {\n captureFCP((t) => this.batcher.addTrace(t), environment);\n captureLCP((t) => this.batcher.addTrace(t), environment);\n capturePageLoad((t) => this.batcher.addTrace(t), environment);\n }\n\n if (autoCapture.pageViews) {\n this._setupPageViewTracking();\n }\n }\n\n private _setupPageViewTracking(): void {\n const trackView = () => {\n // Small delay so the page title has settled after navigation\n setTimeout(() => this.trackWebView(), 0);\n };\n\n // Initial view\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', trackView, { once: true });\n } else {\n setTimeout(() => this.trackWebView(), 0);\n }\n\n // SPA navigation — patch History API\n const origPush = history.pushState.bind(history);\n const origReplace = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n origPush(...args);\n trackView();\n };\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n origReplace(...args);\n // replaceState is often used for URL canonicalisation — don't track.\n };\n\n const onPopState = () => trackView();\n window.addEventListener('popstate', onPopState);\n\n this.cleanup.push(() => {\n history.pushState = origPush;\n history.replaceState = origReplace;\n window.removeEventListener('popstate', onPopState);\n });\n }\n\n // ── Helpers ───────────────────────────────────────────────────────────────\n\n private _timezone(): string | undefined {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n } catch {\n return undefined;\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@watchupltd/browser",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Official Watchup SDK for browsers — analytics, errors, and performance",
5
5
  "license": "MIT",
6
6
  "author": "Watchup Ltd",