@zuplo/cli 7.7.8 → 7.7.9

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.
Files changed (143) hide show
  1. package/node_modules/@posthog/core/dist/error-tracking/parsers/base.d.ts.map +1 -1
  2. package/node_modules/@posthog/core/dist/error-tracking/parsers/base.js +19 -2
  3. package/node_modules/@posthog/core/dist/error-tracking/parsers/base.mjs +19 -2
  4. package/node_modules/@posthog/core/dist/index.d.ts +7 -0
  5. package/node_modules/@posthog/core/dist/index.d.ts.map +1 -1
  6. package/node_modules/@posthog/core/dist/index.js +82 -21
  7. package/node_modules/@posthog/core/dist/index.mjs +4 -0
  8. package/node_modules/@posthog/core/dist/logs/index.d.ts +3 -2
  9. package/node_modules/@posthog/core/dist/logs/index.d.ts.map +1 -1
  10. package/node_modules/@posthog/core/dist/logs/index.js +28 -22
  11. package/node_modules/@posthog/core/dist/logs/index.mjs +29 -23
  12. package/node_modules/@posthog/core/dist/logs/logs-utils.js +1 -1
  13. package/node_modules/@posthog/core/dist/logs/logs-utils.mjs +2 -2
  14. package/node_modules/@posthog/core/dist/logs/types.d.ts +2 -2
  15. package/node_modules/@posthog/core/dist/metrics/index.d.ts +5 -2
  16. package/node_modules/@posthog/core/dist/metrics/index.d.ts.map +1 -1
  17. package/node_modules/@posthog/core/dist/metrics/index.js +28 -14
  18. package/node_modules/@posthog/core/dist/metrics/index.mjs +29 -15
  19. package/node_modules/@posthog/core/dist/metrics/metrics-utils.d.ts.map +1 -1
  20. package/node_modules/@posthog/core/dist/metrics/metrics-utils.js +1 -2
  21. package/node_modules/@posthog/core/dist/metrics/metrics-utils.mjs +2 -3
  22. package/node_modules/@posthog/core/dist/metrics/types.d.ts +7 -0
  23. package/node_modules/@posthog/core/dist/metrics/types.d.ts.map +1 -1
  24. package/node_modules/@posthog/core/dist/posthog-core-stateless.d.ts +32 -4
  25. package/node_modules/@posthog/core/dist/posthog-core-stateless.d.ts.map +1 -1
  26. package/node_modules/@posthog/core/dist/posthog-core-stateless.js +68 -20
  27. package/node_modules/@posthog/core/dist/posthog-core-stateless.mjs +68 -20
  28. package/node_modules/@posthog/core/dist/traces/config.d.ts +13 -0
  29. package/node_modules/@posthog/core/dist/traces/config.d.ts.map +1 -0
  30. package/node_modules/@posthog/core/dist/traces/config.js +101 -0
  31. package/node_modules/@posthog/core/dist/traces/config.mjs +63 -0
  32. package/node_modules/@posthog/core/dist/traces/context.d.ts +18 -0
  33. package/node_modules/@posthog/core/dist/traces/context.d.ts.map +1 -0
  34. package/node_modules/@posthog/core/dist/traces/context.js +53 -0
  35. package/node_modules/@posthog/core/dist/traces/context.mjs +15 -0
  36. package/node_modules/@posthog/core/dist/traces/ids.d.ts +12 -0
  37. package/node_modules/@posthog/core/dist/traces/ids.d.ts.map +1 -0
  38. package/node_modules/@posthog/core/dist/traces/ids.js +92 -0
  39. package/node_modules/@posthog/core/dist/traces/ids.mjs +42 -0
  40. package/node_modules/@posthog/core/dist/traces/index.d.ts +150 -0
  41. package/node_modules/@posthog/core/dist/traces/index.d.ts.map +1 -0
  42. package/node_modules/@posthog/core/dist/traces/index.js +551 -0
  43. package/node_modules/@posthog/core/dist/traces/index.mjs +513 -0
  44. package/node_modules/@posthog/core/dist/traces/otlp.d.ts +24 -0
  45. package/node_modules/@posthog/core/dist/traces/otlp.d.ts.map +1 -0
  46. package/node_modules/@posthog/core/dist/traces/otlp.js +158 -0
  47. package/node_modules/@posthog/core/dist/traces/otlp.mjs +108 -0
  48. package/node_modules/@posthog/core/dist/traces/sanitize.d.ts +32 -0
  49. package/node_modules/@posthog/core/dist/traces/sanitize.d.ts.map +1 -0
  50. package/node_modules/@posthog/core/dist/traces/sanitize.js +93 -0
  51. package/node_modules/@posthog/core/dist/traces/sanitize.mjs +43 -0
  52. package/node_modules/@posthog/core/dist/traces/span.d.ts +189 -0
  53. package/node_modules/@posthog/core/dist/traces/span.d.ts.map +1 -0
  54. package/node_modules/@posthog/core/dist/traces/span.js +533 -0
  55. package/node_modules/@posthog/core/dist/traces/span.mjs +462 -0
  56. package/node_modules/@posthog/core/dist/traces/traceparent.d.ts +51 -0
  57. package/node_modules/@posthog/core/dist/traces/traceparent.d.ts.map +1 -0
  58. package/node_modules/@posthog/core/dist/traces/traceparent.js +116 -0
  59. package/node_modules/@posthog/core/dist/traces/traceparent.mjs +63 -0
  60. package/node_modules/@posthog/core/dist/traces/types.d.ts +114 -0
  61. package/node_modules/@posthog/core/dist/traces/types.d.ts.map +1 -0
  62. package/node_modules/@posthog/core/dist/traces/types.js +18 -0
  63. package/node_modules/@posthog/core/dist/traces/types.mjs +1 -0
  64. package/node_modules/@posthog/core/dist/utils/backoff.d.ts +23 -0
  65. package/node_modules/@posthog/core/dist/utils/backoff.d.ts.map +1 -0
  66. package/node_modules/@posthog/core/dist/utils/backoff.js +65 -0
  67. package/node_modules/@posthog/core/dist/utils/backoff.mjs +14 -0
  68. package/node_modules/@posthog/core/dist/utils/flush-timer.d.ts +26 -0
  69. package/node_modules/@posthog/core/dist/utils/flush-timer.d.ts.map +1 -0
  70. package/node_modules/@posthog/core/dist/utils/flush-timer.js +67 -0
  71. package/node_modules/@posthog/core/dist/utils/flush-timer.mjs +29 -0
  72. package/node_modules/@posthog/core/dist/utils/json-utils.d.ts +7 -0
  73. package/node_modules/@posthog/core/dist/utils/json-utils.d.ts.map +1 -1
  74. package/node_modules/@posthog/core/dist/utils/json-utils.js +27 -0
  75. package/node_modules/@posthog/core/dist/utils/json-utils.mjs +25 -1
  76. package/node_modules/@posthog/core/dist/utils/otlp-resource.d.ts +13 -2
  77. package/node_modules/@posthog/core/dist/utils/otlp-resource.d.ts.map +1 -1
  78. package/node_modules/@posthog/core/dist/utils/otlp-resource.js +34 -7
  79. package/node_modules/@posthog/core/dist/utils/otlp-resource.mjs +26 -2
  80. package/node_modules/@posthog/core/dist/utils/retry-after.d.ts +55 -0
  81. package/node_modules/@posthog/core/dist/utils/retry-after.d.ts.map +1 -0
  82. package/node_modules/@posthog/core/dist/utils/retry-after.js +95 -0
  83. package/node_modules/@posthog/core/dist/utils/retry-after.mjs +50 -0
  84. package/node_modules/@posthog/core/package.json +3 -3
  85. package/node_modules/@posthog/core/src/error-tracking/parsers/base.spec.ts +77 -7
  86. package/node_modules/@posthog/core/src/error-tracking/parsers/base.ts +42 -19
  87. package/node_modules/@posthog/core/src/index.ts +18 -0
  88. package/node_modules/@posthog/core/src/logs/index.spec.ts +490 -2
  89. package/node_modules/@posthog/core/src/logs/index.ts +63 -31
  90. package/node_modules/@posthog/core/src/logs/logs-utils.ts +2 -2
  91. package/node_modules/@posthog/core/src/logs/types.ts +2 -2
  92. package/node_modules/@posthog/core/src/metrics/index.spec.ts +304 -0
  93. package/node_modules/@posthog/core/src/metrics/index.ts +53 -16
  94. package/node_modules/@posthog/core/src/metrics/metrics-utils.ts +2 -3
  95. package/node_modules/@posthog/core/src/metrics/types.ts +10 -2
  96. package/node_modules/@posthog/core/src/posthog-core-stateless.ts +157 -38
  97. package/node_modules/@posthog/core/src/traces/config.spec.ts +271 -0
  98. package/node_modules/@posthog/core/src/traces/config.ts +142 -0
  99. package/node_modules/@posthog/core/src/traces/context.ts +30 -0
  100. package/node_modules/@posthog/core/src/traces/ids.spec.ts +115 -0
  101. package/node_modules/@posthog/core/src/traces/ids.ts +75 -0
  102. package/node_modules/@posthog/core/src/traces/index.spec.ts +3187 -0
  103. package/node_modules/@posthog/core/src/traces/index.ts +1060 -0
  104. package/node_modules/@posthog/core/src/traces/live-spans.spec.ts +69 -0
  105. package/node_modules/@posthog/core/src/traces/otlp.spec.ts +368 -0
  106. package/node_modules/@posthog/core/src/traces/otlp.ts +191 -0
  107. package/node_modules/@posthog/core/src/traces/sanitize.ts +116 -0
  108. package/node_modules/@posthog/core/src/traces/span.spec.ts +1122 -0
  109. package/node_modules/@posthog/core/src/traces/span.ts +822 -0
  110. package/node_modules/@posthog/core/src/traces/traceparent.spec.ts +184 -0
  111. package/node_modules/@posthog/core/src/traces/traceparent.ts +172 -0
  112. package/node_modules/@posthog/core/src/traces/types.ts +140 -0
  113. package/node_modules/@posthog/core/src/utils/backoff.ts +44 -0
  114. package/node_modules/@posthog/core/src/utils/flush-timer.ts +50 -0
  115. package/node_modules/@posthog/core/src/utils/json-utils.ts +33 -0
  116. package/node_modules/@posthog/core/src/utils/otlp-resource.spec.ts +55 -9
  117. package/node_modules/@posthog/core/src/utils/otlp-resource.ts +45 -4
  118. package/node_modules/@posthog/core/src/utils/retry-after.ts +135 -0
  119. package/node_modules/@posthog/types/dist/index.d.ts +1 -0
  120. package/node_modules/@posthog/types/dist/index.d.ts.map +1 -1
  121. package/node_modules/@posthog/types/dist/posthog-config.d.ts +10 -5
  122. package/node_modules/@posthog/types/dist/posthog-config.d.ts.map +1 -1
  123. package/node_modules/@posthog/types/dist/traces.d.ts +387 -0
  124. package/node_modules/@posthog/types/dist/traces.d.ts.map +1 -0
  125. package/node_modules/@posthog/types/dist/traces.js +18 -0
  126. package/node_modules/@posthog/types/dist/traces.mjs +1 -0
  127. package/node_modules/@posthog/types/package.json +1 -1
  128. package/node_modules/@posthog/types/src/index.ts +19 -0
  129. package/node_modules/@posthog/types/src/posthog-config.ts +10 -5
  130. package/node_modules/@posthog/types/src/traces.ts +429 -0
  131. package/node_modules/@zuplo/core/package.json +1 -1
  132. package/node_modules/@zuplo/graphql/package.json +1 -1
  133. package/node_modules/@zuplo/openapi-tools/package.json +1 -1
  134. package/node_modules/@zuplo/otel/package.json +1 -1
  135. package/node_modules/@zuplo/runtime/out/esm/{chunk-DRQ5CMVC.js → chunk-KNAYKBEE.js} +80 -80
  136. package/node_modules/@zuplo/runtime/out/esm/chunk-KNAYKBEE.js.map +1 -0
  137. package/node_modules/@zuplo/runtime/out/esm/index.js +1 -1
  138. package/node_modules/@zuplo/runtime/out/esm/mcp-gateway/index.js +1 -1
  139. package/node_modules/@zuplo/runtime/out/esm/mocks/index.js +1 -1
  140. package/node_modules/@zuplo/runtime/package.json +1 -1
  141. package/package.json +11 -11
  142. package/node_modules/@zuplo/runtime/out/esm/chunk-DRQ5CMVC.js.map +0 -1
  143. /package/node_modules/@zuplo/runtime/out/esm/{chunk-DRQ5CMVC.js.LEGAL.txt → chunk-KNAYKBEE.js.LEGAL.txt} +0 -0
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Longest `Retry-After` the SDK will wait. A rate-limit window can legitimately
3
+ * be long, but nothing upstream of the SDK bounds this header — it is as likely
4
+ * to come from a proxy or CDN as from PostHog — and an unbounded value would
5
+ * strand a queue for hours, so the wait is capped and the retry happens early.
6
+ */
7
+ export declare const MAX_RETRY_AFTER_MS: number;
8
+ /**
9
+ * `Retry-After` as milliseconds from now. The header is either delta-seconds or
10
+ * an HTTP-date; anything else, a date already in the past, or a non-positive
11
+ * delta yields `undefined` so the caller keeps its own backoff. The result is
12
+ * capped at `MAX_RETRY_AFTER_MS`.
13
+ */
14
+ export declare function parseRetryAfterMs(value: unknown, now?: number): number | undefined;
15
+ /** The part of an export outcome the window reads. */
16
+ export type RetryAfterOutcome = {
17
+ kind: 'ok' | 'retry-later' | 'too-large' | 'fatal';
18
+ retryAfterMs?: number;
19
+ };
20
+ /**
21
+ * The wait an ingestion endpoint asked for, shared by the logs, metrics and
22
+ * traces export queues.
23
+ *
24
+ * Held as an absolute deadline rather than a duration, so a timer armed while
25
+ * the wait is already part-served counts down the remainder instead of
26
+ * restarting it.
27
+ */
28
+ export declare class RetryAfterWindow {
29
+ private _until;
30
+ private _installedAt;
31
+ /**
32
+ * Folds one export outcome into the window.
33
+ *
34
+ * A refusal naming a longer wait than the one being served pushes the
35
+ * deadline out, but never past `MAX_RETRY_AFTER_MS` from where the window was
36
+ * first installed. That ceiling is the SDK's own policy rather than OTLP's,
37
+ * which asks for the header to be honoured and names no cap: a host flushing
38
+ * faster than the window is long would otherwise refresh the deadline forever
39
+ * and never recover, since logs gates its size trigger and `onReconnect` on
40
+ * the window and metrics re-arms its timer from it. The cost is that a wait
41
+ * longer than the ceiling is served short.
42
+ *
43
+ * The deadline is never pulled in, so a shorter header cannot cut a wait the
44
+ * endpoint already asked for.
45
+ */
46
+ record(outcome: RetryAfterOutcome): void;
47
+ /**
48
+ * Milliseconds left of the wait, `0` when none is open. Closes a window it
49
+ * finds spent, so a later backward clock step cannot bring it back.
50
+ */
51
+ remainingMs(): number;
52
+ isOpen(): boolean;
53
+ reset(): void;
54
+ }
55
+ //# sourceMappingURL=retry-after.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry-after.d.ts","sourceRoot":"","sources":["../../src/utils/retry-after.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,QAAa,CAAA;AAS5C;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,GAAG,SAAS,CAuB9F;AAED,sDAAsD;AACtD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,IAAI,GAAG,aAAa,GAAG,WAAW,GAAG,OAAO,CAAA;IAClD,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;;GAOG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,YAAY,CAAI;IAExB;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IA8BxC;;;OAGG;IACH,WAAW,IAAI,MAAM;IAarB,MAAM,IAAI,OAAO;IAIjB,KAAK,IAAI,IAAI;CAId"}
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, getters, values)=>{
5
+ var define = (defs, kind)=>{
6
+ for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
7
+ enumerable: true,
8
+ [kind]: defs[key]
9
+ });
10
+ };
11
+ define(getters, "get");
12
+ define(values, "value");
13
+ };
14
+ })();
15
+ (()=>{
16
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
17
+ })();
18
+ (()=>{
19
+ __webpack_require__.r = (exports1)=>{
20
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
21
+ value: 'Module'
22
+ });
23
+ Object.defineProperty(exports1, '__esModule', {
24
+ value: true
25
+ });
26
+ };
27
+ })();
28
+ var __webpack_exports__ = {};
29
+ __webpack_require__.r(__webpack_exports__);
30
+ const MAX_RETRY_AFTER_MS = 300000;
31
+ const CLOCK_STEP_TOLERANCE_MS = 5000;
32
+ function parseRetryAfterMs(value, now = Date.now()) {
33
+ if ('string' != typeof value || !value) return;
34
+ const raw = value.trim();
35
+ const trimmed = /^\d+\s*,/.test(raw) ? raw.slice(0, raw.indexOf(',')).trim() : raw;
36
+ const seconds = /^\d+$/.test(trimmed) ? Number(trimmed) : 0 / 0;
37
+ if (!Number.isFinite(seconds) && /^[+-]?[\d.]+$/.test(trimmed)) return;
38
+ const ms = Number.isFinite(seconds) ? 1000 * seconds : Date.parse(trimmed) - now;
39
+ if (!Number.isFinite(ms) || ms <= 0) return;
40
+ return Math.min(ms, MAX_RETRY_AFTER_MS);
41
+ }
42
+ class RetryAfterWindow {
43
+ record(outcome) {
44
+ if ('too-large' === outcome.kind) return;
45
+ if ('retry-later' !== outcome.kind) return void this.reset();
46
+ if (!outcome.retryAfterMs) return;
47
+ const open = this.isOpen();
48
+ const now = Date.now();
49
+ const asked = Math.min(outcome.retryAfterMs, MAX_RETRY_AFTER_MS);
50
+ if (!open) {
51
+ this._installedAt = now;
52
+ this._until = now + asked;
53
+ return;
54
+ }
55
+ this._until = Math.max(this._until, Math.min(now + asked, this._installedAt + MAX_RETRY_AFTER_MS));
56
+ }
57
+ remainingMs() {
58
+ const now = Date.now();
59
+ if (now < this._installedAt - CLOCK_STEP_TOLERANCE_MS) {
60
+ this.reset();
61
+ return 0;
62
+ }
63
+ const remaining = Math.min(MAX_RETRY_AFTER_MS, Math.max(0, this._until - now));
64
+ if (0 === remaining) this.reset();
65
+ return remaining;
66
+ }
67
+ isOpen() {
68
+ return this.remainingMs() > 0;
69
+ }
70
+ reset() {
71
+ this._until = 0;
72
+ this._installedAt = 0;
73
+ }
74
+ constructor(){
75
+ this._until = 0;
76
+ this._installedAt = 0;
77
+ }
78
+ }
79
+ __webpack_require__.d(__webpack_exports__, {
80
+ RetryAfterWindow: ()=>RetryAfterWindow,
81
+ parseRetryAfterMs: ()=>parseRetryAfterMs
82
+ }, {
83
+ MAX_RETRY_AFTER_MS: MAX_RETRY_AFTER_MS
84
+ });
85
+ exports.MAX_RETRY_AFTER_MS = __webpack_exports__.MAX_RETRY_AFTER_MS;
86
+ exports.RetryAfterWindow = __webpack_exports__.RetryAfterWindow;
87
+ exports.parseRetryAfterMs = __webpack_exports__.parseRetryAfterMs;
88
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
89
+ "MAX_RETRY_AFTER_MS",
90
+ "RetryAfterWindow",
91
+ "parseRetryAfterMs"
92
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
93
+ Object.defineProperty(exports, '__esModule', {
94
+ value: true
95
+ });
@@ -0,0 +1,50 @@
1
+ const MAX_RETRY_AFTER_MS = 300000;
2
+ const CLOCK_STEP_TOLERANCE_MS = 5000;
3
+ function parseRetryAfterMs(value, now = Date.now()) {
4
+ if ('string' != typeof value || !value) return;
5
+ const raw = value.trim();
6
+ const trimmed = /^\d+\s*,/.test(raw) ? raw.slice(0, raw.indexOf(',')).trim() : raw;
7
+ const seconds = /^\d+$/.test(trimmed) ? Number(trimmed) : 0 / 0;
8
+ if (!Number.isFinite(seconds) && /^[+-]?[\d.]+$/.test(trimmed)) return;
9
+ const ms = Number.isFinite(seconds) ? 1000 * seconds : Date.parse(trimmed) - now;
10
+ if (!Number.isFinite(ms) || ms <= 0) return;
11
+ return Math.min(ms, MAX_RETRY_AFTER_MS);
12
+ }
13
+ class RetryAfterWindow {
14
+ record(outcome) {
15
+ if ('too-large' === outcome.kind) return;
16
+ if ('retry-later' !== outcome.kind) return void this.reset();
17
+ if (!outcome.retryAfterMs) return;
18
+ const open = this.isOpen();
19
+ const now = Date.now();
20
+ const asked = Math.min(outcome.retryAfterMs, MAX_RETRY_AFTER_MS);
21
+ if (!open) {
22
+ this._installedAt = now;
23
+ this._until = now + asked;
24
+ return;
25
+ }
26
+ this._until = Math.max(this._until, Math.min(now + asked, this._installedAt + MAX_RETRY_AFTER_MS));
27
+ }
28
+ remainingMs() {
29
+ const now = Date.now();
30
+ if (now < this._installedAt - CLOCK_STEP_TOLERANCE_MS) {
31
+ this.reset();
32
+ return 0;
33
+ }
34
+ const remaining = Math.min(MAX_RETRY_AFTER_MS, Math.max(0, this._until - now));
35
+ if (0 === remaining) this.reset();
36
+ return remaining;
37
+ }
38
+ isOpen() {
39
+ return this.remainingMs() > 0;
40
+ }
41
+ reset() {
42
+ this._until = 0;
43
+ this._installedAt = 0;
44
+ }
45
+ constructor(){
46
+ this._until = 0;
47
+ this._installedAt = 0;
48
+ }
49
+ }
50
+ export { MAX_RETRY_AFTER_MS, RetryAfterWindow, parseRetryAfterMs };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/core",
3
- "version": "1.52.1",
3
+ "version": "1.53.0",
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.410.0"
73
+ "@posthog/types": "^1.411.0"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@rslib/core": "0.23.2",
@@ -84,7 +84,7 @@
84
84
  "lint:fix": "oxlint --report-unused-disable-directives-severity error src --fix",
