@atrim/instrument-node 0.8.1-dev.ae570af.20260116212440 → 0.9.0

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,10 +1,4 @@
1
- import { Data, Context, Effect, Layer, FiberRef, Option, Supervisor, FiberRefs, Tracer, Exit, GlobalValue, RuntimeFlagsPatch, RuntimeFlags } from 'effect';
2
- import { NodeSdk, Resource, Tracer as Tracer$1 } from '@effect/opentelemetry';
3
- import { BasicTracerProvider, ConsoleSpanExporter, SimpleSpanProcessor, BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
4
- import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
5
- import { resourceFromAttributes } from '@opentelemetry/resources';
6
- import { ATTR_SERVICE_VERSION, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
7
- import * as OtelApi from '@opentelemetry/api';
1
+ import { FiberRef, Context, Layer, Effect, Data, GlobalValue, Supervisor, FiberRefs, Option, Exit, Tracer, RuntimeFlagsPatch, RuntimeFlags } from 'effect';
8
2
  import { FileSystem } from '@effect/platform/FileSystem';
9
3
  import * as HttpClient from '@effect/platform/HttpClient';
10
4
  import * as HttpClientRequest from '@effect/platform/HttpClientRequest';
@@ -14,251 +8,35 @@ import { z } from 'zod';
14
8
  import * as fs from 'fs';
15
9
  import * as path2 from 'path';
16
10
  import * as TracerModule from 'effect/Tracer';
11
+ import { Resource, Tracer as Tracer$1 } from '@effect/opentelemetry';
12
+ import * as OtelApi from '@opentelemetry/api';
13
+ import { ConsoleSpanExporter, SimpleSpanProcessor, BatchSpanProcessor, BasicTracerProvider } from '@opentelemetry/sdk-trace-base';
14
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
15
+ import { resourceFromAttributes } from '@opentelemetry/resources';
16
+ import { ATTR_SERVICE_VERSION, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
17
17
 
18
18
  var __defProp = Object.defineProperty;
19
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
20
+ var __getOwnPropNames = Object.getOwnPropertyNames;
21
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
19
22
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
20
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
21
- var __defProp2 = Object.defineProperty;
22
- var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
23
- var __publicField2 = (obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
24
- var PatternConfigSchema = z.object({
25
- pattern: z.string(),
26
- enabled: z.boolean().optional(),
27
- description: z.string().optional()
28
- });
29
- var AutoIsolationConfigSchema = z.object({
30
- // Global enable/disable for auto-isolation
31
- enabled: z.boolean().default(false),
32
- // Which operators to auto-isolate
33
- operators: z.object({
34
- fiberset_run: z.boolean().default(true),
35
- effect_fork: z.boolean().default(true),
36
- effect_fork_daemon: z.boolean().default(true),
37
- effect_fork_in: z.boolean().default(false)
38
- }).default({}),
39
- // Virtual parent tracking configuration
40
- tracking: z.object({
41
- use_span_links: z.boolean().default(true),
42
- use_attributes: z.boolean().default(true),
43
- capture_logical_parent: z.boolean().default(true)
44
- }).default({}),
45
- // Span categorization
46
- attributes: z.object({
47
- category: z.string().default("background_task"),
48
- add_metadata: z.boolean().default(true)
49
- }).default({})
50
- });
51
- var SpanNamingRuleSchema = z.object({
52
- // Match criteria (all specified criteria must match)
53
- match: z.object({
54
- // Regex pattern to match file path
55
- file: z.string().optional(),
56
- // Regex pattern to match function name
57
- function: z.string().optional(),
58
- // Regex pattern to match module name
59
- module: z.string().optional()
60
- }),
61
- // Span name template with variables:
62
- // {fiber_id} - Fiber ID
63
- // {function} - Function name
64
- // {module} - Module name
65
- // {file} - File path
66
- // {line} - Line number
67
- // {operator} - Effect operator (gen, all, forEach, etc.)
68
- // {match:field:N} - Captured regex group from match
69
- name: z.string()
70
- });
71
- var AutoInstrumentationConfigSchema = z.object({
72
- // Enable/disable auto-instrumentation
73
- enabled: z.boolean().default(false),
74
- // Tracing granularity
75
- // - 'fiber': Trace at fiber creation (recommended, lower overhead)
76
- // - 'operator': Trace each Effect operator (higher granularity, more overhead)
77
- granularity: z.enum(["fiber", "operator"]).default("fiber"),
78
- // Smart span naming configuration
79
- span_naming: z.object({
80
- // Default span name template when no rules match
81
- default: z.string().default("effect.fiber.{fiber_id}"),
82
- // Infer span names from source code (requires stack trace parsing)
83
- // Adds ~50-100μs overhead per fiber
84
- infer_from_source: z.boolean().default(true),
85
- // Naming rules (first match wins)
86
- rules: z.array(SpanNamingRuleSchema).default([])
87
- }).default({}),
88
- // Span relationship configuration for forked fibers
89
- // Controls how child fiber spans relate to their parent/forking context
90
- span_relationships: z.object({
91
- // Relationship type between forked fiber spans and their parent context
92
- // - 'parent-child': Use parent-child relationship (default, traditional tracing)
93
- // Parent span shows child as nested. Works well with most observability tools.
94
- // - 'span-links': Use span links (semantically correct for async forks per OTel spec)
95
- // Fibers get independent traces linked to parent. Better for long-running fibers.
96
- // - 'both': Create parent-child AND add span links
97
- // Maximum visibility but may create redundant data.
98
- type: z.enum(["parent-child", "span-links", "both"]).default("parent-child"),
99
- // Custom attributes to add to span links (only used when type includes links)
100
- link_attributes: z.object({
101
- // Link type identifier
102
- "link.type": z.string().default("fork"),
103
- // Custom attributes (key-value pairs)
104
- custom: z.record(z.string()).optional()
105
- }).optional()
106
- }).default({}),
107
- // Pattern-based filtering
108
- filter: z.object({
109
- // Only trace spans matching these patterns (empty = trace all)
110
- include: z.array(z.string()).default([]),
111
- // Never trace spans matching these patterns
112
- exclude: z.array(z.string()).default([])
113
- }).default({}),
114
- // Performance controls
115
- performance: z.object({
116
- // Sample rate (0.0 - 1.0)
117
- sampling_rate: z.number().min(0).max(1).default(1),
118
- // Skip fibers shorter than this duration (e.g., "10ms", "100 millis")
119
- min_duration: z.string().default("0ms"),
120
- // Maximum concurrent traced fibers (0 = unlimited)
121
- max_concurrent: z.number().default(0)
122
- }).default({}),
123
- // Automatic metadata extraction
124
- metadata: z.object({
125
- // Extract Effect fiber information
126
- fiber_info: z.boolean().default(true),
127
- // Extract source location (file:line)
128
- source_location: z.boolean().default(true),
129
- // Extract parent fiber information
130
- parent_fiber: z.boolean().default(true)
131
- }).default({})
132
- });
133
- var OperationTracingConfigSchema = z.object({
134
- // Enable/disable operation tracing
135
- enabled: z.boolean().default(false),
136
- // Global span naming settings
137
- span_naming: z.object({
138
- // Include source location (file:line) in span name
139
- // Default: true - produces "effect.all (index.ts:42)"
140
- // When false - produces "effect.all"
141
- include_location: z.boolean().default(true),
142
- // Span name template with variables:
143
- // {op} - Operation name (all, forEach, retry, etc.)
144
- // {file} - Full file path
145
- // {filename} - Just the filename (basename)
146
- // {line} - Line number
147
- // {column} - Column number
148
- // Default: "effect.{op} ({filename}:{line})"
149
- template: z.string().default("effect.{op} ({filename}:{line})")
150
- }).default({}),
151
- // Operations to trace
152
- operations: z.array(
153
- z.object({
154
- // Operation name: 'all', 'forEach', 'retry', etc.
155
- name: z.string(),
156
- // Custom span name template (overrides global span_naming.template)
157
- // Supports same variables: {op}, {file}, {filename}, {line}, {column}
158
- span_name: z.string().optional(),
159
- // Include item count in span attributes
160
- include_count: z.boolean().default(true),
161
- // Include stack trace in span attributes
162
- include_stack: z.boolean().default(true)
163
- })
164
- ).default([])
165
- });
166
- var HttpFilteringConfigSchema = z.object({
167
- // Patterns to ignore for outgoing HTTP requests (string patterns only in YAML)
168
- ignore_outgoing_urls: z.array(z.string()).optional(),
169
- // Patterns to ignore for incoming HTTP requests (string patterns only in YAML)
170
- ignore_incoming_paths: z.array(z.string()).optional(),
171
- // Require parent span for outgoing requests (prevents root spans for HTTP calls)
172
- require_parent_for_outgoing_spans: z.boolean().optional(),
173
- // Trace context propagation configuration
174
- // Controls which cross-origin requests receive W3C Trace Context headers (traceparent, tracestate)
175
- propagate_trace_context: z.object({
176
- // Strategy for trace propagation
177
- // - "all": Propagate to all cross-origin requests (may cause CORS errors)
178
- // - "none": Never propagate trace headers
179
- // - "same-origin": Only propagate to same-origin requests (default, safe)
180
- // - "patterns": Propagate based on include_urls patterns
181
- strategy: z.enum(["all", "none", "same-origin", "patterns"]).default("same-origin"),
182
- // URL patterns to include when strategy is "patterns"
183
- // Supports regex patterns (e.g., "^https://api\\.myapp\\.com")
184
- include_urls: z.array(z.string()).optional()
185
- }).optional()
186
- });
187
- var ExporterConfigSchema = z.object({
188
- // Exporter type: 'otlp' | 'console' | 'none'
189
- // - 'otlp': Export to OTLP endpoint (production)
190
- // - 'console': Log spans to console (development)
191
- // - 'none': No export (disable tracing)
192
- type: z.enum(["otlp", "console", "none"]).default("otlp"),
193
- // OTLP endpoint URL (for type: otlp)
194
- // Defaults to OTEL_EXPORTER_OTLP_ENDPOINT env var or http://localhost:4318
195
- endpoint: z.string().optional(),
196
- // Custom headers to send with OTLP requests (for type: otlp)
197
- // Useful for authentication (x-api-key, Authorization, etc.)
198
- headers: z.record(z.string()).optional(),
199
- // Span processor type
200
- // - 'batch': Batch spans for export (production, lower overhead)
201
- // - 'simple': Export immediately (development, no batching delay)
202
- processor: z.enum(["batch", "simple"]).default("batch"),
203
- // Batch processor settings (for processor: batch)
204
- batch: z.object({
205
- // Max time to wait before exporting (milliseconds)
206
- scheduled_delay_millis: z.number().default(1e3),
207
- // Max batch size
208
- max_export_batch_size: z.number().default(100)
209
- }).optional()
210
- });
211
- var InstrumentationConfigSchema = z.object({
212
- version: z.string(),
213
- instrumentation: z.object({
214
- enabled: z.boolean(),
215
- description: z.string().optional(),
216
- logging: z.enum(["on", "off", "minimal"]).optional().default("on"),
217
- instrument_patterns: z.array(PatternConfigSchema),
218
- ignore_patterns: z.array(PatternConfigSchema)
219
- }),
220
- effect: z.object({
221
- // Enable/disable Effect tracing entirely
222
- // When false, EffectInstrumentationLive returns Layer.empty
223
- enabled: z.boolean().default(true),
224
- // Exporter mode (legacy - use exporter.type instead):
225
- // - "unified": Use global TracerProvider from Node SDK (recommended, enables filtering)
226
- // - "standalone": Use Effect's own OTLP exporter (bypasses Node SDK filtering)
227
- exporter: z.enum(["unified", "standalone"]).default("unified"),
228
- // Exporter configuration (for auto-instrumentation)
229
- exporter_config: ExporterConfigSchema.optional(),
230
- auto_extract_metadata: z.boolean(),
231
- auto_isolation: AutoIsolationConfigSchema.optional(),
232
- // Auto-instrumentation: automatic tracing of all Effect fibers
233
- auto_instrumentation: AutoInstrumentationConfigSchema.optional(),
234
- // Operation tracing: automatic tracing of Effect.all, Effect.forEach, etc.
235
- operation_tracing: OperationTracingConfigSchema.optional()
236
- }).optional(),
237
- http: HttpFilteringConfigSchema.optional()
238
- });
239
- var defaultConfig = {
240
- version: "1.0",
241
- instrumentation: {
242
- enabled: true,
243
- logging: "on",
244
- description: "Default instrumentation configuration",
245
- instrument_patterns: [
246
- { pattern: "^app\\.", enabled: true, description: "Application operations" },
247
- { pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
248
- { pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
249
- ],
250
- ignore_patterns: [
251
- { pattern: "^test\\.", description: "Test utilities" },
252
- { pattern: "^internal\\.", description: "Internal operations" },
253
- { pattern: "^health\\.", description: "Health checks" }
254
- ]
255
- },
256
- effect: {
257
- enabled: true,
258
- exporter: "unified",
259
- auto_extract_metadata: true
23
+ var __esm = (fn, res) => function __init() {
24
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
25
+ };
26
+ var __export = (target, all) => {
27
+ for (var name in all)
28
+ __defProp(target, name, { get: all[name], enumerable: true });
29
+ };
30
+ var __copyProps = (to, from, except, desc) => {
31
+ if (from && typeof from === "object" || typeof from === "function") {
32
+ for (let key of __getOwnPropNames(from))
33
+ if (!__hasOwnProp.call(to, key) && key !== except)
34
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
260
35
  }
36
+ return to;
261
37
  };
38
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
39
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
262
40
  function parseAndValidateConfig(content) {
263
41
  let parsed;
264
42
  if (typeof content === "string") {
@@ -268,262 +46,508 @@ function parseAndValidateConfig(content) {
268
46
  }
269
47
  return InstrumentationConfigSchema.parse(parsed);
270
48
  }
271
- (class extends Data.TaggedError("ConfigError") {
272
- get message() {
273
- return this.reason;
274
- }
275
- });
276
- var ConfigUrlError = class extends Data.TaggedError("ConfigUrlError") {
277
- get message() {
278
- return this.reason;
279
- }
280
- };
281
- var ConfigValidationError = class extends Data.TaggedError("ConfigValidationError") {
282
- get message() {
283
- return this.reason;
284
- }
285
- };
286
- var ConfigFileError = class extends Data.TaggedError("ConfigFileError") {
287
- get message() {
288
- return this.reason;
289
- }
290
- };
291
- (class extends Data.TaggedError("ServiceDetectionError") {
292
- get message() {
293
- return this.reason;
294
- }
295
- });
296
- (class extends Data.TaggedError("InitializationError") {
297
- get message() {
298
- return this.reason;
299
- }
300
- });
301
- (class extends Data.TaggedError("ExportError") {
302
- get message() {
303
- return this.reason;
304
- }
305
- });
306
- (class extends Data.TaggedError("ShutdownError") {
307
- get message() {
308
- return this.reason;
309
- }
310
- });
311
- var SECURITY_DEFAULTS = {
312
- maxConfigSize: 1e6,
313
- // 1MB
314
- requestTimeout: 5e3
315
- // 5 seconds
316
- };
317
- var ConfigLoader = class extends Context.Tag("ConfigLoader")() {
318
- };
319
- var parseYamlContent = (content, uri) => Effect.gen(function* () {
320
- const parsed = yield* Effect.try({
321
- try: () => parse(content),
322
- catch: (error) => new ConfigValidationError({
323
- reason: uri ? `Failed to parse YAML from ${uri}` : "Failed to parse YAML",
324
- cause: error
325
- })
326
- });
327
- return yield* Effect.try({
328
- try: () => InstrumentationConfigSchema.parse(parsed),
329
- catch: (error) => new ConfigValidationError({
330
- reason: uri ? `Invalid configuration schema from ${uri}` : "Invalid configuration schema",
331
- cause: error
332
- })
333
- });
334
- });
335
- var loadFromFileWithFs = (fs3, path3, uri) => Effect.gen(function* () {
336
- const content = yield* fs3.readFileString(path3).pipe(
337
- Effect.mapError(
338
- (error) => new ConfigFileError({
339
- reason: `Failed to read config file at ${uri}`,
340
- cause: error
341
- })
342
- )
343
- );
344
- if (content.length > SECURITY_DEFAULTS.maxConfigSize) {
345
- return yield* Effect.fail(
346
- new ConfigFileError({
347
- reason: `Config file exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
348
- })
349
- );
350
- }
351
- return yield* parseYamlContent(content, uri);
352
- });
353
- var loadFromHttpWithClient = (client, url) => Effect.scoped(
354
- Effect.gen(function* () {
355
- if (url.startsWith("http://")) {
356
- return yield* Effect.fail(
357
- new ConfigUrlError({
358
- reason: "Insecure protocol: only HTTPS URLs are allowed"
49
+ var __defProp2, __defNormalProp2, __publicField2, PatternConfigSchema, AutoIsolationConfigSchema, SpanNamingRuleSchema, AutoInstrumentationConfigSchema, OperationTracingConfigSchema, HttpFilteringConfigSchema, ExporterConfigSchema, InstrumentationConfigSchema, defaultConfig, ConfigUrlError, ConfigValidationError, ConfigFileError, SECURITY_DEFAULTS, ConfigLoader, parseYamlContent, loadFromFileWithFs, loadFromHttpWithClient, makeConfigLoader, Logger, logger;
50
+ var init_dist = __esm({
51
+ "../core/target/dist/index.js"() {
52
+ __defProp2 = Object.defineProperty;
53
+ __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
54
+ __publicField2 = (obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
55
+ PatternConfigSchema = z.object({
56
+ pattern: z.string(),
57
+ enabled: z.boolean().optional(),
58
+ description: z.string().optional()
59
+ });
60
+ AutoIsolationConfigSchema = z.object({
61
+ // Global enable/disable for auto-isolation
62
+ enabled: z.boolean().default(false),
63
+ // Which operators to auto-isolate
64
+ operators: z.object({
65
+ fiberset_run: z.boolean().default(true),
66
+ effect_fork: z.boolean().default(true),
67
+ effect_fork_daemon: z.boolean().default(true),
68
+ effect_fork_in: z.boolean().default(false)
69
+ }).default({}),
70
+ // Virtual parent tracking configuration
71
+ tracking: z.object({
72
+ use_span_links: z.boolean().default(true),
73
+ use_attributes: z.boolean().default(true),
74
+ capture_logical_parent: z.boolean().default(true)
75
+ }).default({}),
76
+ // Span categorization
77
+ attributes: z.object({
78
+ category: z.string().default("background_task"),
79
+ add_metadata: z.boolean().default(true)
80
+ }).default({})
81
+ });
82
+ SpanNamingRuleSchema = z.object({
83
+ // Match criteria (all specified criteria must match)
84
+ match: z.object({
85
+ // Regex pattern to match file path
86
+ file: z.string().optional(),
87
+ // Regex pattern to match function name
88
+ function: z.string().optional(),
89
+ // Regex pattern to match module name
90
+ module: z.string().optional()
91
+ }),
92
+ // Span name template with variables:
93
+ // {fiber_id} - Fiber ID
94
+ // {function} - Function name
95
+ // {module} - Module name
96
+ // {file} - File path
97
+ // {line} - Line number
98
+ // {operator} - Effect operator (gen, all, forEach, etc.)
99
+ // {match:field:N} - Captured regex group from match
100
+ name: z.string()
101
+ });
102
+ AutoInstrumentationConfigSchema = z.object({
103
+ // Enable/disable auto-instrumentation
104
+ enabled: z.boolean().default(false),
105
+ // Tracing granularity
106
+ // - 'fiber': Trace at fiber creation (recommended, lower overhead)
107
+ // - 'operator': Trace each Effect operator (higher granularity, more overhead)
108
+ granularity: z.enum(["fiber", "operator"]).default("fiber"),
109
+ // Smart span naming configuration
110
+ span_naming: z.object({
111
+ // Default span name template when no rules match
112
+ default: z.string().default("effect.fiber.{fiber_id}"),
113
+ // Infer span names from source code (requires stack trace parsing)
114
+ // Adds ~50-100μs overhead per fiber
115
+ infer_from_source: z.boolean().default(true),
116
+ // Naming rules (first match wins)
117
+ rules: z.array(SpanNamingRuleSchema).default([])
118
+ }).default({}),
119
+ // Span relationship configuration for forked fibers
120
+ // Controls how child fiber spans relate to their parent/forking context
121
+ span_relationships: z.object({
122
+ // Relationship type between forked fiber spans and their parent context
123
+ // - 'parent-child': Use parent-child relationship (default, traditional tracing)
124
+ // Parent span shows child as nested. Works well with most observability tools.
125
+ // - 'span-links': Use span links (semantically correct for async forks per OTel spec)
126
+ // Fibers get independent traces linked to parent. Better for long-running fibers.
127
+ // - 'both': Create parent-child AND add span links
128
+ // Maximum visibility but may create redundant data.
129
+ type: z.enum(["parent-child", "span-links", "both"]).default("parent-child"),
130
+ // Custom attributes to add to span links (only used when type includes links)
131
+ link_attributes: z.object({
132
+ // Link type identifier
133
+ "link.type": z.string().default("fork"),
134
+ // Custom attributes (key-value pairs)
135
+ custom: z.record(z.string()).optional()
136
+ }).optional()
137
+ }).default({}),
138
+ // Pattern-based filtering
139
+ filter: z.object({
140
+ // Only trace spans matching these patterns (empty = trace all)
141
+ include: z.array(z.string()).default([]),
142
+ // Never trace spans matching these patterns
143
+ exclude: z.array(z.string()).default([])
144
+ }).default({}),
145
+ // Performance controls
146
+ performance: z.object({
147
+ // Sample rate (0.0 - 1.0)
148
+ sampling_rate: z.number().min(0).max(1).default(1),
149
+ // Skip fibers shorter than this duration (e.g., "10ms", "100 millis")
150
+ min_duration: z.string().default("0ms"),
151
+ // Maximum concurrent traced fibers (0 = unlimited)
152
+ max_concurrent: z.number().default(0)
153
+ }).default({}),
154
+ // Automatic metadata extraction
155
+ metadata: z.object({
156
+ // Extract Effect fiber information
157
+ fiber_info: z.boolean().default(true),
158
+ // Extract source location (file:line)
159
+ source_location: z.boolean().default(true),
160
+ // Extract parent fiber information
161
+ parent_fiber: z.boolean().default(true)
162
+ }).default({})
163
+ });
164
+ OperationTracingConfigSchema = z.object({
165
+ // Enable/disable operation tracing
166
+ enabled: z.boolean().default(false),
167
+ // Global span naming settings
168
+ span_naming: z.object({
169
+ // Include source location (file:line) in span name
170
+ // Default: true - produces "effect.all (index.ts:42)"
171
+ // When false - produces "effect.all"
172
+ include_location: z.boolean().default(true),
173
+ // Span name template with variables:
174
+ // {op} - Operation name (all, forEach, retry, etc.)
175
+ // {file} - Full file path
176
+ // {filename} - Just the filename (basename)
177
+ // {line} - Line number
178
+ // {column} - Column number
179
+ // Default: "effect.{op} ({filename}:{line})"
180
+ template: z.string().default("effect.{op} ({filename}:{line})")
181
+ }).default({}),
182
+ // Operations to trace
183
+ operations: z.array(
184
+ z.object({
185
+ // Operation name: 'all', 'forEach', 'retry', etc.
186
+ name: z.string(),
187
+ // Custom span name template (overrides global span_naming.template)
188
+ // Supports same variables: {op}, {file}, {filename}, {line}, {column}
189
+ span_name: z.string().optional(),
190
+ // Include item count in span attributes
191
+ include_count: z.boolean().default(true),
192
+ // Include stack trace in span attributes
193
+ include_stack: z.boolean().default(true)
359
194
  })
360
- );
361
- }
362
- const request = HttpClientRequest.get(url).pipe(
363
- HttpClientRequest.setHeaders({
364
- Accept: "application/yaml, text/yaml, application/x-yaml"
365
- })
366
- );
367
- const response = yield* client.execute(request).pipe(
368
- Effect.timeout(`${SECURITY_DEFAULTS.requestTimeout} millis`),
369
- Effect.mapError((error) => {
370
- if (error._tag === "TimeoutException") {
371
- return new ConfigUrlError({
372
- reason: `Config fetch timeout after ${SECURITY_DEFAULTS.requestTimeout}ms from ${url}`
373
- });
374
- }
375
- return new ConfigUrlError({
376
- reason: `Failed to load config from URL: ${url}`,
195
+ ).default([])
196
+ });
197
+ HttpFilteringConfigSchema = z.object({
198
+ // Patterns to ignore for outgoing HTTP requests (string patterns only in YAML)
199
+ ignore_outgoing_urls: z.array(z.string()).optional(),
200
+ // Patterns to ignore for incoming HTTP requests (string patterns only in YAML)
201
+ ignore_incoming_paths: z.array(z.string()).optional(),
202
+ // Require parent span for outgoing requests (prevents root spans for HTTP calls)
203
+ require_parent_for_outgoing_spans: z.boolean().optional(),
204
+ // Trace context propagation configuration
205
+ // Controls which cross-origin requests receive W3C Trace Context headers (traceparent, tracestate)
206
+ propagate_trace_context: z.object({
207
+ // Strategy for trace propagation
208
+ // - "all": Propagate to all cross-origin requests (may cause CORS errors)
209
+ // - "none": Never propagate trace headers
210
+ // - "same-origin": Only propagate to same-origin requests (default, safe)
211
+ // - "patterns": Propagate based on include_urls patterns
212
+ strategy: z.enum(["all", "none", "same-origin", "patterns"]).default("same-origin"),
213
+ // URL patterns to include when strategy is "patterns"
214
+ // Supports regex patterns (e.g., "^https://api\\.myapp\\.com")
215
+ include_urls: z.array(z.string()).optional()
216
+ }).optional()
217
+ });
218
+ ExporterConfigSchema = z.object({
219
+ // Exporter type: 'otlp' | 'console' | 'none'
220
+ // - 'otlp': Export to OTLP endpoint (production)
221
+ // - 'console': Log spans to console (development)
222
+ // - 'none': No export (disable tracing)
223
+ type: z.enum(["otlp", "console", "none"]).default("otlp"),
224
+ // OTLP endpoint URL (for type: otlp)
225
+ // Defaults to OTEL_EXPORTER_OTLP_ENDPOINT env var or http://localhost:4318
226
+ endpoint: z.string().optional(),
227
+ // Custom headers to send with OTLP requests (for type: otlp)
228
+ // Useful for authentication (x-api-key, Authorization, etc.)
229
+ headers: z.record(z.string()).optional(),
230
+ // Span processor type
231
+ // - 'batch': Batch spans for export (production, lower overhead)
232
+ // - 'simple': Export immediately (development, no batching delay)
233
+ processor: z.enum(["batch", "simple"]).default("batch"),
234
+ // Batch processor settings (for processor: batch)
235
+ batch: z.object({
236
+ // Max time to wait before exporting (milliseconds)
237
+ scheduled_delay_millis: z.number().default(1e3),
238
+ // Max batch size
239
+ max_export_batch_size: z.number().default(100)
240
+ }).optional()
241
+ });
242
+ InstrumentationConfigSchema = z.object({
243
+ version: z.string(),
244
+ instrumentation: z.object({
245
+ enabled: z.boolean(),
246
+ description: z.string().optional(),
247
+ logging: z.enum(["on", "off", "minimal"]).optional().default("on"),
248
+ instrument_patterns: z.array(PatternConfigSchema),
249
+ ignore_patterns: z.array(PatternConfigSchema)
250
+ }),
251
+ effect: z.object({
252
+ // Enable/disable Effect tracing entirely
253
+ // When false, EffectInstrumentationLive returns Layer.empty
254
+ enabled: z.boolean().default(true),
255
+ // Exporter mode (legacy - use exporter.type instead):
256
+ // - "unified": Use global TracerProvider from Node SDK (recommended, enables filtering)
257
+ // - "standalone": Use Effect's own OTLP exporter (bypasses Node SDK filtering)
258
+ exporter: z.enum(["unified", "standalone"]).default("unified"),
259
+ // Exporter configuration (for auto-instrumentation)
260
+ exporter_config: ExporterConfigSchema.optional(),
261
+ auto_extract_metadata: z.boolean(),
262
+ auto_isolation: AutoIsolationConfigSchema.optional(),
263
+ // Auto-instrumentation: automatic tracing of all Effect fibers
264
+ auto_instrumentation: AutoInstrumentationConfigSchema.optional(),
265
+ // Operation tracing: automatic tracing of Effect.all, Effect.forEach, etc.
266
+ operation_tracing: OperationTracingConfigSchema.optional()
267
+ }).optional(),
268
+ http: HttpFilteringConfigSchema.optional()
269
+ });
270
+ defaultConfig = {
271
+ version: "1.0",
272
+ instrumentation: {
273
+ enabled: true,
274
+ logging: "on",
275
+ description: "Default instrumentation configuration",
276
+ instrument_patterns: [
277
+ { pattern: "^app\\.", enabled: true, description: "Application operations" },
278
+ { pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
279
+ { pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
280
+ ],
281
+ ignore_patterns: [
282
+ { pattern: "^test\\.", description: "Test utilities" },
283
+ { pattern: "^internal\\.", description: "Internal operations" },
284
+ { pattern: "^health\\.", description: "Health checks" }
285
+ ]
286
+ },
287
+ effect: {
288
+ enabled: true,
289
+ exporter: "unified",
290
+ auto_extract_metadata: true
291
+ }
292
+ };
293
+ (class extends Data.TaggedError("ConfigError") {
294
+ get message() {
295
+ return this.reason;
296
+ }
297
+ });
298
+ ConfigUrlError = class extends Data.TaggedError("ConfigUrlError") {
299
+ get message() {
300
+ return this.reason;
301
+ }
302
+ };
303
+ ConfigValidationError = class extends Data.TaggedError("ConfigValidationError") {
304
+ get message() {
305
+ return this.reason;
306
+ }
307
+ };
308
+ ConfigFileError = class extends Data.TaggedError("ConfigFileError") {
309
+ get message() {
310
+ return this.reason;
311
+ }
312
+ };
313
+ (class extends Data.TaggedError("ServiceDetectionError") {
314
+ get message() {
315
+ return this.reason;
316
+ }
317
+ });
318
+ (class extends Data.TaggedError("InitializationError") {
319
+ get message() {
320
+ return this.reason;
321
+ }
322
+ });
323
+ (class extends Data.TaggedError("ExportError") {
324
+ get message() {
325
+ return this.reason;
326
+ }
327
+ });
328
+ (class extends Data.TaggedError("ShutdownError") {
329
+ get message() {
330
+ return this.reason;
331
+ }
332
+ });
333
+ SECURITY_DEFAULTS = {
334
+ maxConfigSize: 1e6,
335
+ // 1MB
336
+ requestTimeout: 5e3
337
+ // 5 seconds
338
+ };
339
+ ConfigLoader = class extends Context.Tag("ConfigLoader")() {
340
+ };
341
+ parseYamlContent = (content, uri) => Effect.gen(function* () {
342
+ const parsed = yield* Effect.try({
343
+ try: () => parse(content),
344
+ catch: (error) => new ConfigValidationError({
345
+ reason: uri ? `Failed to parse YAML from ${uri}` : "Failed to parse YAML",
377
346
  cause: error
378
- });
379
- })
380
- );
381
- if (response.status >= 400) {
382
- return yield* Effect.fail(
383
- new ConfigUrlError({
384
- reason: `HTTP ${response.status} from ${url}`
385
347
  })
386
- );
387
- }
388
- const text = yield* response.text.pipe(
389
- Effect.mapError(
390
- (error) => new ConfigUrlError({
391
- reason: `Failed to read response body from ${url}`,
348
+ });
349
+ return yield* Effect.try({
350
+ try: () => InstrumentationConfigSchema.parse(parsed),
351
+ catch: (error) => new ConfigValidationError({
352
+ reason: uri ? `Invalid configuration schema from ${uri}` : "Invalid configuration schema",
392
353
  cause: error
393
354
  })
394
- )
395
- );
396
- if (text.length > SECURITY_DEFAULTS.maxConfigSize) {
397
- return yield* Effect.fail(
398
- new ConfigUrlError({
399
- reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
400
- })
355
+ });
356
+ });
357
+ loadFromFileWithFs = (fs2, path3, uri) => Effect.gen(function* () {
358
+ const content = yield* fs2.readFileString(path3).pipe(
359
+ Effect.mapError(
360
+ (error) => new ConfigFileError({
361
+ reason: `Failed to read config file at ${uri}`,
362
+ cause: error
363
+ })
364
+ )
401
365
  );
402
- }
403
- return yield* parseYamlContent(text, url);
404
- })
405
- );
406
- var makeConfigLoader = Effect.gen(function* () {
407
- const fs3 = yield* Effect.serviceOption(FileSystem);
408
- const http = yield* HttpClient.HttpClient;
409
- const loadFromUriUncached = (uri) => Effect.gen(function* () {
410
- if (uri.startsWith("file://")) {
411
- const path3 = uri.slice(7);
412
- if (fs3._tag === "None") {
366
+ if (content.length > SECURITY_DEFAULTS.maxConfigSize) {
413
367
  return yield* Effect.fail(
414
368
  new ConfigFileError({
415
- reason: "FileSystem not available (browser environment?)",
416
- cause: { uri }
369
+ reason: `Config file exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
417
370
  })
418
371
  );
419
372
  }
420
- return yield* loadFromFileWithFs(fs3.value, path3, uri);
421
- }
422
- if (uri.startsWith("http://") || uri.startsWith("https://")) {
423
- return yield* loadFromHttpWithClient(http, uri);
424
- }
425
- if (fs3._tag === "Some") {
426
- return yield* loadFromFileWithFs(fs3.value, uri, uri);
427
- } else {
428
- return yield* loadFromHttpWithClient(http, uri);
429
- }
430
- });
431
- const loadFromUriCached = yield* Effect.cachedFunction(loadFromUriUncached);
432
- return ConfigLoader.of({
433
- loadFromUri: loadFromUriCached,
434
- loadFromInline: (content) => Effect.gen(function* () {
435
- if (typeof content === "string") {
436
- return yield* parseYamlContent(content);
437
- }
438
- return yield* Effect.try({
439
- try: () => InstrumentationConfigSchema.parse(content),
440
- catch: (error) => new ConfigValidationError({
441
- reason: "Invalid configuration schema",
442
- cause: error
373
+ return yield* parseYamlContent(content, uri);
374
+ });
375
+ loadFromHttpWithClient = (client, url) => Effect.scoped(
376
+ Effect.gen(function* () {
377
+ if (url.startsWith("http://")) {
378
+ return yield* Effect.fail(
379
+ new ConfigUrlError({
380
+ reason: "Insecure protocol: only HTTPS URLs are allowed"
381
+ })
382
+ );
383
+ }
384
+ const request = HttpClientRequest.get(url).pipe(
385
+ HttpClientRequest.setHeaders({
386
+ Accept: "application/yaml, text/yaml, application/x-yaml"
387
+ })
388
+ );
389
+ const response = yield* client.execute(request).pipe(
390
+ Effect.timeout(`${SECURITY_DEFAULTS.requestTimeout} millis`),
391
+ Effect.mapError((error) => {
392
+ if (error._tag === "TimeoutException") {
393
+ return new ConfigUrlError({
394
+ reason: `Config fetch timeout after ${SECURITY_DEFAULTS.requestTimeout}ms from ${url}`
395
+ });
396
+ }
397
+ return new ConfigUrlError({
398
+ reason: `Failed to load config from URL: ${url}`,
399
+ cause: error
400
+ });
401
+ })
402
+ );
403
+ if (response.status >= 400) {
404
+ return yield* Effect.fail(
405
+ new ConfigUrlError({
406
+ reason: `HTTP ${response.status} from ${url}`
407
+ })
408
+ );
409
+ }
410
+ const text = yield* response.text.pipe(
411
+ Effect.mapError(
412
+ (error) => new ConfigUrlError({
413
+ reason: `Failed to read response body from ${url}`,
414
+ cause: error
415
+ })
416
+ )
417
+ );
418
+ if (text.length > SECURITY_DEFAULTS.maxConfigSize) {
419
+ return yield* Effect.fail(
420
+ new ConfigUrlError({
421
+ reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
422
+ })
423
+ );
424
+ }
425
+ return yield* parseYamlContent(text, url);
426
+ })
427
+ );
428
+ makeConfigLoader = Effect.gen(function* () {
429
+ const fs2 = yield* Effect.serviceOption(FileSystem);
430
+ const http = yield* HttpClient.HttpClient;
431
+ const loadFromUriUncached = (uri) => Effect.gen(function* () {
432
+ if (uri.startsWith("file://")) {
433
+ const path3 = uri.slice(7);
434
+ if (fs2._tag === "None") {
435
+ return yield* Effect.fail(
436
+ new ConfigFileError({
437
+ reason: "FileSystem not available (browser environment?)",
438
+ cause: { uri }
439
+ })
440
+ );
441
+ }
442
+ return yield* loadFromFileWithFs(fs2.value, path3, uri);
443
+ }
444
+ if (uri.startsWith("http://") || uri.startsWith("https://")) {
445
+ return yield* loadFromHttpWithClient(http, uri);
446
+ }
447
+ if (fs2._tag === "Some") {
448
+ return yield* loadFromFileWithFs(fs2.value, uri, uri);
449
+ } else {
450
+ return yield* loadFromHttpWithClient(http, uri);
451
+ }
452
+ });
453
+ const loadFromUriCached = yield* Effect.cachedFunction(loadFromUriUncached);
454
+ return ConfigLoader.of({
455
+ loadFromUri: loadFromUriCached,
456
+ loadFromInline: (content) => Effect.gen(function* () {
457
+ if (typeof content === "string") {
458
+ return yield* parseYamlContent(content);
459
+ }
460
+ return yield* Effect.try({
461
+ try: () => InstrumentationConfigSchema.parse(content),
462
+ catch: (error) => new ConfigValidationError({
463
+ reason: "Invalid configuration schema",
464
+ cause: error
465
+ })
466
+ });
443
467
  })
444
468
  });
445
- })
446
- });
447
- });
448
- Layer.effect(ConfigLoader, makeConfigLoader);
449
- var Logger = class {
450
- constructor() {
451
- __publicField2(this, "level", "on");
452
- __publicField2(this, "hasLoggedMinimal", false);
453
- }
454
- /**
455
- * Set the logging level
456
- */
457
- setLevel(level) {
458
- this.level = level;
459
- this.hasLoggedMinimal = false;
460
- }
461
- /**
462
- * Get the current logging level
463
- */
464
- getLevel() {
465
- return this.level;
466
- }
467
- /**
468
- * Log a minimal initialization message (only shown once in minimal mode)
469
- */
470
- minimal(message) {
471
- if (this.level === "off") {
472
- return;
473
- }
474
- if (this.level === "minimal" && !this.hasLoggedMinimal) {
475
- console.log(message);
476
- this.hasLoggedMinimal = true;
477
- return;
478
- }
479
- if (this.level === "on") {
480
- console.log(message);
481
- }
482
- }
483
- /**
484
- * Log an informational message
485
- */
486
- log(...args) {
487
- if (this.level === "on") {
488
- console.log(...args);
489
- }
490
- }
491
- /**
492
- * Log a warning message (shown in minimal mode)
493
- */
494
- warn(...args) {
495
- if (this.level !== "off") {
496
- console.warn(...args);
497
- }
498
- }
499
- /**
500
- * Log an error message (shown in minimal mode)
501
- */
502
- error(...args) {
503
- if (this.level !== "off") {
504
- console.error(...args);
505
- }
506
- }
507
- /**
508
- * Check if full logging is enabled
509
- */
510
- isEnabled() {
511
- return this.level === "on";
512
- }
513
- /**
514
- * Check if minimal logging is enabled
515
- */
516
- isMinimal() {
517
- return this.level === "minimal";
518
- }
519
- /**
520
- * Check if logging is completely disabled
521
- */
522
- isDisabled() {
523
- return this.level === "off";
469
+ });
470
+ Layer.effect(ConfigLoader, makeConfigLoader);
471
+ Logger = class {
472
+ constructor() {
473
+ __publicField2(this, "level", "on");
474
+ __publicField2(this, "hasLoggedMinimal", false);
475
+ }
476
+ /**
477
+ * Set the logging level
478
+ */
479
+ setLevel(level) {
480
+ this.level = level;
481
+ this.hasLoggedMinimal = false;
482
+ }
483
+ /**
484
+ * Get the current logging level
485
+ */
486
+ getLevel() {
487
+ return this.level;
488
+ }
489
+ /**
490
+ * Log a minimal initialization message (only shown once in minimal mode)
491
+ */
492
+ minimal(message) {
493
+ if (this.level === "off") {
494
+ return;
495
+ }
496
+ if (this.level === "minimal" && !this.hasLoggedMinimal) {
497
+ console.log(message);
498
+ this.hasLoggedMinimal = true;
499
+ return;
500
+ }
501
+ if (this.level === "on") {
502
+ console.log(message);
503
+ }
504
+ }
505
+ /**
506
+ * Log an informational message
507
+ */
508
+ log(...args) {
509
+ if (this.level === "on") {
510
+ console.log(...args);
511
+ }
512
+ }
513
+ /**
514
+ * Log a warning message (shown in minimal mode)
515
+ */
516
+ warn(...args) {
517
+ if (this.level !== "off") {
518
+ console.warn(...args);
519
+ }
520
+ }
521
+ /**
522
+ * Log an error message (shown in minimal mode)
523
+ */
524
+ error(...args) {
525
+ if (this.level !== "off") {
526
+ console.error(...args);
527
+ }
528
+ }
529
+ /**
530
+ * Check if full logging is enabled
531
+ */
532
+ isEnabled() {
533
+ return this.level === "on";
534
+ }
535
+ /**
536
+ * Check if minimal logging is enabled
537
+ */
538
+ isMinimal() {
539
+ return this.level === "minimal";
540
+ }
541
+ /**
542
+ * Check if logging is completely disabled
543
+ */
544
+ isDisabled() {
545
+ return this.level === "off";
546
+ }
547
+ };
548
+ logger = new Logger();
524
549
  }
525
- };
526
- var logger = new Logger();
550
+ });
527
551
  async function loadFromFile(filePath) {
528
552
  const { readFile } = await import('fs/promises');
529
553
  const content = await readFile(filePath, "utf-8");
@@ -564,170 +588,589 @@ async function loadConfigWithOptions(options = {}) {
564
588
  if (options.configPath) {
565
589
  return loadConfig(options.configPath);
566
590
  }
567
- const { existsSync: existsSync3 } = await import('fs');
591
+ const { existsSync: existsSync2 } = await import('fs');
568
592
  const { join: join2 } = await import('path');
569
593
  const defaultPath = join2(process.cwd(), "instrumentation.yaml");
570
- if (existsSync3(defaultPath)) {
594
+ if (existsSync2(defaultPath)) {
571
595
  return loadConfig(defaultPath);
572
596
  }
573
597
  return defaultConfig;
574
598
  }
575
-
576
- // src/integrations/effect/auto/config.ts
577
- var defaultAutoTracingConfig = {
578
- enabled: false,
579
- granularity: "fiber",
580
- span_naming: {
581
- default: "effect.fiber.{fiber_id}",
582
- infer_from_source: true,
583
- rules: []
584
- },
585
- span_relationships: {
586
- type: "parent-child"
587
- },
588
- filter: {
589
- include: [],
590
- exclude: []
591
- },
592
- performance: {
593
- sampling_rate: 1,
594
- min_duration: "0ms",
595
- max_concurrent: 0
596
- },
597
- metadata: {
598
- fiber_info: true,
599
- source_location: true,
600
- parent_fiber: true
601
- }
602
- };
603
- var AutoTracingConfig = class extends Context.Tag("AutoTracingConfig")() {
604
- };
605
- var loadAutoTracingConfig = (options) => Effect.gen(function* () {
606
- const config = yield* Effect.tryPromise({
607
- try: () => loadConfigWithOptions(options),
608
- catch: (error) => {
609
- logger.log(`@atrim/auto-trace: Failed to load config: ${error}`);
610
- return error;
611
- }
612
- }).pipe(Effect.catchAll(() => Effect.succeed(null)));
613
- if (!config) {
614
- logger.log("@atrim/auto-trace: No config found, using defaults");
615
- return defaultAutoTracingConfig;
599
+ var init_config_loader = __esm({
600
+ "src/core/config-loader.ts"() {
601
+ init_dist();
616
602
  }
617
- const autoConfig = config.effect?.auto_instrumentation;
618
- if (!autoConfig) {
619
- logger.log("@atrim/auto-trace: No auto_instrumentation config, using defaults");
620
- return defaultAutoTracingConfig;
621
- }
622
- const parsed = AutoInstrumentationConfigSchema.safeParse(autoConfig);
623
- if (!parsed.success) {
624
- logger.log(`@atrim/auto-trace: Invalid config, using defaults: ${parsed.error.message}`);
625
- return defaultAutoTracingConfig;
603
+ });
604
+ var defaultAutoTracingConfig, AutoTracingConfig, loadAutoTracingConfig, loadAutoTracingConfigSync, AutoTracingConfigLive, AutoTracingConfigLayer, loadFullConfigSync;
605
+ var init_config = __esm({
606
+ "src/integrations/effect/auto/config.ts"() {
607
+ init_dist();
608
+ init_config_loader();
609
+ defaultAutoTracingConfig = {
610
+ enabled: false,
611
+ granularity: "fiber",
612
+ span_naming: {
613
+ default: "effect.fiber.{fiber_id}",
614
+ infer_from_source: true,
615
+ rules: []
616
+ },
617
+ span_relationships: {
618
+ type: "parent-child"
619
+ },
620
+ filter: {
621
+ include: [],
622
+ exclude: []
623
+ },
624
+ performance: {
625
+ sampling_rate: 1,
626
+ min_duration: "0ms",
627
+ max_concurrent: 0
628
+ },
629
+ metadata: {
630
+ fiber_info: true,
631
+ source_location: true,
632
+ parent_fiber: true
633
+ }
634
+ };
635
+ AutoTracingConfig = class extends Context.Tag("AutoTracingConfig")() {
636
+ };
637
+ loadAutoTracingConfig = (options) => Effect.gen(function* () {
638
+ const config = yield* Effect.tryPromise({
639
+ try: () => loadConfigWithOptions(options),
640
+ catch: (error) => {
641
+ logger.log(`@atrim/auto-trace: Failed to load config: ${error}`);
642
+ return error;
643
+ }
644
+ }).pipe(Effect.catchAll(() => Effect.succeed(null)));
645
+ if (!config) {
646
+ logger.log("@atrim/auto-trace: No config found, using defaults");
647
+ return defaultAutoTracingConfig;
648
+ }
649
+ const autoConfig = config.effect?.auto_instrumentation;
650
+ if (!autoConfig) {
651
+ logger.log("@atrim/auto-trace: No auto_instrumentation config, using defaults");
652
+ return defaultAutoTracingConfig;
653
+ }
654
+ const parsed = AutoInstrumentationConfigSchema.safeParse(autoConfig);
655
+ if (!parsed.success) {
656
+ logger.log(`@atrim/auto-trace: Invalid config, using defaults: ${parsed.error.message}`);
657
+ return defaultAutoTracingConfig;
658
+ }
659
+ logger.log("@atrim/auto-trace: Loaded config from instrumentation.yaml");
660
+ return parsed.data;
661
+ });
662
+ loadAutoTracingConfigSync = () => {
663
+ return defaultAutoTracingConfig;
664
+ };
665
+ AutoTracingConfigLive = Layer.effect(AutoTracingConfig, loadAutoTracingConfig());
666
+ AutoTracingConfigLayer = (config) => Layer.succeed(AutoTracingConfig, config);
667
+ loadFullConfigSync = () => {
668
+ const defaultPath = path2.join(process.cwd(), "instrumentation.yaml");
669
+ try {
670
+ if (fs.existsSync(defaultPath)) {
671
+ const content = fs.readFileSync(defaultPath, "utf-8");
672
+ const parsed = yaml.parse(content);
673
+ const result = InstrumentationConfigSchema.safeParse(parsed);
674
+ if (result.success) {
675
+ logger.log(`@atrim/auto-trace: Loaded config from ${defaultPath}`);
676
+ return result.data;
677
+ } else {
678
+ logger.log(`@atrim/auto-trace: Invalid config, using defaults: ${result.error.message}`);
679
+ return defaultConfig;
680
+ }
681
+ }
682
+ } catch (error) {
683
+ logger.log(`@atrim/auto-trace: Failed to load config: ${error}`);
684
+ }
685
+ logger.log("@atrim/auto-trace: No config found, using defaults");
686
+ return defaultConfig;
687
+ };
626
688
  }
627
- logger.log("@atrim/auto-trace: Loaded config from instrumentation.yaml");
628
- return parsed.data;
629
689
  });
630
- var loadAutoTracingConfigSync = () => {
631
- return defaultAutoTracingConfig;
632
- };
633
- var AutoTracingConfigLive = Layer.effect(AutoTracingConfig, loadAutoTracingConfig());
634
- var AutoTracingConfigLayer = (config) => Layer.succeed(AutoTracingConfig, config);
635
- var loadFullConfig = (options) => Effect.gen(function* () {
636
- const config = yield* Effect.tryPromise({
637
- try: () => loadConfigWithOptions(options),
638
- catch: (error) => {
639
- logger.log(`@atrim/auto-trace: Failed to load config: ${error}`);
640
- return error;
641
- }
642
- }).pipe(Effect.catchAll(() => Effect.succeed(null)));
643
- if (!config) {
644
- logger.log("@atrim/auto-trace: No config found, using defaults");
645
- return defaultConfig;
690
+ var AutoTracingEnabled, AutoTracingSpanName, withoutAutoTracing, setSpanName;
691
+ var init_span_control = __esm({
692
+ "src/integrations/effect/auto/span-control.ts"() {
693
+ AutoTracingEnabled = FiberRef.unsafeMake(true);
694
+ AutoTracingSpanName = FiberRef.unsafeMake(Option.none());
695
+ withoutAutoTracing = (effect) => effect.pipe(Effect.locally(AutoTracingEnabled, false));
696
+ setSpanName = (name) => (effect) => effect.pipe(Effect.locally(AutoTracingSpanName, Option.some(name)));
646
697
  }
647
- return config;
648
698
  });
649
- var loadFullConfigSync = () => {
650
- const defaultPath = path2.join(process.cwd(), "instrumentation.yaml");
651
- try {
652
- if (fs.existsSync(defaultPath)) {
653
- const content = fs.readFileSync(defaultPath, "utf-8");
654
- const parsed = yaml.parse(content);
655
- const result = InstrumentationConfigSchema.safeParse(parsed);
656
- if (result.success) {
657
- logger.log(`@atrim/auto-trace: Loaded config from ${defaultPath}`);
658
- return result.data;
659
- } else {
660
- logger.log(`@atrim/auto-trace: Invalid config, using defaults: ${result.error.message}`);
661
- return defaultConfig;
662
- }
663
- }
664
- } catch (error) {
665
- logger.log(`@atrim/auto-trace: Failed to load config: ${error}`);
699
+
700
+ // src/integrations/effect/auto/unified-tracing-supervisor.ts
701
+ var unified_tracing_supervisor_exports = {};
702
+ __export(unified_tracing_supervisor_exports, {
703
+ AutoTracingEnabled: () => AutoTracingEnabled,
704
+ AutoTracingSpanName: () => AutoTracingSpanName,
705
+ UnifiedTracingLive: () => UnifiedTracingLive,
706
+ UnifiedTracingSupervisor: () => UnifiedTracingSupervisor,
707
+ createUnifiedTracingLayer: () => createUnifiedTracingLayer,
708
+ enableOpSupervision: () => enableOpSupervision,
709
+ flushAndShutdown: () => flushAndShutdown,
710
+ forceFlush: () => forceFlush,
711
+ setSpanName: () => setSpanName,
712
+ withAutoTracing: () => withAutoTracing,
713
+ withUnifiedTracing: () => withUnifiedTracing,
714
+ withoutAutoTracing: () => withoutAutoTracing
715
+ });
716
+ function getOperationMeta(effect) {
717
+ const trace2 = effect.trace;
718
+ if (trace2 && trace2._tag === "OperationMeta") {
719
+ return trace2;
666
720
  }
667
- logger.log("@atrim/auto-trace: No config found, using defaults");
668
- return defaultConfig;
669
- };
670
- var CapturedSourceLocation = FiberRef.unsafeMake(void 0);
671
- function captureCallSite(skipFrames = 0) {
672
- const originalLimit = Error.stackTraceLimit;
673
- Error.stackTraceLimit = 10;
674
- const error = new Error();
675
- Error.stackTraceLimit = originalLimit;
676
- if (!error.stack) return void 0;
677
- const lines = error.stack.split("\n");
678
- const startIndex = 3 + skipFrames;
679
- for (let i = startIndex; i < lines.length; i++) {
680
- const line = lines[i];
681
- if (line === void 0) continue;
682
- if (line.includes("@atrim/instrument") || line.includes("node_modules/effect")) {
721
+ return void 0;
722
+ }
723
+ function parseSourceLocation(stack) {
724
+ const lines = stack.split("\n");
725
+ for (const line of lines.slice(1)) {
726
+ if (line.includes("node_modules") || line.includes("/effect/") || line.includes("fiberRuntime.ts") || line.includes("core.ts")) {
683
727
  continue;
684
728
  }
685
- const match = line.match(/at\s+(?:(.+?)\s+)?\(?(.+?):(\d+):(\d+)\)?/);
686
- if (match) {
687
- const [, funcName, filePath, lineNum, colNum] = match;
729
+ const match = line.match(/at\s+(?:.*?\s+\()?(.+):(\d+):(\d+)\)?/);
730
+ if (match && match[1] && match[2]) {
688
731
  return {
689
- function: funcName?.trim() ?? "anonymous",
690
- file: filePath ?? "unknown",
691
- line: parseInt(lineNum ?? "0", 10),
692
- column: parseInt(colNum ?? "0", 10)
732
+ file: match[1],
733
+ line: parseInt(match[2], 10),
734
+ ...match[3] ? { column: parseInt(match[3], 10) } : {}
693
735
  };
694
736
  }
695
737
  }
696
738
  return void 0;
697
739
  }
698
- var tracedFork = (effect) => {
699
- const callSite = captureCallSite();
700
- if (!callSite) {
701
- return Effect.fork(effect);
702
- }
703
- return FiberRef.set(CapturedSourceLocation, callSite).pipe(Effect.zipRight(Effect.fork(effect)));
704
- };
705
- var tracedForkDaemon = (effect) => {
706
- const callSite = captureCallSite();
707
- if (!callSite) {
708
- return Effect.forkDaemon(effect);
709
- }
710
- return FiberRef.set(CapturedSourceLocation, callSite).pipe(
711
- Effect.zipRight(Effect.forkDaemon(effect))
712
- );
713
- };
714
-
715
- // src/integrations/effect/auto/patch-fork.ts
716
- var patchAttempted = false;
717
- function isEffectForkPatched() {
718
- return false;
740
+ function makeSourceKey(source) {
741
+ if (!source) return "unknown";
742
+ return `${source.file}:${source.line}`;
719
743
  }
720
- function patchEffectFork() {
721
- if (patchAttempted) {
722
- return;
744
+ function formatLocation(source) {
745
+ if (!source) return "unknown";
746
+ const filename = source.file.split("/").pop() ?? source.file;
747
+ return `${filename}:${source.line}`;
748
+ }
749
+ function getGlobalProviderSetup() {
750
+ if (_globalProviderSetup === void 0) {
751
+ _globalProviderSetup = setupGlobalTracerProvider();
723
752
  }
724
- patchAttempted = true;
725
- logger.log("@atrim/auto-trace: Effect.fork auto-patching not available (ESM limitation)");
726
- logger.log("@atrim/auto-trace: Use tracedFork() for call-site capture");
727
- }
728
- function unpatchEffectFork() {
729
- patchAttempted = false;
753
+ return _globalProviderSetup;
730
754
  }
755
+ var currentSourceLocation, UnifiedTracingSupervisor, setupGlobalTracerProvider, _globalProviderSetup, createUnifiedTracingLayer, UnifiedTracingLive, enableOpSupervision, withUnifiedTracing, withAutoTracing, flushAndShutdown, forceFlush;
756
+ var init_unified_tracing_supervisor = __esm({
757
+ "src/integrations/effect/auto/unified-tracing-supervisor.ts"() {
758
+ init_dist();
759
+ init_config();
760
+ init_span_control();
761
+ init_span_control();
762
+ currentSourceLocation = GlobalValue.globalValue(
763
+ /* @__PURE__ */ Symbol.for("effect/FiberRef/currentSourceLocation"),
764
+ () => FiberRef.unsafeMake(void 0)
765
+ );
766
+ UnifiedTracingSupervisor = class extends Supervisor.AbstractSupervisor {
767
+ constructor(config) {
768
+ super();
769
+ this.config = config;
770
+ // ========== Fork span registry ==========
771
+ // Maps sourceKey -> pending fork span (for correlating with child fibers)
772
+ __publicField(this, "pendingForkSpans", /* @__PURE__ */ new Map());
773
+ // ========== Fiber tracking ==========
774
+ __publicField(this, "fiberSpans", /* @__PURE__ */ new WeakMap());
775
+ __publicField(this, "fiberContexts", /* @__PURE__ */ new WeakMap());
776
+ __publicField(this, "fiberStartTimes", /* @__PURE__ */ new WeakMap());
777
+ // ========== Operation tracking ==========
778
+ __publicField(this, "processedEffects", /* @__PURE__ */ new WeakSet());
779
+ __publicField(this, "configuredOps");
780
+ // ========== OTel ==========
781
+ __publicField(this, "_tracer", null);
782
+ __publicField(this, "activeFiberCount", 0);
783
+ const defaultOps = [
784
+ { name: "all", includeCount: true, includeStack: true },
785
+ { name: "forEach", includeCount: true, includeStack: true },
786
+ { name: "fork", includeStack: true }
787
+ ];
788
+ this.configuredOps = new Map(defaultOps.map((op) => [op.name, op]));
789
+ logger.log("@atrim/unified-tracing: Supervisor initialized");
790
+ logger.log(` Operations: ${Array.from(this.configuredOps.keys()).join(", ")}`);
791
+ }
792
+ get tracer() {
793
+ if (!this._tracer) {
794
+ this._tracer = OtelApi.trace.getTracer("@atrim/unified-tracing", "1.0.0");
795
+ }
796
+ return this._tracer;
797
+ }
798
+ get value() {
799
+ return Effect.void;
800
+ }
801
+ // ==========================================================================
802
+ // onEffect - Operation-level tracing (Effect.all, Effect.forEach, Effect.fork)
803
+ // ==========================================================================
804
+ onEffect(fiber, effect) {
805
+ if (this.processedEffects.has(effect)) {
806
+ return;
807
+ }
808
+ const meta = getOperationMeta(effect);
809
+ if (!meta) {
810
+ return;
811
+ }
812
+ const opConfig = this.configuredOps.get(meta.op);
813
+ if (!opConfig) {
814
+ return;
815
+ }
816
+ this.processedEffects.add(effect);
817
+ const fiberId = fiber.id().id;
818
+ const location = parseSourceLocation(meta.capturedAt);
819
+ const sourceKey = makeSourceKey(location);
820
+ logger.log(`@atrim/unified-tracing: onEffect "${meta.op}" in fiber ${fiberId} at ${sourceKey}`);
821
+ const parentContext = this.resolveParentContext(fiber);
822
+ const spanName = location ? `effect.${meta.op} (${formatLocation(location)})` : `effect.${meta.op}`;
823
+ const span = this.tracer.startSpan(spanName, { kind: OtelApi.SpanKind.INTERNAL }, parentContext);
824
+ span.setAttribute("effect.operation", meta.op);
825
+ if (opConfig.includeCount && meta.count !== void 0) {
826
+ span.setAttribute("effect.item_count", meta.count);
827
+ }
828
+ if (opConfig.includeStack && location) {
829
+ span.setAttribute("code.filepath", location.file);
830
+ span.setAttribute("code.lineno", location.line);
831
+ if (location.column !== void 0) {
832
+ span.setAttribute("code.column", location.column);
833
+ }
834
+ }
835
+ logger.log(
836
+ `@atrim/unified-tracing: Created operation span "${spanName}" spanId=${span.spanContext().spanId}`
837
+ );
838
+ if (meta.op === "fork") {
839
+ const forkContext = OtelApi.trace.setSpan(parentContext, span);
840
+ this.pendingForkSpans.set(sourceKey, {
841
+ forkSpan: span,
842
+ forkContext,
843
+ timestamp: process.hrtime.bigint(),
844
+ spanEnded: false
845
+ });
846
+ logger.log(`@atrim/unified-tracing: Registered pending fork at ${sourceKey}`);
847
+ } else {
848
+ span.setStatus({ code: OtelApi.SpanStatusCode.OK });
849
+ span.end();
850
+ }
851
+ }
852
+ // ==========================================================================
853
+ // onStart - Fiber-level tracing
854
+ // ==========================================================================
855
+ onStart(_context, _effect, parent, fiber) {
856
+ const fiberId = fiber.id().id;
857
+ const fiberRefs = fiber.getFiberRefs();
858
+ const enabled = FiberRefs.getOrDefault(fiberRefs, AutoTracingEnabled);
859
+ if (!enabled) {
860
+ logger.log(`@atrim/unified-tracing: Auto-tracing disabled for fiber ${fiberId}`);
861
+ return;
862
+ }
863
+ const samplingRate = this.config.performance?.sampling_rate ?? 1;
864
+ if (samplingRate < 1 && Math.random() > samplingRate) {
865
+ return;
866
+ }
867
+ const nameOverride = FiberRefs.getOrDefault(fiberRefs, AutoTracingSpanName);
868
+ const sourceLocation = FiberRefs.getOrDefault(fiberRefs, currentSourceLocation);
869
+ const sourceKey = makeSourceKey(sourceLocation);
870
+ logger.log(`@atrim/unified-tracing: onStart fiber ${fiberId} at ${sourceKey}`);
871
+ let parentContext;
872
+ const pendingFork = this.pendingForkSpans.get(sourceKey);
873
+ if (pendingFork) {
874
+ parentContext = pendingFork.forkContext;
875
+ logger.log(
876
+ `@atrim/unified-tracing: Matched fiber ${fiberId} to fork span at ${sourceKey} - using fork span as parent`
877
+ );
878
+ if (!pendingFork.spanEnded) {
879
+ pendingFork.forkSpan.setStatus({ code: OtelApi.SpanStatusCode.OK });
880
+ pendingFork.forkSpan.end();
881
+ logger.log(`@atrim/unified-tracing: Ended fork span at ${sourceKey}`);
882
+ }
883
+ this.pendingForkSpans.delete(sourceKey);
884
+ } else {
885
+ parentContext = this.resolveParentContextFromFiberRefs(
886
+ fiberRefs,
887
+ _context,
888
+ parent
889
+ );
890
+ }
891
+ let spanName;
892
+ if (Option.isSome(nameOverride)) {
893
+ spanName = nameOverride.value;
894
+ } else if (sourceLocation) {
895
+ spanName = `effect.fiber (${formatLocation(sourceLocation)})`;
896
+ } else {
897
+ spanName = `effect.fiber-${fiberId}`;
898
+ }
899
+ const span = this.tracer.startSpan(spanName, { kind: OtelApi.SpanKind.INTERNAL }, parentContext);
900
+ span.setAttribute("effect.auto_traced", true);
901
+ span.setAttribute("effect.fiber.id", fiberId);
902
+ if (sourceLocation) {
903
+ span.setAttribute("code.filepath", sourceLocation.file);
904
+ span.setAttribute("code.lineno", sourceLocation.line);
905
+ if (sourceLocation.column !== void 0) {
906
+ span.setAttribute("code.column", sourceLocation.column);
907
+ }
908
+ }
909
+ if (Option.isSome(parent)) {
910
+ span.setAttribute("effect.fiber.parent_id", parent.value.id().id);
911
+ }
912
+ logger.log(
913
+ `@atrim/unified-tracing: Created fiber span "${spanName}" spanId=${span.spanContext().spanId}`
914
+ );
915
+ const newContext = OtelApi.trace.setSpan(parentContext, span);
916
+ this.fiberSpans.set(fiber, span);
917
+ this.fiberContexts.set(fiber, newContext);
918
+ this.fiberStartTimes.set(fiber, process.hrtime.bigint());
919
+ this.activeFiberCount++;
920
+ }
921
+ // ==========================================================================
922
+ // onEnd - Fiber completion
923
+ // ==========================================================================
924
+ onEnd(exit, fiber) {
925
+ const fiberId = fiber.id().id;
926
+ const span = this.fiberSpans.get(fiber);
927
+ if (!span) {
928
+ return;
929
+ }
930
+ const startTime = this.fiberStartTimes.get(fiber);
931
+ if (startTime) {
932
+ const duration = process.hrtime.bigint() - startTime;
933
+ const minDuration = this.parseMinDuration(this.config.performance?.min_duration);
934
+ if (minDuration > 0 && duration < minDuration) {
935
+ this.cleanup(fiber);
936
+ return;
937
+ }
938
+ }
939
+ if (Exit.isSuccess(exit)) {
940
+ span.setStatus({ code: OtelApi.SpanStatusCode.OK });
941
+ } else {
942
+ span.setStatus({ code: OtelApi.SpanStatusCode.ERROR, message: "Fiber failed" });
943
+ span.setAttribute("effect.fiber.failed", true);
944
+ }
945
+ span.end();
946
+ logger.log(`@atrim/unified-tracing: Ended fiber span for fiber ${fiberId}`);
947
+ this.cleanup(fiber);
948
+ }
949
+ // ==========================================================================
950
+ // Helpers
951
+ // ==========================================================================
952
+ cleanup(fiber) {
953
+ this.fiberSpans.delete(fiber);
954
+ this.fiberContexts.delete(fiber);
955
+ this.fiberStartTimes.delete(fiber);
956
+ this.activeFiberCount--;
957
+ }
958
+ resolveParentContext(fiber) {
959
+ const fiberContext = fiber.currentContext;
960
+ const maybeParentSpan = Context.getOption(fiberContext, Tracer.ParentSpan);
961
+ if (Option.isSome(maybeParentSpan)) {
962
+ const effectSpan = maybeParentSpan.value;
963
+ return OtelApi.trace.setSpan(
964
+ OtelApi.ROOT_CONTEXT,
965
+ OtelApi.trace.wrapSpanContext({
966
+ traceId: effectSpan.traceId,
967
+ spanId: effectSpan.spanId,
968
+ traceFlags: OtelApi.TraceFlags.SAMPLED
969
+ })
970
+ );
971
+ }
972
+ return OtelApi.ROOT_CONTEXT;
973
+ }
974
+ resolveParentContextFromFiberRefs(fiberRefs, context2, parent) {
975
+ const inheritedCtx = FiberRefs.getOrDefault(fiberRefs, TracerModule.currentOtelSpanContext);
976
+ if (inheritedCtx) {
977
+ const span = OtelApi.trace.getSpan(inheritedCtx);
978
+ if (span) {
979
+ logger.log(
980
+ `@atrim/unified-tracing: Using inherited OTel context from FiberRef - spanId=${span.spanContext().spanId}`
981
+ );
982
+ }
983
+ return inheritedCtx;
984
+ }
985
+ const maybeParentSpan = Context.getOption(context2, Tracer.ParentSpan);
986
+ if (Option.isSome(maybeParentSpan)) {
987
+ const effectSpan = maybeParentSpan.value;
988
+ logger.log(
989
+ `@atrim/unified-tracing: Using Effect ParentSpan - traceId=${effectSpan.traceId.slice(0, 8)}..., spanId=${effectSpan.spanId.slice(0, 8)}...`
990
+ );
991
+ return OtelApi.trace.setSpan(
992
+ OtelApi.ROOT_CONTEXT,
993
+ OtelApi.trace.wrapSpanContext({
994
+ traceId: effectSpan.traceId,
995
+ spanId: effectSpan.spanId,
996
+ traceFlags: OtelApi.TraceFlags.SAMPLED
997
+ })
998
+ );
999
+ }
1000
+ const activeContext = OtelApi.context.active();
1001
+ const activeSpan = OtelApi.trace.getSpan(activeContext);
1002
+ if (activeSpan && activeSpan.spanContext().traceFlags === OtelApi.TraceFlags.SAMPLED) {
1003
+ logger.log(
1004
+ `@atrim/unified-tracing: Auto-bridging from active OTel context - spanId=${activeSpan.spanContext().spanId}`
1005
+ );
1006
+ return activeContext;
1007
+ }
1008
+ if (Option.isSome(parent)) {
1009
+ const parentCtx = this.fiberContexts.get(parent.value);
1010
+ if (parentCtx) {
1011
+ logger.log(`@atrim/unified-tracing: Using parent fiber's context`);
1012
+ return parentCtx;
1013
+ }
1014
+ }
1015
+ return OtelApi.ROOT_CONTEXT;
1016
+ }
1017
+ parseMinDuration(duration) {
1018
+ if (!duration || duration === "0ms") return BigInt(0);
1019
+ const match = duration.match(/^(\d+)\s*(ms|millis|s|sec|seconds|us|micros)?$/i);
1020
+ if (!match) return BigInt(0);
1021
+ const value = parseInt(match[1] ?? "0", 10);
1022
+ const unit = (match[2] ?? "ms").toLowerCase();
1023
+ switch (unit) {
1024
+ case "us":
1025
+ case "micros":
1026
+ return BigInt(value) * BigInt(1e3);
1027
+ case "ms":
1028
+ case "millis":
1029
+ return BigInt(value) * BigInt(1e6);
1030
+ case "s":
1031
+ case "sec":
1032
+ case "seconds":
1033
+ return BigInt(value) * BigInt(1e9);
1034
+ default:
1035
+ return BigInt(value) * BigInt(1e6);
1036
+ }
1037
+ }
1038
+ };
1039
+ setupGlobalTracerProvider = () => {
1040
+ const config = loadFullConfigSync();
1041
+ const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
1042
+ const serviceVersion = process.env.npm_package_version || "1.0.0";
1043
+ const exporterConfig = config.effect?.exporter_config ?? {
1044
+ type: "otlp",
1045
+ processor: "batch"
1046
+ };
1047
+ logger.log("@atrim/unified-tracing: Setting up global TracerProvider");
1048
+ logger.log(` Service: ${serviceName}`);
1049
+ logger.log(` Exporter: ${exporterConfig.type}`);
1050
+ if (exporterConfig.type === "none") {
1051
+ logger.log('@atrim/unified-tracing: Exporter type is "none", skipping provider setup');
1052
+ return null;
1053
+ }
1054
+ let exporter;
1055
+ if (exporterConfig.type === "console") {
1056
+ exporter = new ConsoleSpanExporter();
1057
+ } else {
1058
+ const endpoint = exporterConfig.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
1059
+ const otlpConfig = {
1060
+ url: `${endpoint}/v1/traces`
1061
+ };
1062
+ if (exporterConfig.headers) {
1063
+ otlpConfig.headers = exporterConfig.headers;
1064
+ }
1065
+ exporter = new OTLPTraceExporter(otlpConfig);
1066
+ }
1067
+ let spanProcessor;
1068
+ if (exporterConfig.processor === "simple" || exporterConfig.type === "console") {
1069
+ spanProcessor = new SimpleSpanProcessor(exporter);
1070
+ } else {
1071
+ const batchConfig = exporterConfig.batch ?? {
1072
+ scheduled_delay_millis: 1e3,
1073
+ max_export_batch_size: 100
1074
+ };
1075
+ spanProcessor = new BatchSpanProcessor(exporter, {
1076
+ scheduledDelayMillis: batchConfig.scheduled_delay_millis,
1077
+ maxExportBatchSize: batchConfig.max_export_batch_size
1078
+ });
1079
+ }
1080
+ const provider = new BasicTracerProvider({
1081
+ resource: resourceFromAttributes({
1082
+ [ATTR_SERVICE_NAME]: serviceName,
1083
+ [ATTR_SERVICE_VERSION]: serviceVersion
1084
+ }),
1085
+ spanProcessors: [spanProcessor]
1086
+ });
1087
+ OtelApi.trace.setGlobalTracerProvider(provider);
1088
+ logger.log("@atrim/unified-tracing: Global TracerProvider registered");
1089
+ return { provider, spanProcessor };
1090
+ };
1091
+ createUnifiedTracingLayer = () => {
1092
+ const config = loadFullConfigSync();
1093
+ const autoConfig = config.effect?.auto_instrumentation ?? {
1094
+ enabled: true,
1095
+ granularity: "fiber",
1096
+ span_naming: { default: "effect.{function}", infer_from_source: true, rules: [] },
1097
+ span_relationships: { type: "parent-child" },
1098
+ filter: { include: [], exclude: [] },
1099
+ performance: { sampling_rate: 1, min_duration: "0ms", max_concurrent: 0 },
1100
+ metadata: { fiber_info: true, source_location: true, parent_fiber: true }
1101
+ };
1102
+ if (!getGlobalProviderSetup()) {
1103
+ logger.log("@atrim/unified-tracing: No TracerProvider, using empty layer");
1104
+ return Layer.empty;
1105
+ }
1106
+ const supervisor = new UnifiedTracingSupervisor(autoConfig);
1107
+ const supervisorLayer = Supervisor.addSupervisor(supervisor);
1108
+ const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
1109
+ const serviceVersion = process.env.npm_package_version || "1.0.0";
1110
+ const resourceLayer = Resource.layer({ serviceName, serviceVersion });
1111
+ const effectTracerLayer = Tracer$1.layerGlobal;
1112
+ const tracerWithResource = effectTracerLayer.pipe(Layer.provide(resourceLayer));
1113
+ const opSupervisionLayer = Layer.effectDiscard(
1114
+ Effect.patchRuntimeFlags(RuntimeFlagsPatch.enable(RuntimeFlags.OpSupervision))
1115
+ );
1116
+ return Layer.mergeAll(
1117
+ Layer.discard(tracerWithResource),
1118
+ Layer.enableSourceCapture,
1119
+ supervisorLayer,
1120
+ opSupervisionLayer
1121
+ );
1122
+ };
1123
+ UnifiedTracingLive = Layer.suspend(
1124
+ () => createUnifiedTracingLayer()
1125
+ );
1126
+ enableOpSupervision = (effect) => Effect.withRuntimeFlagsPatch(effect, RuntimeFlagsPatch.enable(RuntimeFlags.OpSupervision));
1127
+ withUnifiedTracing = (effect) => effect.pipe(enableOpSupervision, Effect.provide(UnifiedTracingLive));
1128
+ withAutoTracing = (effect, config, rootSpanName) => {
1129
+ const autoConfig = config ?? {
1130
+ enabled: true,
1131
+ granularity: "fiber",
1132
+ span_naming: { default: "effect.{function}", infer_from_source: true, rules: [] },
1133
+ span_relationships: { type: "parent-child" },
1134
+ filter: { include: [], exclude: [] },
1135
+ performance: { sampling_rate: 1, min_duration: "0ms", max_concurrent: 0 },
1136
+ metadata: { fiber_info: true, source_location: true, parent_fiber: true }
1137
+ };
1138
+ const supervisor = new UnifiedTracingSupervisor(autoConfig);
1139
+ const supervisorLayer = Supervisor.addSupervisor(supervisor);
1140
+ const opSupervisionLayer = Layer.effectDiscard(
1141
+ Effect.patchRuntimeFlags(RuntimeFlagsPatch.enable(RuntimeFlags.OpSupervision))
1142
+ );
1143
+ const combinedLayer = Layer.mergeAll(
1144
+ supervisorLayer,
1145
+ Layer.enableSourceCapture,
1146
+ opSupervisionLayer
1147
+ );
1148
+ const wrappedEffect = rootSpanName ? effect.pipe(Effect.withSpan(rootSpanName)) : effect;
1149
+ return wrappedEffect.pipe(Effect.provide(combinedLayer));
1150
+ };
1151
+ flushAndShutdown = async () => {
1152
+ const setup = getGlobalProviderSetup();
1153
+ if (setup?.provider) {
1154
+ logger.log("@atrim/unified-tracing: Flushing and shutting down TracerProvider...");
1155
+ await setup.provider.forceFlush();
1156
+ await setup.provider.shutdown();
1157
+ logger.log("@atrim/unified-tracing: TracerProvider shutdown complete");
1158
+ }
1159
+ };
1160
+ forceFlush = async () => {
1161
+ const setup = getGlobalProviderSetup();
1162
+ if (setup?.provider) {
1163
+ logger.log("@atrim/unified-tracing: Force flushing TracerProvider...");
1164
+ await setup.provider.forceFlush();
1165
+ logger.log("@atrim/unified-tracing: Force flush complete");
1166
+ }
1167
+ };
1168
+ }
1169
+ });
1170
+
1171
+ // src/integrations/effect/auto/index.ts
1172
+ init_unified_tracing_supervisor();
1173
+ init_config();
731
1174
  var compiledRulesCache = /* @__PURE__ */ new WeakMap();
732
1175
  function compileNamingRules(rules) {
733
1176
  const cached = compiledRulesCache.get(rules);
@@ -848,1293 +1291,83 @@ function sanitizeSpanName(name) {
848
1291
  }
849
1292
  return sanitized;
850
1293
  }
851
-
852
- // src/integrations/effect/auto/supervisor.ts
853
- var AutoTracingEnabled = FiberRef.unsafeMake(true);
854
- var AutoTracingSpanName = FiberRef.unsafeMake(Option.none());
855
- var AutoTracingSupervisor = class extends Supervisor.AbstractSupervisor {
856
- constructor(config, tracerProvider) {
857
- super();
858
- this.config = config;
859
- // WeakMap to associate fibers with their OTel spans
860
- __publicField(this, "fiberSpans", /* @__PURE__ */ new WeakMap());
861
- // WeakMap for fiber start times (for min_duration filtering)
862
- __publicField(this, "fiberStartTimes", /* @__PURE__ */ new WeakMap());
863
- // OpenTelemetry tracer - lazily initialized
864
- __publicField(this, "_tracer", null);
865
- // Optional TracerProvider (if provided, use this instead of global)
866
- __publicField(this, "tracerProvider", null);
867
- // Compiled filter patterns
868
- __publicField(this, "includePatterns");
869
- __publicField(this, "excludePatterns");
870
- // Active fiber count (for max_concurrent limiting)
871
- __publicField(this, "activeFiberCount", 0);
872
- // Root span for parent context (set by withAutoTracing)
873
- __publicField(this, "_rootSpan", null);
874
- if (tracerProvider) {
875
- this.tracerProvider = tracerProvider;
876
- logger.log("@atrim/auto-trace: Using provided TracerProvider");
877
- }
878
- this.includePatterns = (config.filter?.include || []).map((p) => new RegExp(p));
879
- this.excludePatterns = (config.filter?.exclude || []).map((p) => new RegExp(p));
880
- logger.log("@atrim/auto-trace: Supervisor initialized");
881
- logger.log(` Granularity: ${config.granularity || "fiber"}`);
882
- logger.log(` Sampling rate: ${config.performance?.sampling_rate ?? 1}`);
883
- logger.log(` Infer from source: ${config.span_naming?.infer_from_source ?? true}`);
884
- }
885
- /**
886
- * Set the root span for parent context propagation
887
- */
888
- setRootSpan(span) {
889
- this._rootSpan = span;
890
- }
891
- /**
892
- * Get the tracer lazily - uses provided TracerProvider if available, otherwise uses global
893
- */
894
- get tracer() {
895
- if (!this._tracer) {
896
- if (this.tracerProvider) {
897
- logger.log("@atrim/auto-trace: Getting tracer from provided TracerProvider");
898
- this._tracer = this.tracerProvider.getTracer("@atrim/auto-trace", "1.0.0");
899
- } else {
900
- logger.log("@atrim/auto-trace: Getting tracer from global API");
901
- this._tracer = OtelApi.trace.getTracer("@atrim/auto-trace", "1.0.0");
902
- }
903
- }
904
- return this._tracer;
905
- }
906
- /**
907
- * Returns the current value (void for this supervisor)
908
- */
909
- get value() {
910
- return Effect.void;
911
- }
912
- /**
913
- * Called when a fiber starts executing
914
- */
915
- onStart(_context, _effect, parent, fiber) {
916
- logger.log(`@atrim/auto-trace: onStart called for fiber ${fiber.id().id}`);
917
- const fiberRefsValue = fiber.getFiberRefs();
918
- const enabled = FiberRefs.getOrDefault(fiberRefsValue, AutoTracingEnabled);
919
- if (!enabled) {
920
- logger.log(`@atrim/auto-trace: Auto-tracing disabled for fiber ${fiber.id().id}`);
921
- return;
922
- }
923
- const samplingRate = this.config.performance?.sampling_rate ?? 1;
924
- if (samplingRate < 1 && Math.random() > samplingRate) {
925
- return;
926
- }
927
- const maxConcurrent = this.config.performance?.max_concurrent ?? 0;
928
- if (maxConcurrent > 0 && this.activeFiberCount >= maxConcurrent) {
929
- return;
930
- }
931
- const nameOverride = FiberRefs.getOrDefault(fiberRefsValue, AutoTracingSpanName);
932
- let sourceInfo = FiberRefs.getOrDefault(fiberRefsValue, CapturedSourceLocation);
933
- if (sourceInfo) {
934
- logger.log(`@atrim/auto-trace: Using captured source location for fiber ${fiber.id().id}`);
935
- logger.log(` function: ${sourceInfo.function}`);
936
- logger.log(` file: ${sourceInfo.file}`);
937
- logger.log(` line: ${sourceInfo.line}`);
938
- } else if (this.config.span_naming?.infer_from_source) {
939
- sourceInfo = this.parseStackTrace();
940
- if (sourceInfo) {
941
- logger.log(`@atrim/auto-trace: Inferred source from stack for fiber ${fiber.id().id}`);
942
- logger.log(` function: ${sourceInfo.function}`);
943
- logger.log(` file: ${sourceInfo.file}`);
944
- logger.log(` line: ${sourceInfo.line}`);
945
- } else {
946
- logger.log(`@atrim/auto-trace: No source info for fiber ${fiber.id().id}`);
947
- }
948
- }
949
- let spanName;
950
- if (Option.isSome(nameOverride)) {
951
- spanName = nameOverride.value;
952
- } else {
953
- spanName = inferSpanName(fiber.id().id, sourceInfo, this.config);
954
- }
955
- if (!this.shouldTrace(spanName)) {
956
- return;
957
- }
958
- const relationshipType = this.config.span_relationships?.type ?? "parent-child";
959
- const useParentChild = relationshipType === "parent-child" || relationshipType === "both";
960
- const useSpanLinks = relationshipType === "span-links" || relationshipType === "both";
961
- let parentContext = OtelApi.ROOT_CONTEXT;
962
- let parentFiberId;
963
- let spanLinks = [];
964
- const maybeEffectParentSpan = Context.getOption(_context, Tracer.ParentSpan);
965
- if (Option.isSome(maybeEffectParentSpan)) {
966
- const effectSpan = maybeEffectParentSpan.value;
967
- logger.log(
968
- `@atrim/auto-trace: Found ParentSpan - traceId=${effectSpan.traceId.slice(0, 8)}..., spanId=${effectSpan.spanId.slice(0, 8)}...`
969
- );
970
- const otelSpanContext = {
971
- traceId: effectSpan.traceId,
972
- spanId: effectSpan.spanId,
973
- traceFlags: effectSpan.sampled ? OtelApi.TraceFlags.SAMPLED : OtelApi.TraceFlags.NONE,
974
- isRemote: false
975
- };
976
- if (useParentChild) {
977
- const wrappedSpan = OtelApi.trace.wrapSpanContext(otelSpanContext);
978
- parentContext = OtelApi.trace.setSpan(OtelApi.ROOT_CONTEXT, wrappedSpan);
979
- }
980
- if (useSpanLinks) {
981
- const linkAttributes = this.getLinkAttributes();
982
- spanLinks.push({
983
- context: otelSpanContext,
984
- attributes: linkAttributes
985
- });
986
- logger.log(`@atrim/auto-trace: Added span link to parent (${relationshipType})`);
987
- }
988
- } else if (Option.isSome(parent)) {
989
- parentFiberId = parent.value.id().id;
990
- const parentSpan = this.fiberSpans.get(parent.value);
991
- if (parentSpan) {
992
- if (useParentChild) {
993
- parentContext = OtelApi.trace.setSpan(OtelApi.ROOT_CONTEXT, parentSpan);
994
- }
995
- if (useSpanLinks) {
996
- const linkAttributes = this.getLinkAttributes();
997
- spanLinks.push({
998
- context: parentSpan.spanContext(),
999
- attributes: linkAttributes
1000
- });
1001
- }
1002
- } else if (this._rootSpan) {
1003
- if (useParentChild) {
1004
- parentContext = OtelApi.trace.setSpan(OtelApi.ROOT_CONTEXT, this._rootSpan);
1005
- }
1006
- if (useSpanLinks) {
1007
- const linkAttributes = this.getLinkAttributes();
1008
- spanLinks.push({
1009
- context: this._rootSpan.spanContext(),
1010
- attributes: linkAttributes
1011
- });
1012
- }
1013
- }
1014
- } else if (this._rootSpan) {
1015
- if (useParentChild) {
1016
- parentContext = OtelApi.trace.setSpan(OtelApi.ROOT_CONTEXT, this._rootSpan);
1017
- }
1018
- if (useSpanLinks) {
1019
- const linkAttributes = this.getLinkAttributes();
1020
- spanLinks.push({
1021
- context: this._rootSpan.spanContext(),
1022
- attributes: linkAttributes
1023
- });
1024
- }
1025
- }
1026
- if (Option.isSome(parent)) {
1027
- parentFiberId = parent.value.id().id;
1028
- }
1029
- const spanOptions = {
1030
- kind: OtelApi.SpanKind.INTERNAL,
1031
- attributes: this.getInitialAttributes(fiber, sourceInfo, parentFiberId)
1032
- };
1033
- if (spanLinks.length > 0) {
1034
- spanOptions.links = spanLinks;
1035
- }
1036
- const span = this.tracer.startSpan(spanName, spanOptions, parentContext);
1037
- logger.log(`@atrim/auto-trace: Created span "${spanName}" for fiber ${fiber.id().id}`);
1038
- this.fiberSpans.set(fiber, span);
1039
- this.fiberStartTimes.set(fiber, process.hrtime.bigint());
1040
- this.activeFiberCount++;
1041
- }
1042
- /**
1043
- * Called when a fiber completes (success or failure)
1044
- */
1045
- onEnd(exit, fiber) {
1046
- logger.log(`@atrim/auto-trace: onEnd called for fiber ${fiber.id().id}`);
1047
- const span = this.fiberSpans.get(fiber);
1048
- if (!span) {
1049
- logger.log(
1050
- `@atrim/auto-trace: No span found for fiber ${fiber.id().id} (skipped or filtered)`
1051
- );
1052
- return;
1053
- }
1054
- const startTime = this.fiberStartTimes.get(fiber);
1055
- if (startTime) {
1056
- const duration = process.hrtime.bigint() - startTime;
1057
- const minDuration = this.parseMinDuration(this.config.performance?.min_duration);
1058
- if (minDuration > 0 && duration < minDuration) {
1059
- this.fiberSpans.delete(fiber);
1060
- this.fiberStartTimes.delete(fiber);
1061
- this.activeFiberCount--;
1062
- return;
1063
- }
1064
- }
1065
- if (Exit.isSuccess(exit)) {
1066
- span.setStatus({ code: OtelApi.SpanStatusCode.OK });
1067
- } else {
1068
- span.setStatus({
1069
- code: OtelApi.SpanStatusCode.ERROR,
1070
- message: "Fiber failed"
1071
- });
1072
- span.setAttribute("effect.fiber.failed", true);
1073
- }
1074
- span.end();
1075
- logger.log(`@atrim/auto-trace: Ended span for fiber ${fiber.id().id}`);
1076
- this.fiberSpans.delete(fiber);
1077
- this.fiberStartTimes.delete(fiber);
1078
- this.activeFiberCount--;
1079
- }
1080
- /**
1081
- * Get attributes for span links from config
1082
- */
1083
- getLinkAttributes() {
1084
- const linkConfig = this.config.span_relationships?.link_attributes;
1085
- const attrs = {
1086
- "link.type": linkConfig?.["link.type"] ?? "fork"
1087
- };
1088
- if (linkConfig?.custom) {
1089
- for (const [key, value] of Object.entries(linkConfig.custom)) {
1090
- attrs[key] = value;
1091
- }
1092
- }
1093
- return attrs;
1094
- }
1095
- /**
1096
- * Check if a span name should be traced based on filter patterns
1097
- */
1098
- shouldTrace(spanName) {
1099
- for (const pattern of this.excludePatterns) {
1100
- if (pattern.test(spanName)) {
1101
- return false;
1102
- }
1103
- }
1104
- if (this.includePatterns.length > 0) {
1105
- for (const pattern of this.includePatterns) {
1106
- if (pattern.test(spanName)) {
1107
- return true;
1108
- }
1109
- }
1110
- return false;
1111
- }
1112
- return true;
1113
- }
1114
- /**
1115
- * Get initial span attributes for a fiber
1116
- */
1117
- getInitialAttributes(fiber, sourceInfo, parentFiberId) {
1118
- const attrs = {
1119
- "effect.auto_traced": true
1120
- };
1121
- if (this.config.metadata?.fiber_info !== false) {
1122
- attrs["effect.fiber.id"] = fiber.id().id;
1123
- }
1124
- if (this.config.metadata?.source_location !== false && sourceInfo) {
1125
- attrs["code.function"] = sourceInfo.function;
1126
- attrs["code.filepath"] = sourceInfo.file;
1127
- attrs["code.lineno"] = sourceInfo.line;
1128
- attrs["code.column"] = sourceInfo.column;
1129
- }
1130
- if (this.config.metadata?.parent_fiber !== false && parentFiberId !== void 0) {
1131
- attrs["effect.fiber.parent_id"] = parentFiberId;
1132
- }
1133
- return attrs;
1134
- }
1135
- /**
1136
- * Parse stack trace to get source info
1137
- */
1138
- parseStackTrace() {
1139
- const stack = new Error().stack;
1140
- if (!stack) {
1141
- logger.log("@atrim/auto-trace: [parseStackTrace] No stack available");
1142
- return void 0;
1143
- }
1144
- const lines = stack.split("\n");
1145
- logger.log(`@atrim/auto-trace: [parseStackTrace] Stack has ${lines.length} lines`);
1146
- for (let i = 0; i < Math.min(10, lines.length); i++) {
1147
- logger.log(`@atrim/auto-trace: [stack ${i}] ${lines[i]}`);
1148
- }
1149
- for (let i = 3; i < lines.length; i++) {
1150
- const line = lines[i];
1151
- if (line === void 0) continue;
1152
- if (!line.includes("node_modules/effect") && !line.includes("@atrim/instrument") && !line.includes("auto/supervisor")) {
1153
- logger.log(`@atrim/auto-trace: [parseStackTrace] Found user frame: ${line}`);
1154
- const match = line.match(/at\s+(?:(.+?)\s+)?\(?(.+?):(\d+):(\d+)\)?/);
1155
- if (match) {
1156
- const [, funcName, filePath, lineNum, colNum] = match;
1157
- const sourceInfo = {
1158
- function: funcName ?? "anonymous",
1159
- file: filePath ?? "unknown",
1160
- line: parseInt(lineNum ?? "0", 10),
1161
- column: parseInt(colNum ?? "0", 10)
1162
- };
1163
- logger.log(
1164
- `@atrim/auto-trace: [parseStackTrace] Parsed: ${sourceInfo.function} at ${sourceInfo.file}:${sourceInfo.line}`
1165
- );
1166
- return sourceInfo;
1167
- }
1168
- }
1169
- }
1170
- logger.log("@atrim/auto-trace: [parseStackTrace] No user code frame found in stack");
1171
- return void 0;
1172
- }
1173
- /**
1174
- * Parse min_duration string to nanoseconds
1175
- */
1176
- parseMinDuration(duration) {
1177
- if (!duration || duration === "0ms") return BigInt(0);
1178
- const match = duration.match(/^(\d+)\s*(ms|millis|s|sec|seconds|us|micros)?$/i);
1179
- if (!match) return BigInt(0);
1180
- const value = parseInt(match[1] ?? "0", 10);
1181
- const unit = (match[2] ?? "ms").toLowerCase();
1182
- switch (unit) {
1183
- case "us":
1184
- case "micros":
1185
- return BigInt(value) * BigInt(1e3);
1186
- case "ms":
1187
- case "millis":
1188
- return BigInt(value) * BigInt(1e6);
1189
- case "s":
1190
- case "sec":
1191
- case "seconds":
1192
- return BigInt(value) * BigInt(1e9);
1193
- default:
1194
- return BigInt(value) * BigInt(1e6);
1195
- }
1196
- }
1197
- };
1198
- var createAutoTracingSupervisor = (config, tracerProvider) => {
1199
- return new AutoTracingSupervisor(config, tracerProvider);
1200
- };
1201
- var createAutoTracingLayer = (options) => {
1202
- return Layer.unwrapEffect(
1203
- Effect.gen(function* () {
1204
- const config = options?.config ?? (yield* loadAutoTracingConfig());
1205
- if (!config.enabled) {
1206
- logger.log("@atrim/auto-trace: Auto-tracing disabled via config");
1207
- return Layer.empty;
1208
- }
1209
- patchEffectFork();
1210
- const supervisor = createAutoTracingSupervisor(config);
1211
- return Supervisor.addSupervisor(supervisor);
1212
- })
1213
- );
1214
- };
1215
- var withAutoTracing = (effect, config, mainSpanName) => {
1216
- if (!config.enabled) {
1217
- logger.log("@atrim/auto-trace: Auto-tracing disabled via config");
1218
- return effect;
1219
- }
1220
- const supervisor = createAutoTracingSupervisor(config);
1221
- const tracer = OtelApi.trace.getTracer("@atrim/auto-trace", "1.0.0");
1222
- const spanName = mainSpanName ?? inferSpanName(0, void 0, config);
1223
- const mainSpan = tracer.startSpan(spanName, {
1224
- kind: OtelApi.SpanKind.INTERNAL,
1225
- attributes: {
1226
- "effect.auto_traced": true,
1227
- "effect.fiber.id": 0,
1228
- // Main fiber
1229
- "effect.main_fiber": true
1230
- }
1231
- });
1232
- supervisor.setRootSpan(mainSpan);
1233
- return Effect.acquireUseRelease(
1234
- // Acquire: return the span (already started)
1235
- Effect.succeed(mainSpan),
1236
- // Use: run the supervised effect
1237
- () => Effect.supervised(supervisor)(effect),
1238
- // Release: end the span
1239
- (span, exit) => Effect.sync(() => {
1240
- if (Exit.isSuccess(exit)) {
1241
- span.setStatus({ code: OtelApi.SpanStatusCode.OK });
1242
- } else {
1243
- span.setStatus({
1244
- code: OtelApi.SpanStatusCode.ERROR,
1245
- message: "Effect failed"
1246
- });
1247
- }
1248
- span.end();
1249
- })
1250
- );
1251
- };
1252
- var AutoTracingLive = createAutoTracingLayer();
1253
- var withoutAutoTracing = (effect) => effect.pipe(Effect.locally(AutoTracingEnabled, false));
1254
- var setSpanName = (name) => (effect) => effect.pipe(Effect.locally(AutoTracingSpanName, Option.some(name)));
1255
- var createExporterLayer = (exporterConfig, serviceName, serviceVersion) => {
1256
- const config = exporterConfig ?? {
1257
- type: "otlp",
1258
- processor: "batch",
1259
- batch: {
1260
- scheduled_delay_millis: 1e3,
1261
- max_export_batch_size: 100
1262
- }
1263
- };
1264
- if (config.type === "none") {
1265
- logger.log('@atrim/auto-trace: Exporter type is "none", no spans will be exported');
1266
- return Layer.empty;
1267
- }
1268
- const createSpanExporter = () => {
1269
- if (config.type === "console") {
1270
- logger.log("@atrim/auto-trace: Using ConsoleSpanExporter");
1271
- return new ConsoleSpanExporter();
1272
- }
1273
- const endpoint = config.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
1274
- logger.log(`@atrim/auto-trace: Using OTLPTraceExporter (${endpoint})`);
1275
- const exporterConfig2 = {
1276
- url: `${endpoint}/v1/traces`
1277
- };
1278
- if (config.headers) {
1279
- exporterConfig2.headers = config.headers;
1280
- logger.log(
1281
- `@atrim/auto-trace: Using custom headers: ${Object.keys(config.headers).join(", ")}`
1282
- );
1283
- }
1284
- return new OTLPTraceExporter(exporterConfig2);
1285
- };
1286
- const createSpanProcessor = () => {
1287
- const exporter = createSpanExporter();
1288
- if (config.processor === "simple" || config.type === "console") {
1289
- logger.log("@atrim/auto-trace: Using SimpleSpanProcessor");
1290
- return new SimpleSpanProcessor(exporter);
1294
+ var CapturedSourceLocation = FiberRef.unsafeMake(void 0);
1295
+ function captureCallSite(skipFrames = 0) {
1296
+ const originalLimit = Error.stackTraceLimit;
1297
+ Error.stackTraceLimit = 10;
1298
+ const error = new Error();
1299
+ Error.stackTraceLimit = originalLimit;
1300
+ if (!error.stack) return void 0;
1301
+ const lines = error.stack.split("\n");
1302
+ const startIndex = 3 + skipFrames;
1303
+ for (let i = startIndex; i < lines.length; i++) {
1304
+ const line = lines[i];
1305
+ if (line === void 0) continue;
1306
+ if (line.includes("@atrim/instrument") || line.includes("node_modules/effect")) {
1307
+ continue;
1291
1308
  }
1292
- const batchConfig = config.batch ?? {
1293
- scheduled_delay_millis: 1e3,
1294
- max_export_batch_size: 100
1295
- };
1296
- logger.log("@atrim/auto-trace: Using BatchSpanProcessor");
1297
- return new BatchSpanProcessor(exporter, {
1298
- scheduledDelayMillis: batchConfig.scheduled_delay_millis,
1299
- maxExportBatchSize: batchConfig.max_export_batch_size
1300
- });
1301
- };
1302
- return Layer.effectDiscard(
1303
- Effect.sync(() => {
1304
- const provider = new BasicTracerProvider({
1305
- resource: resourceFromAttributes({
1306
- [ATTR_SERVICE_NAME]: serviceName,
1307
- [ATTR_SERVICE_VERSION]: serviceVersion
1308
- }),
1309
- spanProcessors: [createSpanProcessor()]
1310
- });
1311
- OtelApi.trace.setGlobalTracerProvider(provider);
1312
- logger.log("@atrim/auto-trace: Global TracerProvider registered");
1313
- return () => {
1314
- provider.shutdown().catch((err) => {
1315
- logger.log(`@atrim/auto-trace: Error shutting down provider: ${err}`);
1316
- });
1317
- };
1318
- })
1319
- );
1320
- };
1321
- var createFullAutoTracingLayer = () => {
1322
- return Layer.unwrapEffect(
1323
- Effect.gen(function* () {
1324
- const config = yield* loadFullConfig();
1325
- const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
1326
- const serviceVersion = process.env.npm_package_version || "1.0.0";
1327
- const autoConfig = config.effect?.auto_instrumentation;
1328
- if (!autoConfig?.enabled) {
1329
- logger.log("@atrim/auto-trace: Auto-instrumentation disabled via config");
1330
- return Layer.empty;
1331
- }
1332
- patchEffectFork();
1333
- const exporterConfig = config.effect?.exporter_config;
1334
- logger.log("@atrim/auto-trace: Full auto-instrumentation enabled");
1335
- logger.log(` Service: ${serviceName}`);
1336
- logger.log(` Exporter: ${exporterConfig?.type ?? "otlp"}`);
1337
- const exporterLayer = createExporterLayer(exporterConfig, serviceName, serviceVersion);
1338
- const supervisor = createAutoTracingSupervisor(autoConfig);
1339
- const supervisorLayer = Supervisor.addSupervisor(supervisor);
1340
- return Layer.mergeAll(exporterLayer, supervisorLayer);
1341
- })
1342
- );
1343
- };
1344
- var FullAutoTracingLive = createFullAutoTracingLayer();
1345
-
1346
- // src/integrations/effect/auto/effect-tracing.ts
1347
- var createEffectTracingLayer = () => {
1348
- return Layer.unwrapEffect(
1349
- Effect.gen(function* () {
1350
- const config = yield* loadFullConfig();
1351
- const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
1352
- const serviceVersion = process.env.npm_package_version || "1.0.0";
1353
- const exporterConfig = config.effect?.exporter_config ?? {
1354
- type: "otlp",
1355
- processor: "batch"
1356
- };
1357
- logger.log("@atrim/auto-trace: Effect-native tracing enabled");
1358
- logger.log(` Service: ${serviceName}`);
1359
- logger.log(` Exporter: ${exporterConfig.type}`);
1360
- if (exporterConfig.type === "none") {
1361
- logger.log('@atrim/auto-trace: Exporter type is "none", using empty layer');
1362
- return Layer.empty;
1363
- }
1364
- const createSpanProcessor = () => {
1365
- if (exporterConfig.type === "console") {
1366
- logger.log("@atrim/auto-trace: Using ConsoleSpanExporter with SimpleSpanProcessor");
1367
- return new SimpleSpanProcessor(new ConsoleSpanExporter());
1368
- }
1369
- const endpoint = exporterConfig.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
1370
- logger.log(`@atrim/auto-trace: Using OTLPTraceExporter (${endpoint})`);
1371
- const otlpConfig = {
1372
- url: `${endpoint}/v1/traces`
1373
- };
1374
- if (exporterConfig.headers) {
1375
- otlpConfig.headers = exporterConfig.headers;
1376
- logger.log(
1377
- `@atrim/auto-trace: Using custom headers: ${Object.keys(exporterConfig.headers).join(", ")}`
1378
- );
1379
- }
1380
- const exporter = new OTLPTraceExporter(otlpConfig);
1381
- if (exporterConfig.processor === "simple") {
1382
- logger.log("@atrim/auto-trace: Using SimpleSpanProcessor");
1383
- return new SimpleSpanProcessor(exporter);
1384
- }
1385
- const batchConfig = exporterConfig.batch ?? {
1386
- scheduled_delay_millis: 1e3,
1387
- max_export_batch_size: 100
1388
- };
1389
- logger.log("@atrim/auto-trace: Using BatchSpanProcessor");
1390
- return new BatchSpanProcessor(exporter, {
1391
- scheduledDelayMillis: batchConfig.scheduled_delay_millis,
1392
- maxExportBatchSize: batchConfig.max_export_batch_size
1393
- });
1394
- };
1395
- const sdkLayer = NodeSdk.layer(() => ({
1396
- resource: {
1397
- serviceName,
1398
- serviceVersion
1399
- },
1400
- spanProcessor: createSpanProcessor()
1401
- }));
1402
- logger.log("@atrim/auto-trace: NodeSdk layer created - HTTP requests will be auto-traced");
1403
- return Layer.discard(sdkLayer);
1404
- })
1405
- );
1406
- };
1407
- var EffectTracingLive = createEffectTracingLayer();
1408
- var createCombinedTracingLayer = () => {
1409
- return Layer.unwrapEffect(
1410
- Effect.gen(function* () {
1411
- const config = yield* loadFullConfig();
1412
- const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
1413
- const serviceVersion = process.env.npm_package_version || "1.0.0";
1414
- const exporterConfig = config.effect?.exporter_config ?? {
1415
- type: "otlp",
1416
- processor: "batch"
1417
- };
1418
- const autoConfig = config.effect?.auto_instrumentation ?? {
1419
- enabled: true,
1420
- granularity: "fiber",
1421
- span_naming: {
1422
- default: "effect.{function}",
1423
- infer_from_source: true,
1424
- rules: []
1425
- },
1426
- span_relationships: {
1427
- type: "parent-child"
1428
- },
1429
- filter: { include: [], exclude: [] },
1430
- performance: { sampling_rate: 1, min_duration: "0ms", max_concurrent: 0 },
1431
- metadata: { fiber_info: true, source_location: true, parent_fiber: true }
1432
- };
1433
- logger.log("@atrim/auto-trace: Combined tracing enabled (HTTP + Fiber)");
1434
- logger.log(` Service: ${serviceName}`);
1435
- logger.log(` Exporter: ${exporterConfig.type}`);
1436
- if (exporterConfig.type === "none") {
1437
- logger.log('@atrim/auto-trace: Exporter type is "none", using empty layer');
1438
- return Layer.empty;
1439
- }
1440
- const createSpanExporter = () => {
1441
- if (exporterConfig.type === "console") {
1442
- logger.log("@atrim/auto-trace: Using ConsoleSpanExporter");
1443
- return new ConsoleSpanExporter();
1444
- }
1445
- const endpoint = exporterConfig.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
1446
- logger.log(`@atrim/auto-trace: Using OTLPTraceExporter (${endpoint})`);
1447
- const otlpConfig = {
1448
- url: `${endpoint}/v1/traces`
1449
- };
1450
- if (exporterConfig.headers) {
1451
- otlpConfig.headers = exporterConfig.headers;
1452
- logger.log(
1453
- `@atrim/auto-trace: Using custom headers: ${Object.keys(exporterConfig.headers).join(", ")}`
1454
- );
1455
- }
1456
- return new OTLPTraceExporter(otlpConfig);
1457
- };
1458
- const createSpanProcessor = () => {
1459
- const exporter = createSpanExporter();
1460
- if (exporterConfig.processor === "simple" || exporterConfig.type === "console") {
1461
- logger.log("@atrim/auto-trace: Using SimpleSpanProcessor");
1462
- return new SimpleSpanProcessor(exporter);
1463
- }
1464
- const batchConfig = exporterConfig.batch ?? {
1465
- scheduled_delay_millis: 1e3,
1466
- max_export_batch_size: 100
1467
- };
1468
- logger.log("@atrim/auto-trace: Using BatchSpanProcessor");
1469
- return new BatchSpanProcessor(exporter, {
1470
- scheduledDelayMillis: batchConfig.scheduled_delay_millis,
1471
- maxExportBatchSize: batchConfig.max_export_batch_size
1472
- });
1309
+ const match = line.match(/at\s+(?:(.+?)\s+)?\(?(.+?):(\d+):(\d+)\)?/);
1310
+ if (match) {
1311
+ const [, funcName, filePath, lineNum, colNum] = match;
1312
+ return {
1313
+ function: funcName?.trim() ?? "anonymous",
1314
+ file: filePath ?? "unknown",
1315
+ line: parseInt(lineNum ?? "0", 10),
1316
+ column: parseInt(colNum ?? "0", 10)
1473
1317
  };
1474
- const globalProviderLayer = Layer.effectDiscard(
1475
- Effect.sync(() => {
1476
- const provider = new BasicTracerProvider({
1477
- resource: resourceFromAttributes({
1478
- [ATTR_SERVICE_NAME]: serviceName,
1479
- [ATTR_SERVICE_VERSION]: serviceVersion
1480
- }),
1481
- spanProcessors: [createSpanProcessor()]
1482
- });
1483
- OtelApi.trace.setGlobalTracerProvider(provider);
1484
- logger.log("@atrim/auto-trace: Global TracerProvider registered");
1485
- })
1486
- );
1487
- const resourceLayer = Resource.layer({
1488
- serviceName,
1489
- serviceVersion
1490
- });
1491
- const effectTracerLayer = Tracer$1.layerGlobal;
1492
- const supervisor = createAutoTracingSupervisor(autoConfig);
1493
- const supervisorLayer = Supervisor.addSupervisor(supervisor);
1494
- logger.log("@atrim/auto-trace: Combined layer created");
1495
- logger.log(" - HTTP requests: auto-traced via Effect platform (global provider)");
1496
- logger.log(" - Forked fibers: auto-traced via Supervisor (global provider)");
1497
- const tracerWithResource = effectTracerLayer.pipe(Layer.provide(resourceLayer));
1498
- return Layer.mergeAll(globalProviderLayer, Layer.discard(tracerWithResource), supervisorLayer);
1499
- })
1500
- );
1501
- };
1502
- var CombinedTracingLive = createCombinedTracingLayer();
1503
- var currentSourceLocation = GlobalValue.globalValue(
1504
- /* @__PURE__ */ Symbol.for("effect/FiberRef/currentSourceLocation"),
1505
- () => FiberRef.unsafeMake(void 0)
1506
- );
1507
- var sourceFileCache = /* @__PURE__ */ new Map();
1508
- function extractFunctionNameFromSource(location) {
1509
- try {
1510
- let lines = sourceFileCache.get(location.file);
1511
- if (!lines) {
1512
- if (!fs.existsSync(location.file)) {
1513
- return void 0;
1514
- }
1515
- const content = fs.readFileSync(location.file, "utf-8");
1516
- lines = content.split("\n");
1517
- sourceFileCache.set(location.file, lines);
1518
- }
1519
- const lineIndex = location.line - 1;
1520
- if (lineIndex < 0 || lineIndex >= lines.length) {
1521
- return void 0;
1522
1318
  }
1523
- const line = lines[lineIndex];
1524
- if (!line) return void 0;
1525
- const fromColumn = line.slice(location.column - 1);
1526
- const forkPattern = /(?:fork|forkDaemon|forkScoped|forkIn)\s*\(\s*([a-zA-Z_$][a-zA-Z0-9_$]*)/;
1527
- const match = fromColumn.match(forkPattern);
1528
- if (match && match[1]) {
1529
- return match[1];
1530
- }
1531
- const fullLinePattern = /(?:fork|forkDaemon|forkScoped|forkIn)\s*\(\s*([a-zA-Z_$][a-zA-Z0-9_$]*)/;
1532
- const fullMatch = line.match(fullLinePattern);
1533
- if (fullMatch && fullMatch[1]) {
1534
- return fullMatch[1];
1535
- }
1536
- return void 0;
1537
- } catch {
1538
- return void 0;
1539
1319
  }
1320
+ return void 0;
1540
1321
  }
1541
- var AutoTracingEnabled2 = FiberRef.unsafeMake(true);
1542
- var AutoTracingSpanName2 = FiberRef.unsafeMake(Option.none());
1543
- var SourceCaptureSupervisor = class extends Supervisor.AbstractSupervisor {
1544
- constructor(config) {
1545
- super();
1546
- this.config = config;
1547
- // WeakMap to associate fibers with their OTel spans
1548
- __publicField(this, "fiberSpans", /* @__PURE__ */ new WeakMap());
1549
- // WeakMap to associate fibers with their OTel contexts (for child fiber lookups)
1550
- __publicField(this, "fiberContexts", /* @__PURE__ */ new WeakMap());
1551
- // WeakMap for fiber start times (for min_duration filtering)
1552
- __publicField(this, "fiberStartTimes", /* @__PURE__ */ new WeakMap());
1553
- // OpenTelemetry tracer - lazily initialized
1554
- __publicField(this, "_tracer", null);
1555
- // Active fiber count (for max_concurrent limiting)
1556
- __publicField(this, "activeFiberCount", 0);
1557
- // Default root span for fibers without a ParentSpan in their context
1558
- // This enables auto-instrumentation without requiring Effect.withSpan()
1559
- __publicField(this, "_rootSpan", null);
1560
- logger.log("@atrim/source-capture: Supervisor initialized (native source capture)");
1561
- logger.log(` Granularity: ${config.granularity || "fiber"}`);
1562
- logger.log(` Sampling rate: ${config.performance?.sampling_rate ?? 1}`);
1563
- }
1564
- /**
1565
- * Set the default root span for auto-instrumentation.
1566
- * Fibers without a ParentSpan will use this as their parent.
1567
- */
1568
- setRootSpan(span) {
1569
- this._rootSpan = span;
1570
- logger.log(`@atrim/source-capture: Root span set - spanId=${span.spanContext().spanId}`);
1571
- }
1572
- /**
1573
- * Get the root span (if set)
1574
- */
1575
- get rootSpan() {
1576
- return this._rootSpan;
1577
- }
1578
- /**
1579
- * Get the tracer lazily from global OTel API
1580
- */
1581
- get tracer() {
1582
- if (!this._tracer) {
1583
- logger.log("@atrim/source-capture: Getting tracer from global API");
1584
- this._tracer = OtelApi.trace.getTracer("@atrim/source-capture", "1.0.0");
1585
- }
1586
- return this._tracer;
1587
- }
1588
- /**
1589
- * Returns the current value (void for this supervisor)
1590
- */
1591
- get value() {
1592
- return Effect.void;
1593
- }
1594
- /**
1595
- * Called when a fiber starts executing
1596
- */
1597
- onStart(_context, _effect, parent, fiber) {
1598
- const fiberId = fiber.id().id;
1599
- logger.log(`@atrim/source-capture: onStart called for fiber ${fiberId}`);
1600
- const fiberRefsValue = fiber.getFiberRefs();
1601
- const enabled = FiberRefs.getOrDefault(fiberRefsValue, AutoTracingEnabled2);
1602
- if (!enabled) {
1603
- logger.log(`@atrim/source-capture: Auto-tracing disabled for fiber ${fiberId}`);
1604
- return;
1605
- }
1606
- const samplingRate = this.config.performance?.sampling_rate ?? 1;
1607
- if (samplingRate < 1 && Math.random() > samplingRate) {
1608
- return;
1609
- }
1610
- const maxConcurrent = this.config.performance?.max_concurrent ?? 0;
1611
- if (maxConcurrent > 0 && this.activeFiberCount >= maxConcurrent) {
1612
- return;
1613
- }
1614
- const nameOverride = FiberRefs.getOrDefault(fiberRefsValue, AutoTracingSpanName2);
1615
- const nativeSourceLocation = FiberRefs.getOrDefault(fiberRefsValue, currentSourceLocation);
1616
- if (nativeSourceLocation) {
1617
- logger.log(`@atrim/source-capture: Found native source location for fiber ${fiberId}`);
1618
- logger.log(` file: ${nativeSourceLocation.file}`);
1619
- logger.log(` line: ${nativeSourceLocation.line}`);
1620
- logger.log(` column: ${nativeSourceLocation.column}`);
1621
- logger.log(` functionName: ${nativeSourceLocation.functionName ?? "N/A"}`);
1622
- } else {
1623
- logger.log(
1624
- `@atrim/source-capture: No source location for fiber ${fiberId} (source capture may be disabled)`
1625
- );
1626
- }
1627
- let spanName;
1628
- let extractedFuncName;
1629
- if (Option.isSome(nameOverride)) {
1630
- spanName = nameOverride.value;
1631
- } else if (nativeSourceLocation) {
1632
- extractedFuncName = extractFunctionNameFromSource(nativeSourceLocation);
1633
- const funcName = extractedFuncName ?? nativeSourceLocation.functionName ?? "anonymous";
1634
- const fileName = nativeSourceLocation.file.split("/").pop() ?? "unknown";
1635
- spanName = `effect.${funcName} (${fileName}:${nativeSourceLocation.line})`;
1636
- if (extractedFuncName) {
1637
- logger.log(
1638
- `@atrim/source-capture: Extracted function name "${extractedFuncName}" from source`
1639
- );
1640
- }
1641
- } else {
1642
- spanName = `effect.fiber-${fiberId}`;
1643
- }
1644
- let parentContext = OtelApi.ROOT_CONTEXT;
1645
- let parentFiberId;
1646
- const inheritedOtelContext = FiberRefs.getOrDefault(
1647
- fiberRefsValue,
1648
- TracerModule.currentOtelSpanContext
1649
- );
1650
- if (inheritedOtelContext) {
1651
- parentContext = inheritedOtelContext;
1652
- const inheritedSpan = OtelApi.trace.getSpan(inheritedOtelContext);
1653
- if (inheritedSpan) {
1654
- const spanCtx = inheritedSpan.spanContext();
1655
- logger.log(
1656
- `@atrim/source-capture: Using inherited OTel context - traceId=${spanCtx.traceId}, spanId=${spanCtx.spanId}`
1657
- );
1658
- }
1659
- } else {
1660
- const maybeEffectParentSpan = Context.getOption(_context, Tracer.ParentSpan);
1661
- if (Option.isSome(maybeEffectParentSpan)) {
1662
- const effectSpan = maybeEffectParentSpan.value;
1663
- logger.log(
1664
- `@atrim/source-capture: Found ParentSpan - traceId=${effectSpan.traceId}, spanId=${effectSpan.spanId}`
1665
- );
1666
- const otelSpanContext = {
1667
- traceId: effectSpan.traceId,
1668
- spanId: effectSpan.spanId,
1669
- traceFlags: effectSpan.sampled ? OtelApi.TraceFlags.SAMPLED : OtelApi.TraceFlags.NONE,
1670
- isRemote: false
1671
- };
1672
- const wrappedSpan = OtelApi.trace.wrapSpanContext(otelSpanContext);
1673
- parentContext = OtelApi.trace.setSpan(OtelApi.ROOT_CONTEXT, wrappedSpan);
1674
- } else if (Option.isSome(parent)) {
1675
- parentFiberId = parent.value.id().id;
1676
- const parentOtelContext = this.fiberContexts.get(parent.value);
1677
- if (parentOtelContext) {
1678
- parentContext = parentOtelContext;
1679
- const parentSpan = OtelApi.trace.getSpan(parentOtelContext);
1680
- if (parentSpan) {
1681
- logger.log(
1682
- `@atrim/source-capture: Using parent fiber's OTel context - traceId=${parentSpan.spanContext().traceId}`
1683
- );
1684
- }
1685
- } else {
1686
- const parentSpan = this.fiberSpans.get(parent.value);
1687
- if (parentSpan) {
1688
- parentContext = OtelApi.trace.setSpan(OtelApi.ROOT_CONTEXT, parentSpan);
1689
- }
1690
- }
1691
- }
1692
- }
1693
- if (Option.isSome(parent)) {
1694
- parentFiberId = parent.value.id().id;
1695
- }
1696
- const attributes = {
1697
- "effect.auto_traced": true,
1698
- "effect.source_capture": true,
1699
- "effect.fiber.id": fiberId
1700
- };
1701
- if (nativeSourceLocation) {
1702
- attributes["code.function"] = extractedFuncName ?? nativeSourceLocation.functionName ?? "anonymous";
1703
- attributes["code.filepath"] = nativeSourceLocation.file;
1704
- attributes["code.lineno"] = nativeSourceLocation.line;
1705
- attributes["code.column"] = nativeSourceLocation.column;
1706
- }
1707
- if (parentFiberId !== void 0) {
1708
- attributes["effect.fiber.parent_id"] = parentFiberId;
1709
- }
1710
- const span = this.tracer.startSpan(
1711
- spanName,
1712
- {
1713
- kind: OtelApi.SpanKind.INTERNAL,
1714
- attributes
1715
- },
1716
- parentContext
1717
- );
1718
- logger.log(`@atrim/source-capture: Created span "${spanName}" for fiber ${fiberId}`);
1719
- const newContext = OtelApi.trace.setSpan(parentContext, span);
1720
- this.fiberSpans.set(fiber, span);
1721
- this.fiberContexts.set(fiber, newContext);
1722
- this.fiberStartTimes.set(fiber, process.hrtime.bigint());
1723
- this.activeFiberCount++;
1724
- }
1725
- /**
1726
- * Called when a fiber completes (success or failure)
1727
- */
1728
- onEnd(exit, fiber) {
1729
- const fiberId = fiber.id().id;
1730
- logger.log(`@atrim/source-capture: onEnd called for fiber ${fiberId}`);
1731
- const span = this.fiberSpans.get(fiber);
1732
- if (!span) {
1733
- logger.log(`@atrim/source-capture: No span found for fiber ${fiberId} (skipped or filtered)`);
1734
- return;
1735
- }
1736
- const startTime = this.fiberStartTimes.get(fiber);
1737
- if (startTime) {
1738
- const duration = process.hrtime.bigint() - startTime;
1739
- const minDuration = this.parseMinDuration(this.config.performance?.min_duration);
1740
- if (minDuration > 0 && duration < minDuration) {
1741
- this.fiberSpans.delete(fiber);
1742
- this.fiberContexts.delete(fiber);
1743
- this.fiberStartTimes.delete(fiber);
1744
- this.activeFiberCount--;
1745
- return;
1746
- }
1747
- }
1748
- if (Exit.isSuccess(exit)) {
1749
- span.setStatus({ code: OtelApi.SpanStatusCode.OK });
1750
- } else {
1751
- span.setStatus({
1752
- code: OtelApi.SpanStatusCode.ERROR,
1753
- message: "Fiber failed"
1754
- });
1755
- span.setAttribute("effect.fiber.failed", true);
1756
- }
1757
- span.end();
1758
- logger.log(`@atrim/source-capture: Ended span for fiber ${fiberId}`);
1759
- this.fiberSpans.delete(fiber);
1760
- this.fiberContexts.delete(fiber);
1761
- this.fiberStartTimes.delete(fiber);
1762
- this.activeFiberCount--;
1763
- }
1764
- /**
1765
- * Parse min_duration string to nanoseconds
1766
- */
1767
- parseMinDuration(duration) {
1768
- if (!duration || duration === "0ms") return BigInt(0);
1769
- const match = duration.match(/^(\d+)\s*(ms|millis|s|sec|seconds|us|micros)?$/i);
1770
- if (!match) return BigInt(0);
1771
- const value = parseInt(match[1] ?? "0", 10);
1772
- const unit = (match[2] ?? "ms").toLowerCase();
1773
- switch (unit) {
1774
- case "us":
1775
- case "micros":
1776
- return BigInt(value) * BigInt(1e3);
1777
- case "ms":
1778
- case "millis":
1779
- return BigInt(value) * BigInt(1e6);
1780
- case "s":
1781
- case "sec":
1782
- case "seconds":
1783
- return BigInt(value) * BigInt(1e9);
1784
- default:
1785
- return BigInt(value) * BigInt(1e6);
1786
- }
1787
- }
1788
- };
1789
- var setupGlobalTracerProvider = () => {
1790
- const config = loadFullConfigSync();
1791
- const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
1792
- const serviceVersion = process.env.npm_package_version || "1.0.0";
1793
- const exporterConfig = config.effect?.exporter_config ?? {
1794
- type: "otlp",
1795
- processor: "batch"
1796
- };
1797
- logger.log("@atrim/source-capture: Setting up global TracerProvider");
1798
- logger.log(` Service: ${serviceName}`);
1799
- logger.log(` Exporter: ${exporterConfig.type}`);
1800
- if (exporterConfig.type === "none") {
1801
- logger.log('@atrim/source-capture: Exporter type is "none", skipping provider setup');
1802
- return null;
1803
- }
1804
- let exporter;
1805
- if (exporterConfig.type === "console") {
1806
- logger.log("@atrim/source-capture: Using ConsoleSpanExporter");
1807
- exporter = new ConsoleSpanExporter();
1808
- } else {
1809
- const endpoint = exporterConfig.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
1810
- logger.log(`@atrim/source-capture: Using OTLPTraceExporter (${endpoint})`);
1811
- const otlpConfig = {
1812
- url: `${endpoint}/v1/traces`
1813
- };
1814
- if (exporterConfig.headers) {
1815
- otlpConfig.headers = exporterConfig.headers;
1816
- logger.log(
1817
- `@atrim/source-capture: Using headers: ${Object.keys(exporterConfig.headers).join(", ")}`
1818
- );
1819
- }
1820
- exporter = new OTLPTraceExporter(otlpConfig);
1821
- }
1822
- let spanProcessor;
1823
- if (exporterConfig.processor === "simple" || exporterConfig.type === "console") {
1824
- logger.log("@atrim/source-capture: Using SimpleSpanProcessor");
1825
- spanProcessor = new SimpleSpanProcessor(exporter);
1826
- } else {
1827
- const batchConfig = exporterConfig.batch ?? {
1828
- scheduled_delay_millis: 1e3,
1829
- max_export_batch_size: 100
1830
- };
1831
- logger.log("@atrim/source-capture: Using BatchSpanProcessor");
1832
- spanProcessor = new BatchSpanProcessor(exporter, {
1833
- scheduledDelayMillis: batchConfig.scheduled_delay_millis,
1834
- maxExportBatchSize: batchConfig.max_export_batch_size
1835
- });
1322
+ var tracedFork = (effect) => {
1323
+ const callSite = captureCallSite();
1324
+ if (!callSite) {
1325
+ return Effect.fork(effect);
1836
1326
  }
1837
- const provider = new BasicTracerProvider({
1838
- resource: resourceFromAttributes({
1839
- [ATTR_SERVICE_NAME]: serviceName,
1840
- [ATTR_SERVICE_VERSION]: serviceVersion
1841
- }),
1842
- spanProcessors: [spanProcessor]
1843
- });
1844
- OtelApi.trace.setGlobalTracerProvider(provider);
1845
- logger.log("@atrim/source-capture: Global TracerProvider registered");
1846
- return { provider, spanProcessor };
1327
+ return FiberRef.set(CapturedSourceLocation, callSite).pipe(Effect.zipRight(Effect.fork(effect)));
1847
1328
  };
1848
- var globalProviderSetup = setupGlobalTracerProvider();
1849
- var createSourceCaptureTracingLayer = () => {
1850
- const config = loadFullConfigSync();
1851
- const autoConfig = config.effect?.auto_instrumentation ?? {
1852
- enabled: true,
1853
- granularity: "fiber",
1854
- span_naming: {
1855
- default: "effect.{function}",
1856
- infer_from_source: true,
1857
- rules: []
1858
- },
1859
- span_relationships: {
1860
- type: "parent-child"
1861
- },
1862
- filter: { include: [], exclude: [] },
1863
- performance: { sampling_rate: 1, min_duration: "0ms", max_concurrent: 0 },
1864
- metadata: { fiber_info: true, source_location: true, parent_fiber: true }
1865
- };
1866
- if (!globalProviderSetup) {
1867
- logger.log("@atrim/source-capture: No TracerProvider, using empty layer");
1868
- return Layer.empty;
1329
+ var tracedForkDaemon = (effect) => {
1330
+ const callSite = captureCallSite();
1331
+ if (!callSite) {
1332
+ return Effect.forkDaemon(effect);
1869
1333
  }
1870
- const supervisor = new SourceCaptureSupervisor(autoConfig);
1871
- const supervisorLayer = Supervisor.addSupervisor(supervisor);
1872
- logger.log("@atrim/source-capture: Creating layer");
1873
- logger.log(" - Source capture: ENABLED via Layer.enableSourceCapture");
1874
- logger.log(" - Effect.withSpan(): exports to OTel via our tracer");
1875
- logger.log(" - Forked fibers: auto-traced via SourceCaptureSupervisor");
1876
- const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
1877
- const serviceVersion = process.env.npm_package_version || "1.0.0";
1878
- const otelTracer = OtelApi.trace.getTracer(serviceName, serviceVersion);
1879
- logger.log(`@atrim/source-capture: Using tracer "${serviceName}" from our provider`);
1880
- const effectTracerLayer = Tracer$1.layerWithoutOtelTracer.pipe(
1881
- Layer.provide(Layer.succeed(Tracer$1.OtelTracer, otelTracer))
1334
+ return FiberRef.set(CapturedSourceLocation, callSite).pipe(
1335
+ Effect.zipRight(Effect.forkDaemon(effect))
1882
1336
  );
1883
- return Layer.mergeAll(effectTracerLayer, Layer.enableSourceCapture, supervisorLayer);
1884
- };
1885
- var SourceCaptureTracingLive = createSourceCaptureTracingLayer();
1886
- var withoutSourceCapture = (effect) => Effect.withCaptureStackTraces(false)(effect);
1887
- var flushAndShutdown = async () => {
1888
- if (globalProviderSetup?.provider) {
1889
- logger.log("@atrim/source-capture: Flushing and shutting down TracerProvider...");
1890
- await globalProviderSetup.provider.forceFlush();
1891
- await globalProviderSetup.provider.shutdown();
1892
- logger.log("@atrim/source-capture: TracerProvider shutdown complete");
1893
- }
1894
- };
1895
- var forceFlush = async () => {
1896
- if (globalProviderSetup?.provider) {
1897
- logger.log("@atrim/source-capture: Force flushing TracerProvider...");
1898
- await globalProviderSetup.provider.forceFlush();
1899
- logger.log("@atrim/source-capture: Force flush complete");
1900
- }
1901
1337
  };
1902
- var defaultSpanNaming = {
1903
- includeLocation: true,
1904
- template: "effect.{op} ({filename}:{line})"
1905
- };
1906
- function parseSourceLocation(stack) {
1907
- const lines = stack.split("\n");
1908
- for (const line of lines.slice(1)) {
1909
- if (line.includes("node_modules") || line.includes("/effect/") || line.includes("fiberRuntime.ts") || line.includes("core.ts")) {
1910
- continue;
1911
- }
1912
- const match = line.match(/at\s+(?:.*?\s+\()?(.+):(\d+):(\d+)\)?/);
1913
- if (match && match[1] && match[2]) {
1914
- return {
1915
- file: match[1],
1916
- line: parseInt(match[2], 10),
1917
- column: match[3] ? parseInt(match[3], 10) : void 0
1918
- };
1919
- }
1920
- }
1921
- return void 0;
1338
+
1339
+ // src/integrations/effect/auto/patch-fork.ts
1340
+ init_dist();
1341
+ var patchAttempted = false;
1342
+ function isEffectForkPatched() {
1343
+ return false;
1922
1344
  }
1923
- function getOperationMeta(effect) {
1924
- const trace5 = effect.trace;
1925
- if (trace5 && trace5._tag === "OperationMeta") {
1926
- return trace5;
1345
+ function patchEffectFork() {
1346
+ if (patchAttempted) {
1347
+ return;
1927
1348
  }
1928
- return void 0;
1349
+ patchAttempted = true;
1350
+ logger.log("@atrim/auto-trace: Effect.fork auto-patching not available (ESM limitation)");
1351
+ logger.log("@atrim/auto-trace: Use tracedFork() for call-site capture");
1929
1352
  }
1930
- function applyTemplate2(template, vars) {
1931
- return template.replace(/\{op\}/g, vars.op).replace(/\{file\}/g, vars.file).replace(/\{filename\}/g, vars.filename).replace(/\{line\}/g, vars.line).replace(/\{column\}/g, vars.column);
1353
+ function unpatchEffectFork() {
1354
+ patchAttempted = false;
1932
1355
  }
1933
- var OperationTracingSupervisor = class extends Supervisor.AbstractSupervisor {
1934
- constructor(operations, spanNaming = defaultSpanNaming) {
1935
- super();
1936
- // Map of configured operations by name
1937
- __publicField(this, "configuredOps");
1938
- // Global span naming configuration
1939
- __publicField(this, "spanNaming");
1940
- // Track processed effects to avoid duplicate spans (using WeakSet for GC)
1941
- __publicField(this, "processedEffects", /* @__PURE__ */ new WeakSet());
1942
- // Track spans per fiber for proper lifecycle management
1943
- __publicField(this, "fiberSpans", /* @__PURE__ */ new WeakMap());
1944
- // OpenTelemetry tracer - lazily initialized
1945
- __publicField(this, "_tracer", null);
1946
- this.configuredOps = new Map(operations.map((op) => [op.name, op]));
1947
- this.spanNaming = spanNaming;
1948
- logger.log("@atrim/operation-tracing: Supervisor initialized");
1949
- logger.log(` Configured operations: ${Array.from(this.configuredOps.keys()).join(", ")}`);
1950
- }
1951
- /**
1952
- * Get the tracer lazily from global OTel API
1953
- */
1954
- get tracer() {
1955
- if (!this._tracer) {
1956
- this._tracer = OtelApi.trace.getTracer("@atrim/operation-tracing", "1.0.0");
1957
- }
1958
- return this._tracer;
1959
- }
1960
- /**
1961
- * Returns the current value (void for this supervisor)
1962
- */
1963
- get value() {
1964
- return Effect.void;
1965
- }
1966
- /**
1967
- * Called for EVERY Effect operation when OpSupervision is enabled.
1968
- *
1969
- * This is the key hook for operation-level tracing. We check if the effect
1970
- * has OperationMeta and create a span if configured.
1971
- */
1972
- onEffect(fiber, effect) {
1973
- if (this.processedEffects.has(effect)) {
1974
- return;
1975
- }
1976
- const meta = getOperationMeta(effect);
1977
- if (!meta) {
1978
- return;
1979
- }
1980
- const config = this.configuredOps.get(meta.op);
1981
- if (!config) {
1982
- return;
1983
- }
1984
- this.processedEffects.add(effect);
1985
- const fiberId = fiber.id().id;
1986
- logger.log(`@atrim/operation-tracing: Found operation "${meta.op}" in fiber ${fiberId}`);
1987
- ({
1988
- "effect.operation": meta.op
1989
- });
1990
- if (config.includeCount && meta.count !== void 0) {
1991
- meta.count;
1992
- }
1993
- const location = config.includeStack ? parseSourceLocation(meta.capturedAt) : void 0;
1994
- if (config.includeStack && location) {
1995
- location.file;
1996
- location.line;
1997
- if (location.column !== void 0) {
1998
- location.column;
1999
- }
2000
- }
2001
- let spanName;
2002
- const templateVars = {
2003
- op: meta.op,
2004
- file: location?.file ?? "unknown",
2005
- filename: location?.file?.split("/").pop() ?? "unknown",
2006
- line: location?.line?.toString() ?? "0",
2007
- column: location?.column?.toString() ?? "0"
2008
- };
2009
- if (config.spanNameTemplate) {
2010
- spanName = applyTemplate2(config.spanNameTemplate, templateVars);
2011
- } else if (this.spanNaming.includeLocation && location) {
2012
- spanName = applyTemplate2(this.spanNaming.template, templateVars);
2013
- } else {
2014
- spanName = `effect.${meta.op}`;
2015
- }
2016
- let parentContext = OtelApi.ROOT_CONTEXT;
2017
- const fiberContext = fiber.currentContext;
2018
- const maybeEffectParentSpan = Context.getOption(fiberContext, TracerModule.ParentSpan);
2019
- if (Option.isSome(maybeEffectParentSpan)) {
2020
- const effectSpan = maybeEffectParentSpan.value;
2021
- parentContext = OtelApi.trace.setSpan(
2022
- OtelApi.ROOT_CONTEXT,
2023
- OtelApi.trace.wrapSpanContext({
2024
- traceId: effectSpan.traceId,
2025
- spanId: effectSpan.spanId,
2026
- traceFlags: OtelApi.TraceFlags.SAMPLED
2027
- })
2028
- );
2029
- logger.log(
2030
- `@atrim/operation-tracing: Using parent span traceId=${effectSpan.traceId.slice(0, 8)}...`
2031
- );
2032
- }
2033
- const span = this.tracer.startSpan(
2034
- spanName,
2035
- {
2036
- kind: OtelApi.SpanKind.INTERNAL
2037
- },
2038
- parentContext
2039
- );
2040
- span.setAttribute("effect.operation", meta.op);
2041
- if (config.includeCount && meta.count !== void 0) {
2042
- span.setAttribute("effect.item_count", meta.count);
2043
- }
2044
- if (config.includeStack && location) {
2045
- span.setAttribute("code.filepath", location.file);
2046
- span.setAttribute("code.lineno", location.line);
2047
- if (location.column !== void 0) {
2048
- span.setAttribute("code.column", location.column);
2049
- }
2050
- }
2051
- logger.log(
2052
- `@atrim/operation-tracing: Created span "${spanName}" - spanId=${span.spanContext().spanId}`
2053
- );
2054
- span.setStatus({ code: OtelApi.SpanStatusCode.OK });
2055
- span.end();
2056
- logger.log(`@atrim/operation-tracing: Ended span "${spanName}"`);
2057
- let spanMap = this.fiberSpans.get(fiber);
2058
- if (!spanMap) {
2059
- spanMap = /* @__PURE__ */ new Map();
2060
- this.fiberSpans.set(fiber, spanMap);
2061
- }
2062
- const spanKey = `${meta.op}-${meta.capturedAt.slice(0, 100)}`;
2063
- spanMap.set(spanKey, span);
2064
- }
2065
- /**
2066
- * Called when a fiber starts - we don't need this for operation tracing
2067
- */
2068
- onStart() {
2069
- }
2070
- /**
2071
- * Called when a fiber ends - end all spans for this fiber
2072
- */
2073
- onEnd(exit, fiber) {
2074
- const spanMap = this.fiberSpans.get(fiber);
2075
- if (!spanMap) {
2076
- return;
2077
- }
2078
- const isError = exit._tag === "Failure";
2079
- const fiberId = fiber.id().id;
2080
- for (const [_key, span] of spanMap) {
2081
- span.setStatus({
2082
- code: isError ? OtelApi.SpanStatusCode.ERROR : OtelApi.SpanStatusCode.OK
2083
- });
2084
- span.end();
2085
- logger.log(
2086
- `@atrim/operation-tracing: Ended span for fiber ${fiberId} (${isError ? "error" : "success"})`
2087
- );
2088
- }
2089
- this.fiberSpans.delete(fiber);
2090
- }
2091
- };
2092
- var defaultOperations = [
2093
- { name: "all", includeCount: true, includeStack: true },
2094
- { name: "forEach", includeCount: true, includeStack: true }
2095
- ];
2096
- var makeOperationTracingLayer = (operations = defaultOperations, spanNaming = defaultSpanNaming) => {
2097
- const supervisor = new OperationTracingSupervisor(operations, spanNaming);
2098
- return Supervisor.addSupervisor(supervisor);
2099
- };
2100
- var enableOpSupervision = (effect) => Effect.withRuntimeFlagsPatch(effect, RuntimeFlagsPatch.enable(RuntimeFlags.OpSupervision));
2101
- var loadOperationTracingLayer = () => Layer.unwrapEffect(
2102
- Effect.gen(function* () {
2103
- const config = yield* Effect.tryPromise({
2104
- try: () => loadConfigWithOptions(),
2105
- catch: (error) => {
2106
- logger.log(`@atrim/operation-tracing: Failed to load config: ${error}`);
2107
- return error;
2108
- }
2109
- }).pipe(Effect.catchAll(() => Effect.succeed(null)));
2110
- const opTracingConfig = config?.effect?.operation_tracing;
2111
- if (opTracingConfig?.enabled === false) {
2112
- logger.log("@atrim/operation-tracing: Operation tracing disabled in config");
2113
- return Layer.empty;
2114
- }
2115
- const spanNaming = {
2116
- includeLocation: opTracingConfig?.span_naming?.include_location ?? true,
2117
- template: opTracingConfig?.span_naming?.template ?? "effect.{op} ({filename}:{line})"
2118
- };
2119
- logger.log(
2120
- `@atrim/operation-tracing: Span naming - includeLocation=${spanNaming.includeLocation}, template="${spanNaming.template}"`
2121
- );
2122
- const operations = opTracingConfig?.operations ? opTracingConfig.operations.map((op) => ({
2123
- name: op.name,
2124
- ...op.span_name && { spanNameTemplate: op.span_name },
2125
- includeCount: op.include_count ?? true,
2126
- includeStack: op.include_stack ?? true
2127
- })) : defaultOperations;
2128
- const source = opTracingConfig?.operations ? "instrumentation.yaml" : "defaults";
2129
- logger.log(
2130
- `@atrim/operation-tracing: Loaded ${operations.length} operation configs from ${source}`
2131
- );
2132
- return makeOperationTracingLayer(operations, spanNaming);
2133
- })
2134
- );
2135
- var OperationTracingLive = loadOperationTracingLayer();
2136
- var withOperationTracing = (effect) => effect.pipe(enableOpSupervision, Effect.provide(OperationTracingLive));
2137
1356
 
2138
- export { AutoTracingConfig, AutoTracingConfigLayer, AutoTracingConfigLive, AutoTracingEnabled, AutoTracingLive, AutoTracingSpanName, AutoTracingSupervisor, CapturedSourceLocation, CombinedTracingLive, EffectTracingLive, FullAutoTracingLive, OperationTracingLive, OperationTracingSupervisor, SourceCaptureSupervisor, SourceCaptureTracingLive, captureCallSite, createAutoTracingLayer, createAutoTracingSupervisor, createCombinedTracingLayer, createEffectTracingLayer, createFullAutoTracingLayer, createSourceCaptureTracingLayer, defaultAutoTracingConfig, enableOpSupervision, flushAndShutdown, forceFlush, inferSpanName, isEffectForkPatched, loadAutoTracingConfig, loadAutoTracingConfigSync, makeOperationTracingLayer, patchEffectFork, sanitizeSpanName, setSpanName, tracedFork, tracedForkDaemon, unpatchEffectFork, withAutoTracing, withOperationTracing, withoutAutoTracing, withoutSourceCapture };
1357
+ // src/integrations/effect/auto/index.ts
1358
+ init_unified_tracing_supervisor();
1359
+ init_unified_tracing_supervisor();
1360
+ init_unified_tracing_supervisor();
1361
+ init_unified_tracing_supervisor();
1362
+ init_unified_tracing_supervisor();
1363
+ init_unified_tracing_supervisor();
1364
+ init_unified_tracing_supervisor();
1365
+ init_unified_tracing_supervisor();
1366
+ init_unified_tracing_supervisor();
1367
+ init_unified_tracing_supervisor();
1368
+ init_unified_tracing_supervisor();
1369
+ var createAutoTracingSupervisor = (config) => new (init_unified_tracing_supervisor(), __toCommonJS(unified_tracing_supervisor_exports)).UnifiedTracingSupervisor(config);
1370
+
1371
+ export { AutoTracingConfig, AutoTracingConfigLayer, AutoTracingConfigLive, AutoTracingEnabled, UnifiedTracingLive as AutoTracingLive, AutoTracingSpanName, UnifiedTracingSupervisor as AutoTracingSupervisor, CapturedSourceLocation, UnifiedTracingLive as CombinedTracingLive, UnifiedTracingLive as FullAutoTracingLive, UnifiedTracingLive as OperationTracingLive, UnifiedTracingSupervisor as SourceCaptureSupervisor, UnifiedTracingLive as SourceCaptureTracingLive, UnifiedTracingLive, UnifiedTracingSupervisor, captureCallSite, createUnifiedTracingLayer as createAutoTracingLayer, createAutoTracingSupervisor, createUnifiedTracingLayer as createFullAutoTracingLayer, createUnifiedTracingLayer, defaultAutoTracingConfig, enableOpSupervision, flushAndShutdown, forceFlush, inferSpanName, isEffectForkPatched, loadAutoTracingConfig, loadAutoTracingConfigSync, loadFullConfigSync, patchEffectFork, sanitizeSpanName, setSpanName, tracedFork, tracedForkDaemon, unpatchEffectFork, withAutoTracing, withUnifiedTracing as withOperationTracing, withUnifiedTracing, withoutAutoTracing };
2139
1372
  //# sourceMappingURL=index.js.map
2140
1373
  //# sourceMappingURL=index.js.map