@elench/testkit 0.1.64 → 0.1.66

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 (342) hide show
  1. package/lib/app/browser-bridge.mjs +66 -0
  2. package/lib/app/configs.mjs +81 -0
  3. package/lib/app/configs.test.mjs +34 -0
  4. package/lib/cli/command-helpers.mjs +2 -10
  5. package/lib/cli/commands/browser/serve.mjs +3 -62
  6. package/lib/cli/db.mjs +3 -68
  7. package/lib/config/binaries.mjs +34 -0
  8. package/lib/config/database.mjs +9 -6
  9. package/lib/config/index.mjs +2 -31
  10. package/lib/config/runtime.mjs +24 -95
  11. package/lib/config/validation.mjs +18 -62
  12. package/lib/coverage/backend-discovery.mjs +68 -85
  13. package/lib/coverage/backend-discovery.test.mjs +55 -46
  14. package/lib/coverage/evidence.mjs +15 -3
  15. package/lib/coverage/evidence.test.mjs +13 -4
  16. package/lib/coverage/graph-builder.mjs +25 -26
  17. package/lib/coverage/next-ir-to-graph.mjs +239 -0
  18. package/lib/coverage/routing.mjs +2 -29
  19. package/lib/coverage/routing.test.mjs +0 -16
  20. package/lib/coverage/shared.mjs +22 -82
  21. package/lib/database/fingerprint.mjs +1 -1
  22. package/lib/known-failures/github-cache.mjs +159 -0
  23. package/lib/known-failures/github-transport.mjs +174 -0
  24. package/lib/known-failures/github.mjs +17 -325
  25. package/lib/runner/default-runtime-runner.mjs +4 -10
  26. package/lib/runner/execution-config.mjs +12 -83
  27. package/lib/runner/live-run.mjs +45 -0
  28. package/lib/runner/managed-processes.mjs +29 -0
  29. package/lib/runner/orchestrator.mjs +57 -188
  30. package/lib/runner/playwright-runner.mjs +4 -11
  31. package/lib/runner/run-finalization.mjs +132 -0
  32. package/lib/runner/run-guards.mjs +45 -0
  33. package/lib/runner/runtime-preparation.mjs +1 -1
  34. package/lib/runner/services.mjs +3 -4
  35. package/lib/runner/template-steps.mjs +8 -45
  36. package/lib/runner/template.mjs +7 -28
  37. package/lib/shared/configured-steps.mjs +178 -0
  38. package/lib/shared/configured-steps.test.mjs +73 -0
  39. package/lib/shared/execution-schema.mjs +74 -0
  40. package/lib/shared/execution-schema.test.mjs +26 -0
  41. package/node_modules/@elench/next-analysis/dist/api-routes.d.ts +7 -0
  42. package/node_modules/@elench/next-analysis/dist/api-routes.d.ts.map +1 -0
  43. package/node_modules/@elench/next-analysis/dist/api-routes.js +66 -0
  44. package/node_modules/@elench/next-analysis/dist/api-routes.js.map +1 -0
  45. package/node_modules/@elench/next-analysis/dist/app-root.d.ts +2 -0
  46. package/node_modules/@elench/next-analysis/dist/app-root.d.ts.map +1 -0
  47. package/node_modules/@elench/next-analysis/dist/app-root.js +7 -0
  48. package/node_modules/@elench/next-analysis/dist/app-root.js.map +1 -0
  49. package/node_modules/@elench/next-analysis/dist/backend-links.d.ts +8 -0
  50. package/node_modules/@elench/next-analysis/dist/backend-links.d.ts.map +1 -0
  51. package/node_modules/@elench/next-analysis/dist/backend-links.js +30 -0
  52. package/node_modules/@elench/next-analysis/dist/backend-links.js.map +1 -0
  53. package/node_modules/@elench/next-analysis/dist/index.d.ts +11 -0
  54. package/node_modules/@elench/next-analysis/dist/index.d.ts.map +1 -0
  55. package/node_modules/@elench/next-analysis/dist/index.js +10 -0
  56. package/node_modules/@elench/next-analysis/dist/index.js.map +1 -0
  57. package/node_modules/@elench/next-analysis/dist/pages.d.ts +7 -0
  58. package/node_modules/@elench/next-analysis/dist/pages.d.ts.map +1 -0
  59. package/node_modules/@elench/next-analysis/dist/pages.js +47 -0
  60. package/node_modules/@elench/next-analysis/dist/pages.js.map +1 -0
  61. package/node_modules/@elench/next-analysis/dist/project.d.ts +3 -0
  62. package/node_modules/@elench/next-analysis/dist/project.d.ts.map +1 -0
  63. package/node_modules/@elench/next-analysis/dist/project.js +102 -0
  64. package/node_modules/@elench/next-analysis/dist/project.js.map +1 -0
  65. package/node_modules/@elench/next-analysis/dist/route-tree.d.ts +7 -0
  66. package/node_modules/@elench/next-analysis/dist/route-tree.d.ts.map +1 -0
  67. package/node_modules/@elench/next-analysis/dist/route-tree.js +575 -0
  68. package/node_modules/@elench/next-analysis/dist/route-tree.js.map +1 -0
  69. package/node_modules/@elench/next-analysis/dist/routes.d.ts +6 -0
  70. package/node_modules/@elench/next-analysis/dist/routes.d.ts.map +1 -0
  71. package/node_modules/@elench/next-analysis/dist/routes.js +41 -0
  72. package/node_modules/@elench/next-analysis/dist/routes.js.map +1 -0
  73. package/node_modules/@elench/next-analysis/dist/server-actions.d.ts +7 -0
  74. package/node_modules/@elench/next-analysis/dist/server-actions.d.ts.map +1 -0
  75. package/node_modules/@elench/next-analysis/dist/server-actions.js +37 -0
  76. package/node_modules/@elench/next-analysis/dist/server-actions.js.map +1 -0
  77. package/node_modules/@elench/next-analysis/dist/shared.d.ts +57 -0
  78. package/node_modules/@elench/next-analysis/dist/shared.d.ts.map +1 -0
  79. package/node_modules/@elench/next-analysis/dist/shared.js +229 -0
  80. package/node_modules/@elench/next-analysis/dist/shared.js.map +1 -0
  81. package/node_modules/@elench/next-analysis/dist/swc.d.ts +53 -0
  82. package/node_modules/@elench/next-analysis/dist/swc.d.ts.map +1 -0
  83. package/node_modules/@elench/next-analysis/dist/swc.js +387 -0
  84. package/node_modules/@elench/next-analysis/dist/swc.js.map +1 -0
  85. package/node_modules/@elench/next-analysis/dist/types.d.ts +125 -0
  86. package/node_modules/@elench/next-analysis/dist/types.d.ts.map +1 -0
  87. package/node_modules/@elench/next-analysis/dist/types.js +2 -0
  88. package/node_modules/@elench/next-analysis/dist/types.js.map +1 -0
  89. package/node_modules/@elench/next-analysis/package.json +27 -0
  90. package/node_modules/@elench/testkit-bridge/dist/index.d.ts +36 -0
  91. package/node_modules/@elench/testkit-bridge/dist/index.d.ts.map +1 -0
  92. package/node_modules/@elench/testkit-bridge/dist/index.js +538 -0
  93. package/node_modules/@elench/testkit-bridge/dist/index.js.map +1 -0
  94. package/node_modules/@elench/testkit-bridge/package.json +16 -5
  95. package/node_modules/@elench/testkit-protocol/dist/index.d.ts +190 -0
  96. package/node_modules/@elench/testkit-protocol/dist/index.d.ts.map +1 -0
  97. package/node_modules/@elench/testkit-protocol/dist/index.js +296 -0
  98. package/node_modules/@elench/testkit-protocol/dist/index.js.map +1 -0
  99. package/node_modules/@elench/testkit-protocol/package.json +14 -7
  100. package/node_modules/@elench/ts-analysis/dist/callables.d.ts +8 -0
  101. package/node_modules/@elench/ts-analysis/dist/callables.d.ts.map +1 -0
  102. package/node_modules/@elench/ts-analysis/dist/callables.js +126 -0
  103. package/node_modules/@elench/ts-analysis/dist/callables.js.map +1 -0
  104. package/node_modules/@elench/ts-analysis/dist/exports.d.ts +6 -0
  105. package/node_modules/@elench/ts-analysis/dist/exports.d.ts.map +1 -0
  106. package/node_modules/@elench/ts-analysis/dist/exports.js +70 -0
  107. package/node_modules/@elench/ts-analysis/dist/exports.js.map +1 -0
  108. package/node_modules/@elench/ts-analysis/dist/index.d.ts +10 -0
  109. package/node_modules/@elench/ts-analysis/dist/index.d.ts.map +1 -0
  110. package/node_modules/@elench/ts-analysis/{src/index.mjs → dist/index.js} +9 -14
  111. package/node_modules/@elench/ts-analysis/dist/index.js.map +1 -0
  112. package/node_modules/@elench/ts-analysis/dist/jsx.d.ts +9 -0
  113. package/node_modules/@elench/ts-analysis/dist/jsx.d.ts.map +1 -0
  114. package/node_modules/@elench/ts-analysis/dist/jsx.js +68 -0
  115. package/node_modules/@elench/ts-analysis/dist/jsx.js.map +1 -0
  116. package/node_modules/@elench/ts-analysis/dist/project.d.ts +5 -0
  117. package/node_modules/@elench/ts-analysis/dist/project.d.ts.map +1 -0
  118. package/node_modules/@elench/ts-analysis/dist/project.js +90 -0
  119. package/node_modules/@elench/ts-analysis/dist/project.js.map +1 -0
  120. package/node_modules/@elench/ts-analysis/dist/requests.d.ts +6 -0
  121. package/node_modules/@elench/ts-analysis/dist/requests.d.ts.map +1 -0
  122. package/node_modules/@elench/ts-analysis/dist/requests.js +140 -0
  123. package/node_modules/@elench/ts-analysis/dist/requests.js.map +1 -0
  124. package/node_modules/@elench/ts-analysis/dist/resolution.d.ts +4 -0
  125. package/node_modules/@elench/ts-analysis/dist/resolution.d.ts.map +1 -0
  126. package/node_modules/@elench/ts-analysis/dist/resolution.js +53 -0
  127. package/node_modules/@elench/ts-analysis/dist/resolution.js.map +1 -0
  128. package/node_modules/@elench/ts-analysis/dist/shared.d.ts +6 -0
  129. package/node_modules/@elench/ts-analysis/dist/shared.d.ts.map +1 -0
  130. package/node_modules/@elench/ts-analysis/dist/shared.js +31 -0
  131. package/node_modules/@elench/ts-analysis/dist/shared.js.map +1 -0
  132. package/node_modules/@elench/ts-analysis/dist/syntax.d.ts +7 -0
  133. package/node_modules/@elench/ts-analysis/dist/syntax.d.ts.map +1 -0
  134. package/node_modules/@elench/ts-analysis/dist/syntax.js +27 -0
  135. package/node_modules/@elench/ts-analysis/dist/syntax.js.map +1 -0
  136. package/node_modules/@elench/ts-analysis/dist/types.d.ts +58 -0
  137. package/node_modules/@elench/ts-analysis/dist/types.d.ts.map +1 -0
  138. package/node_modules/@elench/ts-analysis/dist/types.js +2 -0
  139. package/node_modules/@elench/ts-analysis/dist/types.js.map +1 -0
  140. package/node_modules/@elench/ts-analysis/package.json +18 -2
  141. package/node_modules/@next/routing/README.md +91 -0
  142. package/node_modules/@next/routing/dist/__tests__/captures.test.d.ts +1 -0
  143. package/node_modules/@next/routing/dist/__tests__/conditions.test.d.ts +1 -0
  144. package/node_modules/@next/routing/dist/__tests__/dynamic-after-rewrites.test.d.ts +1 -0
  145. package/node_modules/@next/routing/dist/__tests__/i18n-resolve-routes.test.d.ts +1 -0
  146. package/node_modules/@next/routing/dist/__tests__/i18n.test.d.ts +1 -0
  147. package/node_modules/@next/routing/dist/__tests__/middleware.test.d.ts +1 -0
  148. package/node_modules/@next/routing/dist/__tests__/normalize-next-data.test.d.ts +1 -0
  149. package/node_modules/@next/routing/dist/__tests__/redirects.test.d.ts +1 -0
  150. package/node_modules/@next/routing/dist/__tests__/resolve-routes.test.d.ts +1 -0
  151. package/node_modules/@next/routing/dist/__tests__/rewrites.test.d.ts +1 -0
  152. package/node_modules/@next/routing/dist/destination.d.ts +22 -0
  153. package/node_modules/@next/routing/dist/i18n.d.ts +48 -0
  154. package/node_modules/@next/routing/dist/index.d.ts +5 -0
  155. package/node_modules/@next/routing/dist/index.js +1 -0
  156. package/node_modules/@next/routing/dist/matchers.d.ts +12 -0
  157. package/node_modules/@next/routing/dist/middleware.d.ts +12 -0
  158. package/node_modules/@next/routing/dist/next-data.d.ts +10 -0
  159. package/node_modules/@next/routing/dist/resolve-routes.d.ts +2 -0
  160. package/node_modules/@next/routing/dist/types.d.ts +97 -0
  161. package/node_modules/@next/routing/package.json +39 -0
  162. package/node_modules/@swc/core/README.md +100 -0
  163. package/node_modules/@swc/core/Visitor.d.ts +218 -0
  164. package/node_modules/@swc/core/Visitor.js +1399 -0
  165. package/node_modules/@swc/core/binding.d.ts +59 -0
  166. package/node_modules/@swc/core/binding.js +368 -0
  167. package/node_modules/@swc/core/index.d.ts +120 -0
  168. package/node_modules/@swc/core/index.js +443 -0
  169. package/node_modules/@swc/core/package.json +120 -0
  170. package/node_modules/@swc/core/postinstall.js +148 -0
  171. package/node_modules/@swc/core/spack.d.ts +51 -0
  172. package/node_modules/@swc/core/spack.js +87 -0
  173. package/node_modules/@swc/core/util.d.ts +1 -0
  174. package/node_modules/@swc/core/util.js +104 -0
  175. package/node_modules/@swc/core-linux-x64-gnu/README.md +3 -0
  176. package/node_modules/@swc/core-linux-x64-gnu/package.json +46 -0
  177. package/node_modules/@swc/core-linux-x64-gnu/swc.linux-x64-gnu.node +0 -0
  178. package/node_modules/@swc/counter/CHANGELOG.md +7 -0
  179. package/node_modules/@swc/counter/README.md +7 -0
  180. package/node_modules/@swc/counter/index.js +1 -0
  181. package/node_modules/@swc/counter/package.json +27 -0
  182. package/node_modules/@swc/types/LICENSE +201 -0
  183. package/node_modules/@swc/types/README.md +4 -0
  184. package/node_modules/@swc/types/assumptions.d.ts +92 -0
  185. package/node_modules/@swc/types/assumptions.js +2 -0
  186. package/node_modules/@swc/types/index.d.ts +2049 -0
  187. package/node_modules/@swc/types/index.js +2 -0
  188. package/node_modules/@swc/types/package.json +40 -0
  189. package/node_modules/typescript/LICENSE.txt +55 -0
  190. package/node_modules/typescript/README.md +50 -0
  191. package/node_modules/typescript/SECURITY.md +41 -0
  192. package/node_modules/typescript/ThirdPartyNoticeText.txt +193 -0
  193. package/node_modules/typescript/bin/tsc +2 -0
  194. package/node_modules/typescript/bin/tsserver +2 -0
  195. package/node_modules/typescript/lib/_tsc.js +133818 -0
  196. package/node_modules/typescript/lib/_tsserver.js +659 -0
  197. package/node_modules/typescript/lib/_typingsInstaller.js +222 -0
  198. package/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +2122 -0
  199. package/node_modules/typescript/lib/de/diagnosticMessages.generated.json +2122 -0
  200. package/node_modules/typescript/lib/es/diagnosticMessages.generated.json +2122 -0
  201. package/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +2122 -0
  202. package/node_modules/typescript/lib/it/diagnosticMessages.generated.json +2122 -0
  203. package/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +2122 -0
  204. package/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +2122 -0
  205. package/node_modules/typescript/lib/lib.d.ts +22 -0
  206. package/node_modules/typescript/lib/lib.decorators.d.ts +384 -0
  207. package/node_modules/typescript/lib/lib.decorators.legacy.d.ts +22 -0
  208. package/node_modules/typescript/lib/lib.dom.asynciterable.d.ts +41 -0
  209. package/node_modules/typescript/lib/lib.dom.d.ts +39429 -0
  210. package/node_modules/typescript/lib/lib.dom.iterable.d.ts +571 -0
  211. package/node_modules/typescript/lib/lib.es2015.collection.d.ts +147 -0
  212. package/node_modules/typescript/lib/lib.es2015.core.d.ts +597 -0
  213. package/node_modules/typescript/lib/lib.es2015.d.ts +28 -0
  214. package/node_modules/typescript/lib/lib.es2015.generator.d.ts +77 -0
  215. package/node_modules/typescript/lib/lib.es2015.iterable.d.ts +605 -0
  216. package/node_modules/typescript/lib/lib.es2015.promise.d.ts +81 -0
  217. package/node_modules/typescript/lib/lib.es2015.proxy.d.ts +128 -0
  218. package/node_modules/typescript/lib/lib.es2015.reflect.d.ts +144 -0
  219. package/node_modules/typescript/lib/lib.es2015.symbol.d.ts +46 -0
  220. package/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts +326 -0
  221. package/node_modules/typescript/lib/lib.es2016.array.include.d.ts +116 -0
  222. package/node_modules/typescript/lib/lib.es2016.d.ts +21 -0
  223. package/node_modules/typescript/lib/lib.es2016.full.d.ts +23 -0
  224. package/node_modules/typescript/lib/lib.es2016.intl.d.ts +31 -0
  225. package/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts +21 -0
  226. package/node_modules/typescript/lib/lib.es2017.d.ts +26 -0
  227. package/node_modules/typescript/lib/lib.es2017.date.d.ts +31 -0
  228. package/node_modules/typescript/lib/lib.es2017.full.d.ts +23 -0
  229. package/node_modules/typescript/lib/lib.es2017.intl.d.ts +44 -0
  230. package/node_modules/typescript/lib/lib.es2017.object.d.ts +49 -0
  231. package/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts +135 -0
  232. package/node_modules/typescript/lib/lib.es2017.string.d.ts +45 -0
  233. package/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts +53 -0
  234. package/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts +77 -0
  235. package/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts +53 -0
  236. package/node_modules/typescript/lib/lib.es2018.d.ts +24 -0
  237. package/node_modules/typescript/lib/lib.es2018.full.d.ts +24 -0
  238. package/node_modules/typescript/lib/lib.es2018.intl.d.ts +83 -0
  239. package/node_modules/typescript/lib/lib.es2018.promise.d.ts +30 -0
  240. package/node_modules/typescript/lib/lib.es2018.regexp.d.ts +37 -0
  241. package/node_modules/typescript/lib/lib.es2019.array.d.ts +79 -0
  242. package/node_modules/typescript/lib/lib.es2019.d.ts +24 -0
  243. package/node_modules/typescript/lib/lib.es2019.full.d.ts +24 -0
  244. package/node_modules/typescript/lib/lib.es2019.intl.d.ts +23 -0
  245. package/node_modules/typescript/lib/lib.es2019.object.d.ts +33 -0
  246. package/node_modules/typescript/lib/lib.es2019.string.d.ts +37 -0
  247. package/node_modules/typescript/lib/lib.es2019.symbol.d.ts +24 -0
  248. package/node_modules/typescript/lib/lib.es2020.bigint.d.ts +765 -0
  249. package/node_modules/typescript/lib/lib.es2020.d.ts +27 -0
  250. package/node_modules/typescript/lib/lib.es2020.date.d.ts +42 -0
  251. package/node_modules/typescript/lib/lib.es2020.full.d.ts +24 -0
  252. package/node_modules/typescript/lib/lib.es2020.intl.d.ts +474 -0
  253. package/node_modules/typescript/lib/lib.es2020.number.d.ts +28 -0
  254. package/node_modules/typescript/lib/lib.es2020.promise.d.ts +47 -0
  255. package/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts +99 -0
  256. package/node_modules/typescript/lib/lib.es2020.string.d.ts +44 -0
  257. package/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts +41 -0
  258. package/node_modules/typescript/lib/lib.es2021.d.ts +23 -0
  259. package/node_modules/typescript/lib/lib.es2021.full.d.ts +24 -0
  260. package/node_modules/typescript/lib/lib.es2021.intl.d.ts +166 -0
  261. package/node_modules/typescript/lib/lib.es2021.promise.d.ts +48 -0
  262. package/node_modules/typescript/lib/lib.es2021.string.d.ts +33 -0
  263. package/node_modules/typescript/lib/lib.es2021.weakref.d.ts +78 -0
  264. package/node_modules/typescript/lib/lib.es2022.array.d.ts +121 -0
  265. package/node_modules/typescript/lib/lib.es2022.d.ts +25 -0
  266. package/node_modules/typescript/lib/lib.es2022.error.d.ts +75 -0
  267. package/node_modules/typescript/lib/lib.es2022.full.d.ts +24 -0
  268. package/node_modules/typescript/lib/lib.es2022.intl.d.ts +145 -0
  269. package/node_modules/typescript/lib/lib.es2022.object.d.ts +26 -0
  270. package/node_modules/typescript/lib/lib.es2022.regexp.d.ts +39 -0
  271. package/node_modules/typescript/lib/lib.es2022.string.d.ts +25 -0
  272. package/node_modules/typescript/lib/lib.es2023.array.d.ts +924 -0
  273. package/node_modules/typescript/lib/lib.es2023.collection.d.ts +21 -0
  274. package/node_modules/typescript/lib/lib.es2023.d.ts +22 -0
  275. package/node_modules/typescript/lib/lib.es2023.full.d.ts +24 -0
  276. package/node_modules/typescript/lib/lib.es2023.intl.d.ts +56 -0
  277. package/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts +65 -0
  278. package/node_modules/typescript/lib/lib.es2024.collection.d.ts +29 -0
  279. package/node_modules/typescript/lib/lib.es2024.d.ts +26 -0
  280. package/node_modules/typescript/lib/lib.es2024.full.d.ts +24 -0
  281. package/node_modules/typescript/lib/lib.es2024.object.d.ts +29 -0
  282. package/node_modules/typescript/lib/lib.es2024.promise.d.ts +35 -0
  283. package/node_modules/typescript/lib/lib.es2024.regexp.d.ts +25 -0
  284. package/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts +68 -0
  285. package/node_modules/typescript/lib/lib.es2024.string.d.ts +29 -0
  286. package/node_modules/typescript/lib/lib.es5.d.ts +4601 -0
  287. package/node_modules/typescript/lib/lib.es6.d.ts +23 -0
  288. package/node_modules/typescript/lib/lib.esnext.array.d.ts +35 -0
  289. package/node_modules/typescript/lib/lib.esnext.collection.d.ts +96 -0
  290. package/node_modules/typescript/lib/lib.esnext.d.ts +29 -0
  291. package/node_modules/typescript/lib/lib.esnext.decorators.d.ts +28 -0
  292. package/node_modules/typescript/lib/lib.esnext.disposable.d.ts +193 -0
  293. package/node_modules/typescript/lib/lib.esnext.error.d.ts +24 -0
  294. package/node_modules/typescript/lib/lib.esnext.float16.d.ts +445 -0
  295. package/node_modules/typescript/lib/lib.esnext.full.d.ts +24 -0
  296. package/node_modules/typescript/lib/lib.esnext.intl.d.ts +21 -0
  297. package/node_modules/typescript/lib/lib.esnext.iterator.d.ts +148 -0
  298. package/node_modules/typescript/lib/lib.esnext.promise.d.ts +34 -0
  299. package/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts +25 -0
  300. package/node_modules/typescript/lib/lib.scripthost.d.ts +322 -0
  301. package/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts +41 -0
  302. package/node_modules/typescript/lib/lib.webworker.d.ts +13150 -0
  303. package/node_modules/typescript/lib/lib.webworker.importscripts.d.ts +23 -0
  304. package/node_modules/typescript/lib/lib.webworker.iterable.d.ts +340 -0
  305. package/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +2122 -0
  306. package/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +2122 -0
  307. package/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +2122 -0
  308. package/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +2122 -0
  309. package/node_modules/typescript/lib/tsc.js +8 -0
  310. package/node_modules/typescript/lib/tsserver.js +8 -0
  311. package/node_modules/typescript/lib/tsserverlibrary.d.ts +17 -0
  312. package/node_modules/typescript/lib/tsserverlibrary.js +21 -0
  313. package/node_modules/typescript/lib/typesMap.json +497 -0
  314. package/node_modules/typescript/lib/typescript.d.ts +11437 -0
  315. package/node_modules/typescript/lib/typescript.js +200276 -0
  316. package/node_modules/typescript/lib/typingsInstaller.js +8 -0
  317. package/node_modules/typescript/lib/watchGuard.js +53 -0
  318. package/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +2122 -0
  319. package/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +2122 -0
  320. package/node_modules/typescript/package.json +120 -0
  321. package/package.json +13 -8
  322. package/lib/coverage/fs-walk.mjs +0 -64
  323. package/lib/coverage/next-discovery.mjs +0 -205
  324. package/lib/coverage/next-static-analysis.mjs +0 -1045
  325. package/node_modules/@elench/testkit-bridge/src/index.mjs +0 -583
  326. package/node_modules/@elench/testkit-bridge/src/index.test.mjs +0 -409
  327. package/node_modules/@elench/testkit-protocol/src/index.d.ts +0 -231
  328. package/node_modules/@elench/testkit-protocol/src/index.mjs +0 -265
  329. package/node_modules/@elench/testkit-protocol/src/index.test.mjs +0 -242
  330. package/node_modules/@elench/ts-analysis/src/callables.mjs +0 -135
  331. package/node_modules/@elench/ts-analysis/src/callables.test.mjs +0 -55
  332. package/node_modules/@elench/ts-analysis/src/exports.mjs +0 -69
  333. package/node_modules/@elench/ts-analysis/src/exports.test.mjs +0 -50
  334. package/node_modules/@elench/ts-analysis/src/jsx.mjs +0 -69
  335. package/node_modules/@elench/ts-analysis/src/jsx.test.mjs +0 -43
  336. package/node_modules/@elench/ts-analysis/src/project.mjs +0 -100
  337. package/node_modules/@elench/ts-analysis/src/project.test.mjs +0 -54
  338. package/node_modules/@elench/ts-analysis/src/requests.mjs +0 -141
  339. package/node_modules/@elench/ts-analysis/src/requests.test.mjs +0 -35
  340. package/node_modules/@elench/ts-analysis/src/resolution.mjs +0 -53
  341. package/node_modules/@elench/ts-analysis/src/shared.mjs +0 -32
  342. package/node_modules/@elench/ts-analysis/src/syntax.mjs +0 -27
