@angelitosystems/nest-devtools 1.0.13 → 1.0.15
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 +178 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +177 -57
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -591,12 +591,28 @@ var PerformanceInstrumentation = class {
|
|
|
591
591
|
};
|
|
592
592
|
|
|
593
593
|
// src/instrumentation/app-explorer.ts
|
|
594
|
+
import { createRequire } from "module";
|
|
595
|
+
var safeRequire = (() => {
|
|
596
|
+
try {
|
|
597
|
+
return createRequire(import.meta.url);
|
|
598
|
+
} catch {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
})();
|
|
602
|
+
var PATH_METADATA = "path";
|
|
603
|
+
var METHOD_METADATA = "method";
|
|
604
|
+
var GUARDS_METADATA = "__guards__";
|
|
605
|
+
var INTERCEPTORS_METADATA = "__interceptors__";
|
|
606
|
+
var PIPES_METADATA = "__pipes__";
|
|
607
|
+
var EXCEPTION_FILTERS_METADATA = "__exceptionFilters__";
|
|
608
|
+
var ROUTE_ARGS_METADATA = "__routeArguments__";
|
|
609
|
+
var BODY_PARAM_TYPE = 3;
|
|
610
|
+
var HTTP_METHOD_NAMES = ["GET", "POST", "PUT", "DELETE", "PATCH", "ALL", "OPTIONS", "HEAD"];
|
|
594
611
|
var AppExplorer = class {
|
|
595
612
|
constructor(ctx) {
|
|
596
613
|
this.ctx = ctx;
|
|
597
614
|
}
|
|
598
615
|
ctx;
|
|
599
|
-
/** Walk the container and emit app.snapshot. Returns a no-op cleanup. */
|
|
600
616
|
attach(app) {
|
|
601
617
|
try {
|
|
602
618
|
const snapshot = this.buildSnapshot(app);
|
|
@@ -608,12 +624,13 @@ var AppExplorer = class {
|
|
|
608
624
|
}
|
|
609
625
|
buildSnapshot(app) {
|
|
610
626
|
const internal = app.container;
|
|
627
|
+
const globals = this.extractGlobals(app);
|
|
611
628
|
const modules = [];
|
|
612
629
|
const modulesMap = internal?.getModules?.();
|
|
613
630
|
if (modulesMap) {
|
|
614
631
|
for (const [id, node] of modulesMap) {
|
|
615
632
|
const moduleName = node.metatype?.name ?? String(id);
|
|
616
|
-
const members = this.extractMembers(node);
|
|
633
|
+
const members = this.extractMembers(node, globals);
|
|
617
634
|
modules.push({
|
|
618
635
|
name: moduleName,
|
|
619
636
|
imports: [],
|
|
@@ -628,23 +645,55 @@ var AppExplorer = class {
|
|
|
628
645
|
projectName: this.ctx.projectInfo.projectName,
|
|
629
646
|
modules,
|
|
630
647
|
nestjsVersion: this.ctx.projectInfo.nestjsVersion,
|
|
631
|
-
capturedAt: Date.now()
|
|
648
|
+
capturedAt: Date.now(),
|
|
649
|
+
globals
|
|
632
650
|
};
|
|
633
651
|
}
|
|
634
|
-
|
|
652
|
+
/** Global prefix, versioning, and APP_GUARD/APP_INTERCEPTOR/APP_FILTER/APP_PIPE providers (from AppModule's `providers` array). */
|
|
653
|
+
extractGlobals(app) {
|
|
654
|
+
const result = { prefix: null, versioning: null, guards: [], interceptors: [], filters: [], pipes: [] };
|
|
655
|
+
try {
|
|
656
|
+
const config = app.config;
|
|
657
|
+
result.prefix = typeof config?.getGlobalPrefix === "function" ? config.getGlobalPrefix() || null : null;
|
|
658
|
+
const versioning = typeof config?.getVersioning === "function" ? config.getVersioning() : null;
|
|
659
|
+
if (versioning) result.versioning = { type: String(versioning.type), defaultVersion: versioning.defaultVersion };
|
|
660
|
+
const internal = app.container;
|
|
661
|
+
const modulesMap = internal?.getModules?.();
|
|
662
|
+
if (modulesMap) {
|
|
663
|
+
for (const node of modulesMap.values()) {
|
|
664
|
+
const providersMap = node.providers;
|
|
665
|
+
if (!(providersMap instanceof Map)) continue;
|
|
666
|
+
for (const wrapper of providersMap.values()) {
|
|
667
|
+
const w = wrapper;
|
|
668
|
+
const name = w?.metatype?.name ?? w?.name;
|
|
669
|
+
if (!name || typeof name !== "string") continue;
|
|
670
|
+
const instance = w?.instance;
|
|
671
|
+
if (!instance) continue;
|
|
672
|
+
if (typeof instance.canActivate === "function" && name.endsWith("Guard")) pushUnique(result.guards, name);
|
|
673
|
+
else if (typeof instance.intercept === "function" && name.endsWith("Interceptor")) pushUnique(result.interceptors, name);
|
|
674
|
+
else if (typeof instance.catch === "function" && name.endsWith("Filter")) pushUnique(result.filters, name);
|
|
675
|
+
else if (typeof instance.transform === "function" && name.endsWith("Pipe")) pushUnique(result.pipes, name);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
} catch {
|
|
680
|
+
}
|
|
681
|
+
return result;
|
|
682
|
+
}
|
|
683
|
+
extractMembers(node, globals) {
|
|
635
684
|
const controllers = [];
|
|
636
685
|
const providers = [];
|
|
637
686
|
const controllersMap = node.controllers;
|
|
638
687
|
if (controllersMap instanceof Map) {
|
|
639
688
|
for (const [id, wrapper] of controllersMap) {
|
|
640
|
-
const member = this.memberFromWrapper(id, wrapper, "controller");
|
|
689
|
+
const member = this.memberFromWrapper(id, wrapper, globals, "controller");
|
|
641
690
|
if (member) controllers.push(member);
|
|
642
691
|
}
|
|
643
692
|
}
|
|
644
693
|
const providersMap = node.providers;
|
|
645
694
|
if (providersMap instanceof Map) {
|
|
646
695
|
for (const [id, wrapper] of providersMap) {
|
|
647
|
-
const member = this.memberFromWrapper(id, wrapper);
|
|
696
|
+
const member = this.memberFromWrapper(id, wrapper, globals);
|
|
648
697
|
if (!member) continue;
|
|
649
698
|
if (wrapper && wrapper.subtype === "httpController") {
|
|
650
699
|
controllers.push(member);
|
|
@@ -655,73 +704,144 @@ var AppExplorer = class {
|
|
|
655
704
|
}
|
|
656
705
|
return { controllers, providers };
|
|
657
706
|
}
|
|
658
|
-
memberFromWrapper(id, wrapper, forcedType) {
|
|
707
|
+
memberFromWrapper(id, wrapper, globals, forcedType) {
|
|
659
708
|
const w = wrapper;
|
|
660
709
|
const name = w?.metatype?.name ?? (typeof id === "string" ? id.replace(/^[A-Z_0-9]+:/, "") : "unknown");
|
|
661
710
|
const type = forcedType ?? classify(name, w?.instance);
|
|
662
|
-
|
|
663
|
-
|
|
711
|
+
if (type !== "controller" || !w?.metatype) {
|
|
712
|
+
return { name, type };
|
|
713
|
+
}
|
|
714
|
+
const { basePath, routes } = this.extractRoutes(w.metatype, globals);
|
|
715
|
+
return {
|
|
716
|
+
name,
|
|
717
|
+
type,
|
|
718
|
+
basePath,
|
|
719
|
+
routeDetails: routes,
|
|
720
|
+
routes: routes.map((r) => `${r.method} ${r.path}`)
|
|
721
|
+
// legacy field, kept for old dashboards
|
|
722
|
+
};
|
|
664
723
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
if (name.endsWith("Gateway")) return "gateway";
|
|
668
|
-
if (name.endsWith("Guard")) return "guard";
|
|
669
|
-
if (name.endsWith("Interceptor")) return "interceptor";
|
|
670
|
-
if (name.endsWith("Pipe")) return "pipe";
|
|
671
|
-
if (name.endsWith("Filter")) return "filter";
|
|
672
|
-
if (instance && typeof instance.canActivate === "function") return "guard";
|
|
673
|
-
if (instance && typeof instance.intercept === "function") return "interceptor";
|
|
674
|
-
if (instance && typeof instance.transform === "function") return "pipe";
|
|
675
|
-
if (instance && typeof instance.catch === "function") return "filter";
|
|
676
|
-
return "provider";
|
|
677
|
-
}
|
|
678
|
-
function extractControllerRoutes(metatype) {
|
|
679
|
-
if (!metatype) return [];
|
|
680
|
-
try {
|
|
681
|
-
const proto = metatype.prototype;
|
|
682
|
-
if (!proto) return [];
|
|
683
|
-
const props = Object.getOwnPropertyNames(proto).filter((name) => name !== "constructor");
|
|
724
|
+
/** Extract every HTTP route of a controller class, with method/path/guards/interceptors/pipes/filters/dto. */
|
|
725
|
+
extractRoutes(controller, globals) {
|
|
684
726
|
const routes = [];
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
const
|
|
690
|
-
|
|
691
|
-
|
|
727
|
+
let basePath = "";
|
|
728
|
+
try {
|
|
729
|
+
const R = Reflect;
|
|
730
|
+
basePath = normalizeSegment(R.getMetadata?.(PATH_METADATA, controller) ?? "");
|
|
731
|
+
const classGuards = readMetaNames(R, GUARDS_METADATA, controller);
|
|
732
|
+
const classInterceptors = readMetaNames(R, INTERCEPTORS_METADATA, controller);
|
|
733
|
+
const classPipes = readMetaNames(R, PIPES_METADATA, controller);
|
|
734
|
+
const classFilters = readMetaNames(R, EXCEPTION_FILTERS_METADATA, controller);
|
|
735
|
+
const proto = controller.prototype;
|
|
736
|
+
if (!proto) return { basePath, routes };
|
|
737
|
+
const version = globals.versioning?.defaultVersion;
|
|
738
|
+
const versionSegment = globals.versioning?.type === "URI" && version && version !== "-1" ? `v${Array.isArray(version) ? version[0] : version}` : "";
|
|
739
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
740
|
+
if (key === "constructor") continue;
|
|
741
|
+
const handler = proto[key];
|
|
742
|
+
if (typeof handler !== "function") continue;
|
|
743
|
+
const httpMethod = R.getMetadata?.(METHOD_METADATA, handler);
|
|
744
|
+
if (httpMethod === void 0) continue;
|
|
745
|
+
const methodPath = normalizeSegment(R.getMetadata?.(PATH_METADATA, handler) ?? "");
|
|
746
|
+
const fullPath = ["/", globals.prefix, versionSegment, basePath, methodPath].filter((segment) => segment && segment !== "/").join("/").replace(/\/+/g, "/");
|
|
747
|
+
const methodGuards = readMetaNames(R, GUARDS_METADATA, handler);
|
|
748
|
+
const methodInterceptors = readMetaNames(R, INTERCEPTORS_METADATA, handler);
|
|
749
|
+
const methodPipes = readMetaNames(R, PIPES_METADATA, handler);
|
|
750
|
+
const methodFilters = readMetaNames(R, EXCEPTION_FILTERS_METADATA, handler);
|
|
751
|
+
routes.push({
|
|
752
|
+
method: HTTP_METHOD_NAMES[httpMethod] ?? "GET",
|
|
753
|
+
path: fullPath.startsWith("/") ? fullPath : `/${fullPath}`,
|
|
754
|
+
handlerName: key,
|
|
755
|
+
guards: dedupe([...classGuards, ...methodGuards]),
|
|
756
|
+
interceptors: dedupe([...classInterceptors, ...methodInterceptors]),
|
|
757
|
+
pipes: dedupe([...classPipes, ...methodPipes]),
|
|
758
|
+
filters: dedupe([...classFilters, ...methodFilters]),
|
|
759
|
+
dto: this.extractBodyDto(R, controller, proto, key)
|
|
760
|
+
});
|
|
692
761
|
}
|
|
762
|
+
} catch {
|
|
693
763
|
}
|
|
694
|
-
return routes;
|
|
695
|
-
} catch {
|
|
696
|
-
return [];
|
|
764
|
+
return { basePath, routes };
|
|
697
765
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
for (const
|
|
705
|
-
|
|
706
|
-
|
|
766
|
+
/** Best-effort: find the @Body() parameter's class and describe its fields via class-validator, if present. */
|
|
767
|
+
extractBodyDto(R, controller, proto, methodName) {
|
|
768
|
+
try {
|
|
769
|
+
const paramTypes = R.getMetadata?.("design:paramtypes", proto, methodName) ?? [];
|
|
770
|
+
const routeArgs = R.getMetadata?.(ROUTE_ARGS_METADATA, controller, methodName) ?? {};
|
|
771
|
+
let bodyIndex = -1;
|
|
772
|
+
for (const key of Object.keys(routeArgs)) {
|
|
773
|
+
const [paramType] = key.split(":");
|
|
774
|
+
if (Number(paramType) === BODY_PARAM_TYPE) {
|
|
775
|
+
bodyIndex = routeArgs[key]?.index ?? -1;
|
|
776
|
+
break;
|
|
707
777
|
}
|
|
708
778
|
}
|
|
779
|
+
if (bodyIndex === -1) return null;
|
|
780
|
+
const dtoClass = paramTypes[bodyIndex];
|
|
781
|
+
if (!dtoClass || !dtoClass.name || ["Object", "String", "Number", "Boolean", "Array"].includes(dtoClass.name)) {
|
|
782
|
+
return null;
|
|
783
|
+
}
|
|
784
|
+
return { name: dtoClass.name, fields: this.describeDtoFields(dtoClass) };
|
|
785
|
+
} catch {
|
|
786
|
+
return null;
|
|
709
787
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
if (
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
788
|
+
}
|
|
789
|
+
/** Reads class-validator's metadata storage if the package is installed in the host app. Falls back to []. */
|
|
790
|
+
describeDtoFields(dtoClass) {
|
|
791
|
+
try {
|
|
792
|
+
if (!safeRequire) return [];
|
|
793
|
+
const cv = safeRequire("class-validator");
|
|
794
|
+
const storage = cv.getMetadataStorage?.();
|
|
795
|
+
const metas = storage?.getTargetValidationMetadatas?.(dtoClass, "", true, false) ?? [];
|
|
796
|
+
const byProperty = /* @__PURE__ */ new Map();
|
|
797
|
+
for (const meta of metas) {
|
|
798
|
+
const entry = byProperty.get(meta.propertyName) ?? { types: [], optional: false };
|
|
799
|
+
if (meta.name === "isOptional") entry.optional = true;
|
|
800
|
+
if (meta.name) entry.types.push(meta.name);
|
|
801
|
+
byProperty.set(meta.propertyName, entry);
|
|
718
802
|
}
|
|
803
|
+
return [...byProperty.entries()].map(([name, info]) => ({
|
|
804
|
+
name,
|
|
805
|
+
type: info.types.find((t) => /^is[A-Z]/.test(t))?.replace(/^is/, "") ?? "unknown",
|
|
806
|
+
optional: info.optional,
|
|
807
|
+
rules: dedupe(info.types)
|
|
808
|
+
}));
|
|
809
|
+
} catch {
|
|
810
|
+
return [];
|
|
719
811
|
}
|
|
720
|
-
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
function readMetaNames(R, key, target) {
|
|
815
|
+
try {
|
|
816
|
+
const value = R.getMetadata?.(key, target);
|
|
817
|
+
if (!Array.isArray(value)) return [];
|
|
818
|
+
return value.map((fn) => fn?.name).filter((name) => Boolean(name));
|
|
721
819
|
} catch {
|
|
722
820
|
return [];
|
|
723
821
|
}
|
|
724
822
|
}
|
|
823
|
+
function normalizeSegment(segment) {
|
|
824
|
+
const s = Array.isArray(segment) ? segment[0] ?? "" : segment;
|
|
825
|
+
return String(s ?? "").replace(/^\/+|\/+$/g, "");
|
|
826
|
+
}
|
|
827
|
+
function dedupe(items) {
|
|
828
|
+
return [...new Set(items)];
|
|
829
|
+
}
|
|
830
|
+
function pushUnique(list, value) {
|
|
831
|
+
if (!list.includes(value)) list.push(value);
|
|
832
|
+
}
|
|
833
|
+
function classify(name, instance) {
|
|
834
|
+
if (name.endsWith("Gateway")) return "gateway";
|
|
835
|
+
if (name.endsWith("Guard")) return "guard";
|
|
836
|
+
if (name.endsWith("Interceptor")) return "interceptor";
|
|
837
|
+
if (name.endsWith("Pipe")) return "pipe";
|
|
838
|
+
if (name.endsWith("Filter")) return "filter";
|
|
839
|
+
if (instance && typeof instance.canActivate === "function") return "guard";
|
|
840
|
+
if (instance && typeof instance.intercept === "function") return "interceptor";
|
|
841
|
+
if (instance && typeof instance.transform === "function") return "pipe";
|
|
842
|
+
if (instance && typeof instance.catch === "function") return "filter";
|
|
843
|
+
return "provider";
|
|
844
|
+
}
|
|
725
845
|
|
|
726
846
|
// src/instrumentation/websockets.ts
|
|
727
847
|
import { requestContext as requestContext5 } from "@angelitosystems/devtools-core";
|
|
@@ -1398,7 +1518,7 @@ function detectNestJsVersion() {
|
|
|
1398
1518
|
}
|
|
1399
1519
|
|
|
1400
1520
|
// src/version.ts
|
|
1401
|
-
var SDK_VERSION = "1.0.
|
|
1521
|
+
var SDK_VERSION = "1.0.15";
|
|
1402
1522
|
|
|
1403
1523
|
// src/banner.ts
|
|
1404
1524
|
var RESET = "\x1B[0m";
|