@angelitosystems/nest-devtools 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,752 @@
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
+ NestDevTools: () => NestDevTools,
24
+ captureError: () => captureError,
25
+ devtools: () => devtools,
26
+ requestContext: () => import_devtools_core8.requestContext
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+
30
+ // src/sdk.ts
31
+ var import_os = require("os");
32
+ var import_devtools_core7 = require("@angelitosystems/devtools-core");
33
+
34
+ // src/instrumentation/http.ts
35
+ var import_devtools_protocol = require("@angelitosystems/devtools-protocol");
36
+ var import_devtools_core2 = require("@angelitosystems/devtools-core");
37
+
38
+ // src/emitter.ts
39
+ var import_devtools_core = require("@angelitosystems/devtools-core");
40
+ function emit(event, payload) {
41
+ try {
42
+ import_devtools_core.devtools.send(event, payload);
43
+ } catch {
44
+ }
45
+ }
46
+
47
+ // src/instrumentation/timeline.ts
48
+ var recorders = /* @__PURE__ */ new Map();
49
+ function trackSpans(requestId, spans) {
50
+ recorders.set(requestId, spans);
51
+ return () => recorders.delete(requestId);
52
+ }
53
+
54
+ // src/instrumentation/http.ts
55
+ var INTERESTING_HEADERS = [
56
+ "content-type",
57
+ "content-length",
58
+ "accept",
59
+ "origin",
60
+ "referer",
61
+ "user-agent",
62
+ "x-forwarded-for",
63
+ "x-request-id"
64
+ ];
65
+ var WRAPPED = /* @__PURE__ */ Symbol("nest-devtools.wrapped");
66
+ var HttpInstrumentation = class {
67
+ constructor(ctx) {
68
+ this.ctx = ctx;
69
+ this.redactor = new import_devtools_protocol.Redactor({
70
+ redact: ctx.config.redact,
71
+ allow: ctx.config.allow,
72
+ maxBytes: ctx.config.maxPayloadBytes
73
+ });
74
+ }
75
+ ctx;
76
+ redactor;
77
+ /** Begin capturing HTTP requests. Returns a cleanup function. */
78
+ attach(app) {
79
+ try {
80
+ const adapter = app.getHttpAdapter();
81
+ if (!adapter || !["http", "express"].includes(adapter.getType())) return () => {
82
+ };
83
+ const getServer = () => adapter.getHttpServer?.();
84
+ const existing = getServer();
85
+ if (existing) {
86
+ this.wrapServer(existing);
87
+ return () => {
88
+ };
89
+ }
90
+ const appLike = app;
91
+ const originalListen = appLike.listen.bind(app);
92
+ const self = this;
93
+ const patchedListen = (...args) => {
94
+ const result = originalListen(...args);
95
+ Promise.resolve(result).then(() => {
96
+ const server = getServer();
97
+ if (server) self.wrapServer(server);
98
+ }).catch(() => {
99
+ });
100
+ return result;
101
+ };
102
+ appLike.listen = patchedListen;
103
+ return () => {
104
+ appLike.listen = originalListen;
105
+ };
106
+ } catch {
107
+ return () => {
108
+ };
109
+ }
110
+ }
111
+ /** Wrap the 'request' listeners of a server exactly once. */
112
+ wrapServer(server) {
113
+ const target = server;
114
+ if (target[WRAPPED]) return;
115
+ target[WRAPPED] = true;
116
+ const self = this;
117
+ const originalListeners = server.rawListeners("request");
118
+ server.removeAllListeners("request");
119
+ server.on("request", (req, res) => {
120
+ self.instrumentRequest(req, res, () => {
121
+ for (const listener of originalListeners) {
122
+ listener.call(server, req, res);
123
+ }
124
+ });
125
+ });
126
+ }
127
+ /** Wrap a single request lifecycle. */
128
+ instrumentRequest(req, res, next) {
129
+ if (!this.ctx.config.capture.requests) {
130
+ next();
131
+ return;
132
+ }
133
+ const startedAt = Date.now();
134
+ const requestId = (0, import_devtools_protocol.randomId)("req");
135
+ const method = (req.method ?? "GET").toUpperCase();
136
+ const rawUrl = req.url ?? "/";
137
+ const url = safeUrlPath(rawUrl);
138
+ const spans = [];
139
+ const unregister = trackSpans(requestId, spans);
140
+ res.once("finish", unregister);
141
+ const started = {
142
+ requestId,
143
+ projectId: this.ctx.projectInfo.projectId,
144
+ method,
145
+ url,
146
+ headers: this.collectHeaders(req),
147
+ query: parseQuery(rawUrl),
148
+ ip: clientIp(req),
149
+ userAgent: asString(req.headers["user-agent"]) || void 0,
150
+ startedAt
151
+ };
152
+ const middlewareSpan = {
153
+ layer: "middleware",
154
+ label: `${method} ${url}`,
155
+ duration: 0,
156
+ startedAt,
157
+ status: "ok"
158
+ };
159
+ spans.push(middlewareSpan);
160
+ const onFinished = () => {
161
+ const finishedAt = Date.now();
162
+ middlewareSpan.duration = Math.max(0, finishedAt - middlewareSpan.startedAt);
163
+ middlewareSpan.status = res.statusCode >= 500 ? "error" : "ok";
164
+ const responseSpan = {
165
+ layer: "response",
166
+ label: "response",
167
+ duration: 0,
168
+ startedAt: finishedAt,
169
+ status: "ok"
170
+ };
171
+ spans.push(responseSpan);
172
+ const errored = res.statusCode >= 500;
173
+ const completed = {
174
+ requestId,
175
+ projectId: this.ctx.projectInfo.projectId,
176
+ method,
177
+ url,
178
+ statusCode: res.statusCode,
179
+ duration: finishedAt - startedAt,
180
+ startedAt,
181
+ timeline: spans,
182
+ query: parseQuery(rawUrl),
183
+ errored
184
+ };
185
+ emit("request.completed", completed);
186
+ if (errored) {
187
+ emit("error.created", {
188
+ requestId,
189
+ projectId: this.ctx.projectInfo.projectId,
190
+ name: "HttpError",
191
+ message: `${method} ${url} failed with status ${res.statusCode}`,
192
+ fingerprint: httpErrorFingerprint(method, url, res.statusCode),
193
+ request: { method, url, statusCode: res.statusCode },
194
+ timestamp: finishedAt
195
+ });
196
+ }
197
+ };
198
+ const context = (0, import_devtools_core2.requestContext)();
199
+ context.run({ requestId, method, url, startedAt }, () => {
200
+ emit("request.started", started);
201
+ res.once("finish", onFinished);
202
+ next();
203
+ });
204
+ }
205
+ collectHeaders(req) {
206
+ const out = {};
207
+ for (const key of INTERESTING_HEADERS) {
208
+ const value = req.headers[key];
209
+ if (value === void 0) continue;
210
+ out[key] = this.redactor.isSensitive(key) ? "[REDACTED]" : asString(value).slice(0, 200);
211
+ }
212
+ return out;
213
+ }
214
+ };
215
+ function asString(value) {
216
+ if (Array.isArray(value)) return value.join(", ");
217
+ return value ?? "";
218
+ }
219
+ function httpErrorFingerprint(method, url, statusCode) {
220
+ const route = url.replace(/\/\d+(?=\/|$)/g, "/:id");
221
+ const raw = `${method}::${route}::${statusCode}`;
222
+ let hash = 0;
223
+ for (let i = 0; i < raw.length; i++) {
224
+ hash = hash * 31 + raw.charCodeAt(i) >>> 0;
225
+ }
226
+ return hash.toString(36).padStart(7, "0");
227
+ }
228
+ function safeUrlPath(raw) {
229
+ const index = raw.indexOf("?");
230
+ return index === -1 ? raw : raw.slice(0, index);
231
+ }
232
+ function parseQuery(raw) {
233
+ const index = raw.indexOf("?");
234
+ if (index === -1) return {};
235
+ const out = {};
236
+ for (const [key, value] of new URLSearchParams(raw.slice(index + 1))) {
237
+ out[key] = value;
238
+ }
239
+ return out;
240
+ }
241
+ function clientIp(req) {
242
+ const forwarded = req.headers["x-forwarded-for"];
243
+ if (typeof forwarded === "string") return forwarded.split(",")[0]?.trim();
244
+ return req.socket?.remoteAddress ?? void 0;
245
+ }
246
+
247
+ // src/instrumentation/console.ts
248
+ var import_console = require("console");
249
+ var import_devtools_protocol2 = require("@angelitosystems/devtools-protocol");
250
+ var import_devtools_core3 = require("@angelitosystems/devtools-core");
251
+ var import_devtools_core4 = require("@angelitosystems/devtools-core");
252
+ var METHODS = ["log", "info", "warn", "error", "debug"];
253
+ var LEVEL_BY_METHOD = {
254
+ log: "info",
255
+ info: "info",
256
+ warn: "warn",
257
+ error: "error",
258
+ debug: "debug"
259
+ };
260
+ var originalConsole = new import_console.Console({
261
+ stdout: process.stdout,
262
+ stderr: process.stderr,
263
+ colorMode: "auto"
264
+ });
265
+ var ConsoleInstrumentation = class {
266
+ constructor(ctx) {
267
+ this.ctx = ctx;
268
+ this.redactor = new import_devtools_protocol2.Redactor({
269
+ redact: ctx.config.redact,
270
+ allow: ctx.config.allow,
271
+ maxBytes: ctx.config.maxPayloadBytes
272
+ });
273
+ }
274
+ ctx;
275
+ redactor;
276
+ /** Patch console methods. Returns a restore function. */
277
+ attach() {
278
+ const self = this;
279
+ const restores = [];
280
+ for (const method of METHODS) {
281
+ const original = console[method];
282
+ if (typeof original !== "function") continue;
283
+ const patched = (...args) => {
284
+ try {
285
+ original.apply(console, args);
286
+ } catch {
287
+ }
288
+ self.capture(method, args);
289
+ };
290
+ console[method] = patched;
291
+ restores.push(() => {
292
+ console[method] = original;
293
+ });
294
+ }
295
+ return () => {
296
+ for (const restore of restores.splice(0)) restore();
297
+ };
298
+ }
299
+ capture(method, args) {
300
+ if (!this.ctx.config.capture.logs) return;
301
+ try {
302
+ const level = LEVEL_BY_METHOD[method];
303
+ const payload = {
304
+ projectId: this.ctx.projectInfo.projectId,
305
+ level,
306
+ message: formatMessage(args, this.redactor),
307
+ arguments: args.length > 0 ? this.redactor.redact(args) : void 0,
308
+ stack: method === "error" ? captureStack() : void 0,
309
+ source: (0, import_devtools_core3.resolveSourceLocation)(captureStack() ?? "", { skip: 0 }),
310
+ context: void 0,
311
+ processId: process.pid,
312
+ requestId: (0, import_devtools_core4.requestContext)().requestId(),
313
+ timestamp: Date.now()
314
+ };
315
+ if (method === "error") payload.level = "error";
316
+ emit("log.created", payload);
317
+ } catch {
318
+ }
319
+ }
320
+ };
321
+ function formatMessage(args, redactor) {
322
+ if (args.length === 0) return "";
323
+ const first = args[0];
324
+ if (typeof first === "string") {
325
+ return args.length === 1 ? redactor.redactString(first) : `${first} ${redactor.serialize(args.slice(1))}`;
326
+ }
327
+ return redactor.serialize(args);
328
+ }
329
+ function captureStack() {
330
+ const err = new Error();
331
+ const stack = err.stack;
332
+ if (!stack) return void 0;
333
+ return stack.split("\n").slice(3).join("\n");
334
+ }
335
+
336
+ // src/instrumentation/nest-logger.ts
337
+ var import_common = require("@nestjs/common");
338
+ var import_devtools_protocol3 = require("@angelitosystems/devtools-protocol");
339
+ var import_devtools_core5 = require("@angelitosystems/devtools-core");
340
+ var activeRedactor = new import_devtools_protocol3.Redactor();
341
+ var activeConfig = null;
342
+ var activeProjectId = "";
343
+ function capture(level, message, context, stack) {
344
+ try {
345
+ if (!activeConfig?.capture.logs) return;
346
+ const text = typeof message === "string" ? message : activeRedactor.serialize(message);
347
+ const payload = {
348
+ projectId: activeProjectId,
349
+ level,
350
+ message: activeRedactor.redactString(text),
351
+ source: stack ? void 0 : resolveSdkAwareLocation(),
352
+ context,
353
+ stack,
354
+ processId: process.pid,
355
+ requestId: (0, import_devtools_core5.requestContext)().requestId(),
356
+ timestamp: Date.now()
357
+ };
358
+ emit("log.created", payload);
359
+ } catch {
360
+ }
361
+ }
362
+ var LoggerInstrumentation = class {
363
+ constructor(ctx) {
364
+ activeRedactor = new import_devtools_protocol3.Redactor({
365
+ redact: ctx.config.redact,
366
+ allow: ctx.config.allow,
367
+ maxBytes: ctx.config.maxPayloadBytes
368
+ });
369
+ activeConfig = ctx.config;
370
+ activeProjectId = ctx.projectInfo.projectId;
371
+ }
372
+ /** Patch Logger.prototype and the static Logger methods. Returns a restore function. */
373
+ attach() {
374
+ const restores = [];
375
+ const patch = (owner, method, level, instance) => {
376
+ const original = owner[method];
377
+ if (typeof original !== "function") return;
378
+ owner[method] = function(message, ...rest) {
379
+ const context = instance ? this?.context ?? rest[0] : rest[0];
380
+ const stack = rest[1];
381
+ try {
382
+ const result = original.apply(this, [message, ...rest]);
383
+ capture(level, message, context, stack);
384
+ return result;
385
+ } catch (err) {
386
+ capture(level, message, context, stack);
387
+ throw err;
388
+ }
389
+ };
390
+ restores.push(() => {
391
+ owner[method] = original;
392
+ });
393
+ };
394
+ const proto = import_common.Logger.prototype;
395
+ patch(proto, "log", "info", true);
396
+ patch(proto, "error", "error", true);
397
+ patch(proto, "warn", "warn", true);
398
+ patch(proto, "debug", "debug", true);
399
+ patch(proto, "verbose", "verbose", true);
400
+ const statics = import_common.Logger;
401
+ patch(statics, "log", "info", false);
402
+ patch(statics, "error", "error", false);
403
+ patch(statics, "warn", "warn", false);
404
+ patch(statics, "debug", "debug", false);
405
+ patch(statics, "verbose", "verbose", false);
406
+ return () => {
407
+ for (const restore of restores.splice(0)) restore();
408
+ };
409
+ }
410
+ };
411
+ function resolveSdkAwareLocation() {
412
+ try {
413
+ const err = new Error();
414
+ const stack = err.stack?.split("\n").slice(3).join("\n");
415
+ return (0, import_devtools_core5.resolveSourceLocation)(stack ?? "", { skip: 0 });
416
+ } catch {
417
+ return void 0;
418
+ }
419
+ }
420
+
421
+ // src/instrumentation/exceptions.ts
422
+ var import_path = require("path");
423
+ var import_devtools_core6 = require("@angelitosystems/devtools-core");
424
+ var ExceptionsInstrumentation = class {
425
+ /** Attach process-level hooks. Returns a detach function. */
426
+ static attach(ctx) {
427
+ const onUncaught = (error) => {
428
+ reportError(error, ctx, { source: "uncaughtException" });
429
+ };
430
+ const onRejection = (reason) => {
431
+ reportError(reason, ctx, { source: "unhandledRejection" });
432
+ };
433
+ process.on("uncaughtException", onUncaught);
434
+ process.on("unhandledRejection", onRejection);
435
+ return () => {
436
+ process.off("uncaughtException", onUncaught);
437
+ process.off("unhandledRejection", onRejection);
438
+ };
439
+ }
440
+ };
441
+ function captureError(error, options) {
442
+ reportError(error, void 0, options);
443
+ }
444
+ function reportError(error, ctx, options) {
445
+ try {
446
+ if (ctx && !ctx.config.capture.errors) return;
447
+ const normalized = normalizeError(error);
448
+ const location = (0, import_devtools_core6.resolveSourceLocation)(normalized.error ?? normalized.message, { skip: 1 });
449
+ const projectId = ctx?.projectInfo.projectId ?? (0, import_path.basename)(process.cwd());
450
+ const payload = {
451
+ requestId: (0, import_devtools_core6.requestContext)().requestId(),
452
+ projectId,
453
+ name: normalized.name,
454
+ message: normalized.message,
455
+ stack: normalized.error?.stack,
456
+ source: location,
457
+ fingerprint: fingerprint(normalized.name, normalized.message, location),
458
+ context: options?.context ?? options?.source,
459
+ request: currentRequestSummary(),
460
+ controller: options?.controller,
461
+ service: options?.service,
462
+ timestamp: Date.now()
463
+ };
464
+ emit("error.created", payload);
465
+ } catch {
466
+ }
467
+ }
468
+ function normalizeError(error) {
469
+ if (error instanceof Error) {
470
+ return { error, name: error.name, message: error.message };
471
+ }
472
+ if (typeof error === "string") {
473
+ return { error: void 0, name: "Unknown", message: error };
474
+ }
475
+ if (error !== null && typeof error === "object" && "message" in error) {
476
+ const record = error;
477
+ return {
478
+ error: void 0,
479
+ name: typeof record["name"] === "string" ? record["name"] : "Error",
480
+ message: String(record["message"])
481
+ };
482
+ }
483
+ return { error: void 0, name: "Unknown", message: (0, import_devtools_core6.safeStringify)(error) };
484
+ }
485
+ function fingerprint(name, message, location) {
486
+ const raw = name + "::" + message + "::" + (location?.file ?? "") + ":" + String(location?.line ?? "");
487
+ let hash = 0;
488
+ for (let i = 0; i < raw.length; i++) {
489
+ hash = hash * 31 + raw.charCodeAt(i) >>> 0;
490
+ }
491
+ return hash.toString(36).padStart(7, "0");
492
+ }
493
+ function currentRequestSummary() {
494
+ const context = (0, import_devtools_core6.requestContext)().current();
495
+ if (!context?.method || !context?.url) return void 0;
496
+ return { method: context.method, url: context.url };
497
+ }
498
+
499
+ // src/instrumentation/performance.ts
500
+ var PerformanceInstrumentation = class {
501
+ constructor(ctx) {
502
+ this.ctx = ctx;
503
+ }
504
+ ctx;
505
+ /** No-op: sampling is owned by core. Returns a no-op cleanup. */
506
+ attach() {
507
+ void this.ctx;
508
+ return () => {
509
+ };
510
+ }
511
+ };
512
+
513
+ // src/instrumentation/app-explorer.ts
514
+ var AppExplorer = class {
515
+ constructor(ctx) {
516
+ this.ctx = ctx;
517
+ }
518
+ ctx;
519
+ /** Walk the container and emit app.snapshot. Returns a no-op cleanup. */
520
+ attach(app) {
521
+ try {
522
+ const snapshot = this.buildSnapshot(app);
523
+ emit("app.snapshot", snapshot);
524
+ } catch {
525
+ }
526
+ return () => {
527
+ };
528
+ }
529
+ buildSnapshot(app) {
530
+ const internal = app.container;
531
+ const modules = [];
532
+ const modulesMap = internal?.getModules?.();
533
+ if (modulesMap) {
534
+ for (const [id, node] of modulesMap) {
535
+ const moduleName = node.metatype?.name ?? String(id);
536
+ const members = this.extractMembers(node);
537
+ modules.push({
538
+ name: moduleName,
539
+ imports: [],
540
+ controllers: members.controllers,
541
+ providers: members.providers,
542
+ exports: []
543
+ });
544
+ }
545
+ }
546
+ return {
547
+ projectId: this.ctx.projectInfo.projectId,
548
+ projectName: this.ctx.projectInfo.projectName,
549
+ modules,
550
+ nestjsVersion: this.ctx.projectInfo.nestjsVersion,
551
+ capturedAt: Date.now()
552
+ };
553
+ }
554
+ extractMembers(node) {
555
+ const controllers = [];
556
+ const providers = [];
557
+ const providersMap = node.providers;
558
+ if (providersMap instanceof Map) {
559
+ for (const [id, wrapper] of providersMap) {
560
+ const member = this.memberFromWrapper(id, wrapper);
561
+ if (!member) continue;
562
+ if (wrapper && wrapper.subtype === "httpController") {
563
+ controllers.push(member);
564
+ } else {
565
+ providers.push(member);
566
+ }
567
+ }
568
+ }
569
+ return { controllers, providers };
570
+ }
571
+ memberFromWrapper(id, wrapper) {
572
+ const w = wrapper;
573
+ const name = w?.metatype?.name ?? (typeof id === "string" ? id.replace(/^[A-Z_0-9]+:/, "") : "unknown");
574
+ const type = classify(name, w?.instance);
575
+ return { name, type };
576
+ }
577
+ };
578
+ function classify(name, instance) {
579
+ if (name.endsWith("Gateway")) return "gateway";
580
+ if (name.endsWith("Guard")) return "guard";
581
+ if (name.endsWith("Interceptor")) return "interceptor";
582
+ if (name.endsWith("Pipe")) return "pipe";
583
+ if (name.endsWith("Filter")) return "filter";
584
+ if (instance && typeof instance.canActivate === "function") return "guard";
585
+ if (instance && typeof instance.intercept === "function") return "interceptor";
586
+ if (instance && typeof instance.transform === "function") return "pipe";
587
+ if (instance && typeof instance.catch === "function") return "filter";
588
+ return "provider";
589
+ }
590
+
591
+ // src/instrumentation/websockets.ts
592
+ var WebsocketInstrumentation = class {
593
+ constructor(ctx) {
594
+ this.ctx = ctx;
595
+ }
596
+ ctx;
597
+ /** No-op for now. Returns a no-op cleanup. */
598
+ attach() {
599
+ void this.ctx;
600
+ return () => {
601
+ };
602
+ }
603
+ };
604
+
605
+ // src/instrumentation/database.ts
606
+ var DatabaseInstrumentation = class {
607
+ constructor(ctx) {
608
+ this.ctx = ctx;
609
+ }
610
+ ctx;
611
+ /** No-op for now. Returns a no-op cleanup. */
612
+ attach() {
613
+ void this.ctx;
614
+ return () => {
615
+ };
616
+ }
617
+ };
618
+
619
+ // src/detect.ts
620
+ var import_fs = require("fs");
621
+ var import_path2 = require("path");
622
+ var cached;
623
+ function detectNestJsVersion() {
624
+ if (cached !== void 0) return cached;
625
+ cached = null;
626
+ try {
627
+ const candidates = [
628
+ (0, import_path2.resolve)(process.cwd(), "node_modules/@nestjs/core/package.json"),
629
+ // bun/pnpm layouts: walk up a couple of levels
630
+ (0, import_path2.resolve)(process.cwd(), "../../node_modules/@nestjs/core/package.json")
631
+ ];
632
+ for (const path of candidates) {
633
+ if (!(0, import_fs.existsSync)(path)) continue;
634
+ const pkg = JSON.parse((0, import_fs.readFileSync)(path, "utf8"));
635
+ if (pkg.version) {
636
+ cached = pkg.version;
637
+ break;
638
+ }
639
+ }
640
+ } catch {
641
+ cached = null;
642
+ }
643
+ return cached;
644
+ }
645
+
646
+ // src/version.ts
647
+ var SDK_VERSION = "0.1.0";
648
+
649
+ // src/sdk.ts
650
+ var NestDevTools = class {
651
+ static cleanups = [];
652
+ static initialized = false;
653
+ /** Initialize DevTools for a NestJS application. One active instance per process. */
654
+ static init(app, userConfig) {
655
+ const config = (0, import_devtools_core7.resolveConfig)(userConfig);
656
+ const disabled = {
657
+ enabled: false,
658
+ reason: "disabled-by-config",
659
+ projectId: config.projectId,
660
+ projectName: config.project ?? config.projectId,
661
+ server: config.server
662
+ };
663
+ if (this.initialized) return { ...disabled, reason: "already-initialized" };
664
+ if (!config.enabled) return disabled;
665
+ this.initialized = true;
666
+ const registerCleanup = (fn) => this.cleanups.push(fn);
667
+ const projectInfo = {
668
+ projectId: config.projectId,
669
+ projectName: config.project ?? config.projectId,
670
+ environment: config.environment,
671
+ hostname: safeHostname(),
672
+ port: null,
673
+ pid: process.pid,
674
+ runtime: detectRuntime(),
675
+ runtimeVersion: detectRuntimeVersion(),
676
+ nodeVersion: process.version,
677
+ nestjsVersion: detectNestJsVersion(),
678
+ sdkVersion: SDK_VERSION
679
+ };
680
+ import_devtools_core7.devtools.initialize({ config, projectInfo, registerCleanup });
681
+ const adapter = app.getHttpAdapter();
682
+ const httpReady = Boolean(adapter && ["http", "express"].includes(adapter.getType()));
683
+ const http = new HttpInstrumentation({ config, projectInfo });
684
+ registerCleanup(http.attach(app));
685
+ registerCleanup(new ConsoleInstrumentation({ config, projectInfo }).attach());
686
+ registerCleanup(new LoggerInstrumentation({ config, projectInfo }).attach());
687
+ registerCleanup(ExceptionsInstrumentation.attach({ config, projectInfo }));
688
+ registerCleanup(new PerformanceInstrumentation({ config, projectInfo }).attach());
689
+ registerCleanup(new AppExplorer({ config, projectInfo }).attach(app));
690
+ if (config.capture.websockets) {
691
+ registerCleanup(new WebsocketInstrumentation({ config, projectInfo }).attach());
692
+ }
693
+ if (config.capture.database) {
694
+ registerCleanup(new DatabaseInstrumentation({ config, projectInfo }).attach());
695
+ }
696
+ return {
697
+ enabled: true,
698
+ projectId: config.projectId,
699
+ projectName: projectInfo.projectName,
700
+ server: config.server,
701
+ ...httpReady ? {} : { reason: "no-http-adapter" }
702
+ };
703
+ }
704
+ /** Teardown everything (mostly useful in tests). */
705
+ static destroy() {
706
+ for (const cleanup of this.cleanups.splice(0)) {
707
+ try {
708
+ cleanup();
709
+ } catch {
710
+ }
711
+ }
712
+ this.initialized = false;
713
+ import_devtools_core7.devtools.shutdown();
714
+ }
715
+ /** Whether the SDK is currently active in this process. */
716
+ static isActive() {
717
+ return this.initialized;
718
+ }
719
+ };
720
+ var devtools = {
721
+ init: NestDevTools.init.bind(NestDevTools),
722
+ destroy: NestDevTools.destroy.bind(NestDevTools),
723
+ isActive: NestDevTools.isActive.bind(NestDevTools)
724
+ };
725
+ function safeHostname() {
726
+ try {
727
+ return (0, import_os.hostname)();
728
+ } catch {
729
+ return "unknown";
730
+ }
731
+ }
732
+ function detectRuntime() {
733
+ const versions = process.versions;
734
+ if (versions["bun"]) return "bun";
735
+ if (versions["deno"]) return "deno";
736
+ return "node";
737
+ }
738
+ function detectRuntimeVersion() {
739
+ const versions = process.versions;
740
+ return versions["bun"] ?? versions["deno"] ?? process.version;
741
+ }
742
+
743
+ // src/index.ts
744
+ var import_devtools_core8 = require("@angelitosystems/devtools-core");
745
+ // Annotate the CommonJS export names for ESM import in node:
746
+ 0 && (module.exports = {
747
+ NestDevTools,
748
+ captureError,
749
+ devtools,
750
+ requestContext
751
+ });
752
+ //# sourceMappingURL=index.cjs.map