@crashlensorg/browser 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/dist/index.d.mts +24 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +279 -0
- package/dist/index.mjs +253 -0
- package/package.json +28 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { CrashOptions, CrashLens } from '@crashlensorg/core';
|
|
2
|
+
export { Breadcrumb, CaptureParams, CrashLens, CrashLensUser, CrashOptions, DeviceInfo, Severity } from '@crashlensorg/core';
|
|
3
|
+
|
|
4
|
+
interface BrowserInitOptions extends CrashOptions {
|
|
5
|
+
/** Captura window.onerror/unhandledrejection (padrão: true). */
|
|
6
|
+
enableGlobalHandlers?: boolean;
|
|
7
|
+
/** Breadcrumbs de fetch/XHR (padrão: true). */
|
|
8
|
+
enableHttpBreadcrumbs?: boolean;
|
|
9
|
+
/** Breadcrumbs de console (padrão: true). */
|
|
10
|
+
enableConsoleBreadcrumbs?: boolean;
|
|
11
|
+
/** Breadcrumbs de navegação (padrão: true). */
|
|
12
|
+
enableNavigationBreadcrumbs?: boolean;
|
|
13
|
+
/** Coleta info do browser automaticamente (padrão: true). */
|
|
14
|
+
collectBrowserInfo?: boolean;
|
|
15
|
+
/** Rastreia sessões (padrão: true). */
|
|
16
|
+
enableSessionTracking?: boolean;
|
|
17
|
+
}
|
|
18
|
+
interface BrowserInstance extends CrashLens {
|
|
19
|
+
/** Remove listeners e restaura os instrumentos do browser. */
|
|
20
|
+
cleanup(): void;
|
|
21
|
+
}
|
|
22
|
+
declare function initBrowser(options: BrowserInitOptions): BrowserInstance;
|
|
23
|
+
|
|
24
|
+
export { type BrowserInitOptions, type BrowserInstance, initBrowser };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { CrashOptions, CrashLens } from '@crashlensorg/core';
|
|
2
|
+
export { Breadcrumb, CaptureParams, CrashLens, CrashLensUser, CrashOptions, DeviceInfo, Severity } from '@crashlensorg/core';
|
|
3
|
+
|
|
4
|
+
interface BrowserInitOptions extends CrashOptions {
|
|
5
|
+
/** Captura window.onerror/unhandledrejection (padrão: true). */
|
|
6
|
+
enableGlobalHandlers?: boolean;
|
|
7
|
+
/** Breadcrumbs de fetch/XHR (padrão: true). */
|
|
8
|
+
enableHttpBreadcrumbs?: boolean;
|
|
9
|
+
/** Breadcrumbs de console (padrão: true). */
|
|
10
|
+
enableConsoleBreadcrumbs?: boolean;
|
|
11
|
+
/** Breadcrumbs de navegação (padrão: true). */
|
|
12
|
+
enableNavigationBreadcrumbs?: boolean;
|
|
13
|
+
/** Coleta info do browser automaticamente (padrão: true). */
|
|
14
|
+
collectBrowserInfo?: boolean;
|
|
15
|
+
/** Rastreia sessões (padrão: true). */
|
|
16
|
+
enableSessionTracking?: boolean;
|
|
17
|
+
}
|
|
18
|
+
interface BrowserInstance extends CrashLens {
|
|
19
|
+
/** Remove listeners e restaura os instrumentos do browser. */
|
|
20
|
+
cleanup(): void;
|
|
21
|
+
}
|
|
22
|
+
declare function initBrowser(options: BrowserInitOptions): BrowserInstance;
|
|
23
|
+
|
|
24
|
+
export { type BrowserInitOptions, type BrowserInstance, initBrowser };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
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 __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
CrashLens: () => import_core.CrashLens,
|
|
24
|
+
initBrowser: () => initBrowser
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
var import_core = require("@crashlensorg/core");
|
|
28
|
+
|
|
29
|
+
// src/device.ts
|
|
30
|
+
function parseUserAgent(ua) {
|
|
31
|
+
const nameMap = [
|
|
32
|
+
[/Edg\//, "Edge"],
|
|
33
|
+
[/OPR\//, "Opera"],
|
|
34
|
+
[/Chrome\//, "Chrome"],
|
|
35
|
+
[/Firefox\//, "Firefox"],
|
|
36
|
+
[/Safari\//, "Safari"],
|
|
37
|
+
[/MSIE /, "IE"],
|
|
38
|
+
[/Trident\//, "IE"]
|
|
39
|
+
];
|
|
40
|
+
for (const [re, name] of nameMap) {
|
|
41
|
+
if (re.test(ua)) {
|
|
42
|
+
const part = ua.split(" ").find((p) => re.test(p));
|
|
43
|
+
const version = part?.split("/")[1]?.match(/^[\d.]+/)?.[0];
|
|
44
|
+
return { browserName: name, browserVersion: version };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
var STORAGE_KEY = "crashlens_device_id";
|
|
50
|
+
function getOrCreateDeviceId() {
|
|
51
|
+
try {
|
|
52
|
+
const existing = localStorage.getItem(STORAGE_KEY);
|
|
53
|
+
if (existing) return existing;
|
|
54
|
+
const id = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : Math.random().toString(36).slice(2);
|
|
55
|
+
localStorage.setItem(STORAGE_KEY, id);
|
|
56
|
+
return id;
|
|
57
|
+
} catch {
|
|
58
|
+
return Math.random().toString(36).slice(2);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function collectBrowserInfo() {
|
|
62
|
+
const nav = navigator;
|
|
63
|
+
const ua = nav?.userAgent ?? "";
|
|
64
|
+
const { browserName, browserVersion } = parseUserAgent(ua);
|
|
65
|
+
const web = {
|
|
66
|
+
browserName,
|
|
67
|
+
browserVersion,
|
|
68
|
+
userAgent: ua,
|
|
69
|
+
platform: nav?.platform,
|
|
70
|
+
language: nav?.language,
|
|
71
|
+
vendor: nav?.vendor,
|
|
72
|
+
screenResolution: typeof screen !== "undefined" ? `${screen.width}x${screen.height}` : void 0
|
|
73
|
+
};
|
|
74
|
+
return {
|
|
75
|
+
deviceId: getOrCreateDeviceId(),
|
|
76
|
+
deviceOs: "web",
|
|
77
|
+
deviceType: "web",
|
|
78
|
+
deviceName: browserName,
|
|
79
|
+
deviceModel: browserName,
|
|
80
|
+
cpuArchitecture: void 0,
|
|
81
|
+
locale: nav?.language,
|
|
82
|
+
screenResolution: web.screenResolution,
|
|
83
|
+
platformInfo: { web }
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/integrations.ts
|
|
88
|
+
function installGlobalErrorHandlers(hooks) {
|
|
89
|
+
const onError = (event) => {
|
|
90
|
+
let error = event.error;
|
|
91
|
+
if (!(error instanceof Error) && event.message) {
|
|
92
|
+
error = new Error(event.message);
|
|
93
|
+
error.colno = event.colno;
|
|
94
|
+
}
|
|
95
|
+
hooks.onError(error ?? new Error(event.message || "window.onerror"), {
|
|
96
|
+
source: event.filename,
|
|
97
|
+
line: event.lineno,
|
|
98
|
+
column: event.colno
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
const onRejection = (event) => {
|
|
102
|
+
hooks.onUnhandledRejection(event.reason ?? event.reason, {});
|
|
103
|
+
};
|
|
104
|
+
window.addEventListener("error", onError);
|
|
105
|
+
window.addEventListener("unhandledrejection", onRejection);
|
|
106
|
+
return () => {
|
|
107
|
+
window.removeEventListener("error", onError);
|
|
108
|
+
window.removeEventListener("unhandledrejection", onRejection);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function initGlobalHandlers(client) {
|
|
112
|
+
return installGlobalErrorHandlers({
|
|
113
|
+
onError: (error, ctx) => {
|
|
114
|
+
client.captureException(error, {
|
|
115
|
+
handled: false,
|
|
116
|
+
context: "window.onerror",
|
|
117
|
+
tags: { source: "window.onerror" },
|
|
118
|
+
extra: { ...ctx }
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
onUnhandledRejection: (reason, _ctx) => {
|
|
122
|
+
client.captureException(reason, {
|
|
123
|
+
handled: false,
|
|
124
|
+
context: "unhandledrejection",
|
|
125
|
+
tags: { source: "unhandledrejection" }
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/http-breadcrumbs.ts
|
|
132
|
+
var xhrMeta = /* @__PURE__ */ new WeakMap();
|
|
133
|
+
function instrumentHttpBreadcrumbs(client) {
|
|
134
|
+
const addRequest = (method, url, status) => {
|
|
135
|
+
client.addBreadcrumb({
|
|
136
|
+
message: `${method} ${url}`,
|
|
137
|
+
type: "request",
|
|
138
|
+
category: "http",
|
|
139
|
+
data: { status, url }
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
const originalFetch = window.fetch.bind(window);
|
|
143
|
+
const wrappedFetch = (input, init) => {
|
|
144
|
+
let method = init?.method ?? "GET";
|
|
145
|
+
let url;
|
|
146
|
+
if (typeof input === "string") {
|
|
147
|
+
url = input;
|
|
148
|
+
} else if (input instanceof URL) {
|
|
149
|
+
url = input.toString();
|
|
150
|
+
} else {
|
|
151
|
+
url = input.url;
|
|
152
|
+
method = input.method ?? method;
|
|
153
|
+
}
|
|
154
|
+
const promise = originalFetch(input, init);
|
|
155
|
+
promise.then((res) => {
|
|
156
|
+
addRequest(method, url, res.status);
|
|
157
|
+
return res;
|
|
158
|
+
}).catch(() => addRequest(method, url));
|
|
159
|
+
return promise;
|
|
160
|
+
};
|
|
161
|
+
window.fetch = wrappedFetch;
|
|
162
|
+
const originalOpen = XMLHttpRequest.prototype.open;
|
|
163
|
+
const originalSend = XMLHttpRequest.prototype.send;
|
|
164
|
+
XMLHttpRequest.prototype.open = function(method, url) {
|
|
165
|
+
xhrMeta.set(this, { method, url });
|
|
166
|
+
originalOpen.call(this, method, url, true);
|
|
167
|
+
};
|
|
168
|
+
XMLHttpRequest.prototype.send = function(body) {
|
|
169
|
+
this.addEventListener("loadend", () => {
|
|
170
|
+
const meta = xhrMeta.get(this);
|
|
171
|
+
if (meta) addRequest(meta.method ?? "GET", meta.url ?? "", this.status);
|
|
172
|
+
});
|
|
173
|
+
originalSend.call(this, body);
|
|
174
|
+
};
|
|
175
|
+
return () => {
|
|
176
|
+
window.fetch = originalFetch;
|
|
177
|
+
XMLHttpRequest.prototype.open = originalOpen;
|
|
178
|
+
XMLHttpRequest.prototype.send = originalSend;
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/console-breadcrumbs.ts
|
|
183
|
+
function instrumentConsoleBreadcrumbs(client) {
|
|
184
|
+
const restore = [];
|
|
185
|
+
const names = ["log", "error", "warn", "info", "debug", "trace"];
|
|
186
|
+
for (const name of names) {
|
|
187
|
+
const original = console[name]?.bind(console);
|
|
188
|
+
if (!original) continue;
|
|
189
|
+
const level = name === "error" ? "error" : name === "warn" ? "warning" : name === "info" ? "info" : "debug";
|
|
190
|
+
const wrapped = (...args) => {
|
|
191
|
+
const message = args.map((a) => {
|
|
192
|
+
if (a instanceof Error) return a.stack || a.message;
|
|
193
|
+
if (typeof a === "string") return a;
|
|
194
|
+
try {
|
|
195
|
+
return JSON.stringify(a);
|
|
196
|
+
} catch {
|
|
197
|
+
return String(a);
|
|
198
|
+
}
|
|
199
|
+
}).join(" ");
|
|
200
|
+
client.addBreadcrumb({
|
|
201
|
+
message: message || level,
|
|
202
|
+
type: "log",
|
|
203
|
+
category: "console",
|
|
204
|
+
data: { level, args }
|
|
205
|
+
});
|
|
206
|
+
return original(...args);
|
|
207
|
+
};
|
|
208
|
+
console[name] = wrapped;
|
|
209
|
+
restore.push(() => {
|
|
210
|
+
console[name] = original;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return () => {
|
|
214
|
+
for (const r of restore) r();
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/navigation-breadcrumbs.ts
|
|
219
|
+
function instrumentNavigationBreadcrumbs(client) {
|
|
220
|
+
if (typeof window === "undefined" || !window.history) return () => void 0;
|
|
221
|
+
const add = (url) => {
|
|
222
|
+
client.addBreadcrumb({ message: url, type: "navigation", category: "navigation" });
|
|
223
|
+
};
|
|
224
|
+
const originalPush = window.history.pushState.bind(window.history);
|
|
225
|
+
const originalReplace = window.history.replaceState.bind(window.history);
|
|
226
|
+
window.history.pushState = (data, unused, url) => {
|
|
227
|
+
originalPush(data, unused, url);
|
|
228
|
+
add(window.location.href);
|
|
229
|
+
};
|
|
230
|
+
window.history.replaceState = (data, unused, url) => {
|
|
231
|
+
originalReplace(data, unused, url);
|
|
232
|
+
add(window.location.href);
|
|
233
|
+
};
|
|
234
|
+
return () => {
|
|
235
|
+
window.history.pushState = originalPush;
|
|
236
|
+
window.history.replaceState = originalReplace;
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// src/index.ts
|
|
241
|
+
function initBrowser(options) {
|
|
242
|
+
const deviceInfo = options.deviceInfo ?? (options.collectBrowserInfo === false ? void 0 : collectBrowserInfo());
|
|
243
|
+
const client = new import_core.CrashLens({
|
|
244
|
+
...options,
|
|
245
|
+
runtime: options.runtime ?? "browser",
|
|
246
|
+
runtimeVersion: options.runtimeVersion,
|
|
247
|
+
deviceInfo,
|
|
248
|
+
enableSessionTracking: options.enableSessionTracking ?? true
|
|
249
|
+
});
|
|
250
|
+
const cleanups = [];
|
|
251
|
+
if (options.enableGlobalHandlers !== false) {
|
|
252
|
+
cleanups.push(initGlobalHandlers(client));
|
|
253
|
+
}
|
|
254
|
+
if (options.enableHttpBreadcrumbs !== false) {
|
|
255
|
+
cleanups.push(instrumentHttpBreadcrumbs(client));
|
|
256
|
+
}
|
|
257
|
+
if (options.enableConsoleBreadcrumbs !== false) {
|
|
258
|
+
cleanups.push(instrumentConsoleBreadcrumbs(client));
|
|
259
|
+
}
|
|
260
|
+
if (options.enableNavigationBreadcrumbs !== false) {
|
|
261
|
+
cleanups.push(instrumentNavigationBreadcrumbs(client));
|
|
262
|
+
}
|
|
263
|
+
void client.init();
|
|
264
|
+
const instance = client;
|
|
265
|
+
instance.cleanup = () => {
|
|
266
|
+
for (const c of cleanups) {
|
|
267
|
+
try {
|
|
268
|
+
c();
|
|
269
|
+
} catch {
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
return instance;
|
|
274
|
+
}
|
|
275
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
276
|
+
0 && (module.exports = {
|
|
277
|
+
CrashLens,
|
|
278
|
+
initBrowser
|
|
279
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { CrashLens } from "@crashlensorg/core";
|
|
3
|
+
|
|
4
|
+
// src/device.ts
|
|
5
|
+
function parseUserAgent(ua) {
|
|
6
|
+
const nameMap = [
|
|
7
|
+
[/Edg\//, "Edge"],
|
|
8
|
+
[/OPR\//, "Opera"],
|
|
9
|
+
[/Chrome\//, "Chrome"],
|
|
10
|
+
[/Firefox\//, "Firefox"],
|
|
11
|
+
[/Safari\//, "Safari"],
|
|
12
|
+
[/MSIE /, "IE"],
|
|
13
|
+
[/Trident\//, "IE"]
|
|
14
|
+
];
|
|
15
|
+
for (const [re, name] of nameMap) {
|
|
16
|
+
if (re.test(ua)) {
|
|
17
|
+
const part = ua.split(" ").find((p) => re.test(p));
|
|
18
|
+
const version = part?.split("/")[1]?.match(/^[\d.]+/)?.[0];
|
|
19
|
+
return { browserName: name, browserVersion: version };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
var STORAGE_KEY = "crashlens_device_id";
|
|
25
|
+
function getOrCreateDeviceId() {
|
|
26
|
+
try {
|
|
27
|
+
const existing = localStorage.getItem(STORAGE_KEY);
|
|
28
|
+
if (existing) return existing;
|
|
29
|
+
const id = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : Math.random().toString(36).slice(2);
|
|
30
|
+
localStorage.setItem(STORAGE_KEY, id);
|
|
31
|
+
return id;
|
|
32
|
+
} catch {
|
|
33
|
+
return Math.random().toString(36).slice(2);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function collectBrowserInfo() {
|
|
37
|
+
const nav = navigator;
|
|
38
|
+
const ua = nav?.userAgent ?? "";
|
|
39
|
+
const { browserName, browserVersion } = parseUserAgent(ua);
|
|
40
|
+
const web = {
|
|
41
|
+
browserName,
|
|
42
|
+
browserVersion,
|
|
43
|
+
userAgent: ua,
|
|
44
|
+
platform: nav?.platform,
|
|
45
|
+
language: nav?.language,
|
|
46
|
+
vendor: nav?.vendor,
|
|
47
|
+
screenResolution: typeof screen !== "undefined" ? `${screen.width}x${screen.height}` : void 0
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
deviceId: getOrCreateDeviceId(),
|
|
51
|
+
deviceOs: "web",
|
|
52
|
+
deviceType: "web",
|
|
53
|
+
deviceName: browserName,
|
|
54
|
+
deviceModel: browserName,
|
|
55
|
+
cpuArchitecture: void 0,
|
|
56
|
+
locale: nav?.language,
|
|
57
|
+
screenResolution: web.screenResolution,
|
|
58
|
+
platformInfo: { web }
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/integrations.ts
|
|
63
|
+
function installGlobalErrorHandlers(hooks) {
|
|
64
|
+
const onError = (event) => {
|
|
65
|
+
let error = event.error;
|
|
66
|
+
if (!(error instanceof Error) && event.message) {
|
|
67
|
+
error = new Error(event.message);
|
|
68
|
+
error.colno = event.colno;
|
|
69
|
+
}
|
|
70
|
+
hooks.onError(error ?? new Error(event.message || "window.onerror"), {
|
|
71
|
+
source: event.filename,
|
|
72
|
+
line: event.lineno,
|
|
73
|
+
column: event.colno
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
const onRejection = (event) => {
|
|
77
|
+
hooks.onUnhandledRejection(event.reason ?? event.reason, {});
|
|
78
|
+
};
|
|
79
|
+
window.addEventListener("error", onError);
|
|
80
|
+
window.addEventListener("unhandledrejection", onRejection);
|
|
81
|
+
return () => {
|
|
82
|
+
window.removeEventListener("error", onError);
|
|
83
|
+
window.removeEventListener("unhandledrejection", onRejection);
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function initGlobalHandlers(client) {
|
|
87
|
+
return installGlobalErrorHandlers({
|
|
88
|
+
onError: (error, ctx) => {
|
|
89
|
+
client.captureException(error, {
|
|
90
|
+
handled: false,
|
|
91
|
+
context: "window.onerror",
|
|
92
|
+
tags: { source: "window.onerror" },
|
|
93
|
+
extra: { ...ctx }
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
onUnhandledRejection: (reason, _ctx) => {
|
|
97
|
+
client.captureException(reason, {
|
|
98
|
+
handled: false,
|
|
99
|
+
context: "unhandledrejection",
|
|
100
|
+
tags: { source: "unhandledrejection" }
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/http-breadcrumbs.ts
|
|
107
|
+
var xhrMeta = /* @__PURE__ */ new WeakMap();
|
|
108
|
+
function instrumentHttpBreadcrumbs(client) {
|
|
109
|
+
const addRequest = (method, url, status) => {
|
|
110
|
+
client.addBreadcrumb({
|
|
111
|
+
message: `${method} ${url}`,
|
|
112
|
+
type: "request",
|
|
113
|
+
category: "http",
|
|
114
|
+
data: { status, url }
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
const originalFetch = window.fetch.bind(window);
|
|
118
|
+
const wrappedFetch = (input, init) => {
|
|
119
|
+
let method = init?.method ?? "GET";
|
|
120
|
+
let url;
|
|
121
|
+
if (typeof input === "string") {
|
|
122
|
+
url = input;
|
|
123
|
+
} else if (input instanceof URL) {
|
|
124
|
+
url = input.toString();
|
|
125
|
+
} else {
|
|
126
|
+
url = input.url;
|
|
127
|
+
method = input.method ?? method;
|
|
128
|
+
}
|
|
129
|
+
const promise = originalFetch(input, init);
|
|
130
|
+
promise.then((res) => {
|
|
131
|
+
addRequest(method, url, res.status);
|
|
132
|
+
return res;
|
|
133
|
+
}).catch(() => addRequest(method, url));
|
|
134
|
+
return promise;
|
|
135
|
+
};
|
|
136
|
+
window.fetch = wrappedFetch;
|
|
137
|
+
const originalOpen = XMLHttpRequest.prototype.open;
|
|
138
|
+
const originalSend = XMLHttpRequest.prototype.send;
|
|
139
|
+
XMLHttpRequest.prototype.open = function(method, url) {
|
|
140
|
+
xhrMeta.set(this, { method, url });
|
|
141
|
+
originalOpen.call(this, method, url, true);
|
|
142
|
+
};
|
|
143
|
+
XMLHttpRequest.prototype.send = function(body) {
|
|
144
|
+
this.addEventListener("loadend", () => {
|
|
145
|
+
const meta = xhrMeta.get(this);
|
|
146
|
+
if (meta) addRequest(meta.method ?? "GET", meta.url ?? "", this.status);
|
|
147
|
+
});
|
|
148
|
+
originalSend.call(this, body);
|
|
149
|
+
};
|
|
150
|
+
return () => {
|
|
151
|
+
window.fetch = originalFetch;
|
|
152
|
+
XMLHttpRequest.prototype.open = originalOpen;
|
|
153
|
+
XMLHttpRequest.prototype.send = originalSend;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/console-breadcrumbs.ts
|
|
158
|
+
function instrumentConsoleBreadcrumbs(client) {
|
|
159
|
+
const restore = [];
|
|
160
|
+
const names = ["log", "error", "warn", "info", "debug", "trace"];
|
|
161
|
+
for (const name of names) {
|
|
162
|
+
const original = console[name]?.bind(console);
|
|
163
|
+
if (!original) continue;
|
|
164
|
+
const level = name === "error" ? "error" : name === "warn" ? "warning" : name === "info" ? "info" : "debug";
|
|
165
|
+
const wrapped = (...args) => {
|
|
166
|
+
const message = args.map((a) => {
|
|
167
|
+
if (a instanceof Error) return a.stack || a.message;
|
|
168
|
+
if (typeof a === "string") return a;
|
|
169
|
+
try {
|
|
170
|
+
return JSON.stringify(a);
|
|
171
|
+
} catch {
|
|
172
|
+
return String(a);
|
|
173
|
+
}
|
|
174
|
+
}).join(" ");
|
|
175
|
+
client.addBreadcrumb({
|
|
176
|
+
message: message || level,
|
|
177
|
+
type: "log",
|
|
178
|
+
category: "console",
|
|
179
|
+
data: { level, args }
|
|
180
|
+
});
|
|
181
|
+
return original(...args);
|
|
182
|
+
};
|
|
183
|
+
console[name] = wrapped;
|
|
184
|
+
restore.push(() => {
|
|
185
|
+
console[name] = original;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
return () => {
|
|
189
|
+
for (const r of restore) r();
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/navigation-breadcrumbs.ts
|
|
194
|
+
function instrumentNavigationBreadcrumbs(client) {
|
|
195
|
+
if (typeof window === "undefined" || !window.history) return () => void 0;
|
|
196
|
+
const add = (url) => {
|
|
197
|
+
client.addBreadcrumb({ message: url, type: "navigation", category: "navigation" });
|
|
198
|
+
};
|
|
199
|
+
const originalPush = window.history.pushState.bind(window.history);
|
|
200
|
+
const originalReplace = window.history.replaceState.bind(window.history);
|
|
201
|
+
window.history.pushState = (data, unused, url) => {
|
|
202
|
+
originalPush(data, unused, url);
|
|
203
|
+
add(window.location.href);
|
|
204
|
+
};
|
|
205
|
+
window.history.replaceState = (data, unused, url) => {
|
|
206
|
+
originalReplace(data, unused, url);
|
|
207
|
+
add(window.location.href);
|
|
208
|
+
};
|
|
209
|
+
return () => {
|
|
210
|
+
window.history.pushState = originalPush;
|
|
211
|
+
window.history.replaceState = originalReplace;
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/index.ts
|
|
216
|
+
function initBrowser(options) {
|
|
217
|
+
const deviceInfo = options.deviceInfo ?? (options.collectBrowserInfo === false ? void 0 : collectBrowserInfo());
|
|
218
|
+
const client = new CrashLens({
|
|
219
|
+
...options,
|
|
220
|
+
runtime: options.runtime ?? "browser",
|
|
221
|
+
runtimeVersion: options.runtimeVersion,
|
|
222
|
+
deviceInfo,
|
|
223
|
+
enableSessionTracking: options.enableSessionTracking ?? true
|
|
224
|
+
});
|
|
225
|
+
const cleanups = [];
|
|
226
|
+
if (options.enableGlobalHandlers !== false) {
|
|
227
|
+
cleanups.push(initGlobalHandlers(client));
|
|
228
|
+
}
|
|
229
|
+
if (options.enableHttpBreadcrumbs !== false) {
|
|
230
|
+
cleanups.push(instrumentHttpBreadcrumbs(client));
|
|
231
|
+
}
|
|
232
|
+
if (options.enableConsoleBreadcrumbs !== false) {
|
|
233
|
+
cleanups.push(instrumentConsoleBreadcrumbs(client));
|
|
234
|
+
}
|
|
235
|
+
if (options.enableNavigationBreadcrumbs !== false) {
|
|
236
|
+
cleanups.push(instrumentNavigationBreadcrumbs(client));
|
|
237
|
+
}
|
|
238
|
+
void client.init();
|
|
239
|
+
const instance = client;
|
|
240
|
+
instance.cleanup = () => {
|
|
241
|
+
for (const c of cleanups) {
|
|
242
|
+
try {
|
|
243
|
+
c();
|
|
244
|
+
} catch {
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
return instance;
|
|
249
|
+
}
|
|
250
|
+
export {
|
|
251
|
+
CrashLens,
|
|
252
|
+
initBrowser
|
|
253
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@crashlensorg/browser",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CrashLens SDK para o browser — window.onerror, unhandledrejection, fetch/XHR, breadcrumbs e info de dispositivo web",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": ["dist"],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
22
|
+
"typecheck": "tsc --noEmit"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@crashlensorg/core": "0.1.0"
|
|
26
|
+
},
|
|
27
|
+
"keywords": ["crashlens", "browser", "react", "javascript", "error-monitoring", "sentry-alternative"]
|
|
28
|
+
}
|