vantmetry 0.0.4 → 0.0.6

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # vantmetry
2
2
 
3
- Lightweight browser error tracking. Captures JavaScript errors, console errors, and unhandled promise rejections with zero blocking of the main thread.
3
+ Lightweight browser error tracking. Captures JavaScript errors, console errors, and unhandled promise rejections. Uses WebTransport with sendBeacon fallback so error delivery does not block page unload.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,7 +8,7 @@ Lightweight browser error tracking. Captures JavaScript errors, console errors,
8
8
  npm i vantmetry
9
9
  ```
10
10
 
11
- Or use the CDN script tag see [vantmetry.com/docs](https://vantmetry.com/docs).
11
+ Or use the CDN script tag. See [vantmetry.com/docs](https://vantmetry.com/docs).
12
12
 
13
13
  ## Quick start
14
14
 
@@ -4,7 +4,6 @@ export declare class TransportManager {
4
4
  private wtInitPromise;
5
5
  private readonly endpoint;
6
6
  private readonly wtEndpoint;
7
- private readonly debug;
8
7
  constructor(config: VantmetryConfig);
9
8
  private initWT;
10
9
  private connectWT;
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { g as t } from "./init-BfWOgJup.js";
2
- import { V as i, i as g, a as u } from "./init-BfWOgJup.js";
1
+ import { g as t } from "./init-CRDhUMvq.js";
2
+ import { V as i, i as g, a as u } from "./init-CRDhUMvq.js";
3
3
  const o = {
4
4
  get isReady() {
5
5
  return t().isReady;
@@ -1,8 +1,8 @@
1
1
  let f = null;
2
- function T(i) {
2
+ function y(i) {
3
3
  f = i;
4
4
  }
5
- function A() {
5
+ function I() {
6
6
  if (!f)
7
7
  throw new Error("[Vantmetry] Not initialized. Call init() before using logger.");
8
8
  return f;
@@ -15,21 +15,15 @@ const c = {
15
15
  INFO: "INFO",
16
16
  WARN: "WARN",
17
17
  DEBUG: "DEBUG"
18
- }, S = "https://ingestor.vantmetry.com:4433";
19
- class b {
18
+ }, T = "https://ingestor.vantmetry.com:4433", S = !1;
19
+ class R {
20
20
  wtSession = null;
21
21
  wtInitPromise = null;
22
22
  endpoint;
23
23
  wtEndpoint;
24
- debug;
25
24
  constructor(e) {
26
- const t = (e.ingestorUrl ?? S).replace(/\/$/, "");
25
+ const t = (e.ingestorUrl ?? T).replace(/\/$/, "");
27
26
  this.endpoint = `${t}/api/ingestor/push/tcp?public_key=${e.publicKey}`, this.wtEndpoint = `${t}/api/ingestor/push/udp?public_key=${e.publicKey}`;
28
- try {
29
- this.debug = typeof window < "u" && !!window.localStorage?.getItem("vantmetry_debug");
30
- } catch {
31
- this.debug = !1;
32
- }
33
27
  }
34
28
  initWT() {
35
29
  return this.wtInitPromise ? this.wtInitPromise : (this.wtInitPromise = this.connectWT(), this.wtInitPromise);
@@ -38,9 +32,9 @@ class b {
38
32
  if ("WebTransport" in window) {
39
33
  await new Promise((e) => setTimeout(e, 200));
40
34
  try {
41
- this.wtSession = new WebTransport(this.wtEndpoint), await this.wtSession.ready, this.debug && console.log("WT: Connected");
42
- } catch (e) {
43
- this.debug && console.warn("WT: Failed, falling back to beacon", e), this.wtSession = null;
35
+ this.wtSession = new WebTransport(this.wtEndpoint), await this.wtSession.ready;
36
+ } catch {
37
+ this.wtSession = null;
44
38
  }
45
39
  }
46
40
  }
@@ -50,8 +44,8 @@ class b {
50
44
  const s = (await this.wtSession.createUnidirectionalStream()).getWriter();
51
45
  await s.write(new TextEncoder().encode(e)), await s.close();
52
46
  return;
53
- } catch (t) {
54
- this.debug && console.warn("WT: Failed, falling back to beacon", t), this.wtSession = null;
47
+ } catch {
48
+ this.wtSession = null;
55
49
  }
56
50
  if (typeof navigator < "u" && navigator.sendBeacon) {
57
51
  const t = new Blob([e], { type: "application/json" });
@@ -64,14 +58,13 @@ class b {
64
58
  keepalive: !0,
65
59
  headers: { "Content-Type": "application/json" }
66
60
  }).catch((t) => {
67
- this.debug && console.error("Vantmetry send failed", t);
68
61
  });
69
62
  }
70
63
  close() {
71
64
  this.wtSession && (this.wtSession.close(), this.wtSession = null);
72
65
  }
73
66
  }
74
- const u = {
67
+ const l = {
75
68
  // Matches standard email formats
76
69
  email: /\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b/g,
77
70
  // Matches standard CC groupings: 4-4-4-[3-4] with required separators, or Amex 4-6-5 format.
@@ -82,7 +75,7 @@ const u = {
82
75
  jwt: /\bey[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g,
83
76
  // Generic secret detection (Bearer tokens, Basic auth, API keys)
84
77
  authHeader: /\b(Bearer|Basic|Token)\s+[A-Za-z0-9\-._~+/=]+/gi
85
- }, p = /* @__PURE__ */ new Set([
78
+ }, b = [
86
79
  "password",
87
80
  "passwd",
88
81
  "pwd",
@@ -98,42 +91,42 @@ const u = {
98
91
  "credentials",
99
92
  "client_secret",
100
93
  "auth"
101
- ]);
102
- function d(i) {
94
+ ];
95
+ function h(i) {
103
96
  let e = i;
104
- return e = e.replace(u.email, (t) => {
97
+ return e = e.replace(l.email, (t) => {
105
98
  const s = t.split("@");
106
99
  if (s.length !== 2)
107
100
  return t;
108
101
  const [n, a] = s;
109
102
  return `${n.charAt(0)}***@${a}`;
110
- }), e = e.replace(u.creditCard, (t) => {
103
+ }), e = e.replace(l.creditCard, (t) => {
111
104
  const s = t.replace(/[-\s]/g, ""), n = s.slice(-4);
112
105
  return "*".repeat(s.length - 4) + n;
113
- }), e = e.replace(u.ssn, (t) => `***-**-${t.slice(-4)}`), e = e.replace(u.jwt, "[JWT REDACTED]"), e = e.replace(u.authHeader, "$1 [TOKEN REDACTED]"), e;
106
+ }), e = e.replace(l.ssn, (t) => `***-**-${t.slice(-4)}`), e = e.replace(l.jwt, "[JWT REDACTED]"), e = e.replace(l.authHeader, "$1 [TOKEN REDACTED]"), e;
114
107
  }
115
- function h(i, e = /* @__PURE__ */ new WeakSet()) {
108
+ function d(i, e = /* @__PURE__ */ new WeakSet()) {
116
109
  if (typeof i == "string")
117
- return d(i);
110
+ return h(i);
118
111
  if (!i || typeof i != "object")
119
112
  return i;
120
113
  if (e.has(i))
121
114
  return "[Circular]";
122
115
  if (e.add(i), Array.isArray(i))
123
- return i.map((s) => h(s, e));
116
+ return i.map((s) => d(s, e));
124
117
  const t = {};
125
118
  for (const [s, n] of Object.entries(i)) {
126
119
  const a = s.toLowerCase();
127
- if (p.has(a) || Array.from(p).some((r) => a.includes(r))) {
120
+ if (b.some((r) => a.includes(r))) {
128
121
  t[s] = "[REDACTED]";
129
122
  continue;
130
123
  }
131
- typeof n == "string" ? t[s] = d(n) : typeof n == "object" && n !== null ? t[s] = h(n, e) : t[s] = n;
124
+ typeof n == "string" ? t[s] = h(n) : typeof n == "object" && n !== null ? t[s] = d(n, e) : t[s] = n;
132
125
  }
133
126
  return t;
134
127
  }
135
- const R = 50, v = 2e3;
136
- class _ {
128
+ const v = 50, _ = 2e3;
129
+ class A {
137
130
  buffer = [];
138
131
  flushTimer = null;
139
132
  transport;
@@ -145,7 +138,7 @@ class _ {
145
138
  TTL_MS = 6e4;
146
139
  MAX_EVENTS_PER_SEC = 100;
147
140
  constructor(e) {
148
- this.transport = new b(e);
141
+ this.transport = new R(e);
149
142
  }
150
143
  // --- Public API ---
151
144
  error(e, t) {
@@ -182,33 +175,33 @@ class _ {
182
175
  return;
183
176
  const t = Date.now();
184
177
  if (t - this.lastResetTime > 1e3 && (this.eventsThisSecond = 0, this.lastResetTime = t), this.eventsThisSecond++, this.eventsThisSecond > this.MAX_EVENTS_PER_SEC) {
185
- this.isReady = !1, console.error("[Vantmetry] Logging disabled to save browser CPU due to infinite loop detection.");
178
+ this.isReady = !1, console.error("[Vantmetry] Logging disabled due to infinite loop.");
186
179
  return;
187
180
  }
188
181
  const s = this.getSignature(e), n = this.sentErrors.get(s);
189
182
  if (n && t - n < this.TTL_MS)
190
183
  return;
191
- const a = this.buffer.find((y) => this.getSignature(y) === s);
184
+ const a = this.buffer.find((m) => this.getSignature(m) === s);
192
185
  if (a) {
193
186
  a.count = (a.count || 1) + 1;
194
187
  return;
195
188
  }
196
189
  let { message: r, stack: o } = e;
197
- const { details: l } = e;
190
+ const { details: u } = e;
198
191
  r instanceof Error && (o = o ?? r.stack, r = r.message || String(r));
199
- const g = typeof r == "string" ? d(r) : r, w = typeof l == "object" ? h(l) : l, m = typeof o == "string" ? d(o) : o;
192
+ const p = typeof r == "string" ? h(r) : r, g = typeof u == "object" ? d(u) : u, w = typeof o == "string" ? h(o) : o;
200
193
  this.buffer.push({
201
194
  ...e,
202
- message: g,
203
- details: w,
204
- stack: m,
195
+ message: p,
196
+ details: g,
197
+ stack: w,
205
198
  count: 1,
206
199
  ts: Date.now(),
207
200
  url: window.location.href,
208
201
  ua: navigator.userAgent
209
- }), this.buffer.length >= R ? this.flush() : this.flushTimer || (this.flushTimer = setTimeout(() => {
202
+ }), this.buffer.length >= v ? this.flush() : this.flushTimer || (this.flushTimer = setTimeout(() => {
210
203
  this.flush();
211
- }, v));
204
+ }, _));
212
205
  }
213
206
  captureAutoError(e) {
214
207
  this.addToBuffer(e);
@@ -226,7 +219,7 @@ function k(i) {
226
219
  let s, n;
227
220
  const a = t.findIndex((o) => o instanceof Error), r = t[a];
228
221
  if (r) {
229
- const o = t.slice(0, a).filter((l) => typeof l == "string").join(" ");
222
+ const o = t.slice(0, a).filter((u) => typeof u == "string").join(" ");
230
223
  s = o ? `${o}: ${r.message || String(r)}` : r.message || String(r), n = r.stack;
231
224
  } else
232
225
  s = t.map((o) => {
@@ -269,15 +262,15 @@ function k(i) {
269
262
  i.destroy();
270
263
  });
271
264
  }
272
- function I(i) {
265
+ function C(i) {
273
266
  if (E())
274
267
  return;
275
- const e = new _(i);
276
- T(e), k(e);
268
+ const e = new A(i);
269
+ y(e), k(e);
277
270
  }
278
271
  export {
279
- _ as V,
272
+ A as V,
280
273
  k as a,
281
- A as g,
282
- I as i
274
+ I as g,
275
+ C as i
283
276
  };
package/next/index.js CHANGED
@@ -1,21 +1,21 @@
1
- import { jsx as i } from "react/jsx-runtime";
2
- import n from "next/script";
3
- import { i as e } from "../init-BfWOgJup.js";
4
- function m({ publicKey: t, ingestorUrl: r }) {
5
- return /* @__PURE__ */ i(
6
- n,
1
+ import { jsx as n } from "react/jsx-runtime";
2
+ import e from "next/script";
3
+ import { i } from "../init-CRDhUMvq.js";
4
+ function c({ publicKey: t, ingestorUrl: r }) {
5
+ return /* @__PURE__ */ n(
6
+ e,
7
7
  {
8
8
  strategy: "afterInteractive",
9
- "data-public-key": t,
10
- "data-ingestor-url": r,
9
+ "data-vantmetry-key": t,
10
+ "data-vantmetry-url": r,
11
11
  src: "https://cdn.vantmetry.com/tracker.js"
12
12
  }
13
13
  );
14
14
  }
15
15
  function f(t) {
16
- typeof window > "u" || e(t);
16
+ typeof window > "u" || i(t);
17
17
  }
18
18
  export {
19
- m as VantmetryScript,
19
+ c as VantmetryScript,
20
20
  f as initVantmetry
21
21
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vantmetry",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Lightweight frontend error tracking with minimal browser impact.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -34,8 +34,7 @@
34
34
  "error-tracking",
35
35
  "observability",
36
36
  "frontend",
37
- "browser",
38
- "privacy"
37
+ "browser"
39
38
  ],
40
39
  "repository": {
41
40
  "type": "git",
package/react/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx as s } from "react/jsx-runtime";
2
2
  import { createContext as i, Component as a, useEffect as u, useContext as c } from "react";
3
3
  import { logger as o } from "../index.js";
4
- import { i as m } from "../init-BfWOgJup.js";
4
+ import { i as m } from "../init-CRDhUMvq.js";
5
5
  const n = i(null);
6
6
  function h({ publicKey: t, ingestorUrl: r, children: e }) {
7
7
  return u(() => {
@@ -18,8 +18,9 @@ const PATTERNS = {
18
18
  authHeader: /\b(Bearer|Basic|Token)\s+[A-Za-z0-9\-._~+/=]+/gi,
19
19
  };
20
20
 
21
- // Keys that suggest the value is highly sensitive and should be completely redacted
22
- const SENSITIVE_KEYS = new Set([
21
+ // Key fragments that suggest the value is highly sensitive and should be completely redacted.
22
+ // Matched as substrings, so an exact key name is covered too.
23
+ const SENSITIVE_KEYS = [
23
24
  'password',
24
25
  'passwd',
25
26
  'pwd',
@@ -35,7 +36,7 @@ const SENSITIVE_KEYS = new Set([
35
36
  'credentials',
36
37
  'client_secret',
37
38
  'auth',
38
- ]);
39
+ ];
39
40
 
40
41
  /**
41
42
  * Masks sensitive patterns in a string.
@@ -105,7 +106,7 @@ export function maskObjectPII<T>(obj: T, seen = new WeakSet()): T {
105
106
  const lowerKey = key.toLowerCase();
106
107
 
107
108
  // Completely redact known sensitive keys
108
- if (SENSITIVE_KEYS.has(lowerKey) || Array.from(SENSITIVE_KEYS).some((k) => lowerKey.includes(k))) {
109
+ if (SENSITIVE_KEYS.some((fragment) => lowerKey.includes(fragment))) {
109
110
  result[key] = '[REDACTED]';
110
111
  continue;
111
112
  }
@@ -96,7 +96,7 @@ export class VantmetryTracker implements VantmetryInstance {
96
96
 
97
97
  if (this.eventsThisSecond > this.MAX_EVENTS_PER_SEC) {
98
98
  this.isReady = false;
99
- console.error('[Vantmetry] Logging disabled to save browser CPU due to infinite loop detection.');
99
+ console.error('[Vantmetry] Logging disabled due to infinite loop.');
100
100
  return;
101
101
  }
102
102
 
@@ -2,23 +2,23 @@ import type { VantmetryConfig } from './types';
2
2
 
3
3
  const DEFAULT_INGESTOR_URL = 'https://ingestor.vantmetry.com:4433';
4
4
 
5
+ /**
6
+ * True only in the dedicated tracker.debug.js build (Vite mode "debug"). Every other build,
7
+ * including the lean tracker.js and the npm package, folds this to a literal `false`, so the debug
8
+ * branches are dead-code eliminated.
9
+ */
10
+ const DEBUG = import.meta.env.MODE === 'debug';
11
+
5
12
  export class TransportManager {
6
13
  private wtSession: WebTransport | null = null;
7
14
  private wtInitPromise: Promise<void> | null = null;
8
15
  private readonly endpoint: string;
9
16
  private readonly wtEndpoint: string;
10
- private readonly debug: boolean;
11
17
 
12
18
  constructor(config: VantmetryConfig) {
13
19
  const base = (config.ingestorUrl ?? DEFAULT_INGESTOR_URL).replace(/\/$/, '');
14
20
  this.endpoint = `${base}/api/ingestor/push/tcp?public_key=${config.publicKey}`;
15
21
  this.wtEndpoint = `${base}/api/ingestor/push/udp?public_key=${config.publicKey}`;
16
-
17
- try {
18
- this.debug = typeof window !== 'undefined' && !!window.localStorage?.getItem('vantmetry_debug');
19
- } catch {
20
- this.debug = false;
21
- }
22
22
  }
23
23
 
24
24
  private initWT(): Promise<void> {
@@ -41,11 +41,11 @@ export class TransportManager {
41
41
  try {
42
42
  this.wtSession = new WebTransport(this.wtEndpoint);
43
43
  await this.wtSession.ready;
44
- if (this.debug) {
44
+ if (DEBUG) {
45
45
  console.log('WT: Connected');
46
46
  }
47
47
  } catch (err) {
48
- if (this.debug) {
48
+ if (DEBUG) {
49
49
  console.warn('WT: Failed, falling back to beacon', err);
50
50
  }
51
51
  this.wtSession = null;
@@ -63,7 +63,7 @@ export class TransportManager {
63
63
  await writer.close();
64
64
  return;
65
65
  } catch (err) {
66
- if (this.debug) {
66
+ if (DEBUG) {
67
67
  console.warn('WT: Failed, falling back to beacon', err);
68
68
  }
69
69
  this.wtSession = null;
@@ -83,7 +83,7 @@ export class TransportManager {
83
83
  keepalive: true,
84
84
  headers: { 'Content-Type': 'application/json' },
85
85
  }).catch((error) => {
86
- if (this.debug) {
86
+ if (DEBUG) {
87
87
  console.error('Vantmetry send failed', error);
88
88
  }
89
89
  });
@@ -10,8 +10,8 @@ export function VantmetryScript({ publicKey, ingestorUrl }: VantmetryConfig) {
10
10
  return (
11
11
  <Script
12
12
  strategy="afterInteractive"
13
- data-public-key={publicKey}
14
- data-ingestor-url={ingestorUrl}
13
+ data-vantmetry-key={publicKey}
14
+ data-vantmetry-url={ingestorUrl}
15
15
  src="https://cdn.vantmetry.com/tracker.js"
16
16
  />
17
17
  );