@koderlabs/tasks-sdk-nestjs 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/dist/index.js ADDED
@@ -0,0 +1,997 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
+ }) : x)(function(x) {
6
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
9
+
10
+ // src/module.ts
11
+ import { Inject as Inject3, Module } from "@nestjs/common";
12
+ import { APP_INTERCEPTOR } from "@nestjs/core";
13
+
14
+ // src/reporter.service.ts
15
+ import { Inject, Injectable, Logger } from "@nestjs/common";
16
+
17
+ // src/types.ts
18
+ var INSTANTTASKS_OPTIONS = /* @__PURE__ */ Symbol("INSTANTTASKS_OPTIONS");
19
+
20
+ // src/breadcrumb-ring.ts
21
+ import { AsyncLocalStorage } from "async_hooks";
22
+ var RequestContextRing = class {
23
+ static {
24
+ __name(this, "RequestContextRing");
25
+ }
26
+ buf = [];
27
+ spans = [];
28
+ request;
29
+ max;
30
+ constructor(request, max = 50) {
31
+ this.request = request;
32
+ this.max = max;
33
+ }
34
+ add(crumb) {
35
+ this.buf.push(crumb);
36
+ if (this.buf.length > this.max) this.buf.shift();
37
+ }
38
+ addSpan(span) {
39
+ this.spans.push(span);
40
+ if (this.spans.length > this.max) this.spans.shift();
41
+ }
42
+ getBreadcrumbs() {
43
+ return [
44
+ ...this.buf
45
+ ];
46
+ }
47
+ getSpans() {
48
+ return [
49
+ ...this.spans
50
+ ];
51
+ }
52
+ setUser(userId) {
53
+ this.request.userId = userId;
54
+ }
55
+ };
56
+ var requestContextStorage = new AsyncLocalStorage();
57
+ function leaveBreadcrumb(crumb) {
58
+ const store = requestContextStorage.getStore();
59
+ if (!store) return;
60
+ store.add({
61
+ ts: crumb.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
62
+ ...crumb
63
+ });
64
+ }
65
+ __name(leaveBreadcrumb, "leaveBreadcrumb");
66
+ function getCurrentContext() {
67
+ return requestContextStorage.getStore() ?? null;
68
+ }
69
+ __name(getCurrentContext, "getCurrentContext");
70
+
71
+ // src/reporter.service.ts
72
+ function _ts_decorate(decorators, target, key, desc) {
73
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
74
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
75
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
76
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
77
+ }
78
+ __name(_ts_decorate, "_ts_decorate");
79
+ function _ts_metadata(k, v) {
80
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
81
+ }
82
+ __name(_ts_metadata, "_ts_metadata");
83
+ function _ts_param(paramIndex, decorator) {
84
+ return function(target, key) {
85
+ decorator(target, key, paramIndex);
86
+ };
87
+ }
88
+ __name(_ts_param, "_ts_param");
89
+ var installedUncaught = null;
90
+ var installedRejection = null;
91
+ var InstantTasksReporter = class _InstantTasksReporter {
92
+ static {
93
+ __name(this, "InstantTasksReporter");
94
+ }
95
+ opts;
96
+ logger = new Logger(_InstantTasksReporter.name);
97
+ apiUrl = "";
98
+ projectId = "";
99
+ managementKey = "";
100
+ environment = "production";
101
+ release = "prod";
102
+ debug = false;
103
+ dryRun = false;
104
+ beforeSend;
105
+ recent = /* @__PURE__ */ new Map();
106
+ RECENT_WINDOW_MS = 6e4;
107
+ RECENT_LIMIT = 50;
108
+ uncaughtHandler = null;
109
+ rejectionHandler = null;
110
+ constructor(opts) {
111
+ this.opts = opts;
112
+ }
113
+ onModuleInit() {
114
+ const { apiUrl, projectId, managementKey } = this.opts;
115
+ if (!apiUrl || !projectId || !managementKey) {
116
+ this.logger.warn("InstantTasks reporter disabled (missing apiUrl/projectId/managementKey)");
117
+ return;
118
+ }
119
+ this.apiUrl = apiUrl.replace(/\/+$/, "").replace(/\/api\/v\d+$/, "");
120
+ this.projectId = projectId;
121
+ this.managementKey = managementKey;
122
+ this.environment = this.opts.environment ?? process.env.NODE_ENV ?? "production";
123
+ this.release = this.opts.release ?? "prod";
124
+ this.debug = this.opts.debug ?? false;
125
+ this.dryRun = this.opts.dryRun ?? false;
126
+ this.beforeSend = this.opts.beforeSend;
127
+ if (installedUncaught) process.off("uncaughtException", installedUncaught);
128
+ if (installedRejection) process.off("unhandledRejection", installedRejection);
129
+ this.uncaughtHandler = (err) => {
130
+ void this.reportError(err, {
131
+ mechanism: "uncaughtException",
132
+ level: "fatal"
133
+ });
134
+ };
135
+ this.rejectionHandler = (reason) => {
136
+ const err = reason instanceof Error ? reason : new Error(String(reason));
137
+ void this.reportError(err, {
138
+ mechanism: "unhandledrejection",
139
+ level: "error"
140
+ });
141
+ };
142
+ process.on("uncaughtException", this.uncaughtHandler);
143
+ process.on("unhandledRejection", this.rejectionHandler);
144
+ installedUncaught = this.uncaughtHandler;
145
+ installedRejection = this.rejectionHandler;
146
+ this.logger.log(`InstantTasks reporter wired \u2192 ${this.apiUrl} project=${this.projectId}`);
147
+ }
148
+ onModuleDestroy() {
149
+ if (this.uncaughtHandler) {
150
+ process.off("uncaughtException", this.uncaughtHandler);
151
+ if (installedUncaught === this.uncaughtHandler) installedUncaught = null;
152
+ }
153
+ if (this.rejectionHandler) {
154
+ process.off("unhandledRejection", this.rejectionHandler);
155
+ if (installedRejection === this.rejectionHandler) installedRejection = null;
156
+ }
157
+ }
158
+ get enabled() {
159
+ return !!(this.apiUrl && this.projectId && this.managementKey);
160
+ }
161
+ reportError(err, ctx) {
162
+ if (!this.enabled) return;
163
+ const fingerprint = `${err.name}:${err.message}`.slice(0, 200);
164
+ if (this.shouldSuppress(fingerprint)) return;
165
+ const reqCtx = getCurrentContext();
166
+ const reqInfo = reqCtx?.request;
167
+ const breadcrumbs = reqCtx?.getBreadcrumbs();
168
+ const spans = reqCtx?.getSpans();
169
+ const inferredUserId = ctx.userId ?? reqInfo?.userId;
170
+ const inferredUrl = ctx.url ?? reqInfo?.url ?? `node://${process.env.HOSTNAME ?? "api"}`;
171
+ const userAgent = reqInfo?.userAgent ?? `node/${process.version}`;
172
+ let event = {
173
+ kind: "error",
174
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
175
+ release: this.release,
176
+ environment: this.environment,
177
+ url: inferredUrl,
178
+ userAgent,
179
+ viewport: {
180
+ width: 0,
181
+ height: 0
182
+ },
183
+ payload: {
184
+ exception: {
185
+ type: err.name || "Error",
186
+ value: err.message || String(err),
187
+ stack: err.stack,
188
+ mechanism: ctx.mechanism
189
+ },
190
+ level: ctx.level ?? "error"
191
+ },
192
+ user: inferredUserId ? {
193
+ id: inferredUserId
194
+ } : void 0,
195
+ ...reqInfo && {
196
+ request: reqInfo
197
+ },
198
+ ...breadcrumbs && breadcrumbs.length && {
199
+ breadcrumbs
200
+ },
201
+ ...spans && spans.length && {
202
+ spans
203
+ }
204
+ };
205
+ if (this.beforeSend) {
206
+ try {
207
+ event = this.beforeSend(event);
208
+ } catch (e) {
209
+ if (this.debug) this.logger.warn(`beforeSend threw: ${e.message}`);
210
+ }
211
+ if (!event) return;
212
+ }
213
+ if (this.dryRun) {
214
+ if (this.debug) this.logger.log(`[dryRun] would send: ${event.payload.exception.type}: ${event.payload.exception.value}`);
215
+ return;
216
+ }
217
+ void fetch(`${this.apiUrl}/api/v1/sdk/events`, {
218
+ method: "POST",
219
+ headers: {
220
+ Authorization: `Bearer ${this.managementKey}`,
221
+ "Content-Type": "application/json",
222
+ "X-Project-Id": this.projectId
223
+ },
224
+ body: JSON.stringify(event),
225
+ signal: AbortSignal.timeout(1e4)
226
+ }).then((res) => {
227
+ if (!res.ok) {
228
+ this.logger.warn(`InstantTasks report rejected: HTTP ${res.status}`);
229
+ }
230
+ }).catch((reportErr) => {
231
+ this.logger.warn(`InstantTasks report failed: ${reportErr.message}`);
232
+ });
233
+ }
234
+ shouldSuppress(fingerprint) {
235
+ const now = Date.now();
236
+ for (const [fp, ts] of this.recent) {
237
+ if (now - ts > this.RECENT_WINDOW_MS) this.recent.delete(fp);
238
+ }
239
+ if (this.recent.size >= this.RECENT_LIMIT && !this.recent.has(fingerprint)) {
240
+ return true;
241
+ }
242
+ const last = this.recent.get(fingerprint);
243
+ if (last && now - last < 5e3) return true;
244
+ this.recent.set(fingerprint, now);
245
+ return false;
246
+ }
247
+ };
248
+ InstantTasksReporter = _ts_decorate([
249
+ Injectable(),
250
+ _ts_param(0, Inject(INSTANTTASKS_OPTIONS)),
251
+ _ts_metadata("design:type", Function),
252
+ _ts_metadata("design:paramtypes", [
253
+ typeof InstantTasksOptions === "undefined" ? Object : InstantTasksOptions
254
+ ])
255
+ ], InstantTasksReporter);
256
+
257
+ // src/exception.filter.ts
258
+ import { Catch, HttpException, Logger as Logger2 } from "@nestjs/common";
259
+ function _ts_decorate2(decorators, target, key, desc) {
260
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
261
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
262
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
263
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
264
+ }
265
+ __name(_ts_decorate2, "_ts_decorate");
266
+ function _ts_metadata2(k, v) {
267
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
268
+ }
269
+ __name(_ts_metadata2, "_ts_metadata");
270
+ var InstantTasksExceptionFilter = class _InstantTasksExceptionFilter {
271
+ static {
272
+ __name(this, "InstantTasksExceptionFilter");
273
+ }
274
+ reporter;
275
+ logger = new Logger2(_InstantTasksExceptionFilter.name);
276
+ constructor(reporter) {
277
+ this.reporter = reporter;
278
+ }
279
+ catch(exception, host) {
280
+ let shouldReport = true;
281
+ if (exception instanceof HttpException) {
282
+ shouldReport = exception.getStatus() >= 500;
283
+ }
284
+ if (shouldReport && this.reporter.enabled) {
285
+ const err = exception instanceof Error ? exception : new Error(String(exception));
286
+ let url;
287
+ try {
288
+ const req = host.switchToHttp().getRequest();
289
+ url = req?.originalUrl ?? req?.url;
290
+ } catch {
291
+ }
292
+ this.reporter.reportError(err, {
293
+ mechanism: "http",
294
+ level: "error",
295
+ url
296
+ });
297
+ }
298
+ throw exception;
299
+ }
300
+ };
301
+ InstantTasksExceptionFilter = _ts_decorate2([
302
+ Catch(),
303
+ _ts_metadata2("design:type", Function),
304
+ _ts_metadata2("design:paramtypes", [
305
+ typeof InstantTasksReporter === "undefined" ? Object : InstantTasksReporter
306
+ ])
307
+ ], InstantTasksExceptionFilter);
308
+
309
+ // src/report.interceptor.ts
310
+ import { HttpException as HttpException2, Injectable as Injectable2, Logger as Logger3 } from "@nestjs/common";
311
+ import { throwError } from "rxjs";
312
+ import { catchError } from "rxjs/operators";
313
+ function _ts_decorate3(decorators, target, key, desc) {
314
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
315
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
316
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
317
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
318
+ }
319
+ __name(_ts_decorate3, "_ts_decorate");
320
+ function _ts_metadata3(k, v) {
321
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
322
+ }
323
+ __name(_ts_metadata3, "_ts_metadata");
324
+ var InstantTasksReportInterceptor = class _InstantTasksReportInterceptor {
325
+ static {
326
+ __name(this, "InstantTasksReportInterceptor");
327
+ }
328
+ reporter;
329
+ logger = new Logger3(_InstantTasksReportInterceptor.name);
330
+ constructor(reporter) {
331
+ this.reporter = reporter;
332
+ }
333
+ intercept(context, next) {
334
+ return next.handle().pipe(catchError((exception) => {
335
+ let shouldReport = true;
336
+ if (exception instanceof HttpException2) {
337
+ shouldReport = exception.getStatus() >= 500;
338
+ }
339
+ if (shouldReport && this.reporter.enabled) {
340
+ const err = exception instanceof Error ? exception : new Error(String(exception));
341
+ let url;
342
+ try {
343
+ const req = context.switchToHttp().getRequest();
344
+ url = req?.originalUrl ?? req?.url;
345
+ } catch {
346
+ }
347
+ this.reporter.reportError(err, {
348
+ mechanism: "http",
349
+ level: "error",
350
+ url
351
+ });
352
+ }
353
+ return throwError(() => exception);
354
+ }));
355
+ }
356
+ };
357
+ InstantTasksReportInterceptor = _ts_decorate3([
358
+ Injectable2(),
359
+ _ts_metadata3("design:type", Function),
360
+ _ts_metadata3("design:paramtypes", [
361
+ typeof InstantTasksReporter === "undefined" ? Object : InstantTasksReporter
362
+ ])
363
+ ], InstantTasksReportInterceptor);
364
+
365
+ // src/request-context.middleware.ts
366
+ import { Inject as Inject2, Injectable as Injectable3 } from "@nestjs/common";
367
+ function _ts_decorate4(decorators, target, key, desc) {
368
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
369
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
370
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
371
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
372
+ }
373
+ __name(_ts_decorate4, "_ts_decorate");
374
+ function _ts_metadata4(k, v) {
375
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
376
+ }
377
+ __name(_ts_metadata4, "_ts_metadata");
378
+ function _ts_param2(paramIndex, decorator) {
379
+ return function(target, key) {
380
+ decorator(target, key, paramIndex);
381
+ };
382
+ }
383
+ __name(_ts_param2, "_ts_param");
384
+ function extractIp(req) {
385
+ const xff = req.headers?.["x-forwarded-for"];
386
+ if (typeof xff === "string" && xff.length) return xff.split(",")[0].trim();
387
+ if (Array.isArray(xff) && xff.length) return String(xff[0]).split(",")[0].trim();
388
+ return req.ip ?? req.connection?.remoteAddress ?? req.socket?.remoteAddress;
389
+ }
390
+ __name(extractIp, "extractIp");
391
+ var InstantTasksRequestContextMiddleware = class {
392
+ static {
393
+ __name(this, "InstantTasksRequestContextMiddleware");
394
+ }
395
+ max;
396
+ constructor(opts) {
397
+ this.max = opts.maxBreadcrumbs ?? 50;
398
+ }
399
+ use(req, res, next) {
400
+ const ring = new RequestContextRing({
401
+ method: req.method,
402
+ route: req.route?.path ?? req.path,
403
+ url: req.originalUrl ?? req.url,
404
+ userAgent: req.headers?.["user-agent"],
405
+ ip: extractIp(req),
406
+ userId: req.user?.id
407
+ }, this.max);
408
+ res?.on?.("finish", () => {
409
+ ring.request.statusCode = res.statusCode;
410
+ });
411
+ requestContextStorage.run(ring, () => next());
412
+ }
413
+ };
414
+ InstantTasksRequestContextMiddleware = _ts_decorate4([
415
+ Injectable3(),
416
+ _ts_param2(0, Inject2(INSTANTTASKS_OPTIONS)),
417
+ _ts_metadata4("design:type", Function),
418
+ _ts_metadata4("design:paramtypes", [
419
+ typeof InstantTasksOptions === "undefined" ? Object : InstantTasksOptions
420
+ ])
421
+ ], InstantTasksRequestContextMiddleware);
422
+
423
+ // src/span.interceptor.ts
424
+ import { Injectable as Injectable4 } from "@nestjs/common";
425
+ import { tap } from "rxjs/operators";
426
+ function _ts_decorate5(decorators, target, key, desc) {
427
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
428
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
429
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
430
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
431
+ }
432
+ __name(_ts_decorate5, "_ts_decorate");
433
+ var InstantTasksSpanInterceptor = class {
434
+ static {
435
+ __name(this, "InstantTasksSpanInterceptor");
436
+ }
437
+ intercept(context, next) {
438
+ if (context.getType() !== "http") return next.handle();
439
+ const ctx = getCurrentContext();
440
+ const req = context.switchToHttp().getRequest();
441
+ const res = context.switchToHttp().getResponse();
442
+ const method = String(req?.method ?? "GET").toUpperCase();
443
+ const route = req?.route?.path ?? req?.path ?? req?.url ?? "unknown";
444
+ const start = Date.now();
445
+ const startIso = new Date(start).toISOString();
446
+ const recordSpan = /* @__PURE__ */ __name((status, extraAttrs) => {
447
+ if (!ctx) return;
448
+ const end = Date.now();
449
+ const span = {
450
+ name: `http.${method.toLowerCase()}.${route}`,
451
+ startTime: startIso,
452
+ endTime: new Date(end).toISOString(),
453
+ durationMs: end - start,
454
+ status,
455
+ attrs: {
456
+ "http.method": method,
457
+ "http.route": route,
458
+ "http.status_code": res?.statusCode,
459
+ "user.id": req?.user?.id,
460
+ ...extraAttrs
461
+ }
462
+ };
463
+ ctx.addSpan(span);
464
+ }, "recordSpan");
465
+ return next.handle().pipe(tap({
466
+ next: /* @__PURE__ */ __name(() => recordSpan("ok"), "next"),
467
+ error: /* @__PURE__ */ __name((err) => recordSpan("error", {
468
+ "error.message": err?.message
469
+ }), "error")
470
+ }));
471
+ }
472
+ };
473
+ InstantTasksSpanInterceptor = _ts_decorate5([
474
+ Injectable4()
475
+ ], InstantTasksSpanInterceptor);
476
+
477
+ // src/sanitize.ts
478
+ var TOKEN_QUERY_KEYS = /* @__PURE__ */ new Set([
479
+ "token",
480
+ "access_token",
481
+ "apikey",
482
+ "api_key",
483
+ "secret"
484
+ ]);
485
+ function sanitizeUrl(url) {
486
+ if (!url || typeof url !== "string") return String(url);
487
+ const qIdx = url.indexOf("?");
488
+ if (qIdx === -1) return url;
489
+ const base = url.slice(0, qIdx);
490
+ const qs = url.slice(qIdx + 1);
491
+ const parts = qs.split("&").map((pair) => {
492
+ const eq = pair.indexOf("=");
493
+ if (eq === -1) return pair;
494
+ const key = pair.slice(0, eq);
495
+ if (TOKEN_QUERY_KEYS.has(key.toLowerCase())) return `${key}=[Filtered]`;
496
+ return pair;
497
+ });
498
+ return `${base}?${parts.join("&")}`;
499
+ }
500
+ __name(sanitizeUrl, "sanitizeUrl");
501
+ function shouldIgnoreUrl(url, ignoreUrls = []) {
502
+ for (const pattern of ignoreUrls) {
503
+ if (typeof pattern === "string" && url.includes(pattern)) return true;
504
+ if (pattern instanceof RegExp && pattern.test(url)) return true;
505
+ }
506
+ return false;
507
+ }
508
+ __name(shouldIgnoreUrl, "shouldIgnoreUrl");
509
+
510
+ // src/wrap-sentinel.ts
511
+ var INSTANT_TASKS_WRAPPED = /* @__PURE__ */ Symbol.for("@koderlabs/tasks-sdk-nestjs.wrapped");
512
+ function isWrapped(fn) {
513
+ return !!fn && typeof fn === "function" && fn[INSTANT_TASKS_WRAPPED];
514
+ }
515
+ __name(isWrapped, "isWrapped");
516
+ function markWrapped(wrapper, original) {
517
+ wrapper[INSTANT_TASKS_WRAPPED] = {
518
+ original
519
+ };
520
+ return wrapper;
521
+ }
522
+ __name(markWrapped, "markWrapped");
523
+ function unwrap(fn) {
524
+ let cur = fn;
525
+ while (cur && cur[INSTANT_TASKS_WRAPPED]) cur = cur[INSTANT_TASKS_WRAPPED].original;
526
+ return cur;
527
+ }
528
+ __name(unwrap, "unwrap");
529
+
530
+ // src/http-tracing.ts
531
+ var installed = null;
532
+ function installHttpTracing(opts = {}) {
533
+ if (installed) return installed;
534
+ const ignoreUrls = opts.ignoreUrls ?? [];
535
+ const restorers = [];
536
+ if (typeof globalThis.fetch === "function" && !isWrapped(globalThis.fetch)) {
537
+ const originalFetch = globalThis.fetch.bind(globalThis);
538
+ const wrapped = /* @__PURE__ */ __name(async (input, init) => {
539
+ const start = Date.now();
540
+ const inputMethod = typeof input === "object" && input !== null && "method" in input ? input.method : void 0;
541
+ const method = String(init?.method ?? inputMethod ?? "GET").toUpperCase();
542
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input?.url ?? "";
543
+ const safeUrl = sanitizeUrl(url);
544
+ if (shouldIgnoreUrl(url, ignoreUrls)) {
545
+ return originalFetch(input, init);
546
+ }
547
+ try {
548
+ const res = await originalFetch(input, init);
549
+ leaveBreadcrumb({
550
+ category: "http",
551
+ level: res.status >= 400 ? "error" : "info",
552
+ message: `${method} ${safeUrl} \u2192 ${res.status}`,
553
+ data: {
554
+ method,
555
+ url: safeUrl,
556
+ status: res.status,
557
+ durationMs: Date.now() - start
558
+ }
559
+ });
560
+ return res;
561
+ } catch (err) {
562
+ leaveBreadcrumb({
563
+ category: "http",
564
+ level: "error",
565
+ message: `${method} ${safeUrl} \u2192 ERR`,
566
+ data: {
567
+ method,
568
+ url: safeUrl,
569
+ error: err?.message ?? String(err),
570
+ durationMs: Date.now() - start
571
+ }
572
+ });
573
+ throw err;
574
+ }
575
+ }, "wrapped");
576
+ markWrapped(wrapped, originalFetch);
577
+ globalThis.fetch = wrapped;
578
+ restorers.push(() => {
579
+ if (globalThis.fetch === wrapped) globalThis.fetch = unwrap(wrapped);
580
+ });
581
+ }
582
+ for (const mod of [
583
+ "http",
584
+ "https"
585
+ ]) {
586
+ try {
587
+ const lib = __require(mod);
588
+ if (!lib || typeof lib.request !== "function" || isWrapped(lib.request)) continue;
589
+ const originalRequest = lib.request.bind(lib);
590
+ const wrapped = /* @__PURE__ */ __name(function(...args) {
591
+ const start = Date.now();
592
+ let url = "";
593
+ let method = "GET";
594
+ try {
595
+ const first = args[0];
596
+ if (typeof first === "string") {
597
+ url = first;
598
+ } else if (first instanceof URL) {
599
+ url = first.toString();
600
+ } else if (first && typeof first === "object") {
601
+ const host = first.host ?? first.hostname ?? "";
602
+ const path = first.path ?? "";
603
+ url = `${mod}://${host}${path}`;
604
+ method = (first.method ?? "GET").toUpperCase();
605
+ }
606
+ const optsArg = args.find((a, i) => i > 0 && a && typeof a === "object" && !("on" in a));
607
+ if (optsArg?.method) method = String(optsArg.method).toUpperCase();
608
+ } catch {
609
+ }
610
+ const safeUrl = sanitizeUrl(url);
611
+ const req = originalRequest(...args);
612
+ if (!shouldIgnoreUrl(url, ignoreUrls)) {
613
+ req.once?.("response", (res) => {
614
+ leaveBreadcrumb({
615
+ category: "http",
616
+ level: (res.statusCode ?? 0) >= 400 ? "error" : "info",
617
+ message: `${method} ${safeUrl} \u2192 ${res.statusCode}`,
618
+ data: {
619
+ method,
620
+ url: safeUrl,
621
+ status: res.statusCode,
622
+ durationMs: Date.now() - start
623
+ }
624
+ });
625
+ });
626
+ req.once?.("error", (err) => {
627
+ leaveBreadcrumb({
628
+ category: "http",
629
+ level: "error",
630
+ message: `${method} ${safeUrl} \u2192 ERR`,
631
+ data: {
632
+ method,
633
+ url: safeUrl,
634
+ error: err.message,
635
+ durationMs: Date.now() - start
636
+ }
637
+ });
638
+ });
639
+ }
640
+ return req;
641
+ }, "wrapped");
642
+ markWrapped(wrapped, originalRequest);
643
+ lib.request = wrapped;
644
+ restorers.push(() => {
645
+ if (lib.request === wrapped) lib.request = unwrap(wrapped);
646
+ });
647
+ } catch {
648
+ }
649
+ }
650
+ installed = {
651
+ restore() {
652
+ for (const r of restorers) r();
653
+ installed = null;
654
+ }
655
+ };
656
+ return installed;
657
+ }
658
+ __name(installHttpTracing, "installHttpTracing");
659
+
660
+ // src/console-capture.ts
661
+ var installed2 = null;
662
+ var NEST_LOGGER_HINTS = [
663
+ "[Nest]",
664
+ "NestApplication",
665
+ "NestFactory",
666
+ "InstanceLoader",
667
+ "RoutesResolver",
668
+ "RouterExplorer"
669
+ ];
670
+ function looksLikeNestLogger(args) {
671
+ if (!args.length) return false;
672
+ const first = String(args[0] ?? "");
673
+ return NEST_LOGGER_HINTS.some((h) => first.includes(h));
674
+ }
675
+ __name(looksLikeNestLogger, "looksLikeNestLogger");
676
+ function format(args) {
677
+ return args.map((a) => {
678
+ if (typeof a === "string") return a;
679
+ if (a instanceof Error) return `${a.name}: ${a.message}`;
680
+ try {
681
+ return JSON.stringify(a);
682
+ } catch {
683
+ return String(a);
684
+ }
685
+ }).join(" ").slice(0, 1e3);
686
+ }
687
+ __name(format, "format");
688
+ function installConsoleCapture() {
689
+ if (installed2) return installed2;
690
+ const restorers = [];
691
+ const levels = [
692
+ [
693
+ "log",
694
+ "info"
695
+ ],
696
+ [
697
+ "warn",
698
+ "warning"
699
+ ],
700
+ [
701
+ "error",
702
+ "error"
703
+ ]
704
+ ];
705
+ for (const [method, level] of levels) {
706
+ const fn = console[method];
707
+ if (typeof fn !== "function" || isWrapped(fn)) continue;
708
+ const original = fn.bind(console);
709
+ const wrapped = /* @__PURE__ */ __name(function(...args) {
710
+ try {
711
+ if (!looksLikeNestLogger(args)) {
712
+ leaveBreadcrumb({
713
+ category: "console",
714
+ level,
715
+ message: format(args)
716
+ });
717
+ }
718
+ } catch {
719
+ }
720
+ return original(...args);
721
+ }, "wrapped");
722
+ markWrapped(wrapped, original);
723
+ console[method] = wrapped;
724
+ restorers.push(() => {
725
+ if (console[method] === wrapped) {
726
+ console[method] = unwrap(wrapped);
727
+ }
728
+ });
729
+ }
730
+ installed2 = {
731
+ restore() {
732
+ for (const r of restorers) r();
733
+ installed2 = null;
734
+ }
735
+ };
736
+ return installed2;
737
+ }
738
+ __name(installConsoleCapture, "installConsoleCapture");
739
+
740
+ // src/typeorm-tracing.ts
741
+ function parseQueryType(sql) {
742
+ const m = /^\s*(\w+)/.exec(sql);
743
+ return (m?.[1] ?? "UNKNOWN").toUpperCase();
744
+ }
745
+ __name(parseQueryType, "parseQueryType");
746
+ function parseTable(sql) {
747
+ const re = /\b(?:FROM|INTO|UPDATE|JOIN)\s+["']?(\w+)/i;
748
+ const m = re.exec(sql);
749
+ return m?.[1];
750
+ }
751
+ __name(parseTable, "parseTable");
752
+ var InstantTasksTypeOrmLogger = class {
753
+ static {
754
+ __name(this, "InstantTasksTypeOrmLogger");
755
+ }
756
+ slowQueryMs;
757
+ constructor(slowQueryMs = 200) {
758
+ this.slowQueryMs = slowQueryMs;
759
+ }
760
+ emit(sql, durationMs, status, extraAttrs = {}) {
761
+ const ctx = getCurrentContext();
762
+ const queryType = parseQueryType(sql);
763
+ const table = parseTable(sql);
764
+ const truncated = sql.length > 300 ? `${sql.slice(0, 300)}\u2026` : sql;
765
+ leaveBreadcrumb({
766
+ category: "db",
767
+ level: status === "error" ? "error" : durationMs && durationMs > this.slowQueryMs ? "warning" : "info",
768
+ message: `${queryType}${table ? ` ${table}` : ""}${durationMs != null ? ` (${durationMs}ms)` : ""}`,
769
+ data: {
770
+ sql: truncated,
771
+ queryType,
772
+ table,
773
+ durationMs,
774
+ status,
775
+ ...extraAttrs
776
+ }
777
+ });
778
+ if (ctx && durationMs != null) {
779
+ const end = Date.now();
780
+ const span = {
781
+ name: "db.query",
782
+ startTime: new Date(end - durationMs).toISOString(),
783
+ endTime: new Date(end).toISOString(),
784
+ durationMs,
785
+ status,
786
+ attrs: {
787
+ "db.statement": truncated,
788
+ "db.operation": queryType,
789
+ "db.table": table,
790
+ ...extraAttrs
791
+ }
792
+ };
793
+ ctx.addSpan(span);
794
+ }
795
+ }
796
+ // ── TypeORM Logger interface ─────────────────────────────────────
797
+ logQuery(sql) {
798
+ this.emit(sql, null, "ok");
799
+ }
800
+ logQueryError(error, sql) {
801
+ this.emit(sql, null, "error", {
802
+ "error.message": error instanceof Error ? error.message : String(error)
803
+ });
804
+ }
805
+ logQuerySlow(time, sql) {
806
+ this.emit(sql, time, "ok", {
807
+ slow: true
808
+ });
809
+ }
810
+ logSchemaBuild() {
811
+ }
812
+ logMigration() {
813
+ }
814
+ log() {
815
+ }
816
+ };
817
+ function installTypeOrmTracing(dataSource) {
818
+ if (!dataSource || typeof dataSource.setOptions !== "function") return null;
819
+ const existing = dataSource.options?.logger;
820
+ if (existing instanceof InstantTasksTypeOrmLogger) return existing;
821
+ const logger = new InstantTasksTypeOrmLogger();
822
+ dataSource.setOptions({
823
+ logger
824
+ });
825
+ return logger;
826
+ }
827
+ __name(installTypeOrmTracing, "installTypeOrmTracing");
828
+
829
+ // src/module.ts
830
+ function _ts_decorate6(decorators, target, key, desc) {
831
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
832
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
833
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
834
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
835
+ }
836
+ __name(_ts_decorate6, "_ts_decorate");
837
+ function _ts_metadata5(k, v) {
838
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
839
+ }
840
+ __name(_ts_metadata5, "_ts_metadata");
841
+ function _ts_param3(paramIndex, decorator) {
842
+ return function(target, key) {
843
+ decorator(target, key, paramIndex);
844
+ };
845
+ }
846
+ __name(_ts_param3, "_ts_param");
847
+ var InstantTasksLifecycle = class InstantTasksLifecycle2 {
848
+ static {
849
+ __name(this, "InstantTasksLifecycle");
850
+ }
851
+ opts;
852
+ restorers = [];
853
+ constructor(opts) {
854
+ this.opts = opts;
855
+ }
856
+ onModuleInit() {
857
+ if (this.opts.captureHttp) {
858
+ const ignoreUrls = [
859
+ ...this.opts.ignoreUrls ?? [],
860
+ `${(this.opts.apiUrl ?? "").replace(/\/+$/, "").replace(/\/api\/v\d+$/, "")}/api/v1/sdk/`
861
+ ];
862
+ const { restore } = installHttpTracing({
863
+ ignoreUrls
864
+ });
865
+ this.restorers.push(restore);
866
+ }
867
+ if (this.opts.captureConsole) {
868
+ const { restore } = installConsoleCapture();
869
+ this.restorers.push(restore);
870
+ }
871
+ if (this.opts.captureTypeOrm && this.opts.dataSource) {
872
+ installTypeOrmTracing(this.opts.dataSource);
873
+ }
874
+ }
875
+ onModuleDestroy() {
876
+ for (const r of this.restorers) {
877
+ try {
878
+ r();
879
+ } catch {
880
+ }
881
+ }
882
+ this.restorers = [];
883
+ }
884
+ };
885
+ InstantTasksLifecycle = _ts_decorate6([
886
+ _ts_param3(0, Inject3(INSTANTTASKS_OPTIONS)),
887
+ _ts_metadata5("design:type", Function),
888
+ _ts_metadata5("design:paramtypes", [
889
+ typeof InstantTasksOptions === "undefined" ? Object : InstantTasksOptions
890
+ ])
891
+ ], InstantTasksLifecycle);
892
+ var InstantTasksModule = class _InstantTasksModule {
893
+ static {
894
+ __name(this, "InstantTasksModule");
895
+ }
896
+ opts;
897
+ static forRoot(options) {
898
+ return _InstantTasksModule.build({
899
+ provide: INSTANTTASKS_OPTIONS,
900
+ useValue: options
901
+ }, options);
902
+ }
903
+ static forRootAsync(opts) {
904
+ const optsProvider = {
905
+ provide: INSTANTTASKS_OPTIONS,
906
+ useFactory: opts.useFactory,
907
+ inject: opts.inject ?? []
908
+ };
909
+ const conditional = [
910
+ // Span interceptor self-checks for context — safe to always register.
911
+ {
912
+ provide: APP_INTERCEPTOR,
913
+ useClass: InstantTasksSpanInterceptor
914
+ }
915
+ ];
916
+ return {
917
+ module: _InstantTasksModule,
918
+ imports: opts.imports ?? [],
919
+ providers: [
920
+ optsProvider,
921
+ InstantTasksReporter,
922
+ InstantTasksExceptionFilter,
923
+ InstantTasksReportInterceptor,
924
+ InstantTasksRequestContextMiddleware,
925
+ InstantTasksLifecycle,
926
+ ...conditional
927
+ ],
928
+ exports: [
929
+ InstantTasksReporter,
930
+ InstantTasksExceptionFilter,
931
+ InstantTasksReportInterceptor
932
+ ],
933
+ global: true
934
+ };
935
+ }
936
+ static build(optsProvider, options) {
937
+ const conditional = [];
938
+ if (options.captureSpans) {
939
+ conditional.push({
940
+ provide: APP_INTERCEPTOR,
941
+ useClass: InstantTasksSpanInterceptor
942
+ });
943
+ }
944
+ return {
945
+ module: _InstantTasksModule,
946
+ providers: [
947
+ optsProvider,
948
+ InstantTasksReporter,
949
+ InstantTasksExceptionFilter,
950
+ InstantTasksReportInterceptor,
951
+ InstantTasksRequestContextMiddleware,
952
+ InstantTasksLifecycle,
953
+ ...conditional
954
+ ],
955
+ exports: [
956
+ InstantTasksReporter,
957
+ InstantTasksExceptionFilter,
958
+ InstantTasksReportInterceptor
959
+ ],
960
+ global: true
961
+ };
962
+ }
963
+ constructor(opts) {
964
+ this.opts = opts;
965
+ }
966
+ configure(consumer) {
967
+ if (this.opts.captureRequestContext) {
968
+ consumer.apply(InstantTasksRequestContextMiddleware).forRoutes("*");
969
+ }
970
+ }
971
+ };
972
+ InstantTasksModule = _ts_decorate6([
973
+ Module({}),
974
+ _ts_param3(0, Inject3(INSTANTTASKS_OPTIONS)),
975
+ _ts_metadata5("design:type", Function),
976
+ _ts_metadata5("design:paramtypes", [
977
+ typeof InstantTasksOptions === "undefined" ? Object : InstantTasksOptions
978
+ ])
979
+ ], InstantTasksModule);
980
+ export {
981
+ INSTANTTASKS_OPTIONS,
982
+ InstantTasksExceptionFilter,
983
+ InstantTasksModule,
984
+ InstantTasksReportInterceptor,
985
+ InstantTasksReporter,
986
+ InstantTasksRequestContextMiddleware,
987
+ InstantTasksSpanInterceptor,
988
+ InstantTasksTypeOrmLogger,
989
+ RequestContextRing,
990
+ getCurrentContext,
991
+ installConsoleCapture,
992
+ installHttpTracing,
993
+ installTypeOrmTracing,
994
+ leaveBreadcrumb,
995
+ requestContextStorage
996
+ };
997
+ //# sourceMappingURL=index.js.map