@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.
@@ -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;IAUvD,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,QAAQ;CAGjB"}
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: this.getStack(err),
40
+ stack: stack ?? ctx.syntheticException?.stack,
39
41
  cause: err.cause ? ctx.next(err.cause) : void 0,
40
- synthetic: false
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: this.getStack(err),
12
+ stack: stack ?? ctx.syntheticException?.stack,
11
13
  cause: err.cause ? ctx.next(err.cause) : void 0,
12
- synthetic: false
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;AAEnC,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,CAkBZ"}
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: true
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: true
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.2",
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.404.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: this.getStack(err),
19
+ stack: stack ?? ctx.syntheticException?.stack,
14
20
  cause: err.cause ? ctx.next(err.cause) : undefined,
15
- synthetic: false,
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
- in_app: true, // All browser frames are considered in_app
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)) {