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