@@ -0,0 +1,239 @@
1
+ import {
2
+ pageLabelFromRoute,
3
+ } from "@elench/next-analysis";
4
+ import {
5
+ dedupeEdges,
6
+ dedupeNodes,
7
+ findMatchingApiRouteEntry,
8
+ hasWord,
9
+ modulePathKey,
10
+ } from "./shared.mjs";
11
+
12
+ export function convertNextAnalysisToGraph({ serviceName, pages = [], apiRoutes = [], serverActions = [], routeTrees = [] }) {
13
+ const nodes = [];
14
+ const edges = [];
15
+ const pageEntries = [];
16
+ const routeEntries = [];
17
+ const actionEntries = [];
18
+
19
+ const routeTreeByRoute = new Map(routeTrees.filter(Boolean).map((entry) => [entry.route, entry]));
20
+
21
+ for (const page of pages) {
22
+ const pageNode = {
23
+ id: `page_view:${serviceName}:${page.route}`,
24
+ kind: "page_view",
25
+ service: serviceName,
26
+ label: pageLabelFromRoute(page.route),
27
+ route: page.route,
28
+ filePath: page.filePath,
29
+ };
30
+ nodes.push(pageNode);
31
+
32
+ const routeTree = routeTreeByRoute.get(page.route);
33
+ const surfacesByTargetValue = new Map();
34
+ if (routeTree) {
35
+ for (const surface of routeTree.surfaces) {
36
+ const surfaceNode = {
37
+ id: `ui_surface:${serviceName}:${page.route}:${surface.targetHint?.value || `${surface.filePath}:${surface.tagName}:${surface.line}`}`,
38
+ kind: "ui_surface",
39
+ service: serviceName,
40
+ label: surface.label,
41
+ route: page.route,
42
+ filePath: surface.filePath,
43
+ ...(surface.targetHint ? { target: surface.targetHint } : {}),
44
+ metadata: {
45
+ surfaceKind: surface.surfaceKind,
46
+ tagName: surface.tagName,
47
+ },
48
+ };
49
+ nodes.push(surfaceNode);
50
+ edges.push({
51
+ id: `contains:${pageNode.id}:${surfaceNode.id}`,
52
+ kind: "contains",
53
+ from: pageNode.id,
54
+ to: surfaceNode.id,
55
+ confidence: "high",
56
+ });
57
+ if (surface.targetHint?.kind === "testId") {
58
+ surfacesByTargetValue.set(surface.targetHint.value, surfaceNode);
59
+ }
60
+ if (surface.actionId) {
61
+ const actionKey = stripActionPrefix(surface.actionId, page.route);
62
+ edges.push({
63
+ id: `triggers:${surfaceNode.id}:ui_action:${serviceName}:${page.route}:${actionKey}`,
64
+ kind: "triggers",
65
+ from: surfaceNode.id,
66
+ to: `ui_action:${serviceName}:${page.route}:${actionKey}`,
67
+ confidence: "high",
68
+ });
69
+ }
70
+ }
71
+
72
+ for (const action of routeTree.actions) {
73
+ const actionKey = stripActionPrefix(action.id, page.route);
74
+ nodes.push({
75
+ id: `ui_action:${serviceName}:${page.route}:${actionKey}`,
76
+ kind: "ui_action",
77
+ service: serviceName,
78
+ label: action.label,
79
+ route: page.route,
80
+ filePath: action.filePath,
81
+ metadata: {
82
+ bindingKind: action.bindingKind,
83
+ actionProp: action.actionProp,
84
+ },
85
+ });
86
+ }
87
+
88
+ for (const request of routeTree.requests) {
89
+ const ownerId = mapRouteTreeOwnerToGraphId(serviceName, page.route, request.ownerId);
90
+ const requestNode = {
91
+ id: `client_request:${serviceName}:${request.filePath}:${request.ownerId}:${request.method}:${request.path}`,
92
+ kind: "client_request",
93
+ service: serviceName,
94
+ label: `${request.method} ${request.path}`,
95
+ method: request.method,
96
+ path: request.path,
97
+ filePath: request.filePath,
98
+ };
99
+ nodes.push(requestNode);
100
+ edges.push({
101
+ id: `requests:${ownerId}:${requestNode.id}`,
102
+ kind: "requests",
103
+ from: ownerId,
104
+ to: requestNode.id,
105
+ confidence: request.confidence,
106
+ });
107
+ }
108
+
109
+ pageEntries.push({
110
+ node: pageNode,
111
+ route: page.route,
112
+ filePath: page.filePath,
113
+ requests: routeTree.requests.map((request) => ({
114
+ originNodeId: mapRouteTreeOwnerToGraphId(serviceName, page.route, request.ownerId),
115
+ node: {
116
+ id: `client_request:${serviceName}:${request.filePath}:${request.ownerId}:${request.method}:${request.path}`,
117
+ kind: "client_request",
118
+ service: serviceName,
119
+ label: `${request.method} ${request.path}`,
120
+ method: request.method,
121
+ path: request.path,
122
+ filePath: request.filePath,
123
+ },
124
+ method: request.method,
125
+ path: request.path,
126
+ })),
127
+ serverActionRefs: routeTree.serverActionRefs.map((ref) => ({
128
+ originNodeId: mapRouteTreeOwnerToGraphId(serviceName, page.route, ref.ownerId),
129
+ exportKey: ref.exportKey,
130
+ confidence: ref.confidence,
131
+ })),
132
+ surfacesByTargetValue,
133
+ });
134
+ } else {
135
+ pageEntries.push({
136
+ node: pageNode,
137
+ route: page.route,
138
+ filePath: page.filePath,
139
+ requests: [],
140
+ serverActionRefs: [],
141
+ surfacesByTargetValue,
142
+ });
143
+ }
144
+ }
145
+
146
+ for (const apiRoute of apiRoutes) {
147
+ const node = {
148
+ id: `api_route:${serviceName}:${apiRoute.method}:${apiRoute.requestPath}`,
149
+ kind: "api_route",
150
+ service: serviceName,
151
+ label: `${apiRoute.method} ${apiRoute.requestPath}`,
152
+ route: apiRoute.route,
153
+ method: apiRoute.method,
154
+ path: apiRoute.requestPath,
155
+ filePath: apiRoute.filePath,
156
+ };
157
+ nodes.push(node);
158
+ routeEntries.push({
159
+ node,
160
+ method: apiRoute.method,
161
+ route: apiRoute.route,
162
+ requestPath: apiRoute.requestPath,
163
+ filePath: apiRoute.filePath,
164
+ });
165
+
166
+ for (const backendRef of apiRoute.backendRefs || []) {
167
+ const capabilityNode = createServerCapabilityNode(serviceName, backendRef);
168
+ nodes.push(capabilityNode);
169
+ edges.push({
170
+ id: `delegates_to:${node.id}:${capabilityNode.id}`,
171
+ kind: "delegates_to",
172
+ from: node.id,
173
+ to: capabilityNode.id,
174
+ confidence: "high",
175
+ });
176
+ }
177
+ }
178
+
179
+ for (const serverAction of serverActions) {
180
+ const node = {
181
+ id: `server_action:${serviceName}:${serverAction.filePath}#${serverAction.exportName}`,
182
+ kind: "server_action",
183
+ service: serviceName,
184
+ label: serverAction.exportName,
185
+ filePath: serverAction.filePath,
186
+ };
187
+ nodes.push(node);
188
+ actionEntries.push({
189
+ node,
190
+ exportName: serverAction.exportName,
191
+ sourceFile: serverAction.filePath,
192
+ });
193
+ for (const backendRef of serverAction.backendRefs || []) {
194
+ const capabilityNode = createServerCapabilityNode(serviceName, backendRef);
195
+ nodes.push(capabilityNode);
196
+ edges.push({
197
+ id: `delegates_to:${node.id}:${capabilityNode.id}`,
198
+ kind: "delegates_to",
199
+ from: node.id,
200
+ to: capabilityNode.id,
201
+ confidence: "high",
202
+ });
203
+ }
204
+ }
205
+
206
+ return {
207
+ nodes: dedupeNodes(nodes),
208
+ edges: dedupeEdges(edges),
209
+ pageEntries,
210
+ routeEntries,
211
+ actionEntries,
212
+ };
213
+ }
214
+
215
+ function mapRouteTreeOwnerToGraphId(serviceName, route, ownerId) {
216
+ if (ownerId === `page:${route}`) return `page_view:${serviceName}:${route}`;
217
+ if (ownerId.startsWith("action:")) {
218
+ return `ui_action:${serviceName}:${route}:${stripActionPrefix(ownerId, route)}`;
219
+ }
220
+ return ownerId;
221
+ }
222
+
223
+ function stripActionPrefix(actionId, route) {
224
+ const prefix = `action:${route}:`;
225
+ return actionId.startsWith(prefix) ? actionId.slice(prefix.length) : actionId;
226
+ }
227
+
228
+ function createServerCapabilityNode(serviceName, backendRef) {
229
+ return {
230
+ id: `server_capability:${modulePathKey(backendRef.modulePath)}#${backendRef.exportName}`,
231
+ kind: "server_capability",
232
+ service: serviceName,
233
+ label: backendRef.exportName,
234
+ filePath: backendRef.modulePath || null,
235
+ metadata: {
236
+ specifier: backendRef.specifier,
237
+ },
238
+ };
239
+ }
@@ -1,5 +1,6 @@
1
1
  import path from "path";
