@fullevent/node 0.0.1
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/dist/index.d.mts +1096 -0
- package/dist/index.d.ts +1096 -0
- package/dist/index.js +573 -0
- package/dist/index.mjs +543 -0
- package/package.json +23 -0
- package/scripts/generate-docs.ts +917 -0
- package/src/builder.ts +307 -0
- package/src/client.ts +325 -0
- package/src/index.ts +272 -0
- package/src/middleware/express.ts +193 -0
- package/src/middleware/hono.ts +395 -0
- package/tsconfig.json +11 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
FullEvent: () => FullEvent,
|
|
24
|
+
WideEventBuilder: () => WideEventBuilder,
|
|
25
|
+
expressWideLogger: () => expressWideLogger,
|
|
26
|
+
wideLogger: () => wideLogger
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// src/client.ts
|
|
31
|
+
var FullEvent = class {
|
|
32
|
+
/**
|
|
33
|
+
* Creates a new FullEvent client instance.
|
|
34
|
+
*
|
|
35
|
+
* @param config - Configuration options
|
|
36
|
+
*/
|
|
37
|
+
constructor(config) {
|
|
38
|
+
this.apiKey = config.apiKey;
|
|
39
|
+
this.baseUrl = config.baseUrl || "https://api.fullevent.io";
|
|
40
|
+
if (config.ping) {
|
|
41
|
+
this.ping().catch((err) => {
|
|
42
|
+
console.error("[FullEvent SDK] Auto-ping failed:", err);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Ingest a generic event with any properties.
|
|
48
|
+
*
|
|
49
|
+
* @param event - Event name/type (e.g., 'user.signup', 'order.completed')
|
|
50
|
+
* @param properties - Key-value pairs of event data
|
|
51
|
+
* @param timestamp - Optional ISO timestamp (defaults to now)
|
|
52
|
+
* @returns Promise resolving to success/error status
|
|
53
|
+
*
|
|
54
|
+
* @remarks
|
|
55
|
+
* Events are processed asynchronously. The promise resolves when
|
|
56
|
+
* the event is accepted by the API, not when it's fully processed.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* await client.ingest('checkout.completed', {
|
|
61
|
+
* order_id: 'ord_123',
|
|
62
|
+
* total_cents: 9999,
|
|
63
|
+
* items: 3,
|
|
64
|
+
* user: {
|
|
65
|
+
* id: 'usr_456',
|
|
66
|
+
* plan: 'premium',
|
|
67
|
+
* },
|
|
68
|
+
* });
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
async ingest(event, properties = {}, timestamp) {
|
|
72
|
+
try {
|
|
73
|
+
const response = await fetch(`${this.baseUrl}/ingest`, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
headers: {
|
|
76
|
+
"Content-Type": "application/json",
|
|
77
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
78
|
+
},
|
|
79
|
+
body: JSON.stringify({
|
|
80
|
+
event,
|
|
81
|
+
properties,
|
|
82
|
+
timestamp: timestamp || (/* @__PURE__ */ new Date()).toISOString()
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
const error = await response.json();
|
|
87
|
+
console.error("[FullEvent SDK] Ingestion failed:", error);
|
|
88
|
+
return { success: false, error };
|
|
89
|
+
}
|
|
90
|
+
return { success: true };
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error("[FullEvent SDK] Network error during ingestion:", error);
|
|
93
|
+
return { success: false, error };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Ingest an HTTP request event with typed properties.
|
|
98
|
+
*
|
|
99
|
+
* @param properties - HTTP request properties with optional custom data
|
|
100
|
+
* @param timestamp - Optional ISO timestamp (defaults to now)
|
|
101
|
+
* @returns Promise resolving to success/error status
|
|
102
|
+
*
|
|
103
|
+
* @remarks
|
|
104
|
+
* This is a convenience method that provides TypeScript autocomplete
|
|
105
|
+
* for standard HTTP properties. Under the hood, it calls `ingest()`
|
|
106
|
+
* with the event type `'http_request'`.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* await client.ingestHttpRequest({
|
|
111
|
+
* status_code: 200,
|
|
112
|
+
* method: 'POST',
|
|
113
|
+
* path: '/api/checkout',
|
|
114
|
+
* duration_ms: 234,
|
|
115
|
+
* outcome: 'success',
|
|
116
|
+
* // Custom properties
|
|
117
|
+
* user_id: 'usr_123',
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
async ingestHttpRequest(properties, timestamp) {
|
|
122
|
+
return this.ingest("http_request", properties, timestamp);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Ping the FullEvent API to verify connection.
|
|
126
|
+
*
|
|
127
|
+
* @returns Promise resolving to connection status with latency
|
|
128
|
+
*
|
|
129
|
+
* @remarks
|
|
130
|
+
* Use this method to verify your SDK is correctly configured.
|
|
131
|
+
* It sends a lightweight ping event and measures round-trip latency.
|
|
132
|
+
* Commonly used during setup or in health check endpoints.
|
|
133
|
+
*
|
|
134
|
+
* @example Basic ping
|
|
135
|
+
* ```typescript
|
|
136
|
+
* const result = await client.ping();
|
|
137
|
+
* if (result.success) {
|
|
138
|
+
* console.log(`Connected! Latency: ${result.latency_ms}ms`);
|
|
139
|
+
* } else {
|
|
140
|
+
* console.error('Connection failed:', result.error);
|
|
141
|
+
* }
|
|
142
|
+
* ```
|
|
143
|
+
*
|
|
144
|
+
* @example Health check endpoint
|
|
145
|
+
* ```typescript
|
|
146
|
+
* app.get('/health', async (c) => {
|
|
147
|
+
* const ping = await fullevent.ping();
|
|
148
|
+
* return c.json({
|
|
149
|
+
* status: ping.success ? 'healthy' : 'degraded',
|
|
150
|
+
* fullevent: ping,
|
|
151
|
+
* });
|
|
152
|
+
* });
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
async ping() {
|
|
156
|
+
const start = Date.now();
|
|
157
|
+
try {
|
|
158
|
+
const result = await this.ingest("fullevent.ping", {
|
|
159
|
+
// Standard wide event properties
|
|
160
|
+
status_code: 200,
|
|
161
|
+
outcome: "success",
|
|
162
|
+
duration_ms: 0,
|
|
163
|
+
// Will be updated after
|
|
164
|
+
// SDK info
|
|
165
|
+
sdk: "@fullevent/node-sdk",
|
|
166
|
+
sdk_version: "1.0.0",
|
|
167
|
+
// Runtime info
|
|
168
|
+
runtime: typeof process !== "undefined" ? "node" : "browser",
|
|
169
|
+
node_version: typeof process !== "undefined" ? process.version : void 0,
|
|
170
|
+
platform: typeof process !== "undefined" ? process.platform : "browser",
|
|
171
|
+
// Ping metadata
|
|
172
|
+
ping_type: "connection_test",
|
|
173
|
+
message: "\u{1F389} Your first event! FullEvent is connected and ready."
|
|
174
|
+
});
|
|
175
|
+
const latency_ms = Date.now() - start;
|
|
176
|
+
if (result.success) {
|
|
177
|
+
return { success: true, latency_ms };
|
|
178
|
+
} else {
|
|
179
|
+
return { success: false, latency_ms, error: result.error };
|
|
180
|
+
}
|
|
181
|
+
} catch (error) {
|
|
182
|
+
const latency_ms = Date.now() - start;
|
|
183
|
+
return { success: false, latency_ms, error };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// src/middleware/hono.ts
|
|
189
|
+
function shouldSample(event, config) {
|
|
190
|
+
const sampling = config ?? {};
|
|
191
|
+
const defaultRate = sampling.defaultRate ?? 1;
|
|
192
|
+
const alwaysKeepErrors = sampling.alwaysKeepErrors ?? true;
|
|
193
|
+
const slowThreshold = sampling.slowRequestThresholdMs ?? 2e3;
|
|
194
|
+
if (alwaysKeepErrors) {
|
|
195
|
+
if (event.outcome === "error") return true;
|
|
196
|
+
if (event.status_code && event.status_code >= 400) return true;
|
|
197
|
+
}
|
|
198
|
+
if (event.duration_ms && event.duration_ms > slowThreshold) return true;
|
|
199
|
+
if (sampling.alwaysKeepPaths?.some((p) => event.path.includes(p))) return true;
|
|
200
|
+
if (event.user_id && sampling.alwaysKeepUsers?.includes(String(event.user_id))) return true;
|
|
201
|
+
if (event.trace_id) {
|
|
202
|
+
let hash = 5381;
|
|
203
|
+
const str = event.trace_id;
|
|
204
|
+
for (let i = 0; i < str.length; i++) {
|
|
205
|
+
hash = (hash << 5) + hash + str.charCodeAt(i);
|
|
206
|
+
}
|
|
207
|
+
const normalized = (hash >>> 0) % 1e4 / 1e4;
|
|
208
|
+
return normalized < defaultRate;
|
|
209
|
+
}
|
|
210
|
+
return Math.random() < defaultRate;
|
|
211
|
+
}
|
|
212
|
+
var wideLogger = (config) => {
|
|
213
|
+
const client = new FullEvent({
|
|
214
|
+
apiKey: config.apiKey,
|
|
215
|
+
baseUrl: config.baseUrl
|
|
216
|
+
});
|
|
217
|
+
return async (c, next) => {
|
|
218
|
+
const startTime = Date.now();
|
|
219
|
+
const requestId = c.req.header("x-fullevent-trace-id") || c.req.header("x-request-id") || crypto.randomUUID();
|
|
220
|
+
const event = {
|
|
221
|
+
request_id: requestId,
|
|
222
|
+
trace_id: requestId,
|
|
223
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
224
|
+
method: c.req.method,
|
|
225
|
+
path: c.req.path,
|
|
226
|
+
service: config.service,
|
|
227
|
+
environment: config.environment || process.env.NODE_ENV || "development",
|
|
228
|
+
region: config.region
|
|
229
|
+
};
|
|
230
|
+
c.set("wideEvent", event);
|
|
231
|
+
c.res.headers.set("x-fullevent-trace-id", requestId);
|
|
232
|
+
try {
|
|
233
|
+
await next();
|
|
234
|
+
event.status_code = c.res.status;
|
|
235
|
+
event.outcome = c.res.status >= 400 ? "error" : "success";
|
|
236
|
+
} catch (err) {
|
|
237
|
+
event.status_code = 500;
|
|
238
|
+
event.outcome = "error";
|
|
239
|
+
if (err instanceof Error) {
|
|
240
|
+
event.error = {
|
|
241
|
+
type: err.name || "Error",
|
|
242
|
+
message: err.message || String(err),
|
|
243
|
+
stack: err.stack
|
|
244
|
+
};
|
|
245
|
+
} else {
|
|
246
|
+
event.error = {
|
|
247
|
+
type: "Error",
|
|
248
|
+
message: String(err)
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
throw err;
|
|
252
|
+
} finally {
|
|
253
|
+
event.duration_ms = Date.now() - startTime;
|
|
254
|
+
if (config.apiKey && shouldSample(event, config.sampling)) {
|
|
255
|
+
client.ingest(
|
|
256
|
+
`${event.method} ${event.path}`,
|
|
257
|
+
event,
|
|
258
|
+
event.timestamp
|
|
259
|
+
).catch((err) => {
|
|
260
|
+
console.error("[FullEvent] Failed to send event:", err);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// src/middleware/express.ts
|
|
268
|
+
function shouldSample2(event, config) {
|
|
269
|
+
const sampling = config ?? {};
|
|
270
|
+
const defaultRate = sampling.defaultRate ?? 1;
|
|
271
|
+
const alwaysKeepErrors = sampling.alwaysKeepErrors ?? true;
|
|
272
|
+
const slowThreshold = sampling.slowRequestThresholdMs ?? 2e3;
|
|
273
|
+
if (alwaysKeepErrors) {
|
|
274
|
+
if (event.outcome === "error") return true;
|
|
275
|
+
if (event.status_code && event.status_code >= 400) return true;
|
|
276
|
+
}
|
|
277
|
+
if (event.duration_ms && event.duration_ms > slowThreshold) return true;
|
|
278
|
+
if (sampling.alwaysKeepPaths?.some((p) => event.path.includes(p))) return true;
|
|
279
|
+
if (event.user_id && sampling.alwaysKeepUsers?.includes(String(event.user_id))) return true;
|
|
280
|
+
if (event.trace_id) {
|
|
281
|
+
let hash = 5381;
|
|
282
|
+
const str = event.trace_id;
|
|
283
|
+
for (let i = 0; i < str.length; i++) {
|
|
284
|
+
hash = (hash << 5) + hash + str.charCodeAt(i);
|
|
285
|
+
}
|
|
286
|
+
const normalized = (hash >>> 0) % 1e4 / 1e4;
|
|
287
|
+
return normalized < defaultRate;
|
|
288
|
+
}
|
|
289
|
+
return Math.random() < defaultRate;
|
|
290
|
+
}
|
|
291
|
+
function expressWideLogger(config) {
|
|
292
|
+
const client = new FullEvent({
|
|
293
|
+
apiKey: config.apiKey,
|
|
294
|
+
baseUrl: config.baseUrl
|
|
295
|
+
});
|
|
296
|
+
return (req, res, next) => {
|
|
297
|
+
const startTime = Date.now();
|
|
298
|
+
const requestId = req.headers["x-fullevent-trace-id"] || req.headers["x-request-id"] || crypto.randomUUID();
|
|
299
|
+
const event = {
|
|
300
|
+
request_id: requestId,
|
|
301
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
302
|
+
method: req.method,
|
|
303
|
+
path: req.path,
|
|
304
|
+
service: config.service,
|
|
305
|
+
environment: config.environment || process.env.NODE_ENV || "development",
|
|
306
|
+
region: config.region
|
|
307
|
+
};
|
|
308
|
+
req.wideEvent = event;
|
|
309
|
+
res.setHeader("x-fullevent-trace-id", requestId);
|
|
310
|
+
res.on("finish", () => {
|
|
311
|
+
event.status_code = res.statusCode;
|
|
312
|
+
event.outcome = res.statusCode >= 400 ? "error" : "success";
|
|
313
|
+
event.duration_ms = Date.now() - startTime;
|
|
314
|
+
if (config.apiKey && shouldSample2(event, config.sampling)) {
|
|
315
|
+
client.ingest(
|
|
316
|
+
`${event.method} ${event.path}`,
|
|
317
|
+
event,
|
|
318
|
+
event.timestamp
|
|
319
|
+
).catch((err) => {
|
|
320
|
+
console.error("[FullEvent] Failed to send event:", err);
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
next();
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/builder.ts
|
|
329
|
+
var WideEventBuilder = class {
|
|
330
|
+
/**
|
|
331
|
+
* Creates a new builder wrapping the given event.
|
|
332
|
+
*
|
|
333
|
+
* @param event - The wide event to enrich
|
|
334
|
+
*/
|
|
335
|
+
constructor(event) {
|
|
336
|
+
this.event = event;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Returns the underlying event object for direct access.
|
|
340
|
+
*
|
|
341
|
+
* @returns The wrapped WideEvent
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* ```typescript
|
|
345
|
+
* const event = builder.getEvent();
|
|
346
|
+
* console.log(event.user_id);
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
349
|
+
getEvent() {
|
|
350
|
+
return this.event;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Sets any key-value pair on the event.
|
|
354
|
+
*
|
|
355
|
+
* @param key - Property name
|
|
356
|
+
* @param value - Property value (any type)
|
|
357
|
+
* @returns `this` for chaining
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* ```typescript
|
|
361
|
+
* builder
|
|
362
|
+
* .set('order_id', 'ord_123')
|
|
363
|
+
* .set('llm_model', 'gpt-4')
|
|
364
|
+
* .set('tokens_used', 1500);
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
367
|
+
set(key, value) {
|
|
368
|
+
this.event[key] = value;
|
|
369
|
+
return this;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Sets a named context object on the event.
|
|
373
|
+
*
|
|
374
|
+
* @remarks
|
|
375
|
+
* This is the primary method for adding structured business context.
|
|
376
|
+
* Each context is a nested object that groups related properties.
|
|
377
|
+
*
|
|
378
|
+
* @param name - Context name (e.g., 'user', 'cart', 'payment')
|
|
379
|
+
* @param data - Context data as key-value pairs
|
|
380
|
+
* @returns `this` for chaining
|
|
381
|
+
*
|
|
382
|
+
* @example
|
|
383
|
+
* ```typescript
|
|
384
|
+
* builder
|
|
385
|
+
* .setContext('user', {
|
|
386
|
+
* id: 'user_456',
|
|
387
|
+
* subscription: 'premium',
|
|
388
|
+
* account_age_days: 847,
|
|
389
|
+
* })
|
|
390
|
+
* .setContext('cart', {
|
|
391
|
+
* id: 'cart_xyz',
|
|
392
|
+
* item_count: 3,
|
|
393
|
+
* total_cents: 15999,
|
|
394
|
+
* })
|
|
395
|
+
* .setContext('payment', {
|
|
396
|
+
* method: 'card',
|
|
397
|
+
* provider: 'stripe',
|
|
398
|
+
* });
|
|
399
|
+
* ```
|
|
400
|
+
*/
|
|
401
|
+
setContext(name, data) {
|
|
402
|
+
this.event[name] = data;
|
|
403
|
+
return this;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Merges additional fields into an existing context object.
|
|
407
|
+
*
|
|
408
|
+
* @remarks
|
|
409
|
+
* Useful for progressively building context throughout the request.
|
|
410
|
+
* If the context doesn't exist, it will be created.
|
|
411
|
+
*
|
|
412
|
+
* @param name - Context name to merge into
|
|
413
|
+
* @param data - Additional data to merge
|
|
414
|
+
* @returns `this` for chaining
|
|
415
|
+
*
|
|
416
|
+
* @example
|
|
417
|
+
* ```typescript
|
|
418
|
+
* // Initial payment context
|
|
419
|
+
* builder.setContext('payment', { method: 'card', provider: 'stripe' });
|
|
420
|
+
*
|
|
421
|
+
* // After payment completes, add timing
|
|
422
|
+
* builder.mergeContext('payment', {
|
|
423
|
+
* latency_ms: Date.now() - paymentStart,
|
|
424
|
+
* attempt: 1,
|
|
425
|
+
* transaction_id: 'txn_123',
|
|
426
|
+
* });
|
|
427
|
+
*
|
|
428
|
+
* // Result: { method, provider, latency_ms, attempt, transaction_id }
|
|
429
|
+
* ```
|
|
430
|
+
*/
|
|
431
|
+
mergeContext(name, data) {
|
|
432
|
+
const existing = this.event[name];
|
|
433
|
+
if (existing && typeof existing === "object" && !Array.isArray(existing)) {
|
|
434
|
+
this.event[name] = { ...existing, ...data };
|
|
435
|
+
} else {
|
|
436
|
+
this.event[name] = data;
|
|
437
|
+
}
|
|
438
|
+
return this;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Sets the user ID on the event.
|
|
442
|
+
*
|
|
443
|
+
* @remarks
|
|
444
|
+
* This sets the top-level `user_id` field, which is commonly used
|
|
445
|
+
* for filtering and user-centric analytics. For richer user context,
|
|
446
|
+
* use `setContext('user', {...})` instead or in addition.
|
|
447
|
+
*
|
|
448
|
+
* @param userId - The user identifier
|
|
449
|
+
* @returns `this` for chaining
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* ```typescript
|
|
453
|
+
* builder.setUser('usr_123');
|
|
454
|
+
*
|
|
455
|
+
* // For richer context, also set a user object:
|
|
456
|
+
* builder.setContext('user', {
|
|
457
|
+
* id: 'usr_123',
|
|
458
|
+
* plan: 'premium',
|
|
459
|
+
* ltv_cents: 50000,
|
|
460
|
+
* });
|
|
461
|
+
* ```
|
|
462
|
+
*/
|
|
463
|
+
setUser(userId) {
|
|
464
|
+
this.event.user_id = userId;
|
|
465
|
+
return this;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Captures an error with structured details.
|
|
469
|
+
*
|
|
470
|
+
* @remarks
|
|
471
|
+
* Automatically sets `outcome` to 'error'. Accepts either a native
|
|
472
|
+
* Error object or a custom error object with additional fields.
|
|
473
|
+
*
|
|
474
|
+
* @param err - Error object or custom error details
|
|
475
|
+
* @returns `this` for chaining
|
|
476
|
+
*
|
|
477
|
+
* @example Native Error
|
|
478
|
+
* ```typescript
|
|
479
|
+
* try {
|
|
480
|
+
* await riskyOperation();
|
|
481
|
+
* } catch (err) {
|
|
482
|
+
* builder.setError(err);
|
|
483
|
+
* }
|
|
484
|
+
* ```
|
|
485
|
+
*
|
|
486
|
+
* @example Custom Error with Context
|
|
487
|
+
* ```typescript
|
|
488
|
+
* builder.setError({
|
|
489
|
+
* type: 'PaymentError',
|
|
490
|
+
* message: 'Card declined by issuer',
|
|
491
|
+
* code: 'card_declined',
|
|
492
|
+
* stripe_decline_code: 'insufficient_funds',
|
|
493
|
+
* card_brand: 'visa',
|
|
494
|
+
* card_last4: '4242',
|
|
495
|
+
* });
|
|
496
|
+
* ```
|
|
497
|
+
*/
|
|
498
|
+
setError(err) {
|
|
499
|
+
this.event.outcome = "error";
|
|
500
|
+
if (err instanceof Error) {
|
|
501
|
+
this.event.error = {
|
|
502
|
+
type: err.name,
|
|
503
|
+
message: err.message,
|
|
504
|
+
stack: err.stack
|
|
505
|
+
};
|
|
506
|
+
} else {
|
|
507
|
+
const { type, message, code, stack, ...extra } = err;
|
|
508
|
+
this.event.error = {
|
|
509
|
+
type: type || "Error",
|
|
510
|
+
message,
|
|
511
|
+
code,
|
|
512
|
+
stack,
|
|
513
|
+
...extra
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
return this;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Sets the HTTP status code and outcome.
|
|
520
|
+
*
|
|
521
|
+
* @remarks
|
|
522
|
+
* Automatically sets `outcome` based on the status code:
|
|
523
|
+
* - `< 400` → 'success'
|
|
524
|
+
* - `>= 400` → 'error'
|
|
525
|
+
*
|
|
526
|
+
* @param code - HTTP status code
|
|
527
|
+
* @returns `this` for chaining
|
|
528
|
+
*
|
|
529
|
+
* @example
|
|
530
|
+
* ```typescript
|
|
531
|
+
* builder.setStatus(404); // outcome = 'error'
|
|
532
|
+
* builder.setStatus(200); // outcome = 'success'
|
|
533
|
+
* ```
|
|
534
|
+
*/
|
|
535
|
+
setStatus(code) {
|
|
536
|
+
this.event.status_code = code;
|
|
537
|
+
this.event.outcome = code >= 400 ? "error" : "success";
|
|
538
|
+
return this;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Records timing for a specific operation.
|
|
542
|
+
*
|
|
543
|
+
* @remarks
|
|
544
|
+
* A convenience method for calculating and setting duration values.
|
|
545
|
+
* The value is calculated as `Date.now() - startTime`.
|
|
546
|
+
*
|
|
547
|
+
* @param key - Property name for the timing (e.g., 'db_latency_ms')
|
|
548
|
+
* @param startTime - Start timestamp from `Date.now()`
|
|
549
|
+
* @returns `this` for chaining
|
|
550
|
+
*
|
|
551
|
+
* @example
|
|
552
|
+
* ```typescript
|
|
553
|
+
* const dbStart = Date.now();
|
|
554
|
+
* const result = await db.query('SELECT ...');
|
|
555
|
+
* builder.setTiming('db_latency_ms', dbStart);
|
|
556
|
+
*
|
|
557
|
+
* const paymentStart = Date.now();
|
|
558
|
+
* await processPayment();
|
|
559
|
+
* builder.setTiming('payment_latency_ms', paymentStart);
|
|
560
|
+
* ```
|
|
561
|
+
*/
|
|
562
|
+
setTiming(key, startTime) {
|
|
563
|
+
this.event[key] = Date.now() - startTime;
|
|
564
|
+
return this;
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
568
|
+
0 && (module.exports = {
|
|
569
|
+
FullEvent,
|
|
570
|
+
WideEventBuilder,
|
|
571
|
+
expressWideLogger,
|
|
572
|
+
wideLogger
|
|
573
|
+
});
|