@deeptracer/core 0.6.4 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var SDK_VERSION = "0.6.4";
2
+ var SDK_VERSION = "0.8.0";
3
3
  var SDK_NAME = "core";
4
4
 
5
5
  // src/transport.ts
@@ -48,7 +48,10 @@ var Transport = class {
48
48
  Authorization: `Bearer ${this.authKey}`,
49
49
  "x-deeptracer-sdk": `${SDK_NAME}/${SDK_VERSION}`
50
50
  },
51
- body: JSON.stringify(body)
51
+ body: JSON.stringify(body),
52
+ // Keep the request alive if the browser tab closes or navigates away
53
+ // mid-flight. Node.js (undici) silently ignores this flag.
54
+ keepalive: typeof window !== "undefined"
52
55
  });
53
56
  if (res.ok) {
54
57
  this.warnedLabels.delete(label);
@@ -99,6 +102,7 @@ var Transport = class {
99
102
  track(promise) {
100
103
  this.inFlightRequests.add(promise);
101
104
  promise.finally(() => this.inFlightRequests.delete(promise));
105
+ this.config.waitUntil?.(promise);
102
106
  }
103
107
  async sendLogs(logs) {
104
108
  const p = this.sendWithRetry(
@@ -256,6 +260,13 @@ function parseTraceparent(header) {
256
260
  if (/^0+$/.test(traceId) || /^0+$/.test(parentId)) return null;
257
261
  return { traceId, parentId, flags };
258
262
  }
263
+ function resolveRelease(config) {
264
+ if (config.release) return config.release;
265
+ if (typeof process !== "undefined" && process.env) {
266
+ return process.env.DEEPTRACER_RELEASE || process.env.VERCEL_GIT_COMMIT_SHA || process.env.RAILWAY_GIT_COMMIT_SHA || process.env.RENDER_GIT_COMMIT || process.env.FLY_IMAGE_REF || process.env.GIT_COMMIT_SHA || process.env.COMMIT_SHA || void 0;
267
+ }
268
+ return void 0;
269
+ }
259
270
  var _originalConsole = {
260
271
  log: console.log,
261
272
  info: console.info,
@@ -268,6 +279,7 @@ var Logger = class _Logger {
268
279
  transport;
269
280
  isRoot;
270
281
  effectiveLevel;
282
+ release;
271
283
  contextName;
272
284
  config;
273
285
  state;
@@ -291,6 +303,7 @@ var Logger = class _Logger {
291
303
  _originalConsole.warn("[@deeptracer/core] No `endpoint` provided. Events will not be sent.");
292
304
  }
293
305
  }
306
+ this.release = resolveRelease(config);
294
307
  this.effectiveLevel = LOG_LEVEL_VALUES[config.level ?? (config.environment === "production" ? "info" : "debug")];
295
308
  this.transport = sharedTransport ?? new Transport(config);
296
309
  this.batcher = sharedBatcher ?? new Batcher(
@@ -454,7 +467,8 @@ var Logger = class _Logger {
454
467
  trace_id: this.requestMeta?.trace_id,
455
468
  span_id: this.requestMeta?.span_id,
456
469
  request_id: this.requestMeta?.request_id,
457
- vercel_id: this.requestMeta?.vercel_id
470
+ vercel_id: this.requestMeta?.vercel_id,
471
+ release: this.release
458
472
  };
459
473
  const hookResult = this.applyBeforeSend({ type: "log", data: entry });
460
474
  if (hookResult === null) return;
@@ -546,7 +560,8 @@ var Logger = class _Logger {
546
560
  context: Object.keys(enrichedContext).length > 0 ? enrichedContext : void 0,
547
561
  trace_id: this.requestMeta?.trace_id,
548
562
  user_id: context?.userId || this.state.user?.id,
549
- breadcrumbs: context?.breadcrumbs || [...this.state.breadcrumbs]
563
+ breadcrumbs: context?.breadcrumbs || [...this.state.breadcrumbs],
564
+ release: this.release
550
565
  };
551
566
  const hookResult = this.applyBeforeSend({ type: "error", data: report });
552
567
  if (hookResult === null) return;
@@ -566,7 +581,8 @@ var Logger = class _Logger {
566
581
  output_tokens: report.outputTokens,
567
582
  cost_usd: report.costUsd || 0,
568
583
  latency_ms: report.latencyMs,
569
- metadata
584
+ metadata,
585
+ release: this.release
570
586
  };
571
587
  const hookResult = this.applyBeforeSend({ type: "llm", data: report });
572
588
  if (hookResult === null) return;
@@ -645,7 +661,8 @@ var Logger = class _Logger {
645
661
  start_time: startTime,
646
662
  duration_ms: durationMs,
647
663
  status: options?.status || "ok",
648
- metadata: this.mergeStateMetadata(options?.metadata)
664
+ metadata: this.mergeStateMetadata(options?.metadata),
665
+ release: this.release
649
666
  };
650
667
  const hookResult = this.applyBeforeSend({ type: "trace", data: spanData });
651
668
  if (hookResult === null) return;
package/dist/index.cjs CHANGED
@@ -64,7 +64,7 @@ var Batcher = class {
64
64
  };
65
65
 
66
66
  // src/version.ts
67
- var SDK_VERSION = "0.6.4";
67
+ var SDK_VERSION = "0.8.0";
68
68
  var SDK_NAME = "core";
69
69
 
70
70
  // src/transport.ts
@@ -113,7 +113,10 @@ var Transport = class {
113
113
  Authorization: `Bearer ${this.authKey}`,
114
114
  "x-deeptracer-sdk": `${SDK_NAME}/${SDK_VERSION}`
115
115
  },
116
- body: JSON.stringify(body)
116
+ body: JSON.stringify(body),
117
+ // Keep the request alive if the browser tab closes or navigates away
118
+ // mid-flight. Node.js (undici) silently ignores this flag.
119
+ keepalive: typeof window !== "undefined"
117
120
  });
118
121
  if (res.ok) {
119
122
  this.warnedLabels.delete(label);
@@ -164,6 +167,7 @@ var Transport = class {
164
167
  track(promise) {
165
168
  this.inFlightRequests.add(promise);
166
169
  promise.finally(() => this.inFlightRequests.delete(promise));
170
+ this.config.waitUntil?.(promise);
167
171
  }
168
172
  async sendLogs(logs) {
169
173
  const p = this.sendWithRetry(
@@ -286,6 +290,13 @@ function parseTraceparent(header) {
286
290
  if (/^0+$/.test(traceId) || /^0+$/.test(parentId)) return null;
287
291
  return { traceId, parentId, flags };
288
292
  }
293
+ function resolveRelease(config) {
294
+ if (config.release) return config.release;
295
+ if (typeof process !== "undefined" && process.env) {
296
+ return process.env.DEEPTRACER_RELEASE || process.env.VERCEL_GIT_COMMIT_SHA || process.env.RAILWAY_GIT_COMMIT_SHA || process.env.RENDER_GIT_COMMIT || process.env.FLY_IMAGE_REF || process.env.GIT_COMMIT_SHA || process.env.COMMIT_SHA || void 0;
297
+ }
298
+ return void 0;
299
+ }
289
300
  var _originalConsole = {
290
301
  log: console.log,
291
302
  info: console.info,
@@ -298,6 +309,7 @@ var Logger = class _Logger {
298
309
  transport;
299
310
  isRoot;
300
311
  effectiveLevel;
312
+ release;
301
313
  contextName;
302
314
  config;
303
315
  state;
@@ -321,6 +333,7 @@ var Logger = class _Logger {
321
333
  _originalConsole.warn("[@deeptracer/core] No `endpoint` provided. Events will not be sent.");
322
334
  }
323
335
  }
336
+ this.release = resolveRelease(config);
324
337
  this.effectiveLevel = LOG_LEVEL_VALUES[config.level ?? (config.environment === "production" ? "info" : "debug")];
325
338
  this.transport = sharedTransport ?? new Transport(config);
326
339
  this.batcher = sharedBatcher ?? new Batcher(
@@ -484,7 +497,8 @@ var Logger = class _Logger {
484
497
  trace_id: this.requestMeta?.trace_id,
485
498
  span_id: this.requestMeta?.span_id,
486
499
  request_id: this.requestMeta?.request_id,
487
- vercel_id: this.requestMeta?.vercel_id
500
+ vercel_id: this.requestMeta?.vercel_id,
501
+ release: this.release
488
502
  };
489
503
  const hookResult = this.applyBeforeSend({ type: "log", data: entry });
490
504
  if (hookResult === null) return;
@@ -576,7 +590,8 @@ var Logger = class _Logger {
576
590
  context: Object.keys(enrichedContext).length > 0 ? enrichedContext : void 0,
577
591
  trace_id: this.requestMeta?.trace_id,
578
592
  user_id: context?.userId || this.state.user?.id,
579
- breadcrumbs: context?.breadcrumbs || [...this.state.breadcrumbs]
593
+ breadcrumbs: context?.breadcrumbs || [...this.state.breadcrumbs],
594
+ release: this.release
580
595
  };
581
596
  const hookResult = this.applyBeforeSend({ type: "error", data: report });
582
597
  if (hookResult === null) return;
@@ -596,7 +611,8 @@ var Logger = class _Logger {
596
611
  output_tokens: report.outputTokens,
597
612
  cost_usd: report.costUsd || 0,
598
613
  latency_ms: report.latencyMs,
599
- metadata
614
+ metadata,
615
+ release: this.release
600
616
  };
601
617
  const hookResult = this.applyBeforeSend({ type: "llm", data: report });
602
618
  if (hookResult === null) return;
@@ -675,7 +691,8 @@ var Logger = class _Logger {
675
691
  start_time: startTime,
676
692
  duration_ms: durationMs,
677
693
  status: options?.status || "ok",
678
- metadata: this.mergeStateMetadata(options?.metadata)
694
+ metadata: this.mergeStateMetadata(options?.metadata),
695
+ release: this.release
679
696
  };
680
697
  const hookResult = this.applyBeforeSend({ type: "trace", data: spanData });
681
698
  if (hookResult === null) return;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { L as Logger } from './logger-DyJENLNA.cjs';
2
- export { B as BeforeSendEvent, a as Breadcrumb, E as ErrorReport, I as InactiveSpan, b as LLMUsageReport, c as LogEntry, d as LogLevel, e as LoggerConfig, M as MiddlewareOptions, S as Span, f as SpanData, U as User, g as createLogger } from './logger-DyJENLNA.cjs';
1
+ import { L as Logger } from './logger-B-igTEwz.cjs';
2
+ export { B as BeforeSendEvent, a as Breadcrumb, E as ErrorReport, I as InactiveSpan, b as LLMUsageReport, c as LogEntry, d as LogLevel, e as LoggerConfig, M as MiddlewareOptions, S as Span, f as SpanData, U as User, g as createLogger } from './logger-B-igTEwz.cjs';
3
3
 
4
4
  /**
5
5
  * A Logger-compatible object where every method is a silent no-op.
@@ -18,7 +18,7 @@ export { B as BeforeSendEvent, a as Breadcrumb, E as ErrorReport, I as InactiveS
18
18
  declare const noopLogger: Logger;
19
19
 
20
20
  /** SDK version. Update on each release. */
21
- declare const SDK_VERSION = "0.6.4";
21
+ declare const SDK_VERSION = "0.8.0";
22
22
  declare const SDK_NAME = "core";
23
23
 
24
24
  export { Logger, SDK_NAME, SDK_VERSION, noopLogger };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { L as Logger } from './logger-DyJENLNA.js';
2
- export { B as BeforeSendEvent, a as Breadcrumb, E as ErrorReport, I as InactiveSpan, b as LLMUsageReport, c as LogEntry, d as LogLevel, e as LoggerConfig, M as MiddlewareOptions, S as Span, f as SpanData, U as User, g as createLogger } from './logger-DyJENLNA.js';
1
+ import { L as Logger } from './logger-B-igTEwz.js';
2
+ export { B as BeforeSendEvent, a as Breadcrumb, E as ErrorReport, I as InactiveSpan, b as LLMUsageReport, c as LogEntry, d as LogLevel, e as LoggerConfig, M as MiddlewareOptions, S as Span, f as SpanData, U as User, g as createLogger } from './logger-B-igTEwz.js';
3
3
 
4
4
  /**
5
5
  * A Logger-compatible object where every method is a silent no-op.
@@ -18,7 +18,7 @@ export { B as BeforeSendEvent, a as Breadcrumb, E as ErrorReport, I as InactiveS
18
18
  declare const noopLogger: Logger;
19
19
 
20
20
  /** SDK version. Update on each release. */
21
- declare const SDK_VERSION = "0.6.4";
21
+ declare const SDK_VERSION = "0.8.0";
22
22
  declare const SDK_NAME = "core";
23
23
 
24
24
  export { Logger, SDK_NAME, SDK_VERSION, noopLogger };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  SDK_NAME,
4
4
  SDK_VERSION,
5
5
  createLogger
6
- } from "./chunk-QAJHHVX4.js";
6
+ } from "./chunk-Z3AVDZKS.js";
7
7
 
8
8
  // src/noop-logger.ts
9
9
  var NOOP_INACTIVE_SPAN = {
package/dist/internal.cjs CHANGED
@@ -64,7 +64,7 @@ var Batcher = class {
64
64
  };
65
65
 
66
66
  // src/version.ts
67
- var SDK_VERSION = "0.6.4";
67
+ var SDK_VERSION = "0.8.0";
68
68
  var SDK_NAME = "core";
69
69
 
70
70
  // src/transport.ts
@@ -113,7 +113,10 @@ var Transport = class {
113
113
  Authorization: `Bearer ${this.authKey}`,
114
114
  "x-deeptracer-sdk": `${SDK_NAME}/${SDK_VERSION}`
115
115
  },
116
- body: JSON.stringify(body)
116
+ body: JSON.stringify(body),
117
+ // Keep the request alive if the browser tab closes or navigates away
118
+ // mid-flight. Node.js (undici) silently ignores this flag.
119
+ keepalive: typeof window !== "undefined"
117
120
  });
118
121
  if (res.ok) {
119
122
  this.warnedLabels.delete(label);
@@ -164,6 +167,7 @@ var Transport = class {
164
167
  track(promise) {
165
168
  this.inFlightRequests.add(promise);
166
169
  promise.finally(() => this.inFlightRequests.delete(promise));
170
+ this.config.waitUntil?.(promise);
167
171
  }
168
172
  async sendLogs(logs) {
169
173
  const p = this.sendWithRetry(
@@ -286,6 +290,13 @@ function parseTraceparent(header) {
286
290
  if (/^0+$/.test(traceId) || /^0+$/.test(parentId)) return null;
287
291
  return { traceId, parentId, flags };
288
292
  }
293
+ function resolveRelease(config) {
294
+ if (config.release) return config.release;
295
+ if (typeof process !== "undefined" && process.env) {
296
+ return process.env.DEEPTRACER_RELEASE || process.env.VERCEL_GIT_COMMIT_SHA || process.env.RAILWAY_GIT_COMMIT_SHA || process.env.RENDER_GIT_COMMIT || process.env.FLY_IMAGE_REF || process.env.GIT_COMMIT_SHA || process.env.COMMIT_SHA || void 0;
297
+ }
298
+ return void 0;
299
+ }
289
300
  var _originalConsole = {
290
301
  log: console.log,
291
302
  info: console.info,
@@ -298,6 +309,7 @@ var Logger = class _Logger {
298
309
  transport;
299
310
  isRoot;
300
311
  effectiveLevel;
312
+ release;
301
313
  contextName;
302
314
  config;
303
315
  state;
@@ -321,6 +333,7 @@ var Logger = class _Logger {
321
333
  _originalConsole.warn("[@deeptracer/core] No `endpoint` provided. Events will not be sent.");
322
334
  }
323
335
  }
336
+ this.release = resolveRelease(config);
324
337
  this.effectiveLevel = LOG_LEVEL_VALUES[config.level ?? (config.environment === "production" ? "info" : "debug")];
325
338
  this.transport = sharedTransport ?? new Transport(config);
326
339
  this.batcher = sharedBatcher ?? new Batcher(
@@ -484,7 +497,8 @@ var Logger = class _Logger {
484
497
  trace_id: this.requestMeta?.trace_id,
485
498
  span_id: this.requestMeta?.span_id,
486
499
  request_id: this.requestMeta?.request_id,
487
- vercel_id: this.requestMeta?.vercel_id
500
+ vercel_id: this.requestMeta?.vercel_id,
501
+ release: this.release
488
502
  };
489
503
  const hookResult = this.applyBeforeSend({ type: "log", data: entry });
490
504
  if (hookResult === null) return;
@@ -576,7 +590,8 @@ var Logger = class _Logger {
576
590
  context: Object.keys(enrichedContext).length > 0 ? enrichedContext : void 0,
577
591
  trace_id: this.requestMeta?.trace_id,
578
592
  user_id: context?.userId || this.state.user?.id,
579
- breadcrumbs: context?.breadcrumbs || [...this.state.breadcrumbs]
593
+ breadcrumbs: context?.breadcrumbs || [...this.state.breadcrumbs],
594
+ release: this.release
580
595
  };
581
596
  const hookResult = this.applyBeforeSend({ type: "error", data: report });
582
597
  if (hookResult === null) return;
@@ -596,7 +611,8 @@ var Logger = class _Logger {
596
611
  output_tokens: report.outputTokens,
597
612
  cost_usd: report.costUsd || 0,
598
613
  latency_ms: report.latencyMs,
599
- metadata
614
+ metadata,
615
+ release: this.release
600
616
  };
601
617
  const hookResult = this.applyBeforeSend({ type: "llm", data: report });
602
618
  if (hookResult === null) return;
@@ -675,7 +691,8 @@ var Logger = class _Logger {
675
691
  start_time: startTime,
676
692
  duration_ms: durationMs,
677
693
  status: options?.status || "ok",
678
- metadata: this.mergeStateMetadata(options?.metadata)
694
+ metadata: this.mergeStateMetadata(options?.metadata),
695
+ release: this.release
679
696
  };
680
697
  const hookResult = this.applyBeforeSend({ type: "trace", data: spanData });
681
698
  if (hookResult === null) return;
@@ -1,4 +1,4 @@
1
- export { L as Logger, e as LoggerConfig, h as LoggerState, M as MiddlewareOptions, T as Transport, _ as _originalConsole, p as parseTraceparent } from './logger-DyJENLNA.cjs';
1
+ export { L as Logger, e as LoggerConfig, h as LoggerState, M as MiddlewareOptions, T as Transport, _ as _originalConsole, p as parseTraceparent } from './logger-B-igTEwz.cjs';
2
2
 
3
3
  /**
4
4
  * Parse console.log/info/warn/error arguments into a structured `{ message, metadata }` pair.
@@ -1,4 +1,4 @@
1
- export { L as Logger, e as LoggerConfig, h as LoggerState, M as MiddlewareOptions, T as Transport, _ as _originalConsole, p as parseTraceparent } from './logger-DyJENLNA.js';
1
+ export { L as Logger, e as LoggerConfig, h as LoggerState, M as MiddlewareOptions, T as Transport, _ as _originalConsole, p as parseTraceparent } from './logger-B-igTEwz.js';
2
2
 
3
3
  /**
4
4
  * Parse console.log/info/warn/error arguments into a structured `{ message, metadata }` pair.
package/dist/internal.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  Transport,
4
4
  _originalConsole,
5
5
  parseTraceparent
6
- } from "./chunk-QAJHHVX4.js";
6
+ } from "./chunk-Z3AVDZKS.js";
7
7
 
8
8
  // src/internal.ts
9
9
  function safeStringify(value) {
@@ -39,6 +39,17 @@ interface LoggerConfig {
39
39
  * Override via `DEEPTRACER_LOG_LEVEL` (server) or `NEXT_PUBLIC_DEEPTRACER_LOG_LEVEL` (client).
40
40
  */
41
41
  level?: LogLevel;
42
+ /**
43
+ * Release identifier sent with all events (logs, errors, traces, LLM usage).
44
+ *
45
+ * Used for source map resolution on errors and deployment correlation
46
+ * across all event types.
47
+ *
48
+ * Auto-detected (in order) from environment variables when not set explicitly:
49
+ * `DEEPTRACER_RELEASE`, `VERCEL_GIT_COMMIT_SHA`, `RAILWAY_GIT_COMMIT_SHA`,
50
+ * `RENDER_GIT_COMMIT`, `FLY_IMAGE_REF`, `GIT_COMMIT_SHA`, `COMMIT_SHA`.
51
+ */
52
+ release?: string;
42
53
  /** Enable console output for all log calls (useful for local development) */
43
54
  debug?: boolean;
44
55
  /** Maximum breadcrumbs to retain for error reports. Default: 20 */
@@ -63,6 +74,28 @@ interface LoggerConfig {
63
74
  * ```
64
75
  */
65
76
  beforeSend?: (event: BeforeSendEvent) => BeforeSendEvent | null;
77
+ /**
78
+ * Called with each outgoing HTTP request promise when the transport sends data.
79
+ * Use this to keep a serverless function alive until all in-flight requests
80
+ * complete — even for logs written after the HTTP response has been returned
81
+ * (e.g., from Better Auth background task callbacks or other post-response work).
82
+ *
83
+ * On Vercel this is wired up automatically by `@deeptracer/nextjs`.
84
+ * On Cloudflare Workers, pass `ctx.waitUntil.bind(ctx)` when creating the logger.
85
+ * On persistent servers (Railway, Fly, Docker) you don't need this — the process
86
+ * stays alive and the timer-based flush handles it.
87
+ *
88
+ * @example Cloudflare Workers
89
+ * ```ts
90
+ * export default {
91
+ * async fetch(request, env, ctx) {
92
+ * const logger = createLogger({ waitUntil: ctx.waitUntil.bind(ctx), ... })
93
+ * // logs are now kept alive past the response
94
+ * }
95
+ * }
96
+ * ```
97
+ */
98
+ waitUntil?: (promise: Promise<unknown>) => void;
66
99
  }
67
100
  /** Log severity level */
68
101
  type LogLevel = "debug" | "info" | "warn" | "error";
@@ -80,6 +113,8 @@ interface LogEntry {
80
113
  request_id?: string;
81
114
  vercel_id?: string;
82
115
  context?: string;
116
+ /** Release identifier for deployment correlation */
117
+ release?: string;
83
118
  }
84
119
  /**
85
120
  * A breadcrumb entry — a trail of events leading up to an error.
@@ -106,6 +141,8 @@ interface ErrorReport {
106
141
  trace_id?: string;
107
142
  user_id?: string;
108
143
  breadcrumbs?: Breadcrumb[];
144
+ /** Release identifier for source map lookup */
145
+ release?: string;
109
146
  }
110
147
  /**
111
148
  * LLM usage report for tracking AI costs and performance.
@@ -132,6 +169,8 @@ interface SpanData {
132
169
  duration_ms: number;
133
170
  status: "ok" | "error";
134
171
  metadata?: Record<string, unknown>;
172
+ /** Release identifier for deployment correlation */
173
+ release?: string;
135
174
  }
136
175
  /**
137
176
  * A span representing a unit of work within a trace.
@@ -254,7 +293,7 @@ declare class Transport {
254
293
  * (e.g., when the ingestion endpoint is unreachable during development).
255
294
  */
256
295
  private warnedLabels;
257
- constructor(config: Pick<LoggerConfig, "endpoint" | "apiKey" | "service" | "environment">);
296
+ constructor(config: Pick<LoggerConfig, "endpoint" | "apiKey" | "service" | "environment" | "waitUntil">);
258
297
  private get authKey();
259
298
  /**
260
299
  * Send a request with automatic retry and exponential backoff.
@@ -361,6 +400,7 @@ declare class Logger {
361
400
  private transport;
362
401
  private readonly isRoot;
363
402
  private effectiveLevel;
403
+ private release;
364
404
  protected contextName?: string;
365
405
  protected config: LoggerConfig;
366
406
  protected state: LoggerState;
@@ -39,6 +39,17 @@ interface LoggerConfig {
39
39
  * Override via `DEEPTRACER_LOG_LEVEL` (server) or `NEXT_PUBLIC_DEEPTRACER_LOG_LEVEL` (client).
40
40
  */
41
41
  level?: LogLevel;
42
+ /**
43
+ * Release identifier sent with all events (logs, errors, traces, LLM usage).
44
+ *
45
+ * Used for source map resolution on errors and deployment correlation
46
+ * across all event types.
47
+ *
48
+ * Auto-detected (in order) from environment variables when not set explicitly:
49
+ * `DEEPTRACER_RELEASE`, `VERCEL_GIT_COMMIT_SHA`, `RAILWAY_GIT_COMMIT_SHA`,
50
+ * `RENDER_GIT_COMMIT`, `FLY_IMAGE_REF`, `GIT_COMMIT_SHA`, `COMMIT_SHA`.
51
+ */
52
+ release?: string;
42
53
  /** Enable console output for all log calls (useful for local development) */
43
54
  debug?: boolean;
44
55
  /** Maximum breadcrumbs to retain for error reports. Default: 20 */
@@ -63,6 +74,28 @@ interface LoggerConfig {
63
74
  * ```
64
75
  */
65
76
  beforeSend?: (event: BeforeSendEvent) => BeforeSendEvent | null;
77
+ /**
78
+ * Called with each outgoing HTTP request promise when the transport sends data.
79
+ * Use this to keep a serverless function alive until all in-flight requests
80
+ * complete — even for logs written after the HTTP response has been returned
81
+ * (e.g., from Better Auth background task callbacks or other post-response work).
82
+ *
83
+ * On Vercel this is wired up automatically by `@deeptracer/nextjs`.
84
+ * On Cloudflare Workers, pass `ctx.waitUntil.bind(ctx)` when creating the logger.
85
+ * On persistent servers (Railway, Fly, Docker) you don't need this — the process
86
+ * stays alive and the timer-based flush handles it.
87
+ *
88
+ * @example Cloudflare Workers
89
+ * ```ts
90
+ * export default {
91
+ * async fetch(request, env, ctx) {
92
+ * const logger = createLogger({ waitUntil: ctx.waitUntil.bind(ctx), ... })
93
+ * // logs are now kept alive past the response
94
+ * }
95
+ * }
96
+ * ```
97
+ */
98
+ waitUntil?: (promise: Promise<unknown>) => void;
66
99
  }
67
100
  /** Log severity level */
68
101
  type LogLevel = "debug" | "info" | "warn" | "error";
@@ -80,6 +113,8 @@ interface LogEntry {
80
113
  request_id?: string;
81
114
  vercel_id?: string;
82
115
  context?: string;
116
+ /** Release identifier for deployment correlation */
117
+ release?: string;
83
118
  }
84
119
  /**
85
120
  * A breadcrumb entry — a trail of events leading up to an error.
@@ -106,6 +141,8 @@ interface ErrorReport {
106
141
  trace_id?: string;
107
142
  user_id?: string;
108
143
  breadcrumbs?: Breadcrumb[];
144
+ /** Release identifier for source map lookup */
145
+ release?: string;
109
146
  }
110
147
  /**
111
148
  * LLM usage report for tracking AI costs and performance.
@@ -132,6 +169,8 @@ interface SpanData {
132
169
  duration_ms: number;
133
170
  status: "ok" | "error";
134
171
  metadata?: Record<string, unknown>;
172
+ /** Release identifier for deployment correlation */
173
+ release?: string;
135
174
  }
136
175
  /**
137
176
  * A span representing a unit of work within a trace.
@@ -254,7 +293,7 @@ declare class Transport {
254
293
  * (e.g., when the ingestion endpoint is unreachable during development).
255
294
  */
256
295
  private warnedLabels;
257
- constructor(config: Pick<LoggerConfig, "endpoint" | "apiKey" | "service" | "environment">);
296
+ constructor(config: Pick<LoggerConfig, "endpoint" | "apiKey" | "service" | "environment" | "waitUntil">);
258
297
  private get authKey();
259
298
  /**
260
299
  * Send a request with automatic retry and exponential backoff.
@@ -361,6 +400,7 @@ declare class Logger {
361
400
  private transport;
362
401
  private readonly isRoot;
363
402
  private effectiveLevel;
403
+ private release;
364
404
  protected contextName?: string;
365
405
  protected config: LoggerConfig;
366
406
  protected state: LoggerState;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeptracer/core",
3
- "version": "0.6.4",
3
+ "version": "0.8.0",
4
4
  "description": "Core SDK for DeepTracer — Logger class, types, transport, batcher, tracing",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",