@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.cjs +124 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +124 -51
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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 =
|
|
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
|
|
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.
|
|
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
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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 =
|
|
1137
|
-
if (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 =
|
|
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 =
|
|
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 = "
|
|
1413
|
+
var SDK_VERSION = "1.0.9";
|
|
1334
1414
|
|
|
1335
1415
|
// src/banner.ts
|
|
1336
1416
|
var RESET = "\x1B[0m";
|