@elench/testkit 0.1.65 → 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 (305) 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/graph-builder.mjs +5 -5
  15. package/lib/coverage/next-ir-to-graph.mjs +0 -1
  16. package/lib/coverage/routing.mjs +2 -29
  17. package/lib/coverage/routing.test.mjs +0 -16
  18. package/lib/coverage/shared.mjs +22 -82
  19. package/lib/database/fingerprint.mjs +1 -1
  20. package/lib/known-failures/github-cache.mjs +159 -0
  21. package/lib/known-failures/github-transport.mjs +174 -0
  22. package/lib/known-failures/github.mjs +17 -325
  23. package/lib/runner/default-runtime-runner.mjs +4 -10
  24. package/lib/runner/execution-config.mjs +12 -83
  25. package/lib/runner/live-run.mjs +45 -0
  26. package/lib/runner/managed-processes.mjs +29 -0
  27. package/lib/runner/orchestrator.mjs +57 -188
  28. package/lib/runner/playwright-runner.mjs +4 -11
  29. package/lib/runner/run-finalization.mjs +132 -0
  30. package/lib/runner/run-guards.mjs +45 -0
  31. package/lib/runner/runtime-preparation.mjs +1 -1
  32. package/lib/runner/services.mjs +3 -4
  33. package/lib/runner/template-steps.mjs +8 -45
  34. package/lib/runner/template.mjs +7 -28
  35. package/lib/shared/configured-steps.mjs +178 -0
  36. package/lib/shared/configured-steps.test.mjs +73 -0
  37. package/lib/shared/execution-schema.mjs +74 -0
  38. package/lib/shared/execution-schema.test.mjs +26 -0
  39. package/node_modules/@elench/next-analysis/dist/api-routes.d.ts +7 -0
  40. package/node_modules/@elench/next-analysis/dist/api-routes.d.ts.map +1 -0
  41. package/node_modules/@elench/next-analysis/dist/api-routes.js +66 -0
  42. package/node_modules/@elench/next-analysis/dist/api-routes.js.map +1 -0
  43. package/node_modules/@elench/next-analysis/dist/app-root.d.ts +2 -0
  44. package/node_modules/@elench/next-analysis/dist/app-root.d.ts.map +1 -0
  45. package/node_modules/@elench/next-analysis/dist/app-root.js +7 -0
  46. package/node_modules/@elench/next-analysis/dist/app-root.js.map +1 -0
  47. package/node_modules/@elench/next-analysis/dist/backend-links.d.ts +8 -0
  48. package/node_modules/@elench/next-analysis/dist/backend-links.d.ts.map +1 -0
  49. package/node_modules/@elench/next-analysis/dist/backend-links.js +30 -0
  50. package/node_modules/@elench/next-analysis/dist/backend-links.js.map +1 -0
  51. package/node_modules/@elench/next-analysis/dist/index.d.ts +11 -0
  52. package/node_modules/@elench/next-analysis/dist/index.d.ts.map +1 -0
  53. package/node_modules/@elench/next-analysis/dist/index.js +10 -0
  54. package/node_modules/@elench/next-analysis/dist/index.js.map +1 -0
  55. package/node_modules/@elench/next-analysis/dist/pages.d.ts +7 -0
  56. package/node_modules/@elench/next-analysis/dist/pages.d.ts.map +1 -0
  57. package/node_modules/@elench/next-analysis/dist/pages.js +47 -0
  58. package/node_modules/@elench/next-analysis/dist/pages.js.map +1 -0
  59. package/node_modules/@elench/next-analysis/dist/project.d.ts +3 -0
  60. package/node_modules/@elench/next-analysis/dist/project.d.ts.map +1 -0
  61. package/node_modules/@elench/next-analysis/dist/project.js +102 -0
  62. package/node_modules/@elench/next-analysis/dist/project.js.map +1 -0
  63. package/node_modules/@elench/next-analysis/dist/route-tree.d.ts +7 -0
  64. package/node_modules/@elench/next-analysis/dist/route-tree.d.ts.map +1 -0
  65. package/node_modules/@elench/next-analysis/dist/route-tree.js +575 -0
  66. package/node_modules/@elench/next-analysis/dist/route-tree.js.map +1 -0
  67. package/node_modules/@elench/next-analysis/dist/routes.d.ts +6 -0
  68. package/node_modules/@elench/next-analysis/dist/routes.d.ts.map +1 -0
  69. package/node_modules/@elench/next-analysis/dist/routes.js +41 -0
  70. package/node_modules/@elench/next-analysis/dist/routes.js.map +1 -0
  71. package/node_modules/@elench/next-analysis/dist/server-actions.d.ts +7 -0
  72. package/node_modules/@elench/next-analysis/dist/server-actions.d.ts.map +1 -0
  73. package/node_modules/@elench/next-analysis/dist/server-actions.js +37 -0
  74. package/node_modules/@elench/next-analysis/dist/server-actions.js.map +1 -0
  75. package/node_modules/@elench/next-analysis/dist/shared.d.ts +57 -0
  76. package/node_modules/@elench/next-analysis/dist/shared.d.ts.map +1 -0
  77. package/node_modules/@elench/next-analysis/dist/shared.js +229 -0
  78. package/node_modules/@elench/next-analysis/dist/shared.js.map +1 -0
  79. package/node_modules/@elench/next-analysis/dist/swc.d.ts +53 -0
  80. package/node_modules/@elench/next-analysis/dist/swc.d.ts.map +1 -0
  81. package/node_modules/@elench/next-analysis/dist/swc.js +387 -0
  82. package/node_modules/@elench/next-analysis/dist/swc.js.map +1 -0
  83. package/node_modules/@elench/next-analysis/dist/types.d.ts +125 -0
  84. package/node_modules/@elench/next-analysis/dist/types.d.ts.map +1 -0
  85. package/node_modules/@elench/next-analysis/dist/types.js +2 -0
  86. package/node_modules/@elench/next-analysis/dist/types.js.map +1 -0
  87. package/node_modules/@elench/next-analysis/package.json +15 -2
  88. package/node_modules/@elench/testkit-bridge/dist/index.d.ts +36 -0
  89. package/node_modules/@elench/testkit-bridge/dist/index.d.ts.map +1 -0
  90. package/node_modules/@elench/testkit-bridge/dist/index.js +538 -0
  91. package/node_modules/@elench/testkit-bridge/dist/index.js.map +1 -0
  92. package/node_modules/@elench/testkit-bridge/package.json +16 -5
  93. package/node_modules/@elench/testkit-protocol/dist/index.d.ts +190 -0
  94. package/node_modules/@elench/testkit-protocol/dist/index.d.ts.map +1 -0
  95. package/node_modules/@elench/testkit-protocol/dist/index.js +296 -0
  96. package/node_modules/@elench/testkit-protocol/dist/index.js.map +1 -0
  97. package/node_modules/@elench/testkit-protocol/package.json +14 -7
  98. package/node_modules/@elench/ts-analysis/dist/callables.d.ts +8 -0
  99. package/node_modules/@elench/ts-analysis/dist/callables.d.ts.map +1 -0
  100. package/node_modules/@elench/ts-analysis/dist/callables.js +126 -0
  101. package/node_modules/@elench/ts-analysis/dist/callables.js.map +1 -0
  102. package/node_modules/@elench/ts-analysis/dist/exports.d.ts +6 -0
  103. package/node_modules/@elench/ts-analysis/dist/exports.d.ts.map +1 -0
  104. package/node_modules/@elench/ts-analysis/dist/exports.js +70 -0
  105. package/node_modules/@elench/ts-analysis/dist/exports.js.map +1 -0
  106. package/node_modules/@elench/ts-analysis/dist/index.d.ts +10 -0
  107. package/node_modules/@elench/ts-analysis/dist/index.d.ts.map +1 -0
  108. package/node_modules/@elench/ts-analysis/{src/index.mjs → dist/index.js} +9 -14
  109. package/node_modules/@elench/ts-analysis/dist/index.js.map +1 -0
  110. package/node_modules/@elench/ts-analysis/dist/jsx.d.ts +9 -0
  111. package/node_modules/@elench/ts-analysis/dist/jsx.d.ts.map +1 -0
  112. package/node_modules/@elench/ts-analysis/dist/jsx.js +68 -0
  113. package/node_modules/@elench/ts-analysis/dist/jsx.js.map +1 -0
  114. package/node_modules/@elench/ts-analysis/dist/project.d.ts +5 -0
  115. package/node_modules/@elench/ts-analysis/dist/project.d.ts.map +1 -0
  116. package/node_modules/@elench/ts-analysis/dist/project.js +90 -0
  117. package/node_modules/@elench/ts-analysis/dist/project.js.map +1 -0
  118. package/node_modules/@elench/ts-analysis/dist/requests.d.ts +6 -0
  119. package/node_modules/@elench/ts-analysis/dist/requests.d.ts.map +1 -0
  120. package/node_modules/@elench/ts-analysis/dist/requests.js +140 -0
  121. package/node_modules/@elench/ts-analysis/dist/requests.js.map +1 -0
  122. package/node_modules/@elench/ts-analysis/dist/resolution.d.ts +4 -0
  123. package/node_modules/@elench/ts-analysis/dist/resolution.d.ts.map +1 -0
  124. package/node_modules/@elench/ts-analysis/dist/resolution.js +53 -0
  125. package/node_modules/@elench/ts-analysis/dist/resolution.js.map +1 -0
  126. package/node_modules/@elench/ts-analysis/dist/shared.d.ts +6 -0
  127. package/node_modules/@elench/ts-analysis/dist/shared.d.ts.map +1 -0
  128. package/node_modules/@elench/ts-analysis/dist/shared.js +31 -0
  129. package/node_modules/@elench/ts-analysis/dist/shared.js.map +1 -0
  130. package/node_modules/@elench/ts-analysis/dist/syntax.d.ts +7 -0
  131. package/node_modules/@elench/ts-analysis/dist/syntax.d.ts.map +1 -0
  132. package/node_modules/@elench/ts-analysis/dist/syntax.js +27 -0
  133. package/node_modules/@elench/ts-analysis/dist/syntax.js.map +1 -0
  134. package/node_modules/@elench/ts-analysis/dist/types.d.ts +58 -0
  135. package/node_modules/@elench/ts-analysis/dist/types.d.ts.map +1 -0
  136. package/node_modules/@elench/ts-analysis/dist/types.js +2 -0
  137. package/node_modules/@elench/ts-analysis/dist/types.js.map +1 -0
  138. package/node_modules/@elench/ts-analysis/package.json +18 -2
  139. package/node_modules/typescript/LICENSE.txt +55 -0
  140. package/node_modules/typescript/README.md +50 -0
  141. package/node_modules/typescript/SECURITY.md +41 -0
  142. package/node_modules/typescript/ThirdPartyNoticeText.txt +193 -0
  143. package/node_modules/typescript/bin/tsc +2 -0
  144. package/node_modules/typescript/bin/tsserver +2 -0
  145. package/node_modules/typescript/lib/_tsc.js +133818 -0
  146. package/node_modules/typescript/lib/_tsserver.js +659 -0
  147. package/node_modules/typescript/lib/_typingsInstaller.js +222 -0
  148. package/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +2122 -0
  149. package/node_modules/typescript/lib/de/diagnosticMessages.generated.json +2122 -0
  150. package/node_modules/typescript/lib/es/diagnosticMessages.generated.json +2122 -0
  151. package/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +2122 -0
  152. package/node_modules/typescript/lib/it/diagnosticMessages.generated.json +2122 -0
  153. package/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +2122 -0
  154. package/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +2122 -0
  155. package/node_modules/typescript/lib/lib.d.ts +22 -0
  156. package/node_modules/typescript/lib/lib.decorators.d.ts +384 -0
  157. package/node_modules/typescript/lib/lib.decorators.legacy.d.ts +22 -0
  158. package/node_modules/typescript/lib/lib.dom.asynciterable.d.ts +41 -0
  159. package/node_modules/typescript/lib/lib.dom.d.ts +39429 -0
  160. package/node_modules/typescript/lib/lib.dom.iterable.d.ts +571 -0
  161. package/node_modules/typescript/lib/lib.es2015.collection.d.ts +147 -0
  162. package/node_modules/typescript/lib/lib.es2015.core.d.ts +597 -0
  163. package/node_modules/typescript/lib/lib.es2015.d.ts +28 -0
  164. package/node_modules/typescript/lib/lib.es2015.generator.d.ts +77 -0
  165. package/node_modules/typescript/lib/lib.es2015.iterable.d.ts +605 -0
  166. package/node_modules/typescript/lib/lib.es2015.promise.d.ts +81 -0
  167. package/node_modules/typescript/lib/lib.es2015.proxy.d.ts +128 -0
  168. package/node_modules/typescript/lib/lib.es2015.reflect.d.ts +144 -0
  169. package/node_modules/typescript/lib/lib.es2015.symbol.d.ts +46 -0
  170. package/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts +326 -0
  171. package/node_modules/typescript/lib/lib.es2016.array.include.d.ts +116 -0
  172. package/node_modules/typescript/lib/lib.es2016.d.ts +21 -0
  173. package/node_modules/typescript/lib/lib.es2016.full.d.ts +23 -0
  174. package/node_modules/typescript/lib/lib.es2016.intl.d.ts +31 -0
  175. package/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts +21 -0
  176. package/node_modules/typescript/lib/lib.es2017.d.ts +26 -0
  177. package/node_modules/typescript/lib/lib.es2017.date.d.ts +31 -0
  178. package/node_modules/typescript/lib/lib.es2017.full.d.ts +23 -0
  179. package/node_modules/typescript/lib/lib.es2017.intl.d.ts +44 -0
  180. package/node_modules/typescript/lib/lib.es2017.object.d.ts +49 -0
  181. package/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts +135 -0
  182. package/node_modules/typescript/lib/lib.es2017.string.d.ts +45 -0
  183. package/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts +53 -0
  184. package/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts +77 -0
  185. package/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts +53 -0
  186. package/node_modules/typescript/lib/lib.es2018.d.ts +24 -0
  187. package/node_modules/typescript/lib/lib.es2018.full.d.ts +24 -0
  188. package/node_modules/typescript/lib/lib.es2018.intl.d.ts +83 -0
  189. package/node_modules/typescript/lib/lib.es2018.promise.d.ts +30 -0
  190. package/node_modules/typescript/lib/lib.es2018.regexp.d.ts +37 -0
  191. package/node_modules/typescript/lib/lib.es2019.array.d.ts +79 -0
  192. package/node_modules/typescript/lib/lib.es2019.d.ts +24 -0
  193. package/node_modules/typescript/lib/lib.es2019.full.d.ts +24 -0
  194. package/node_modules/typescript/lib/lib.es2019.intl.d.ts +23 -0
  195. package/node_modules/typescript/lib/lib.es2019.object.d.ts +33 -0
  196. package/node_modules/typescript/lib/lib.es2019.string.d.ts +37 -0
  197. package/node_modules/typescript/lib/lib.es2019.symbol.d.ts +24 -0
  198. package/node_modules/typescript/lib/lib.es2020.bigint.d.ts +765 -0
  199. package/node_modules/typescript/lib/lib.es2020.d.ts +27 -0
  200. package/node_modules/typescript/lib/lib.es2020.date.d.ts +42 -0
  201. package/node_modules/typescript/lib/lib.es2020.full.d.ts +24 -0
  202. package/node_modules/typescript/lib/lib.es2020.intl.d.ts +474 -0
  203. package/node_modules/typescript/lib/lib.es2020.number.d.ts +28 -0
  204. package/node_modules/typescript/lib/lib.es2020.promise.d.ts +47 -0
  205. package/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts +99 -0
  206. package/node_modules/typescript/lib/lib.es2020.string.d.ts +44 -0
  207. package/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts +41 -0
  208. package/node_modules/typescript/lib/lib.es2021.d.ts +23 -0
  209. package/node_modules/typescript/lib/lib.es2021.full.d.ts +24 -0
  210. package/node_modules/typescript/lib/lib.es2021.intl.d.ts +166 -0
  211. package/node_modules/typescript/lib/lib.es2021.promise.d.ts +48 -0
  212. package/node_modules/typescript/lib/lib.es2021.string.d.ts +33 -0
  213. package/node_modules/typescript/lib/lib.es2021.weakref.d.ts +78 -0
  214. package/node_modules/typescript/lib/lib.es2022.array.d.ts +121 -0
  215. package/node_modules/typescript/lib/lib.es2022.d.ts +25 -0
  216. package/node_modules/typescript/lib/lib.es2022.error.d.ts +75 -0
  217. package/node_modules/typescript/lib/lib.es2022.full.d.ts +24 -0
  218. package/node_modules/typescript/lib/lib.es2022.intl.d.ts +145 -0
  219. package/node_modules/typescript/lib/lib.es2022.object.d.ts +26 -0
  220. package/node_modules/typescript/lib/lib.es2022.regexp.d.ts +39 -0
  221. package/node_modules/typescript/lib/lib.es2022.string.d.ts +25 -0
  222. package/node_modules/typescript/lib/lib.es2023.array.d.ts +924 -0
  223. package/node_modules/typescript/lib/lib.es2023.collection.d.ts +21 -0
  224. package/node_modules/typescript/lib/lib.es2023.d.ts +22 -0
  225. package/node_modules/typescript/lib/lib.es2023.full.d.ts +24 -0
  226. package/node_modules/typescript/lib/lib.es2023.intl.d.ts +56 -0
  227. package/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts +65 -0
  228. package/node_modules/typescript/lib/lib.es2024.collection.d.ts +29 -0
  229. package/node_modules/typescript/lib/lib.es2024.d.ts +26 -0
  230. package/node_modules/typescript/lib/lib.es2024.full.d.ts +24 -0
  231. package/node_modules/typescript/lib/lib.es2024.object.d.ts +29 -0
  232. package/node_modules/typescript/lib/lib.es2024.promise.d.ts +35 -0
  233. package/node_modules/typescript/lib/lib.es2024.regexp.d.ts +25 -0
  234. package/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts +68 -0
  235. package/node_modules/typescript/lib/lib.es2024.string.d.ts +29 -0
  236. package/node_modules/typescript/lib/lib.es5.d.ts +4601 -0
  237. package/node_modules/typescript/lib/lib.es6.d.ts +23 -0
  238. package/node_modules/typescript/lib/lib.esnext.array.d.ts +35 -0
  239. package/node_modules/typescript/lib/lib.esnext.collection.d.ts +96 -0
  240. package/node_modules/typescript/lib/lib.esnext.d.ts +29 -0
  241. package/node_modules/typescript/lib/lib.esnext.decorators.d.ts +28 -0
  242. package/node_modules/typescript/lib/lib.esnext.disposable.d.ts +193 -0
  243. package/node_modules/typescript/lib/lib.esnext.error.d.ts +24 -0
  244. package/node_modules/typescript/lib/lib.esnext.float16.d.ts +445 -0
  245. package/node_modules/typescript/lib/lib.esnext.full.d.ts +24 -0
  246. package/node_modules/typescript/lib/lib.esnext.intl.d.ts +21 -0
  247. package/node_modules/typescript/lib/lib.esnext.iterator.d.ts +148 -0
  248. package/node_modules/typescript/lib/lib.esnext.promise.d.ts +34 -0
  249. package/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts +25 -0
  250. package/node_modules/typescript/lib/lib.scripthost.d.ts +322 -0
  251. package/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts +41 -0
  252. package/node_modules/typescript/lib/lib.webworker.d.ts +13150 -0
  253. package/node_modules/typescript/lib/lib.webworker.importscripts.d.ts +23 -0
  254. package/node_modules/typescript/lib/lib.webworker.iterable.d.ts +340 -0
  255. package/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +2122 -0
  256. package/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +2122 -0
  257. package/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +2122 -0
  258. package/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +2122 -0
  259. package/node_modules/typescript/lib/tsc.js +8 -0
  260. package/node_modules/typescript/lib/tsserver.js +8 -0
  261. package/node_modules/typescript/lib/tsserverlibrary.d.ts +17 -0
  262. package/node_modules/typescript/lib/tsserverlibrary.js +21 -0
  263. package/node_modules/typescript/lib/typesMap.json +497 -0
  264. package/node_modules/typescript/lib/typescript.d.ts +11437 -0
  265. package/node_modules/typescript/lib/typescript.js +200276 -0
  266. package/node_modules/typescript/lib/typingsInstaller.js +8 -0
  267. package/node_modules/typescript/lib/watchGuard.js +53 -0
  268. package/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +2122 -0
  269. package/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +2122 -0
  270. package/node_modules/typescript/package.json +120 -0
  271. package/package.json +12 -9
  272. package/lib/coverage/fs-walk.mjs +0 -64
  273. package/node_modules/@elench/next-analysis/src/api-routes.mjs +0 -81
  274. package/node_modules/@elench/next-analysis/src/api-routes.test.mjs +0 -22
  275. package/node_modules/@elench/next-analysis/src/app-root.mjs +0 -7
  276. package/node_modules/@elench/next-analysis/src/backend-links.mjs +0 -31
  277. package/node_modules/@elench/next-analysis/src/index.mjs +0 -21
  278. package/node_modules/@elench/next-analysis/src/pages.mjs +0 -68
  279. package/node_modules/@elench/next-analysis/src/project.mjs +0 -94
  280. package/node_modules/@elench/next-analysis/src/project.test.mjs +0 -35
  281. package/node_modules/@elench/next-analysis/src/route-tree.mjs +0 -621
  282. package/node_modules/@elench/next-analysis/src/routes.mjs +0 -41
  283. package/node_modules/@elench/next-analysis/src/routes.test.mjs +0 -25
  284. package/node_modules/@elench/next-analysis/src/server-actions.mjs +0 -53
  285. package/node_modules/@elench/next-analysis/src/server-actions.test.mjs +0 -37
  286. package/node_modules/@elench/next-analysis/src/shared.mjs +0 -209
  287. package/node_modules/@elench/next-analysis/src/swc.mjs +0 -388
  288. package/node_modules/@elench/testkit-bridge/src/index.mjs +0 -583
  289. package/node_modules/@elench/testkit-bridge/src/index.test.mjs +0 -409
  290. package/node_modules/@elench/testkit-protocol/src/index.d.ts +0 -231
  291. package/node_modules/@elench/testkit-protocol/src/index.mjs +0 -265
  292. package/node_modules/@elench/testkit-protocol/src/index.test.mjs +0 -242
  293. package/node_modules/@elench/ts-analysis/src/callables.mjs +0 -135
  294. package/node_modules/@elench/ts-analysis/src/callables.test.mjs +0 -55
  295. package/node_modules/@elench/ts-analysis/src/exports.mjs +0 -69
  296. package/node_modules/@elench/ts-analysis/src/exports.test.mjs +0 -50
  297. package/node_modules/@elench/ts-analysis/src/jsx.mjs +0 -69
  298. package/node_modules/@elench/ts-analysis/src/jsx.test.mjs +0 -43
  299. package/node_modules/@elench/ts-analysis/src/project.mjs +0 -100
  300. package/node_modules/@elench/ts-analysis/src/project.test.mjs +0 -54
  301. package/node_modules/@elench/ts-analysis/src/requests.mjs +0 -141
  302. package/node_modules/@elench/ts-analysis/src/requests.test.mjs +0 -35
  303. package/node_modules/@elench/ts-analysis/src/resolution.mjs +0 -53
  304. package/node_modules/@elench/ts-analysis/src/shared.mjs +0 -32
  305. package/node_modules/@elench/ts-analysis/src/syntax.mjs +0 -27
