@codesentinel/codesentinel 0.1.1 → 1.0.1
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/LICENSE +21 -0
- package/README.md +102 -0
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/package.json +18 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aleix Alonso
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# CodeSentinel
|
|
2
|
+
|
|
3
|
+
CodeSentinel is a structural and evolutionary risk analysis engine for modern TypeScript/JavaScript codebases. It turns architecture, change history, and dependency health into a unified risk model that helps engineering teams spot fragility before it becomes failure.
|
|
4
|
+
|
|
5
|
+
This repository contains the initial monorepo scaffolding and CLI foundation. The analysis engines are intentionally lean right now, but the structure is designed to scale cleanly as the system grows.
|
|
6
|
+
|
|
7
|
+
## Vision
|
|
8
|
+
|
|
9
|
+
CodeSentinel combines three signals into a single, explainable risk profile:
|
|
10
|
+
|
|
11
|
+
- **Structural risk**: dependency graph topology, cycles, coupling, fan-in/fan-out, boundary violations.
|
|
12
|
+
- **Evolutionary risk**: change frequency, hotspots, bus factor, volatility.
|
|
13
|
+
- **External risk**: transitive dependency exposure, maintainer risk, staleness and abandonment indicators.
|
|
14
|
+
|
|
15
|
+
The goal is a practical, engineering-grade model that supports both strategic architecture decisions and daily code review workflows.
|
|
16
|
+
|
|
17
|
+
## Monorepo Layout
|
|
18
|
+
|
|
19
|
+
- `packages/core`: shared domain types and cross-cutting services.
|
|
20
|
+
- `packages/code-graph`: source graph analysis primitives.
|
|
21
|
+
- `packages/git-analyzer`: Git history and evolutionary signals.
|
|
22
|
+
- `packages/dependency-firewall`: external dependency and supply chain signals.
|
|
23
|
+
- `packages/risk-engine`: risk aggregation and scoring model.
|
|
24
|
+
- `packages/reporter`: structured report output (console, JSON, CI).
|
|
25
|
+
- `packages/cli`: user-facing CLI entrypoint.
|
|
26
|
+
|
|
27
|
+
Each package is standalone, ESM-only, TypeScript-first, and built with `tsup`. The CLI depends on `core`; domain packages are kept decoupled to avoid circular dependencies.
|
|
28
|
+
|
|
29
|
+
## Requirements
|
|
30
|
+
|
|
31
|
+
- Node.js 24
|
|
32
|
+
- pnpm
|
|
33
|
+
|
|
34
|
+
## Commands
|
|
35
|
+
|
|
36
|
+
- `pnpm install`
|
|
37
|
+
- `pnpm build`
|
|
38
|
+
- `pnpm dev`
|
|
39
|
+
- `pnpm test`
|
|
40
|
+
- `pnpm release`
|
|
41
|
+
|
|
42
|
+
## CLI
|
|
43
|
+
|
|
44
|
+
Install globally with npm:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install -g @codesentinel/codesentinel
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Then run:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
codesentinel analyze [path]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Examples:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
codesentinel analyze
|
|
60
|
+
codesentinel analyze .
|
|
61
|
+
codesentinel analyze ../project
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
When running through pnpm, pass CLI arguments after `--`:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pnpm dev -- analyze
|
|
68
|
+
pnpm dev -- analyze .
|
|
69
|
+
pnpm dev -- analyze ../project
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Release Automation
|
|
73
|
+
|
|
74
|
+
- Pull requests to `main` run build and tests via `.github/workflows/ci.yml`.
|
|
75
|
+
- Merges to `main` run semantic-release via `.github/workflows/release.yml`.
|
|
76
|
+
- semantic-release bumps `packages/cli/package.json`, creates a GitHub release, publishes to npm, and pushes the version-bump commit back to `main`.
|
|
77
|
+
- Dependabot is configured monthly in `.github/dependabot.yml` for npm and GitHub Actions updates.
|
|
78
|
+
|
|
79
|
+
Trusted Publisher setup (no `NPM_TOKEN` secret):
|
|
80
|
+
|
|
81
|
+
- In npm package settings for `@codesentinel/codesentinel`, add a Trusted Publisher.
|
|
82
|
+
- Provider: `GitHub Actions`.
|
|
83
|
+
- Repository: `getcodesentinel/codesentinel`.
|
|
84
|
+
- Workflow filename: `release.yml`.
|
|
85
|
+
- Environment name: leave empty unless you explicitly use a GitHub Actions environment in this workflow.
|
|
86
|
+
|
|
87
|
+
Commit messages on `main` should follow Conventional Commits (example: `feat:`, `fix:`, `chore:`) so semantic-release can calculate versions automatically.
|
|
88
|
+
|
|
89
|
+
## Contributing
|
|
90
|
+
|
|
91
|
+
This project aims to be production-grade and minimal. If you add new dependencies or abstractions, justify them clearly and keep the architecture clean.
|
|
92
|
+
|
|
93
|
+
## ESM Import Policy
|
|
94
|
+
|
|
95
|
+
- The workspace uses `TypeScript` with `moduleResolution: "NodeNext"` and ESM output.
|
|
96
|
+
- For local relative imports, use `.js` specifiers in source files (example: `import { x } from "./x.js"`).
|
|
97
|
+
- Do not use `.ts` specifiers for runtime imports in package source files.
|
|
98
|
+
- This keeps emitted code and runtime resolution aligned with Node.js ESM behavior.
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
+
import { readFileSync } from "fs";
|
|
6
|
+
import { dirname, resolve as resolve3 } from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
5
8
|
|
|
6
9
|
// ../code-graph/dist/index.js
|
|
7
10
|
import { extname, isAbsolute, relative, resolve } from "path";
|
|
@@ -459,7 +462,9 @@ var runAnalyzeCommand = (inputPath) => {
|
|
|
459
462
|
|
|
460
463
|
// src/index.ts
|
|
461
464
|
var program = new Command();
|
|
462
|
-
|
|
465
|
+
var packageJsonPath = resolve3(dirname(fileURLToPath(import.meta.url)), "../package.json");
|
|
466
|
+
var { version } = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
467
|
+
program.name("codesentinel").description("Structural and evolutionary risk analysis for TypeScript/JavaScript codebases").version(version);
|
|
463
468
|
program.command("analyze").argument("[path]", "path to the project to analyze").action((path) => {
|
|
464
469
|
const output = runAnalyzeCommand(path);
|
|
465
470
|
process.stdout.write(`${output}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../../code-graph/src/domain/graph-model.ts","../../code-graph/src/domain/tarjan.ts","../../code-graph/src/domain/graph-metrics.ts","../../code-graph/src/infrastructure/typescript-project.ts","../../code-graph/src/application/build-project-graph-summary.ts","../../core/src/index.ts","../src/application/run-analyze-command.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { runAnalyzeCommand } from \"./application/run-analyze-command.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"codesentinel\")\n .description(\"Structural and evolutionary risk analysis for TypeScript/JavaScript codebases\")\n .version(\"0.1.1\");\n\nprogram\n .command(\"analyze\")\n .argument(\"[path]\", \"path to the project to analyze\")\n .action((path?: string) => {\n const output = runAnalyzeCommand(path);\n process.stdout.write(`${output}\\n`);\n });\n\nif (process.argv.length <= 2) {\n program.outputHelp();\n process.exit(0);\n}\n\nprogram.parse(process.argv);\n","export type NodeRecord = {\n id: string;\n absolutePath: string;\n relativePath: string;\n};\n\nexport type EdgeRecord = {\n from: string;\n to: string;\n};\n\nexport type GraphData = {\n nodes: readonly NodeRecord[];\n edges: readonly EdgeRecord[];\n adjacencyById: ReadonlyMap<string, readonly string[]>;\n};\n\nconst edgeKey = (from: string, to: string): string => `${from}\\u0000${to}`;\n\nexport const createGraphData = (\n nodes: readonly NodeRecord[],\n rawEdges: readonly EdgeRecord[],\n): GraphData => {\n const sortedNodes = [...nodes].sort((a, b) => a.id.localeCompare(b.id));\n const knownNodeIds = new Set(sortedNodes.map((node) => node.id));\n\n const uniqueEdgeMap = new Map<string, EdgeRecord>();\n for (const edge of rawEdges) {\n if (edge.from === edge.to) {\n continue;\n }\n\n if (!knownNodeIds.has(edge.from) || !knownNodeIds.has(edge.to)) {\n continue;\n }\n\n uniqueEdgeMap.set(edgeKey(edge.from, edge.to), edge);\n }\n\n const sortedEdges = [...uniqueEdgeMap.values()].sort((a, b) => {\n const fromCompare = a.from.localeCompare(b.from);\n if (fromCompare !== 0) {\n return fromCompare;\n }\n\n return a.to.localeCompare(b.to);\n });\n\n const adjacency = new Map<string, string[]>();\n for (const node of sortedNodes) {\n adjacency.set(node.id, []);\n }\n\n for (const edge of sortedEdges) {\n adjacency.get(edge.from)?.push(edge.to);\n }\n\n const adjacencyById = new Map<string, readonly string[]>();\n for (const [nodeId, targets] of adjacency.entries()) {\n adjacencyById.set(nodeId, [...targets]);\n }\n\n return {\n nodes: sortedNodes,\n edges: sortedEdges,\n adjacencyById,\n };\n};\n","type TarjanResult = {\n components: readonly (readonly string[])[];\n};\n\nexport const runTarjanScc = (adjacencyById: ReadonlyMap<string, readonly string[]>): TarjanResult => {\n let index = 0;\n const indices = new Map<string, number>();\n const lowLink = new Map<string, number>();\n const stack: string[] = [];\n const onStack = new Set<string>();\n const components: string[][] = [];\n\n const strongConnect = (nodeId: string): void => {\n indices.set(nodeId, index);\n lowLink.set(nodeId, index);\n index += 1;\n\n stack.push(nodeId);\n onStack.add(nodeId);\n\n const neighbors = adjacencyById.get(nodeId) ?? [];\n for (const nextId of neighbors) {\n if (!indices.has(nextId)) {\n strongConnect(nextId);\n const nodeLowLink = lowLink.get(nodeId);\n const nextLowLink = lowLink.get(nextId);\n if (nodeLowLink !== undefined && nextLowLink !== undefined && nextLowLink < nodeLowLink) {\n lowLink.set(nodeId, nextLowLink);\n }\n continue;\n }\n\n if (onStack.has(nextId)) {\n const nodeLowLink = lowLink.get(nodeId);\n const nextIndex = indices.get(nextId);\n if (nodeLowLink !== undefined && nextIndex !== undefined && nextIndex < nodeLowLink) {\n lowLink.set(nodeId, nextIndex);\n }\n }\n }\n\n const nodeLowLink = lowLink.get(nodeId);\n const nodeIndex = indices.get(nodeId);\n if (nodeLowLink === undefined || nodeIndex === undefined || nodeLowLink !== nodeIndex) {\n return;\n }\n\n const component: string[] = [];\n for (;;) {\n const popped = stack.pop();\n if (popped === undefined) {\n break;\n }\n\n onStack.delete(popped);\n component.push(popped);\n if (popped === nodeId) {\n break;\n }\n }\n\n component.sort((a, b) => a.localeCompare(b));\n components.push(component);\n };\n\n const nodeIds = [...adjacencyById.keys()].sort((a, b) => a.localeCompare(b));\n for (const nodeId of nodeIds) {\n if (!indices.has(nodeId)) {\n strongConnect(nodeId);\n }\n }\n\n components.sort((a, b) => {\n const firstA = a[0] ?? \"\";\n const firstB = b[0] ?? \"\";\n return firstA.localeCompare(firstB);\n });\n\n return { components };\n};\n","import type { FileDependency, GraphAnalysisSummary, GraphCycle, GraphMetrics } from \"@codesentinel/core\";\nimport type { GraphData } from \"./graph-model.js\";\nimport { runTarjanScc } from \"./tarjan.js\";\n\ntype DepthComputation = {\n depthByNodeId: ReadonlyMap<string, number>;\n graphDepth: number;\n cycles: readonly GraphCycle[];\n};\n\nconst hasSelfLoop = (nodeId: string, adjacencyById: ReadonlyMap<string, readonly string[]>): boolean => {\n const targets = adjacencyById.get(nodeId) ?? [];\n return targets.includes(nodeId);\n};\n\nconst computeCyclesAndDepth = (graph: GraphData): DepthComputation => {\n const { components } = runTarjanScc(graph.adjacencyById);\n\n const cycles: GraphCycle[] = [];\n const componentByNodeId = new Map<string, number>();\n components.forEach((component, index) => {\n for (const nodeId of component) {\n componentByNodeId.set(nodeId, index);\n }\n\n if (component.length > 1) {\n cycles.push({ nodes: [...component] });\n return;\n }\n\n const onlyNode = component[0];\n if (onlyNode !== undefined && hasSelfLoop(onlyNode, graph.adjacencyById)) {\n cycles.push({ nodes: [...component] });\n }\n });\n\n const dagOutgoing = new Map<number, Set<number>>();\n const inDegree = new Map<number, number>();\n\n for (let i = 0; i < components.length; i += 1) {\n dagOutgoing.set(i, new Set());\n inDegree.set(i, 0);\n }\n\n for (const edge of graph.edges) {\n const fromComponent = componentByNodeId.get(edge.from);\n const toComponent = componentByNodeId.get(edge.to);\n\n if (fromComponent === undefined || toComponent === undefined || fromComponent === toComponent) {\n continue;\n }\n\n const outgoing = dagOutgoing.get(fromComponent);\n if (outgoing?.has(toComponent) === true) {\n continue;\n }\n\n outgoing?.add(toComponent);\n inDegree.set(toComponent, (inDegree.get(toComponent) ?? 0) + 1);\n }\n\n const queue: number[] = [];\n const depthByComponent = new Map<number, number>();\n\n for (let i = 0; i < components.length; i += 1) {\n if ((inDegree.get(i) ?? 0) === 0) {\n queue.push(i);\n depthByComponent.set(i, 0);\n }\n }\n\n let cursor = 0;\n while (cursor < queue.length) {\n const componentId = queue[cursor];\n cursor += 1;\n\n if (componentId === undefined) {\n continue;\n }\n\n const currentDepth = depthByComponent.get(componentId) ?? 0;\n const outgoing = dagOutgoing.get(componentId) ?? new Set<number>();\n\n for (const nextComponent of outgoing) {\n const nextDepth = depthByComponent.get(nextComponent) ?? 0;\n if (currentDepth + 1 > nextDepth) {\n depthByComponent.set(nextComponent, currentDepth + 1);\n }\n\n const remainingIncoming = (inDegree.get(nextComponent) ?? 0) - 1;\n inDegree.set(nextComponent, remainingIncoming);\n if (remainingIncoming === 0) {\n queue.push(nextComponent);\n }\n }\n }\n\n const depthByNodeId = new Map<string, number>();\n let graphDepth = 0;\n\n components.forEach((component, componentId) => {\n const componentDepth = depthByComponent.get(componentId) ?? 0;\n if (componentDepth > graphDepth) {\n graphDepth = componentDepth;\n }\n\n for (const nodeId of component) {\n depthByNodeId.set(nodeId, componentDepth);\n }\n });\n\n cycles.sort((a, b) => {\n const firstA = a.nodes[0] ?? \"\";\n const firstB = b.nodes[0] ?? \"\";\n return firstA.localeCompare(firstB);\n });\n\n return {\n depthByNodeId,\n graphDepth,\n cycles,\n };\n};\n\nexport const createGraphAnalysisSummary = (\n targetPath: string,\n graph: GraphData,\n): GraphAnalysisSummary => {\n const fanInById = new Map<string, number>();\n const fanOutById = new Map<string, number>();\n\n for (const node of graph.nodes) {\n fanInById.set(node.id, 0);\n fanOutById.set(node.id, graph.adjacencyById.get(node.id)?.length ?? 0);\n }\n\n for (const edge of graph.edges) {\n fanInById.set(edge.to, (fanInById.get(edge.to) ?? 0) + 1);\n }\n\n const { cycles, depthByNodeId, graphDepth } = computeCyclesAndDepth(graph);\n\n let maxFanIn = 0;\n let maxFanOut = 0;\n\n const files: FileDependency[] = graph.nodes.map((node) => {\n const fanIn = fanInById.get(node.id) ?? 0;\n const fanOut = fanOutById.get(node.id) ?? 0;\n\n if (fanIn > maxFanIn) {\n maxFanIn = fanIn;\n }\n\n if (fanOut > maxFanOut) {\n maxFanOut = fanOut;\n }\n\n return {\n id: node.id,\n relativePath: node.relativePath,\n directDependencies: graph.adjacencyById.get(node.id) ?? [],\n fanIn,\n fanOut,\n depth: depthByNodeId.get(node.id) ?? 0,\n };\n });\n\n const metrics: GraphMetrics = {\n nodeCount: graph.nodes.length,\n edgeCount: graph.edges.length,\n cycleCount: cycles.length,\n graphDepth,\n maxFanIn,\n maxFanOut,\n };\n\n return {\n targetPath,\n nodes: graph.nodes,\n edges: graph.edges,\n cycles,\n files,\n metrics,\n };\n};\n","import { extname, isAbsolute, relative, resolve } from \"node:path\";\nimport * as ts from \"typescript\";\nimport type { EdgeRecord, NodeRecord } from \"../domain/graph-model.js\";\n\ntype ParsedProject = {\n nodes: readonly NodeRecord[];\n edges: readonly EdgeRecord[];\n};\n\nconst SOURCE_EXTENSIONS = new Set([\".ts\", \".tsx\", \".mts\", \".cts\", \".js\", \".jsx\", \".mjs\", \".cjs\"]);\n\nconst normalizePath = (pathValue: string): string => pathValue.replaceAll(\"\\\\\", \"/\");\n\nconst isProjectSourceFile = (filePath: string, projectRoot: string): boolean => {\n const extension = extname(filePath);\n if (!SOURCE_EXTENSIONS.has(extension)) {\n return false;\n }\n\n const relativePath = relative(projectRoot, filePath);\n if (relativePath.startsWith(\"..\")) {\n return false;\n }\n\n return !relativePath.includes(\"node_modules\");\n};\n\nconst findProjectFiles = (projectRoot: string): readonly string[] => {\n const files = ts.sys.readDirectory(projectRoot, [...SOURCE_EXTENSIONS], undefined, undefined);\n return files.map((filePath) => resolve(filePath));\n};\n\nconst parseTsConfig = (projectRoot: string): { fileNames: readonly string[]; options: ts.CompilerOptions } => {\n const configPath = ts.findConfigFile(projectRoot, ts.sys.fileExists, \"tsconfig.json\");\n if (configPath === undefined) {\n return {\n fileNames: findProjectFiles(projectRoot),\n options: {\n allowJs: true,\n moduleResolution: ts.ModuleResolutionKind.NodeNext,\n },\n };\n }\n\n const parsedCommandLine = ts.getParsedCommandLineOfConfigFile(\n configPath,\n {},\n {\n ...ts.sys,\n onUnRecoverableConfigFileDiagnostic: () => {\n throw new Error(`Failed to parse TypeScript configuration at ${configPath}`);\n },\n },\n );\n\n if (parsedCommandLine === undefined) {\n throw new Error(`Failed to parse TypeScript configuration at ${configPath}`);\n }\n\n const fileNames = parsedCommandLine.fileNames.map((filePath) => resolve(filePath));\n if (fileNames.length === 0) {\n return {\n fileNames: findProjectFiles(projectRoot),\n options: parsedCommandLine.options,\n };\n }\n\n return {\n fileNames,\n options: parsedCommandLine.options,\n };\n};\n\nconst getSpecifierFromExpression = (expression: ts.Expression): string | undefined => {\n if (ts.isStringLiteral(expression)) {\n return expression.text;\n }\n\n if (ts.isNoSubstitutionTemplateLiteral(expression)) {\n return expression.text;\n }\n\n return undefined;\n};\n\nconst hasRuntimeImport = (importDeclaration: ts.ImportDeclaration): boolean => {\n const importClause = importDeclaration.importClause;\n if (importClause === undefined) {\n return true;\n }\n\n if (importClause.isTypeOnly) {\n return false;\n }\n\n if (importClause.name !== undefined) {\n return true;\n }\n\n const namedBindings = importClause.namedBindings;\n if (namedBindings === undefined) {\n return false;\n }\n\n if (ts.isNamespaceImport(namedBindings)) {\n return true;\n }\n\n if (namedBindings.elements.length === 0) {\n return true;\n }\n\n return namedBindings.elements.some((element) => !element.isTypeOnly);\n};\n\nconst extractModuleSpecifiers = (sourceFile: ts.SourceFile): readonly string[] => {\n const specifiers = new Set<string>();\n\n const visit = (node: ts.Node): void => {\n if (ts.isImportDeclaration(node)) {\n if (hasRuntimeImport(node) && node.moduleSpecifier !== undefined) {\n const specifier = getSpecifierFromExpression(node.moduleSpecifier);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n return;\n }\n\n if (ts.isExportDeclaration(node)) {\n if (!node.isTypeOnly && node.moduleSpecifier !== undefined) {\n const specifier = getSpecifierFromExpression(node.moduleSpecifier);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n return;\n }\n\n if (ts.isCallExpression(node)) {\n if (node.expression.kind === ts.SyntaxKind.ImportKeyword && node.arguments.length > 0) {\n const firstArgument = node.arguments[0];\n if (firstArgument !== undefined) {\n const specifier = getSpecifierFromExpression(firstArgument);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n }\n\n if (ts.isIdentifier(node.expression) && node.expression.text === \"require\" && node.arguments.length > 0) {\n const firstArgument = node.arguments[0];\n if (firstArgument !== undefined) {\n const specifier = getSpecifierFromExpression(firstArgument);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n }\n }\n\n ts.forEachChild(node, visit);\n };\n\n visit(sourceFile);\n return [...specifiers];\n};\n\nexport const parseTypescriptProject = (projectPath: string): ParsedProject => {\n const projectRoot = isAbsolute(projectPath) ? projectPath : resolve(projectPath);\n const { fileNames, options } = parseTsConfig(projectRoot);\n\n const sourceFilePaths = fileNames\n .filter((filePath) => isProjectSourceFile(filePath, projectRoot))\n .map((filePath) => normalizePath(resolve(filePath)));\n\n const uniqueSourceFilePaths = [...new Set(sourceFilePaths)].sort((a, b) => a.localeCompare(b));\n const sourceFilePathSet = new Set(uniqueSourceFilePaths);\n\n const program = ts.createProgram({\n rootNames: uniqueSourceFilePaths,\n options,\n });\n\n const nodeByAbsolutePath = new Map<string, NodeRecord>();\n for (const sourcePath of uniqueSourceFilePaths) {\n const relativePath = normalizePath(relative(projectRoot, sourcePath));\n const nodeId = relativePath;\n nodeByAbsolutePath.set(sourcePath, {\n id: nodeId,\n absolutePath: sourcePath,\n relativePath,\n });\n }\n\n const resolverCache = new Map<string, string | undefined>();\n const edges: EdgeRecord[] = [];\n\n for (const sourcePath of uniqueSourceFilePaths) {\n const sourceFile = program.getSourceFile(sourcePath);\n if (sourceFile === undefined) {\n continue;\n }\n\n const fromNode = nodeByAbsolutePath.get(sourcePath);\n if (fromNode === undefined) {\n continue;\n }\n\n const moduleSpecifiers = extractModuleSpecifiers(sourceFile);\n for (const specifier of moduleSpecifiers) {\n const cacheKey = `${sourcePath}\\u0000${specifier}`;\n let resolvedPath = resolverCache.get(cacheKey);\n\n if (resolvedPath === undefined && !resolverCache.has(cacheKey)) {\n const resolved = ts.resolveModuleName(specifier, sourcePath, options, ts.sys).resolvedModule;\n if (resolved !== undefined) {\n resolvedPath = normalizePath(resolve(resolved.resolvedFileName));\n }\n resolverCache.set(cacheKey, resolvedPath);\n }\n\n if (resolvedPath === undefined || !sourceFilePathSet.has(resolvedPath)) {\n continue;\n }\n\n const toNode = nodeByAbsolutePath.get(resolvedPath);\n if (toNode === undefined) {\n continue;\n }\n\n edges.push({ from: fromNode.id, to: toNode.id });\n }\n }\n\n return {\n nodes: [...nodeByAbsolutePath.values()],\n edges,\n };\n};\n","import type { GraphAnalysisSummary } from \"@codesentinel/core\";\nimport { createGraphData } from \"../domain/graph-model.js\";\nimport { createGraphAnalysisSummary } from \"../domain/graph-metrics.js\";\nimport { parseTypescriptProject } from \"../infrastructure/typescript-project.js\";\n\nexport type BuildProjectGraphSummaryInput = {\n projectPath: string;\n};\n\nexport const buildProjectGraphSummary = (\n input: BuildProjectGraphSummaryInput,\n): GraphAnalysisSummary => {\n const parsedProject = parseTypescriptProject(input.projectPath);\n const graphData = createGraphData(parsedProject.nodes, parsedProject.edges);\n return createGraphAnalysisSummary(input.projectPath, graphData);\n};\n","import { resolve } from \"node:path\";\n\nexport type AnalyzeTarget = {\n absolutePath: string;\n};\n\nexport type GraphNode = {\n id: string;\n absolutePath: string;\n relativePath: string;\n};\n\nexport type GraphEdge = {\n from: string;\n to: string;\n};\n\nexport type GraphCycle = {\n nodes: readonly string[];\n};\n\nexport type FileDependency = {\n id: string;\n relativePath: string;\n directDependencies: readonly string[];\n fanIn: number;\n fanOut: number;\n depth: number;\n};\n\nexport type GraphMetrics = {\n nodeCount: number;\n edgeCount: number;\n cycleCount: number;\n graphDepth: number;\n maxFanIn: number;\n maxFanOut: number;\n};\n\nexport type GraphAnalysisSummary = {\n targetPath: string;\n nodes: readonly GraphNode[];\n edges: readonly GraphEdge[];\n cycles: readonly GraphCycle[];\n files: readonly FileDependency[];\n metrics: GraphMetrics;\n};\n\nexport const resolveTargetPath = (\n inputPath: string | undefined,\n cwd: string = process.cwd(),\n): AnalyzeTarget => {\n const absolutePath = resolve(cwd, inputPath ?? \".\");\n return { absolutePath };\n};\n","import { buildProjectGraphSummary } from \"@codesentinel/code-graph\";\nimport { resolveTargetPath } from \"@codesentinel/core\";\n\nexport const runAnalyzeCommand = (inputPath: string | undefined): string => {\n const invocationCwd = process.env[\"INIT_CWD\"] ?? process.cwd();\n const target = resolveTargetPath(inputPath, invocationCwd);\n const summary = buildProjectGraphSummary({ projectPath: target.absolutePath });\n return JSON.stringify(summary, null, 2);\n};\n"],"mappings":"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","names":["nodeLowLink","program","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../../code-graph/src/domain/graph-model.ts","../../code-graph/src/domain/tarjan.ts","../../code-graph/src/domain/graph-metrics.ts","../../code-graph/src/infrastructure/typescript-project.ts","../../code-graph/src/application/build-project-graph-summary.ts","../../core/src/index.ts","../src/application/run-analyze-command.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { runAnalyzeCommand } from \"./application/run-analyze-command.js\";\n\nconst program = new Command();\nconst packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), \"../package.json\");\nconst { version } = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as { version: string };\n\nprogram\n .name(\"codesentinel\")\n .description(\"Structural and evolutionary risk analysis for TypeScript/JavaScript codebases\")\n .version(version);\n\nprogram\n .command(\"analyze\")\n .argument(\"[path]\", \"path to the project to analyze\")\n .action((path?: string) => {\n const output = runAnalyzeCommand(path);\n process.stdout.write(`${output}\\n`);\n });\n\nif (process.argv.length <= 2) {\n program.outputHelp();\n process.exit(0);\n}\n\nprogram.parse(process.argv);\n","export type NodeRecord = {\n id: string;\n absolutePath: string;\n relativePath: string;\n};\n\nexport type EdgeRecord = {\n from: string;\n to: string;\n};\n\nexport type GraphData = {\n nodes: readonly NodeRecord[];\n edges: readonly EdgeRecord[];\n adjacencyById: ReadonlyMap<string, readonly string[]>;\n};\n\nconst edgeKey = (from: string, to: string): string => `${from}\\u0000${to}`;\n\nexport const createGraphData = (\n nodes: readonly NodeRecord[],\n rawEdges: readonly EdgeRecord[],\n): GraphData => {\n const sortedNodes = [...nodes].sort((a, b) => a.id.localeCompare(b.id));\n const knownNodeIds = new Set(sortedNodes.map((node) => node.id));\n\n const uniqueEdgeMap = new Map<string, EdgeRecord>();\n for (const edge of rawEdges) {\n if (edge.from === edge.to) {\n continue;\n }\n\n if (!knownNodeIds.has(edge.from) || !knownNodeIds.has(edge.to)) {\n continue;\n }\n\n uniqueEdgeMap.set(edgeKey(edge.from, edge.to), edge);\n }\n\n const sortedEdges = [...uniqueEdgeMap.values()].sort((a, b) => {\n const fromCompare = a.from.localeCompare(b.from);\n if (fromCompare !== 0) {\n return fromCompare;\n }\n\n return a.to.localeCompare(b.to);\n });\n\n const adjacency = new Map<string, string[]>();\n for (const node of sortedNodes) {\n adjacency.set(node.id, []);\n }\n\n for (const edge of sortedEdges) {\n adjacency.get(edge.from)?.push(edge.to);\n }\n\n const adjacencyById = new Map<string, readonly string[]>();\n for (const [nodeId, targets] of adjacency.entries()) {\n adjacencyById.set(nodeId, [...targets]);\n }\n\n return {\n nodes: sortedNodes,\n edges: sortedEdges,\n adjacencyById,\n };\n};\n","type TarjanResult = {\n components: readonly (readonly string[])[];\n};\n\nexport const runTarjanScc = (adjacencyById: ReadonlyMap<string, readonly string[]>): TarjanResult => {\n let index = 0;\n const indices = new Map<string, number>();\n const lowLink = new Map<string, number>();\n const stack: string[] = [];\n const onStack = new Set<string>();\n const components: string[][] = [];\n\n const strongConnect = (nodeId: string): void => {\n indices.set(nodeId, index);\n lowLink.set(nodeId, index);\n index += 1;\n\n stack.push(nodeId);\n onStack.add(nodeId);\n\n const neighbors = adjacencyById.get(nodeId) ?? [];\n for (const nextId of neighbors) {\n if (!indices.has(nextId)) {\n strongConnect(nextId);\n const nodeLowLink = lowLink.get(nodeId);\n const nextLowLink = lowLink.get(nextId);\n if (nodeLowLink !== undefined && nextLowLink !== undefined && nextLowLink < nodeLowLink) {\n lowLink.set(nodeId, nextLowLink);\n }\n continue;\n }\n\n if (onStack.has(nextId)) {\n const nodeLowLink = lowLink.get(nodeId);\n const nextIndex = indices.get(nextId);\n if (nodeLowLink !== undefined && nextIndex !== undefined && nextIndex < nodeLowLink) {\n lowLink.set(nodeId, nextIndex);\n }\n }\n }\n\n const nodeLowLink = lowLink.get(nodeId);\n const nodeIndex = indices.get(nodeId);\n if (nodeLowLink === undefined || nodeIndex === undefined || nodeLowLink !== nodeIndex) {\n return;\n }\n\n const component: string[] = [];\n for (;;) {\n const popped = stack.pop();\n if (popped === undefined) {\n break;\n }\n\n onStack.delete(popped);\n component.push(popped);\n if (popped === nodeId) {\n break;\n }\n }\n\n component.sort((a, b) => a.localeCompare(b));\n components.push(component);\n };\n\n const nodeIds = [...adjacencyById.keys()].sort((a, b) => a.localeCompare(b));\n for (const nodeId of nodeIds) {\n if (!indices.has(nodeId)) {\n strongConnect(nodeId);\n }\n }\n\n components.sort((a, b) => {\n const firstA = a[0] ?? \"\";\n const firstB = b[0] ?? \"\";\n return firstA.localeCompare(firstB);\n });\n\n return { components };\n};\n","import type { FileDependency, GraphAnalysisSummary, GraphCycle, GraphMetrics } from \"@codesentinel/core\";\nimport type { GraphData } from \"./graph-model.js\";\nimport { runTarjanScc } from \"./tarjan.js\";\n\ntype DepthComputation = {\n depthByNodeId: ReadonlyMap<string, number>;\n graphDepth: number;\n cycles: readonly GraphCycle[];\n};\n\nconst hasSelfLoop = (nodeId: string, adjacencyById: ReadonlyMap<string, readonly string[]>): boolean => {\n const targets = adjacencyById.get(nodeId) ?? [];\n return targets.includes(nodeId);\n};\n\nconst computeCyclesAndDepth = (graph: GraphData): DepthComputation => {\n const { components } = runTarjanScc(graph.adjacencyById);\n\n const cycles: GraphCycle[] = [];\n const componentByNodeId = new Map<string, number>();\n components.forEach((component, index) => {\n for (const nodeId of component) {\n componentByNodeId.set(nodeId, index);\n }\n\n if (component.length > 1) {\n cycles.push({ nodes: [...component] });\n return;\n }\n\n const onlyNode = component[0];\n if (onlyNode !== undefined && hasSelfLoop(onlyNode, graph.adjacencyById)) {\n cycles.push({ nodes: [...component] });\n }\n });\n\n const dagOutgoing = new Map<number, Set<number>>();\n const inDegree = new Map<number, number>();\n\n for (let i = 0; i < components.length; i += 1) {\n dagOutgoing.set(i, new Set());\n inDegree.set(i, 0);\n }\n\n for (const edge of graph.edges) {\n const fromComponent = componentByNodeId.get(edge.from);\n const toComponent = componentByNodeId.get(edge.to);\n\n if (fromComponent === undefined || toComponent === undefined || fromComponent === toComponent) {\n continue;\n }\n\n const outgoing = dagOutgoing.get(fromComponent);\n if (outgoing?.has(toComponent) === true) {\n continue;\n }\n\n outgoing?.add(toComponent);\n inDegree.set(toComponent, (inDegree.get(toComponent) ?? 0) + 1);\n }\n\n const queue: number[] = [];\n const depthByComponent = new Map<number, number>();\n\n for (let i = 0; i < components.length; i += 1) {\n if ((inDegree.get(i) ?? 0) === 0) {\n queue.push(i);\n depthByComponent.set(i, 0);\n }\n }\n\n let cursor = 0;\n while (cursor < queue.length) {\n const componentId = queue[cursor];\n cursor += 1;\n\n if (componentId === undefined) {\n continue;\n }\n\n const currentDepth = depthByComponent.get(componentId) ?? 0;\n const outgoing = dagOutgoing.get(componentId) ?? new Set<number>();\n\n for (const nextComponent of outgoing) {\n const nextDepth = depthByComponent.get(nextComponent) ?? 0;\n if (currentDepth + 1 > nextDepth) {\n depthByComponent.set(nextComponent, currentDepth + 1);\n }\n\n const remainingIncoming = (inDegree.get(nextComponent) ?? 0) - 1;\n inDegree.set(nextComponent, remainingIncoming);\n if (remainingIncoming === 0) {\n queue.push(nextComponent);\n }\n }\n }\n\n const depthByNodeId = new Map<string, number>();\n let graphDepth = 0;\n\n components.forEach((component, componentId) => {\n const componentDepth = depthByComponent.get(componentId) ?? 0;\n if (componentDepth > graphDepth) {\n graphDepth = componentDepth;\n }\n\n for (const nodeId of component) {\n depthByNodeId.set(nodeId, componentDepth);\n }\n });\n\n cycles.sort((a, b) => {\n const firstA = a.nodes[0] ?? \"\";\n const firstB = b.nodes[0] ?? \"\";\n return firstA.localeCompare(firstB);\n });\n\n return {\n depthByNodeId,\n graphDepth,\n cycles,\n };\n};\n\nexport const createGraphAnalysisSummary = (\n targetPath: string,\n graph: GraphData,\n): GraphAnalysisSummary => {\n const fanInById = new Map<string, number>();\n const fanOutById = new Map<string, number>();\n\n for (const node of graph.nodes) {\n fanInById.set(node.id, 0);\n fanOutById.set(node.id, graph.adjacencyById.get(node.id)?.length ?? 0);\n }\n\n for (const edge of graph.edges) {\n fanInById.set(edge.to, (fanInById.get(edge.to) ?? 0) + 1);\n }\n\n const { cycles, depthByNodeId, graphDepth } = computeCyclesAndDepth(graph);\n\n let maxFanIn = 0;\n let maxFanOut = 0;\n\n const files: FileDependency[] = graph.nodes.map((node) => {\n const fanIn = fanInById.get(node.id) ?? 0;\n const fanOut = fanOutById.get(node.id) ?? 0;\n\n if (fanIn > maxFanIn) {\n maxFanIn = fanIn;\n }\n\n if (fanOut > maxFanOut) {\n maxFanOut = fanOut;\n }\n\n return {\n id: node.id,\n relativePath: node.relativePath,\n directDependencies: graph.adjacencyById.get(node.id) ?? [],\n fanIn,\n fanOut,\n depth: depthByNodeId.get(node.id) ?? 0,\n };\n });\n\n const metrics: GraphMetrics = {\n nodeCount: graph.nodes.length,\n edgeCount: graph.edges.length,\n cycleCount: cycles.length,\n graphDepth,\n maxFanIn,\n maxFanOut,\n };\n\n return {\n targetPath,\n nodes: graph.nodes,\n edges: graph.edges,\n cycles,\n files,\n metrics,\n };\n};\n","import { extname, isAbsolute, relative, resolve } from \"node:path\";\nimport * as ts from \"typescript\";\nimport type { EdgeRecord, NodeRecord } from \"../domain/graph-model.js\";\n\ntype ParsedProject = {\n nodes: readonly NodeRecord[];\n edges: readonly EdgeRecord[];\n};\n\nconst SOURCE_EXTENSIONS = new Set([\".ts\", \".tsx\", \".mts\", \".cts\", \".js\", \".jsx\", \".mjs\", \".cjs\"]);\n\nconst normalizePath = (pathValue: string): string => pathValue.replaceAll(\"\\\\\", \"/\");\n\nconst isProjectSourceFile = (filePath: string, projectRoot: string): boolean => {\n const extension = extname(filePath);\n if (!SOURCE_EXTENSIONS.has(extension)) {\n return false;\n }\n\n const relativePath = relative(projectRoot, filePath);\n if (relativePath.startsWith(\"..\")) {\n return false;\n }\n\n return !relativePath.includes(\"node_modules\");\n};\n\nconst findProjectFiles = (projectRoot: string): readonly string[] => {\n const files = ts.sys.readDirectory(projectRoot, [...SOURCE_EXTENSIONS], undefined, undefined);\n return files.map((filePath) => resolve(filePath));\n};\n\nconst parseTsConfig = (projectRoot: string): { fileNames: readonly string[]; options: ts.CompilerOptions } => {\n const configPath = ts.findConfigFile(projectRoot, ts.sys.fileExists, \"tsconfig.json\");\n if (configPath === undefined) {\n return {\n fileNames: findProjectFiles(projectRoot),\n options: {\n allowJs: true,\n moduleResolution: ts.ModuleResolutionKind.NodeNext,\n },\n };\n }\n\n const parsedCommandLine = ts.getParsedCommandLineOfConfigFile(\n configPath,\n {},\n {\n ...ts.sys,\n onUnRecoverableConfigFileDiagnostic: () => {\n throw new Error(`Failed to parse TypeScript configuration at ${configPath}`);\n },\n },\n );\n\n if (parsedCommandLine === undefined) {\n throw new Error(`Failed to parse TypeScript configuration at ${configPath}`);\n }\n\n const fileNames = parsedCommandLine.fileNames.map((filePath) => resolve(filePath));\n if (fileNames.length === 0) {\n return {\n fileNames: findProjectFiles(projectRoot),\n options: parsedCommandLine.options,\n };\n }\n\n return {\n fileNames,\n options: parsedCommandLine.options,\n };\n};\n\nconst getSpecifierFromExpression = (expression: ts.Expression): string | undefined => {\n if (ts.isStringLiteral(expression)) {\n return expression.text;\n }\n\n if (ts.isNoSubstitutionTemplateLiteral(expression)) {\n return expression.text;\n }\n\n return undefined;\n};\n\nconst hasRuntimeImport = (importDeclaration: ts.ImportDeclaration): boolean => {\n const importClause = importDeclaration.importClause;\n if (importClause === undefined) {\n return true;\n }\n\n if (importClause.isTypeOnly) {\n return false;\n }\n\n if (importClause.name !== undefined) {\n return true;\n }\n\n const namedBindings = importClause.namedBindings;\n if (namedBindings === undefined) {\n return false;\n }\n\n if (ts.isNamespaceImport(namedBindings)) {\n return true;\n }\n\n if (namedBindings.elements.length === 0) {\n return true;\n }\n\n return namedBindings.elements.some((element) => !element.isTypeOnly);\n};\n\nconst extractModuleSpecifiers = (sourceFile: ts.SourceFile): readonly string[] => {\n const specifiers = new Set<string>();\n\n const visit = (node: ts.Node): void => {\n if (ts.isImportDeclaration(node)) {\n if (hasRuntimeImport(node) && node.moduleSpecifier !== undefined) {\n const specifier = getSpecifierFromExpression(node.moduleSpecifier);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n return;\n }\n\n if (ts.isExportDeclaration(node)) {\n if (!node.isTypeOnly && node.moduleSpecifier !== undefined) {\n const specifier = getSpecifierFromExpression(node.moduleSpecifier);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n return;\n }\n\n if (ts.isCallExpression(node)) {\n if (node.expression.kind === ts.SyntaxKind.ImportKeyword && node.arguments.length > 0) {\n const firstArgument = node.arguments[0];\n if (firstArgument !== undefined) {\n const specifier = getSpecifierFromExpression(firstArgument);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n }\n\n if (ts.isIdentifier(node.expression) && node.expression.text === \"require\" && node.arguments.length > 0) {\n const firstArgument = node.arguments[0];\n if (firstArgument !== undefined) {\n const specifier = getSpecifierFromExpression(firstArgument);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n }\n }\n\n ts.forEachChild(node, visit);\n };\n\n visit(sourceFile);\n return [...specifiers];\n};\n\nexport const parseTypescriptProject = (projectPath: string): ParsedProject => {\n const projectRoot = isAbsolute(projectPath) ? projectPath : resolve(projectPath);\n const { fileNames, options } = parseTsConfig(projectRoot);\n\n const sourceFilePaths = fileNames\n .filter((filePath) => isProjectSourceFile(filePath, projectRoot))\n .map((filePath) => normalizePath(resolve(filePath)));\n\n const uniqueSourceFilePaths = [...new Set(sourceFilePaths)].sort((a, b) => a.localeCompare(b));\n const sourceFilePathSet = new Set(uniqueSourceFilePaths);\n\n const program = ts.createProgram({\n rootNames: uniqueSourceFilePaths,\n options,\n });\n\n const nodeByAbsolutePath = new Map<string, NodeRecord>();\n for (const sourcePath of uniqueSourceFilePaths) {\n const relativePath = normalizePath(relative(projectRoot, sourcePath));\n const nodeId = relativePath;\n nodeByAbsolutePath.set(sourcePath, {\n id: nodeId,\n absolutePath: sourcePath,\n relativePath,\n });\n }\n\n const resolverCache = new Map<string, string | undefined>();\n const edges: EdgeRecord[] = [];\n\n for (const sourcePath of uniqueSourceFilePaths) {\n const sourceFile = program.getSourceFile(sourcePath);\n if (sourceFile === undefined) {\n continue;\n }\n\n const fromNode = nodeByAbsolutePath.get(sourcePath);\n if (fromNode === undefined) {\n continue;\n }\n\n const moduleSpecifiers = extractModuleSpecifiers(sourceFile);\n for (const specifier of moduleSpecifiers) {\n const cacheKey = `${sourcePath}\\u0000${specifier}`;\n let resolvedPath = resolverCache.get(cacheKey);\n\n if (resolvedPath === undefined && !resolverCache.has(cacheKey)) {\n const resolved = ts.resolveModuleName(specifier, sourcePath, options, ts.sys).resolvedModule;\n if (resolved !== undefined) {\n resolvedPath = normalizePath(resolve(resolved.resolvedFileName));\n }\n resolverCache.set(cacheKey, resolvedPath);\n }\n\n if (resolvedPath === undefined || !sourceFilePathSet.has(resolvedPath)) {\n continue;\n }\n\n const toNode = nodeByAbsolutePath.get(resolvedPath);\n if (toNode === undefined) {\n continue;\n }\n\n edges.push({ from: fromNode.id, to: toNode.id });\n }\n }\n\n return {\n nodes: [...nodeByAbsolutePath.values()],\n edges,\n };\n};\n","import type { GraphAnalysisSummary } from \"@codesentinel/core\";\nimport { createGraphData } from \"../domain/graph-model.js\";\nimport { createGraphAnalysisSummary } from \"../domain/graph-metrics.js\";\nimport { parseTypescriptProject } from \"../infrastructure/typescript-project.js\";\n\nexport type BuildProjectGraphSummaryInput = {\n projectPath: string;\n};\n\nexport const buildProjectGraphSummary = (\n input: BuildProjectGraphSummaryInput,\n): GraphAnalysisSummary => {\n const parsedProject = parseTypescriptProject(input.projectPath);\n const graphData = createGraphData(parsedProject.nodes, parsedProject.edges);\n return createGraphAnalysisSummary(input.projectPath, graphData);\n};\n","import { resolve } from \"node:path\";\n\nexport type AnalyzeTarget = {\n absolutePath: string;\n};\n\nexport type GraphNode = {\n id: string;\n absolutePath: string;\n relativePath: string;\n};\n\nexport type GraphEdge = {\n from: string;\n to: string;\n};\n\nexport type GraphCycle = {\n nodes: readonly string[];\n};\n\nexport type FileDependency = {\n id: string;\n relativePath: string;\n directDependencies: readonly string[];\n fanIn: number;\n fanOut: number;\n depth: number;\n};\n\nexport type GraphMetrics = {\n nodeCount: number;\n edgeCount: number;\n cycleCount: number;\n graphDepth: number;\n maxFanIn: number;\n maxFanOut: number;\n};\n\nexport type GraphAnalysisSummary = {\n targetPath: string;\n nodes: readonly GraphNode[];\n edges: readonly GraphEdge[];\n cycles: readonly GraphCycle[];\n files: readonly FileDependency[];\n metrics: GraphMetrics;\n};\n\nexport const resolveTargetPath = (\n inputPath: string | undefined,\n cwd: string = process.cwd(),\n): AnalyzeTarget => {\n const absolutePath = resolve(cwd, inputPath ?? \".\");\n return { absolutePath };\n};\n","import { buildProjectGraphSummary } from \"@codesentinel/code-graph\";\nimport { resolveTargetPath } from \"@codesentinel/core\";\n\nexport const runAnalyzeCommand = (inputPath: string | undefined): string => {\n const invocationCwd = process.env[\"INIT_CWD\"] ?? process.cwd();\n const target = resolveTargetPath(inputPath, invocationCwd);\n const summary = buildProjectGraphSummary({ projectPath: target.absolutePath });\n return JSON.stringify(summary, null, 2);\n};\n"],"mappings":"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","names":["resolve","nodeLowLink","program","resolve","resolve"]}
|
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codesentinel/codesentinel",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Command-line interface for CodeSentinel.",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/getcodesentinel/codesentinel"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/getcodesentinel/codesentinel#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/getcodesentinel/codesentinel/issues"
|
|
13
|
+
},
|
|
6
14
|
"license": "MIT",
|
|
7
15
|
"engines": {
|
|
8
16
|
"node": ">=24"
|
|
@@ -13,13 +21,17 @@
|
|
|
13
21
|
"scripts": {
|
|
14
22
|
"build": "tsup",
|
|
15
23
|
"dev": "node --import tsx/esm src/index.ts",
|
|
16
|
-
"prepack": "pnpm --filter @codesentinel/core build && pnpm --filter @codesentinel/code-graph build && pnpm run build",
|
|
17
|
-
"test": "vitest run"
|
|
24
|
+
"prepack": "cp ../../README.md ./README.md && cp ../../LICENSE ./LICENSE && pnpm --filter @codesentinel/core build && pnpm --filter @codesentinel/code-graph build && pnpm run build",
|
|
25
|
+
"test": "vitest run --passWithNoTests"
|
|
18
26
|
},
|
|
19
27
|
"dependencies": {
|
|
20
28
|
"commander": "^12.1.0",
|
|
21
29
|
"typescript": "^5.6.3"
|
|
22
30
|
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@codesentinel/code-graph": "workspace:*",
|
|
33
|
+
"@codesentinel/core": "workspace:*"
|
|
34
|
+
},
|
|
23
35
|
"exports": {
|
|
24
36
|
".": {
|
|
25
37
|
"types": "./dist/index.d.ts",
|
|
@@ -28,7 +40,9 @@
|
|
|
28
40
|
},
|
|
29
41
|
"types": "./dist/index.d.ts",
|
|
30
42
|
"files": [
|
|
31
|
-
"dist"
|
|
43
|
+
"dist",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE"
|
|
32
46
|
],
|
|
33
47
|
"publishConfig": {
|
|
34
48
|
"access": "public"
|