ts-openapi-codegen 2.1.0-beta.5 → 2.1.0-beta.7

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 (210) hide show
  1. package/README.md +3 -0
  2. package/README.rus.md +3 -0
  3. package/dist/cli/analyzeDiff/__tests__/analyzeDiff.cli.test.js +9 -0
  4. package/dist/cli/analyzeDiff/__tests__/analyzeDiffRenameAndInvalidRegex.test.js +9 -0
  5. package/dist/cli/analyzeDiff/__tests__/analyzeDiffTypeCoercion.test.js +9 -0
  6. package/dist/cli/analyzeUsage/analyzeUsage.d.ts +4 -0
  7. package/dist/cli/analyzeUsage/analyzeUsage.d.ts.map +1 -0
  8. package/dist/cli/analyzeUsage/analyzeUsage.js +62 -0
  9. package/dist/cli/analyzeUsage/core/Analyzer.d.ts +9 -0
  10. package/dist/cli/analyzeUsage/core/Analyzer.d.ts.map +1 -0
  11. package/dist/cli/analyzeUsage/core/Analyzer.js +29 -0
  12. package/dist/cli/analyzeUsage/core/ProjectContext.d.ts +10 -0
  13. package/dist/cli/analyzeUsage/core/ProjectContext.d.ts.map +1 -0
  14. package/dist/cli/analyzeUsage/core/ProjectContext.js +63 -0
  15. package/dist/cli/analyzeUsage/core/Scanner.d.ts +15 -0
  16. package/dist/cli/analyzeUsage/core/Scanner.d.ts.map +1 -0
  17. package/dist/cli/analyzeUsage/core/Scanner.js +115 -0
  18. package/dist/cli/analyzeUsage/rules/ClientRule.d.ts +6 -0
  19. package/dist/cli/analyzeUsage/rules/ClientRule.d.ts.map +1 -0
  20. package/dist/cli/analyzeUsage/rules/ClientRule.js +78 -0
  21. package/dist/cli/analyzeUsage/rules/CoverageRule.d.ts +7 -0
  22. package/dist/cli/analyzeUsage/rules/CoverageRule.d.ts.map +1 -0
  23. package/dist/cli/analyzeUsage/rules/CoverageRule.js +63 -0
  24. package/dist/cli/analyzeUsage/rules/DiagnosticsRule.d.ts +6 -0
  25. package/dist/cli/analyzeUsage/rules/DiagnosticsRule.d.ts.map +1 -0
  26. package/dist/cli/analyzeUsage/rules/DiagnosticsRule.js +60 -0
  27. package/dist/cli/analyzeUsage/rules/ImportRule.d.ts +6 -0
  28. package/dist/cli/analyzeUsage/rules/ImportRule.d.ts.map +1 -0
  29. package/dist/cli/analyzeUsage/rules/ImportRule.js +52 -0
  30. package/dist/cli/analyzeUsage/rules/ModelRule.d.ts +6 -0
  31. package/dist/cli/analyzeUsage/rules/ModelRule.d.ts.map +1 -0
  32. package/dist/cli/analyzeUsage/rules/ModelRule.js +38 -0
  33. package/dist/cli/analyzeUsage/rules/SchemaRule.d.ts +6 -0
  34. package/dist/cli/analyzeUsage/rules/SchemaRule.d.ts.map +1 -0
  35. package/dist/cli/analyzeUsage/rules/SchemaRule.js +48 -0
  36. package/dist/cli/analyzeUsage/rules/ServiceRule.d.ts +7 -0
  37. package/dist/cli/analyzeUsage/rules/ServiceRule.d.ts.map +1 -0
  38. package/dist/cli/analyzeUsage/rules/ServiceRule.js +92 -0
  39. package/dist/cli/analyzeUsage/types.d.ts +52 -0
  40. package/dist/cli/analyzeUsage/types.d.ts.map +1 -0
  41. package/dist/cli/analyzeUsage/types.js +2 -0
  42. package/dist/cli/analyzeUsage/utils/fuzzy.d.ts +5 -0
  43. package/dist/cli/analyzeUsage/utils/fuzzy.d.ts.map +1 -0
  44. package/dist/cli/analyzeUsage/utils/fuzzy.js +38 -0
  45. package/dist/cli/analyzeUsage/utils/report.d.ts +16 -0
  46. package/dist/cli/analyzeUsage/utils/report.d.ts.map +1 -0
  47. package/dist/cli/analyzeUsage/utils/report.js +85 -0
  48. package/dist/cli/checkAndUpdateConfig/__tests__/checkConfig.test.d.ts +2 -0
  49. package/dist/cli/checkAndUpdateConfig/__tests__/checkConfig.test.d.ts.map +1 -0
  50. package/dist/cli/checkAndUpdateConfig/__tests__/checkConfig.test.js +53 -0
  51. package/dist/cli/checkAndUpdateConfig/__tests__/updateConfig.test.d.ts +2 -0
  52. package/dist/cli/checkAndUpdateConfig/__tests__/updateConfig.test.d.ts.map +1 -0
  53. package/dist/cli/checkAndUpdateConfig/__tests__/updateConfig.test.js +52 -0
  54. package/dist/cli/checkAndUpdateConfig/checkConfig.d.ts +2 -1
  55. package/dist/cli/checkAndUpdateConfig/checkConfig.d.ts.map +1 -1
  56. package/dist/cli/checkAndUpdateConfig/checkConfig.js +8 -4
  57. package/dist/cli/checkAndUpdateConfig/updateConfig.d.ts +2 -1
  58. package/dist/cli/checkAndUpdateConfig/updateConfig.d.ts.map +1 -1
  59. package/dist/cli/checkAndUpdateConfig/updateConfig.js +6 -3
  60. package/dist/cli/checkAndUpdateConfig/utils/__tests__/prepareConfigData.test.d.ts +2 -0
  61. package/dist/cli/checkAndUpdateConfig/utils/__tests__/prepareConfigData.test.d.ts.map +1 -0
  62. package/dist/cli/checkAndUpdateConfig/utils/__tests__/prepareConfigData.test.js +20 -0
  63. package/dist/cli/checkAndUpdateConfig/utils/__tests__/removeDefaultConfigValues.test.d.ts +2 -0
  64. package/dist/cli/checkAndUpdateConfig/utils/__tests__/removeDefaultConfigValues.test.d.ts.map +1 -0
  65. package/dist/cli/checkAndUpdateConfig/utils/__tests__/removeDefaultConfigValues.test.js +35 -0
  66. package/dist/cli/checkAndUpdateConfig/utils/__tests__/validateAndMigrateConfigData.test.d.ts +2 -0
  67. package/dist/cli/checkAndUpdateConfig/utils/__tests__/validateAndMigrateConfigData.test.d.ts.map +1 -0
  68. package/dist/cli/checkAndUpdateConfig/utils/__tests__/validateAndMigrateConfigData.test.js +59 -0
  69. package/dist/cli/checkAndUpdateConfig/utils/validateAndMigrateConfigData.d.ts.map +1 -1
  70. package/dist/cli/checkAndUpdateConfig/utils/validateAndMigrateConfigData.js +19 -1
  71. package/dist/cli/generateOpenApiClient/__tests__/generateOpenApiClient.strict.test.js +18 -9
  72. package/dist/cli/generateOpenApiClient/generateOpenApiClient.d.ts +2 -5
  73. package/dist/cli/generateOpenApiClient/generateOpenApiClient.d.ts.map +1 -1
  74. package/dist/cli/generateOpenApiClient/generateOpenApiClient.js +12 -16
  75. package/dist/cli/index.js +41 -7
  76. package/dist/cli/initOpenApiConfig/__tests__/init.test.d.ts +2 -0
  77. package/dist/cli/initOpenApiConfig/__tests__/init.test.d.ts.map +1 -0
  78. package/dist/cli/initOpenApiConfig/__tests__/init.test.js +20 -0
  79. package/dist/cli/initOpenApiConfig/__tests__/initConfig.test.d.ts +2 -0
  80. package/dist/cli/initOpenApiConfig/__tests__/initConfig.test.d.ts.map +1 -0
  81. package/dist/cli/initOpenApiConfig/__tests__/initConfig.test.js +49 -0
  82. package/dist/cli/initOpenApiConfig/init.d.ts +2 -1
  83. package/dist/cli/initOpenApiConfig/init.d.ts.map +1 -1
  84. package/dist/cli/initOpenApiConfig/init.js +3 -1
  85. package/dist/cli/initOpenApiConfig/utils/__tests__/buildConfig.test.d.ts +2 -0
  86. package/dist/cli/initOpenApiConfig/utils/__tests__/buildConfig.test.d.ts.map +1 -0
  87. package/dist/cli/initOpenApiConfig/utils/__tests__/buildConfig.test.js +99 -0
  88. package/dist/cli/initOpenApiConfig/utils/__tests__/findSpecFiles.test.d.ts +2 -0
  89. package/dist/cli/initOpenApiConfig/utils/__tests__/findSpecFiles.test.d.ts.map +1 -0
  90. package/dist/cli/initOpenApiConfig/utils/__tests__/findSpecFiles.test.js +45 -0
  91. package/dist/cli/initOpenApiConfig/utils/__tests__/validateSpecFiles.test.d.ts +2 -0
  92. package/dist/cli/initOpenApiConfig/utils/__tests__/validateSpecFiles.test.d.ts.map +1 -0
  93. package/dist/cli/initOpenApiConfig/utils/__tests__/validateSpecFiles.test.js +61 -0
  94. package/dist/cli/previewChanges/previewChanges.d.ts +2 -1
  95. package/dist/cli/previewChanges/previewChanges.d.ts.map +1 -1
  96. package/dist/cli/previewChanges/previewChanges.js +6 -6
  97. package/dist/cli/previewChanges/utils/__tests__/compareFiles.test.d.ts +2 -0
  98. package/dist/cli/previewChanges/utils/__tests__/compareFiles.test.d.ts.map +1 -0
  99. package/dist/cli/previewChanges/utils/__tests__/compareFiles.test.js +52 -0
  100. package/dist/cli/previewChanges/utils/__tests__/isDirectoryEmpty.test.d.ts +2 -0
  101. package/dist/cli/previewChanges/utils/__tests__/isDirectoryEmpty.test.d.ts.map +1 -0
  102. package/dist/cli/previewChanges/utils/__tests__/isDirectoryEmpty.test.js +43 -0
  103. package/dist/cli/previewChanges/utils/__tests__/readDirectoryRecursive.test.d.ts +2 -0
  104. package/dist/cli/previewChanges/utils/__tests__/readDirectoryRecursive.test.d.ts.map +1 -0
  105. package/dist/cli/previewChanges/utils/__tests__/readDirectoryRecursive.test.js +32 -0
  106. package/dist/cli/previewChanges/utils/__tests__/updateOutputPaths.test.d.ts +2 -0
  107. package/dist/cli/previewChanges/utils/__tests__/updateOutputPaths.test.d.ts.map +1 -0
  108. package/dist/cli/previewChanges/utils/__tests__/updateOutputPaths.test.js +68 -0
  109. package/dist/cli/schemas/__tests__/checkConfigAndUpdate.test.d.ts +2 -0
  110. package/dist/cli/schemas/__tests__/checkConfigAndUpdate.test.d.ts.map +1 -0
  111. package/dist/cli/schemas/__tests__/checkConfigAndUpdate.test.js +36 -0
  112. package/dist/cli/schemas/__tests__/generateOptionsSchema.test.d.ts +2 -0
  113. package/dist/cli/schemas/__tests__/generateOptionsSchema.test.d.ts.map +1 -0
  114. package/dist/cli/schemas/__tests__/generateOptionsSchema.test.js +59 -0
  115. package/dist/cli/schemas/__tests__/initOptionsSchema.test.d.ts +2 -0
  116. package/dist/cli/schemas/__tests__/initOptionsSchema.test.d.ts.map +1 -0
  117. package/dist/cli/schemas/__tests__/initOptionsSchema.test.js +43 -0
  118. package/dist/cli/schemas/__tests__/previewChangesSchema.test.d.ts +2 -0
  119. package/dist/cli/schemas/__tests__/previewChangesSchema.test.d.ts.map +1 -0
  120. package/dist/cli/schemas/__tests__/previewChangesSchema.test.js +31 -0
  121. package/dist/cli/schemas/analyzeUsage.d.ts +13 -0
  122. package/dist/cli/schemas/analyzeUsage.d.ts.map +1 -0
  123. package/dist/cli/schemas/analyzeUsage.js +38 -0
  124. package/dist/cli/schemas/generate.d.ts +7 -0
  125. package/dist/cli/schemas/generate.d.ts.map +1 -1
  126. package/dist/cli/schemas/generate.js +4 -0
  127. package/dist/cli/schemas/index.d.ts +2 -2
  128. package/dist/cli/schemas/index.d.ts.map +1 -1
  129. package/dist/cli/schemas/index.js +1 -1
  130. package/dist/cli/types.d.ts +6 -0
  131. package/dist/cli/types.d.ts.map +1 -0
  132. package/dist/cli/types.js +2 -0
  133. package/dist/common/Consts.d.ts +1 -0
  134. package/dist/common/Consts.d.ts.map +1 -1
  135. package/dist/common/Consts.js +6 -1
  136. package/dist/common/Logger.js +2 -2
  137. package/dist/common/LoggerMessages.d.ts +59 -12
  138. package/dist/common/LoggerMessages.d.ts.map +1 -1
  139. package/dist/common/LoggerMessages.js +62 -15
  140. package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedOptionsSchemaV5.d.ts +7 -0
  141. package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedOptionsSchemaV5.d.ts.map +1 -1
  142. package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedOptionsSchemaV5.js +5 -1
  143. package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedVersionedSchemas.d.ts +14 -0
  144. package/dist/common/VersionedSchema/AllVersionedSchemas/UnifiedVersionedSchemas.d.ts.map +1 -1
  145. package/dist/common/VersionedSchema/Utils/__mocks__/compatibilityCases.d.ts.map +1 -1
  146. package/dist/common/VersionedSchema/Utils/__mocks__/compatibilityCases.js +6 -1
  147. package/dist/common/VersionedSchema/Utils/migrateDataToLatestSchemaVersion.d.ts.map +1 -1
  148. package/dist/common/VersionedSchema/Utils/migrateDataToLatestSchemaVersion.js +1 -1
  149. package/dist/core/OpenApiClient.d.ts +12 -1
  150. package/dist/core/OpenApiClient.d.ts.map +1 -1
  151. package/dist/core/OpenApiClient.js +192 -10
  152. package/dist/core/WriteClient.d.ts +11 -0
  153. package/dist/core/WriteClient.d.ts.map +1 -1
  154. package/dist/core/WriteClient.js +23 -2
  155. package/dist/core/plugins/getBuiltinPlugins.d.ts.map +1 -1
  156. package/dist/core/plugins/loadGeneratorPlugins.d.ts.map +1 -1
  157. package/dist/core/plugins/loadGeneratorPlugins.js +1 -1
  158. package/dist/core/types/shared/Client.model.d.ts +1 -1
  159. package/dist/core/types/shared/Client.model.d.ts.map +1 -1
  160. package/dist/core/types/shared/Model.model.d.ts +1 -1
  161. package/dist/core/types/shared/Model.model.d.ts.map +1 -1
  162. package/dist/core/utils/GenerationCache.d.ts +18 -0
  163. package/dist/core/utils/GenerationCache.d.ts.map +1 -0
  164. package/dist/core/utils/GenerationCache.js +41 -0
  165. package/dist/core/utils/__tests__/GenerationCache.test.d.ts +2 -0
  166. package/dist/core/utils/__tests__/GenerationCache.test.d.ts.map +1 -0
  167. package/dist/core/utils/__tests__/GenerationCache.test.js +37 -0
  168. package/dist/core/utils/__tests__/loadDiffReport.test.d.ts +2 -0
  169. package/dist/core/utils/__tests__/loadDiffReport.test.d.ts.map +1 -0
  170. package/dist/core/utils/__tests__/loadDiffReport.test.js +97 -0
  171. package/dist/core/utils/__tests__/prepareDtoModels.test.js +95 -0
  172. package/dist/core/utils/__tests__/templateRendering.test.d.ts +2 -0
  173. package/dist/core/utils/__tests__/templateRendering.test.d.ts.map +1 -0
  174. package/dist/core/utils/__tests__/templateRendering.test.js +119 -0
  175. package/dist/core/utils/__tests__/writeFileIfChanged.test.d.ts +2 -0
  176. package/dist/core/utils/__tests__/writeFileIfChanged.test.d.ts.map +1 -0
  177. package/dist/core/utils/__tests__/writeFileIfChanged.test.js +30 -0
  178. package/dist/core/utils/loadDiffReport.d.ts.map +1 -1
  179. package/dist/core/utils/loadDiffReport.js +5 -4
  180. package/dist/core/utils/precompileTemplates.js +3 -2
  181. package/dist/core/utils/prepareDtoModels.d.ts.map +1 -1
  182. package/dist/core/utils/prepareDtoModels.js +1 -7
  183. package/dist/core/utils/writeClientCore.d.ts.map +1 -1
  184. package/dist/core/utils/writeClientCore.js +18 -16
  185. package/dist/core/utils/writeClientCoreIndex.d.ts.map +1 -1
  186. package/dist/core/utils/writeClientCoreIndex.js +14 -4
  187. package/dist/core/utils/writeClientExecutor.d.ts.map +1 -1
  188. package/dist/core/utils/writeClientExecutor.js +4 -4
  189. package/dist/core/utils/writeClientFullIndex.js +4 -4
  190. package/dist/core/utils/writeClientModels.d.ts.map +1 -1
  191. package/dist/core/utils/writeClientModels.js +10 -10
  192. package/dist/core/utils/writeClientModelsIndex.d.ts +1 -1
  193. package/dist/core/utils/writeClientModelsIndex.d.ts.map +1 -1
  194. package/dist/core/utils/writeClientModelsIndex.js +14 -4
  195. package/dist/core/utils/writeClientSchemas.d.ts.map +1 -1
  196. package/dist/core/utils/writeClientSchemas.js +7 -7
  197. package/dist/core/utils/writeClientSchemasIndex.d.ts.map +1 -1
  198. package/dist/core/utils/writeClientSchemasIndex.js +14 -4
  199. package/dist/core/utils/writeClientServices.d.ts.map +1 -1
  200. package/dist/core/utils/writeClientServices.js +6 -6
  201. package/dist/core/utils/writeClientServicesIndex.d.ts.map +1 -1
  202. package/dist/core/utils/writeClientServicesIndex.js +14 -4
  203. package/dist/core/utils/writeClientSimpleIndex.js +4 -4
  204. package/dist/core/utils/writeFileIfChanged.d.ts +3 -0
  205. package/dist/core/utils/writeFileIfChanged.d.ts.map +1 -0
  206. package/dist/core/utils/writeFileIfChanged.js +22 -0
  207. package/dist/test/helpers/silenceLoggers.d.ts +11 -0
  208. package/dist/test/helpers/silenceLoggers.d.ts.map +1 -0
  209. package/dist/test/helpers/silenceLoggers.js +88 -0
  210. package/package.json +4 -4
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DiagnosticsRule = void 0;
4
+ /* eslint-disable @typescript-eslint/no-unused-vars */
5
+ const ts_morph_1 = require("ts-morph");
6
+ class DiagnosticsRule {
7
+ async check(context, _contract, _stats) {
8
+ const findings = [];
9
+ for (const file of context.getConsumerSourceFiles()) {
10
+ const hasApiImport = file.getImportDeclarations().some(imp => {
11
+ const moduleName = imp.getModuleSpecifierValue();
12
+ return moduleName === '@lom-api' || moduleName.startsWith('@lom-api/');
13
+ });
14
+ if (!hasApiImport)
15
+ continue;
16
+ const diagnostics = context.project.getPreEmitDiagnostics().filter(diag => {
17
+ const sourceFile = diag.getSourceFile();
18
+ return sourceFile?.getFilePath() === file.getFilePath();
19
+ });
20
+ for (const diagnostic of diagnostics) {
21
+ const category = diagnostic.getCategory();
22
+ if (category !== ts_morph_1.ts.DiagnosticCategory.Error)
23
+ continue;
24
+ if (diagnostic.getCode() === 2305 || diagnostic.getCode() === 2307) {
25
+ // Covered by ImportRule to avoid duplicate findings for the same import issue.
26
+ continue;
27
+ }
28
+ const line = diagnostic.getLineNumber() ?? 0;
29
+ const messageText = diagnostic.getMessageText();
30
+ if (typeof messageText === 'string') {
31
+ findings.push({
32
+ id: `TS_DIAGNOSTIC_${diagnostic.getCode()}`,
33
+ category: 'TYPE_MISMATCH',
34
+ severity: 'ERROR',
35
+ message: String(diagnostic.getMessageText()),
36
+ file: file.getFilePath(),
37
+ line,
38
+ context: { code: diagnostic.getCode() },
39
+ });
40
+ }
41
+ else if (typeof messageText === 'object') {
42
+ // For multiline messages, concatenate the text parts.
43
+ const mainMessage = messageText.getMessageText();
44
+ const details = messageText.getNext()?.map(part => part.getMessageText()).join(' ') ?? '';
45
+ findings.push({
46
+ id: `TS_DIAGNOSTIC_${diagnostic.getCode()}`,
47
+ category: 'TYPE_MISMATCH',
48
+ severity: 'ERROR',
49
+ message: `${mainMessage} ${details}`.trim(),
50
+ file: file.getFilePath(),
51
+ line,
52
+ context: { code: diagnostic.getCode() },
53
+ });
54
+ }
55
+ }
56
+ }
57
+ return findings;
58
+ }
59
+ }
60
+ exports.DiagnosticsRule = DiagnosticsRule;
@@ -0,0 +1,6 @@
1
+ import type { ProjectContext } from "../core/ProjectContext";
2
+ import type { Contract, Finding, Rule, Stats } from "../types";
3
+ export declare class ImportRule implements Rule {
4
+ check(context: ProjectContext, contract: Contract, _stats: Stats): Promise<Finding[]>;
5
+ }
6
+ //# sourceMappingURL=ImportRule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImportRule.d.ts","sourceRoot":"","sources":["../../../../src/cli/analyzeUsage/rules/ImportRule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAG/D,qBAAa,UAAW,YAAW,IAAI;IAC/B,KAAK,CACT,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,QAAQ,EAElB,MAAM,EAAE,KAAK,GACZ,OAAO,CAAC,OAAO,EAAE,CAAC;CAiDtB"}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ImportRule = void 0;
4
+ const fuzzy_1 = require("../utils/fuzzy");
5
+ class ImportRule {
6
+ async check(context, contract,
7
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
8
+ _stats) {
9
+ const findings = [];
10
+ const rootExports = new Set(contract.sourceFile.getExportedDeclarations().keys());
11
+ for (const file of context.getConsumerSourceFiles()) {
12
+ for (const imp of file.getImportDeclarations()) {
13
+ const moduleName = imp.getModuleSpecifierValue();
14
+ if (moduleName !== "@lom-api" && !moduleName.startsWith("@lom-api/")) {
15
+ continue;
16
+ }
17
+ const importedSource = imp.getModuleSpecifierSourceFile();
18
+ if (!importedSource) {
19
+ findings.push({
20
+ id: "INVALID_IMPORT_PATH",
21
+ category: "INVALID_IMPORT",
22
+ severity: "ERROR",
23
+ message: `Import "${moduleName}" could not be resolved.`,
24
+ file: file.getFilePath(),
25
+ line: imp.getStartLineNumber(),
26
+ });
27
+ continue;
28
+ }
29
+ const allowedExports = moduleName === "@lom-api"
30
+ ? rootExports
31
+ : new Set(importedSource.getExportedDeclarations().keys());
32
+ for (const namedImport of imp.getNamedImports()) {
33
+ const importedName = namedImport.getName();
34
+ if (allowedExports.has(importedName))
35
+ continue;
36
+ const suggestion = (0, fuzzy_1.findBestMatch)(importedName, [...allowedExports]);
37
+ findings.push({
38
+ id: "INVALID_IMPORT_NAME",
39
+ category: suggestion ? "RENAMED_SYMBOL" : "MISSING_EXPORT",
40
+ severity: "ERROR",
41
+ message: `Import "${importedName}" is not exported by "${moduleName}".${suggestion ? ` Did you mean "${suggestion}"?` : ""}`,
42
+ file: file.getFilePath(),
43
+ line: namedImport.getStartLineNumber(),
44
+ context: { suggestion },
45
+ });
46
+ }
47
+ }
48
+ }
49
+ return findings;
50
+ }
51
+ }
52
+ exports.ImportRule = ImportRule;
@@ -0,0 +1,6 @@
1
+ import type { ProjectContext } from "../core/ProjectContext";
2
+ import type { Contract, Finding, Rule, Stats } from "../types";
3
+ export declare class ModelRule implements Rule {
4
+ check(context: ProjectContext, contract: Contract, stats: Stats): Promise<Finding[]>;
5
+ }
6
+ //# sourceMappingURL=ModelRule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModelRule.d.ts","sourceRoot":"","sources":["../../../../src/cli/analyzeUsage/rules/ModelRule.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAE/D,qBAAa,SAAU,YAAW,IAAI;IAC9B,KAAK,CACT,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,OAAO,EAAE,CAAC;CAuCtB"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ModelRule = void 0;
4
+ const ts_morph_1 = require("ts-morph");
5
+ class ModelRule {
6
+ async check(context, contract, stats) {
7
+ const findings = [];
8
+ const knownModels = new Set(contract.models);
9
+ for (const file of context.getConsumerSourceFiles()) {
10
+ for (const imp of file.getImportDeclarations()) {
11
+ const moduleName = imp.getModuleSpecifierValue();
12
+ if (moduleName !== "@lom-api" && !moduleName.startsWith("@lom-api/")) {
13
+ continue;
14
+ }
15
+ for (const namedImport of imp.getNamedImports()) {
16
+ const importedName = namedImport.getName();
17
+ if (importedName.endsWith("Schema") ||
18
+ importedName.endsWith("Service") ||
19
+ importedName === "createClient") {
20
+ continue;
21
+ }
22
+ const localName = namedImport.getAliasNode()?.getText() || importedName;
23
+ if (!knownModels.has(importedName))
24
+ continue;
25
+ const identifiers = file
26
+ .getDescendantsOfKind(ts_morph_1.SyntaxKind.Identifier)
27
+ .filter((id) => id.getText() === localName);
28
+ const usedOutsideImport = identifiers.some((id) => id.getFirstAncestorByKind(ts_morph_1.SyntaxKind.ImportDeclaration) == null);
29
+ if (usedOutsideImport) {
30
+ stats.usedModels.add(importedName);
31
+ }
32
+ }
33
+ }
34
+ }
35
+ return findings;
36
+ }
37
+ }
38
+ exports.ModelRule = ModelRule;
@@ -0,0 +1,6 @@
1
+ import type { ProjectContext } from "../core/ProjectContext";
2
+ import type { Contract, Finding, Rule, Stats } from "../types";
3
+ export declare class SchemaRule implements Rule {
4
+ check(context: ProjectContext, contract: Contract, stats: Stats): Promise<Finding[]>;
5
+ }
6
+ //# sourceMappingURL=SchemaRule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SchemaRule.d.ts","sourceRoot":"","sources":["../../../../src/cli/analyzeUsage/rules/SchemaRule.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAG/D,qBAAa,UAAW,YAAW,IAAI;IAC/B,KAAK,CACT,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,OAAO,EAAE,CAAC;CAgDtB"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchemaRule = void 0;
4
+ const ts_morph_1 = require("ts-morph");
5
+ const fuzzy_1 = require("../utils/fuzzy");
6
+ class SchemaRule {
7
+ async check(context, contract, stats) {
8
+ const findings = [];
9
+ const knownSchemas = new Set(contract.schemas);
10
+ for (const file of context.getConsumerSourceFiles()) {
11
+ const imports = file.getImportDeclarations();
12
+ for (const imp of imports) {
13
+ const moduleName = imp.getModuleSpecifierValue();
14
+ if (moduleName !== "@lom-api" && !moduleName.startsWith("@lom-api/")) {
15
+ continue;
16
+ }
17
+ for (const namedImport of imp.getNamedImports()) {
18
+ const importedName = namedImport.getName();
19
+ if (!importedName.endsWith("Schema"))
20
+ continue;
21
+ const localName = namedImport.getAliasNode()?.getText() || importedName;
22
+ if (!knownSchemas.has(importedName)) {
23
+ const suggestion = (0, fuzzy_1.findBestMatch)(importedName, contract.schemas);
24
+ findings.push({
25
+ id: "SCHEMA_NOT_FOUND",
26
+ category: suggestion ? "RENAMED_SYMBOL" : "MISSING_EXPORT",
27
+ severity: "ERROR",
28
+ message: `Schema "${importedName}" was not found in the API.${suggestion ? ` Did you mean "${suggestion}"?` : ""}`,
29
+ file: file.getFilePath(),
30
+ line: namedImport.getStartLineNumber(),
31
+ context: { suggestion },
32
+ });
33
+ continue;
34
+ }
35
+ const identifiers = file
36
+ .getDescendantsOfKind(ts_morph_1.SyntaxKind.Identifier)
37
+ .filter((id) => id.getText() === localName);
38
+ const usedOutsideImport = identifiers.some((id) => id.getFirstAncestorByKind(ts_morph_1.SyntaxKind.ImportDeclaration) == null);
39
+ if (usedOutsideImport) {
40
+ stats.usedSchemas.add(importedName);
41
+ }
42
+ }
43
+ }
44
+ }
45
+ return findings;
46
+ }
47
+ }
48
+ exports.SchemaRule = SchemaRule;
@@ -0,0 +1,7 @@
1
+ import type { ProjectContext } from "../core/ProjectContext";
2
+ import type { Contract, Finding, Rule, Stats } from "../types";
3
+ export declare class ServiceRule implements Rule {
4
+ check(context: ProjectContext, contract: Contract, stats: Stats): Promise<Finding[]>;
5
+ private getExpectedParameterType;
6
+ }
7
+ //# sourceMappingURL=ServiceRule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ServiceRule.d.ts","sourceRoot":"","sources":["../../../../src/cli/analyzeUsage/rules/ServiceRule.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAG/D,qBAAa,WAAY,YAAW,IAAI;IAChC,KAAK,CACT,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,OAAO,EAAE,CAAC;IA0FrB,OAAO,CAAC,wBAAwB;CAiBjC"}
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ServiceRule = void 0;
4
+ const ts_morph_1 = require("ts-morph");
5
+ const fuzzy_1 = require("../utils/fuzzy");
6
+ class ServiceRule {
7
+ async check(context, contract, stats) {
8
+ const findings = [];
9
+ const checker = context.getTypeChecker();
10
+ for (const file of context.getConsumerSourceFiles()) {
11
+ const calls = file.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression);
12
+ for (const call of calls) {
13
+ const expression = call.getExpression();
14
+ if (!ts_morph_1.Node.isPropertyAccessExpression(expression))
15
+ continue;
16
+ const serviceAccess = expression.getExpression();
17
+ if (!ts_morph_1.Node.isPropertyAccessExpression(serviceAccess))
18
+ continue;
19
+ const serviceName = serviceAccess.getName();
20
+ const methodName = expression.getName();
21
+ if (!contract.services[serviceName])
22
+ continue;
23
+ stats.usedMethods.add(`${serviceName}.${methodName}`);
24
+ const methodContract = contract.services[serviceName].find((m) => m.name === methodName);
25
+ if (!methodContract) {
26
+ const suggestion = (0, fuzzy_1.findBestMatch)(methodName, contract.services[serviceName].map((m) => m.name));
27
+ findings.push({
28
+ id: "SERVICE_METHOD_NOT_FOUND",
29
+ category: suggestion ? "RENAMED_SYMBOL" : "MISSING_EXPORT",
30
+ severity: "ERROR",
31
+ message: `Method "${serviceName}.${methodName}" was not found in the generated API.${suggestion ? ` Did you mean "${serviceName}.${suggestion}"?` : ""}`,
32
+ file: file.getFilePath(),
33
+ line: call.getStartLineNumber(),
34
+ context: { suggestion },
35
+ });
36
+ continue;
37
+ }
38
+ const args = call.getArguments();
39
+ const serviceDecl = contract.sourceFile
40
+ .getExportedDeclarations()
41
+ .get(serviceName)
42
+ ?.find((d) => ts_morph_1.Node.isClassDeclaration(d));
43
+ const methodDecl = serviceDecl && ts_morph_1.Node.isClassDeclaration(serviceDecl)
44
+ ? serviceDecl.getMethod(methodName)
45
+ : undefined;
46
+ const requiredParams = methodDecl
47
+ ? methodDecl.getParameters().filter((p) => !p.isOptional())
48
+ : methodContract.params.filter((p) => !p.isOptional);
49
+ if (args.length < requiredParams.length) {
50
+ findings.push({
51
+ id: "SERVICE_ARGUMENTS_COUNT_MISMATCH",
52
+ category: "TYPE_MISMATCH",
53
+ severity: "ERROR",
54
+ message: `Method ${serviceName}.${methodName} expects at least ${requiredParams.length} arguments, but got ${args.length}.`,
55
+ file: file.getFilePath(),
56
+ line: call.getStartLineNumber(),
57
+ });
58
+ continue;
59
+ }
60
+ for (let i = 0; i < Math.min(args.length, methodContract.params.length); i++) {
61
+ const expectedType = this.getExpectedParameterType(contract, serviceName, methodName, i);
62
+ if (!expectedType)
63
+ continue;
64
+ const providedType = checker.getTypeAtLocation(args[i]);
65
+ if (!providedType.isAssignableTo(expectedType)) {
66
+ findings.push({
67
+ id: "SERVICE_ARGUMENT_TYPE_MISMATCH",
68
+ category: "TYPE_MISMATCH",
69
+ severity: "ERROR",
70
+ message: `Argument #${i + 1} type mismatch in ${serviceName}.${methodName}. Expected ${expectedType.getText()}, got ${providedType.getText()}.`,
71
+ file: file.getFilePath(),
72
+ line: args[i].getStartLineNumber(),
73
+ });
74
+ }
75
+ }
76
+ }
77
+ }
78
+ return findings;
79
+ }
80
+ getExpectedParameterType(contract, serviceName, methodName, paramIndex) {
81
+ const exportedDecl = contract.sourceFile
82
+ .getExportedDeclarations()
83
+ .get(serviceName)
84
+ ?.find((d) => ts_morph_1.Node.isClassDeclaration(d));
85
+ const method = exportedDecl && ts_morph_1.Node.isClassDeclaration(exportedDecl)
86
+ ? exportedDecl.getMethod(methodName)
87
+ : undefined;
88
+ const param = method?.getParameters()[paramIndex];
89
+ return param?.getType();
90
+ }
91
+ }
92
+ exports.ServiceRule = ServiceRule;
@@ -0,0 +1,52 @@
1
+ import type { SourceFile } from "ts-morph";
2
+ import type { ProjectContext } from "./core/ProjectContext";
3
+ export interface MethodMetadata {
4
+ name: string;
5
+ params: {
6
+ name: string;
7
+ type: string;
8
+ isOptional: boolean;
9
+ }[];
10
+ returnType: string;
11
+ }
12
+ export interface Contract {
13
+ services: Record<string, MethodMetadata[]>;
14
+ schemas: string[];
15
+ models: string[];
16
+ sourceFile: SourceFile;
17
+ }
18
+ export interface Finding {
19
+ id: string;
20
+ severity: "ERROR" | "WARNING";
21
+ category: "INVALID_IMPORT" | "MISSING_EXPORT" | "RENAMED_SYMBOL" | "TYPE_MISMATCH" | "UNUSED" | "CONFIG" | "USAGE";
22
+ message: string;
23
+ file: string;
24
+ line: number;
25
+ context?: any;
26
+ }
27
+ export interface Stats {
28
+ usedMethods: Set<string>;
29
+ usedSchemas: Set<string>;
30
+ usedModels: Set<string>;
31
+ }
32
+ export interface CoverageReport {
33
+ methods: {
34
+ total: number;
35
+ used: number;
36
+ percent: string;
37
+ };
38
+ schemas: {
39
+ total: number;
40
+ used: number;
41
+ percent: string;
42
+ };
43
+ models: {
44
+ total: number;
45
+ used: number;
46
+ percent: string;
47
+ };
48
+ }
49
+ export interface Rule {
50
+ check(context: ProjectContext, contract: Contract, stats: Stats): Promise<Finding[]>;
51
+ }
52
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/cli/analyzeUsage/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAC9D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;IAC3C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,QAAQ,EACJ,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,GAChB,eAAe,GACf,QAAQ,GACR,QAAQ,GACR,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,GAAG,CAAC;CACf;AAED,MAAM,WAAW,KAAK;IACpB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1D;AAED,MAAM,WAAW,IAAI;IACjB,KAAK,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;CACxF"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Ищет наиболее подходящую замену из списка доступных опций.
3
+ */
4
+ export declare function findBestMatch(target: string, options: string[], threshold?: number): string | null;
5
+ //# sourceMappingURL=fuzzy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fuzzy.d.ts","sourceRoot":"","sources":["../../../../src/cli/analyzeUsage/utils/fuzzy.ts"],"names":[],"mappings":"AAwBA;;GAEG;AACH,wBAAgB,aAAa,CACzB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EAAE,EACjB,SAAS,SAAI,GACd,MAAM,GAAG,IAAI,CAaf"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findBestMatch = findBestMatch;
4
+ /**
5
+ * Вычисляет расстояние Левенштейна между двумя строками.
6
+ */
7
+ function getLevenshteinDistance(a, b) {
8
+ const matrix = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
9
+ for (let i = 0; i <= a.length; i++)
10
+ matrix[i][0] = i;
11
+ for (let j = 0; j <= b.length; j++)
12
+ matrix[0][j] = j;
13
+ for (let i = 1; i <= a.length; i++) {
14
+ for (let j = 1; j <= b.length; j++) {
15
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
16
+ matrix[i][j] = Math.min(matrix[i - 1][j] + 1, // удаление
17
+ matrix[i][j - 1] + 1, // вставка
18
+ matrix[i - 1][j - 1] + cost // замена
19
+ );
20
+ }
21
+ }
22
+ return matrix[a.length][b.length];
23
+ }
24
+ /**
25
+ * Ищет наиболее подходящую замену из списка доступных опций.
26
+ */
27
+ function findBestMatch(target, options, threshold = 3) {
28
+ let bestMatch = null;
29
+ let minDistance = Infinity;
30
+ for (const option of options) {
31
+ const distance = getLevenshteinDistance(target, option);
32
+ if (distance < minDistance && distance <= threshold) {
33
+ minDistance = distance;
34
+ bestMatch = option;
35
+ }
36
+ }
37
+ return bestMatch;
38
+ }
@@ -0,0 +1,16 @@
1
+ import type { CoverageReport, Finding, Stats } from "../types";
2
+ export declare class Reporter {
3
+ /**
4
+ * Выводит сводную таблицу и результаты в консоль
5
+ */
6
+ static renderConsole(findings: Finding[], coverage: CoverageReport): void;
7
+ /**
8
+ * Сохраняет отчет в формате JSON для CI/CD инструментов
9
+ */
10
+ static saveJsonReport(filePath: string, findings: Finding[], coverage: CoverageReport): void;
11
+ /**
12
+ * Вычисляет процент покрытия на основе статистики
13
+ */
14
+ static calculateCoverage(stats: Stats, contract: any): CoverageReport;
15
+ }
16
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../../src/cli/analyzeUsage/utils/report.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAC,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAE9D,qBAAa,QAAQ;IACnB;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI;IA8BzE;;OAEG;IACH,MAAM,CAAC,cAAc,CACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,EAAE,EACnB,QAAQ,EAAE,cAAc,GACvB,IAAI;IAwBP;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,GAAG,cAAc;CA0BtE"}
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Reporter = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ class Reporter {
10
+ /**
11
+ * Выводит сводную таблицу и результаты в консоль
12
+ */
13
+ static renderConsole(findings, coverage) {
14
+ console.log("\n📊 API Coverage:");
15
+ console.log(` Methods: ${coverage.methods.used}/${coverage.methods.total} (${coverage.methods.percent})`);
16
+ console.log(` Schemas: ${coverage.schemas.used}/${coverage.schemas.total} (${coverage.schemas.percent})`);
17
+ console.log(` Models: ${coverage.models.used}/${coverage.models.total} (${coverage.models.percent})`);
18
+ if (findings.length === 0) {
19
+ console.log("\n✅ No mismatches found. Project is in sync.");
20
+ return;
21
+ }
22
+ console.log("\n⚠️ Findings:");
23
+ // Форматируем данные для console.table
24
+ const tableData = findings.map((f) => ({
25
+ Status: f.severity === "ERROR" ? "🔴 ERROR" : "🟡 WARN",
26
+ Category: f.category,
27
+ Location: `${path_1.default.basename(f.file)}:${f.line}`,
28
+ Message: f.message,
29
+ }));
30
+ console.table(tableData);
31
+ }
32
+ /**
33
+ * Сохраняет отчет в формате JSON для CI/CD инструментов
34
+ */
35
+ static saveJsonReport(filePath, findings, coverage) {
36
+ const report = {
37
+ timestamp: new Date().toISOString(),
38
+ summary: {
39
+ totalIssues: findings.length,
40
+ errors: findings.filter((f) => f.severity === "ERROR").length,
41
+ warnings: findings.filter((f) => f.severity === "WARNING").length,
42
+ },
43
+ categories: findings.reduce((acc, finding) => {
44
+ acc[finding.category] = (acc[finding.category] || 0) + 1;
45
+ return acc;
46
+ }, {}),
47
+ coverage,
48
+ findings,
49
+ };
50
+ try {
51
+ fs_1.default.writeFileSync(filePath, JSON.stringify(report, null, 2));
52
+ console.log(`\n💾 Report saved: ${path_1.default.resolve(filePath)}`);
53
+ }
54
+ catch (error) {
55
+ console.error(`\n❌ Failed to save JSON report: ${error.message}`);
56
+ }
57
+ }
58
+ /**
59
+ * Вычисляет процент покрытия на основе статистики
60
+ */
61
+ static calculateCoverage(stats, contract) {
62
+ const totalMethods = Object.values(contract.services).flat().length;
63
+ const totalSchemas = contract.schemas.length;
64
+ const totalModels = contract.models.length;
65
+ const getPercent = (used, total) => total > 0 ? ((used / total) * 100).toFixed(2) + "%" : "0%";
66
+ return {
67
+ methods: {
68
+ total: totalMethods,
69
+ used: stats.usedMethods.size,
70
+ percent: getPercent(stats.usedMethods.size, totalMethods),
71
+ },
72
+ schemas: {
73
+ total: totalSchemas,
74
+ used: stats.usedSchemas.size,
75
+ percent: getPercent(stats.usedSchemas.size, totalSchemas),
76
+ },
77
+ models: {
78
+ total: totalModels,
79
+ used: stats.usedModels.size,
80
+ percent: getPercent(stats.usedModels.size, totalModels),
81
+ },
82
+ };
83
+ }
84
+ }
85
+ exports.Reporter = Reporter;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=checkConfig.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkConfig.test.d.ts","sourceRoot":"","sources":["../../../../src/cli/checkAndUpdateConfig/__tests__/checkConfig.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_assert_1 = __importDefault(require("node:assert"));
7
+ const promises_1 = require("node:fs/promises");
8
+ const node_os_1 = require("node:os");
9
+ const node_path_1 = require("node:path");
10
+ const node_test_1 = require("node:test");
11
+ const Consts_1 = require("../../../common/Consts");
12
+ const HttpClient_enum_1 = require("../../../core/types/enums/HttpClient.enum");
13
+ const checkConfig_1 = require("../checkConfig");
14
+ /** Config without schema-default fields so checkConfig does not open enquirer. */
15
+ const flatConfig = {
16
+ input: './test/spec/v3.json',
17
+ output: './test/generated',
18
+ httpClient: HttpClient_enum_1.HttpClient.AXIOS,
19
+ };
20
+ async function writeConfig(dir, content) {
21
+ const configPath = (0, node_path_1.join)(dir, 'openapi.config.json');
22
+ await (0, promises_1.writeFile)(configPath, JSON.stringify(content, null, 2), 'utf8');
23
+ return configPath;
24
+ }
25
+ (0, node_test_1.describe)('@unit: checkConfig', () => {
26
+ (0, node_test_1.test)('returns failure when options fail schema validation', async () => {
27
+ const shutdownMock = node_test_1.mock.method(Consts_1.APP_LOGGER, 'shutdownLoggerAsync', async () => undefined);
28
+ const errorMock = node_test_1.mock.method(Consts_1.APP_LOGGER, 'error', () => undefined);
29
+ const result = await (0, checkConfig_1.checkConfig)({ openapiConfig: 123 });
30
+ node_assert_1.default.strictEqual(result.success, false);
31
+ node_assert_1.default.ok(result.error);
32
+ shutdownMock.mock.restore();
33
+ errorMock.mock.restore();
34
+ });
35
+ (0, node_test_1.test)('returns failure when config file is missing', async () => {
36
+ const dir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'check-config-'));
37
+ const configPath = (0, node_path_1.join)(dir, 'missing.json');
38
+ const shutdownMock = node_test_1.mock.method(Consts_1.APP_LOGGER, 'shutdownLoggerAsync', async () => undefined);
39
+ const result = await (0, checkConfig_1.checkConfig)({ openapiConfig: configPath });
40
+ node_assert_1.default.strictEqual(result.success, false);
41
+ node_assert_1.default.ok(result.error);
42
+ shutdownMock.mock.restore();
43
+ });
44
+ (0, node_test_1.test)('reports valid config without prompting when up to date', async () => {
45
+ const dir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'check-config-'));
46
+ const configPath = await writeConfig(dir, flatConfig);
47
+ const infoMock = node_test_1.mock.method(Consts_1.APP_LOGGER, 'info', () => undefined);
48
+ const result = await (0, checkConfig_1.checkConfig)({ openapiConfig: configPath });
49
+ node_assert_1.default.strictEqual(result.success, true);
50
+ node_assert_1.default.strictEqual(infoMock.mock.callCount(), 1);
51
+ infoMock.mock.restore();
52
+ });
53
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=updateConfig.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updateConfig.test.d.ts","sourceRoot":"","sources":["../../../../src/cli/checkAndUpdateConfig/__tests__/updateConfig.test.ts"],"names":[],"mappings":""}