85
85
  "build": "rslib build",
86
86
  "dev": "rslib build -w",
87
- "test:unit": "vitest run",
87
+ "test:unit": "NODE_OPTIONS=--expose-gc vitest run",
88
88
  "package": "pnpm pack --out $PACKAGE_DEST/%s.tgz"
89
89
  }
90
90
  }
@@ -1,21 +1,47 @@
1
1
  import { createFrame } from './base'
2
2
  import { createDefaultStackParser } from './index'
3
3
 
4
- describe('createFrame', () => {
5
- const platform = 'web:javascript'
6
-
4
+ describe.each(['web:javascript', 'hermes'] as const)('createFrame (%s)', (platform) => {
7
5
  it('marks ordinary browser frames as in_app', () => {
8
6
  const frame = createFrame(platform, 'https://example.com/app.js', 'doThing', 1, 2)
9
7
  expect(frame.in_app).toBe(true)
10
8
  })
11
9
 
12
- it('does not mark frames Safari has masked as in_app', () => {
13
- // Safari hides the origin of extension content scripts, and of blob:/eval'd code, behind
14
- // this placeholder. None of it is the page's own code.
15
- const frame = createFrame(platform, 'webkit-masked-url://hidden/', '_classCallCheck', 1, 2)
10
+ it.each([
11
+ ['Safari masking an extension content script', 'webkit-masked-url://hidden/'],
12
+ ['an Android in-app browser bridge', 'iabjs://navigation_performance_logger_android'],
13
+ ['a Chromium extension content script', 'chrome-extension://abcdef/content.js'],
14
+ ['a Firefox extension content script', 'moz-extension://abcdef/content.js'],
15
+ ['a Safari extension, which the parser prefixes with a bare scheme', 'safari-extension:abcdef/content.js'],
16
+ ['a Safari web extension', 'safari-web-extension:abcdef/content.js'],
17
+ ['an extension with a mixed-case scheme', 'CHROME-Extension://abcdef/content.js'],
18
+ ['an unknown container scheme', 'other-bridge://injected.js'],
19
+ ['an inline data URL', 'data:text/javascript,void 0'],
20
+ ])('does not mark a frame the container injected as in_app: %s', (_name, filename) => {
21
+ const frame = createFrame(platform, filename, 'doThing', 1, 2)
16
22
  expect(frame.in_app).toBe(false)
17
23
  })
18
24
 
25
+ it.each([
26
+ ['a script served over http', 'http://localhost:8000/app.js'],
27
+ ['a Cordova or Electron app served from disk', 'file:///android_asset/www/app.js'],
28
+ ['a worker the page created from a blob', 'blob:https://example.com/6b0b0e6a'],
29
+ ['a Capacitor webview', 'capacitor://localhost/app.js'],
30
+ ['an Ionic webview', 'ionic://localhost/app.js'],
31
+ ['a script with a mixed-case scheme', 'HTTPS://example.com/app.js'],
32
+ ['a webpack source frame', 'webpack:///./src/App.js'],
33
+ ['an Angular JIT component', 'ng:///CheckoutComponent.js'],
34
+ ['a custom app scheme, as an Electron or React Native shell serves', 'app://checkout.js'],
35
+ ['a bundler-rewritten dev frame', 'webpack-internal:///./src/App.js'],
36
+ ['a React Native release bundle, which has no scheme', 'index.android.bundle'],
37
+ ['an absolute path with no scheme', '/data/user/0/com.example/files/index.bundle'],
38
+ ['a Windows path with backslashes', 'C:\\app\\index.bundle'],
39
+ ['a Windows path with forward slashes', 'C:/app/index.bundle'],
40
+ ])("still marks the app's own code as in_app: %s", (_name, filename) => {
41
+ const frame = createFrame(platform, filename, 'doThing', 1, 2)
42
+ expect(frame.in_app).toBe(true)
43
+ })
44
+
19
45
  it('keeps the masked frame rather than discarding it', () => {
20
46
  const frame = createFrame(platform, 'webkit-masked-url://hidden/', 'someFn', 3, 4)
21
47
  expect(frame).toMatchObject({
@@ -70,6 +96,50 @@ describe('createDefaultStackParser in_app classification', () => {
70
96
  ])
71
97
  })
72
98
 
99
+ it('demotes the bridge frames an Android in-app browser serves from its own scheme', () => {
100
+ const frames = parse(
101
+ 'Error: Error invoking postMessage: Java object is gone\n' +
102
+ ' at sendDataToNative (iabjs://navigation_performance_logger_android:1:10198)\n' +
103
+ ' at sendJsBlockingTimeMessage (iabjs://navigation_performance_logger_android:1:14668)'
104
+ )
105
+ expect(frames.map((f) => [f.function, f.in_app])).toEqual([
106
+ ['sendJsBlockingTimeMessage', false],
107
+ ['sendDataToNative', false],
108
+ ])
109
+ })
110
+
111
+ it.each([
112
+ ' at CheckoutComponent_Template (ng:///CheckoutComponent.js:10:5)',
113
+ 'CheckoutComponent_Template@ng:///CheckoutComponent.js:10:5',
114
+ ])('keeps Angular JIT application frames as in_app: %s', (stackLine) => {
115
+ const frames = parse(`Error: template failed\n${stackLine}`)
116
+ expect(frames).toEqual([
117
+ expect.objectContaining({
118
+ filename: 'ng:///CheckoutComponent.js',
119
+ function: 'CheckoutComponent_Template',
120
+ lineno: 10,
121
+ colno: 5,
122
+ in_app: true,
123
+ }),
124
+ ])
125
+ })
126
+
127
+ it.each([
128
+ 'webkit-masked-url://hidden/',
129
+ '<anonymous>',
130
+ 'iabjs://navigation_performance_logger_android',
131
+ 'chrome-extension://abcdef/content.js',
132
+ 'moz-extension://abcdef/content.js',
133
+ ])('retains injected frames without demoting the calling app frame: %s', (filename) => {
134
+ const frames = parse(
135
+ `Error: boom\n at injected (${filename}:1:2)\n at checkout (https://example.com/app.js:10:5)`
136
+ )
137
+ expect(frames.map((frame) => [frame.filename, frame.in_app])).toEqual([
138
+ ['https://example.com/app.js', true],
139
+ [filename, false],
140
+ ])
141
+ })
142
+
73
143
  it("keeps the page's own eval'd code as in_app", () => {
74
144
  // V8 attributes eval'd code to the script that called eval; the chrome parser already
75
145
  // rewrites these frames to that URL, so they must not be caught by the <anonymous> rule.
@@ -7,21 +7,47 @@ 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
-
17
- // Chromium's counterpart: a script with no URL at all -- code injected by an extension
18
- // (`chrome.scripting.executeScript`), pasted into devtools, or evaluated from a string -- shows
19
- // up as a bare `<anonymous>:line:col` frame. The page's own eval'd code is *not* affected: V8
20
- // reports it as `eval at <anonymous> (https://site/app.js:1:2)` and the chrome parser already
21
- // rewrites that to the site URL. A bare `<anonymous>` can never be symbolicated, so treating it
22
- // as in_app only ever pulled an unresolvable frame into the site's own stack.
10
+ // Chromium reports a script with no URL at all -- code injected by an extension
11
+ // (`chrome.scripting.executeScript`), pasted into devtools, or evaluated from a string -- as a bare
12
+ // `<anonymous>:line:col` frame. The page's own eval'd code is *not* affected: V8 reports it as
13
+ // `eval at <anonymous> (https://site/app.js:1:2)` and the chrome parser already rewrites that to
14
+ // the site URL. A bare `<anonymous>` can never be symbolicated.
23
15
  const ANONYMOUS_FILENAME = '<anonymous>'
24
16
 
17
+ // A leading `scheme:` in a filename, needing two characters or more so that a Windows drive letter
18
+ // such as `C:` does not read as one.
19
+ const URL_SCHEME = /^([a-z][a-z0-9.+-]+):/i
20
+
21
+ // The schemes an app is served over. Everything else names code the container put into the page
22
+ // rather than code the app loaded: Safari masks extension content scripts as `webkit-masked-url://`,
23
+ // Chromium serves them from `chrome-extension://`, and Android in-app browsers serve their native
24
+ // bridge from schemes such as `iabjs://`. Listing every injector's scheme never keeps up with the
25
+ // next one, so accept the schemes an app is served over and reject the rest.
26
+ const APP_URL_SCHEMES = [
27
+ 'http',
28
+ 'https',
29
+ 'file',
30
+ 'blob',
31
+ 'app',
32
+ 'capacitor',
33
+ 'ionic',
34
+ 'webpack',
35
+ 'webpack-internal',
36
+ 'ng',
37
+ ]
38
+
39
+ // Is this filename a script the app itself loaded? A filename with no scheme is one: React Native
40
+ // names its own bundle `index.android.bundle`, and a bundler can report a bare path.
41
+ function isAppFilename(filename: string): boolean {
42
+ if (!filename || filename === ANONYMOUS_FILENAME) {
43
+ return false
44
+ }
45
+
46
+ const scheme = URL_SCHEME.exec(filename)
47
+
48
+ return !scheme || APP_URL_SCHEMES.includes((scheme[1] as string).toLowerCase())
49
+ }
50
+
25
51
  export function createFrame(
26
52
  platform: StackFrame['platform'],
27
53
  filename: string,
@@ -34,12 +60,9 @@ export function createFrame(
34
60
  platform,
35
61
  filename,
36
62
  function: func === '<anonymous>' ? UNKNOWN_FUNCTION : func,
37
- // Browser frames are considered in_app unless the runtime has masked or dropped their origin.
38
- // A frame with no filename at all is the third case: the runtime reports a function name, a
39
- // line and a column, but no script URL. Android in-app browsers report their injected native
40
- // bridge scripts this way. Such a frame can never be symbolicated either, so it must not
41
- // count as the page's own code.
42
- in_app: !!filename && !filename.startsWith(MASKED_URL_PREFIX) && filename !== ANONYMOUS_FILENAME,
63
+ // Keep a frame the app did not load -- it is still useful context -- but do not let it count as
64
+ // in_app and pull the issue into the app's own stack.
65
+ in_app: isAppFilename(filename),
43
66
  }
44
67
 
45
68
  if (!isUndefined(lineno)) {
@@ -87,6 +87,24 @@ export type {
87
87
  Metrics,
88
88
  MetricsConfig,
89
89
  } from './metrics/types'
90
+ export { PostHogTraces } from './traces'
91
+ export { SyncSpanContextManager } from './traces/context'
92
+ export { inertSpan, runWithActiveSpan } from './traces/span'
93
+ export { resolveTracesConfig } from './traces/config'
94
+ export type { ResolvedTracesConfig, SpanContextManager, TraceSdkContext } from './traces/types'
95
+ // The `beforeSpanSend` shapes come straight from @posthog/types: hooks see the
96
+ // public record, not core's internal one, which also carries `traceState`.
97
+ export type { SpanRecord, BeforeSpanSendFn } from '@posthog/types'
98
+ export type {
99
+ Span,
100
+ SpanAttributes,
101
+ SpanAttributeValue,
102
+ SpanKind,
103
+ SpanStatusCode,
104
+ SpanTimeInput,
105
+ StartSpanOptions,
106
+ TracesConfig,
107
+ } from './traces/types'
90
108
  export { uuidv7 } from './vendor/uuidv7'
91
109
  export * from './cookie'
92
110
  export * from './posthog-core'