tracebeam-sdk 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.
- package/README.md +141 -0
- package/dist/client-CaHega3m.d.mts +207 -0
- package/dist/client-CaHega3m.d.ts +207 -0
- package/dist/index.d.mts +28 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +580 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +550 -0
- package/dist/index.mjs.map +1 -0
- package/dist/integrations/express.d.mts +70 -0
- package/dist/integrations/express.d.ts +70 -0
- package/dist/integrations/express.js +90 -0
- package/dist/integrations/express.js.map +1 -0
- package/dist/integrations/express.mjs +64 -0
- package/dist/integrations/express.mjs.map +1 -0
- package/dist/integrations/fastify.d.mts +76 -0
- package/dist/integrations/fastify.d.ts +76 -0
- package/dist/integrations/fastify.js +87 -0
- package/dist/integrations/fastify.js.map +1 -0
- package/dist/integrations/fastify.mjs +61 -0
- package/dist/integrations/fastify.mjs.map +1 -0
- package/package.json +67 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
var DEFAULT_CONFIG = {
|
|
3
|
+
environment: "development",
|
|
4
|
+
endpoint: "https://ingest.tracebeam.io",
|
|
5
|
+
batchSize: 100,
|
|
6
|
+
flushInterval: 5e3,
|
|
7
|
+
timeout: 1e4,
|
|
8
|
+
maxQueueSize: 1e4,
|
|
9
|
+
sampleRate: 1,
|
|
10
|
+
debug: false
|
|
11
|
+
};
|
|
12
|
+
var ENV_VARS = {
|
|
13
|
+
API_KEY: "TRACEBEAM_API_KEY",
|
|
14
|
+
PROJECT_ID: "TRACEBEAM_PROJECT_ID",
|
|
15
|
+
ENVIRONMENT: "TRACEBEAM_ENVIRONMENT",
|
|
16
|
+
ENDPOINT: "TRACEBEAM_ENDPOINT",
|
|
17
|
+
BATCH_SIZE: "TRACEBEAM_BATCH_SIZE",
|
|
18
|
+
FLUSH_INTERVAL: "TRACEBEAM_FLUSH_INTERVAL",
|
|
19
|
+
TIMEOUT: "TRACEBEAM_HTTP_TIMEOUT",
|
|
20
|
+
MAX_QUEUE_SIZE: "TRACEBEAM_MAX_QUEUE_SIZE",
|
|
21
|
+
SAMPLE_RATE: "TRACEBEAM_SAMPLE_RATE",
|
|
22
|
+
DEBUG: "TRACEBEAM_DEBUG"
|
|
23
|
+
};
|
|
24
|
+
var ConfigError = class extends Error {
|
|
25
|
+
constructor(message) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = "ConfigError";
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
function loadConfigFromEnv() {
|
|
31
|
+
const apiKey = process.env[ENV_VARS.API_KEY];
|
|
32
|
+
const projectId = process.env[ENV_VARS.PROJECT_ID];
|
|
33
|
+
if (!apiKey) {
|
|
34
|
+
throw new ConfigError(`Missing required environment variable: ${ENV_VARS.API_KEY}`);
|
|
35
|
+
}
|
|
36
|
+
if (!projectId) {
|
|
37
|
+
throw new ConfigError(`Missing required environment variable: ${ENV_VARS.PROJECT_ID}`);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
apiKey,
|
|
41
|
+
projectId,
|
|
42
|
+
environment: process.env[ENV_VARS.ENVIRONMENT] || DEFAULT_CONFIG.environment,
|
|
43
|
+
endpoint: process.env[ENV_VARS.ENDPOINT] || DEFAULT_CONFIG.endpoint,
|
|
44
|
+
batchSize: parseIntEnv(ENV_VARS.BATCH_SIZE, DEFAULT_CONFIG.batchSize),
|
|
45
|
+
flushInterval: parseIntEnv(ENV_VARS.FLUSH_INTERVAL, DEFAULT_CONFIG.flushInterval),
|
|
46
|
+
timeout: parseIntEnv(ENV_VARS.TIMEOUT, DEFAULT_CONFIG.timeout),
|
|
47
|
+
maxQueueSize: parseIntEnv(ENV_VARS.MAX_QUEUE_SIZE, DEFAULT_CONFIG.maxQueueSize),
|
|
48
|
+
sampleRate: parseFloatEnv(ENV_VARS.SAMPLE_RATE, DEFAULT_CONFIG.sampleRate),
|
|
49
|
+
debug: parseBoolEnv(ENV_VARS.DEBUG, DEFAULT_CONFIG.debug)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function mergeConfig(config) {
|
|
53
|
+
return {
|
|
54
|
+
apiKey: config.apiKey,
|
|
55
|
+
projectId: config.projectId,
|
|
56
|
+
environment: config.environment ?? DEFAULT_CONFIG.environment,
|
|
57
|
+
endpoint: config.endpoint ?? DEFAULT_CONFIG.endpoint,
|
|
58
|
+
batchSize: config.batchSize ?? DEFAULT_CONFIG.batchSize,
|
|
59
|
+
flushInterval: config.flushInterval ?? DEFAULT_CONFIG.flushInterval,
|
|
60
|
+
timeout: config.timeout ?? DEFAULT_CONFIG.timeout,
|
|
61
|
+
maxQueueSize: config.maxQueueSize ?? DEFAULT_CONFIG.maxQueueSize,
|
|
62
|
+
sampleRate: config.sampleRate ?? DEFAULT_CONFIG.sampleRate,
|
|
63
|
+
debug: config.debug ?? DEFAULT_CONFIG.debug
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function validateConfig(config) {
|
|
67
|
+
if (!config.apiKey || config.apiKey.trim() === "") {
|
|
68
|
+
throw new ConfigError("apiKey is required");
|
|
69
|
+
}
|
|
70
|
+
if (!config.projectId || config.projectId.trim() === "") {
|
|
71
|
+
throw new ConfigError("projectId is required");
|
|
72
|
+
}
|
|
73
|
+
if (config.environment !== void 0 && config.environment.length > 64) {
|
|
74
|
+
throw new ConfigError("environment must be at most 64 characters");
|
|
75
|
+
}
|
|
76
|
+
if (config.batchSize !== void 0 && (config.batchSize < 1 || config.batchSize > 1e4)) {
|
|
77
|
+
throw new ConfigError("batchSize must be between 1 and 10000");
|
|
78
|
+
}
|
|
79
|
+
if (config.sampleRate !== void 0 && (config.sampleRate < 0 || config.sampleRate > 1)) {
|
|
80
|
+
throw new ConfigError("sampleRate must be between 0 and 1");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function parseIntEnv(name, defaultValue) {
|
|
84
|
+
const value = process.env[name];
|
|
85
|
+
if (!value) return defaultValue;
|
|
86
|
+
const parsed = parseInt(value, 10);
|
|
87
|
+
return isNaN(parsed) ? defaultValue : parsed;
|
|
88
|
+
}
|
|
89
|
+
function parseFloatEnv(name, defaultValue) {
|
|
90
|
+
const value = process.env[name];
|
|
91
|
+
if (!value) return defaultValue;
|
|
92
|
+
const parsed = parseFloat(value);
|
|
93
|
+
return isNaN(parsed) ? defaultValue : parsed;
|
|
94
|
+
}
|
|
95
|
+
function parseBoolEnv(name, defaultValue) {
|
|
96
|
+
const value = process.env[name];
|
|
97
|
+
if (!value) return defaultValue;
|
|
98
|
+
return value.toLowerCase() === "true" || value === "1";
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/transport.ts
|
|
102
|
+
var defaultLogger = {
|
|
103
|
+
debug: (msg, ...args) => console.debug(`[tracebeam] ${msg}`, ...args),
|
|
104
|
+
warn: (msg, ...args) => console.warn(`[tracebeam] ${msg}`, ...args),
|
|
105
|
+
error: (msg, ...args) => console.error(`[tracebeam] ${msg}`, ...args)
|
|
106
|
+
};
|
|
107
|
+
var Transport = class {
|
|
108
|
+
endpoint;
|
|
109
|
+
apiKey;
|
|
110
|
+
timeout;
|
|
111
|
+
logger;
|
|
112
|
+
debug;
|
|
113
|
+
state = "active";
|
|
114
|
+
constructor(options) {
|
|
115
|
+
this.endpoint = options.endpoint.replace(/\/$/, "");
|
|
116
|
+
this.apiKey = options.apiKey;
|
|
117
|
+
this.timeout = options.timeout;
|
|
118
|
+
this.debug = options.debug;
|
|
119
|
+
this.logger = options.logger ?? defaultLogger;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get current transport state
|
|
123
|
+
*/
|
|
124
|
+
getState() {
|
|
125
|
+
return this.state;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Check if transport is active
|
|
129
|
+
*/
|
|
130
|
+
isActive() {
|
|
131
|
+
return this.state === "active";
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Send a batch of events to the Ingest API
|
|
135
|
+
* Implements retry with exponential backoff
|
|
136
|
+
*/
|
|
137
|
+
async send(events) {
|
|
138
|
+
if (!this.isActive()) {
|
|
139
|
+
return {
|
|
140
|
+
success: false,
|
|
141
|
+
error: "Transport disabled",
|
|
142
|
+
message: `Transport is ${this.state}`,
|
|
143
|
+
code: 0
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const batch = { events };
|
|
147
|
+
const url = `${this.endpoint}/api/v1/events`;
|
|
148
|
+
let lastError = null;
|
|
149
|
+
const maxRetries = 3;
|
|
150
|
+
const baseDelay = 1e3;
|
|
151
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
152
|
+
try {
|
|
153
|
+
const response = await this.executeRequest(url, batch);
|
|
154
|
+
if (response.success) {
|
|
155
|
+
if (this.debug) {
|
|
156
|
+
this.logger.debug(`Sent ${events.length} events successfully`);
|
|
157
|
+
}
|
|
158
|
+
return response;
|
|
159
|
+
}
|
|
160
|
+
const errorResponse = response;
|
|
161
|
+
lastError = errorResponse;
|
|
162
|
+
switch (errorResponse.code) {
|
|
163
|
+
case 401:
|
|
164
|
+
this.state = "disabled";
|
|
165
|
+
this.logger.error("Invalid API key. SDK disabled.");
|
|
166
|
+
return errorResponse;
|
|
167
|
+
case 402:
|
|
168
|
+
this.state = "quota_exceeded";
|
|
169
|
+
this.logger.warn("Event quota exceeded. SDK paused.");
|
|
170
|
+
return errorResponse;
|
|
171
|
+
case 429:
|
|
172
|
+
const retryAfter = errorResponse.retry_after ?? 1;
|
|
173
|
+
if (attempt < maxRetries) {
|
|
174
|
+
await this.sleep(retryAfter * 1e3);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
return errorResponse;
|
|
178
|
+
case 400:
|
|
179
|
+
this.logger.warn("Bad request:", errorResponse.message);
|
|
180
|
+
return errorResponse;
|
|
181
|
+
default:
|
|
182
|
+
if (errorResponse.code >= 500 && attempt < maxRetries) {
|
|
183
|
+
const delay = this.calculateBackoff(attempt, baseDelay);
|
|
184
|
+
if (this.debug) {
|
|
185
|
+
this.logger.debug(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
|
|
186
|
+
}
|
|
187
|
+
await this.sleep(delay);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
return errorResponse;
|
|
191
|
+
}
|
|
192
|
+
} catch (error) {
|
|
193
|
+
if (attempt < maxRetries) {
|
|
194
|
+
const delay = this.calculateBackoff(attempt, baseDelay);
|
|
195
|
+
if (this.debug) {
|
|
196
|
+
this.logger.debug(`Network error, retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
|
|
197
|
+
}
|
|
198
|
+
await this.sleep(delay);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const networkError = {
|
|
202
|
+
success: false,
|
|
203
|
+
error: "Network Error",
|
|
204
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
205
|
+
code: 0
|
|
206
|
+
};
|
|
207
|
+
return networkError;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return lastError ?? {
|
|
211
|
+
success: false,
|
|
212
|
+
error: "Max retries exceeded",
|
|
213
|
+
message: "Failed after maximum retry attempts",
|
|
214
|
+
code: 0
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Execute HTTP request
|
|
219
|
+
*/
|
|
220
|
+
async executeRequest(url, batch) {
|
|
221
|
+
const controller = new AbortController();
|
|
222
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
223
|
+
try {
|
|
224
|
+
const response = await fetch(url, {
|
|
225
|
+
method: "POST",
|
|
226
|
+
headers: {
|
|
227
|
+
"Content-Type": "application/json",
|
|
228
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
229
|
+
},
|
|
230
|
+
body: JSON.stringify(batch),
|
|
231
|
+
signal: controller.signal
|
|
232
|
+
});
|
|
233
|
+
const data = await response.json();
|
|
234
|
+
return data;
|
|
235
|
+
} finally {
|
|
236
|
+
clearTimeout(timeoutId);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Calculate backoff delay with jitter
|
|
241
|
+
*/
|
|
242
|
+
calculateBackoff(attempt, baseDelay) {
|
|
243
|
+
const exponentialDelay = baseDelay * Math.pow(2, attempt);
|
|
244
|
+
const jitter = exponentialDelay * 0.1 * (Math.random() * 2 - 1);
|
|
245
|
+
return Math.round(exponentialDelay + jitter);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Sleep for specified milliseconds
|
|
249
|
+
*/
|
|
250
|
+
sleep(ms) {
|
|
251
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// src/queue.ts
|
|
256
|
+
var EventQueue = class {
|
|
257
|
+
queue = [];
|
|
258
|
+
options;
|
|
259
|
+
onFlush;
|
|
260
|
+
flushTimer = null;
|
|
261
|
+
isFlushing = false;
|
|
262
|
+
constructor(options, onFlush) {
|
|
263
|
+
this.options = options;
|
|
264
|
+
this.onFlush = onFlush;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Start the background flush timer
|
|
268
|
+
*/
|
|
269
|
+
start() {
|
|
270
|
+
if (this.flushTimer) return;
|
|
271
|
+
this.flushTimer = setInterval(() => {
|
|
272
|
+
this.flush().catch(() => {
|
|
273
|
+
});
|
|
274
|
+
}, this.options.flushInterval);
|
|
275
|
+
if (this.flushTimer.unref) {
|
|
276
|
+
this.flushTimer.unref();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Stop the background flush timer
|
|
281
|
+
*/
|
|
282
|
+
stop() {
|
|
283
|
+
if (this.flushTimer) {
|
|
284
|
+
clearInterval(this.flushTimer);
|
|
285
|
+
this.flushTimer = null;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Add an event to the queue
|
|
290
|
+
* Returns immediately (non-blocking)
|
|
291
|
+
*/
|
|
292
|
+
add(event) {
|
|
293
|
+
if (this.queue.length >= this.options.maxQueueSize) {
|
|
294
|
+
this.queue.shift();
|
|
295
|
+
if (this.options.debug) {
|
|
296
|
+
console.warn("[tracebeam] Queue full, dropping oldest event");
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
this.queue.push(event);
|
|
300
|
+
if (this.queue.length >= this.options.batchSize) {
|
|
301
|
+
this.flush().catch(() => {
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Flush all queued events
|
|
307
|
+
*/
|
|
308
|
+
async flush() {
|
|
309
|
+
if (this.isFlushing || this.queue.length === 0) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
this.isFlushing = true;
|
|
313
|
+
try {
|
|
314
|
+
const events = this.queue.splice(0, this.queue.length);
|
|
315
|
+
await this.onFlush(events);
|
|
316
|
+
} finally {
|
|
317
|
+
this.isFlushing = false;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Get current queue size
|
|
322
|
+
*/
|
|
323
|
+
size() {
|
|
324
|
+
return this.queue.length;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Check if queue is empty
|
|
328
|
+
*/
|
|
329
|
+
isEmpty() {
|
|
330
|
+
return this.queue.length === 0;
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
function createEvent(projectId, environment, error, options = {}, globalTags = {}, globalExtra = {}) {
|
|
334
|
+
if (!projectId || projectId.trim() === "") {
|
|
335
|
+
throw new ConfigError('createEvent: "projectId" must be a non-empty string.');
|
|
336
|
+
}
|
|
337
|
+
if (!environment || environment.trim() === "") {
|
|
338
|
+
throw new ConfigError('createEvent: "environment" must be a non-empty string.');
|
|
339
|
+
}
|
|
340
|
+
const level = options.level ?? "error";
|
|
341
|
+
const timestamp = options.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
342
|
+
const tags = {
|
|
343
|
+
...globalTags,
|
|
344
|
+
...options.tags
|
|
345
|
+
};
|
|
346
|
+
const extra = {
|
|
347
|
+
...globalExtra,
|
|
348
|
+
...options.extra
|
|
349
|
+
};
|
|
350
|
+
const truncatedError = error.length > 1e4 ? error.slice(0, 1e4) : error;
|
|
351
|
+
return {
|
|
352
|
+
project_id: projectId,
|
|
353
|
+
error: truncatedError,
|
|
354
|
+
level,
|
|
355
|
+
environment,
|
|
356
|
+
timestamp,
|
|
357
|
+
tags,
|
|
358
|
+
extra
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// src/client.ts
|
|
363
|
+
var TraceBeamSDK = class _TraceBeamSDK {
|
|
364
|
+
config;
|
|
365
|
+
transport;
|
|
366
|
+
queue;
|
|
367
|
+
globalTags = {};
|
|
368
|
+
globalExtra = {};
|
|
369
|
+
userId = null;
|
|
370
|
+
isInitialized = true;
|
|
371
|
+
/**
|
|
372
|
+
* Create SDK instance from configuration
|
|
373
|
+
*/
|
|
374
|
+
constructor(config) {
|
|
375
|
+
validateConfig(config);
|
|
376
|
+
this.config = mergeConfig(config);
|
|
377
|
+
this.transport = new Transport({
|
|
378
|
+
endpoint: this.config.endpoint,
|
|
379
|
+
apiKey: this.config.apiKey,
|
|
380
|
+
timeout: this.config.timeout,
|
|
381
|
+
debug: this.config.debug
|
|
382
|
+
});
|
|
383
|
+
this.queue = new EventQueue(
|
|
384
|
+
{
|
|
385
|
+
batchSize: this.config.batchSize,
|
|
386
|
+
flushInterval: this.config.flushInterval,
|
|
387
|
+
maxQueueSize: this.config.maxQueueSize,
|
|
388
|
+
debug: this.config.debug
|
|
389
|
+
},
|
|
390
|
+
this.handleFlush.bind(this)
|
|
391
|
+
);
|
|
392
|
+
this.queue.start();
|
|
393
|
+
if (this.config.debug) {
|
|
394
|
+
console.debug("[tracebeam] Initialized", {
|
|
395
|
+
projectId: this.config.projectId,
|
|
396
|
+
environment: this.config.environment,
|
|
397
|
+
endpoint: this.config.endpoint
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Create SDK instance from environment variables
|
|
403
|
+
*
|
|
404
|
+
* Required env vars:
|
|
405
|
+
* - TRACEBEAM_API_KEY
|
|
406
|
+
* - TRACEBEAM_PROJECT_ID
|
|
407
|
+
*
|
|
408
|
+
* Optional env vars:
|
|
409
|
+
* - TRACEBEAM_ENVIRONMENT (default: 'development')
|
|
410
|
+
* - TRACEBEAM_ENDPOINT (default: 'https://ingest.tracebeam.io')
|
|
411
|
+
* - TRACEBEAM_BATCH_SIZE (default: 100)
|
|
412
|
+
* - TRACEBEAM_FLUSH_INTERVAL (default: 5000)
|
|
413
|
+
* - TRACEBEAM_HTTP_TIMEOUT (default: 10000)
|
|
414
|
+
* - TRACEBEAM_MAX_QUEUE_SIZE (default: 10000)
|
|
415
|
+
* - TRACEBEAM_SAMPLE_RATE (default: 1.0)
|
|
416
|
+
* - TRACEBEAM_DEBUG (default: false)
|
|
417
|
+
*/
|
|
418
|
+
static fromEnv() {
|
|
419
|
+
const config = loadConfigFromEnv();
|
|
420
|
+
return new _TraceBeamSDK(config);
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Capture an exception/error
|
|
424
|
+
* Returns immediately (non-blocking)
|
|
425
|
+
*/
|
|
426
|
+
captureException(error, options = {}) {
|
|
427
|
+
if (!this.canCapture()) return;
|
|
428
|
+
const errorMessage = error instanceof Error ? `${error.name}: ${error.message}
|
|
429
|
+
${error.stack ?? ""}` : String(error);
|
|
430
|
+
const event = this.createEventWithContext(errorMessage, {
|
|
431
|
+
level: "error",
|
|
432
|
+
...options
|
|
433
|
+
});
|
|
434
|
+
this.queue.add(event);
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Capture a message/event
|
|
438
|
+
* Returns immediately (non-blocking)
|
|
439
|
+
*/
|
|
440
|
+
captureMessage(message, options = {}) {
|
|
441
|
+
if (!this.canCapture()) return;
|
|
442
|
+
const event = this.createEventWithContext(message, {
|
|
443
|
+
level: "info",
|
|
444
|
+
...options
|
|
445
|
+
});
|
|
446
|
+
this.queue.add(event);
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Set the current user ID
|
|
450
|
+
* Will be included in all subsequent events
|
|
451
|
+
*/
|
|
452
|
+
setUser(userId) {
|
|
453
|
+
this.userId = userId;
|
|
454
|
+
if (userId) {
|
|
455
|
+
this.globalExtra["user_id"] = userId;
|
|
456
|
+
} else {
|
|
457
|
+
delete this.globalExtra["user_id"];
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Set a global tag
|
|
462
|
+
* Will be included in all subsequent events
|
|
463
|
+
*/
|
|
464
|
+
setTag(key, value) {
|
|
465
|
+
this.globalTags[key] = value;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Set multiple global tags
|
|
469
|
+
* Will be included in all subsequent events
|
|
470
|
+
*/
|
|
471
|
+
setTags(tags) {
|
|
472
|
+
Object.assign(this.globalTags, tags);
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Set global extra context
|
|
476
|
+
* Will be included in all subsequent events
|
|
477
|
+
*/
|
|
478
|
+
setExtra(key, value) {
|
|
479
|
+
this.globalExtra[key] = value;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Set multiple global extra context values
|
|
483
|
+
*/
|
|
484
|
+
setExtras(extras) {
|
|
485
|
+
Object.assign(this.globalExtra, extras);
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Manually flush queued events
|
|
489
|
+
* Call this to ensure events are sent before process exit
|
|
490
|
+
*/
|
|
491
|
+
async flush() {
|
|
492
|
+
await this.queue.flush();
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Graceful shutdown
|
|
496
|
+
* Flushes remaining events and stops background workers
|
|
497
|
+
*/
|
|
498
|
+
async close() {
|
|
499
|
+
this.isInitialized = false;
|
|
500
|
+
this.queue.stop();
|
|
501
|
+
await this.flush();
|
|
502
|
+
if (this.config.debug) {
|
|
503
|
+
console.debug("[tracebeam] Closed");
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Check if SDK is active and can capture events
|
|
508
|
+
*/
|
|
509
|
+
isActive() {
|
|
510
|
+
return this.isInitialized && this.transport.isActive();
|
|
511
|
+
}
|
|
512
|
+
// Private methods
|
|
513
|
+
canCapture() {
|
|
514
|
+
if (!this.isInitialized) return false;
|
|
515
|
+
if (!this.transport.isActive()) return false;
|
|
516
|
+
if (this.config.sampleRate < 1) {
|
|
517
|
+
if (Math.random() > this.config.sampleRate) {
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return true;
|
|
522
|
+
}
|
|
523
|
+
createEventWithContext(error, options) {
|
|
524
|
+
return createEvent(
|
|
525
|
+
this.config.projectId,
|
|
526
|
+
this.config.environment,
|
|
527
|
+
error,
|
|
528
|
+
options,
|
|
529
|
+
this.globalTags,
|
|
530
|
+
this.globalExtra
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
async handleFlush(events) {
|
|
534
|
+
if (events.length === 0) return;
|
|
535
|
+
try {
|
|
536
|
+
await this.transport.send(events);
|
|
537
|
+
} catch (error) {
|
|
538
|
+
if (this.config.debug) {
|
|
539
|
+
console.error("[tracebeam] Failed to flush events:", error);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
export {
|
|
545
|
+
ConfigError,
|
|
546
|
+
DEFAULT_CONFIG,
|
|
547
|
+
TraceBeamSDK,
|
|
548
|
+
loadConfigFromEnv
|
|
549
|
+
};
|
|
550
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/transport.ts","../src/queue.ts","../src/client.ts"],"sourcesContent":["import type { TraceBeamConfig } from './types.js';\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_CONFIG = {\n environment: 'development',\n endpoint: 'https://ingest.tracebeam.io',\n batchSize: 100,\n flushInterval: 5000,\n timeout: 10000,\n maxQueueSize: 10000,\n sampleRate: 1.0,\n debug: false,\n} as const;\n\n/**\n * Environment variable names\n */\nconst ENV_VARS = {\n API_KEY: 'TRACEBEAM_API_KEY',\n PROJECT_ID: 'TRACEBEAM_PROJECT_ID',\n ENVIRONMENT: 'TRACEBEAM_ENVIRONMENT',\n ENDPOINT: 'TRACEBEAM_ENDPOINT',\n BATCH_SIZE: 'TRACEBEAM_BATCH_SIZE',\n FLUSH_INTERVAL: 'TRACEBEAM_FLUSH_INTERVAL',\n TIMEOUT: 'TRACEBEAM_HTTP_TIMEOUT',\n MAX_QUEUE_SIZE: 'TRACEBEAM_MAX_QUEUE_SIZE',\n SAMPLE_RATE: 'TRACEBEAM_SAMPLE_RATE',\n DEBUG: 'TRACEBEAM_DEBUG',\n} as const;\n\n/**\n * Configuration error thrown when required values are missing\n */\nexport class ConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ConfigError';\n }\n}\n\n/**\n * Load configuration from environment variables\n */\nexport function loadConfigFromEnv(): TraceBeamConfig {\n const apiKey = process.env[ENV_VARS.API_KEY];\n const projectId = process.env[ENV_VARS.PROJECT_ID];\n\n if (!apiKey) {\n throw new ConfigError(`Missing required environment variable: ${ENV_VARS.API_KEY}`);\n }\n\n if (!projectId) {\n throw new ConfigError(`Missing required environment variable: ${ENV_VARS.PROJECT_ID}`);\n }\n\n return {\n apiKey,\n projectId,\n environment: process.env[ENV_VARS.ENVIRONMENT] || DEFAULT_CONFIG.environment,\n endpoint: process.env[ENV_VARS.ENDPOINT] || DEFAULT_CONFIG.endpoint,\n batchSize: parseIntEnv(ENV_VARS.BATCH_SIZE, DEFAULT_CONFIG.batchSize),\n flushInterval: parseIntEnv(ENV_VARS.FLUSH_INTERVAL, DEFAULT_CONFIG.flushInterval),\n timeout: parseIntEnv(ENV_VARS.TIMEOUT, DEFAULT_CONFIG.timeout),\n maxQueueSize: parseIntEnv(ENV_VARS.MAX_QUEUE_SIZE, DEFAULT_CONFIG.maxQueueSize),\n sampleRate: parseFloatEnv(ENV_VARS.SAMPLE_RATE, DEFAULT_CONFIG.sampleRate),\n debug: parseBoolEnv(ENV_VARS.DEBUG, DEFAULT_CONFIG.debug),\n };\n}\n\n/**\n * Merge user config with defaults\n */\nexport function mergeConfig(config: TraceBeamConfig): Required<TraceBeamConfig> {\n return {\n apiKey: config.apiKey,\n projectId: config.projectId,\n environment: config.environment ?? DEFAULT_CONFIG.environment,\n endpoint: config.endpoint ?? DEFAULT_CONFIG.endpoint,\n batchSize: config.batchSize ?? DEFAULT_CONFIG.batchSize,\n flushInterval: config.flushInterval ?? DEFAULT_CONFIG.flushInterval,\n timeout: config.timeout ?? DEFAULT_CONFIG.timeout,\n maxQueueSize: config.maxQueueSize ?? DEFAULT_CONFIG.maxQueueSize,\n sampleRate: config.sampleRate ?? DEFAULT_CONFIG.sampleRate,\n debug: config.debug ?? DEFAULT_CONFIG.debug,\n };\n}\n\n/**\n * Validate configuration\n */\nexport function validateConfig(config: TraceBeamConfig): void {\n if (!config.apiKey || config.apiKey.trim() === '') {\n throw new ConfigError('apiKey is required');\n }\n\n if (!config.projectId || config.projectId.trim() === '') {\n throw new ConfigError('projectId is required');\n }\n\n if (config.environment !== undefined && config.environment.length > 64) {\n throw new ConfigError('environment must be at most 64 characters');\n }\n\n if (config.batchSize !== undefined && (config.batchSize < 1 || config.batchSize > 10000)) {\n throw new ConfigError('batchSize must be between 1 and 10000');\n }\n\n if (config.sampleRate !== undefined && (config.sampleRate < 0 || config.sampleRate > 1)) {\n throw new ConfigError('sampleRate must be between 0 and 1');\n }\n}\n\n// Helper functions for parsing environment variables\n\nfunction parseIntEnv(name: string, defaultValue: number): number {\n const value = process.env[name];\n if (!value) return defaultValue;\n const parsed = parseInt(value, 10);\n return isNaN(parsed) ? defaultValue : parsed;\n}\n\nfunction parseFloatEnv(name: string, defaultValue: number): number {\n const value = process.env[name];\n if (!value) return defaultValue;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? defaultValue : parsed;\n}\n\nfunction parseBoolEnv(name: string, defaultValue: boolean): boolean {\n const value = process.env[name];\n if (!value) return defaultValue;\n return value.toLowerCase() === 'true' || value === '1';\n}\n","import type { Event, EventBatch, TransportResponse, TransportErrorResponse } from './types.js';\n\n/**\n * Transport state\n */\nexport type TransportState = 'active' | 'disabled' | 'quota_exceeded';\n\n/**\n * Logger interface for transport\n */\nexport interface TransportLogger {\n debug: (message: string, ...args: unknown[]) => void;\n warn: (message: string, ...args: unknown[]) => void;\n error: (message: string, ...args: unknown[]) => void;\n}\n\n/**\n * Transport options\n */\nexport interface TransportOptions {\n endpoint: string;\n apiKey: string;\n timeout: number;\n debug: boolean;\n logger?: TransportLogger;\n}\n\n/**\n * Default console logger\n */\nconst defaultLogger: TransportLogger = {\n debug: (msg, ...args) => console.debug(`[tracebeam] ${msg}`, ...args),\n warn: (msg, ...args) => console.warn(`[tracebeam] ${msg}`, ...args),\n error: (msg, ...args) => console.error(`[tracebeam] ${msg}`, ...args),\n};\n\n/**\n * HTTP transport for sending events to the Ingest API\n */\nexport class Transport {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly timeout: number;\n private readonly logger: TransportLogger;\n private readonly debug: boolean;\n\n private state: TransportState = 'active';\n\n constructor(options: TransportOptions) {\n this.endpoint = options.endpoint.replace(/\\/$/, ''); // Remove trailing slash\n this.apiKey = options.apiKey;\n this.timeout = options.timeout;\n this.debug = options.debug;\n this.logger = options.logger ?? defaultLogger;\n }\n\n /**\n * Get current transport state\n */\n getState(): TransportState {\n return this.state;\n }\n\n /**\n * Check if transport is active\n */\n isActive(): boolean {\n return this.state === 'active';\n }\n\n /**\n * Send a batch of events to the Ingest API\n * Implements retry with exponential backoff\n */\n async send(events: Event[]): Promise<TransportResponse> {\n if (!this.isActive()) {\n return {\n success: false,\n error: 'Transport disabled',\n message: `Transport is ${this.state}`,\n code: 0,\n };\n }\n\n const batch: EventBatch = { events };\n const url = `${this.endpoint}/api/v1/events`;\n\n let lastError: TransportErrorResponse | null = null;\n const maxRetries = 3;\n const baseDelay = 1000;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await this.executeRequest(url, batch);\n\n if (response.success) {\n if (this.debug) {\n this.logger.debug(`Sent ${events.length} events successfully`);\n }\n return response;\n }\n\n // Handle specific error codes\n const errorResponse = response as TransportErrorResponse;\n lastError = errorResponse;\n\n switch (errorResponse.code) {\n case 401:\n // Invalid API key - disable SDK\n this.state = 'disabled';\n this.logger.error('Invalid API key. SDK disabled.');\n return errorResponse;\n\n case 402:\n // Quota exceeded - stop sending\n this.state = 'quota_exceeded';\n this.logger.warn('Event quota exceeded. SDK paused.');\n return errorResponse;\n\n case 429:\n // Rate limited - retry after delay\n const retryAfter = errorResponse.retry_after ?? 1;\n if (attempt < maxRetries) {\n await this.sleep(retryAfter * 1000);\n continue;\n }\n return errorResponse;\n\n case 400:\n // Bad request - don't retry\n this.logger.warn('Bad request:', errorResponse.message);\n return errorResponse;\n\n default:\n // 5xx errors - retry with backoff\n if (errorResponse.code >= 500 && attempt < maxRetries) {\n const delay = this.calculateBackoff(attempt, baseDelay);\n if (this.debug) {\n this.logger.debug(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms`);\n }\n await this.sleep(delay);\n continue;\n }\n return errorResponse;\n }\n } catch (error) {\n // Network error - retry with backoff\n if (attempt < maxRetries) {\n const delay = this.calculateBackoff(attempt, baseDelay);\n if (this.debug) {\n this.logger.debug(`Network error, retry ${attempt + 1}/${maxRetries} after ${delay}ms`);\n }\n await this.sleep(delay);\n continue;\n }\n\n const networkError: TransportErrorResponse = {\n success: false,\n error: 'Network Error',\n message: error instanceof Error ? error.message : 'Unknown error',\n code: 0,\n };\n return networkError;\n }\n }\n\n return lastError ?? {\n success: false,\n error: 'Max retries exceeded',\n message: 'Failed after maximum retry attempts',\n code: 0,\n };\n }\n\n /**\n * Execute HTTP request\n */\n private async executeRequest(url: string, batch: EventBatch): Promise<TransportResponse> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(batch),\n signal: controller.signal,\n });\n\n const data = await response.json() as TransportResponse;\n return data;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Calculate backoff delay with jitter\n */\n private calculateBackoff(attempt: number, baseDelay: number): number {\n const exponentialDelay = baseDelay * Math.pow(2, attempt);\n const jitter = exponentialDelay * 0.1 * (Math.random() * 2 - 1); // ±10% jitter\n return Math.round(exponentialDelay + jitter);\n }\n\n /**\n * Sleep for specified milliseconds\n */\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n}\n","import type { Event, Tags, Extra, CaptureOptions, EventLevel } from './types.js';\nimport { ConfigError } from './config.js';\n\n/**\n * Queue flush callback\n */\nexport type FlushCallback = (events: Event[]) => Promise<void>;\n\n/**\n * Queue options\n */\nexport interface QueueOptions {\n /** Max events per batch before auto-flush */\n batchSize: number;\n /** Flush interval in milliseconds */\n flushInterval: number;\n /** Max events in queue */\n maxQueueSize: number;\n /** Debug logging */\n debug: boolean;\n}\n\n/**\n * Event queue with batching support\n * \n * Accumulates events and flushes them either when:\n * - Batch size is reached (default: 100)\n * - Flush interval elapsed (default: 5 seconds)\n * - Manual flush() is called\n */\nexport class EventQueue {\n private readonly queue: Event[] = [];\n private readonly options: QueueOptions;\n private readonly onFlush: FlushCallback;\n\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private isFlushing = false;\n\n constructor(options: QueueOptions, onFlush: FlushCallback) {\n this.options = options;\n this.onFlush = onFlush;\n }\n\n /**\n * Start the background flush timer\n */\n start(): void {\n if (this.flushTimer) return;\n\n this.flushTimer = setInterval(() => {\n this.flush().catch(() => {\n // Errors handled in flush()\n });\n }, this.options.flushInterval);\n\n // Don't block Node.js from exiting\n if (this.flushTimer.unref) {\n this.flushTimer.unref();\n }\n }\n\n /**\n * Stop the background flush timer\n */\n stop(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n }\n\n /**\n * Add an event to the queue\n * Returns immediately (non-blocking)\n */\n add(event: Event): void {\n // Check if queue is full\n if (this.queue.length >= this.options.maxQueueSize) {\n // Drop oldest event\n this.queue.shift();\n if (this.options.debug) {\n console.warn('[tracebeam] Queue full, dropping oldest event');\n }\n }\n\n this.queue.push(event);\n\n // Auto-flush if batch size reached\n if (this.queue.length >= this.options.batchSize) {\n this.flush().catch(() => {\n // Errors handled in flush()\n });\n }\n }\n\n /**\n * Flush all queued events\n */\n async flush(): Promise<void> {\n // Prevent concurrent flushes\n if (this.isFlushing || this.queue.length === 0) {\n return;\n }\n\n this.isFlushing = true;\n\n try {\n // Extract events to flush (leave queue empty for new events)\n const events = this.queue.splice(0, this.queue.length);\n await this.onFlush(events);\n } finally {\n this.isFlushing = false;\n }\n }\n\n /**\n * Get current queue size\n */\n size(): number {\n return this.queue.length;\n }\n\n /**\n * Check if queue is empty\n */\n isEmpty(): boolean {\n return this.queue.length === 0;\n }\n}\n\n/**\n * Event builder utility\n */\nexport function createEvent(\n projectId: string,\n environment: string,\n error: string,\n options: CaptureOptions = {},\n globalTags: Tags = {},\n globalExtra: Extra = {}\n): Event {\n // Validate required parameters\n if (!projectId || projectId.trim() === '') {\n throw new ConfigError('createEvent: \"projectId\" must be a non-empty string.');\n }\n if (!environment || environment.trim() === '') {\n throw new ConfigError('createEvent: \"environment\" must be a non-empty string.');\n }\n\n const level: EventLevel = options.level ?? 'error';\n const timestamp = options.timestamp ?? new Date().toISOString();\n\n // Merge tags (options override globals)\n const tags: Tags = {\n ...globalTags,\n ...options.tags,\n };\n\n // Merge extra (options override globals)\n const extra: Extra = {\n ...globalExtra,\n ...options.extra,\n };\n\n // Truncate error message to 10,000 chars\n const truncatedError = error.length > 10000 ? error.slice(0, 10000) : error;\n\n return {\n project_id: projectId,\n error: truncatedError,\n level,\n environment,\n timestamp,\n tags,\n extra,\n };\n}\n","import type { TraceBeamConfig, CaptureOptions, Tags, Extra, Event } from './types.js';\nimport { loadConfigFromEnv, mergeConfig, validateConfig } from './config.js';\nimport { Transport } from './transport.js';\nimport { EventQueue, createEvent } from './queue.js';\n\n/**\n * TraceBeamSDK client for capturing and sending events\n * \n * @example\n * ```typescript\n * // Initialize from environment variables\n * const sdk = TraceBeamSDK.fromEnv();\n * \n * // Capture an exception\n * try {\n * doSomething();\n * } catch (error) {\n * sdk.captureException(error);\n * }\n * \n * // Capture a message\n * sdk.captureMessage('User signup completed', {\n * level: 'info',\n * tags: { feature: 'auth' }\n * });\n * \n * // Graceful shutdown\n * await sdk.close();\n * ```\n */\nexport class TraceBeamSDK {\n private readonly config: Required<TraceBeamConfig>;\n private readonly transport: Transport;\n private readonly queue: EventQueue;\n\n private globalTags: Tags = {};\n private globalExtra: Extra = {};\n private userId: string | null = null;\n\n private isInitialized = true;\n\n /**\n * Create SDK instance from configuration\n */\n constructor(config: TraceBeamConfig) {\n validateConfig(config);\n this.config = mergeConfig(config);\n\n this.transport = new Transport({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n timeout: this.config.timeout,\n debug: this.config.debug,\n });\n\n this.queue = new EventQueue(\n {\n batchSize: this.config.batchSize,\n flushInterval: this.config.flushInterval,\n maxQueueSize: this.config.maxQueueSize,\n debug: this.config.debug,\n },\n this.handleFlush.bind(this)\n );\n\n // Start background flush timer\n this.queue.start();\n\n if (this.config.debug) {\n console.debug('[tracebeam] Initialized', {\n projectId: this.config.projectId,\n environment: this.config.environment,\n endpoint: this.config.endpoint,\n });\n }\n }\n\n /**\n * Create SDK instance from environment variables\n * \n * Required env vars:\n * - TRACEBEAM_API_KEY\n * - TRACEBEAM_PROJECT_ID\n * \n * Optional env vars:\n * - TRACEBEAM_ENVIRONMENT (default: 'development')\n * - TRACEBEAM_ENDPOINT (default: 'https://ingest.tracebeam.io')\n * - TRACEBEAM_BATCH_SIZE (default: 100)\n * - TRACEBEAM_FLUSH_INTERVAL (default: 5000)\n * - TRACEBEAM_HTTP_TIMEOUT (default: 10000)\n * - TRACEBEAM_MAX_QUEUE_SIZE (default: 10000)\n * - TRACEBEAM_SAMPLE_RATE (default: 1.0)\n * - TRACEBEAM_DEBUG (default: false)\n */\n static fromEnv(): TraceBeamSDK {\n const config = loadConfigFromEnv();\n return new TraceBeamSDK(config);\n }\n\n /**\n * Capture an exception/error\n * Returns immediately (non-blocking)\n */\n captureException(error: Error | string, options: CaptureOptions = {}): void {\n if (!this.canCapture()) return;\n\n const errorMessage = error instanceof Error\n ? `${error.name}: ${error.message}\\n${error.stack ?? ''}`\n : String(error);\n\n const event = this.createEventWithContext(errorMessage, {\n level: 'error',\n ...options,\n });\n\n this.queue.add(event);\n }\n\n /**\n * Capture a message/event\n * Returns immediately (non-blocking)\n */\n captureMessage(message: string, options: CaptureOptions = {}): void {\n if (!this.canCapture()) return;\n\n const event = this.createEventWithContext(message, {\n level: 'info',\n ...options,\n });\n\n this.queue.add(event);\n }\n\n /**\n * Set the current user ID\n * Will be included in all subsequent events\n */\n setUser(userId: string | null): void {\n this.userId = userId;\n if (userId) {\n this.globalExtra['user_id'] = userId;\n } else {\n delete this.globalExtra['user_id'];\n }\n }\n\n /**\n * Set a global tag\n * Will be included in all subsequent events\n */\n setTag(key: string, value: string): void {\n this.globalTags[key] = value;\n }\n\n /**\n * Set multiple global tags\n * Will be included in all subsequent events\n */\n setTags(tags: Tags): void {\n Object.assign(this.globalTags, tags);\n }\n\n /**\n * Set global extra context\n * Will be included in all subsequent events\n */\n setExtra(key: string, value: unknown): void {\n this.globalExtra[key] = value;\n }\n\n /**\n * Set multiple global extra context values\n */\n setExtras(extras: Extra): void {\n Object.assign(this.globalExtra, extras);\n }\n\n /**\n * Manually flush queued events\n * Call this to ensure events are sent before process exit\n */\n async flush(): Promise<void> {\n await this.queue.flush();\n }\n\n /**\n * Graceful shutdown\n * Flushes remaining events and stops background workers\n */\n async close(): Promise<void> {\n this.isInitialized = false;\n this.queue.stop();\n await this.flush();\n\n if (this.config.debug) {\n console.debug('[tracebeam] Closed');\n }\n }\n\n /**\n * Check if SDK is active and can capture events\n */\n isActive(): boolean {\n return this.isInitialized && this.transport.isActive();\n }\n\n // Private methods\n\n private canCapture(): boolean {\n if (!this.isInitialized) return false;\n if (!this.transport.isActive()) return false;\n\n // Sample rate check\n if (this.config.sampleRate < 1.0) {\n if (Math.random() > this.config.sampleRate) {\n return false;\n }\n }\n\n return true;\n }\n\n private createEventWithContext(error: string, options: CaptureOptions): Event {\n return createEvent(\n this.config.projectId,\n this.config.environment,\n error,\n options,\n this.globalTags,\n this.globalExtra\n );\n }\n\n private async handleFlush(events: Event[]): Promise<void> {\n if (events.length === 0) return;\n\n try {\n await this.transport.send(events);\n } catch (error) {\n if (this.config.debug) {\n console.error('[tracebeam] Failed to flush events:', error);\n }\n // Events are lost, but we don't crash the app\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { a as TraceBeamSDK, c as Tags, d as Extra } from '../client-CaHega3m.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Express error handler types
|
|
5
|
+
*/
|
|
6
|
+
interface ExpressRequest {
|
|
7
|
+
method?: string;
|
|
8
|
+
url?: string;
|
|
9
|
+
path?: string;
|
|
10
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
11
|
+
ip?: string;
|
|
12
|
+
params?: Record<string, string>;
|
|
13
|
+
query?: Record<string, string>;
|
|
14
|
+
body?: unknown;
|
|
15
|
+
}
|
|
16
|
+
interface ExpressResponse {
|
|
17
|
+
statusCode?: number;
|
|
18
|
+
headersSent?: boolean;
|
|
19
|
+
}
|
|
20
|
+
type ExpressNextFunction = (err?: unknown) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Express error handler middleware
|
|
23
|
+
*
|
|
24
|
+
* Captures unhandled errors and passes them through to the next error handler.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* import express from 'express';
|
|
29
|
+
* import { TraceBeamSDK } from 'tracebeam-sdk';
|
|
30
|
+
* import { expressErrorHandler } from 'tracebeam-sdk/integrations/express';
|
|
31
|
+
*
|
|
32
|
+
* const app = express();
|
|
33
|
+
* const sdk = TraceBeamSDK.fromEnv();
|
|
34
|
+
*
|
|
35
|
+
* // Your routes here...
|
|
36
|
+
*
|
|
37
|
+
* // Add error handler AFTER all routes
|
|
38
|
+
* app.use(expressErrorHandler(sdk));
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
declare function expressErrorHandler(sdk: TraceBeamSDK, options?: ExpressErrorHandlerOptions): (err: Error | unknown, req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => void;
|
|
42
|
+
/**
|
|
43
|
+
* Options for Express error handler
|
|
44
|
+
*/
|
|
45
|
+
interface ExpressErrorHandlerOptions {
|
|
46
|
+
/** Additional tags to include with every error */
|
|
47
|
+
tags?: Tags;
|
|
48
|
+
/** Additional context to include with every error */
|
|
49
|
+
extra?: Extra;
|
|
50
|
+
/** Extract user ID from request */
|
|
51
|
+
getUserId?: (req: ExpressRequest) => string | undefined;
|
|
52
|
+
/** Filter which errors to capture */
|
|
53
|
+
shouldCapture?: (err: Error, req: ExpressRequest) => boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Express request handler wrapper
|
|
57
|
+
*
|
|
58
|
+
* Wraps an async route handler to automatically capture errors
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* app.get('/users/:id', wrapHandler(sdk, async (req, res) => {
|
|
63
|
+
* const user = await getUser(req.params.id);
|
|
64
|
+
* res.json(user);
|
|
65
|
+
* }));
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
declare function wrapHandler<Req extends ExpressRequest, Res>(sdk: TraceBeamSDK, handler: (req: Req, res: Res, next: ExpressNextFunction) => Promise<unknown> | unknown): (req: Req, res: Res, next: ExpressNextFunction) => Promise<void>;
|
|
69
|
+
|
|
70
|
+
export { type ExpressErrorHandlerOptions, expressErrorHandler, wrapHandler };
|