auditor-lambda 0.10.3 → 0.10.8
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/audit-code-wrapper-build.mjs +198 -0
- package/audit-code-wrapper-install-hosts.mjs +1140 -0
- package/audit-code-wrapper-io.mjs +155 -0
- package/audit-code-wrapper-legacy.mjs +125 -0
- package/audit-code-wrapper-lib.mjs +22 -1806
- package/audit-code-wrapper-opencode.mjs +255 -0
- package/dispatch/merge-results.mjs +5 -3
- package/dispatch/validate-result.mjs +2 -2
- package/dist/adapters/coverageSummary.js +6 -2
- package/dist/adapters/normalizeExternal.js +16 -1
- package/dist/adapters/npmAudit.js +20 -9
- package/dist/adapters/semgrep.js +26 -1
- package/dist/cli/advanceAuditCommand.d.ts +1 -0
- package/dist/cli/advanceAuditCommand.js +95 -0
- package/dist/cli/args.js +2 -3
- package/dist/cli/auditStep.js +2 -2
- package/dist/cli/cleanup.d.ts +11 -1
- package/dist/cli/cleanup.js +25 -5
- package/dist/cli/cleanupCommand.d.ts +1 -0
- package/dist/cli/cleanupCommand.js +24 -0
- package/dist/cli/dispatch.d.ts +55 -31
- package/dist/cli/dispatch.js +298 -241
- package/dist/cli/dispatchStatusCommand.d.ts +1 -0
- package/dist/cli/dispatchStatusCommand.js +68 -0
- package/dist/cli/explainTaskCommand.d.ts +1 -0
- package/dist/cli/explainTaskCommand.js +33 -0
- package/dist/cli/importExternalAnalyzerCommand.d.ts +1 -0
- package/dist/cli/importExternalAnalyzerCommand.js +20 -0
- package/dist/cli/ingestResultsCommand.d.ts +1 -0
- package/dist/cli/ingestResultsCommand.js +34 -0
- package/dist/cli/intakeCommand.d.ts +1 -0
- package/dist/cli/intakeCommand.js +17 -0
- package/dist/cli/lineIndex.js +19 -12
- package/dist/cli/nextStepCommand.d.ts +139 -0
- package/dist/cli/nextStepCommand.js +281 -234
- package/dist/cli/planCommand.d.ts +1 -0
- package/dist/cli/planCommand.js +16 -0
- package/dist/cli/prepareDispatchCommand.d.ts +1 -0
- package/dist/cli/prepareDispatchCommand.js +25 -0
- package/dist/cli/quotaCommand.d.ts +1 -0
- package/dist/cli/quotaCommand.js +56 -0
- package/dist/cli/requeueCommand.d.ts +1 -0
- package/dist/cli/requeueCommand.js +10 -0
- package/dist/cli/runToCompletion.js +451 -412
- package/dist/cli/sampleRunCommand.d.ts +1 -0
- package/dist/cli/sampleRunCommand.js +93 -0
- package/dist/cli/statusCommand.js +1 -1
- package/dist/cli/steps.js +4 -1
- package/dist/cli/submitPacketCommand.js +16 -15
- package/dist/cli/synthesizeCommand.d.ts +1 -0
- package/dist/cli/synthesizeCommand.js +15 -0
- package/dist/cli/updateRuntimeValidationCommand.d.ts +1 -0
- package/dist/cli/updateRuntimeValidationCommand.js +16 -0
- package/dist/cli/validateCommand.d.ts +1 -0
- package/dist/cli/validateCommand.js +41 -0
- package/dist/cli/validateResultCommand.d.ts +1 -0
- package/dist/cli/validateResultCommand.js +63 -0
- package/dist/cli/validateResultsCommand.d.ts +1 -0
- package/dist/cli/validateResultsCommand.js +31 -0
- package/dist/cli/workerRunCommand.d.ts +15 -1
- package/dist/cli/workerRunCommand.js +40 -4
- package/dist/cli.d.ts +3 -2
- package/dist/cli.js +21 -628
- package/dist/coverage.js +7 -3
- package/dist/extractors/analyzers/css.js +2 -2
- package/dist/extractors/analyzers/html.js +2 -2
- package/dist/extractors/analyzers/python.js +2 -2
- package/dist/extractors/analyzers/registry.js +17 -36
- package/dist/extractors/analyzers/treeSitter.d.ts +10 -1
- package/dist/extractors/analyzers/treeSitter.js +28 -6
- package/dist/extractors/analyzers/typescript.js +104 -85
- package/dist/extractors/browserExtension.js +4 -1
- package/dist/extractors/designAssessment.js +21 -21
- package/dist/extractors/fsIntake.js +35 -11
- package/dist/extractors/graph.js +17 -7
- package/dist/extractors/graphManifestEdges/cargo.d.ts +4 -0
- package/dist/extractors/graphManifestEdges/cargo.js +107 -0
- package/dist/extractors/graphManifestEdges/go.d.ts +5 -0
- package/dist/extractors/graphManifestEdges/go.js +151 -0
- package/dist/extractors/graphManifestEdges/index.d.ts +8 -0
- package/dist/extractors/graphManifestEdges/index.js +11 -0
- package/dist/extractors/graphManifestEdges/jsonc.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/jsonc.js +97 -0
- package/dist/extractors/graphManifestEdges/maven.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/maven.js +73 -0
- package/dist/extractors/graphManifestEdges/packageJson.d.ts +19 -0
- package/dist/extractors/graphManifestEdges/packageJson.js +204 -0
- package/dist/extractors/graphManifestEdges/pnpm.d.ts +2 -0
- package/dist/extractors/graphManifestEdges/pnpm.js +42 -0
- package/dist/extractors/graphManifestEdges/pyproject.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/pyproject.js +83 -0
- package/dist/extractors/graphManifestEdges/toml.d.ts +4 -0
- package/dist/extractors/graphManifestEdges/toml.js +68 -0
- package/dist/extractors/graphManifestEdges/typescript.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/typescript.js +56 -0
- package/dist/extractors/graphManifestEdges/workspace.d.ts +10 -0
- package/dist/extractors/graphManifestEdges/workspace.js +72 -0
- package/dist/extractors/graphManifestEdges/yaml.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/yaml.js +59 -0
- package/dist/extractors/graphManifestEdges/yamlPaths.d.ts +4 -0
- package/dist/extractors/graphManifestEdges/yamlPaths.js +89 -0
- package/dist/extractors/graphPythonImports.js +4 -20
- package/dist/extractors/pathPatterns.js +3 -13
- package/dist/io/artifacts.d.ts +1 -2
- package/dist/io/artifacts.js +8 -4
- package/dist/io/runArtifacts.d.ts +8 -2
- package/dist/io/runArtifacts.js +103 -69
- package/dist/io/toolingManifest.js +2 -1
- package/dist/orchestrator/advance.js +36 -0
- package/dist/orchestrator/artifactFreshness.d.ts +1 -1
- package/dist/orchestrator/artifactFreshness.js +1 -1
- package/dist/orchestrator/artifactMetadata.js +5 -5
- package/dist/orchestrator/auditTaskUtils.d.ts +4 -0
- package/dist/orchestrator/auditTaskUtils.js +8 -12
- package/dist/orchestrator/autoFixExecutor.js +40 -26
- package/dist/orchestrator/dependencyMap.js +1 -1
- package/dist/orchestrator/executorResult.d.ts +33 -0
- package/dist/orchestrator/executors.d.ts +7 -0
- package/dist/orchestrator/executors.js +24 -0
- package/dist/orchestrator/fileAnchors.js +42 -29
- package/dist/orchestrator/fileIntegrity.js +6 -1
- package/dist/orchestrator/flowCoverage.js +1 -2
- package/dist/orchestrator/flowPlanning.js +8 -4
- package/dist/orchestrator/graphEnrichmentExecutor.js +67 -45
- package/dist/orchestrator/ingestionExecutors.js +9 -1
- package/dist/orchestrator/intakeExecutors.d.ts +0 -4
- package/dist/orchestrator/intakeExecutors.js +24 -14
- package/dist/orchestrator/localCommands.d.ts +1 -0
- package/dist/orchestrator/localCommands.js +10 -17
- package/dist/orchestrator/nextStep.js +3 -1
- package/dist/orchestrator/requeueCommand.js +4 -0
- package/dist/orchestrator/reviewPacketGraph.js +50 -18
- package/dist/orchestrator/reviewPackets.js +10 -8
- package/dist/orchestrator/runtimeCommand.js +35 -7
- package/dist/orchestrator/runtimeValidationUpdate.js +6 -0
- package/dist/orchestrator/selectiveDeepening/highRiskClean.js +3 -2
- package/dist/orchestrator/selectiveDeepening/lensVerification.js +44 -18
- package/dist/orchestrator/staleness.js +3 -3
- package/dist/orchestrator/state.js +1 -1
- package/dist/orchestrator/syntaxResolutionExecutor.js +17 -24
- package/dist/orchestrator/synthesisExecutors.js +1 -0
- package/dist/orchestrator/taskBuilder.js +5 -4
- package/dist/providers/claudeCodeProvider.js +5 -2
- package/dist/providers/opencodeProvider.js +4 -1
- package/dist/quota/discoveredLimits.js +3 -3
- package/dist/quota/headerExtraction.js +5 -2
- package/dist/quota/headerExtractors/claudeCodeHeaderExtractor.js +3 -0
- package/dist/quota/headerExtractors/index.js +3 -3
- package/dist/quota/index.d.ts +3 -1
- package/dist/quota/index.js +3 -0
- package/dist/reporting/findingRanks.d.ts +3 -0
- package/dist/reporting/findingRanks.js +24 -0
- package/dist/reporting/mergeFindings.js +1 -24
- package/dist/reporting/synthesis.d.ts +3 -1
- package/dist/reporting/synthesis.js +30 -6
- package/dist/reporting/synthesisNarrativePrompt.js +3 -0
- package/dist/reporting/workBlocks.js +1 -14
- package/dist/supervisor/operatorHandoff.js +2 -6
- package/dist/supervisor/runLedger.js +30 -41
- package/dist/types/activeDispatch.d.ts +31 -0
- package/dist/types/activeDispatch.js +2 -0
- package/dist/types.d.ts +21 -4
- package/dist/types.js +24 -16
- package/dist/validation/artifacts.js +3 -0
- package/dist/validation/auditResults.js +8 -2
- package/package.json +2 -2
- package/schemas/audit_findings.schema.json +5 -1
- package/schemas/audit_plan_metrics.schema.json +1 -1
- package/schemas/audit_result.schema.json +5 -6
- package/schemas/audit_task.schema.json +1 -4
- package/schemas/blind_spot_register.schema.json +1 -1
- package/schemas/coverage_matrix.schema.json +2 -8
- package/schemas/finding.schema.json +1 -16
- package/schemas/flow_coverage.schema.json +2 -8
- package/schemas/graph_bundle.schema.json +31 -0
- package/schemas/lens.schema.json +7 -0
- package/schemas/review_packets.schema.json +6 -17
- package/schemas/step_contract.schema.json +8 -2
- package/schemas/unit_manifest.schema.json +1 -4
- package/scripts/postinstall.mjs +4 -3
- package/skills/audit-code/audit-code.prompt.md +3 -4
- package/dist/extractors/graphManifestEdges.d.ts +0 -12
- package/dist/extractors/graphManifestEdges.js +0 -1135
|
@@ -1,1135 +0,0 @@
|
|
|
1
|
-
import { posix } from "node:path";
|
|
2
|
-
import { graphEdge, normalizeGraphPath, resolveCandidate, isCargoManifestPath, isGoModuleManifestPath, isGoWorkspaceManifestPath, isMavenPomPath, isPackageManifestPath, isPnpmWorkspaceManifestPath, isPyprojectPath, isTypescriptProjectConfigPath, } from "./graphPathUtils.js";
|
|
3
|
-
// Re-exported for the graph builder, which imports these manifest predicates
|
|
4
|
-
// from here for historical reasons; the canonical definitions live in
|
|
5
|
-
// graphPathUtils.
|
|
6
|
-
export { isCargoManifestPath, isGoWorkspaceManifestPath, isMavenPomPath, isPyprojectPath, };
|
|
7
|
-
const PACKAGE_ENTRYPOINT_EDGE_CONFIDENCE = 0.9;
|
|
8
|
-
const PACKAGE_SCRIPT_EDGE_CONFIDENCE = 0.88;
|
|
9
|
-
const WORKSPACE_PACKAGE_EDGE_CONFIDENCE = 0.86;
|
|
10
|
-
const TYPESCRIPT_PROJECT_REFERENCE_EDGE_CONFIDENCE = 0.87;
|
|
11
|
-
const GO_WORKSPACE_MODULE_EDGE_CONFIDENCE = 0.87;
|
|
12
|
-
const CARGO_WORKSPACE_MEMBER_EDGE_CONFIDENCE = 0.87;
|
|
13
|
-
const MAVEN_MODULE_EDGE_CONFIDENCE = 0.87;
|
|
14
|
-
const PACKAGE_SCRIPT_REFERENCE_PATTERN = /(?:^|[\s"'`])((?:\.{1,2}\/)?(?:[\w.-]+\/)*[\w.-]+\.(?:cjs|cts|js|jsx|mjs|mts|ts|tsx))(?:$|[\s"'`])/gi;
|
|
15
|
-
function collectPackageEntrypointValues(value, fieldPath, entries) {
|
|
16
|
-
if (typeof value === "string") {
|
|
17
|
-
if (value.trim().length > 0) {
|
|
18
|
-
entries.push({ field: fieldPath, specifier: value });
|
|
19
|
-
}
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
if (Array.isArray(value)) {
|
|
23
|
-
value.forEach((item, index) => collectPackageEntrypointValues(item, `${fieldPath}.${index}`, entries));
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
if (value === null || typeof value !== "object") {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
for (const [key, item] of Object.entries(value)) {
|
|
30
|
-
collectPackageEntrypointValues(item, `${fieldPath}.${key}`, entries);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function packageEntrypointCandidates(content) {
|
|
34
|
-
let parsed;
|
|
35
|
-
try {
|
|
36
|
-
parsed = JSON.parse(content);
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
return [];
|
|
40
|
-
}
|
|
41
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
42
|
-
return [];
|
|
43
|
-
}
|
|
44
|
-
const record = parsed;
|
|
45
|
-
const entries = [];
|
|
46
|
-
for (const field of ["main", "module", "types", "typings", "browser"]) {
|
|
47
|
-
collectPackageEntrypointValues(record[field], field, entries);
|
|
48
|
-
}
|
|
49
|
-
collectPackageEntrypointValues(record.bin, "bin", entries);
|
|
50
|
-
collectPackageEntrypointValues(record.exports, "exports", entries);
|
|
51
|
-
return entries;
|
|
52
|
-
}
|
|
53
|
-
function resolvePackageEntrypoint(packagePath, specifier, pathLookup) {
|
|
54
|
-
const normalizedSpecifier = normalizeGraphPath(specifier);
|
|
55
|
-
if (normalizedSpecifier.length === 0 ||
|
|
56
|
-
normalizedSpecifier.startsWith("/") ||
|
|
57
|
-
/^[a-z][a-z0-9+.-]*:/i.test(normalizedSpecifier)) {
|
|
58
|
-
return undefined;
|
|
59
|
-
}
|
|
60
|
-
const packageDir = posix.dirname(normalizeGraphPath(packagePath));
|
|
61
|
-
const packageRelative = packageDir === "."
|
|
62
|
-
? normalizedSpecifier
|
|
63
|
-
: posix.join(packageDir, normalizedSpecifier);
|
|
64
|
-
return resolveCandidate(packageRelative, pathLookup);
|
|
65
|
-
}
|
|
66
|
-
export function extractPackageEntrypointEdges(fromPath, content, pathLookup) {
|
|
67
|
-
if (!isPackageManifestPath(fromPath)) {
|
|
68
|
-
return [];
|
|
69
|
-
}
|
|
70
|
-
const edges = [];
|
|
71
|
-
for (const { field, specifier } of packageEntrypointCandidates(content)) {
|
|
72
|
-
const target = resolvePackageEntrypoint(fromPath, specifier, pathLookup);
|
|
73
|
-
if (!target) {
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
edges.push(graphEdge({
|
|
77
|
-
from: fromPath,
|
|
78
|
-
to: target,
|
|
79
|
-
kind: "package-entrypoint-link",
|
|
80
|
-
confidence: PACKAGE_ENTRYPOINT_EDGE_CONFIDENCE,
|
|
81
|
-
reason: `Package manifest field '${field}' points to '${specifier}'.`,
|
|
82
|
-
}));
|
|
83
|
-
}
|
|
84
|
-
return edges;
|
|
85
|
-
}
|
|
86
|
-
function packageScriptCandidates(content) {
|
|
87
|
-
let parsed;
|
|
88
|
-
try {
|
|
89
|
-
parsed = JSON.parse(content);
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
return [];
|
|
93
|
-
}
|
|
94
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
95
|
-
return [];
|
|
96
|
-
}
|
|
97
|
-
const scripts = parsed.scripts;
|
|
98
|
-
if (scripts === null ||
|
|
99
|
-
typeof scripts !== "object" ||
|
|
100
|
-
Array.isArray(scripts)) {
|
|
101
|
-
return [];
|
|
102
|
-
}
|
|
103
|
-
const entries = [];
|
|
104
|
-
for (const [script, command] of Object.entries(scripts)) {
|
|
105
|
-
if (typeof command !== "string") {
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
PACKAGE_SCRIPT_REFERENCE_PATTERN.lastIndex = 0;
|
|
109
|
-
for (const match of command.matchAll(PACKAGE_SCRIPT_REFERENCE_PATTERN)) {
|
|
110
|
-
const specifier = match[1]?.trim();
|
|
111
|
-
if (specifier) {
|
|
112
|
-
entries.push({ script, specifier });
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return entries;
|
|
117
|
-
}
|
|
118
|
-
export function extractPackageScriptEdges(fromPath, content, pathLookup) {
|
|
119
|
-
if (!isPackageManifestPath(fromPath)) {
|
|
120
|
-
return [];
|
|
121
|
-
}
|
|
122
|
-
const edges = [];
|
|
123
|
-
for (const { script, specifier } of packageScriptCandidates(content)) {
|
|
124
|
-
const target = resolvePackageEntrypoint(fromPath, specifier, pathLookup);
|
|
125
|
-
if (!target) {
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
edges.push(graphEdge({
|
|
129
|
-
from: fromPath,
|
|
130
|
-
to: target,
|
|
131
|
-
kind: "package-script-link",
|
|
132
|
-
confidence: PACKAGE_SCRIPT_EDGE_CONFIDENCE,
|
|
133
|
-
reason: `Package script '${script}' references '${specifier}'.`,
|
|
134
|
-
}));
|
|
135
|
-
}
|
|
136
|
-
return edges;
|
|
137
|
-
}
|
|
138
|
-
function addWorkspacePattern(patterns, rawPattern) {
|
|
139
|
-
const trimmedPattern = rawPattern.trim();
|
|
140
|
-
if (trimmedPattern.length === 0) {
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
const negated = trimmedPattern.startsWith("!");
|
|
144
|
-
const pattern = negated ? trimmedPattern.slice(1).trim() : trimmedPattern;
|
|
145
|
-
if (pattern.length > 0) {
|
|
146
|
-
patterns.push({ pattern, negated });
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
function collectWorkspacePatternValues(value, patterns) {
|
|
150
|
-
if (!Array.isArray(value)) {
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
for (const item of value) {
|
|
154
|
-
if (typeof item !== "string") {
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
addWorkspacePattern(patterns, item);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
function packageWorkspacePatterns(content) {
|
|
161
|
-
let parsed;
|
|
162
|
-
try {
|
|
163
|
-
parsed = JSON.parse(content);
|
|
164
|
-
}
|
|
165
|
-
catch {
|
|
166
|
-
return [];
|
|
167
|
-
}
|
|
168
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
169
|
-
return [];
|
|
170
|
-
}
|
|
171
|
-
const record = parsed;
|
|
172
|
-
const patterns = [];
|
|
173
|
-
collectWorkspacePatternValues(record.workspaces, patterns);
|
|
174
|
-
if (record.workspaces !== null &&
|
|
175
|
-
typeof record.workspaces === "object" &&
|
|
176
|
-
!Array.isArray(record.workspaces)) {
|
|
177
|
-
collectWorkspacePatternValues(record.workspaces.packages, patterns);
|
|
178
|
-
}
|
|
179
|
-
return patterns;
|
|
180
|
-
}
|
|
181
|
-
function stripJsonComments(content) {
|
|
182
|
-
let result = "";
|
|
183
|
-
let inString = false;
|
|
184
|
-
let escaped = false;
|
|
185
|
-
for (let index = 0; index < content.length; index++) {
|
|
186
|
-
const char = content[index];
|
|
187
|
-
const next = content[index + 1];
|
|
188
|
-
if (inString) {
|
|
189
|
-
result += char;
|
|
190
|
-
if (escaped) {
|
|
191
|
-
escaped = false;
|
|
192
|
-
}
|
|
193
|
-
else if (char === "\\") {
|
|
194
|
-
escaped = true;
|
|
195
|
-
}
|
|
196
|
-
else if (char === "\"") {
|
|
197
|
-
inString = false;
|
|
198
|
-
}
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
if (char === "\"") {
|
|
202
|
-
inString = true;
|
|
203
|
-
result += char;
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
if (char === "/" && next === "/") {
|
|
207
|
-
while (index < content.length && content[index] !== "\n") {
|
|
208
|
-
index++;
|
|
209
|
-
}
|
|
210
|
-
if (index < content.length) {
|
|
211
|
-
result += content[index];
|
|
212
|
-
}
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
if (char === "/" && next === "*") {
|
|
216
|
-
index += 2;
|
|
217
|
-
while (index < content.length &&
|
|
218
|
-
!(content[index] === "*" && content[index + 1] === "/")) {
|
|
219
|
-
if (content[index] === "\n") {
|
|
220
|
-
result += "\n";
|
|
221
|
-
}
|
|
222
|
-
index++;
|
|
223
|
-
}
|
|
224
|
-
if (index < content.length) {
|
|
225
|
-
index++;
|
|
226
|
-
}
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
result += char;
|
|
230
|
-
}
|
|
231
|
-
return result;
|
|
232
|
-
}
|
|
233
|
-
function removeTrailingJsonCommas(content) {
|
|
234
|
-
let result = "";
|
|
235
|
-
let inString = false;
|
|
236
|
-
let escaped = false;
|
|
237
|
-
for (let index = 0; index < content.length; index++) {
|
|
238
|
-
const char = content[index];
|
|
239
|
-
if (inString) {
|
|
240
|
-
result += char;
|
|
241
|
-
if (escaped) {
|
|
242
|
-
escaped = false;
|
|
243
|
-
}
|
|
244
|
-
else if (char === "\\") {
|
|
245
|
-
escaped = true;
|
|
246
|
-
}
|
|
247
|
-
else if (char === "\"") {
|
|
248
|
-
inString = false;
|
|
249
|
-
}
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
if (char === "\"") {
|
|
253
|
-
inString = true;
|
|
254
|
-
result += char;
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
if (char === ",") {
|
|
258
|
-
let lookahead = index + 1;
|
|
259
|
-
while (/\s/.test(content[lookahead] ?? "")) {
|
|
260
|
-
lookahead++;
|
|
261
|
-
}
|
|
262
|
-
if (content[lookahead] === "}" || content[lookahead] === "]") {
|
|
263
|
-
continue;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
result += char;
|
|
267
|
-
}
|
|
268
|
-
return result;
|
|
269
|
-
}
|
|
270
|
-
function parseJsoncObject(content) {
|
|
271
|
-
let parsed;
|
|
272
|
-
try {
|
|
273
|
-
parsed = JSON.parse(removeTrailingJsonCommas(stripJsonComments(content)));
|
|
274
|
-
}
|
|
275
|
-
catch {
|
|
276
|
-
return undefined;
|
|
277
|
-
}
|
|
278
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
279
|
-
return undefined;
|
|
280
|
-
}
|
|
281
|
-
return parsed;
|
|
282
|
-
}
|
|
283
|
-
function stripYamlComment(line) {
|
|
284
|
-
let quote;
|
|
285
|
-
for (let index = 0; index < line.length; index++) {
|
|
286
|
-
const char = line[index];
|
|
287
|
-
if (quote) {
|
|
288
|
-
if (char === quote) {
|
|
289
|
-
quote = undefined;
|
|
290
|
-
}
|
|
291
|
-
continue;
|
|
292
|
-
}
|
|
293
|
-
if (char === '"' || char === "'") {
|
|
294
|
-
quote = char;
|
|
295
|
-
continue;
|
|
296
|
-
}
|
|
297
|
-
if (char === "#") {
|
|
298
|
-
return line.slice(0, index);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
return line;
|
|
302
|
-
}
|
|
303
|
-
function unquoteYamlScalar(value) {
|
|
304
|
-
const trimmed = value.trim();
|
|
305
|
-
if (trimmed.length < 2) {
|
|
306
|
-
return trimmed;
|
|
307
|
-
}
|
|
308
|
-
const quote = trimmed[0];
|
|
309
|
-
if ((quote === '"' || quote === "'") && trimmed.at(-1) === quote) {
|
|
310
|
-
return trimmed.slice(1, -1).trim();
|
|
311
|
-
}
|
|
312
|
-
return trimmed;
|
|
313
|
-
}
|
|
314
|
-
function splitYamlInlineList(value) {
|
|
315
|
-
const trimmed = value.trim();
|
|
316
|
-
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
|
|
317
|
-
return [];
|
|
318
|
-
}
|
|
319
|
-
const values = [];
|
|
320
|
-
let quote;
|
|
321
|
-
let start = 1;
|
|
322
|
-
for (let index = 1; index < trimmed.length - 1; index++) {
|
|
323
|
-
const char = trimmed[index];
|
|
324
|
-
if (quote) {
|
|
325
|
-
if (char === quote) {
|
|
326
|
-
quote = undefined;
|
|
327
|
-
}
|
|
328
|
-
continue;
|
|
329
|
-
}
|
|
330
|
-
if (char === '"' || char === "'") {
|
|
331
|
-
quote = char;
|
|
332
|
-
continue;
|
|
333
|
-
}
|
|
334
|
-
if (char === ",") {
|
|
335
|
-
values.push(unquoteYamlScalar(trimmed.slice(start, index)));
|
|
336
|
-
start = index + 1;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
values.push(unquoteYamlScalar(trimmed.slice(start, -1)));
|
|
340
|
-
return values.filter((item) => item.length > 0);
|
|
341
|
-
}
|
|
342
|
-
function pnpmWorkspacePatterns(content) {
|
|
343
|
-
const patterns = [];
|
|
344
|
-
const lines = content.split(/\r?\n/);
|
|
345
|
-
let inPackagesList = false;
|
|
346
|
-
let packagesIndent = 0;
|
|
347
|
-
for (const line of lines) {
|
|
348
|
-
const withoutComment = stripYamlComment(line);
|
|
349
|
-
if (withoutComment.trim().length === 0) {
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
|
-
const indent = withoutComment.match(/^\s*/)?.[0].length ?? 0;
|
|
353
|
-
const trimmed = withoutComment.trim();
|
|
354
|
-
if (inPackagesList) {
|
|
355
|
-
if (indent <= packagesIndent) {
|
|
356
|
-
inPackagesList = false;
|
|
357
|
-
}
|
|
358
|
-
else {
|
|
359
|
-
const itemMatch = /^-\s+(.+)$/.exec(trimmed);
|
|
360
|
-
if (itemMatch?.[1]) {
|
|
361
|
-
addWorkspacePattern(patterns, unquoteYamlScalar(itemMatch[1]));
|
|
362
|
-
}
|
|
363
|
-
continue;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
const packagesMatch = /^packages\s*:\s*(.*)$/.exec(trimmed);
|
|
367
|
-
if (!packagesMatch || indent !== 0) {
|
|
368
|
-
continue;
|
|
369
|
-
}
|
|
370
|
-
const inlineValue = packagesMatch[1]?.trim() ?? "";
|
|
371
|
-
if (inlineValue.length === 0) {
|
|
372
|
-
inPackagesList = true;
|
|
373
|
-
packagesIndent = indent;
|
|
374
|
-
continue;
|
|
375
|
-
}
|
|
376
|
-
for (const pattern of splitYamlInlineList(inlineValue)) {
|
|
377
|
-
addWorkspacePattern(patterns, pattern);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
return patterns;
|
|
381
|
-
}
|
|
382
|
-
function stripTomlComment(line) {
|
|
383
|
-
let quote;
|
|
384
|
-
let escaped = false;
|
|
385
|
-
for (let index = 0; index < line.length; index++) {
|
|
386
|
-
const char = line[index];
|
|
387
|
-
if (quote) {
|
|
388
|
-
if (quote === '"' && escaped) {
|
|
389
|
-
escaped = false;
|
|
390
|
-
}
|
|
391
|
-
else if (quote === '"' && char === "\\") {
|
|
392
|
-
escaped = true;
|
|
393
|
-
}
|
|
394
|
-
else if (char === quote) {
|
|
395
|
-
quote = undefined;
|
|
396
|
-
}
|
|
397
|
-
continue;
|
|
398
|
-
}
|
|
399
|
-
if (char === '"' || char === "'") {
|
|
400
|
-
quote = char;
|
|
401
|
-
continue;
|
|
402
|
-
}
|
|
403
|
-
if (char === "#") {
|
|
404
|
-
return line.slice(0, index);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
return line;
|
|
408
|
-
}
|
|
409
|
-
function tomlArrayIsClosed(value) {
|
|
410
|
-
let quote;
|
|
411
|
-
let escaped = false;
|
|
412
|
-
let depth = 0;
|
|
413
|
-
for (const char of value) {
|
|
414
|
-
if (quote) {
|
|
415
|
-
if (quote === '"' && escaped) {
|
|
416
|
-
escaped = false;
|
|
417
|
-
}
|
|
418
|
-
else if (quote === '"' && char === "\\") {
|
|
419
|
-
escaped = true;
|
|
420
|
-
}
|
|
421
|
-
else if (char === quote) {
|
|
422
|
-
quote = undefined;
|
|
423
|
-
}
|
|
424
|
-
continue;
|
|
425
|
-
}
|
|
426
|
-
if (char === '"' || char === "'") {
|
|
427
|
-
quote = char;
|
|
428
|
-
continue;
|
|
429
|
-
}
|
|
430
|
-
if (char === "[") {
|
|
431
|
-
depth += 1;
|
|
432
|
-
}
|
|
433
|
-
else if (char === "]") {
|
|
434
|
-
depth -= 1;
|
|
435
|
-
if (depth <= 0) {
|
|
436
|
-
return true;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
return false;
|
|
441
|
-
}
|
|
442
|
-
function unquoteTomlString(value, quote) {
|
|
443
|
-
if (quote === "'") {
|
|
444
|
-
return value.trim();
|
|
445
|
-
}
|
|
446
|
-
try {
|
|
447
|
-
const parsed = JSON.parse(`"${value}"`);
|
|
448
|
-
return typeof parsed === "string" ? parsed.trim() : value.trim();
|
|
449
|
-
}
|
|
450
|
-
catch {
|
|
451
|
-
return value.replace(/\\"/g, "\"").trim();
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
function tomlStringArrayValues(value) {
|
|
455
|
-
const values = [];
|
|
456
|
-
let quote;
|
|
457
|
-
let escaped = false;
|
|
458
|
-
let start = 0;
|
|
459
|
-
for (let index = 0; index < value.length; index++) {
|
|
460
|
-
const char = value[index];
|
|
461
|
-
if (quote) {
|
|
462
|
-
if (quote === '"' && escaped) {
|
|
463
|
-
escaped = false;
|
|
464
|
-
}
|
|
465
|
-
else if (quote === '"' && char === "\\") {
|
|
466
|
-
escaped = true;
|
|
467
|
-
}
|
|
468
|
-
else if (char === quote) {
|
|
469
|
-
const item = unquoteTomlString(value.slice(start, index), quote);
|
|
470
|
-
if (item.length > 0) {
|
|
471
|
-
values.push(item);
|
|
472
|
-
}
|
|
473
|
-
quote = undefined;
|
|
474
|
-
}
|
|
475
|
-
continue;
|
|
476
|
-
}
|
|
477
|
-
if (char === '"' || char === "'") {
|
|
478
|
-
quote = char;
|
|
479
|
-
start = index + 1;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
return values;
|
|
483
|
-
}
|
|
484
|
-
function cargoWorkspacePatterns(content) {
|
|
485
|
-
const patterns = [];
|
|
486
|
-
let currentSection;
|
|
487
|
-
let collectingKey;
|
|
488
|
-
let collectedValue = "";
|
|
489
|
-
const flushCollectedValue = () => {
|
|
490
|
-
if (!collectingKey) {
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
|
-
for (const value of tomlStringArrayValues(collectedValue)) {
|
|
494
|
-
addWorkspacePattern(patterns, collectingKey === "exclude" ? `!${value}` : value);
|
|
495
|
-
}
|
|
496
|
-
collectingKey = undefined;
|
|
497
|
-
collectedValue = "";
|
|
498
|
-
};
|
|
499
|
-
for (const rawLine of content.split(/\r?\n/)) {
|
|
500
|
-
const withoutComment = stripTomlComment(rawLine);
|
|
501
|
-
const trimmed = withoutComment.trim();
|
|
502
|
-
if (trimmed.length === 0) {
|
|
503
|
-
continue;
|
|
504
|
-
}
|
|
505
|
-
const sectionMatch = /^\[([^\]]+)\]\s*$/.exec(trimmed);
|
|
506
|
-
if (sectionMatch?.[1]) {
|
|
507
|
-
if (collectingKey) {
|
|
508
|
-
flushCollectedValue();
|
|
509
|
-
}
|
|
510
|
-
currentSection = sectionMatch[1].trim();
|
|
511
|
-
continue;
|
|
512
|
-
}
|
|
513
|
-
if (collectingKey) {
|
|
514
|
-
collectedValue = `${collectedValue}\n${trimmed}`;
|
|
515
|
-
if (tomlArrayIsClosed(collectedValue)) {
|
|
516
|
-
flushCollectedValue();
|
|
517
|
-
}
|
|
518
|
-
continue;
|
|
519
|
-
}
|
|
520
|
-
if (currentSection !== "workspace") {
|
|
521
|
-
continue;
|
|
522
|
-
}
|
|
523
|
-
const arrayMatch = /^(members|exclude)\s*=\s*(.+)$/.exec(trimmed);
|
|
524
|
-
if (!arrayMatch?.[1] || !arrayMatch[2]) {
|
|
525
|
-
continue;
|
|
526
|
-
}
|
|
527
|
-
const value = arrayMatch[2].trim();
|
|
528
|
-
if (!value.startsWith("[")) {
|
|
529
|
-
continue;
|
|
530
|
-
}
|
|
531
|
-
collectingKey = arrayMatch[1];
|
|
532
|
-
collectedValue = value;
|
|
533
|
-
if (tomlArrayIsClosed(collectedValue)) {
|
|
534
|
-
flushCollectedValue();
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
if (collectingKey) {
|
|
538
|
-
flushCollectedValue();
|
|
539
|
-
}
|
|
540
|
-
return patterns;
|
|
541
|
-
}
|
|
542
|
-
function workspacePatternsForFile(path, content) {
|
|
543
|
-
if (isPackageManifestPath(path)) {
|
|
544
|
-
return packageWorkspacePatterns(content);
|
|
545
|
-
}
|
|
546
|
-
if (isPnpmWorkspaceManifestPath(path)) {
|
|
547
|
-
return pnpmWorkspacePatterns(content);
|
|
548
|
-
}
|
|
549
|
-
return [];
|
|
550
|
-
}
|
|
551
|
-
function normalizeWorkspacePattern(workspacePath, pattern) {
|
|
552
|
-
const normalizedPattern = pattern
|
|
553
|
-
.replace(/\\/g, "/")
|
|
554
|
-
.replace(/^\.\//, "")
|
|
555
|
-
.replace(/\/+$/, "");
|
|
556
|
-
if (normalizedPattern.length === 0 ||
|
|
557
|
-
normalizedPattern.startsWith("/") ||
|
|
558
|
-
/^[a-z][a-z0-9+.-]*:/i.test(normalizedPattern)) {
|
|
559
|
-
return undefined;
|
|
560
|
-
}
|
|
561
|
-
const workspaceDir = posix.dirname(normalizeGraphPath(workspacePath));
|
|
562
|
-
return workspaceDir === "."
|
|
563
|
-
? normalizedPattern
|
|
564
|
-
: posix.join(workspaceDir, normalizedPattern);
|
|
565
|
-
}
|
|
566
|
-
function globPatternToRegExp(pattern) {
|
|
567
|
-
let source = "^";
|
|
568
|
-
for (let index = 0; index < pattern.length; index++) {
|
|
569
|
-
const char = pattern[index];
|
|
570
|
-
const next = pattern[index + 1];
|
|
571
|
-
if (char === "*" && next === "*") {
|
|
572
|
-
source += ".*";
|
|
573
|
-
index++;
|
|
574
|
-
}
|
|
575
|
-
else if (char === "*") {
|
|
576
|
-
source += "[^/]*";
|
|
577
|
-
}
|
|
578
|
-
else if (char === "?") {
|
|
579
|
-
source += "[^/]";
|
|
580
|
-
}
|
|
581
|
-
else {
|
|
582
|
-
source += char.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
return new RegExp(`${source}$`, "i");
|
|
586
|
-
}
|
|
587
|
-
function workspacePatternMatchesPackage(workspacePattern, packagePath) {
|
|
588
|
-
return workspacePatternMatchesManifest(workspacePattern, packagePath, "package.json");
|
|
589
|
-
}
|
|
590
|
-
function workspacePatternMatchesManifest(workspacePattern, manifestPath, manifestName) {
|
|
591
|
-
const normalizedManifestPath = normalizeGraphPath(manifestPath);
|
|
592
|
-
const manifestDir = posix.dirname(normalizedManifestPath);
|
|
593
|
-
const lowerManifestPattern = `/${manifestName.toLowerCase()}`;
|
|
594
|
-
const patternTarget = workspacePattern.toLowerCase().endsWith(lowerManifestPattern)
|
|
595
|
-
? normalizedManifestPath
|
|
596
|
-
: manifestDir;
|
|
597
|
-
return globPatternToRegExp(workspacePattern).test(patternTarget);
|
|
598
|
-
}
|
|
599
|
-
export function extractWorkspacePackageEdges(fromPath, content, pathLookup) {
|
|
600
|
-
const rawPatterns = workspacePatternsForFile(fromPath, content);
|
|
601
|
-
if (rawPatterns.length === 0) {
|
|
602
|
-
return [];
|
|
603
|
-
}
|
|
604
|
-
const positivePatterns = [];
|
|
605
|
-
const negativePatterns = [];
|
|
606
|
-
for (const { pattern, negated } of rawPatterns) {
|
|
607
|
-
const normalized = normalizeWorkspacePattern(fromPath, pattern);
|
|
608
|
-
if (!normalized) {
|
|
609
|
-
continue;
|
|
610
|
-
}
|
|
611
|
-
if (negated) {
|
|
612
|
-
negativePatterns.push(normalized);
|
|
613
|
-
}
|
|
614
|
-
else {
|
|
615
|
-
positivePatterns.push(normalized);
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
const edges = [];
|
|
619
|
-
for (const pattern of positivePatterns) {
|
|
620
|
-
for (const target of pathLookup.values()) {
|
|
621
|
-
if (target === fromPath || !isPackageManifestPath(target)) {
|
|
622
|
-
continue;
|
|
623
|
-
}
|
|
624
|
-
if (!workspacePatternMatchesPackage(pattern, target)) {
|
|
625
|
-
continue;
|
|
626
|
-
}
|
|
627
|
-
if (negativePatterns.some((negativePattern) => workspacePatternMatchesPackage(negativePattern, target))) {
|
|
628
|
-
continue;
|
|
629
|
-
}
|
|
630
|
-
edges.push(graphEdge({
|
|
631
|
-
from: fromPath,
|
|
632
|
-
to: target,
|
|
633
|
-
kind: "workspace-package-link",
|
|
634
|
-
confidence: WORKSPACE_PACKAGE_EDGE_CONFIDENCE,
|
|
635
|
-
reason: `Workspace pattern '${pattern}' includes package manifest '${target}'.`,
|
|
636
|
-
}));
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
return edges;
|
|
640
|
-
}
|
|
641
|
-
export function extractCargoWorkspaceMemberEdges(fromPath, content, pathLookup) {
|
|
642
|
-
if (!isCargoManifestPath(fromPath)) {
|
|
643
|
-
return [];
|
|
644
|
-
}
|
|
645
|
-
const rawPatterns = cargoWorkspacePatterns(content);
|
|
646
|
-
if (rawPatterns.length === 0) {
|
|
647
|
-
return [];
|
|
648
|
-
}
|
|
649
|
-
const positivePatterns = [];
|
|
650
|
-
const negativePatterns = [];
|
|
651
|
-
for (const { pattern, negated } of rawPatterns) {
|
|
652
|
-
const normalized = normalizeWorkspacePattern(fromPath, pattern);
|
|
653
|
-
if (!normalized) {
|
|
654
|
-
continue;
|
|
655
|
-
}
|
|
656
|
-
if (negated) {
|
|
657
|
-
negativePatterns.push(normalized);
|
|
658
|
-
}
|
|
659
|
-
else {
|
|
660
|
-
positivePatterns.push(normalized);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
const edges = [];
|
|
664
|
-
for (const pattern of positivePatterns) {
|
|
665
|
-
for (const target of pathLookup.values()) {
|
|
666
|
-
if (target === fromPath || !isCargoManifestPath(target)) {
|
|
667
|
-
continue;
|
|
668
|
-
}
|
|
669
|
-
if (!workspacePatternMatchesManifest(pattern, target, "Cargo.toml")) {
|
|
670
|
-
continue;
|
|
671
|
-
}
|
|
672
|
-
if (negativePatterns.some((negativePattern) => workspacePatternMatchesManifest(negativePattern, target, "Cargo.toml"))) {
|
|
673
|
-
continue;
|
|
674
|
-
}
|
|
675
|
-
edges.push(graphEdge({
|
|
676
|
-
from: fromPath,
|
|
677
|
-
to: target,
|
|
678
|
-
kind: "cargo-workspace-member-link",
|
|
679
|
-
confidence: CARGO_WORKSPACE_MEMBER_EDGE_CONFIDENCE,
|
|
680
|
-
reason: `Cargo workspace pattern '${pattern}' includes member manifest '${target}'.`,
|
|
681
|
-
}));
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
return edges;
|
|
685
|
-
}
|
|
686
|
-
function typescriptProjectReferenceSpecifiers(content) {
|
|
687
|
-
const parsed = parseJsoncObject(content);
|
|
688
|
-
if (!parsed || !Array.isArray(parsed.references)) {
|
|
689
|
-
return [];
|
|
690
|
-
}
|
|
691
|
-
return parsed.references
|
|
692
|
-
.map((item) => {
|
|
693
|
-
if (item === null || typeof item !== "object" || Array.isArray(item)) {
|
|
694
|
-
return undefined;
|
|
695
|
-
}
|
|
696
|
-
const referencePath = item.path;
|
|
697
|
-
return typeof referencePath === "string" ? referencePath.trim() : undefined;
|
|
698
|
-
})
|
|
699
|
-
.filter((specifier) => Boolean(specifier));
|
|
700
|
-
}
|
|
701
|
-
function resolveTypescriptProjectReference(fromPath, specifier, pathLookup) {
|
|
702
|
-
const normalizedSpecifier = normalizeGraphPath(specifier);
|
|
703
|
-
if (normalizedSpecifier.length === 0 ||
|
|
704
|
-
normalizedSpecifier.startsWith("/") ||
|
|
705
|
-
/^[a-z][a-z0-9+.-]*:/i.test(normalizedSpecifier)) {
|
|
706
|
-
return undefined;
|
|
707
|
-
}
|
|
708
|
-
const configDir = posix.dirname(normalizeGraphPath(fromPath));
|
|
709
|
-
const target = configDir === "."
|
|
710
|
-
? normalizedSpecifier
|
|
711
|
-
: posix.join(configDir, normalizedSpecifier);
|
|
712
|
-
const direct = resolveCandidate(target, pathLookup);
|
|
713
|
-
if (direct && isTypescriptProjectConfigPath(direct)) {
|
|
714
|
-
return direct;
|
|
715
|
-
}
|
|
716
|
-
return pathLookup.get(posix.join(target, "tsconfig.json").toLowerCase());
|
|
717
|
-
}
|
|
718
|
-
export function extractTypescriptProjectReferenceEdges(fromPath, content, pathLookup) {
|
|
719
|
-
if (!isTypescriptProjectConfigPath(fromPath)) {
|
|
720
|
-
return [];
|
|
721
|
-
}
|
|
722
|
-
const edges = [];
|
|
723
|
-
for (const specifier of typescriptProjectReferenceSpecifiers(content)) {
|
|
724
|
-
const target = resolveTypescriptProjectReference(fromPath, specifier, pathLookup);
|
|
725
|
-
if (!target) {
|
|
726
|
-
continue;
|
|
727
|
-
}
|
|
728
|
-
edges.push(graphEdge({
|
|
729
|
-
from: fromPath,
|
|
730
|
-
to: target,
|
|
731
|
-
kind: "typescript-project-reference-link",
|
|
732
|
-
confidence: TYPESCRIPT_PROJECT_REFERENCE_EDGE_CONFIDENCE,
|
|
733
|
-
reason: `TypeScript project reference '${specifier}' resolves to '${target}'.`,
|
|
734
|
-
}));
|
|
735
|
-
}
|
|
736
|
-
return edges;
|
|
737
|
-
}
|
|
738
|
-
function stripGoLineComment(line) {
|
|
739
|
-
let quote;
|
|
740
|
-
let escaped = false;
|
|
741
|
-
for (let index = 0; index < line.length; index++) {
|
|
742
|
-
const char = line[index];
|
|
743
|
-
const next = line[index + 1];
|
|
744
|
-
if (quote) {
|
|
745
|
-
if (quote === '"' && escaped) {
|
|
746
|
-
escaped = false;
|
|
747
|
-
}
|
|
748
|
-
else if (quote === '"' && char === "\\") {
|
|
749
|
-
escaped = true;
|
|
750
|
-
}
|
|
751
|
-
else if (char === quote) {
|
|
752
|
-
quote = undefined;
|
|
753
|
-
}
|
|
754
|
-
continue;
|
|
755
|
-
}
|
|
756
|
-
if (char === '"' || char === "`") {
|
|
757
|
-
quote = char;
|
|
758
|
-
continue;
|
|
759
|
-
}
|
|
760
|
-
if (char === "/" && next === "/") {
|
|
761
|
-
return line.slice(0, index);
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
return line;
|
|
765
|
-
}
|
|
766
|
-
function unquoteGoWorkspaceSpecifier(value) {
|
|
767
|
-
const trimmed = value.trim();
|
|
768
|
-
if (trimmed.length < 2) {
|
|
769
|
-
return trimmed;
|
|
770
|
-
}
|
|
771
|
-
if (trimmed[0] === '"' && trimmed.at(-1) === '"') {
|
|
772
|
-
try {
|
|
773
|
-
const parsed = JSON.parse(trimmed);
|
|
774
|
-
return typeof parsed === "string" ? parsed.trim() : trimmed;
|
|
775
|
-
}
|
|
776
|
-
catch {
|
|
777
|
-
return trimmed.slice(1, -1).trim();
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
if (trimmed[0] === "`" && trimmed.at(-1) === "`") {
|
|
781
|
-
return trimmed.slice(1, -1).trim();
|
|
782
|
-
}
|
|
783
|
-
return trimmed;
|
|
784
|
-
}
|
|
785
|
-
function splitGoWorkspaceSpecifiers(value) {
|
|
786
|
-
const specifiers = [];
|
|
787
|
-
let quote;
|
|
788
|
-
let escaped = false;
|
|
789
|
-
let start;
|
|
790
|
-
for (let index = 0; index <= value.length; index++) {
|
|
791
|
-
const char = value[index] ?? " ";
|
|
792
|
-
if (quote) {
|
|
793
|
-
if (quote === '"' && escaped) {
|
|
794
|
-
escaped = false;
|
|
795
|
-
}
|
|
796
|
-
else if (quote === '"' && char === "\\") {
|
|
797
|
-
escaped = true;
|
|
798
|
-
}
|
|
799
|
-
else if (char === quote) {
|
|
800
|
-
quote = undefined;
|
|
801
|
-
}
|
|
802
|
-
continue;
|
|
803
|
-
}
|
|
804
|
-
if (char === '"' || char === "`") {
|
|
805
|
-
quote = char;
|
|
806
|
-
start ??= index;
|
|
807
|
-
continue;
|
|
808
|
-
}
|
|
809
|
-
if (/\s/.test(char)) {
|
|
810
|
-
if (start !== undefined) {
|
|
811
|
-
const specifier = unquoteGoWorkspaceSpecifier(value.slice(start, index));
|
|
812
|
-
if (specifier.length > 0) {
|
|
813
|
-
specifiers.push(specifier);
|
|
814
|
-
}
|
|
815
|
-
start = undefined;
|
|
816
|
-
}
|
|
817
|
-
continue;
|
|
818
|
-
}
|
|
819
|
-
start ??= index;
|
|
820
|
-
}
|
|
821
|
-
return specifiers;
|
|
822
|
-
}
|
|
823
|
-
function goWorkspaceUseSpecifiers(content) {
|
|
824
|
-
const specifiers = [];
|
|
825
|
-
let inUseBlock = false;
|
|
826
|
-
for (const rawLine of content.split(/\r?\n/)) {
|
|
827
|
-
const trimmed = stripGoLineComment(rawLine).trim();
|
|
828
|
-
if (trimmed.length === 0) {
|
|
829
|
-
continue;
|
|
830
|
-
}
|
|
831
|
-
if (inUseBlock) {
|
|
832
|
-
const closeIndex = trimmed.indexOf(")");
|
|
833
|
-
const body = closeIndex >= 0 ? trimmed.slice(0, closeIndex).trim() : trimmed;
|
|
834
|
-
specifiers.push(...splitGoWorkspaceSpecifiers(body));
|
|
835
|
-
if (closeIndex >= 0) {
|
|
836
|
-
inUseBlock = false;
|
|
837
|
-
}
|
|
838
|
-
continue;
|
|
839
|
-
}
|
|
840
|
-
const useMatch = /^use(?:\s+(.+)|\s*)$/.exec(trimmed);
|
|
841
|
-
if (!useMatch) {
|
|
842
|
-
continue;
|
|
843
|
-
}
|
|
844
|
-
let body = useMatch[1]?.trim() ?? "";
|
|
845
|
-
if (!body.startsWith("(")) {
|
|
846
|
-
specifiers.push(...splitGoWorkspaceSpecifiers(body));
|
|
847
|
-
continue;
|
|
848
|
-
}
|
|
849
|
-
body = body.slice(1).trim();
|
|
850
|
-
const closeIndex = body.indexOf(")");
|
|
851
|
-
if (closeIndex >= 0) {
|
|
852
|
-
specifiers.push(...splitGoWorkspaceSpecifiers(body.slice(0, closeIndex).trim()));
|
|
853
|
-
}
|
|
854
|
-
else {
|
|
855
|
-
specifiers.push(...splitGoWorkspaceSpecifiers(body));
|
|
856
|
-
inUseBlock = true;
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
return specifiers;
|
|
860
|
-
}
|
|
861
|
-
function resolveGoWorkspaceModuleReference(fromPath, specifier, pathLookup) {
|
|
862
|
-
const normalizedSpecifier = normalizeGraphPath(specifier);
|
|
863
|
-
if (normalizedSpecifier.length === 0 ||
|
|
864
|
-
normalizedSpecifier.startsWith("/") ||
|
|
865
|
-
/^[a-z][a-z0-9+.-]*:/i.test(normalizedSpecifier)) {
|
|
866
|
-
return undefined;
|
|
867
|
-
}
|
|
868
|
-
const workspaceDir = posix.dirname(normalizeGraphPath(fromPath));
|
|
869
|
-
const target = workspaceDir === "."
|
|
870
|
-
? normalizedSpecifier
|
|
871
|
-
: posix.join(workspaceDir, normalizedSpecifier);
|
|
872
|
-
const direct = pathLookup.get(target.toLowerCase());
|
|
873
|
-
if (direct && isGoModuleManifestPath(direct)) {
|
|
874
|
-
return direct;
|
|
875
|
-
}
|
|
876
|
-
return pathLookup.get(posix.join(target, "go.mod").toLowerCase());
|
|
877
|
-
}
|
|
878
|
-
export function extractGoWorkspaceModuleEdges(fromPath, content, pathLookup) {
|
|
879
|
-
if (!isGoWorkspaceManifestPath(fromPath)) {
|
|
880
|
-
return [];
|
|
881
|
-
}
|
|
882
|
-
const edges = [];
|
|
883
|
-
for (const specifier of goWorkspaceUseSpecifiers(content)) {
|
|
884
|
-
const target = resolveGoWorkspaceModuleReference(fromPath, specifier, pathLookup);
|
|
885
|
-
if (!target) {
|
|
886
|
-
continue;
|
|
887
|
-
}
|
|
888
|
-
edges.push(graphEdge({
|
|
889
|
-
from: fromPath,
|
|
890
|
-
to: target,
|
|
891
|
-
kind: "go-workspace-module-link",
|
|
892
|
-
confidence: GO_WORKSPACE_MODULE_EDGE_CONFIDENCE,
|
|
893
|
-
reason: `Go workspace use directive '${specifier}' resolves to module '${target}'.`,
|
|
894
|
-
}));
|
|
895
|
-
}
|
|
896
|
-
return edges;
|
|
897
|
-
}
|
|
898
|
-
function stripXmlComments(content) {
|
|
899
|
-
return content.replace(/<!--[\s\S]*?-->/g, "");
|
|
900
|
-
}
|
|
901
|
-
function decodeXmlText(value) {
|
|
902
|
-
return value
|
|
903
|
-
.replace(/</g, "<")
|
|
904
|
-
.replace(/>/g, ">")
|
|
905
|
-
.replace(/"/g, "\"")
|
|
906
|
-
.replace(/'/g, "'")
|
|
907
|
-
.replace(/&/g, "&");
|
|
908
|
-
}
|
|
909
|
-
function mavenModuleSpecifiers(content) {
|
|
910
|
-
const specifiers = [];
|
|
911
|
-
const withoutComments = stripXmlComments(content);
|
|
912
|
-
const modulesPattern = /<modules(?:\s[^>]*)?>([\s\S]*?)<\/modules>/gi;
|
|
913
|
-
let modulesMatch;
|
|
914
|
-
while ((modulesMatch = modulesPattern.exec(withoutComments))) {
|
|
915
|
-
const body = modulesMatch[1] ?? "";
|
|
916
|
-
const modulePattern = /<module(?:\s[^>]*)?>([\s\S]*?)<\/module>/gi;
|
|
917
|
-
let moduleMatch;
|
|
918
|
-
while ((moduleMatch = modulePattern.exec(body))) {
|
|
919
|
-
const specifier = decodeXmlText(moduleMatch[1] ?? "").trim();
|
|
920
|
-
if (specifier.length > 0) {
|
|
921
|
-
specifiers.push(specifier);
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
return specifiers;
|
|
926
|
-
}
|
|
927
|
-
function resolveMavenModuleReference(fromPath, specifier, pathLookup) {
|
|
928
|
-
const normalizedSpecifier = normalizeGraphPath(specifier);
|
|
929
|
-
if (normalizedSpecifier.length === 0 ||
|
|
930
|
-
normalizedSpecifier.startsWith("/") ||
|
|
931
|
-
/^[a-z][a-z0-9+.-]*:/i.test(normalizedSpecifier)) {
|
|
932
|
-
return undefined;
|
|
933
|
-
}
|
|
934
|
-
const manifestDir = posix.dirname(normalizeGraphPath(fromPath));
|
|
935
|
-
const target = manifestDir === "."
|
|
936
|
-
? normalizedSpecifier
|
|
937
|
-
: posix.join(manifestDir, normalizedSpecifier);
|
|
938
|
-
const normalizedTarget = normalizeGraphPath(target);
|
|
939
|
-
if (normalizedTarget === ".." || normalizedTarget.startsWith("../")) {
|
|
940
|
-
return undefined;
|
|
941
|
-
}
|
|
942
|
-
const direct = pathLookup.get(normalizedTarget.toLowerCase());
|
|
943
|
-
if (direct && isMavenPomPath(direct)) {
|
|
944
|
-
return direct;
|
|
945
|
-
}
|
|
946
|
-
return pathLookup.get(posix.join(normalizedTarget, "pom.xml").toLowerCase());
|
|
947
|
-
}
|
|
948
|
-
export function extractMavenModuleEdges(fromPath, content, pathLookup) {
|
|
949
|
-
if (!isMavenPomPath(fromPath)) {
|
|
950
|
-
return [];
|
|
951
|
-
}
|
|
952
|
-
const edges = [];
|
|
953
|
-
for (const specifier of mavenModuleSpecifiers(content)) {
|
|
954
|
-
const target = resolveMavenModuleReference(fromPath, specifier, pathLookup);
|
|
955
|
-
if (!target || target === fromPath) {
|
|
956
|
-
continue;
|
|
957
|
-
}
|
|
958
|
-
edges.push(graphEdge({
|
|
959
|
-
from: fromPath,
|
|
960
|
-
to: target,
|
|
961
|
-
kind: "maven-module-link",
|
|
962
|
-
confidence: MAVEN_MODULE_EDGE_CONFIDENCE,
|
|
963
|
-
reason: `Maven module '${specifier}' resolves to module manifest '${target}'.`,
|
|
964
|
-
}));
|
|
965
|
-
}
|
|
966
|
-
return edges;
|
|
967
|
-
}
|
|
968
|
-
// ─── Pyproject / pytest ───────────────────────────────────────────────────────
|
|
969
|
-
const PYPROJECT_TESTPATHS_LINK_CONFIDENCE = 0.85;
|
|
970
|
-
function pyprojectTestpaths(content) {
|
|
971
|
-
const values = [];
|
|
972
|
-
let currentSection = "";
|
|
973
|
-
let collectingKey;
|
|
974
|
-
let collectedValue = "";
|
|
975
|
-
const flush = () => {
|
|
976
|
-
if (!collectingKey)
|
|
977
|
-
return;
|
|
978
|
-
for (const v of tomlStringArrayValues(collectedValue)) {
|
|
979
|
-
values.push(v);
|
|
980
|
-
}
|
|
981
|
-
collectingKey = undefined;
|
|
982
|
-
collectedValue = "";
|
|
983
|
-
};
|
|
984
|
-
for (const rawLine of content.split(/\r?\n/)) {
|
|
985
|
-
const withoutComment = stripTomlComment(rawLine);
|
|
986
|
-
const trimmed = withoutComment.trim();
|
|
987
|
-
if (trimmed.length === 0)
|
|
988
|
-
continue;
|
|
989
|
-
const sectionMatch = /^\[([^\]]+)\]\s*$/.exec(trimmed);
|
|
990
|
-
if (sectionMatch?.[1]) {
|
|
991
|
-
flush();
|
|
992
|
-
currentSection = sectionMatch[1].trim();
|
|
993
|
-
continue;
|
|
994
|
-
}
|
|
995
|
-
if (collectingKey) {
|
|
996
|
-
collectedValue = `${collectedValue}\n${trimmed}`;
|
|
997
|
-
if (tomlArrayIsClosed(collectedValue)) {
|
|
998
|
-
flush();
|
|
999
|
-
}
|
|
1000
|
-
continue;
|
|
1001
|
-
}
|
|
1002
|
-
if (currentSection !== "tool.pytest.ini_options")
|
|
1003
|
-
continue;
|
|
1004
|
-
const keyMatch = /^testpaths\s*=\s*(.+)$/.exec(trimmed);
|
|
1005
|
-
if (!keyMatch?.[1])
|
|
1006
|
-
continue;
|
|
1007
|
-
const value = keyMatch[1].trim();
|
|
1008
|
-
if (!value.startsWith("[")) {
|
|
1009
|
-
const bare = value.replace(/^["']|["']$/g, "").trim();
|
|
1010
|
-
if (bare.length > 0)
|
|
1011
|
-
values.push(bare);
|
|
1012
|
-
continue;
|
|
1013
|
-
}
|
|
1014
|
-
collectingKey = "testpaths";
|
|
1015
|
-
collectedValue = value;
|
|
1016
|
-
if (tomlArrayIsClosed(collectedValue)) {
|
|
1017
|
-
flush();
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
flush();
|
|
1021
|
-
return values;
|
|
1022
|
-
}
|
|
1023
|
-
export function extractPyprojectTestpathLinks(fromPath, content, pathLookup) {
|
|
1024
|
-
if (!isPyprojectPath(fromPath)) {
|
|
1025
|
-
return [];
|
|
1026
|
-
}
|
|
1027
|
-
const testpaths = pyprojectTestpaths(content);
|
|
1028
|
-
if (testpaths.length === 0) {
|
|
1029
|
-
return [];
|
|
1030
|
-
}
|
|
1031
|
-
const pyprojectDir = posix.dirname(normalizeGraphPath(fromPath));
|
|
1032
|
-
const edges = [];
|
|
1033
|
-
for (const testpath of testpaths) {
|
|
1034
|
-
const resolvedTestpath = pyprojectDir === "." ? testpath : posix.join(pyprojectDir, testpath);
|
|
1035
|
-
const conftestKey = posix.join(resolvedTestpath, "conftest.py").toLowerCase();
|
|
1036
|
-
const conftestTarget = pathLookup.get(conftestKey);
|
|
1037
|
-
if (!conftestTarget || conftestTarget === fromPath)
|
|
1038
|
-
continue;
|
|
1039
|
-
edges.push(graphEdge({
|
|
1040
|
-
from: fromPath,
|
|
1041
|
-
to: conftestTarget,
|
|
1042
|
-
kind: "pyproject-testpaths-link",
|
|
1043
|
-
confidence: PYPROJECT_TESTPATHS_LINK_CONFIDENCE,
|
|
1044
|
-
reason: `pyproject.toml testpaths entry '${testpath}' resolves to '${conftestTarget}'.`,
|
|
1045
|
-
}));
|
|
1046
|
-
}
|
|
1047
|
-
return edges;
|
|
1048
|
-
}
|
|
1049
|
-
// ─── YAML path references ─────────────────────────────────────────────────────
|
|
1050
|
-
const YAML_PATH_REFERENCE_LINK_CONFIDENCE = 0.8;
|
|
1051
|
-
const YAML_CONFIG_EXTENSIONS = [".yaml", ".yml", ".json", ".toml"];
|
|
1052
|
-
function isYamlSourcePath(path) {
|
|
1053
|
-
const lower = normalizeGraphPath(path).toLowerCase();
|
|
1054
|
-
return lower.endsWith(".yaml") || lower.endsWith(".yml");
|
|
1055
|
-
}
|
|
1056
|
-
function looksLikeConfigFilePath(value) {
|
|
1057
|
-
if (!value.includes("/"))
|
|
1058
|
-
return false;
|
|
1059
|
-
if (/^[a-z][a-z0-9+.-]*:/i.test(value))
|
|
1060
|
-
return false;
|
|
1061
|
-
if (value.startsWith("/"))
|
|
1062
|
-
return false;
|
|
1063
|
-
const lower = value.toLowerCase();
|
|
1064
|
-
return YAML_CONFIG_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
1065
|
-
}
|
|
1066
|
-
function extractYamlScalarValues(content) {
|
|
1067
|
-
const values = [];
|
|
1068
|
-
for (const rawLine of content.split(/\r?\n/)) {
|
|
1069
|
-
const withoutComment = stripYamlComment(rawLine);
|
|
1070
|
-
const trimmed = withoutComment.trim();
|
|
1071
|
-
if (trimmed.length === 0)
|
|
1072
|
-
continue;
|
|
1073
|
-
let rawValue;
|
|
1074
|
-
// key: value
|
|
1075
|
-
const keyValueMatch = /^[^:[\]{}]+:\s+(.+)$/.exec(trimmed);
|
|
1076
|
-
if (keyValueMatch?.[1]) {
|
|
1077
|
-
rawValue = keyValueMatch[1].trim();
|
|
1078
|
-
}
|
|
1079
|
-
else {
|
|
1080
|
-
// - value (list item)
|
|
1081
|
-
const listItemMatch = /^-\s+(.+)$/.exec(trimmed);
|
|
1082
|
-
if (listItemMatch?.[1]) {
|
|
1083
|
-
rawValue = listItemMatch[1].trim();
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
if (!rawValue)
|
|
1087
|
-
continue;
|
|
1088
|
-
const value = unquoteYamlScalar(rawValue);
|
|
1089
|
-
if (looksLikeConfigFilePath(value)) {
|
|
1090
|
-
values.push(value);
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
return values;
|
|
1094
|
-
}
|
|
1095
|
-
function resolveYamlPathReference(fromPath, specifier, pathLookup) {
|
|
1096
|
-
const normalized = normalizeGraphPath(specifier.replace(/^\.\//, ""));
|
|
1097
|
-
if (normalized.length === 0)
|
|
1098
|
-
return undefined;
|
|
1099
|
-
// Try as repo-root-relative first (many YAML configs use repo-root paths)
|
|
1100
|
-
const repoRootTarget = resolveCandidate(normalized, pathLookup);
|
|
1101
|
-
if (repoRootTarget)
|
|
1102
|
-
return repoRootTarget;
|
|
1103
|
-
// Fallback: relative to the YAML file's directory
|
|
1104
|
-
const fromDir = posix.dirname(normalizeGraphPath(fromPath));
|
|
1105
|
-
if (fromDir !== ".") {
|
|
1106
|
-
const dirRelative = posix.join(fromDir, normalized);
|
|
1107
|
-
const dirTarget = resolveCandidate(dirRelative, pathLookup);
|
|
1108
|
-
if (dirTarget)
|
|
1109
|
-
return dirTarget;
|
|
1110
|
-
}
|
|
1111
|
-
return undefined;
|
|
1112
|
-
}
|
|
1113
|
-
export function extractYamlPathReferenceEdges(fromPath, content, pathLookup) {
|
|
1114
|
-
if (!isYamlSourcePath(fromPath))
|
|
1115
|
-
return [];
|
|
1116
|
-
const values = extractYamlScalarValues(content);
|
|
1117
|
-
if (values.length === 0)
|
|
1118
|
-
return [];
|
|
1119
|
-
const edges = [];
|
|
1120
|
-
const seen = new Set();
|
|
1121
|
-
for (const value of values) {
|
|
1122
|
-
const target = resolveYamlPathReference(fromPath, value, pathLookup);
|
|
1123
|
-
if (!target || target === fromPath || seen.has(target))
|
|
1124
|
-
continue;
|
|
1125
|
-
seen.add(target);
|
|
1126
|
-
edges.push(graphEdge({
|
|
1127
|
-
from: fromPath,
|
|
1128
|
-
to: target,
|
|
1129
|
-
kind: "yaml-path-reference-link",
|
|
1130
|
-
confidence: YAML_PATH_REFERENCE_LINK_CONFIDENCE,
|
|
1131
|
-
reason: `YAML file references path '${value}'.`,
|
|
1132
|
-
}));
|
|
1133
|
-
}
|
|
1134
|
-
return edges;
|
|
1135
|
-
}
|