@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.
@@ -5,12 +5,37 @@ var sdkNode = require('@opentelemetry/sdk-node');
5
5
  var sdkTraceBase = require('@opentelemetry/sdk-trace-base');
6
6
  var autoInstrumentationsNode = require('@opentelemetry/auto-instrumentations-node');
7
7
  var api = require('@opentelemetry/api');
8
- var fs = require('fs');
9
- var path = require('path');
8
+ var FileSystem = require('@effect/platform/FileSystem');
9
+ var HttpClient = require('@effect/platform/HttpClient');
10
+ var HttpClientRequest = require('@effect/platform/HttpClientRequest');
10
11
  var yaml = require('yaml');
11
12
  var zod = require('zod');
12
13
  var exporterTraceOtlpHttp = require('@opentelemetry/exporter-trace-otlp-http');
13
14
  var promises = require('fs/promises');
15
+ var path = require('path');
16
+ var platformNode = require('@effect/platform-node');
17
+ var platform = require('@effect/platform');
18
+
19
+ function _interopNamespace(e) {
20
+ if (e && e.__esModule) return e;
21
+ var n = Object.create(null);
22
+ if (e) {
23
+ Object.keys(e).forEach(function (k) {
24
+ if (k !== 'default') {
25
+ var d = Object.getOwnPropertyDescriptor(e, k);
26
+ Object.defineProperty(n, k, d.get ? d : {
27
+ enumerable: true,
28
+ get: function () { return e[k]; }
29
+ });
30
+ }
31
+ });
32
+ }
33
+ n.default = e;
34
+ return Object.freeze(n);
35
+ }
36
+
37
+ var HttpClient__namespace = /*#__PURE__*/_interopNamespace(HttpClient);
38
+ var HttpClientRequest__namespace = /*#__PURE__*/_interopNamespace(HttpClientRequest);
14
39
 
15
40
  var __defProp = Object.defineProperty;
16
41
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -57,7 +82,20 @@ var HttpFilteringConfigSchema = zod.z.object({
57
82
  // Patterns to ignore for incoming HTTP requests (string patterns only in YAML)
58
83
  ignore_incoming_paths: zod.z.array(zod.z.string()).optional(),
59
84
  // Require parent span for outgoing requests (prevents root spans for HTTP calls)
60
- require_parent_for_outgoing_spans: zod.z.boolean().optional()
85
+ require_parent_for_outgoing_spans: zod.z.boolean().optional(),
86
+ // Trace context propagation configuration
87
+ // Controls which cross-origin requests receive W3C Trace Context headers (traceparent, tracestate)
88
+ propagate_trace_context: zod.z.object({
89
+ // Strategy for trace propagation
90
+ // - "all": Propagate to all cross-origin requests (may cause CORS errors)
91
+ // - "none": Never propagate trace headers
92
+ // - "same-origin": Only propagate to same-origin requests (default, safe)
93
+ // - "patterns": Propagate based on include_urls patterns
94
+ strategy: zod.z.enum(["all", "none", "same-origin", "patterns"]).default("same-origin"),
95
+ // URL patterns to include when strategy is "patterns"
96
+ // Supports regex patterns (e.g., "^https://api\\.myapp\\.com")
97
+ include_urls: zod.z.array(zod.z.string()).optional()
98
+ }).optional()
61
99
  });
