@deeptracer/core 0.4.1 → 0.4.2

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 CHANGED
@@ -47,11 +47,10 @@ The package ships as both ESM and CJS with full TypeScript declarations. Zero ru
47
47
  import { createLogger } from "@deeptracer/core"
48
48
 
49
49
  const logger = createLogger({
50
- product: "my-app",
51
50
  service: "api",
52
51
  environment: "production",
53
52
  endpoint: "https://your-deeptracer.example.com",
54
- apiKey: "dt_live_xxx",
53
+ secretKey: "dt_secret_xxx",
55
54
  })
56
55
 
57
56
  // Structured logging (batched -- sent in groups of 50 or every 5 seconds)
@@ -85,11 +84,10 @@ Pass a `LoggerConfig` object to `createLogger()`:
85
84
  ```ts
86
85
  const logger = createLogger({
87
86
  // Required
88
- product: "spotbeam", // Product name for grouping in the dashboard
89
87
  service: "api", // Service name within the product
90
88
  environment: "production", // "production" or "staging"
91
89
  endpoint: "https://dt.co", // DeepTracer ingestion endpoint URL
92
- apiKey: "dt_live_xxx", // API key for authentication
90
+ secretKey: "dt_secret_xxx", // Server-side API key for authentication
93
91
 
94
92
  // Optional
95
93
  batchSize: 50, // Logs to buffer before sending (default: 50)
@@ -100,11 +98,11 @@ const logger = createLogger({
100
98
 
101
99
  | Field | Type | Required | Default | Description |
102
100
  |-------|------|----------|---------|-------------|
103
- | `product` | `string` | Yes | -- | Product name (e.g., `"spotbeam"`, `"macro"`) |
104
101
  | `service` | `string` | Yes | -- | Service name (e.g., `"api"`, `"worker"`, `"web"`) |
105
102
  | `environment` | `"production" \| "staging"` | Yes | -- | Deployment environment |
106
103
  | `endpoint` | `string` | Yes | -- | DeepTracer ingestion endpoint URL |
107
- | `apiKey` | `string` | Yes | -- | DeepTracer API key |
104
+ | `secretKey` | `string` | Yes | -- | Server-side API key (prefix: `dt_secret_`) |
105
+ | `publicKey` | `string` | No | -- | Client-side API key (prefix: `dt_public_`) |
108
106
  | `batchSize` | `number` | No | `50` | Number of log entries to buffer before flushing |
109
107
  | `flushIntervalMs` | `number` | No | `5000` | Milliseconds between automatic flushes |
110
108
  | `debug` | `boolean` | No | `false` | When `true`, all log calls also print to the console |
@@ -119,11 +117,10 @@ Create a new `Logger` instance. This is the main entry point.
119
117
  import { createLogger } from "@deeptracer/core"
120
118
 
121
119
  const logger = createLogger({
122
- product: "my-app",
123
120
  service: "api",
124
121
  environment: "production",
125
122
  endpoint: "https://your-deeptracer.example.com",
126
- apiKey: "dt_live_xxx",
123
+ secretKey: "dt_secret_xxx",
127
124
  })
128
125
  ```
129
126
 
