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.
Files changed (181) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +149 -0
  3. package/biome.json +62 -0
  4. package/dist/cli/commands/audit.d.ts +17 -0
  5. package/dist/cli/commands/audit.d.ts.map +1 -0
  6. package/dist/cli/commands/audit.js +179 -0
  7. package/dist/cli/commands/audit.js.map +1 -0
  8. package/dist/cli/index.d.ts +3 -0
  9. package/dist/cli/index.d.ts.map +1 -0
  10. package/dist/cli/index.js +41 -0
  11. package/dist/cli/index.js.map +1 -0
  12. package/dist/cli/output/ascii-logo.d.ts +3 -0
  13. package/dist/cli/output/ascii-logo.d.ts.map +1 -0
  14. package/dist/cli/output/ascii-logo.js +23 -0
  15. package/dist/cli/output/ascii-logo.js.map +1 -0
  16. package/dist/cli/output/formatters/console.d.ts +11 -0
  17. package/dist/cli/output/formatters/console.d.ts.map +1 -0
  18. package/dist/cli/output/formatters/console.js +46 -0
  19. package/dist/cli/output/formatters/console.js.map +1 -0
  20. package/dist/cli/output/formatters/html.d.ts +3 -0
  21. package/dist/cli/output/formatters/html.d.ts.map +1 -0
  22. package/dist/cli/output/formatters/html.js +215 -0
  23. package/dist/cli/output/formatters/html.js.map +1 -0
  24. package/dist/cli/output/formatters/json.d.ts +9 -0
  25. package/dist/cli/output/formatters/json.d.ts.map +1 -0
  26. package/dist/cli/output/formatters/json.js +51 -0
  27. package/dist/cli/output/formatters/json.js.map +1 -0
  28. package/dist/cli/output/table-renderer.d.ts +8 -0
  29. package/dist/cli/output/table-renderer.d.ts.map +1 -0
  30. package/dist/cli/output/table-renderer.js +44 -0
  31. package/dist/cli/output/table-renderer.js.map +1 -0
  32. package/dist/cli/schemas.d.ts +59 -0
  33. package/dist/cli/schemas.d.ts.map +1 -0
  34. package/dist/cli/schemas.js +43 -0
  35. package/dist/cli/schemas.js.map +1 -0
  36. package/dist/models/finding.d.ts +23 -0
  37. package/dist/models/finding.d.ts.map +1 -0
  38. package/dist/models/finding.js +37 -0
  39. package/dist/models/finding.js.map +1 -0
  40. package/dist/models/project.d.ts +13 -0
  41. package/dist/models/project.d.ts.map +1 -0
  42. package/dist/models/project.js +2 -0
  43. package/dist/models/project.js.map +1 -0
  44. package/dist/models/report.d.ts +78 -0
  45. package/dist/models/report.d.ts.map +1 -0
  46. package/dist/models/report.js +2 -0
  47. package/dist/models/report.js.map +1 -0
  48. package/dist/rules/index.d.ts +57 -0
  49. package/dist/rules/index.d.ts.map +1 -0
  50. package/dist/rules/index.js +115 -0
  51. package/dist/rules/index.js.map +1 -0
  52. package/dist/rules/rule-01-nuget-packages.d.ts +3 -0
  53. package/dist/rules/rule-01-nuget-packages.d.ts.map +1 -0
  54. package/dist/rules/rule-01-nuget-packages.js +103 -0
  55. package/dist/rules/rule-01-nuget-packages.js.map +1 -0
  56. package/dist/rules/rule-02-obsolete-controller-classes.d.ts +3 -0
  57. package/dist/rules/rule-02-obsolete-controller-classes.d.ts.map +1 -0
  58. package/dist/rules/rule-02-obsolete-controller-classes.js +70 -0
  59. package/dist/rules/rule-02-obsolete-controller-classes.js.map +1 -0
  60. package/dist/rules/rule-03-tiptap-import.d.ts +3 -0
  61. package/dist/rules/rule-03-tiptap-import.d.ts.map +1 -0
  62. package/dist/rules/rule-03-tiptap-import.js +46 -0
  63. package/dist/rules/rule-03-tiptap-import.js.map +1 -0
  64. package/dist/rules/rule-04-removed-packages.d.ts +3 -0
  65. package/dist/rules/rule-04-removed-packages.d.ts.map +1 -0
  66. package/dist/rules/rule-04-removed-packages.js +51 -0
  67. package/dist/rules/rule-04-removed-packages.js.map +1 -0
  68. package/dist/rules/rule-05-program-cs.d.ts +3 -0
  69. package/dist/rules/rule-05-program-cs.d.ts.map +1 -0
  70. package/dist/rules/rule-05-program-cs.js +46 -0
  71. package/dist/rules/rule-05-program-cs.js.map +1 -0
  72. package/dist/rules/rule-06-view-imports.d.ts +3 -0
  73. package/dist/rules/rule-06-view-imports.d.ts.map +1 -0
  74. package/dist/rules/rule-06-view-imports.js +48 -0
  75. package/dist/rules/rule-06-view-imports.js.map +1 -0
  76. package/dist/rules/rule-07-angular-detection.d.ts +3 -0
  77. package/dist/rules/rule-07-angular-detection.d.ts.map +1 -0
  78. package/dist/rules/rule-07-angular-detection.js +140 -0
  79. package/dist/rules/rule-07-angular-detection.js.map +1 -0
  80. package/dist/rules/rule-08-published-snapshot-interfaces.d.ts +3 -0
  81. package/dist/rules/rule-08-published-snapshot-interfaces.d.ts.map +1 -0
  82. package/dist/rules/rule-08-published-snapshot-interfaces.js +104 -0
  83. package/dist/rules/rule-08-published-snapshot-interfaces.js.map +1 -0
  84. package/dist/rules/rule-09-uda-property-editors.d.ts +3 -0
  85. package/dist/rules/rule-09-uda-property-editors.d.ts.map +1 -0
  86. package/dist/rules/rule-09-uda-property-editors.js +78 -0
  87. package/dist/rules/rule-09-uda-property-editors.js.map +1 -0
  88. package/dist/rules/rule-10-license-files.d.ts +3 -0
  89. package/dist/rules/rule-10-license-files.d.ts.map +1 -0
  90. package/dist/rules/rule-10-license-files.js +67 -0
  91. package/dist/rules/rule-10-license-files.js.map +1 -0
  92. package/dist/rules/types.d.ts +77 -0
  93. package/dist/rules/types.d.ts.map +1 -0
  94. package/dist/rules/types.js +2 -0
  95. package/dist/rules/types.js.map +1 -0
  96. package/dist/scanners/code-searcher.d.ts +43 -0
  97. package/dist/scanners/code-searcher.d.ts.map +1 -0
  98. package/dist/scanners/code-searcher.js +76 -0
  99. package/dist/scanners/code-searcher.js.map +1 -0
  100. package/dist/scanners/csproj-parser.d.ts +35 -0
  101. package/dist/scanners/csproj-parser.d.ts.map +1 -0
  102. package/dist/scanners/csproj-parser.js +71 -0
  103. package/dist/scanners/csproj-parser.js.map +1 -0
  104. package/dist/scanners/file-scanner.d.ts +44 -0
  105. package/dist/scanners/file-scanner.d.ts.map +1 -0
  106. package/dist/scanners/file-scanner.js +66 -0
  107. package/dist/scanners/file-scanner.js.map +1 -0
  108. package/dist/scanners/nuget-client.d.ts +19 -0
  109. package/dist/scanners/nuget-client.d.ts.map +1 -0
  110. package/dist/scanners/nuget-client.js +208 -0
  111. package/dist/scanners/nuget-client.js.map +1 -0
  112. package/dist/utils/hours.d.ts +29 -0
  113. package/dist/utils/hours.d.ts.map +1 -0
  114. package/dist/utils/hours.js +36 -0
  115. package/dist/utils/hours.js.map +1 -0
  116. package/dist/utils/logger.d.ts +35 -0
  117. package/dist/utils/logger.d.ts.map +1 -0
  118. package/dist/utils/logger.js +52 -0
  119. package/dist/utils/logger.js.map +1 -0
  120. package/package.json +49 -0
  121. package/specs/001-umbraco-upgrade-audit/checklists/requirements.md +36 -0
  122. package/specs/001-umbraco-upgrade-audit/contracts/cli-interface.md +278 -0
  123. package/specs/001-umbraco-upgrade-audit/data-model.md +354 -0
  124. package/specs/001-umbraco-upgrade-audit/plan.md +120 -0
  125. package/specs/001-umbraco-upgrade-audit/quickstart.md +187 -0
  126. package/specs/001-umbraco-upgrade-audit/research.md +238 -0
  127. package/specs/001-umbraco-upgrade-audit/spec.md +162 -0
  128. package/specs/001-umbraco-upgrade-audit/tasks.md +291 -0
  129. package/src/cli/commands/audit.ts +221 -0
  130. package/src/cli/index.ts +47 -0
  131. package/src/cli/output/ascii-logo.ts +27 -0
  132. package/src/cli/output/formatters/console.ts +57 -0
  133. package/src/cli/output/formatters/html.ts +227 -0
  134. package/src/cli/output/formatters/json.ts +54 -0
  135. package/src/cli/output/table-renderer.ts +58 -0
  136. package/src/cli/schemas.ts +53 -0
  137. package/src/models/finding.ts +47 -0
  138. package/src/models/project.ts +16 -0
  139. package/src/models/report.ts +97 -0
  140. package/src/rules/index.ts +128 -0
  141. package/src/rules/rule-01-nuget-packages.ts +132 -0
  142. package/src/rules/rule-02-obsolete-controller-classes.ts +89 -0
  143. package/src/rules/rule-03-tiptap-import.ts +66 -0
  144. package/src/rules/rule-04-removed-packages.ts +70 -0
  145. package/src/rules/rule-05-program-cs.ts +66 -0
  146. package/src/rules/rule-06-view-imports.ts +69 -0
  147. package/src/rules/rule-07-angular-detection.ts +169 -0
  148. package/src/rules/rule-08-published-snapshot-interfaces.ts +147 -0
  149. package/src/rules/rule-09-uda-property-editors.ts +98 -0
  150. package/src/rules/rule-10-license-files.ts +88 -0
  151. package/src/rules/types.ts +102 -0
  152. package/src/scanners/code-searcher.ts +114 -0
  153. package/src/scanners/csproj-parser.ts +97 -0
  154. package/src/scanners/file-scanner.ts +77 -0
  155. package/src/scanners/nuget-client.ts +266 -0
  156. package/src/utils/hours.ts +38 -0
  157. package/src/utils/logger.ts +59 -0
  158. package/tests/fixtures/sample-umbraco-project/BlogPost.uda +58 -0
  159. package/tests/fixtures/sample-umbraco-project/ContentModels.generated.cs +20 -0
  160. package/tests/fixtures/sample-umbraco-project/ModernPage.uda +36 -0
  161. package/tests/fixtures/sample-umbraco-project/Program.cs +26 -0
  162. package/tests/fixtures/sample-umbraco-project/PublishedContentService.cs +25 -0
  163. package/tests/fixtures/sample-umbraco-project/SampleCode.cs +16 -0
  164. package/tests/fixtures/sample-umbraco-project/TestProject.csproj +13 -0
  165. package/tests/fixtures/sample-umbraco-project/obj/Debug/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs +4 -0
  166. package/tests/fixtures/sample-umbraco-project/obj/Debug/net8.0/TestProject.AssemblyInfo.cs +22 -0
  167. package/tests/fixtures/sample-umbraco-project/obj/Debug/net8.0/TestProject.AssemblyInfoInputs.cache +1 -0
  168. package/tests/fixtures/sample-umbraco-project/obj/Debug/net8.0/TestProject.GeneratedMSBuildEditorConfig.editorconfig +31 -0
  169. package/tests/fixtures/sample-umbraco-project/obj/Debug/net8.0/TestProject.GlobalUsings.g.cs +19 -0
  170. package/tests/fixtures/sample-umbraco-project/obj/Debug/net8.0/TestProject.assets.cache +0 -0
  171. package/tests/fixtures/sample-umbraco-project/obj/Debug/net8.0/TestProject.csproj.AssemblyReference.cache +0 -0
  172. package/tests/fixtures/sample-umbraco-project/obj/TestProject.csproj.nuget.dgspec.json +84 -0
  173. package/tests/fixtures/sample-umbraco-project/obj/TestProject.csproj.nuget.g.props +26 -0
  174. package/tests/fixtures/sample-umbraco-project/obj/TestProject.csproj.nuget.g.targets +13 -0
  175. package/tests/fixtures/sample-umbraco-project/obj/project.assets.json +17414 -0
  176. package/tests/fixtures/sample-umbraco-project/obj/project.nuget.cache +404 -0
  177. package/tests/fixtures/sample-umbraco-project/umbraco/Licenses/umbracoDeploy.lic +1 -0
  178. package/tests/fixtures/sample-umbraco-project/umbraco/Licenses/umbracoForms.lic +1 -0
  179. package/tests/unit/rules/rule-09-uda-property-editors.test.ts +131 -0
  180. package/tests/unit/rules/rule-10-license-files.test.ts +82 -0
  181. 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"}