|
1 |
| -import type { Client, ClientOptions, Event, EventHint, StackFrame, StackParser } from '@sentry/types'; |
2 |
| -import { dateTimestampInSeconds, GLOBAL_OBJ, normalize, resolvedSyncPromise, truncate, uuid4 } from '@sentry/utils'; |
| 1 | +import type { |
| 2 | + CaptureContext, |
| 3 | + Client, |
| 4 | + ClientOptions, |
| 5 | + Event, |
| 6 | + EventHint, |
| 7 | + Scope as ScopeInterface, |
| 8 | + ScopeContext, |
| 9 | + StackFrame, |
| 10 | + StackParser, |
| 11 | +} from '@sentry/types'; |
| 12 | +import { |
| 13 | + addExceptionMechanism, |
| 14 | + dateTimestampInSeconds, |
| 15 | + GLOBAL_OBJ, |
| 16 | + normalize, |
| 17 | + resolvedSyncPromise, |
| 18 | + truncate, |
| 19 | + uuid4, |
| 20 | +} from '@sentry/utils'; |
3 | 21 |
|
4 | 22 | import { DEFAULT_ENVIRONMENT } from '../constants';
|
5 | 23 | import { getGlobalEventProcessors, notifyEventProcessors } from '../eventProcessors';
|
6 | 24 | import { Scope } from '../scope';
|
7 | 25 |
|
| 26 | +/** |
| 27 | + * This type makes sure that we get either a CaptureContext, OR an EventHint. |
| 28 | + * It does not allow mixing them, which could lead to unexpected outcomes, e.g. this is disallowed: |
| 29 | + * { user: { id: '123' }, mechanism: { handled: false } } |
| 30 | + */ |
| 31 | +export type ExclusiveEventHintOrCaptureContext = |
| 32 | + | (CaptureContext & Partial<{ [key in keyof EventHint]: never }>) |
| 33 | + | (EventHint & Partial<{ [key in keyof ScopeContext]: never }>); |
| 34 | + |
8 | 35 | /**
|
9 | 36 | * Adds common information to events.
|
10 | 37 | *
|
@@ -52,6 +79,10 @@ export function prepareEvent(
|
52 | 79 | finalScope = Scope.clone(finalScope).update(hint.captureContext);
|
53 | 80 | }
|
54 | 81 |
|
| 82 | + if (hint.mechanism) { |
| 83 | + addExceptionMechanism(prepared, hint.mechanism); |
| 84 | + } |
| 85 | + |
55 | 86 | // We prepare the result here with a resolved Event.
|
56 | 87 | let result = resolvedSyncPromise<Event | null>(prepared);
|
57 | 88 |
|
@@ -309,3 +340,50 @@ function normalizeEvent(event: Event | null, depth: number, maxBreadth: number):
|
309 | 340 |
|
310 | 341 | return normalized;
|
311 | 342 | }
|
| 343 | + |
| 344 | +/** |
| 345 | + * Parse either an `EventHint` directly, or convert a `CaptureContext` to an `EventHint`. |
| 346 | + * This is used to allow to update method signatures that used to accept a `CaptureContext` but should now accept an `EventHint`. |
| 347 | + */ |
| 348 | +export function parseEventHintOrCaptureContext( |
| 349 | + hint: ExclusiveEventHintOrCaptureContext | undefined, |
| 350 | +): EventHint | undefined { |
| 351 | + if (!hint) { |
| 352 | + return undefined; |
| 353 | + } |
| 354 | + |
| 355 | + // If you pass a Scope or `() => Scope` as CaptureContext, we just return this as captureContext |
| 356 | + if (hintIsScopeOrFunction(hint)) { |
| 357 | + return { captureContext: hint }; |
| 358 | + } |
| 359 | + |
| 360 | + if (hintIsScopeContext(hint)) { |
| 361 | + return { |
| 362 | + captureContext: hint, |
| 363 | + }; |
| 364 | + } |
| 365 | + |
| 366 | + return hint; |
| 367 | +} |
| 368 | + |
| 369 | +function hintIsScopeOrFunction( |
| 370 | + hint: CaptureContext | EventHint, |
| 371 | +): hint is ScopeInterface | ((scope: ScopeInterface) => ScopeInterface) { |
| 372 | + return hint instanceof Scope || typeof hint === 'function'; |
| 373 | +} |
| 374 | + |
| 375 | +type ScopeContextProperty = keyof ScopeContext; |
| 376 | +const captureContextKeys: readonly ScopeContextProperty[] = [ |
| 377 | + 'user', |
| 378 | + 'level', |
| 379 | + 'extra', |
| 380 | + 'contexts', |
| 381 | + 'tags', |
| 382 | + 'fingerprint', |
| 383 | + 'requestSession', |
| 384 | + 'propagationContext', |
| 385 | +] as const; |
| 386 | + |
| 387 | +function hintIsScopeContext(hint: Partial<ScopeContext> | EventHint): hint is Partial<ScopeContext> { |
| 388 | + return Object.keys(hint).some(key => captureContextKeys.includes(key as ScopeContextProperty)); |
| 389 | +} |
0 commit comments