@sentry/browser 10.25.0 → 10.27.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.
Files changed (70) hide show
  1. package/build/npm/cjs/dev/client.js +0 -1
  2. package/build/npm/cjs/dev/client.js.map +1 -1
  3. package/build/npm/cjs/dev/index.js +2 -0
  4. package/build/npm/cjs/dev/index.js.map +1 -1
  5. package/build/npm/cjs/dev/profiling/{lifecycleMode/traceLifecycleProfiler.js → UIProfiler.js} +158 -103
  6. package/build/npm/cjs/dev/profiling/UIProfiler.js.map +1 -0
  7. package/build/npm/cjs/dev/profiling/index.js +59 -0
  8. package/build/npm/cjs/dev/profiling/index.js.map +1 -0
  9. package/build/npm/cjs/dev/profiling/integration.js +13 -11
  10. package/build/npm/cjs/dev/profiling/integration.js.map +1 -1
  11. package/build/npm/cjs/dev/profiling/utils.js +11 -5
  12. package/build/npm/cjs/dev/profiling/utils.js.map +1 -1
  13. package/build/npm/cjs/prod/client.js +0 -1
  14. package/build/npm/cjs/prod/client.js.map +1 -1
  15. package/build/npm/cjs/prod/index.js +2 -0
  16. package/build/npm/cjs/prod/index.js.map +1 -1
  17. package/build/npm/cjs/prod/profiling/{lifecycleMode/traceLifecycleProfiler.js → UIProfiler.js} +158 -103
  18. package/build/npm/cjs/prod/profiling/UIProfiler.js.map +1 -0
  19. package/build/npm/cjs/prod/profiling/index.js +59 -0
  20. package/build/npm/cjs/prod/profiling/index.js.map +1 -0
  21. package/build/npm/cjs/prod/profiling/integration.js +13 -11
  22. package/build/npm/cjs/prod/profiling/integration.js.map +1 -1
  23. package/build/npm/cjs/prod/profiling/utils.js +11 -5
  24. package/build/npm/cjs/prod/profiling/utils.js.map +1 -1
  25. package/build/npm/esm/dev/client.js +0 -1
  26. package/build/npm/esm/dev/client.js.map +1 -1
  27. package/build/npm/esm/dev/index.js +1 -0
  28. package/build/npm/esm/dev/index.js.map +1 -1
  29. package/build/npm/esm/dev/package.json +1 -1
  30. package/build/npm/esm/dev/profiling/{lifecycleMode/traceLifecycleProfiler.js → UIProfiler.js} +159 -104
  31. package/build/npm/esm/dev/profiling/UIProfiler.js.map +1 -0
  32. package/build/npm/esm/dev/profiling/index.js +57 -0
  33. package/build/npm/esm/dev/profiling/index.js.map +1 -0
  34. package/build/npm/esm/dev/profiling/integration.js +14 -12
  35. package/build/npm/esm/dev/profiling/integration.js.map +1 -1
  36. package/build/npm/esm/dev/profiling/utils.js +12 -6
  37. package/build/npm/esm/dev/profiling/utils.js.map +1 -1
  38. package/build/npm/esm/prod/client.js +0 -1
  39. package/build/npm/esm/prod/client.js.map +1 -1
  40. package/build/npm/esm/prod/index.js +1 -0
  41. package/build/npm/esm/prod/index.js.map +1 -1
  42. package/build/npm/esm/prod/package.json +1 -1
  43. package/build/npm/esm/prod/profiling/{lifecycleMode/traceLifecycleProfiler.js → UIProfiler.js} +159 -104
  44. package/build/npm/esm/prod/profiling/UIProfiler.js.map +1 -0
  45. package/build/npm/esm/prod/profiling/index.js +57 -0
  46. package/build/npm/esm/prod/profiling/index.js.map +1 -0
  47. package/build/npm/esm/prod/profiling/integration.js +14 -12
  48. package/build/npm/esm/prod/profiling/integration.js.map +1 -1
  49. package/build/npm/esm/prod/profiling/utils.js +12 -6
  50. package/build/npm/esm/prod/profiling/utils.js.map +1 -1
  51. package/build/npm/types/client.d.ts.map +1 -1
  52. package/build/npm/types/exports.d.ts +1 -0
  53. package/build/npm/types/exports.d.ts.map +1 -1
  54. package/build/npm/{types-ts3.8/profiling/lifecycleMode/traceLifecycleProfiler.d.ts → types/profiling/UIProfiler.d.ts} +75 -70
  55. package/build/npm/types/profiling/UIProfiler.d.ts.map +1 -0
  56. package/build/npm/types/profiling/index.d.ts +8 -0
  57. package/build/npm/types/profiling/index.d.ts.map +1 -0
  58. package/build/npm/types/profiling/integration.d.ts.map +1 -1
  59. package/build/npm/types/profiling/utils.d.ts +1 -1
  60. package/build/npm/types/profiling/utils.d.ts.map +1 -1
  61. package/build/npm/types-ts3.8/exports.d.ts +1 -0
  62. package/build/npm/{types/profiling/lifecycleMode/traceLifecycleProfiler.d.ts → types-ts3.8/profiling/UIProfiler.d.ts} +75 -70
  63. package/build/npm/types-ts3.8/profiling/index.d.ts +8 -0
  64. package/build/npm/types-ts3.8/profiling/utils.d.ts +1 -1
  65. package/package.json +7 -7
  66. package/build/npm/cjs/dev/profiling/lifecycleMode/traceLifecycleProfiler.js.map +0 -1
  67. package/build/npm/cjs/prod/profiling/lifecycleMode/traceLifecycleProfiler.js.map +0 -1
  68. package/build/npm/esm/dev/profiling/lifecycleMode/traceLifecycleProfiler.js.map +0 -1
  69. package/build/npm/esm/prod/profiling/lifecycleMode/traceLifecycleProfiler.js.map +0 -1
  70. package/build/npm/types/profiling/lifecycleMode/traceLifecycleProfiler.d.ts.map +0 -1
@@ -1,127 +1,112 @@
1
- import { uuid4, debug, getRootSpan, getGlobalScope, getSdkMetadataForEnvelopeHeader, createEnvelope, dsnToString } from '@sentry/core';
2
- import { DEBUG_BUILD } from '../../debug-build.js';
3
- import { startJSSelfProfile, createProfileChunkPayload, validateProfileChunk } from '../utils.js';
1
+ import { debug, uuid4, getGlobalScope, getRootSpan, getSdkMetadataForEnvelopeHeader, createEnvelope, dsnToString } from '@sentry/core';
2
+ import { DEBUG_BUILD } from '../debug-build.js';
3
+ import { shouldProfileSession, startJSSelfProfile, createProfileChunkPayload, validateProfileChunk } from './utils.js';
4
4
 
5
5
  const CHUNK_INTERVAL_MS = 60000; // 1 minute
6
6
  // Maximum length for trace lifecycle profiling per root span (e.g. if spanEnd never fires)
7
- const MAX_ROOT_SPAN_PROFILE_MS = 300000; // 5 minutes
7
+ const MAX_ROOT_SPAN_PROFILE_MS = 300000; // 5 minutes max per root span in trace mode
8
8
 