@@ -439,11 +436,11 @@ process.on("SIGTERM", () => {
439
436
 
440
437
  ```ts
441
438
  interface LoggerConfig {
442
- product: string
443
439
  service: string
444
440
  environment: "production" | "staging"
445
441
  endpoint: string
446
- apiKey: string
442
+ secretKey: string
443
+ publicKey?: string
447
444
  batchSize?: number // default: 50
448
445
  flushIntervalMs?: number // default: 5000
449
446
  debug?: boolean // default: false
@@ -587,9 +584,9 @@ The transport layer sends data to four DeepTracer ingestion endpoints:
587
584
  | `POST /ingest/llm` | Immediate | LLM usage reports |
588
585
 
589
586
  All requests include:
590
- - `Authorization: Bearer <apiKey>` header
587
+ - `Authorization: Bearer <secretKey>` header
591
588
  - `Content-Type: application/json` header
592
- - `product`, `service`, and `environment` fields in the JSON body
589
+ - `service` and `environment` fields in the JSON body
593
590
 
594
591
  If a request fails, a warning is logged to the console. The SDK does not retry failed requests -- it is designed to be non-blocking and never crash your application.
595
592
 
@@ -1,11 +1,14 @@
1
1
  // src/version.ts
2
- var SDK_VERSION = "0.4.1";
2
+ var SDK_VERSION = "0.4.2";
3
3
  var SDK_NAME = "core";
4
4
 
5
5
  // src/transport.ts
6
6
  var Transport = class {
7
7
  constructor(config) {
8
8
  this.config = config;
9
+ const hasKey = !!(config.secretKey || config.publicKey);
10
+ const hasEndpoint = !!config.endpoint;
11
+ this.disabled = !hasKey || !hasEndpoint;
9
12
  if (config.secretKey?.startsWith("dt_secret_") && typeof globalThis.window !== "undefined") {
10
13
  console.error(
11
14
  "[@deeptracer/core] WARNING: `secretKey` (dt_secret_...) detected in a browser bundle. This exposes your server key to end users. Use `publicKey` (dt_public_...) for client-side code."
@@ -13,6 +16,20 @@ var Transport = class {
13
16
  }
14
17
  }
15
18
  inFlightRequests = /* @__PURE__ */ new Set();
19
+ /**
20
+ * When true, all send methods become silent no-ops.
21
+ * Set automatically when no auth key or no endpoint is configured.
22
+ * This prevents pointless network requests and console noise during
23
+ * local development without API keys.
24
+ */
25
+ disabled;
26
+ /**
27
+ * Tracks which send types (logs, error, trace, LLM usage) have already
28
+ * logged a failure warning. After the first failure for a given type,
29
+ * subsequent failures are silently dropped to prevent console spam
30
+ * (e.g., when the ingestion endpoint is unreachable during development).
31
+ */
32
+ warnedLabels = /* @__PURE__ */ new Set();
16
33
  /** Resolve the auth key: prefer secretKey (server), fall back to publicKey (client). */
17
34
  get authKey() {
18
35
  return this.config.secretKey ?? this.config.publicKey ?? "";
@@ -21,8 +38,12 @@ var Transport = class {
21
38
  * Send a request with automatic retry and exponential backoff.
22
39
  * Retries up to `maxRetries` times on network errors and 5xx responses.
23
40
  * Does NOT retry on 4xx (client errors — bad payload, auth failure, etc.).
41
+ *
42
+ * After the first total failure for a given label, subsequent failures
43
+ * are silently dropped (no more console warnings).
24
44
  */
25
45
  async sendWithRetry(url, body, label, maxRetries = 3) {
46
+ if (this.disabled) return;
26
47
  const baseDelays = [1e3, 2e3, 4e3];
27
48
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
28
49
  try {
@@ -35,26 +56,40 @@ var Transport = class {
35
56
  },
36
57
  body: JSON.stringify(body)
37
58
  });
38
- if (res.ok) return;
59
+ if (res.ok) {
60
+ this.warnedLabels.delete(label);
61
+ return;
62
+ }
39
63
  if (res.status >= 400 && res.status < 500) {
40
- console.warn(
41
- `[@deeptracer/core] Failed to send ${label}: ${res.status} ${res.statusText}`
42
- );
64
+ if (!this.warnedLabels.has(label)) {
65
+ this.warnedLabels.add(label);
66
+ console.warn(
67
+ `[@deeptracer/core] Failed to send ${label}: ${res.status} ${res.statusText}`
68
+ );
69
+ }
43
70
  return;
44
71
  }
45
72
  if (attempt < maxRetries) {
46
73
  await this.sleep(this.jitter(baseDelays[attempt]));
47
74
  continue;
48
75
  }
49
- console.warn(
50
- `[@deeptracer/core] Failed to send ${label}: ${res.status} ${res.statusText} (exhausted ${maxRetries} retries)`
51
- );
76
+ if (!this.warnedLabels.has(label)) {
77
+ this.warnedLabels.add(label);
78
+ console.warn(
79
+ `[@deeptracer/core] Failed to send ${label}: ${res.status} ${res.statusText} (exhausted ${maxRetries} retries). Suppressing further warnings.`
80
+ );
81
+ }
52
82
  } catch {
53
83
  if (attempt < maxRetries) {
54
84
  await this.sleep(this.jitter(baseDelays[attempt]));
55
85
  continue;
56
86
  }
57
- console.warn(`[@deeptracer/core] Failed to send ${label} (exhausted ${maxRetries} retries)`);
87
+ if (!this.warnedLabels.has(label)) {
88
+ this.warnedLabels.add(label);
89
+ console.warn(
90
+ `[@deeptracer/core] Failed to send ${label} (exhausted ${maxRetries} retries). Suppressing further warnings.`
91
+ );
92
+ }
58
93
  }
59
94
  }
60
95
  }
@@ -186,9 +221,7 @@ function cloneState(state) {
186
221
  return {
187
222
  user: state.user ? { ...state.user } : null,
188
223
  tags: { ...state.tags },
189
- contexts: Object.fromEntries(
190
- Object.entries(state.contexts).map(([k, v]) => [k, { ...v }])
191
- ),
224
+ contexts: Object.fromEntries(Object.entries(state.contexts).map(([k, v]) => [k, { ...v }])),
192
225
  breadcrumbs: [...state.breadcrumbs],
193
226
  maxBreadcrumbs: state.maxBreadcrumbs
194
227
  };
@@ -248,15 +281,18 @@ var Logger = class _Logger {
248
281
  this.contextName = contextName;
249
282
  this.requestMeta = requestMeta;
250
283
  this.state = state ?? createLoggerState(config.maxBreadcrumbs ?? 20);
251
- if (!config.secretKey && !config.publicKey) {
252
- _originalConsole.error(
253
- "[@deeptracer/core] No `secretKey` or `publicKey` provided. Events will not be authenticated."
284
+ const hasKey = !!(config.secretKey || config.publicKey);
285
+ const hasEndpoint = !!config.endpoint;
286
+ if (!hasKey && !hasEndpoint) {
287
+ _originalConsole.warn(
288
+ "[@deeptracer/core] No API key or endpoint configured. Running in local-only mode (logging methods work, but events are not sent). Set DEEPTRACER_SECRET_KEY and DEEPTRACER_ENDPOINT to enable."
254
289
  );
255
- }
256
- if (!config.endpoint) {
257
- _originalConsole.error(
258
- "[@deeptracer/core] No `endpoint` provided. Events will not be sent."
290
+ } else if (!hasKey) {
291
+ _originalConsole.warn(
292
+ "[@deeptracer/core] No `secretKey` or `publicKey` provided. Events will not be sent."
259
293
  );
294
+ } else if (!hasEndpoint) {
295
+ _originalConsole.warn("[@deeptracer/core] No `endpoint` provided. Events will not be sent.");
260
296
  }
261
297
  this.effectiveLevel = LOG_LEVEL_VALUES[config.level ?? (config.environment === "production" ? "info" : "debug")];
262
298
  this.transport = new Transport(config);
package/dist/index.cjs CHANGED
@@ -62,13 +62,16 @@ var Batcher = class {
62
62
  };
63
63
 
64
64
  // src/version.ts
65
- var SDK_VERSION = "0.4.1";
65
+ var SDK_VERSION = "0.4.2";
66
66
  var SDK_NAME = "core";
67
67
 
68
68
  // src/transport.ts
69
69
  var Transport = class {
70
70
  constructor(config) {
71
71
  this.config = config;
72
+ const hasKey = !!(config.secretKey || config.publicKey);
73
+ const hasEndpoint = !!config.endpoint;
74
+ this.disabled = !hasKey || !hasEndpoint;
72
75
  if (config.secretKey?.startsWith("dt_secret_") && typeof globalThis.window !== "undefined") {
73
76
  console.error(
74
77
  "[@deeptracer/core] WARNING: `secretKey` (dt_secret_...) detected in a browser bundle. This exposes your server key to end users. Use `publicKey` (dt_public_...) for client-side code."
@@ -76,6 +79,20 @@ var Transport = class {
76
79
  }
77
80
  }
78
81
  inFlightRequests = /* @__PURE__ */ new Set();
82
+ /**
83
+ * When true, all send methods become silent no-ops.
84
+ * Set automatically when no auth key or no endpoint is configured.
85
+ * This prevents pointless network requests and console noise during
86
+ * local development without API keys.
87
+ */
88
+ disabled;
89
+ /**
90
+ * Tracks which send types (logs, error, trace, LLM usage) have already
91
+ * logged a failure warning. After the first failure for a given type,
92
+ * subsequent failures are silently dropped to prevent console spam
93
+ * (e.g., when the ingestion endpoint is unreachable during development).
94
+ */
95
+ warnedLabels = /* @__PURE__ */ new Set();
79
96
  /** Resolve the auth key: prefer secretKey (server), fall back to publicKey (client). */
80
97
  get authKey() {
81
98
  return this.config.secretKey ?? this.config.publicKey ?? "";
@@ -84,8 +101,12 @@ var Transport = class {
84
101
  * Send a request with automatic retry and exponential backoff.
85
102
  * Retries up to `maxRetries` times on network errors and 5xx responses.
86
103
  * Does NOT retry on 4xx (client errors — bad payload, auth failure, etc.).
104
+ *
105
+ * After the first total failure for a given label, subsequent failures
106
+ * are silently dropped (no more console warnings).
87
107
  */
88
108
  async sendWithRetry(url, body, label, maxRetries = 3) {
109
+ if (this.disabled) return;
89
110
  const baseDelays = [1e3, 2e3, 4e3];
90
111
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
91
112
  try {
@@ -98,26 +119,40 @@ var Transport = class {
98
119
  },
99
120
  body: JSON.stringify(body)
100
121
  });
101
- if (res.ok) return;
122
+ if (res.ok) {
123
+ this.warnedLabels.delete(label);
124
+ return;
125
+ }
102
126
  if (res.status >= 400 && res.status < 500) {
103
- console.warn(
104
- `[@deeptracer/core] Failed to send ${label}: ${res.status} ${res.statusText}`
105
- );
127
+ if (!this.warnedLabels.has(label)) {
128
+ this.warnedLabels.add(label);
129
+ console.warn(
130
+ `[@deeptracer/core] Failed to send ${label}: ${res.status} ${res.statusText}`
131
+ );
132
+ }
106
133
  return;
107
134
  }
108
135
  if (attempt < maxRetries) {
109
136
  await this.sleep(this.jitter(baseDelays[attempt]));
110
137
  continue;
111
138
  }
112
- console.warn(
113
- `[@deeptracer/core] Failed to send ${label}: ${res.status} ${res.statusText} (exhausted ${maxRetries} retries)`
114
- );
139
+ if (!this.warnedLabels.has(label)) {
140
+ this.warnedLabels.add(label);
141
+ console.warn(
142
+ `[@deeptracer/core] Failed to send ${label}: ${res.status} ${res.statusText} (exhausted ${maxRetries} retries). Suppressing further warnings.`
143
+ );
144
+ }
115
145
  } catch {
116
146
  if (attempt < maxRetries) {
117
147
  await this.sleep(this.jitter(baseDelays[attempt]));
118
148
  continue;
119
149
  }
120
- console.warn(`[@deeptracer/core] Failed to send ${label} (exhausted ${maxRetries} retries)`);
150
+ if (!this.warnedLabels.has(label)) {
151
+ this.warnedLabels.add(label);
152
+ console.warn(
153
+ `[@deeptracer/core] Failed to send ${label} (exhausted ${maxRetries} retries). Suppressing further warnings.`
154
+ );
155
+ }
121
156
  }
122
157
  }
123
158
  }
@@ -215,9 +250,7 @@ function cloneState(state) {
215
250
  return {
216
251
  user: state.user ? { ...state.user } : null,
217
252
  tags: { ...state.tags },
218
- contexts: Object.fromEntries(
219
- Object.entries(state.contexts).map(([k, v]) => [k, { ...v }])
220
- ),
253
+ contexts: Object.fromEntries(Object.entries(state.contexts).map(([k, v]) => [k, { ...v }])),
221
254
  breadcrumbs: [...state.breadcrumbs],
222
255
  maxBreadcrumbs: state.maxBreadcrumbs
223
256
  };
@@ -277,15 +310,18 @@ var Logger = class _Logger {
277
310
  this.contextName = contextName;
278
311
  this.requestMeta = requestMeta;
279
312
  this.state = state ?? createLoggerState(config.maxBreadcrumbs ?? 20);
280
- if (!config.secretKey && !config.publicKey) {
281
- _originalConsole.error(
282
- "[@deeptracer/core] No `secretKey` or `publicKey` provided. Events will not be authenticated."
313
+ const hasKey = !!(config.secretKey || config.publicKey);
314
+ const hasEndpoint = !!config.endpoint;
315
+ if (!hasKey && !hasEndpoint) {
316
+ _originalConsole.warn(
317
+ "[@deeptracer/core] No API key or endpoint configured. Running in local-only mode (logging methods work, but events are not sent). Set DEEPTRACER_SECRET_KEY and DEEPTRACER_ENDPOINT to enable."
283
318
  );
284
- }
285
- if (!config.endpoint) {
286
- _originalConsole.error(
287
- "[@deeptracer/core] No `endpoint` provided. Events will not be sent."
319
+ } else if (!hasKey) {
320
+ _originalConsole.warn(
321
+ "[@deeptracer/core] No `secretKey` or `publicKey` provided. Events will not be sent."
288
322
  );
323
+ } else if (!hasEndpoint) {
324
+ _originalConsole.warn("[@deeptracer/core] No `endpoint` provided. Events will not be sent.");
289
325
  }
290
326
  this.effectiveLevel = LOG_LEVEL_VALUES[config.level ?? (config.environment === "production" ? "info" : "debug")];
291
327
  this.transport = new Transport(config);
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { B as BeforeSendEvent, a as Breadcrumb, E as ErrorReport, I as InactiveSpan, L as LLMUsageReport, b as LogEntry, c as LogLevel, d as Logger, e as LoggerConfig, M as MiddlewareOptions, S as Span, f as SpanData, U as User, g as createLogger } from './logger-BDTEt7Gi.cjs';
2
2
 
3
3
  /** SDK version. Update on each release. */
4
- declare const SDK_VERSION = "0.4.1";
4
+ declare const SDK_VERSION = "0.4.2";
5
5
  declare const SDK_NAME = "core";
6
6
 
7
7
  export { SDK_NAME, SDK_VERSION };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { B as BeforeSendEvent, a as Breadcrumb, E as ErrorReport, I as InactiveSpan, L as LLMUsageReport, b as LogEntry, c as LogLevel, d as Logger, e as LoggerConfig, M as MiddlewareOptions, S as Span, f as SpanData, U as User, g as createLogger } from './logger-BDTEt7Gi.js';
2
2
 
3
3
  /** SDK version. Update on each release. */
4
- declare const SDK_VERSION = "0.4.1";
4
+ declare const SDK_VERSION = "0.4.2";
5
5
  declare const SDK_NAME = "core";
6
6
 
7
7
  export { SDK_NAME, SDK_VERSION };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  SDK_NAME,
4
4
  SDK_VERSION,
5
5
  createLogger
6
- } from "./chunk-YBQXVNNR.js";
6
+ } from "./chunk-IWPXOBZV.js";
7
7
  export {
8
8
  Logger,
9
9
  SDK_NAME,
package/dist/internal.cjs CHANGED
@@ -63,13 +63,16 @@ var Batcher = class {
63
63
  };
64
64
 
65
65
  // src/version.ts
66
- var SDK_VERSION = "0.4.1";
66
+ var SDK_VERSION = "0.4.2";
67
67
  var SDK_NAME = "core";
68
68
 
69
69
  // src/transport.ts
70
70
  var Transport = class {
71
71
  constructor(config) {
72
72
  this.config = config;
73
+ const hasKey = !!(config.secretKey || config.publicKey);
74
+ const hasEndpoint = !!config.endpoint;
75
+ this.disabled = !hasKey || !hasEndpoint;
73
76
  if (config.secretKey?.startsWith("dt_secret_") && typeof globalThis.window !== "undefined") {
74
77
  console.error(
75
78
  "[@deeptracer/core] WARNING: `secretKey` (dt_secret_...) detected in a browser bundle. This exposes your server key to end users. Use `publicKey` (dt_public_...) for client-side code."
@@ -77,6 +80,20 @@ var Transport = class {
77
80
  }
78
81
  }
79
82
  inFlightRequests = /* @__PURE__ */ new Set();
83
+ /**
84
+ * When true, all send methods become silent no-ops.
85
+ * Set automatically when no auth key or no endpoint is configured.
86
+ * This prevents pointless network requests and console noise during
87
+ * local development without API keys.
88
+ */
89
+ disabled;
90
+ /**
91
+ * Tracks which send types (logs, error, trace, LLM usage) have already
92
+ * logged a failure warning. After the first failure for a given type,
93
+ * subsequent failures are silently dropped to prevent console spam
94
+ * (e.g., when the ingestion endpoint is unreachable during development).
95
+ */
96
+ warnedLabels = /* @__PURE__ */ new Set();
80
97
  /** Resolve the auth key: prefer secretKey (server), fall back to publicKey (client). */
81
98
  get authKey() {
82
99
  return this.config.secretKey ?? this.config.publicKey ?? "";
@@ -85,8 +102,12 @@ var Transport = class {
85
102
  * Send a request with automatic retry and exponential backoff.
86
103
  * Retries up to `maxRetries` times on network errors and 5xx responses.
87
104
  * Does NOT retry on 4xx (client errors — bad payload, auth failure, etc.).
105
+ *
106
+ * After the first total failure for a given label, subsequent failures
107
+ * are silently dropped (no more console warnings).
88
108
  */
89
109
  async sendWithRetry(url, body, label, maxRetries = 3) {
110
+ if (this.disabled) return;
90
111
  const baseDelays = [1e3, 2e3, 4e3];
91
112
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
92
113
  try {
@@ -99,26 +120,40 @@ var Transport = class {
99
120
  },
100
121
  body: JSON.stringify(body)
101
122
  });
102
- if (res.ok) return;
123
+ if (res.ok) {
124
+ this.warnedLabels.delete(label);
125
+ return;
126
+ }
103
127
  if (res.status >= 400 && res.status < 500) {
104
- console.warn(
105
- `[@deeptracer/core] Failed to send ${label}: ${res.status} ${res.statusText}`
106
- );
128
+ if (!this.warnedLabels.has(label)) {
129
+ this.warnedLabels.add(label);
130
+ console.warn(
131
+ `[@deeptracer/core] Failed to send ${label}: ${res.status} ${res.statusText}`
132
+ );
133
+ }
107
134
  return;
108
135
  }
109
136
  if (attempt < maxRetries) {
110
137
  await this.sleep(this.jitter(baseDelays[attempt]));
111
138
  continue;
112
139
  }
113
- console.warn(
114
- `[@deeptracer/core] Failed to send ${label}: ${res.status} ${res.statusText} (exhausted ${maxRetries} retries)`
115
- );
140
+ if (!this.warnedLabels.has(label)) {
141
+ this.warnedLabels.add(label);
142
+ console.warn(
143
+ `[@deeptracer/core] Failed to send ${label}: ${res.status} ${res.statusText} (exhausted ${maxRetries} retries). Suppressing further warnings.`
144
+ );
145
+ }
116
146
  } catch {
117
147
  if (attempt < maxRetries) {
118
148
  await this.sleep(this.jitter(baseDelays[attempt]));
119
149
  continue;
120
150
  }
121
- console.warn(`[@deeptracer/core] Failed to send ${label} (exhausted ${maxRetries} retries)`);
151
+ if (!this.warnedLabels.has(label)) {
152
+ this.warnedLabels.add(label);
153
+ console.warn(
154
+ `[@deeptracer/core] Failed to send ${label} (exhausted ${maxRetries} retries). Suppressing further warnings.`
155
+ );
156
+ }
122
157
  }
123
158
  }
124
159
  }
@@ -216,9 +251,7 @@ function cloneState(state) {
216
251
  return {
217
252
  user: state.user ? { ...state.user } : null,
218
253
  tags: { ...state.tags },
219
- contexts: Object.fromEntries(
220
- Object.entries(state.contexts).map(([k, v]) => [k, { ...v }])
221
- ),
254
+ contexts: Object.fromEntries(Object.entries(state.contexts).map(([k, v]) => [k, { ...v }])),
222
255
  breadcrumbs: [...state.breadcrumbs],
223
256
  maxBreadcrumbs: state.maxBreadcrumbs
224
257
  };
@@ -278,15 +311,18 @@ var Logger = class _Logger {
278
311
  this.contextName = contextName;
279
312
  this.requestMeta = requestMeta;
280
313
  this.state = state ?? createLoggerState(config.maxBreadcrumbs ?? 20);
281
- if (!config.secretKey && !config.publicKey) {
282
- _originalConsole.error(
283
- "[@deeptracer/core] No `secretKey` or `publicKey` provided. Events will not be authenticated."
314
+ const hasKey = !!(config.secretKey || config.publicKey);
315
+ const hasEndpoint = !!config.endpoint;
316
+ if (!hasKey && !hasEndpoint) {
317
+ _originalConsole.warn(
318
+ "[@deeptracer/core] No API key or endpoint configured. Running in local-only mode (logging methods work, but events are not sent). Set DEEPTRACER_SECRET_KEY and DEEPTRACER_ENDPOINT to enable."
284
319
  );
285
- }
286
- if (!config.endpoint) {
287
- _originalConsole.error(
288
- "[@deeptracer/core] No `endpoint` provided. Events will not be sent."
320
+ } else if (!hasKey) {
321
+ _originalConsole.warn(
322
+ "[@deeptracer/core] No `secretKey` or `publicKey` provided. Events will not be sent."
289
323
  );
324
+ } else if (!hasEndpoint) {
325
+ _originalConsole.warn("[@deeptracer/core] No `endpoint` provided. Events will not be sent.");
290
326
  }
291
327
  this.effectiveLevel = LOG_LEVEL_VALUES[config.level ?? (config.environment === "production" ? "info" : "debug")];
292
328
  this.transport = new Transport(config);
@@ -9,10 +9,27 @@ export { d as Logger, h as LoggerState, M as MiddlewareOptions, _ as _originalCo
9
9
  * - Only retries on network errors and 5xx (not 4xx client errors)
10
10
  * - SDK version header on every request (`x-deeptracer-sdk: core/0.3.0`)
11
11
  * - In-flight request tracking for graceful shutdown via `drain()`
12
+ * - Silent no-op when no API key or endpoint is configured (no retries, no console noise)
13
+ * - Warn-once per failure type — first failure for each send type (logs, error, trace)
14
+ * logs a warning, subsequent identical failures are silently dropped
12
15
  */
13
16
  declare class Transport {
14
17
  private config;
15
18
  private inFlightRequests;
19
+ /**
20
+ * When true, all send methods become silent no-ops.
21
+ * Set automatically when no auth key or no endpoint is configured.
22
+ * This prevents pointless network requests and console noise during
23
+ * local development without API keys.
24
+ */
25
+ private readonly disabled;
26
+ /**
27
+ * Tracks which send types (logs, error, trace, LLM usage) have already
28
+ * logged a failure warning. After the first failure for a given type,
29
+ * subsequent failures are silently dropped to prevent console spam
30
+ * (e.g., when the ingestion endpoint is unreachable during development).
31
+ */
32
+ private warnedLabels;
16
33
  constructor(config: Pick<LoggerConfig, "endpoint" | "secretKey" | "publicKey" | "service" | "environment">);
17
34
  /** Resolve the auth key: prefer secretKey (server), fall back to publicKey (client). */
18
35
  private get authKey();
@@ -20,6 +37,9 @@ declare class Transport {
20
37
  * Send a request with automatic retry and exponential backoff.
21
38
  * Retries up to `maxRetries` times on network errors and 5xx responses.
22
39
  * Does NOT retry on 4xx (client errors — bad payload, auth failure, etc.).
40
+ *
41
+ * After the first total failure for a given label, subsequent failures
42
+ * are silently dropped (no more console warnings).
23
43
  */
24
44
  private sendWithRetry;
25
45
  /** Add +/- 20% jitter to a delay to prevent thundering herd. */
@@ -9,10 +9,27 @@ export { d as Logger, h as LoggerState, M as MiddlewareOptions, _ as _originalCo
9
9
  * - Only retries on network errors and 5xx (not 4xx client errors)
10
10
  * - SDK version header on every request (`x-deeptracer-sdk: core/0.3.0`)
11
11
  * - In-flight request tracking for graceful shutdown via `drain()`
12
+ * - Silent no-op when no API key or endpoint is configured (no retries, no console noise)
13
+ * - Warn-once per failure type — first failure for each send type (logs, error, trace)
14
+ * logs a warning, subsequent identical failures are silently dropped
12
15
  */
13
16
  declare class Transport {
14
17
  private config;
15
18
  private inFlightRequests;
19
+ /**
20
+ * When true, all send methods become silent no-ops.
21
+ * Set automatically when no auth key or no endpoint is configured.
22
+ * This prevents pointless network requests and console noise during
23
+ * local development without API keys.
24
+ */
25
+ private readonly disabled;
26
+ /**
27
+ * Tracks which send types (logs, error, trace, LLM usage) have already
28
+ * logged a failure warning. After the first failure for a given type,
29
+ * subsequent failures are silently dropped to prevent console spam
30
+ * (e.g., when the ingestion endpoint is unreachable during development).
31
+ */
32
+ private warnedLabels;
16
33
  constructor(config: Pick<LoggerConfig, "endpoint" | "secretKey" | "publicKey" | "service" | "environment">);
17
34
  /** Resolve the auth key: prefer secretKey (server), fall back to publicKey (client). */
18
35
  private get authKey();
@@ -20,6 +37,9 @@ declare class Transport {
20
37
  * Send a request with automatic retry and exponential backoff.
21
38
  * Retries up to `maxRetries` times on network errors and 5xx responses.
22
39
  * Does NOT retry on 4xx (client errors — bad payload, auth failure, etc.).
40
+ *
41
+ * After the first total failure for a given label, subsequent failures
42
+ * are silently dropped (no more console warnings).
23
43
  */
24
44
  private sendWithRetry;
25
45
  /** Add +/- 20% jitter to a delay to prevent thundering herd. */
package/dist/internal.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  Transport,
4
4
  _originalConsole,
5
5
  parseTraceparent
6
- } from "./chunk-YBQXVNNR.js";
6
+ } from "./chunk-IWPXOBZV.js";
7
7
 
8
8
  // src/internal.ts
9
9
  function safeStringify(value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeptracer/core",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Core SDK for DeepTracer — Logger class, types, transport, batcher, tracing",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -35,6 +35,9 @@
35
35
  "directory": "packages/core"
36
36
  },
37
37
  "license": "MIT",
38
+ "engines": {
39
+ "node": ">=20"
40
+ },
38
41
  "scripts": {
39
42
  "build": "tsup",
40
43
  "dev": "tsup --watch"