@vybesec/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/dist/index.cjs +376 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +53 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +349 -0
- package/dist/index.js.map +1 -0
- package/dist/v1/sdk.js +2 -0
- package/dist/v1/sdk.js.map +7 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# @vybesec/sdk
|
|
2
|
+
|
|
3
|
+
Browser SDK for sending VybeSec events to your ingest endpoint.
|
|
4
|
+
|
|
5
|
+
## Install (npm)
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @vybesec/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { VybeSecSDK } from "@vybesec/sdk";
|
|
13
|
+
|
|
14
|
+
const sdk = new VybeSecSDK();
|
|
15
|
+
sdk.init({
|
|
16
|
+
key: "pk_xxxxxxxxxxxxxxxx"
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## CDN (script tag)
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<script src="https://cdn.eazipost.com/v1/sdk.js" data-key="pk_xxxxxxxxxxxxxxxx" async></script>
|
|
24
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
21
|
+
|
|
22
|
+
// src/index.ts
|
|
23
|
+
var index_exports = {};
|
|
24
|
+
__export(index_exports, {
|
|
25
|
+
VybeSecSDK: () => VybeSecSDK,
|
|
26
|
+
captureError: () => captureError,
|
|
27
|
+
captureEvent: () => captureEvent,
|
|
28
|
+
init: () => init,
|
|
29
|
+
vybesec: () => vybesec
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(index_exports);
|
|
32
|
+
var DEFAULT_INGEST_URL = "https://vybesec-ingest.hexeldigitalstudio.workers.dev";
|
|
33
|
+
var SESSION_KEY = "vybesec_session_id";
|
|
34
|
+
function getSessionId() {
|
|
35
|
+
try {
|
|
36
|
+
const existing = window.localStorage.getItem(SESSION_KEY);
|
|
37
|
+
if (existing) return existing;
|
|
38
|
+
const next = crypto.randomUUID();
|
|
39
|
+
window.localStorage.setItem(SESSION_KEY, next);
|
|
40
|
+
return next;
|
|
41
|
+
} catch {
|
|
42
|
+
return crypto.randomUUID();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function getErrorMessage(error) {
|
|
46
|
+
if (error instanceof Error) return error.message || "Unknown error";
|
|
47
|
+
if (typeof error === "string") return error;
|
|
48
|
+
try {
|
|
49
|
+
return JSON.stringify(error);
|
|
50
|
+
} catch {
|
|
51
|
+
return "Unknown error";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function getErrorType(error) {
|
|
55
|
+
if (error instanceof Error && error.name) return error.name;
|
|
56
|
+
return "Error";
|
|
57
|
+
}
|
|
58
|
+
function getStackTrace(error) {
|
|
59
|
+
if (error instanceof Error) return error.stack;
|
|
60
|
+
return void 0;
|
|
61
|
+
}
|
|
62
|
+
function scrubSensitiveText(value) {
|
|
63
|
+
if (!value) return value;
|
|
64
|
+
const patterns = [
|
|
65
|
+
/sk-[a-zA-Z0-9]{20,}/g,
|
|
66
|
+
/pk_[a-zA-Z0-9]{20,}/g,
|
|
67
|
+
/Bearer\s+[a-zA-Z0-9\-_.]{20,}/g,
|
|
68
|
+
/password["']?\s*[:=]\s*["'][^"']+/gi
|
|
69
|
+
];
|
|
70
|
+
let output = value;
|
|
71
|
+
for (const pattern of patterns) {
|
|
72
|
+
output = output.replace(pattern, "[REDACTED]");
|
|
73
|
+
}
|
|
74
|
+
return output;
|
|
75
|
+
}
|
|
76
|
+
var SENSITIVE_KEY = /(password|token|secret|api[_-]?key|authorization)/i;
|
|
77
|
+
var MAX_SCRUB_DEPTH = 3;
|
|
78
|
+
function scrubValue(value, depth = 0) {
|
|
79
|
+
if (depth >= MAX_SCRUB_DEPTH) return value;
|
|
80
|
+
if (typeof value === "string") return scrubSensitiveText(value) ?? value;
|
|
81
|
+
if (Array.isArray(value)) {
|
|
82
|
+
return value.map((entry) => scrubValue(entry, depth + 1));
|
|
83
|
+
}
|
|
84
|
+
if (value && typeof value === "object") {
|
|
85
|
+
const record = value;
|
|
86
|
+
const next = {};
|
|
87
|
+
for (const [key, entry] of Object.entries(record)) {
|
|
88
|
+
if (SENSITIVE_KEY.test(key)) {
|
|
89
|
+
next[key] = "[REDACTED]";
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
next[key] = scrubValue(entry, depth + 1);
|
|
93
|
+
}
|
|
94
|
+
return next;
|
|
95
|
+
}
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
function scrubEvent(event) {
|
|
99
|
+
return {
|
|
100
|
+
...event,
|
|
101
|
+
message: scrubSensitiveText(event.message) ?? event.message,
|
|
102
|
+
stackTrace: scrubSensitiveText(event.stackTrace),
|
|
103
|
+
tags: event.tags ? scrubValue(event.tags) : event.tags,
|
|
104
|
+
extra: event.extra ? scrubValue(event.extra) : event.extra
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
var VybeSecSDK = class {
|
|
108
|
+
constructor() {
|
|
109
|
+
__publicField(this, "config", null);
|
|
110
|
+
__publicField(this, "buffer", []);
|
|
111
|
+
__publicField(this, "eventTimestamps", []);
|
|
112
|
+
__publicField(this, "flushTimer");
|
|
113
|
+
__publicField(this, "retryTimer");
|
|
114
|
+
__publicField(this, "retryAttempt", 0);
|
|
115
|
+
__publicField(this, "isFlushing", false);
|
|
116
|
+
}
|
|
117
|
+
init(config) {
|
|
118
|
+
this.config = {
|
|
119
|
+
...config,
|
|
120
|
+
sampleRate: config.sampleRate ?? 1,
|
|
121
|
+
maxBuffer: config.maxBuffer ?? 100,
|
|
122
|
+
maxEventsPerMinute: config.maxEventsPerMinute ?? 120,
|
|
123
|
+
ingestUrl: config.ingestUrl ?? DEFAULT_INGEST_URL
|
|
124
|
+
};
|
|
125
|
+
if (typeof window === "undefined") return;
|
|
126
|
+
window.addEventListener("error", (event) => {
|
|
127
|
+
this.captureError(event.error ?? event.message, {
|
|
128
|
+
url: event.filename
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
window.addEventListener("unhandledrejection", (event) => {
|
|
132
|
+
this.captureError(event.reason);
|
|
133
|
+
});
|
|
134
|
+
this.patchFetch();
|
|
135
|
+
this.patchXHR();
|
|
136
|
+
this.trackNavigation();
|
|
137
|
+
window.addEventListener("visibilitychange", () => {
|
|
138
|
+
if (document.visibilityState === "hidden") {
|
|
139
|
+
this.flushBeacon();
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
if (this.flushTimer) {
|
|
143
|
+
window.clearInterval(this.flushTimer);
|
|
144
|
+
}
|
|
145
|
+
if (this.retryTimer) {
|
|
146
|
+
window.clearTimeout(this.retryTimer);
|
|
147
|
+
this.retryTimer = void 0;
|
|
148
|
+
}
|
|
149
|
+
this.retryAttempt = 0;
|
|
150
|
+
this.flushTimer = window.setInterval(() => this.flush(), 5e3);
|
|
151
|
+
}
|
|
152
|
+
captureError(error, context) {
|
|
153
|
+
if (!this.config) return;
|
|
154
|
+
if (!this.shouldAcceptEvent()) return;
|
|
155
|
+
const event = scrubEvent({
|
|
156
|
+
type: "error",
|
|
157
|
+
message: getErrorMessage(error),
|
|
158
|
+
stackTrace: getStackTrace(error),
|
|
159
|
+
errorType: getErrorType(error),
|
|
160
|
+
url: typeof window !== "undefined" ? window.location.href : void 0,
|
|
161
|
+
sessionId: typeof window !== "undefined" ? getSessionId() : void 0,
|
|
162
|
+
userId: this.config.userId,
|
|
163
|
+
timestamp: Date.now(),
|
|
164
|
+
tags: {
|
|
165
|
+
...context ?? {},
|
|
166
|
+
platform: this.config.platform ?? "other",
|
|
167
|
+
environment: this.config.environment ?? "production",
|
|
168
|
+
release: this.config.release ?? ""
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
this.pushEvent(event);
|
|
172
|
+
}
|
|
173
|
+
captureEvent(event) {
|
|
174
|
+
if (!this.config) return;
|
|
175
|
+
if (!this.shouldAcceptEvent()) return;
|
|
176
|
+
const enriched = scrubEvent({
|
|
177
|
+
...event,
|
|
178
|
+
sessionId: event.sessionId ?? (typeof window !== "undefined" ? getSessionId() : void 0),
|
|
179
|
+
userId: event.userId ?? this.config.userId,
|
|
180
|
+
timestamp: event.timestamp ?? Date.now(),
|
|
181
|
+
tags: {
|
|
182
|
+
...event.tags ?? {},
|
|
183
|
+
platform: this.config.platform ?? "other",
|
|
184
|
+
environment: this.config.environment ?? "production",
|
|
185
|
+
release: this.config.release ?? ""
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
this.pushEvent(enriched);
|
|
189
|
+
}
|
|
190
|
+
pushEvent(event) {
|
|
191
|
+
if (!this.config) return;
|
|
192
|
+
const maxBuffer = this.config.maxBuffer ?? 100;
|
|
193
|
+
if (maxBuffer <= 0) return;
|
|
194
|
+
while (this.buffer.length >= maxBuffer) {
|
|
195
|
+
this.buffer.shift();
|
|
196
|
+
}
|
|
197
|
+
this.buffer.push(event);
|
|
198
|
+
if (this.buffer.length >= 10) {
|
|
199
|
+
void this.flush();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async flush() {
|
|
203
|
+
if (!this.config || this.isFlushing) return;
|
|
204
|
+
if (!this.buffer.length) return;
|
|
205
|
+
this.isFlushing = true;
|
|
206
|
+
const events = this.buffer.splice(0);
|
|
207
|
+
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
|
+
});
|
|
214
|
+
this.retryAttempt = 0;
|
|
215
|
+
if (this.retryTimer) {
|
|
216
|
+
window.clearTimeout(this.retryTimer);
|
|
217
|
+
this.retryTimer = void 0;
|
|
218
|
+
}
|
|
219
|
+
} catch {
|
|
220
|
+
this.buffer.unshift(...events);
|
|
221
|
+
this.scheduleRetry();
|
|
222
|
+
} finally {
|
|
223
|
+
this.isFlushing = false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
flushBeacon() {
|
|
227
|
+
if (!this.config || !this.buffer.length) return;
|
|
228
|
+
const events = this.buffer.splice(0);
|
|
229
|
+
const payload = new Blob([JSON.stringify(events)], {
|
|
230
|
+
type: "application/json"
|
|
231
|
+
});
|
|
232
|
+
if (navigator.sendBeacon) {
|
|
233
|
+
navigator.sendBeacon(
|
|
234
|
+
`${this.config.ingestUrl}/v1/events/${this.config.key}`,
|
|
235
|
+
payload
|
|
236
|
+
);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
void fetch(`${this.config.ingestUrl}/v1/events/${this.config.key}`, {
|
|
240
|
+
method: "POST",
|
|
241
|
+
headers: { "Content-Type": "application/json" },
|
|
242
|
+
body: JSON.stringify(events),
|
|
243
|
+
keepalive: true
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
scheduleRetry() {
|
|
247
|
+
if (typeof window === "undefined") return;
|
|
248
|
+
const backoffMs = Math.min(3e4, 1e3 * 2 ** this.retryAttempt);
|
|
249
|
+
this.retryAttempt = Math.min(this.retryAttempt + 1, 5);
|
|
250
|
+
if (this.retryTimer) {
|
|
251
|
+
window.clearTimeout(this.retryTimer);
|
|
252
|
+
}
|
|
253
|
+
this.retryTimer = window.setTimeout(() => this.flush(), backoffMs);
|
|
254
|
+
}
|
|
255
|
+
shouldAcceptEvent() {
|
|
256
|
+
if (!this.config) return false;
|
|
257
|
+
if (Math.random() > (this.config.sampleRate ?? 1)) return false;
|
|
258
|
+
if (!this.consumeRateLimit()) return false;
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
consumeRateLimit() {
|
|
262
|
+
if (!this.config) return false;
|
|
263
|
+
const maxPerMinute = this.config.maxEventsPerMinute ?? 120;
|
|
264
|
+
if (maxPerMinute <= 0) return false;
|
|
265
|
+
const now = Date.now();
|
|
266
|
+
const windowStart = now - 6e4;
|
|
267
|
+
this.eventTimestamps = this.eventTimestamps.filter(
|
|
268
|
+
(timestamp) => timestamp > windowStart
|
|
269
|
+
);
|
|
270
|
+
if (this.eventTimestamps.length >= maxPerMinute) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
this.eventTimestamps.push(now);
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
patchFetch() {
|
|
277
|
+
if (typeof window === "undefined" || !("fetch" in window)) return;
|
|
278
|
+
const originalFetch = window.fetch.bind(window);
|
|
279
|
+
window.fetch = async (...args) => {
|
|
280
|
+
const response = await originalFetch(...args);
|
|
281
|
+
try {
|
|
282
|
+
const request = args[0];
|
|
283
|
+
const method = (request instanceof Request ? request.method : args[1]?.method) ?? "GET";
|
|
284
|
+
const url = request instanceof Request ? request.url : String(request);
|
|
285
|
+
if (!response.ok) {
|
|
286
|
+
this.captureEvent({
|
|
287
|
+
type: "error",
|
|
288
|
+
message: `Request failed with ${response.status}`,
|
|
289
|
+
errorType: "HttpError",
|
|
290
|
+
requestUrl: url,
|
|
291
|
+
requestMethod: method,
|
|
292
|
+
responseStatus: response.status,
|
|
293
|
+
url: typeof window !== "undefined" ? window.location.href : void 0,
|
|
294
|
+
timestamp: Date.now()
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
} catch {
|
|
298
|
+
}
|
|
299
|
+
return response;
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
patchXHR() {
|
|
303
|
+
if (typeof window === "undefined" || !("XMLHttpRequest" in window)) return;
|
|
304
|
+
const sdk = this;
|
|
305
|
+
const proto = XMLHttpRequest.prototype;
|
|
306
|
+
if (proto.__vwPatched) return;
|
|
307
|
+
proto.__vwPatched = true;
|
|
308
|
+
const originalOpen = proto.open;
|
|
309
|
+
const originalSend = proto.send;
|
|
310
|
+
proto.open = function(method, url, async, user, password) {
|
|
311
|
+
const xhr = this;
|
|
312
|
+
xhr.__vwMeta = { method, url: String(url) };
|
|
313
|
+
return originalOpen.call(xhr, method, url, async ?? true, user ?? null, password ?? null);
|
|
314
|
+
};
|
|
315
|
+
proto.send = function(body) {
|
|
316
|
+
const xhr = this;
|
|
317
|
+
const meta = xhr.__vwMeta;
|
|
318
|
+
const handleComplete = () => {
|
|
319
|
+
if (!sdk.config || !meta) return;
|
|
320
|
+
const status = xhr.status;
|
|
321
|
+
if (status >= 400 || status === 0) {
|
|
322
|
+
sdk.captureEvent({
|
|
323
|
+
type: "error",
|
|
324
|
+
message: `XHR failed with ${status || "network error"}`,
|
|
325
|
+
errorType: "HttpError",
|
|
326
|
+
requestUrl: meta.url,
|
|
327
|
+
requestMethod: meta.method,
|
|
328
|
+
responseStatus: status || void 0,
|
|
329
|
+
url: typeof window !== "undefined" ? window.location.href : void 0,
|
|
330
|
+
timestamp: Date.now()
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
xhr.addEventListener("loadend", handleComplete);
|
|
335
|
+
xhr.addEventListener("error", handleComplete);
|
|
336
|
+
return originalSend.call(xhr, body ?? null);
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
trackNavigation() {
|
|
340
|
+
if (typeof window === "undefined") return;
|
|
341
|
+
const pushState = history.pushState;
|
|
342
|
+
const replaceState = history.replaceState;
|
|
343
|
+
const handleNavigation = () => {
|
|
344
|
+
this.captureEvent({
|
|
345
|
+
type: "info",
|
|
346
|
+
message: `Navigation to ${window.location.href}`,
|
|
347
|
+
url: window.location.href,
|
|
348
|
+
timestamp: Date.now()
|
|
349
|
+
});
|
|
350
|
+
};
|
|
351
|
+
history.pushState = function(...args) {
|
|
352
|
+
const result = pushState.apply(this, args);
|
|
353
|
+
handleNavigation();
|
|
354
|
+
return result;
|
|
355
|
+
};
|
|
356
|
+
history.replaceState = function(...args) {
|
|
357
|
+
const result = replaceState.apply(this, args);
|
|
358
|
+
handleNavigation();
|
|
359
|
+
return result;
|
|
360
|
+
};
|
|
361
|
+
window.addEventListener("popstate", handleNavigation);
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
var vybesec = new VybeSecSDK();
|
|
365
|
+
var init = (config) => vybesec.init(config);
|
|
366
|
+
var captureError = (error) => vybesec.captureError(error);
|
|
367
|
+
var captureEvent = (event) => vybesec.captureEvent(event);
|
|
368
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
369
|
+
0 && (module.exports = {
|
|
370
|
+
VybeSecSDK,
|
|
371
|
+
captureError,
|
|
372
|
+
captureEvent,
|
|
373
|
+
init,
|
|
374
|
+
vybesec
|
|
375
|
+
});
|
|
376
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export 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\nexport type RawEvent = {\n type: \"error\" | \"warning\" | \"info\" | \"performance\" | \"custom\";\n message: string;\n stackTrace?: string;\n errorType?: string;\n url?: string;\n requestUrl?: string;\n requestMethod?: string;\n responseStatus?: number;\n sessionId?: string;\n userId?: string;\n timestamp: number;\n tags?: Record<string, string>;\n extra?: Record<string, unknown>;\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 __vwPatched?: boolean;\n __vwMeta?: { method: string; url: string };\n };\n\n if (proto.__vwPatched) return;\n proto.__vwPatched = true;\n\n const originalOpen = proto.open;\n const originalSend = proto.send;\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.__vwMeta = { method, url: String(url) };\n return originalOpen.call(xhr, method, url, async ?? true, user ?? null, password ?? null);\n };\n\n proto.send = function (body?: Document | XMLHttpRequestBodyInit | null) {\n const xhr = this as typeof proto;\n const meta = xhr.__vwMeta;\n\n const handleComplete = () => {\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 };\n\n xhr.addEventListener(\"loadend\", handleComplete);\n xhr.addEventListener(\"error\", handleComplete);\n\n return originalSend.call(xhr, body ?? null);\n };\n }\n\n private trackNavigation() {\n if (typeof window === \"undefined\") return;\n\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":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
type VybeSecConfig = {
|
|
2
|
+
key: string;
|
|
3
|
+
environment?: "production" | "staging" | "development";
|
|
4
|
+
platform?: "lovable" | "cursor" | "replit" | "v0" | "bolt" | "windsurf" | "other";
|
|
5
|
+
release?: string;
|
|
6
|
+
userId?: string;
|
|
7
|
+
sampleRate?: number;
|
|
8
|
+
maxBuffer?: number;
|
|
9
|
+
maxEventsPerMinute?: number;
|
|
10
|
+
ingestUrl?: string;
|
|
11
|
+
};
|
|
12
|
+
type RawEvent = {
|
|
13
|
+
type: "error" | "warning" | "info" | "performance" | "custom";
|
|
14
|
+
message: string;
|
|
15
|
+
stackTrace?: string;
|
|
16
|
+
errorType?: string;
|
|
17
|
+
url?: string;
|
|
18
|
+
requestUrl?: string;
|
|
19
|
+
requestMethod?: string;
|
|
20
|
+
responseStatus?: number;
|
|
21
|
+
sessionId?: string;
|
|
22
|
+
userId?: string;
|
|
23
|
+
timestamp: number;
|
|
24
|
+
tags?: Record<string, string>;
|
|
25
|
+
extra?: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
declare class VybeSecSDK {
|
|
28
|
+
private config;
|
|
29
|
+
private buffer;
|
|
30
|
+
private eventTimestamps;
|
|
31
|
+
private flushTimer?;
|
|
32
|
+
private retryTimer?;
|
|
33
|
+
private retryAttempt;
|
|
34
|
+
private isFlushing;
|
|
35
|
+
init(config: VybeSecConfig): void;
|
|
36
|
+
captureError(error: unknown, context?: Record<string, string>): void;
|
|
37
|
+
captureEvent(event: RawEvent): void;
|
|
38
|
+
private pushEvent;
|
|
39
|
+
private flush;
|
|
40
|
+
private flushBeacon;
|
|
41
|
+
private scheduleRetry;
|
|
42
|
+
private shouldAcceptEvent;
|
|
43
|
+
private consumeRateLimit;
|
|
44
|
+
private patchFetch;
|
|
45
|
+
private patchXHR;
|
|
46
|
+
private trackNavigation;
|
|
47
|
+
}
|
|
48
|
+
declare const vybesec: VybeSecSDK;
|
|
49
|
+
declare const init: (config: VybeSecConfig) => void;
|
|
50
|
+
declare const captureError: (error: unknown) => void;
|
|
51
|
+
declare const captureEvent: (event: RawEvent) => void;
|
|
52
|
+
|
|
53
|
+
export { type RawEvent, type VybeSecConfig, VybeSecSDK, captureError, captureEvent, init, vybesec };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
type VybeSecConfig = {
|
|
2
|
+
key: string;
|
|
3
|
+
environment?: "production" | "staging" | "development";
|
|
4
|
+
platform?: "lovable" | "cursor" | "replit" | "v0" | "bolt" | "windsurf" | "other";
|
|
5
|
+
release?: string;
|
|
6
|
+
userId?: string;
|
|
7
|
+
sampleRate?: number;
|
|
8
|
+
maxBuffer?: number;
|
|
9
|
+
maxEventsPerMinute?: number;
|
|
10
|
+
ingestUrl?: string;
|
|
11
|
+
};
|
|
12
|
+
type RawEvent = {
|
|
13
|
+
type: "error" | "warning" | "info" | "performance" | "custom";
|
|
14
|
+
message: string;
|
|
15
|
+
stackTrace?: string;
|
|
16
|
+
errorType?: string;
|
|
17
|
+
url?: string;
|
|
18
|
+
requestUrl?: string;
|
|
19
|
+
requestMethod?: string;
|
|
20
|
+
responseStatus?: number;
|
|
21
|
+
sessionId?: string;
|
|
22
|
+
userId?: string;
|
|
23
|
+
timestamp: number;
|
|
24
|
+
tags?: Record<string, string>;
|
|
25
|
+
extra?: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
declare class VybeSecSDK {
|
|
28
|
+
private config;
|
|
29
|
+
private buffer;
|
|
30
|
+
private eventTimestamps;
|
|
31
|
+
private flushTimer?;
|
|
32
|
+
private retryTimer?;
|
|
33
|
+
private retryAttempt;
|
|
34
|
+
private isFlushing;
|
|
35
|
+
init(config: VybeSecConfig): void;
|
|
36
|
+
captureError(error: unknown, context?: Record<string, string>): void;
|
|
37
|
+
captureEvent(event: RawEvent): void;
|
|
38
|
+
private pushEvent;
|
|
39
|
+
private flush;
|
|
40
|
+
private flushBeacon;
|
|
41
|
+
private scheduleRetry;
|
|
42
|
+
private shouldAcceptEvent;
|
|
43
|
+
private consumeRateLimit;
|
|
44
|
+
private patchFetch;
|
|
45
|
+
private patchXHR;
|
|
46
|
+
private trackNavigation;
|
|
47
|
+
}
|
|
48
|
+
declare const vybesec: VybeSecSDK;
|
|
49
|
+
declare const init: (config: VybeSecConfig) => void;
|
|
50
|
+
declare const captureError: (error: unknown) => void;
|
|
51
|
+
declare const captureEvent: (event: RawEvent) => void;
|
|
52
|
+
|
|
53
|
+
export { type RawEvent, type VybeSecConfig, VybeSecSDK, captureError, captureEvent, init, vybesec };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
|
|
5
|
+
// src/index.ts
|
|
6
|
+
var DEFAULT_INGEST_URL = "https://vybesec-ingest.hexeldigitalstudio.workers.dev";
|
|
7
|
+
var SESSION_KEY = "vybesec_session_id";
|
|
8
|
+
function getSessionId() {
|
|
9
|
+
try {
|
|
10
|
+
const existing = window.localStorage.getItem(SESSION_KEY);
|
|
11
|
+
if (existing) return existing;
|
|
12
|
+
const next = crypto.randomUUID();
|
|
13
|
+
window.localStorage.setItem(SESSION_KEY, next);
|
|
14
|
+
return next;
|
|
15
|
+
} catch {
|
|
16
|
+
return crypto.randomUUID();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function getErrorMessage(error) {
|
|
20
|
+
if (error instanceof Error) return error.message || "Unknown error";
|
|
21
|
+
if (typeof error === "string") return error;
|
|
22
|
+
try {
|
|
23
|
+
return JSON.stringify(error);
|
|
24
|
+
} catch {
|
|
25
|
+
return "Unknown error";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function getErrorType(error) {
|
|
29
|
+
if (error instanceof Error && error.name) return error.name;
|
|
30
|
+
return "Error";
|
|
31
|
+
}
|
|
32
|
+
function getStackTrace(error) {
|
|
33
|
+
if (error instanceof Error) return error.stack;
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
function scrubSensitiveText(value) {
|
|
37
|
+
if (!value) return value;
|
|
38
|
+
const patterns = [
|
|
39
|
+
/sk-[a-zA-Z0-9]{20,}/g,
|
|
40
|
+
/pk_[a-zA-Z0-9]{20,}/g,
|
|
41
|
+
/Bearer\s+[a-zA-Z0-9\-_.]{20,}/g,
|
|
42
|
+
/password["']?\s*[:=]\s*["'][^"']+/gi
|
|
43
|
+
];
|
|
44
|
+
let output = value;
|
|
45
|
+
for (const pattern of patterns) {
|
|
46
|
+
output = output.replace(pattern, "[REDACTED]");
|
|
47
|
+
}
|
|
48
|
+
return output;
|
|
49
|
+
}
|
|
50
|
+
var SENSITIVE_KEY = /(password|token|secret|api[_-]?key|authorization)/i;
|
|
51
|
+
var MAX_SCRUB_DEPTH = 3;
|
|
52
|
+
function scrubValue(value, depth = 0) {
|
|
53
|
+
if (depth >= MAX_SCRUB_DEPTH) return value;
|
|
54
|
+
if (typeof value === "string") return scrubSensitiveText(value) ?? value;
|
|
55
|
+
if (Array.isArray(value)) {
|
|
56
|
+
return value.map((entry) => scrubValue(entry, depth + 1));
|
|
57
|
+
}
|
|
58
|
+
if (value && typeof value === "object") {
|
|
59
|
+
const record = value;
|
|
60
|
+
const next = {};
|
|
61
|
+
for (const [key, entry] of Object.entries(record)) {
|
|
62
|
+
if (SENSITIVE_KEY.test(key)) {
|
|
63
|
+
next[key] = "[REDACTED]";
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
next[key] = scrubValue(entry, depth + 1);
|
|
67
|
+
}
|
|
68
|
+
return next;
|
|
69
|
+
}
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
function scrubEvent(event) {
|
|
73
|
+
return {
|
|
74
|
+
...event,
|
|
75
|
+
message: scrubSensitiveText(event.message) ?? event.message,
|
|
76
|
+
stackTrace: scrubSensitiveText(event.stackTrace),
|
|
77
|
+
tags: event.tags ? scrubValue(event.tags) : event.tags,
|
|
78
|
+
extra: event.extra ? scrubValue(event.extra) : event.extra
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
var VybeSecSDK = class {
|
|
82
|
+
constructor() {
|
|
83
|
+
__publicField(this, "config", null);
|
|
84
|
+
__publicField(this, "buffer", []);
|
|
85
|
+
__publicField(this, "eventTimestamps", []);
|
|
86
|
+
__publicField(this, "flushTimer");
|
|
87
|
+
__publicField(this, "retryTimer");
|
|
88
|
+
__publicField(this, "retryAttempt", 0);
|
|
89
|
+
__publicField(this, "isFlushing", false);
|
|
90
|
+
}
|
|
91
|
+
init(config) {
|
|
92
|
+
this.config = {
|
|
93
|
+
...config,
|
|
94
|
+
sampleRate: config.sampleRate ?? 1,
|
|
95
|
+
maxBuffer: config.maxBuffer ?? 100,
|
|
96
|
+
maxEventsPerMinute: config.maxEventsPerMinute ?? 120,
|
|
97
|
+
ingestUrl: config.ingestUrl ?? DEFAULT_INGEST_URL
|
|
98
|
+
};
|
|
99
|
+
if (typeof window === "undefined") return;
|
|
100
|
+
window.addEventListener("error", (event) => {
|
|
101
|
+
this.captureError(event.error ?? event.message, {
|
|
102
|
+
url: event.filename
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
window.addEventListener("unhandledrejection", (event) => {
|
|
106
|
+
this.captureError(event.reason);
|
|
107
|
+
});
|
|
108
|
+
this.patchFetch();
|
|
109
|
+
this.patchXHR();
|
|
110
|
+
this.trackNavigation();
|
|
111
|
+
window.addEventListener("visibilitychange", () => {
|
|
112
|
+
if (document.visibilityState === "hidden") {
|
|
113
|
+
this.flushBeacon();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
if (this.flushTimer) {
|
|
117
|
+
window.clearInterval(this.flushTimer);
|
|
118
|
+
}
|
|
119
|
+
if (this.retryTimer) {
|
|
120
|
+
window.clearTimeout(this.retryTimer);
|
|
121
|
+
this.retryTimer = void 0;
|
|
122
|
+
}
|
|
123
|
+
this.retryAttempt = 0;
|
|
124
|
+
this.flushTimer = window.setInterval(() => this.flush(), 5e3);
|
|
125
|
+
}
|
|
126
|
+
captureError(error, context) {
|
|
127
|
+
if (!this.config) return;
|
|
128
|
+
if (!this.shouldAcceptEvent()) return;
|
|
129
|
+
const event = scrubEvent({
|
|
130
|
+
type: "error",
|
|
131
|
+
message: getErrorMessage(error),
|
|
132
|
+
stackTrace: getStackTrace(error),
|
|
133
|
+
errorType: getErrorType(error),
|
|
134
|
+
url: typeof window !== "undefined" ? window.location.href : void 0,
|
|
135
|
+
sessionId: typeof window !== "undefined" ? getSessionId() : void 0,
|
|
136
|
+
userId: this.config.userId,
|
|
137
|
+
timestamp: Date.now(),
|
|
138
|
+
tags: {
|
|
139
|
+
...context ?? {},
|
|
140
|
+
platform: this.config.platform ?? "other",
|
|
141
|
+
environment: this.config.environment ?? "production",
|
|
142
|
+
release: this.config.release ?? ""
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
this.pushEvent(event);
|
|
146
|
+
}
|
|
147
|
+
captureEvent(event) {
|
|
148
|
+
if (!this.config) return;
|
|
149
|
+
if (!this.shouldAcceptEvent()) return;
|
|
150
|
+
const enriched = scrubEvent({
|
|
151
|
+
...event,
|
|
152
|
+
sessionId: event.sessionId ?? (typeof window !== "undefined" ? getSessionId() : void 0),
|
|
153
|
+
userId: event.userId ?? this.config.userId,
|
|
154
|
+
timestamp: event.timestamp ?? Date.now(),
|
|
155
|
+
tags: {
|
|
156
|
+
...event.tags ?? {},
|
|
157
|
+
platform: this.config.platform ?? "other",
|
|
158
|
+
environment: this.config.environment ?? "production",
|
|
159
|
+
release: this.config.release ?? ""
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
this.pushEvent(enriched);
|
|
163
|
+
}
|
|
164
|
+
pushEvent(event) {
|
|
165
|
+
if (!this.config) return;
|
|
166
|
+
const maxBuffer = this.config.maxBuffer ?? 100;
|
|
167
|
+
if (maxBuffer <= 0) return;
|
|
168
|
+
while (this.buffer.length >= maxBuffer) {
|
|
169
|
+
this.buffer.shift();
|
|
170
|
+
}
|
|
171
|
+
this.buffer.push(event);
|
|
172
|
+
if (this.buffer.length >= 10) {
|
|
173
|
+
void this.flush();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async flush() {
|
|
177
|
+
if (!this.config || this.isFlushing) return;
|
|
178
|
+
if (!this.buffer.length) return;
|
|
179
|
+
this.isFlushing = true;
|
|
180
|
+
const events = this.buffer.splice(0);
|
|
181
|
+
try {
|
|
182
|
+
await fetch(`${this.config.ingestUrl}/v1/events/${this.config.key}`, {
|
|
183
|
+
method: "POST",
|
|
184
|
+
headers: { "Content-Type": "application/json" },
|
|
185
|
+
body: JSON.stringify(events),
|
|
186
|
+
keepalive: true
|
|
187
|
+
});
|
|
188
|
+
this.retryAttempt = 0;
|
|
189
|
+
if (this.retryTimer) {
|
|
190
|
+
window.clearTimeout(this.retryTimer);
|
|
191
|
+
this.retryTimer = void 0;
|
|
192
|
+
}
|
|
193
|
+
} catch {
|
|
194
|
+
this.buffer.unshift(...events);
|
|
195
|
+
this.scheduleRetry();
|
|
196
|
+
} finally {
|
|
197
|
+
this.isFlushing = false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
flushBeacon() {
|
|
201
|
+
if (!this.config || !this.buffer.length) return;
|
|
202
|
+
const events = this.buffer.splice(0);
|
|
203
|
+
const payload = new Blob([JSON.stringify(events)], {
|
|
204
|
+
type: "application/json"
|
|
205
|
+
});
|
|
206
|
+
if (navigator.sendBeacon) {
|
|
207
|
+
navigator.sendBeacon(
|
|
208
|
+
`${this.config.ingestUrl}/v1/events/${this.config.key}`,
|
|
209
|
+
payload
|
|
210
|
+
);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
void fetch(`${this.config.ingestUrl}/v1/events/${this.config.key}`, {
|
|
214
|
+
method: "POST",
|
|
215
|
+
headers: { "Content-Type": "application/json" },
|
|
216
|
+
body: JSON.stringify(events),
|
|
217
|
+
keepalive: true
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
scheduleRetry() {
|
|
221
|
+
if (typeof window === "undefined") return;
|
|
222
|
+
const backoffMs = Math.min(3e4, 1e3 * 2 ** this.retryAttempt);
|
|
223
|
+
this.retryAttempt = Math.min(this.retryAttempt + 1, 5);
|
|
224
|
+
if (this.retryTimer) {
|
|
225
|
+
window.clearTimeout(this.retryTimer);
|
|
226
|
+
}
|
|
227
|
+
this.retryTimer = window.setTimeout(() => this.flush(), backoffMs);
|
|
228
|
+
}
|
|
229
|
+
shouldAcceptEvent() {
|
|
230
|
+
if (!this.config) return false;
|
|
231
|
+
if (Math.random() > (this.config.sampleRate ?? 1)) return false;
|
|
232
|
+
if (!this.consumeRateLimit()) return false;
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
consumeRateLimit() {
|
|
236
|
+
if (!this.config) return false;
|
|
237
|
+
const maxPerMinute = this.config.maxEventsPerMinute ?? 120;
|
|
238
|
+
if (maxPerMinute <= 0) return false;
|
|
239
|
+
const now = Date.now();
|
|
240
|
+
const windowStart = now - 6e4;
|
|
241
|
+
this.eventTimestamps = this.eventTimestamps.filter(
|
|
242
|
+
(timestamp) => timestamp > windowStart
|
|
243
|
+
);
|
|
244
|
+
if (this.eventTimestamps.length >= maxPerMinute) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
this.eventTimestamps.push(now);
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
patchFetch() {
|
|
251
|
+
if (typeof window === "undefined" || !("fetch" in window)) return;
|
|
252
|
+
const originalFetch = window.fetch.bind(window);
|
|
253
|
+
window.fetch = async (...args) => {
|
|
254
|
+
const response = await originalFetch(...args);
|
|
255
|
+
try {
|
|
256
|
+
const request = args[0];
|
|
257
|
+
const method = (request instanceof Request ? request.method : args[1]?.method) ?? "GET";
|
|
258
|
+
const url = request instanceof Request ? request.url : String(request);
|
|
259
|
+
if (!response.ok) {
|
|
260
|
+
this.captureEvent({
|
|
261
|
+
type: "error",
|
|
262
|
+
message: `Request failed with ${response.status}`,
|
|
263
|
+
errorType: "HttpError",
|
|
264
|
+
requestUrl: url,
|
|
265
|
+
requestMethod: method,
|
|
266
|
+
responseStatus: response.status,
|
|
267
|
+
url: typeof window !== "undefined" ? window.location.href : void 0,
|
|
268
|
+
timestamp: Date.now()
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
} catch {
|
|
272
|
+
}
|
|
273
|
+
return response;
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
patchXHR() {
|
|
277
|
+
if (typeof window === "undefined" || !("XMLHttpRequest" in window)) return;
|
|
278
|
+
const sdk = this;
|
|
279
|
+
const proto = XMLHttpRequest.prototype;
|
|
280
|
+
if (proto.__vwPatched) return;
|
|
281
|
+
proto.__vwPatched = true;
|
|
282
|
+
const originalOpen = proto.open;
|
|
283
|
+
const originalSend = proto.send;
|
|
284
|
+
proto.open = function(method, url, async, user, password) {
|
|
285
|
+
const xhr = this;
|
|
286
|
+
xhr.__vwMeta = { method, url: String(url) };
|
|
287
|
+
return originalOpen.call(xhr, method, url, async ?? true, user ?? null, password ?? null);
|
|
288
|
+
};
|
|
289
|
+
proto.send = function(body) {
|
|
290
|
+
const xhr = this;
|
|
291
|
+
const meta = xhr.__vwMeta;
|
|
292
|
+
const handleComplete = () => {
|
|
293
|
+
if (!sdk.config || !meta) return;
|
|
294
|
+
const status = xhr.status;
|
|
295
|
+
if (status >= 400 || status === 0) {
|
|
296
|
+
sdk.captureEvent({
|
|
297
|
+
type: "error",
|
|
298
|
+
message: `XHR failed with ${status || "network error"}`,
|
|
299
|
+
errorType: "HttpError",
|
|
300
|
+
requestUrl: meta.url,
|
|
301
|
+
requestMethod: meta.method,
|
|
302
|
+
responseStatus: status || void 0,
|
|
303
|
+
url: typeof window !== "undefined" ? window.location.href : void 0,
|
|
304
|
+
timestamp: Date.now()
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
xhr.addEventListener("loadend", handleComplete);
|
|
309
|
+
xhr.addEventListener("error", handleComplete);
|
|
310
|
+
return originalSend.call(xhr, body ?? null);
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
trackNavigation() {
|
|
314
|
+
if (typeof window === "undefined") return;
|
|
315
|
+
const pushState = history.pushState;
|
|
316
|
+
const replaceState = history.replaceState;
|
|
317
|
+
const handleNavigation = () => {
|
|
318
|
+
this.captureEvent({
|
|
319
|
+
type: "info",
|
|
320
|
+
message: `Navigation to ${window.location.href}`,
|
|
321
|
+
url: window.location.href,
|
|
322
|
+
timestamp: Date.now()
|
|
323
|
+
});
|
|
324
|
+
};
|
|
325
|
+
history.pushState = function(...args) {
|
|
326
|
+
const result = pushState.apply(this, args);
|
|
327
|
+
handleNavigation();
|
|
328
|
+
return result;
|
|
329
|
+
};
|
|
330
|
+
history.replaceState = function(...args) {
|
|
331
|
+
const result = replaceState.apply(this, args);
|
|
332
|
+
handleNavigation();
|
|
333
|
+
return result;
|
|
334
|
+
};
|
|
335
|
+
window.addEventListener("popstate", handleNavigation);
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
var vybesec = new VybeSecSDK();
|
|
339
|
+
var init = (config) => vybesec.init(config);
|
|
340
|
+
var captureError = (error) => vybesec.captureError(error);
|
|
341
|
+
var captureEvent = (event) => vybesec.captureEvent(event);
|
|
342
|
+
export {
|
|
343
|
+
VybeSecSDK,
|
|
344
|
+
captureError,
|
|
345
|
+
captureEvent,
|
|
346
|
+
init,
|
|
347
|
+
vybesec
|
|
348
|
+
};
|
|
349
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export 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\nexport type RawEvent = {\n type: \"error\" | \"warning\" | \"info\" | \"performance\" | \"custom\";\n message: string;\n stackTrace?: string;\n errorType?: string;\n url?: string;\n requestUrl?: string;\n requestMethod?: string;\n responseStatus?: number;\n sessionId?: string;\n userId?: string;\n timestamp: number;\n tags?: Record<string, string>;\n extra?: Record<string, unknown>;\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 __vwPatched?: boolean;\n __vwMeta?: { method: string; url: string };\n };\n\n if (proto.__vwPatched) return;\n proto.__vwPatched = true;\n\n const originalOpen = proto.open;\n const originalSend = proto.send;\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.__vwMeta = { method, url: String(url) };\n return originalOpen.call(xhr, method, url, async ?? true, user ?? null, password ?? null);\n };\n\n proto.send = function (body?: Document | XMLHttpRequestBodyInit | null) {\n const xhr = this as typeof proto;\n const meta = xhr.__vwMeta;\n\n const handleComplete = () => {\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 };\n\n xhr.addEventListener(\"loadend\", handleComplete);\n xhr.addEventListener(\"error\", handleComplete);\n\n return originalSend.call(xhr, body ?? null);\n };\n }\n\n private trackNavigation() {\n if (typeof window === \"undefined\") return;\n\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":[]}
|
package/dist/v1/sdk.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var VybeSec=(()=>{var p=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var b=(t,e)=>{for(var n in e)p(t,n,{get:e[n],enumerable:!0})},R=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of E(e))!T.call(t,i)&&i!==n&&p(t,i,{get:()=>e[i],enumerable:!(r=v(e,i))||r.enumerable});return t};var k=t=>R(p({},"__esModule",{value:!0}),t);var L={};b(L,{VybeSecSDK:()=>h,captureError:()=>q,captureEvent:()=>D,init:()=>U,vybesec:()=>d});var S="https://vybesec-ingest.hexeldigitalstudio.workers.dev",m="vybesec_session_id";function w(){try{let t=window.localStorage.getItem(m);if(t)return t;let e=crypto.randomUUID();return window.localStorage.setItem(m,e),e}catch{return crypto.randomUUID()}}function x(t){if(t instanceof Error)return t.message||"Unknown error";if(typeof t=="string")return t;try{return JSON.stringify(t)}catch{return"Unknown error"}}function _(t){return t instanceof Error&&t.name?t.name:"Error"}function I(t){if(t instanceof Error)return t.stack}function l(t){if(!t)return t;let e=[/sk-[a-zA-Z0-9]{20,}/g,/pk_[a-zA-Z0-9]{20,}/g,/Bearer\s+[a-zA-Z0-9\-_.]{20,}/g,/password["']?\s*[:=]\s*["'][^"']+/gi],n=t;for(let r of e)n=n.replace(r,"[REDACTED]");return n}var M=/(password|token|secret|api[_-]?key|authorization)/i,A=3;function c(t,e=0){if(e>=A)return t;if(typeof t=="string")return l(t)??t;if(Array.isArray(t))return t.map(n=>c(n,e+1));if(t&&typeof t=="object"){let n=t,r={};for(let[i,s]of Object.entries(n)){if(M.test(i)){r[i]="[REDACTED]";continue}r[i]=c(s,e+1)}return r}return t}function y(t){return{...t,message:l(t.message)??t.message,stackTrace:l(t.stackTrace),tags:t.tags?c(t.tags):t.tags,extra:t.extra?c(t.extra):t.extra}}var h=class{config=null;buffer=[];eventTimestamps=[];flushTimer;retryTimer;retryAttempt=0;isFlushing=!1;init(e){this.config={...e,sampleRate:e.sampleRate??1,maxBuffer:e.maxBuffer??100,maxEventsPerMinute:e.maxEventsPerMinute??120,ingestUrl:e.ingestUrl??S},!(typeof window>"u")&&(window.addEventListener("error",n=>{this.captureError(n.error??n.message,{url:n.filename})}),window.addEventListener("unhandledrejection",n=>{this.captureError(n.reason)}),this.patchFetch(),this.patchXHR(),this.trackNavigation(),window.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flushBeacon()}),this.flushTimer&&window.clearInterval(this.flushTimer),this.retryTimer&&(window.clearTimeout(this.retryTimer),this.retryTimer=void 0),this.retryAttempt=0,this.flushTimer=window.setInterval(()=>this.flush(),5e3))}captureError(e,n){if(!this.config||!this.shouldAcceptEvent())return;let r=y({type:"error",message:x(e),stackTrace:I(e),errorType:_(e),url:typeof window<"u"?window.location.href:void 0,sessionId:typeof window<"u"?w():void 0,userId:this.config.userId,timestamp:Date.now(),tags:{...n??{},platform:this.config.platform??"other",environment:this.config.environment??"production",release:this.config.release??""}});this.pushEvent(r)}captureEvent(e){if(!this.config||!this.shouldAcceptEvent())return;let n=y({...e,sessionId:e.sessionId??(typeof window<"u"?w():void 0),userId:e.userId??this.config.userId,timestamp:e.timestamp??Date.now(),tags:{...e.tags??{},platform:this.config.platform??"other",environment:this.config.environment??"production",release:this.config.release??""}});this.pushEvent(n)}pushEvent(e){if(!this.config)return;let n=this.config.maxBuffer??100;if(!(n<=0)){for(;this.buffer.length>=n;)this.buffer.shift();this.buffer.push(e),this.buffer.length>=10&&this.flush()}}async flush(){if(!this.config||this.isFlushing||!this.buffer.length)return;this.isFlushing=!0;let e=this.buffer.splice(0);try{await fetch(`${this.config.ingestUrl}/v1/events/${this.config.key}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),keepalive:!0}),this.retryAttempt=0,this.retryTimer&&(window.clearTimeout(this.retryTimer),this.retryTimer=void 0)}catch{this.buffer.unshift(...e),this.scheduleRetry()}finally{this.isFlushing=!1}}flushBeacon(){if(!this.config||!this.buffer.length)return;let e=this.buffer.splice(0),n=new Blob([JSON.stringify(e)],{type:"application/json"});if(navigator.sendBeacon){navigator.sendBeacon(`${this.config.ingestUrl}/v1/events/${this.config.key}`,n);return}fetch(`${this.config.ingestUrl}/v1/events/${this.config.key}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),keepalive:!0})}scheduleRetry(){if(typeof window>"u")return;let e=Math.min(3e4,1e3*2**this.retryAttempt);this.retryAttempt=Math.min(this.retryAttempt+1,5),this.retryTimer&&window.clearTimeout(this.retryTimer),this.retryTimer=window.setTimeout(()=>this.flush(),e)}shouldAcceptEvent(){return!(!this.config||Math.random()>(this.config.sampleRate??1)||!this.consumeRateLimit())}consumeRateLimit(){if(!this.config)return!1;let e=this.config.maxEventsPerMinute??120;if(e<=0)return!1;let n=Date.now(),r=n-6e4;return this.eventTimestamps=this.eventTimestamps.filter(i=>i>r),this.eventTimestamps.length>=e?!1:(this.eventTimestamps.push(n),!0)}patchFetch(){if(typeof window>"u"||!("fetch"in window))return;let e=window.fetch.bind(window);window.fetch=async(...n)=>{let r=await e(...n);try{let i=n[0],s=(i instanceof Request?i.method:n[1]?.method)??"GET",o=i instanceof Request?i.url:String(i);r.ok||this.captureEvent({type:"error",message:`Request failed with ${r.status}`,errorType:"HttpError",requestUrl:o,requestMethod:s,responseStatus:r.status,url:typeof window<"u"?window.location.href:void 0,timestamp:Date.now()})}catch{}return r}}patchXHR(){if(typeof window>"u"||!("XMLHttpRequest"in window))return;let e=this,n=XMLHttpRequest.prototype;if(n.__vwPatched)return;n.__vwPatched=!0;let r=n.open,i=n.send;n.open=function(s,o,u,f,a){let g=this;return g.__vwMeta={method:s,url:String(o)},r.call(g,s,o,u??!0,f??null,a??null)},n.send=function(s){let o=this,u=o.__vwMeta,f=()=>{if(!e.config||!u)return;let a=o.status;(a>=400||a===0)&&e.captureEvent({type:"error",message:`XHR failed with ${a||"network error"}`,errorType:"HttpError",requestUrl:u.url,requestMethod:u.method,responseStatus:a||void 0,url:typeof window<"u"?window.location.href:void 0,timestamp:Date.now()})};return o.addEventListener("loadend",f),o.addEventListener("error",f),i.call(o,s??null)}}trackNavigation(){if(typeof window>"u")return;let e=history.pushState,n=history.replaceState,r=()=>{this.captureEvent({type:"info",message:`Navigation to ${window.location.href}`,url:window.location.href,timestamp:Date.now()})};history.pushState=function(...i){let s=e.apply(this,i);return r(),s},history.replaceState=function(...i){let s=n.apply(this,i);return r(),s},window.addEventListener("popstate",r)}},d=new h,U=t=>d.init(t),q=t=>d.captureError(t),D=t=>d.captureEvent(t);return k(L);})();
|
|
2
|
+
//# sourceMappingURL=sdk.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["export 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\nexport type RawEvent = {\n type: \"error\" | \"warning\" | \"info\" | \"performance\" | \"custom\";\n message: string;\n stackTrace?: string;\n errorType?: string;\n url?: string;\n requestUrl?: string;\n requestMethod?: string;\n responseStatus?: number;\n sessionId?: string;\n userId?: string;\n timestamp: number;\n tags?: Record<string, string>;\n extra?: Record<string, unknown>;\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 __vwPatched?: boolean;\n __vwMeta?: { method: string; url: string };\n };\n\n if (proto.__vwPatched) return;\n proto.__vwPatched = true;\n\n const originalOpen = proto.open;\n const originalSend = proto.send;\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.__vwMeta = { method, url: String(url) };\n return originalOpen.call(xhr, method, url, async ?? true, user ?? null, password ?? null);\n };\n\n proto.send = function (body?: Document | XMLHttpRequestBodyInit | null) {\n const xhr = this as typeof proto;\n const meta = xhr.__vwMeta;\n\n const handleComplete = () => {\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 };\n\n xhr.addEventListener(\"loadend\", handleComplete);\n xhr.addEventListener(\"error\", handleComplete);\n\n return originalSend.call(xhr, body ?? null);\n };\n }\n\n private trackNavigation() {\n if (typeof window === \"undefined\") return;\n\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"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["index_exports", "__export", "VybeSecSDK", "captureError", "captureEvent", "init", "vybesec", "DEFAULT_INGEST_URL", "SESSION_KEY", "getSessionId", "existing", "next", "getErrorMessage", "error", "getErrorType", "getStackTrace", "scrubSensitiveText", "value", "patterns", "output", "pattern", "SENSITIVE_KEY", "MAX_SCRUB_DEPTH", "scrubValue", "depth", "entry", "record", "key", "scrubEvent", "event", "config", "context", "enriched", "maxBuffer", "events", "payload", "backoffMs", "maxPerMinute", "now", "windowStart", "timestamp", "originalFetch", "args", "response", "request", "method", "url", "sdk", "proto", "originalOpen", "originalSend", "async", "user", "password", "xhr", "body", "meta", "handleComplete", "status", "pushState", "replaceState", "handleNavigation", "result"]
|
|
7
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vybesec/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"build:cdn": "mkdir -p dist/v1 && esbuild src/index.ts --bundle --format=iife --global-name=VybeSec --minify --sourcemap --outfile=dist/v1/sdk.js",
|
|
27
|
+
"build:all": "bun run build && bun run build:cdn",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"lint": "echo 'lint not configured yet'"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"esbuild": "^0.25.9",
|
|
34
|
+
"tsup": "^8.5.0",
|
|
35
|
+
"typescript": "^5.7.3"
|
|
36
|
+
}
|
|
37
|
+
}
|