@angelitosystems/nest-devtools 1.0.8 → 1.0.9

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 CHANGED
@@ -1,10 +1,3 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/sdk.ts
9
2
  import { hostname } from "os";
10
3
  import { devtools as coreDevtools2, resolveConfig } from "@angelitosystems/devtools-core";
@@ -53,25 +46,6 @@ function recordSpan(requestId, layer, label, options) {
53
46
  }
54
47
 
55
48
  // src/instrumentation/http.ts
56
- var REQUEST_HEADERS = [
57
- "content-type",
58
- "content-length",
59
- "accept",
60
- "origin",
61
- "referer",
62
- "user-agent",
63
- "x-forwarded-for",
64
- "x-request-id",
65
- "x-forwarded-proto"
66
- ];
67
- var RESPONSE_HEADERS = [
68
- "content-type",
69
- "content-length",
70
- "location",
71
- "set-cookie",
72
- "x-request-id",
73
- "x-response-time"
74
- ];
75
49
  var WRAPPED = /* @__PURE__ */ Symbol("nest-devtools.wrapped");
76
50
  var HttpInstrumentation = class {
77
51
  constructor(ctx) {
@@ -169,6 +143,7 @@ var HttpInstrumentation = class {
169
143
  };
170
144
  spans.push(middlewareSpan);
171
145
  const capturedBody = this.ctx.config.capture.requests && this.ctx.config.maxPayloadBytes > 0 ? captureRequestPreview(req, this.redactor) : void 0;
146
+ const responseCapture = createResponseCapture(res, this.ctx.config.maxPayloadBytes, this.redactor);
172
147
  const onFinished = () => {
173
148
  const finishedAt = Date.now();
174
149
  middlewareSpan.duration = Math.max(0, finishedAt - middlewareSpan.startedAt);
@@ -182,7 +157,8 @@ var HttpInstrumentation = class {
182
157
  };
183
158
  spans.push(responseSpan);
184
159
  const responseHeaders = this.collectResponseHeaders(res);
185
- const responsePreview = this.ctx.config.capture.requests && this.ctx.config.maxPayloadBytes > 0 ? captureResponsePreview(res, this.redactor) : void 0;
160
+ const responsePreview = responseCapture.read();
161
+ const requestBody = this.ctx.config.capture.requests && this.ctx.config.maxPayloadBytes > 0 ? captureRequestPreview(req, this.redactor) : void 0;
186
162
  const errored = res.statusCode >= 500;
187
163
  const completed = {
188
164
  requestId,
@@ -194,10 +170,11 @@ var HttpInstrumentation = class {
194
170
  duration: finishedAt - startedAt,
195
171
  startedAt,
196
172
  timeline: spans,
173
+ requestHeaders: started.headers,
197
174
  query: parseQuery(rawUrl),
198
175
  headers: responseHeaders,
199
176
  responsePreview,
200
- requestBody: capturedBody,
177
+ requestBody: requestBody ?? capturedBody,
201
178
  errored
202
179
  };
203
180
  emit("request.completed", completed);
@@ -222,8 +199,7 @@ var HttpInstrumentation = class {
222
199
  }
223
200
  collectRequestHeaders(req) {
224
201
  const out = {};
225
- for (const key of REQUEST_HEADERS) {
226
- const value = req.headers[key];
202
+ for (const [key, value] of Object.entries(req.headers)) {
227
203
  if (value === void 0) continue;
228
204
  out[key] = this.redactor.isSensitive(key) ? "[REDACTED]" : asString(value).slice(0, 200);
229
205
  }
@@ -231,12 +207,9 @@ var HttpInstrumentation = class {
231
207
  }
232
208
  collectResponseHeaders(res) {
233
209
  const out = {};
234
- const headers = res._headers;
235
- if (!headers || typeof headers !== "object") return out;
210
+ const headers = typeof res.getHeaders === "function" ? res.getHeaders() : {};
236
211
  for (const rawKey of Object.keys(headers)) {
237
212
  const key = rawKey.toLowerCase();
238
- const ok = RESPONSE_HEADERS.some((h) => h.toLowerCase() === key);
239
- if (!ok) continue;
240
213
  const raw = headers[rawKey] ?? "";
241
214
  const value = String(raw);
242
215
  if (value === "") continue;
@@ -313,16 +286,38 @@ function captureRequestPreview(req, redactor) {
313
286
  return void 0;
314
287
  }
315
288
  }
316
- function captureResponsePreview(res, redactor) {
317
- try {
318
- const resLike = res;
319
- const body = resLike.body;
320
- if (body === void 0 || body === null) return void 0;
321
- if (typeof body === "string") return redactor.redactString(body);
322
- return redactor.serialize(body);
323
- } catch {
324
- return void 0;
325
- }
289
+ function createResponseCapture(res, maxBytes, redactor) {
290
+ if (maxBytes <= 0) return { read: () => void 0 };
291
+ const chunks = [];
292
+ let size = 0;
293
+ const originalWrite = res.write.bind(res);
294
+ const originalEnd = res.end.bind(res);
295
+ const capture2 = (chunk) => {
296
+ if (chunk === void 0 || chunk === null || size >= maxBytes) return;
297
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
298
+ const remaining = maxBytes - size;
299
+ chunks.push(buffer.subarray(0, remaining));
300
+ size += Math.min(buffer.length, remaining);
301
+ };
302
+ res.write = (chunk, ...args) => {
303
+ capture2(chunk);
304
+ return originalWrite(chunk, ...args);
305
+ };
306
+ res.end = (chunk, ...args) => {
307
+ capture2(chunk);
308
+ return originalEnd(chunk, ...args);
309
+ };
310
+ res.once("finish", () => {
311
+ res.write = originalWrite;
312
+ res.end = originalEnd;
313
+ });
314
+ return {
315
+ read: () => {
316
+ if (chunks.length === 0) return void 0;
317
+ const raw = Buffer.concat(chunks).toString("utf8");
318
+ return redactor.redactString(raw);
319
+ }
320
+ };
326
321
  }
327
322
 
328
323
  // src/instrumentation/console.ts
@@ -639,6 +634,13 @@ var AppExplorer = class {
639
634
  extractMembers(node) {
640
635
  const controllers = [];
641
636
  const providers = [];
637
+ const controllersMap = node.controllers;
638
+ if (controllersMap instanceof Map) {
639
+ for (const [id, wrapper] of controllersMap) {
640
+ const member = this.memberFromWrapper(id, wrapper, "controller");
641
+ if (member) controllers.push(member);
642
+ }
643
+ }
642
644
  const providersMap = node.providers;
643
645
  if (providersMap instanceof Map) {
644
646
  for (const [id, wrapper] of providersMap) {
@@ -653,10 +655,10 @@ var AppExplorer = class {
653
655
  }
654
656
  return { controllers, providers };
655
657
  }
656
- memberFromWrapper(id, wrapper) {
658
+ memberFromWrapper(id, wrapper, forcedType) {
657
659
  const w = wrapper;
658
660
  const name = w?.metatype?.name ?? (typeof id === "string" ? id.replace(/^[A-Z_0-9]+:/, "") : "unknown");
659
- const type = classify(name, w?.instance);
661
+ const type = forcedType ?? classify(name, w?.instance);
660
662
  const routes = type === "controller" ? extractControllerRoutes(w?.metatype) : void 0;
661
663
  return { name, type, routes };
662
664
  }
@@ -813,6 +815,7 @@ var WebsocketInstrumentation = class {
813
815
  }
814
816
  if (method === "handleMessage" || method === "handleEvent") {
815
817
  const payload = this.extractPayload(args);
818
+ this.emitMessage("received", instance, args, payload, requestId, started);
816
819
  recordSpan(requestId, "websocket", `ws.receive:${this.estimateEvent(args)}`, {
817
820
  detail: `gateway=${instance.constructor?.name} payload=${this.redactor.serialize(payload)}`
818
821
  });
@@ -821,6 +824,7 @@ var WebsocketInstrumentation = class {
821
824
  const result = await Promise.resolve(original.call(instance, ...args));
822
825
  if (method === "handleMessage" || method === "handleEvent") {
823
826
  const payload = this.extractPayload(args);
827
+ this.emitMessage("sent", instance, args, payload, requestId, started);
824
828
  recordSpan(requestId, "websocket", `ws.send:${this.estimateEvent(args)}`, {
825
829
  detail: `gateway=${instance.constructor?.name} payload=${this.redactor.serialize(payload)}`,
826
830
  status: "ok"
@@ -838,6 +842,21 @@ var WebsocketInstrumentation = class {
838
842
  }
839
843
  };
840
844
  }
845
+ emitMessage(direction, instance, args, payload, requestId, startedAt) {
846
+ const preview = this.redactor.redact(payload);
847
+ const serialized = this.redactor.serialize(payload);
848
+ emit("websocket.message", {
849
+ projectId: this.projectId,
850
+ gateway: instance.constructor?.name ?? this.estimateGatewayName(instance),
851
+ event: this.estimateEvent(args),
852
+ direction,
853
+ payloadSize: Buffer.byteLength(serialized, "utf8"),
854
+ payloadPreview: preview,
855
+ requestId,
856
+ duration: Date.now() - startedAt,
857
+ timestamp: Date.now()
858
+ });
859
+ }
841
860
  extractPayload(args) {
842
861
  for (const arg of args) {
843
862
  if (arg && typeof arg === "object" && !Buffer.isBuffer(arg) && !ArrayBuffer.isView(arg)) {
@@ -915,6 +934,23 @@ var DatabaseInstrumentation = class {
915
934
  if (!app) return false;
916
935
  const dataSource = this.resolveTypeOrmDataSource(app);
917
936
  if (!dataSource) return false;
937
+ if (typeof dataSource.query === "function") {
938
+ const originalQuery = dataSource.query.bind(dataSource);
939
+ dataSource.__nestDevToolsOriginalQuery = originalQuery;
940
+ dataSource.query = async (query, parameters) => {
941
+ const started = Date.now();
942
+ this.startQuery(String(query), parameters ?? []);
943
+ try {
944
+ const result = await originalQuery(query, parameters);
945
+ this.endQuery(String(query), parameters ?? [], Date.now() - started);
946
+ return result;
947
+ } catch (error) {
948
+ this.endQuery(String(query), parameters ?? [], Date.now() - started, error);
949
+ throw error;
950
+ }
951
+ };
952
+ return true;
953
+ }
918
954
  const listener = {
919
955
  beforeQuery: (query, parameters) => {
920
956
  this.startQuery(query, parameters);
@@ -967,6 +1003,22 @@ var DatabaseInstrumentation = class {
967
1003
  if (!app) return false;
968
1004
  const prisma = this.resolvePrismaClient(app);
969
1005
  if (!prisma) return false;
1006
+ if (typeof prisma.$use === "function") {
1007
+ prisma.$use(async (params, next) => {
1008
+ const label = `${params.model ?? "prisma"}.${params.action ?? "query"}`;
1009
+ const started = Date.now();
1010
+ this.startQuery(label, [params.args]);
1011
+ try {
1012
+ const result = await next(params);
1013
+ this.endQuery(label, [params.args], Date.now() - started);
1014
+ return result;
1015
+ } catch (error) {
1016
+ this.endQuery(label, [params.args], Date.now() - started, error);
1017
+ throw error;
1018
+ }
1019
+ });
1020
+ return true;
1021
+ }
970
1022
  const original = prisma.$queryRaw ?? prisma.$executeRaw ?? null;
971
1023
  if (!original) return false;
972
1024
  const wrapped = (query, parameters) => {
@@ -1110,8 +1162,8 @@ var DatabaseInstrumentation = class {
1110
1162
  }
1111
1163
  resolveTypeOrmDataSource(app) {
1112
1164
  try {
1113
- const dataSource = app.get(__require("@nestjs/typeorm").TypeOrmModule)?.options?.DataSource ?? null;
1114
- if (dataSource && typeof dataSource.query === "function") return dataSource;
1165
+ const dataSource = this.findProvider(app, (value) => typeof value?.query === "function" && typeof value?.manager === "object");
1166
+ if (dataSource) return dataSource;
1115
1167
  return null;
1116
1168
  } catch {
1117
1169
  return null;
@@ -1119,7 +1171,7 @@ var DatabaseInstrumentation = class {
1119
1171
  }
1120
1172
  resolvePrismaClient(app) {
1121
1173
  try {
1122
- const prisma = app.get("PrismaService") ?? app.get("PrismaClient") ?? app.get("prisma") ?? null;
1174
+ const prisma = this.safeGet(app, "PrismaService") ?? this.safeGet(app, "PrismaClient") ?? this.safeGet(app, "prisma") ?? this.findProvider(app, (value) => typeof value?.$queryRaw === "function");
1123
1175
  if (prisma && typeof prisma.$queryRaw === "function") return prisma;
1124
1176
  return null;
1125
1177
  } catch {
@@ -1128,13 +1180,34 @@ var DatabaseInstrumentation = class {
1128
1180
  }
1129
1181
  resolveMongoose(app) {
1130
1182
  try {
1131
- const mongoose = app.get("MongooseService") ?? app.get("Mongoose") ?? app.get("mongoose") ?? null;
1183
+ const mongoose = this.safeGet(app, "MongooseService") ?? this.safeGet(app, "Mongoose") ?? this.safeGet(app, "mongoose") ?? this.findProvider(app, (value) => typeof value?.plugin === "function");
1132
1184
  if (mongoose && typeof mongoose.plugin === "function") return mongoose;
1133
1185
  return null;
1134
1186
  } catch {
1135
1187
  return null;
1136
1188
  }
1137
1189
  }
1190
+ safeGet(app, token) {
1191
+ try {
1192
+ return app?.get?.(token, { strict: false }) ?? null;
1193
+ } catch {
1194
+ return null;
1195
+ }
1196
+ }
1197
+ findProvider(app, predicate) {
1198
+ try {
1199
+ const modules = app?.container?.getModules?.() ?? /* @__PURE__ */ new Map();
1200
+ for (const module of modules.values()) {
1201
+ for (const wrapper of module.providers?.values?.() ?? []) {
1202
+ const instance = wrapper?.instance;
1203
+ if (instance && predicate(instance)) return instance;
1204
+ }
1205
+ }
1206
+ return null;
1207
+ } catch {
1208
+ return null;
1209
+ }
1210
+ }
1138
1211
  };
1139
1212
 
1140
1213
  // src/instrumentation/queue.ts
@@ -1307,7 +1380,7 @@ function detectNestJsVersion() {
1307
1380
  }
1308
1381
 
1309
1382
  // src/version.ts
1310
- var SDK_VERSION = "0.1.0";
1383
+ var SDK_VERSION = "1.0.9";
1311
1384
 
1312
1385
  // src/banner.ts
1313
1386
  var RESET = "\x1B[0m";