@atrim/instrument-node 0.4.0 → 0.5.0-c05e3a1-20251119131235

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.
@@ -1,14 +1,18 @@
1
- import { Data, Effect, Cache, Duration, Schedule } from 'effect';
1
+ import { Data, Context, Effect, Layer, Deferred } from 'effect';
2
2
  import { NodeSDK } from '@opentelemetry/sdk-node';
3
3
  import { SimpleSpanProcessor, BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
4
4
  import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
5
5
  import { trace } from '@opentelemetry/api';
6
- import { existsSync, readFileSync } from 'fs';
7
- import { join } from 'path';
6
+ import { FileSystem } from '@effect/platform/FileSystem';
7
+ import * as HttpClient from '@effect/platform/HttpClient';
8
+ import * as HttpClientRequest from '@effect/platform/HttpClientRequest';
8
9
  import { parse } from 'yaml';
9
10
  import { z } from 'zod';
10
11
  import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
11
12
  import { readFile } from 'fs/promises';
13
+ import { join } from 'path';
14
+ import { NodeContext } from '@effect/platform-node';
15
+ import { FetchHttpClient } from '@effect/platform';
12
16
 
13
17
  var __defProp = Object.defineProperty;
14
18
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -55,7 +59,20 @@ var HttpFilteringConfigSchema = z.object({
55
59
  // Patterns to ignore for incoming HTTP requests (string patterns only in YAML)
56
60
  ignore_incoming_paths: z.array(z.string()).optional(),
57
61
  // Require parent span for outgoing requests (prevents root spans for HTTP calls)
58
- require_parent_for_outgoing_spans: z.boolean().optional()
62
+ require_parent_for_outgoing_spans: z.boolean().optional(),
63
+ // Trace context propagation configuration
64
+ // Controls which cross-origin requests receive W3C Trace Context headers (traceparent, tracestate)
65
+ propagate_trace_context: z.object({
66
+ // Strategy for trace propagation
67
+ // - "all": Propagate to all cross-origin requests (may cause CORS errors)
68
+ // - "none": Never propagate trace headers
69
+ // - "same-origin": Only propagate to same-origin requests (default, safe)
70
+ // - "patterns": Propagate based on include_urls patterns
71
+ strategy: z.enum(["all", "none", "same-origin", "patterns"]).default("same-origin"),
72
+ // URL patterns to include when strategy is "patterns"
73
+ // Supports regex patterns (e.g., "^https://api\\.myapp\\.com")
74
+ include_urls: z.array(z.string()).optional()
75
+ }).optional()
59
76
  });
60
77
  var InstrumentationConfigSchema = z.object({
61
78
  version: z.string(),
@@ -73,233 +90,183 @@ var InstrumentationConfigSchema = z.object({
73
90
  http: HttpFilteringConfigSchema.optional()
74
91
  });
75
92
  (class extends Data.TaggedError("ConfigError") {
93
+ get message() {
94
+ return this.reason;
95
+ }
76
96
  });
77
97
  var ConfigUrlError = class extends Data.TaggedError("ConfigUrlError") {
98
+ get message() {
99
+ return this.reason;
100
+ }
78
101
  };
79
102
  var ConfigValidationError = class extends Data.TaggedError("ConfigValidationError") {
103
+ get message() {
104
+ return this.reason;
105
+ }
80
106
  };
81
107
  var ConfigFileError = class extends Data.TaggedError("ConfigFileError") {
108
+ get message() {
109
+ return this.reason;
110
+ }
82
111
  };
83
112
  (class extends Data.TaggedError("ServiceDetectionError") {
113
+ get message() {
114
+ return this.reason;
115
+ }
84
116
  });
85
117
  (class extends Data.TaggedError("InitializationError") {
118
+ get message() {
119
+ return this.reason;
120
+ }
86
121
  });
87
122
  (class extends Data.TaggedError("ExportError") {
123
+ get message() {
124
+ return this.reason;
125
+ }
88
126
  });
89
127
  (class extends Data.TaggedError("ShutdownError") {
128
+ get message() {
129
+ return this.reason;
130
+ }
90
131
  });
91
132
  var SECURITY_DEFAULTS = {
92
133
  maxConfigSize: 1e6,
93
134
  // 1MB
94
- requestTimeout: 5e3,
95
- allowedProtocols: ["https:"],
96
- // Only HTTPS for remote configs
97
- cacheTimeout: 3e5
98
- // 5 minutes
135
+ requestTimeout: 5e3
136
+ // 5 seconds
99
137
  };
100
- function getDefaultConfig() {
101
- return {
102
- version: "1.0",
103
- instrumentation: {
104
- enabled: true,
105
- logging: "on",
106
- description: "Default instrumentation configuration",
107
- instrument_patterns: [
108
- { pattern: "^app\\.", enabled: true, description: "Application operations" },
109
- { pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
110
- { pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
111
- ],
112
- ignore_patterns: [
113
- { pattern: "^test\\.", description: "Test utilities" },
114
- { pattern: "^internal\\.", description: "Internal operations" },
115
- { pattern: "^health\\.", description: "Health checks" }
116
- ]
117
- },
118
- effect: {
119
- auto_extract_metadata: true
120
- }
121
- };
122
- }
123
- var validateConfigEffect = (rawConfig) => Effect.try({
124
- try: () => InstrumentationConfigSchema.parse(rawConfig),
125
- catch: (error) => new ConfigValidationError({
126
- reason: "Invalid configuration schema",
127
- cause: error
128
- })
129
- });
130
- var loadConfigFromFileEffect = (filePath) => Effect.gen(function* () {
131
- const fileContents = yield* Effect.try({
132
- try: () => readFileSync(filePath, "utf8"),
133
- catch: (error) => new ConfigFileError({
134
- reason: `Failed to read config file at ${filePath}`,
138
+ var ConfigLoader = class extends Context.Tag("ConfigLoader")() {
139
+ };
140
+ var parseYamlContent = (content, uri) => Effect.gen(function* () {
141
+ const parsed = yield* Effect.try({
142
+ try: () => parse(content),
143
+ catch: (error) => new ConfigValidationError({
144
+ reason: uri ? `Failed to parse YAML from ${uri}` : "Failed to parse YAML",
145
+ cause: error
146
+ })
147
+ });
148
+ return yield* Effect.try({
149
+ try: () => InstrumentationConfigSchema.parse(parsed),
150
+ catch: (error) => new ConfigValidationError({
151
+ reason: uri ? `Invalid configuration schema from ${uri}` : "Invalid configuration schema",
135
152
  cause: error
136
153
  })
137
154
  });
138
- if (fileContents.length > SECURITY_DEFAULTS.maxConfigSize) {
155
+ });
156
+ var loadFromFileWithFs = (fs, path, uri) => Effect.gen(function* () {
157
+ const content = yield* fs.readFileString(path).pipe(
158
+ Effect.mapError(
159
+ (error) => new ConfigFileError({
160
+ reason: `Failed to read config file at ${uri}`,
161
+ cause: error
162
+ })
163
+ )
164
+ );
165
+ if (content.length > SECURITY_DEFAULTS.maxConfigSize) {
139
166
  return yield* Effect.fail(
140
167
  new ConfigFileError({
141
168
  reason: `Config file exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
142
169
  })
143
170
  );
144
171
  }
145
- let rawConfig;
146
- try {
147
- rawConfig = parse(fileContents);
148
- } catch (error) {
149
- return yield* Effect.fail(
150
- new ConfigValidationError({
151
- reason: "Invalid YAML syntax",
152
- cause: error
153
- })
154
- );
155
- }
156
- return yield* validateConfigEffect(rawConfig);
172
+ return yield* parseYamlContent(content, uri);
157
173
  });
158
- var fetchAndParseConfig = (url) => Effect.gen(function* () {
159
- let urlObj;
160
- try {
161
- urlObj = new URL(url);
162
- } catch (error) {
163
- return yield* Effect.fail(
164
- new ConfigUrlError({
165
- reason: `Invalid URL: ${url}`,
166
- cause: error
174
+ var loadFromHttpWithClient = (client, url) => Effect.scoped(
175
+ Effect.gen(function* () {
176
+ if (url.startsWith("http://")) {
177
+ return yield* Effect.fail(
178
+ new ConfigUrlError({
179
+ reason: "Insecure protocol: only HTTPS URLs are allowed"
180
+ })
181
+ );
182
+ }
183
+ const request = HttpClientRequest.get(url).pipe(
184
+ HttpClientRequest.setHeaders({
185
+ Accept: "application/yaml, text/yaml, application/x-yaml"
167
186
  })
168
187
  );
169
- }
170
- if (!SECURITY_DEFAULTS.allowedProtocols.includes(urlObj.protocol)) {
171
- return yield* Effect.fail(
172
- new ConfigUrlError({
173
- reason: `Insecure protocol ${urlObj.protocol}. Only ${SECURITY_DEFAULTS.allowedProtocols.join(", ")} are allowed`
188
+ const response = yield* client.execute(request).pipe(
189
+ Effect.timeout(`${SECURITY_DEFAULTS.requestTimeout} millis`),
190
+ Effect.mapError((error) => {
191
+ if (error._tag === "TimeoutException") {
192
+ return new ConfigUrlError({
193
+ reason: `Config fetch timeout after ${SECURITY_DEFAULTS.requestTimeout}ms from ${url}`
194
+ });
195
+ }
196
+ return new ConfigUrlError({
197
+ reason: `Failed to load config from URL: ${url}`,
198
+ cause: error
199
+ });
174
200
  })
175
201
  );
176
- }
177
- const response = yield* Effect.tryPromise({
178
- try: () => fetch(url, {
179
- redirect: "follow",
180
- headers: {
181
- Accept: "application/yaml, text/yaml, text/x-yaml"
182
- }
183
- }),
184
- catch: (error) => new ConfigUrlError({
185
- reason: `Failed to load config from URL ${url}`,
186
- cause: error
187
- })
188
- }).pipe(
189
- Effect.timeout(Duration.millis(SECURITY_DEFAULTS.requestTimeout)),
190
- Effect.retry({
191
- times: 3,
192
- schedule: Schedule.exponential(Duration.millis(100))
193
- }),
194
- Effect.catchAll((error) => {
195
- if (error._tag === "TimeoutException") {
196
- return Effect.fail(
197
- new ConfigUrlError({
198
- reason: `Config fetch timeout after ${SECURITY_DEFAULTS.requestTimeout}ms`
202
+ if (response.status >= 400) {
203
+ return yield* Effect.fail(
204
+ new ConfigUrlError({
205
+ reason: `HTTP ${response.status} from ${url}`
206
+ })
207
+ );
208
+ }
209
+ const text = yield* response.text.pipe(
210
+ Effect.mapError(
211
+ (error) => new ConfigUrlError({
212
+ reason: `Failed to read response body from ${url}`,
213
+ cause: error
214
+ })
215
+ )
216
+ );
217
+ if (text.length > SECURITY_DEFAULTS.maxConfigSize) {
218
+ return yield* Effect.fail(
219
+ new ConfigUrlError({
220
+ reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
221
+ })
222
+ );
223
+ }
224
+ return yield* parseYamlContent(text, url);
225
+ })
226
+ );
227
+ var makeConfigLoader = Effect.gen(function* () {
228
+ const fs = yield* Effect.serviceOption(FileSystem);
229
+ const http = yield* HttpClient.HttpClient;
230
+ const loadFromUriUncached = (uri) => Effect.gen(function* () {
231
+ if (uri.startsWith("file://")) {
232
+ const path = uri.slice(7);
233
+ if (fs._tag === "None") {
234
+ return yield* Effect.fail(
235
+ new ConfigFileError({
236
+ reason: "FileSystem not available (browser environment?)",
237
+ cause: { uri }
199
238
  })
200
239
  );
201
240
  }
202
- return Effect.fail(error);
203
- })
204
- );
205
- if (!response.ok) {
206
- return yield* Effect.fail(
207
- new ConfigUrlError({
208
- reason: `HTTP ${response.status}: ${response.statusText}`
209
- })
210
- );
211
- }
212
- const contentLength = response.headers.get("content-length");
213
- if (contentLength && parseInt(contentLength) > SECURITY_DEFAULTS.maxConfigSize) {
214
- return yield* Effect.fail(
215
- new ConfigUrlError({
216
- reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
217
- })
218
- );
219
- }
220
- const text = yield* Effect.tryPromise({
221
- try: () => response.text(),
222
- catch: (error) => new ConfigUrlError({
223
- reason: "Failed to read response body",
224
- cause: error
241
+ return yield* loadFromFileWithFs(fs.value, path, uri);
242
+ }
243
+ if (uri.startsWith("http://") || uri.startsWith("https://")) {
244
+ return yield* loadFromHttpWithClient(http, uri);
245
+ }
246
+ if (fs._tag === "Some") {
247
+ return yield* loadFromFileWithFs(fs.value, uri, uri);
248
+ } else {
249
+ return yield* loadFromHttpWithClient(http, uri);
250
+ }
251
+ });
252
+ const loadFromUriCached = yield* Effect.cachedFunction(loadFromUriUncached);
253
+ return ConfigLoader.of({
254
+ loadFromUri: loadFromUriCached,
255
+ loadFromInline: (content) => Effect.gen(function* () {
256
+ if (typeof content === "string") {
257
+ return yield* parseYamlContent(content);
258
+ }
259
+ return yield* Effect.try({
260
+ try: () => InstrumentationConfigSchema.parse(content),
261
+ catch: (error) => new ConfigValidationError({
262
+ reason: "Invalid configuration schema",
263
+ cause: error
264
+ })
265
+ });
225
266
  })
226
267
  });
227
- if (text.length > SECURITY_DEFAULTS.maxConfigSize) {
228
- return yield* Effect.fail(
229
- new ConfigUrlError({
230
- reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
231
- })
232
- );
233
- }
234
- let rawConfig;
235
- try {
236
- rawConfig = parse(text);
237
- } catch (error) {
238
- return yield* Effect.fail(
239
- new ConfigValidationError({
240
- reason: "Invalid YAML syntax",
241
- cause: error
242
- })
243
- );
244
- }
245
- return yield* validateConfigEffect(rawConfig);
246
- });
247
- var makeConfigCache = () => Cache.make({
248
- capacity: 100,
249
- timeToLive: Duration.minutes(5),
250
- lookup: (url) => fetchAndParseConfig(url)
251
- });
252
- var cacheInstance = null;
253
- var getCache = Effect.gen(function* () {
254
- if (!cacheInstance) {
255
- cacheInstance = yield* makeConfigCache();
256
- }
257
- return cacheInstance;
258
- });
259
- var loadConfigFromUrlEffect = (url, cacheTimeout = SECURITY_DEFAULTS.cacheTimeout) => Effect.gen(function* () {
260
- if (cacheTimeout === 0) {
261
- return yield* fetchAndParseConfig(url);
262
- }
263
- const cache = yield* getCache;
264
- return yield* cache.get(url);
265
- });
266
- var loadConfigEffect = (options = {}) => Effect.gen(function* () {
267
- if (options.config) {
268
- return yield* validateConfigEffect(options.config);
269
- }
270
- const envConfigPath = process.env.ATRIM_INSTRUMENTATION_CONFIG;
271
- if (envConfigPath) {
272
- if (envConfigPath.startsWith("http://") || envConfigPath.startsWith("https://")) {
273
- return yield* loadConfigFromUrlEffect(envConfigPath, options.cacheTimeout);
274
- }
275
- return yield* loadConfigFromFileEffect(envConfigPath);
276
- }
277
- if (options.configUrl) {
278
- return yield* loadConfigFromUrlEffect(options.configUrl, options.cacheTimeout);
279
- }
280
- if (options.configPath) {
281
- return yield* loadConfigFromFileEffect(options.configPath);
282
- }
283
- const defaultPath = join(process.cwd(), "instrumentation.yaml");
284
- const exists = yield* Effect.sync(() => existsSync(defaultPath));
285
- if (exists) {
286
- return yield* loadConfigFromFileEffect(defaultPath);
287
- }
288
- return getDefaultConfig();
289
268
  });
290
- async function loadConfig(options = {}) {
291
- return Effect.runPromise(
292
- loadConfigEffect(options).pipe(
293
- // Convert typed errors to regular Error with reason message for backward compatibility
294
- Effect.mapError((error) => {
295
- const message = error.reason;
296
- const newError = new Error(message);
297
- newError.cause = error.cause;
298
- return newError;
299
- })
300
- )
301
- );
302
- }
269
+ var ConfigLoaderLive = Layer.effect(ConfigLoader, makeConfigLoader);
303
270
  var PatternMatcher = class {
304
271
  constructor(config) {
305
272
  __publicField2(this, "ignorePatterns", []);
@@ -716,19 +683,88 @@ var getServiceInfoWithFallback = detectServiceInfo.pipe(
716
683
  })
717
684
  )
718
685
  );
719
- async function detectServiceInfoAsync() {
720
- return Effect.runPromise(getServiceInfoWithFallback);
686
+ var NodeConfigLoaderLive = ConfigLoaderLive.pipe(
687
+ Layer.provide(Layer.mergeAll(NodeContext.layer, FetchHttpClient.layer))
688
+ );
689
+ var cachedLoaderPromise = null;
690
+ function getCachedLoader() {
691
+ if (!cachedLoaderPromise) {
692
+ cachedLoaderPromise = Effect.runPromise(
693
+ Effect.gen(function* () {
694
+ return yield* ConfigLoader;
695
+ }).pipe(Effect.provide(NodeConfigLoaderLive))
696
+ );
697
+ }
698
+ return cachedLoaderPromise;
699
+ }
700
+ function _resetConfigLoaderCache() {
701
+ cachedLoaderPromise = null;
721
702
  }
722
- async function getServiceNameAsync() {
723
- return Effect.runPromise(getServiceName);
703
+ async function loadConfig(uri, options) {
704
+ if (options?.cacheTimeout === 0) {
705
+ const program = Effect.gen(function* () {
706
+ const loader2 = yield* ConfigLoader;
707
+ return yield* loader2.loadFromUri(uri);
708
+ });
709
+ return Effect.runPromise(program.pipe(Effect.provide(NodeConfigLoaderLive)));
710
+ }
711
+ const loader = await getCachedLoader();
712
+ return Effect.runPromise(loader.loadFromUri(uri));
713
+ }
714
+ async function loadConfigFromInline(content) {
715
+ const loader = await getCachedLoader();
716
+ return Effect.runPromise(loader.loadFromInline(content));
717
+ }
718
+ function getDefaultConfig() {
719
+ return {
720
+ version: "1.0",
721
+ instrumentation: {
722
+ enabled: true,
723
+ logging: "on",
724
+ description: "Default instrumentation configuration",
725
+ instrument_patterns: [
726
+ { pattern: "^app\\.", enabled: true, description: "Application operations" },
727
+ { pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
728
+ { pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
729
+ ],
730
+ ignore_patterns: [
731
+ { pattern: "^test\\.", description: "Test utilities" },
732
+ { pattern: "^internal\\.", description: "Internal operations" },
733
+ { pattern: "^health\\.", description: "Health checks" }
734
+ ]
735
+ },
736
+ effect: {
737
+ auto_extract_metadata: true
738
+ }
739
+ };
724
740
  }
725
- async function getServiceVersionAsync() {
726
- return Effect.runPromise(getServiceVersion);
741
+ async function loadConfigWithOptions(options = {}) {
742
+ const loadOptions = options.cacheTimeout !== void 0 ? { cacheTimeout: options.cacheTimeout } : void 0;
743
+ if (options.config) {
744
+ return loadConfigFromInline(options.config);
745
+ }
746
+ const envConfigPath = process.env.ATRIM_INSTRUMENTATION_CONFIG;
747
+ if (envConfigPath) {
748
+ return loadConfig(envConfigPath, loadOptions);
749
+ }
750
+ if (options.configUrl) {
751
+ return loadConfig(options.configUrl, loadOptions);
752
+ }
753
+ if (options.configPath) {
754
+ return loadConfig(options.configPath, loadOptions);
755
+ }
756
+ const { existsSync } = await import('fs');
757
+ const { join: join2 } = await import('path');
758
+ const defaultPath = join2(process.cwd(), "instrumentation.yaml");
759
+ if (existsSync(defaultPath)) {
760
+ return loadConfig(defaultPath, loadOptions);
761
+ }
762
+ return getDefaultConfig();
727
763
  }
728
764
 
729
765
  // src/core/sdk-initializer.ts
730
766
  var sdkInstance = null;
731
- var initializationPromise = null;
767
+ var initializationDeferred = null;
732
768
  function buildHttpInstrumentationConfig(options, config, _otlpEndpoint) {
733
769
  const httpConfig = { enabled: true };
734
770
  const programmaticPatterns = options.http?.ignoreOutgoingUrls || [];
@@ -844,27 +880,38 @@ function isTracingAlreadyInitialized() {
844
880
  return false;
845
881
  }
846
882
  }
847
- async function initializeSdk(options = {}) {
883
+ var initializeSdkEffect = (options = {}) => Effect.gen(function* () {
848
884
  if (sdkInstance) {
849
885
  logger.warn("@atrim/instrumentation: SDK already initialized. Returning existing instance.");
850
886
  return sdkInstance;
851
887
  }
852
- if (initializationPromise) {
888
+ if (initializationDeferred) {
853
889
  logger.log(
854
- "@atrim/instrumentation: SDK already initialized, waiting for initialization to complete..."
890
+ "@atrim/instrumentation: SDK initialization in progress, waiting for completion..."
855
891
  );
856
- return initializationPromise;
857
- }
858
- initializationPromise = performInitialization(options);
859
- try {
860
- const result = await initializationPromise;
861
- return result;
862
- } finally {
863
- initializationPromise = null;
864
- }
865
- }
866
- async function performInitialization(options) {
867
- const config = await loadConfig(options);
892
+ return yield* Deferred.await(initializationDeferred);
893
+ }
894
+ const deferred = yield* Deferred.make();
895
+ initializationDeferred = deferred;
896
+ const result = yield* performInitializationEffect(options).pipe(
897
+ Effect.tap((sdk) => Deferred.succeed(deferred, sdk)),
898
+ Effect.tapError((error) => Deferred.fail(deferred, error)),
899
+ Effect.ensuring(
900
+ Effect.sync(() => {
901
+ initializationDeferred = null;
902
+ })
903
+ )
904
+ );
905
+ return result;
906
+ });
907
+ var performInitializationEffect = (options) => Effect.gen(function* () {
908
+ const config = yield* Effect.tryPromise({
909
+ try: () => loadConfigWithOptions(options),
910
+ catch: (error) => new InitializationError2({
911
+ reason: "Failed to load configuration",
912
+ cause: error
913
+ })
914
+ });
868
915
  const loggingLevel = config.instrumentation.logging || "on";
869
916
  logger.setLevel(loggingLevel);
870
917
  const alreadyInitialized = isTracingAlreadyInitialized();
@@ -880,14 +927,25 @@ async function performInitialization(options) {
880
927
  logger.log("");
881
928
  return null;
882
929
  }
883
- const serviceInfo = await detectServiceInfoAsync();
930
+ const serviceInfo = yield* detectServiceInfo.pipe(
931
+ Effect.catchAll(
932
+ () => Effect.succeed({
933
+ name: "unknown-service",
934
+ version: void 0
935
+ })
936
+ )
937
+ );
884
938
  const serviceName = options.serviceName || serviceInfo.name;
885
939
  const serviceVersion = options.serviceVersion || serviceInfo.version;
886
- const rawExporter = createOtlpExporter(options.otlp);
887
- const exporter = new SafeSpanExporter(rawExporter);
940
+ const rawExporter = yield* Effect.sync(() => createOtlpExporter(options.otlp));
941
+ const exporter = yield* Effect.sync(() => new SafeSpanExporter(rawExporter));
888
942
  const useSimpleProcessor = process.env.NODE_ENV === "test" || process.env.OTEL_USE_SIMPLE_PROCESSOR === "true";
889
- const baseProcessor = useSimpleProcessor ? new SimpleSpanProcessor(exporter) : new BatchSpanProcessor(exporter);
890
- const patternProcessor = new PatternSpanProcessor(config, baseProcessor);
943
+ const baseProcessor = yield* Effect.sync(
944
+ () => useSimpleProcessor ? new SimpleSpanProcessor(exporter) : new BatchSpanProcessor(exporter)
945
+ );
946
+ const patternProcessor = yield* Effect.sync(
947
+ () => new PatternSpanProcessor(config, baseProcessor)
948
+ );
891
949
  const instrumentations = [];
892
950
  const hasWebFramework = hasWebFrameworkInstalled();
893
951
  const enableAutoInstrumentation = shouldEnableAutoInstrumentation(
@@ -900,15 +958,11 @@ async function performInitialization(options) {
900
958
  const undiciConfig = buildUndiciInstrumentationConfig(options, config);
901
959
  instrumentations.push(
902
960
  ...getNodeAutoInstrumentations({
903
- // Enable HTTP instrumentation with filtering (for http/https modules)
904
961
  "@opentelemetry/instrumentation-http": httpConfig,
905
- // Enable undici instrumentation with filtering (for fetch API)
906
962
  "@opentelemetry/instrumentation-undici": undiciConfig,
907
- // Enable web framework instrumentations
908
963
  "@opentelemetry/instrumentation-express": { enabled: true },
909
964
  "@opentelemetry/instrumentation-fastify": { enabled: true },
910
965
  "@opentelemetry/instrumentation-koa": { enabled: true },
911
- // Disable noisy instrumentations by default
912
966
  "@opentelemetry/instrumentation-fs": { enabled: false },
913
967
  "@opentelemetry/instrumentation-dns": { enabled: false }
914
968
  })
@@ -936,18 +990,20 @@ async function performInitialization(options) {
936
990
  serviceName,
937
991
  ...serviceVersion && { serviceVersion },
938
992
  instrumentations,
939
- // Allow advanced overrides
940
993
  ...options.sdk
941
994
  };
942
- const sdk = new NodeSDK(sdkConfig);
943
- sdk.start();
995
+ const sdk = yield* Effect.sync(() => {
996
+ const s = new NodeSDK(sdkConfig);
997
+ s.start();
998
+ return s;
999
+ });
944
1000
  sdkInstance = sdk;
945
1001
  if (!options.disableAutoShutdown) {
946
- registerShutdownHandlers(sdk);
1002
+ yield* Effect.sync(() => registerShutdownHandlers(sdk));
947
1003
  }
948
1004
  logInitialization(config, serviceName, serviceVersion, options, enableAutoInstrumentation);
949
1005
  return sdk;
950
- }
1006
+ });
951
1007
  function getSdkInstance() {
952
1008
  return sdkInstance;
953
1009
  }
@@ -960,7 +1016,7 @@ async function shutdownSdk() {
960
1016
  }
961
1017
  function resetSdk() {
962
1018
  sdkInstance = null;
963
- initializationPromise = null;
1019
+ initializationDeferred = null;
964
1020
  }
965
1021
  function registerShutdownHandlers(sdk) {
966
1022
  const shutdown = async (signal) => {
@@ -1017,33 +1073,11 @@ function logInitialization(config, serviceName, serviceVersion, options, autoIns
1017
1073
  }
1018
1074
 
1019
1075
  // src/api.ts
1020
- async function initializeInstrumentation(options = {}) {
1021
- const sdk = await initializeSdk(options);
1022
- if (sdk) {
1023
- const config = await loadConfig(options);
1024
- initializePatternMatcher(config);
1025
- }
1026
- return sdk;
1027
- }
1028
- async function initializePatternMatchingOnly(options = {}) {
1029
- const config = await loadConfig(options);
1030
- initializePatternMatcher(config);
1031
- logger.log("@atrim/instrumentation: Pattern matching initialized (legacy mode)");
1032
- logger.log(
1033
- " Note: NodeSDK is not initialized. Use initializeInstrumentation() for complete setup."
1034
- );
1035
- }
1036
- var initializeInstrumentationEffect = (options = {}) => Effect.gen(function* () {
1037
- const sdk = yield* Effect.tryPromise({
1038
- try: () => initializeSdk(options),
1039
- catch: (error) => new InitializationError2({
1040
- reason: "SDK initialization failed",
1041
- cause: error
1042
- })
1043
- });
1076
+ var initializeInstrumentation = (options = {}) => Effect.gen(function* () {
1077
+ const sdk = yield* initializeSdkEffect(options);
1044
1078
  if (sdk) {
1045
1079
  yield* Effect.tryPromise({
1046
- try: () => loadConfig(options),
1080
+ try: () => loadConfigWithOptions(options),
1047
1081
  catch: (error) => new ConfigError2({
1048
1082
  reason: "Failed to load config for pattern matcher",
1049
1083
  cause: error
@@ -1058,9 +1092,9 @@ var initializeInstrumentationEffect = (options = {}) => Effect.gen(function* ()
1058
1092
  }
1059
1093
  return sdk;
1060
1094
  });
1061
- var initializePatternMatchingOnlyEffect = (options = {}) => Effect.gen(function* () {
1095
+ var initializePatternMatchingOnly = (options = {}) => Effect.gen(function* () {
1062
1096
  const config = yield* Effect.tryPromise({
1063
- try: () => loadConfig(options),
1097
+ try: () => loadConfigWithOptions(options),
1064
1098
  catch: (error) => new ConfigError2({
1065
1099
  reason: "Failed to load configuration",
1066
1100
  cause: error
@@ -1068,7 +1102,7 @@ var initializePatternMatchingOnlyEffect = (options = {}) => Effect.gen(function*
1068
1102
  });
1069
1103
  yield* Effect.sync(() => {
1070
1104
  initializePatternMatcher(config);
1071
- logger.log("@atrim/instrumentation: Pattern matching initialized (legacy mode)");
1105
+ logger.log("@atrim/instrumentation: Pattern matching initialized (pattern-only mode)");
1072
1106
  logger.log(
1073
1107
  " Note: NodeSDK is not initialized. Use initializeInstrumentation() for complete setup."
1074
1108
  );
@@ -1169,6 +1203,6 @@ function suppressShutdownErrors() {
1169
1203
  });
1170
1204
  }
1171
1205
 
1172
- export { ConfigError2 as ConfigError, ConfigFileError2 as ConfigFileError, ConfigUrlError2 as ConfigUrlError, ConfigValidationError2 as ConfigValidationError, ExportError2 as ExportError, InitializationError2 as InitializationError, PatternMatcher, PatternSpanProcessor, ServiceDetectionError2 as ServiceDetectionError, ShutdownError2 as ShutdownError, annotateCacheOperation, annotateDbQuery, annotateHttpRequest, createOtlpExporter, detectServiceInfoAsync as detectServiceInfo, detectServiceInfo as detectServiceInfoEffect, getOtlpEndpoint, getPatternMatcher, getSdkInstance, getServiceInfoWithFallback, getServiceNameAsync as getServiceName, getServiceName as getServiceNameEffect, getServiceVersionAsync as getServiceVersion, getServiceVersion as getServiceVersionEffect, initializeInstrumentation, initializeInstrumentationEffect, initializePatternMatchingOnly, initializePatternMatchingOnlyEffect, loadConfig, markSpanError, markSpanSuccess, recordException, resetSdk, setSpanAttributes, shouldInstrumentSpan, shutdownSdk, suppressShutdownErrors };
1206
+ export { ConfigError2 as ConfigError, ConfigFileError2 as ConfigFileError, ConfigUrlError2 as ConfigUrlError, ConfigValidationError2 as ConfigValidationError, ExportError2 as ExportError, InitializationError2 as InitializationError, PatternMatcher, PatternSpanProcessor, ServiceDetectionError2 as ServiceDetectionError, ShutdownError2 as ShutdownError, annotateCacheOperation, annotateDbQuery, annotateHttpRequest, _resetConfigLoaderCache as clearConfigCache, createOtlpExporter, detectServiceInfo, getOtlpEndpoint, getPatternMatcher, getSdkInstance, getServiceInfoWithFallback, getServiceName, getServiceVersion, initializeInstrumentation, initializePatternMatchingOnly, loadConfig, loadConfigFromInline, loadConfigWithOptions, markSpanError, markSpanSuccess, recordException, resetSdk, setSpanAttributes, shouldInstrumentSpan, shutdownSdk, suppressShutdownErrors };
1173
1207
  //# sourceMappingURL=index.js.map
1174
1208
  //# sourceMappingURL=index.js.map