@od-oneapp/observability 2026.1.1301

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 (107) hide show
  1. package/README.md +523 -0
  2. package/dist/client-next.d.mts +20 -0
  3. package/dist/client-next.d.mts.map +1 -0
  4. package/dist/client-next.mjs +64 -0
  5. package/dist/client-next.mjs.map +1 -0
  6. package/dist/client.d.mts +11 -0
  7. package/dist/client.d.mts.map +1 -0
  8. package/dist/client.mjs +47 -0
  9. package/dist/client.mjs.map +1 -0
  10. package/dist/env.d.mts +15 -0
  11. package/dist/env.d.mts.map +1 -0
  12. package/dist/env.mjs +45 -0
  13. package/dist/env.mjs.map +1 -0
  14. package/dist/factory-DkY353r8.mjs +380 -0
  15. package/dist/factory-DkY353r8.mjs.map +1 -0
  16. package/dist/hooks-useObservability.d.mts +11 -0
  17. package/dist/hooks-useObservability.d.mts.map +1 -0
  18. package/dist/hooks-useObservability.mjs +174 -0
  19. package/dist/hooks-useObservability.mjs.map +1 -0
  20. package/dist/index-CpcdzWrF.d.mts +24 -0
  21. package/dist/index-CpcdzWrF.d.mts.map +1 -0
  22. package/dist/index.d.mts +88 -0
  23. package/dist/index.d.mts.map +1 -0
  24. package/dist/index.mjs +97 -0
  25. package/dist/index.mjs.map +1 -0
  26. package/dist/manager-BxQqOPEg.d.mts +33 -0
  27. package/dist/manager-BxQqOPEg.d.mts.map +1 -0
  28. package/dist/plugin-Bfq-o3nr.d.mts +60 -0
  29. package/dist/plugin-Bfq-o3nr.d.mts.map +1 -0
  30. package/dist/plugin-Bt-ygG1m.d.mts +254 -0
  31. package/dist/plugin-Bt-ygG1m.d.mts.map +1 -0
  32. package/dist/plugin-CLFwRERa.mjs +593 -0
  33. package/dist/plugin-CLFwRERa.mjs.map +1 -0
  34. package/dist/plugin-CP895lBx.mjs +534 -0
  35. package/dist/plugin-CP895lBx.mjs.map +1 -0
  36. package/dist/plugin-CaQxviDs.d.mts +61 -0
  37. package/dist/plugin-CaQxviDs.d.mts.map +1 -0
  38. package/dist/plugin-lPdJirTY.mjs +234 -0
  39. package/dist/plugin-lPdJirTY.mjs.map +1 -0
  40. package/dist/plugins-betterstack-env.d.mts +29 -0
  41. package/dist/plugins-betterstack-env.d.mts.map +1 -0
  42. package/dist/plugins-betterstack-env.mjs +75 -0
  43. package/dist/plugins-betterstack-env.mjs.map +1 -0
  44. package/dist/plugins-betterstack.d.mts +4 -0
  45. package/dist/plugins-betterstack.mjs +4 -0
  46. package/dist/plugins-console.d.mts +37 -0
  47. package/dist/plugins-console.d.mts.map +1 -0
  48. package/dist/plugins-console.mjs +196 -0
  49. package/dist/plugins-console.mjs.map +1 -0
  50. package/dist/plugins-sentry-env.d.mts +37 -0
  51. package/dist/plugins-sentry-env.d.mts.map +1 -0
  52. package/dist/plugins-sentry-env.mjs +79 -0
  53. package/dist/plugins-sentry-env.mjs.map +1 -0
  54. package/dist/plugins-sentry-microfrontend-env.d.mts +49 -0
  55. package/dist/plugins-sentry-microfrontend-env.d.mts.map +1 -0
  56. package/dist/plugins-sentry-microfrontend-env.mjs +80 -0
  57. package/dist/plugins-sentry-microfrontend-env.mjs.map +1 -0
  58. package/dist/plugins-sentry-microfrontend.d.mts +2 -0
  59. package/dist/plugins-sentry-microfrontend.mjs +3 -0
  60. package/dist/plugins-sentry.d.mts +5 -0
  61. package/dist/plugins-sentry.mjs +6 -0
  62. package/dist/server-edge.d.mts +15 -0
  63. package/dist/server-edge.d.mts.map +1 -0
  64. package/dist/server-edge.mjs +53 -0
  65. package/dist/server-edge.mjs.map +1 -0
  66. package/dist/server-next.d.mts +17 -0
  67. package/dist/server-next.d.mts.map +1 -0
  68. package/dist/server-next.mjs +64 -0
  69. package/dist/server-next.mjs.map +1 -0
  70. package/dist/server.d.mts +11 -0
  71. package/dist/server.d.mts.map +1 -0
  72. package/dist/server.mjs +48 -0
  73. package/dist/server.mjs.map +1 -0
  74. package/dist/utils-CuGrTcD6.d.mts +77 -0
  75. package/dist/utils-CuGrTcD6.d.mts.map +1 -0
  76. package/env.ts +67 -0
  77. package/package.json +147 -0
  78. package/src/client-next.ts +131 -0
  79. package/src/client.ts +70 -0
  80. package/src/core/index.ts +15 -0
  81. package/src/core/manager.ts +361 -0
  82. package/src/core/plugin.ts +61 -0
  83. package/src/core/types.ts +151 -0
  84. package/src/factory/builder.ts +132 -0
  85. package/src/factory/index.ts +67 -0
  86. package/src/factory/presets.ts +78 -0
  87. package/src/hooks/useObservability.ts +206 -0
  88. package/src/plugins/betterstack/env.ts +101 -0
  89. package/src/plugins/betterstack/index.ts +15 -0
  90. package/src/plugins/betterstack/plugin.ts +373 -0
  91. package/src/plugins/console/index.ts +323 -0
  92. package/src/plugins/sentry/__tests__/plugin-tracing.test.ts +511 -0
  93. package/src/plugins/sentry/env.ts +93 -0
  94. package/src/plugins/sentry/index.ts +28 -0
  95. package/src/plugins/sentry/plugin.ts +953 -0
  96. package/src/plugins/sentry/types.ts +252 -0
  97. package/src/plugins/sentry-microfrontend/env.ts +105 -0
  98. package/src/plugins/sentry-microfrontend/index.ts +12 -0
  99. package/src/plugins/sentry-microfrontend/multiplexed-transport.ts +221 -0
  100. package/src/plugins/sentry-microfrontend/plugin.ts +500 -0
  101. package/src/plugins/sentry-microfrontend/sentry-types.ts +140 -0
  102. package/src/plugins/sentry-microfrontend/types.ts +130 -0
  103. package/src/plugins/sentry-microfrontend/utils.ts +326 -0
  104. package/src/server-edge.ts +113 -0
  105. package/src/server-next.ts +114 -0
  106. package/src/server.ts +71 -0
  107. package/src/shared.ts +148 -0
