@vybesec/sdk 0.1.3 → 0.1.5

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,7 @@
1
1
  # @vybesec/sdk
2
2
 
3
3
  Browser SDK for sending VybeSec events to your ingest endpoint.
4
+ This package is **browser-only** (not intended for Node.js or React Native yet).
4
5
 
5
6
  ## Install (npm)
6
7
 
@@ -41,19 +42,42 @@ captureEvent({
41
42
  </script>
42
43
  ```
43
44
 
45
+ ## Dev test snippet
46
+
47
+ Paste this into a dev page to confirm events are flowing:
48
+
49
+ ```html
50
+ <button id="vs-test-success">Trigger test success</button>
51
+ <button id="vs-test-error">Trigger test error</button>
52
+ <script>
53
+ const sdk = window.VybeSec?.vybesec ?? window.VybeSec;
54
+ document.getElementById("vs-test-success")?.addEventListener("click", () => {
55
+ sdk?.captureEvent?.({
56
+ type: "info",
57
+ message: "VybeSec test success",
58
+ timestamp: Date.now()
59
+ });
60
+ });
61
+ document.getElementById("vs-test-error")?.addEventListener("click", () => {
62
+ sdk?.captureError?.(new Error("VybeSec test error"));
63
+ });
64
+ </script>
65
+ ```
66
+
44
67
  ## Configuration
45
68
 
46
69
  | Option | Type | Description | Default |
47
70
  | --- | --- | --- | --- |
48
71
  | `key` | `string` | Project public key (DSN key). | **Required** |
49
72
  | `environment` | `"production" | "staging" | "development"` | Environment tag. | `"production"` |
50
- | `platform` | `"lovable" | "cursor" | "replit" | "v0" | "bolt" | "windsurf" | "other"` | AI/dev platform tag. | `"other"` |
73
+ | `platform` | `Platform` | Tool/framework tag (examples: `lovable`, `cursor`, `nextjs`, `react`, `vue`, `svelte`, `react-native`, `expo`, `other`). | `"other"` |
51
74
  | `release` | `string` | Release identifier. | `""` |
52
75
  | `userId` | `string` | User identifier. | `undefined` |
53
76
  | `sampleRate` | `number` | 0–1 sampling. | `1` |
54
77
  | `maxBuffer` | `number` | Max buffered events. | `100` |
55
78
  | `maxEventsPerMinute` | `number` | Client-side rate limit. | `120` |
56
- | `ingestUrl` | `string` | Override ingest base URL. | VybeSec ingest |
79
+
80
+ Full platform list is documented in the VybeSec docs and kept in sync with the dashboard.
57
81
 
58
82
  ## Events
59
83
 
package/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
9
  var __export = (target, all) => {
@@ -16,6 +18,14 @@ var __copyProps = (to, from, except, desc) => {
16
18
  }
17
19
  return to;
18
20
  };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
19
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
30
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
21
31
 
@@ -29,7 +39,122 @@ __export(index_exports, {
29
39
  vybesec: () => vybesec
30
40
  });
31
41
  module.exports = __toCommonJS(index_exports);
42
+
43
+ // src/vitals.ts
44
+ var thresholds = {
45
+ LCP: [2500, 4e3],
46
+ TTFB: [800, 1800],
47
+ FCP: [1800, 3e3],
48
+ INP: [200, 500],
49
+ CLS: [0.1, 0.25]
50
+ };
51
+ function rateMetric(metric) {
52
+ const [good, poor] = thresholds[metric.name] ?? [0, 0];
53
+ if (metric.value <= good) return "good";
54
+ if (metric.value <= poor) return "needs_improvement";
55
+ return "poor";
56
+ }
57
+ async function captureWebVitals(onMetric) {
58
+ const { onLCP, onTTFB, onFCP, onINP, onCLS } = await import("web-vitals");
59
+ const report = (metric) => {
60
+ onMetric({
61
+ metricName: metric.name,
62
+ value: metric.value,
63
+ rating: rateMetric(metric)
64
+ });
65
+ };
66
+ onLCP(report);
67
+ onTTFB(report);
68
+ onFCP(report);
69
+ onINP(report);
70
+ onCLS(report);
71
+ }
72
+
73
+ // src/replay.ts
74
+ var BUFFER_SECONDS = 60;
75
+ var MAX_EVENTS = 2e3;
76
+ var ReplayCapture = class {
77
+ constructor() {
78
+ __publicField(this, "events", []);
79
+ __publicField(this, "stopFn");
80
+ __publicField(this, "flushed", false);
81
+ }
82
+ async start() {
83
+ if (this.stopFn) return;
84
+ const { record } = await import("rrweb");
85
+ this.stopFn = record({
86
+ emit: (event) => {
87
+ const now = Date.now();
88
+ this.events.push(event);
89
+ const cutoff = now - BUFFER_SECONDS * 1e3;
90
+ while (this.events.length > 0 && this.events[0].timestamp < cutoff) {
91
+ this.events.shift();
92
+ }
93
+ if (this.events.length > MAX_EVENTS) {
94
+ this.events = this.events.slice(-MAX_EVENTS);
95
+ }
96
+ },
97
+ maskAllInputs: true,
98
+ maskInputOptions: {
99
+ password: true,
100
+ email: true,
101
+ tel: true,
102
+ text: true,
103
+ number: false,
104
+ search: false
105
+ },
106
+ blockClass: "vybesec-block",
107
+ maskTextClass: "vybesec-mask",
108
+ ignoreClass: "vybesec-ignore",
109
+ recordCrossOriginIframes: false,
110
+ sampling: {
111
+ mousemove: 100,
112
+ scroll: 150,
113
+ input: "last"
114
+ }
115
+ });
116
+ }
117
+ async flushOnError(config, errorFingerprint) {
118
+ if (this.flushed || this.events.length < 10) return;
119
+ this.flushed = true;
120
+ const payload = scrubReplayPayload(
121
+ this.events.map((event) => JSON.stringify(event)).join("\n")
122
+ );
123
+ const blob = new Blob([payload], { type: "application/x-ndjson" });
124
+ const url = new URL(
125
+ `${config.replayUrl}/v1/replay/${config.publicKey}/${config.sessionId}`
126
+ );
127
+ if (errorFingerprint) {
128
+ url.searchParams.set("fingerprint", errorFingerprint);
129
+ }
130
+ if (typeof window !== "undefined") {
131
+ url.searchParams.set("urlPath", window.location.pathname);
132
+ }
133
+ if (navigator.sendBeacon) {
134
+ navigator.sendBeacon(url.toString(), blob);
135
+ return;
136
+ }
137
+ await fetch(url.toString(), {
138
+ method: "POST",
139
+ headers: { "Content-Type": "application/x-ndjson" },
140
+ body: blob
141
+ });
142
+ }
143
+ stop() {
144
+ this.stopFn?.();
145
+ this.stopFn = void 0;
146
+ }
147
+ };
148
+ function scrubReplayPayload(payload) {
149
+ return payload.replace(
150
+ /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g,
151
+ "[email]"
152
+ ).replace(/\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/g, "[card]").replace(/sk-[a-zA-Z0-9]{20,}/g, "[api-key]").replace(/Bearer\\s[a-zA-Z0-9\\-_.]{20,}/g, "Bearer [token]").replace(/\b\d{3}-\d{2}-\d{4}\b/g, "[ssn]");
153
+ }
154
+
155
+ // src/index.ts
32
156
  var DEFAULT_INGEST_URL = "https://vybesec-ingest.hexeldigitalstudio.workers.dev";
157
+ var DEFAULT_REPLAY_URL = "https://vybesec-replay.hexeldigitalstudio.workers.dev";
33
158
  var SESSION_KEY = "vybesec_session_id";
34
159
  function getSessionId() {
35
160
  try {
@@ -59,6 +184,34 @@ function getStackTrace(error) {
59
184
  if (error instanceof Error) return error.stack;
60
185
  return void 0;
61
186
  }
187
+ function normalizeMessage(message) {
188
+ return message.replace(/0x[0-9a-f]+/gi, "0xADDR").replace(/\d+:\d+/g, "L:C").replace(/\"[^\"]{0,50}\"/g, '"VAL"');
189
+ }
190
+ function parseStackTrace(stack) {
191
+ if (!stack) return [];
192
+ const lines = stack.split("\n");
193
+ const frames = [];
194
+ for (const line of lines) {
195
+ const match = line.match(/at\\s+(.*?)(?:\\s+\\(|\\s)(.+?):\\d+:\\d+\\)?/);
196
+ if (match) {
197
+ frames.push({ fn: match[1] || "anonymous", file: match[2] || "unknown" });
198
+ }
199
+ }
200
+ return frames;
201
+ }
202
+ async function sha256(input) {
203
+ const buffer = new TextEncoder().encode(input);
204
+ const hash = await crypto.subtle.digest("SHA-256", buffer);
205
+ return Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
206
+ }
207
+ async function computeFingerprint(event) {
208
+ const normalized = normalizeMessage(event.message ?? "");
209
+ const frames = parseStackTrace(event.stackTrace).slice(0, 3).map((frame) => `${frame.file}:${frame.fn}`).join("|");
210
+ const errorType = event.errorType ?? "Error";
211
+ const input = `${errorType}:${normalized}:${frames}`;
212
+ const hash = await sha256(input);
213
+ return hash.slice(0, 16);
214
+ }
62
215
  function scrubSensitiveText(value) {
63
216
  if (!value) return value;
64
217
  const patterns = [
@@ -113,6 +266,10 @@ var VybeSecSDK = class {
113
266
  __publicField(this, "retryTimer");
114
267
  __publicField(this, "retryAttempt", 0);
115
268
  __publicField(this, "isFlushing", false);
269
+ __publicField(this, "replayCapture");
270
+ __publicField(this, "replayEnabled", false);
271
+ __publicField(this, "replayStarted", false);
272
+ __publicField(this, "sessionId");
116
273
  }
117
274
  init(config) {
118
275
  this.config = {
@@ -120,9 +277,18 @@ var VybeSecSDK = class {
120
277
  sampleRate: config.sampleRate ?? 1,
121
278
  maxBuffer: config.maxBuffer ?? 100,
122
279
  maxEventsPerMinute: config.maxEventsPerMinute ?? 120,
123
- ingestUrl: config.ingestUrl ?? DEFAULT_INGEST_URL
280
+ ingestUrl: DEFAULT_INGEST_URL,
281
+ replayUrl: DEFAULT_REPLAY_URL,
282
+ enableVitals: config.enableVitals ?? true,
283
+ enableReplay: config.enableReplay ?? true
124
284
  };
125
285
  if (typeof window === "undefined") return;
286
+ this.sessionId = getSessionId();
287
+ this.replayEnabled = false;
288
+ this.replayStarted = false;
289
+ if (this.config.enableReplay) {
290
+ this.replayCapture = new ReplayCapture();
291
+ }
126
292
  window.addEventListener("error", (event) => {
127
293
  this.captureError(event.error ?? event.message, {
128
294
  url: event.filename
@@ -139,6 +305,21 @@ var VybeSecSDK = class {
139
305
  this.flushBeacon();
140
306
  }
141
307
  });
308
+ if (this.config.enableVitals) {
309
+ void captureWebVitals((metric) => {
310
+ this.captureEvent({
311
+ type: "performance",
312
+ message: metric.metricName,
313
+ timestamp: Date.now(),
314
+ sessionId: this.sessionId,
315
+ extra: {
316
+ metricName: metric.metricName,
317
+ value: metric.value,
318
+ rating: metric.rating
319
+ }
320
+ });
321
+ });
322
+ }
142
323
  if (this.flushTimer) {
143
324
  window.clearInterval(this.flushTimer);
144
325
  }
@@ -158,7 +339,7 @@ var VybeSecSDK = class {
158
339
  stackTrace: getStackTrace(error),
159
340
  errorType: getErrorType(error),
160
341
  url: typeof window !== "undefined" ? window.location.href : void 0,
161
- sessionId: typeof window !== "undefined" ? getSessionId() : void 0,
342
+ sessionId: typeof window !== "undefined" ? this.sessionId ?? getSessionId() : void 0,
162
343
  userId: this.config.userId,
163
344
  timestamp: Date.now(),
164
345
  tags: {
@@ -169,13 +350,14 @@ var VybeSecSDK = class {
169
350
  }
170
351
  });
171
352
  this.pushEvent(event);
353
+ void this.flushReplayOnError(event);
172
354
  }
173
355
  captureEvent(event) {
174
356
  if (!this.config) return;
175
357
  if (!this.shouldAcceptEvent()) return;
176
358
  const enriched = scrubEvent({
177
359
  ...event,
178
- sessionId: event.sessionId ?? (typeof window !== "undefined" ? getSessionId() : void 0),
360
+ sessionId: event.sessionId ?? (typeof window !== "undefined" ? this.sessionId ?? getSessionId() : void 0),
179
361
  userId: event.userId ?? this.config.userId,
180
362
  timestamp: event.timestamp ?? Date.now(),
181
363
  tags: {
@@ -199,18 +381,48 @@ var VybeSecSDK = class {
199
381
  void this.flush();
200
382
  }
201
383
  }
384
+ async startReplay() {
385
+ if (!this.replayCapture || this.replayStarted) return;
386
+ await this.replayCapture.start();
387
+ this.replayStarted = true;
388
+ }
389
+ async flushReplayOnError(event) {
390
+ if (!this.config) return;
391
+ if (!this.replayCapture || !this.replayEnabled) return;
392
+ const fingerprint = await computeFingerprint(event);
393
+ await this.replayCapture.flushOnError(
394
+ {
395
+ publicKey: this.config.key,
396
+ sessionId: this.sessionId ?? getSessionId(),
397
+ replayUrl: this.config.replayUrl
398
+ },
399
+ fingerprint
400
+ );
401
+ }
202
402
  async flush() {
203
403
  if (!this.config || this.isFlushing) return;
204
404
  if (!this.buffer.length) return;
205
405
  this.isFlushing = true;
206
406
  const events = this.buffer.splice(0);
207
407
  try {
208
- await fetch(`${this.config.ingestUrl}/v1/events/${this.config.key}`, {
209
- method: "POST",
210
- headers: { "Content-Type": "application/json" },
211
- body: JSON.stringify(events),
212
- keepalive: true
213
- });
408
+ const response = await fetch(
409
+ `${this.config.ingestUrl}/v1/events/${this.config.key}`,
410
+ {
411
+ method: "POST",
412
+ headers: { "Content-Type": "application/json" },
413
+ body: JSON.stringify(events),
414
+ keepalive: true
415
+ }
416
+ );
417
+ if (response.ok) {
418
+ const data = await response.json().catch(() => null);
419
+ if (data && typeof data.replayEnabled === "boolean") {
420
+ this.replayEnabled = data.replayEnabled;
421
+ if (this.replayEnabled) {
422
+ await this.startReplay();
423
+ }
424
+ }
425
+ }
214
426
  this.retryAttempt = 0;
215
427
  if (this.retryTimer) {
216
428
  window.clearTimeout(this.retryTimer);
@@ -277,7 +489,9 @@ var VybeSecSDK = class {
277
489
  if (typeof window === "undefined" || !("fetch" in window)) return;
278
490
  const originalFetch = window.fetch.bind(window);
279
491
  window.fetch = async (...args) => {
492
+ const start = Date.now();
280
493
  const response = await originalFetch(...args);
494
+ const duration = Date.now() - start;
281
495
  try {
282
496
  const request = args[0];
283
497
  const method = (request instanceof Request ? request.method : args[1]?.method) ?? "GET";
@@ -290,6 +504,7 @@ var VybeSecSDK = class {
290
504
  requestUrl: url,
291
505
  requestMethod: method,
292
506
  responseStatus: response.status,
507
+ responseTimeMs: duration,
293
508
  url: typeof window !== "undefined" ? window.location.href : void 0,
294
509
  timestamp: Date.now()
295
510
  });
@@ -322,6 +537,7 @@ var VybeSecSDK = class {
322
537
  proto.send = function(body) {
323
538
  const xhr = this;
324
539
  const meta = xhr.__vsMeta;
540
+ const startedAt = Date.now();
325
541
  const handleComplete = () => {
326
542
  try {
327
543
  if (!sdk.config || !meta) return;
@@ -334,6 +550,7 @@ var VybeSecSDK = class {
334
550
  requestUrl: meta.url,
335
551
  requestMethod: meta.method,
336
552
  responseStatus: status || void 0,
553
+ responseTimeMs: Date.now() - startedAt,
337
554
  url: typeof window !== "undefined" ? window.location.href : void 0,
338
555
  timestamp: Date.now()
339
556
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { RawEvent } from \"@vybesec/types\";\n\nexport type VybeSecConfig = {\n key: string;\n environment?: \"production\" | \"staging\" | \"development\";\n platform?:\n | \"lovable\"\n | \"cursor\"\n | \"replit\"\n | \"v0\"\n | \"bolt\"\n | \"windsurf\"\n | \"other\";\n release?: string;\n userId?: string;\n sampleRate?: number;\n maxBuffer?: number;\n maxEventsPerMinute?: number;\n ingestUrl?: string;\n};\n\nconst DEFAULT_INGEST_URL = \"https://vybesec-ingest.hexeldigitalstudio.workers.dev\";\nconst SESSION_KEY = \"vybesec_session_id\";\n\nfunction getSessionId(): string {\n try {\n const existing = window.localStorage.getItem(SESSION_KEY);\n if (existing) return existing;\n const next = crypto.randomUUID();\n window.localStorage.setItem(SESSION_KEY, next);\n return next;\n } catch {\n return crypto.randomUUID();\n }\n}\n\nfunction getErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message || \"Unknown error\";\n if (typeof error === \"string\") return error;\n try {\n return JSON.stringify(error);\n } catch {\n return \"Unknown error\";\n }\n}\n\nfunction getErrorType(error: unknown): string {\n if (error instanceof Error && error.name) return error.name;\n return \"Error\";\n}\n\nfunction getStackTrace(error: unknown): string | undefined {\n if (error instanceof Error) return error.stack;\n return undefined;\n}\n\nfunction scrubSensitiveText(value?: string): string | undefined {\n if (!value) return value;\n const patterns = [\n /sk-[a-zA-Z0-9]{20,}/g,\n /pk_[a-zA-Z0-9]{20,}/g,\n /Bearer\\s+[a-zA-Z0-9\\-_.]{20,}/g,\n /password[\"']?\\s*[:=]\\s*[\"'][^\"']+/gi\n ];\n let output = value;\n for (const pattern of patterns) {\n output = output.replace(pattern, \"[REDACTED]\");\n }\n return output;\n}\n\nconst SENSITIVE_KEY = /(password|token|secret|api[_-]?key|authorization)/i;\nconst MAX_SCRUB_DEPTH = 3;\n\nfunction scrubValue(value: unknown, depth = 0): unknown {\n if (depth >= MAX_SCRUB_DEPTH) return value;\n if (typeof value === \"string\") return scrubSensitiveText(value) ?? value;\n if (Array.isArray(value)) {\n return value.map((entry) => scrubValue(entry, depth + 1));\n }\n if (value && typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n const next: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(record)) {\n if (SENSITIVE_KEY.test(key)) {\n next[key] = \"[REDACTED]\";\n continue;\n }\n next[key] = scrubValue(entry, depth + 1);\n }\n return next;\n }\n return value;\n}\n\nfunction scrubEvent(event: RawEvent): RawEvent {\n return {\n ...event,\n message: scrubSensitiveText(event.message) ?? event.message,\n stackTrace: scrubSensitiveText(event.stackTrace),\n tags: event.tags ? (scrubValue(event.tags) as Record<string, string>) : event.tags,\n extra: event.extra ? (scrubValue(event.extra) as Record<string, unknown>) : event.extra\n };\n}\n\nexport class VybeSecSDK {\n private config: VybeSecConfig | null = null;\n private buffer: RawEvent[] = [];\n private eventTimestamps: number[] = [];\n private flushTimer?: number;\n private retryTimer?: number;\n private retryAttempt = 0;\n private isFlushing = false;\n\n init(config: VybeSecConfig) {\n this.config = {\n ...config,\n sampleRate: config.sampleRate ?? 1,\n maxBuffer: config.maxBuffer ?? 100,\n maxEventsPerMinute: config.maxEventsPerMinute ?? 120,\n ingestUrl: config.ingestUrl ?? DEFAULT_INGEST_URL\n };\n\n if (typeof window === \"undefined\") return;\n\n window.addEventListener(\"error\", (event) => {\n this.captureError(event.error ?? event.message, {\n url: event.filename\n });\n });\n\n window.addEventListener(\"unhandledrejection\", (event) => {\n this.captureError(event.reason);\n });\n\n this.patchFetch();\n this.patchXHR();\n this.trackNavigation();\n\n window.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.flushBeacon();\n }\n });\n\n if (this.flushTimer) {\n window.clearInterval(this.flushTimer);\n }\n if (this.retryTimer) {\n window.clearTimeout(this.retryTimer);\n this.retryTimer = undefined;\n }\n this.retryAttempt = 0;\n this.flushTimer = window.setInterval(() => this.flush(), 5000);\n }\n\n captureError(error: unknown, context?: Record<string, string>) {\n if (!this.config) return;\n if (!this.shouldAcceptEvent()) return;\n\n const event: RawEvent = scrubEvent({\n type: \"error\",\n message: getErrorMessage(error),\n stackTrace: getStackTrace(error),\n errorType: getErrorType(error),\n url: typeof window !== \"undefined\" ? window.location.href : undefined,\n sessionId: typeof window !== \"undefined\" ? getSessionId() : undefined,\n userId: this.config.userId,\n timestamp: Date.now(),\n tags: {\n ...(context ?? {}),\n platform: this.config.platform ?? \"other\",\n environment: this.config.environment ?? \"production\",\n release: this.config.release ?? \"\"\n }\n });\n\n this.pushEvent(event);\n }\n\n captureEvent(event: RawEvent) {\n if (!this.config) return;\n if (!this.shouldAcceptEvent()) return;\n const enriched: RawEvent = scrubEvent({\n ...event,\n sessionId: event.sessionId ?? (typeof window !== \"undefined\" ? getSessionId() : undefined),\n userId: event.userId ?? this.config.userId,\n timestamp: event.timestamp ?? Date.now(),\n tags: {\n ...(event.tags ?? {}),\n platform: this.config.platform ?? \"other\",\n environment: this.config.environment ?? \"production\",\n release: this.config.release ?? \"\"\n }\n });\n\n this.pushEvent(enriched);\n }\n\n private pushEvent(event: RawEvent) {\n if (!this.config) return;\n const maxBuffer = this.config.maxBuffer ?? 100;\n if (maxBuffer <= 0) return;\n while (this.buffer.length >= maxBuffer) {\n this.buffer.shift();\n }\n this.buffer.push(event);\n if (this.buffer.length >= 10) {\n void this.flush();\n }\n }\n\n private async flush() {\n if (!this.config || this.isFlushing) return;\n if (!this.buffer.length) return;\n\n this.isFlushing = true;\n const events = this.buffer.splice(0);\n\n try {\n await fetch(`${this.config.ingestUrl}/v1/events/${this.config.key}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(events),\n keepalive: true\n });\n this.retryAttempt = 0;\n if (this.retryTimer) {\n window.clearTimeout(this.retryTimer);\n this.retryTimer = undefined;\n }\n } catch {\n this.buffer.unshift(...events);\n this.scheduleRetry();\n } finally {\n this.isFlushing = false;\n }\n }\n\n private flushBeacon() {\n if (!this.config || !this.buffer.length) return;\n\n const events = this.buffer.splice(0);\n const payload = new Blob([JSON.stringify(events)], {\n type: \"application/json\"\n });\n\n if (navigator.sendBeacon) {\n navigator.sendBeacon(\n `${this.config.ingestUrl}/v1/events/${this.config.key}`,\n payload\n );\n return;\n }\n\n void fetch(`${this.config.ingestUrl}/v1/events/${this.config.key}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(events),\n keepalive: true\n });\n }\n\n private scheduleRetry() {\n if (typeof window === \"undefined\") return;\n const backoffMs = Math.min(30_000, 1000 * 2 ** this.retryAttempt);\n this.retryAttempt = Math.min(this.retryAttempt + 1, 5);\n if (this.retryTimer) {\n window.clearTimeout(this.retryTimer);\n }\n this.retryTimer = window.setTimeout(() => this.flush(), backoffMs);\n }\n\n private shouldAcceptEvent(): boolean {\n if (!this.config) return false;\n if (Math.random() > (this.config.sampleRate ?? 1)) return false;\n if (!this.consumeRateLimit()) return false;\n return true;\n }\n\n private consumeRateLimit(): boolean {\n if (!this.config) return false;\n const maxPerMinute = this.config.maxEventsPerMinute ?? 120;\n if (maxPerMinute <= 0) return false;\n\n const now = Date.now();\n const windowStart = now - 60_000;\n this.eventTimestamps = this.eventTimestamps.filter(\n (timestamp) => timestamp > windowStart\n );\n\n if (this.eventTimestamps.length >= maxPerMinute) {\n return false;\n }\n\n this.eventTimestamps.push(now);\n return true;\n }\n\n private patchFetch() {\n if (typeof window === \"undefined\" || !(\"fetch\" in window)) return;\n\n const originalFetch = window.fetch.bind(window);\n window.fetch = async (...args) => {\n const response = await originalFetch(...args);\n\n try {\n const request = args[0];\n const method =\n (request instanceof Request\n ? request.method\n : args[1]?.method) ?? \"GET\";\n const url = request instanceof Request ? request.url : String(request);\n\n if (!response.ok) {\n this.captureEvent({\n type: \"error\",\n message: `Request failed with ${response.status}`,\n errorType: \"HttpError\",\n requestUrl: url,\n requestMethod: method,\n responseStatus: response.status,\n url: typeof window !== \"undefined\" ? window.location.href : undefined,\n timestamp: Date.now()\n });\n }\n } catch {\n // ignore fetch instrumentation failures\n }\n\n return response;\n };\n }\n\n private patchXHR() {\n if (typeof window === \"undefined\" || !(\"XMLHttpRequest\" in window)) return;\n const sdk = this;\n const proto = XMLHttpRequest.prototype as XMLHttpRequest & {\n __vsPatched?: boolean;\n __vsMeta?: { method: string; url: string };\n };\n\n if (proto.__vsPatched) return;\n proto.__vsPatched = true;\n\n const originalOpen = proto.open;\n // narrow the send signature to match what we'll call\n const originalSend = proto.send as (\n this: XMLHttpRequest,\n body?: Document | XMLHttpRequestBodyInit | null\n ) => void;\n\n proto.open = function (\n method: string,\n url: string,\n async?: boolean,\n user?: string | null,\n password?: string | null\n ) {\n const xhr = this as typeof proto;\n xhr.__vsMeta = { method, url: String(url) };\n return originalOpen.call(\n xhr,\n method,\n url,\n async ?? true,\n user ?? null,\n password ?? null\n );\n };\n\n // Accept any here and cast when calling the native send to avoid\n // TypeScript diagnostics around BodyInit / ReadableStream mismatch.\n proto.send = function (body?: any) {\n const xhr = this as typeof proto;\n const meta = xhr.__vsMeta;\n\n const handleComplete = () => {\n try {\n if (!sdk.config || !meta) return;\n const status = xhr.status;\n if (status >= 400 || status === 0) {\n sdk.captureEvent({\n type: \"error\",\n message: `XHR failed with ${status || \"network error\"}`,\n errorType: \"HttpError\",\n requestUrl: meta.url,\n requestMethod: meta.method,\n responseStatus: status || undefined,\n url: typeof window !== \"undefined\" ? window.location.href : undefined,\n timestamp: Date.now()\n });\n }\n } finally {\n // clean up listeners to avoid leaks / duplicate reporting\n xhr.removeEventListener(\"loadend\", handleComplete);\n xhr.removeEventListener(\"error\", handleComplete);\n }\n };\n\n xhr.addEventListener(\"loadend\", handleComplete);\n xhr.addEventListener(\"error\", handleComplete);\n\n // Cast to the native expected type to satisfy the method signature.\n return originalSend.call(xhr, body as Document | XMLHttpRequestBodyInit | null);\n };\n }\n\n private trackNavigation() {\n if (typeof window === \"undefined\") return;\n const pushState = history.pushState;\n const replaceState = history.replaceState;\n\n const handleNavigation = () => {\n this.captureEvent({\n type: \"info\",\n message: `Navigation to ${window.location.href}`,\n url: window.location.href,\n timestamp: Date.now()\n });\n };\n\n history.pushState = function (...args) {\n const result = pushState.apply(this, args as never);\n handleNavigation();\n return result;\n };\n\n history.replaceState = function (...args) {\n const result = replaceState.apply(this, args as never);\n handleNavigation();\n return result;\n };\n\n window.addEventListener(\"popstate\", handleNavigation);\n }\n}\n\nexport const vybesec = new VybeSecSDK();\nexport const init = (config: VybeSecConfig) => vybesec.init(config);\nexport const captureError = (error: unknown) => vybesec.captureError(error);\nexport const captureEvent = (event: RawEvent) => vybesec.captureEvent(event);\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/vitals.ts","../src/replay.ts"],"sourcesContent":["import type { Platform, RawEvent } from \"@vybesec/types\";\nimport { captureWebVitals } from \"./vitals\";\nimport { ReplayCapture } from \"./replay\";\n\nexport type VybeSecConfig = {\n key: string;\n environment?: \"production\" | \"staging\" | \"development\";\n platform?: Platform;\n release?: string;\n userId?: string;\n sampleRate?: number;\n maxBuffer?: number;\n maxEventsPerMinute?: number;\n enableVitals?: boolean;\n enableReplay?: boolean;\n};\n\ntype InternalConfig = VybeSecConfig & {\n ingestUrl: string;\n replayUrl: string;\n};\n\nconst DEFAULT_INGEST_URL = \"https://vybesec-ingest.hexeldigitalstudio.workers.dev\";\nconst DEFAULT_REPLAY_URL = \"https://vybesec-replay.hexeldigitalstudio.workers.dev\";\nconst SESSION_KEY = \"vybesec_session_id\";\n\nfunction getSessionId(): string {\n try {\n const existing = window.localStorage.getItem(SESSION_KEY);\n if (existing) return existing;\n const next = crypto.randomUUID();\n window.localStorage.setItem(SESSION_KEY, next);\n return next;\n } catch {\n return crypto.randomUUID();\n }\n}\n\nfunction getErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message || \"Unknown error\";\n if (typeof error === \"string\") return error;\n try {\n return JSON.stringify(error);\n } catch {\n return \"Unknown error\";\n }\n}\n\nfunction getErrorType(error: unknown): string {\n if (error instanceof Error && error.name) return error.name;\n return \"Error\";\n}\n\nfunction getStackTrace(error: unknown): string | undefined {\n if (error instanceof Error) return error.stack;\n return undefined;\n}\n\nfunction normalizeMessage(message: string): string {\n return message\n .replace(/0x[0-9a-f]+/gi, \"0xADDR\")\n .replace(/\\d+:\\d+/g, \"L:C\")\n .replace(/\\\"[^\\\"]{0,50}\\\"/g, '\"VAL\"');\n}\n\ntype StackFrame = { file: string; fn: string };\n\nfunction parseStackTrace(stack: string | undefined): StackFrame[] {\n if (!stack) return [];\n const lines = stack.split(\"\\n\");\n const frames: StackFrame[] = [];\n for (const line of lines) {\n const match = line.match(/at\\\\s+(.*?)(?:\\\\s+\\\\(|\\\\s)(.+?):\\\\d+:\\\\d+\\\\)?/);\n if (match) {\n frames.push({ fn: match[1] || \"anonymous\", file: match[2] || \"unknown\" });\n }\n }\n return frames;\n}\n\nasync function sha256(input: string): Promise<string> {\n const buffer = new TextEncoder().encode(input);\n const hash = await crypto.subtle.digest(\"SHA-256\", buffer);\n return Array.from(new Uint8Array(hash))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nasync function computeFingerprint(event: RawEvent): Promise<string> {\n const normalized = normalizeMessage(event.message ?? \"\");\n const frames = parseStackTrace(event.stackTrace)\n .slice(0, 3)\n .map((frame) => `${frame.file}:${frame.fn}`)\n .join(\"|\");\n const errorType = event.errorType ?? \"Error\";\n const input = `${errorType}:${normalized}:${frames}`;\n const hash = await sha256(input);\n return hash.slice(0, 16);\n}\n\nfunction scrubSensitiveText(value?: string): string | undefined {\n if (!value) return value;\n const patterns = [\n /sk-[a-zA-Z0-9]{20,}/g,\n /pk_[a-zA-Z0-9]{20,}/g,\n /Bearer\\s+[a-zA-Z0-9\\-_.]{20,}/g,\n /password[\"']?\\s*[:=]\\s*[\"'][^\"']+/gi\n ];\n let output = value;\n for (const pattern of patterns) {\n output = output.replace(pattern, \"[REDACTED]\");\n }\n return output;\n}\n\nconst SENSITIVE_KEY = /(password|token|secret|api[_-]?key|authorization)/i;\nconst MAX_SCRUB_DEPTH = 3;\n\nfunction scrubValue(value: unknown, depth = 0): unknown {\n if (depth >= MAX_SCRUB_DEPTH) return value;\n if (typeof value === \"string\") return scrubSensitiveText(value) ?? value;\n if (Array.isArray(value)) {\n return value.map((entry) => scrubValue(entry, depth + 1));\n }\n if (value && typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n const next: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(record)) {\n if (SENSITIVE_KEY.test(key)) {\n next[key] = \"[REDACTED]\";\n continue;\n }\n next[key] = scrubValue(entry, depth + 1);\n }\n return next;\n }\n return value;\n}\n\nfunction scrubEvent(event: RawEvent): RawEvent {\n return {\n ...event,\n message: scrubSensitiveText(event.message) ?? event.message,\n stackTrace: scrubSensitiveText(event.stackTrace),\n tags: event.tags ? (scrubValue(event.tags) as Record<string, string>) : event.tags,\n extra: event.extra ? (scrubValue(event.extra) as Record<string, unknown>) : event.extra\n };\n}\n\nexport class VybeSecSDK {\n private config: InternalConfig | null = null;\n private buffer: RawEvent[] = [];\n private eventTimestamps: number[] = [];\n private flushTimer?: number;\n private retryTimer?: number;\n private retryAttempt = 0;\n private isFlushing = false;\n private replayCapture?: ReplayCapture;\n private replayEnabled = false;\n private replayStarted = false;\n private sessionId?: string;\n\n init(config: VybeSecConfig) {\n this.config = {\n ...config,\n sampleRate: config.sampleRate ?? 1,\n maxBuffer: config.maxBuffer ?? 100,\n maxEventsPerMinute: config.maxEventsPerMinute ?? 120,\n ingestUrl: DEFAULT_INGEST_URL,\n replayUrl: DEFAULT_REPLAY_URL,\n enableVitals: config.enableVitals ?? true,\n enableReplay: config.enableReplay ?? true\n };\n\n if (typeof window === \"undefined\") return;\n\n this.sessionId = getSessionId();\n this.replayEnabled = false;\n this.replayStarted = false;\n\n if (this.config.enableReplay) {\n this.replayCapture = new ReplayCapture();\n }\n\n window.addEventListener(\"error\", (event) => {\n this.captureError(event.error ?? event.message, {\n url: event.filename\n });\n });\n\n window.addEventListener(\"unhandledrejection\", (event) => {\n this.captureError(event.reason);\n });\n\n this.patchFetch();\n this.patchXHR();\n this.trackNavigation();\n\n window.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.flushBeacon();\n }\n });\n\n if (this.config.enableVitals) {\n void captureWebVitals((metric) => {\n this.captureEvent({\n type: \"performance\",\n message: metric.metricName,\n timestamp: Date.now(),\n sessionId: this.sessionId,\n extra: {\n metricName: metric.metricName,\n value: metric.value,\n rating: metric.rating\n }\n });\n });\n }\n\n if (this.flushTimer) {\n window.clearInterval(this.flushTimer);\n }\n if (this.retryTimer) {\n window.clearTimeout(this.retryTimer);\n this.retryTimer = undefined;\n }\n this.retryAttempt = 0;\n this.flushTimer = window.setInterval(() => this.flush(), 5000);\n }\n\n captureError(error: unknown, context?: Record<string, string>) {\n if (!this.config) return;\n if (!this.shouldAcceptEvent()) return;\n\n const event: RawEvent = scrubEvent({\n type: \"error\",\n message: getErrorMessage(error),\n stackTrace: getStackTrace(error),\n errorType: getErrorType(error),\n url: typeof window !== \"undefined\" ? window.location.href : undefined,\n sessionId:\n typeof window !== \"undefined\"\n ? this.sessionId ?? getSessionId()\n : undefined,\n userId: this.config.userId,\n timestamp: Date.now(),\n tags: {\n ...(context ?? {}),\n platform: this.config.platform ?? \"other\",\n environment: this.config.environment ?? \"production\",\n release: this.config.release ?? \"\"\n }\n });\n\n this.pushEvent(event);\n void this.flushReplayOnError(event);\n }\n\n captureEvent(event: RawEvent) {\n if (!this.config) return;\n if (!this.shouldAcceptEvent()) return;\n const enriched: RawEvent = scrubEvent({\n ...event,\n sessionId:\n event.sessionId ??\n (typeof window !== \"undefined\"\n ? this.sessionId ?? getSessionId()\n : undefined),\n userId: event.userId ?? this.config.userId,\n timestamp: event.timestamp ?? Date.now(),\n tags: {\n ...(event.tags ?? {}),\n platform: this.config.platform ?? \"other\",\n environment: this.config.environment ?? \"production\",\n release: this.config.release ?? \"\"\n }\n });\n\n this.pushEvent(enriched);\n }\n\n private pushEvent(event: RawEvent) {\n if (!this.config) return;\n const maxBuffer = this.config.maxBuffer ?? 100;\n if (maxBuffer <= 0) return;\n while (this.buffer.length >= maxBuffer) {\n this.buffer.shift();\n }\n this.buffer.push(event);\n if (this.buffer.length >= 10) {\n void this.flush();\n }\n }\n\n private async startReplay() {\n if (!this.replayCapture || this.replayStarted) return;\n await this.replayCapture.start();\n this.replayStarted = true;\n }\n\n private async flushReplayOnError(event: RawEvent) {\n if (!this.config) return;\n if (!this.replayCapture || !this.replayEnabled) return;\n const fingerprint = await computeFingerprint(event);\n await this.replayCapture.flushOnError(\n {\n publicKey: this.config.key,\n sessionId: this.sessionId ?? getSessionId(),\n replayUrl: this.config.replayUrl\n },\n fingerprint\n );\n }\n\n private async flush() {\n if (!this.config || this.isFlushing) return;\n if (!this.buffer.length) return;\n\n this.isFlushing = true;\n const events = this.buffer.splice(0);\n\n try {\n const response = await fetch(\n `${this.config.ingestUrl}/v1/events/${this.config.key}`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(events),\n keepalive: true\n }\n );\n if (response.ok) {\n const data = await response.json().catch(() => null);\n if (data && typeof data.replayEnabled === \"boolean\") {\n this.replayEnabled = data.replayEnabled;\n if (this.replayEnabled) {\n await this.startReplay();\n }\n }\n }\n this.retryAttempt = 0;\n if (this.retryTimer) {\n window.clearTimeout(this.retryTimer);\n this.retryTimer = undefined;\n }\n } catch {\n this.buffer.unshift(...events);\n this.scheduleRetry();\n } finally {\n this.isFlushing = false;\n }\n }\n\n private flushBeacon() {\n if (!this.config || !this.buffer.length) return;\n\n const events = this.buffer.splice(0);\n const payload = new Blob([JSON.stringify(events)], {\n type: \"application/json\"\n });\n\n if (navigator.sendBeacon) {\n navigator.sendBeacon(\n `${this.config.ingestUrl}/v1/events/${this.config.key}`,\n payload\n );\n return;\n }\n\n void fetch(`${this.config.ingestUrl}/v1/events/${this.config.key}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(events),\n keepalive: true\n });\n }\n\n private scheduleRetry() {\n if (typeof window === \"undefined\") return;\n const backoffMs = Math.min(30_000, 1000 * 2 ** this.retryAttempt);\n this.retryAttempt = Math.min(this.retryAttempt + 1, 5);\n if (this.retryTimer) {\n window.clearTimeout(this.retryTimer);\n }\n this.retryTimer = window.setTimeout(() => this.flush(), backoffMs);\n }\n\n private shouldAcceptEvent(): boolean {\n if (!this.config) return false;\n if (Math.random() > (this.config.sampleRate ?? 1)) return false;\n if (!this.consumeRateLimit()) return false;\n return true;\n }\n\n private consumeRateLimit(): boolean {\n if (!this.config) return false;\n const maxPerMinute = this.config.maxEventsPerMinute ?? 120;\n if (maxPerMinute <= 0) return false;\n\n const now = Date.now();\n const windowStart = now - 60_000;\n this.eventTimestamps = this.eventTimestamps.filter(\n (timestamp) => timestamp > windowStart\n );\n\n if (this.eventTimestamps.length >= maxPerMinute) {\n return false;\n }\n\n this.eventTimestamps.push(now);\n return true;\n }\n\n private patchFetch() {\n if (typeof window === \"undefined\" || !(\"fetch\" in window)) return;\n\n const originalFetch = window.fetch.bind(window);\n window.fetch = async (...args) => {\n const start = Date.now();\n const response = await originalFetch(...args);\n const duration = Date.now() - start;\n\n try {\n const request = args[0];\n const method =\n (request instanceof Request\n ? request.method\n : args[1]?.method) ?? \"GET\";\n const url = request instanceof Request ? request.url : String(request);\n\n if (!response.ok) {\n this.captureEvent({\n type: \"error\",\n message: `Request failed with ${response.status}`,\n errorType: \"HttpError\",\n requestUrl: url,\n requestMethod: method,\n responseStatus: response.status,\n responseTimeMs: duration,\n url: typeof window !== \"undefined\" ? window.location.href : undefined,\n timestamp: Date.now()\n });\n }\n } catch {\n // ignore fetch instrumentation failures\n }\n\n return response;\n };\n }\n\n private patchXHR() {\n if (typeof window === \"undefined\" || !(\"XMLHttpRequest\" in window)) return;\n const sdk = this;\n const proto = XMLHttpRequest.prototype as XMLHttpRequest & {\n __vsPatched?: boolean;\n __vsMeta?: { method: string; url: string };\n };\n\n if (proto.__vsPatched) return;\n proto.__vsPatched = true;\n\n const originalOpen = proto.open;\n // narrow the send signature to match what we'll call\n const originalSend = proto.send as (\n this: XMLHttpRequest,\n body?: Document | XMLHttpRequestBodyInit | null\n ) => void;\n\n proto.open = function (\n method: string,\n url: string,\n async?: boolean,\n user?: string | null,\n password?: string | null\n ) {\n const xhr = this as typeof proto;\n xhr.__vsMeta = { method, url: String(url) };\n return originalOpen.call(\n xhr,\n method,\n url,\n async ?? true,\n user ?? null,\n password ?? null\n );\n };\n\n // Accept any here and cast when calling the native send to avoid\n // TypeScript diagnostics around BodyInit / ReadableStream mismatch.\n proto.send = function (body?: any) {\n const xhr = this as typeof proto;\n const meta = xhr.__vsMeta;\n\n const startedAt = Date.now();\n const handleComplete = () => {\n try {\n if (!sdk.config || !meta) return;\n const status = xhr.status;\n if (status >= 400 || status === 0) {\n sdk.captureEvent({\n type: \"error\",\n message: `XHR failed with ${status || \"network error\"}`,\n errorType: \"HttpError\",\n requestUrl: meta.url,\n requestMethod: meta.method,\n responseStatus: status || undefined,\n responseTimeMs: Date.now() - startedAt,\n url: typeof window !== \"undefined\" ? window.location.href : undefined,\n timestamp: Date.now()\n });\n }\n } finally {\n // clean up listeners to avoid leaks / duplicate reporting\n xhr.removeEventListener(\"loadend\", handleComplete);\n xhr.removeEventListener(\"error\", handleComplete);\n }\n };\n\n xhr.addEventListener(\"loadend\", handleComplete);\n xhr.addEventListener(\"error\", handleComplete);\n\n // Cast to the native expected type to satisfy the method signature.\n return originalSend.call(xhr, body as Document | XMLHttpRequestBodyInit | null);\n };\n }\n\n private trackNavigation() {\n if (typeof window === \"undefined\") return;\n const pushState = history.pushState;\n const replaceState = history.replaceState;\n\n const handleNavigation = () => {\n this.captureEvent({\n type: \"info\",\n message: `Navigation to ${window.location.href}`,\n url: window.location.href,\n timestamp: Date.now()\n });\n };\n\n history.pushState = function (...args) {\n const result = pushState.apply(this, args as never);\n handleNavigation();\n return result;\n };\n\n history.replaceState = function (...args) {\n const result = replaceState.apply(this, args as never);\n handleNavigation();\n return result;\n };\n\n window.addEventListener(\"popstate\", handleNavigation);\n }\n}\n\nexport const vybesec = new VybeSecSDK();\nexport const init = (config: VybeSecConfig) => vybesec.init(config);\nexport const captureError = (error: unknown) => vybesec.captureError(error);\nexport const captureEvent = (event: RawEvent) => vybesec.captureEvent(event);\n","import type { Metric } from \"web-vitals\";\n\nexport type VitalsMetric = {\n metricName: string;\n value: number;\n rating: \"good\" | \"needs_improvement\" | \"poor\";\n};\n\nconst thresholds: Record<string, [number, number]> = {\n LCP: [2500, 4000],\n TTFB: [800, 1800],\n FCP: [1800, 3000],\n INP: [200, 500],\n CLS: [0.1, 0.25]\n};\n\nfunction rateMetric(metric: Metric): VitalsMetric[\"rating\"] {\n const [good, poor] = thresholds[metric.name] ?? [0, 0];\n if (metric.value <= good) return \"good\";\n if (metric.value <= poor) return \"needs_improvement\";\n return \"poor\";\n}\n\nexport async function captureWebVitals(\n onMetric: (metric: VitalsMetric) => void\n): Promise<void> {\n const { onLCP, onTTFB, onFCP, onINP, onCLS } = await import(\"web-vitals\");\n const report = (metric: Metric) => {\n onMetric({\n metricName: metric.name,\n value: metric.value,\n rating: rateMetric(metric)\n });\n };\n\n onLCP(report);\n onTTFB(report);\n onFCP(report);\n onINP(report);\n onCLS(report);\n}\n","const BUFFER_SECONDS = 60;\nconst MAX_EVENTS = 2000;\n\nexport type ReplayConfig = {\n publicKey: string;\n sessionId: string;\n replayUrl: string;\n};\n\nexport class ReplayCapture {\n private events: { type: number; data: unknown; timestamp: number }[] = [];\n private stopFn?: () => void;\n private flushed = false;\n\n async start(): Promise<void> {\n if (this.stopFn) return;\n const { record } = await import(\"rrweb\");\n this.stopFn = record({\n emit: (event) => {\n const now = Date.now();\n this.events.push(event);\n const cutoff = now - BUFFER_SECONDS * 1000;\n while (this.events.length > 0 && this.events[0].timestamp < cutoff) {\n this.events.shift();\n }\n if (this.events.length > MAX_EVENTS) {\n this.events = this.events.slice(-MAX_EVENTS);\n }\n },\n maskAllInputs: true,\n maskInputOptions: {\n password: true,\n email: true,\n tel: true,\n text: true,\n number: false,\n search: false\n },\n blockClass: \"vybesec-block\",\n maskTextClass: \"vybesec-mask\",\n ignoreClass: \"vybesec-ignore\",\n recordCrossOriginIframes: false,\n sampling: {\n mousemove: 100,\n scroll: 150,\n input: \"last\"\n }\n });\n }\n\n async flushOnError(\n config: ReplayConfig,\n errorFingerprint: string\n ): Promise<void> {\n if (this.flushed || this.events.length < 10) return;\n this.flushed = true;\n\n const payload = scrubReplayPayload(\n this.events.map((event) => JSON.stringify(event)).join(\"\\n\")\n );\n\n const blob = new Blob([payload], { type: \"application/x-ndjson\" });\n const url = new URL(\n `${config.replayUrl}/v1/replay/${config.publicKey}/${config.sessionId}`\n );\n if (errorFingerprint) {\n url.searchParams.set(\"fingerprint\", errorFingerprint);\n }\n if (typeof window !== \"undefined\") {\n url.searchParams.set(\"urlPath\", window.location.pathname);\n }\n\n if (navigator.sendBeacon) {\n navigator.sendBeacon(url.toString(), blob);\n return;\n }\n\n await fetch(url.toString(), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-ndjson\" },\n body: blob\n });\n }\n\n stop(): void {\n this.stopFn?.();\n this.stopFn = undefined;\n }\n}\n\nfunction scrubReplayPayload(payload: string): string {\n return payload\n .replace(\n /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b/g,\n \"[email]\"\n )\n .replace(/\\b\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}\\b/g, \"[card]\")\n .replace(/sk-[a-zA-Z0-9]{20,}/g, \"[api-key]\")\n .replace(/Bearer\\\\s[a-zA-Z0-9\\\\-_.]{20,}/g, \"Bearer [token]\")\n .replace(/\\b\\d{3}-\\d{2}-\\d{4}\\b/g, \"[ssn]\");\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,60 +1,16 @@
1
- import { z } from 'zod';
2
-
3
- declare const rawEventSchema: z.ZodObject<{
4
- type: z.ZodEnum<["error", "warning", "info", "performance", "custom"]>;
5
- message: z.ZodString;
6
- stackTrace: z.ZodOptional<z.ZodString>;
7
- errorType: z.ZodOptional<z.ZodString>;
8
- url: z.ZodOptional<z.ZodString>;
9
- requestUrl: z.ZodOptional<z.ZodString>;
10
- requestMethod: z.ZodOptional<z.ZodString>;
11
- responseStatus: z.ZodOptional<z.ZodNumber>;
12
- sessionId: z.ZodOptional<z.ZodString>;
13
- userId: z.ZodOptional<z.ZodString>;
14
- timestamp: z.ZodNumber;
15
- tags: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
16
- extra: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
17
- }, "strip", z.ZodTypeAny, {
18
- type: "custom" | "error" | "warning" | "info" | "performance";
19
- message: string;
20
- timestamp: number;
21
- stackTrace?: string | undefined;
22
- errorType?: string | undefined;
23
- url?: string | undefined;
24
- requestUrl?: string | undefined;
25
- requestMethod?: string | undefined;
26
- responseStatus?: number | undefined;
27
- sessionId?: string | undefined;
28
- userId?: string | undefined;
29
- tags?: Record<string, string> | undefined;
30
- extra?: Record<string, any> | undefined;
31
- }, {
32
- type: "custom" | "error" | "warning" | "info" | "performance";
33
- message: string;
34
- timestamp: number;
35
- stackTrace?: string | undefined;
36
- errorType?: string | undefined;
37
- url?: string | undefined;
38
- requestUrl?: string | undefined;
39
- requestMethod?: string | undefined;
40
- responseStatus?: number | undefined;
41
- sessionId?: string | undefined;
42
- userId?: string | undefined;
43
- tags?: Record<string, string> | undefined;
44
- extra?: Record<string, any> | undefined;
45
- }>;
46
- type RawEvent = z.infer<typeof rawEventSchema>;
1
+ import { Platform, RawEvent } from '@vybesec/types';
47
2
 
48
3
  type VybeSecConfig = {
49
4
  key: string;
50
5
  environment?: "production" | "staging" | "development";
51
- platform?: "lovable" | "cursor" | "replit" | "v0" | "bolt" | "windsurf" | "other";
6
+ platform?: Platform;
52
7
  release?: string;
53
8
  userId?: string;
54
9
  sampleRate?: number;
55
10
  maxBuffer?: number;
56
11
  maxEventsPerMinute?: number;
57
- ingestUrl?: string;
12
+ enableVitals?: boolean;
13
+ enableReplay?: boolean;
58
14
  };
59
15
  declare class VybeSecSDK {
60
16
  private config;
@@ -64,10 +20,16 @@ declare class VybeSecSDK {
64
20
  private retryTimer?;
65
21
  private retryAttempt;
66
22
  private isFlushing;
23
+ private replayCapture?;
24
+ private replayEnabled;
25
+ private replayStarted;
26
+ private sessionId?;
67
27
  init(config: VybeSecConfig): void;
68
28
  captureError(error: unknown, context?: Record<string, string>): void;
69
29
  captureEvent(event: RawEvent): void;
70
30
  private pushEvent;
31
+ private startReplay;
32
+ private flushReplayOnError;
71
33
  private flush;
72
34
  private flushBeacon;
73
35
  private scheduleRetry;