@angelitosystems/nest-devtools 1.0.7 → 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.cjs CHANGED
@@ -76,25 +76,6 @@ function recordSpan(requestId, layer, label, options) {
76
76
  }
77
77
 
78
78
  // src/instrumentation/http.ts
79
- var REQUEST_HEADERS = [
80
- "content-type",
81
- "content-length",
82
- "accept",
83
- "origin",
84
- "referer",
85
- "user-agent",
86
- "x-forwarded-for",
87
- "x-request-id",
88
- "x-forwarded-proto"
89
- ];
90
- var RESPONSE_HEADERS = [
91
- "content-type",
92
- "content-length",
93
- "location",
94
- "set-cookie",
95
- "x-request-id",
96
- "x-response-time"
97
- ];
98
79
  var WRAPPED = /* @__PURE__ */ Symbol("nest-devtools.wrapped");
99
80
  var HttpInstrumentation = class {
100
81
  constructor(ctx) {
@@ -192,6 +173,7 @@ var HttpInstrumentation = class {
192
173
  };
193
174
  spans.push(middlewareSpan);
194
175
  const capturedBody = this.ctx.config.capture.requests && this.ctx.config.maxPayloadBytes > 0 ? captureRequestPreview(req, this.redactor) : void 0;
176
+ const responseCapture = createResponseCapture(res, this.ctx.config.maxPayloadBytes, this.redactor);
195
177
  const onFinished = () => {
196
178
  const finishedAt = Date.now();
197
179
  middlewareSpan.duration = Math.max(0, finishedAt - middlewareSpan.startedAt);
@@ -205,7 +187,8 @@ var HttpInstrumentation = class {
205
187
  };
206
188
  spans.push(responseSpan);
207
189
  const responseHeaders = this.collectResponseHeaders(res);
208
- const responsePreview = this.ctx.config.capture.requests && this.ctx.config.maxPayloadBytes > 0 ? captureResponsePreview(res, this.redactor) : void 0;
190
+ const responsePreview = responseCapture.read();
191
+ const requestBody = this.ctx.config.capture.requests && this.ctx.config.maxPayloadBytes > 0 ? captureRequestPreview(req, this.redactor) : void 0;
209
192
  const errored = res.statusCode >= 500;
210
193
  const completed = {
211
194
  requestId,
@@ -217,10 +200,11 @@ var HttpInstrumentation = class {
217
200
  duration: finishedAt - startedAt,
218
201
  startedAt,
219
202
  timeline: spans,
203
+ requestHeaders: started.headers,
220
204
  query: parseQuery(rawUrl),
221
205
  headers: responseHeaders,
222
206
  responsePreview,
223
- requestBody: capturedBody,
207
+ requestBody: requestBody ?? capturedBody,
224
208
  errored
225
209
  };
226
210
  emit("request.completed", completed);
@@ -245,8 +229,7 @@ var HttpInstrumentation = class {
245
229
  }
246
230
  collectRequestHeaders(req) {
247
231
  const out = {};
248
- for (const key of REQUEST_HEADERS) {
249
- const value = req.headers[key];
232
+ for (const [key, value] of Object.entries(req.headers)) {
250
233
  if (value === void 0) continue;
251
234
  out[key] = this.redactor.isSensitive(key) ? "[REDACTED]" : asString(value).slice(0, 200);
252
235
  }
@@ -254,12 +237,9 @@ var HttpInstrumentation = class {
254
237
  }
255
238
  collectResponseHeaders(res) {
256
239
  const out = {};
257
- const headers = res._headers;
258
- if (!headers || typeof headers !== "object") return out;
240
+ const headers = typeof res.getHeaders === "function" ? res.getHeaders() : {};
259
241
  for (const rawKey of Object.keys(headers)) {
260
242
  const key = rawKey.toLowerCase();
261
- const ok = RESPONSE_HEADERS.some((h) => h.toLowerCase() === key);
262
- if (!ok) continue;
263
243
  const raw = headers[rawKey] ?? "";
264
244
  const value = String(raw);
265
245
  if (value === "") continue;
@@ -336,16 +316,38 @@ function captureRequestPreview(req, redactor) {
336
316
  return void 0;
337
317
  }
338
318
  }
339
- function captureResponsePreview(res, redactor) {
340
- try {
341
- const resLike = res;
342
- const body = resLike.body;
343
- if (body === void 0 || body === null) return void 0;
344
- if (typeof body === "string") return redactor.redactString(body);
345
- return redactor.serialize(body);
346
- } catch {
347
- return void 0;
348
- }
319
+ function createResponseCapture(res, maxBytes, redactor) {
320
+ if (maxBytes <= 0) return { read: () => void 0 };
321
+ const chunks = [];
322
+ let size = 0;
323
+ const originalWrite = res.write.bind(res);
324
+ const originalEnd = res.end.bind(res);
325
+ const capture2 = (chunk) => {
326
+ if (chunk === void 0 || chunk === null || size >= maxBytes) return;
327
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
328
+ const remaining = maxBytes - size;
329
+ chunks.push(buffer.subarray(0, remaining));
330
+ size += Math.min(buffer.length, remaining);
331
+ };
332
+ res.write = (chunk, ...args) => {
333
+ capture2(chunk);
334
+ return originalWrite(chunk, ...args);
335
+ };
336
+ res.end = (chunk, ...args) => {
337
+ capture2(chunk);
338
+ return originalEnd(chunk, ...args);
339
+ };
340
+ res.once("finish", () => {
341
+ res.write = originalWrite;
342
+ res.end = originalEnd;
343
+ });
344
+ return {
345
+ read: () => {
346
+ if (chunks.length === 0) return void 0;
347
+ const raw = Buffer.concat(chunks).toString("utf8");
348
+ return redactor.redactString(raw);
349
+ }
350
+ };
349
351
  }
350
352
 
351
353
  // src/instrumentation/console.ts
@@ -662,6 +664,13 @@ var AppExplorer = class {
662
664
  extractMembers(node) {
663
665
  const controllers = [];
664
666
  const providers = [];
667
+ const controllersMap = node.controllers;
668
+ if (controllersMap instanceof Map) {
669
+ for (const [id, wrapper] of controllersMap) {
670
+ const member = this.memberFromWrapper(id, wrapper, "controller");
671
+ if (member) controllers.push(member);
672
+ }
673
+ }
665
674
  const providersMap = node.providers;
666
675
  if (providersMap instanceof Map) {
667
676
  for (const [id, wrapper] of providersMap) {
@@ -676,10 +685,10 @@ var AppExplorer = class {
676
685
  }
677
686
  return { controllers, providers };
678
687
  }
679
- memberFromWrapper(id, wrapper) {
688
+ memberFromWrapper(id, wrapper, forcedType) {
680
689
  const w = wrapper;
681
690
  const name = w?.metatype?.name ?? (typeof id === "string" ? id.replace(/^[A-Z_0-9]+:/, "") : "unknown");
682
- const type = classify(name, w?.instance);
691
+ const type = forcedType ?? classify(name, w?.instance);
683
692
  const routes = type === "controller" ? extractControllerRoutes(w?.metatype) : void 0;
684
693
  return { name, type, routes };
685
694
  }
@@ -836,6 +845,7 @@ var WebsocketInstrumentation = class {
836
845
  }
837
846
  if (method === "handleMessage" || method === "handleEvent") {
838
847
  const payload = this.extractPayload(args);
848
+ this.emitMessage("received", instance, args, payload, requestId, started);
839
849
  recordSpan(requestId, "websocket", `ws.receive:${this.estimateEvent(args)}`, {
840
850
  detail: `gateway=${instance.constructor?.name} payload=${this.redactor.serialize(payload)}`
841
851
  });
@@ -844,6 +854,7 @@ var WebsocketInstrumentation = class {
844
854
  const result = await Promise.resolve(original.call(instance, ...args));
845
855
  if (method === "handleMessage" || method === "handleEvent") {
846
856
  const payload = this.extractPayload(args);
857
+ this.emitMessage("sent", instance, args, payload, requestId, started);
847
858
  recordSpan(requestId, "websocket", `ws.send:${this.estimateEvent(args)}`, {
848
859
  detail: `gateway=${instance.constructor?.name} payload=${this.redactor.serialize(payload)}`,
849
860
  status: "ok"
@@ -861,6 +872,21 @@ var WebsocketInstrumentation = class {
861
872
  }
862
873
  };
863
874
  }
875
+ emitMessage(direction, instance, args, payload, requestId, startedAt) {
876
+ const preview = this.redactor.redact(payload);
877
+ const serialized = this.redactor.serialize(payload);
878
+ emit("websocket.message", {
879
+ projectId: this.projectId,
880
+ gateway: instance.constructor?.name ?? this.estimateGatewayName(instance),
881
+ event: this.estimateEvent(args),
882
+ direction,
883
+ payloadSize: Buffer.byteLength(serialized, "utf8"),
884
+ payloadPreview: preview,
885
+ requestId,
886
+ duration: Date.now() - startedAt,
887
+ timestamp: Date.now()
888
+ });
889
+ }
864
890
  extractPayload(args) {
865
891
  for (const arg of args) {
866
892
  if (arg && typeof arg === "object" && !Buffer.isBuffer(arg) && !ArrayBuffer.isView(arg)) {
@@ -938,6 +964,23 @@ var DatabaseInstrumentation = class {
938
964
  if (!app) return false;
939
965
  const dataSource = this.resolveTypeOrmDataSource(app);
940
966
  if (!dataSource) return false;
967
+ if (typeof dataSource.query === "function") {
968
+ const originalQuery = dataSource.query.bind(dataSource);
969
+ dataSource.__nestDevToolsOriginalQuery = originalQuery;
970
+ dataSource.query = async (query, parameters) => {
971
+ const started = Date.now();
972
+ this.startQuery(String(query), parameters ?? []);
973
+ try {
974
+ const result = await originalQuery(query, parameters);
975
+ this.endQuery(String(query), parameters ?? [], Date.now() - started);
976
+ return result;
977
+ } catch (error) {
978
+ this.endQuery(String(query), parameters ?? [], Date.now() - started, error);
979
+ throw error;
980
+ }
981
+ };
982
+ return true;
983
+ }
941
984
  const listener = {
942
985
  beforeQuery: (query, parameters) => {
943
986
  this.startQuery(query, parameters);
@@ -990,6 +1033,22 @@ var DatabaseInstrumentation = class {
990
1033
  if (!app) return false;
991
1034
  const prisma = this.resolvePrismaClient(app);
992
1035
  if (!prisma) return false;
1036
+ if (typeof prisma.$use === "function") {
1037
+ prisma.$use(async (params, next) => {
1038
+ const label = `${params.model ?? "prisma"}.${params.action ?? "query"}`;
1039
+ const started = Date.now();
1040
+ this.startQuery(label, [params.args]);
1041
+ try {
1042
+ const result = await next(params);
1043
+ this.endQuery(label, [params.args], Date.now() - started);
1044
+ return result;
1045
+ } catch (error) {
1046
+ this.endQuery(label, [params.args], Date.now() - started, error);
1047
+ throw error;
1048
+ }
1049
+ });
1050
+ return true;
1051
+ }
993
1052
  const original = prisma.$queryRaw ?? prisma.$executeRaw ?? null;
994
1053
  if (!original) return false;
995
1054
  const wrapped = (query, parameters) => {
@@ -1133,8 +1192,8 @@ var DatabaseInstrumentation = class {
1133
1192
  }
1134
1193
  resolveTypeOrmDataSource(app) {
1135
1194
  try {
1136
- const dataSource = app.get(require("@nestjs/typeorm").TypeOrmModule)?.options?.DataSource ?? null;
1137
- if (dataSource && typeof dataSource.query === "function") return dataSource;
1195
+ const dataSource = this.findProvider(app, (value) => typeof value?.query === "function" && typeof value?.manager === "object");
1196
+ if (dataSource) return dataSource;
1138
1197
  return null;
1139
1198
  } catch {
1140
1199
  return null;
@@ -1142,7 +1201,7 @@ var DatabaseInstrumentation = class {
1142
1201
  }
1143
1202
  resolvePrismaClient(app) {
1144
1203
  try {
1145
- const prisma = app.get("PrismaService") ?? app.get("PrismaClient") ?? app.get("prisma") ?? null;
1204
+ const prisma = this.safeGet(app, "PrismaService") ?? this.safeGet(app, "PrismaClient") ?? this.safeGet(app, "prisma") ?? this.findProvider(app, (value) => typeof value?.$queryRaw === "function");
1146
1205
  if (prisma && typeof prisma.$queryRaw === "function") return prisma;
1147
1206
  return null;
1148
1207
  } catch {
@@ -1151,13 +1210,34 @@ var DatabaseInstrumentation = class {
1151
1210
  }
1152
1211
  resolveMongoose(app) {
1153
1212
  try {
1154
- const mongoose = app.get("MongooseService") ?? app.get("Mongoose") ?? app.get("mongoose") ?? null;
1213
+ const mongoose = this.safeGet(app, "MongooseService") ?? this.safeGet(app, "Mongoose") ?? this.safeGet(app, "mongoose") ?? this.findProvider(app, (value) => typeof value?.plugin === "function");
1155
1214
  if (mongoose && typeof mongoose.plugin === "function") return mongoose;
1156
1215
  return null;
1157
1216
  } catch {
1158
1217
  return null;
1159
1218
  }
1160
1219
  }
1220
+ safeGet(app, token) {
1221
+ try {
1222
+ return app?.get?.(token, { strict: false }) ?? null;
1223
+ } catch {
1224
+ return null;
1225
+ }
1226
+ }
1227
+ findProvider(app, predicate) {
1228
+ try {
1229
+ const modules = app?.container?.getModules?.() ?? /* @__PURE__ */ new Map();
1230
+ for (const module2 of modules.values()) {
1231
+ for (const wrapper of module2.providers?.values?.() ?? []) {
1232
+ const instance = wrapper?.instance;
1233
+ if (instance && predicate(instance)) return instance;
1234
+ }
1235
+ }
1236
+ return null;
1237
+ } catch {
1238
+ return null;
1239
+ }
1240
+ }
1161
1241
  };
1162
1242
 
1163
1243
  // src/instrumentation/queue.ts
@@ -1330,7 +1410,7 @@ function detectNestJsVersion() {
1330
1410
  }
1331
1411
 
1332
1412
  // src/version.ts
1333
- var SDK_VERSION = "0.1.0";
1413
+ var SDK_VERSION = "1.0.9";
1334
1414
 
1335
1415
  // src/banner.ts
1336
1416
  var RESET = "\x1B[0m";