@@ -0,0 +1,252 @@
1
+ /**
2
+ * @fileoverview Sentry tracing type definitions
3
+ * Sentry tracing type definitions
4
+ */
5
+
6
+ /**
7
+ * Transaction context for starting a new transaction
8
+ */
9
+ export interface TransactionContext {
10
+ name: string;
11
+ op?: string;
12
+ tags?: Record<string, string>;
13
+ data?: Record<string, any>;
14
+ startTimestamp?: number;
15
+ parentSpanId?: string;
16
+ parentSampled?: boolean;
17
+ trimEnd?: boolean;
18
+ metadata?: {
19
+ source?: 'custom' | 'route' | 'url';
20
+ [key: string]: any;
21
+ };
22
+ }
23
+
24
+ /**
25
+ * Span context for creating child spans
26
+ */
27
+ export interface SpanContext {
28
+ name: string;
29
+ op?: string;
30
+ description?: string;
31
+ tags?: Record<string, string>;
32
+ data?: Record<string, any>;
33
+ startTimestamp?: number;
34
+ status?: SpanStatus;
35
+ origin?: string;
36
+ attributes?: Record<string, any>;
37
+ }
38
+
39
+ /**
40
+ * Span status values
41
+ */
42
+ export type SpanStatus =
43
+ | 'ok'
44
+ | 'cancelled'
45
+ | 'internal_error'
46
+ | 'invalid_argument'
47
+ | 'deadline_exceeded'
48
+ | 'not_found'
49
+ | 'already_exists'
50
+ | 'permission_denied'
51
+ | 'resource_exhausted'
52
+ | 'failed_precondition'
53
+ | 'aborted'
54
+ | 'out_of_range'
55
+ | 'unimplemented'
56
+ | 'unavailable'
57
+ | 'data_loss'
58
+ | 'unauthenticated';
59
+
60
+ /**
61
+ * Transaction interface
62
+ */
63
+ export interface Transaction {
64
+ name: string;
65
+ spanId: string;
66
+ traceId: string;
67
+ startTimestamp: number;
68
+ tags: Record<string, string>;
69
+ data: Record<string, any>;
70
+
71
+ // Methods
72
+ setName(name: string): void;
73
+ setTag(key: string, value: string | number | boolean | null): void;
74
+ setData(key: string, value: any): void;
75
+ setStatus(status: SpanStatus): void;
76
+ setHttpStatus(httpStatus: number): void;
77
+ setMeasurement(name: string, value: number, unit?: string): void;
78
+ finish(endTimestamp?: number): void;
79
+ startChild(spanContext?: SpanContext): Span;
80
+ toTraceparent(): string;
81
+ toContext(): TransactionContext;
82
+ }
83
+
84
+ /**
85
+ * Span interface
86
+ */
87
+ export interface Span {
88
+ spanId: string;
89
+ traceId: string;
90
+ parentSpanId?: string;
91
+ op?: string;
92
+ description?: string;
93
+ startTimestamp: number;
94
+ tags: Record<string, string>;
95
+ data: Record<string, any>;
96
+
97
+ // Methods
98
+ setTag(key: string, value: string | number | boolean | null): void;
99
+ setData(key: string, value: any): void;
100
+ setStatus(status: SpanStatus): void;
101
+ setHttpStatus(httpStatus: number): void;
102
+ finish(endTimestamp?: number): void;
103
+ startChild(spanContext?: SpanContext): Span;
104
+ isRecording(): boolean;
105
+ }
106
+
107
+ /**
108
+ * Scope interface for managing context
109
+ */
110
+ export interface Scope {
111
+ setTag(key: string, value: string | number | boolean | null): void;
112
+ setTags(tags: Record<string, string | number | boolean | null>): void;
113
+ setContext(key: string, context: Record<string, any> | null): void;
114
+ setUser(user: Record<string, any> | null): void;
115
+ setLevel(level: SeverityLevel): void;
116
+ setFingerprint(fingerprint: string[]): void;
117
+ setRequestSession(requestSession?: RequestSession): void;
118
+ setPropagationContext(context: PropagationContext): void;
119
+ clear(): void;
120
+ addBreadcrumb(breadcrumb: SentryBreadcrumb, maxBreadcrumbs?: number): void;
121
+ clearBreadcrumbs(): void;
122
+ setSpan(span: Span | undefined): void;
123
+ getSpan(): Span | undefined;
124
+ setTransaction(transaction: Transaction | undefined): void;
125
+ getTransaction(): Transaction | undefined;
126
+ }
127
+
128
+ /**
129
+ * Hub interface for managing scopes and clients
130
+ */
131
+ export interface Hub {
132
+ getClient(): any;
133
+ getScope(): Scope;
134
+ configureScope(callback: (scope: Scope) => void): void;
135
+ pushScope(): Scope;
136
+ popScope(): boolean;
137
+ withScope(callback: (scope: Scope) => void): void;
138
+ startTransaction(context: TransactionContext, customSamplingContext?: any): Transaction;
139
+ startSpan(context: SpanContext): Span;
140
+ }
141
+
142
+ /**
143
+ * Propagation context for distributed tracing
144
+ */
145
+ export interface PropagationContext {
146
+ traceId: string;
147
+ spanId: string;
148
+ parentSpanId?: string;
149
+ sampled?: boolean;
150
+ dsc?: DynamicSamplingContext;
151
+ }
152
+
153
+ /**
154
+ * Dynamic sampling context
155
+ */
156
+ export interface DynamicSamplingContext {
157
+ trace_id: string;
158
+ public_key: string;
159
+ sample_rate?: string;
160
+ release?: string;
161
+ environment?: string;
162
+ transaction?: string;
163
+ user_segment?: string;
164
+ [key: string]: string | undefined;
165
+ }
166
+
167
+ /**
168
+ * Traceparent data extracted from headers
169
+ */
170
+ export interface TraceparentData {
171
+ traceId?: string;
172
+ parentSpanId?: string;
173
+ parentSampled?: boolean;
174
+ baggage?: Record<string, string>;
175
+ }
176
+
177
+ /**
178
+ * Sentry-specific breadcrumb interface
179
+ * Note: Use core Breadcrumb type for plugin-agnostic code.
180
+ * This type matches Sentry SDK's expected format.
181
+ */
182
+ export interface SentryBreadcrumb {
183
+ type?: string;
184
+ level?: SeverityLevel;
185
+ category?: string;
186
+ message?: string;
187
+ data?: Record<string, any>;
188
+ timestamp?: number;
189
+ }
190
+
191
+ /**
192
+ * Severity levels
193
+ */
194
+ export type SeverityLevel = 'fatal' | 'error' | 'warning' | 'log' | 'info' | 'debug';
195
+
196
+ /**
197
+ * Request session status
198
+ */
199
+ export interface RequestSession {
200
+ status?: 'ok' | 'errored' | 'crashed';
201
+ }
202
+
203
+ /**
204
+ * Sampling context for trace sampling decisions
205
+ */
206
+ export interface SamplingContext {
207
+ transactionContext: TransactionContext;
208
+ parentSampled?: boolean;
209
+ request?: any;
210
+ location?: {
211
+ href?: string;
212
+ pathname?: string;
213
+ search?: string;
214
+ hash?: string;
215
+ [key: string]: unknown;
216
+ };
217
+ }
218
+
219
+ /**
220
+ * Custom sampling function type
221
+ */
222
+ export type TraceSampler = (samplingContext: SamplingContext) => number | boolean;
223
+
224
+ /**
225
+ * Measurement units
226
+ */
227
+ export type MeasurementUnit =
228
+ | 'nanosecond'
229
+ | 'microsecond'
230
+ | 'millisecond'
231
+ | 'second'
232
+ | 'minute'
233
+ | 'hour'
234
+ | 'day'
235
+ | 'week'
236
+ | 'bit'
237
+ | 'byte'
238
+ | 'kilobyte'
239
+ | 'kibibyte'
240
+ | 'megabyte'
241
+ | 'mebibyte'
242
+ | 'gigabyte'
243
+ | 'gibibyte'
244
+ | 'terabyte'
245
+ | 'tebibyte'
246
+ | 'petabyte'
247
+ | 'pebibyte'
248
+ | 'exabyte'
249
+ | 'exbibyte'
250
+ | 'ratio'
251
+ | 'percent'
252
+ | 'none';
@@ -0,0 +1,105 @@
1
+ /**
2
+ * @fileoverview Environment configuration for Sentry Micro Frontend Plugin
3
+ * Environment configuration for Sentry Micro Frontend Plugin
4
+ */
5
+
6
+ import { logWarn } from '@repo/shared/logger';
7
+ import { createEnv } from '@t3-oss/env-core';
8
+ import { z } from 'zod/v4';
9
+
10
+ /**
11
+ * Environment configuration specific to Sentry micro frontend functionality
12
+ */
13
+ export const env = createEnv({
14
+ server: {
15
+ // Backstage app-specific DSNs for server-side
16
+ SENTRY_DSN_CMS: z.string().url().optional(),
17
+ SENTRY_DSN_WORKFLOWS: z.string().url().optional(),
18
+ SENTRY_DSN_AUTHMGMT: z.string().url().optional(),
19
+
20
+ // Micro frontend configuration
21
+ SENTRY_MICROFRONTEND_MODE: z.enum(['host', 'child', 'standalone']).optional(),
22
+ SENTRY_MICROFRONTEND_ZONE: z.string().optional(),
23
+
24
+ // Feature toggles
25
+ SENTRY_MICROFRONTEND_ENABLED: z.boolean().optional(),
26
+ SENTRY_MICROFRONTEND_MULTIPLEXED_TRANSPORT: z.boolean().optional(),
27
+ },
28
+ client: {
29
+ // Backstage app-specific DSNs for client-side
30
+ NEXT_PUBLIC_SENTRY_DSN_CMS: z.string().url().optional(),
31
+ NEXT_PUBLIC_SENTRY_DSN_WORKFLOWS: z.string().url().optional(),
32
+ NEXT_PUBLIC_SENTRY_DSN_AUTHMGMT: z.string().url().optional(),
33
+
34
+ // Client-side micro frontend configuration
35
+ NEXT_PUBLIC_SENTRY_MICROFRONTEND_MODE: z.enum(['host', 'child', 'standalone']).optional(),
36
+ NEXT_PUBLIC_SENTRY_MICROFRONTEND_ZONE: z.string().optional(),
37
+
38
+ // Feature toggles
39
+ NEXT_PUBLIC_SENTRY_MICROFRONTEND_ENABLED: z.boolean().optional(),
40
+ },
41
+ clientPrefix: 'NEXT_PUBLIC_',
42
+ runtimeEnv: process.env,
43
+ skipValidation: process.env.SKIP_ENV_VALIDATION === 'true' || process.env.NODE_ENV === 'test',
44
+ emptyStringAsUndefined: true,
45
+ onInvalidAccess: (variable: string) => {
46
+ throw new Error(
47
+ `❌ Attempted to access a server-side environment variable on the client: ${variable}`,
48
+ );
49
+ },
50
+ onValidationError: error => {
51
+ // Non-critical plugin, warn but don't throw
52
+ logWarn('Sentry Micro Frontend Plugin env validation failed', { error });
53
+ return undefined as never;
54
+ },
55
+ });
56
+
57
+ /**
58
+ * Safe environment access for non-Next.js contexts.
59
+ *
60
+ * Provides fallback environment variable access for Node.js, workers, and test environments
61
+ * where the validated env object may not be available. Returns fallback values for Sentry
62
+ * micro frontend configuration.
63
+ *
64
+ * @returns Environment object with Sentry micro frontend configuration values
65
+ */
66
+ export function safeEnv() {
67
+ if (env) {
68
+ return env;
69
+ }
70
+
71
+ // Fallback for non-Next.js contexts
72
+ return {
73
+ // Server
74
+ SENTRY_DSN_CMS: process.env.SENTRY_DSN_CMS,
75
+ SENTRY_DSN_WORKFLOWS: process.env.SENTRY_DSN_WORKFLOWS,
76
+ SENTRY_DSN_AUTHMGMT: process.env.SENTRY_DSN_AUTHMGMT,
77
+ SENTRY_MICROFRONTEND_MODE: process.env.SENTRY_MICROFRONTEND_MODE as
78
+ | 'host'
79
+ | 'child'
80
+ | 'standalone'
81
+ | undefined,
82
+ SENTRY_MICROFRONTEND_APP:
83
+ process.env.SENTRY_MICROFRONTEND_APP ?? process.env.SENTRY_MICROFRONTEND_ZONE,
84
+ SENTRY_MICROFRONTEND_ENABLED: process.env.SENTRY_MICROFRONTEND_ENABLED === 'true',
85
+ SENTRY_MICROFRONTEND_MULTIPLEXED_TRANSPORT:
86
+ process.env.SENTRY_MICROFRONTEND_MULTIPLEXED_TRANSPORT === 'true',
87
+
88
+ // Client
89
+ NEXT_PUBLIC_SENTRY_DSN_CMS: process.env.NEXT_PUBLIC_SENTRY_DSN_CMS,
90
+ NEXT_PUBLIC_SENTRY_DSN_WORKFLOWS: process.env.NEXT_PUBLIC_SENTRY_DSN_WORKFLOWS,
91
+ NEXT_PUBLIC_SENTRY_DSN_AUTHMGMT: process.env.NEXT_PUBLIC_SENTRY_DSN_AUTHMGMT,
92
+ NEXT_PUBLIC_SENTRY_MICROFRONTEND_MODE: process.env.NEXT_PUBLIC_SENTRY_MICROFRONTEND_MODE as
93
+ | 'host'
94
+ | 'child'
95
+ | 'standalone'
96
+ | undefined,
97
+ NEXT_PUBLIC_SENTRY_MICROFRONTEND_APP:
98
+ process.env.NEXT_PUBLIC_SENTRY_MICROFRONTEND_APP ??
99
+ process.env.NEXT_PUBLIC_SENTRY_MICROFRONTEND_ZONE,
100
+ NEXT_PUBLIC_SENTRY_MICROFRONTEND_ENABLED:
101
+ process.env.NEXT_PUBLIC_SENTRY_MICROFRONTEND_ENABLED === 'true',
102
+ };
103
+ }
104
+
105
+ export type Env = typeof env;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @fileoverview Sentry Micro Frontend Plugin
3
+ * Sentry Micro Frontend Plugin
4
+ *
5
+ * Provides specialized Sentry integration for micro frontend architectures.
6
+ * Supports both host and child modes with automatic parent detection.
7
+ */
8
+
9
+ export { createMultiplexedTransport } from './multiplexed-transport';
10
+ export { SentryMicroFrontendPlugin, createSentryMicroFrontendPlugin } from './plugin';
11
+ export type { BackstageAppConfig, MicroFrontendMode, SentryMicroFrontendConfig } from './types';
12
+ export { createBackstageScope, detectCurrentBackstageApp, isHostEnvironment } from './utils';
@@ -0,0 +1,221 @@
1
+ /**
2
+ * @fileoverview Multiplexed transport utilities for Sentry Micro Frontend Plugin
3
+ * Multiplexed transport utilities for Sentry Micro Frontend Plugin
4
+ */
5
+
6
+ import { logWarn } from '@repo/shared/logs';
7
+
8
+ import type { BackstageAppConfig } from './types';
9
+
10
+ /**
11
+ * Create a multiplexed transport that routes events to different Sentry projects.
12
+ *
13
+ * Creates a Sentry transport that routes events to different DSNs based on the
14
+ * backstageApp information in the event. Supports edge runtime environments.
15
+ *
16
+ * @param backstage - Array of backstage app configurations with DSNs
17
+ * @param fallbackDsn - Optional fallback DSN if no backstageApp match is found
18
+ * @param sentryClient - Optional Sentry client instance (defaults to global Sentry)
19
+ * @returns Multiplexed transport instance, or undefined if Sentry is not available
20
+ */
21
+
22
+ export function createMultiplexedTransport(
23
+ backstage: BackstageAppConfig[],
24
+ fallbackDsn?: string,
25
+
26
+ sentryClient?: any,
27
+ ): any {
28
+ // Import Sentry dynamically or use provided client (edge runtime compatible)
29
+ const Sentry =
30
+ sentryClient !== undefined
31
+ ? sentryClient
32
+ : typeof globalThis !== 'undefined'
33
+ ? (globalThis as any).Sentry
34
+ : null;
35
+
36
+ if (!Sentry?.makeMultiplexedTransport || !Sentry.makeFetchTransport) {
37
+ logWarn('Sentry multiplexed transport not available');
38
+ return undefined;
39
+ }
40
+
41
+ return Sentry.makeMultiplexedTransport(Sentry.makeFetchTransport, (args: any) => {
42
+ const event = args.getEvent();
43
+
44
+ if (!event) {
45
+ return [];
46
+ }
47
+
48
+ // Extract backstageApp information from various sources
49
+ const backstageApp =
50
+ event.tags?.backstageApp ??
51
+ event.extra?.backstageApp ??
52
+ event.contexts?.microFrontend?.backstageApp;
53
+
54
+ if (backstageApp) {
55
+ // Find backstageApp configuration
56
+ const backstageAppConfig = backstage.find(z => z.name === backstageApp);
57
+
58
+ if (backstageAppConfig?.dsn) {
59
+ // Route to backstageApp-specific DSN
60
+
61
+ const envelope: any[] = [
62
+ {
63
+ dsn: backstageAppConfig.dsn,
64
+ release: backstageAppConfig.release ?? event.release,
65
+ },
66
+ ];
67
+
68
+ // Add backstageApp-specific tags if configured
69
+ if (backstageAppConfig.tags && event.tags) {
70
+ Object.assign(event.tags, backstageAppConfig.tags);
71
+ }
72
+
73
+ return envelope;
74
+ }
75
+ }
76
+
77
+ // Use fallback DSN if provided
78
+ if (fallbackDsn) {
79
+ return [{ dsn: fallbackDsn }];
80
+ }
81
+
82
+ // Let the default DSN handle it
83
+ return [];
84
+ });
85
+ }
86
+
87
+ /**
88
+ * Create a transport matcher function for more complex routing logic.
89
+ *
90
+ * Creates a matcher function that can be used with Sentry's multiplexed transport
91
+ * to route events based on custom logic, including stack frame analysis.
92
+ *
93
+ * @param backstage - Array of backstage app configurations
94
+ * @param customMatcher - Optional custom matcher function to extract backstageApp from event
95
+ * @returns Matcher function that returns an array of transport configurations
96
+ */
97
+ export function createTransportMatcher(
98
+ backstage: BackstageAppConfig[],
99
+
100
+ customMatcher?: (event: any) => string | undefined,
101
+ ): (args: any) => any[] {
102
+ return (args: any) => {
103
+ const event = args.getEvent();
104
+
105
+ if (!event) {
106
+ return [];
107
+ }
108
+
109
+ // Use custom matcher if provided
110
+ let backstageApp: string | undefined;
111
+ if (customMatcher) {
112
+ backstageApp = customMatcher(event);
113
+ }
114
+
115
+ // Fall back to default backstageApp detection
116
+ backstageApp ??=
117
+ event.tags?.backstageApp ??
118
+ event.extra?.backstageApp ??
119
+ event.contexts?.microFrontend?.backstageApp;
120
+
121
+ // Try to detect backstageApp from stack frames
122
+ if (!backstageApp && event.exception?.values?.[0]?.stacktrace?.frames) {
123
+ const { frames } = event.exception.values[0].stacktrace;
124
+ for (const frame of frames) {
125
+ if (frame.filename) {
126
+ // Check if filename contains backstageApp hints
127
+ if (frame.filename.includes('/cms/')) {
128
+ backstageApp = 'cms';
129
+ break;
130
+ } else if (frame.filename.includes('/workflows/')) {
131
+ backstageApp = 'workflows';
132
+ break;
133
+ } else if (frame.filename.includes('/authmgmt/')) {
134
+ backstageApp = 'authmgmt';
135
+ break;
136
+ }
137
+ }
138
+ }
139
+ }
140
+
141
+ if (backstageApp) {
142
+ const backstageAppConfig = backstage.find(z => z.name === backstageApp);
143
+ if (backstageAppConfig?.dsn) {
144
+ return [
145
+ {
146
+ dsn: backstageAppConfig.dsn,
147
+ release: backstageAppConfig.release,
148
+ },
149
+ ];
150
+ }
151
+ }
152
+
153
+ return [];
154
+ };
155
+ }
156
+
157
+ /**
158
+ * Enhance an event with Backstage app information before sending.
159
+ *
160
+ * Adds backstageApp tags, context, and extra data to a Sentry event.
161
+ * This ensures events are properly tagged for micro frontend routing.
162
+ *
163
+ * @param event - Sentry event to enhance
164
+ * @param backstageApp - Name of the backstage app
165
+ * @param additionalContext - Optional additional context to add
166
+ * @returns Enhanced event with backstageApp information
167
+ */
168
+
169
+ export function enhanceEventWithBackstageApp(
170
+ event: any,
171
+ backstageApp: string,
172
+
173
+ additionalContext?: Record<string, any>,
174
+ ): any {
175
+ // Add backstageApp tag
176
+ event.tags ??= {};
177
+ event.tags.backstageApp = backstageApp;
178
+ event.tags.microFrontend = true;
179
+
180
+ // Add backstageApp context
181
+ event.contexts ??= {};
182
+ event.contexts.microFrontend = {
183
+ backstageApp,
184
+ timestamp: new Date().toISOString(),
185
+ ...additionalContext,
186
+ };
187
+
188
+ // Add backstageApp to extra for backward compatibility
189
+ event.extra ??= {};
190
+ event.extra.backstageApp = backstageApp;
191
+
192
+ return event;
193
+ }
194
+
195
+ /**
196
+ * Create a beforeSend hook that adds backstageApp information.
197
+ *
198
+ * Creates a Sentry beforeSend hook that enhances events with backstageApp information
199
+ * before they are sent. Can wrap an existing beforeSend hook.
200
+ *
201
+ * @param backstageApp - Name of the backstage app
202
+ * @param originalBeforeSend - Optional original beforeSend hook to wrap
203
+ * @returns beforeSend hook function
204
+ */
205
+ export function createBackstageBeforeSend(
206
+ backstageApp: string,
207
+
208
+ originalBeforeSend?: (event: any, hint: any) => any,
209
+ ): (event: any, hint: any) => any {
210
+ return (event: any, hint: any) => {
211
+ // Enhance with backstageApp information
212
+ enhanceEventWithBackstageApp(event, backstageApp);
213
+
214
+ // Call original beforeSend if provided
215
+ if (originalBeforeSend) {
216
+ return originalBeforeSend(event, hint);
217
+ }
218
+
219
+ return event;
220
+ };
221
+ }