@silasfmartins/testhub 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/.github/copilot-instructions.md +520 -0
  2. package/biome.json +37 -0
  3. package/dist/index.d.ts +45 -0
  4. package/dist/index.js +169 -0
  5. package/dist/scripts/consumer-postinstall.d.ts +15 -0
  6. package/dist/scripts/consumer-postinstall.js +785 -0
  7. package/dist/scripts/generate-docs.d.ts +16 -0
  8. package/dist/scripts/generate-docs.js +1363 -0
  9. package/dist/scripts/generate-index.d.ts +2 -0
  10. package/dist/scripts/generate-index.js +314 -0
  11. package/dist/scripts/init-api.d.ts +2 -0
  12. package/dist/scripts/init-api.js +525 -0
  13. package/dist/scripts/init-banco.d.ts +2 -0
  14. package/dist/scripts/init-banco.js +347 -0
  15. package/dist/scripts/init-frontend.d.ts +2 -0
  16. package/dist/scripts/init-frontend.js +627 -0
  17. package/dist/scripts/init-mobile.d.ts +2 -0
  18. package/dist/scripts/init-mobile.js +481 -0
  19. package/dist/scripts/init-scenarios.d.ts +2 -0
  20. package/dist/scripts/init-scenarios.js +846 -0
  21. package/dist/scripts/init-ssh.d.ts +2 -0
  22. package/dist/scripts/init-ssh.js +639 -0
  23. package/dist/scripts/package-versions.d.ts +57 -0
  24. package/dist/scripts/package-versions.js +768 -0
  25. package/dist/scripts/postinstall.d.ts +1 -0
  26. package/dist/scripts/postinstall.js +527 -0
  27. package/dist/scripts/robust-build.d.ts +7 -0
  28. package/dist/scripts/robust-build.js +88 -0
  29. package/dist/scripts/setup-local-packages.d.ts +31 -0
  30. package/dist/scripts/setup-local-packages.js +237 -0
  31. package/dist/scripts/smart-override.d.ts +2 -0
  32. package/dist/scripts/smart-override.js +1360 -0
  33. package/dist/scripts/sync-configs.d.ts +27 -0
  34. package/dist/scripts/sync-configs.js +248 -0
  35. package/dist/scripts/test-biome-parse.d.ts +5 -0
  36. package/dist/scripts/test-biome-parse.js +84 -0
  37. package/dist/scripts/ultracite-setup.d.ts +4 -0
  38. package/dist/scripts/ultracite-setup.js +310 -0
  39. package/dist/scripts/update-all-init-scripts.d.ts +2 -0
  40. package/dist/scripts/update-all-init-scripts.js +52 -0
  41. package/dist/scripts/update-biome-schema.d.ts +15 -0
  42. package/dist/scripts/update-biome-schema.js +124 -0
  43. package/dist/src/AutoCoreFacade.d.ts +145 -0
  44. package/dist/src/AutoCoreFacade.js +217 -0
  45. package/dist/src/api/ApiActions.d.ts +297 -0
  46. package/dist/src/api/ApiActions.js +1905 -0
  47. package/dist/src/api/Certificate.d.ts +60 -0
  48. package/dist/src/api/Certificate.js +79 -0
  49. package/dist/src/api/JsonResponse.d.ts +116 -0
  50. package/dist/src/api/JsonResponse.js +206 -0
  51. package/dist/src/appium/DeviceFarmViewer.d.ts +79 -0
  52. package/dist/src/appium/DeviceFarmViewer.js +1083 -0
  53. package/dist/src/appium/MobileActions.d.ts +347 -0
  54. package/dist/src/appium/MobileActions.js +1632 -0
  55. package/dist/src/appium/MobileConnection.d.ts +160 -0
  56. package/dist/src/appium/MobileConnection.js +772 -0
  57. package/dist/src/config/envLoader.d.ts +123 -0
  58. package/dist/src/config/envLoader.js +361 -0
  59. package/dist/src/config/jest-safe-setup.d.ts +19 -0
  60. package/dist/src/config/jest-safe-setup.js +369 -0
  61. package/dist/src/config/timeouts.d.ts +32 -0
  62. package/dist/src/config/timeouts.js +38 -0
  63. package/dist/src/desktop/DesktopActions.d.ts +46 -0
  64. package/dist/src/desktop/DesktopActions.js +398 -0
  65. package/dist/src/desktop/DesktopConnection.d.ts +32 -0
  66. package/dist/src/desktop/DesktopConnection.js +84 -0
  67. package/dist/src/domain/entities/TestExecution.d.ts +117 -0
  68. package/dist/src/domain/entities/TestExecution.js +150 -0
  69. package/dist/src/domain/entities/TestReport.d.ts +114 -0
  70. package/dist/src/domain/entities/TestReport.js +179 -0
  71. package/dist/src/domain/repositories/ITestRepository.d.ts +196 -0
  72. package/dist/src/domain/repositories/ITestRepository.js +14 -0
  73. package/dist/src/domain/schemas/ValidationSchemas.d.ts +159 -0
  74. package/dist/src/domain/schemas/ValidationSchemas.js +181 -0
  75. package/dist/src/functions/errors/BaseError.d.ts +78 -0
  76. package/dist/src/functions/errors/BaseError.js +245 -0
  77. package/dist/src/functions/errors/ConfigurationError.d.ts +16 -0
  78. package/dist/src/functions/errors/ConfigurationError.js +48 -0
  79. package/dist/src/functions/errors/ErrorCatalog.d.ts +148 -0
  80. package/dist/src/functions/errors/ErrorCatalog.js +157 -0
  81. package/dist/src/functions/errors/GlobalErrorHandler.d.ts +101 -0
  82. package/dist/src/functions/errors/GlobalErrorHandler.js +281 -0
  83. package/dist/src/functions/errors/IntegrationError.d.ts +17 -0
  84. package/dist/src/functions/errors/IntegrationError.js +51 -0
  85. package/dist/src/functions/errors/SecurityError.d.ts +14 -0
  86. package/dist/src/functions/errors/SecurityError.js +42 -0
  87. package/dist/src/functions/errors/SystemError.d.ts +12 -0
  88. package/dist/src/functions/errors/SystemError.js +36 -0
  89. package/dist/src/functions/errors/ValidationError.d.ts +14 -0
  90. package/dist/src/functions/errors/ValidationError.js +61 -0
  91. package/dist/src/functions/errors/index.d.ts +12 -0
  92. package/dist/src/functions/errors/index.js +13 -0
  93. package/dist/src/global-setup.d.ts +1 -0
  94. package/dist/src/global-setup.js +1037 -0
  95. package/dist/src/helpers/BancoActions.d.ts +188 -0
  96. package/dist/src/helpers/BancoActions.js +581 -0
  97. package/dist/src/helpers/EnviromentHelper.d.ts +17 -0
  98. package/dist/src/helpers/EnviromentHelper.js +66 -0
  99. package/dist/src/helpers/ParallelExecutionHelper.d.ts +183 -0
  100. package/dist/src/helpers/ParallelExecutionHelper.js +375 -0
  101. package/dist/src/helpers/SyncSignal.d.ts +15 -0
  102. package/dist/src/helpers/SyncSignal.js +44 -0
  103. package/dist/src/hubdocs/CategoryDetector.d.ts +83 -0
  104. package/dist/src/hubdocs/CategoryDetector.js +401 -0
  105. package/dist/src/hubdocs/DirectStatementInterceptor.d.ts +54 -0
  106. package/dist/src/hubdocs/DirectStatementInterceptor.js +243 -0
  107. package/dist/src/hubdocs/ExecutionTracker.d.ts +107 -0
  108. package/dist/src/hubdocs/ExecutionTracker.js +702 -0
  109. package/dist/src/hubdocs/HubDocs.d.ts +395 -0
  110. package/dist/src/hubdocs/HubDocs.js +3586 -0
  111. package/dist/src/hubdocs/StatementMethodFilter.d.ts +71 -0
  112. package/dist/src/hubdocs/StatementMethodFilter.js +618 -0
  113. package/dist/src/hubdocs/StatementTracker.d.ts +417 -0
  114. package/dist/src/hubdocs/StatementTracker.js +2419 -0
  115. package/dist/src/hubdocs/SwaggerGenerator.d.ts +59 -0
  116. package/dist/src/hubdocs/SwaggerGenerator.js +405 -0
  117. package/dist/src/hubdocs/index.d.ts +9 -0
  118. package/dist/src/hubdocs/index.js +9 -0
  119. package/dist/src/hubdocs/types.d.ts +114 -0
  120. package/dist/src/hubdocs/types.js +5 -0
  121. package/dist/src/infrastructure/DependencyContainer.d.ts +142 -0
  122. package/dist/src/infrastructure/DependencyContainer.js +250 -0
  123. package/dist/src/infrastructure/adapters/AppiumAdapter.d.ts +168 -0
  124. package/dist/src/infrastructure/adapters/AppiumAdapter.js +468 -0
  125. package/dist/src/infrastructure/adapters/OracleAdapter.d.ts +150 -0
  126. package/dist/src/infrastructure/adapters/OracleAdapter.js +388 -0
  127. package/dist/src/infrastructure/adapters/PlaywrightAdapter.d.ts +192 -0
  128. package/dist/src/infrastructure/adapters/PlaywrightAdapter.js +382 -0
  129. package/dist/src/infrastructure/adapters/SSHAdapter.d.ts +141 -0
  130. package/dist/src/infrastructure/adapters/SSHAdapter.js +428 -0
  131. package/dist/src/interfaces.d.ts +501 -0
  132. package/dist/src/interfaces.js +25 -0
  133. package/dist/src/internal/fakes/__fake-actions__.d.ts +17 -0
  134. package/dist/src/internal/fakes/__fake-actions__.js +21 -0
  135. package/dist/src/internal/fakes/__forbidden__.d.ts +10 -0
  136. package/dist/src/internal/fakes/__forbidden__.js +18 -0
  137. package/dist/src/internal/fakes/__honeypot__.d.ts +15 -0
  138. package/dist/src/internal/fakes/__honeypot__.js +24 -0
  139. package/dist/src/octane/OctaneReporter.d.ts +13 -0
  140. package/dist/src/octane/OctaneReporter.js +61 -0
  141. package/dist/src/playwright/CryptoActions.d.ts +20 -0
  142. package/dist/src/playwright/CryptoActions.js +75 -0
  143. package/dist/src/playwright/EnhancedWebActions.d.ts +7 -0
  144. package/dist/src/playwright/EnhancedWebActions.js +65 -0
  145. package/dist/src/playwright/WebActions.d.ts +1599 -0
  146. package/dist/src/playwright/WebActions.js +11788 -0
  147. package/dist/src/playwright/actions/ActionTimeline.d.ts +36 -0
  148. package/dist/src/playwright/actions/ActionTimeline.js +101 -0
  149. package/dist/src/playwright/actions/RecoveryQueue.d.ts +82 -0
  150. package/dist/src/playwright/actions/RecoveryQueue.js +130 -0
  151. package/dist/src/playwright/actions/SelectorCache.d.ts +53 -0
  152. package/dist/src/playwright/actions/SelectorCache.js +96 -0
  153. package/dist/src/playwright/actions/index.d.ts +13 -0
  154. package/dist/src/playwright/actions/index.js +14 -0
  155. package/dist/src/playwright/actions/types.d.ts +147 -0
  156. package/dist/src/playwright/actions/types.js +5 -0
  157. package/dist/src/playwright/fixtures.d.ts +112 -0
  158. package/dist/src/playwright/fixtures.js +718 -0
  159. package/dist/src/playwright/network-logs-reporter.d.ts +7 -0
  160. package/dist/src/playwright/network-logs-reporter.js +66 -0
  161. package/dist/src/playwright/registerRecoveryWrappers.d.ts +1 -0
  162. package/dist/src/playwright/registerRecoveryWrappers.js +54 -0
  163. package/dist/src/security/BuildSecurity.d.ts +12 -0
  164. package/dist/src/security/BuildSecurity.js +138 -0
  165. package/dist/src/security/EulaProtection.d.ts +70 -0
  166. package/dist/src/security/EulaProtection.js +155 -0
  167. package/dist/src/security/HoneypotManager.d.ts +46 -0
  168. package/dist/src/security/HoneypotManager.js +234 -0
  169. package/dist/src/security/KeysManager.d.ts +36 -0
  170. package/dist/src/security/KeysManager.js +158 -0
  171. package/dist/src/security/ProofOfWorkIntegration.d.ts +64 -0
  172. package/dist/src/security/ProofOfWorkIntegration.js +206 -0
  173. package/dist/src/security/SecurityValidation.d.ts +21 -0
  174. package/dist/src/security/SecurityValidation.js +163 -0
  175. package/dist/src/security/SourceMapProtection.d.ts +55 -0
  176. package/dist/src/security/SourceMapProtection.js +220 -0
  177. package/dist/src/security/protector.d.ts +1 -0
  178. package/dist/src/security/protector.js +97 -0
  179. package/dist/src/ssh/SSHActions.d.ts +262 -0
  180. package/dist/src/ssh/SSHActions.js +790 -0
  181. package/dist/src/ssh/SSHClient.d.ts +99 -0
  182. package/dist/src/ssh/SSHClient.js +409 -0
  183. package/dist/src/statements/BaseStatement.d.ts +38 -0
  184. package/dist/src/statements/BaseStatement.js +78 -0
  185. package/dist/src/testContext/AuthStateManager.d.ts +93 -0
  186. package/dist/src/testContext/AuthStateManager.js +256 -0
  187. package/dist/src/testContext/CoverageManager.d.ts +198 -0
  188. package/dist/src/testContext/CoverageManager.js +917 -0
  189. package/dist/src/testContext/TestAnnotations.d.ts +476 -0
  190. package/dist/src/testContext/TestAnnotations.js +2647 -0
  191. package/dist/src/testContext/TestContext.d.ts +138 -0
  192. package/dist/src/testContext/TestContext.js +369 -0
  193. package/dist/src/testContext/UnifiedHtmlGenerator.d.ts +7 -0
  194. package/dist/src/testContext/UnifiedHtmlGenerator.js +264 -0
  195. package/dist/src/testContext/UnifiedReportManager.d.ts +211 -0
  196. package/dist/src/testContext/UnifiedReportManager.js +1206 -0
  197. package/dist/src/testhub/DynamicConfigManager.d.ts +121 -0
  198. package/dist/src/testhub/DynamicConfigManager.js +320 -0
  199. package/dist/src/testhub/SystemsManager.d.ts +119 -0
  200. package/dist/src/testhub/SystemsManager.js +365 -0
  201. package/dist/src/testhub/TestHubClient.d.ts +335 -0
  202. package/dist/src/testhub/TestHubClient.js +1215 -0
  203. package/dist/src/testhub/TestHubReporter.d.ts +62 -0
  204. package/dist/src/testhub/TestHubReporter.js +576 -0
  205. package/dist/src/testhub/TestHubVars.d.ts +116 -0
  206. package/dist/src/testhub/TestHubVars.js +273 -0
  207. package/dist/src/utils/ActionInterceptor.d.ts +59 -0
  208. package/dist/src/utils/ActionInterceptor.js +741 -0
  209. package/dist/src/utils/ArtifactsCompressor.d.ts +43 -0
  210. package/dist/src/utils/ArtifactsCompressor.js +181 -0
  211. package/dist/src/utils/AutoLogsFinal.d.ts +47 -0
  212. package/dist/src/utils/AutoLogsFinal.js +148 -0
  213. package/dist/src/utils/CodeGenSession.d.ts +114 -0
  214. package/dist/src/utils/CodeGenSession.js +264 -0
  215. package/dist/src/utils/ConfigLogger.d.ts +133 -0
  216. package/dist/src/utils/ConfigLogger.js +611 -0
  217. package/dist/src/utils/CustomReporter.d.ts +22 -0
  218. package/dist/src/utils/CustomReporter.js +352 -0
  219. package/dist/src/utils/DataStore.d.ts +171 -0
  220. package/dist/src/utils/DataStore.js +484 -0
  221. package/dist/src/utils/DatabaseInterceptor.d.ts +19 -0
  222. package/dist/src/utils/DatabaseInterceptor.js +295 -0
  223. package/dist/src/utils/DateHelper.d.ts +16 -0
  224. package/dist/src/utils/DateHelper.js +120 -0
  225. package/dist/src/utils/DateValidator.d.ts +4 -0
  226. package/dist/src/utils/DateValidator.js +51 -0
  227. package/dist/src/utils/DocumentGenerator.d.ts +35 -0
  228. package/dist/src/utils/DocumentGenerator.js +129 -0
  229. package/dist/src/utils/EvidenceCapture.d.ts +90 -0
  230. package/dist/src/utils/EvidenceCapture.js +600 -0
  231. package/dist/src/utils/EvidenceReportGenerator.d.ts +70 -0
  232. package/dist/src/utils/EvidenceReportGenerator.js +799 -0
  233. package/dist/src/utils/FrameManagementUtil.d.ts +42 -0
  234. package/dist/src/utils/FrameManagementUtil.js +75 -0
  235. package/dist/src/utils/GlobalStatementsInterceptor.d.ts +1 -0
  236. package/dist/src/utils/GlobalStatementsInterceptor.js +1 -0
  237. package/dist/src/utils/HTMLTemplate.d.ts +1 -0
  238. package/dist/src/utils/HTMLTemplate.js +1034 -0
  239. package/dist/src/utils/InterceptacaoMagica.d.ts +23 -0
  240. package/dist/src/utils/InterceptacaoMagica.js +365 -0
  241. package/dist/src/utils/LogSanitizer.d.ts +35 -0
  242. package/dist/src/utils/LogSanitizer.js +110 -0
  243. package/dist/src/utils/Logger.d.ts +65 -0
  244. package/dist/src/utils/Logger.js +284 -0
  245. package/dist/src/utils/McpLocalClient.d.ts +141 -0
  246. package/dist/src/utils/McpLocalClient.js +871 -0
  247. package/dist/src/utils/PDFEvidenceGenerator.d.ts +20 -0
  248. package/dist/src/utils/PDFEvidenceGenerator.js +156 -0
  249. package/dist/src/utils/SpecFileAnalyzer.d.ts +35 -0
  250. package/dist/src/utils/SpecFileAnalyzer.js +209 -0
  251. package/dist/src/utils/StatementInterceptor.d.ts +18 -0
  252. package/dist/src/utils/StatementInterceptor.js +87 -0
  253. package/dist/src/utils/StatementLogger.d.ts +33 -0
  254. package/dist/src/utils/StatementLogger.js +113 -0
  255. package/dist/src/utils/StatementsInterceptor.d.ts +1 -0
  256. package/dist/src/utils/StatementsInterceptor.js +1 -0
  257. package/dist/src/utils/TeamsFlushHook.d.ts +17 -0
  258. package/dist/src/utils/TeamsFlushHook.js +168 -0
  259. package/dist/src/utils/TerminalLogCapture.d.ts +158 -0
  260. package/dist/src/utils/TerminalLogCapture.js +531 -0
  261. package/dist/src/utils/TestMethodLogger.d.ts +70 -0
  262. package/dist/src/utils/TestMethodLogger.js +95 -0
  263. package/dist/src/utils/UnifiedTeardown.d.ts +4 -0
  264. package/dist/src/utils/UnifiedTeardown.js +400 -0
  265. package/dist/src/utils/XPathCatalog.d.ts +152 -0
  266. package/dist/src/utils/XPathCatalog.js +350 -0
  267. package/dist/src/utils/generators.d.ts +90 -0
  268. package/dist/src/utils/generators.js +167 -0
  269. package/dist/src/utils/testRecovery/ResilientPlaywright.d.ts +152 -0
  270. package/dist/src/utils/testRecovery/ResilientPlaywright.js +715 -0
  271. package/dist/src/utils/testRecovery/TestRecoveryClient.d.ts +801 -0
  272. package/dist/src/utils/testRecovery/TestRecoveryClient.js +1415 -0
  273. package/dist/src/utils/testRecovery/autoFixCode.d.ts +65 -0
  274. package/dist/src/utils/testRecovery/autoFixCode.js +32 -0
  275. package/dist/vitest.config.d.ts +2 -0
  276. package/dist/vitest.config.js +59 -0
  277. package/dist/wdio.conf.d.ts +1 -0
  278. package/dist/wdio.conf.js +420 -0
  279. package/package.json +137 -0
  280. package/protect-loader.mjs +643 -0
  281. package/scripts/consumer-postinstall.ts +975 -0
  282. package/scripts/generate-index.ts +343 -0
  283. package/scripts/init-api.ts +613 -0
  284. package/scripts/init-banco.ts +437 -0
  285. package/scripts/init-frontend.ts +727 -0
  286. package/scripts/init-mobile.ts +558 -0
  287. package/scripts/init-scenarios.ts +925 -0
  288. package/scripts/init-ssh.ts +734 -0
  289. package/scripts/package-versions.ts +978 -0
  290. package/scripts/postinstall.ts +605 -0
  291. package/scripts/smart-override.ts +1675 -0
  292. package/scripts/sync-configs.ts +302 -0
  293. package/scripts/ultracite-setup.ts +370 -0
  294. package/src/types/globals.d.ts +48 -0
  295. package/tsconfig.json +29 -0
  296. package/types/autocore-sync-signal.d.ts +10 -0