62
100
  var InstrumentationConfigSchema = zod.z.object({
63
101
  version: zod.z.string(),
@@ -75,233 +113,183 @@ var InstrumentationConfigSchema = zod.z.object({
75
113
  http: HttpFilteringConfigSchema.optional()
76
114
  });
77
115
  (class extends effect.Data.TaggedError("ConfigError") {
116
+ get message() {
117
+ return this.reason;
118
+ }
78
119
  });
79
120
  var ConfigUrlError = class extends effect.Data.TaggedError("ConfigUrlError") {
121
+ get message() {
122
+ return this.reason;
123
+ }
80
124
  };
81
125
  var ConfigValidationError = class extends effect.Data.TaggedError("ConfigValidationError") {
126
+ get message() {
127
+ return this.reason;
128
+ }
82
129
  };
83
130
  var ConfigFileError = class extends effect.Data.TaggedError("ConfigFileError") {
131
+ get message() {
132
+ return this.reason;
133
+ }
84
134
  };
85
135
  (class extends effect.Data.TaggedError("ServiceDetectionError") {
136
+ get message() {
137
+ return this.reason;
138
+ }
86
139
  });
87
140
  (class extends effect.Data.TaggedError("InitializationError") {
141
+ get message() {
142
+ return this.reason;
143
+ }
88
144
  });
89
145
  (class extends effect.Data.TaggedError("ExportError") {
146
+ get message() {
147
+ return this.reason;
148
+ }
90
149
  });
91
150
  (class extends effect.Data.TaggedError("ShutdownError") {
151
+ get message() {
152
+ return this.reason;
153
+ }
92
154
  });
93
155
  var SECURITY_DEFAULTS = {
94
156
  maxConfigSize: 1e6,
95
157
  // 1MB
96
- requestTimeout: 5e3,
97
- allowedProtocols: ["https:"],
98
- // Only HTTPS for remote configs
99
- cacheTimeout: 3e5
100
- // 5 minutes
158
+ requestTimeout: 5e3
159
+ // 5 seconds
101
160
  };
102
- function getDefaultConfig() {
103
- return {
104
- version: "1.0",
105
- instrumentation: {
106
- enabled: true,
107
- logging: "on",
108
- description: "Default instrumentation configuration",
109
- instrument_patterns: [
110
- { pattern: "^app\\.", enabled: true, description: "Application operations" },
111
- { pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
112
- { pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
113
- ],
114
- ignore_patterns: [
115
- { pattern: "^test\\.", description: "Test utilities" },
116
- { pattern: "^internal\\.", description: "Internal operations" },
117
- { pattern: "^health\\.", description: "Health checks" }
118
- ]
119
- },
120
- effect: {
121
- auto_extract_metadata: true
122
- }
123
- };
124
- }
125
- var validateConfigEffect = (rawConfig) => effect.Effect.try({
126
- try: () => InstrumentationConfigSchema.parse(rawConfig),
127
- catch: (error) => new ConfigValidationError({
128
- reason: "Invalid configuration schema",
129
- cause: error
130
- })
131
- });
132
- var loadConfigFromFileEffect = (filePath) => effect.Effect.gen(function* () {
133
- const fileContents = yield* effect.Effect.try({
134
- try: () => fs.readFileSync(filePath, "utf8"),
135
- catch: (error) => new ConfigFileError({
136
- reason: `Failed to read config file at ${filePath}`,
161
+ var ConfigLoader = class extends effect.Context.Tag("ConfigLoader")() {
162
+ };
163
+ var parseYamlContent = (content, uri) => effect.Effect.gen(function* () {
164
+ const parsed = yield* effect.Effect.try({
165
+ try: () => yaml.parse(content),
166
+ catch: (error) => new ConfigValidationError({
167
+ reason: uri ? `Failed to parse YAML from ${uri}` : "Failed to parse YAML",
137
168
  cause: error
138
169
  })
139
170
  });
140
- if (fileContents.length > SECURITY_DEFAULTS.maxConfigSize) {
171
+ return yield* effect.Effect.try({
172
+ try: () => InstrumentationConfigSchema.parse(parsed),
173
+ catch: (error) => new ConfigValidationError({
174
+ reason: uri ? `Invalid configuration schema from ${uri}` : "Invalid configuration schema",
175
+ cause: error
176
+ })
177
+ });
178
+ });
179
+ var loadFromFileWithFs = (fs, path, uri) => effect.Effect.gen(function* () {
180
+ const content = yield* fs.readFileString(path).pipe(
181
+ effect.Effect.mapError(
182
+ (error) => new ConfigFileError({
183
+ reason: `Failed to read config file at ${uri}`,
184
+ cause: error
185
+ })
186
+ )
187
+ );
188
+ if (content.length > SECURITY_DEFAULTS.maxConfigSize) {
141
189
  return yield* effect.Effect.fail(
142
190
  new ConfigFileError({
143
191
  reason: `Config file exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
144
192
  })
145
193
  );
146
194
  }
147
- let rawConfig;
148
- try {
149
- rawConfig = yaml.parse(fileContents);
150
- } catch (error) {
151
- return yield* effect.Effect.fail(
152
- new ConfigValidationError({
153
- reason: "Invalid YAML syntax",
154
- cause: error
155
- })
156
- );
157
- }
158
- return yield* validateConfigEffect(rawConfig);
195
+ return yield* parseYamlContent(content, uri);
159
196
  });
160
- var fetchAndParseConfig = (url) => effect.Effect.gen(function* () {
161
- let urlObj;
162
- try {
163
- urlObj = new URL(url);
164
- } catch (error) {
165
- return yield* effect.Effect.fail(
166
- new ConfigUrlError({
167
- reason: `Invalid URL: ${url}`,
168
- cause: error
197
+ var loadFromHttpWithClient = (client, url) => effect.Effect.scoped(
198
+ effect.Effect.gen(function* () {
199
+ if (url.startsWith("http://")) {
200
+ return yield* effect.Effect.fail(
201
+ new ConfigUrlError({
202
+ reason: "Insecure protocol: only HTTPS URLs are allowed"
203
+ })
204
+ );
205
+ }
206
+ const request = HttpClientRequest__namespace.get(url).pipe(
207
+ HttpClientRequest__namespace.setHeaders({
208
+ Accept: "application/yaml, text/yaml, application/x-yaml"
169
209
  })
170
210
  );
171
- }
172
- if (!SECURITY_DEFAULTS.allowedProtocols.includes(urlObj.protocol)) {
173
- return yield* effect.Effect.fail(
174
- new ConfigUrlError({
175
- reason: `Insecure protocol ${urlObj.protocol}. Only ${SECURITY_DEFAULTS.allowedProtocols.join(", ")} are allowed`
211
+ const response = yield* client.execute(request).pipe(
212
+ effect.Effect.timeout(`${SECURITY_DEFAULTS.requestTimeout} millis`),
213
+ effect.Effect.mapError((error) => {
214
+ if (error._tag === "TimeoutException") {
215
+ return new ConfigUrlError({
216
+ reason: `Config fetch timeout after ${SECURITY_DEFAULTS.requestTimeout}ms from ${url}`
217
+ });
218
+ }
219
+ return new ConfigUrlError({
220
+ reason: `Failed to load config from URL: ${url}`,
221
+ cause: error
222
+ });
176
223
  })
177
224
  );
178
- }
179
- const response = yield* effect.Effect.tryPromise({
180
- try: () => fetch(url, {
181
- redirect: "follow",
182
- headers: {
183
- Accept: "application/yaml, text/yaml, text/x-yaml"
184
- }
185
- }),
186
- catch: (error) => new ConfigUrlError({
187
- reason: `Failed to load config from URL ${url}`,
188
- cause: error
189
- })
190
- }).pipe(
191
- effect.Effect.timeout(effect.Duration.millis(SECURITY_DEFAULTS.requestTimeout)),
192
- effect.Effect.retry({
193
- times: 3,
194
- schedule: effect.Schedule.exponential(effect.Duration.millis(100))
195
- }),
196
- effect.Effect.catchAll((error) => {
197
- if (error._tag === "TimeoutException") {
198
- return effect.Effect.fail(
199
- new ConfigUrlError({
200
- reason: `Config fetch timeout after ${SECURITY_DEFAULTS.requestTimeout}ms`
225
+ if (response.status >= 400) {
226
+ return yield* effect.Effect.fail(
227
+ new ConfigUrlError({
228
+ reason: `HTTP ${response.status} from ${url}`
229
+ })
230
+ );
231
+ }
232
+ const text = yield* response.text.pipe(
233
+ effect.Effect.mapError(
234
+ (error) => new ConfigUrlError({
235
+ reason: `Failed to read response body from ${url}`,
236
+ cause: error
237
+ })
238
+ )
239
+ );
240
+ if (text.length > SECURITY_DEFAULTS.maxConfigSize) {
241
+ return yield* effect.Effect.fail(
242
+ new ConfigUrlError({
243
+ reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
244
+ })
245
+ );
246
+ }
247
+ return yield* parseYamlContent(text, url);
248
+ })
249
+ );
250
+ var makeConfigLoader = effect.Effect.gen(function* () {
251
+ const fs = yield* effect.Effect.serviceOption(FileSystem.FileSystem);
252
+ const http = yield* HttpClient__namespace.HttpClient;
253
+ const loadFromUriUncached = (uri) => effect.Effect.gen(function* () {
254
+ if (uri.startsWith("file://")) {
255
+ const path = uri.slice(7);
256
+ if (fs._tag === "None") {
257
+ return yield* effect.Effect.fail(
258
+ new ConfigFileError({
259
+ reason: "FileSystem not available (browser environment?)",
260
+ cause: { uri }
201
261
  })
202
262
  );
203
263
  }
204
- return effect.Effect.fail(error);
205
- })
206
- );
207
- if (!response.ok) {
208
- return yield* effect.Effect.fail(
209
- new ConfigUrlError({
210
- reason: `HTTP ${response.status}: ${response.statusText}`
211
- })
212
- );
213
- }
214
- const contentLength = response.headers.get("content-length");
215
- if (contentLength && parseInt(contentLength) > SECURITY_DEFAULTS.maxConfigSize) {
216
- return yield* effect.Effect.fail(
217
- new ConfigUrlError({
218
- reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
219
- })
220
- );
221
- }
222
- const text = yield* effect.Effect.tryPromise({
223
- try: () => response.text(),
224
- catch: (error) => new ConfigUrlError({
225
- reason: "Failed to read response body",
226
- cause: error
264
+ return yield* loadFromFileWithFs(fs.value, path, uri);
265
+ }
266
+ if (uri.startsWith("http://") || uri.startsWith("https://")) {
267
+ return yield* loadFromHttpWithClient(http, uri);
268
+ }
269
+ if (fs._tag === "Some") {
270
+ return yield* loadFromFileWithFs(fs.value, uri, uri);
271
+ } else {
272
+ return yield* loadFromHttpWithClient(http, uri);
273
+ }
274
+ });
275
+ const loadFromUriCached = yield* effect.Effect.cachedFunction(loadFromUriUncached);
276
+ return ConfigLoader.of({
277
+ loadFromUri: loadFromUriCached,
278
+ loadFromInline: (content) => effect.Effect.gen(function* () {
279
+ if (typeof content === "string") {
280
+ return yield* parseYamlContent(content);
281
+ }
282
+ return yield* effect.Effect.try({
283
+ try: () => InstrumentationConfigSchema.parse(content),
284
+ catch: (error) => new ConfigValidationError({
285
+ reason: "Invalid configuration schema",
286
+ cause: error
287
+ })
288
+ });
227
289
  })
228
290
  });
229
- if (text.length > SECURITY_DEFAULTS.maxConfigSize) {
230
- return yield* effect.Effect.fail(
231
- new ConfigUrlError({
232
- reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
233
- })
234
- );
235
- }
236
- let rawConfig;
237
- try {
238
- rawConfig = yaml.parse(text);
239
- } catch (error) {
240
- return yield* effect.Effect.fail(
241
- new ConfigValidationError({
242
- reason: "Invalid YAML syntax",
243
- cause: error
244
- })
245
- );
246
- }
247
- return yield* validateConfigEffect(rawConfig);
248
- });
249
- var makeConfigCache = () => effect.Cache.make({
250
- capacity: 100,
251
- timeToLive: effect.Duration.minutes(5),
252
- lookup: (url) => fetchAndParseConfig(url)
253
- });
254
- var cacheInstance = null;
255
- var getCache = effect.Effect.gen(function* () {
256
- if (!cacheInstance) {
257
- cacheInstance = yield* makeConfigCache();
258
- }
259
- return cacheInstance;
260
291
  });
261
- var loadConfigFromUrlEffect = (url, cacheTimeout = SECURITY_DEFAULTS.cacheTimeout) => effect.Effect.gen(function* () {
262
- if (cacheTimeout === 0) {
263
- return yield* fetchAndParseConfig(url);
264
- }
265
- const cache = yield* getCache;
266
- return yield* cache.get(url);
267
- });
268
- var loadConfigEffect = (options = {}) => effect.Effect.gen(function* () {
269
- if (options.config) {
270
- return yield* validateConfigEffect(options.config);
271
- }
272
- const envConfigPath = process.env.ATRIM_INSTRUMENTATION_CONFIG;
273
- if (envConfigPath) {
274
- if (envConfigPath.startsWith("http://") || envConfigPath.startsWith("https://")) {
275
- return yield* loadConfigFromUrlEffect(envConfigPath, options.cacheTimeout);
276
- }
277
- return yield* loadConfigFromFileEffect(envConfigPath);
278
- }
279
- if (options.configUrl) {
280
- return yield* loadConfigFromUrlEffect(options.configUrl, options.cacheTimeout);
281
- }
282
- if (options.configPath) {
283
- return yield* loadConfigFromFileEffect(options.configPath);
284
- }
285
- const defaultPath = path.join(process.cwd(), "instrumentation.yaml");
286
- const exists = yield* effect.Effect.sync(() => fs.existsSync(defaultPath));
287
- if (exists) {
288
- return yield* loadConfigFromFileEffect(defaultPath);
289
- }
290
- return getDefaultConfig();
291
- });
292
- async function loadConfig(options = {}) {
293
- return effect.Effect.runPromise(
294
- loadConfigEffect(options).pipe(
295
- // Convert typed errors to regular Error with reason message for backward compatibility
296
- effect.Effect.mapError((error) => {
297
- const message = error.reason;
298
- const newError = new Error(message);
299
- newError.cause = error.cause;
300
- return newError;
301
- })
302
- )
303
- );
304
- }
292
+ var ConfigLoaderLive = effect.Layer.effect(ConfigLoader, makeConfigLoader);
305
293
  var PatternMatcher = class {
306
294
  constructor(config) {
307
295
  __publicField2(this, "ignorePatterns", []);
@@ -718,19 +706,88 @@ var getServiceInfoWithFallback = detectServiceInfo.pipe(
718
706
  })
719
707
  )
720
708
  );
721
- async function detectServiceInfoAsync() {
722
- return effect.Effect.runPromise(getServiceInfoWithFallback);
709
+ var NodeConfigLoaderLive = ConfigLoaderLive.pipe(
710
+ effect.Layer.provide(effect.Layer.mergeAll(platformNode.NodeContext.layer, platform.FetchHttpClient.layer))
711
+ );
712
+ var cachedLoaderPromise = null;
713
+ function getCachedLoader() {
714
+ if (!cachedLoaderPromise) {
715
+ cachedLoaderPromise = effect.Effect.runPromise(
716
+ effect.Effect.gen(function* () {
717
+ return yield* ConfigLoader;
718
+ }).pipe(effect.Effect.provide(NodeConfigLoaderLive))
719
+ );
720
+ }
721
+ return cachedLoaderPromise;
723
722
  }
724
- async function getServiceNameAsync() {
725
- return effect.Effect.runPromise(getServiceName);
723
+ function _resetConfigLoaderCache() {
724
+ cachedLoaderPromise = null;
726
725
  }
727
- async function getServiceVersionAsync() {
728
- return effect.Effect.runPromise(getServiceVersion);
726
+ async function loadConfig(uri, options) {
727
+ if (options?.cacheTimeout === 0) {
728
+ const program = effect.Effect.gen(function* () {
729
+ const loader2 = yield* ConfigLoader;
730
+ return yield* loader2.loadFromUri(uri);
731
+ });
732
+ return effect.Effect.runPromise(program.pipe(effect.Effect.provide(NodeConfigLoaderLive)));
733
+ }
734
+ const loader = await getCachedLoader();
735
+ return effect.Effect.runPromise(loader.loadFromUri(uri));
736
+ }
737
+ async function loadConfigFromInline(content) {
738
+ const loader = await getCachedLoader();
739
+ return effect.Effect.runPromise(loader.loadFromInline(content));
740
+ }
741
+ function getDefaultConfig() {
742
+ return {
743
+ version: "1.0",
744
+ instrumentation: {
745
+ enabled: true,
746
+ logging: "on",
747
+ description: "Default instrumentation configuration",
748
+ instrument_patterns: [
749
+ { pattern: "^app\\.", enabled: true, description: "Application operations" },
750
+ { pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
751
+ { pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
752
+ ],
753
+ ignore_patterns: [
754
+ { pattern: "^test\\.", description: "Test utilities" },
755
+ { pattern: "^internal\\.", description: "Internal operations" },
756
+ { pattern: "^health\\.", description: "Health checks" }
757
+ ]
758
+ },
759
+ effect: {
760
+ auto_extract_metadata: true
761
+ }
762
+ };
763
+ }
764
+ async function loadConfigWithOptions(options = {}) {
765
+ const loadOptions = options.cacheTimeout !== void 0 ? { cacheTimeout: options.cacheTimeout } : void 0;
766
+ if (options.config) {
767
+ return loadConfigFromInline(options.config);
768
+ }
769
+ const envConfigPath = process.env.ATRIM_INSTRUMENTATION_CONFIG;
770
+ if (envConfigPath) {
771
+ return loadConfig(envConfigPath, loadOptions);
772
+ }
773
+ if (options.configUrl) {
774
+ return loadConfig(options.configUrl, loadOptions);
775
+ }
776
+ if (options.configPath) {
777
+ return loadConfig(options.configPath, loadOptions);
778
+ }
779
+ const { existsSync } = await import('fs');
780
+ const { join: join2 } = await import('path');
781
+ const defaultPath = join2(process.cwd(), "instrumentation.yaml");
782
+ if (existsSync(defaultPath)) {
783
+ return loadConfig(defaultPath, loadOptions);
784
+ }
785
+ return getDefaultConfig();
729
786
  }
730
787
 
731
788
  // src/core/sdk-initializer.ts
732
789
  var sdkInstance = null;
733
- var initializationPromise = null;
790
+ var initializationDeferred = null;
734
791
  function buildHttpInstrumentationConfig(options, config, _otlpEndpoint) {
735
792
  const httpConfig = { enabled: true };
736
793
  const programmaticPatterns = options.http?.ignoreOutgoingUrls || [];
@@ -846,27 +903,38 @@ function isTracingAlreadyInitialized() {
846
903
  return false;
847
904
  }
848
905
  }
849
- async function initializeSdk(options = {}) {
906
+ var initializeSdkEffect = (options = {}) => effect.Effect.gen(function* () {
850
907
  if (sdkInstance) {
851
908
  logger.warn("@atrim/instrumentation: SDK already initialized. Returning existing instance.");
852
909
  return sdkInstance;
853
910
  }
854
- if (initializationPromise) {
911
+ if (initializationDeferred) {
855
912
  logger.log(
856
- "@atrim/instrumentation: SDK already initialized, waiting for initialization to complete..."
913
+ "@atrim/instrumentation: SDK initialization in progress, waiting for completion..."
857
914
  );
858
- return initializationPromise;
859
- }
860
- initializationPromise = performInitialization(options);
861
- try {
862
- const result = await initializationPromise;
863
- return result;
864
- } finally {
865
- initializationPromise = null;
866
- }
867
- }
868
- async function performInitialization(options) {
869
- const config = await loadConfig(options);
915
+ return yield* effect.Deferred.await(initializationDeferred);
916
+ }
917
+ const deferred = yield* effect.Deferred.make();
918
+ initializationDeferred = deferred;
919
+ const result = yield* performInitializationEffect(options).pipe(
920
+ effect.Effect.tap((sdk) => effect.Deferred.succeed(deferred, sdk)),
921
+ effect.Effect.tapError((error) => effect.Deferred.fail(deferred, error)),
922
+ effect.Effect.ensuring(
923
+ effect.Effect.sync(() => {
924
+ initializationDeferred = null;
925
+ })
926
+ )
927
+ );
928
+ return result;
929
+ });
930
+ var performInitializationEffect = (options) => effect.Effect.gen(function* () {
931
+ const config = yield* effect.Effect.tryPromise({
932
+ try: () => loadConfigWithOptions(options),
933
+ catch: (error) => new InitializationError2({
934
+ reason: "Failed to load configuration",
935
+ cause: error
936
+ })
937
+ });
870
938
  const loggingLevel = config.instrumentation.logging || "on";
871
939
  logger.setLevel(loggingLevel);
872
940
  const alreadyInitialized = isTracingAlreadyInitialized();
@@ -882,14 +950,25 @@ async function performInitialization(options) {
882
950
  logger.log("");
883
951
  return null;
884
952
  }
885
- const serviceInfo = await detectServiceInfoAsync();
953
+ const serviceInfo = yield* detectServiceInfo.pipe(
954
+ effect.Effect.catchAll(
955
+ () => effect.Effect.succeed({
956
+ name: "unknown-service",
957
+ version: void 0
958
+ })
959
+ )
960
+ );
886
961
  const serviceName = options.serviceName || serviceInfo.name;
887
962
  const serviceVersion = options.serviceVersion || serviceInfo.version;
888
- const rawExporter = createOtlpExporter(options.otlp);
889
- const exporter = new SafeSpanExporter(rawExporter);
963
+ const rawExporter = yield* effect.Effect.sync(() => createOtlpExporter(options.otlp));
964
+ const exporter = yield* effect.Effect.sync(() => new SafeSpanExporter(rawExporter));
890
965
  const useSimpleProcessor = process.env.NODE_ENV === "test" || process.env.OTEL_USE_SIMPLE_PROCESSOR === "true";
891
- const baseProcessor = useSimpleProcessor ? new sdkTraceBase.SimpleSpanProcessor(exporter) : new sdkTraceBase.BatchSpanProcessor(exporter);
892
- const patternProcessor = new PatternSpanProcessor(config, baseProcessor);
966
+ const baseProcessor = yield* effect.Effect.sync(
967
+ () => useSimpleProcessor ? new sdkTraceBase.SimpleSpanProcessor(exporter) : new sdkTraceBase.BatchSpanProcessor(exporter)
968
+ );
969
+ const patternProcessor = yield* effect.Effect.sync(
970
+ () => new PatternSpanProcessor(config, baseProcessor)
971
+ );
893
972
  const instrumentations = [];
894
973
  const hasWebFramework = hasWebFrameworkInstalled();
895
974
  const enableAutoInstrumentation = shouldEnableAutoInstrumentation(
@@ -902,15 +981,11 @@ async function performInitialization(options) {
902
981
  const undiciConfig = buildUndiciInstrumentationConfig(options, config);
903
982
  instrumentations.push(
904
983
  ...autoInstrumentationsNode.getNodeAutoInstrumentations({
905
- // Enable HTTP instrumentation with filtering (for http/https modules)
906
984
  "@opentelemetry/instrumentation-http": httpConfig,
907
- // Enable undici instrumentation with filtering (for fetch API)
908
985
  "@opentelemetry/instrumentation-undici": undiciConfig,
909
- // Enable web framework instrumentations
910
986
  "@opentelemetry/instrumentation-express": { enabled: true },
911
987
  "@opentelemetry/instrumentation-fastify": { enabled: true },
912
988
  "@opentelemetry/instrumentation-koa": { enabled: true },
913
- // Disable noisy instrumentations by default
914
989
  "@opentelemetry/instrumentation-fs": { enabled: false },
915
990
  "@opentelemetry/instrumentation-dns": { enabled: false }
916
991
  })
@@ -938,18 +1013,20 @@ async function performInitialization(options) {
938
1013
  serviceName,
939
1014
  ...serviceVersion && { serviceVersion },
940
1015
  instrumentations,
941
- // Allow advanced overrides
942
1016
  ...options.sdk
943
1017
  };
944
- const sdk = new sdkNode.NodeSDK(sdkConfig);
945
- sdk.start();
1018
+ const sdk = yield* effect.Effect.sync(() => {
1019
+ const s = new sdkNode.NodeSDK(sdkConfig);
1020
+ s.start();
1021
+ return s;
1022
+ });
946
1023
  sdkInstance = sdk;
947
1024
  if (!options.disableAutoShutdown) {
948
- registerShutdownHandlers(sdk);
1025
+ yield* effect.Effect.sync(() => registerShutdownHandlers(sdk));
949
1026
  }
950
1027
  logInitialization(config, serviceName, serviceVersion, options, enableAutoInstrumentation);
951
1028
  return sdk;
952
- }
1029
+ });
953
1030
  function getSdkInstance() {
954
1031
  return sdkInstance;
955
1032
  }
@@ -962,7 +1039,7 @@ async function shutdownSdk() {
962
1039
  }
963
1040
  function resetSdk() {
964
1041
  sdkInstance = null;
965
- initializationPromise = null;
1042
+ initializationDeferred = null;
966
1043
  }
967
1044
  function registerShutdownHandlers(sdk) {
968
1045
  const shutdown = async (signal) => {
@@ -1019,33 +1096,11 @@ function logInitialization(config, serviceName, serviceVersion, options, autoIns
1019
1096
  }
1020
1097
 
1021
1098
  // src/api.ts
1022
- async function initializeInstrumentation(options = {}) {
1023
- const sdk = await initializeSdk(options);
1024
- if (sdk) {
1025
- const config = await loadConfig(options);
1026
- initializePatternMatcher(config);
1027
- }
1028
- return sdk;
1029
- }
1030
- async function initializePatternMatchingOnly(options = {}) {
1031
- const config = await loadConfig(options);
1032
- initializePatternMatcher(config);
1033
- logger.log("@atrim/instrumentation: Pattern matching initialized (legacy mode)");
1034
- logger.log(
1035
- " Note: NodeSDK is not initialized. Use initializeInstrumentation() for complete setup."
1036
- );
1037
- }
1038
- var initializeInstrumentationEffect = (options = {}) => effect.Effect.gen(function* () {
1039
- const sdk = yield* effect.Effect.tryPromise({
1040
- try: () => initializeSdk(options),
1041
- catch: (error) => new InitializationError2({
1042
- reason: "SDK initialization failed",
1043
- cause: error
1044
- })
1045
- });
1099
+ var initializeInstrumentation = (options = {}) => effect.Effect.gen(function* () {
1100
+ const sdk = yield* initializeSdkEffect(options);
1046
1101
  if (sdk) {
1047
1102
  yield* effect.Effect.tryPromise({
1048
- try: () => loadConfig(options),
1103
+ try: () => loadConfigWithOptions(options),
1049
1104
  catch: (error) => new ConfigError2({
1050
1105
  reason: "Failed to load config for pattern matcher",
1051
1106
  cause: error
@@ -1060,9 +1115,9 @@ var initializeInstrumentationEffect = (options = {}) => effect.Effect.gen(functi
1060
1115
  }
1061
1116
  return sdk;
1062
1117
  });
1063
- var initializePatternMatchingOnlyEffect = (options = {}) => effect.Effect.gen(function* () {
1118
+ var initializePatternMatchingOnly = (options = {}) => effect.Effect.gen(function* () {
1064
1119
  const config = yield* effect.Effect.tryPromise({
1065
- try: () => loadConfig(options),
1120
+ try: () => loadConfigWithOptions(options),
1066
1121
  catch: (error) => new ConfigError2({
1067
1122
  reason: "Failed to load configuration",
1068
1123
  cause: error
@@ -1070,7 +1125,7 @@ var initializePatternMatchingOnlyEffect = (options = {}) => effect.Effect.gen(fu
1070
1125
  });
1071
1126
  yield* effect.Effect.sync(() => {
1072
1127
  initializePatternMatcher(config);
1073
- logger.log("@atrim/instrumentation: Pattern matching initialized (legacy mode)");
1128
+ logger.log("@atrim/instrumentation: Pattern matching initialized (pattern-only mode)");
1074
1129
  logger.log(
1075
1130
  " Note: NodeSDK is not initialized. Use initializeInstrumentation() for complete setup."
1076
1131
  );
@@ -1184,22 +1239,20 @@ exports.ShutdownError = ShutdownError2;
1184
1239
  exports.annotateCacheOperation = annotateCacheOperation;
1185
1240
  exports.annotateDbQuery = annotateDbQuery;
1186
1241
  exports.annotateHttpRequest = annotateHttpRequest;
1242
+ exports.clearConfigCache = _resetConfigLoaderCache;
1187
1243
  exports.createOtlpExporter = createOtlpExporter;
1188
- exports.detectServiceInfo = detectServiceInfoAsync;
1189
- exports.detectServiceInfoEffect = detectServiceInfo;
1244
+ exports.detectServiceInfo = detectServiceInfo;
1190
1245
  exports.getOtlpEndpoint = getOtlpEndpoint;
1191
1246
  exports.getPatternMatcher = getPatternMatcher;
1192
1247
  exports.getSdkInstance = getSdkInstance;
1193
1248
  exports.getServiceInfoWithFallback = getServiceInfoWithFallback;
1194
- exports.getServiceName = getServiceNameAsync;
1195
- exports.getServiceNameEffect = getServiceName;
1196
- exports.getServiceVersion = getServiceVersionAsync;
1197
- exports.getServiceVersionEffect = getServiceVersion;
1249
+ exports.getServiceName = getServiceName;
1250
+ exports.getServiceVersion = getServiceVersion;
1198
1251
  exports.initializeInstrumentation = initializeInstrumentation;
1199
- exports.initializeInstrumentationEffect = initializeInstrumentationEffect;
1200
1252
  exports.initializePatternMatchingOnly = initializePatternMatchingOnly;
1201
- exports.initializePatternMatchingOnlyEffect = initializePatternMatchingOnlyEffect;
1202
1253
  exports.loadConfig = loadConfig;
1254
+ exports.loadConfigFromInline = loadConfigFromInline;
1255
+ exports.loadConfigWithOptions = loadConfigWithOptions;
1203
1256
  exports.markSpanError = markSpanError;
1204
1257
  exports.markSpanSuccess = markSpanSuccess;
1205
1258
  exports.recordException = recordException;