@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,130 @@
1
+ /**
2
+ * @fileoverview Types for Sentry Micro Frontend Plugin
3
+ * Types for Sentry Micro Frontend Plugin
4
+ */
5
+
6
+ import type { SentryPluginConfig } from '../sentry';
7
+
8
+ /**
9
+ * Configuration for a specific micro frontend backstageApp
10
+ */
11
+ export interface BackstageAppConfig {
12
+ /**
13
+ * Name of the backstageApp (e.g., 'cms', 'workflows', 'authmgmt')
14
+ */
15
+ name: string;
16
+
17
+ /**
18
+ * Sentry DSN for this backstageApp (optional, uses default if not provided)
19
+ */
20
+ dsn?: string;
21
+
22
+ /**
23
+ * Release version for this backstageApp
24
+ */
25
+ release?: string;
26
+
27
+ /**
28
+ * Additional tags to apply to events from this backstageApp
29
+ */
30
+ tags?: Record<string, string>;
31
+
32
+ /**
33
+ * Path patterns that identify this backstageApp
34
+ */
35
+ pathPatterns?: (string | RegExp)[];
36
+ }
37
+
38
+ /**
39
+ * Mode of operation for the micro frontend
40
+ */
41
+ export type MicroFrontendMode = 'host' | 'child' | 'standalone';
42
+
43
+ /**
44
+ * Configuration for Sentry Micro Frontend Plugin
45
+ */
46
+ export interface SentryMicroFrontendConfig extends Omit<SentryPluginConfig, 'transport'> {
47
+ /**
48
+ * Whether this is the host application that manages routing
49
+ */
50
+ isHost?: boolean;
51
+
52
+ /**
53
+ * The backstageApp identifier for this micro frontend (e.g., 'cms', 'workflows')
54
+ */
55
+ backstageApp?: string;
56
+
57
+ /**
58
+ * Configuration for all backstage (only used in host mode)
59
+ */
60
+ backstageApps?: BackstageAppConfig[];
61
+
62
+ /**
63
+ * Whether to detect and use parent Sentry instance
64
+ * @default true
65
+ */
66
+ detectParent?: boolean;
67
+
68
+ /**
69
+ * Custom function to detect the current backstageApp
70
+ */
71
+ backstageAppDetector?: () => string | undefined;
72
+
73
+ /**
74
+ * Whether to add backstageApp information to all events
75
+ * @default true
76
+ */
77
+ addBackstageContext?: boolean;
78
+
79
+ /**
80
+ * Fallback DSN to use if backstageApp-specific DSN is not found
81
+ */
82
+ fallbackDsn?: string;
83
+
84
+ /**
85
+ * Whether to use multiplexed transport in host mode
86
+ * @default true
87
+ */
88
+ useMultiplexedTransport?: boolean;
89
+
90
+ /**
91
+ * Global tags to apply to all events
92
+ */
93
+ globalTags?: Record<string, string>;
94
+
95
+ /**
96
+ * Whether to prevent initialization if parent exists
97
+ * @default true
98
+ */
99
+ preventDuplicateInit?: boolean;
100
+
101
+ /**
102
+ * Integration configuration to be handled during initialization
103
+ */
104
+ integrationConfig?: {
105
+ nodeProfilingIntegration?: boolean;
106
+ captureConsoleIntegration?: boolean;
107
+ vercelAIIntegration?: boolean;
108
+ };
109
+
110
+ /**
111
+ * Sentry experimental features
112
+ */
113
+
114
+ _experiments?: {
115
+ enableLogs?: boolean;
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Extended window interface for Sentry
121
+ */
122
+ declare global {
123
+ interface Window {
124
+ Sentry?: any;
125
+
126
+ __SENTRY_MICRO_FRONTEND_HOST__?: boolean;
127
+
128
+ __SENTRY_MICRO_FRONTEND_APP__?: string;
129
+ }
130
+ }
@@ -0,0 +1,326 @@
1
+ /**
2
+ * @fileoverview Utility functions for Sentry Micro Frontend Plugin
3
+ * Utility functions for Sentry Micro Frontend Plugin
4
+ */
5
+
6
+ import { logError } from '@repo/shared/logs';
7
+
8
+ import type { BackstageAppConfig } from './types';
9
+
10
+ /**
11
+ * Detect the current micro frontend backstageApp based on URL path.
12
+ *
13
+ * Analyzes the current URL pathname to determine which micro frontend application
14
+ * is active. Supports custom path patterns and falls back to default patterns.
15
+ *
16
+ * @param customPatterns - Optional array of custom backstage app configurations with path patterns
17
+ * @returns Detected backstage app name, or undefined if not detected
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const app = detectCurrentBackstageApp([
22
+ * { name: 'admin', pathPatterns: ['/admin', '/manage'] },
23
+ * { name: 'dashboard', pathPatterns: [/^\/dashboard/] }
24
+ * ]);
25
+ * // Returns: 'admin' if pathname starts with '/admin' or '/manage'
26
+ * ```
27
+ */
28
+ export function detectCurrentBackstageApp(
29
+ customPatterns?: BackstageAppConfig[],
30
+ ): string | undefined {
31
+ // Edge runtime compatible check
32
+ if (
33
+ typeof globalThis === 'undefined' ||
34
+ !(globalThis as { location?: { pathname?: string; href?: string } }).location
35
+ ) {
36
+ // Server-side or edge: check Next.js basePath or environment variables
37
+ // Note: process.env is not available in edge runtime
38
+ if (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_BASE_PATH) {
39
+ return process.env.NEXT_PUBLIC_BASE_PATH.replace('/', '');
40
+ }
41
+ return undefined;
42
+ }
43
+
44
+ const path = (globalThis as { location?: { pathname?: string } }).location?.pathname;
45
+ if (!path) {
46
+ return undefined;
47
+ }
48
+
49
+ // Check custom patterns first
50
+ if (customPatterns) {
51
+ for (const config of customPatterns) {
52
+ if (config.pathPatterns) {
53
+ for (const pattern of config.pathPatterns) {
54
+ if (typeof pattern === 'string' && path.startsWith(pattern)) {
55
+ return config.name;
56
+ } else if (pattern instanceof RegExp && pattern.test(path)) {
57
+ return config.name;
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
+ // Default patterns for common microfrontend zones
65
+ if (path.startsWith('/admin')) return 'admin';
66
+ if (path.startsWith('/dashboard')) return 'dashboard';
67
+ if (path.startsWith('/settings')) return 'settings';
68
+
69
+ // Check for backstageApp in global object (set by host)
70
+
71
+ if ((globalThis as any).__SENTRY_MICRO_FRONTEND_APP__) {
72
+ return (globalThis as any).__SENTRY_MICRO_FRONTEND_APP__;
73
+ }
74
+
75
+ if ((globalThis as any).__SENTRY_MICRO_FRONTEND_ZONE__) {
76
+ // Backwards compatibility with legacy zone naming
77
+
78
+ return (globalThis as any).__SENTRY_MICRO_FRONTEND_ZONE__;
79
+ }
80
+
81
+ return 'main';
82
+ }
83
+
84
+ /**
85
+ * Check if running in a host environment.
86
+ *
87
+ * Determines whether the current application is acting as a host for micro frontends
88
+ * or as a child micro frontend. Checks for explicit host markers and Sentry initialization state.
89
+ *
90
+ * @returns True if running as host, false if child or standalone
91
+ */
92
+ export function isHostEnvironment(): boolean {
93
+ if (typeof globalThis === 'undefined') {
94
+ return false;
95
+ }
96
+
97
+ // Check if explicitly marked as host
98
+
99
+ if ((globalThis as any).__SENTRY_MICRO_FRONTEND_HOST__) {
100
+ return true;
101
+ }
102
+
103
+ // Check if Sentry is already initialized (likely means we're a child)
104
+ if (
105
+ (globalThis as any).Sentry &&
106
+ typeof (globalThis as any).Sentry.getCurrentHub === 'function'
107
+ ) {
108
+ return false;
109
+ }
110
+
111
+ // Default to false (safer to assume child mode)
112
+ return false;
113
+ }
114
+
115
+ /**
116
+ * Create a Sentry scope with backstageApp-specific context.
117
+ *
118
+ * Creates a new Sentry scope configured with micro frontend tags and context.
119
+ * Includes backstageApp tag, microFrontend context, and any additional tags provided.
120
+ *
121
+ * @param backstageApp - Name of the backstage app
122
+ * @param additionalTags - Optional additional tags to set on the scope
123
+ * @returns Sentry scope instance, or null if Sentry is not available
124
+ */
125
+
126
+ export function createBackstageScope(
127
+ backstageApp: string,
128
+ additionalTags?: Record<string, string>,
129
+ ): unknown {
130
+ if (typeof globalThis === 'undefined' || !(globalThis as any).Sentry) {
131
+ return null;
132
+ }
133
+
134
+ const { Sentry } = globalThis as any;
135
+
136
+ // Check if Scope constructor is available
137
+ if (!Sentry.Scope || typeof Sentry.Scope !== 'function') {
138
+ return null;
139
+ }
140
+
141
+ try {
142
+ const scope = new Sentry.Scope();
143
+
144
+ // Set backstageApp tag
145
+ scope.setTag('backstageApp', backstageApp);
146
+ scope.setTag('microFrontend', true);
147
+
148
+ // Set backstageApp context
149
+ scope.setContext('microFrontend', {
150
+ backstageApp,
151
+
152
+ isHost: (globalThis as any).__SENTRY_MICRO_FRONTEND_HOST__ ?? false,
153
+ url: (globalThis as { location?: { href?: string } }).location?.href ?? undefined,
154
+ });
155
+
156
+ // Add additional tags if provided
157
+ if (additionalTags) {
158
+ Object.entries(additionalTags).forEach(([key, value]) => {
159
+ scope.setTag(key, value);
160
+ });
161
+ }
162
+
163
+ return scope;
164
+ } catch (error) {
165
+ logError('[SentryMicroFrontendPlugin] Failed to create Backstage scope', { error });
166
+ return null;
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Check if Sentry is already initialized by a parent application.
172
+ *
173
+ * Verifies whether a parent Sentry instance exists in the global scope.
174
+ * Used to determine if the current micro frontend should use the parent's Sentry
175
+ * or initialize its own instance.
176
+ *
177
+ * @returns True if parent Sentry is available, false otherwise
178
+ */
179
+ export function hasParentSentry(): boolean {
180
+ if (typeof globalThis === 'undefined') {
181
+ return false;
182
+ }
183
+
184
+ // Check for Sentry global
185
+
186
+ const { Sentry } = globalThis as any;
187
+ if (!Sentry) {
188
+ return false;
189
+ }
190
+
191
+ // Verify it's a real Sentry instance with expected methods
192
+ const requiredMethods = ['captureException', 'captureMessage', 'withScope'];
193
+ return requiredMethods.every(method => typeof Sentry[method] === 'function');
194
+ }
195
+
196
+ /**
197
+ * Get the parent Sentry instance if available.
198
+ *
199
+ * Returns the global Sentry instance if it exists and has been initialized
200
+ * by a parent application. Returns null if no parent Sentry is available.
201
+ *
202
+ * @returns Parent Sentry SDK instance, or null if not available
203
+ */
204
+
205
+ export function getParentSentry(): any {
206
+ if (hasParentSentry()) {
207
+ return (globalThis as any).Sentry;
208
+ }
209
+ return null;
210
+ }
211
+
212
+ /**
213
+ * Mark the current environment as a host.
214
+ *
215
+ * Sets global flags indicating that this application is acting as a host
216
+ * for micro frontends. This affects how Sentry events are routed and tagged.
217
+ *
218
+ * @param backstageApp - Optional backstage app name to set
219
+ */
220
+ export function markAsHost(backstageApp?: string): void {
221
+ if (typeof globalThis !== 'undefined') {
222
+ (globalThis as any).__SENTRY_MICRO_FRONTEND_HOST__ = true;
223
+ if (backstageApp) {
224
+ (globalThis as any).__SENTRY_MICRO_FRONTEND_APP__ = backstageApp;
225
+
226
+ (globalThis as any).__SENTRY_MICRO_FRONTEND_ZONE__ = backstageApp;
227
+ }
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Get backstageApp-specific configuration.
233
+ *
234
+ * Finds the configuration for a specific backstage app from the provided array.
235
+ *
236
+ * @param backstageApp - Name of the backstage app to find
237
+ * @param backstage - Array of backstage app configurations
238
+ * @returns Configuration for the specified app, or undefined if not found
239
+ */
240
+ export function getBackstageAppConfig(
241
+ backstageApp: string,
242
+ backstage: BackstageAppConfig[],
243
+ ): BackstageAppConfig | undefined {
244
+ return backstage.find(app => app.name === backstageApp);
245
+ }
246
+
247
+ /**
248
+ * Ensure only one Sentry initialization happens (thread-safe).
249
+ *
250
+ * Prevents multiple Sentry initializations using global state tracking.
251
+ * Uses timestamps and unique IDs to detect concurrent initialization attempts.
252
+ * Throws an error if Sentry is already initialized or if concurrent initialization is detected.
253
+ *
254
+ * @throws Error if Sentry is already initialized or concurrent initialization detected
255
+ */
256
+ export function ensureSingleInit(): void {
257
+ // Use a more robust initialization check with timestamps
258
+ const initKey = '__SENTRY_INIT_STATE__';
259
+ const now = Date.now();
260
+
261
+ if (typeof globalThis !== 'undefined') {
262
+ const globalState = (globalThis as any)[initKey];
263
+
264
+ // Check if already initialized or currently initializing
265
+ if (globalState) {
266
+ const { status, timestamp, id } = globalState;
267
+
268
+ // If initialized, throw error
269
+ if (status === 'initialized') {
270
+ throw new Error(
271
+ 'Sentry has already been initialized! Use hasParentSentry() to check before initializing.',
272
+ );
273
+ }
274
+
275
+ // If initializing and it's been less than 5 seconds, assume another process is initializing
276
+ if (status === 'initializing' && now - timestamp < 5000) {
277
+ throw new Error(
278
+ `Sentry initialization already in progress (started ${now - timestamp}ms ago by ${id})`,
279
+ );
280
+ }
281
+ }
282
+
283
+ // Mark as initializing with timestamp and unique ID
284
+ const initId = `${now}-${Math.random().toString(36).substr(2, 9)}`;
285
+
286
+ (globalThis as any)[initKey] = {
287
+ status: 'initializing',
288
+ timestamp: now,
289
+ id: initId,
290
+ };
291
+
292
+ // Small delay to allow for race condition detection
293
+ // In a real thread-safe implementation, this would use proper locking
294
+
295
+ const checkState = (globalThis as any)[initKey];
296
+ if (checkState.id !== initId) {
297
+ throw new Error('Concurrent Sentry initialization detected');
298
+ }
299
+
300
+ // Mark as initialized
301
+
302
+ (globalThis as any)[initKey] = {
303
+ status: 'initialized',
304
+ timestamp: now,
305
+ id: initId,
306
+ };
307
+
308
+ // Also set legacy flag for backward compatibility
309
+
310
+ (globalThis as any).__SENTRY_INITIALIZED__ = true;
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Reset initialization flag (mainly for testing).
316
+ *
317
+ * Clears the global Sentry initialization state. Useful for testing scenarios
318
+ * where you need to test initialization multiple times.
319
+ */
320
+ export function resetInitFlag(): void {
321
+ if (typeof globalThis !== 'undefined') {
322
+ delete (globalThis as any).__SENTRY_INITIALIZED__;
323
+
324
+ delete (globalThis as any).__SENTRY_INIT_STATE__;
325
+ }
326
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * @fileoverview Next.js edge runtime observability export
3
+ * Next.js edge runtime observability export
4
+ * Limited to fetch-based implementations only
5
+ */
6
+
7
+ import { env } from '../env';
8
+
9
+ import { ObservabilityBuilder } from './factory/builder';
10
+ import { createBetterStackPlugin } from './plugins/betterstack';
11
+ import { env as betterStackEnv } from './plugins/betterstack/env';
12
+ import { createConsolePlugin } from './plugins/console';
13
+ import { shouldEnableConsole } from './shared';
14
+
15
+ import type { ObservabilityManager } from './core/manager';
16
+
17
+ /**
18
+ * Create auto-configured observability for Next.js edge runtime
19
+ * Note: Sentry is not included here as it requires Node.js APIs
20
+ */
21
+ export async function createEdgeObservability() {
22
+ const builder = ObservabilityBuilder.create().withAutoInitialize(false); // Manual init in edge runtime
23
+
24
+ // Console logging control using shared utility
25
+ const enableConsole = shouldEnableConsole(
26
+ env.NEXT_PUBLIC_NODE_ENV,
27
+ env.NEXT_PUBLIC_OBSERVABILITY_CONSOLE_ENABLED,
28
+ env.NEXT_PUBLIC_OBSERVABILITY_DEBUG,
29
+ );
30
+
31
+ // Always add console plugin, control via enabled flag
32
+ builder.withPlugin(
33
+ createConsolePlugin({
34
+ prefix: '[Next.js Edge]',
35
+ enabled: enableConsole,
36
+ }),
37
+ );
38
+
39
+ // Auto-activate Better Stack if source token is provided (uses fetch API)
40
+ const betterStackToken =
41
+ betterStackEnv.BETTER_STACK_SOURCE_TOKEN ??
42
+ betterStackEnv.BETTERSTACK_SOURCE_TOKEN ??
43
+ betterStackEnv.LOGTAIL_SOURCE_TOKEN;
44
+
45
+ if (betterStackToken) {
46
+ builder.withPlugin(
47
+ createBetterStackPlugin({
48
+ sourceToken: betterStackToken,
49
+ }),
50
+ );
51
+ }
52
+
53
+ // Note: Sentry requires Node.js APIs and isn't available in edge runtime
54
+ // Better Stack is the recommended solution for edge runtime logging
55
+
56
+ return builder.build();
57
+ }
58
+
59
+ // Lazy initialization for the observability instance using promise-based singleton
60
+ let initializationPromise: Promise<ObservabilityManager> | null = null;
61
+
62
+ /**
63
+ * Get or create the observability instance
64
+ * Uses promise-based singleton pattern to prevent race conditions in concurrent scenarios
65
+ * @returns Promise resolving to the ObservabilityManager instance
66
+ */
67
+ export async function getObservability(): Promise<ObservabilityManager> {
68
+ if (!initializationPromise) {
69
+ initializationPromise = (async () => {
70
+ try {
71
+ return await createEdgeObservability();
72
+ } catch (error) {
73
+ // Reset cached promise on failure so callers can retry
74
+ initializationPromise = null;
75
+ throw error;
76
+ }
77
+ })();
78
+ }
79
+ return initializationPromise;
80
+ }
81
+
82
+ // Export types and utilities
83
+ export * from './core/types';
84
+ export type {
85
+ ObservabilityPlugin,
86
+ ObservabilityServerPlugin,
87
+ PluginFactory,
88
+ PluginLifecycle,
89
+ } from './core/plugin';
90
+ export { createObservability } from './factory';
91
+ export { ObservabilityBuilder } from './factory/builder';
92
+
93
+ // Re-export edge-compatible plugins only (explicit to avoid duplicate ObservabilityPlugin/ObservabilityServerPlugin)
94
+ export { BetterStackPlugin, createBetterStackPlugin, env, safeEnv } from './plugins/betterstack';
95
+ export type { BetterStackPluginConfig } from './plugins/betterstack';
96
+ export {
97
+ ConsolePlugin,
98
+ ConsoleServerPlugin,
99
+ createConsolePlugin,
100
+ createConsoleServerPlugin,
101
+ } from './plugins/console';
102
+ export type { ConsolePluginConfig } from './plugins/console';
103
+
104
+ // Legacy function for backward compatibility (no-op)
105
+ /**
106
+ * @deprecated Configuration now happens through the observability system
107
+ */
108
+ export const configureLogger = () => {
109
+ // No-op: Configuration now happens through the observability system
110
+ };
111
+
112
+ // Re-export type
113
+ export type LogContext = Record<string, unknown>;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @fileoverview Next.js server-specific observability export
3
+ * Next.js server-specific observability export
4
+ */
5
+
6
+ import { env } from '../env';
7
+
8
+ import { ObservabilityBuilder } from './factory/builder';
9
+ import { createBetterStackPlugin } from './plugins/betterstack';
10
+ import { env as betterStackEnv } from './plugins/betterstack/env';
11
+ import { createConsoleServerPlugin } from './plugins/console';
12
+ import { createSentryPlugin } from './plugins/sentry';
13
+ import { env as sentryEnv } from './plugins/sentry/env';
14
+ import { shouldEnableConsole } from './shared';
15
+
16
+ import type { ObservabilityManager } from './core/manager';
17
+
18
+ /**
19
+ * Create auto-configured observability for Next.js server
20
+ */
21
+ export async function createServerObservability() {
22
+ const builder = ObservabilityBuilder.create();
23
+
24
+ // Console logging control using shared utility
25
+ const enableConsole = shouldEnableConsole(
26
+ env.NEXT_PUBLIC_NODE_ENV,
27
+ env.NEXT_PUBLIC_OBSERVABILITY_CONSOLE_ENABLED,
28
+ env.NEXT_PUBLIC_OBSERVABILITY_DEBUG,
29
+ );
30
+
31
+ // Always add console plugin, control via enabled flag
32
+ builder.withPlugin(
33
+ createConsoleServerPlugin({
34
+ prefix: '[Next.js Server]',
35
+ enabled: enableConsole,
36
+ }),
37
+ );
38
+
39
+ // Auto-activate Sentry if DSN is provided
40
+ // Only use client-accessible DSN for auto-detection to avoid server/client boundary issues
41
+ const sentryDSN = sentryEnv.NEXT_PUBLIC_SENTRY_DSN;
42
+ if (sentryDSN) {
43
+ const sentry = createSentryPlugin({
44
+ dsn: sentryDSN,
45
+ });
46
+ await sentry.initialize();
47
+ builder.withPlugin(sentry);
48
+ }
49
+
50
+ // Auto-activate Better Stack if source token is provided
51
+ const betterStackToken =
52
+ betterStackEnv.BETTER_STACK_SOURCE_TOKEN ??
53
+ betterStackEnv.BETTERSTACK_SOURCE_TOKEN ??
54
+ betterStackEnv.LOGTAIL_SOURCE_TOKEN;
55
+
56
+ if (betterStackToken) {
57
+ const betterstack = createBetterStackPlugin({
58
+ sourceToken: betterStackToken,
59
+ });
60
+ await betterstack.initialize();
61
+ builder.withPlugin(betterstack);
62
+ }
63
+
64
+ return builder.build();
65
+ }
66
+
67
+ // Lazy initialization for the observability instance using promise-based singleton
68
+ let initializationPromise: Promise<ObservabilityManager> | null = null;
69
+
70
+ /**
71
+ * Get or create the observability instance
72
+ * Uses promise-based singleton pattern to prevent race conditions in concurrent scenarios
73
+ * @returns Promise resolving to the ObservabilityManager instance
74
+ */
75
+ export async function getObservability(): Promise<ObservabilityManager> {
76
+ if (!initializationPromise) {
77
+ initializationPromise = (async () => {
78
+ try {
79
+ return await createServerObservability();
80
+ } catch (error) {
81
+ // Reset cached promise on failure so callers can retry
82
+ initializationPromise = null;
83
+ throw error;
84
+ }
85
+ })();
86
+ }
87
+ return initializationPromise;
88
+ }
89
+
90
+ // Export types and utilities
91
+ export * from './core/types';
92
+ export { createObservability } from './factory';
93
+ export { ObservabilityBuilder } from './factory/builder';
94
+ export { ObservabilityManager } from "./factory";
95
+
96
+ // Re-export plugins for direct access (excluding conflicting exports)
97
+ export { BetterStackPlugin } from './plugins/betterstack';
98
+ export { ConsolePlugin } from './plugins/console';
99
+ export { SentryPlugin } from './plugins/sentry';
100
+ export { SentryMicroFrontendPlugin } from './plugins/sentry-microfrontend';
101
+
102
+ // Re-export plugin-specific types with aliases to avoid conflicts
103
+ // Removed for Rollup compatibility
104
+ // Removed for Rollup compatibility
105
+
106
+ // Legacy function for backward compatibility (no-op)
107
+ /**
108
+ * @deprecated Configuration now happens through the observability system
109
+ */
110
+ export const configureLogger = (): void => {
111
+ // No-op: Configuration now happens through the observability system
112
+ };
113
+
114
+ // Re-export type - removed for Rollup compatibility