@@ -1,621 +0,0 @@
1
- import path from "path";
2
- import {
3
- collectExportedCallables,
4
- collectImports,
5
- collectJsxAttributes,
6
- collectScopeLocalFunctions,
7
- collectTopLevelFunctions,
8
- extractJsxLabel,
9
- extractLineNumber,
10
- parseModule,
11
- walkJsx,
12
- } from "./swc.mjs";
13
- import {
14
- createDiagnostic,
15
- dedupeById,
16
- dedupeRefs,
17
- dedupeRequests,
18
- HTTP_WRAPPER_METHODS,
19
- humanizeTagName,
20
- normalizePath,
21
- SURFACE_COMPONENT_NAMES,
22
- } from "./shared.mjs";
23
-
24
- export function analyzeRouteTree({ project, routeOrPageFile, diagnostics = [] }) {
25
- const pageEntry = resolvePageEntry(project, routeOrPageFile);
26
- if (!pageEntry) return null;
27
-
28
- const routeTree = {
29
- id: `route-tree:${pageEntry.route}`,
30
- kind: "route_tree",
31
- route: pageEntry.route,
32
- pageFile: pageEntry.filePath,
33
- rootFiles: [...pageEntry.rootFiles],
34
- surfaces: [],
35
- actions: [],
36
- requests: [],
37
- serverActionRefs: [],
38
- reachableModules: [],
39
- diagnostics: [],
40
- };
41
-
42
- const analyzerContext = {
43
- project,
44
- route: pageEntry.route,
45
- pageId: pageEntry.id,
46
- rootFiles: pageEntry.rootFiles,
47
- routeTree,
48
- diagnostics,
49
- visitedExports: new Set(),
50
- visitedComponentImports: new Set(),
51
- };
52
-
53
- for (const rootFile of pageEntry.rootFiles) {
54
- const moduleInfo = loadModuleInfo(project, rootFile);
55
- if (!moduleInfo) continue;
56
- analyzeModuleExport(moduleInfo, "default", analyzerContext);
57
- }
58
-
59
- routeTree.surfaces = dedupeById(routeTree.surfaces);
60
- routeTree.actions = dedupeById(routeTree.actions);
61
- routeTree.requests = dedupeRequests(routeTree.requests);
62
- routeTree.serverActionRefs = dedupeRefs(routeTree.serverActionRefs);
63
- routeTree.reachableModules = [...new Set(routeTree.reachableModules)].sort();
64
- routeTree.diagnostics = dedupeDiagnostics(routeTree.diagnostics);
65
- return routeTree;
66
- }
67
-
68
- function resolvePageEntry(project, routeOrPageFile) {
69
- const normalized = normalizePath(routeOrPageFile);
70
- const pages = project.discoverPages();
71
- return pages.find((page) => page.route === normalized || page.filePath === normalized) || null;
72
- }
73
-
74
- function loadModuleInfo(project, relativeFilePath) {
75
- const existing = project.loadModule(relativeFilePath);
76
- if (existing) return existing;
77
- const content = project.readSourceFile(relativeFilePath);
78
- if (!content) return null;
79
- const ast = parseModule(content, relativeFilePath);
80
- const imports = collectImports(ast, {
81
- rootDir: project.rootDir,
82
- filePath: relativeFilePath,
83
- readSourceFile: (absolutePath) => {
84
- try {
85
- return project.readSourceFile(normalizePath(path.relative(project.rootDir, absolutePath)));
86
- } catch {
87
- return null;
88
- }
89
- },
90
- });
91
- const topLevelFunctions = collectTopLevelFunctions(ast);
92
- const { exports, defaultExport } = collectExportedCallables(ast, topLevelFunctions);
93
- const moduleInfo = {
94
- filePath: relativeFilePath,
95
- content,
96
- ast,
97
- imports,
98
- topLevelFunctions,
99
- exports,
100
- defaultExport,
101
- };
102
- project.setModule(relativeFilePath, moduleInfo);
103
- return moduleInfo;
104
- }
105
-
106
- function analyzeModuleExport(moduleInfo, exportName, context) {
107
- if (!moduleInfo) return;
108
- const visitKey = `${moduleInfo.filePath}#${exportName}`;
109
- if (context.visitedExports.has(visitKey)) return;
110
- context.visitedExports.add(visitKey);
111
- pushReachableModule(context, moduleInfo.filePath);
112
-
113
- const callable = resolveModuleExportCallable(moduleInfo, exportName);
114
- if (!callable) {
115
- pushDiagnostic(context, {
116
- level: "info",
117
- code: "missing-export-callable",
118
- filePath: moduleInfo.filePath,
119
- message: `No callable export "${exportName}" found in "${moduleInfo.filePath}".`,
120
- });
121
- return;
122
- }
123
-
124
- const localFunctions = new Map(moduleInfo.topLevelFunctions);
125
- for (const [name, node] of collectScopeLocalFunctions(callable)) {
126
- localFunctions.set(name, node);
127
- }
128
-
129
- const directAnalysis = analyzeCallableLikeNode(callable, {
130
- moduleInfo,
131
- localFunctions,
132
- context,
133
- }, new Set(), { skipNestedFunctions: true });
134
-
135
- for (const request of directAnalysis.requests) {
136
- context.routeTree.requests.push({
137
- ...request,
138
- ownerId: context.pageId,
139
- ownerKind: "page",
140
- route: context.route,
141
- filePath: moduleInfo.filePath,
142
- });
143
- }
144
- for (const ref of directAnalysis.serverActionRefs) {
145
- context.routeTree.serverActionRefs.push({
146
- ...ref,
147
- ownerId: context.pageId,
148
- ownerKind: "page",
149
- });
150
- }
151
-
152
- walkJsx(callable, (element, formContext) => {
153
- const descriptor = describeSurface(moduleInfo, element, formContext);
154
- if (descriptor) {
155
- const surfaceId = descriptor.targetHint?.value
156
- ? `surface:${context.route}:${descriptor.targetHint.value}`
157
- : `surface:${context.route}:${moduleInfo.filePath}:${descriptor.tagName}:${descriptor.line}`;
158
- const surface = {
159
- id: surfaceId,
160
- kind: "ui_surface",
161
- route: context.route,
162
- filePath: moduleInfo.filePath,
163
- label: descriptor.label,
164
- line: descriptor.line,
165
- surfaceKind: descriptor.surfaceKind,
166
- tagName: descriptor.tagName,
167
- ...(descriptor.actionBinding ? { actionId: `action:${context.route}:${descriptor.actionBinding.key}` } : {}),
168
- ...(descriptor.targetHint ? { targetHint: descriptor.targetHint } : {}),
169
- };
170
- context.routeTree.surfaces.push(surface);
171
-
172
- if (descriptor.actionBinding) {
173
- const actionId = `action:${context.route}:${descriptor.actionBinding.key}`;
174
- context.routeTree.actions.push({
175
- id: actionId,
176
- kind: "ui_action",
177
- route: context.route,
178
- filePath: moduleInfo.filePath,
179
- line: descriptor.line,
180
- label: descriptor.actionBinding.label,
181
- bindingKind: descriptor.actionBinding.kind,
182
- actionProp: descriptor.actionBinding.actionProp,
183
- });
184
-
185
- const bindingAnalysis = analyzeActionBinding(descriptor.actionBinding, {
186
- moduleInfo,
187
- localFunctions,
188
- context,
189
- });
190
-
191
- for (const request of bindingAnalysis.requests) {
192
- context.routeTree.requests.push({
193
- ...request,
194
- ownerId: actionId,
195
- ownerKind: "action",
196
- route: context.route,
197
- filePath: moduleInfo.filePath,
198
- });
199
- }
200
- for (const ref of bindingAnalysis.serverActionRefs) {
201
- context.routeTree.serverActionRefs.push({
202
- ...ref,
203
- ownerId: actionId,
204
- ownerKind: "action",
205
- });
206
- }
207
- }
208
-
209
- return descriptor.formContext ?? formContext;
210
- }
211
-
212
- const componentRef = resolveImportedComponentRef(element, moduleInfo.imports);
213
- if (componentRef) analyzeImportedComponent(componentRef, context);
214
- return formContext;
215
- });
216
- }
217
-
218
- function describeSurface(moduleInfo, node, inheritedFormContext) {
219
- const opening = node.type === "JSXElement" ? node.opening : node;
220
- const tagName = opening.name?.value || null;
221
- if (!SURFACE_COMPONENT_NAMES.has(tagName)) return null;
222
- const attributes = collectJsxAttributes(opening);
223
- const targetValue = attributes["data-testid"]?.stringValue || null;
224
- const role = tagName === "form" || tagName === "Form" ? "form" : tagName === "a" || tagName === "Link" ? "link" : "button";
225
- const surfaceKind =
226
- role === "form" ? "form" : role === "link" ? "link" : attributes.type?.stringValue === "submit" ? "submit" : "control";
227
- const ownBinding =
228
- buildActionBinding(attributes.onClick?.expression, "onClick") ||
229
- buildActionBinding(attributes.action?.expression, "action") ||
230
- buildActionBinding(attributes.onSubmit?.expression, "onSubmit");
231
- const inheritedSubmitBinding =
232
- !ownBinding && isSubmitSurface(tagName, attributes) && inheritedFormContext?.actionBinding
233
- ? inheritedFormContext.actionBinding
234
- : null;
235
- const actionBinding = ownBinding || inheritedSubmitBinding;
236
-
237
- if (!actionBinding && !targetValue) {
238
- return tagName === "form" || tagName === "Form"
239
- ? { formContext: buildFormContext(attributes) }
240
- : null;
241
- }
242
-
243
- const label =
244
- extractJsxLabel(node, attributes) ||
245
- actionBinding?.label ||
246
- targetValue ||
247
- humanizeTagName(tagName);
248
- const line = extractLineNumber(moduleInfo.content, node);
249
- return {
250
- tagName,
251
- surfaceKind,
252
- label,
253
- line,
254
- targetHint: targetValue
255
- ? {
256
- kind: "testId",
257
- value: targetValue,
258
- label,
259
- confidence: "high",
260
- }
261
- : null,
262
- actionBinding,
263
- formContext: buildFormContext(attributes, actionBinding),
264
- };
265
- }
266
-
267
- function buildFormContext(attributes, explicitBinding = null) {
268
- const actionBinding =
269
- explicitBinding ||
270
- buildActionBinding(attributes.action?.expression, "action") ||
271
- buildActionBinding(attributes.onSubmit?.expression, "onSubmit");
272
- return actionBinding ? { actionBinding } : null;
273
- }
274
-
275
- function buildActionBinding(expression, actionProp) {
276
- if (!expression) return null;
277
- if (expression.type === "Identifier") {
278
- return {
279
- kind: "identifier",
280
- actionProp,
281
- key: expression.value,
282
- label: expression.value,
283
- node: expression,
284
- confidence: actionProp === "action" ? "high" : "medium",
285
- };
286
- }
287
- if (expression.type === "ArrowFunctionExpression" || expression.type === "FunctionExpression") {
288
- return {
289
- kind: "inline",
290
- actionProp,
291
- key: `inline:${expression.span?.start || 0}`,
292
- label: actionProp === "action" ? "Inline action" : "Inline handler",
293
- node: expression,
294
- confidence: "medium",
295
- };
296
- }
297
- return null;
298
- }
299
-
300
- function analyzeActionBinding(binding, options) {
301
- if (!binding) return emptyActionAnalysis();
302
- if (binding.kind === "identifier" && binding.node?.type === "Identifier") {
303
- const identifier = binding.node.value;
304
- const imported = options.moduleInfo.imports.get(identifier);
305
- if (imported?.isServerAction) {
306
- return {
307
- requests: [],
308
- serverActionRefs: [{
309
- exportKey: `${imported.resolvedFilePath}#${imported.importedName}`,
310
- confidence: "high",
311
- }],
312
- };
313
- }
314
-
315
- if (imported?.resolvedFilePath) {
316
- return analyzeImportedCallable(imported, options, new Set([imported.resolvedFilePath]));
317
- }
318
-
319
- const localFunction = options.localFunctions.get(identifier);
320
- if (!localFunction) return emptyActionAnalysis();
321
- return analyzeCallableLikeNode(localFunction, options, new Set([identifier]));
322
- }
323
-
324
- if (binding.kind === "inline") {
325
- return analyzeCallableLikeNode(binding.node, options, new Set());
326
- }
327
-
328
- return emptyActionAnalysis();
329
- }
330
-
331
- function analyzeImportedCallable(imported, options, visited) {
332
- if (!imported?.resolvedFilePath || imported.isServerAction || visited.has(imported.resolvedFilePath)) {
333
- return emptyActionAnalysis();
334
- }
335
- const moduleInfo = loadModuleInfo(options.context.project, imported.resolvedFilePath);
336
- if (!moduleInfo) return emptyActionAnalysis();
337
- pushReachableModule(options.context, imported.resolvedFilePath);
338
- const callable = resolveModuleExportCallable(moduleInfo, imported.importedName);
339
- if (!callable) return emptyActionAnalysis();
340
-
341
- const localFunctions = new Map(moduleInfo.topLevelFunctions);
342
- for (const [name, node] of collectScopeLocalFunctions(callable)) {
343
- localFunctions.set(name, node);
344
- }
345
-
346
- const nextVisited = new Set(visited);
347
- nextVisited.add(imported.resolvedFilePath);
348
- return analyzeCallableLikeNode(callable, {
349
- moduleInfo,
350
- localFunctions,
351
- context: options.context,
352
- }, nextVisited);
353
- }
354
-
355
- function analyzeCallableLikeNode(node, options, visited = new Set(), settings = {}) {
356
- const requests = [];
357
- const serverActionRefs = [];
358
-
359
- walkCallableNode(node, (child) => {
360
- if (
361
- settings.skipNestedFunctions &&
362
- child !== node &&
363
- (child.type === "ArrowFunctionExpression" || child.type === "FunctionExpression" || child.type === "FunctionDeclaration")
364
- ) {
365
- return "skip";
366
- }
367
-
368
- if (child.type === "CallExpression") {
369
- const request = resolveHttpRequestCall(child);
370
- if (request) {
371
- requests.push(request);
372
- return;
373
- }
374
-
375
- const serverAction = resolveServerActionCall(child, options.moduleInfo.imports);
376
- if (serverAction) {
377
- serverActionRefs.push(serverAction);
378
- return;
379
- }
380
-
381
- const localCall = resolveLocalFunctionCall(child, options, visited);
382
- if (localCall) {
383
- requests.push(...localCall.requests);
384
- serverActionRefs.push(...localCall.serverActionRefs);
385
- return;
386
- }
387
-
388
- const importedCall = resolveImportedLocalCall(child, options, visited);
389
- if (importedCall) {
390
- requests.push(...importedCall.requests);
391
- serverActionRefs.push(...importedCall.serverActionRefs);
392
- return;
393
- }
394
-
395
- for (const callbackAnalysis of resolveExecutedCallbackAnalyses(child, options, visited)) {
396
- requests.push(...callbackAnalysis.requests);
397
- serverActionRefs.push(...callbackAnalysis.serverActionRefs);
398
- }
399
- }
400
- });
401
-
402
- return {
403
- requests: dedupeRequests(requests),
404
- serverActionRefs: dedupeRefs(serverActionRefs),
405
- };
406
- }
407
-
408
- function resolveLocalFunctionCall(callExpression, options, visited) {
409
- const callee = callExpression.callee;
410
- if (callee?.type !== "Identifier") return null;
411
- const functionName = callee.value;
412
- const localFunction = options.localFunctions.get(functionName);
413
- if (!localFunction || visited.has(functionName)) return null;
414
- const nextVisited = new Set(visited);
415
- nextVisited.add(functionName);
416
- return analyzeCallableLikeNode(localFunction, options, nextVisited);
417
- }
418
-
419
- function resolveImportedLocalCall(callExpression, options, visited) {
420
- const callee = callExpression.callee;
421
- if (callee?.type !== "Identifier") return null;
422
- const imported = options.moduleInfo.imports.get(callee.value);
423
- if (!imported?.resolvedFilePath || imported.isServerAction) return null;
424
- return analyzeImportedCallable(imported, options, visited);
425
- }
426
-
427
- function resolveExecutedCallbackAnalyses(callExpression, options, visited) {
428
- const callbacks = [];
429
- const callee = callExpression.callee;
430
- if (callee?.type === "Identifier" && (callee.value === "useEffect" || callee.value === "useLayoutEffect")) {
431
- const callbackArg = callExpression.arguments?.[0]?.expression;
432
- if (callbackArg && (callbackArg.type === "ArrowFunctionExpression" || callbackArg.type === "FunctionExpression")) {
433
- callbacks.push(analyzeCallableLikeNode(callbackArg, options, new Set(visited)));
434
- }
435
- }
436
- return callbacks;
437
- }
438
-
439
- function resolveServerActionCall(callExpression, imports) {
440
- const callee = callExpression.callee;
441
- if (callee?.type !== "Identifier") return null;
442
- const imported = imports.get(callee.value);
443
- if (!imported?.isServerAction) return null;
444
- return {
445
- exportKey: `${imported.resolvedFilePath}#${imported.importedName}`,
446
- confidence: "high",
447
- };
448
- }
449
-
450
- function resolveHttpRequestCall(callExpression) {
451
- const callee = callExpression.callee;
452
- if (callee?.type === "Identifier") {
453
- if (callee.value === "fetch") {
454
- const path = extractRequestPath(callExpression.arguments?.[0]?.expression);
455
- if (!path || !path.startsWith("/api/")) return null;
456
- const method = extractFetchMethod(callExpression.arguments?.[1]?.expression) || "GET";
457
- return {
458
- id: `request:${callee.span?.start || 0}:${method}:${path}`,
459
- method,
460
- path,
461
- confidence: "high",
462
- };
463
- }
464
-
465
- if (callee.value === "rawReq") {
466
- const methodLiteral = extractStringLiteral(callExpression.arguments?.[0]?.expression);
467
- const path = extractRequestPath(callExpression.arguments?.[1]?.expression);
468
- if (!methodLiteral || !path) return null;
469
- return {
470
- id: `request:${callee.span?.start || 0}:${methodLiteral.toUpperCase()}:${path}`,
471
- method: methodLiteral.toUpperCase(),
472
- path,
473
- confidence: "high",
474
- };
475
- }
476
-
477
- const wrapperMethod = HTTP_WRAPPER_METHODS[callee.value];
478
- if (wrapperMethod) {
479
- const path = extractRequestPath(callExpression.arguments?.[0]?.expression);
480
- if (!path || !path.startsWith("/api/")) return null;
481
- return {
482
- id: `request:${callee.span?.start || 0}:${wrapperMethod}:${path}`,
483
- method: wrapperMethod,
484
- path,
485
- confidence: "high",
486
- };
487
- }
488
- }
489
-
490
- if (callee?.type === "MemberExpression" && callee.property?.type === "Identifier" && callee.property.value === "rawReq") {
491
- const methodLiteral = extractStringLiteral(callExpression.arguments?.[0]?.expression);
492
- const path = extractRequestPath(callExpression.arguments?.[1]?.expression);
493
- if (!methodLiteral || !path) return null;
494
- return {
495
- id: `request:${callee.span?.start || 0}:${methodLiteral.toUpperCase()}:${path}`,
496
- method: methodLiteral.toUpperCase(),
497
- path,
498
- confidence: "high",
499
- };
500
- }
501
-
502
- return null;
503
- }
504
-
505
- function resolveImportedComponentRef(node, imports) {
506
- const opening = node.type === "JSXElement" ? node.opening : node;
507
- const tagName = opening.name?.value || null;
508
- if (!tagName || !/^[A-Z]/u.test(tagName)) return null;
509
- const imported = imports.get(tagName);
510
- if (!imported?.resolvedFilePath || imported.isServerAction) return null;
511
- if (imported.resolvedFilePath.includes("/components/ui/")) return null;
512
- return {
513
- filePath: imported.resolvedFilePath,
514
- exportName: imported.importedName,
515
- };
516
- }
517
-
518
- function analyzeImportedComponent(componentRef, context) {
519
- const key = `${componentRef.filePath}#${componentRef.exportName}`;
520
- if (context.visitedComponentImports.has(key)) return;
521
- context.visitedComponentImports.add(key);
522
- const moduleInfo = loadModuleInfo(context.project, componentRef.filePath);
523
- if (!moduleInfo) return;
524
- analyzeModuleExport(moduleInfo, componentRef.exportName, context);
525
- }
526
-
527
- function resolveModuleExportCallable(moduleInfo, exportName) {
528
- if (exportName === "default") return moduleInfo.defaultExport || null;
529
- const direct = moduleInfo.exports.get(exportName);
530
- if (direct?.type === "Identifier") {
531
- return moduleInfo.topLevelFunctions.get(direct.value) || null;
532
- }
533
- return direct || null;
534
- }
535
-
536
- function pushReachableModule(context, filePath) {
537
- context.routeTree.reachableModules.push(filePath);
538
- }
539
-
540
- function pushDiagnostic(context, diagnostic) {
541
- const entry = createDiagnostic(diagnostic);
542
- context.routeTree.diagnostics.push(entry);
543
- context.diagnostics.push(entry);
544
- }
545
-
546
- function dedupeDiagnostics(entries) {
547
- const seen = new Set();
548
- return entries.filter((entry) => {
549
- const key = `${entry.code}:${entry.filePath || ""}:${entry.line || ""}:${entry.message}`;
550
- if (seen.has(key)) return false;
551
- seen.add(key);
552
- return true;
553
- });
554
- }
555
-
556
- function walkCallableNode(node, visitor) {
557
- const visit = (current) => {
558
- const action = visitor(current);
559
- if (action === "skip") return;
560
- for (const value of Object.values(current || {})) {
561
- if (!value) continue;
562
- if (Array.isArray(value)) {
563
- for (const item of value) {
564
- if (item && typeof item === "object" && item.type) visit(item);
565
- }
566
- } else if (value && typeof value === "object" && value.type) {
567
- visit(value);
568
- }
569
- }
570
- };
571
-
572
- if (node?.body?.type === "BlockStatement") {
573
- visit(node.body);
574
- } else if (node?.type) {
575
- visit(node);
576
- }
577
- }
578
-
579
- function extractStringLiteral(node) {
580
- if (!node) return null;
581
- if (node.type === "StringLiteral") return node.value;
582
- if (node.type === "TemplateLiteral") {
583
- let value = node.quasis?.[0]?.raw || "";
584
- for (let index = 0; index < (node.expressions || []).length; index += 1) {
585
- value += "[id]";
586
- value += node.quasis?.[index + 1]?.raw || "";
587
- }
588
- return value;
589
- }
590
- return null;
591
- }
592
-
593
- function extractRequestPath(node) {
594
- const literal = extractStringLiteral(node);
595
- if (!literal) return null;
596
- return literal.split("?")[0];
597
- }
598
-
599
- function extractFetchMethod(node) {
600
- if (!node || node.type !== "ObjectExpression") return null;
601
- for (const property of node.properties || []) {
602
- if (property.type !== "KeyValueProperty") continue;
603
- const key = property.key?.value || property.key?.name?.value || property.key?.name;
604
- if (key !== "method") continue;
605
- const value = extractStringLiteral(property.value);
606
- return value ? value.toUpperCase() : null;
607
- }
608
- return null;
609
- }
610
-
611
- function isSubmitSurface(tagName, attributes) {
612
- const type = attributes.type?.stringValue || "";
613
- return tagName === "button" || tagName === "Button" || (tagName === "input" && type === "submit");
614
- }
615
-
616
- function emptyActionAnalysis() {
617
- return {
618
- requests: [],
619
- serverActionRefs: [],
620
- };
621
- }
@@ -1,41 +0,0 @@
1
- import path from "path";
2
- import { normalizePath, normalizeRouteSegments } from "./shared.mjs";
3
-
4
- export function routeFromAppFile(appRoot, filePath) {
5
- const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(appRoot, filePath);
6
- const relative = normalizePath(path.relative(appRoot, absolutePath));
7
- const segments = relative.split("/");
8
- segments.pop();
9
- return normalizeRouteSegments(segments);
10
- }
11
-
12
- export function routeFromApiFile(appRoot, filePath) {
13
- const appRelative = normalizePath(path.relative(appRoot, path.dirname(filePath)));
14
- const segments = appRelative.split("/").filter(Boolean);
15
- if (segments[0] === "api") segments.shift();
16
- return normalizeRouteSegments(segments);
17
- }
18
-
19
- export function requestPathPatternFromLiteral(literal) {
20
- const normalized = String(literal || "").trim();
21
- if (!normalized) return null;
22
- const withoutQuery = normalized.split("?")[0];
23
- const withDynamicSegments = withoutQuery
24
- .replace(/\$\{[^}]+\}/gu, "[id]")
25
- .replace(/__TESTKIT_DYNAMIC_SEGMENT__/gu, "[id]");
26
- return withDynamicSegments.startsWith("/") ? withDynamicSegments : `/${withDynamicSegments}`;
27
- }
28
-
29
- export function routePatternMatches(routePattern, candidatePath) {
30
- const routeSegments = normalizePath(routePattern).split("/").filter(Boolean);
31
- const candidateSegments = normalizePath(candidatePath).split("/").filter(Boolean);
32
- if (routeSegments.length !== candidateSegments.length) return false;
33
- return routeSegments.every((segment, index) => {
34
- if (segment.startsWith("[") && segment.endsWith("]")) return true;
35
- return segment === candidateSegments[index];
36
- });
37
- }
38
-
39
- export function normalizeRequestPathFromRoute(route) {
40
- return route === "/" ? "/api" : `/api${route}`;
41
- }
@@ -1,25 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import {
3
- requestPathPatternFromLiteral,
4
- routeFromApiFile,
5
- routeFromAppFile,
6
- routePatternMatches,
7
- } from "./index.mjs";
8
-
9
- describe("@elench/next-analysis route helpers", () => {
10
- it("derives app and API routes from App Router files", () => {
11
- const appRoot = "/repo/src/app";
12
- expect(routeFromAppFile(appRoot, "/repo/src/app/(dashboard)/projects/page.tsx")).toBe("/projects");
13
- expect(routeFromApiFile(appRoot, "/repo/src/app/api/projects/[projectId]/events/route.ts")).toBe("/projects/[projectId]/events");
14
- });
15
-
16
- it("normalizes dynamic request literals into route patterns", () => {
17
- expect(requestPathPatternFromLiteral("/api/projects/${projectId}/events?limit=100")).toBe("/api/projects/[id]/events");
18
- expect(requestPathPatternFromLiteral("/api/projects/__TESTKIT_DYNAMIC_SEGMENT__/overview")).toBe("/api/projects/[id]/overview");
19
- });
20
-
21
- it("matches dynamic route patterns against concrete paths", () => {
22
- expect(routePatternMatches("/api/projects/[projectId]/events", "/api/projects/123/events")).toBe(true);
23
- expect(routePatternMatches("/api/projects/[projectId]/events", "/api/projects/123/stats")).toBe(false);
24
- });
25
- });