intentpatch 0.0.0-stage → 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 +21 -0
- package/README.md +452 -2
- package/dist/application/ports/analysis-rule.d.ts +23 -0
- package/dist/application/ports/analysis-rule.d.ts.map +1 -0
- package/dist/application/ports/analysis-rule.js +2 -0
- package/dist/application/ports/analysis-rule.js.map +1 -0
- package/dist/application/ports/change-impact-analyzer.d.ts +6 -0
- package/dist/application/ports/change-impact-analyzer.d.ts.map +1 -0
- package/dist/application/ports/change-impact-analyzer.js +2 -0
- package/dist/application/ports/change-impact-analyzer.js.map +1 -0
- package/dist/application/ports/change-source.d.ts +10 -0
- package/dist/application/ports/change-source.d.ts.map +1 -0
- package/dist/application/ports/change-source.js +2 -0
- package/dist/application/ports/change-source.js.map +1 -0
- package/dist/application/ports/code-structure-analyzer.d.ts +6 -0
- package/dist/application/ports/code-structure-analyzer.d.ts.map +1 -0
- package/dist/application/ports/code-structure-analyzer.js +2 -0
- package/dist/application/ports/code-structure-analyzer.js.map +1 -0
- package/dist/application/ports/code-structure-extractor.d.ts +33 -0
- package/dist/application/ports/code-structure-extractor.d.ts.map +1 -0
- package/dist/application/ports/code-structure-extractor.js +2 -0
- package/dist/application/ports/code-structure-extractor.js.map +1 -0
- package/dist/application/ports/file-snapshot-source.d.ts +10 -0
- package/dist/application/ports/file-snapshot-source.d.ts.map +1 -0
- package/dist/application/ports/file-snapshot-source.js +2 -0
- package/dist/application/ports/file-snapshot-source.js.map +1 -0
- package/dist/application/ports/module-reference-extractor.d.ts +13 -0
- package/dist/application/ports/module-reference-extractor.d.ts.map +1 -0
- package/dist/application/ports/module-reference-extractor.js +2 -0
- package/dist/application/ports/module-reference-extractor.js.map +1 -0
- package/dist/application/ports/project-file-source.d.ts +16 -0
- package/dist/application/ports/project-file-source.d.ts.map +1 -0
- package/dist/application/ports/project-file-source.js +2 -0
- package/dist/application/ports/project-file-source.js.map +1 -0
- package/dist/application/ports/source-symbol-extractor.d.ts +14 -0
- package/dist/application/ports/source-symbol-extractor.d.ts.map +1 -0
- package/dist/application/ports/source-symbol-extractor.js +2 -0
- package/dist/application/ports/source-symbol-extractor.js.map +1 -0
- package/dist/application/ports/symbol-change-analyzer.d.ts +6 -0
- package/dist/application/ports/symbol-change-analyzer.d.ts.map +1 -0
- package/dist/application/ports/symbol-change-analyzer.js +2 -0
- package/dist/application/ports/symbol-change-analyzer.js.map +1 -0
- package/dist/application/ports/test-change-analyzer.d.ts +6 -0
- package/dist/application/ports/test-change-analyzer.d.ts.map +1 -0
- package/dist/application/ports/test-change-analyzer.js +2 -0
- package/dist/application/ports/test-change-analyzer.js.map +1 -0
- package/dist/application/rules/change-budget-rule.d.ts +7 -0
- package/dist/application/rules/change-budget-rule.d.ts.map +1 -0
- package/dist/application/rules/change-budget-rule.js +42 -0
- package/dist/application/rules/change-budget-rule.js.map +1 -0
- package/dist/application/rules/duplicate-implementation-rule.d.ts +7 -0
- package/dist/application/rules/duplicate-implementation-rule.d.ts.map +1 -0
- package/dist/application/rules/duplicate-implementation-rule.js +20 -0
- package/dist/application/rules/duplicate-implementation-rule.js.map +1 -0
- package/dist/application/rules/expected-scope-rule.d.ts +7 -0
- package/dist/application/rules/expected-scope-rule.d.ts.map +1 -0
- package/dist/application/rules/expected-scope-rule.js +27 -0
- package/dist/application/rules/expected-scope-rule.js.map +1 -0
- package/dist/application/rules/missing-test-change-rule.d.ts +7 -0
- package/dist/application/rules/missing-test-change-rule.d.ts.map +1 -0
- package/dist/application/rules/missing-test-change-rule.js +25 -0
- package/dist/application/rules/missing-test-change-rule.js.map +1 -0
- package/dist/application/rules/package-dependency-rule.d.ts +13 -0
- package/dist/application/rules/package-dependency-rule.d.ts.map +1 -0
- package/dist/application/rules/package-dependency-rule.js +174 -0
- package/dist/application/rules/package-dependency-rule.js.map +1 -0
- package/dist/application/rules/public-api-compatibility-rule.d.ts +7 -0
- package/dist/application/rules/public-api-compatibility-rule.d.ts.map +1 -0
- package/dist/application/rules/public-api-compatibility-rule.js +92 -0
- package/dist/application/rules/public-api-compatibility-rule.js.map +1 -0
- package/dist/application/rules/public-api-removal-rule.d.ts +7 -0
- package/dist/application/rules/public-api-removal-rule.d.ts.map +1 -0
- package/dist/application/rules/public-api-removal-rule.js +45 -0
- package/dist/application/rules/public-api-removal-rule.js.map +1 -0
- package/dist/application/rules/single-implementation-abstraction-rule.d.ts +7 -0
- package/dist/application/rules/single-implementation-abstraction-rule.d.ts.map +1 -0
- package/dist/application/rules/single-implementation-abstraction-rule.js +20 -0
- package/dist/application/rules/single-implementation-abstraction-rule.js.map +1 -0
- package/dist/application/services/analyze-code-structure.d.ts +14 -0
- package/dist/application/services/analyze-code-structure.d.ts.map +1 -0
- package/dist/application/services/analyze-code-structure.js +113 -0
- package/dist/application/services/analyze-code-structure.js.map +1 -0
- package/dist/application/services/analyze-import-impact.d.ts +14 -0
- package/dist/application/services/analyze-import-impact.d.ts.map +1 -0
- package/dist/application/services/analyze-import-impact.js +142 -0
- package/dist/application/services/analyze-import-impact.js.map +1 -0
- package/dist/application/services/analyze-test-changes.d.ts +8 -0
- package/dist/application/services/analyze-test-changes.d.ts.map +1 -0
- package/dist/application/services/analyze-test-changes.js +41 -0
- package/dist/application/services/analyze-test-changes.js.map +1 -0
- package/dist/application/services/compare-source-symbols.d.ts +13 -0
- package/dist/application/services/compare-source-symbols.d.ts.map +1 -0
- package/dist/application/services/compare-source-symbols.js +103 -0
- package/dist/application/services/compare-source-symbols.js.map +1 -0
- package/dist/application/services/path-pattern.d.ts +3 -0
- package/dist/application/services/path-pattern.d.ts.map +1 -0
- package/dist/application/services/path-pattern.js +34 -0
- package/dist/application/services/path-pattern.js.map +1 -0
- package/dist/application/services/rule-engine.d.ts +9 -0
- package/dist/application/services/rule-engine.d.ts.map +1 -0
- package/dist/application/services/rule-engine.js +13 -0
- package/dist/application/services/rule-engine.js.map +1 -0
- package/dist/application/use-cases/analyze-changes.d.ts +28 -0
- package/dist/application/use-cases/analyze-changes.d.ts.map +1 -0
- package/dist/application/use-cases/analyze-changes.js +88 -0
- package/dist/application/use-cases/analyze-changes.js.map +1 -0
- package/dist/domain/change-contract.d.ts +33 -0
- package/dist/domain/change-contract.d.ts.map +1 -0
- package/dist/domain/change-contract.js +76 -0
- package/dist/domain/change-contract.js.map +1 -0
- package/dist/domain/change.d.ts +37 -0
- package/dist/domain/change.d.ts.map +1 -0
- package/dist/domain/change.js +61 -0
- package/dist/domain/change.js.map +1 -0
- package/dist/domain/code-structure.d.ts +38 -0
- package/dist/domain/code-structure.d.ts.map +1 -0
- package/dist/domain/code-structure.js +63 -0
- package/dist/domain/code-structure.js.map +1 -0
- package/dist/domain/finding.d.ts +24 -0
- package/dist/domain/finding.d.ts.map +1 -0
- package/dist/domain/finding.js +30 -0
- package/dist/domain/finding.js.map +1 -0
- package/dist/domain/impact.d.ts +45 -0
- package/dist/domain/impact.d.ts.map +1 -0
- package/dist/domain/impact.js +60 -0
- package/dist/domain/impact.js.map +1 -0
- package/dist/domain/report.d.ts +22 -0
- package/dist/domain/report.d.ts.map +1 -0
- package/dist/domain/report.js +2 -0
- package/dist/domain/report.js.map +1 -0
- package/dist/domain/symbol-change.d.ts +60 -0
- package/dist/domain/symbol-change.d.ts.map +1 -0
- package/dist/domain/symbol-change.js +79 -0
- package/dist/domain/symbol-change.js.map +1 -0
- package/dist/domain/test-change.d.ts +29 -0
- package/dist/domain/test-change.d.ts.map +1 -0
- package/dist/domain/test-change.js +35 -0
- package/dist/domain/test-change.js.map +1 -0
- package/dist/infrastructure/config/json-change-contract-loader.d.ts +6 -0
- package/dist/infrastructure/config/json-change-contract-loader.d.ts.map +1 -0
- package/dist/infrastructure/config/json-change-contract-loader.js +130 -0
- package/dist/infrastructure/config/json-change-contract-loader.js.map +1 -0
- package/dist/infrastructure/filesystem/repository-path.d.ts +3 -0
- package/dist/infrastructure/filesystem/repository-path.d.ts.map +1 -0
- package/dist/infrastructure/filesystem/repository-path.js +11 -0
- package/dist/infrastructure/filesystem/repository-path.js.map +1 -0
- package/dist/infrastructure/git/diff-parser.d.ts +3 -0
- package/dist/infrastructure/git/diff-parser.d.ts.map +1 -0
- package/dist/infrastructure/git/diff-parser.js +124 -0
- package/dist/infrastructure/git/diff-parser.js.map +1 -0
- package/dist/infrastructure/git/git-change-source.d.ts +13 -0
- package/dist/infrastructure/git/git-change-source.d.ts.map +1 -0
- package/dist/infrastructure/git/git-change-source.js +43 -0
- package/dist/infrastructure/git/git-change-source.js.map +1 -0
- package/dist/infrastructure/git/git-file-snapshot-source.d.ts +14 -0
- package/dist/infrastructure/git/git-file-snapshot-source.d.ts.map +1 -0
- package/dist/infrastructure/git/git-file-snapshot-source.js +87 -0
- package/dist/infrastructure/git/git-file-snapshot-source.js.map +1 -0
- package/dist/infrastructure/git/git-project-file-source.d.ts +16 -0
- package/dist/infrastructure/git/git-project-file-source.d.ts.map +1 -0
- package/dist/infrastructure/git/git-project-file-source.js +113 -0
- package/dist/infrastructure/git/git-project-file-source.js.map +1 -0
- package/dist/infrastructure/git/untracked-file-collector.d.ts +4 -0
- package/dist/infrastructure/git/untracked-file-collector.d.ts.map +1 -0
- package/dist/infrastructure/git/untracked-file-collector.js +52 -0
- package/dist/infrastructure/git/untracked-file-collector.js.map +1 -0
- package/dist/infrastructure/process/command-runner.d.ts +17 -0
- package/dist/infrastructure/process/command-runner.d.ts.map +1 -0
- package/dist/infrastructure/process/command-runner.js +35 -0
- package/dist/infrastructure/process/command-runner.js.map +1 -0
- package/dist/infrastructure/typescript/parse-typescript-source.d.ts +11 -0
- package/dist/infrastructure/typescript/parse-typescript-source.d.ts.map +1 -0
- package/dist/infrastructure/typescript/parse-typescript-source.js +31 -0
- package/dist/infrastructure/typescript/parse-typescript-source.js.map +1 -0
- package/dist/infrastructure/typescript/typescript-code-structure-extractor.d.ts +7 -0
- package/dist/infrastructure/typescript/typescript-code-structure-extractor.d.ts.map +1 -0
- package/dist/infrastructure/typescript/typescript-code-structure-extractor.js +80 -0
- package/dist/infrastructure/typescript/typescript-code-structure-extractor.js.map +1 -0
- package/dist/infrastructure/typescript/typescript-module-reference-extractor.d.ts +7 -0
- package/dist/infrastructure/typescript/typescript-module-reference-extractor.d.ts.map +1 -0
- package/dist/infrastructure/typescript/typescript-module-reference-extractor.js +37 -0
- package/dist/infrastructure/typescript/typescript-module-reference-extractor.js.map +1 -0
- package/dist/infrastructure/typescript/typescript-symbol-extractor.d.ts +7 -0
- package/dist/infrastructure/typescript/typescript-symbol-extractor.d.ts.map +1 -0
- package/dist/infrastructure/typescript/typescript-symbol-extractor.js +206 -0
- package/dist/infrastructure/typescript/typescript-symbol-extractor.js.map +1 -0
- package/dist/presentation/analyze-repository.d.ts +10 -0
- package/dist/presentation/analyze-repository.d.ts.map +1 -0
- package/dist/presentation/analyze-repository.js +55 -0
- package/dist/presentation/analyze-repository.js.map +1 -0
- package/dist/presentation/cli/arguments.d.ts +26 -0
- package/dist/presentation/cli/arguments.d.ts.map +1 -0
- package/dist/presentation/cli/arguments.js +120 -0
- package/dist/presentation/cli/arguments.js.map +1 -0
- package/dist/presentation/cli/failure-policy.d.ts +3 -0
- package/dist/presentation/cli/failure-policy.d.ts.map +1 -0
- package/dist/presentation/cli/failure-policy.js +5 -0
- package/dist/presentation/cli/failure-policy.js.map +1 -0
- package/dist/presentation/cli/main.d.ts +3 -0
- package/dist/presentation/cli/main.d.ts.map +1 -0
- package/dist/presentation/cli/main.js +71 -0
- package/dist/presentation/cli/main.js.map +1 -0
- package/dist/presentation/cli/report-file-writer.d.ts +3 -0
- package/dist/presentation/cli/report-file-writer.d.ts.map +1 -0
- package/dist/presentation/cli/report-file-writer.js +9 -0
- package/dist/presentation/cli/report-file-writer.js.map +1 -0
- package/dist/presentation/cli/report-formatter.d.ts +5 -0
- package/dist/presentation/cli/report-formatter.d.ts.map +1 -0
- package/dist/presentation/cli/report-formatter.js +14 -0
- package/dist/presentation/cli/report-formatter.js.map +1 -0
- package/dist/presentation/cli/text-report.d.ts +3 -0
- package/dist/presentation/cli/text-report.d.ts.map +1 -0
- package/dist/presentation/cli/text-report.js +151 -0
- package/dist/presentation/cli/text-report.js.map +1 -0
- package/dist/presentation/github/action-inputs.d.ts +14 -0
- package/dist/presentation/github/action-inputs.d.ts.map +1 -0
- package/dist/presentation/github/action-inputs.js +24 -0
- package/dist/presentation/github/action-inputs.js.map +1 -0
- package/dist/presentation/github/action-main.d.ts +3 -0
- package/dist/presentation/github/action-main.d.ts.map +1 -0
- package/dist/presentation/github/action-main.js +20 -0
- package/dist/presentation/github/action-main.js.map +1 -0
- package/dist/presentation/github/action-runtime.d.ts +22 -0
- package/dist/presentation/github/action-runtime.d.ts.map +1 -0
- package/dist/presentation/github/action-runtime.js +58 -0
- package/dist/presentation/github/action-runtime.js.map +1 -0
- package/dist/presentation/github/comparison.d.ts +11 -0
- package/dist/presentation/github/comparison.d.ts.map +1 -0
- package/dist/presentation/github/comparison.js +35 -0
- package/dist/presentation/github/comparison.js.map +1 -0
- package/dist/presentation/github/markdown-summary.d.ts +4 -0
- package/dist/presentation/github/markdown-summary.d.ts.map +1 -0
- package/dist/presentation/github/markdown-summary.js +47 -0
- package/dist/presentation/github/markdown-summary.js.map +1 -0
- package/dist/presentation/github/run-action.d.ts +7 -0
- package/dist/presentation/github/run-action.d.ts.map +1 -0
- package/dist/presentation/github/run-action.js +81 -0
- package/dist/presentation/github/run-action.js.map +1 -0
- package/dist/presentation/html/html-report.d.ts +4 -0
- package/dist/presentation/html/html-report.d.ts.map +1 -0
- package/dist/presentation/html/html-report.js +362 -0
- package/dist/presentation/html/html-report.js.map +1 -0
- package/package.json +52 -4
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Git 경로용 최소 glob 문법(*, **, ?)을 정규식으로 변환해 전체 경로를 비교한다. */
|
|
2
|
+
export function matchesPathPattern(path, pattern) {
|
|
3
|
+
return new RegExp(`^${toRegularExpression(pattern)}$`, "u").test(path);
|
|
4
|
+
}
|
|
5
|
+
function toRegularExpression(pattern) {
|
|
6
|
+
let expression = "";
|
|
7
|
+
for (let index = 0; index < pattern.length; index += 1) {
|
|
8
|
+
const character = pattern[index];
|
|
9
|
+
if (character === "*" && pattern[index + 1] === "*" && pattern[index + 2] === "/") {
|
|
10
|
+
expression += "(?:.*/)?";
|
|
11
|
+
index += 2;
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
if (character === "*" && pattern[index + 1] === "*") {
|
|
15
|
+
expression += ".*";
|
|
16
|
+
index += 1;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (character === "*") {
|
|
20
|
+
expression += "[^/]*";
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (character === "?") {
|
|
24
|
+
expression += "[^/]";
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
expression += escapeRegularExpression(character ?? "");
|
|
28
|
+
}
|
|
29
|
+
return expression;
|
|
30
|
+
}
|
|
31
|
+
function escapeRegularExpression(value) {
|
|
32
|
+
return /[\\^$.*+?()[\]{}|]/u.test(value) ? `\\${value}` : value;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=path-pattern.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-pattern.js","sourceRoot":"","sources":["../../../src/application/services/path-pattern.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,OAAe;IAC9D,OAAO,IAAI,MAAM,CAAC,IAAI,mBAAmB,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzE,CAAC;AAED,SAAS,mBAAmB,CAAC,OAAe;IAC1C,IAAI,UAAU,GAAG,EAAE,CAAC;IAEpB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACvD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,SAAS,KAAK,GAAG,IAAI,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,IAAI,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAClF,UAAU,IAAI,UAAU,CAAC;YACzB,KAAK,IAAI,CAAC,CAAC;YACX,SAAS;QACX,CAAC;QACD,IAAI,SAAS,KAAK,GAAG,IAAI,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACpD,UAAU,IAAI,IAAI,CAAC;YACnB,KAAK,IAAI,CAAC,CAAC;YACX,SAAS;QACX,CAAC;QACD,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;YACtB,UAAU,IAAI,OAAO,CAAC;YACtB,SAAS;QACX,CAAC;QACD,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;YACtB,UAAU,IAAI,MAAM,CAAC;YACrB,SAAS;QACX,CAAC;QACD,UAAU,IAAI,uBAAuB,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAa;IAC5C,OAAO,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type FindingSet } from "../../domain/finding.js";
|
|
2
|
+
import type { AnalysisContext, AnalysisRule } from "../ports/analysis-rule.js";
|
|
3
|
+
/** 독립 규칙을 병렬 실행하고 결과 순서를 도메인 팩토리에 맡긴다. */
|
|
4
|
+
export declare class RuleEngine {
|
|
5
|
+
private readonly rules;
|
|
6
|
+
constructor(rules: readonly AnalysisRule[]);
|
|
7
|
+
run(context: AnalysisContext): Promise<FindingSet>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=rule-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-engine.d.ts","sourceRoot":"","sources":["../../../src/application/services/rule-engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE/E,0CAA0C;AAC1C,qBAAa,UAAU;IACF,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,SAAS,YAAY,EAAE;IAErD,GAAG,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC;CAIhE"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createFindingSet } from "../../domain/finding.js";
|
|
2
|
+
/** 독립 규칙을 병렬 실행하고 결과 순서를 도메인 팩토리에 맡긴다. */
|
|
3
|
+
export class RuleEngine {
|
|
4
|
+
rules;
|
|
5
|
+
constructor(rules) {
|
|
6
|
+
this.rules = rules;
|
|
7
|
+
}
|
|
8
|
+
async run(context) {
|
|
9
|
+
const results = await Promise.all(this.rules.map((rule) => rule.analyze(context)));
|
|
10
|
+
return createFindingSet(results.flat());
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=rule-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-engine.js","sourceRoot":"","sources":["../../../src/application/services/rule-engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAmB,MAAM,yBAAyB,CAAC;AAG5E,0CAA0C;AAC1C,MAAM,OAAO,UAAU;IACe;IAApC,YAAoC,KAA8B;QAA9B,UAAK,GAAL,KAAK,CAAyB;IAAG,CAAC;IAE/D,KAAK,CAAC,GAAG,CAAC,OAAwB;QACvC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACnF,OAAO,gBAAgB,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC;CACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ChangeContract } from "../../domain/change-contract.js";
|
|
2
|
+
import type { ChangeReport } from "../../domain/report.js";
|
|
3
|
+
import type { ChangeImpactAnalyzer } from "../ports/change-impact-analyzer.js";
|
|
4
|
+
import type { ChangeSource } from "../ports/change-source.js";
|
|
5
|
+
import type { CodeStructureAnalyzer } from "../ports/code-structure-analyzer.js";
|
|
6
|
+
import type { SymbolChangeAnalyzer } from "../ports/symbol-change-analyzer.js";
|
|
7
|
+
import type { TestChangeAnalyzer } from "../ports/test-change-analyzer.js";
|
|
8
|
+
import { RuleEngine } from "../services/rule-engine.js";
|
|
9
|
+
export interface AnalyzeChangesInput {
|
|
10
|
+
readonly baseRef?: string;
|
|
11
|
+
readonly headRef?: string;
|
|
12
|
+
readonly contract?: ChangeContract;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 분석 입력을 정규화하고 변경사항 수집을 조율하는 유스케이스다.
|
|
16
|
+
* Git 명령이나 출력 형식은 외부 계층에 위임해 핵심 흐름을 기술 세부사항과 분리한다.
|
|
17
|
+
*/
|
|
18
|
+
export declare class AnalyzeChanges {
|
|
19
|
+
private readonly changeSource;
|
|
20
|
+
private readonly ruleEngine;
|
|
21
|
+
private readonly symbolChangeAnalyzer;
|
|
22
|
+
private readonly impactAnalyzer;
|
|
23
|
+
private readonly testChangeAnalyzer;
|
|
24
|
+
private readonly codeStructureAnalyzer;
|
|
25
|
+
constructor(changeSource: ChangeSource, ruleEngine?: RuleEngine, symbolChangeAnalyzer?: SymbolChangeAnalyzer, impactAnalyzer?: ChangeImpactAnalyzer, testChangeAnalyzer?: TestChangeAnalyzer, codeStructureAnalyzer?: CodeStructureAnalyzer);
|
|
26
|
+
execute(input?: AnalyzeChangesInput): Promise<ChangeReport>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=analyze-changes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-changes.d.ts","sourceRoot":"","sources":["../../../src/application/use-cases/analyze-changes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAGtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;CACpC;AA6BD;;;GAGG;AACH,qBAAa,cAAc;IAEvB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,qBAAqB;gBALrB,YAAY,EAAE,YAAY,EAC1B,UAAU,GAAE,UAA+B,EAC3C,oBAAoB,GAAE,oBAAgD,EACtE,cAAc,GAAE,oBAA0C,EAC1D,kBAAkB,GAAE,kBAA4C,EAChE,qBAAqB,GAAE,qBAAkD;IAG/E,OAAO,CAAC,KAAK,GAAE,mBAAwB,GAAG,OAAO,CAAC,YAAY,CAAC;CAsC7E"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { createCodeStructureAnalysis } from "../../domain/code-structure.js";
|
|
2
|
+
import { createImpactAnalysis } from "../../domain/impact.js";
|
|
3
|
+
import { createSymbolChangeSet } from "../../domain/symbol-change.js";
|
|
4
|
+
import { createTestChangeAnalysis } from "../../domain/test-change.js";
|
|
5
|
+
import { RuleEngine } from "../services/rule-engine.js";
|
|
6
|
+
const emptySymbolChangeAnalyzer = {
|
|
7
|
+
analyze: async () => createSymbolChangeSet([]),
|
|
8
|
+
};
|
|
9
|
+
const emptyImpactAnalyzer = {
|
|
10
|
+
analyze: async () => createImpactAnalysis({
|
|
11
|
+
sourceFiles: 0,
|
|
12
|
+
changedModules: [],
|
|
13
|
+
dependencies: [],
|
|
14
|
+
impactedFiles: [],
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
const emptyTestChangeAnalyzer = {
|
|
18
|
+
analyze: async () => createTestChangeAnalysis({ testFiles: [], sourceCoverage: [] }),
|
|
19
|
+
};
|
|
20
|
+
const emptyCodeStructureAnalyzer = {
|
|
21
|
+
analyze: async () => createCodeStructureAnalysis({
|
|
22
|
+
sourceFiles: 0,
|
|
23
|
+
duplicateCandidates: [],
|
|
24
|
+
singleImplementationAbstractions: [],
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* 분석 입력을 정규화하고 변경사항 수집을 조율하는 유스케이스다.
|
|
29
|
+
* Git 명령이나 출력 형식은 외부 계층에 위임해 핵심 흐름을 기술 세부사항과 분리한다.
|
|
30
|
+
*/
|
|
31
|
+
export class AnalyzeChanges {
|
|
32
|
+
changeSource;
|
|
33
|
+
ruleEngine;
|
|
34
|
+
symbolChangeAnalyzer;
|
|
35
|
+
impactAnalyzer;
|
|
36
|
+
testChangeAnalyzer;
|
|
37
|
+
codeStructureAnalyzer;
|
|
38
|
+
constructor(changeSource, ruleEngine = new RuleEngine([]), symbolChangeAnalyzer = emptySymbolChangeAnalyzer, impactAnalyzer = emptyImpactAnalyzer, testChangeAnalyzer = emptyTestChangeAnalyzer, codeStructureAnalyzer = emptyCodeStructureAnalyzer) {
|
|
39
|
+
this.changeSource = changeSource;
|
|
40
|
+
this.ruleEngine = ruleEngine;
|
|
41
|
+
this.symbolChangeAnalyzer = symbolChangeAnalyzer;
|
|
42
|
+
this.impactAnalyzer = impactAnalyzer;
|
|
43
|
+
this.testChangeAnalyzer = testChangeAnalyzer;
|
|
44
|
+
this.codeStructureAnalyzer = codeStructureAnalyzer;
|
|
45
|
+
}
|
|
46
|
+
async execute(input = {}) {
|
|
47
|
+
const baseRef = input.baseRef?.trim() || "HEAD";
|
|
48
|
+
const headRef = input.headRef?.trim() || undefined;
|
|
49
|
+
assertSafeGitRef(baseRef);
|
|
50
|
+
if (headRef !== undefined) {
|
|
51
|
+
assertSafeGitRef(headRef);
|
|
52
|
+
}
|
|
53
|
+
const target = headRef === undefined ? { baseRef } : { baseRef, headRef };
|
|
54
|
+
const changes = await this.changeSource.collect(target);
|
|
55
|
+
const initialContext = {
|
|
56
|
+
target,
|
|
57
|
+
changes,
|
|
58
|
+
...(input.contract === undefined ? {} : { contract: input.contract }),
|
|
59
|
+
};
|
|
60
|
+
const [testChanges, symbolChanges] = await Promise.all([
|
|
61
|
+
this.testChangeAnalyzer.analyze(initialContext),
|
|
62
|
+
this.symbolChangeAnalyzer.analyze(initialContext),
|
|
63
|
+
]);
|
|
64
|
+
const factContext = { ...initialContext, testChanges, symbolChanges };
|
|
65
|
+
const [codeStructure, impact] = await Promise.all([
|
|
66
|
+
this.codeStructureAnalyzer.analyze(factContext),
|
|
67
|
+
this.impactAnalyzer.analyze(factContext),
|
|
68
|
+
]);
|
|
69
|
+
const context = { ...factContext, codeStructure };
|
|
70
|
+
const findings = await this.ruleEngine.run(context);
|
|
71
|
+
return {
|
|
72
|
+
target,
|
|
73
|
+
...(input.contract === undefined ? {} : { contract: input.contract }),
|
|
74
|
+
changes,
|
|
75
|
+
findings,
|
|
76
|
+
symbolChanges,
|
|
77
|
+
codeStructure,
|
|
78
|
+
impact,
|
|
79
|
+
testChanges,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function assertSafeGitRef(ref) {
|
|
84
|
+
if (ref.startsWith("-")) {
|
|
85
|
+
throw new Error(`Git references must not start with a hyphen: ${ref}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=analyze-changes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-changes.js","sourceRoot":"","sources":["../../../src/application/use-cases/analyze-changes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface ChangeScope {
|
|
2
|
+
readonly include: readonly string[];
|
|
3
|
+
readonly allow: readonly string[];
|
|
4
|
+
readonly maxFiles?: number;
|
|
5
|
+
readonly maxLines?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface TestChangePolicy {
|
|
8
|
+
readonly requireFor: readonly string[];
|
|
9
|
+
readonly include: readonly string[];
|
|
10
|
+
readonly exclude: readonly string[];
|
|
11
|
+
}
|
|
12
|
+
export interface ChangeContract {
|
|
13
|
+
readonly intent?: string;
|
|
14
|
+
readonly scope: ChangeScope;
|
|
15
|
+
readonly tests?: TestChangePolicy;
|
|
16
|
+
}
|
|
17
|
+
export interface CreateChangeContractInput {
|
|
18
|
+
readonly intent?: string;
|
|
19
|
+
readonly scope?: Readonly<{
|
|
20
|
+
include?: readonly string[];
|
|
21
|
+
allow?: readonly string[];
|
|
22
|
+
maxFiles?: number;
|
|
23
|
+
maxLines?: number;
|
|
24
|
+
}>;
|
|
25
|
+
readonly tests?: Readonly<{
|
|
26
|
+
requireFor?: readonly string[];
|
|
27
|
+
include?: readonly string[];
|
|
28
|
+
exclude?: readonly string[];
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
31
|
+
/** 사용자 기대 범위를 정규화해 규칙마다 설정 해석이 달라지지 않게 한다. */
|
|
32
|
+
export declare function createChangeContract(input: CreateChangeContractInput): ChangeContract;
|
|
33
|
+
//# sourceMappingURL=change-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change-contract.d.ts","sourceRoot":"","sources":["../../src/domain/change-contract.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;CACnC;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC;QACxB,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC5B,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC;QACxB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC/B,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC5B,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KAC7B,CAAC,CAAC;CACJ;AAED,8CAA8C;AAC9C,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,GAAG,cAAc,CAmBrF"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** 사용자 기대 범위를 정규화해 규칙마다 설정 해석이 달라지지 않게 한다. */
|
|
2
|
+
export function createChangeContract(input) {
|
|
3
|
+
const intent = normalizeIntent(input.intent);
|
|
4
|
+
const scope = input.scope ?? {};
|
|
5
|
+
const include = normalizePatterns(scope.include ?? [], "scope.include");
|
|
6
|
+
const allow = normalizePatterns(scope.allow ?? [], "scope.allow");
|
|
7
|
+
const maxFiles = normalizeBudget(scope.maxFiles, "maxFiles");
|
|
8
|
+
const maxLines = normalizeBudget(scope.maxLines, "maxLines");
|
|
9
|
+
const tests = normalizeTestPolicy(input.tests);
|
|
10
|
+
return Object.freeze({
|
|
11
|
+
...(intent === undefined ? {} : { intent }),
|
|
12
|
+
scope: Object.freeze({
|
|
13
|
+
include: Object.freeze(include),
|
|
14
|
+
allow: Object.freeze(allow),
|
|
15
|
+
...(maxFiles === undefined ? {} : { maxFiles }),
|
|
16
|
+
...(maxLines === undefined ? {} : { maxLines }),
|
|
17
|
+
}),
|
|
18
|
+
...(tests === undefined ? {} : { tests }),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function normalizeTestPolicy(input) {
|
|
22
|
+
if (input === undefined) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
const requireFor = normalizePatterns(input.requireFor ?? [], "tests.requireFor");
|
|
26
|
+
const include = normalizePatterns(input.include ?? [], "tests.include");
|
|
27
|
+
const exclude = normalizePatterns(input.exclude ?? [], "tests.exclude");
|
|
28
|
+
if (requireFor.length === 0) {
|
|
29
|
+
throw new Error("Change contract tests.requireFor must contain at least one pattern.");
|
|
30
|
+
}
|
|
31
|
+
if (include.length === 0) {
|
|
32
|
+
throw new Error("Change contract tests.include must contain at least one pattern.");
|
|
33
|
+
}
|
|
34
|
+
return Object.freeze({
|
|
35
|
+
requireFor: Object.freeze(requireFor),
|
|
36
|
+
include: Object.freeze(include),
|
|
37
|
+
exclude: Object.freeze(exclude),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function normalizeIntent(value) {
|
|
41
|
+
if (value === undefined) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const intent = value.trim();
|
|
45
|
+
if (intent.length === 0) {
|
|
46
|
+
throw new Error("Change contract intent must not be blank.");
|
|
47
|
+
}
|
|
48
|
+
return intent;
|
|
49
|
+
}
|
|
50
|
+
function normalizePatterns(values, field) {
|
|
51
|
+
const patterns = values.map((value) => normalizePattern(value, field));
|
|
52
|
+
return [...new Set(patterns)].sort((left, right) => left.localeCompare(right));
|
|
53
|
+
}
|
|
54
|
+
function normalizePattern(value, field) {
|
|
55
|
+
let pattern = value.trim().replaceAll("\\", "/");
|
|
56
|
+
while (pattern.startsWith("./")) {
|
|
57
|
+
pattern = pattern.slice(2);
|
|
58
|
+
}
|
|
59
|
+
if (pattern.length === 0) {
|
|
60
|
+
throw new Error(`Change contract ${field} must not contain a blank pattern.`);
|
|
61
|
+
}
|
|
62
|
+
if (pattern.startsWith("/") || pattern.split("/").includes("..")) {
|
|
63
|
+
throw new Error(`Change contract patterns must be repository-relative: ${value}`);
|
|
64
|
+
}
|
|
65
|
+
return pattern;
|
|
66
|
+
}
|
|
67
|
+
function normalizeBudget(value, field) {
|
|
68
|
+
if (value === undefined) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
72
|
+
throw new Error(`Change contract scope.${field} must be a non-negative integer.`);
|
|
73
|
+
}
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=change-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change-contract.js","sourceRoot":"","sources":["../../src/domain/change-contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export declare const fileChangeKinds: readonly ["added", "modified", "deleted", "renamed", "copied", "type-changed", "unmerged", "unknown"];
|
|
2
|
+
export type FileChangeKind = (typeof fileChangeKinds)[number];
|
|
3
|
+
/** 라인 수 0과 측정 불가 상태를 혼동하지 않도록 구별된 유니온으로 표현한다. */
|
|
4
|
+
export type LineDelta = Readonly<{
|
|
5
|
+
kind: "measured";
|
|
6
|
+
additions: number;
|
|
7
|
+
deletions: number;
|
|
8
|
+
}> | Readonly<{
|
|
9
|
+
kind: "binary";
|
|
10
|
+
}> | Readonly<{
|
|
11
|
+
kind: "unavailable";
|
|
12
|
+
reason: string;
|
|
13
|
+
}>;
|
|
14
|
+
export interface FileChange {
|
|
15
|
+
readonly path: string;
|
|
16
|
+
readonly previousPath?: string;
|
|
17
|
+
readonly kind: FileChangeKind;
|
|
18
|
+
readonly lines: LineDelta;
|
|
19
|
+
}
|
|
20
|
+
export interface ChangeSummary {
|
|
21
|
+
readonly filesChanged: number;
|
|
22
|
+
readonly additions: number;
|
|
23
|
+
readonly deletions: number;
|
|
24
|
+
readonly binaryFiles: number;
|
|
25
|
+
readonly unmeasuredFiles: number;
|
|
26
|
+
readonly filesByKind: Readonly<Record<FileChangeKind, number>>;
|
|
27
|
+
}
|
|
28
|
+
export interface ChangeSet {
|
|
29
|
+
readonly files: readonly FileChange[];
|
|
30
|
+
readonly summary: ChangeSummary;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 파일 단위 변경을 정렬하고 보고서에서 재사용할 요약 통계를 한 번만 계산한다.
|
|
34
|
+
* 같은 결과 경로가 두 번 들어오면 분석 결과가 모호해지므로 도메인 경계에서 거부한다.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createChangeSet(changes: readonly FileChange[]): ChangeSet;
|
|
37
|
+
//# sourceMappingURL=change.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change.d.ts","sourceRoot":"","sources":["../../src/domain/change.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,uGASlB,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9D,iDAAiD;AACjD,MAAM,MAAM,SAAS,GACjB,QAAQ,CAAC;IACP,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,GACF,QAAQ,CAAC;IACP,IAAI,EAAE,QAAQ,CAAC;CAChB,CAAC,GACF,QAAQ,CAAC;IACP,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEP,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;CACjC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,CA6CzE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export const fileChangeKinds = [
|
|
2
|
+
"added",
|
|
3
|
+
"modified",
|
|
4
|
+
"deleted",
|
|
5
|
+
"renamed",
|
|
6
|
+
"copied",
|
|
7
|
+
"type-changed",
|
|
8
|
+
"unmerged",
|
|
9
|
+
"unknown",
|
|
10
|
+
];
|
|
11
|
+
/**
|
|
12
|
+
* 파일 단위 변경을 정렬하고 보고서에서 재사용할 요약 통계를 한 번만 계산한다.
|
|
13
|
+
* 같은 결과 경로가 두 번 들어오면 분석 결과가 모호해지므로 도메인 경계에서 거부한다.
|
|
14
|
+
*/
|
|
15
|
+
export function createChangeSet(changes) {
|
|
16
|
+
assertUniquePaths(changes);
|
|
17
|
+
const files = [...changes]
|
|
18
|
+
.map((change) => Object.freeze({ ...change, lines: Object.freeze({ ...change.lines }) }))
|
|
19
|
+
.sort((left, right) => left.path.localeCompare(right.path));
|
|
20
|
+
const filesByKind = Object.fromEntries(fileChangeKinds.map((kind) => [kind, 0]));
|
|
21
|
+
let additions = 0;
|
|
22
|
+
let deletions = 0;
|
|
23
|
+
let binaryFiles = 0;
|
|
24
|
+
let unmeasuredFiles = 0;
|
|
25
|
+
for (const file of files) {
|
|
26
|
+
filesByKind[file.kind] += 1;
|
|
27
|
+
switch (file.lines.kind) {
|
|
28
|
+
case "measured":
|
|
29
|
+
additions += file.lines.additions;
|
|
30
|
+
deletions += file.lines.deletions;
|
|
31
|
+
break;
|
|
32
|
+
case "binary":
|
|
33
|
+
binaryFiles += 1;
|
|
34
|
+
break;
|
|
35
|
+
case "unavailable":
|
|
36
|
+
unmeasuredFiles += 1;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return Object.freeze({
|
|
41
|
+
files: Object.freeze(files),
|
|
42
|
+
summary: Object.freeze({
|
|
43
|
+
filesChanged: files.length,
|
|
44
|
+
additions,
|
|
45
|
+
deletions,
|
|
46
|
+
binaryFiles,
|
|
47
|
+
unmeasuredFiles,
|
|
48
|
+
filesByKind: Object.freeze(filesByKind),
|
|
49
|
+
}),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function assertUniquePaths(changes) {
|
|
53
|
+
const paths = new Set();
|
|
54
|
+
for (const change of changes) {
|
|
55
|
+
if (paths.has(change.path)) {
|
|
56
|
+
throw new Error(`Duplicate file change path: ${change.path}`);
|
|
57
|
+
}
|
|
58
|
+
paths.add(change.path);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=change.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change.js","sourceRoot":"","sources":["../../src/domain/change.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,OAAO;IACP,UAAU;IACV,SAAS;IACT,SAAS;IACT,QAAQ;IACR,cAAc;IACd,UAAU;IACV,SAAS;CACD,CAAC;AAwCX;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAA8B;IAC5D,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAE3B,MAAM,KAAK,GAAG,CAAC,GAAG,OAAO,CAAC;SACvB,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;SACxF,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAE9D,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAG9E,CAAC;IAEF,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,eAAe,GAAG,CAAC,CAAC;IAExB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE5B,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YACxB,KAAK,UAAU;gBACb,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;gBAClC,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;gBAClC,MAAM;YACR,KAAK,QAAQ;gBACX,WAAW,IAAI,CAAC,CAAC;gBACjB,MAAM;YACR,KAAK,aAAa;gBAChB,eAAe,IAAI,CAAC,CAAC;gBACrB,MAAM;QACV,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;QAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC;YACrB,YAAY,EAAE,KAAK,CAAC,MAAM;YAC1B,SAAS;YACT,SAAS;YACT,WAAW;YACX,eAAe;YACf,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC;SACxC,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAED,SAAS,iBAAiB,CAAC,OAA8B;IACvD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAEhC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,+BAA+B,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface CodeSymbolLocation {
|
|
2
|
+
readonly path: string;
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly line: number;
|
|
5
|
+
}
|
|
6
|
+
export interface DuplicateImplementationCandidate {
|
|
7
|
+
readonly added: CodeSymbolLocation;
|
|
8
|
+
readonly existing: CodeSymbolLocation;
|
|
9
|
+
}
|
|
10
|
+
export interface SingleImplementationAbstraction {
|
|
11
|
+
readonly abstraction: CodeSymbolLocation;
|
|
12
|
+
readonly implementation: CodeSymbolLocation;
|
|
13
|
+
}
|
|
14
|
+
export interface CodeStructureAnalysisIssue {
|
|
15
|
+
readonly path: string;
|
|
16
|
+
readonly reason: string;
|
|
17
|
+
}
|
|
18
|
+
export interface CodeStructureAnalysisSummary {
|
|
19
|
+
readonly sourceFiles: number;
|
|
20
|
+
readonly filesUnavailable: number;
|
|
21
|
+
readonly duplicateCandidates: number;
|
|
22
|
+
readonly singleImplementationAbstractions: number;
|
|
23
|
+
}
|
|
24
|
+
export interface CodeStructureAnalysis {
|
|
25
|
+
readonly duplicateCandidates: readonly DuplicateImplementationCandidate[];
|
|
26
|
+
readonly singleImplementationAbstractions: readonly SingleImplementationAbstraction[];
|
|
27
|
+
readonly issues: readonly CodeStructureAnalysisIssue[];
|
|
28
|
+
readonly summary: CodeStructureAnalysisSummary;
|
|
29
|
+
}
|
|
30
|
+
export interface CodeStructureAnalysisInput {
|
|
31
|
+
readonly sourceFiles: number;
|
|
32
|
+
readonly duplicateCandidates: readonly DuplicateImplementationCandidate[];
|
|
33
|
+
readonly singleImplementationAbstractions: readonly SingleImplementationAbstraction[];
|
|
34
|
+
readonly issues?: readonly CodeStructureAnalysisIssue[];
|
|
35
|
+
}
|
|
36
|
+
/** 구조 분석 근거를 안정적으로 정렬하고 외부에서 변경할 수 없는 결과로 만든다. */
|
|
37
|
+
export declare function createCodeStructureAnalysis(input: CodeStructureAnalysisInput): CodeStructureAnalysis;
|
|
38
|
+
//# sourceMappingURL=code-structure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-structure.d.ts","sourceRoot":"","sources":["../../src/domain/code-structure.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;CACvC;AAED,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,WAAW,EAAE,kBAAkB,CAAC;IACzC,QAAQ,CAAC,cAAc,EAAE,kBAAkB,CAAC;CAC7C;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,gCAAgC,EAAE,MAAM,CAAC;CACnD;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,mBAAmB,EAAE,SAAS,gCAAgC,EAAE,CAAC;IAC1E,QAAQ,CAAC,gCAAgC,EAAE,SAAS,+BAA+B,EAAE,CAAC;IACtF,QAAQ,CAAC,MAAM,EAAE,SAAS,0BAA0B,EAAE,CAAC;IACvD,QAAQ,CAAC,OAAO,EAAE,4BAA4B,CAAC;CAChD;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,mBAAmB,EAAE,SAAS,gCAAgC,EAAE,CAAC;IAC1E,QAAQ,CAAC,gCAAgC,EAAE,SAAS,+BAA+B,EAAE,CAAC;IACtF,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,0BAA0B,EAAE,CAAC;CACzD;AAED,kDAAkD;AAClD,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,0BAA0B,GAChC,qBAAqB,CAsCvB"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** 구조 분석 근거를 안정적으로 정렬하고 외부에서 변경할 수 없는 결과로 만든다. */
|
|
2
|
+
export function createCodeStructureAnalysis(input) {
|
|
3
|
+
if (!Number.isInteger(input.sourceFiles) || input.sourceFiles < 0) {
|
|
4
|
+
throw new Error("Code structure source file count must be a non-negative integer");
|
|
5
|
+
}
|
|
6
|
+
assertUniqueDuplicateCandidates(input.duplicateCandidates);
|
|
7
|
+
assertUniqueAbstractions(input.singleImplementationAbstractions);
|
|
8
|
+
const duplicateCandidates = input.duplicateCandidates
|
|
9
|
+
.map((candidate) => Object.freeze({
|
|
10
|
+
added: freezeLocation(candidate.added),
|
|
11
|
+
existing: freezeLocation(candidate.existing),
|
|
12
|
+
}))
|
|
13
|
+
.sort(compareDuplicateCandidates);
|
|
14
|
+
const singleImplementationAbstractions = input.singleImplementationAbstractions
|
|
15
|
+
.map((candidate) => Object.freeze({
|
|
16
|
+
abstraction: freezeLocation(candidate.abstraction),
|
|
17
|
+
implementation: freezeLocation(candidate.implementation),
|
|
18
|
+
}))
|
|
19
|
+
.sort((left, right) => compareLocations(left.abstraction, right.abstraction));
|
|
20
|
+
const issues = [...(input.issues ?? [])]
|
|
21
|
+
.map((issue) => Object.freeze({ ...issue }))
|
|
22
|
+
.sort((left, right) => left.path.localeCompare(right.path));
|
|
23
|
+
return Object.freeze({
|
|
24
|
+
duplicateCandidates: Object.freeze(duplicateCandidates),
|
|
25
|
+
singleImplementationAbstractions: Object.freeze(singleImplementationAbstractions),
|
|
26
|
+
issues: Object.freeze(issues),
|
|
27
|
+
summary: Object.freeze({
|
|
28
|
+
sourceFiles: input.sourceFiles,
|
|
29
|
+
filesUnavailable: issues.length,
|
|
30
|
+
duplicateCandidates: duplicateCandidates.length,
|
|
31
|
+
singleImplementationAbstractions: singleImplementationAbstractions.length,
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function freezeLocation(location) {
|
|
36
|
+
return Object.freeze({ ...location });
|
|
37
|
+
}
|
|
38
|
+
function compareDuplicateCandidates(left, right) {
|
|
39
|
+
return (compareLocations(left.added, right.added) || compareLocations(left.existing, right.existing));
|
|
40
|
+
}
|
|
41
|
+
function compareLocations(left, right) {
|
|
42
|
+
return (left.path.localeCompare(right.path) ||
|
|
43
|
+
left.line - right.line ||
|
|
44
|
+
left.name.localeCompare(right.name));
|
|
45
|
+
}
|
|
46
|
+
function assertUniqueDuplicateCandidates(candidates) {
|
|
47
|
+
assertUnique(candidates.map((candidate) => [
|
|
48
|
+
candidate.added.path,
|
|
49
|
+
candidate.added.name,
|
|
50
|
+
candidate.existing.path,
|
|
51
|
+
candidate.existing.name,
|
|
52
|
+
].join("\0")), "duplicate implementation candidate");
|
|
53
|
+
}
|
|
54
|
+
function assertUniqueAbstractions(candidates) {
|
|
55
|
+
assertUnique(candidates.map((candidate) => [candidate.abstraction.path, candidate.abstraction.name].join("\0")), "single-implementation abstraction");
|
|
56
|
+
}
|
|
57
|
+
function assertUnique(identities, label) {
|
|
58
|
+
const unique = new Set(identities);
|
|
59
|
+
if (unique.size !== identities.length) {
|
|
60
|
+
throw new Error(`Duplicate ${label}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=code-structure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-structure.js","sourceRoot":"","sources":["../../src/domain/code-structure.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const severities: readonly ["high", "medium", "low"];
|
|
2
|
+
export type Severity = (typeof severities)[number];
|
|
3
|
+
export type FindingEvidenceValue = string | number | boolean;
|
|
4
|
+
/** 분석 규칙이 내린 판단과 재현 가능한 근거를 함께 보존한다. */
|
|
5
|
+
export interface Finding {
|
|
6
|
+
readonly ruleId: string;
|
|
7
|
+
readonly severity: Severity;
|
|
8
|
+
readonly title: string;
|
|
9
|
+
readonly description: string;
|
|
10
|
+
readonly file: string;
|
|
11
|
+
readonly evidence: Readonly<Record<string, FindingEvidenceValue>>;
|
|
12
|
+
}
|
|
13
|
+
export interface FindingSummary {
|
|
14
|
+
readonly total: number;
|
|
15
|
+
readonly bySeverity: Readonly<Record<Severity, number>>;
|
|
16
|
+
}
|
|
17
|
+
export interface FindingSet {
|
|
18
|
+
readonly items: readonly Finding[];
|
|
19
|
+
readonly summary: FindingSummary;
|
|
20
|
+
}
|
|
21
|
+
/** 규칙 실행 순서와 관계없이 같은 보고서가 나오도록 finding을 정렬하고 집계한다. */
|
|
22
|
+
export declare function createFindingSet(findings: readonly Finding[]): FindingSet;
|
|
23
|
+
export declare function isSeverityAtLeast(severity: Severity, threshold: Severity): boolean;
|
|
24
|
+
//# sourceMappingURL=finding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding.d.ts","sourceRoot":"","sources":["../../src/domain/finding.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,oCAAqC,CAAC;AAE7D,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE7D,wCAAwC;AACxC,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;CACnE;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;CACzD;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;CAClC;AAQD,sDAAsD;AACtD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,UAAU,CAgBzE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,GAAG,OAAO,CAElF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const severities = ["high", "medium", "low"];
|
|
2
|
+
const severityRank = {
|
|
3
|
+
high: 3,
|
|
4
|
+
medium: 2,
|
|
5
|
+
low: 1,
|
|
6
|
+
};
|
|
7
|
+
/** 규칙 실행 순서와 관계없이 같은 보고서가 나오도록 finding을 정렬하고 집계한다. */
|
|
8
|
+
export function createFindingSet(findings) {
|
|
9
|
+
const items = findings
|
|
10
|
+
.map((finding) => Object.freeze({ ...finding, evidence: Object.freeze({ ...finding.evidence }) }))
|
|
11
|
+
.sort(compareFindings);
|
|
12
|
+
const bySeverity = { high: 0, medium: 0, low: 0 };
|
|
13
|
+
for (const finding of items) {
|
|
14
|
+
bySeverity[finding.severity] += 1;
|
|
15
|
+
}
|
|
16
|
+
return Object.freeze({
|
|
17
|
+
items: Object.freeze(items),
|
|
18
|
+
summary: Object.freeze({ total: items.length, bySeverity: Object.freeze(bySeverity) }),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export function isSeverityAtLeast(severity, threshold) {
|
|
22
|
+
return severityRank[severity] >= severityRank[threshold];
|
|
23
|
+
}
|
|
24
|
+
function compareFindings(left, right) {
|
|
25
|
+
return (severityRank[right.severity] - severityRank[left.severity] ||
|
|
26
|
+
left.file.localeCompare(right.file) ||
|
|
27
|
+
left.ruleId.localeCompare(right.ruleId) ||
|
|
28
|
+
left.title.localeCompare(right.title));
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=finding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding.js","sourceRoot":"","sources":["../../src/domain/finding.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAU,CAAC;AA0B7D,MAAM,YAAY,GAAuC;IACvD,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,GAAG,EAAE,CAAC;CACP,CAAC;AAEF,sDAAsD;AACtD,MAAM,UAAU,gBAAgB,CAAC,QAA4B;IAC3D,MAAM,KAAK,GAAG,QAAQ;SACnB,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACf,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAChF;SACA,IAAI,CAAC,eAAe,CAAC,CAAC;IACzB,MAAM,UAAU,GAA6B,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;IAE5E,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;QAC5B,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;QAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;KACvF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAkB,EAAE,SAAmB;IACvE,OAAO,YAAY,CAAC,QAAQ,CAAC,IAAI,YAAY,CAAC,SAAS,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,eAAe,CAAC,IAAa,EAAE,KAAc;IACpD,OAAO,CACL,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC1D,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;QACvC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,CACtC,CAAC;AACJ,CAAC"}
|