2
- import { normalizePath, normalizeRoute } from "./shared.mjs";
2
+ import { normalizeRouteSegments, toApiRequestPath } from "@elench/next-analysis";
3
+ import { normalizePath } from "./shared.mjs";
3
4
 
4
5
  export function inferPageRouteFromTestFile(filePath, nextAppRoot, serviceRoot) {
5
6
  const appRelativeSegments = extractRouteOwnerSegments(filePath, nextAppRoot, serviceRoot);
@@ -47,38 +48,10 @@ export function extractRouteOwnerSegments(filePath, nextAppRoot, serviceRoot) {
47
48
  return segments.slice(0, testkitIndex);
48
49
  }
49
50
 
50
- export function routeFromAppFile(nextAppRoot, filePath) {
51
- const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(nextAppRoot, filePath);
52
- const relative = normalizePath(path.relative(nextAppRoot, absolutePath));
53
- const segments = relative.split("/");
54
- segments.pop();
55
- return normalizeRouteSegments(segments);
56
- }
57
-
58
- export function routeFromApiFile(nextAppRoot, filePath) {
59
- const appRelative = normalizePath(path.relative(nextAppRoot, path.dirname(filePath)));
60
- const segments = appRelative.split("/").filter(Boolean);
61
- if (segments[0] === "api") segments.shift();
62
- return normalizeRouteSegments(segments);
63
- }
64
-
65
- export function normalizeRouteSegments(segments) {
66
- const normalizedSegments = segments
67
- .filter(Boolean)
68
- .filter((segment) => !segment.startsWith("(") || !segment.endsWith(")"))
69
- .filter((segment) => !segment.startsWith("@"))
70
- .filter((segment) => segment !== "page" && segment !== "route");
71
- return normalizeRoute(`/${normalizedSegments.join("/")}`);
72
- }
73
-
74
51
  export function apiRouteLookupKey(method, route) {
75
52
  return `${method}:${route}`;
76
53
  }
77
54
 
78
- export function toApiRequestPath(route) {
79
- return normalizeRoute(`/api${route === "/" ? "" : route}`);
80
- }
81
-
82
55
  export function toSelectionType(type, framework) {
83
56
  if (framework === "playwright") return "pw";
84
57
  if (type === "integration") return "int";
@@ -4,19 +4,10 @@ import {
4
4
  inferApiRoutesFromTestFile,
5
5
  inferOwnerDirectoryFromTestFile,
6
6
  inferPageRouteFromTestFile,
7
- normalizeRouteSegments,
8
7
  pathMatchesOwner,
9
- routeFromApiFile,
10
- routeFromAppFile,
11
8
  } from "./routing.mjs";
12
9
 
13
10
  describe("coverage routing helpers", () => {
14
- it("normalizes route segments by stripping route groups and slots", () => {
15
- expect(normalizeRouteSegments(["(marketing)", "@modal", "campaigns", "[campaignId]", "page"])).toBe(
16
- "/campaigns/[campaignId]"
17
- );
18
- });
19
-
20
11
  it("infers page ownership routes from colocated __testkit__ files", () => {
21
12
  const serviceRoot = "/repo";
22
13
  const nextAppRoot = path.join(serviceRoot, "src", "app");
@@ -35,13 +26,6 @@ describe("coverage routing helpers", () => {
35
26
  ).toEqual(["/campaigns"]);
36
27
  });
37
28
 
38
- it("derives page and API routes from Next app file paths", () => {
39
- const nextAppRoot = "/repo/src/app";
40
-
41
- expect(routeFromAppFile(nextAppRoot, "/repo/src/app/settings/page.tsx")).toBe("/settings");
42
- expect(routeFromApiFile(nextAppRoot, "/repo/src/app/api/campaigns/route.ts")).toBe("/campaigns");
43
- });
44
-
45
29
  it("matches nested ownership directories for DAL evidence", () => {
46
30
  expect(inferOwnerDirectoryFromTestFile("src/backend/data/campaigns/__testkit__/save.dal.testkit.ts")).toBe(
47
31
  "src/backend/data/campaigns"
@@ -1,7 +1,28 @@
1
1
  import path from "path";
2
2
  import { TESTKIT_COVERAGE_GRAPH_VERSION } from "@elench/testkit-protocol";
3
+ import {
4
+ DYNAMIC_SEGMENT_TOKEN,
5
+ HTTP_METHODS,
6
+ isBackendSpecifier,
7
+ isDataSpecifier,
8
+ modulePathKey,
9
+ normalizePath,
10
+ normalizeRoute,
11
+ pageLabelFromRoute,
12
+ toApiRequestPath,
13
+ } from "@elench/next-analysis";
14
+ export {
15
+ DYNAMIC_SEGMENT_TOKEN,
16
+ HTTP_METHODS,
17
+ isBackendSpecifier,
18
+ isDataSpecifier,
19
+ modulePathKey,
20
+ normalizePath,
21
+ normalizeRoute,
22
+ pageLabelFromRoute,
23
+ toApiRequestPath,
24
+ };
3
25
 
4
- export const HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
5
26
  export const HTTP_WRAPPER_METHODS = {
6
27
  getJson: "GET",
7
28
  postJson: "POST",
@@ -9,7 +30,6 @@ export const HTTP_WRAPPER_METHODS = {
9
30
  patchJson: "PATCH",
10
31
  deleteJson: "DELETE",
11
32
  };
12
- export const DYNAMIC_SEGMENT_TOKEN = "__TESTKIT_DYNAMIC_SEGMENT__";
13
33
 
14
34
  export function createEmptyGraph() {
15
35
  return {
@@ -59,51 +79,12 @@ export function apiRouteLookupKey(method, route) {
59
79
  return `${method}:${route}`;
60
80
  }
61
81
 
62
- export function toApiRequestPath(route) {
63
- return normalizeRoute(`/api${route === "/" ? "" : route}`);
64
- }
65
-
66
82
  export function toSelectionType(type, framework) {
67
83
  if (framework === "playwright") return "pw";
68
84
  if (type === "integration") return "int";
69
85
  return type;
70
86
  }
71
87
 
72
- export function pageLabelFromRoute(route) {
73
- if (route === "/") return "Home";
74
- return route
75
- .split("/")
76
- .filter(Boolean)
77
- .map((segment) => {
78
- if (segment.startsWith("[") && segment.endsWith("]")) {
79
- return segment.slice(1, -1);
80
- }
81
- return segment;
82
- })
83
- .map((segment) => segment.replace(/[-_]+/gu, " "))
84
- .map((segment) => segment.replace(/^\w/u, (char) => char.toUpperCase()))
85
- .join(" ");
86
- }
87
-
88
- export function normalizeRoute(value) {
89
- const trimmed = String(value || "/").trim();
90
- if (!trimmed || trimmed === "/") return "/";
91
- const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
92
- return withLeadingSlash.length > 1 ? withLeadingSlash.replace(/\/+$/u, "") : withLeadingSlash;
93
- }
94
-
95
- export function normalizePath(filePath) {
96
- return filePath.split(path.sep).join("/");
97
- }
98
-
99
- export function modulePathKey(filePath) {
100
- const normalized = normalizePath(filePath);
101
- const ext = path.extname(normalized);
102
- if (path.basename(normalized, ext) === "index") {
103
- return normalizePath(path.dirname(normalized));
104
- }
105
- return normalized.slice(0, normalized.length - ext.length);
106
- }
107
88
 
108
89
  export function escapeRegExp(value) {
109
90
  return String(value).replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
@@ -114,27 +95,6 @@ export function isServerActionFile(content) {
114
95
  return trimmed.startsWith('"use server"') || trimmed.startsWith("'use server'");
115
96
  }
116
97
 
117
- export function isBackendSpecifier(specifier) {
118
- return (
119
- specifier.startsWith("@/backend/server/") ||
120
- specifier.includes("/backend/server/") ||
121
- specifier.startsWith("./backend/server/") ||
122
- specifier.startsWith("../backend/server/")
123
- );
124
- }
125
-
126
- export function isDataSpecifier(specifier) {
127
- return (
128
- specifier.startsWith("@/backend/data/") ||
129
- specifier.startsWith("@/backend/dal/") ||
130
- specifier.includes("/backend/data/") ||
131
- specifier.includes("/backend/dal/") ||
132
- specifier.includes("/db/") ||
133
- specifier.includes("/repository/") ||
134
- specifier.includes("/repositories/")
135
- );
136
- }
137
-
138
98
  export function hasWord(content, word) {
139
99
  if (!content || !word) return false;
140
100
  return new RegExp(`\\b${escapeRegExp(word)}\\b`, "u").test(content);
@@ -178,26 +138,6 @@ export function dedupeTargets(targets) {
178
138
  });
179
139
  }
180
140
 
181
- export function dedupeBackendImports(entries) {
182
- const seen = new Set();
183
- return entries.filter((entry) => {
184
- const key = `${entry.importName}:${entry.node.filePath || ""}:${entry.node.label}`;
185
- if (seen.has(key)) return false;
186
- seen.add(key);
187
- return true;
188
- });
189
- }
190
-
191
- export function dedupeDataImports(entries) {
192
- const seen = new Set();
193
- return entries.filter((entry) => {
194
- const key = `${entry.importName}:${entry.node.filePath || ""}:${entry.node.label}`;
195
- if (seen.has(key)) return false;
196
- seen.add(key);
197
- return true;
198
- });
199
- }
200
-
201
141
  export function findMatchingApiRouteEntry(method, candidatePath, routeEntries = []) {
202
142
  const normalizedMethod = String(method || "").trim().toUpperCase();
203
143
  const normalizedCandidate = normalizeRequestPathCandidate(candidatePath);
@@ -1,7 +1,7 @@
1
1
  import crypto from "crypto";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
- import { resolveServiceCwd } from "../config/index.mjs";
4
+ import { resolveServiceCwd } from "../config/paths.mjs";
5
5
  import { collectTemplateInputs } from "./template-steps.mjs";
6
6
 
7
7
  const LOCAL_IMAGE = "pgvector/pgvector:pg16";
@@ -0,0 +1,159 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export function loadIssueCache(productDir, cachePath, schemaVersion) {
5
+ const filePath = path.join(productDir, ...cachePath);
6
+ if (!fs.existsSync(filePath)) {
7
+ return {
8
+ schemaVersion,
9
+ entries: {},
10
+ };
11
+ }
12
+
13
+ try {
14
+ const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
15
+ if (parsed?.schemaVersion !== schemaVersion || typeof parsed.entries !== "object") {
16
+ return {
17
+ schemaVersion,
18
+ entries: {},
19
+ };
20
+ }
21
+ return parsed;
22
+ } catch {
23
+ return {
24
+ schemaVersion,
25
+ entries: {},
26
+ };
27
+ }
28
+ }
29
+
30
+ export function resolveIssueCache(cache, issueNumbersByRepo, config, now) {
31
+ const issuesByRepo = new Map();
32
+ const missingByRepo = new Map();
33
+ const staleByRepo = new Map();
34
+ const ttlMs = config.cacheTtlSeconds * 1000;
35
+
36
+ for (const [repo, numbers] of issueNumbersByRepo.entries()) {
37
+ const cachedIssues = new Map();
38
+ const missing = [];
39
+ const stale = [];
40
+
41
+ for (const number of numbers) {
42
+ const key = buildIssueCacheKey(repo, number);
43
+ const cached = cache.entries[key];
44
+ if (!cached) {
45
+ missing.push(number);
46
+ continue;
47
+ }
48
+
49
+ const checkedAt = Date.parse(cached.checkedAt);
50
+ const normalized = {
51
+ repo,
52
+ number,
53
+ exists: Boolean(cached.exists),
54
+ title: normalizeOptionalString(cached.title),
55
+ state: normalizeOptionalString(cached.state),
56
+ url: normalizeOptionalString(cached.url),
57
+ checkedAt: cached.checkedAt || null,
58
+ source: "cache",
59
+ };
60
+
61
+ if (!Number.isFinite(checkedAt) || now - checkedAt > ttlMs) {
62
+ stale.push(number);
63
+ cachedIssues.set(number, normalized);
64
+ continue;
65
+ }
66
+
67
+ cachedIssues.set(number, normalized);
68
+ }
69
+
70
+ issuesByRepo.set(repo, cachedIssues);
71
+ if (missing.length > 0 || stale.length > 0) {
72
+ missingByRepo.set(repo, [...new Set([...missing, ...stale])].sort((a, b) => a - b));
73
+ }
74
+ if (stale.length > 0) {
75
+ staleByRepo.set(repo, stale.sort((a, b) => a - b));
76
+ }
77
+ }
78
+
79
+ return {
80
+ issuesByRepo,
81
+ missingByRepo,
82
+ staleByRepo,
83
+ };
84
+ }
85
+
86
+ export function updateIssueCache(cache, issuesByRepo, now, schemaVersion) {
87
+ cache.schemaVersion = schemaVersion;
88
+ cache.entries = cache.entries || {};
89
+ const checkedAt = new Date(now).toISOString();
90
+
91
+ for (const [repo, issues] of issuesByRepo.entries()) {
92
+ for (const [number, issue] of issues.entries()) {
93
+ issue.checkedAt = checkedAt;
94
+ cache.entries[buildIssueCacheKey(repo, number)] = {
95
+ exists: issue.exists,
96
+ title: issue.title,
97
+ state: issue.state,
98
+ url: issue.url,
99
+ checkedAt,
100
+ };
101
+ }
102
+ }
103
+ }
104
+
105
+ export function writeIssueCache(productDir, cachePath, cache) {
106
+ const filePath = path.join(productDir, ...cachePath);
107
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
108
+ fs.writeFileSync(filePath, `${JSON.stringify(cache, null, 2)}\n`);
109
+ }
110
+
111
+ export function applyStaleCacheFallback(issuesByRepo, staleByRepo, findings) {
112
+ let usedStale = false;
113
+ for (const [repo, numbers] of staleByRepo.entries()) {
114
+ const issues = issuesByRepo.get(repo);
115
+ if (!issues) continue;
116
+ for (const number of numbers) {
117
+ if (issues.has(number)) {
118
+ usedStale = true;
119
+ }
120
+ }
121
+ }
122
+ if (usedStale) {
123
+ findings.push({
124
+ code: "used_stale_cache",
125
+ severity: "warning",
126
+ message: "Used stale cached GitHub issue metadata because fresh validation failed",
127
+ });
128
+ }
129
+ return issuesByRepo;
130
+ }
131
+
132
+ export function mergeIssuesByRepo(left, right) {
133
+ const merged = new Map();
134
+ for (const [repo, issues] of left.entries()) {
135
+ merged.set(repo, new Map(issues.entries()));
136
+ }
137
+
138
+ for (const [repo, issues] of right.entries()) {
139
+ if (!merged.has(repo)) {
140
+ merged.set(repo, new Map());
141
+ }
142
+ const mergedIssues = merged.get(repo);
143
+ for (const [number, issue] of issues.entries()) {
144
+ mergedIssues.set(number, issue);
145
+ }
146
+ }
147
+
148
+ return merged;
149
+ }
150
+
151
+ function buildIssueCacheKey(repo, number) {
152
+ return `${repo}#${number}`;
153
+ }
154
+
155
+ function normalizeOptionalString(value) {
156
+ if (typeof value !== "string") return null;
157
+ const normalized = value.trim();
158
+ return normalized.length > 0 ? normalized : null;
159
+ }