vantmetry 0.0.3 → 0.0.4

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/core/tracker.d.ts CHANGED
@@ -15,6 +15,7 @@ export declare class VantmetryTracker implements VantmetryInstance {
15
15
  info(message: string, details?: LogDetails): void;
16
16
  debug(message: string, details?: LogDetails): void;
17
17
  flush(): Promise<void>;
18
+ destroy(): Promise<void>;
18
19
  private addToBuffer;
19
20
  captureAutoError(payload: LogPayload): void;
20
21
  private getSignature;
@@ -1,10 +1,13 @@
1
1
  import { VantmetryConfig } from './types';
2
2
  export declare class TransportManager {
3
3
  private wtSession;
4
+ private wtInitPromise;
4
5
  private readonly endpoint;
5
6
  private readonly wtEndpoint;
6
7
  private readonly debug;
7
8
  constructor(config: VantmetryConfig);
8
9
  private initWT;
10
+ private connectWT;
9
11
  send(payload: string): Promise<void>;
12
+ close(): void;
10
13
  }
package/core/types.d.ts CHANGED
@@ -13,6 +13,7 @@ export interface VantmetryInstance {
13
13
  info: (message: string, details?: LogDetails) => void;
14
14
  debug: (message: string, details?: LogDetails) => void;
15
15
  flush: () => Promise<void>;
16
+ destroy: () => Promise<void>;
16
17
  }
17
18
  declare global {
18
19
  interface Window {
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { g as t } from "./init-DdlNmKDT.js";
2
- import { V as i, i as g, a as f } from "./init-DdlNmKDT.js";
3
- const n = {
1
+ import { g as t } from "./init-BfWOgJup.js";
2
+ import { V as i, i as g, a as u } from "./init-BfWOgJup.js";
3
+ const o = {
4
4
  get isReady() {
5
5
  return t().isReady;
6
6
  },
@@ -18,11 +18,14 @@ const n = {
18
18
  },
19
19
  flush() {
20
20
  return t().flush();
21
+ },
22
+ destroy() {
23
+ return t().destroy();
21
24
  }
22
25
  };
23
26
  export {
24
27
  i as VantmetryTracker,
25
28
  g as init,
26
- f as initGlobalListeners,
27
- n as logger
29
+ u as initGlobalListeners,
30
+ o as logger
28
31
  };
@@ -1,13 +1,13 @@
1
1
  let f = null;
2
- function E(n) {
3
- f = n;
2
+ function T(i) {
3
+ f = i;
4
4
  }
5
5
  function A() {
6
6
  if (!f)
7
7
  throw new Error("[Vantmetry] Not initialized. Call init() before using logger.");
8
8
  return f;
9
9
  }
10
- function T() {
10
+ function E() {
11
11
  return f !== null;
12
12
  }
13
13
  const c = {
@@ -15,23 +15,26 @@ const c = {
15
15
  INFO: "INFO",
16
16
  WARN: "WARN",
17
17
  DEBUG: "DEBUG"
18
- }, b = "https://ingestor.vantmetry.com:4433";
19
- class S {
18
+ }, S = "https://ingestor.vantmetry.com:4433";
19
+ class b {
20
20
  wtSession = null;
21
+ wtInitPromise = null;
21
22
  endpoint;
22
23
  wtEndpoint;
23
24
  debug;
24
25
  constructor(e) {
25
- const t = (e.ingestorUrl ?? b).replace(/\/$/, "");
26
+ const t = (e.ingestorUrl ?? S).replace(/\/$/, "");
26
27
  this.endpoint = `${t}/api/ingestor/push/tcp?public_key=${e.publicKey}`, this.wtEndpoint = `${t}/api/ingestor/push/udp?public_key=${e.publicKey}`;
27
28
  try {
28
29
  this.debug = typeof window < "u" && !!window.localStorage?.getItem("vantmetry_debug");
29
30
  } catch {
30
31
  this.debug = !1;
31
32
  }
32
- this.initWT();
33
33
  }
34
- async initWT() {
34
+ initWT() {
35
+ return this.wtInitPromise ? this.wtInitPromise : (this.wtInitPromise = this.connectWT(), this.wtInitPromise);
36
+ }
37
+ async connectWT() {
35
38
  if ("WebTransport" in window) {
36
39
  await new Promise((e) => setTimeout(e, 200));
37
40
  try {
@@ -42,7 +45,7 @@ class S {
42
45
  }
43
46
  }
44
47
  async send(e) {
45
- if (this.wtSession)
48
+ if (await this.initWT(), this.wtSession)
46
49
  try {
47
50
  const s = (await this.wtSession.createUnidirectionalStream()).getWriter();
48
51
  await s.write(new TextEncoder().encode(e)), await s.close();
@@ -64,8 +67,11 @@ class S {
64
67
  this.debug && console.error("Vantmetry send failed", t);
65
68
  });
66
69
  }
70
+ close() {
71
+ this.wtSession && (this.wtSession.close(), this.wtSession = null);
72
+ }
67
73
  }
68
- const l = {
74
+ const u = {
69
75
  // Matches standard email formats
70
76
  email: /\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b/g,
71
77
  // Matches standard CC groupings: 4-4-4-[3-4] with required separators, or Amex 4-6-5 format.
@@ -93,41 +99,41 @@ const l = {
93
99
  "client_secret",
94
100
  "auth"
95
101
  ]);
96
- function d(n) {
97
- let e = n;
98
- return e = e.replace(l.email, (t) => {
102
+ function d(i) {
103
+ let e = i;
104
+ return e = e.replace(u.email, (t) => {
99
105
  const s = t.split("@");
100
106
  if (s.length !== 2)
101
107
  return t;
102
- const [r, a] = s;
103
- return `${r.charAt(0)}***@${a}`;
104
- }), e = e.replace(l.creditCard, (t) => {
105
- const s = t.replace(/[-\s]/g, ""), r = s.slice(-4);
106
- return "*".repeat(s.length - 4) + r;
107
- }), 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;
108
+ const [n, a] = s;
109
+ return `${n.charAt(0)}***@${a}`;
110
+ }), e = e.replace(u.creditCard, (t) => {
111
+ const s = t.replace(/[-\s]/g, ""), n = s.slice(-4);
112
+ 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;
108
114
  }
109
- function h(n, e = /* @__PURE__ */ new WeakSet()) {
110
- if (typeof n == "string")
111
- return d(n);
112
- if (!n || typeof n != "object")
113
- return n;
114
- if (e.has(n))
115
+ function h(i, e = /* @__PURE__ */ new WeakSet()) {
116
+ if (typeof i == "string")
117
+ return d(i);
118
+ if (!i || typeof i != "object")
119
+ return i;
120
+ if (e.has(i))
115
121
  return "[Circular]";
116
- if (e.add(n), Array.isArray(n))
117
- return n.map((s) => h(s, e));
122
+ if (e.add(i), Array.isArray(i))
123
+ return i.map((s) => h(s, e));
118
124
  const t = {};
119
- for (const [s, r] of Object.entries(n)) {
125
+ for (const [s, n] of Object.entries(i)) {
120
126
  const a = s.toLowerCase();
121
- if (p.has(a) || Array.from(p).some((i) => a.includes(i))) {
127
+ if (p.has(a) || Array.from(p).some((r) => a.includes(r))) {
122
128
  t[s] = "[REDACTED]";
123
129
  continue;
124
130
  }
125
- typeof r == "string" ? t[s] = d(r) : typeof r == "object" && r !== null ? t[s] = h(r, e) : t[s] = r;
131
+ typeof n == "string" ? t[s] = d(n) : typeof n == "object" && n !== null ? t[s] = h(n, e) : t[s] = n;
126
132
  }
127
133
  return t;
128
134
  }
129
- const R = 50, k = 2e3;
130
- class v {
135
+ const R = 50, v = 2e3;
136
+ class _ {
131
137
  buffer = [];
132
138
  flushTimer = null;
133
139
  transport;
@@ -139,7 +145,7 @@ class v {
139
145
  TTL_MS = 6e4;
140
146
  MAX_EVENTS_PER_SEC = 100;
141
147
  constructor(e) {
142
- this.transport = new S(e);
148
+ this.transport = new b(e);
143
149
  }
144
150
  // --- Public API ---
145
151
  error(e, t) {
@@ -155,16 +161,21 @@ class v {
155
161
  this.addToBuffer({ severity: c.DEBUG, type: "manual", message: e, details: t });
156
162
  }
157
163
  async flush() {
158
- if (this.buffer.length === 0)
164
+ if (this.buffer.length === 0) {
165
+ this.flushTimer && (clearTimeout(this.flushTimer), this.flushTimer = null);
159
166
  return;
167
+ }
160
168
  const e = Date.now();
161
169
  for (const s of this.buffer)
162
170
  this.sentErrors.set(this.getSignature(s), e);
163
- for (const [s, r] of this.sentErrors.entries())
164
- e - r >= this.TTL_MS && this.sentErrors.delete(s);
171
+ for (const [s, n] of this.sentErrors.entries())
172
+ e - n >= this.TTL_MS && this.sentErrors.delete(s);
165
173
  const t = JSON.stringify(this.buffer);
166
174
  this.buffer = [], this.flushTimer && (clearTimeout(this.flushTimer), this.flushTimer = null), await this.transport.send(t);
167
175
  }
176
+ async destroy() {
177
+ this.transport.close(), await this.flush();
178
+ }
168
179
  // --- Internal Logic ---
169
180
  addToBuffer(e) {
170
181
  if (!this.isReady)
@@ -174,30 +185,30 @@ class v {
174
185
  this.isReady = !1, console.error("[Vantmetry] Logging disabled to save browser CPU due to infinite loop detection.");
175
186
  return;
176
187
  }
177
- const s = this.getSignature(e), r = this.sentErrors.get(s);
178
- if (r && t - r < this.TTL_MS)
188
+ const s = this.getSignature(e), n = this.sentErrors.get(s);
189
+ if (n && t - n < this.TTL_MS)
179
190
  return;
180
- const a = this.buffer.find((m) => this.getSignature(m) === s);
191
+ const a = this.buffer.find((y) => this.getSignature(y) === s);
181
192
  if (a) {
182
193
  a.count = (a.count || 1) + 1;
183
194
  return;
184
195
  }
185
- let { message: i, stack: o } = e;
186
- const { details: u } = e;
187
- i instanceof Error && (o = o ?? i.stack, i = i.message || String(i));
188
- const g = typeof i == "string" ? d(i) : i, y = typeof u == "object" ? h(u) : u, w = typeof o == "string" ? d(o) : o;
196
+ let { message: r, stack: o } = e;
197
+ const { details: l } = e;
198
+ 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;
189
200
  this.buffer.push({
190
201
  ...e,
191
202
  message: g,
192
- details: y,
193
- stack: w,
203
+ details: w,
204
+ stack: m,
194
205
  count: 1,
195
206
  ts: Date.now(),
196
207
  url: window.location.href,
197
208
  ua: navigator.userAgent
198
209
  }), this.buffer.length >= R ? this.flush() : this.flushTimer || (this.flushTimer = setTimeout(() => {
199
210
  this.flush();
200
- }, k));
211
+ }, v));
201
212
  }
202
213
  captureAutoError(e) {
203
214
  this.addToBuffer(e);
@@ -206,17 +217,17 @@ class v {
206
217
  return `${e.type}:${e.severity}:${e.message}`;
207
218
  }
208
219
  }
209
- function _(n) {
220
+ function k(i) {
210
221
  const e = console.error;
211
222
  console.error = function(...t) {
212
- if (e.apply(console, t), !n._isCapturingConsoleError) {
213
- n._isCapturingConsoleError = !0;
223
+ if (e.apply(console, t), !i._isCapturingConsoleError) {
224
+ i._isCapturingConsoleError = !0;
214
225
  try {
215
- let s, r;
216
- const a = t.findIndex((o) => o instanceof Error), i = t[a];
217
- if (i) {
218
- const o = t.slice(0, a).filter((u) => typeof u == "string").join(" ");
219
- s = o ? `${o}: ${i.message || String(i)}` : i.message || String(i), r = i.stack;
226
+ let s, n;
227
+ const a = t.findIndex((o) => o instanceof Error), r = t[a];
228
+ if (r) {
229
+ const o = t.slice(0, a).filter((l) => typeof l == "string").join(" ");
230
+ s = o ? `${o}: ${r.message || String(r)}` : r.message || String(r), n = r.stack;
220
231
  } else
221
232
  s = t.map((o) => {
222
233
  if (typeof o == "string") return o;
@@ -226,18 +237,18 @@ function _(n) {
226
237
  return String(o);
227
238
  }
228
239
  }).join(" ");
229
- n.captureAutoError({
240
+ i.captureAutoError({
230
241
  type: "console.error",
231
242
  message: s || "Unknown console.error",
232
- stack: r,
243
+ stack: n,
233
244
  severity: c.ERROR
234
245
  });
235
246
  } finally {
236
- n._isCapturingConsoleError = !1;
247
+ i._isCapturingConsoleError = !1;
237
248
  }
238
249
  }
239
250
  }, window.addEventListener("error", function(t) {
240
- n.captureAutoError({
251
+ i.captureAutoError({
241
252
  type: "crash",
242
253
  message: t.message || "Script error.",
243
254
  stack: t.error?.stack,
@@ -245,26 +256,28 @@ function _(n) {
245
256
  severity: c.ERROR
246
257
  });
247
258
  }, { capture: !0 }), window.addEventListener("unhandledrejection", function(t) {
248
- const s = t.reason, r = s instanceof Error;
249
- n.captureAutoError({
259
+ const s = t.reason, n = s instanceof Error;
260
+ i.captureAutoError({
250
261
  type: "promise",
251
- message: r ? s.message : String(s),
252
- stack: r ? s.stack : new Error(`Unhandled rejection: ${String(s)}`).stack,
262
+ message: n ? s.message : String(s),
263
+ stack: n ? s.stack : new Error(`Unhandled rejection: ${String(s)}`).stack,
253
264
  severity: c.ERROR
254
265
  });
255
- }, { capture: !0 }), document.addEventListener("visibilitychange", function() {
256
- document.visibilityState === "hidden" && n.flush();
266
+ }, { capture: !0 }), window.addEventListener("visibilitychange", function() {
267
+ document.visibilityState === "hidden" && i.flush();
268
+ }), window.addEventListener("pagehide", () => {
269
+ i.destroy();
257
270
  });
258
271
  }
259
- function C(n) {
260
- if (T())
272
+ function I(i) {
273
+ if (E())
261
274
  return;
262
- const e = new v(n);
263
- E(e), _(e);
275
+ const e = new _(i);
276
+ T(e), k(e);
264
277
  }
265
278
  export {
266
- v as V,
267
- _ as a,
279
+ _ as V,
280
+ k as a,
268
281
  A as g,
269
- C as i
282
+ I as i
270
283
  };
package/next/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx as i } from "react/jsx-runtime";
2
2
  import n from "next/script";
3
- import { i as e } from "../init-DdlNmKDT.js";
3
+ import { i as e } from "../init-BfWOgJup.js";
4
4
  function m({ publicKey: t, ingestorUrl: r }) {
5
5
  return /* @__PURE__ */ i(
6
6
  n,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vantmetry",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Lightweight frontend error tracking with minimal browser impact.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
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-DdlNmKDT.js";
4
+ import { i as m } from "../init-BfWOgJup.js";
5
5
  const n = i(null);
6
6
  function h({ publicKey: t, ingestorUrl: r, children: e }) {
7
7
  return u(() => {
@@ -67,10 +67,15 @@ export function initGlobalListeners(tracker: VantmetryTracker) {
67
67
  });
68
68
  }, { capture: true });
69
69
 
70
- // Flush on page unload
71
- document.addEventListener('visibilitychange', function () {
70
+ // Flush on page unload or visibility change
71
+ window.addEventListener('visibilitychange', function () {
72
72
  if (document.visibilityState === 'hidden') {
73
73
  void tracker.flush();
74
74
  }
75
75
  });
76
+
77
+ // Enable bfcache restoration by closing transport on navigate away
78
+ window.addEventListener('pagehide', () => {
79
+ void tracker.destroy();
80
+ });
76
81
  }
@@ -43,6 +43,10 @@ export class VantmetryTracker implements VantmetryInstance {
43
43
 
44
44
  public async flush() {
45
45
  if (this.buffer.length === 0) {
46
+ if (this.flushTimer) {
47
+ clearTimeout(this.flushTimer);
48
+ this.flushTimer = null;
49
+ }
46
50
  return;
47
51
  }
48
52
 
@@ -69,6 +73,11 @@ export class VantmetryTracker implements VantmetryInstance {
69
73
  await this.transport.send(dataPayload);
70
74
  }
71
75
 
76
+ public async destroy() {
77
+ this.transport.close();
78
+ await this.flush();
79
+ }
80
+
72
81
  // --- Internal Logic ---
73
82
 
74
83
  private addToBuffer(payload: LogPayload) {
@@ -4,6 +4,7 @@ const DEFAULT_INGESTOR_URL = 'https://ingestor.vantmetry.com:4433';
4
4
 
5
5
  export class TransportManager {
6
6
  private wtSession: WebTransport | null = null;
7
+ private wtInitPromise: Promise<void> | null = null;
7
8
  private readonly endpoint: string;
8
9
  private readonly wtEndpoint: string;
9
10
  private readonly debug: boolean;
@@ -18,11 +19,17 @@ export class TransportManager {
18
19
  } catch {
19
20
  this.debug = false;
20
21
  }
22
+ }
21
23
 
22
- void this.initWT();
24
+ private initWT(): Promise<void> {
25
+ if (this.wtInitPromise) {
26
+ return this.wtInitPromise;
27
+ }
28
+ this.wtInitPromise = this.connectWT();
29
+ return this.wtInitPromise;
23
30
  }
24
31
 
25
- private async initWT() {
32
+ private async connectWT(): Promise<void> {
26
33
  if (!('WebTransport' in window)) {
27
34
  return;
28
35
  }
@@ -46,6 +53,8 @@ export class TransportManager {
46
53
  }
47
54
 
48
55
  public async send(payload: string): Promise<void> {
56
+ await this.initWT();
57
+
49
58
  if (this.wtSession) {
50
59
  try {
51
60
  const stream = await this.wtSession.createUnidirectionalStream();
@@ -79,4 +88,11 @@ export class TransportManager {
79
88
  }
80
89
  });
81
90
  }
91
+
92
+ public close() {
93
+ if (this.wtSession) {
94
+ this.wtSession.close();
95
+ this.wtSession = null;
96
+ }
97
+ }
82
98
  }
package/src/core/types.ts CHANGED
@@ -15,6 +15,7 @@ export interface VantmetryInstance {
15
15
  info: (message: string, details?: LogDetails) => void;
16
16
  debug: (message: string, details?: LogDetails) => void;
17
17
  flush: () => Promise<void>;
18
+ destroy: () => Promise<void>;
18
19
  }
19
20
 
20
21
  declare global {
package/src/index.ts CHANGED
@@ -25,4 +25,7 @@ export const logger: VantmetryInstance = {
25
25
  flush() {
26
26
  return getInstance().flush();
27
27
  },
28
+ destroy() {
29
+ return getInstance().destroy();
30
+ },
28
31
  };