vedatrace 0.2.1 → 0.3.1

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.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var transports_index = require('./transports/index.cjs');
5
+ var transports_index = require('./index-osD28Hs0.cjs');
6
6
 
7
7
  class VedaTraceBatcher {
8
8
  constructor(transports, config, immediateFlush = false) {
@@ -10,55 +10,53 @@ class VedaTraceBatcher {
10
10
  this.config = config;
11
11
  this.immediateFlush = immediateFlush;
12
12
  this.context = config.executionContext;
13
+ this.waitUntilFn = config.waitUntil;
13
14
  }
14
15
  queue = [];
15
16
  flushTimer = null;
16
- flushDebounceTimer = null;
17
+ flushQueued = false;
17
18
  isFlushing = false;
18
19
  pendingFlush = null;
19
20
  context;
20
- /** Attach execution context after initialization */
21
+ waitUntilFn;
22
+ /**
23
+ * Set when the ingestion endpoint rejects our credentials. Every subsequent
24
+ * batch would fail identically, so we stop rather than spend a request (and a
25
+ * retry storm) per flush for the life of the process. start() clears it.
26
+ */
27
+ halted = false;
21
28
  setContext(ctx) {
22
29
  this.context = ctx;
23
30
  }
24
- /** Get current context */
25
31
  getContext() {
26
32
  return this.context;
27
33
  }
28
- /** Add log to queue with context-aware flush */
29
34
  add(log) {
35
+ if (this.halted) return;
30
36
  this.queue.push(log);
31
- if (!this.flushTimer && !this.immediateFlush) {
37
+ if (this.context || this.waitUntilFn) {
38
+ this.debouncedFlush();
39
+ return;
40
+ }
41
+ if (this.immediateFlush) {
42
+ this.flush();
43
+ return;
44
+ }
45
+ if (!this.flushTimer) {
32
46
  this.startFlushTimer();
33
47
  }
34
- if (this.immediateFlush || this.context) {
35
- this.debouncedFlush();
36
- } else if (this.queue.length >= this.config.batchSize) {
48
+ if (this.queue.length >= this.config.batchSize) {
37
49
  this.flush();
38
50
  }
39
51
  }
40
- /** Debounced flush - prevents rapid-fire flushes */
41
52
  debouncedFlush() {
42
- if (this.flushDebounceTimer) {
43
- clearTimeout(this.flushDebounceTimer);
44
- }
45
- this.flushDebounceTimer = setTimeout(() => {
46
- this.flushDebounceTimer = null;
47
- this.flush().catch((error) => {
48
- if (this.config.onError) {
49
- this.config.onError(
50
- error instanceof Error ? error : new Error(String(error))
51
- );
52
- } else {
53
- console.error(
54
- "[VedaTrace] Debounced flush error:",
55
- error instanceof Error ? error.message : String(error)
56
- );
57
- }
58
- });
59
- }, 100);
53
+ if (this.flushQueued) return;
54
+ this.flushQueued = true;
55
+ queueMicrotask(() => {
56
+ this.flushQueued = false;
57
+ this.flush().catch((error) => this.reportError(error));
58
+ });
60
59
  }
61
- /** Flush logs to all transports with waitUntil protection */
62
60
  async flush() {
63
61
  if (this.isFlushing) {
64
62
  return this.pendingFlush ?? Promise.resolve();
@@ -72,10 +70,16 @@ class VedaTraceBatcher {
72
70
  const flushPromise = this.sendWithRetry(logsToSend).finally(() => {
73
71
  this.isFlushing = false;
74
72
  this.pendingFlush = null;
73
+ }).then(() => {
74
+ if (this.queue.length > 0 && !this.halted) {
75
+ return this.flush();
76
+ }
75
77
  });
76
78
  this.pendingFlush = flushPromise;
77
- if (this.context) {
79
+ if (this.context && typeof this.context.waitUntil === "function") {
78
80
  this.context.waitUntil(flushPromise);
81
+ } else if (typeof this.waitUntilFn === "function") {
82
+ this.waitUntilFn(flushPromise);
79
83
  }
80
84
  return flushPromise;
81
85
  }
@@ -85,26 +89,47 @@ class VedaTraceBatcher {
85
89
  try {
86
90
  await transport.send(logs);
87
91
  } catch (error) {
88
- errors.push(error instanceof Error ? error : new Error(String(error)));
92
+ errors.push(error);
89
93
  }
90
94
  }
91
- if (errors.length > 0 && errors.length === this.transports.length) {
92
- if (attempt < this.config.maxRetries) {
93
- await this.delay(this.config.retryDelay * (attempt + 1));
94
- return this.sendWithRetry(logs, attempt + 1);
95
- }
96
- const combinedError = new Error(
97
- `Failed to send logs after ${this.config.maxRetries} retries: ${errors.map((e) => e.message).join(", ")}`
98
- );
99
- if (this.config.onError) {
100
- this.config.onError(combinedError);
101
- } else {
102
- console.error("[VedaTrace]", combinedError.message);
103
- }
95
+ if (errors.length === 0 || errors.length < this.transports.length) {
96
+ this.config.onSuccess?.();
104
97
  return;
105
98
  }
106
- if (this.config.onSuccess) {
107
- this.config.onSuccess();
99
+ const fatalError = errors.find(transports_index.isFatal);
100
+ if (fatalError) {
101
+ this.halt(fatalError);
102
+ return;
103
+ }
104
+ if (errors.some(transports_index.isRetryable) && attempt < this.config.maxRetries) {
105
+ const backoff = this.config.retryDelay * (attempt + 1);
106
+ await this.delay(transports_index.retryAfterMs(errors) ?? backoff);
107
+ return this.sendWithRetry(logs, attempt + 1);
108
+ }
109
+ const detail = errors.map(describeError).join(", ");
110
+ this.reportError(
111
+ new Error(
112
+ errors.some(transports_index.isRetryable) ? `Failed to send ${logs.length} logs after ${this.config.maxRetries} retries: ${detail}` : `Failed to send ${logs.length} logs: ${detail}`
113
+ )
114
+ );
115
+ }
116
+ /** Stop accepting logs after an unrecoverable authentication failure. */
117
+ halt(error) {
118
+ this.halted = true;
119
+ this.queue = [];
120
+ this.stop();
121
+ this.reportError(
122
+ new Error(
123
+ `VedaTrace disabled: ${describeError(error)}. Check your API key and the key's allowed origins, then call logger.start() to resume.`
124
+ )
125
+ );
126
+ }
127
+ reportError(error) {
128
+ const normalized = error instanceof Error ? error : new Error(String(error));
129
+ if (this.config.onError) {
130
+ this.config.onError(normalized);
131
+ } else {
132
+ console.error("[VedaTrace]", normalized.message);
108
133
  }
109
134
  }
110
135
  startFlushTimer() {
@@ -113,22 +138,11 @@ class VedaTraceBatcher {
113
138
  }
114
139
  this.flushTimer = setInterval(() => {
115
140
  if (this.queue.length > 0) {
116
- this.flush().catch((error) => {
117
- if (this.config.onError) {
118
- this.config.onError(
119
- error instanceof Error ? error : new Error(String(error))
120
- );
121
- } else {
122
- console.error(
123
- "[VedaTrace] Flush error:",
124
- error instanceof Error ? error.message : String(error)
125
- );
126
- }
127
- });
141
+ this.flush().catch((error) => this.reportError(error));
128
142
  }
129
143
  }, this.config.flushInterval);
130
144
  if (this.config.unrefTimer === true) {
131
- this.flushTimer.unref();
145
+ this.flushTimer.unref?.();
132
146
  }
133
147
  }
134
148
  stop() {
@@ -136,12 +150,10 @@ class VedaTraceBatcher {
136
150
  clearInterval(this.flushTimer);
137
151
  this.flushTimer = null;
138
152
  }
139
- if (this.flushDebounceTimer) {
140
- clearTimeout(this.flushDebounceTimer);
141
- this.flushDebounceTimer = null;
142
- }
153
+ this.flushQueued = false;
143
154
  }
144
155
  start() {
156
+ this.halted = false;
145
157
  if (!this.flushTimer && !this.immediateFlush) {
146
158
  this.startFlushTimer();
147
159
  }
@@ -152,10 +164,17 @@ class VedaTraceBatcher {
152
164
  getQueueSize() {
153
165
  return this.queue.length;
154
166
  }
167
+ /** True once an auth failure has shut the batcher down. */
168
+ isHalted() {
169
+ return this.halted;
170
+ }
155
171
  setExecutionContext(ctx) {
156
172
  this.context = ctx;
157
173
  }
158
174
  }
175
+ function describeError(error) {
176
+ return error instanceof Error ? error.message : String(error);
177
+ }
159
178
 
160
179
  function detectRuntime() {
161
180
  if (typeof navigator !== "undefined" && navigator.userAgent === "Cloudflare-Workers") {
@@ -198,7 +217,8 @@ function isBrowser() {
198
217
  return detectRuntime() === "browser";
199
218
  }
200
219
 
201
- const SDK_VERSION = process.env.npm_package_version ?? "0.0.0";
220
+ const SDK_VERSION = "0.3.1";
221
+
202
222
  class VedaTraceLogger {
203
223
  batcher = null;
204
224
  runtime;
@@ -351,24 +371,17 @@ class BrowserLifecycle {
351
371
  this.config = config;
352
372
  this.boundVisibilityHandler = this.handleVisibilityChange.bind(this);
353
373
  this.boundPageHideHandler = this.handlePageHide.bind(this);
354
- this.boundBeforeUnloadHandler = this.handleBeforeUnload.bind(this);
355
- this.boundUnloadHandler = this.handleUnload.bind(this);
356
374
  }
357
375
  boundVisibilityHandler;
358
376
  boundPageHideHandler;
359
- boundBeforeUnloadHandler;
360
- boundUnloadHandler;
361
377
  isAttached = false;
362
378
  pendingFlush = null;
363
379
  /** Start listening for browser lifecycle events */
364
380
  attach() {
365
381
  if (this.isAttached) return;
366
- if (typeof document !== "undefined") {
367
- document.addEventListener("visibilitychange", this.boundVisibilityHandler);
368
- window.addEventListener("pagehide", this.boundPageHideHandler);
369
- window.addEventListener("beforeunload", this.boundBeforeUnloadHandler);
370
- window.addEventListener("unload", this.boundUnloadHandler);
371
- }
382
+ if (typeof document === "undefined" || typeof window === "undefined") return;
383
+ document.addEventListener("visibilitychange", this.boundVisibilityHandler);
384
+ window.addEventListener("pagehide", this.boundPageHideHandler);
372
385
  this.isAttached = true;
373
386
  if (this.config.debug) {
374
387
  console.log("[VedaTrace] Browser lifecycle handlers attached");
@@ -377,21 +390,25 @@ class BrowserLifecycle {
377
390
  /** Stop listening for browser lifecycle events */
378
391
  detach() {
379
392
  if (!this.isAttached) return;
380
- if (typeof document !== "undefined") {
393
+ if (typeof document !== "undefined" && typeof window !== "undefined") {
381
394
  document.removeEventListener(
382
395
  "visibilitychange",
383
396
  this.boundVisibilityHandler
384
397
  );
385
398
  window.removeEventListener("pagehide", this.boundPageHideHandler);
386
- window.removeEventListener("beforeunload", this.boundBeforeUnloadHandler);
387
- window.removeEventListener("unload", this.boundUnloadHandler);
388
399
  }
389
400
  this.isAttached = false;
390
401
  if (this.config.debug) {
391
402
  console.log("[VedaTrace] Browser lifecycle handlers detached");
392
403
  }
393
404
  }
394
- /** Handle visibility change - flush when page becomes hidden */
405
+ /**
406
+ * Flush when the page becomes hidden.
407
+ *
408
+ * This is the one that actually matters: on mobile, a backgrounded tab is
409
+ * often discarded without ever firing pagehide, so "hidden" is the last
410
+ * reliable moment to ship what we have.
411
+ */
395
412
  handleVisibilityChange() {
396
413
  if (document.visibilityState === "hidden") {
397
414
  if (this.config.debug) {
@@ -400,7 +417,7 @@ class BrowserLifecycle {
400
417
  this.scheduleFlush();
401
418
  }
402
419
  }
403
- /** Handle pagehide event - primary flush handler for Safari */
420
+ /** Final flush as the page is torn down or frozen into the bfcache. */
404
421
  handlePageHide(event) {
405
422
  if (this.config.debug) {
406
423
  console.log(
@@ -408,46 +425,16 @@ class BrowserLifecycle {
408
425
  event.persisted ? "(cached)" : "(navigation)"
409
426
  );
410
427
  }
411
- if (event.persisted) {
412
- this.scheduleFlush();
413
- } else {
414
- this.finalFlush();
415
- }
416
- }
417
- /** Handle beforeunload - backup flush mechanism */
418
- handleBeforeUnload(event) {
419
- if (this.config.debug) {
420
- console.log("[VedaTrace] Before unload event");
421
- }
422
- this.finalFlush();
423
- }
424
- /** Handle unload - fallback for older browsers */
425
- handleUnload() {
426
- if (this.config.debug) {
427
- console.log("[VedaTrace] Unload event");
428
- }
429
- this.finalFlush();
428
+ this.scheduleFlush();
430
429
  }
431
- /** Schedule a debounced flush (for visibility change) */
430
+ /** Flush at most once at a time; keepalive carries it past the document. */
432
431
  scheduleFlush() {
433
432
  if (this.pendingFlush) return;
434
- this.pendingFlush = this.config.flush().finally(() => {
433
+ this.pendingFlush = this.config.flush().catch(() => {
434
+ }).finally(() => {
435
435
  this.pendingFlush = null;
436
436
  });
437
437
  }
438
- /**
439
- * Final flush using keepalive fetch
440
- * For sending logs after the page context is destroyed
441
- */
442
- finalFlush() {
443
- for (const transport of this.config.transports) {
444
- if (transport.name === "http" && "flush" in transport) {
445
- transport.flush?.();
446
- }
447
- }
448
- this.config.flush().catch(() => {
449
- });
450
- }
451
438
  /** Check if handlers are attached */
452
439
  isActive() {
453
440
  return this.isAttached;
@@ -528,6 +515,16 @@ function redactPii(value, mask) {
528
515
  return result;
529
516
  }
530
517
 
518
+ function resolveApiKey(config) {
519
+ if (config.apiKey) return config.apiKey;
520
+ try {
521
+ if (typeof process !== "undefined" && process.env) {
522
+ return process.env.VEDATRACE_API_KEY || void 0;
523
+ }
524
+ } catch {
525
+ }
526
+ return void 0;
527
+ }
531
528
  const RUNTIME_FLUSH_INTERVALS = {
532
529
  node: 3e3,
533
530
  bun: 3e3,
@@ -538,15 +535,16 @@ const RUNTIME_FLUSH_INTERVALS = {
538
535
  };
539
536
  function vedatrace(config = {}) {
540
537
  const runtime = detectRuntime();
541
- const logger = new VedaTraceLogger(config);
542
- if (config.apiKey && (!config.transports || config.transports.length === 0)) {
538
+ const apiKey = resolveApiKey(config);
539
+ const logger = new VedaTraceLogger(apiKey ? { ...config, apiKey } : config);
540
+ if (apiKey && (!config.transports || config.transports.length === 0)) {
543
541
  const isBrowserEnv = isBrowser();
544
542
  const isServerlessEnv = isServerless();
545
543
  const isLongRunningEnv = isLongRunning();
546
544
  const HttpTransport = isBrowserEnv ? transports_index.VedaTraceHttpTransportBrowser : transports_index.VedaTraceHttpTransport;
547
545
  const httpConfig = {
548
- apiKey: config.apiKey,
549
- keepalive: isBrowserEnv
546
+ apiKey,
547
+ keepalive: isBrowserEnv || isServerlessEnv
550
548
  };
551
549
  if (config.endpoint) httpConfig.endpoint = config.endpoint;
552
550
  const httpTransport = new HttpTransport(httpConfig);
@@ -571,12 +569,15 @@ function vedatrace(config = {}) {
571
569
  unrefTimer: config.unrefTimer ?? shouldUnrefTimer,
572
570
  executionContext: config.executionContext,
573
571
  onError: config.onError,
574
- onSuccess: config.onSuccess
572
+ onSuccess: config.onSuccess,
573
+ debug: config.debug,
574
+ waitUntil: config.waitUntil
575
575
  },
576
576
  immediateFlush
577
577
  );
578
578
  logger.setBatcher(batcher);
579
- if (typeof process !== "undefined" && isLongRunningEnv) {
579
+ const isNodeRuntime = typeof process !== "undefined" && process.versions?.node;
580
+ if (typeof process !== "undefined" && (isLongRunningEnv || isNodeRuntime)) {
580
581
  const flushLogs = async () => {
581
582
  await batcher.flush();
582
583
  };
@@ -612,7 +613,9 @@ function devVedatrace(config = {}) {
612
613
  exports.VedaTraceConsoleTransport = transports_index.VedaTraceConsoleTransport;
613
614
  exports.VedaTraceHttpTransport = transports_index.VedaTraceHttpTransport;
614
615
  exports.VedaTraceHttpTransportBrowser = transports_index.VedaTraceHttpTransportBrowser;
616
+ exports.VedaTraceTransportError = transports_index.VedaTraceTransportError;
615
617
  exports.BrowserLifecycle = BrowserLifecycle;
618
+ exports.SDK_VERSION = SDK_VERSION;
616
619
  exports.VedaTraceBatcher = VedaTraceBatcher;
617
620
  exports.VedaTraceLogger = VedaTraceLogger;
618
621
  exports.default = vedatrace;
package/dist/index.d.cts CHANGED
@@ -1,17 +1,14 @@
1
- import { b as VedaTraceTransport, B as BatcherConfig, c as VedaTraceEdgeContext, I as InternalLogEntry, V as VedaTraceLoggerInterface, R as RuntimeType$1, a as VedaTraceConfig, L as LogMetadata, d as RedactionConfig } from './types-BU0UESs9.cjs';
2
- export { e as VedaTraceLevel, f as VedaTraceLog } from './types-BU0UESs9.cjs';
1
+ import { b as VedaTraceTransport, B as BatcherConfig, c as VedaTraceEdgeContext, I as InternalLogEntry, V as VedaTraceLoggerInterface, R as RuntimeType$1, a as VedaTraceConfig, L as LogMetadata, d as RedactionConfig } from './types-CSBzeGyK.cjs';
2
+ export { e as VedaTraceLevel, f as VedaTraceLog } from './types-CSBzeGyK.cjs';
3
3
  export { ConsoleFormat, ConsoleTransportConfig, HttpTransportConfig, VedaTraceConsoleTransport, VedaTraceHttpTransport, VedaTraceHttpTransportBrowser } from './transports/index.cjs';
4
4
 
5
5
  /**
6
6
  * VedaTrace Batcher - Context-Aware for Cloudflare Workers
7
7
  *
8
- * Key features:
9
- * 1. Stores EdgeContext for waitUntil() integration
10
- * 2. setContext() method for post-initialization context attachment
11
- * 3. Fire-and-forget flush wrapped in ctx.waitUntil() when context is available
12
- * 4. Debounced flush to avoid excessive network calls
13
- * 5. Automatic flush on each log entry when context is present
14
- * 6. All callbacks moved into BatcherConfig for cleaner API
8
+ * Three flush modes:
9
+ * 1. Reliable mode (context or waitUntilFn): debounced flush + waitUntil for guaranteed delivery
10
+ * 2. Immediate mode: flush() called directly from add() during handler execution
11
+ * 3. Batch mode: periodic/batch-size flush for long-running environments
15
12
  */
16
13
 
17
14
  declare class VedaTraceBatcher {
@@ -20,30 +17,63 @@ declare class VedaTraceBatcher {
20
17
  private immediateFlush;
21
18
  private queue;
22
19
  private flushTimer;
23
- private flushDebounceTimer;
20
+ private flushQueued;
24
21
  private isFlushing;
25
22
  private pendingFlush;
26
23
  private context;
24
+ private waitUntilFn;
25
+ /**
26
+ * Set when the ingestion endpoint rejects our credentials. Every subsequent
27
+ * batch would fail identically, so we stop rather than spend a request (and a
28
+ * retry storm) per flush for the life of the process. start() clears it.
29
+ */
30
+ private halted;
27
31
  constructor(transports: VedaTraceTransport[], config: BatcherConfig, immediateFlush?: boolean);
28
- /** Attach execution context after initialization */
29
32
  setContext(ctx: VedaTraceEdgeContext): void;
30
- /** Get current context */
31
33
  getContext(): VedaTraceEdgeContext | undefined;
32
- /** Add log to queue with context-aware flush */
33
34
  add(log: InternalLogEntry): void;
34
- /** Debounced flush - prevents rapid-fire flushes */
35
35
  private debouncedFlush;
36
- /** Flush logs to all transports with waitUntil protection */
37
36
  flush(): Promise<void>;
38
37
  private sendWithRetry;
38
+ /** Stop accepting logs after an unrecoverable authentication failure. */
39
+ private halt;
40
+ private reportError;
39
41
  private startFlushTimer;
40
42
  stop(): void;
41
43
  start(): void;
42
44
  private delay;
43
45
  getQueueSize(): number;
46
+ /** True once an auth failure has shut the batcher down. */
47
+ isHalted(): boolean;
44
48
  setExecutionContext(ctx: VedaTraceEdgeContext): void;
45
49
  }
46
50
 
51
+ /**
52
+ * Transport error types
53
+ *
54
+ * The batcher needs to tell three cases apart:
55
+ * - retryable (network blip, timeout, 429, 5xx) — back off and try again
56
+ * - permanent (400 malformed batch) — retrying wastes the caller's time
57
+ * - fatal (401/403 bad key or blocked origin) — every future batch will
58
+ * fail the same way, so stop instead of burning a request per flush
59
+ */
60
+ declare class VedaTraceTransportError extends Error {
61
+ readonly name = "VedaTraceTransportError";
62
+ readonly status: number | undefined;
63
+ readonly retryable: boolean;
64
+ readonly fatal: boolean;
65
+ /** Server-requested backoff from a `Retry-After` header, in milliseconds. */
66
+ readonly retryAfterMs: number | undefined;
67
+ constructor(message: string, options?: {
68
+ status?: number;
69
+ retryable?: boolean;
70
+ fatal?: boolean;
71
+ retryAfterMs?: number;
72
+ });
73
+ /** Build an error from an HTTP response status. */
74
+ static fromStatus(status: number, body: string, retryAfterMs?: number): VedaTraceTransportError;
75
+ }
76
+
47
77
  /**
48
78
  * Core VedaTrace Logger implementation
49
79
  * Supports Cloudflare Workers / Pages via withContext() integration
@@ -80,12 +110,17 @@ declare class VedaTraceLogger implements VedaTraceLoggerInterface {
80
110
  * Browser lifecycle management for VedaTrace
81
111
  *
82
112
  * Handles:
83
- * - visibilitychange: flush when page becomes hidden
84
- * - pagehide: final flush when user leaves the page
85
- * - beforeunload: backup flush before page unload
113
+ * - visibilitychange: flush when the page becomes hidden
114
+ * - pagehide: final flush when the page goes away
86
115
  *
87
- * Uses fetch with keepalive: true for final flush to ensure
88
- * the request completes after the tab is closed.
116
+ * `beforeunload` and `unload` are deliberately not used. Registering either one
117
+ * makes Chrome and Safari ineligible for the back/forward cache, so a logging
118
+ * SDK that listens for them measurably slows down every back-navigation in the
119
+ * host app. `visibilitychange` + `pagehide` covers every case they did,
120
+ * including Safari, and is the pair the browsers themselves recommend.
121
+ *
122
+ * The final flush goes out through the batcher's normal HTTP transport, which
123
+ * sets `keepalive: true` in the browser so the request outlives the document.
89
124
  */
90
125
 
91
126
  interface BrowserLifecycleConfig {
@@ -101,8 +136,6 @@ declare class BrowserLifecycle {
101
136
  private config;
102
137
  private boundVisibilityHandler;
103
138
  private boundPageHideHandler;
104
- private boundBeforeUnloadHandler;
105
- private boundUnloadHandler;
106
139
  private isAttached;
107
140
  private pendingFlush;
108
141
  constructor(config: BrowserLifecycleConfig);
@@ -110,21 +143,18 @@ declare class BrowserLifecycle {
110
143
  attach(): void;
111
144
  /** Stop listening for browser lifecycle events */
112
145
  detach(): void;
113
- /** Handle visibility change - flush when page becomes hidden */
146
+ /**
147
+ * Flush when the page becomes hidden.
148
+ *
149
+ * This is the one that actually matters: on mobile, a backgrounded tab is
150
+ * often discarded without ever firing pagehide, so "hidden" is the last
151
+ * reliable moment to ship what we have.
152
+ */
114
153
  private handleVisibilityChange;
115
- /** Handle pagehide event - primary flush handler for Safari */
154
+ /** Final flush as the page is torn down or frozen into the bfcache. */
116
155
  private handlePageHide;
117
- /** Handle beforeunload - backup flush mechanism */
118
- private handleBeforeUnload;
119
- /** Handle unload - fallback for older browsers */
120
- private handleUnload;
121
- /** Schedule a debounced flush (for visibility change) */
156
+ /** Flush at most once at a time; keepalive carries it past the document. */
122
157
  private scheduleFlush;
123
- /**
124
- * Final flush using keepalive fetch
125
- * For sending logs after the page context is destroyed
126
- */
127
- private finalFlush;
128
158
  /** Check if handlers are attached */
129
159
  isActive(): boolean;
130
160
  }
@@ -149,6 +179,21 @@ declare function isServerless(): boolean;
149
179
  declare function isLongRunning(): boolean;
150
180
  declare function isBrowser(): boolean;
151
181
 
182
+ /**
183
+ * SDK version, inlined at build time.
184
+ *
185
+ * This file is generated by `scripts/sync-version.mjs` from package.json and is
186
+ * checked in so type-checking and tests work without a build step. Do not edit
187
+ * it by hand — `bun run build` and `changeset version` both regenerate it, and
188
+ * a test asserts it matches package.json.
189
+ *
190
+ * It exists because the previous `process.env.npm_package_version` read was
191
+ * wrong twice over: npm only sets that variable for its own lifecycle scripts,
192
+ * so it was always undefined for consumers, and the bare `process` reference
193
+ * survived bundling and threw a ReferenceError in browsers.
194
+ */
195
+ declare const SDK_VERSION = "0.3.1";
196
+
152
197
  /**
153
198
  * VedaTrace SDK - Universal JavaScript logging
154
199
  *
@@ -189,4 +234,4 @@ declare function vedatrace(config?: VedaTraceConfig): VedaTraceLoggerInterface;
189
234
  */
190
235
  declare function devVedatrace(config?: Omit<VedaTraceConfig, "apiKey" | "transports">): VedaTraceLoggerInterface;
191
236
 
192
- export { BatcherConfig, BrowserLifecycle, InternalLogEntry, LogMetadata, RedactionConfig, RuntimeType$1 as RuntimeType, VedaTraceBatcher, VedaTraceConfig, VedaTraceEdgeContext, VedaTraceLogger, VedaTraceLoggerInterface, VedaTraceTransport, vedatrace as default, detectRuntime, devVedatrace, isBrowser, isEdgeRuntime, isLongRunning, isServerless, redact, vedatrace };
237
+ export { BatcherConfig, BrowserLifecycle, InternalLogEntry, LogMetadata, RedactionConfig, RuntimeType$1 as RuntimeType, SDK_VERSION, VedaTraceBatcher, VedaTraceConfig, VedaTraceEdgeContext, VedaTraceLogger, VedaTraceLoggerInterface, VedaTraceTransport, VedaTraceTransportError, vedatrace as default, detectRuntime, devVedatrace, isBrowser, isEdgeRuntime, isLongRunning, isServerless, redact, vedatrace };