Skip to content

Commit fb4683e

Browse files
feat/added stacktrace parser to browser exceptions, generic types to transport service
1 parent 1473c6a commit fb4683e

8 files changed

Lines changed: 122 additions & 21 deletions

File tree

packages/browser/src/client.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { IBrowserClient, TraceoOptions } from "./types/client";
22
import { Transport } from "./transport";
33
import { utils } from "./utils";
4+
import { stacktrace } from "./exceptions/stacktrace";
5+
import { Trace } from "./types/stacktrace";
6+
import { BrowserIncidentType } from "./types/transport";
47

58
export abstract class BrowserClient implements IBrowserClient {
69
public headers!: { [key: string]: any };
@@ -17,13 +20,29 @@ export abstract class BrowserClient implements IBrowserClient {
1720
public abstract postInitSDK(): void;
1821

1922
public sendError(error: Error): void {
23+
const err = this.constructError(error);
24+
this.transport.send<BrowserIncidentType>(err, this.headers);
25+
}
26+
27+
private constructError(error: Error): BrowserIncidentType {
2028
const browser = utils.browserDetails();
21-
this.transport.send({
22-
type: error.name,
23-
message: error.message,
24-
stack: error.stack as string,
29+
30+
const type = error.name;
31+
const message = error.message;
32+
33+
const stack = error.stack || "";
34+
35+
const traces: Trace[] = stacktrace.parse(stack);
36+
37+
const err: BrowserIncidentType = {
38+
type,
39+
message,
40+
stack,
41+
traces,
2542
browser
26-
}, this.headers);
43+
};
44+
45+
return err;
2746
}
2847

2948
private initSDK(): void {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { Trace } from "../types/stacktrace";
2+
3+
const LINE_REGEXP = /\s*at\s*(?:(.+?)\s*\()?(?:(.+):(\d+):(\d+))?\)?/;
4+
const EXTENSION_REGEXP = /\.(\w+)$/;
5+
const CLASS_METHOD_REGEXP = /(.+)\.([^.]+)$/;
6+
7+
const EXCLUDED = ["<anonymous>"];
8+
9+
const parse = (stackTrace: string): Trace[] => {
10+
const lines = stackTrace.split("\n");
11+
const traces: Trace[] = [];
12+
13+
for (const line of lines) {
14+
const stackTraceLine = parseStackTraceLine(line);
15+
if (stackTraceLine) {
16+
traces.push(stackTraceLine);
17+
}
18+
}
19+
20+
return traces;
21+
};
22+
23+
const parseStackTraceLine = (line: string): Trace | null => {
24+
const match = line.match(LINE_REGEXP);
25+
if (!match || EXCLUDED.includes(match[1])) {
26+
return null;
27+
}
28+
29+
const [, method, file, lineStr, columnStr] = match;
30+
const lineNo = parseOptionalInt(lineStr);
31+
const columnNo = parseOptionalInt(columnStr);
32+
const ext = getFileExtension(file);
33+
const methodName = getFullMethodName(method);
34+
35+
return {
36+
filename: file,
37+
lineNo,
38+
columnNo,
39+
function: methodName,
40+
extension: ext
41+
};
42+
};
43+
44+
const parseOptionalInt = (str?: string): number | null => {
45+
return str ? parseInt(str, 10) : null;
46+
};
47+
48+
const getFileExtension = (file?: string): string | null => {
49+
if (!file) {
50+
return null;
51+
}
52+
53+
const match = file.match(EXTENSION_REGEXP);
54+
return match ? match[1] : null;
55+
};
56+
57+
const getFullMethodName = (method?: string): string | null => {
58+
if (!method) {
59+
return null;
60+
}
61+
62+
const match = method.match(CLASS_METHOD_REGEXP);
63+
return match ? `${match[1]}.${match[2]}` : method;
64+
};
65+
66+
export const stacktrace = {
67+
parse
68+
};

packages/browser/src/transport/fetch.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { ITransport, RequestOptions } from "../types/transport";
22

3-
export class FetchTransport implements ITransport {
4-
private _options: RequestOptions;
3+
export class FetchTransport<T> implements ITransport {
4+
private _options: RequestOptions<T>;
55

6-
constructor(options: RequestOptions) {
6+
constructor(options: RequestOptions<T>) {
77
this._options = options;
88
}
99

packages/browser/src/transport/index.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,24 @@ export class Transport {
1010
this._options = options;
1111
}
1212

13-
public send(body: object, headers: Dictionary<string>) {
13+
public send<T>(body: T, headers: Dictionary<string>) {
1414
try {
15-
const options = this.requestOptions(body, headers);
16-
this.transport(options).request();
15+
const options = this.requestOptions<T>(body, headers);
16+
this.transport<T>(options).request();
1717
} catch (error) {
1818
console.log("Error sending data to traceo: ", error);
1919
}
2020
}
2121

22-
private transport(options: RequestOptions) {
22+
private transport<T>(options: RequestOptions<T>) {
2323
if (window.XMLHttpRequest && !window.fetch) {
24-
return new XhrTransport(options);
24+
return new XhrTransport<T>(options);
2525
}
2626

27-
return new FetchTransport(options);
27+
return new FetchTransport<T>(options);
2828
}
2929

30-
private requestOptions(body: {}, headers: Dictionary<string>): RequestOptions {
30+
private requestOptions<T>(body: T, headers: Dictionary<string>): RequestOptions<T> {
3131
const reqUrl = this.clientURL;
3232

3333
// http://localhost:3000/api/worker/incident/app-id

packages/browser/src/transport/xhr.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { ITransport, RequestOptions } from "../types/transport";
22

33
const XHR_DONE = 4;
4-
export class XhrTransport implements ITransport {
5-
public _options: RequestOptions;
4+
export class XhrTransport<T> implements ITransport {
5+
public _options: RequestOptions<T>;
66

7-
constructor(options: RequestOptions) {
7+
constructor(options: RequestOptions<T>) {
88
this._options = options;
99
}
1010

packages/browser/src/types/browser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ export type BrowserInfoType = {
1616
version?: string;
1717
};
1818
url: string;
19-
};
19+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export interface Trace {
2+
filename?: string;
3+
function?: string | null;
4+
lineNo?: number | null;
5+
columnNo?: number | null;
6+
internal?: boolean;
7+
absPath?: string | null;
8+
extension?: string | null;
9+
preCode?: string[] | null;
10+
code?: string | null;
11+
postCode?: string[] | null;
12+
}

packages/browser/src/types/transport.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { BrowserInfoType } from "./browser";
22
import { Dictionary } from "./client";
3+
import { Trace } from "./stacktrace";
34

45
export interface ITransport {
56
request(): Promise<void>;
67
}
78

8-
export type RequestOptions = {
9-
body: object;
9+
export type RequestOptions<T> = {
10+
body: T;
1011
headers: Dictionary<string>;
1112
url: string;
1213
protocol: string; //"http" | "https"
@@ -20,4 +21,5 @@ export type BrowserIncidentType = {
2021
message: string;
2122
stack: string;
2223
browser: BrowserInfoType;
24+
traces: Trace[];
2325
};

0 commit comments

Comments
 (0)