import "react/jsx-runtime"; //#region src/common/rule-scope.d.ts type RuleScope = "file" | "project" | "schema"; //#endregion //#region src/common/diagnostic.d.ts type Severity = "error" | "warning" | "info"; /** * Where a diagnostic is allowed to appear. A rule can be reported without * moving the score or failing a build. */ type DiagnosticSurface = "cli" | "prComment" | "score" | "ciFailure"; type Category = "security" | "performance" | "correctness" | "architecture" | "schema"; interface SourceLine { line: number; text: string; } interface BaseDiagnostic { category: Category; filePath: string; help: string; message: string; rule: string; scope?: RuleScope; severity: Severity; /** Absent means every surface. */ surfaces?: DiagnosticSurface[]; /** The emitting rule's `meta.tags`, when it declares any. */ tags?: string[]; } interface CodeDiagnostic extends BaseDiagnostic { column: number; line: number; sourceLines?: SourceLine[]; } interface SchemaDiagnostic extends BaseDiagnostic { entity: string; schemaColumn?: string; } type Diagnostic = CodeDiagnostic | SchemaDiagnostic; //#endregion //#region src/common/endpoint.d.ts /** * Classifies a dependency's role in the NestJS application. */ type DependencyType = "service" | "repository" | "guard" | "interceptor" | "pipe" | "filter" | "gateway" | "step" | "throw" | "unknown"; interface StepStatement { assignedTo: string | null; text: string; } /** * Merged guard-throw info attached to a call node when the return value * is immediately null-checked and throws an exception. */ interface GuardThrow { branchKind: string | null; callSiteLine: number; className: string; conditionText: string | null; message: string | null; } /** * A per-method dependency node. Each method call becomes its own node * so that call order and conditionality are visible in the graph. */ interface MethodDependencyNode { /** Variable name the return value is assigned to (e.g. "existing" from `const existing = ...`) */ assignedTo: string | null; /** Shared ID for mutually exclusive branches from the same conditional (e.g., "L358") */ branchGroupId: string | null; /** Branch type: "if" | "else-if" | "else" | "case" | "default" | "catch" | "ternary-true" | "ternary-false" */ branchKind: string | null; /** Line where the call to this dependency is made in the parent method */ callSiteLine: number; className: string; /** Leading comment above the call site (e.g. "Verify pool belongs to organization") */ comment: string | null; conditional: boolean; /** Condition expression text (e.g., "!owner"), null if unconditional */ conditionText: string | null; dependencies: MethodDependencyNode[]; /** Last line of the method declaration (for full-function highlighting) */ endLine: number; /** Set when this class's subtree was already expanded at an earlier call site */ expandedElsewhere?: true; filePath: string; /** Merged guard-throw for call nodes (fetch + null-check + throw pattern) */ guardThrow: GuardThrow | null; /** Iteration context: "loop" | "callback" | "concurrent" | null */ iterationKind: "loop" | "callback" | "concurrent" | null; /** Short label for the construct: "map" | "forEach" | "for-of" | "all" | etc. */ iterationLabel: string | null; line: number; methodName: string | null; order: number; /** Method parameter names and types (from TS signature) */ parameters: MethodParameterInfo[]; /** Return type from TS method signature (unwrapped from Promise/Observable) */ returnType: string | null; /** Inline logic statements for step nodes (only populated when type === "step") */ stepStatements: StepStatement[]; /** Exception message for standalone throw nodes */ throwMessage: string | null; totalMethods: number; type: DependencyType; } interface MethodParameterInfo { name: string; type: string | null; } interface ApiBodyInfo { description: string | null; type: string | null; } interface ApiParamInfo { description: string | null; name: string; required: boolean; type: string | null; } interface ApiResponseInfo { description: string | null; status: number; type: string | null; } interface SwaggerMetadata { body: ApiBodyInfo | null; description: string | null; params: ApiParamInfo[]; queryParams: ApiParamInfo[]; responses: ApiResponseInfo[]; summary: string | null; } /** * Represents a single HTTP endpoint in a NestJS controller. * Contains a per-method dependency tree. */ interface EndpointNode { controllerClass: string; dependencies: MethodDependencyNode[]; /** Last line of the handler method (for full-function highlighting) */ endLine: number; filePath: string; handlerMethod: string; httpMethod: string; line: number; /** Return type from TS method signature (unwrapped from Promise/Observable) */ returnType: string | null; routePath: string; /** Swagger/OpenAPI metadata, null when no swagger decorators present */ swagger: SwaggerMetadata | null; /** Set when the trace hit MAX_DEPENDENCY_NODES, so `dependencies` is incomplete */ truncated?: true; } /** * Complete endpoint dependency graph for a NestJS project. * JSON-safe — contains no Maps or AST references. */ interface EndpointGraph { endpoints: EndpointNode[]; } //#endregion //#region src/common/schema.d.ts interface SchemaColumn { defaultValue?: string; hasIndex?: boolean; isGenerated: boolean; isNullable: boolean; isPrimary: boolean; isUnique: boolean; name: string; type: string; } interface SchemaRelation { fromEntity: string; isNullable: boolean; onDelete?: string; propertyName: string; toEntity: string; type: "one-to-one" | "one-to-many" | "many-to-one" | "many-to-many"; } /** * JSON-safe version of SchemaGraph for HTML reports and API responses. * Entities are flattened to an array since Maps are not serializable. */ interface SerializedSchemaGraph { entities: SerializedSchemaEntity[]; orm: string; relations: SchemaRelation[]; } /** * JSON-safe version of SchemaEntity (omits indexes since they are * only needed during rule analysis, not in serialized output). */ interface SerializedSchemaEntity { columns: SchemaColumn[]; filePath: string; name: string; relations: SchemaRelation[]; tableName: string; } //#endregion //#region src/common/scope.d.ts /** * How much of a scan gets reported: everything, the changed files, the changed * lines, or what the change introduced. Every mode analyses the whole project. */ type ScopeMode = "full" | "files" | "lines" | "changed"; /** Scope metadata attached to a result so consumers can see what was reported. */ interface ScopeInfo { /** Present in `changed` mode: whether the base revision could be scanned. */ baselineAvailable?: boolean; baseRef?: string; /** Files the scope narrowed to, not the change's total file count. */ changedFiles?: number; /** Files the change touched before filtering, when the caller knows it. */ changedFilesTotal?: number; /** Set when the requested mode could not be honoured. */ degradedFrom?: ScopeMode; /** Present in `changed` mode: findings the change resolved. */ fixed?: number; mode: ScopeMode; } //#endregion //#region src/common/result.d.ts interface Score { label: string; value: number; } interface ProjectInfo { fileCount: number; framework: "express" | "fastify" | null; moduleCount: number; name: string; nestVersion: string | null; orm: string | null; } interface DiagnoseSummary { byCategory: Record; errors: number; info: number; total: number; warnings: number; } interface RuleErrorInfo { error: string; ruleId: string; } //#endregion //#region src/common/share.d.ts /** Version of the shared payload shape, stamped on every shared file. */ declare const SHARED_REPORT_VERSION = 1; /** A section the share flow can offer, with the count shown beside it. */ interface ShareSection { count: number; id: string; label: string; } interface SharedEndpoint { controllerClass: string; handlerMethod: string; httpMethod: string; routePath: string; } /** One findings category's slice: its diagnostics and its counts alone. */ interface ShareCategorySlice { findings: CodeDiagnostic[]; schemaIssues: SchemaDiagnostic[]; summary: DiagnoseSummary; } /** The module graph as a share exports it: no timings, relative paths. */ interface SharedModules { bootstrapRoots?: string[]; circularDeps: string[][]; edges: Array<{ from: string; to: string; }>; modules: Array & { filePath: string; }>; projects: string[]; } interface SharedSchema { entities: Array & { filePath: string; }>; orm: string; relations: SchemaRelation[]; } /** * Everything the report page needs to assemble a shared payload without * deciding anything about its shape: the sections, each section's slice, * and the payload constants. */ interface ShareManifest { endpoints?: SharedEndpoint[]; filename: string; findingsByCategory: Partial>; modules?: SharedModules; /** Offered to the share when the score section is picked. */ project: ProjectInfo; schema?: SharedSchema; /** What the scan's diagnostics covered, when anything was narrowed. */ scope?: ScopeInfo; score: Score; sections: ShareSection[]; version: number; } /** The assembled shareable document, whichever surface produced it. */ interface SharedReport { endpoints?: SharedEndpoint[]; findings: CodeDiagnostic[]; generatedAt: string; generator: { name: "nestjs-doctor"; version: string; }; includeCode: boolean; modules?: SharedModules; /** Present only when the score section is shared. */ project?: ProjectInfo; schema?: SharedSchema; schemaIssues: SchemaDiagnostic[]; /** Present when the scan ran narrowed, so a thin share reads as such. */ scope?: ScopeInfo; /** Present only when the score section is shared. */ score?: Score; sections: string[]; summary: DiagnoseSummary; version: number; } //#endregion //#region src/common/timings.d.ts /** One class's construction time during a captured bootstrap, in milliseconds. */ interface ClassTiming { id: string; initTime: number; name: string; type: string; } /** One lifecycle hook's total duration for one class, in milliseconds. */ interface HookTiming { count?: number; hook: string; ms: number; /** Offset from bootstrap start, when captured and the hook ran once. */ startMs?: number; } /** Cumulative milliseconds from bootstrap start to the end of each phase. */ interface BootPhases { createMs?: number; initMs?: number; moduleInitMs?: number; } /** One class in the boot trace: its timing plus the classes it injects. */ interface TraceNode { deps: string[]; hooks?: HookTiming[]; initTime: number; name: string; type: string; } //#endregion //#region src/common/artifact.d.ts /** * Version of the report artifact shape. Bump when a field changes meaning; * additive fields do not bump. Consumers narrow on this literal after * checking it. */ declare const REPORT_ARTIFACT_VERSION = 1; /** A rule's bad/good example pair, keyed by rule id. */ type RuleExampleMap = Record; /** A provider as any UI needs it: no ts-morph node, safe to serialise. */ interface ReportProvider { dependencies: string[]; filePath: string; /** Owning module, prefixed with the sub-project in a monorepo. */ module?: string; name: string; project?: string; publicMethodCount: number; scope?: "request" | "transient"; } interface SerializedModuleNode { controllers: string[]; dynamicImports?: Record; exports: string[]; filePath: string; hookTimings?: HookTiming[]; imports: string[]; initTimings?: ClassTiming[]; isGlobal?: boolean; line?: number; name: string; project?: string; providers: string[]; providerTokens?: string[]; } interface SerializedModuleGraph { bootstrapRoots?: string[]; circularDepRecommendations: Record; circularDeps: string[][]; edges: Array<{ from: string; to: string; }>; modules: SerializedModuleNode[]; phases?: BootPhases; projects: string[]; startupMs?: number; timingsAvailable?: boolean; timingsTrace?: Record; } /** * Everything an interactive report needs, as one plain-JSON document. * The engine produces it; HTML, `--format report-json`, and any future UI * consume the same bytes. */ interface ReportArtifact { diagnostics: Diagnostic[]; elapsedMs: number; endpoints: EndpointGraph; examples: RuleExampleMap; generatedAt: string; generator: { name: "nestjs-doctor"; version: string; }; graph: SerializedModuleGraph; monorepo: boolean; project: ProjectInfo; providers: ReportProvider[]; ruleErrors: RuleErrorInfo[]; schema: SerializedSchemaGraph; schemaVersion: typeof REPORT_ARTIFACT_VERSION; /** What `diagnostics` covers. Absent when nothing was narrowed. */ scope?: ScopeInfo; score: Score; /** Precomputed share slices, so the page merges instead of rebuilding. */ share: ShareManifest; /** Full source text keyed by absolute posix path. */ sources: Record; summary: DiagnoseSummary; } //#endregion //#region src/report/shared-view.d.ts type ParsedReportFile = { kind: "artifact"; artifact: ReportArtifact; } | { kind: "shared"; shared: SharedReport; } | { kind: "error"; error: string; }; /** Classifies a report-json artifact vs a shared file by its version keys. */ declare function parseReportFile(text: string): ParsedReportFile; /** Expands a shared file into the artifact shape the report app renders. */ declare function sharedReportToArtifact(shared: SharedReport): ReportArtifact; /** Tabs a shared file cannot fill; schema and endpoints hide on their own. */ declare function sharedHiddenTabs(shared: SharedReport): string[]; /** First tab that has something to show, mirroring the tab bar's hiding. */ declare function initialTab(artifact: ReportArtifact, hiddenTabs: string[]): string; //#endregion //#region src/report/ui/app/templates/diagnosis.d.ts interface DiagnosisCallbacks { setDiagnosisBadge: (withNotScored: boolean) => void; } //#endregion //#region src/report/ui/app/entry.d.ts /** Host-supplied tweaks for embedding the report outside the CLI page. */ interface ChromeOptions { hiddenTabs?: string[]; hideShare?: boolean; onLoadAnother?: () => void; } declare function renderSummary(report: ReportArtifact): void; declare function renderDiagnosis(report: ReportArtifact, callbacks: DiagnosisCallbacks): void; declare function renderEndpoints(report: ReportArtifact): void; declare function resizeEndpoints(): void; declare function renderSchema(report: ReportArtifact): void; declare function renderModules(report: ReportArtifact): void; declare function resizeModules(): void; declare function renderBoot(report: ReportArtifact): void; declare function focusBootTrace(className?: string): void; declare function openModule(name: string): void; declare function renderLab(report: ReportArtifact): void; declare function labOpened(): void; declare function renderChrome(report: ReportArtifact, options?: ChromeOptions): void; declare function unmountAll(): void; declare function setActiveTab(name: string): void; declare function setDiagnosisBadge(withNotScored: boolean): void; //#endregion //#region src/report/ui/html.d.ts declare function getReportHtml(): string; //#endregion //#region src/report/ui/styles.d.ts declare function getReportStyles(): string; //#endregion export { ChromeOptions, type ParsedReportFile, REPORT_ARTIFACT_VERSION, type ReportArtifact, SHARED_REPORT_VERSION, type SharedReport, focusBootTrace, getReportHtml, getReportStyles, initialTab, labOpened, openModule, parseReportFile, renderBoot, renderChrome, renderDiagnosis, renderEndpoints, renderLab, renderModules, renderSchema, renderSummary, resizeEndpoints, resizeModules, setActiveTab, setDiagnosisBadge, sharedHiddenTabs, sharedReportToArtifact, unmountAll };