@atrim/instrument-web 0.5.0-c05e3a1-20251119131241 → 0.5.0-cda80ad-20251119134818

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atrim/instrument-web",
3
- "version": "0.5.0-c05e3a1-20251119131241",
3
+ "version": "0.5.0-cda80ad-20251119134818",
4
4
  "description": "OpenTelemetry instrumentation for browsers with centralized YAML configuration",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,8 +1,8 @@
1
- import { Effect, Layer } from 'effect';
2
1
  import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
3
- import { InstrumentationConfig, InitializationError, ConfigLoader } from '@atrim/instrument-core';
2
+ import { InstrumentationConfig, ConfigLoader } from '@atrim/instrument-core';
4
3
  export { ConfigError, ConfigFileError, ConfigUrlError, ConfigValidationError, ExportError, InitializationError, InstrumentationConfig, PatternConfig, PatternMatcher, ShutdownError, clearPatternMatcher, getPatternMatcher, initializePatternMatcher, shouldInstrumentSpan } from '@atrim/instrument-core';
5
4
  import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
5
+ import { Layer } from 'effect';
6
6
  import { SpanProcessor, ReadableSpan } from '@opentelemetry/sdk-trace-base';
7
7
  import { Context, Span } from '@opentelemetry/api';
8
8
 
@@ -70,6 +70,31 @@ interface SdkInitializationOptions {
70
70
  * @default true
71
71
  */
72
72
  enableXhr?: boolean;
73
+ /**
74
+ * Control trace context propagation for cross-origin requests
75
+ *
76
+ * Determines which cross-origin requests receive W3C Trace Context headers
77
+ * (traceparent, tracestate). Note: Backends must allow these headers in CORS
78
+ * configuration or requests will fail with CORS errors.
79
+ *
80
+ * - 'all': Propagate to all cross-origin requests (may cause CORS errors)
81
+ * - 'none': Never propagate trace headers
82
+ * - 'same-origin': Only propagate to same-origin requests (default)
83
+ * - Array of URL patterns: Custom propagation patterns (regex strings)
84
+ *
85
+ * @default 'same-origin'
86
+ *
87
+ * @example Propagate to specific API domains
88
+ * ```typescript
89
+ * propagateTraceContext: ['^https://api\\.myapp\\.com', '^http://localhost:300[0-9]']
90
+ * ```
91
+ *
92
+ * @example Disable propagation (for debugging CORS issues)
93
+ * ```typescript
94
+ * propagateTraceContext: 'none'
95
+ * ```
96
+ */
97
+ propagateTraceContext?: 'all' | 'none' | 'same-origin' | string[];
73
98
  }
74
99
  /**
75
100
  * Get the current SDK instance
@@ -103,47 +128,37 @@ declare function resetSdk(): void;
103
128
  * Call this function once at application startup, before any other code runs.
104
129
  *
105
130
  * @param options - Initialization options
106
- * @returns Effect that yields WebTracerProvider instance
131
+ * @returns WebTracerProvider instance
132
+ * @throws {Error} If initialization fails
107
133
  *
108
134
  * @example
109
135
  * ```typescript
110
- * import { Effect } from 'effect'
111
136
  * import { initializeInstrumentation } from '@atrim/instrument-web'
112
137
  *
113
- * const program = initializeInstrumentation({
138
+ * await initializeInstrumentation({
114
139
  * serviceName: 'my-app',
115
140
  * otlpEndpoint: 'http://localhost:4318/v1/traces'
116
141
  * })
117
- *
118
- * await Effect.runPromise(program)
119
142
  * ```
120
143
  *
121
144
  * @example With pattern-based filtering
122
145
  * ```typescript
123
- * const program = initializeInstrumentation({
146
+ * await initializeInstrumentation({
124
147
  * serviceName: 'my-app',
125
148
  * configUrl: 'https://config.company.com/instrumentation.yaml'
126
149
  * })
127
- *
128
- * await Effect.runPromise(program)
129
150
  * ```
130
151
  *
131
- * @example With error handling
152
+ * @example Disable specific instrumentations
132
153
  * ```typescript
133
- * const program = initializeInstrumentation({
154
+ * await initializeInstrumentation({
134
155
  * serviceName: 'my-app',
135
- * enableUserInteraction: false
136
- * }).pipe(
137
- * Effect.catchTag('InitializationError', (error) => {
138
- * console.error('Failed to initialize:', error.reason)
139
- * return Effect.die(error) // Re-throw or handle
140
- * })
141
- * )
142
- *
143
- * await Effect.runPromise(program)
156
+ * enableUserInteraction: false, // Disable click tracking
157
+ * enableXhr: false // Disable XMLHttpRequest tracking
158
+ * })
144
159
  * ```
145
160
  */