@@ -0,0 +1,1675 @@
1
+ #!/usr/bin/env node
2
+ // @ts-nocheck
3
+
4
+ /**
5
+ * 🔧 Smart Override para TypeScript puro
6
+ * Versão otimizada para desenvolvimento TypeScript sem dependências externas
7
+ */
8
+
9
+ import { execSync } from 'node:child_process'
10
+ import * as fs from 'node:fs'
11
+ import * as os from 'node:os'
12
+ import * as path from 'node:path'
13
+
14
+ const LOCAL_REGISTRY = path.join(os.homedir(), '.rbqa-local-packages.json')
15
+ const WATCH_REGISTRY = path.join(os.homedir(), '.rbqa-watch-registry.json')
16
+
17
+ // 🔒 Pacotes que PREFERENCIALMENTE usam Azure (quando disponível)
18
+ // Se o pacote não existir no Azure ainda, usará versão local automaticamente
19
+ const PREFER_AZURE_PACKAGES = new Set([
20
+ '@silasfmartins/testhub', // AutoCore prefere Azure, mas usa local se não existir
21
+ ])
22
+
23
+ // 🎯 Função para verificar se é um pacote scoped (começa com @)
24
+ // Reconhece: @rbqa/*, @sfa/*, @org/*, etc.
25
+ function isScopedPackage(name: string): boolean {
26
+ return name.startsWith('@') && name.includes('/')
27
+ }
28
+
29
+ interface LocalPackage {
30
+ name: string
31
+ version: string
32
+ path: string
33
+ lastBuild: number
34
+ buildDuration?: number
35
+ buildDetails?: {
36
+ compiledFiles?: number
37
+ buildSize?: string
38
+ nodeVersion?: string
39
+ timestamp?: string
40
+ }
41
+ }
42
+
43
+ interface WatchedProject {
44
+ name: string
45
+ path: string
46
+ lastChecked: number
47
+ dependencies: { [pkg: string]: string }
48
+ }
49
+
50
+ interface BuildMetrics {
51
+ startTime: number
52
+ endTime?: number
53
+ duration?: number
54
+ compiledFiles: number
55
+ buildSize: string
56
+ success: boolean
57
+ errors: string[]
58
+ warnings: string[]
59
+ }
60
+
61
+ class SmartOverride {
62
+ private buildMetrics: BuildMetrics = {
63
+ startTime: Date.now(),
64
+ compiledFiles: 0,
65
+ buildSize: '0 KB',
66
+ success: false,
67
+ errors: [],
68
+ warnings: [],
69
+ }
70
+
71
+ private getLocalRegistry(): Record<string, LocalPackage> {
72
+ try {
73
+ if (fs.existsSync(LOCAL_REGISTRY)) {
74
+ return JSON.parse(fs.readFileSync(LOCAL_REGISTRY, 'utf8'))
75
+ }
76
+ } catch {}
77
+ return {}
78
+ }
79
+
80
+ private saveLocalRegistry(registry: Record<string, LocalPackage>): void {
81
+ fs.writeFileSync(LOCAL_REGISTRY, JSON.stringify(registry, null, 2))
82
+ }
83
+
84
+ private getWatchRegistry(): Record<string, WatchedProject> {
85
+ try {
86
+ if (fs.existsSync(WATCH_REGISTRY)) {
87
+ return JSON.parse(fs.readFileSync(WATCH_REGISTRY, 'utf8'))
88
+ }
89
+ } catch {}
90
+ return {}
91
+ }
92
+
93
+ private saveWatchRegistry(registry: Record<string, WatchedProject>): void {
94
+ fs.writeFileSync(WATCH_REGISTRY, JSON.stringify(registry, null, 2))
95
+ }
96
+
97
+ /**
98
+ * Calcula o tamanho de um diretório recursivamente
99
+ */
100
+ private calculateDirectorySize(dirPath: string): number {
101
+ let totalSize = 0
102
+
103
+ try {
104
+ const items = fs.readdirSync(dirPath, { withFileTypes: true })
105
+
106
+ for (const item of items) {
107
+ const itemPath = path.join(dirPath, item.name)
108
+
109
+ if (item.isDirectory()) {
110
+ totalSize += this.calculateDirectorySize(itemPath)
111
+ } else if (item.isFile()) {
112
+ totalSize += fs.statSync(itemPath).size
113
+ }
114
+ }
115
+ } catch (error) {
116
+ // Ignora erros de acesso a arquivos
117
+ }
118
+
119
+ return totalSize
120
+ }
121
+
122
+ /**
123
+ * Formata tamanho em bytes para uma string legível
124
+ */
125
+ private formatSize(bytes: number): string {
126
+ const units = ['B', 'KB', 'MB', 'GB']
127
+ let size = bytes
128
+ let unitIndex = 0
129
+
130
+ while (size >= 1024 && unitIndex < units.length - 1) {
131
+ size /= 1024
132
+ unitIndex++
133
+ }
134
+
135
+ return `${size.toFixed(size < 10 ? 1 : 0)} ${units[unitIndex]}`
136
+ }
137
+
138
+ /**
139
+ * Conta arquivos compilados no diretório dist
140
+ */
141
+ private countCompiledFiles(distPath: string): number {
142
+ let count = 0
143
+
144
+ try {
145
+ const items = fs.readdirSync(distPath, { withFileTypes: true })
146
+
147
+ for (const item of items) {
148
+ const itemPath = path.join(distPath, item.name)
149
+
150
+ if (item.isDirectory()) {
151
+ count += this.countCompiledFiles(itemPath)
152
+ } else if (
153
+ item.isFile() &&
154
+ (item.name.endsWith('.js') || item.name.endsWith('.d.ts'))
155
+ ) {
156
+ count++
157
+ }
158
+ }
159
+ } catch (error) {
160
+ // Ignora erros
161
+ }
162
+
163
+ return count
164
+ }
165
+
166
+ /**
167
+ * Formata duração em milissegundos para string legível
168
+ */
169
+ private formatDuration(ms: number): string {
170
+ if (ms < 1000) {
171
+ return `${ms}ms`
172
+ }
173
+ if (ms < 60_000) {
174
+ return `${(ms / 1000).toFixed(1)}s`
175
+ }
176
+ const minutes = Math.floor(ms / 60_000)
177
+ const seconds = ((ms % 60_000) / 1000).toFixed(1)
178
+ return `${minutes}m ${seconds}s`
179
+ }
180
+
181
+ /**
182
+ * Exibe banner de início do build
183
+ */
184
+ private showBuildStartBanner(packageName: string, version: string): void {
185
+ const line = '═'.repeat(80)
186
+ console.log(`\n${line}`)
187
+ console.log(`🏗️ INICIANDO BUILD - ${packageName}@${version}`)
188
+ console.log(`${line}`)
189
+ console.log(`📅 Timestamp: ${new Date().toLocaleString('pt-BR')}`)
190
+ console.log(`🖥️ Sistema: ${os.platform()} ${os.arch()}`)
191
+ console.log(`⚡ Node.js: ${process.version}`)
192
+ console.log(`📂 Diretório: ${process.cwd()}`)
193
+ console.log(`${line}\n`)
194
+ }
195
+
196
+ /**
197
+ * Exibe resumo detalhado do build
198
+ */
199
+ private showBuildSummary(packageName: string, version: string): void {
200
+ this.buildMetrics.endTime = Date.now()
201
+ this.buildMetrics.duration =
202
+ this.buildMetrics.endTime - this.buildMetrics.startTime
203
+
204
+ const line = '═'.repeat(80)
205
+ const statusIcon = this.buildMetrics.success ? '✅' : '❌'
206
+ const statusText = this.buildMetrics.success ? 'SUCESSO' : 'FALHOU'
207
+
208
+ console.log(`\n${line}`)
209
+ console.log(`${statusIcon} BUILD ${statusText} - ${packageName}@${version}`)
210
+ console.log(`${line}`)
211
+
212
+ // Métricas principais
213
+ console.log(
214
+ `⏱️ Duração total: ${this.formatDuration(this.buildMetrics.duration!)}`,
215
+ )
216
+ console.log(`📁 Arquivos compilados: ${this.buildMetrics.compiledFiles}`)
217
+ console.log(`📦 Tamanho do build: ${this.buildMetrics.buildSize}`)
218
+ console.log(`🕐 Concluído em: ${new Date().toLocaleString('pt-BR')}`)
219
+
220
+ // Performance
221
+ const filesPerSecond =
222
+ this.buildMetrics.compiledFiles / (this.buildMetrics.duration! / 1000)
223
+ console.log(`⚡ Performance: ${filesPerSecond.toFixed(1)} arquivos/segundo`)
224
+
225
+ // Status de erros e warnings
226
+ if (this.buildMetrics.errors.length > 0) {
227
+ console.log(`❌ Erros: ${this.buildMetrics.errors.length}`)
228
+ this.buildMetrics.errors.forEach((error) => {
229
+ console.log(` • ${error}`)
230
+ })
231
+ }
232
+
233
+ if (this.buildMetrics.warnings.length > 0) {
234
+ console.log(`⚠️ Warnings: ${this.buildMetrics.warnings.length}`)
235
+ this.buildMetrics.warnings.forEach((warning) => {
236
+ console.log(` • ${warning}`)
237
+ })
238
+ }
239
+
240
+ if (
241
+ this.buildMetrics.errors.length === 0 &&
242
+ this.buildMetrics.warnings.length === 0
243
+ ) {
244
+ console.log('✨ Build limpo: sem erros ou warnings')
245
+ }
246
+
247
+ console.log(`${line}\n`)
248
+
249
+ // Mostrar próximos passos se sucesso
250
+ if (this.buildMetrics.success) {
251
+ console.log('📋 PRÓXIMOS PASSOS:')
252
+ console.log(' • Pacote registrado no registro local')
253
+ console.log(' • Outros projetos detectarão automaticamente as mudanças')
254
+ console.log(
255
+ ` • Use 'smart-override status' para verificar dependências`,
256
+ )
257
+ console.log(
258
+ ` • Use 'smart-override remove' para voltar às versões do Azure\n`,
259
+ )
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Registra um pacote após build e atualiza projetos dependentes
265
+ */
266
+ register(): void {
267
+ const packageJsonPath = path.join(process.cwd(), 'package.json')
268
+
269
+ if (!fs.existsSync(packageJsonPath)) {
270
+ console.log('❌ package.json não encontrado no diretório atual')
271
+ return
272
+ }
273
+
274
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
275
+ const distPath = path.join(process.cwd(), 'dist')
276
+
277
+ // Exibir banner de início
278
+ this.showBuildStartBanner(packageJson.name, packageJson.version)
279
+
280
+ console.log('🔍 Verificando estrutura do projeto...')
281
+
282
+ if (!fs.existsSync(distPath)) {
283
+ console.log('❌ Pasta dist não encontrada')
284
+ console.log(
285
+ '💡 Execute o build do TypeScript primeiro: npm run build ou tsc',
286
+ )
287
+ this.buildMetrics.errors.push('Pasta dist não encontrada')
288
+ this.showBuildSummary(packageJson.name, packageJson.version)
289
+ return
290
+ }
291
+
292
+ // Coletar métricas do build
293
+ console.log('📊 Coletando métricas do build...')
294
+
295
+ this.buildMetrics.compiledFiles = this.countCompiledFiles(distPath)
296
+ const distSize = this.calculateDirectorySize(distPath)
297
+ this.buildMetrics.buildSize = this.formatSize(distSize)
298
+
299
+ console.log(` • Arquivos compilados: ${this.buildMetrics.compiledFiles}`)
300
+ console.log(` • Tamanho do build: ${this.buildMetrics.buildSize}`)
301
+
302
+ // Verificar arquivos essenciais
303
+ console.log('🔎 Verificando arquivos essenciais...')
304
+
305
+ const essentialFiles = ['dist/index.js', 'dist/index.d.ts']
306
+ const missingFiles = essentialFiles.filter(
307
+ (file) => !fs.existsSync(path.join(process.cwd(), file)),
308
+ )
309
+
310
+ if (missingFiles.length > 0) {
311
+ console.log(`⚠️ Arquivos essenciais ausentes: ${missingFiles.join(', ')}`)
312
+ this.buildMetrics.warnings.push(
313
+ `Arquivos ausentes: ${missingFiles.join(', ')}`,
314
+ )
315
+ } else {
316
+ console.log('✅ Todos os arquivos essenciais presentes')
317
+ }
318
+
319
+ // Registrar no registry local
320
+ console.log('📝 Registrando no registry local...')
321
+
322
+ const registry = this.getLocalRegistry()
323
+ const buildDetails = {
324
+ compiledFiles: this.buildMetrics.compiledFiles,
325
+ buildSize: this.buildMetrics.buildSize,
326
+ nodeVersion: process.version,
327
+ timestamp: new Date().toISOString(),
328
+ }
329
+
330
+ registry[packageJson.name] = {
331
+ name: packageJson.name,
332
+ version: packageJson.version,
333
+ path: process.cwd(),
334
+ lastBuild: Date.now(),
335
+ buildDuration: this.buildMetrics.duration,
336
+ buildDetails,
337
+ }
338
+
339
+ this.saveLocalRegistry(registry)
340
+ this.buildMetrics.success = true
341
+
342
+ console.log('✅ Pacote registrado com sucesso')
343
+
344
+ // Registrar este projeto como "watchable" pelos outros
345
+ this.registerAsWatchableProject(packageJson.name)
346
+
347
+ // Verificar e notificar projetos dependentes
348
+ this.notifyDependentProjects(packageJson.name)
349
+
350
+ // Exibir resumo final
351
+ this.showBuildSummary(packageJson.name, packageJson.version)
352
+ }
353
+
354
+ /**
355
+ * Registra o projeto atual como um que pode ser "observado" por outros
356
+ */
357
+ private registerAsWatchableProject(packageName: string): void {
358
+ const watchRegistry = this.getWatchRegistry()
359
+ const packageJsonPath = path.join(process.cwd(), 'package.json')
360
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
361
+ const dependencies = {
362
+ ...packageJson.dependencies,
363
+ ...packageJson.devDependencies,
364
+ }
365
+
366
+ watchRegistry[packageName] = {
367
+ name: packageName,
368
+ path: process.cwd(),
369
+ lastChecked: Date.now(),
370
+ dependencies: Object.keys(dependencies)
371
+ .filter((dep) => isScopedPackage(dep))
372
+ .reduce(
373
+ (acc, dep) => {
374
+ acc[dep] = dependencies[dep]
375
+ return acc
376
+ },
377
+ {} as { [pkg: string]: string },
378
+ ),
379
+ }
380
+
381
+ this.saveWatchRegistry(watchRegistry)
382
+ }
383
+
384
+ /**
385
+ * Notifica e atualiza automaticamente projetos que dependem do pacote atual
386
+ */
387
+ private notifyDependentProjects(packageName: string): void {
388
+ console.log(
389
+ '🔗 Verificando projetos dependentes para atualização automática...',
390
+ )
391
+
392
+ const watchRegistry = this.getWatchRegistry()
393
+ const localRegistry = this.getLocalRegistry()
394
+ const currentPackage = localRegistry[packageName]
395
+
396
+ if (!currentPackage) return
397
+
398
+ let updatedProjects = 0
399
+
400
+ // Percorrer todos os projetos registrados
401
+ Object.values(watchRegistry).forEach((watchedProject) => {
402
+ if (watchedProject.name === packageName) return // Pular o próprio projeto
403
+
404
+ // Verificar se este projeto depende do pacote que acabou de ser buildado
405
+ if (watchedProject.dependencies[packageName]) {
406
+ const requiredVersion = watchedProject.dependencies[
407
+ packageName
408
+ ].replace(/[\^~]/, '')
409
+ const versionComparison = this.compareVersions(
410
+ currentPackage.version,
411
+ requiredVersion,
412
+ )
413
+
414
+ if (versionComparison >= 0) {
415
+ console.log(` 🎯 Atualizando ${watchedProject.name}...`)
416
+
417
+ // Aplicar override no projeto dependente
418
+ if (
419
+ this.applyOverrideToProject(
420
+ watchedProject,
421
+ packageName,
422
+ currentPackage,
423
+ )
424
+ ) {
425
+ updatedProjects++
426
+ console.log(
427
+ ` ✅ ${watchedProject.name} atualizado com ${packageName}@${currentPackage.version}`,
428
+ )
429
+ } else {
430
+ console.log(` ℹ️ ${watchedProject.name} já está atualizado`)
431
+ }
432
+ } else {
433
+ console.log(
434
+ ` ⚠️ ${watchedProject.name} requer v${requiredVersion}, mas local é v${currentPackage.version}`,
435
+ )
436
+ }
437
+ }
438
+ })
439
+
440
+ if (updatedProjects > 0) {
441
+ console.log(
442
+ `\n🎉 ${updatedProjects} projeto(s) atualizado(s) automaticamente!`,
443
+ )
444
+ console.log(
445
+ '💡 Os projetos atualizados agora usam sua versão local mais recente',
446
+ )
447
+ } else {
448
+ console.log(' • Nenhum projeto dependente precisou de atualização')
449
+ }
450
+ }
451
+
452
+ /**
453
+ * Aplica override em um projeto específico
454
+ */
455
+ private applyOverrideToProject(
456
+ watchedProject: WatchedProject,
457
+ packageName: string,
458
+ localPkg: LocalPackage,
459
+ ): boolean {
460
+ const nodeModulesPath = path.join(
461
+ watchedProject.path,
462
+ 'node_modules',
463
+ packageName,
464
+ )
465
+
466
+ // Verificar se o projeto ainda existe
467
+ if (!fs.existsSync(watchedProject.path)) return false
468
+
469
+ // Verificar se já está aplicado e é a mesma versão
470
+ const overrideMarker = path.join(nodeModulesPath, '.local-override.json')
471
+ if (fs.existsSync(overrideMarker)) {
472
+ try {
473
+ const existing = JSON.parse(fs.readFileSync(overrideMarker, 'utf8'))
474
+ if (existing.lastBuild >= localPkg.lastBuild) {
475
+ return false // Já está atualizado
476
+ }
477
+ } catch {}
478
+ }
479
+
480
+ try {
481
+ // Remove versão atual do node_modules
482
+ if (fs.existsSync(nodeModulesPath)) {
483
+ if (process.platform === 'win32') {
484
+ execSync(`rmdir /s /q "${nodeModulesPath}"`, {
485
+ stdio: 'pipe',
486
+ timeout: 10_000,
487
+ })
488
+ } else {
489
+ execSync(`rm -rf "${nodeModulesPath}"`, {
490
+ stdio: 'pipe',
491
+ timeout: 10_000,
492
+ })
493
+ }
494
+ }
495
+
496
+ // Cria diretório
497
+ fs.mkdirSync(nodeModulesPath, { recursive: true })
498
+
499
+ // Copia arquivos essenciais
500
+ this.copyFiles(localPkg.path, nodeModulesPath)
501
+
502
+ // Marca como override
503
+ fs.writeFileSync(
504
+ overrideMarker,
505
+ JSON.stringify(
506
+ {
507
+ source: localPkg.path,
508
+ version: localPkg.version,
509
+ lastBuild: localPkg.lastBuild,
510
+ appliedAt: Date.now(),
511
+ buildDetails: localPkg.buildDetails,
512
+ autoApplied: true, // Marca que foi aplicado automaticamente
513
+ },
514
+ null,
515
+ 2,
516
+ ),
517
+ )
518
+
519
+ return true
520
+ } catch (error) {
521
+ return false
522
+ }
523
+ }
524
+
525
+ /**
526
+ * Verifica e aplica overrides locais (versão melhorada)
527
+ */
528
+ check(): void {
529
+ const packageJsonPath = path.join(process.cwd(), 'package.json')
530
+ if (!fs.existsSync(packageJsonPath)) {
531
+ console.log('❌ package.json não encontrado')
532
+ return
533
+ }
534
+
535
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
536
+ const dependencies = {
537
+ ...packageJson.dependencies,
538
+ ...packageJson.devDependencies,
539
+ }
540
+ const registry = this.getLocalRegistry()
541
+
542
+ console.log('\n🔍 Verificando dependências locais...')
543
+ console.log(`📦 Projeto: ${packageJson.name}@${packageJson.version}`)
544
+
545
+ const scopedDeps = Object.keys(dependencies).filter((dep) =>
546
+ isScopedPackage(dep),
547
+ )
548
+
549
+ if (scopedDeps.length === 0) {
550
+ console.log('ℹ️ Nenhuma dependência scoped (@) encontrada')
551
+ return
552
+ }
553
+
554
+ console.log(`🎯 Dependências scoped encontradas: ${scopedDeps.length}`)
555
+
556
+ // 🆕 ATUALIZAR O REGISTRY LOCAL COM AS VERSÕES DO PACKAGE.JSON
557
+ console.log(
558
+ '\n🔄 Atualizando registry local com versões do package.json...',
559
+ )
560
+ let updatedRegistry = false
561
+
562
+ scopedDeps.forEach((depName) => {
563
+ const requiredVersion = dependencies[depName].replace(/[\^~]/, '')
564
+
565
+ // Se o pacote não existe no registry local, tentar encontrá-lo
566
+ if (!registry[depName]) {
567
+ // Procurar pelo pacote nos node_modules
568
+ const nodeModulesPath = path.join(
569
+ process.cwd(),
570
+ 'node_modules',
571
+ depName,
572
+ )
573
+ if (fs.existsSync(nodeModulesPath)) {
574
+ const depPackageJson = path.join(nodeModulesPath, 'package.json')
575
+ if (fs.existsSync(depPackageJson)) {
576
+ try {
577
+ const depPkg = JSON.parse(fs.readFileSync(depPackageJson, 'utf8'))
578
+
579
+ // Adicionar ao registry local
580
+ registry[depName] = {
581
+ name: depName,
582
+ version: depPkg.version,
583
+ path: nodeModulesPath,
584
+ lastBuild: Date.now(),
585
+ buildDetails: {
586
+ timestamp: new Date().toISOString(),
587
+ nodeVersion: process.version,
588
+ },
589
+ }
590
+
591
+ updatedRegistry = true
592
+ console.log(
593
+ ` ✅ Adicionado ao registry: ${depName}@${depPkg.version}`,
594
+ )
595
+ } catch (parseError) {
596
+ console.log(
597
+ ` ⚠️ Erro ao ler package.json de ${depName} (arquivo corrompido ou com conflitos de merge)`,
598
+ )
599
+ }
600
+ }
601
+ }
602
+ } else {
603
+ // Atualizar versão se diferente
604
+ const currentVersion = registry[depName].version
605
+ if (currentVersion !== requiredVersion) {
606
+ const nodeModulesPath = path.join(
607
+ process.cwd(),
608
+ 'node_modules',
609
+ depName,
610
+ )
611
+ if (fs.existsSync(nodeModulesPath)) {
612
+ const depPackageJson = path.join(nodeModulesPath, 'package.json')
613
+ if (fs.existsSync(depPackageJson)) {
614
+ try {
615
+ const depPkg = JSON.parse(fs.readFileSync(depPackageJson, 'utf8'))
616
+ registry[depName].version = depPkg.version
617
+ registry[depName].lastBuild = Date.now()
618
+ updatedRegistry = true
619
+ console.log(
620
+ ` 🔄 Atualizado: ${depName} v${currentVersion} → v${depPkg.version}`,
621
+ )
622
+ } catch (parseError) {
623
+ console.log(
624
+ ` ⚠️ Erro ao ler package.json de ${depName} (arquivo corrompido ou com conflitos de merge)`,
625
+ )
626
+ }
627
+ }
628
+ }
629
+ }
630
+ }
631
+ })
632
+
633
+ // Salvar registry atualizado
634
+ if (updatedRegistry) {
635
+ fs.writeFileSync(LOCAL_REGISTRY, JSON.stringify(registry, null, 2))
636
+ console.log('✅ Registry local atualizado')
637
+ }
638
+
639
+ let appliedCount = 0
640
+ let skippedCount = 0
641
+ let alreadyInstalledCount = 0
642
+
643
+ console.log('\n📦 Sincronizando dependências scoped (@)...')
644
+ console.log(' 🔒 Framework (@silasfmartins/testhub): sempre do Azure DevOps')
645
+ console.log(' 📦 Outros pacotes (@rbqa/*, @sfa/*, etc.): versão local prevalece sobre Azure\n')
646
+
647
+ for (const depName of scopedDeps) {
648
+ const requiredVersionRaw = dependencies[depName]
649
+ const requiredVersion = requiredVersionRaw.replace(/[\^~]/, '')
650
+ console.log(`\n 📋 ${depName}`)
651
+ console.log(` 📄 Versão no package.json: ${requiredVersionRaw}`)
652
+
653
+ // Verificar versão instalada atualmente
654
+ const nodeModulesPath = path.join(process.cwd(), 'node_modules', depName)
655
+ let installedVersion: string | null = null
656
+ let hasLocalOverride = false
657
+
658
+ if (fs.existsSync(nodeModulesPath)) {
659
+ const depPkgPath = path.join(nodeModulesPath, 'package.json')
660
+ const overrideMarker = path.join(nodeModulesPath, '.local-override.json')
661
+
662
+ if (fs.existsSync(depPkgPath)) {
663
+ try {
664
+ const depPkg = JSON.parse(fs.readFileSync(depPkgPath, 'utf8'))
665
+ installedVersion = depPkg.version
666
+ } catch (parseError) {
667
+ console.log(` ⚠️ Erro ao ler package.json (arquivo corrompido ou com conflitos de merge)`)
668
+ console.log(` 💡 Tente: rm -rf node_modules/${depName} && npm install`)
669
+ }
670
+ }
671
+
672
+ hasLocalOverride = fs.existsSync(overrideMarker)
673
+ }
674
+
675
+ if (installedVersion) {
676
+ console.log(` 📦 Versão instalada: ${installedVersion}${hasLocalOverride ? ' (LOCAL)' : ' (AZURE)'}`)
677
+ }
678
+
679
+ // 🔒 FRAMEWORK (@silasfmartins/testhub): SEMPRE instalar do Azure DevOps
680
+ if (PREFER_AZURE_PACKAGES.has(depName)) {
681
+ console.log(` 🔒 FRAMEWORK: garantindo instalação do Azure DevOps`)
682
+
683
+ // Verificar se já está na versão correta (do Azure, sem override local)
684
+ if (installedVersion && !hasLocalOverride) {
685
+ const versionMatch = this.compareVersions(installedVersion, requiredVersion) >= 0
686
+ if (versionMatch) {
687
+ console.log(` ✅ Framework já está na versão correta (${installedVersion})`)
688
+ alreadyInstalledCount++
689
+ continue
690
+ }
691
+ }
692
+
693
+ // Remover override local se existir (framework SEMPRE vem do Azure)
694
+ if (hasLocalOverride) {
695
+ console.log(` 🗑️ Removendo override local (framework deve vir do Azure)...`)
696
+ try {
697
+ if (process.platform === 'win32') {
698
+ execSync(`rmdir /s /q "${nodeModulesPath}"`, { stdio: 'pipe', timeout: 10000 })
699
+ } else {
700
+ execSync(`rm -rf "${nodeModulesPath}"`, { stdio: 'pipe', timeout: 10000 })
701
+ }
702
+ } catch (e) {
703
+ // Ignorar erro de remoção
704
+ }
705
+ }
706
+
707
+ // Instalar a versão do package.json do Azure
708
+ console.log(` 🔄 Instalando ${depName}@${requiredVersion} do Azure DevOps...`)
709
+ if (this.installFromAzure(depName, requiredVersion)) {
710
+ appliedCount++
711
+ console.log(` ✅ Framework instalado com sucesso: ${depName}@${requiredVersion}`)
712
+ } else {
713
+ skippedCount++
714
+ console.log(` ❌ Falha ao instalar framework do Azure`)
715
+ console.log(` 💡 Verifique sua conexão com o Azure DevOps`)
716
+ }
717
+ continue
718
+ }
719
+
720
+ // 📦 OUTROS PACOTES SCOPED (@rbqa/*, @sfa/*, etc.): versão LOCAL prevalece sobre Azure
721
+ // Verificar se tem versão local disponível no registry
722
+ const localPkg = registry[depName]
723
+
724
+ if (localPkg) {
725
+ // Tem versão local disponível
726
+ console.log(` 🔗 Versão local disponível: ${localPkg.version}`)
727
+
728
+ // Verificar se a versão local atende ao requisito
729
+ const localMeetsRequirement = this.compareVersions(localPkg.version, requiredVersion) >= 0
730
+
731
+ if (localMeetsRequirement) {
732
+ // Versão local atende - usar LOCAL (prevalece sobre Azure)
733
+ if (hasLocalOverride && installedVersion === localPkg.version) {
734
+ console.log(` ✅ Versão local já está aplicada (${localPkg.version})`)
735
+ alreadyInstalledCount++
736
+ } else {
737
+ console.log(` 🔗 Aplicando versão LOCAL (prevalece sobre Azure)...`)
738
+ if (this.applyOverride(depName, localPkg)) {
739
+ appliedCount++
740
+ console.log(` ✅ Versão local aplicada: ${depName}@${localPkg.version}`)
741
+ } else {
742
+ alreadyInstalledCount++
743
+ console.log(` ✅ Versão local já está atualizada`)
744
+ }
745
+ }
746
+ } else {
747
+ // Versão local não atende - verificar Azure
748
+ console.log(` ⚠️ Versão local (${localPkg.version}) não atende requisito (${requiredVersion})`)
749
+
750
+ const existsInAzure = this.checkIfExistsInAzure(depName)
751
+ if (existsInAzure) {
752
+ console.log(` 🔄 Instalando ${depName}@${requiredVersion} do Azure DevOps...`)
753
+ if (this.installFromAzure(depName, requiredVersion)) {
754
+ appliedCount++
755
+ console.log(` ✅ Instalado do Azure: ${depName}@${requiredVersion}`)
756
+ } else {
757
+ skippedCount++
758
+ console.log(` ❌ Falha ao instalar do Azure`)
759
+ }
760
+ } else {
761
+ skippedCount++
762
+ console.log(` ❌ Versão não disponível (nem local, nem Azure)`)
763
+ }
764
+ }
765
+ } else {
766
+ // Não tem versão local - verificar se já está instalado ou buscar no Azure
767
+ if (installedVersion) {
768
+ const installedMeetsRequirement = this.compareVersions(installedVersion, requiredVersion) >= 0
769
+ if (installedMeetsRequirement) {
770
+ console.log(` ✅ Já está instalado na versão correta (${installedVersion})`)
771
+ alreadyInstalledCount++
772
+ continue
773
+ }
774
+ }
775
+
776
+ // Buscar no Azure
777
+ const existsInAzure = this.checkIfExistsInAzure(depName)
778
+
779
+ if (existsInAzure) {
780
+ console.log(` ☁️ Instalando ${depName}@${requiredVersion} do Azure DevOps...`)
781
+ if (this.installFromAzure(depName, requiredVersion)) {
782
+ appliedCount++
783
+ console.log(` ✅ Instalado do Azure: ${depName}@${requiredVersion}`)
784
+ } else {
785
+ skippedCount++
786
+ console.log(` ❌ Falha ao instalar do Azure`)
787
+ }
788
+ } else {
789
+ if (installedVersion) {
790
+ console.log(` ℹ️ Mantendo versão instalada: ${installedVersion}`)
791
+ alreadyInstalledCount++
792
+ } else {
793
+ skippedCount++
794
+ console.log(` ❌ Pacote não encontrado (nem local, nem Azure)`)
795
+ console.log(` 💡 Execute 'npm run build' no projeto da dependência`)
796
+ }
797
+ }
798
+ }
799
+ }
800
+
801
+ // Registrar este projeto para futuras atualizações automáticas
802
+ this.registerAsWatchableProject(packageJson.name)
803
+
804
+ // Resumo final
805
+ console.log('\n' + '═'.repeat(60))
806
+ console.log('📊 RESUMO DA SINCRONIZAÇÃO:')
807
+ console.log(` ✅ Sincronizados: ${appliedCount}`)
808
+ console.log(` ✔️ Já na versão correta: ${alreadyInstalledCount}`)
809
+ console.log(` ❌ Falhas: ${skippedCount}`)
810
+ console.log(` 📦 Total de dependências scoped: ${scopedDeps.length}`)
811
+
812
+ if (appliedCount > 0) {
813
+ console.log(`\n🎯 ${appliedCount} pacote(s) sincronizado(s)`)
814
+ }
815
+
816
+ if (alreadyInstalledCount > 0) {
817
+ console.log(`✅ ${alreadyInstalledCount} pacote(s) já estavam corretos`)
818
+ }
819
+
820
+ if (skippedCount > 0) {
821
+ console.log(`\n⚠️ ${skippedCount} pacote(s) com problema`)
822
+ console.log(' 💡 Verifique sua conexão com o Azure DevOps')
823
+ console.log(' 💡 Tente: npm cache clean --force && npm install')
824
+ }
825
+
826
+ if (appliedCount === 0 && skippedCount === 0) {
827
+ console.log('\n✨ Todas as dependências já estão sincronizadas!')
828
+ }
829
+
830
+ console.log('═'.repeat(60))
831
+ }
832
+
833
+ /**
834
+ * 🔍 Verifica se um pacote existe no Azure DevOps (sem falhar o build)
835
+ */
836
+ private checkIfExistsInAzure(packageName: string): boolean {
837
+ try {
838
+ // Tentar verificar se o pacote existe no registry do Azure
839
+ const result = execSync(`npm view ${packageName} version`, {
840
+ stdio: 'pipe',
841
+ timeout: 10000,
842
+ })
843
+ return result.toString().trim().length > 0
844
+ } catch (error) {
845
+ // Se der erro 404 ou timeout, assume que não existe
846
+ return false
847
+ }
848
+ }
849
+
850
+ /**
851
+ * 🔍 Obtém a versão mais recente disponível no Azure DevOps
852
+ */
853
+ private getLatestAzureVersion(packageName: string): string | null {
854
+ try {
855
+ const result = execSync(`npm view ${packageName} version`, {
856
+ stdio: 'pipe',
857
+ timeout: 15000,
858
+ })
859
+ return result.toString().trim() || null
860
+ } catch (error) {
861
+ return null
862
+ }
863
+ }
864
+
865
+ /**
866
+ * 🔧 Instala um pacote do Azure DevOps
867
+ */
868
+ private installFromAzure(packageName: string, version?: string): boolean {
869
+ const versionSpec = version ? `${packageName}@${version}` : `${packageName}@latest`
870
+
871
+ try {
872
+ // 🔧 Usar --ignore-scripts para evitar erros de postinstall em sub-dependências
873
+ // Também usar --prefer-online para garantir que busca a versão correta do Azure
874
+ console.log(` ℹ️ Instalando com --ignore-scripts para evitar conflitos...`)
875
+ execSync(`npm install ${versionSpec} --prefer-online --ignore-scripts`, {
876
+ stdio: 'inherit',
877
+ cwd: process.cwd(),
878
+ timeout: 180000, // 3 minutos de timeout
879
+ })
880
+ return true
881
+ } catch (error) {
882
+ // Tentar com --force e --ignore-scripts se falhou
883
+ console.log(` 🔄 Tentando com --force --ignore-scripts...`)
884
+ try {
885
+ execSync(`npm install ${versionSpec} --force --ignore-scripts`, {
886
+ stdio: 'inherit',
887
+ cwd: process.cwd(),
888
+ timeout: 180000,
889
+ })
890
+ return true
891
+ } catch (retryError) {
892
+ console.error(
893
+ ` ❌ Falha ao instalar ${versionSpec}`,
894
+ )
895
+ console.log(` 💡 Tente manualmente: npm install ${versionSpec} --force --ignore-scripts`)
896
+ return false
897
+ }
898
+ }
899
+ }
900
+
901
+ private compareVersions(v1: string, v2: string): number {
902
+ const parts1 = v1.split('.').map(Number)
903
+ const parts2 = v2.split('.').map(Number)
904
+
905
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
906
+ const a = parts1[i] || 0
907
+ const b = parts2[i] || 0
908
+ if (a > b) return 1
909
+ if (a < b) return -1
910
+ }
911
+ return 0
912
+ }
913
+
914
+ private applyOverride(packageName: string, localPkg: LocalPackage): boolean {
915
+ const nodeModulesPath = path.join(
916
+ process.cwd(),
917
+ 'node_modules',
918
+ packageName,
919
+ )
920
+
921
+ // Verificar se o local ainda existe e é mais recente
922
+ if (!fs.existsSync(localPkg.path)) return false
923
+
924
+ const localDistPath = path.join(localPkg.path, 'dist')
925
+ if (!fs.existsSync(localDistPath)) return false
926
+
927
+ // Verificar se já está aplicado e é a mesma versão
928
+ const overrideMarker = path.join(nodeModulesPath, '.local-override.json')
929
+ if (fs.existsSync(overrideMarker)) {
930
+ const existing = JSON.parse(fs.readFileSync(overrideMarker, 'utf8'))
931
+ if (existing.lastBuild >= localPkg.lastBuild) {
932
+ return false // Já está atualizado
933
+ }
934
+ }
935
+
936
+ try {
937
+ // Remove versão atual do node_modules
938
+ if (fs.existsSync(nodeModulesPath)) {
939
+ if (process.platform === 'win32') {
940
+ execSync(`rmdir /s /q "${nodeModulesPath}"`, {
941
+ stdio: 'pipe',
942
+ timeout: 10_000,
943
+ })
944
+ } else {
945
+ execSync(`rm -rf "${nodeModulesPath}"`, {
946
+ stdio: 'pipe',
947
+ timeout: 10_000,
948
+ })
949
+ }
950
+ }
951
+
952
+ // Cria diretório
953
+ fs.mkdirSync(nodeModulesPath, { recursive: true })
954
+
955
+ // Copia arquivos essenciais
956
+ this.copyFiles(localPkg.path, nodeModulesPath)
957
+
958
+ // Marca como override
959
+ fs.writeFileSync(
960
+ overrideMarker,
961
+ JSON.stringify(
962
+ {
963
+ source: localPkg.path,
964
+ version: localPkg.version,
965
+ lastBuild: localPkg.lastBuild,
966
+ appliedAt: Date.now(),
967
+ buildDetails: localPkg.buildDetails,
968
+ },
969
+ null,
970
+ 2,
971
+ ),
972
+ )
973
+
974
+ return true
975
+ } catch (error) {
976
+ return false
977
+ }
978
+ }
979
+
980
+ private copyFiles(source: string, destination: string): void {
981
+ // Copiar arquivos essenciais
982
+ const filesToCopy = ['package.json', 'README.md']
983
+ const dirsToCopy = ['dist']
984
+
985
+ filesToCopy.forEach((file) => {
986
+ const srcFile = path.join(source, file)
987
+ const destFile = path.join(destination, file)
988
+ if (fs.existsSync(srcFile)) {
989
+ fs.copyFileSync(srcFile, destFile)
990
+ }
991
+ })
992
+
993
+ dirsToCopy.forEach((dir) => {
994
+ const srcDir = path.join(source, dir)
995
+ const destDir = path.join(destination, dir)
996
+ if (fs.existsSync(srcDir)) {
997
+ try {
998
+ if (process.platform === 'win32') {
999
+ execSync(`xcopy "${srcDir}" "${destDir}" /E /I /Y /Q`, {
1000
+ stdio: 'pipe',
1001
+ timeout: 15_000,
1002
+ })
1003
+ } else {
1004
+ execSync(`cp -r "${srcDir}" "${destDir}"`, {
1005
+ stdio: 'pipe',
1006
+ timeout: 15_000,
1007
+ })
1008
+ }
1009
+ } catch (error) {
1010
+ console.warn(`Aviso: Erro ao copiar diretório ${dir}`)
1011
+ }
1012
+ }
1013
+ })
1014
+ }
1015
+
1016
+ /**
1017
+ * Remove todos os overrides locais (versão melhorada com suporte a pacotes locais)
1018
+ */
1019
+ remove(): void {
1020
+ console.log('\n🔄 Removendo overrides locais...')
1021
+
1022
+ const nodeModulesPath = path.join(process.cwd(), 'node_modules')
1023
+ if (!fs.existsSync(nodeModulesPath)) {
1024
+ console.log('📂 Diretório node_modules não encontrado')
1025
+ return
1026
+ }
1027
+
1028
+ const packages = fs
1029
+ .readdirSync(nodeModulesPath)
1030
+ .filter((dir) => dir.startsWith('@'))
1031
+ .filter((dir) =>
1032
+ fs.existsSync(path.join(nodeModulesPath, dir, '.local-override.json')),
1033
+ )
1034
+
1035
+ if (packages.length === 0) {
1036
+ console.log('ℹ️ Nenhum override local encontrado')
1037
+ return
1038
+ }
1039
+
1040
+ console.log(`🎯 Encontrados ${packages.length} override(s) para remover:`)
1041
+
1042
+ const startTime = Date.now()
1043
+ let removedCount = 0
1044
+ const localOnlyPackages: string[] = []
1045
+
1046
+ // Verificar quais pacotes existem apenas localmente
1047
+ console.log('\n🔍 Verificando disponibilidade no Azure DevOps...')
1048
+ packages.forEach((pkg) => {
1049
+ if (this.checkIfExistsInAzure(pkg)) {
1050
+ console.log(` ✅ ${pkg} - Disponível no Azure`)
1051
+ } else {
1052
+ localOnlyPackages.push(pkg)
1053
+ console.log(` 💡 ${pkg} - Existe APENAS localmente`)
1054
+ }
1055
+ })
1056
+
1057
+ if (localOnlyPackages.length > 0) {
1058
+ console.log(
1059
+ `\n⚠️ ATENÇÃO: ${localOnlyPackages.length} pacote(s) existe(m) APENAS localmente:`,
1060
+ )
1061
+ localOnlyPackages.forEach((pkg) => {
1062
+ console.log(` • ${pkg}`)
1063
+ })
1064
+ console.log('\n❌ Não é possível remover overrides desses pacotes!')
1065
+ console.log('💡 Esses pacotes não estão publicados no Azure DevOps.')
1066
+ console.log(
1067
+ '💡 Mantenha os overrides locais ou publique-os no Azure primeiro.',
1068
+ )
1069
+
1070
+ // Filtrar apenas pacotes que podem ser removidos
1071
+ const packagesToRemove = packages.filter(
1072
+ (pkg) => !localOnlyPackages.includes(pkg),
1073
+ )
1074
+
1075
+ if (packagesToRemove.length === 0) {
1076
+ console.log(
1077
+ '\n📦 Nenhum pacote pode ser removido (todos são apenas locais).',
1078
+ )
1079
+ console.log('═'.repeat(60))
1080
+ return
1081
+ }
1082
+
1083
+ console.log(
1084
+ `\n🔄 Removendo apenas pacotes disponíveis no Azure (${packagesToRemove.length})...`,
1085
+ )
1086
+ } else {
1087
+ const packagesToRemove = packages
1088
+ }
1089
+
1090
+ const packagesToRemove = packages.filter(
1091
+ (pkg) => !localOnlyPackages.includes(pkg),
1092
+ )
1093
+
1094
+ packagesToRemove.forEach((pkg) => {
1095
+ const pkgPath = path.join(nodeModulesPath, pkg)
1096
+ console.log(` 🗑️ Removendo ${pkg}...`)
1097
+
1098
+ try {
1099
+ if (process.platform === 'win32') {
1100
+ execSync(`rmdir /s /q "${pkgPath}"`, { stdio: 'pipe', timeout: 5000 })
1101
+ } else {
1102
+ execSync(`rm -rf "${pkgPath}"`, { stdio: 'pipe', timeout: 5000 })
1103
+ }
1104
+ removedCount++
1105
+ console.log(' ✅ Removido com sucesso')
1106
+ } catch (error) {
1107
+ console.log(` ⚠️ Erro ao remover: ${pkg}`)
1108
+ }
1109
+ })
1110
+
1111
+ if (removedCount > 0) {
1112
+ console.log(
1113
+ `\n📦 Reinstalando ${removedCount} dependência(s) do Azure DevOps...`,
1114
+ )
1115
+ const installStart = Date.now()
1116
+
1117
+ try {
1118
+ // Instalar apenas os pacotes que foram removidos (que existem no Azure)
1119
+ const packagesToInstall = packagesToRemove.join(' ')
1120
+ execSync(`npm install ${packagesToInstall} --force`, {
1121
+ stdio: 'inherit',
1122
+ })
1123
+ const installTime = Date.now() - installStart
1124
+
1125
+ console.log('\n✅ Processo concluído com sucesso!')
1126
+ console.log('📊 Resumo:')
1127
+ console.log(` • Overrides removidos: ${removedCount}`)
1128
+ console.log(` • Mantidos (apenas local): ${localOnlyPackages.length}`)
1129
+ console.log(
1130
+ ` • Tempo de remoção: ${this.formatDuration(Date.now() - startTime)}`,
1131
+ )
1132
+ console.log(
1133
+ ` • Tempo de reinstalação: ${this.formatDuration(installTime)}`,
1134
+ )
1135
+ console.log(' • Pacotes do Azure reinstalados com sucesso')
1136
+
1137
+ if (localOnlyPackages.length > 0) {
1138
+ console.log('\n💡 Pacotes mantidos localmente:')
1139
+ localOnlyPackages.forEach((pkg) => {
1140
+ console.log(` • ${pkg} - Não disponível no Azure`)
1141
+ })
1142
+ }
1143
+ } catch (error) {
1144
+ console.log(`❌ Erro durante reinstalação: ${error}`)
1145
+ console.log('💡 Alguns pacotes podem não existir no Azure DevOps.')
1146
+ console.log(` Execute 'npm install' manualmente para resolver.`)
1147
+ }
1148
+ } else {
1149
+ console.log('\n⚠️ Nenhum pacote foi removido.')
1150
+ console.log('📦 Todos os pacotes com override existem apenas localmente.')
1151
+ }
1152
+
1153
+ console.log('═'.repeat(60))
1154
+ }
1155
+
1156
+ /**
1157
+ * Mostra status dos overrides (versão melhorada)
1158
+ */
1159
+ status(): void {
1160
+ console.log('\n📊 STATUS DAS DEPENDÊNCIAS SCOPED (@):')
1161
+ console.log('═'.repeat(70))
1162
+
1163
+ const nodeModulesPath = path.join(process.cwd(), 'node_modules')
1164
+ if (!fs.existsSync(nodeModulesPath)) {
1165
+ console.log('📂 Diretório node_modules não encontrado')
1166
+ return
1167
+ }
1168
+
1169
+ const packages = fs
1170
+ .readdirSync(nodeModulesPath)
1171
+ .filter((dir) => dir.startsWith('@'))
1172
+
1173
+ if (packages.length === 0) {
1174
+ console.log('📦 Nenhuma dependência scoped (@) encontrada')
1175
+ return
1176
+ }
1177
+
1178
+ let localCount = 0
1179
+ let azureCount = 0
1180
+ let autoUpdatedCount = 0
1181
+
1182
+ packages.forEach((pkg) => {
1183
+ const pkgPath = path.join(nodeModulesPath, pkg)
1184
+ const overrideFile = path.join(pkgPath, '.local-override.json')
1185
+
1186
+ if (fs.existsSync(overrideFile)) {
1187
+ localCount++
1188
+ try {
1189
+ const override = JSON.parse(fs.readFileSync(overrideFile, 'utf8'))
1190
+ const age = Math.round((Date.now() - override.appliedAt) / 1000 / 60)
1191
+ const autoApplied = override.autoApplied ? ' (AUTO)' : ''
1192
+
1193
+ if (override.autoApplied) autoUpdatedCount++
1194
+
1195
+ console.log(`🔗 ${pkg}@${override.version} (LOCAL${autoApplied})`)
1196
+ console.log(` 📅 Aplicado há ${age} minuto(s)`)
1197
+
1198
+ if (override.buildDetails) {
1199
+ console.log(
1200
+ ` 📊 Build: ${override.buildDetails.buildSize}, ${override.buildDetails.compiledFiles} arquivos`,
1201
+ )
1202
+ console.log(
1203
+ ` 🕐 Compilado: ${new Date(override.buildDetails.timestamp).toLocaleString('pt-BR')}`,
1204
+ )
1205
+ }
1206
+
1207
+ console.log(` 📂 Origem: ${override.source}`)
1208
+
1209
+ if (override.autoApplied) {
1210
+ console.log(' 🤖 Atualização automática ativada')
1211
+ }
1212
+ } catch {
1213
+ console.log(`🔗 ${pkg} (LOCAL - erro ao ler detalhes)`)
1214
+ }
1215
+ console.log('')
1216
+ } else {
1217
+ azureCount++
1218
+ const packageJsonPath = path.join(pkgPath, 'package.json')
1219
+ if (fs.existsSync(packageJsonPath)) {
1220
+ try {
1221
+ const pkgJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
1222
+ console.log(`☁️ ${pkg}@${pkgJson.version} (AZURE DEVOPS)`)
1223
+ } catch {
1224
+ console.log(`☁️ ${pkg} (AZURE DEVOPS - erro ao ler versão)`)
1225
+ }
1226
+ }
1227
+ }
1228
+ })
1229
+
1230
+ console.log('═'.repeat(70))
1231
+ console.log('📋 RESUMO:')
1232
+ console.log(` 🔗 Dependências locais: ${localCount}`)
1233
+ console.log(` 🤖 Atualizações automáticas: ${autoUpdatedCount}`)
1234
+ console.log(` ☁️ Dependências Azure: ${azureCount}`)
1235
+ console.log(` 📦 Total: ${packages.length}`)
1236
+
1237
+ if (localCount > 0) {
1238
+ console.log('\n💡 DICAS:')
1239
+ console.log(
1240
+ ` • Use 'smart-override remove' para voltar às versões do Azure`,
1241
+ )
1242
+ console.log(
1243
+ ' • Dependências com (AUTO) são atualizadas automaticamente',
1244
+ )
1245
+ console.log(' • Builds locais têm prioridade sobre versões do registry')
1246
+ }
1247
+
1248
+ console.log('═'.repeat(70))
1249
+ }
1250
+
1251
+ /**
1252
+ * Atualiza o registry local com a versão atual do package.json
1253
+ */
1254
+ private updateLocalRegistry(): void {
1255
+ const packageJsonPath = path.join(process.cwd(), 'package.json')
1256
+
1257
+ if (!fs.existsSync(packageJsonPath)) {
1258
+ console.log('⚠️ package.json não encontrado')
1259
+ return
1260
+ }
1261
+
1262
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
1263
+ const registry = this.getLocalRegistry()
1264
+
1265
+ // Verificar se é um pacote scoped (@org/nome)
1266
+ if (!isScopedPackage(packageJson.name)) {
1267
+ console.log('⚠️ Este não é um pacote scoped (@org/nome)')
1268
+ return
1269
+ }
1270
+
1271
+ const distPath = path.join(process.cwd(), 'dist')
1272
+ const hasDistFolder = fs.existsSync(distPath)
1273
+
1274
+ // Atualizar ou criar entrada no registry
1275
+ const existingEntry = registry[packageJson.name]
1276
+
1277
+ registry[packageJson.name] = {
1278
+ name: packageJson.name,
1279
+ version: packageJson.version,
1280
+ path: process.cwd(),
1281
+ lastBuild: existingEntry?.lastBuild || Date.now(),
1282
+ buildDuration: existingEntry?.buildDuration,
1283
+ buildDetails: existingEntry?.buildDetails || {
1284
+ compiledFiles: hasDistFolder ? this.countCompiledFiles(distPath) : 0,
1285
+ buildSize: hasDistFolder
1286
+ ? this.formatSize(this.calculateDirectorySize(distPath))
1287
+ : '0 KB',
1288
+ nodeVersion: process.version,
1289
+ timestamp: new Date().toISOString(),
1290
+ },
1291
+ }
1292
+
1293
+ this.saveLocalRegistry(registry)
1294
+ console.log(
1295
+ `✅ Registry local atualizado: ${packageJson.name}@${packageJson.version}`,
1296
+ )
1297
+ }
1298
+
1299
+ /**
1300
+ * Comando para sincronizar manualmente todos os projetos
1301
+ * Agora também instala do Azure DevOps quando necessário
1302
+ */
1303
+ sync(): void {
1304
+ console.log('\n🔄 SINCRONIZAÇÃO MANUAL DE PROJETOS:')
1305
+ console.log('═'.repeat(70))
1306
+
1307
+ // Primeiro, atualizar o registry local com a versão atual
1308
+ this.updateLocalRegistry()
1309
+ console.log() // linha em branco
1310
+
1311
+ // Verificar dependências do projeto ATUAL primeiro
1312
+ const packageJsonPath = path.join(process.cwd(), 'package.json')
1313
+ if (fs.existsSync(packageJsonPath)) {
1314
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
1315
+ const dependencies = {
1316
+ ...packageJson.dependencies,
1317
+ ...packageJson.devDependencies,
1318
+ }
1319
+
1320
+ const scopedDeps = Object.keys(dependencies).filter((dep) =>
1321
+ isScopedPackage(dep),
1322
+ )
1323
+
1324
+ if (scopedDeps.length > 0) {
1325
+ console.log(`📦 Sincronizando dependências do projeto atual: ${packageJson.name}`)
1326
+ console.log(`🎯 Dependências scoped encontradas: ${scopedDeps.length}`)
1327
+ console.log()
1328
+
1329
+ let installedFromAzure = 0
1330
+ let localApplied = 0
1331
+ let upToDate = 0
1332
+ let failed = 0
1333
+
1334
+ const localRegistry = this.getLocalRegistry()
1335
+
1336
+ for (const depName of scopedDeps) {
1337
+ const requiredVersion = dependencies[depName].replace(/[\^~]/, '')
1338
+ console.log(`\n 📋 ${depName}@${requiredVersion}`)
1339
+
1340
+ // Verificar versão instalada atualmente
1341
+ const nodeModulesPath = path.join(process.cwd(), 'node_modules', depName)
1342
+ let installedVersion: string | null = null
1343
+ let hasLocalOverride = false
1344
+
1345
+ if (fs.existsSync(nodeModulesPath)) {
1346
+ const depPkgPath = path.join(nodeModulesPath, 'package.json')
1347
+ const overrideMarker = path.join(nodeModulesPath, '.local-override.json')
1348
+
1349
+ if (fs.existsSync(depPkgPath)) {
1350
+ try {
1351
+ const depPkg = JSON.parse(fs.readFileSync(depPkgPath, 'utf8'))
1352
+ installedVersion = depPkg.version
1353
+ } catch (parseError) {
1354
+ console.log(` ⚠️ Erro ao ler package.json (arquivo corrompido ou com conflitos de merge)`)
1355
+ console.log(` 💡 Tente: rm -rf node_modules/${depName} && npm install`)
1356
+ }
1357
+ }
1358
+
1359
+ hasLocalOverride = fs.existsSync(overrideMarker)
1360
+ }
1361
+
1362
+ // Verificar se o pacote prefere Azure
1363
+ const prefersAzure = PREFER_AZURE_PACKAGES.has(depName)
1364
+
1365
+ // Obter versão mais recente do Azure
1366
+ console.log(` 🔍 Verificando Azure DevOps...`)
1367
+ const azureVersion = this.getLatestAzureVersion(depName)
1368
+
1369
+ if (azureVersion) {
1370
+ console.log(` ☁️ Versão Azure: ${azureVersion}`)
1371
+
1372
+ if (installedVersion) {
1373
+ console.log(` 📦 Versão instalada: ${installedVersion}${hasLocalOverride ? ' (LOCAL)' : ''}`)
1374
+ }
1375
+
1376
+ // Comparar versões
1377
+ const azureIsNewer = installedVersion
1378
+ ? this.compareVersions(azureVersion, installedVersion) > 0
1379
+ : true
1380
+
1381
+ const azureMatchesRequired = this.compareVersions(azureVersion, requiredVersion) >= 0
1382
+
1383
+ // Lógica de decisão
1384
+ if (prefersAzure || azureIsNewer) {
1385
+ if (azureMatchesRequired) {
1386
+ if (!installedVersion || azureIsNewer) {
1387
+ console.log(` ⬆️ Nova versão disponível no Azure!`)
1388
+
1389
+ // Remover override local se existir
1390
+ if (hasLocalOverride) {
1391
+ console.log(` 🗑️ Removendo override local...`)
1392
+ try {
1393
+ if (process.platform === 'win32') {
1394
+ execSync(`rmdir /s /q "${nodeModulesPath}"`, { stdio: 'pipe', timeout: 10000 })
1395
+ } else {
1396
+ execSync(`rm -rf "${nodeModulesPath}"`, { stdio: 'pipe', timeout: 10000 })
1397
+ }
1398
+ } catch (e) {
1399
+ // Ignorar erro de remoção
1400
+ }
1401
+ }
1402
+
1403
+ // Instalar do Azure
1404
+ if (this.installFromAzure(depName, azureVersion)) {
1405
+ installedFromAzure++
1406
+ } else {
1407
+ failed++
1408
+ }
1409
+ } else {
1410
+ console.log(` ✅ Já está na versão mais recente do Azure`)
1411
+ upToDate++
1412
+ }
1413
+ } else {
1414
+ console.log(` ⚠️ Versão Azure (${azureVersion}) não atende requisito (${requiredVersion})`)
1415
+
1416
+ // Verificar se há versão local compatível
1417
+ if (localRegistry[depName]) {
1418
+ const localPkg = localRegistry[depName]
1419
+ if (this.compareVersions(localPkg.version, requiredVersion) >= 0) {
1420
+ console.log(` 💡 Usando versão local: ${localPkg.version}`)
1421
+ if (this.applyOverride(depName, localPkg)) {
1422
+ localApplied++
1423
+ } else {
1424
+ upToDate++
1425
+ }
1426
+ } else {
1427
+ console.log(` ❌ Nenhuma versão compatível disponível`)
1428
+ failed++
1429
+ }
1430
+ } else {
1431
+ console.log(` ❌ Nenhuma versão local disponível`)
1432
+ failed++
1433
+ }
1434
+ }
1435
+ } else if (!prefersAzure && localRegistry[depName]) {
1436
+ // Pacote não prefere Azure e tem versão local
1437
+ const localPkg = localRegistry[depName]
1438
+ const localVersion = localPkg.version
1439
+
1440
+ console.log(` 🔗 Versão local disponível: ${localVersion}`)
1441
+
1442
+ if (this.compareVersions(localVersion, requiredVersion) >= 0) {
1443
+ if (this.applyOverride(depName, localPkg)) {
1444
+ console.log(` ✅ Override local aplicado`)
1445
+ localApplied++
1446
+ } else {
1447
+ console.log(` ℹ️ Override local já está atualizado`)
1448
+ upToDate++
1449
+ }
1450
+ } else {
1451
+ // Versão local desatualizada, instalar do Azure
1452
+ console.log(` ⚠️ Versão local desatualizada, instalando do Azure...`)
1453
+ if (this.installFromAzure(depName, azureVersion)) {
1454
+ installedFromAzure++
1455
+ } else {
1456
+ failed++
1457
+ }
1458
+ }
1459
+ } else {
1460
+ // Não tem versão local e Azure está ok
1461
+ if (!installedVersion || azureIsNewer) {
1462
+ if (this.installFromAzure(depName, azureVersion)) {
1463
+ installedFromAzure++
1464
+ } else {
1465
+ failed++
1466
+ }
1467
+ } else {
1468
+ upToDate++
1469
+ }
1470
+ }
1471
+ } else {
1472
+ console.log(` ⚠️ Não encontrado no Azure DevOps`)
1473
+
1474
+ // Tentar usar versão local
1475
+ if (localRegistry[depName]) {
1476
+ const localPkg = localRegistry[depName]
1477
+ console.log(` 💡 Usando versão local: ${localPkg.version}`)
1478
+
1479
+ if (this.compareVersions(localPkg.version, requiredVersion) >= 0) {
1480
+ if (this.applyOverride(depName, localPkg)) {
1481
+ localApplied++
1482
+ } else {
1483
+ upToDate++
1484
+ }
1485
+ } else {
1486
+ console.log(` ❌ Versão local incompatível`)
1487
+ failed++
1488
+ }
1489
+ } else if (installedVersion) {
1490
+ console.log(` ℹ️ Mantendo versão instalada: ${installedVersion}`)
1491
+ upToDate++
1492
+ } else {
1493
+ console.log(` ❌ Pacote não encontrado (nem Azure, nem local)`)
1494
+ failed++
1495
+ }
1496
+ }
1497
+ }
1498
+
1499
+ // Resumo do projeto atual
1500
+ console.log('\n' + '═'.repeat(70))
1501
+ console.log('📊 RESUMO DA SINCRONIZAÇÃO:')
1502
+ console.log(` ☁️ Instalados do Azure: ${installedFromAzure}`)
1503
+ console.log(` 🔗 Overrides locais aplicados: ${localApplied}`)
1504
+ console.log(` ✅ Já atualizados: ${upToDate}`)
1505
+ console.log(` ❌ Falhas: ${failed}`)
1506
+ console.log(` 📦 Total de dependências scoped: ${scopedDeps.length}`)
1507
+ console.log(` 🕐 Concluído em: ${new Date().toLocaleString('pt-BR')}`)
1508
+
1509
+ if (failed > 0) {
1510
+ console.log('\n⚠️ AÇÕES RECOMENDADAS PARA FALHAS:')
1511
+ console.log(' 1. Verifique sua conexão com o Azure DevOps')
1512
+ console.log(' 2. Execute: npm cache clean --force')
1513
+ console.log(' 3. Tente: npm install --force')
1514
+ console.log(' 4. Verifique o arquivo .npmrc para configurações de registry')
1515
+ }
1516
+
1517
+ if (installedFromAzure > 0) {
1518
+ console.log('\n✨ Pacotes atualizados do Azure DevOps com sucesso!')
1519
+ }
1520
+
1521
+ console.log('═'.repeat(70))
1522
+ return
1523
+ }
1524
+ }
1525
+
1526
+ // Fallback: comportamento antigo para projetos registrados
1527
+ const watchRegistry = this.getWatchRegistry()
1528
+ const localRegistry = this.getLocalRegistry()
1529
+
1530
+ if (Object.keys(watchRegistry).length === 0) {
1531
+ console.log('📂 Nenhum projeto registrado para sincronização')
1532
+ console.log('💡 Execute npm run build em alguns projetos primeiro')
1533
+ return
1534
+ }
1535
+
1536
+ console.log(`🎯 Projetos registrados: ${Object.keys(watchRegistry).length}`)
1537
+ console.log(
1538
+ `📦 Pacotes locais disponíveis: ${Object.keys(localRegistry).length}`,
1539
+ )
1540
+
1541
+ let totalUpdated = 0
1542
+
1543
+ // Para cada projeto registrado
1544
+ Object.values(watchRegistry).forEach((project) => {
1545
+ console.log(`\n📋 Verificando ${project.name}...`)
1546
+
1547
+ let projectUpdated = 0
1548
+
1549
+ // Para cada dependência scoped do projeto
1550
+ Object.keys(project.dependencies).forEach((depName) => {
1551
+ if (localRegistry[depName]) {
1552
+ const localPkg = localRegistry[depName]
1553
+ const requiredVersion = project.dependencies[depName].replace(
1554
+ /[\^~]/,
1555
+ '',
1556
+ )
1557
+ const versionComparison = this.compareVersions(
1558
+ localPkg.version,
1559
+ requiredVersion,
1560
+ )
1561
+
1562
+ if (versionComparison >= 0) {
1563
+ if (this.applyOverrideToProject(project, depName, localPkg)) {
1564
+ console.log(` ✅ ${depName}@${localPkg.version} aplicado`)
1565
+ projectUpdated++
1566
+ totalUpdated++
1567
+ } else {
1568
+ console.log(` ℹ️ ${depName}@${localPkg.version} já atualizado`)
1569
+ }
1570
+ } else {
1571
+ console.log(
1572
+ ` ⚠️ ${depName}: local v${localPkg.version} < required v${requiredVersion}`,
1573
+ )
1574
+ }
1575
+ }
1576
+ })
1577
+
1578
+ if (projectUpdated === 0) {
1579
+ console.log(' 📦 Nenhuma atualização necessária')
1580
+ }
1581
+ })
1582
+
1583
+ console.log('\n' + '═'.repeat(70))
1584
+ console.log('📊 RESUMO DA SINCRONIZAÇÃO:')
1585
+ console.log(
1586
+ ` 🔄 Projetos verificados: ${Object.keys(watchRegistry).length}`,
1587
+ )
1588
+ console.log(` ✅ Atualizações aplicadas: ${totalUpdated}`)
1589
+ console.log(` 🕐 Concluído em: ${new Date().toLocaleString('pt-BR')}`)
1590
+ console.log('═'.repeat(70))
1591
+ }
1592
+ }
1593
+
1594
+ // CLI
1595
+ const command = process.argv[2]
1596
+ const override = new SmartOverride()
1597
+
1598
+ switch (command) {
1599
+ case 'register':
1600
+ override.register()
1601
+ break
1602
+ case 'check':
1603
+ override.check()
1604
+ break
1605
+ case 'remove':
1606
+ override.remove()
1607
+ break
1608
+ case 'status':
1609
+ override.status()
1610
+ break
1611
+ case 'sync':
1612
+ await (async () => {
1613
+ await override.sync()
1614
+
1615
+ try {
1616
+ // Atualizar package-lock.json imediatamente para evitar inconsistências
1617
+ console.log('\n🔄 Atualizando package-lock.json (npm install --package-lock-only) ...')
1618
+ execSync('npm install --package-lock-only', { stdio: 'inherit', timeout: 120_000 })
1619
+ console.log('✅ package-lock.json atualizado com sucesso')
1620
+ } catch (err) {
1621
+ console.warn('⚠️ Falha ao atualizar package-lock.json automaticamente:', err?.message || err)
1622
+ console.log('💡 Você pode atualizar manualmente executando: npm install --package-lock-only')
1623
+ }
1624
+ })()
1625
+ break
1626
+ default:
1627
+ console.log('\n📖 USO DO SMART OVERRIDE:')
1628
+ console.log('═'.repeat(60))
1629
+ console.log(
1630
+ 'npx autocore-sync check - Sincroniza dependências scoped (@)',
1631
+ )
1632
+ console.log('npx autocore-sync status - Mostra status das dependências')
1633
+ console.log('npx autocore-sync remove - Remove overrides locais')
1634
+ console.log('npx autocore-sync sync - Sincroniza todos os projetos')
1635
+ console.log('═'.repeat(60))
1636
+ console.log('\n💡 COMANDO RECOMENDADO NO package.json:')
1637
+ console.log('')
1638
+ console.log(' "sync": "npx autocore-sync-configs && npx autocore-sync check"')
1639
+ console.log('')
1640
+ console.log('═'.repeat(60))
1641
+ console.log('\n🎯 COMANDOS PARA USUÁRIOS:')
1642
+ console.log('')
1643
+ console.log('📦 Para sincronizar dependências:')
1644
+ console.log(
1645
+ ' npm run sync - Sincroniza configs e dependências',
1646
+ )
1647
+ console.log(
1648
+ ' npx autocore-sync check - Sincroniza dependências scoped (@)',
1649
+ )
1650
+ console.log(
1651
+ ' npx autocore-sync status - Mostra dependências atuais',
1652
+ )
1653
+ console.log('')
1654
+ console.log('🔧 COMANDOS DE MANUTENÇÃO:')
1655
+ console.log(
1656
+ ' npx autocore-sync remove - Remove overrides locais',
1657
+ )
1658
+ console.log(
1659
+ ' npm install --force - Reinstala dependências limpas',
1660
+ )
1661
+ console.log('')
1662
+ console.log('═'.repeat(60))
1663
+ console.log('\n🚀 EXEMPLOS DE USO:')
1664
+ console.log('')
1665
+ console.log('# Sincronizar tudo (configurações + dependências):')
1666
+ console.log('npm run sync')
1667
+ console.log('')
1668
+ console.log('# Ver status das dependências:')
1669
+ console.log('npx autocore-sync status')
1670
+ console.log('')
1671
+ console.log('# Voltar para versões do Azure DevOps:')
1672
+ console.log('npx autocore-sync remove')
1673
+ console.log('')
1674
+ console.log('═'.repeat(60))
1675
+ }