@deepeloper/deeptracker 0.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/core/context.ts","../src/core/tracer.ts","../src/core/ids.ts","../src/core/buffer.ts","../src/transport/console.ts","../src/transport/http.ts","../src/instruments/http.ts","../src/instruments/wrap.ts","../src/instruments/pg.ts"],"sourcesContent":["/**\n * @deepeloper/deeptracker — public API surface.\n *\n * Call {@link init} once at your app's entry point, before requiring the\n * frameworks/drivers you want auto-instrumented. Everything else (root traces,\n * outbound HTTP, DB queries) is captured automatically. See PLANNING.md §4.\n */\n\nimport { getStore } from './core/context.js';\nimport { configure, runInSpan } from './core/tracer.js';\nimport { TraceBuffer } from './core/buffer.js';\nimport { ConsoleExporter } from './transport/console.js';\nimport { HttpExporter } from './transport/http.js';\nimport type { Exporter } from './transport/exporter.js';\nimport { installHttpClient, installHttpServer } from './instruments/http.js';\nimport { installPg } from './instruments/pg.js';\nimport type { TrackerConfig } from './types.js';\n\nexport type * from './types.js';\n\nlet activeConfig: TrackerConfig | null = null;\nlet buffer: TraceBuffer | null = null;\n\n/**\n * Initialize the tracker: install instrumentation and start the trace buffer.\n * Idempotent-ish — calling twice re-applies config but won't double-wrap.\n */\nexport function init(config: TrackerConfig): void {\n if (!config.apiKey) throw new Error('[tracker] `apiKey` is required');\n if (!config.appName) throw new Error('[tracker] `appName` is required');\n activeConfig = config;\n\n const environment = config.environment ?? process.env['NODE_ENV'] ?? 'development';\n\n const exporter: Exporter =\n config.endpoint !== undefined\n ? new HttpExporter(config.endpoint, config.apiKey)\n : new ConsoleExporter();\n\n buffer = new TraceBuffer(exporter, {\n ...(config.maxBatchSize !== undefined ? { maxSize: config.maxBatchSize } : {}),\n ...(config.flushIntervalMs !== undefined ? { flushIntervalMs: config.flushIntervalMs } : {}),\n });\n buffer.start();\n\n configure({\n appName: config.appName,\n environment,\n sampleRate: config.sampleRate ?? 1,\n ignore: config.ignore ?? [],\n sink: (trace) => buffer?.add(trace),\n });\n\n installHttpServer();\n installHttpClient();\n installPg();\n\n registerShutdown();\n}\n\nlet shutdownRegistered = false;\nfunction registerShutdown(): void {\n if (shutdownRegistered) return;\n shutdownRegistered = true;\n const stop = (): void => {\n void buffer?.shutdown();\n };\n process.once('beforeExit', stop);\n process.once('SIGTERM', stop);\n process.once('SIGINT', stop);\n}\n\n/** Wrap a unit of work in a manual span so it appears in the trace tree. */\nexport async function span<T>(name: string, fn: () => Promise<T> | T): Promise<T> {\n return await runInSpan(name, 'custom', fn);\n}\n\n/** Attach a tag to the currently active trace (e.g. userId, plan). */\nexport function setTag(key: string, value: string): void {\n const store = getStore();\n if (store) store.trace.tags[key] = String(value);\n}\n\n/** Record a log event inside the current span (no-op outside an active span). */\nexport function log(message: string): void {\n const store = getStore();\n if (store?.parent) {\n store.parent.events.push({ timestamp: Date.now(), message });\n }\n}\n\n/** Force-flush buffered traces. Mainly useful in tests and before shutdown. */\nexport async function flush(): Promise<void> {\n await buffer?.flush();\n}\n\n/** Current config (used by instrumentation modules / introspection). */\nexport function getConfig(): TrackerConfig | null {\n return activeConfig;\n}\n\nexport default { init, span, setTag, log, flush, getConfig };\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Span, TraceHttpInfo } from '../types.js';\n\n/** A span while it's being built — adds a monotonic start marker we strip on serialize. */\nexport interface InternalSpan extends Span {\n _startMono: number;\n}\n\n/** A trace while it's being built, before serialization to the public {@link Trace}. */\nexport interface InternalTrace {\n traceId: string;\n appName: string;\n environment: string;\n rootSpanId: string;\n startWall: number;\n startMono: number;\n tags: Record<string, string>;\n spans: InternalSpan[];\n http: Partial<TraceHttpInfo>;\n ended: boolean;\n}\n\n/**\n * What we keep in AsyncLocalStorage. `parent` is the span that new child spans\n * attach to in the current async scope (null = attach to the trace root).\n */\nexport interface Store {\n trace: InternalTrace;\n parent: InternalSpan | null;\n}\n\nconst als = new AsyncLocalStorage<Store>();\n\nexport function getStore(): Store | undefined {\n return als.getStore();\n}\n\n/** Run `fn` with `store` as the active context for this async scope and below. */\nexport function runWith<T>(store: Store, fn: () => T): T {\n return als.run(store, fn);\n}\n","import { performance } from 'node:perf_hooks';\nimport { newSpanId, newTraceId } from './ids.js';\nimport { getStore, runWith, type InternalSpan, type InternalTrace } from './context.js';\nimport type { IgnoreRule, Span, SpanError, SpanType, Trace } from '../types.js';\n\ntype Sink = (trace: Trace) => void;\n\ninterface TracerState {\n appName: string;\n environment: string;\n sampleRate: number;\n ignore: IgnoreRule[];\n sink: Sink | null;\n}\n\nlet state: TracerState = {\n appName: 'unknown',\n environment: 'development',\n sampleRate: 1,\n ignore: [],\n sink: null,\n};\n\nexport function configure(patch: Partial<TracerState>): void {\n state = { ...state, ...patch };\n}\n\n/** True if this request should be traced (respects sampleRate + ignore rules). */\nexport function shouldTrace(req: { url?: string }): boolean {\n if (state.sampleRate < 1 && Math.random() > state.sampleRate) return false;\n const path = stripQuery(req.url ?? '');\n for (const rule of state.ignore) {\n if (typeof rule === 'string' ? rule === path : safeRule(rule, req)) return false;\n }\n return true;\n}\n\nexport function createTrace(): InternalTrace {\n return {\n traceId: newTraceId(),\n appName: state.appName,\n environment: state.environment,\n rootSpanId: newSpanId(),\n startWall: Date.now(),\n startMono: performance.now(),\n tags: {},\n spans: [],\n http: {},\n ended: false,\n };\n}\n\n/** Open a child span under the current async scope. Returns null if no active trace. */\nexport function startSpan(\n name: string,\n type: SpanType,\n metadata: Record<string, unknown> = {}\n): InternalSpan | null {\n const store = getStore();\n if (!store) return null;\n const parentId = store.parent ? store.parent.spanId : store.trace.rootSpanId;\n const span: InternalSpan = {\n spanId: newSpanId(),\n parentSpanId: parentId,\n traceId: store.trace.traceId,\n name,\n type,\n startTime: Date.now(),\n endTime: 0,\n duration: 0,\n status: 'ok',\n metadata,\n events: [],\n _startMono: performance.now(),\n };\n store.trace.spans.push(span);\n return span;\n}\n\nexport function endSpan(span: InternalSpan | null, error?: unknown): void {\n if (!span || span.endTime !== 0) return;\n span.duration = performance.now() - span._startMono;\n span.endTime = span.startTime + span.duration;\n if (error !== undefined) {\n span.status = 'error';\n span.error = toSpanError(error);\n }\n}\n\n/**\n * Run `fn` inside a new child span, establishing it as the parent for anything\n * deeper. Times the work and records thrown errors before re-throwing.\n */\nexport async function runInSpan<T>(\n name: string,\n type: SpanType,\n fn: () => Promise<T> | T\n): Promise<T> {\n const store = getStore();\n if (!store) return await fn();\n const span = startSpan(name, type);\n if (!span) return await fn();\n return await runWith({ trace: store.trace, parent: span }, async () => {\n try {\n const result = await fn();\n endSpan(span);\n return result;\n } catch (err) {\n endSpan(span, err);\n throw err;\n }\n });\n}\n\n/** Finalize a trace and hand it to the configured sink. */\nexport function endTrace(trace: InternalTrace, statusCode: number, path: string): void {\n if (trace.ended) return;\n trace.ended = true;\n\n const duration = performance.now() - trace.startMono;\n const hasError = statusCode >= 500 || trace.spans.some((s) => s.status === 'error');\n\n const out: Trace = {\n traceId: trace.traceId,\n appName: trace.appName,\n environment: trace.environment,\n startTime: trace.startWall,\n endTime: trace.startWall + duration,\n duration,\n status: hasError ? 'error' : 'ok',\n http: {\n method: trace.http.method ?? 'GET',\n path,\n rawPath: trace.http.rawPath ?? path,\n statusCode,\n ...(trace.http.userAgent !== undefined ? { userAgent: trace.http.userAgent } : {}),\n },\n tags: trace.tags,\n rootSpanId: trace.rootSpanId,\n spans: trace.spans.map(serializeSpan),\n };\n\n state.sink?.(out);\n}\n\nfunction serializeSpan(span: InternalSpan): Span {\n const { _startMono: _ignored, ...rest } = span;\n void _ignored;\n return rest;\n}\n\nfunction toSpanError(error: unknown): SpanError {\n if (error instanceof Error) {\n return {\n message: error.message,\n stack: error.stack ?? '',\n type: error.name || 'Error',\n };\n }\n return { message: String(error), stack: '', type: 'Error' };\n}\n\nfunction safeRule(rule: (req: unknown) => boolean, req: unknown): boolean {\n try {\n return rule(req);\n } catch {\n return false;\n }\n}\n\nexport function stripQuery(url: string): string {\n const q = url.indexOf('?');\n return q === -1 ? url : url.slice(0, q);\n}\n","import { randomBytes } from 'node:crypto';\n\n/** 128-bit trace id as lowercase hex (32 chars). */\nexport function newTraceId(): string {\n return randomBytes(16).toString('hex');\n}\n\n/** 64-bit span id as lowercase hex (16 chars). */\nexport function newSpanId(): string {\n return randomBytes(8).toString('hex');\n}\n","import type { Exporter } from '../transport/exporter.js';\nimport type { Trace } from '../types.js';\n\nexport interface BufferOptions {\n maxSize: number;\n flushIntervalMs: number;\n}\n\nconst DEFAULTS: BufferOptions = { maxSize: 100, flushIntervalMs: 5000 };\n\n/**\n * In-memory hold for finished traces. Flushes on an interval, when full, or on\n * shutdown. The interval timer is `unref`'d so it never keeps the process alive.\n */\nexport class TraceBuffer {\n private items: Trace[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private readonly opts: BufferOptions;\n\n constructor(\n private readonly exporter: Exporter,\n opts: Partial<BufferOptions> = {}\n ) {\n this.opts = { ...DEFAULTS, ...opts };\n }\n\n start(): void {\n if (this.timer) return;\n this.timer = setInterval(() => void this.flush(), this.opts.flushIntervalMs);\n this.timer.unref?.();\n }\n\n add(trace: Trace): void {\n this.items.push(trace);\n if (this.items.length >= this.opts.maxSize) void this.flush();\n }\n\n async flush(): Promise<void> {\n if (this.items.length === 0) return;\n const batch = this.items;\n this.items = [];\n try {\n await this.exporter.export(batch);\n } catch {\n // Drop on failure — never block or throw into the host app.\n }\n }\n\n async shutdown(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n}\n","import type { Exporter } from './exporter.js';\nimport type { Span, Trace } from '../types.js';\n\n/**\n * Dev-friendly exporter: prints each trace as a compact waterfall to stdout.\n * Used by default until an ingest `endpoint` is configured.\n */\nexport class ConsoleExporter implements Exporter {\n export(traces: Trace[]): void {\n for (const trace of traces) {\n this.print(trace);\n }\n }\n\n private print(trace: Trace): void {\n const flag = trace.status === 'error' ? '✗' : '✓';\n const head = `${flag} ${trace.http.method} ${trace.http.path} ${trace.http.statusCode} ${fmt(trace.duration)}`;\n console.log(`\\n[tracker] ${head} (${trace.traceId})`);\n\n const byParent = new Map<string, Span[]>();\n for (const span of trace.spans) {\n const key = span.parentSpanId ?? trace.rootSpanId;\n const list = byParent.get(key) ?? [];\n list.push(span);\n byParent.set(key, list);\n }\n\n const walk = (parentId: string, depth: number): void => {\n const children = (byParent.get(parentId) ?? []).sort((a, b) => a.startTime - b.startTime);\n for (const span of children) {\n const indent = ' '.repeat(depth + 1);\n const mark = span.status === 'error' ? ' ✗' : '';\n console.log(`${indent}${span.type}: ${span.name} ${fmt(span.duration)}${mark}`);\n if (span.error) {\n console.log(`${indent} ↳ ${span.error.type}: ${span.error.message}`);\n }\n walk(span.spanId, depth + 1);\n }\n };\n walk(trace.rootSpanId, 0);\n }\n}\n\nfunction fmt(ms: number): string {\n return ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(2)}s`;\n}\n","import type { Exporter } from './exporter.js';\nimport type { Trace } from '../types.js';\n\n/**\n * Ships traces to the platform ingest endpoint. Fire-and-forget: failures are\n * swallowed so the host app is never affected by tracker transport issues.\n */\nexport class HttpExporter implements Exporter {\n constructor(\n private readonly endpoint: string,\n private readonly apiKey: string,\n private readonly timeoutMs = 3000\n ) {}\n\n async export(traces: Trace[]): Promise<void> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n try {\n await fetch(this.endpoint, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ traces }),\n signal: controller.signal,\n });\n } catch {\n // Never let transport errors bubble into the host application.\n } finally {\n clearTimeout(timer);\n }\n }\n}\n","import http from 'node:http';\nimport https from 'node:https';\nimport { runWith } from '../core/context.js';\nimport {\n createTrace,\n endSpan,\n endTrace,\n shouldTrace,\n startSpan,\n stripQuery,\n} from '../core/tracer.js';\nimport { wrap, type AnyFunction } from './wrap.js';\n\nconst HANDLED = Symbol.for('deeptracker.http.handled');\n\n/**\n * Inbound: every HTTP request becomes a root trace — framework-agnostic, since\n * Express/Fastify/Koa all run on `http.Server`. We patch `Server.prototype.emit`\n * and intercept the synchronous `'request'` event so the whole handler chain\n * (and its async continuations) runs inside our AsyncLocalStorage context.\n */\nexport function installHttpServer(): void {\n const patchServer = (proto: any): void => {\n wrap(\n proto,\n 'emit',\n (emit: AnyFunction) =>\n function (this: unknown, event: string, ...args: any[]) {\n if (event !== 'request') return emit.call(this, event, ...args);\n\n const req = args[0];\n const res = args[1];\n if (!req || !res || req[HANDLED]) return emit.call(this, event, ...args);\n req[HANDLED] = true;\n\n if (!shouldTrace(req)) return emit.call(this, event, ...args);\n\n const trace = createTrace();\n trace.http.method = req.method ?? 'GET';\n trace.http.rawPath = stripQuery(String(req.url ?? '/'));\n const ua = req.headers?.['user-agent'];\n if (typeof ua === 'string') trace.http.userAgent = ua;\n\n return runWith({ trace, parent: null }, () => {\n res.on('finish', () => {\n endTrace(trace, res.statusCode ?? 0, resolvePath(req, trace.http.rawPath ?? '/'));\n });\n return emit.call(this, event, ...args);\n });\n }\n );\n };\n\n patchServer(http.Server.prototype);\n // https.Server has its own prototype; patch it too when present.\n if (https.Server && https.Server.prototype !== http.Server.prototype) {\n patchServer(https.Server.prototype);\n }\n}\n\n/** Prefer Express's parameterized route (`/users/:id`) when available. */\nfunction resolvePath(req: any, fallback: string): string {\n const routePath = req.route?.path;\n if (typeof routePath === 'string') {\n return (req.baseUrl ?? '') + routePath;\n }\n return fallback;\n}\n\n/**\n * Outbound: wrap `request`/`get` on http & https so calls made during a request\n * (axios, fetch-over-http, raw http) show up as child spans.\n */\nexport function installHttpClient(): void {\n for (const [mod, scheme] of [\n [http, 'http'],\n [https, 'https'],\n ] as const) {\n for (const method of ['request', 'get'] as const) {\n wrap(\n mod as any,\n method,\n (original: AnyFunction) =>\n function (this: unknown, ...args: any[]) {\n const desc = describeOutbound(args, scheme);\n const span = startSpan(`${desc.method} ${desc.url}`, 'http', {\n method: desc.method,\n url: desc.url,\n });\n if (!span) return original.apply(this, args);\n\n const req = original.apply(this, args);\n let settled = false;\n const finish = (err?: unknown): void => {\n if (settled) return;\n settled = true;\n endSpan(span, err);\n };\n\n try {\n req.on('response', (res: any) => {\n span.metadata.statusCode = res.statusCode;\n });\n req.on('error', (err: unknown) => finish(err));\n req.on('close', () => finish());\n } catch {\n finish();\n }\n return req;\n }\n );\n }\n }\n}\n\nfunction describeOutbound(args: any[], scheme: string): { method: string; url: string } {\n const first = args[0];\n let method = 'GET';\n let url = '';\n\n if (typeof first === 'string') {\n url = first;\n } else if (first instanceof URL) {\n url = first.toString();\n } else if (first && typeof first === 'object') {\n const host = first.hostname ?? first.host ?? 'localhost';\n const path = first.path ?? '/';\n method = first.method ?? 'GET';\n url = `${scheme}://${host}${path}`;\n }\n\n // (url, options, cb) form: method may live in the 2nd arg.\n const second = args[1];\n if (second && typeof second === 'object' && typeof second.method === 'string') {\n method = second.method;\n }\n\n return { method, url };\n}\n","/* Monkey-patching helpers. Instrumentation inherently fights the type system,\n so `any` is allowed in this folder (see eslint.config.js override). */\n\nexport type AnyFunction = (...args: any[]) => any;\n\nconst WRAPPED = Symbol.for('deeptracker.wrapped');\n\n/**\n * Replace `target[name]` with `factory(original)`, preserving the function name\n * and guarding against double-wrapping (idempotent across multiple `init()`s).\n */\nexport function wrap(\n target: Record<string, any>,\n name: string,\n factory: (original: AnyFunction) => AnyFunction\n): void {\n const original = target[name];\n if (typeof original !== 'function') return;\n if ((original as any)[WRAPPED]) return;\n\n const wrapped = factory(original);\n Object.defineProperty(wrapped, 'name', {\n value: original.name,\n configurable: true,\n });\n (wrapped as any)[WRAPPED] = true;\n (wrapped as any).__original = original;\n target[name] = wrapped;\n}\n","import { createRequire } from 'node:module';\nimport { endSpan, startSpan } from '../core/tracer.js';\nimport { wrap, type AnyFunction } from './wrap.js';\n\n/**\n * Patch a `pg`-style Client class so `.query()` becomes a `db` span. Exported\n * separately from {@link installPg} so tests can pass a fake Client.\n *\n * Supports both the promise and callback forms of `pg.Client#query`.\n */\nexport function patchPgClient(ClientClass: { prototype: Record<string, any> } | undefined): void {\n if (!ClientClass?.prototype) return;\n\n wrap(\n ClientClass.prototype,\n 'query',\n (original: AnyFunction) =>\n function (this: unknown, ...args: any[]) {\n const span = startSpan(describeQuery(args[0]), 'db', { query: extractSql(args[0]) });\n if (!span) return original.apply(this, args);\n\n const last = args[args.length - 1];\n\n // Callback form: query(text, [values], cb)\n if (typeof last === 'function') {\n args[args.length - 1] = function (this: unknown, ...cbArgs: any[]) {\n const [err, result] = cbArgs;\n if (result?.rowCount != null) span.metadata.rowCount = result.rowCount;\n endSpan(span, err ?? undefined);\n return last.apply(this, cbArgs);\n };\n return original.apply(this, args);\n }\n\n // Promise form\n const promise = original.apply(this, args);\n if (promise && typeof promise.then === 'function') {\n return promise.then(\n (result: any) => {\n if (result?.rowCount != null) span.metadata.rowCount = result.rowCount;\n endSpan(span);\n return result;\n },\n (err: unknown) => {\n endSpan(span, err);\n throw err;\n }\n );\n }\n\n endSpan(span);\n return promise;\n }\n );\n}\n\n/** Load the host app's `pg` (if installed) and patch it. No-op when absent. */\nexport function installPg(): void {\n try {\n const require = createRequire(import.meta.url);\n const pg = require('pg') as { Client?: { prototype: Record<string, any> } };\n patchPgClient(pg.Client);\n } catch {\n // pg not installed — nothing to instrument.\n }\n}\n\nfunction extractSql(arg: unknown): string {\n const text = typeof arg === 'string' ? arg : ((arg as any)?.text ?? '');\n return String(text).slice(0, 2000);\n}\n\nfunction describeQuery(arg: unknown): string {\n const sql = extractSql(arg).trim().replace(/\\s+/g, ' ');\n const verb = sql.split(' ')[0]?.toUpperCase() ?? 'QUERY';\n return `db.${verb.toLowerCase()}`;\n}\n"],"mappings":"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","names":["span","span","http","https","span","span","require"]}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Core data structures for the tracker.
3
+ * These mirror the persisted shapes documented in PLANNING.md (§4.5).
4
+ */
5
+ type SpanType = 'http' | 'db' | 'cache' | 'middleware' | 'custom';
6
+ type SpanStatus = 'ok' | 'error';
7
+ interface SpanError {
8
+ message: string;
9
+ stack: string;
10
+ type: string;
11
+ }
12
+ interface SpanEvent {
13
+ timestamp: number;
14
+ message: string;
15
+ }
16
+ interface Span {
17
+ spanId: string;
18
+ parentSpanId: string | null;
19
+ traceId: string;
20
+ name: string;
21
+ type: SpanType;
22
+ startTime: number;
23
+ endTime: number;
24
+ duration: number;
25
+ status: SpanStatus;
26
+ error?: SpanError;
27
+ metadata: Record<string, unknown>;
28
+ events: SpanEvent[];
29
+ }
30
+ interface TraceHttpInfo {
31
+ method: string;
32
+ path: string;
33
+ rawPath: string;
34
+ statusCode: number;
35
+ userAgent?: string;
36
+ }
37
+ interface Trace {
38
+ traceId: string;
39
+ appName: string;
40
+ environment: string;
41
+ startTime: number;
42
+ endTime: number;
43
+ duration: number;
44
+ status: SpanStatus;
45
+ http: TraceHttpInfo;
46
+ tags: Record<string, string>;
47
+ rootSpanId: string;
48
+ spans: Span[];
49
+ }
50
+ /** Predicate or path string used to skip tracking for certain requests. */
51
+ type IgnoreRule = string | ((req: unknown) => boolean);
52
+ interface CaptureOptions {
53
+ requestBody: boolean;
54
+ responseBody: boolean;
55
+ queryParams: boolean;
56
+ headers: string[];
57
+ }
58
+ interface TrackerConfig {
59
+ /** API key obtained from the dashboard. Required. */
60
+ apiKey: string;
61
+ /** Human-readable name of the app being traced. Required. */
62
+ appName: string;
63
+ /** Defaults to process.env.NODE_ENV. */
64
+ environment?: string;
65
+ /** Override the ingest endpoint (defaults to the hosted platform). */
66
+ endpoint?: string;
67
+ capture?: Partial<CaptureOptions>;
68
+ ignore?: IgnoreRule[];
69
+ /** 0.0–1.0. Fraction of traces to keep. Defaults to 1.0. */
70
+ sampleRate?: number;
71
+ /** Flush buffered traces at least every N ms. Defaults to 5000. */
72
+ flushIntervalMs?: number;
73
+ /** Flush as soon as this many traces are buffered. Defaults to 100. */
74
+ maxBatchSize?: number;
75
+ }
76
+
77
+ /**
78
+ * @deepeloper/deeptracker — public API surface.
79
+ *
80
+ * Call {@link init} once at your app's entry point, before requiring the
81
+ * frameworks/drivers you want auto-instrumented. Everything else (root traces,
82
+ * outbound HTTP, DB queries) is captured automatically. See PLANNING.md §4.
83
+ */
84
+
85
+ /**
86
+ * Initialize the tracker: install instrumentation and start the trace buffer.
87
+ * Idempotent-ish — calling twice re-applies config but won't double-wrap.
88
+ */
89
+ declare function init(config: TrackerConfig): void;
90
+ /** Wrap a unit of work in a manual span so it appears in the trace tree. */
91
+ declare function span<T>(name: string, fn: () => Promise<T> | T): Promise<T>;
92
+ /** Attach a tag to the currently active trace (e.g. userId, plan). */
93
+ declare function setTag(key: string, value: string): void;
94
+ /** Record a log event inside the current span (no-op outside an active span). */
95
+ declare function log(message: string): void;
96
+ /** Force-flush buffered traces. Mainly useful in tests and before shutdown. */
97
+ declare function flush(): Promise<void>;
98
+ /** Current config (used by instrumentation modules / introspection). */
99
+ declare function getConfig(): TrackerConfig | null;
100
+ declare const _default: {
101
+ init: typeof init;
102
+ span: typeof span;
103
+ setTag: typeof setTag;
104
+ log: typeof log;
105
+ flush: typeof flush;
106
+ getConfig: typeof getConfig;
107
+ };
108
+
109
+ export { type CaptureOptions, type IgnoreRule, type Span, type SpanError, type SpanEvent, type SpanStatus, type SpanType, type Trace, type TraceHttpInfo, type TrackerConfig, _default as default, flush, getConfig, init, log, setTag, span };
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Core data structures for the tracker.
3
+ * These mirror the persisted shapes documented in PLANNING.md (§4.5).
4
+ */
5
+ type SpanType = 'http' | 'db' | 'cache' | 'middleware' | 'custom';
6
+ type SpanStatus = 'ok' | 'error';
7
+ interface SpanError {
8
+ message: string;
9
+ stack: string;
10
+ type: string;
11
+ }
12
+ interface SpanEvent {
13
+ timestamp: number;
14
+ message: string;
15
+ }
16
+ interface Span {
17
+ spanId: string;
18
+ parentSpanId: string | null;
19
+ traceId: string;
20
+ name: string;
21
+ type: SpanType;
22
+ startTime: number;
23
+ endTime: number;
24
+ duration: number;
25
+ status: SpanStatus;
26
+ error?: SpanError;
27
+ metadata: Record<string, unknown>;
28
+ events: SpanEvent[];
29
+ }
30
+ interface TraceHttpInfo {
31
+ method: string;
32
+ path: string;
33
+ rawPath: string;
34
+ statusCode: number;
35
+ userAgent?: string;
36
+ }
37
+ interface Trace {
38
+ traceId: string;
39
+ appName: string;
40
+ environment: string;
41
+ startTime: number;
42
+ endTime: number;
43
+ duration: number;
44
+ status: SpanStatus;
45
+ http: TraceHttpInfo;
46
+ tags: Record<string, string>;
47
+ rootSpanId: string;
48
+ spans: Span[];
49
+ }
50
+ /** Predicate or path string used to skip tracking for certain requests. */
51
+ type IgnoreRule = string | ((req: unknown) => boolean);
52
+ interface CaptureOptions {
53
+ requestBody: boolean;
54
+ responseBody: boolean;
55
+ queryParams: boolean;
56
+ headers: string[];
57
+ }
58
+ interface TrackerConfig {
59
+ /** API key obtained from the dashboard. Required. */
60
+ apiKey: string;
61
+ /** Human-readable name of the app being traced. Required. */
62
+ appName: string;
63
+ /** Defaults to process.env.NODE_ENV. */
64
+ environment?: string;
65
+ /** Override the ingest endpoint (defaults to the hosted platform). */
66
+ endpoint?: string;
67
+ capture?: Partial<CaptureOptions>;
68
+ ignore?: IgnoreRule[];
69
+ /** 0.0–1.0. Fraction of traces to keep. Defaults to 1.0. */
70
+ sampleRate?: number;
71
+ /** Flush buffered traces at least every N ms. Defaults to 5000. */
72
+ flushIntervalMs?: number;
73
+ /** Flush as soon as this many traces are buffered. Defaults to 100. */
74
+ maxBatchSize?: number;
75
+ }
76
+
77
+ /**
78
+ * @deepeloper/deeptracker — public API surface.
79
+ *
80
+ * Call {@link init} once at your app's entry point, before requiring the
81
+ * frameworks/drivers you want auto-instrumented. Everything else (root traces,
82
+ * outbound HTTP, DB queries) is captured automatically. See PLANNING.md §4.
83
+ */
84
+
85
+ /**
86
+ * Initialize the tracker: install instrumentation and start the trace buffer.
87
+ * Idempotent-ish — calling twice re-applies config but won't double-wrap.
88
+ */
89
+ declare function init(config: TrackerConfig): void;
90
+ /** Wrap a unit of work in a manual span so it appears in the trace tree. */
91
+ declare function span<T>(name: string, fn: () => Promise<T> | T): Promise<T>;
92
+ /** Attach a tag to the currently active trace (e.g. userId, plan). */
93
+ declare function setTag(key: string, value: string): void;
94
+ /** Record a log event inside the current span (no-op outside an active span). */
95
+ declare function log(message: string): void;
96
+ /** Force-flush buffered traces. Mainly useful in tests and before shutdown. */
97
+ declare function flush(): Promise<void>;
98
+ /** Current config (used by instrumentation modules / introspection). */
99
+ declare function getConfig(): TrackerConfig | null;
100
+ declare const _default: {
101
+ init: typeof init;
102
+ span: typeof span;
103
+ setTag: typeof setTag;
104
+ log: typeof log;
105
+ flush: typeof flush;
106
+ getConfig: typeof getConfig;
107
+ };
108
+
109
+ export { type CaptureOptions, type IgnoreRule, type Span, type SpanError, type SpanEvent, type SpanStatus, type SpanType, type Trace, type TraceHttpInfo, type TrackerConfig, _default as default, flush, getConfig, init, log, setTag, span };