146
- declare const initializeInstrumentation: (options: SdkInitializationOptions) => Effect.Effect<WebTracerProvider, InitializationError>;
161
+ declare function initializeInstrumentation(options: SdkInitializationOptions): Promise<WebTracerProvider>;
147
162
 
148
163
  /**
149
164
  * OTLP Exporter Factory for Browser
@@ -1,9 +1,9 @@
1
- import { Data, Context, Effect, Layer, Deferred } from 'effect';
2
1
  import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
3
2
  import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
4
3
  import { registerInstrumentations } from '@opentelemetry/instrumentation';
5
4
  import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web';
6
5
  import { ZoneContextManager } from '@opentelemetry/context-zone';
6
+ import { Data, Context, Effect, Layer } from 'effect';
7
7
  import { FileSystem } from '@effect/platform/FileSystem';
8
8
  import * as HttpClient from '@effect/platform/HttpClient';
9
9
  import * as HttpClientRequest from '@effect/platform/HttpClientRequest';
@@ -4539,34 +4539,92 @@ var PatternSpanProcessor = class {
4539
4539
 
4540
4540
  // src/core/sdk-initializer.ts
4541
4541
  var sdkInstance = null;
4542
- var initializationDeferred = null;
4543
- var initializeSdkEffect = (options) => Effect.gen(function* () {
4542
+ function buildPropagateTraceUrls(options, config) {
4543
+ const apiOption = options.propagateTraceContext;
4544
+ const yamlStrategy = config?.http?.propagate_trace_context?.strategy;
4545
+ const yamlIncludeUrls = config?.http?.propagate_trace_context?.include_urls;
4546
+ let effectiveStrategy;
4547
+ let patterns = [];
4548
+ if (apiOption !== void 0) {
4549
+ if (typeof apiOption === "string") {
4550
+ effectiveStrategy = apiOption;
4551
+ } else {
4552
+ effectiveStrategy = "patterns";
4553
+ patterns = apiOption;
4554
+ }
4555
+ } else if (yamlStrategy) {
4556
+ effectiveStrategy = yamlStrategy;
4557
+ if (effectiveStrategy === "patterns" && yamlIncludeUrls) {
4558
+ patterns = yamlIncludeUrls;
4559
+ }
4560
+ } else {
4561
+ effectiveStrategy = "same-origin";
4562
+ }
4563
+ switch (effectiveStrategy) {
4564
+ case "all":
4565
+ return [/.*/];
4566
+ case "none":
4567
+ case "same-origin":
4568
+ return [];
4569
+ case "patterns": {
4570
+ const regexes = [];
4571
+ for (const pattern of patterns) {
4572
+ try {
4573
+ regexes.push(new RegExp(pattern));
4574
+ } catch (error) {
4575
+ console.warn(
4576
+ `[@atrim/instrument-web] Invalid trace propagation pattern: "${pattern}"`,
4577
+ error
4578
+ );
4579
+ }
4580
+ }
4581
+ return regexes;
4582
+ }
4583
+ default:
4584
+ console.warn(
4585
+ `[@atrim/instrument-web] Unknown trace propagation strategy: "${effectiveStrategy}". Defaulting to same-origin only.`
4586
+ );
4587
+ return [];
4588
+ }
4589
+ }
4590
+ async function initializeSdk(options) {
4544
4591
  if (sdkInstance) {
4545
4592
  return sdkInstance;
4546
4593
  }
4547
- if (initializationDeferred) {
4548
- return yield* Deferred.await(initializationDeferred);
4549
- }
4550
- const deferred = yield* Deferred.make();
4551
- initializationDeferred = deferred;
4552
- const result = yield* performInitializationEffect(options).pipe(
4553
- Effect.tap((provider) => Deferred.succeed(deferred, provider)),
4554
- Effect.tapError((error) => Deferred.fail(deferred, error)),
4555
- Effect.ensuring(
4556
- Effect.sync(() => {
4557
- initializationDeferred = null;
4558
- })
4559
- )
4560
- );
4561
- return result;
4562
- });
4563
- var performInitializationEffect = (options) => Effect.gen(function* () {
4564
- const config = yield* loadConfigEffect(options);
4565
- if (config) {
4566
- initializePatternMatcher(config);
4567
- }
4568
- const ignoreUrls = buildIgnoreUrls(config);
4569
- const exporter = yield* Effect.sync(() => {
4594
+ try {
4595
+ let config = null;
4596
+ if (options.config) {
4597
+ config = await loadConfigFromInline(options.config);
4598
+ } else if (options.configPath || options.configUrl) {
4599
+ const url = options.configUrl || options.configPath;
4600
+ config = await loadConfig(url);
4601
+ }
4602
+ if (config) {
4603
+ initializePatternMatcher(config);
4604
+ }
4605
+ const ignoreUrls = [
4606
+ // Always ignore standard OTLP endpoints (prevents infinite loops)
4607
+ /\/v1\/traces$/,
4608
+ /\/v1\/metrics$/,
4609
+ /\/v1\/logs$/
4610
+ ];
4611
+ if (config?.http?.ignore_outgoing_urls) {
4612
+ for (const pattern of config.http.ignore_outgoing_urls) {
4613
+ try {
4614
+ ignoreUrls.push(new RegExp(pattern));
4615
+ } catch (error) {
4616
+ console.warn(
4617
+ `[@atrim/instrument-web] Invalid ignore_outgoing_urls pattern: "${pattern}"`,
4618
+ error
4619
+ );
4620
+ }
4621
+ }
4622
+ } else {
4623
+ console.warn(
4624
+ "[@atrim/instrument-web] Missing http.ignore_outgoing_urls in instrumentation.yaml. Using default OTLP endpoint patterns only. Consider adding http filtering to your config for better control."
4625
+ );
4626
+ }
4627
+ const propagateTraceUrls = buildPropagateTraceUrls(options, config);
4570
4628
  const exporterOptions = {};
4571
4629
  if (options.otlpEndpoint) {
4572
4630
  exporterOptions.endpoint = options.otlpEndpoint;
@@ -4574,23 +4632,18 @@ var performInitializationEffect = (options) => Effect.gen(function* () {
4574
4632
  if (options.otlpHeaders) {
4575
4633
  exporterOptions.headers = options.otlpHeaders;
4576
4634
  }
4577
- return createOtlpExporter(exporterOptions);
4578
- });
4579
- const spanProcessors = [];
4580
- if (config) {
4581
- spanProcessors.push(new PatternSpanProcessor());
4582
- }
4583
- spanProcessors.push(new SimpleSpanProcessor(exporter));
4584
- const provider = yield* Effect.sync(() => {
4585
- const p = new WebTracerProvider({
4635
+ const exporter = createOtlpExporter(exporterOptions);
4636
+ const spanProcessors = [];
4637
+ if (config) {
4638
+ spanProcessors.push(new PatternSpanProcessor());
4639
+ }
4640
+ spanProcessors.push(new SimpleSpanProcessor(exporter));
4641
+ const provider = new WebTracerProvider({
4586
4642
  spanProcessors
4587
4643
  });
4588
- p.register({
4644
+ provider.register({
4589
4645
  contextManager: new ZoneContextManager()
4590
4646
  });
4591
- return p;
4592
- });
4593
- yield* Effect.sync(() => {
4594
4647
  registerInstrumentations({
4595
4648
  instrumentations: [
4596
4649
  getWebAutoInstrumentations({
@@ -4603,69 +4656,25 @@ var performInitializationEffect = (options) => Effect.gen(function* () {
4603
4656
  },
4604
4657
  "@opentelemetry/instrumentation-fetch": {
4605
4658
  enabled: options.enableFetch ?? true,
4606
- propagateTraceHeaderCorsUrls: [/.*/],
4607
- // Propagate to all origins
4659
+ propagateTraceHeaderCorsUrls: propagateTraceUrls,
4660
+ // Controlled by config/API
4608
4661
  clearTimingResources: true,
4609
4662
  ignoreUrls
4610
4663
  // Prevent self-instrumentation of OTLP exports
4611
4664
  },
4612
4665
  "@opentelemetry/instrumentation-xml-http-request": {
4613
4666
  enabled: options.enableXhr ?? true,
4614
- propagateTraceHeaderCorsUrls: [/.*/]
4667
+ propagateTraceHeaderCorsUrls: propagateTraceUrls
4668
+ // Controlled by config/API
4615
4669
  }
4616
4670
  })
4617
4671
  ]
4618
4672
  });
4619
- });
4620
- sdkInstance = provider;
4621
- return provider;
4622
- });
4623
- var loadConfigEffect = (options) => Effect.gen(function* () {
4624
- if (options.config) {
4625
- return yield* Effect.tryPromise({
4626
- try: () => loadConfigFromInline(options.config),
4627
- catch: (error) => new InitializationError({
4628
- reason: "Failed to load inline config",
4629
- cause: error
4630
- })
4631
- });
4673
+ sdkInstance = provider;
4674
+ return provider;
4675
+ } catch (error) {
4676
+ throw new Error(`Failed to initialize OpenTelemetry SDK: ${error}`);
4632
4677
  }
4633
- if (options.configPath || options.configUrl) {
4634
- const url = options.configUrl || options.configPath;
4635
- return yield* Effect.tryPromise({
4636
- try: () => loadConfig(url),
4637
- catch: (error) => new InitializationError({
4638
- reason: `Failed to load config from ${url}`,
4639
- cause: error
4640
- })
4641
- });
4642
- }
4643
- return null;
4644
- });
4645
- function buildIgnoreUrls(config) {
4646
- const ignoreUrls = [
4647
- // Always ignore standard OTLP endpoints (prevents infinite loops)
4648
- /\/v1\/traces$/,
4649
- /\/v1\/metrics$/,
4650
- /\/v1\/logs$/
4651
- ];
4652
- if (config?.http?.ignore_outgoing_urls) {
4653
- for (const pattern of config.http.ignore_outgoing_urls) {
4654
- try {
4655
- ignoreUrls.push(new RegExp(pattern));
4656
- } catch (error) {
4657
- console.warn(
4658
- `[@atrim/instrument-web] Invalid ignore_outgoing_urls pattern: "${pattern}"`,
4659
- error
4660
- );
4661
- }
4662
- }
4663
- } else {
4664
- console.warn(
4665
- "[@atrim/instrument-web] Missing http.ignore_outgoing_urls in instrumentation.yaml. Using default OTLP endpoint patterns only. Consider adding http filtering to your config for better control."
4666
- );
4667
- }
4668
- return ignoreUrls;
4669
4678
  }
4670
4679
  function getSdkInstance() {
4671
4680
  return sdkInstance;
@@ -4678,11 +4687,12 @@ async function shutdownSdk() {
4678
4687
  }
4679
4688
  function resetSdk() {
4680
4689
  sdkInstance = null;
4681
- initializationDeferred = null;
4682
4690
  }
4683
4691
 
4684
4692
  // src/api.ts
4685
- var initializeInstrumentation = (options) => initializeSdkEffect(options);
4693
+ async function initializeInstrumentation(options) {
4694
+ return await initializeSdk(options);
4695
+ }
4686
4696
  function setSpanAttributes(span, attributes) {
4687
4697
  for (const [key, value] of Object.entries(attributes)) {
4688
4698
  span.setAttribute(key, value);