9
9
  /**
10
- * Browser trace-lifecycle profiler (UI Profiling / Profiling V2):
11
- * - Starts when the first sampled root span starts
12
- * - Stops when the last sampled root span ends
13
- * - While running, periodically stops and restarts the JS self-profiling API to collect chunks
10
+ * UIProfiler (Profiling V2):
11
+ * Supports two lifecycle modes:
12
+ * - 'manual': controlled explicitly via start()/stop()
13
+ * - 'trace': automatically runs while there are active sampled root spans
14
14
  *
15
15
  * Profiles are emitted as standalone `profile_chunk` envelopes either when:
16
16
  * - there are no more sampled root spans, or
17
17
  * - the 60s chunk timer elapses while profiling is running.
18
18
  */
19
- class BrowserTraceLifecycleProfiler {
19
+ class UIProfiler {
20
20
 
21
- // For keeping track of active root spans
21
+ // Manual + Trace
22
+ // one per Profiler session
23
+ // current profiler instance active flag
24
+ // sampling decision for entire session
22
25
 
23
- // ID for Profiler session
26
+ // Trace-only
24
27
 
25
28
  constructor() {
26
29
  this._client = undefined;
27
30
  this._profiler = undefined;
28
31
  this._chunkTimer = undefined;
29
- this._activeRootSpanIds = new Set();
30
- this._rootSpanTimeouts = new Map();
32
+
31
33
  this._profilerId = undefined;
32
34
  this._isRunning = false;
33
35
  this._sessionSampled = false;
36
+ this._lifecycleMode = undefined;
37
+
38
+ this._activeRootSpanIds = new Set();
39
+ this._rootSpanTimeouts = new Map();
34
40
  }
35
41
 
36
42
  /**
37
- * Initialize the profiler with client and session sampling decision computed by the integration.
43
+ * Initialize the profiler with client, session sampling and lifecycle mode.
38
44
  */
39
- initialize(client, sessionSampled) {
40
- // One Profiler ID per profiling session (user session)
41
- this._profilerId = uuid4();
45
+ initialize(client) {
46
+ const lifecycleMode = (client.getOptions() ).profileLifecycle;
47
+ const sessionSampled = shouldProfileSession(client.getOptions());
42
48
 
43
- DEBUG_BUILD && debug.log("[Profiling] Initializing profiler (lifecycle='trace').");
49
+ DEBUG_BUILD && debug.log(`[Profiling] Initializing profiler (lifecycle='${lifecycleMode}').`);
50
+
51
+ if (!sessionSampled) {
52
+ DEBUG_BUILD && debug.log('[Profiling] Session not sampled. Skipping lifecycle profiler initialization.');
53
+ }
44
54
 
55
+ // One Profiler ID per profiling session (user session)
56
+ this._profilerId = uuid4();
45
57
  this._client = client;
46
58
  this._sessionSampled = sessionSampled;
59
+ this._lifecycleMode = lifecycleMode;
47
60
 
48
- client.on('spanStart', span => {
49
- if (!this._sessionSampled) {
50
- DEBUG_BUILD && debug.log('[Profiling] Session not sampled because of negative sampling decision.');
51
- return;
52
- }
53
- if (span !== getRootSpan(span)) {
54
- return;
55
- }
56
- // Only count sampled root spans
57
- if (!span.isRecording()) {
58
- DEBUG_BUILD && debug.log('[Profiling] Discarding profile because root span was not sampled.');
59
- return;
60
- }
61
-
62
- // Matching root spans with profiles
63
- getGlobalScope().setContext('profile', {
64
- profiler_id: this._profilerId,
65
- });
66
-
67
- const spanId = span.spanContext().spanId;
68
- if (!spanId) {
69
- return;
70
- }
71
- if (this._activeRootSpanIds.has(spanId)) {
72
- return;
73
- }
74
-
75
- this._activeRootSpanIds.add(spanId);
76
- const rootSpanCount = this._activeRootSpanIds.size;
77
-
78
- const timeout = setTimeout(() => {
79
- this._onRootSpanTimeout(spanId);
80
- }, MAX_ROOT_SPAN_PROFILE_MS);
81
- this._rootSpanTimeouts.set(spanId, timeout);
82
-
83
- if (rootSpanCount === 1) {
84
- DEBUG_BUILD &&
85
- debug.log(
86
- `[Profiling] Root span with ID ${spanId} started. Will continue profiling for as long as there are active root spans (currently: ${rootSpanCount}).`,
87
- );
61
+ if (lifecycleMode === 'trace') {
62
+ this._setupTraceLifecycleListeners(client);
63
+ }
64
+ }
88
65
 
89
- this.start();
90
- }
91
- });
66
+ /** Starts UI profiling (only effective in 'manual' mode and when sampled). */
67
+ start() {
68
+ if (this._lifecycleMode === 'trace') {
69
+ DEBUG_BUILD &&
70
+ debug.warn(
71
+ '[Profiling] `profileLifecycle` is set to "trace". Calls to `uiProfiler.start()` are ignored in trace mode.',
72
+ );
73
+ return;
74
+ }
92
75
 
93
- client.on('spanEnd', span => {
94
- if (!this._sessionSampled) {
95
- return;
96
- }
76
+ if (this._isRunning) {
77
+ DEBUG_BUILD && debug.warn('[Profiling] Profile session is already running, `uiProfiler.start()` is a no-op.');
78
+ return;
79
+ }
97
80
 
98
- const spanId = span.spanContext().spanId;
99
- if (!spanId || !this._activeRootSpanIds.has(spanId)) {
100
- return;
101
- }
81
+ if (!this._sessionSampled) {
82
+ DEBUG_BUILD && debug.warn('[Profiling] Session is not sampled, `uiProfiler.start()` is a no-op.');
83
+ return;
84
+ }
102
85
 
103
- this._activeRootSpanIds.delete(spanId);
104
- const rootSpanCount = this._activeRootSpanIds.size;
86
+ this._beginProfiling();
87
+ }
105
88
 
89
+ /** Stops UI profiling (only effective in 'manual' mode). */
90
+ stop() {
91
+ if (this._lifecycleMode === 'trace') {
106
92
  DEBUG_BUILD &&
107
- debug.log(
108
- `[Profiling] Root span with ID ${spanId} ended. Will continue profiling for as long as there are active root spans (currently: ${rootSpanCount}).`,
93
+ debug.warn(
94
+ '[Profiling] `profileLifecycle` is set to "trace". Calls to `uiProfiler.stop()` are ignored in trace mode.',
109
95
  );
110
- if (rootSpanCount === 0) {
111
- this._collectCurrentChunk().catch(e => {
112
- DEBUG_BUILD && debug.error('[Profiling] Failed to collect current profile chunk on `spanEnd`:', e);
113
- });
96
+ return;
97
+ }
114
98
 
115
- this.stop();
116
- }
117
- });
99
+ if (!this._isRunning) {
100
+ DEBUG_BUILD && debug.warn('[Profiling] Profiler is not running, `uiProfiler.stop()` is a no-op.');
101
+ return;
102
+ }
103
+
104
+ this._endProfiling();
118
105
  }
119
106
 
120
- /**
121
- * Handle an already-active root span at integration setup time.
122
- */
107
+ /** Handle an already-active root span at integration setup time (used only in trace mode). */
123
108
  notifyRootSpanActive(rootSpan) {
124
- if (!this._sessionSampled) {
109
+ if (this._lifecycleMode !== 'trace' || !this._sessionSampled) {
125
110
  return;
126
111
  }
127
112
 
@@ -130,7 +115,7 @@ class BrowserTraceLifecycleProfiler {
130
115
  return;
131
116
  }
132
117
 
133
- this._activeRootSpanIds.add(spanId);
118
+ this._registerTraceRootSpan(spanId);
134
119
 
135
120
  const rootSpanCount = this._activeRootSpanIds.size;
136
121
 
@@ -138,25 +123,28 @@ class BrowserTraceLifecycleProfiler {
138
123
  DEBUG_BUILD &&
139
124
  debug.log('[Profiling] Detected already active root span during setup. Active root spans now:', rootSpanCount);
140
125
 
141
- this.start();
126
+ this._beginProfiling();
142
127
  }
143
128
  }
144
129
 
145
130
  /**
146
- * Start profiling if not already running.
131
+ * Begin profiling if not already running.
147
132
  */
148
- start() {
133
+ _beginProfiling() {
149
134
  if (this._isRunning) {
150
135
  return;
151
136
  }
152
137
  this._isRunning = true;
153
138
 
154
- DEBUG_BUILD && debug.log('[Profiling] Started profiling with profile ID:', this._profilerId);
139
+ DEBUG_BUILD && debug.log('[Profiling] Started profiling with profiler ID:', this._profilerId);
140
+
141
+ // Expose profiler_id to match root spans with profiles
142
+ getGlobalScope().setContext('profile', { profiler_id: this._profilerId });
155
143
 
156
144
  this._startProfilerInstance();
157
145
 
158
146
  if (!this._profiler) {
159
- DEBUG_BUILD && debug.log('[Profiling] Stopping trace lifecycle profiling.');
147
+ DEBUG_BUILD && debug.log('[Profiling] Failed to start JS Profiler; stopping.');
160
148
  this._resetProfilerInfo();
161
149
  return;
162
150
  }
@@ -164,15 +152,13 @@ class BrowserTraceLifecycleProfiler {
164
152
  this._startPeriodicChunking();
165
153
  }
166
154
 
167
- /**
168
- * Stop profiling; final chunk will be collected and sent.
169
- */
170
- stop() {
155
+ /** End profiling session; final chunk will be collected and sent. */
156
+ _endProfiling() {
171
157
  if (!this._isRunning) {
172
158
  return;
173
159
  }
174
-
175
160
  this._isRunning = false;
161
+
176
162
  if (this._chunkTimer) {
177
163
  clearTimeout(this._chunkTimer);
178
164
  this._chunkTimer = undefined;
@@ -184,10 +170,73 @@ class BrowserTraceLifecycleProfiler {
184
170
  this._collectCurrentChunk().catch(e => {
185
171
  DEBUG_BUILD && debug.error('[Profiling] Failed to collect current profile chunk on `stop()`:', e);
186
172
  });
173
+
174
+ // Manual: Clear profiling context so spans outside start()/stop() aren't marked as profiled
175
+ // Trace: Profile context is kept for the whole session duration
176
+ if (this._lifecycleMode === 'manual') {
177
+ getGlobalScope().setContext('profile', {});
178
+ }
179
+ }
180
+
181
+ /** Trace-mode: attach spanStart/spanEnd listeners. */
182
+ _setupTraceLifecycleListeners(client) {
183
+ client.on('spanStart', span => {
184
+ if (!this._sessionSampled) {
185
+ DEBUG_BUILD && debug.log('[Profiling] Session not sampled because of negative sampling decision.');
186
+ return;
187
+ }
188
+ if (span !== getRootSpan(span)) {
189
+ return; // only care about root spans
190
+ }
191
+ // Only count sampled root spans
192
+ if (!span.isRecording()) {
193
+ DEBUG_BUILD && debug.log('[Profiling] Discarding profile because root span was not sampled.');
194
+ return;
195
+ }
196
+
197
+ const spanId = span.spanContext().spanId;
198
+ if (!spanId || this._activeRootSpanIds.has(spanId)) {
199
+ return;
200
+ }
201
+
202
+ this._registerTraceRootSpan(spanId);
203
+
204
+ const rootSpanCount = this._activeRootSpanIds.size;
205
+ if (rootSpanCount === 1) {
206
+ DEBUG_BUILD &&
207
+ debug.log(
208
+ `[Profiling] Root span ${spanId} started. Profiling active while there are active root spans (count=${rootSpanCount}).`,
209
+ );
210
+ this._beginProfiling();
211
+ }
212
+ });
213
+
214
+ client.on('spanEnd', span => {
215
+ if (!this._sessionSampled) {
216
+ return;
217
+ }
218
+ const spanId = span.spanContext().spanId;
219
+ if (!spanId || !this._activeRootSpanIds.has(spanId)) {
220
+ return;
221
+ }
222
+ this._activeRootSpanIds.delete(spanId);
223
+ const rootSpanCount = this._activeRootSpanIds.size;
224
+
225
+ DEBUG_BUILD &&
226
+ debug.log(
227
+ `[Profiling] Root span with ID ${spanId} ended. Will continue profiling for as long as there are active root spans (currently: ${rootSpanCount}).`,
228
+ );
229
+ if (rootSpanCount === 0) {
230
+ this._collectCurrentChunk().catch(e => {
231
+ DEBUG_BUILD && debug.error('[Profiling] Failed to collect current profile chunk on last `spanEnd`:', e);
232
+ });
233
+ this._endProfiling();
234
+ }
235
+ });
187
236
  }
188
237
 
189
238
  /**
190
- * Resets profiling information from scope and resets running state
239
+ * Resets profiling information from scope and resets running state (used on failure)
191
240
  */
192
241
  _resetProfilerInfo() {
193
242
  this._isRunning = false;
@@ -202,16 +251,23 @@ class BrowserTraceLifecycleProfiler {
202
251
  this._rootSpanTimeouts.clear();
203
252
  }
204
253
 
254
+ /** Keep track of root spans and schedule safeguard timeout (trace mode). */
255
+ _registerTraceRootSpan(spanId) {
256
+ this._activeRootSpanIds.add(spanId);
257
+ const timeout = setTimeout(() => this._onRootSpanTimeout(spanId), MAX_ROOT_SPAN_PROFILE_MS);
258
+ this._rootSpanTimeouts.set(spanId, timeout);
259
+ }
260
+
205
261
  /**
206
262
  * Start a profiler instance if needed.
207
263
  */
208
264
  _startProfilerInstance() {
209
265
  if (this._profiler?.stopped === false) {
210
- return;
266
+ return; // already running
211
267
  }
212
268
  const profiler = startJSSelfProfile();
213
269
  if (!profiler) {
214
- DEBUG_BUILD && debug.log('[Profiling] Failed to start JS Profiler in trace lifecycle.');
270
+ DEBUG_BUILD && debug.log('[Profiling] Failed to start JS Profiler.');
215
271
  return;
216
272
  }
217
273
  this._profiler = profiler;
@@ -268,14 +324,13 @@ class BrowserTraceLifecycleProfiler {
268
324
 
269
325
  this._activeRootSpanIds.delete(rootSpanId);
270
326
 
271
- const rootSpanCount = this._activeRootSpanIds.size;
272
- if (rootSpanCount === 0) {
273
- this.stop();
327
+ if (this._activeRootSpanIds.size === 0) {
328
+ this._endProfiling();
274
329
  }
275
330
  }
276
331
 
277
332
  /**
278
- * Stop the current profiler, convert and send a profile chunk.
333
+ * Stop current profiler instance, convert profile to chunk & send.
279
334
  */
280
335
  async _collectCurrentChunk() {
281
336
  const prevProfiler = this._profiler;
@@ -337,5 +392,5 @@ class BrowserTraceLifecycleProfiler {
337
392
  }
338
393
  }
339
394
 
340
- export { BrowserTraceLifecycleProfiler };
341
- //# sourceMappingURL=traceLifecycleProfiler.js.map
395
+ export { UIProfiler };
396
+ //# sourceMappingURL=UIProfiler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UIProfiler.js","sources":["../../../../../src/profiling/UIProfiler.ts"],"sourcesContent":["import type { Client, ContinuousProfiler, ProfileChunk, Span } from '@sentry/core';\nimport {\n type ProfileChunkEnvelope,\n createEnvelope,\n debug,\n dsnToString,\n getGlobalScope,\n getRootSpan,\n getSdkMetadataForEnvelopeHeader,\n uuid4,\n} from '@sentry/core';\nimport type { BrowserOptions } from '../client';\nimport { DEBUG_BUILD } from './../debug-build';\nimport type { JSSelfProfiler } from './jsSelfProfiling';\nimport { createProfileChunkPayload, shouldProfileSession, startJSSelfProfile, validateProfileChunk } from './utils';\n\nconst CHUNK_INTERVAL_MS = 60_000; // 1 minute\n// Maximum length for trace lifecycle profiling per root span (e.g. if spanEnd never fires)\nconst MAX_ROOT_SPAN_PROFILE_MS = 300_000; // 5 minutes max per root span in trace mode\n\n/**\n * UIProfiler (Profiling V2):\n * Supports two lifecycle modes:\n * - 'manual': controlled explicitly via start()/stop()\n * - 'trace': automatically runs while there are active sampled root spans\n *\n * Profiles are emitted as standalone `profile_chunk` envelopes either when:\n * - there are no more sampled root spans, or\n * - the 60s chunk timer elapses while profiling is running.\n */\nexport class UIProfiler implements ContinuousProfiler<Client> {\n private _client: Client | undefined;\n private _profiler: JSSelfProfiler | undefined;\n private _chunkTimer: ReturnType<typeof setTimeout> | undefined;\n\n // Manual + Trace\n private _profilerId: string | undefined; // one per Profiler session\n private _isRunning: boolean; // current profiler instance active flag\n private _sessionSampled: boolean; // sampling decision for entire session\n private _lifecycleMode: 'manual' | 'trace' | undefined;\n\n // Trace-only\n private _activeRootSpanIds: Set<string>;\n private _rootSpanTimeouts: Map<string, ReturnType<typeof setTimeout>>;\n\n public constructor() {\n this._client = undefined;\n this._profiler = undefined;\n this._chunkTimer = undefined;\n\n this._profilerId = undefined;\n this._isRunning = false;\n this._sessionSampled = false;\n this._lifecycleMode = undefined;\n\n this._activeRootSpanIds = new Set();\n this._rootSpanTimeouts = new Map();\n }\n\n /**\n * Initialize the profiler with client, session sampling and lifecycle mode.\n */\n public initialize(client: Client): void {\n const lifecycleMode = (client.getOptions() as BrowserOptions).profileLifecycle;\n const sessionSampled = shouldProfileSession(client.getOptions());\n\n DEBUG_BUILD && debug.log(`[Profiling] Initializing profiler (lifecycle='${lifecycleMode}').`);\n\n if (!sessionSampled) {\n DEBUG_BUILD && debug.log('[Profiling] Session not sampled. Skipping lifecycle profiler initialization.');\n }\n\n // One Profiler ID per profiling session (user session)\n this._profilerId = uuid4();\n this._client = client;\n this._sessionSampled = sessionSampled;\n this._lifecycleMode = lifecycleMode;\n\n if (lifecycleMode === 'trace') {\n this._setupTraceLifecycleListeners(client);\n }\n }\n\n /** Starts UI profiling (only effective in 'manual' mode and when sampled). */\n public start(): void {\n if (this._lifecycleMode === 'trace') {\n DEBUG_BUILD &&\n debug.warn(\n '[Profiling] `profileLifecycle` is set to \"trace\". Calls to `uiProfiler.start()` are ignored in trace mode.',\n );\n return;\n }\n\n if (this._isRunning) {\n DEBUG_BUILD && debug.warn('[Profiling] Profile session is already running, `uiProfiler.start()` is a no-op.');\n return;\n }\n\n if (!this._sessionSampled) {\n DEBUG_BUILD && debug.warn('[Profiling] Session is not sampled, `uiProfiler.start()` is a no-op.');\n return;\n }\n\n this._beginProfiling();\n }\n\n /** Stops UI profiling (only effective in 'manual' mode). */\n public stop(): void {\n if (this._lifecycleMode === 'trace') {\n DEBUG_BUILD &&\n debug.warn(\n '[Profiling] `profileLifecycle` is set to \"trace\". Calls to `uiProfiler.stop()` are ignored in trace mode.',\n );\n return;\n }\n\n if (!this._isRunning) {\n DEBUG_BUILD && debug.warn('[Profiling] Profiler is not running, `uiProfiler.stop()` is a no-op.');\n return;\n }\n\n this._endProfiling();\n }\n\n /** Handle an already-active root span at integration setup time (used only in trace mode). */\n public notifyRootSpanActive(rootSpan: Span): void {\n if (this._lifecycleMode !== 'trace' || !this._sessionSampled) {\n return;\n }\n\n const spanId = rootSpan.spanContext().spanId;\n if (!spanId || this._activeRootSpanIds.has(spanId)) {\n return;\n }\n\n this._registerTraceRootSpan(spanId);\n\n const rootSpanCount = this._activeRootSpanIds.size;\n\n if (rootSpanCount === 1) {\n DEBUG_BUILD &&\n debug.log('[Profiling] Detected already active root span during setup. Active root spans now:', rootSpanCount);\n\n this._beginProfiling();\n }\n }\n\n /**\n * Begin profiling if not already running.\n */\n private _beginProfiling(): void {\n if (this._isRunning) {\n return;\n }\n this._isRunning = true;\n\n DEBUG_BUILD && debug.log('[Profiling] Started profiling with profiler ID:', this._profilerId);\n\n // Expose profiler_id to match root spans with profiles\n getGlobalScope().setContext('profile', { profiler_id: this._profilerId });\n\n this._startProfilerInstance();\n\n if (!this._profiler) {\n DEBUG_BUILD && debug.log('[Profiling] Failed to start JS Profiler; stopping.');\n this._resetProfilerInfo();\n return;\n }\n\n this._startPeriodicChunking();\n }\n\n /** End profiling session; final chunk will be collected and sent. */\n private _endProfiling(): void {\n if (!this._isRunning) {\n return;\n }\n this._isRunning = false;\n\n if (this._chunkTimer) {\n clearTimeout(this._chunkTimer);\n this._chunkTimer = undefined;\n }\n\n this._clearAllRootSpanTimeouts();\n\n // Collect whatever was currently recording\n this._collectCurrentChunk().catch(e => {\n DEBUG_BUILD && debug.error('[Profiling] Failed to collect current profile chunk on `stop()`:', e);\n });\n\n // Manual: Clear profiling context so spans outside start()/stop() aren't marked as profiled\n // Trace: Profile context is kept for the whole session duration\n if (this._lifecycleMode === 'manual') {\n getGlobalScope().setContext('profile', {});\n }\n }\n\n /** Trace-mode: attach spanStart/spanEnd listeners. */\n private _setupTraceLifecycleListeners(client: Client): void {\n client.on('spanStart', span => {\n if (!this._sessionSampled) {\n DEBUG_BUILD && debug.log('[Profiling] Session not sampled because of negative sampling decision.');\n return;\n }\n if (span !== getRootSpan(span)) {\n return; // only care about root spans\n }\n // Only count sampled root spans\n if (!span.isRecording()) {\n DEBUG_BUILD && debug.log('[Profiling] Discarding profile because root span was not sampled.');\n return;\n }\n\n const spanId = span.spanContext().spanId;\n if (!spanId || this._activeRootSpanIds.has(spanId)) {\n return;\n }\n\n this._registerTraceRootSpan(spanId);\n\n const rootSpanCount = this._activeRootSpanIds.size;\n if (rootSpanCount === 1) {\n DEBUG_BUILD &&\n debug.log(\n `[Profiling] Root span ${spanId} started. Profiling active while there are active root spans (count=${rootSpanCount}).`,\n );\n this._beginProfiling();\n }\n });\n\n client.on('spanEnd', span => {\n if (!this._sessionSampled) {\n return;\n }\n const spanId = span.spanContext().spanId;\n if (!spanId || !this._activeRootSpanIds.has(spanId)) {\n return;\n }\n this._activeRootSpanIds.delete(spanId);\n const rootSpanCount = this._activeRootSpanIds.size;\n\n DEBUG_BUILD &&\n debug.log(\n `[Profiling] Root span with ID ${spanId} ended. Will continue profiling for as long as there are active root spans (currently: ${rootSpanCount}).`,\n );\n if (rootSpanCount === 0) {\n this._collectCurrentChunk().catch(e => {\n DEBUG_BUILD && debug.error('[Profiling] Failed to collect current profile chunk on last `spanEnd`:', e);\n });\n this._endProfiling();\n }\n });\n }\n\n /**\n * Resets profiling information from scope and resets running state (used on failure)\n */\n private _resetProfilerInfo(): void {\n this._isRunning = false;\n getGlobalScope().setContext('profile', {});\n }\n\n /**\n * Clear and reset all per-root-span timeouts.\n */\n private _clearAllRootSpanTimeouts(): void {\n this._rootSpanTimeouts.forEach(timeout => clearTimeout(timeout));\n this._rootSpanTimeouts.clear();\n }\n\n /** Keep track of root spans and schedule safeguard timeout (trace mode). */\n private _registerTraceRootSpan(spanId: string): void {\n this._activeRootSpanIds.add(spanId);\n const timeout = setTimeout(() => this._onRootSpanTimeout(spanId), MAX_ROOT_SPAN_PROFILE_MS);\n this._rootSpanTimeouts.set(spanId, timeout);\n }\n\n /**\n * Start a profiler instance if needed.\n */\n private _startProfilerInstance(): void {\n if (this._profiler?.stopped === false) {\n return; // already running\n }\n const profiler = startJSSelfProfile();\n if (!profiler) {\n DEBUG_BUILD && debug.log('[Profiling] Failed to start JS Profiler.');\n return;\n }\n this._profiler = profiler;\n }\n\n /**\n * Schedule the next 60s chunk while running.\n * Each tick collects a chunk and restarts the profiler.\n * A chunk should be closed when there are no active root spans anymore OR when the maximum chunk interval is reached.\n */\n private _startPeriodicChunking(): void {\n if (!this._isRunning) {\n return;\n }\n\n this._chunkTimer = setTimeout(() => {\n this._collectCurrentChunk().catch(e => {\n DEBUG_BUILD && debug.error('[Profiling] Failed to collect current profile chunk during periodic chunking:', e);\n });\n\n if (this._isRunning) {\n this._startProfilerInstance();\n\n if (!this._profiler) {\n // If restart failed, stop scheduling further chunks and reset context.\n this._resetProfilerInfo();\n return;\n }\n\n this._startPeriodicChunking();\n }\n }, CHUNK_INTERVAL_MS);\n }\n\n /**\n * Handle timeout for a specific root span ID to avoid indefinitely running profiler if `spanEnd` never fires.\n * If this was the last active root span, collect the current chunk and stop profiling.\n */\n private _onRootSpanTimeout(rootSpanId: string): void {\n // If span already ended, ignore\n if (!this._rootSpanTimeouts.has(rootSpanId)) {\n return;\n }\n this._rootSpanTimeouts.delete(rootSpanId);\n\n if (!this._activeRootSpanIds.has(rootSpanId)) {\n return;\n }\n\n DEBUG_BUILD &&\n debug.log(\n `[Profiling] Reached 5-minute timeout for root span ${rootSpanId}. You likely started a manual root span that never called \\`.end()\\`.`,\n );\n\n this._activeRootSpanIds.delete(rootSpanId);\n\n if (this._activeRootSpanIds.size === 0) {\n this._endProfiling();\n }\n }\n\n /**\n * Stop current profiler instance, convert profile to chunk & send.\n */\n private async _collectCurrentChunk(): Promise<void> {\n const prevProfiler = this._profiler;\n this._profiler = undefined;\n\n if (!prevProfiler) {\n return;\n }\n\n try {\n const profile = await prevProfiler.stop();\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const chunk = createProfileChunkPayload(profile, this._client!, this._profilerId);\n\n // Validate chunk before sending\n const validationReturn = validateProfileChunk(chunk);\n if ('reason' in validationReturn) {\n DEBUG_BUILD &&\n debug.log(\n '[Profiling] Discarding invalid profile chunk (this is probably a bug in the SDK):',\n validationReturn.reason,\n );\n return;\n }\n\n this._sendProfileChunk(chunk);\n\n DEBUG_BUILD && debug.log('[Profiling] Collected browser profile chunk.');\n } catch (e) {\n DEBUG_BUILD && debug.log('[Profiling] Error while stopping JS Profiler for chunk:', e);\n }\n }\n\n /**\n * Send a profile chunk as a standalone envelope.\n */\n private _sendProfileChunk(chunk: ProfileChunk): void {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const client = this._client!;\n\n const sdkInfo = getSdkMetadataForEnvelopeHeader(client.getSdkMetadata?.());\n const dsn = client.getDsn();\n const tunnel = client.getOptions().tunnel;\n\n const envelope = createEnvelope<ProfileChunkEnvelope>(\n {\n event_id: uuid4(),\n sent_at: new Date().toISOString(),\n ...(sdkInfo && { sdk: sdkInfo }),\n ...(!!tunnel && dsn && { dsn: dsnToString(dsn) }),\n },\n [[{ type: 'profile_chunk' }, chunk]],\n );\n\n client.sendEnvelope(envelope).then(null, reason => {\n DEBUG_BUILD && debug.error('Error while sending profile chunk envelope:', reason);\n });\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import { getClient, debug } from '@sentry/core';
2
+ import { DEBUG_BUILD } from '../debug-build.js';
3
+
4
+ /**
5
+ * Starts the Sentry UI profiler.
6
+ * This mode is exclusive with the transaction profiler and will only work if the profilesSampleRate is set to a falsy value.
7
+ * In UI profiling mode, the profiler will keep reporting profile chunks to Sentry until it is stopped, which allows for continuous profiling of the application.
8
+ */
9
+ function startProfiler() {
10
+ const client = getClient();
11
+ if (!client) {
12
+ DEBUG_BUILD && debug.warn('No Sentry client available, profiling is not started');
13
+ return;
14
+ }
15
+
16
+ const integration = client.getIntegrationByName('BrowserProfiling');
17
+
18
+ if (!integration) {
19
+ DEBUG_BUILD && debug.warn('BrowserProfiling integration is not available');
20
+ return;
21
+ }
22
+
23
+ client.emit('startUIProfiler');
24
+ }
25
+
26
+ /**
27
+ * Stops the Sentry UI profiler.
28
+ * Calls to stop will stop the profiler and flush the currently collected profile data to Sentry.
29
+ */
30
+ function stopProfiler() {
31
+ const client = getClient();
32
+ if (!client) {
33
+ DEBUG_BUILD && debug.warn('No Sentry client available, profiling is not started');
34
+ return;
35
+ }
36
+
37
+ const integration = client.getIntegrationByName('BrowserProfiling');
38
+ if (!integration) {
39
+ DEBUG_BUILD && debug.warn('ProfilingIntegration is not available');
40
+ return;
41
+ }
42
+
43
+ client.emit('stopUIProfiler');
44
+ }
45
+
46
+ /**
47
+ * Profiler namespace for controlling the JS profiler in 'manual' mode.
48
+ *
49
+ * Requires the `browserProfilingIntegration` from the `@sentry/browser` package.
50
+ */
51
+ const uiProfiler = {
52
+ startProfiler,
53
+ stopProfiler,
54
+ };
55
+
56
+ export { uiProfiler };
57
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../../../../src/profiling/index.ts"],"sourcesContent":["import type { Profiler } from '@sentry/core';\nimport { debug, getClient } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\n\n/**\n * Starts the Sentry UI profiler.\n * This mode is exclusive with the transaction profiler and will only work if the profilesSampleRate is set to a falsy value.\n * In UI profiling mode, the profiler will keep reporting profile chunks to Sentry until it is stopped, which allows for continuous profiling of the application.\n */\nfunction startProfiler(): void {\n const client = getClient();\n if (!client) {\n DEBUG_BUILD && debug.warn('No Sentry client available, profiling is not started');\n return;\n }\n\n const integration = client.getIntegrationByName('BrowserProfiling');\n\n if (!integration) {\n DEBUG_BUILD && debug.warn('BrowserProfiling integration is not available');\n return;\n }\n\n client.emit('startUIProfiler');\n}\n\n/**\n * Stops the Sentry UI profiler.\n * Calls to stop will stop the profiler and flush the currently collected profile data to Sentry.\n */\nfunction stopProfiler(): void {\n const client = getClient();\n if (!client) {\n DEBUG_BUILD && debug.warn('No Sentry client available, profiling is not started');\n return;\n }\n\n const integration = client.getIntegrationByName('BrowserProfiling');\n if (!integration) {\n DEBUG_BUILD && debug.warn('ProfilingIntegration is not available');\n return;\n }\n\n client.emit('stopUIProfiler');\n}\n\n/**\n * Profiler namespace for controlling the JS profiler in 'manual' mode.\n *\n * Requires the `browserProfilingIntegration` from the `@sentry/browser` package.\n */\nexport const uiProfiler: Profiler = {\n startProfiler,\n stopProfiler,\n};\n"],"names":[],"mappings":";;;AAIA;AACA;AACA;AACA;AACA;AACA,SAAS,aAAa,GAAS;AAC/B,EAAE,MAAM,MAAA,GAAS,SAAS,EAAE;AAC5B,EAAE,IAAI,CAAC,MAAM,EAAE;AACf,IAAI,eAAe,KAAK,CAAC,IAAI,CAAC,sDAAsD,CAAC;AACrF,IAAI;AACJ,EAAE;;AAEF,EAAE,MAAM,cAAc,MAAM,CAAC,oBAAoB,CAAC,kBAAkB,CAAC;;AAErE,EAAE,IAAI,CAAC,WAAW,EAAE;AACpB,IAAI,eAAe,KAAK,CAAC,IAAI,CAAC,+CAA+C,CAAC;AAC9E,IAAI;AACJ,EAAE;;AAEF,EAAE,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC;AAChC;;AAEA;AACA;AACA;AACA;AACA,SAAS,YAAY,GAAS;AAC9B,EAAE,MAAM,MAAA,GAAS,SAAS,EAAE;AAC5B,EAAE,IAAI,CAAC,MAAM,EAAE;AACf,IAAI,eAAe,KAAK,CAAC,IAAI,CAAC,sDAAsD,CAAC;AACrF,IAAI;AACJ,EAAE;;AAEF,EAAE,MAAM,cAAc,MAAM,CAAC,oBAAoB,CAAC,kBAAkB,CAAC;AACrE,EAAE,IAAI,CAAC,WAAW,EAAE;AACpB,IAAI,eAAe,KAAK,CAAC,IAAI,CAAC,uCAAuC,CAAC;AACtE,IAAI;AACJ,EAAE;;AAEF,EAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC;AAC/B;;AAEA;AACA;AACA;AACA;AACA;AACO,MAAM,UAAU,GAAa;AACpC,EAAE,aAAa;AACf,EAAE,YAAY;AACd;;;;"}
@@ -1,9 +1,9 @@
1
1
  import { defineIntegration, debug, getActiveSpan, getRootSpan, hasSpansEnabled } from '@sentry/core';
2
2
  import { DEBUG_BUILD } from '../debug-build.js';
3
3
  import { WINDOW } from '../helpers.js';
4
- import { BrowserTraceLifecycleProfiler } from './lifecycleMode/traceLifecycleProfiler.js';
5
4
  import { startProfileForSpan } from './startProfileForSpan.js';
6
- import { attachProfiledThreadToEvent, hasLegacyProfiling, shouldProfileSession, isAutomatedPageLoadSpan, shouldProfileSpanLegacy, getActiveProfilesCount, findProfiledTransactionsFromEnvelope, takeProfileFromGlobalCache, createProfilingEvent, addProfilesToEnvelope } from './utils.js';
5
+ import { UIProfiler } from './UIProfiler.js';
6
+ import { attachProfiledThreadToEvent, hasLegacyProfiling, isAutomatedPageLoadSpan, shouldProfileSpanLegacy, getActiveProfilesCount, findProfiledTransactionsFromEnvelope, takeProfileFromGlobalCache, createProfilingEvent, addProfilesToEnvelope } from './utils.js';
7
7
 
8
8
  const INTEGRATION_NAME = 'BrowserProfiling';
9
9
 
@@ -12,12 +12,14 @@ const _browserProfilingIntegration = (() => {
12
12
  name: INTEGRATION_NAME,
13
13
  setup(client) {
14
14
  const options = client.getOptions() ;
15
+ const profiler = new UIProfiler();
15
16
 
16
17
  if (!hasLegacyProfiling(options) && !options.profileLifecycle) {
17
18
  // Set default lifecycle mode
18
19
  options.profileLifecycle = 'manual';
19
20
  }
20
21
 
22
+ // eslint-disable-next-line deprecation/deprecation
21
23
  if (hasLegacyProfiling(options) && !options.profilesSampleRate) {
22
24
  DEBUG_BUILD && debug.log('[Profiling] Profiling disabled, no profiling options found.');
23
25
  return;
@@ -35,14 +37,15 @@ const _browserProfilingIntegration = (() => {
35
37
 
36
38
  // UI PROFILING (Profiling V2)
37
39
  if (!hasLegacyProfiling(options)) {
38
- const sessionSampled = shouldProfileSession(options);
39
- if (!sessionSampled) {
40
- DEBUG_BUILD && debug.log('[Profiling] Session not sampled. Skipping lifecycle profiler initialization.');
41
- }
42
-
43
40
  const lifecycleMode = options.profileLifecycle;
44
41
 
45
- if (lifecycleMode === 'trace') {
42
+ // Registering hooks in all lifecycle modes to be able to notify users in case they want to start/stop the profiler manually in `trace` mode
43
+ client.on('startUIProfiler', () => profiler.start());
44
+ client.on('stopUIProfiler', () => profiler.stop());
45
+
46
+ if (lifecycleMode === 'manual') {
47
+ profiler.initialize(client);
48
+ } else if (lifecycleMode === 'trace') {
46
49
  if (!hasSpansEnabled(options)) {
47
50
  DEBUG_BUILD &&
48
51
  debug.warn(
@@ -51,12 +54,11 @@ const _browserProfilingIntegration = (() => {
51
54
  return;
52
55
  }
53
56
 
54
- const traceLifecycleProfiler = new BrowserTraceLifecycleProfiler();
55
- traceLifecycleProfiler.initialize(client, sessionSampled);
57
+ profiler.initialize(client);
56
58
 
57
59
  // If there is an active, sampled root span already, notify the profiler
58
60
  if (rootSpan) {
59
- traceLifecycleProfiler.notifyRootSpanActive(rootSpan);
61
+ profiler.notifyRootSpanActive(rootSpan);
60
62
  }
61
63
 
62
64
  // In case rootSpan is created slightly after setup -> schedule microtask to re-check and notify.
@@ -64,7 +66,7 @@ const _browserProfilingIntegration = (() => {
64
66
  const laterActiveSpan = getActiveSpan();
65
67
  const laterRootSpan = laterActiveSpan && getRootSpan(laterActiveSpan);
66
68
  if (laterRootSpan) {
67
- traceLifecycleProfiler.notifyRootSpanActive(laterRootSpan);
69
+ profiler.notifyRootSpanActive(laterRootSpan);
68
70
  }
69
71
  }, 0);
70
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"integration.js","sources":["../../../../../src/profiling/integration.ts"],"sourcesContent":["import type { EventEnvelope, IntegrationFn, Profile, Span } from '@sentry/core';\nimport { debug, defineIntegration, getActiveSpan, getRootSpan, hasSpansEnabled } from '@sentry/core';\nimport type { BrowserOptions } from '../client';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { WINDOW } from '../helpers';\nimport { BrowserTraceLifecycleProfiler } from './lifecycleMode/traceLifecycleProfiler';\nimport { startProfileForSpan } from './startProfileForSpan';\nimport type { ProfiledEvent } from './utils';\nimport {\n addProfilesToEnvelope,\n attachProfiledThreadToEvent,\n createProfilingEvent,\n findProfiledTransactionsFromEnvelope,\n getActiveProfilesCount,\n hasLegacyProfiling,\n isAutomatedPageLoadSpan,\n shouldProfileSession,\n shouldProfileSpanLegacy,\n takeProfileFromGlobalCache,\n} from './utils';\n\nconst INTEGRATION_NAME = 'BrowserProfiling';\n\nconst _browserProfilingIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n const options = client.getOptions() as BrowserOptions;\n\n if (!hasLegacyProfiling(options) && !options.profileLifecycle) {\n // Set default lifecycle mode\n options.profileLifecycle = 'manual';\n }\n\n if (hasLegacyProfiling(options) && !options.profilesSampleRate) {\n DEBUG_BUILD && debug.log('[Profiling] Profiling disabled, no profiling options found.');\n return;\n }\n\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n\n if (hasLegacyProfiling(options) && options.profileSessionSampleRate !== undefined) {\n DEBUG_BUILD &&\n debug.warn(\n '[Profiling] Both legacy profiling (`profilesSampleRate`) and UI profiling settings are defined. `profileSessionSampleRate` has no effect when legacy profiling is enabled.',\n );\n }\n\n // UI PROFILING (Profiling V2)\n if (!hasLegacyProfiling(options)) {\n const sessionSampled = shouldProfileSession(options);\n if (!sessionSampled) {\n DEBUG_BUILD && debug.log('[Profiling] Session not sampled. Skipping lifecycle profiler initialization.');\n }\n\n const lifecycleMode = options.profileLifecycle;\n\n if (lifecycleMode === 'trace') {\n if (!hasSpansEnabled(options)) {\n DEBUG_BUILD &&\n debug.warn(\n \"[Profiling] `profileLifecycle` is 'trace' but tracing is disabled. Set a `tracesSampleRate` or `tracesSampler` to enable span tracing.\",\n );\n return;\n }\n\n const traceLifecycleProfiler = new BrowserTraceLifecycleProfiler();\n traceLifecycleProfiler.initialize(client, sessionSampled);\n\n // If there is an active, sampled root span already, notify the profiler\n if (rootSpan) {\n traceLifecycleProfiler.notifyRootSpanActive(rootSpan);\n }\n\n // In case rootSpan is created slightly after setup -> schedule microtask to re-check and notify.\n WINDOW.setTimeout(() => {\n const laterActiveSpan = getActiveSpan();\n const laterRootSpan = laterActiveSpan && getRootSpan(laterActiveSpan);\n if (laterRootSpan) {\n traceLifecycleProfiler.notifyRootSpanActive(laterRootSpan);\n }\n }, 0);\n }\n } else {\n // LEGACY PROFILING (v1)\n if (rootSpan && isAutomatedPageLoadSpan(rootSpan)) {\n if (shouldProfileSpanLegacy(rootSpan)) {\n startProfileForSpan(rootSpan);\n }\n }\n\n client.on('spanStart', (span: Span) => {\n if (span === getRootSpan(span) && shouldProfileSpanLegacy(span)) {\n startProfileForSpan(span);\n }\n });\n\n client.on('beforeEnvelope', (envelope): void => {\n // if not profiles are in queue, there is nothing to add to the envelope.\n if (!getActiveProfilesCount()) {\n return;\n }\n\n const profiledTransactionEvents = findProfiledTransactionsFromEnvelope(envelope);\n if (!profiledTransactionEvents.length) {\n return;\n }\n\n const profilesToAddToEnvelope: Profile[] = [];\n\n for (const profiledTransaction of profiledTransactionEvents) {\n const context = profiledTransaction?.contexts;\n const profile_id = context?.profile?.['profile_id'];\n const start_timestamp = context?.profile?.['start_timestamp'];\n\n if (typeof profile_id !== 'string') {\n DEBUG_BUILD && debug.log('[Profiling] cannot find profile for a span without a profile context');\n continue;\n }\n\n if (!profile_id) {\n DEBUG_BUILD && debug.log('[Profiling] cannot find profile for a span without a profile context');\n continue;\n }\n\n // Remove the profile from the span context before sending, relay will take care of the rest.\n if (context?.profile) {\n delete context.profile;\n }\n\n const profile = takeProfileFromGlobalCache(profile_id);\n if (!profile) {\n DEBUG_BUILD && debug.log(`[Profiling] Could not retrieve profile for span: ${profile_id}`);\n continue;\n }\n\n const profileEvent = createProfilingEvent(\n profile_id,\n start_timestamp as number | undefined,\n profile,\n profiledTransaction as ProfiledEvent,\n );\n if (profileEvent) {\n profilesToAddToEnvelope.push(profileEvent);\n }\n }\n\n addProfilesToEnvelope(envelope as EventEnvelope, profilesToAddToEnvelope);\n });\n }\n },\n processEvent(event) {\n return attachProfiledThreadToEvent(event);\n },\n };\n}) satisfies IntegrationFn;\n\nexport const browserProfilingIntegration = defineIntegration(_browserProfilingIntegration);\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"integration.js","sources":["../../../../../src/profiling/integration.ts"],"sourcesContent":["import type { EventEnvelope, IntegrationFn, Profile, Span } from '@sentry/core';\nimport { debug, defineIntegration, getActiveSpan, getRootSpan, hasSpansEnabled } from '@sentry/core';\nimport type { BrowserOptions } from '../client';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { WINDOW } from '../helpers';\nimport { startProfileForSpan } from './startProfileForSpan';\nimport { UIProfiler } from './UIProfiler';\nimport type { ProfiledEvent } from './utils';\nimport {\n addProfilesToEnvelope,\n attachProfiledThreadToEvent,\n createProfilingEvent,\n findProfiledTransactionsFromEnvelope,\n getActiveProfilesCount,\n hasLegacyProfiling,\n isAutomatedPageLoadSpan,\n shouldProfileSpanLegacy,\n takeProfileFromGlobalCache,\n} from './utils';\n\nconst INTEGRATION_NAME = 'BrowserProfiling';\n\nconst _browserProfilingIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n const options = client.getOptions() as BrowserOptions;\n const profiler = new UIProfiler();\n\n if (!hasLegacyProfiling(options) && !options.profileLifecycle) {\n // Set default lifecycle mode\n options.profileLifecycle = 'manual';\n }\n\n // eslint-disable-next-line deprecation/deprecation\n if (hasLegacyProfiling(options) && !options.profilesSampleRate) {\n DEBUG_BUILD && debug.log('[Profiling] Profiling disabled, no profiling options found.');\n return;\n }\n\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n\n if (hasLegacyProfiling(options) && options.profileSessionSampleRate !== undefined) {\n DEBUG_BUILD &&\n debug.warn(\n '[Profiling] Both legacy profiling (`profilesSampleRate`) and UI profiling settings are defined. `profileSessionSampleRate` has no effect when legacy profiling is enabled.',\n );\n }\n\n // UI PROFILING (Profiling V2)\n if (!hasLegacyProfiling(options)) {\n const lifecycleMode = options.profileLifecycle;\n\n // Registering hooks in all lifecycle modes to be able to notify users in case they want to start/stop the profiler manually in `trace` mode\n client.on('startUIProfiler', () => profiler.start());\n client.on('stopUIProfiler', () => profiler.stop());\n\n if (lifecycleMode === 'manual') {\n profiler.initialize(client);\n } else if (lifecycleMode === 'trace') {\n if (!hasSpansEnabled(options)) {\n DEBUG_BUILD &&\n debug.warn(\n \"[Profiling] `profileLifecycle` is 'trace' but tracing is disabled. Set a `tracesSampleRate` or `tracesSampler` to enable span tracing.\",\n );\n return;\n }\n\n profiler.initialize(client);\n\n // If there is an active, sampled root span already, notify the profiler\n if (rootSpan) {\n profiler.notifyRootSpanActive(rootSpan);\n }\n\n // In case rootSpan is created slightly after setup -> schedule microtask to re-check and notify.\n WINDOW.setTimeout(() => {\n const laterActiveSpan = getActiveSpan();\n const laterRootSpan = laterActiveSpan && getRootSpan(laterActiveSpan);\n if (laterRootSpan) {\n profiler.notifyRootSpanActive(laterRootSpan);\n }\n }, 0);\n }\n } else {\n // LEGACY PROFILING (v1)\n if (rootSpan && isAutomatedPageLoadSpan(rootSpan)) {\n if (shouldProfileSpanLegacy(rootSpan)) {\n startProfileForSpan(rootSpan);\n }\n }\n\n client.on('spanStart', (span: Span) => {\n if (span === getRootSpan(span) && shouldProfileSpanLegacy(span)) {\n startProfileForSpan(span);\n }\n });\n\n client.on('beforeEnvelope', (envelope): void => {\n // if not profiles are in queue, there is nothing to add to the envelope.\n if (!getActiveProfilesCount()) {\n return;\n }\n\n const profiledTransactionEvents = findProfiledTransactionsFromEnvelope(envelope);\n if (!profiledTransactionEvents.length) {\n return;\n }\n\n const profilesToAddToEnvelope: Profile[] = [];\n\n for (const profiledTransaction of profiledTransactionEvents) {\n const context = profiledTransaction?.contexts;\n const profile_id = context?.profile?.['profile_id'];\n const start_timestamp = context?.profile?.['start_timestamp'];\n\n if (typeof profile_id !== 'string') {\n DEBUG_BUILD && debug.log('[Profiling] cannot find profile for a span without a profile context');\n continue;\n }\n\n if (!profile_id) {\n DEBUG_BUILD && debug.log('[Profiling] cannot find profile for a span without a profile context');\n continue;\n }\n\n // Remove the profile from the span context before sending, relay will take care of the rest.\n if (context?.profile) {\n delete context.profile;\n }\n\n const profile = takeProfileFromGlobalCache(profile_id);\n if (!profile) {\n DEBUG_BUILD && debug.log(`[Profiling] Could not retrieve profile for span: ${profile_id}`);\n continue;\n }\n\n const profileEvent = createProfilingEvent(\n profile_id,\n start_timestamp as number | undefined,\n profile,\n profiledTransaction as ProfiledEvent,\n );\n if (profileEvent) {\n profilesToAddToEnvelope.push(profileEvent);\n }\n }\n\n addProfilesToEnvelope(envelope as EventEnvelope, profilesToAddToEnvelope);\n });\n }\n },\n processEvent(event) {\n return attachProfiledThreadToEvent(event);\n },\n };\n}) satisfies IntegrationFn;\n\nexport const browserProfilingIntegration = defineIntegration(_browserProfilingIntegration);\n"],"names":[],"mappings":"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"}