umbaudit 0.1.0
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.md +21 -0
- package/README.md +149 -0
- package/biome.json +62 -0
- package/dist/cli/commands/audit.d.ts +17 -0
- package/dist/cli/commands/audit.d.ts.map +1 -0
- package/dist/cli/commands/audit.js +179 -0
- package/dist/cli/commands/audit.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +41 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/output/ascii-logo.d.ts +3 -0
- package/dist/cli/output/ascii-logo.d.ts.map +1 -0
- package/dist/cli/output/ascii-logo.js +23 -0
- package/dist/cli/output/ascii-logo.js.map +1 -0
- package/dist/cli/output/formatters/console.d.ts +11 -0
- package/dist/cli/output/formatters/console.d.ts.map +1 -0
- package/dist/cli/output/formatters/console.js +46 -0
- package/dist/cli/output/formatters/console.js.map +1 -0
- package/dist/cli/output/formatters/html.d.ts +3 -0
- package/dist/cli/output/formatters/html.d.ts.map +1 -0
- package/dist/cli/output/formatters/html.js +215 -0
- package/dist/cli/output/formatters/html.js.map +1 -0
- package/dist/cli/output/formatters/json.d.ts +9 -0
- package/dist/cli/output/formatters/json.d.ts.map +1 -0
- package/dist/cli/output/formatters/json.js +51 -0
- package/dist/cli/output/formatters/json.js.map +1 -0
- package/dist/cli/output/table-renderer.d.ts +8 -0
- package/dist/cli/output/table-renderer.d.ts.map +1 -0
- package/dist/cli/output/table-renderer.js +44 -0
- package/dist/cli/output/table-renderer.js.map +1 -0
- package/dist/cli/schemas.d.ts +59 -0
- package/dist/cli/schemas.d.ts.map +1 -0
- package/dist/cli/schemas.js +43 -0
- package/dist/cli/schemas.js.map +1 -0
- package/dist/models/finding.d.ts +23 -0
- package/dist/models/finding.d.ts.map +1 -0
- package/dist/models/finding.js +37 -0
- package/dist/models/finding.js.map +1 -0
- package/dist/models/project.d.ts +13 -0
- package/dist/models/project.d.ts.map +1 -0
- package/dist/models/project.js +2 -0
- package/dist/models/project.js.map +1 -0
- package/dist/models/report.d.ts +78 -0
- package/dist/models/report.d.ts.map +1 -0
- package/dist/models/report.js +2 -0
- package/dist/models/report.js.map +1 -0
- package/dist/rules/index.d.ts +57 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +115 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/rule-01-nuget-packages.d.ts +3 -0
- package/dist/rules/rule-01-nuget-packages.d.ts.map +1 -0
- package/dist/rules/rule-01-nuget-packages.js +103 -0
- package/dist/rules/rule-01-nuget-packages.js.map +1 -0
- package/dist/rules/rule-02-obsolete-controller-classes.d.ts +3 -0
- package/dist/rules/rule-02-obsolete-controller-classes.d.ts.map +1 -0
- package/dist/rules/rule-02-obsolete-controller-classes.js +70 -0
- package/dist/rules/rule-02-obsolete-controller-classes.js.map +1 -0
- package/dist/rules/rule-03-tiptap-import.d.ts +3 -0
- package/dist/rules/rule-03-tiptap-import.d.ts.map +1 -0
- package/dist/rules/rule-03-tiptap-import.js +46 -0
- package/dist/rules/rule-03-tiptap-import.js.map +1 -0
- package/dist/rules/rule-04-removed-packages.d.ts +3 -0
- package/dist/rules/rule-04-removed-packages.d.ts.map +1 -0
- package/dist/rules/rule-04-removed-packages.js +51 -0
- package/dist/rules/rule-04-removed-packages.js.map +1 -0
- package/dist/rules/rule-05-program-cs.d.ts +3 -0
- package/dist/rules/rule-05-program-cs.d.ts.map +1 -0
- package/dist/rules/rule-05-program-cs.js +46 -0
- package/dist/rules/rule-05-program-cs.js.map +1 -0
- package/dist/rules/rule-06-view-imports.d.ts +3 -0
- package/dist/rules/rule-06-view-imports.d.ts.map +1 -0
- package/dist/rules/rule-06-view-imports.js +48 -0
- package/dist/rules/rule-06-view-imports.js.map +1 -0
- package/dist/rules/rule-07-angular-detection.d.ts +3 -0
- package/dist/rules/rule-07-angular-detection.d.ts.map +1 -0
- package/dist/rules/rule-07-angular-detection.js +140 -0
- package/dist/rules/rule-07-angular-detection.js.map +1 -0
- package/dist/rules/rule-08-published-snapshot-interfaces.d.ts +3 -0
- package/dist/rules/rule-08-published-snapshot-interfaces.d.ts.map +1 -0
- package/dist/rules/rule-08-published-snapshot-interfaces.js +104 -0
- package/dist/rules/rule-08-published-snapshot-interfaces.js.map +1 -0
- package/dist/rules/rule-09-uda-property-editors.d.ts +3 -0
- package/dist/rules/rule-09-uda-property-editors.d.ts.map +1 -0
- package/dist/rules/rule-09-uda-property-editors.js +78 -0
- package/dist/rules/rule-09-uda-property-editors.js.map +1 -0
- package/dist/rules/rule-10-license-files.d.ts +3 -0
- package/dist/rules/rule-10-license-files.d.ts.map +1 -0
- package/dist/rules/rule-10-license-files.js +67 -0
- package/dist/rules/rule-10-license-files.js.map +1 -0
- package/dist/rules/types.d.ts +77 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +2 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/scanners/code-searcher.d.ts +43 -0
- package/dist/scanners/code-searcher.d.ts.map +1 -0
- package/dist/scanners/code-searcher.js +76 -0
- package/dist/scanners/code-searcher.js.map +1 -0
- package/dist/scanners/csproj-parser.d.ts +35 -0
- package/dist/scanners/csproj-parser.d.ts.map +1 -0
- package/dist/scanners/csproj-parser.js +71 -0
- package/dist/scanners/csproj-parser.js.map +1 -0
- package/dist/scanners/file-scanner.d.ts +44 -0
- package/dist/scanners/file-scanner.d.ts.map +1 -0
- package/dist/scanners/file-scanner.js +66 -0
- package/dist/scanners/file-scanner.js.map +1 -0
- package/dist/scanners/nuget-client.d.ts +19 -0
- package/dist/scanners/nuget-client.d.ts.map +1 -0
- package/dist/scanners/nuget-client.js +208 -0
- package/dist/scanners/nuget-client.js.map +1 -0
- package/dist/utils/hours.d.ts +29 -0
- package/dist/utils/hours.d.ts.map +1 -0
- package/dist/utils/hours.js +36 -0
- package/dist/utils/hours.js.map +1 -0
- package/dist/utils/logger.d.ts +35 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +52 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +49 -0
- package/specs/001-umbraco-upgrade-audit/checklists/requirements.md +36 -0
- package/specs/001-umbraco-upgrade-audit/contracts/cli-interface.md +278 -0
- package/specs/001-umbraco-upgrade-audit/data-model.md +354 -0
- package/specs/001-umbraco-upgrade-audit/plan.md +120 -0
- package/specs/001-umbraco-upgrade-audit/quickstart.md +187 -0
- package/specs/001-umbraco-upgrade-audit/research.md +238 -0
- package/specs/001-umbraco-upgrade-audit/spec.md +162 -0
- package/specs/001-umbraco-upgrade-audit/tasks.md +291 -0
- package/src/cli/commands/audit.ts +221 -0
- package/src/cli/index.ts +47 -0
- package/src/cli/output/ascii-logo.ts +27 -0
- package/src/cli/output/formatters/console.ts +57 -0
- package/src/cli/output/formatters/html.ts +227 -0
- package/src/cli/output/formatters/json.ts +54 -0
- package/src/cli/output/table-renderer.ts +58 -0
- package/src/cli/schemas.ts +53 -0
- package/src/models/finding.ts +47 -0
- package/src/models/project.ts +16 -0
- package/src/models/report.ts +97 -0
- package/src/rules/index.ts +128 -0
- package/src/rules/rule-01-nuget-packages.ts +132 -0
- package/src/rules/rule-02-obsolete-controller-classes.ts +89 -0
- package/src/rules/rule-03-tiptap-import.ts +66 -0
- package/src/rules/rule-04-removed-packages.ts +70 -0
- package/src/rules/rule-05-program-cs.ts +66 -0
- package/src/rules/rule-06-view-imports.ts +69 -0
- package/src/rules/rule-07-angular-detection.ts +169 -0
- package/src/rules/rule-08-published-snapshot-interfaces.ts +147 -0
- package/src/rules/rule-09-uda-property-editors.ts +98 -0
- package/src/rules/rule-10-license-files.ts +88 -0
- package/src/rules/types.ts +102 -0
- package/src/scanners/code-searcher.ts +114 -0
- package/src/scanners/csproj-parser.ts +97 -0
- package/src/scanners/file-scanner.ts +77 -0
- package/src/scanners/nuget-client.ts +266 -0
- package/src/utils/hours.ts +38 -0
- package/src/utils/logger.ts +59 -0
- package/tests/fixtures/sample-umbraco-project/BlogPost.uda +58 -0
- package/tests/fixtures/sample-umbraco-project/ContentModels.generated.cs +20 -0
- package/tests/fixtures/sample-umbraco-project/ModernPage.uda +36 -0
- package/tests/fixtures/sample-umbraco-project/Program.cs +26 -0
- package/tests/fixtures/sample-umbraco-project/PublishedContentService.cs +25 -0
- package/tests/fixtures/sample-umbraco-project/SampleCode.cs +16 -0
- package/tests/fixtures/sample-umbraco-project/TestProject.csproj +13 -0
- package/tests/fixtures/sample-umbraco-project/obj/Debug/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs +4 -0
- package/tests/fixtures/sample-umbraco-project/obj/Debug/net8.0/TestProject.AssemblyInfo.cs +22 -0
- package/tests/fixtures/sample-umbraco-project/obj/Debug/net8.0/TestProject.AssemblyInfoInputs.cache +1 -0
- package/tests/fixtures/sample-umbraco-project/obj/Debug/net8.0/TestProject.GeneratedMSBuildEditorConfig.editorconfig +31 -0
- package/tests/fixtures/sample-umbraco-project/obj/Debug/net8.0/TestProject.GlobalUsings.g.cs +19 -0
- package/tests/fixtures/sample-umbraco-project/obj/Debug/net8.0/TestProject.assets.cache +0 -0
- package/tests/fixtures/sample-umbraco-project/obj/Debug/net8.0/TestProject.csproj.AssemblyReference.cache +0 -0
- package/tests/fixtures/sample-umbraco-project/obj/TestProject.csproj.nuget.dgspec.json +84 -0
- package/tests/fixtures/sample-umbraco-project/obj/TestProject.csproj.nuget.g.props +26 -0
- package/tests/fixtures/sample-umbraco-project/obj/TestProject.csproj.nuget.g.targets +13 -0
- package/tests/fixtures/sample-umbraco-project/obj/project.assets.json +17414 -0
- package/tests/fixtures/sample-umbraco-project/obj/project.nuget.cache +404 -0
- package/tests/fixtures/sample-umbraco-project/umbraco/Licenses/umbracoDeploy.lic +1 -0
- package/tests/fixtures/sample-umbraco-project/umbraco/Licenses/umbracoForms.lic +1 -0
- package/tests/unit/rules/rule-09-uda-property-editors.test.ts +131 -0
- package/tests/unit/rules/rule-10-license-files.test.ts +82 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { debug } from '../utils/logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Search for a pattern in a file
|
|
5
|
+
*
|
|
6
|
+
* @param filePath - File to search
|
|
7
|
+
* @param pattern - Regex pattern or string to find
|
|
8
|
+
* @param caseSensitive - Whether search is case-sensitive
|
|
9
|
+
* @returns Array of matches
|
|
10
|
+
*/
|
|
11
|
+
export async function searchInFile(filePath, pattern, caseSensitive = true) {
|
|
12
|
+
debug(`Searching in file: ${filePath}`);
|
|
13
|
+
debug(`Pattern: ${pattern}`);
|
|
14
|
+
const content = await readFile(filePath, 'utf-8');
|
|
15
|
+
const lines = content.split('\n');
|
|
16
|
+
const matches = [];
|
|
17
|
+
const regex = typeof pattern === 'string' ? new RegExp(pattern, caseSensitive ? 'g' : 'gi') : pattern;
|
|
18
|
+
lines.forEach((line, index) => {
|
|
19
|
+
if (regex.test(line)) {
|
|
20
|
+
matches.push({
|
|
21
|
+
lineNumber: index + 1, // Convert to 1-based
|
|
22
|
+
lineContent: line.trim(),
|
|
23
|
+
filePath,
|
|
24
|
+
});
|
|
25
|
+
// Reset regex lastIndex for global regexes
|
|
26
|
+
regex.lastIndex = 0;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
debug(`Found ${matches.length} matches in ${filePath}`);
|
|
30
|
+
return matches;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Extract code snippet with surrounding context
|
|
34
|
+
*
|
|
35
|
+
* @param filePath - File to extract from
|
|
36
|
+
* @param lineNumber - Center line (1-based)
|
|
37
|
+
* @param contextLines - Number of lines before/after
|
|
38
|
+
* @returns Code snippet with context
|
|
39
|
+
*/
|
|
40
|
+
export async function extractCodeSnippet(filePath, lineNumber, contextLines = 3) {
|
|
41
|
+
const content = await readFile(filePath, 'utf-8');
|
|
42
|
+
const lines = content.split('\n');
|
|
43
|
+
// Convert to 0-based index
|
|
44
|
+
const index = lineNumber - 1;
|
|
45
|
+
// Calculate bounds
|
|
46
|
+
const startIndex = Math.max(0, index - contextLines);
|
|
47
|
+
const endIndex = Math.min(lines.length - 1, index + contextLines);
|
|
48
|
+
const before = lines.slice(startIndex, index);
|
|
49
|
+
const line = lines[index] || '';
|
|
50
|
+
const after = lines.slice(index + 1, endIndex + 1);
|
|
51
|
+
return {
|
|
52
|
+
before,
|
|
53
|
+
line,
|
|
54
|
+
after,
|
|
55
|
+
startLine: startIndex + 1, // Convert back to 1-based
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Search for multiple patterns in a file (OR logic)
|
|
60
|
+
*
|
|
61
|
+
* @param filePath - File to search
|
|
62
|
+
* @param patterns - Array of patterns
|
|
63
|
+
* @param caseSensitive - Whether search is case-sensitive
|
|
64
|
+
* @returns Array of matches
|
|
65
|
+
*/
|
|
66
|
+
export async function searchMultiplePatterns(filePath, patterns, caseSensitive = true) {
|
|
67
|
+
const allMatches = [];
|
|
68
|
+
for (const pattern of patterns) {
|
|
69
|
+
const matches = await searchInFile(filePath, pattern, caseSensitive);
|
|
70
|
+
allMatches.push(...matches);
|
|
71
|
+
}
|
|
72
|
+
// Remove duplicates based on line number
|
|
73
|
+
const uniqueMatches = Array.from(new Map(allMatches.map((m) => [m.lineNumber, m])).values());
|
|
74
|
+
return uniqueMatches.sort((a, b) => a.lineNumber - b.lineNumber);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=code-searcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-searcher.js","sourceRoot":"","sources":["../../src/scanners/code-searcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* .csproj XML parsing utilities
|
|
3
|
+
*
|
|
4
|
+
* Why: NuGet package references are in XML format.
|
|
5
|
+
* fast-xml-parser handles .NET XML quirks reliably.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Package reference from .csproj
|
|
9
|
+
*/
|
|
10
|
+
export interface PackageReference {
|
|
11
|
+
name: string;
|
|
12
|
+
version: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse .csproj file and extract package references
|
|
16
|
+
*
|
|
17
|
+
* @param csprojPath - Absolute path to .csproj file
|
|
18
|
+
* @returns Array of package references
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseProjectFile(csprojPath: string): Promise<PackageReference[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Check if a package name matches Umbraco packages
|
|
23
|
+
*
|
|
24
|
+
* @param packageName - Package name to check
|
|
25
|
+
* @returns True if it's an Umbraco package
|
|
26
|
+
*/
|
|
27
|
+
export declare function isUmbracoPackage(packageName: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Extract Umbraco version from package references
|
|
30
|
+
*
|
|
31
|
+
* @param packages - List of package references
|
|
32
|
+
* @returns Umbraco version string or null
|
|
33
|
+
*/
|
|
34
|
+
export declare function extractUmbracoVersion(packages: PackageReference[]): string | null;
|
|
35
|
+
//# sourceMappingURL=csproj-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csproj-parser.d.ts","sourceRoot":"","sources":["../../src/scanners/csproj-parser.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA2CtF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,MAAM,GAAG,IAAI,CAUjF"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { XMLParser } from 'fast-xml-parser';
|
|
3
|
+
import { debug } from '../utils/logger.js';
|
|
4
|
+
/**
|
|
5
|
+
* Parse .csproj file and extract package references
|
|
6
|
+
*
|
|
7
|
+
* @param csprojPath - Absolute path to .csproj file
|
|
8
|
+
* @returns Array of package references
|
|
9
|
+
*/
|
|
10
|
+
export async function parseProjectFile(csprojPath) {
|
|
11
|
+
debug(`Parsing .csproj file: ${csprojPath}`);
|
|
12
|
+
const content = await readFile(csprojPath, 'utf-8');
|
|
13
|
+
const parser = new XMLParser({
|
|
14
|
+
ignoreAttributes: false,
|
|
15
|
+
attributeNamePrefix: '@_',
|
|
16
|
+
});
|
|
17
|
+
const parsed = parser.parse(content);
|
|
18
|
+
const packages = [];
|
|
19
|
+
// Navigate the XML structure to find PackageReference elements
|
|
20
|
+
const project = parsed.Project;
|
|
21
|
+
if (!project) {
|
|
22
|
+
debug('No Project element found in .csproj');
|
|
23
|
+
return packages;
|
|
24
|
+
}
|
|
25
|
+
// ItemGroup can be a single object or an array
|
|
26
|
+
const itemGroups = Array.isArray(project.ItemGroup) ? project.ItemGroup : [project.ItemGroup];
|
|
27
|
+
for (const itemGroup of itemGroups) {
|
|
28
|
+
if (!itemGroup)
|
|
29
|
+
continue;
|
|
30
|
+
// PackageReference can be a single object or an array
|
|
31
|
+
const packageRefs = itemGroup.PackageReference;
|
|
32
|
+
if (!packageRefs)
|
|
33
|
+
continue;
|
|
34
|
+
const refs = Array.isArray(packageRefs) ? packageRefs : [packageRefs];
|
|
35
|
+
for (const ref of refs) {
|
|
36
|
+
const name = ref['@_Include'];
|
|
37
|
+
const version = ref['@_Version'];
|
|
38
|
+
if (name && version) {
|
|
39
|
+
packages.push({ name, version });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
debug(`Found ${packages.length} package references`);
|
|
44
|
+
return packages;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Check if a package name matches Umbraco packages
|
|
48
|
+
*
|
|
49
|
+
* @param packageName - Package name to check
|
|
50
|
+
* @returns True if it's an Umbraco package
|
|
51
|
+
*/
|
|
52
|
+
export function isUmbracoPackage(packageName) {
|
|
53
|
+
return packageName.startsWith('Umbraco.');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Extract Umbraco version from package references
|
|
57
|
+
*
|
|
58
|
+
* @param packages - List of package references
|
|
59
|
+
* @returns Umbraco version string or null
|
|
60
|
+
*/
|
|
61
|
+
export function extractUmbracoVersion(packages) {
|
|
62
|
+
// Look for Umbraco.Cms package
|
|
63
|
+
const umbracoCms = packages.find((p) => p.name === 'Umbraco.Cms');
|
|
64
|
+
if (umbracoCms) {
|
|
65
|
+
return umbracoCms.version;
|
|
66
|
+
}
|
|
67
|
+
// Fallback to any Umbraco.* package
|
|
68
|
+
const umbracoPackage = packages.find((p) => isUmbracoPackage(p.name));
|
|
69
|
+
return umbracoPackage?.version || null;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=csproj-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csproj-parser.js","sourceRoot":"","sources":["../../src/scanners/csproj-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAiB3C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,UAAkB;IACvD,KAAK,CAAC,yBAAyB,UAAU,EAAE,CAAC,CAAC;IAE7C,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,gBAAgB,EAAE,KAAK;QACvB,mBAAmB,EAAE,IAAI;KAC1B,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAuB,EAAE,CAAC;IAExC,+DAA+D;IAC/D,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC/B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,KAAK,CAAC,qCAAqC,CAAC,CAAC;QAC7C,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,+CAA+C;IAC/C,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAE9F,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC,SAAS;YAAE,SAAS;QAEzB,sDAAsD;QACtD,MAAM,WAAW,GAAG,SAAS,CAAC,gBAAgB,CAAC;QAC/C,IAAI,CAAC,WAAW;YAAE,SAAS;QAE3B,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QAEtE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC;YAC9B,MAAM,OAAO,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC;YAEjC,IAAI,IAAI,IAAI,OAAO,EAAE,CAAC;gBACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS,QAAQ,CAAC,MAAM,qBAAqB,CAAC,CAAC;IACrD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,OAAO,WAAW,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAA4B;IAChE,+BAA+B;IAC/B,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC;IAClE,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,UAAU,CAAC,OAAO,CAAC;IAC5B,CAAC;IAED,oCAAoC;IACpC,MAAM,cAAc,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACtE,OAAO,cAAc,EAAE,OAAO,IAAI,IAAI,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursively discover files matching patterns
|
|
3
|
+
*
|
|
4
|
+
* Why: Constitution principle IV (Performance) requires efficient scanning.
|
|
5
|
+
* fast-glob is optimized for large directory trees.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Scan for files matching glob patterns
|
|
9
|
+
*
|
|
10
|
+
* @param patterns - Glob patterns to match
|
|
11
|
+
* @param cwd - Current working directory (project root)
|
|
12
|
+
* @param ignore - Patterns to ignore
|
|
13
|
+
* @returns Array of absolute file paths
|
|
14
|
+
*/
|
|
15
|
+
export declare function scanFiles(patterns: string | string[], cwd: string, ignore?: string[]): Promise<string[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Find all .csproj files in a directory tree
|
|
18
|
+
*
|
|
19
|
+
* @param projectPath - Root path to scan
|
|
20
|
+
* @returns Array of .csproj file paths
|
|
21
|
+
*/
|
|
22
|
+
export declare function findProjectFiles(projectPath: string): Promise<string[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Find all C# source files
|
|
25
|
+
*
|
|
26
|
+
* @param projectPath - Root path to scan
|
|
27
|
+
* @returns Array of .cs file paths
|
|
28
|
+
*/
|
|
29
|
+
export declare function findCSharpFiles(projectPath: string): Promise<string[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Find all JavaScript/TypeScript files in App_Plugins
|
|
32
|
+
*
|
|
33
|
+
* @param projectPath - Root path to scan
|
|
34
|
+
* @returns Array of .js/.ts file paths
|
|
35
|
+
*/
|
|
36
|
+
export declare function findAppPluginFiles(projectPath: string): Promise<string[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Find all Razor view files
|
|
39
|
+
*
|
|
40
|
+
* @param projectPath - Root path to scan
|
|
41
|
+
* @returns Array of .cshtml file paths
|
|
42
|
+
*/
|
|
43
|
+
export declare function findRazorFiles(projectPath: string): Promise<string[]>;
|
|
44
|
+
//# sourceMappingURL=file-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-scanner.d.ts","sourceRoot":"","sources":["../../src/scanners/file-scanner.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AAEH;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,EAC3B,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,MAAM,EAAqD,GAClE,OAAO,CAAC,MAAM,EAAE,CAAC,CAcnB;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAE7E;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAE5E;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAE/E;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAE3E"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import fg from 'fast-glob';
|
|
2
|
+
import { debug } from '../utils/logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Recursively discover files matching patterns
|
|
5
|
+
*
|
|
6
|
+
* Why: Constitution principle IV (Performance) requires efficient scanning.
|
|
7
|
+
* fast-glob is optimized for large directory trees.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Scan for files matching glob patterns
|
|
11
|
+
*
|
|
12
|
+
* @param patterns - Glob patterns to match
|
|
13
|
+
* @param cwd - Current working directory (project root)
|
|
14
|
+
* @param ignore - Patterns to ignore
|
|
15
|
+
* @returns Array of absolute file paths
|
|
16
|
+
*/
|
|
17
|
+
export async function scanFiles(patterns, cwd, ignore = ['**/node_modules/**', '**/bin/**', '**/obj/**']) {
|
|
18
|
+
debug(`Scanning files with patterns: ${JSON.stringify(patterns)}`);
|
|
19
|
+
debug(`CWD: ${cwd}`);
|
|
20
|
+
debug(`Ignore: ${JSON.stringify(ignore)}`);
|
|
21
|
+
const files = await fg(patterns, {
|
|
22
|
+
cwd,
|
|
23
|
+
absolute: true,
|
|
24
|
+
ignore,
|
|
25
|
+
onlyFiles: true,
|
|
26
|
+
});
|
|
27
|
+
debug(`Found ${files.length} files`);
|
|
28
|
+
return files;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Find all .csproj files in a directory tree
|
|
32
|
+
*
|
|
33
|
+
* @param projectPath - Root path to scan
|
|
34
|
+
* @returns Array of .csproj file paths
|
|
35
|
+
*/
|
|
36
|
+
export async function findProjectFiles(projectPath) {
|
|
37
|
+
return scanFiles('**/*.csproj', projectPath);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Find all C# source files
|
|
41
|
+
*
|
|
42
|
+
* @param projectPath - Root path to scan
|
|
43
|
+
* @returns Array of .cs file paths
|
|
44
|
+
*/
|
|
45
|
+
export async function findCSharpFiles(projectPath) {
|
|
46
|
+
return scanFiles('**/*.cs', projectPath);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Find all JavaScript/TypeScript files in App_Plugins
|
|
50
|
+
*
|
|
51
|
+
* @param projectPath - Root path to scan
|
|
52
|
+
* @returns Array of .js/.ts file paths
|
|
53
|
+
*/
|
|
54
|
+
export async function findAppPluginFiles(projectPath) {
|
|
55
|
+
return scanFiles('**/App_Plugins/**/*.{js,ts,html}', projectPath);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Find all Razor view files
|
|
59
|
+
*
|
|
60
|
+
* @param projectPath - Root path to scan
|
|
61
|
+
* @returns Array of .cshtml file paths
|
|
62
|
+
*/
|
|
63
|
+
export async function findRazorFiles(projectPath) {
|
|
64
|
+
return scanFiles('**/*.cshtml', projectPath);
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=file-scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-scanner.js","sourceRoot":"","sources":["../../src/scanners/file-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,WAAW,CAAC;AAC3B,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C;;;;;GAKG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,QAA2B,EAC3B,GAAW,EACX,SAAmB,CAAC,oBAAoB,EAAE,WAAW,EAAE,WAAW,CAAC;IAEnE,KAAK,CAAC,iCAAiC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACnE,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC;IACrB,KAAK,CAAC,WAAW,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAE3C,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC/B,GAAG;QACH,QAAQ,EAAE,IAAI;QACd,MAAM;QACN,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;IAEH,KAAK,CAAC,SAAS,KAAK,CAAC,MAAM,QAAQ,CAAC,CAAC;IACrC,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,WAAmB;IACxD,OAAO,SAAS,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,WAAmB;IACvD,OAAO,SAAS,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,WAAmB;IAC1D,OAAO,SAAS,CAAC,kCAAkC,EAAE,WAAW,CAAC,CAAC;AACpE,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,WAAmB;IACtD,OAAO,SAAS,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NuGet API client for package version lookup
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Package metadata from NuGet API
|
|
6
|
+
*/
|
|
7
|
+
export interface NuGetPackageMetadata {
|
|
8
|
+
packageName: string;
|
|
9
|
+
latestVersion: string | null;
|
|
10
|
+
isCompatible: boolean | null;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function clearPackageCache(): void;
|
|
14
|
+
export declare function queryNuGetPackage(packageName: string): Promise<NuGetPackageMetadata | null>;
|
|
15
|
+
/**
|
|
16
|
+
* Batch helper
|
|
17
|
+
*/
|
|
18
|
+
export declare function batchQueryPackages(packageNames: string[]): Promise<Map<string, NuGetPackageMetadata | null>>;
|
|
19
|
+
//# sourceMappingURL=nuget-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nuget-client.d.ts","sourceRoot":"","sources":["../../src/scanners/nuget-client.ts"],"names":[],"mappings":"AAEA;;GAEG;AAEH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAuDjG;AA+KD;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC,CAAC,CAUlH"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { debug, warn } from '../utils/logger.js';
|
|
2
|
+
const packageCache = new Map();
|
|
3
|
+
export function clearPackageCache() {
|
|
4
|
+
packageCache.clear();
|
|
5
|
+
}
|
|
6
|
+
export async function queryNuGetPackage(packageName) {
|
|
7
|
+
if (packageCache.has(packageName)) {
|
|
8
|
+
debug(`Cache hit for package: ${packageName}`);
|
|
9
|
+
return packageCache.get(packageName);
|
|
10
|
+
}
|
|
11
|
+
debug(`Querying NuGet API for package: ${packageName}`);
|
|
12
|
+
try {
|
|
13
|
+
const latestEntry = await fetchLatestStableEntry(packageName);
|
|
14
|
+
if (!latestEntry) {
|
|
15
|
+
const metadata = {
|
|
16
|
+
packageName,
|
|
17
|
+
latestVersion: null,
|
|
18
|
+
isCompatible: null,
|
|
19
|
+
error: 'No stable versions found',
|
|
20
|
+
};
|
|
21
|
+
packageCache.set(packageName, metadata);
|
|
22
|
+
warn(`No stable package metadata found for: ${packageName}`);
|
|
23
|
+
return metadata;
|
|
24
|
+
}
|
|
25
|
+
const { version: latestVersion, dependencyGroups } = latestEntry;
|
|
26
|
+
let targetFrameworks = dependencyGroups?.map((g) => g.targetFramework).filter((f) => !!f) ?? [];
|
|
27
|
+
if (targetFrameworks.length === 0) {
|
|
28
|
+
debug(`Falling back to nuspec for ${packageName}@${latestVersion}`);
|
|
29
|
+
targetFrameworks = await fetchFrameworksFromNuspec(packageName, latestVersion);
|
|
30
|
+
}
|
|
31
|
+
const isCompatible = targetFrameworks.length === 0 ? null : isSupportedFramework(targetFrameworks);
|
|
32
|
+
const metadata = {
|
|
33
|
+
packageName,
|
|
34
|
+
latestVersion,
|
|
35
|
+
isCompatible,
|
|
36
|
+
};
|
|
37
|
+
packageCache.set(packageName, metadata);
|
|
38
|
+
debug(`Retrieved package metadata: ${JSON.stringify(metadata)}`);
|
|
39
|
+
return metadata;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
debug(`Failed to query NuGet API for ${packageName}: ${error}`);
|
|
43
|
+
warn(`Failed to retrieve package metadata for: ${packageName}`);
|
|
44
|
+
const metadata = {
|
|
45
|
+
packageName,
|
|
46
|
+
latestVersion: null,
|
|
47
|
+
isCompatible: null,
|
|
48
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
49
|
+
};
|
|
50
|
+
packageCache.set(packageName, metadata);
|
|
51
|
+
return metadata;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/* -------------------------------------------------------------------------- */
|
|
55
|
+
/* Core logic */
|
|
56
|
+
/* -------------------------------------------------------------------------- */
|
|
57
|
+
/**
|
|
58
|
+
* Fetch the latest stable catalog entry (SemVer2, prerelease excluded).
|
|
59
|
+
* This supports both:
|
|
60
|
+
* • index with inline items
|
|
61
|
+
* • index pointing to page URLs
|
|
62
|
+
*/
|
|
63
|
+
async function fetchLatestStableEntry(packageName) {
|
|
64
|
+
const indexUrl = `https://api.nuget.org/v3/registration5-gz-semver2/${packageName.toLowerCase()}/index.json`;
|
|
65
|
+
const indexResponse = await fetch(indexUrl);
|
|
66
|
+
if (indexResponse.status === 404)
|
|
67
|
+
return null;
|
|
68
|
+
if (!indexResponse.ok) {
|
|
69
|
+
throw new Error(`NuGet API error: ${indexResponse.status}`);
|
|
70
|
+
}
|
|
71
|
+
const index = (await indexResponse.json());
|
|
72
|
+
// Try to extract entries from inline items first
|
|
73
|
+
if (Array.isArray(index.items) && index.items.length > 0) {
|
|
74
|
+
const inlineEntries = extractEntriesFromInlineItems(index.items);
|
|
75
|
+
const stableInline = findLatestStable(inlineEntries);
|
|
76
|
+
if (stableInline)
|
|
77
|
+
return stableInline;
|
|
78
|
+
}
|
|
79
|
+
// Fallback: fetch pages from @id (for non-inline formats)
|
|
80
|
+
const pages = Array.isArray(index.items) ? index.items : [];
|
|
81
|
+
for (let p = pages.length - 1; p >= 0; p--) {
|
|
82
|
+
const pageUrl = pages[p]['@id'];
|
|
83
|
+
if (!pageUrl)
|
|
84
|
+
continue;
|
|
85
|
+
const pageResponse = await fetch(pageUrl);
|
|
86
|
+
if (!pageResponse.ok)
|
|
87
|
+
continue;
|
|
88
|
+
const page = (await pageResponse.json());
|
|
89
|
+
if (Array.isArray(page.items)) {
|
|
90
|
+
const entries = extractEntriesFromInlineItems(page.items);
|
|
91
|
+
const stable = findLatestStable(entries);
|
|
92
|
+
if (stable)
|
|
93
|
+
return stable;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Helper to flatten "items" blocks (index or page) into {version, dependencyGroups}
|
|
100
|
+
* Handles both inline format (Serilog) and page format (Umbraco.Cloud.Cms.PublicAccess)
|
|
101
|
+
*/
|
|
102
|
+
function extractEntriesFromInlineItems(items) {
|
|
103
|
+
const result = [];
|
|
104
|
+
for (const item of items) {
|
|
105
|
+
// Check if this item has nested items array (inline format like Serilog)
|
|
106
|
+
if (Array.isArray(item.items)) {
|
|
107
|
+
for (const subItem of item.items) {
|
|
108
|
+
const catalogEntry = subItem.catalogEntry;
|
|
109
|
+
if (catalogEntry && typeof catalogEntry.version === 'string') {
|
|
110
|
+
result.push({
|
|
111
|
+
version: catalogEntry.version,
|
|
112
|
+
dependencyGroups: catalogEntry.dependencyGroups,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Also check if the item itself has catalogEntry (some formats)
|
|
118
|
+
else if (item.catalogEntry && typeof item.catalogEntry.version === 'string') {
|
|
119
|
+
result.push({
|
|
120
|
+
version: item.catalogEntry.version,
|
|
121
|
+
dependencyGroups: item.catalogEntry.dependencyGroups,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Parse a semantic version string into comparable parts
|
|
129
|
+
*/
|
|
130
|
+
function parseSemanticVersion(version) {
|
|
131
|
+
const parts = version.split('.').map((p) => {
|
|
132
|
+
const num = parseInt(p, 10);
|
|
133
|
+
return Number.isNaN(num) ? 0 : num;
|
|
134
|
+
});
|
|
135
|
+
// Ensure we have at least [major, minor, patch]
|
|
136
|
+
while (parts.length < 3)
|
|
137
|
+
parts.push(0);
|
|
138
|
+
return parts;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Compare two semantic versions: returns -1 if a < b, 0 if equal, 1 if a > b
|
|
142
|
+
*/
|
|
143
|
+
function compareSemanticVersions(a, b) {
|
|
144
|
+
const aParts = parseSemanticVersion(a);
|
|
145
|
+
const bParts = parseSemanticVersion(b);
|
|
146
|
+
const maxLength = Math.max(aParts.length, bParts.length);
|
|
147
|
+
for (let i = 0; i < maxLength; i++) {
|
|
148
|
+
const aVal = aParts[i] ?? 0;
|
|
149
|
+
const bVal = bParts[i] ?? 0;
|
|
150
|
+
if (aVal < bVal)
|
|
151
|
+
return -1;
|
|
152
|
+
if (aVal > bVal)
|
|
153
|
+
return 1;
|
|
154
|
+
}
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Pick highest stable (semver) from a flat array
|
|
159
|
+
*/
|
|
160
|
+
function findLatestStable(entries) {
|
|
161
|
+
// Filter stable versions and sort by proper semantic version comparison
|
|
162
|
+
const sorted = entries
|
|
163
|
+
.filter((e) => isStableVersion(e.version))
|
|
164
|
+
.sort((a, b) => compareSemanticVersions(b.version, a.version)); // descending order
|
|
165
|
+
return sorted.length > 0 ? sorted[0] : null;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Extract frameworks from .nuspec (source of truth fallback)
|
|
169
|
+
*/
|
|
170
|
+
async function fetchFrameworksFromNuspec(packageName, version) {
|
|
171
|
+
const id = packageName.toLowerCase();
|
|
172
|
+
const url = `https://api.nuget.org/v3-flatcontainer/${id}/${version}/${id}.nuspec`;
|
|
173
|
+
const response = await fetch(url);
|
|
174
|
+
if (!response.ok)
|
|
175
|
+
return [];
|
|
176
|
+
const xml = await response.text();
|
|
177
|
+
return Array.from(xml.matchAll(/<group\s+targetFramework="([^"]+)"/gi)).map((match) => match[1]);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Compatibility rule
|
|
181
|
+
*/
|
|
182
|
+
function isSupportedFramework(targetFrameworks) {
|
|
183
|
+
return targetFrameworks.some((tf) => tf.includes('net10.0') ||
|
|
184
|
+
tf.includes('net9.0') ||
|
|
185
|
+
tf.includes('net8.0') ||
|
|
186
|
+
tf.includes('netstandard2.0') ||
|
|
187
|
+
tf.includes('.NETStandard2.0'));
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Stable SemVer check
|
|
191
|
+
*/
|
|
192
|
+
function isStableVersion(version) {
|
|
193
|
+
// Any '-' means prerelease in SemVer
|
|
194
|
+
return !version.includes('-');
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Batch helper
|
|
198
|
+
*/
|
|
199
|
+
export async function batchQueryPackages(packageNames) {
|
|
200
|
+
debug(`Batch querying ${packageNames.length} packages`);
|
|
201
|
+
const results = new Map();
|
|
202
|
+
await Promise.all(packageNames.map(async (name) => {
|
|
203
|
+
const metadata = await queryNuGetPackage(name);
|
|
204
|
+
results.set(name, metadata);
|
|
205
|
+
}));
|
|
206
|
+
return results;
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=nuget-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nuget-client.js","sourceRoot":"","sources":["../../src/scanners/nuget-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hour calculation utilities
|
|
3
|
+
*
|
|
4
|
+
* Why: Implements linear scaling per clarification decision.
|
|
5
|
+
* Hours scale as: total = baseHours × instanceCount
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Calculate total hours using linear scaling
|
|
9
|
+
*
|
|
10
|
+
* @param baseHours - Base hours per instance
|
|
11
|
+
* @param instanceCount - Number of instances found
|
|
12
|
+
* @returns Total hours (rounded to 0.5h granularity)
|
|
13
|
+
*/
|
|
14
|
+
export declare function calculateHours(baseHours: number, instanceCount: number): number;
|
|
15
|
+
/**
|
|
16
|
+
* Round hours to 0.5h granularity (30 minutes minimum)
|
|
17
|
+
*
|
|
18
|
+
* @param hours - Raw hour value
|
|
19
|
+
* @returns Hours rounded to nearest 0.5
|
|
20
|
+
*/
|
|
21
|
+
export declare function roundToHalfHour(hours: number): number;
|
|
22
|
+
/**
|
|
23
|
+
* Convert hours to days (8 hours = 1 day)
|
|
24
|
+
*
|
|
25
|
+
* @param hours - Hours to convert
|
|
26
|
+
* @returns Days (rounded to 1 decimal place)
|
|
27
|
+
*/
|
|
28
|
+
export declare function hoursToDays(hours: number): number;
|
|
29
|
+
//# sourceMappingURL=hours.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hours.d.ts","sourceRoot":"","sources":["../../src/utils/hours.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAG/E;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hour calculation utilities
|
|
3
|
+
*
|
|
4
|
+
* Why: Implements linear scaling per clarification decision.
|
|
5
|
+
* Hours scale as: total = baseHours × instanceCount
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Calculate total hours using linear scaling
|
|
9
|
+
*
|
|
10
|
+
* @param baseHours - Base hours per instance
|
|
11
|
+
* @param instanceCount - Number of instances found
|
|
12
|
+
* @returns Total hours (rounded to 0.5h granularity)
|
|
13
|
+
*/
|
|
14
|
+
export function calculateHours(baseHours, instanceCount) {
|
|
15
|
+
const total = baseHours * instanceCount;
|
|
16
|
+
return roundToHalfHour(total);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Round hours to 0.5h granularity (30 minutes minimum)
|
|
20
|
+
*
|
|
21
|
+
* @param hours - Raw hour value
|
|
22
|
+
* @returns Hours rounded to nearest 0.5
|
|
23
|
+
*/
|
|
24
|
+
export function roundToHalfHour(hours) {
|
|
25
|
+
return Math.round(hours * 2) / 2;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Convert hours to days (8 hours = 1 day)
|
|
29
|
+
*
|
|
30
|
+
* @param hours - Hours to convert
|
|
31
|
+
* @returns Days (rounded to 1 decimal place)
|
|
32
|
+
*/
|
|
33
|
+
export function hoursToDays(hours) {
|
|
34
|
+
return Math.round((hours / 8) * 10) / 10;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=hours.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hours.js","sourceRoot":"","sources":["../../src/utils/hours.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,aAAqB;IACrE,MAAM,KAAK,GAAG,SAAS,GAAG,aAAa,CAAC;IACxC,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;AAC3C,CAAC"}
|