mnehmos.trace.mcp 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 (339) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1662 -0
  3. package/dist/adapters/bootstrap.d.ts +29 -0
  4. package/dist/adapters/bootstrap.d.ts.map +1 -0
  5. package/dist/adapters/bootstrap.js +46 -0
  6. package/dist/adapters/bootstrap.js.map +1 -0
  7. package/dist/adapters/errors.d.ts +94 -0
  8. package/dist/adapters/errors.d.ts.map +1 -0
  9. package/dist/adapters/errors.js +107 -0
  10. package/dist/adapters/errors.js.map +1 -0
  11. package/dist/adapters/graphql/index.d.ts +9 -0
  12. package/dist/adapters/graphql/index.d.ts.map +1 -0
  13. package/dist/adapters/graphql/index.js +9 -0
  14. package/dist/adapters/graphql/index.js.map +1 -0
  15. package/dist/adapters/graphql/sdl-parser.d.ts +74 -0
  16. package/dist/adapters/graphql/sdl-parser.d.ts.map +1 -0
  17. package/dist/adapters/graphql/sdl-parser.js +559 -0
  18. package/dist/adapters/graphql/sdl-parser.js.map +1 -0
  19. package/dist/adapters/grpc/adapter.d.ts +76 -0
  20. package/dist/adapters/grpc/adapter.d.ts.map +1 -0
  21. package/dist/adapters/grpc/adapter.js +362 -0
  22. package/dist/adapters/grpc/adapter.js.map +1 -0
  23. package/dist/adapters/grpc/index.d.ts +10 -0
  24. package/dist/adapters/grpc/index.d.ts.map +1 -0
  25. package/dist/adapters/grpc/index.js +12 -0
  26. package/dist/adapters/grpc/index.js.map +1 -0
  27. package/dist/adapters/grpc/proto-parser.d.ts +76 -0
  28. package/dist/adapters/grpc/proto-parser.d.ts.map +1 -0
  29. package/dist/adapters/grpc/proto-parser.js +523 -0
  30. package/dist/adapters/grpc/proto-parser.js.map +1 -0
  31. package/dist/adapters/grpc/type-converter.d.ts +43 -0
  32. package/dist/adapters/grpc/type-converter.d.ts.map +1 -0
  33. package/dist/adapters/grpc/type-converter.js +270 -0
  34. package/dist/adapters/grpc/type-converter.js.map +1 -0
  35. package/dist/adapters/grpc/types.d.ts +85 -0
  36. package/dist/adapters/grpc/types.d.ts.map +1 -0
  37. package/dist/adapters/grpc/types.js +7 -0
  38. package/dist/adapters/grpc/types.js.map +1 -0
  39. package/dist/adapters/index.d.ts +39 -0
  40. package/dist/adapters/index.d.ts.map +1 -0
  41. package/dist/adapters/index.js +50 -0
  42. package/dist/adapters/index.js.map +1 -0
  43. package/dist/adapters/mcp.d.ts +23 -0
  44. package/dist/adapters/mcp.d.ts.map +1 -0
  45. package/dist/adapters/mcp.js +293 -0
  46. package/dist/adapters/mcp.js.map +1 -0
  47. package/dist/adapters/openapi/adapter.d.ts +213 -0
  48. package/dist/adapters/openapi/adapter.d.ts.map +1 -0
  49. package/dist/adapters/openapi/adapter.js +557 -0
  50. package/dist/adapters/openapi/adapter.js.map +1 -0
  51. package/dist/adapters/openapi/convert.d.ts +120 -0
  52. package/dist/adapters/openapi/convert.d.ts.map +1 -0
  53. package/dist/adapters/openapi/convert.js +363 -0
  54. package/dist/adapters/openapi/convert.js.map +1 -0
  55. package/dist/adapters/openapi/index.d.ts +39 -0
  56. package/dist/adapters/openapi/index.d.ts.map +1 -0
  57. package/dist/adapters/openapi/index.js +48 -0
  58. package/dist/adapters/openapi/index.js.map +1 -0
  59. package/dist/adapters/openapi/parser.d.ts +95 -0
  60. package/dist/adapters/openapi/parser.d.ts.map +1 -0
  61. package/dist/adapters/openapi/parser.js +171 -0
  62. package/dist/adapters/openapi/parser.js.map +1 -0
  63. package/dist/adapters/registry.d.ts +116 -0
  64. package/dist/adapters/registry.d.ts.map +1 -0
  65. package/dist/adapters/registry.js +246 -0
  66. package/dist/adapters/registry.js.map +1 -0
  67. package/dist/adapters/trpc/adapter.d.ts +159 -0
  68. package/dist/adapters/trpc/adapter.d.ts.map +1 -0
  69. package/dist/adapters/trpc/adapter.js +223 -0
  70. package/dist/adapters/trpc/adapter.js.map +1 -0
  71. package/dist/adapters/trpc/extractor.d.ts +218 -0
  72. package/dist/adapters/trpc/extractor.d.ts.map +1 -0
  73. package/dist/adapters/trpc/extractor.js +708 -0
  74. package/dist/adapters/trpc/extractor.js.map +1 -0
  75. package/dist/adapters/trpc/index.d.ts +31 -0
  76. package/dist/adapters/trpc/index.d.ts.map +1 -0
  77. package/dist/adapters/trpc/index.js +40 -0
  78. package/dist/adapters/trpc/index.js.map +1 -0
  79. package/dist/adapters/trpc/parser.d.ts +119 -0
  80. package/dist/adapters/trpc/parser.d.ts.map +1 -0
  81. package/dist/adapters/trpc/parser.js +128 -0
  82. package/dist/adapters/trpc/parser.js.map +1 -0
  83. package/dist/compare/index.d.ts +33 -0
  84. package/dist/compare/index.d.ts.map +1 -0
  85. package/dist/compare/index.js +261 -0
  86. package/dist/compare/index.js.map +1 -0
  87. package/dist/core/types.d.ts +188 -0
  88. package/dist/core/types.d.ts.map +1 -0
  89. package/dist/core/types.js +9 -0
  90. package/dist/core/types.js.map +1 -0
  91. package/dist/extract/index.d.ts +26 -0
  92. package/dist/extract/index.d.ts.map +1 -0
  93. package/dist/extract/index.js +44 -0
  94. package/dist/extract/index.js.map +1 -0
  95. package/dist/index.d.ts +9 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +674 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/languages/base.d.ts +57 -0
  100. package/dist/languages/base.d.ts.map +1 -0
  101. package/dist/languages/base.js +6 -0
  102. package/dist/languages/base.js.map +1 -0
  103. package/dist/languages/bootstrap.d.ts +10 -0
  104. package/dist/languages/bootstrap.d.ts.map +1 -0
  105. package/dist/languages/bootstrap.js +25 -0
  106. package/dist/languages/bootstrap.js.map +1 -0
  107. package/dist/languages/go/handlers/chi.d.ts +24 -0
  108. package/dist/languages/go/handlers/chi.d.ts.map +1 -0
  109. package/dist/languages/go/handlers/chi.js +205 -0
  110. package/dist/languages/go/handlers/chi.js.map +1 -0
  111. package/dist/languages/go/handlers/gin.d.ts +24 -0
  112. package/dist/languages/go/handlers/gin.d.ts.map +1 -0
  113. package/dist/languages/go/handlers/gin.js +156 -0
  114. package/dist/languages/go/handlers/gin.js.map +1 -0
  115. package/dist/languages/go/handlers/stdlib.d.ts +19 -0
  116. package/dist/languages/go/handlers/stdlib.d.ts.map +1 -0
  117. package/dist/languages/go/handlers/stdlib.js +112 -0
  118. package/dist/languages/go/handlers/stdlib.js.map +1 -0
  119. package/dist/languages/go/index.d.ts +18 -0
  120. package/dist/languages/go/index.d.ts.map +1 -0
  121. package/dist/languages/go/index.js +20 -0
  122. package/dist/languages/go/index.js.map +1 -0
  123. package/dist/languages/go/parser.d.ts +33 -0
  124. package/dist/languages/go/parser.d.ts.map +1 -0
  125. package/dist/languages/go/parser.js +95 -0
  126. package/dist/languages/go/parser.js.map +1 -0
  127. package/dist/languages/go/struct-extractor.d.ts +59 -0
  128. package/dist/languages/go/struct-extractor.d.ts.map +1 -0
  129. package/dist/languages/go/struct-extractor.js +483 -0
  130. package/dist/languages/go/struct-extractor.js.map +1 -0
  131. package/dist/languages/go/tag-parser.d.ts +62 -0
  132. package/dist/languages/go/tag-parser.d.ts.map +1 -0
  133. package/dist/languages/go/tag-parser.js +108 -0
  134. package/dist/languages/go/tag-parser.js.map +1 -0
  135. package/dist/languages/go/type-converter.d.ts +32 -0
  136. package/dist/languages/go/type-converter.d.ts.map +1 -0
  137. package/dist/languages/go/type-converter.js +226 -0
  138. package/dist/languages/go/type-converter.js.map +1 -0
  139. package/dist/languages/go/types.d.ts +153 -0
  140. package/dist/languages/go/types.d.ts.map +1 -0
  141. package/dist/languages/go/types.js +6 -0
  142. package/dist/languages/go/types.js.map +1 -0
  143. package/dist/languages/import-resolver.d.ts +645 -0
  144. package/dist/languages/import-resolver.d.ts.map +1 -0
  145. package/dist/languages/import-resolver.js +1278 -0
  146. package/dist/languages/import-resolver.js.map +1 -0
  147. package/dist/languages/index.d.ts +34 -0
  148. package/dist/languages/index.d.ts.map +1 -0
  149. package/dist/languages/index.js +93 -0
  150. package/dist/languages/index.js.map +1 -0
  151. package/dist/languages/json-schema.d.ts +40 -0
  152. package/dist/languages/json-schema.d.ts.map +1 -0
  153. package/dist/languages/json-schema.js +188 -0
  154. package/dist/languages/json-schema.js.map +1 -0
  155. package/dist/languages/python-ast/index.d.ts +8 -0
  156. package/dist/languages/python-ast/index.d.ts.map +1 -0
  157. package/dist/languages/python-ast/index.js +7 -0
  158. package/dist/languages/python-ast/index.js.map +1 -0
  159. package/dist/languages/python-ast/parser.d.ts +174 -0
  160. package/dist/languages/python-ast/parser.d.ts.map +1 -0
  161. package/dist/languages/python-ast/parser.js +1205 -0
  162. package/dist/languages/python-ast/parser.js.map +1 -0
  163. package/dist/languages/python-ast/type-resolver.d.ts +75 -0
  164. package/dist/languages/python-ast/type-resolver.d.ts.map +1 -0
  165. package/dist/languages/python-ast/type-resolver.js +421 -0
  166. package/dist/languages/python-ast/type-resolver.js.map +1 -0
  167. package/dist/languages/python-ast/types.d.ts +216 -0
  168. package/dist/languages/python-ast/types.d.ts.map +1 -0
  169. package/dist/languages/python-ast/types.js +6 -0
  170. package/dist/languages/python-ast/types.js.map +1 -0
  171. package/dist/languages/python.d.ts +55 -0
  172. package/dist/languages/python.d.ts.map +1 -0
  173. package/dist/languages/python.js +311 -0
  174. package/dist/languages/python.js.map +1 -0
  175. package/dist/languages/typescript.d.ts +272 -0
  176. package/dist/languages/typescript.d.ts.map +1 -0
  177. package/dist/languages/typescript.js +1381 -0
  178. package/dist/languages/typescript.js.map +1 -0
  179. package/dist/patterns/base.d.ts +146 -0
  180. package/dist/patterns/base.d.ts.map +1 -0
  181. package/dist/patterns/base.js +89 -0
  182. package/dist/patterns/base.js.map +1 -0
  183. package/dist/patterns/errors.d.ts +172 -0
  184. package/dist/patterns/errors.d.ts.map +1 -0
  185. package/dist/patterns/errors.js +185 -0
  186. package/dist/patterns/errors.js.map +1 -0
  187. package/dist/patterns/extractors.d.ts +170 -0
  188. package/dist/patterns/extractors.d.ts.map +1 -0
  189. package/dist/patterns/extractors.js +305 -0
  190. package/dist/patterns/extractors.js.map +1 -0
  191. package/dist/patterns/graphql/apollo-client.d.ts +80 -0
  192. package/dist/patterns/graphql/apollo-client.d.ts.map +1 -0
  193. package/dist/patterns/graphql/apollo-client.js +800 -0
  194. package/dist/patterns/graphql/apollo-client.js.map +1 -0
  195. package/dist/patterns/graphql/apollo-server.d.ts +55 -0
  196. package/dist/patterns/graphql/apollo-server.d.ts.map +1 -0
  197. package/dist/patterns/graphql/apollo-server.js +523 -0
  198. package/dist/patterns/graphql/apollo-server.js.map +1 -0
  199. package/dist/patterns/graphql/index.d.ts +11 -0
  200. package/dist/patterns/graphql/index.d.ts.map +1 -0
  201. package/dist/patterns/graphql/index.js +12 -0
  202. package/dist/patterns/graphql/index.js.map +1 -0
  203. package/dist/patterns/graphql/types.d.ts +213 -0
  204. package/dist/patterns/graphql/types.d.ts.map +1 -0
  205. package/dist/patterns/graphql/types.js +16 -0
  206. package/dist/patterns/graphql/types.js.map +1 -0
  207. package/dist/patterns/http-clients/axios.d.ts +148 -0
  208. package/dist/patterns/http-clients/axios.d.ts.map +1 -0
  209. package/dist/patterns/http-clients/axios.js +652 -0
  210. package/dist/patterns/http-clients/axios.js.map +1 -0
  211. package/dist/patterns/http-clients/fetch.d.ts +88 -0
  212. package/dist/patterns/http-clients/fetch.d.ts.map +1 -0
  213. package/dist/patterns/http-clients/fetch.js +364 -0
  214. package/dist/patterns/http-clients/fetch.js.map +1 -0
  215. package/dist/patterns/http-clients/index.d.ts +36 -0
  216. package/dist/patterns/http-clients/index.d.ts.map +1 -0
  217. package/dist/patterns/http-clients/index.js +50 -0
  218. package/dist/patterns/http-clients/index.js.map +1 -0
  219. package/dist/patterns/http-clients/property-access.d.ts +46 -0
  220. package/dist/patterns/http-clients/property-access.d.ts.map +1 -0
  221. package/dist/patterns/http-clients/property-access.js +818 -0
  222. package/dist/patterns/http-clients/property-access.js.map +1 -0
  223. package/dist/patterns/http-clients/type-inference.d.ts +48 -0
  224. package/dist/patterns/http-clients/type-inference.d.ts.map +1 -0
  225. package/dist/patterns/http-clients/type-inference.js +293 -0
  226. package/dist/patterns/http-clients/type-inference.js.map +1 -0
  227. package/dist/patterns/http-clients/types.d.ts +168 -0
  228. package/dist/patterns/http-clients/types.d.ts.map +1 -0
  229. package/dist/patterns/http-clients/types.js +10 -0
  230. package/dist/patterns/http-clients/types.js.map +1 -0
  231. package/dist/patterns/http-clients/url-extractor.d.ts +53 -0
  232. package/dist/patterns/http-clients/url-extractor.d.ts.map +1 -0
  233. package/dist/patterns/http-clients/url-extractor.js +338 -0
  234. package/dist/patterns/http-clients/url-extractor.js.map +1 -0
  235. package/dist/patterns/index.d.ts +44 -0
  236. package/dist/patterns/index.d.ts.map +1 -0
  237. package/dist/patterns/index.js +49 -0
  238. package/dist/patterns/index.js.map +1 -0
  239. package/dist/patterns/python/aiohttp.d.ts +21 -0
  240. package/dist/patterns/python/aiohttp.d.ts.map +1 -0
  241. package/dist/patterns/python/aiohttp.js +188 -0
  242. package/dist/patterns/python/aiohttp.js.map +1 -0
  243. package/dist/patterns/python/httpx.d.ts +20 -0
  244. package/dist/patterns/python/httpx.d.ts.map +1 -0
  245. package/dist/patterns/python/httpx.js +183 -0
  246. package/dist/patterns/python/httpx.js.map +1 -0
  247. package/dist/patterns/python/index.d.ts +32 -0
  248. package/dist/patterns/python/index.d.ts.map +1 -0
  249. package/dist/patterns/python/index.js +63 -0
  250. package/dist/patterns/python/index.js.map +1 -0
  251. package/dist/patterns/python/property-access.d.ts +27 -0
  252. package/dist/patterns/python/property-access.d.ts.map +1 -0
  253. package/dist/patterns/python/property-access.js +132 -0
  254. package/dist/patterns/python/property-access.js.map +1 -0
  255. package/dist/patterns/python/requests.d.ts +19 -0
  256. package/dist/patterns/python/requests.d.ts.map +1 -0
  257. package/dist/patterns/python/requests.js +239 -0
  258. package/dist/patterns/python/requests.js.map +1 -0
  259. package/dist/patterns/python/types.d.ts +95 -0
  260. package/dist/patterns/python/types.d.ts.map +1 -0
  261. package/dist/patterns/python/types.js +43 -0
  262. package/dist/patterns/python/types.js.map +1 -0
  263. package/dist/patterns/registry.d.ts +181 -0
  264. package/dist/patterns/registry.d.ts.map +1 -0
  265. package/dist/patterns/registry.js +304 -0
  266. package/dist/patterns/registry.js.map +1 -0
  267. package/dist/patterns/rest/express.d.ts +78 -0
  268. package/dist/patterns/rest/express.d.ts.map +1 -0
  269. package/dist/patterns/rest/express.js +289 -0
  270. package/dist/patterns/rest/express.js.map +1 -0
  271. package/dist/patterns/rest/fastify.d.ts +93 -0
  272. package/dist/patterns/rest/fastify.d.ts.map +1 -0
  273. package/dist/patterns/rest/fastify.js +420 -0
  274. package/dist/patterns/rest/fastify.js.map +1 -0
  275. package/dist/patterns/rest/index.d.ts +31 -0
  276. package/dist/patterns/rest/index.d.ts.map +1 -0
  277. package/dist/patterns/rest/index.js +45 -0
  278. package/dist/patterns/rest/index.js.map +1 -0
  279. package/dist/patterns/rest/middleware.d.ts +25 -0
  280. package/dist/patterns/rest/middleware.d.ts.map +1 -0
  281. package/dist/patterns/rest/middleware.js +219 -0
  282. package/dist/patterns/rest/middleware.js.map +1 -0
  283. package/dist/patterns/rest/path-parser.d.ts +50 -0
  284. package/dist/patterns/rest/path-parser.d.ts.map +1 -0
  285. package/dist/patterns/rest/path-parser.js +137 -0
  286. package/dist/patterns/rest/path-parser.js.map +1 -0
  287. package/dist/patterns/rest/response-inference.d.ts +44 -0
  288. package/dist/patterns/rest/response-inference.d.ts.map +1 -0
  289. package/dist/patterns/rest/response-inference.js +218 -0
  290. package/dist/patterns/rest/response-inference.js.map +1 -0
  291. package/dist/patterns/rest/types.d.ts +102 -0
  292. package/dist/patterns/rest/types.d.ts.map +1 -0
  293. package/dist/patterns/rest/types.js +10 -0
  294. package/dist/patterns/rest/types.js.map +1 -0
  295. package/dist/patterns/types.d.ts +105 -0
  296. package/dist/patterns/types.d.ts.map +1 -0
  297. package/dist/patterns/types.js +11 -0
  298. package/dist/patterns/types.js.map +1 -0
  299. package/dist/report/index.d.ts +11 -0
  300. package/dist/report/index.d.ts.map +1 -0
  301. package/dist/report/index.js +55 -0
  302. package/dist/report/index.js.map +1 -0
  303. package/dist/tools/contract-comments.d.ts +48 -0
  304. package/dist/tools/contract-comments.d.ts.map +1 -0
  305. package/dist/tools/contract-comments.js +130 -0
  306. package/dist/tools/contract-comments.js.map +1 -0
  307. package/dist/tools/index.d.ts +6 -0
  308. package/dist/tools/index.d.ts.map +1 -0
  309. package/dist/tools/index.js +6 -0
  310. package/dist/tools/index.js.map +1 -0
  311. package/dist/tools/scaffold.d.ts +38 -0
  312. package/dist/tools/scaffold.d.ts.map +1 -0
  313. package/dist/tools/scaffold.js +373 -0
  314. package/dist/tools/scaffold.js.map +1 -0
  315. package/dist/trace/index.d.ts +28 -0
  316. package/dist/trace/index.d.ts.map +1 -0
  317. package/dist/trace/index.js +45 -0
  318. package/dist/trace/index.js.map +1 -0
  319. package/dist/types.d.ts +135 -0
  320. package/dist/types.d.ts.map +1 -0
  321. package/dist/types.js +22 -0
  322. package/dist/types.js.map +1 -0
  323. package/dist/watch/cache.d.ts +41 -0
  324. package/dist/watch/cache.d.ts.map +1 -0
  325. package/dist/watch/cache.js +230 -0
  326. package/dist/watch/cache.js.map +1 -0
  327. package/dist/watch/index.d.ts +9 -0
  328. package/dist/watch/index.d.ts.map +1 -0
  329. package/dist/watch/index.js +7 -0
  330. package/dist/watch/index.js.map +1 -0
  331. package/dist/watch/project.d.ts +128 -0
  332. package/dist/watch/project.d.ts.map +1 -0
  333. package/dist/watch/project.js +152 -0
  334. package/dist/watch/project.js.map +1 -0
  335. package/dist/watch/watcher.d.ts +76 -0
  336. package/dist/watch/watcher.d.ts.map +1 -0
  337. package/dist/watch/watcher.js +235 -0
  338. package/dist/watch/watcher.js.map +1 -0
  339. package/package.json +70 -0
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Pattern Registry
3
+ *
4
+ * Registry for pattern matchers, following the same design as AdapterRegistry.
5
+ * Provides registration, lookup, and scanning capabilities.
6
+ *
7
+ * The registry uses a module-scoped singleton pattern with exported wrapper
8
+ * functions for a clean, functional API.
9
+ *
10
+ * @module patterns/registry
11
+ * @see .context/ADR-P2-1-PATTERN-MATCHER.md
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { registerPattern, scanForPatterns } from './patterns';
16
+ *
17
+ * // Register a custom pattern matcher
18
+ * registerPattern(myExpressMatcher);
19
+ *
20
+ * // Scan source file for patterns
21
+ * const matches = scanForPatterns(sourceFile, {
22
+ * frameworks: ['express'],
23
+ * types: ['call']
24
+ * });
25
+ * ```
26
+ */
27
+ import { PatternNotFoundError, PatternValidationError, } from './errors.js';
28
+ /**
29
+ * Registry of pattern matchers
30
+ *
31
+ * Follows the same design as AdapterRegistry from Phase 1,
32
+ * with module-scoped singleton and exported wrapper functions.
33
+ */
34
+ class PatternRegistry {
35
+ matchers = new Map();
36
+ matchersByFramework = new Map();
37
+ /**
38
+ * Register a pattern matcher
39
+ * @throws {PatternValidationError} if matcher is invalid
40
+ */
41
+ register(matcher) {
42
+ this.validate(matcher);
43
+ // Warn on overwrite in debug mode
44
+ if (this.matchers.has(matcher.name) && process.env.DEBUG_TRACE_MCP) {
45
+ console.error(`[PatternRegistry] Overwriting matcher: ${matcher.name}`);
46
+ }
47
+ this.matchers.set(matcher.name, matcher);
48
+ // Index by framework
49
+ const existing = this.matchersByFramework.get(matcher.framework) || [];
50
+ this.matchersByFramework.set(matcher.framework, [...existing.filter(m => m.name !== matcher.name), matcher]);
51
+ if (process.env.DEBUG_TRACE_MCP) {
52
+ console.error(`[PatternRegistry] Registered: ${matcher.name} (${matcher.framework})`);
53
+ }
54
+ }
55
+ /**
56
+ * Get matcher by name
57
+ * @throws {PatternNotFoundError} if not found
58
+ */
59
+ get(name) {
60
+ const matcher = this.matchers.get(name);
61
+ if (!matcher) {
62
+ throw new PatternNotFoundError(name, this.names());
63
+ }
64
+ return matcher;
65
+ }
66
+ /**
67
+ * Get all matchers for a framework
68
+ */
69
+ getByFramework(framework) {
70
+ return this.matchersByFramework.get(framework) || [];
71
+ }
72
+ /**
73
+ * Get all matchers supporting a pattern type
74
+ */
75
+ getByType(type) {
76
+ return Array.from(this.matchers.values())
77
+ .filter(m => m.supportedTypes.includes(type));
78
+ }
79
+ /**
80
+ * Check if a matcher exists
81
+ */
82
+ has(name) {
83
+ return this.matchers.has(name);
84
+ }
85
+ /**
86
+ * List all matcher names
87
+ */
88
+ names() {
89
+ return Array.from(this.matchers.keys());
90
+ }
91
+ /**
92
+ * List all frameworks
93
+ */
94
+ frameworks() {
95
+ return Array.from(this.matchersByFramework.keys());
96
+ }
97
+ /**
98
+ * Scan a source file with all registered matchers
99
+ *
100
+ * @param sourceFile - The file to scan
101
+ * @param options - Optional filtering options
102
+ * @returns All matches found across all matchers
103
+ */
104
+ scan(sourceFile, options) {
105
+ let matchers = Array.from(this.matchers.values());
106
+ // Filter by framework
107
+ if (options?.frameworks?.length) {
108
+ matchers = matchers.filter(m => options.frameworks.includes(m.framework));
109
+ }
110
+ // Filter by pattern type
111
+ if (options?.types?.length) {
112
+ matchers = matchers.filter(m => m.supportedTypes.some(t => options.types.includes(t)));
113
+ }
114
+ const allMatches = [];
115
+ for (const matcher of matchers) {
116
+ const matches = matcher.scan
117
+ ? matcher.scan(sourceFile)
118
+ : this.defaultScan(matcher, sourceFile);
119
+ allMatches.push(...matches);
120
+ }
121
+ return allMatches;
122
+ }
123
+ defaultScan(matcher, sourceFile) {
124
+ const matches = [];
125
+ sourceFile.forEachDescendant((node) => {
126
+ const result = matcher.match(node);
127
+ if (result) {
128
+ matches.push(result);
129
+ }
130
+ });
131
+ return matches;
132
+ }
133
+ validate(matcher) {
134
+ const m = matcher;
135
+ if (!m.name || typeof m.name !== 'string') {
136
+ throw new PatternValidationError(matcher, "Matcher must have 'name' property");
137
+ }
138
+ if (!m.framework || typeof m.framework !== 'string') {
139
+ throw new PatternValidationError(matcher, "Matcher must have 'framework' property");
140
+ }
141
+ if (!Array.isArray(m.patterns) || m.patterns.length === 0) {
142
+ throw new PatternValidationError(matcher, "Matcher must have at least one pattern");
143
+ }
144
+ if (!Array.isArray(m.supportedTypes) || m.supportedTypes.length === 0) {
145
+ throw new PatternValidationError(matcher, "Matcher must declare supportedTypes");
146
+ }
147
+ if (typeof m.match !== 'function') {
148
+ throw new PatternValidationError(matcher, "Matcher must implement match()");
149
+ }
150
+ if (typeof m.extract !== 'function') {
151
+ throw new PatternValidationError(matcher, "Matcher must implement extract()");
152
+ }
153
+ }
154
+ }
155
+ // Module-scoped singleton
156
+ const registry = new PatternRegistry();
157
+ // ─────────────────────────────────────────────────────────────────────────────
158
+ // Public API - Wrapper Functions
159
+ // ─────────────────────────────────────────────────────────────────────────────
160
+ /**
161
+ * Register a pattern matcher with the global registry.
162
+ *
163
+ * The matcher is validated before registration. If validation fails,
164
+ * a {@link PatternValidationError} is thrown with details.
165
+ *
166
+ * @param matcher - The pattern matcher to register
167
+ * @throws {PatternValidationError} If the matcher fails validation
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * import { registerPattern, BasePatternMatcher } from './patterns';
172
+ *
173
+ * class MyMatcher extends BasePatternMatcher {
174
+ * readonly name = 'my-matcher';
175
+ * readonly framework = 'express';
176
+ * // ... implementation
177
+ * }
178
+ *
179
+ * registerPattern(new MyMatcher());
180
+ * ```
181
+ */
182
+ export function registerPattern(matcher) {
183
+ registry.register(matcher);
184
+ }
185
+ /**
186
+ * Get a pattern matcher by its unique name.
187
+ *
188
+ * @param name - The unique name of the matcher to retrieve
189
+ * @returns The registered pattern matcher
190
+ * @throws {PatternNotFoundError} If no matcher with that name exists
191
+ *
192
+ * @example
193
+ * ```typescript
194
+ * const matcher = getPattern('express-router');
195
+ * const matches = matcher.scan(sourceFile);
196
+ * ```
197
+ */
198
+ export function getPattern(name) {
199
+ return registry.get(name);
200
+ }
201
+ /**
202
+ * Get all pattern matchers for a specific framework.
203
+ *
204
+ * @param framework - The framework name to filter by
205
+ * @returns Array of matchers for that framework (empty if none)
206
+ *
207
+ * @example
208
+ * ```typescript
209
+ * const expressMatchers = getPatternsByFramework('express');
210
+ * console.log(`Found ${expressMatchers.length} Express matchers`);
211
+ * ```
212
+ */
213
+ export function getPatternsByFramework(framework) {
214
+ return registry.getByFramework(framework);
215
+ }
216
+ /**
217
+ * Get all pattern matchers supporting a specific pattern type.
218
+ *
219
+ * @param type - The pattern type to filter by
220
+ * @returns Array of matchers supporting that type
221
+ *
222
+ * @example
223
+ * ```typescript
224
+ * const decoratorMatchers = getPatternsByType('decorator');
225
+ * // Returns matchers like NestJS that use decorators
226
+ * ```
227
+ */
228
+ export function getPatternsByType(type) {
229
+ return registry.getByType(type);
230
+ }
231
+ /**
232
+ * Check if a pattern matcher is registered by name.
233
+ *
234
+ * @param name - The name to check
235
+ * @returns `true` if a matcher with that name exists
236
+ *
237
+ * @example
238
+ * ```typescript
239
+ * if (!hasPattern('express-router')) {
240
+ * registerPattern(new ExpressRouterMatcher());
241
+ * }
242
+ * ```
243
+ */
244
+ export function hasPattern(name) {
245
+ return registry.has(name);
246
+ }
247
+ /**
248
+ * List all registered pattern matcher names.
249
+ *
250
+ * @returns Array of all registered matcher names
251
+ *
252
+ * @example
253
+ * ```typescript
254
+ * console.log('Available matchers:', listPatterns().join(', '));
255
+ * ```
256
+ */
257
+ export function listPatterns() {
258
+ return registry.names();
259
+ }
260
+ /**
261
+ * List all unique framework names from registered matchers.
262
+ *
263
+ * @returns Array of unique framework names
264
+ *
265
+ * @example
266
+ * ```typescript
267
+ * const frameworks = listFrameworks();
268
+ * // ['express', 'nestjs', 'trpc', ...]
269
+ * ```
270
+ */
271
+ export function listFrameworks() {
272
+ return registry.frameworks();
273
+ }
274
+ /**
275
+ * Scan a source file for patterns using all registered matchers.
276
+ *
277
+ * This is the primary entry point for pattern detection. It applies
278
+ * all registered matchers (or a filtered subset) to find API patterns.
279
+ *
280
+ * @param sourceFile - The ts-morph SourceFile to scan
281
+ * @param options - Optional filtering options
282
+ * @returns Array of all matches found across all matchers
283
+ *
284
+ * @example
285
+ * ```typescript
286
+ * import { Project } from 'ts-morph';
287
+ *
288
+ * const project = new Project();
289
+ * const sourceFile = project.addSourceFileAtPath('./src/routes.ts');
290
+ *
291
+ * // Scan with all matchers
292
+ * const allMatches = scanForPatterns(sourceFile);
293
+ *
294
+ * // Scan with filters
295
+ * const expressMatches = scanForPatterns(sourceFile, {
296
+ * frameworks: ['express'],
297
+ * types: ['call']
298
+ * });
299
+ * ```
300
+ */
301
+ export function scanForPatterns(sourceFile, options) {
302
+ return registry.scan(sourceFile, options);
303
+ }
304
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/patterns/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Express Pattern Matcher
3
+ *
4
+ * Detects Express.js route patterns including:
5
+ * - app.get(), app.post(), etc.
6
+ * - router.get(), router.post(), etc.
7
+ * - app.route().get().post()
8
+ * - app.all(), app.use()
9
+ *
10
+ * @module patterns/rest/express
11
+ * @see .context/ADR-P2-2-REST-DETECTION.md
12
+ */
13
+ import { Node } from 'ts-morph';
14
+ import type { NormalizedSchema } from '../../core/types.js';
15
+ import { BasePatternMatcher } from '../base.js';
16
+ import type { PatternDef, MatchResult, PatternType } from '../types.js';
17
+ /**
18
+ * Pattern matcher for Express.js routes.
19
+ *
20
+ * Detects patterns:
21
+ * - `app.get()`, `app.post()`, etc.
22
+ * - `router.get()`, `router.post()`, etc.
23
+ * - `app.route('/path').get().post()`
24
+ * - `app.all()`, `app.use()`
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const matcher = new ExpressPatternMatcher();
29
+ * const result = matcher.match(callExpressionNode);
30
+ * if (result) {
31
+ * console.log(result.captures.httpMethod); // 'GET'
32
+ * console.log(result.captures.routePath); // '/users/:id'
33
+ * }
34
+ * ```
35
+ */
36
+ export declare class ExpressPatternMatcher extends BasePatternMatcher {
37
+ readonly name = "express";
38
+ readonly framework = "express";
39
+ readonly supportedTypes: PatternType[];
40
+ readonly patterns: PatternDef[];
41
+ /**
42
+ * Match an AST node against Express patterns
43
+ */
44
+ match(node: Node): MatchResult | null;
45
+ /**
46
+ * Match direct HTTP method calls: app.get(), router.post(), etc.
47
+ */
48
+ private matchDirectMethod;
49
+ /**
50
+ * Match route chain: app.route('/path').get().post()
51
+ */
52
+ private matchRouteChain;
53
+ /**
54
+ * Find app.route('/path') in a chain
55
+ */
56
+ private findRouteCall;
57
+ /**
58
+ * Check if a method name is an HTTP method
59
+ */
60
+ private isHttpMethod;
61
+ /**
62
+ * Get the router/app name from an expression
63
+ */
64
+ private getRouterName;
65
+ /**
66
+ * Extract string value from a path argument
67
+ */
68
+ private extractPathString;
69
+ /**
70
+ * Extract pattern - implement abstract method
71
+ */
72
+ protected matchPattern(pattern: PatternDef, node: Node): MatchResult | null;
73
+ /**
74
+ * Extract schema from match
75
+ */
76
+ extract(match: MatchResult): Promise<NormalizedSchema>;
77
+ }
78
+ //# sourceMappingURL=express.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../../src/patterns/rest/express.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,IAAI,EAAwD,MAAM,UAAU,CAAC;AACtF,OAAO,KAAK,EAAE,gBAAgB,EAAkB,MAAM,qBAAqB,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAmBxE;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,qBAAsB,SAAQ,kBAAkB;IAC3D,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,SAAS,aAAa;IAC/B,QAAQ,CAAC,cAAc,EAAE,WAAW,EAAE,CAAqB;IAE3D,QAAQ,CAAC,QAAQ,EAAE,UAAU,EAAE,CAa7B;IAEF;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,WAAW,GAAG,IAAI;IAkBrC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA4DzB;;OAEG;IACH,OAAO,CAAC,eAAe;IA2CvB;;OAEG;IACH,OAAO,CAAC,aAAa;IA4BrB;;OAEG;IACH,OAAO,CAAC,YAAY;IAIpB;;OAEG;IACH,OAAO,CAAC,aAAa;IAWrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAezB;;OAEG;IACH,SAAS,CAAC,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,GAAG,WAAW,GAAG,IAAI;IAK3E;;OAEG;IACG,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC;CA8C7D"}
@@ -0,0 +1,289 @@
1
+ /**
2
+ * Express Pattern Matcher
3
+ *
4
+ * Detects Express.js route patterns including:
5
+ * - app.get(), app.post(), etc.
6
+ * - router.get(), router.post(), etc.
7
+ * - app.route().get().post()
8
+ * - app.all(), app.use()
9
+ *
10
+ * @module patterns/rest/express
11
+ * @see .context/ADR-P2-2-REST-DETECTION.md
12
+ */
13
+ import { Node } from 'ts-morph';
14
+ import { BasePatternMatcher } from '../base.js';
15
+ import { parsePath } from './path-parser.js';
16
+ import { detectExpressMiddleware } from './middleware.js';
17
+ /* ═══════════════════════════════════════════════════════════════════════════
18
+ * 📋 Constants
19
+ * ═══════════════════════════════════════════════════════════════════════════ */
20
+ /**
21
+ * HTTP methods supported by Express
22
+ */
23
+ const HTTP_METHODS = ['get', 'post', 'put', 'delete', 'patch', 'options', 'head', 'all'];
24
+ /* ═══════════════════════════════════════════════════════════════════════════
25
+ * 🔍 ExpressPatternMatcher Class
26
+ * ═══════════════════════════════════════════════════════════════════════════ */
27
+ /**
28
+ * Pattern matcher for Express.js routes.
29
+ *
30
+ * Detects patterns:
31
+ * - `app.get()`, `app.post()`, etc.
32
+ * - `router.get()`, `router.post()`, etc.
33
+ * - `app.route('/path').get().post()`
34
+ * - `app.all()`, `app.use()`
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const matcher = new ExpressPatternMatcher();
39
+ * const result = matcher.match(callExpressionNode);
40
+ * if (result) {
41
+ * console.log(result.captures.httpMethod); // 'GET'
42
+ * console.log(result.captures.routePath); // '/users/:id'
43
+ * }
44
+ * ```
45
+ */
46
+ export class ExpressPatternMatcher extends BasePatternMatcher {
47
+ name = 'express';
48
+ framework = 'express';
49
+ supportedTypes = ['call', 'chain'];
50
+ patterns = [
51
+ // app.get(), router.post(), etc.
52
+ {
53
+ type: 'call',
54
+ signature: /^(get|post|put|delete|patch|options|head|all)$/i,
55
+ inputSchemaLocation: { type: 'body' },
56
+ },
57
+ // app.route().get().post()
58
+ {
59
+ type: 'chain',
60
+ signature: /\.route\s*\([^)]+\)\s*\./,
61
+ inputSchemaLocation: { type: 'chain-method', method: 'route' },
62
+ },
63
+ ];
64
+ /**
65
+ * Match an AST node against Express patterns
66
+ */
67
+ match(node) {
68
+ if (!node)
69
+ return null;
70
+ // Must be a call expression
71
+ if (!Node.isCallExpression(node)) {
72
+ return null;
73
+ }
74
+ // Check for route chain (app.route('/path').get())
75
+ const routeChainResult = this.matchRouteChain(node);
76
+ if (routeChainResult) {
77
+ return routeChainResult;
78
+ }
79
+ // Check for direct method call (app.get('/path', handler))
80
+ return this.matchDirectMethod(node);
81
+ }
82
+ /**
83
+ * Match direct HTTP method calls: app.get(), router.post(), etc.
84
+ */
85
+ matchDirectMethod(call) {
86
+ const expression = call.getExpression();
87
+ if (!Node.isPropertyAccessExpression(expression)) {
88
+ return null;
89
+ }
90
+ const methodName = expression.getName().toLowerCase();
91
+ if (!this.isHttpMethod(methodName)) {
92
+ return null;
93
+ }
94
+ // Get the receiver (app, router, userRouter, etc.)
95
+ const receiver = expression.getExpression();
96
+ const routerName = this.getRouterName(receiver);
97
+ // First argument should be the path
98
+ const args = call.getArguments();
99
+ if (args.length === 0) {
100
+ return null;
101
+ }
102
+ const pathArg = args[0];
103
+ const routePath = this.extractPathString(pathArg);
104
+ if (!routePath) {
105
+ return null;
106
+ }
107
+ // Parse path parameters
108
+ const pathParams = parsePath(routePath);
109
+ // Detect validation middleware
110
+ const middlewareArgs = args.slice(1, -1); // All but first (path) and last (handler)
111
+ const validationMiddleware = detectExpressMiddleware(middlewareArgs);
112
+ // Get handler (last argument)
113
+ const handler = args[args.length - 1];
114
+ const handlerNode = args.length > 1 ? handler : undefined;
115
+ const httpMethod = methodName.toUpperCase();
116
+ const sourceFile = call.getSourceFile();
117
+ const captures = {
118
+ httpMethod,
119
+ routePath,
120
+ pathParameters: pathParams,
121
+ routerName,
122
+ validationMiddleware: validationMiddleware.length > 0 ? validationMiddleware : undefined,
123
+ handlerNode,
124
+ };
125
+ return {
126
+ pattern: this.patterns[0],
127
+ node: call,
128
+ framework: this.framework,
129
+ identifier: `${httpMethod} ${routePath}`,
130
+ location: this.getLocation(call, sourceFile.getFilePath()),
131
+ captures,
132
+ };
133
+ }
134
+ /**
135
+ * Match route chain: app.route('/path').get().post()
136
+ */
137
+ matchRouteChain(call) {
138
+ const expression = call.getExpression();
139
+ if (!Node.isPropertyAccessExpression(expression)) {
140
+ return null;
141
+ }
142
+ const methodName = expression.getName().toLowerCase();
143
+ if (!this.isHttpMethod(methodName)) {
144
+ return null;
145
+ }
146
+ // Check if this is part of a route() chain
147
+ const routeInfo = this.findRouteCall(expression.getExpression());
148
+ if (!routeInfo) {
149
+ return null;
150
+ }
151
+ // Get handler (first argument for chained methods)
152
+ const args = call.getArguments();
153
+ const handlerNode = args.length > 0 ? args[0] : undefined;
154
+ const httpMethod = methodName.toUpperCase();
155
+ const pathParams = parsePath(routeInfo.path);
156
+ const sourceFile = call.getSourceFile();
157
+ const captures = {
158
+ httpMethod,
159
+ routePath: routeInfo.path,
160
+ pathParameters: pathParams,
161
+ routerName: routeInfo.routerName,
162
+ handlerNode,
163
+ };
164
+ return {
165
+ pattern: this.patterns[1],
166
+ node: call,
167
+ framework: this.framework,
168
+ identifier: `${httpMethod} ${routeInfo.path}`,
169
+ location: this.getLocation(call, sourceFile.getFilePath()),
170
+ captures,
171
+ };
172
+ }
173
+ /**
174
+ * Find app.route('/path') in a chain
175
+ */
176
+ findRouteCall(node) {
177
+ // Could be: app.route('/path') directly
178
+ if (Node.isCallExpression(node)) {
179
+ const expr = node.getExpression();
180
+ if (Node.isPropertyAccessExpression(expr) && expr.getName() === 'route') {
181
+ const args = node.getArguments();
182
+ if (args.length > 0) {
183
+ const path = this.extractPathString(args[0]);
184
+ if (path) {
185
+ const routerName = this.getRouterName(expr.getExpression());
186
+ return { path, routerName };
187
+ }
188
+ }
189
+ }
190
+ // Could be chained: app.route('/path').get().post()
191
+ // Walk up the chain
192
+ return this.findRouteCall(expr);
193
+ }
194
+ // Could be property access in a chain
195
+ if (Node.isPropertyAccessExpression(node)) {
196
+ return this.findRouteCall(node.getExpression());
197
+ }
198
+ return null;
199
+ }
200
+ /**
201
+ * Check if a method name is an HTTP method
202
+ */
203
+ isHttpMethod(name) {
204
+ return HTTP_METHODS.includes(name.toLowerCase());
205
+ }
206
+ /**
207
+ * Get the router/app name from an expression
208
+ */
209
+ getRouterName(expr) {
210
+ if (Node.isIdentifier(expr)) {
211
+ return expr.getText();
212
+ }
213
+ if (Node.isPropertyAccessExpression(expr)) {
214
+ // Could be something like this.router
215
+ return expr.getText();
216
+ }
217
+ return undefined;
218
+ }
219
+ /**
220
+ * Extract string value from a path argument
221
+ */
222
+ extractPathString(arg) {
223
+ if (Node.isStringLiteral(arg)) {
224
+ return arg.getLiteralValue();
225
+ }
226
+ if (Node.isNoSubstitutionTemplateLiteral(arg)) {
227
+ return arg.getLiteralValue();
228
+ }
229
+ if (Node.isTemplateExpression(arg)) {
230
+ // For now, just return the head text
231
+ // Full template analysis would need variable resolution
232
+ return arg.getHead().getText().slice(1, -2); // Remove ` and ${
233
+ }
234
+ return undefined;
235
+ }
236
+ /**
237
+ * Extract pattern - implement abstract method
238
+ */
239
+ matchPattern(pattern, node) {
240
+ // Delegate to the main match method
241
+ return this.match(node);
242
+ }
243
+ /**
244
+ * Extract schema from match
245
+ */
246
+ async extract(match) {
247
+ const captures = match.captures;
248
+ // Build properties from path parameters
249
+ const properties = {};
250
+ // Add path parameters
251
+ if (captures.pathParameters) {
252
+ for (const param of captures.pathParameters) {
253
+ properties[`params.${param.name}`] = {
254
+ type: { kind: 'primitive', value: param.inferredType === 'number' ? 'number' : 'string' },
255
+ optional: param.optional,
256
+ nullable: false,
257
+ readonly: false,
258
+ deprecated: false,
259
+ };
260
+ }
261
+ }
262
+ // Add validation middleware schemas
263
+ if (captures.validationMiddleware) {
264
+ for (const middleware of captures.validationMiddleware) {
265
+ // The actual schema extraction would need to parse the Zod/Joi schema
266
+ // For now, just note that validation exists
267
+ properties[`${middleware.target}._validated`] = {
268
+ type: { kind: 'unknown' },
269
+ optional: false,
270
+ nullable: false,
271
+ readonly: false,
272
+ deprecated: false,
273
+ description: `Validated by ${middleware.library}`,
274
+ };
275
+ }
276
+ }
277
+ return {
278
+ name: match.identifier,
279
+ properties,
280
+ required: Object.keys(properties).filter(k => !properties[k].optional),
281
+ source: {
282
+ source: 'typescript',
283
+ id: `express:${match.identifier}`,
284
+ },
285
+ location: match.location,
286
+ };
287
+ }
288
+ }
289
+ //# sourceMappingURL=express.js.map