@effect-gql/opentelemetry 0.1.0 → 1.1.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.
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # Effect GraphQL
2
+
3
+ A GraphQL framework for Effect-TS that brings full type safety, composability, and functional programming to your GraphQL servers.
4
+
5
+ > **Note:** This is an experimental prototype exploring the integration between Effect Schema, Effect's service system, and GraphQL.
6
+
7
+ ## Features
8
+
9
+ - **Type-Safe End-to-End** - Define schemas once with Effect Schema, get TypeScript types and GraphQL types automatically
10
+ - **Effect-Powered Resolvers** - Resolvers are Effect programs with built-in error handling and service injection
11
+ - **Immutable Builder** - Fluent, pipe-able API for composing schemas from reusable parts
12
+ - **Service Integration** - Use Effect's Layer system for dependency injection
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @effect-gql/core effect graphql
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```typescript
23
+ import { Effect, Layer } from "effect"
24
+ import * as S from "effect/Schema"
25
+ import { GraphQLSchemaBuilder, execute } from "@effect-gql/core"
26
+
27
+ // Define your schema with Effect Schema
28
+ const UserSchema = S.Struct({
29
+ id: S.String,
30
+ name: S.String,
31
+ email: S.String,
32
+ })
33
+
34
+ // Build your GraphQL schema
35
+ const schema = GraphQLSchemaBuilder.empty
36
+ .objectType({ name: "User", schema: UserSchema })
37
+ .query("users", {
38
+ type: S.Array(UserSchema),
39
+ resolve: () => Effect.succeed([
40
+ { id: "1", name: "Alice", email: "alice@example.com" },
41
+ { id: "2", name: "Bob", email: "bob@example.com" },
42
+ ]),
43
+ })
44
+ .query("user", {
45
+ type: UserSchema,
46
+ args: S.Struct({ id: S.String }),
47
+ resolve: (args) => Effect.succeed({
48
+ id: args.id,
49
+ name: "Alice",
50
+ email: "alice@example.com",
51
+ }),
52
+ })
53
+ .buildSchema()
54
+
55
+ // Execute a query
56
+ const result = await Effect.runPromise(
57
+ execute(schema, Layer.empty)(`
58
+ query {
59
+ users { id name email }
60
+ }
61
+ `)
62
+ )
63
+ ```
64
+
65
+ ## Documentation
66
+
67
+ For full documentation, guides, and API reference, visit the [documentation site](https://nrf110.github.io/effect-gql/).
68
+
69
+ ## Development
70
+
71
+ ```bash
72
+ # Install dependencies
73
+ npm install
74
+
75
+ # Build the project
76
+ npm run build
77
+
78
+ # Run tests
79
+ npm test
80
+
81
+ # Development mode with watch
82
+ npm run dev
83
+ ```
84
+
85
+ ## Contributing
86
+
87
+ Contributions are welcome! Here's how you can help:
88
+
89
+ 1. **Report bugs** - Open an issue describing the problem and steps to reproduce
90
+ 2. **Suggest features** - Open an issue describing your idea
91
+ 3. **Submit PRs** - Fork the repo, make your changes, and open a pull request
92
+
93
+ Please ensure your code:
94
+ - Passes all existing tests (`npm test`)
95
+ - Includes tests for new functionality
96
+ - Follows the existing code style
97
+
98
+ ## License
99
+
100
+ MIT
package/index.cjs ADDED
@@ -0,0 +1,374 @@
1
+ 'use strict';
2
+
3
+ var effect = require('effect');
4
+ var core = require('@effect-gql/core');
5
+ var platform = require('@effect/platform');
6
+ var server = require('@effect-gql/core/server');
7
+
8
+ // src/tracing-extension.ts
9
+ var getOperationName = (document) => {
10
+ for (const definition of document.definitions) {
11
+ if (definition.kind === "OperationDefinition") {
12
+ return definition.name?.value;
13
+ }
14
+ }
15
+ return void 0;
16
+ };
17
+ var getOperationType = (document) => {
18
+ for (const definition of document.definitions) {
19
+ if (definition.kind === "OperationDefinition") {
20
+ return definition.operation;
21
+ }
22
+ }
23
+ return "unknown";
24
+ };
25
+ var tracingExtension = (config) => ({
26
+ name: "opentelemetry-tracing",
27
+ description: "Adds OpenTelemetry tracing to GraphQL execution phases",
28
+ onParse: (source, document) => effect.Effect.withSpan("graphql.parse")(
29
+ effect.Effect.gen(function* () {
30
+ const operationName = getOperationName(document) ?? "anonymous";
31
+ yield* effect.Effect.annotateCurrentSpan("graphql.document.name", operationName);
32
+ yield* effect.Effect.annotateCurrentSpan(
33
+ "graphql.document.operation_count",
34
+ document.definitions.filter((d) => d.kind === "OperationDefinition").length
35
+ );
36
+ if (config?.includeQuery) {
37
+ yield* effect.Effect.annotateCurrentSpan("graphql.source", source);
38
+ }
39
+ if (config?.customAttributes) {
40
+ for (const [key, value] of Object.entries(config.customAttributes)) {
41
+ yield* effect.Effect.annotateCurrentSpan(key, value);
42
+ }
43
+ }
44
+ })
45
+ ),
46
+ onValidate: (document, errors) => effect.Effect.withSpan("graphql.validate")(
47
+ effect.Effect.gen(function* () {
48
+ yield* effect.Effect.annotateCurrentSpan("graphql.validation.error_count", errors.length);
49
+ if (errors.length > 0) {
50
+ yield* effect.Effect.annotateCurrentSpan(
51
+ "graphql.validation.errors",
52
+ JSON.stringify(errors.map((e) => e.message))
53
+ );
54
+ yield* effect.Effect.annotateCurrentSpan("error", true);
55
+ }
56
+ })
57
+ ),
58
+ onExecuteStart: (args) => effect.Effect.gen(function* () {
59
+ const operationName = args.operationName ?? getOperationName(args.document) ?? "anonymous";
60
+ const operationType = getOperationType(args.document);
61
+ yield* effect.Effect.annotateCurrentSpan("graphql.operation.name", operationName);
62
+ yield* effect.Effect.annotateCurrentSpan("graphql.operation.type", operationType);
63
+ if (config?.includeVariables && args.variableValues) {
64
+ yield* effect.Effect.annotateCurrentSpan("graphql.variables", JSON.stringify(args.variableValues));
65
+ }
66
+ if (config?.exposeTraceIdInResponse) {
67
+ const currentSpanOption = yield* effect.Effect.option(effect.Effect.currentSpan);
68
+ if (effect.Option.isSome(currentSpanOption)) {
69
+ const span = currentSpanOption.value;
70
+ const ext = yield* core.ExtensionsService;
71
+ yield* ext.set("tracing", {
72
+ traceId: span.traceId,
73
+ spanId: span.spanId
74
+ });
75
+ }
76
+ }
77
+ }),
78
+ onExecuteEnd: (result) => effect.Effect.gen(function* () {
79
+ const hasErrors = result.errors !== void 0 && result.errors.length > 0;
80
+ yield* effect.Effect.annotateCurrentSpan("graphql.response.has_errors", hasErrors);
81
+ yield* effect.Effect.annotateCurrentSpan(
82
+ "graphql.response.has_data",
83
+ result.data !== null && result.data !== void 0
84
+ );
85
+ if (hasErrors) {
86
+ yield* effect.Effect.annotateCurrentSpan("error", true);
87
+ yield* effect.Effect.annotateCurrentSpan(
88
+ "graphql.errors",
89
+ JSON.stringify(
90
+ result.errors.map((e) => ({
91
+ message: e.message,
92
+ path: e.path
93
+ }))
94
+ )
95
+ );
96
+ }
97
+ })
98
+ });
99
+
100
+ // src/utils.ts
101
+ var pathToString = (path) => {
102
+ if (!path) return "";
103
+ const segments = [];
104
+ let current = path;
105
+ while (current) {
106
+ segments.unshift(current.key);
107
+ current = current.prev;
108
+ }
109
+ return segments.join(".");
110
+ };
111
+ var getFieldDepth = (info) => {
112
+ let depth = 0;
113
+ let current = info.path;
114
+ while (current?.prev) {
115
+ if (typeof current.key === "string") {
116
+ depth++;
117
+ }
118
+ current = current.prev;
119
+ }
120
+ return depth;
121
+ };
122
+ var isIntrospectionField = (info) => {
123
+ return info.fieldName.startsWith("__");
124
+ };
125
+
126
+ // src/tracing-middleware.ts
127
+ var shouldTraceField = (info, config) => {
128
+ if (!config?.traceIntrospection && isIntrospectionField(info)) {
129
+ return false;
130
+ }
131
+ const depth = getFieldDepth(info);
132
+ if (config?.minDepth !== void 0 && depth < config.minDepth) {
133
+ return false;
134
+ }
135
+ if (config?.maxDepth !== void 0 && depth > config.maxDepth) {
136
+ return false;
137
+ }
138
+ if (config?.excludePatterns) {
139
+ const fieldPath = `${info.parentType.name}.${info.fieldName}`;
140
+ for (const pattern of config.excludePatterns) {
141
+ if (pattern.test(fieldPath)) {
142
+ return false;
143
+ }
144
+ }
145
+ }
146
+ return true;
147
+ };
148
+ var resolverTracingMiddleware = (config) => ({
149
+ name: "opentelemetry-resolver-tracing",
150
+ description: "Wraps resolvers in OpenTelemetry spans",
151
+ match: (info) => shouldTraceField(info, config),
152
+ apply: (effect$1, context) => {
153
+ const { info } = context;
154
+ const spanName = config?.spanNameGenerator ? config.spanNameGenerator(info) : `graphql.resolve ${info.parentType.name}.${info.fieldName}`;
155
+ return effect.Effect.withSpan(spanName)(
156
+ effect.Effect.gen(function* () {
157
+ yield* effect.Effect.annotateCurrentSpan("graphql.field.name", info.fieldName);
158
+ yield* effect.Effect.annotateCurrentSpan("graphql.field.path", pathToString(info.path));
159
+ yield* effect.Effect.annotateCurrentSpan("graphql.field.type", String(info.returnType));
160
+ if (config?.includeParentType !== false) {
161
+ yield* effect.Effect.annotateCurrentSpan("graphql.parent.type", info.parentType.name);
162
+ }
163
+ if (info.operation?.name?.value) {
164
+ yield* effect.Effect.annotateCurrentSpan("graphql.operation.name", info.operation.name.value);
165
+ }
166
+ if (config?.includeArgs && context.args && Object.keys(context.args).length > 0) {
167
+ yield* effect.Effect.annotateCurrentSpan("graphql.field.args", JSON.stringify(context.args));
168
+ }
169
+ const result = yield* effect$1.pipe(
170
+ effect.Effect.tapError(
171
+ (error) => effect.Effect.gen(function* () {
172
+ yield* effect.Effect.annotateCurrentSpan("error", true);
173
+ yield* effect.Effect.annotateCurrentSpan(
174
+ "error.type",
175
+ error instanceof Error ? error.constructor.name : "Error"
176
+ );
177
+ yield* effect.Effect.annotateCurrentSpan(
178
+ "error.message",
179
+ error instanceof Error ? error.message : String(error)
180
+ );
181
+ })
182
+ )
183
+ );
184
+ return result;
185
+ })
186
+ );
187
+ }
188
+ });
189
+ var TRACEPARENT_HEADER = "traceparent";
190
+ var TRACESTATE_HEADER = "tracestate";
191
+ var TraceContextTag = class extends effect.Context.Tag("@effect-gql/opentelemetry/TraceContext")() {
192
+ };
193
+ var parseTraceParent = (header) => {
194
+ const trimmed = header.trim().toLowerCase();
195
+ const parts = trimmed.split("-");
196
+ if (parts.length !== 4) {
197
+ return null;
198
+ }
199
+ const [version, traceId, parentSpanId, flagsHex] = parts;
200
+ if (version.length !== 2 || !/^[0-9a-f]{2}$/.test(version)) {
201
+ return null;
202
+ }
203
+ if (traceId.length !== 32 || !/^[0-9a-f]{32}$/.test(traceId)) {
204
+ return null;
205
+ }
206
+ if (traceId === "00000000000000000000000000000000") {
207
+ return null;
208
+ }
209
+ if (parentSpanId.length !== 16 || !/^[0-9a-f]{16}$/.test(parentSpanId)) {
210
+ return null;
211
+ }
212
+ if (parentSpanId === "0000000000000000") {
213
+ return null;
214
+ }
215
+ if (flagsHex.length !== 2 || !/^[0-9a-f]{2}$/.test(flagsHex)) {
216
+ return null;
217
+ }
218
+ const traceFlags = parseInt(flagsHex, 16);
219
+ return {
220
+ version,
221
+ traceId,
222
+ parentSpanId,
223
+ traceFlags
224
+ };
225
+ };
226
+ var isSampled = (context) => {
227
+ return (context.traceFlags & 1) === 1;
228
+ };
229
+ var extractTraceContext = effect.Effect.gen(function* () {
230
+ const request = yield* platform.HttpServerRequest.HttpServerRequest;
231
+ const headers = request.headers;
232
+ const traceparentKey = Object.keys(headers).find((k) => k.toLowerCase() === TRACEPARENT_HEADER);
233
+ if (!traceparentKey) {
234
+ return null;
235
+ }
236
+ const traceparentValue = headers[traceparentKey];
237
+ const traceparent = Array.isArray(traceparentValue) ? traceparentValue[0] : traceparentValue;
238
+ if (!traceparent) {
239
+ return null;
240
+ }
241
+ const context = parseTraceParent(traceparent);
242
+ if (!context) {
243
+ return null;
244
+ }
245
+ const tracestateKey = Object.keys(headers).find((k) => k.toLowerCase() === TRACESTATE_HEADER);
246
+ if (tracestateKey) {
247
+ const tracestateValue = headers[tracestateKey];
248
+ const tracestate = Array.isArray(tracestateValue) ? tracestateValue[0] : tracestateValue;
249
+ if (tracestate) {
250
+ return { ...context, traceState: tracestate };
251
+ }
252
+ }
253
+ return context;
254
+ });
255
+ var formatTraceParent = (context) => {
256
+ const flags = context.traceFlags.toString(16).padStart(2, "0");
257
+ return `${context.version}-${context.traceId}-${context.parentSpanId}-${flags}`;
258
+ };
259
+ var createSpanOptions = (traceContext, request, config) => {
260
+ const attributes = {
261
+ "http.method": request.method,
262
+ "http.url": request.url,
263
+ "http.target": request.url,
264
+ ...config.rootSpanAttributes
265
+ };
266
+ if (traceContext && config.propagateContext !== false) {
267
+ return {
268
+ attributes,
269
+ parent: effect.Tracer.externalSpan({
270
+ traceId: traceContext.traceId,
271
+ spanId: traceContext.parentSpanId,
272
+ sampled: (traceContext.traceFlags & 1) === 1
273
+ })
274
+ };
275
+ }
276
+ return { attributes };
277
+ };
278
+ var makeTracedGraphQLRouter = (schema, layer, options = {}) => {
279
+ const rootSpanName = options.rootSpanName ?? "graphql.http";
280
+ const baseRouter = server.makeGraphQLRouter(schema, layer, options);
281
+ const baseApp = platform.HttpRouter.toHttpApp(baseRouter);
282
+ return platform.HttpRouter.empty.pipe(
283
+ platform.HttpRouter.all(
284
+ "*",
285
+ effect.Effect.gen(function* () {
286
+ const request = yield* platform.HttpServerRequest.HttpServerRequest;
287
+ const traceContext = options.propagateContext !== false ? yield* extractTraceContext.pipe(effect.Effect.catchAll(() => effect.Effect.succeed(null))) : null;
288
+ const spanOptions = createSpanOptions(traceContext, request, options);
289
+ return yield* effect.Effect.withSpan(
290
+ rootSpanName,
291
+ spanOptions
292
+ )(
293
+ effect.Effect.gen(function* () {
294
+ const app = yield* baseApp;
295
+ const response = yield* app.pipe(
296
+ effect.Effect.catchTag(
297
+ "RouteNotFound",
298
+ () => platform.HttpServerResponse.text(JSON.stringify({ errors: [{ message: "Not Found" }] }), {
299
+ status: 404,
300
+ headers: { "content-type": "application/json" }
301
+ })
302
+ )
303
+ );
304
+ yield* effect.Effect.annotateCurrentSpan("http.status_code", response.status);
305
+ return response;
306
+ })
307
+ );
308
+ })
309
+ )
310
+ );
311
+ };
312
+ var withTracedRouter = (router, options = {}) => {
313
+ const rootSpanName = options.rootSpanName ?? "graphql.http";
314
+ const baseApp = platform.HttpRouter.toHttpApp(router);
315
+ return platform.HttpRouter.empty.pipe(
316
+ platform.HttpRouter.all(
317
+ "*",
318
+ effect.Effect.gen(function* () {
319
+ const request = yield* platform.HttpServerRequest.HttpServerRequest;
320
+ const traceContext = options.propagateContext !== false ? yield* extractTraceContext.pipe(effect.Effect.catchAll(() => effect.Effect.succeed(null))) : null;
321
+ const spanOptions = {
322
+ attributes: {
323
+ "http.method": request.method,
324
+ "http.url": request.url,
325
+ ...options.rootSpanAttributes
326
+ }
327
+ };
328
+ if (traceContext && options.propagateContext !== false) {
329
+ spanOptions.parent = effect.Tracer.externalSpan({
330
+ traceId: traceContext.traceId,
331
+ spanId: traceContext.parentSpanId,
332
+ sampled: (traceContext.traceFlags & 1) === 1
333
+ });
334
+ }
335
+ return yield* effect.Effect.withSpan(
336
+ rootSpanName,
337
+ spanOptions
338
+ )(
339
+ effect.Effect.gen(function* () {
340
+ const app = yield* baseApp;
341
+ const response = yield* app;
342
+ yield* effect.Effect.annotateCurrentSpan("http.status_code", response.status);
343
+ return response;
344
+ })
345
+ );
346
+ })
347
+ )
348
+ );
349
+ };
350
+
351
+ // src/index.ts
352
+ var withTracing = (config) => (builder) => {
353
+ let result = builder.extension(tracingExtension(config?.extension));
354
+ result = result.middleware(resolverTracingMiddleware(config?.resolver));
355
+ return result;
356
+ };
357
+
358
+ exports.TRACEPARENT_HEADER = TRACEPARENT_HEADER;
359
+ exports.TRACESTATE_HEADER = TRACESTATE_HEADER;
360
+ exports.TraceContextTag = TraceContextTag;
361
+ exports.extractTraceContext = extractTraceContext;
362
+ exports.formatTraceParent = formatTraceParent;
363
+ exports.getFieldDepth = getFieldDepth;
364
+ exports.isIntrospectionField = isIntrospectionField;
365
+ exports.isSampled = isSampled;
366
+ exports.makeTracedGraphQLRouter = makeTracedGraphQLRouter;
367
+ exports.parseTraceParent = parseTraceParent;
368
+ exports.pathToString = pathToString;
369
+ exports.resolverTracingMiddleware = resolverTracingMiddleware;
370
+ exports.tracingExtension = tracingExtension;
371
+ exports.withTracedRouter = withTracedRouter;
372
+ exports.withTracing = withTracing;
373
+ //# sourceMappingURL=index.cjs.map
374
+ //# sourceMappingURL=index.cjs.map
package/index.cjs.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tracing-extension.ts","../src/utils.ts","../src/tracing-middleware.ts","../src/context-propagation.ts","../src/traced-router.ts","../src/index.ts"],"names":["Effect","Option","ExtensionsService","effect","Context","HttpServerRequest","Tracer","makeGraphQLRouter","HttpRouter","HttpServerResponse"],"mappings":"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","file":"index.cjs","sourcesContent":["import { Effect, Option } from \"effect\"\nimport type { DocumentNode, ExecutionResult, GraphQLError, OperationDefinitionNode } from \"graphql\"\nimport type { GraphQLExtension, ExecutionArgs } from \"@effect-gql/core\"\nimport { ExtensionsService } from \"@effect-gql/core\"\n\n/**\n * Configuration for the GraphQL tracing extension\n */\nexport interface TracingExtensionConfig {\n /**\n * Include the query source in span attributes.\n * Default: false (for security - queries may contain sensitive data)\n */\n readonly includeQuery?: boolean\n\n /**\n * Include variables in span attributes.\n * Default: false (for security - variables may contain sensitive data)\n */\n readonly includeVariables?: boolean\n\n /**\n * Add trace ID and span ID to the GraphQL response extensions.\n * Useful for correlating client requests with backend traces.\n * Default: false\n */\n readonly exposeTraceIdInResponse?: boolean\n\n /**\n * Custom attributes to add to all spans.\n */\n readonly customAttributes?: Record<string, string | number | boolean>\n}\n\n/**\n * Extract the operation name from a parsed GraphQL document\n */\nconst getOperationName = (document: DocumentNode): string | undefined => {\n for (const definition of document.definitions) {\n if (definition.kind === \"OperationDefinition\") {\n return definition.name?.value\n }\n }\n return undefined\n}\n\n/**\n * Extract the operation type (query, mutation, subscription) from a parsed document\n */\nconst getOperationType = (document: DocumentNode): string => {\n for (const definition of document.definitions) {\n if (definition.kind === \"OperationDefinition\") {\n return (definition as OperationDefinitionNode).operation\n }\n }\n return \"unknown\"\n}\n\n/**\n * Creates a GraphQL extension that adds OpenTelemetry tracing to all execution phases.\n *\n * This extension:\n * - Creates spans for parse, validate phases\n * - Annotates the current span with operation metadata during execution\n * - Optionally exposes trace ID in response extensions\n *\n * Requires an OpenTelemetry tracer to be provided via Effect's tracing layer\n * (e.g., `@effect/opentelemetry` NodeSdk.layer or OtlpTracer.layer).\n *\n * @example\n * ```typescript\n * import { tracingExtension } from \"@effect-gql/opentelemetry\"\n *\n * const builder = GraphQLSchemaBuilder.empty.pipe(\n * extension(tracingExtension({\n * exposeTraceIdInResponse: true\n * })),\n * query(\"hello\", { ... })\n * )\n * ```\n */\nexport const tracingExtension = (\n config?: TracingExtensionConfig\n): GraphQLExtension<ExtensionsService> => ({\n name: \"opentelemetry-tracing\",\n description: \"Adds OpenTelemetry tracing to GraphQL execution phases\",\n\n onParse: (source: string, document: DocumentNode) =>\n Effect.withSpan(\"graphql.parse\")(\n Effect.gen(function* () {\n const operationName = getOperationName(document) ?? \"anonymous\"\n yield* Effect.annotateCurrentSpan(\"graphql.document.name\", operationName)\n yield* Effect.annotateCurrentSpan(\n \"graphql.document.operation_count\",\n document.definitions.filter((d) => d.kind === \"OperationDefinition\").length\n )\n\n if (config?.includeQuery) {\n yield* Effect.annotateCurrentSpan(\"graphql.source\", source)\n }\n\n // Add custom attributes if provided\n if (config?.customAttributes) {\n for (const [key, value] of Object.entries(config.customAttributes)) {\n yield* Effect.annotateCurrentSpan(key, value)\n }\n }\n })\n ),\n\n onValidate: (document: DocumentNode, errors: readonly GraphQLError[]) =>\n Effect.withSpan(\"graphql.validate\")(\n Effect.gen(function* () {\n yield* Effect.annotateCurrentSpan(\"graphql.validation.error_count\", errors.length)\n\n if (errors.length > 0) {\n yield* Effect.annotateCurrentSpan(\n \"graphql.validation.errors\",\n JSON.stringify(errors.map((e) => e.message))\n )\n yield* Effect.annotateCurrentSpan(\"error\", true)\n }\n })\n ),\n\n onExecuteStart: (args: ExecutionArgs) =>\n Effect.gen(function* () {\n const operationName = args.operationName ?? getOperationName(args.document) ?? \"anonymous\"\n const operationType = getOperationType(args.document)\n\n yield* Effect.annotateCurrentSpan(\"graphql.operation.name\", operationName)\n yield* Effect.annotateCurrentSpan(\"graphql.operation.type\", operationType)\n\n if (config?.includeVariables && args.variableValues) {\n yield* Effect.annotateCurrentSpan(\"graphql.variables\", JSON.stringify(args.variableValues))\n }\n\n // Expose trace ID in response extensions if configured\n if (config?.exposeTraceIdInResponse) {\n const currentSpanOption = yield* Effect.option(Effect.currentSpan)\n if (Option.isSome(currentSpanOption)) {\n const span = currentSpanOption.value\n const ext = yield* ExtensionsService\n yield* ext.set(\"tracing\", {\n traceId: span.traceId,\n spanId: span.spanId,\n })\n }\n }\n }),\n\n onExecuteEnd: (result: ExecutionResult) =>\n Effect.gen(function* () {\n const hasErrors = result.errors !== undefined && result.errors.length > 0\n\n yield* Effect.annotateCurrentSpan(\"graphql.response.has_errors\", hasErrors)\n yield* Effect.annotateCurrentSpan(\n \"graphql.response.has_data\",\n result.data !== null && result.data !== undefined\n )\n\n if (hasErrors) {\n yield* Effect.annotateCurrentSpan(\"error\", true)\n yield* Effect.annotateCurrentSpan(\n \"graphql.errors\",\n JSON.stringify(\n result.errors!.map((e) => ({\n message: e.message,\n path: e.path,\n }))\n )\n )\n }\n }),\n})\n","import type { GraphQLResolveInfo, ResponsePath } from \"graphql\"\n\n/**\n * Convert a GraphQL response path to a string representation.\n *\n * @example\n * // For path: Query -> users -> 0 -> posts -> 1 -> title\n * // Returns: \"Query.users.0.posts.1.title\"\n */\nexport const pathToString = (path: ResponsePath | undefined): string => {\n if (!path) return \"\"\n\n const segments: (string | number)[] = []\n let current: ResponsePath | undefined = path\n\n while (current) {\n segments.unshift(current.key)\n current = current.prev\n }\n\n return segments.join(\".\")\n}\n\n/**\n * Get the depth of a field in the query tree.\n * Root fields (Query.*, Mutation.*) have depth 0.\n */\nexport const getFieldDepth = (info: GraphQLResolveInfo): number => {\n let depth = 0\n let current: ResponsePath | undefined = info.path\n\n while (current?.prev) {\n // Skip array indices in depth calculation\n if (typeof current.key === \"string\") {\n depth++\n }\n current = current.prev\n }\n\n return depth\n}\n\n/**\n * Check if a field is an introspection field (__schema, __type, etc.)\n */\nexport const isIntrospectionField = (info: GraphQLResolveInfo): boolean => {\n return info.fieldName.startsWith(\"__\")\n}\n","import { Effect } from \"effect\"\nimport type { GraphQLResolveInfo } from \"graphql\"\nimport type { MiddlewareRegistration, MiddlewareContext } from \"@effect-gql/core\"\nimport { pathToString, getFieldDepth, isIntrospectionField } from \"./utils\"\n\n/**\n * Configuration for resolver tracing middleware\n */\nexport interface ResolverTracingConfig {\n /**\n * Minimum field depth to trace.\n * Depth 0 = root fields (Query.*, Mutation.*).\n * Default: 0 (trace all fields)\n */\n readonly minDepth?: number\n\n /**\n * Maximum field depth to trace.\n * Default: Infinity (no limit)\n */\n readonly maxDepth?: number\n\n /**\n * Field patterns to exclude from tracing.\n * Patterns are matched against \"TypeName.fieldName\".\n *\n * @example\n * // Exclude introspection and internal fields\n * excludePatterns: [/^Query\\.__/, /\\.id$/]\n */\n readonly excludePatterns?: readonly RegExp[]\n\n /**\n * Whether to include field arguments in span attributes.\n * Default: false (for security - args may contain sensitive data)\n */\n readonly includeArgs?: boolean\n\n /**\n * Whether to include parent type in span attributes.\n * Default: true\n */\n readonly includeParentType?: boolean\n\n /**\n * Whether to trace introspection fields (__schema, __type, etc.).\n * Default: false\n */\n readonly traceIntrospection?: boolean\n\n /**\n * Custom span name generator.\n * Default: \"graphql.resolve TypeName.fieldName\"\n */\n readonly spanNameGenerator?: (info: GraphQLResolveInfo) => string\n}\n\n/**\n * Check if a field should be traced based on configuration\n */\nconst shouldTraceField = (info: GraphQLResolveInfo, config?: ResolverTracingConfig): boolean => {\n // Skip introspection fields unless explicitly enabled\n if (!config?.traceIntrospection && isIntrospectionField(info)) {\n return false\n }\n\n const depth = getFieldDepth(info)\n\n // Check depth bounds\n if (config?.minDepth !== undefined && depth < config.minDepth) {\n return false\n }\n if (config?.maxDepth !== undefined && depth > config.maxDepth) {\n return false\n }\n\n // Check exclude patterns\n if (config?.excludePatterns) {\n const fieldPath = `${info.parentType.name}.${info.fieldName}`\n for (const pattern of config.excludePatterns) {\n if (pattern.test(fieldPath)) {\n return false\n }\n }\n }\n\n return true\n}\n\n/**\n * Creates middleware that wraps each resolver in an OpenTelemetry span.\n *\n * Each resolver execution creates a child span with GraphQL-specific attributes:\n * - `graphql.field.name`: The field being resolved\n * - `graphql.field.path`: Full path to the field (e.g., \"Query.users.0.posts\")\n * - `graphql.field.type`: The return type of the field\n * - `graphql.parent.type`: The parent type name\n * - `graphql.operation.name`: The operation name (if available)\n * - `error`: Set to true if the resolver fails\n * - `error.type`: Error type/class name\n * - `error.message`: Error message\n *\n * Requires an OpenTelemetry tracer to be provided via Effect's tracing layer.\n *\n * @example\n * ```typescript\n * import { resolverTracingMiddleware } from \"@effect-gql/opentelemetry\"\n *\n * const builder = GraphQLSchemaBuilder.empty.pipe(\n * middleware(resolverTracingMiddleware({\n * minDepth: 0,\n * excludePatterns: [/^Query\\.__/],\n * includeArgs: false\n * })),\n * query(\"users\", { ... })\n * )\n * ```\n */\nexport const resolverTracingMiddleware = (\n config?: ResolverTracingConfig\n): MiddlewareRegistration<never> => ({\n name: \"opentelemetry-resolver-tracing\",\n description: \"Wraps resolvers in OpenTelemetry spans\",\n\n match: (info: GraphQLResolveInfo) => shouldTraceField(info, config),\n\n apply: <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n context: MiddlewareContext\n ): Effect.Effect<A, E, R> => {\n const { info } = context\n\n const spanName = config?.spanNameGenerator\n ? config.spanNameGenerator(info)\n : `graphql.resolve ${info.parentType.name}.${info.fieldName}`\n\n return Effect.withSpan(spanName)(\n Effect.gen(function* () {\n // Add standard attributes\n yield* Effect.annotateCurrentSpan(\"graphql.field.name\", info.fieldName)\n yield* Effect.annotateCurrentSpan(\"graphql.field.path\", pathToString(info.path))\n yield* Effect.annotateCurrentSpan(\"graphql.field.type\", String(info.returnType))\n\n if (config?.includeParentType !== false) {\n yield* Effect.annotateCurrentSpan(\"graphql.parent.type\", info.parentType.name)\n }\n\n if (info.operation?.name?.value) {\n yield* Effect.annotateCurrentSpan(\"graphql.operation.name\", info.operation.name.value)\n }\n\n if (config?.includeArgs && context.args && Object.keys(context.args).length > 0) {\n yield* Effect.annotateCurrentSpan(\"graphql.field.args\", JSON.stringify(context.args))\n }\n\n // Execute resolver and handle errors\n const result = yield* effect.pipe(\n Effect.tapError((error) =>\n Effect.gen(function* () {\n yield* Effect.annotateCurrentSpan(\"error\", true)\n yield* Effect.annotateCurrentSpan(\n \"error.type\",\n error instanceof Error ? error.constructor.name : \"Error\"\n )\n yield* Effect.annotateCurrentSpan(\n \"error.message\",\n error instanceof Error ? error.message : String(error)\n )\n })\n )\n )\n\n return result\n })\n ) as Effect.Effect<A, E, R>\n },\n})\n","import { Effect, Context } from \"effect\"\nimport { HttpServerRequest } from \"@effect/platform\"\n\n/**\n * W3C Trace Context header names\n * @see https://www.w3.org/TR/trace-context/\n */\nexport const TRACEPARENT_HEADER = \"traceparent\"\nexport const TRACESTATE_HEADER = \"tracestate\"\n\n/**\n * Parsed W3C Trace Context from incoming HTTP headers\n */\nexport interface TraceContext {\n /**\n * Version of the trace context format (always \"00\" for current spec)\n */\n readonly version: string\n\n /**\n * Trace ID - 32 character lowercase hex string\n */\n readonly traceId: string\n\n /**\n * Parent Span ID - 16 character lowercase hex string\n */\n readonly parentSpanId: string\n\n /**\n * Trace flags - determines if trace is sampled\n * Bit 0 (0x01) = sampled flag\n */\n readonly traceFlags: number\n\n /**\n * Optional trace state from upstream services\n */\n readonly traceState?: string\n}\n\n/**\n * Context tag for TraceContext\n */\nexport class TraceContextTag extends Context.Tag(\"@effect-gql/opentelemetry/TraceContext\")<\n TraceContextTag,\n TraceContext\n>() {}\n\n/**\n * Parse a W3C traceparent header value.\n *\n * Format: {version}-{trace-id}-{parent-id}-{trace-flags}\n * Example: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01\n *\n * @see https://www.w3.org/TR/trace-context/#traceparent-header\n *\n * @param header - The traceparent header value\n * @returns Parsed trace context or null if invalid\n */\nexport const parseTraceParent = (header: string): TraceContext | null => {\n const trimmed = header.trim().toLowerCase()\n const parts = trimmed.split(\"-\")\n\n if (parts.length !== 4) {\n return null\n }\n\n const [version, traceId, parentSpanId, flagsHex] = parts\n\n // Validate version (2 hex chars)\n if (version.length !== 2 || !/^[0-9a-f]{2}$/.test(version)) {\n return null\n }\n\n // Validate trace ID (32 hex chars, not all zeros)\n if (traceId.length !== 32 || !/^[0-9a-f]{32}$/.test(traceId)) {\n return null\n }\n if (traceId === \"00000000000000000000000000000000\") {\n return null\n }\n\n // Validate parent span ID (16 hex chars, not all zeros)\n if (parentSpanId.length !== 16 || !/^[0-9a-f]{16}$/.test(parentSpanId)) {\n return null\n }\n if (parentSpanId === \"0000000000000000\") {\n return null\n }\n\n // Validate trace flags (2 hex chars)\n if (flagsHex.length !== 2 || !/^[0-9a-f]{2}$/.test(flagsHex)) {\n return null\n }\n\n const traceFlags = parseInt(flagsHex, 16)\n\n return {\n version,\n traceId,\n parentSpanId,\n traceFlags,\n }\n}\n\n/**\n * Check if a trace context is sampled (should be recorded)\n */\nexport const isSampled = (context: TraceContext): boolean => {\n return (context.traceFlags & 0x01) === 0x01\n}\n\n/**\n * Extract trace context from HTTP request headers.\n *\n * Looks for the W3C Trace Context headers:\n * - `traceparent`: Required, contains trace ID, span ID, and flags\n * - `tracestate`: Optional, vendor-specific trace data\n *\n * @example\n * ```typescript\n * const context = yield* extractTraceContext\n * if (context) {\n * console.log(`Continuing trace: ${context.traceId}`)\n * }\n * ```\n */\nexport const extractTraceContext: Effect.Effect<\n TraceContext | null,\n never,\n HttpServerRequest.HttpServerRequest\n> = Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest\n const headers = request.headers\n\n // Get traceparent header (case-insensitive)\n const traceparentKey = Object.keys(headers).find((k) => k.toLowerCase() === TRACEPARENT_HEADER)\n\n if (!traceparentKey) {\n return null\n }\n\n const traceparentValue = headers[traceparentKey]\n const traceparent = Array.isArray(traceparentValue) ? traceparentValue[0] : traceparentValue\n\n if (!traceparent) {\n return null\n }\n\n const context = parseTraceParent(traceparent)\n if (!context) {\n return null\n }\n\n // Get optional tracestate header\n const tracestateKey = Object.keys(headers).find((k) => k.toLowerCase() === TRACESTATE_HEADER)\n\n if (tracestateKey) {\n const tracestateValue = headers[tracestateKey]\n const tracestate = Array.isArray(tracestateValue) ? tracestateValue[0] : tracestateValue\n\n if (tracestate) {\n return { ...context, traceState: tracestate }\n }\n }\n\n return context\n})\n\n/**\n * Format a trace context as a traceparent header value\n */\nexport const formatTraceParent = (context: TraceContext): string => {\n const flags = context.traceFlags.toString(16).padStart(2, \"0\")\n return `${context.version}-${context.traceId}-${context.parentSpanId}-${flags}`\n}\n","import { HttpRouter, HttpServerRequest, HttpServerResponse } from \"@effect/platform\"\nimport { Effect, Layer, Tracer } from \"effect\"\nimport type { GraphQLSchema } from \"graphql\"\nimport { makeGraphQLRouter, type MakeGraphQLRouterOptions } from \"@effect-gql/core/server\"\nimport { extractTraceContext, type TraceContext } from \"./context-propagation\"\n\n/**\n * Options for the traced GraphQL router\n */\nexport interface TracedRouterOptions extends MakeGraphQLRouterOptions {\n /**\n * Name for the root HTTP span.\n * Default: \"graphql.http\"\n */\n readonly rootSpanName?: string\n\n /**\n * Additional attributes to add to the root span.\n */\n readonly rootSpanAttributes?: Record<string, string | number | boolean>\n\n /**\n * Whether to propagate trace context from incoming HTTP headers.\n * Uses W3C Trace Context (traceparent header).\n * Default: true\n */\n readonly propagateContext?: boolean\n}\n\n/**\n * Create an Effect span options object from trace context\n */\nconst createSpanOptions = (\n traceContext: TraceContext | null,\n request: HttpServerRequest.HttpServerRequest,\n config: TracedRouterOptions\n): {\n attributes?: Record<string, unknown>\n parent?: Tracer.ExternalSpan\n} => {\n const attributes: Record<string, unknown> = {\n \"http.method\": request.method,\n \"http.url\": request.url,\n \"http.target\": request.url,\n ...config.rootSpanAttributes,\n }\n\n if (traceContext && config.propagateContext !== false) {\n return {\n attributes,\n parent: Tracer.externalSpan({\n traceId: traceContext.traceId,\n spanId: traceContext.parentSpanId,\n sampled: (traceContext.traceFlags & 0x01) === 0x01,\n }),\n }\n }\n\n return { attributes }\n}\n\n/**\n * Creates a GraphQL router with OpenTelemetry tracing at the HTTP level.\n *\n * This wraps the standard makeGraphQLRouter to:\n * 1. Extract trace context from incoming HTTP headers (W3C Trace Context)\n * 2. Create a root span for the entire HTTP request\n * 3. Propagate trace context to child spans created by extensions/middleware\n *\n * **Span Hierarchy:**\n * ```\n * graphql.http (created by this router)\n * ├── graphql.parse (from tracing extension)\n * ├── graphql.validate (from tracing extension)\n * └── graphql.resolve Query.* (from tracing middleware)\n * ```\n *\n * @example\n * ```typescript\n * import { makeTracedGraphQLRouter } from \"@effect-gql/opentelemetry\"\n * import { NodeSdk } from \"@effect/opentelemetry\"\n *\n * const router = makeTracedGraphQLRouter(schema, serviceLayer, {\n * path: \"/graphql\",\n * graphiql: { path: \"/graphiql\" },\n * rootSpanName: \"graphql.http\",\n * rootSpanAttributes: {\n * \"service.name\": \"my-api\"\n * }\n * })\n *\n * // Provide OpenTelemetry layer when serving\n * const TracingLayer = NodeSdk.layer(() => ({\n * resource: { serviceName: \"my-graphql-api\" },\n * spanProcessor: new BatchSpanProcessor(new OTLPTraceExporter())\n * }))\n * ```\n *\n * @param schema - The GraphQL schema\n * @param layer - Effect layer providing services required by resolvers\n * @param options - Router and tracing configuration\n * @returns An HttpRouter with tracing enabled\n */\nexport const makeTracedGraphQLRouter = <R>(\n schema: GraphQLSchema,\n layer: Layer.Layer<R>,\n options: TracedRouterOptions = {}\n): HttpRouter.HttpRouter<never, never> => {\n const rootSpanName = options.rootSpanName ?? \"graphql.http\"\n\n // Create the base router (handles GraphQL logic)\n const baseRouter = makeGraphQLRouter(schema, layer, options)\n\n // Convert base router to an HttpApp for Effect-based handling\n const baseApp = HttpRouter.toHttpApp(baseRouter)\n\n // Wrap with tracing\n return HttpRouter.empty.pipe(\n HttpRouter.all(\n \"*\",\n Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest\n\n // Extract trace context from headers (if enabled)\n const traceContext =\n options.propagateContext !== false\n ? yield* extractTraceContext.pipe(Effect.catchAll(() => Effect.succeed(null)))\n : null\n\n // Create span options with parent context if available\n const spanOptions = createSpanOptions(traceContext, request, options)\n\n // Execute the request inside a root span\n return yield* Effect.withSpan(\n rootSpanName,\n spanOptions\n )(\n Effect.gen(function* () {\n // Delegate to the base app (which handles the request from context)\n const app = yield* baseApp\n const response = yield* app.pipe(\n Effect.catchTag(\"RouteNotFound\", () =>\n HttpServerResponse.text(JSON.stringify({ errors: [{ message: \"Not Found\" }] }), {\n status: 404,\n headers: { \"content-type\": \"application/json\" },\n })\n )\n )\n\n // Annotate span with response info\n yield* Effect.annotateCurrentSpan(\"http.status_code\", response.status)\n\n return response\n })\n )\n })\n )\n )\n}\n\n/**\n * Wrap an existing HttpRouter with OpenTelemetry tracing.\n *\n * This is useful when you already have a router and want to add\n * request-level tracing without recreating it.\n *\n * @example\n * ```typescript\n * import { toRouter } from \"@effect-gql/core/server\"\n * import { withTracedRouter } from \"@effect-gql/opentelemetry\"\n *\n * const baseRouter = toRouter(builder, serviceLayer)\n * const tracedRouter = withTracedRouter(baseRouter, {\n * rootSpanName: \"graphql.http\"\n * })\n * ```\n */\nexport const withTracedRouter = (\n router: HttpRouter.HttpRouter<any, any>,\n options: {\n rootSpanName?: string\n rootSpanAttributes?: Record<string, string | number | boolean>\n propagateContext?: boolean\n } = {}\n): HttpRouter.HttpRouter<any, any> => {\n const rootSpanName = options.rootSpanName ?? \"graphql.http\"\n\n // Convert router to an HttpApp for Effect-based handling\n const baseApp = HttpRouter.toHttpApp(router)\n\n return HttpRouter.empty.pipe(\n HttpRouter.all(\n \"*\",\n Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest\n\n // Extract trace context from headers\n const traceContext =\n options.propagateContext !== false\n ? yield* extractTraceContext.pipe(Effect.catchAll(() => Effect.succeed(null)))\n : null\n\n const spanOptions: {\n attributes: Record<string, unknown>\n parent?: Tracer.ExternalSpan\n } = {\n attributes: {\n \"http.method\": request.method,\n \"http.url\": request.url,\n ...options.rootSpanAttributes,\n },\n }\n\n if (traceContext && options.propagateContext !== false) {\n spanOptions.parent = Tracer.externalSpan({\n traceId: traceContext.traceId,\n spanId: traceContext.parentSpanId,\n sampled: (traceContext.traceFlags & 0x01) === 0x01,\n })\n }\n\n return yield* Effect.withSpan(\n rootSpanName,\n spanOptions\n )(\n Effect.gen(function* () {\n // Delegate to the base app (which handles the request from context)\n const app = yield* baseApp\n const response = yield* app\n\n // Annotate span with response info\n yield* Effect.annotateCurrentSpan(\"http.status_code\", response.status)\n\n return response\n })\n )\n })\n )\n )\n}\n","/**\n * @effect-gql/opentelemetry\n *\n * OpenTelemetry tracing integration for Effect GraphQL.\n *\n * Provides distributed tracing using Effect's native OpenTelemetry support.\n * Works with any OpenTelemetry-compatible backend (Jaeger, Tempo, Honeycomb, etc.).\n *\n * @example\n * ```typescript\n * import { GraphQLSchemaBuilder } from \"@effect-gql/core\"\n * import { serve } from \"@effect-gql/node\"\n * import { withTracing } from \"@effect-gql/opentelemetry\"\n * import { NodeSdk } from \"@effect/opentelemetry\"\n * import { BatchSpanProcessor } from \"@opentelemetry/sdk-trace-base\"\n * import { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\"\n *\n * // Add tracing to schema\n * const builder = GraphQLSchemaBuilder.empty\n * .query(\"users\", { ... })\n * .pipe(withTracing({\n * extension: { exposeTraceIdInResponse: true },\n * resolver: { excludePatterns: [/^Query\\.__/] }\n * }))\n *\n * // Configure OpenTelemetry\n * const TracingLayer = NodeSdk.layer(() => ({\n * resource: { serviceName: \"my-graphql-api\" },\n * spanProcessor: new BatchSpanProcessor(\n * new OTLPTraceExporter({ url: \"http://localhost:4318/v1/traces\" })\n * )\n * }))\n *\n * // Serve with tracing\n * serve(builder, TracingLayer.pipe(Layer.merge(serviceLayer)))\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { GraphQLSchemaBuilder } from \"@effect-gql/core\"\nimport { tracingExtension, type TracingExtensionConfig } from \"./tracing-extension\"\nimport { resolverTracingMiddleware, type ResolverTracingConfig } from \"./tracing-middleware\"\n\n/**\n * Complete configuration for GraphQL tracing\n */\nexport interface GraphQLTracingConfig {\n /**\n * Configuration for phase-level tracing (parse, validate, execute).\n * Uses the Extensions system.\n */\n readonly extension?: TracingExtensionConfig\n\n /**\n * Configuration for resolver-level tracing.\n * Uses the Middleware system.\n */\n readonly resolver?: ResolverTracingConfig\n}\n\n/**\n * Add OpenTelemetry tracing to a GraphQL schema builder.\n *\n * This is a convenience function that registers both the tracing extension\n * (for phase-level spans) and resolver middleware (for field-level spans).\n *\n * **Span Hierarchy:**\n * ```\n * graphql.request (if using traced router)\n * ├── graphql.parse\n * ├── graphql.validate\n * └── graphql.execute\n * ├── graphql.resolve Query.users\n * ├── graphql.resolve User.posts\n * └── graphql.resolve Post.author\n * ```\n *\n * **Requirements:**\n * - An OpenTelemetry tracer must be provided via Effect's tracing layer\n * - Use `@effect/opentelemetry` NodeSdk.layer or OtlpTracer.layer\n *\n * @example\n * ```typescript\n * import { GraphQLSchemaBuilder } from \"@effect-gql/core\"\n * import { withTracing } from \"@effect-gql/opentelemetry\"\n *\n * const builder = GraphQLSchemaBuilder.empty\n * .query(\"hello\", {\n * type: S.String,\n * resolve: () => Effect.succeed(\"world\")\n * })\n * .pipe(withTracing({\n * extension: {\n * exposeTraceIdInResponse: true, // Add traceId to response extensions\n * includeQuery: false, // Don't include query in spans (security)\n * },\n * resolver: {\n * minDepth: 0, // Trace all resolvers\n * excludePatterns: [/^Query\\.__/], // Skip introspection\n * includeArgs: false, // Don't include args (security)\n * }\n * }))\n * ```\n *\n * @param config - Optional tracing configuration\n * @returns A function that adds tracing to a GraphQLSchemaBuilder\n */\nexport const withTracing =\n <R>(config?: GraphQLTracingConfig) =>\n (builder: GraphQLSchemaBuilder<R>): GraphQLSchemaBuilder<R> => {\n // Add tracing extension for phase-level spans\n let result = builder.extension(tracingExtension(config?.extension))\n\n // Add resolver tracing middleware for field-level spans\n result = result.middleware(resolverTracingMiddleware(config?.resolver))\n\n return result as GraphQLSchemaBuilder<R>\n }\n\n// Re-export components for individual use\nexport { tracingExtension, type TracingExtensionConfig } from \"./tracing-extension\"\nexport { resolverTracingMiddleware, type ResolverTracingConfig } from \"./tracing-middleware\"\nexport {\n extractTraceContext,\n parseTraceParent,\n formatTraceParent,\n isSampled,\n TraceContextTag,\n TRACEPARENT_HEADER,\n TRACESTATE_HEADER,\n type TraceContext,\n} from \"./context-propagation\"\nexport { pathToString, getFieldDepth, isIntrospectionField } from \"./utils\"\nexport {\n makeTracedGraphQLRouter,\n withTracedRouter,\n type TracedRouterOptions,\n} from \"./traced-router\"\n"]}