agent-telemetry 0.1.0 → 0.2.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.
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Traced Fetch Adapter
3
+ *
4
+ * Wraps fetch with telemetry for external service calls. Does NOT monkey-patch
5
+ * the global — returns a new function with identical semantics.
6
+ *
7
+ * duration_ms measures time-to-headers (TTFB), not total transfer time.
8
+ * The Response object is returned untouched — streaming bodies work correctly.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { createTelemetry, type ExternalEvents } from 'agent-telemetry'
13
+ * import { createTracedFetch } from 'agent-telemetry/fetch'
14
+ *
15
+ * const telemetry = await createTelemetry<ExternalEvents>()
16
+ * const fetch = createTracedFetch({ telemetry })
17
+ *
18
+ * const res = await fetch('https://api.example.com/users')
19
+ * ```
20
+ */
21
+
22
+ import { generateSpanId, generateTraceId } from "../ids.ts";
23
+ import type { ExternalCallEvent, Telemetry } from "../types.ts";
24
+
25
+ /** Callable fetch signature (without static properties like `preconnect`). */
26
+ export type FetchFn = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
27
+
28
+ /** Options for the traced fetch adapter. */
29
+ export interface TracedFetchOptions {
30
+ /** Telemetry instance to emit events through. */
31
+ telemetry: Telemetry<ExternalCallEvent>;
32
+ /** Base fetch implementation. Default: globalThis.fetch. */
33
+ baseFetch?: FetchFn;
34
+ /** Provide trace context for correlating with a parent HTTP request. */
35
+ getTraceContext?: () => { traceId: string; parentSpanId?: string } | undefined;
36
+ /** Guard function — return false to skip tracing. */
37
+ isEnabled?: () => boolean;
38
+ }
39
+
40
+ /**
41
+ * Extract URL metadata from the three fetch input types.
42
+ * This is metadata-only — the original input is never modified.
43
+ */
44
+ function resolveInput(input: RequestInfo | URL): {
45
+ url: string;
46
+ method: string;
47
+ } {
48
+ if (input instanceof Request) {
49
+ return { url: input.url, method: input.method };
50
+ }
51
+ if (input instanceof URL) {
52
+ return { url: input.href, method: "GET" };
53
+ }
54
+ // string — try absolute first, then relative with localhost fallback
55
+ try {
56
+ return { url: new URL(input).href, method: "GET" };
57
+ } catch {
58
+ return {
59
+ url: new URL(input, "http://localhost").href,
60
+ method: "GET",
61
+ };
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Create a traced fetch function that emits external.call telemetry events.
67
+ *
68
+ * The returned function has the same signature as globalThis.fetch.
69
+ * The original input and init are passed through to baseFetch untouched.
70
+ * Non-2xx responses are returned normally (not thrown). Network errors
71
+ * are emitted as status "error" and re-thrown.
72
+ */
73
+ export function createTracedFetch(options: TracedFetchOptions): FetchFn {
74
+ const { telemetry, baseFetch = globalThis.fetch, getTraceContext, isEnabled } = options;
75
+
76
+ return async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
77
+ if (isEnabled && !isEnabled()) {
78
+ return baseFetch(input, init);
79
+ }
80
+
81
+ const { url, method: resolvedMethod } = resolveInput(input);
82
+ const method = init?.method?.toUpperCase() ?? resolvedMethod;
83
+
84
+ let service = "unknown";
85
+ let pathname = "/";
86
+ try {
87
+ const parsed = new URL(url);
88
+ service = parsed.hostname;
89
+ pathname = parsed.pathname;
90
+ } catch {
91
+ // keep defaults
92
+ }
93
+
94
+ const operation = `${method} ${pathname}`;
95
+
96
+ const ctx = getTraceContext?.();
97
+ const traceId = ctx?.traceId ?? generateTraceId();
98
+ const spanId = generateSpanId();
99
+
100
+ const start = performance.now();
101
+
102
+ try {
103
+ const response = await baseFetch(input, init);
104
+ const duration_ms = Math.round(performance.now() - start);
105
+
106
+ telemetry.emit({
107
+ kind: "external.call",
108
+ traceId,
109
+ spanId,
110
+ service,
111
+ operation,
112
+ duration_ms,
113
+ status: "success",
114
+ });
115
+
116
+ return response;
117
+ } catch (err) {
118
+ const duration_ms = Math.round(performance.now() - start);
119
+
120
+ telemetry.emit({
121
+ kind: "external.call",
122
+ traceId,
123
+ spanId,
124
+ service,
125
+ operation,
126
+ duration_ms,
127
+ status: "error",
128
+ });
129
+
130
+ throw err;
131
+ }
132
+ };
133
+ }
@@ -21,6 +21,7 @@
21
21
 
22
22
  import type { Context, MiddlewareHandler } from "hono";
23
23
  import { extractEntities } from "../entities.ts";
24
+ import { toSafeErrorLabel } from "../error.ts";
24
25
  import { generateSpanId, generateTraceId } from "../ids.ts";
25
26
  import { formatTraceparent, parseTraceparent } from "../traceparent.ts";
26
27
  import type { EntityPattern, HttpRequestEvent, Telemetry } from "../types.ts";
@@ -67,7 +68,7 @@ export function createHonoTrace(options: HonoTraceOptions): MiddlewareHandler {
67
68
  try {
68
69
  await next();
69
70
  } catch (err) {
70
- error = err instanceof Error ? err.message : "Unknown error";
71
+ error = toSafeErrorLabel(err);
71
72
  throw err;
72
73
  } finally {
73
74
  const status = error && c.res.status < 400 ? 500 : c.res.status;
@@ -90,7 +91,11 @@ export function createHonoTrace(options: HonoTraceOptions): MiddlewareHandler {
90
91
  if (entities) event.entities = entities;
91
92
  }
92
93
 
93
- if (error) event.error = error;
94
+ if (error) {
95
+ event.error = status >= 500 ? `HTTP ${status}` : error;
96
+ } else if (status >= 500) {
97
+ event.error = `HTTP ${status}`;
98
+ }
94
99
 
95
100
  telemetry.emit(event);
96
101
  }
@@ -18,6 +18,7 @@
18
18
 
19
19
  import { InngestMiddleware } from "inngest";
20
20
  import { extractEntitiesFromEvent } from "../entities.ts";
21
+ import { toSafeErrorLabel } from "../error.ts";
21
22
  import { generateSpanId, generateTraceId } from "../ids.ts";
22
23
  import type {
23
24
  JobDispatchEvent,
@@ -86,9 +87,7 @@ export function createInngestTrace(options: InngestTraceOptions): InngestMiddlew
86
87
  runId,
87
88
  duration_ms,
88
89
  status: hasError ? "error" : "success",
89
- error: hasError
90
- ? ((result.error as Error)?.message ?? String(result.error))
91
- : undefined,
90
+ error: hasError ? toSafeErrorLabel(result.error) : undefined,
92
91
  };
93
92
  telemetry.emit(endEvent);
94
93
  },
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Prisma Adapter
3
+ *
4
+ * Creates a Prisma client extension that emits db.query telemetry events
5
+ * for all model operations. Uses $extends({ query }) — no $use() middleware.
6
+ *
7
+ * No runtime import of @prisma/client — the extension object is structurally
8
+ * compatible with PrismaClient.$extends().
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { createTelemetry, type DbEvents } from 'agent-telemetry'
13
+ * import { createPrismaTrace } from 'agent-telemetry/prisma'
14
+ *
15
+ * const telemetry = await createTelemetry<DbEvents>()
16
+ * const prisma = new PrismaClient().$extends(createPrismaTrace({ telemetry }))
17
+ * ```
18
+ */
19
+
20
+ import { toSafeErrorLabel } from "../error.ts";
21
+ import { generateSpanId, generateTraceId } from "../ids.ts";
22
+ import type { DbQueryEvent, Telemetry } from "../types.ts";
23
+
24
+ /** Options for the Prisma trace extension. */
25
+ export interface PrismaTraceOptions {
26
+ /** Telemetry instance to emit events through. */
27
+ telemetry: Telemetry<DbQueryEvent>;
28
+ /** Guard function — return false to skip tracing. */
29
+ isEnabled?: () => boolean;
30
+ /** Provide parent trace context for correlating with an incoming request. */
31
+ getTraceContext?: () => { traceId: string } | undefined;
32
+ }
33
+
34
+ /** Callback params passed by Prisma's $allOperations hook. */
35
+ interface AllOperationsParams {
36
+ model: string;
37
+ operation: string;
38
+ args: unknown;
39
+ query: (args: unknown) => Promise<unknown>;
40
+ }
41
+
42
+ /** Shape returned by createPrismaTrace, compatible with PrismaClient.$extends(). */
43
+ export interface PrismaTraceExtension {
44
+ query: {
45
+ $allModels: {
46
+ $allOperations(params: AllOperationsParams): Promise<unknown>;
47
+ };
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Create a Prisma client extension that traces all model queries.
53
+ *
54
+ * Returns a plain object compatible with `PrismaClient.$extends()`.
55
+ * Emits a db.query event for every model operation with timing,
56
+ * status, and optional trace context correlation.
57
+ */
58
+ export function createPrismaTrace(options: PrismaTraceOptions): PrismaTraceExtension {
59
+ const { telemetry, isEnabled, getTraceContext } = options;
60
+
61
+ return {
62
+ query: {
63
+ $allModels: {
64
+ async $allOperations({ model, operation, args, query }) {
65
+ if (isEnabled && !isEnabled()) {
66
+ return query(args);
67
+ }
68
+
69
+ const start = performance.now();
70
+ const traceId = getTraceContext?.()?.traceId ?? generateTraceId();
71
+ const spanId = generateSpanId();
72
+
73
+ try {
74
+ const result = await query(args);
75
+ const duration_ms = Math.round(performance.now() - start);
76
+
77
+ const event: DbQueryEvent = {
78
+ kind: "db.query",
79
+ traceId,
80
+ spanId,
81
+ provider: "prisma",
82
+ model,
83
+ operation,
84
+ duration_ms,
85
+ status: "success",
86
+ };
87
+ telemetry.emit(event);
88
+
89
+ return result;
90
+ } catch (err) {
91
+ const duration_ms = Math.round(performance.now() - start);
92
+
93
+ const event: DbQueryEvent = {
94
+ kind: "db.query",
95
+ traceId,
96
+ spanId,
97
+ provider: "prisma",
98
+ model,
99
+ operation,
100
+ duration_ms,
101
+ status: "error",
102
+ error: toSafeErrorLabel(err),
103
+ };
104
+ telemetry.emit(event);
105
+
106
+ throw err;
107
+ }
108
+ },
109
+ },
110
+ },
111
+ };
112
+ }
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Supabase Adapter
3
+ *
4
+ * Creates a traced fetch function for Supabase's createClient({ global: { fetch } }).
5
+ * Parses Supabase URL patterns to emit rich, service-aware telemetry:
6
+ * - PostgREST calls -> db.query events with table/operation
7
+ * - Auth/Storage/Functions calls -> external.call events with service context
8
+ *
9
+ * Each fetch invocation emits one event. Supabase's built-in retry logic
10
+ * will generate separate events per retry — each is a real network call.
11
+ *
12
+ * duration_ms measures time-to-headers (TTFB). Response is returned untouched.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { createClient } from '@supabase/supabase-js'
17
+ * import { createTelemetry, type SupabaseEvents } from 'agent-telemetry'
18
+ * import { createSupabaseTrace } from 'agent-telemetry/supabase'
19
+ *
20
+ * const telemetry = await createTelemetry<SupabaseEvents>()
21
+ * const tracedFetch = createSupabaseTrace({ telemetry })
22
+ * const supabase = createClient(url, key, { global: { fetch: tracedFetch } })
23
+ * ```
24
+ */
25
+
26
+ import { toSafeErrorLabel } from "../error.ts";
27
+ import { generateSpanId, generateTraceId } from "../ids.ts";
28
+ import type { DbQueryEvent, ExternalCallEvent, SupabaseEvents, Telemetry } from "../types.ts";
29
+
30
+ /** Callable fetch signature (without static properties like `preconnect`). */
31
+ export type FetchFn = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
32
+
33
+ /** Options for the Supabase trace adapter. */
34
+ export interface SupabaseTraceOptions {
35
+ /** Telemetry instance to emit events through. */
36
+ telemetry: Telemetry<SupabaseEvents>;
37
+ /** Base fetch implementation. Default: globalThis.fetch. */
38
+ baseFetch?: FetchFn;
39
+ /** Provide trace context for correlating with a parent HTTP request. */
40
+ getTraceContext?: () => { traceId: string; parentSpanId?: string } | undefined;
41
+ /** Guard function — return false to skip tracing. */
42
+ isEnabled?: () => boolean;
43
+ }
44
+
45
+ /** Classification result for a Supabase URL. */
46
+ type Classification =
47
+ | {
48
+ kind: "db.query";
49
+ provider: "supabase";
50
+ model: string;
51
+ operation: string;
52
+ }
53
+ | { kind: "external.call"; service: string; operation: string };
54
+
55
+ /** HTTP method to PostgREST operation mapping. */
56
+ const METHOD_TO_OPERATION: Record<string, string> = {
57
+ GET: "select",
58
+ POST: "insert",
59
+ PATCH: "update",
60
+ PUT: "upsert",
61
+ DELETE: "delete",
62
+ };
63
+
64
+ // URL pattern regexes — use /v\d+/ to handle future API versions (I5).
65
+ const REST_RE = /\/rest\/v\d+\/([^?/]+)/;
66
+ const AUTH_RE = /\/auth\/v\d+\/(.+)/;
67
+ const STORAGE_RE = /\/storage\/v\d+\/object\/([^/]+)/;
68
+ const FUNCTIONS_RE = /\/functions\/v\d+\/([^?/]+)/;
69
+
70
+ /**
71
+ * Extract URL metadata from the three fetch input types.
72
+ * This is metadata-only — the original input is never modified (C3/C4).
73
+ */
74
+ function resolveInput(input: RequestInfo | URL): {
75
+ url: string;
76
+ method: string;
77
+ } {
78
+ if (input instanceof Request) {
79
+ return { url: input.url, method: input.method };
80
+ }
81
+ if (input instanceof URL) {
82
+ return { url: input.href, method: "GET" };
83
+ }
84
+ // string
85
+ try {
86
+ return { url: new URL(input).href, method: "GET" };
87
+ } catch {
88
+ return {
89
+ url: new URL(input, "http://localhost").href,
90
+ method: "GET",
91
+ };
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Classify a Supabase request URL into the appropriate event type.
97
+ * Uses the URL pathname to determine if it's a PostgREST, Auth,
98
+ * Storage, Functions, or fallback request.
99
+ */
100
+ function classifyRequest(url: URL, method: string): Classification {
101
+ const pathname = url.pathname;
102
+
103
+ // PostgREST: /rest/v{N}/{table}
104
+ const restMatch = REST_RE.exec(pathname);
105
+ if (restMatch) {
106
+ const table = restMatch[1];
107
+ const operation = METHOD_TO_OPERATION[method] ?? method.toLowerCase();
108
+ return {
109
+ kind: "db.query",
110
+ provider: "supabase",
111
+ model: table,
112
+ operation,
113
+ };
114
+ }
115
+
116
+ // Auth: /auth/v{N}/{endpoint}
117
+ const authMatch = AUTH_RE.exec(pathname);
118
+ if (authMatch) {
119
+ return {
120
+ kind: "external.call",
121
+ service: "supabase-auth",
122
+ operation: authMatch[1],
123
+ };
124
+ }
125
+
126
+ // Storage: /storage/v{N}/object/{bucket}/...
127
+ const storageMatch = STORAGE_RE.exec(pathname);
128
+ if (storageMatch) {
129
+ return {
130
+ kind: "external.call",
131
+ service: "supabase-storage",
132
+ operation: `${method} ${storageMatch[1]}`,
133
+ };
134
+ }
135
+
136
+ // Functions: /functions/v{N}/{name}
137
+ const functionsMatch = FUNCTIONS_RE.exec(pathname);
138
+ if (functionsMatch) {
139
+ return {
140
+ kind: "external.call",
141
+ service: "supabase-functions",
142
+ operation: functionsMatch[1],
143
+ };
144
+ }
145
+
146
+ // Fallback: unknown path
147
+ return {
148
+ kind: "external.call",
149
+ service: "supabase",
150
+ operation: `${method} ${pathname}`,
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Create a traced fetch function for Supabase that emits telemetry events.
156
+ *
157
+ * The returned function has the same signature as globalThis.fetch.
158
+ * The original input and init are passed through to baseFetch untouched.
159
+ * The Response object is returned as-is — streaming bodies work correctly.
160
+ */
161
+ export function createSupabaseTrace(options: SupabaseTraceOptions): FetchFn {
162
+ const { telemetry, baseFetch = globalThis.fetch, getTraceContext, isEnabled } = options;
163
+
164
+ return async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
165
+ if (isEnabled && !isEnabled()) {
166
+ return baseFetch(input, init);
167
+ }
168
+
169
+ // Extract metadata only — original input is never modified.
170
+ const { url, method: resolvedMethod } = resolveInput(input);
171
+ const method = init?.method?.toUpperCase() ?? resolvedMethod.toUpperCase();
172
+
173
+ let parsed: URL;
174
+ try {
175
+ parsed = new URL(url);
176
+ } catch {
177
+ parsed = new URL(url, "http://localhost");
178
+ }
179
+
180
+ const classification = classifyRequest(parsed, method);
181
+
182
+ const ctx = getTraceContext?.();
183
+ const traceId = ctx?.traceId ?? generateTraceId();
184
+ const spanId = generateSpanId();
185
+
186
+ const start = performance.now();
187
+
188
+ try {
189
+ // Pass ORIGINAL input/init to baseFetch unchanged.
190
+ const response = await baseFetch(input, init);
191
+ const duration_ms = Math.round(performance.now() - start);
192
+
193
+ if (classification.kind === "db.query") {
194
+ const event: DbQueryEvent = {
195
+ kind: "db.query",
196
+ traceId,
197
+ spanId,
198
+ provider: classification.provider,
199
+ model: classification.model,
200
+ operation: classification.operation,
201
+ duration_ms,
202
+ status: "success",
203
+ };
204
+ telemetry.emit(event);
205
+ } else {
206
+ const event: ExternalCallEvent = {
207
+ kind: "external.call",
208
+ traceId,
209
+ spanId,
210
+ service: classification.service,
211
+ operation: classification.operation,
212
+ duration_ms,
213
+ status: "success",
214
+ };
215
+ telemetry.emit(event);
216
+ }
217
+
218
+ return response;
219
+ } catch (err) {
220
+ const duration_ms = Math.round(performance.now() - start);
221
+
222
+ if (classification.kind === "db.query") {
223
+ const event: DbQueryEvent = {
224
+ kind: "db.query",
225
+ traceId,
226
+ spanId,
227
+ provider: classification.provider,
228
+ model: classification.model,
229
+ operation: classification.operation,
230
+ duration_ms,
231
+ status: "error",
232
+ error: toSafeErrorLabel(err),
233
+ };
234
+ telemetry.emit(event);
235
+ } else {
236
+ const event: ExternalCallEvent = {
237
+ kind: "external.call",
238
+ traceId,
239
+ spanId,
240
+ service: classification.service,
241
+ operation: classification.operation,
242
+ duration_ms,
243
+ status: "error",
244
+ };
245
+ telemetry.emit(event);
246
+ }
247
+
248
+ throw err;
249
+ }
250
+ };
251
+ }
package/src/error.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Error helpers.
3
+ *
4
+ * Keeps telemetry error fields low-sensitivity by using stable labels
5
+ * (error names) instead of raw exception messages.
6
+ */
7
+
8
+ const DEFAULT_ERROR_LABEL = "Error";
9
+ const MAX_ERROR_LABEL_LENGTH = 80;
10
+
11
+ export function toSafeErrorLabel(err: unknown): string {
12
+ if (!(err instanceof Error)) return DEFAULT_ERROR_LABEL;
13
+ const name = err.name.trim();
14
+ if (!name) return DEFAULT_ERROR_LABEL;
15
+ return name.slice(0, MAX_ERROR_LABEL_LENGTH);
16
+ }
package/src/index.ts CHANGED
@@ -51,6 +51,8 @@ export async function createTelemetry<TEvent extends BaseTelemetryEvent = BaseTe
51
51
  // Re-export all public types
52
52
  export type {
53
53
  BaseTelemetryEvent,
54
+ DbEvents,
55
+ DbQueryEvent,
54
56
  EntityPattern,
55
57
  ExternalCallEvent,
56
58
  ExternalEvents,
@@ -62,6 +64,7 @@ export type {
62
64
  JobStartEvent,
63
65
  JobStepEvent,
64
66
  PresetEvents,
67
+ SupabaseEvents,
65
68
  Telemetry,
66
69
  TelemetryConfig,
67
70
  TraceContext,
@@ -10,15 +10,15 @@
10
10
 
11
11
  /** Parsed representation of a `traceparent` header. */
12
12
  export interface Traceparent {
13
- version: string
14
- traceId: string
15
- parentId: string
16
- traceFlags: string
13
+ version: string;
14
+ traceId: string;
15
+ parentId: string;
16
+ traceFlags: string;
17
17
  }
18
18
 
19
- const TRACEPARENT_RE = /^([\da-f]{2})-([\da-f]{32})-([\da-f]{16})-([\da-f]{2})$/
20
- const ALL_ZEROS_32 = '0'.repeat(32)
21
- const ALL_ZEROS_16 = '0'.repeat(16)
19
+ const TRACEPARENT_RE = /^([\da-f]{2})-([\da-f]{32})-([\da-f]{16})-([\da-f]{2})$/;
20
+ const ALL_ZEROS_32 = "0".repeat(32);
21
+ const ALL_ZEROS_16 = "0".repeat(16);
22
22
 
23
23
  /**
24
24
  * Parse a `traceparent` header value.
@@ -27,21 +27,22 @@ const ALL_ZEROS_16 = '0'.repeat(16)
27
27
  * malformed, or violates the W3C spec (e.g. all-zero trace-id/parent-id).
28
28
  */
29
29
  export function parseTraceparent(header: string | undefined | null): Traceparent | null {
30
- if (!header) return null
30
+ if (!header) return null;
31
31
 
32
- const match = TRACEPARENT_RE.exec(header.trim().toLowerCase())
33
- if (!match) return null
32
+ const match = TRACEPARENT_RE.exec(header.trim().toLowerCase());
33
+ if (!match) return null;
34
34
 
35
35
  // Captures are guaranteed by the regex match above
36
- const version = match[1] as string
37
- const traceId = match[2] as string
38
- const parentId = match[3] as string
39
- const traceFlags = match[4] as string
36
+ const version = match[1] as string;
37
+ const traceId = match[2] as string;
38
+ const parentId = match[3] as string;
39
+ const traceFlags = match[4] as string;
40
40
 
41
- // W3C spec: all-zero trace-id and parent-id are invalid
42
- if (traceId === ALL_ZEROS_32 || parentId === ALL_ZEROS_16) return null
41
+ // W3C spec: "ff" is invalid as the version, and all-zero IDs are invalid.
42
+ if (version === "ff") return null;
43
+ if (traceId === ALL_ZEROS_32 || parentId === ALL_ZEROS_16) return null;
43
44
 
44
- return { version, traceId, parentId, traceFlags }
45
+ return { version, traceId, parentId, traceFlags };
45
46
  }
46
47
 
47
48
  /**
@@ -51,6 +52,6 @@ export function parseTraceparent(header: string | undefined | null): Traceparent
51
52
  * @param parentId 16-char lowercase hex parent/span ID
52
53
  * @param flags 2-char hex trace flags (default: "01" = sampled)
53
54
  */
54
- export function formatTraceparent(traceId: string, parentId: string, flags = '01'): string {
55
- return `00-${traceId}-${parentId}-${flags}`
55
+ export function formatTraceparent(traceId: string, parentId: string, flags = "01"): string {
56
+ return `00-${traceId}-${parentId}-${flags}`;
56
57
  }
package/src/types.ts CHANGED
@@ -81,8 +81,28 @@ export interface ExternalCallEvent extends BaseTelemetryEvent {
81
81
  /** All external service call events. */
82
82
  export type ExternalEvents = ExternalCallEvent;
83
83
 
84
+ export interface DbQueryEvent extends BaseTelemetryEvent {
85
+ kind: "db.query";
86
+ spanId: string;
87
+ /** Provider identifier (e.g. "prisma", "supabase", "drizzle"). */
88
+ provider: string;
89
+ /** The data entity being operated on — ORM model name or database table. */
90
+ model?: string;
91
+ /** Operation name (e.g. "findMany", "create", "select", "insert"). */
92
+ operation: string;
93
+ duration_ms: number;
94
+ status: "success" | "error";
95
+ error?: string;
96
+ }
97
+
98
+ /** All database query events. */
99
+ export type DbEvents = DbQueryEvent;
100
+
101
+ /** Events emitted by the Supabase adapter (db.query for PostgREST, external.call for auth/storage/functions). */
102
+ export type SupabaseEvents = DbQueryEvent | ExternalCallEvent;
103
+
84
104
  /** Union of all preset event types. */
85
- export type PresetEvents = HttpEvents | JobEvents | ExternalEvents;
105
+ export type PresetEvents = HttpEvents | JobEvents | ExternalEvents | DbEvents;
86
106
 
87
107
  // ============================================================================
88
108
  // Entity Extraction