@posthog/core 1.48.2 → 1.48.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/error-tracking/coercers/error-coercer.d.ts.map +1 -1
- package/dist/error-tracking/coercers/error-coercer.js +4 -2
- package/dist/error-tracking/coercers/error-coercer.mjs +4 -2
- package/dist/error-tracking/parsers/base.d.ts.map +1 -1
- package/dist/error-tracking/parsers/base.js +2 -1
- package/dist/error-tracking/parsers/base.mjs +2 -1
- package/package.json +2 -2
- package/src/error-tracking/coercers/error-coercer.ts +8 -2
- package/src/error-tracking/error-properties-builder.coerce.spec.ts +15 -0
- package/src/error-tracking/parsers/base.spec.ts +33 -0
- package/src/error-tracking/parsers/base.ts +9 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-coercer.d.ts","sourceRoot":"","sources":["../../../src/error-tracking/coercers/error-coercer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAE/E,qBAAa,YAAa,YAAW,oBAAoB,CAAC,KAAK,CAAC;IAC9D,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,KAAK;IAIjC,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,eAAe,GAAG,aAAa;
|
|
1
|
+
{"version":3,"file":"error-coercer.d.ts","sourceRoot":"","sources":["../../../src/error-tracking/coercers/error-coercer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAE/E,qBAAa,YAAa,YAAW,oBAAoB,CAAC,KAAK,CAAC;IAC9D,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,KAAK;IAIjC,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,eAAe,GAAG,aAAa;IAgBvD,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,QAAQ;CAGjB"}
|
|
@@ -32,12 +32,14 @@ class ErrorCoercer {
|
|
|
32
32
|
return (0, type_utils_js_namespaceObject.isError)(err);
|
|
33
33
|
}
|
|
34
34
|
coerce(err, ctx) {
|
|
35
|
+
const stack = this.getStack(err);
|
|
36
|
+
const synthetic = void 0 === stack;
|
|
35
37
|
return {
|
|
36
38
|
type: this.getType(err),
|
|
37
39
|
value: this.getMessage(err, ctx),
|
|
38
|
-
stack:
|
|
40
|
+
stack: stack ?? ctx.syntheticException?.stack,
|
|
39
41
|
cause: err.cause ? ctx.next(err.cause) : void 0,
|
|
40
|
-
synthetic
|
|
42
|
+
synthetic
|
|
41
43
|
};
|
|
42
44
|
}
|
|
43
45
|
getType(err) {
|
|
@@ -4,12 +4,14 @@ class ErrorCoercer {
|
|
|
4
4
|
return isError(err);
|
|
5
5
|
}
|
|
6
6
|
coerce(err, ctx) {
|
|
7
|
+
const stack = this.getStack(err);
|
|
8
|
+
const synthetic = void 0 === stack;
|
|
7
9
|
return {
|
|
8
10
|
type: this.getType(err),
|
|
9
11
|
value: this.getMessage(err, ctx),
|
|
10
|
-
stack:
|
|
12
|
+
stack: stack ?? ctx.syntheticException?.stack,
|
|
11
13
|
cause: err.cause ? ctx.next(err.cause) : void 0,
|
|
12
|
-
synthetic
|
|
14
|
+
synthetic
|
|
13
15
|
};
|
|
14
16
|
}
|
|
15
17
|
getType(err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/error-tracking/parsers/base.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAErC,eAAO,MAAM,gBAAgB,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/error-tracking/parsers/base.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAErC,eAAO,MAAM,gBAAgB,MAAM,CAAA;AASnC,wBAAgB,WAAW,CACzB,QAAQ,EAAE,UAAU,CAAC,UAAU,CAAC,EAChC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GACb,UAAU,CAmBZ"}
|
|
@@ -29,12 +29,13 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
29
29
|
});
|
|
30
30
|
const index_js_namespaceObject = require("../../utils/index.js");
|
|
31
31
|
const UNKNOWN_FUNCTION = '?';
|
|
32
|
+
const MASKED_URL_PREFIX = 'webkit-masked-url://';
|
|
32
33
|
function createFrame(platform, filename, func, lineno, colno) {
|
|
33
34
|
const frame = {
|
|
34
35
|
platform,
|
|
35
36
|
filename,
|
|
36
37
|
function: '<anonymous>' === func ? UNKNOWN_FUNCTION : func,
|
|
37
|
-
in_app:
|
|
38
|
+
in_app: !filename?.startsWith(MASKED_URL_PREFIX)
|
|
38
39
|
};
|
|
39
40
|
if (!(0, index_js_namespaceObject.isUndefined)(lineno)) frame.lineno = lineno;
|
|
40
41
|
if (!(0, index_js_namespaceObject.isUndefined)(colno)) frame.colno = colno;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { isUndefined } from "../../utils/index.mjs";
|
|
2
2
|
const UNKNOWN_FUNCTION = '?';
|
|
3
|
+
const MASKED_URL_PREFIX = 'webkit-masked-url://';
|
|
3
4
|
function createFrame(platform, filename, func, lineno, colno) {
|
|
4
5
|
const frame = {
|
|
5
6
|
platform,
|
|
6
7
|
filename,
|
|
7
8
|
function: '<anonymous>' === func ? UNKNOWN_FUNCTION : func,
|
|
8
|
-
in_app:
|
|
9
|
+
in_app: !filename?.startsWith(MASKED_URL_PREFIX)
|
|
9
10
|
};
|
|
10
11
|
if (!isUndefined(lineno)) frame.lineno = lineno;
|
|
11
12
|
if (!isUndefined(colno)) frame.colno = colno;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@posthog/core",
|
|
3
|
-
"version": "1.48.
|
|
3
|
+
"version": "1.48.4",
|
|
4
4
|
"bugs": {
|
|
5
5
|
"url": "https://github.com/PostHog/posthog-js/issues"
|
|
6
6
|
},
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
}
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@posthog/types": "^1.
|
|
73
|
+
"@posthog/types": "^1.405.0"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@rslib/core": "0.10.6",
|
|
@@ -7,12 +7,18 @@ export class ErrorCoercer implements ErrorTrackingCoercer<Error> {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
coerce(err: Error, ctx: CoercingContext): ExceptionLike {
|
|
10
|
+
const stack = this.getStack(err)
|
|
11
|
+
// Some errors carry no usable stack (for example a Firefox network `fetch`
|
|
12
|
+
// `TypeError`). Fall back to the synthetic exception captured at the call
|
|
13
|
+
// site so the frames still point at the caller. Mark it synthetic so the
|
|
14
|
+
// parser trims the SDK's own frames.
|
|
15
|
+
const synthetic = stack === undefined
|
|
10
16
|
return {
|
|
11
17
|
type: this.getType(err),
|
|
12
18
|
value: this.getMessage(err, ctx),
|
|
13
|
-
stack:
|
|
19
|
+
stack: stack ?? ctx.syntheticException?.stack,
|
|
14
20
|
cause: err.cause ? ctx.next(err.cause) : undefined,
|
|
15
|
-
synthetic
|
|
21
|
+
synthetic,
|
|
16
22
|
}
|
|
17
23
|
}
|
|
18
24
|
|
|
@@ -112,6 +112,21 @@ describe('ErrorPropertiesBuilder', () => {
|
|
|
112
112
|
})
|
|
113
113
|
})
|
|
114
114
|
|
|
115
|
+
it('should fall back to the synthetic stack when the error has no stack', () => {
|
|
116
|
+
// Firefox rejects a network `fetch` with a `TypeError` that carries no
|
|
117
|
+
// stack, so the frames must come from the synthetic exception instead.
|
|
118
|
+
const errorObject = new CustomTestError('My special error')
|
|
119
|
+
errorObject.stack = ''
|
|
120
|
+
const syntheticError = new Error()
|
|
121
|
+
const exception = coerceInput(errorObject, syntheticError)
|
|
122
|
+
expect(exception).toMatchObject({
|
|
123
|
+
type: 'CustomTestError',
|
|
124
|
+
value: 'My special error',
|
|
125
|
+
stack: syntheticError.stack,
|
|
126
|
+
synthetic: true,
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
115
130
|
it('should preserve a cross-realm error', () => {
|
|
116
131
|
const crossRealmError = runInNewContext(
|
|
117
132
|
`new TypeError('cross-realm error', {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createFrame } from './base'
|
|
2
|
+
|
|
3
|
+
describe('createFrame', () => {
|
|
4
|
+
const platform = 'web:javascript'
|
|
5
|
+
|
|
6
|
+
it('marks ordinary browser frames as in_app', () => {
|
|
7
|
+
const frame = createFrame(platform, 'https://example.com/app.js', 'doThing', 1, 2)
|
|
8
|
+
expect(frame.in_app).toBe(true)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('does not mark frames Safari has masked as in_app', () => {
|
|
12
|
+
// Safari hides the origin of extension content scripts, and of blob:/eval'd code, behind
|
|
13
|
+
// this placeholder. None of it is the page's own code.
|
|
14
|
+
const frame = createFrame(platform, 'webkit-masked-url://hidden/', '_classCallCheck', 1, 2)
|
|
15
|
+
expect(frame.in_app).toBe(false)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('keeps the masked frame rather than discarding it', () => {
|
|
19
|
+
const frame = createFrame(platform, 'webkit-masked-url://hidden/', 'someFn', 3, 4)
|
|
20
|
+
expect(frame).toMatchObject({
|
|
21
|
+
filename: 'webkit-masked-url://hidden/',
|
|
22
|
+
function: 'someFn',
|
|
23
|
+
lineno: 3,
|
|
24
|
+
colno: 4,
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('falls back to in_app when the parser could not determine a filename', () => {
|
|
29
|
+
// Regex capture groups that do not participate arrive here as undefined despite the type
|
|
30
|
+
const frame = createFrame(platform, undefined as unknown as string, 'doThing')
|
|
31
|
+
expect(frame.in_app).toBe(true)
|
|
32
|
+
})
|
|
33
|
+
})
|
|
@@ -7,6 +7,13 @@ import { StackFrame } from '../types'
|
|
|
7
7
|
|
|
8
8
|
export const UNKNOWN_FUNCTION = '?'
|
|
9
9
|
|
|
10
|
+
// Safari replaces the URL of scripts it will not attribute to the page with this placeholder.
|
|
11
|
+
// Web extension content scripts are the common source, but blob:, eval'd and injected code can
|
|
12
|
+
// be masked the same way, so the filename alone cannot tell us which one we are looking at.
|
|
13
|
+
// Either way it is not the page's own code, so keep the frame -- it is still useful context --
|
|
14
|
+
// but do not let it count as in_app and pull the issue into the site's own stack.
|
|
15
|
+
const MASKED_URL_PREFIX = 'webkit-masked-url://'
|
|
16
|
+
|
|
10
17
|
export function createFrame(
|
|
11
18
|
platform: StackFrame['platform'],
|
|
12
19
|
filename: string,
|
|
@@ -19,7 +26,8 @@ export function createFrame(
|
|
|
19
26
|
platform,
|
|
20
27
|
filename,
|
|
21
28
|
function: func === '<anonymous>' ? UNKNOWN_FUNCTION : func,
|
|
22
|
-
|
|
29
|
+
// Browser frames are considered in_app unless the runtime has masked their origin
|
|
30
|
+
in_app: !filename?.startsWith(MASKED_URL_PREFIX),
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
if (!isUndefined(lineno)) {
|