@mgamil/mapx 0.2.4

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 (203) hide show
  1. package/LICENSE +194 -0
  2. package/README.md +488 -0
  3. package/VERSION +1 -0
  4. package/dist/agents/generator.d.ts +74 -0
  5. package/dist/agents/generator.js +375 -0
  6. package/dist/agents/templates.d.ts +29 -0
  7. package/dist/agents/templates.js +459 -0
  8. package/dist/cli.d.ts +16 -0
  9. package/dist/cli.js +1835 -0
  10. package/dist/core/cluster-engine.d.ts +32 -0
  11. package/dist/core/cluster-engine.js +314 -0
  12. package/dist/core/config.d.ts +29 -0
  13. package/dist/core/config.js +178 -0
  14. package/dist/core/context-builder.d.ts +61 -0
  15. package/dist/core/context-builder.js +252 -0
  16. package/dist/core/flow-tracer.d.ts +63 -0
  17. package/dist/core/flow-tracer.js +366 -0
  18. package/dist/core/git-tracker.d.ts +20 -0
  19. package/dist/core/git-tracker.js +159 -0
  20. package/dist/core/graph.d.ts +42 -0
  21. package/dist/core/graph.js +186 -0
  22. package/dist/core/metrics.d.ts +24 -0
  23. package/dist/core/metrics.js +87 -0
  24. package/dist/core/scanner.d.ts +53 -0
  25. package/dist/core/scanner.js +949 -0
  26. package/dist/core/store-bun.d.ts +13 -0
  27. package/dist/core/store-bun.js +34 -0
  28. package/dist/core/store-interface.d.ts +15 -0
  29. package/dist/core/store-interface.js +7 -0
  30. package/dist/core/store-node.d.ts +13 -0
  31. package/dist/core/store-node.js +35 -0
  32. package/dist/core/store.d.ts +132 -0
  33. package/dist/core/store.js +614 -0
  34. package/dist/core/workspace-manager.d.ts +9 -0
  35. package/dist/core/workspace-manager.js +64 -0
  36. package/dist/exporters/dot-exporter.d.ts +16 -0
  37. package/dist/exporters/dot-exporter.js +179 -0
  38. package/dist/exporters/graph-exporter.d.ts +14 -0
  39. package/dist/exporters/graph-exporter.js +85 -0
  40. package/dist/exporters/index.d.ts +9 -0
  41. package/dist/exporters/index.js +12 -0
  42. package/dist/exporters/llm-exporter.d.ts +18 -0
  43. package/dist/exporters/llm-exporter.js +224 -0
  44. package/dist/exporters/svg-exporter.d.ts +19 -0
  45. package/dist/exporters/svg-exporter.js +319 -0
  46. package/dist/exporters/toon-exporter.d.ts +16 -0
  47. package/dist/exporters/toon-exporter.js +246 -0
  48. package/dist/frameworks/detectors/aspnet.d.ts +11 -0
  49. package/dist/frameworks/detectors/aspnet.js +52 -0
  50. package/dist/frameworks/detectors/django.d.ts +14 -0
  51. package/dist/frameworks/detectors/django.js +135 -0
  52. package/dist/frameworks/detectors/drupal.d.ts +13 -0
  53. package/dist/frameworks/detectors/drupal.js +94 -0
  54. package/dist/frameworks/detectors/express.d.ts +12 -0
  55. package/dist/frameworks/detectors/express.js +234 -0
  56. package/dist/frameworks/detectors/fastapi.d.ts +12 -0
  57. package/dist/frameworks/detectors/fastapi.js +203 -0
  58. package/dist/frameworks/detectors/flask.d.ts +12 -0
  59. package/dist/frameworks/detectors/flask.js +244 -0
  60. package/dist/frameworks/detectors/go.d.ts +11 -0
  61. package/dist/frameworks/detectors/go.js +75 -0
  62. package/dist/frameworks/detectors/laravel.d.ts +11 -0
  63. package/dist/frameworks/detectors/laravel.js +462 -0
  64. package/dist/frameworks/detectors/nestjs.d.ts +12 -0
  65. package/dist/frameworks/detectors/nestjs.js +155 -0
  66. package/dist/frameworks/detectors/nextjs.d.ts +11 -0
  67. package/dist/frameworks/detectors/nextjs.js +118 -0
  68. package/dist/frameworks/detectors/rails.d.ts +12 -0
  69. package/dist/frameworks/detectors/rails.js +76 -0
  70. package/dist/frameworks/detectors/react-router.d.ts +11 -0
  71. package/dist/frameworks/detectors/react-router.js +115 -0
  72. package/dist/frameworks/detectors/rust.d.ts +11 -0
  73. package/dist/frameworks/detectors/rust.js +59 -0
  74. package/dist/frameworks/detectors/spring.d.ts +11 -0
  75. package/dist/frameworks/detectors/spring.js +56 -0
  76. package/dist/frameworks/detectors/sveltekit.d.ts +11 -0
  77. package/dist/frameworks/detectors/sveltekit.js +154 -0
  78. package/dist/frameworks/detectors/symfony.d.ts +13 -0
  79. package/dist/frameworks/detectors/symfony.js +175 -0
  80. package/dist/frameworks/detectors/tanstack-router.d.ts +12 -0
  81. package/dist/frameworks/detectors/tanstack-router.js +80 -0
  82. package/dist/frameworks/detectors/vapor.d.ts +11 -0
  83. package/dist/frameworks/detectors/vapor.js +52 -0
  84. package/dist/frameworks/detectors/vue-router.d.ts +12 -0
  85. package/dist/frameworks/detectors/vue-router.js +237 -0
  86. package/dist/frameworks/detectors/wordpress.d.ts +13 -0
  87. package/dist/frameworks/detectors/wordpress.js +141 -0
  88. package/dist/frameworks/detectors/yii.d.ts +11 -0
  89. package/dist/frameworks/detectors/yii.js +131 -0
  90. package/dist/frameworks/framework-registry.d.ts +13 -0
  91. package/dist/frameworks/framework-registry.js +77 -0
  92. package/dist/frameworks/route-registry.d.ts +26 -0
  93. package/dist/frameworks/route-registry.js +102 -0
  94. package/dist/index.d.ts +19 -0
  95. package/dist/index.js +30 -0
  96. package/dist/languages/index.d.ts +2 -0
  97. package/dist/languages/index.js +7 -0
  98. package/dist/languages/installer.d.ts +13 -0
  99. package/dist/languages/installer.js +103 -0
  100. package/dist/languages/registry.d.ts +19 -0
  101. package/dist/languages/registry.js +427 -0
  102. package/dist/main.d.ts +2 -0
  103. package/dist/main.js +20 -0
  104. package/dist/mcp.d.ts +11 -0
  105. package/dist/mcp.js +1699 -0
  106. package/dist/parsers/common-methods.d.ts +3 -0
  107. package/dist/parsers/common-methods.js +33 -0
  108. package/dist/parsers/fallback-parser.d.ts +10 -0
  109. package/dist/parsers/fallback-parser.js +18 -0
  110. package/dist/parsers/generic-wasm-parser.d.ts +23 -0
  111. package/dist/parsers/generic-wasm-parser.js +168 -0
  112. package/dist/parsers/ignored-symbols.d.ts +26 -0
  113. package/dist/parsers/ignored-symbols.js +77 -0
  114. package/dist/parsers/index.d.ts +9 -0
  115. package/dist/parsers/index.js +13 -0
  116. package/dist/parsers/languages/javascript.d.ts +11 -0
  117. package/dist/parsers/languages/javascript.js +28 -0
  118. package/dist/parsers/languages/php.d.ts +15 -0
  119. package/dist/parsers/languages/php.js +648 -0
  120. package/dist/parsers/languages/typescript.d.ts +10 -0
  121. package/dist/parsers/languages/typescript.js +9 -0
  122. package/dist/parsers/languages/vue.d.ts +13 -0
  123. package/dist/parsers/languages/vue.js +63 -0
  124. package/dist/parsers/parse-worker.d.ts +2 -0
  125. package/dist/parsers/parse-worker.js +185 -0
  126. package/dist/parsers/parser-interface.d.ts +9 -0
  127. package/dist/parsers/parser-interface.js +0 -0
  128. package/dist/parsers/parser-registry.d.ts +8 -0
  129. package/dist/parsers/parser-registry.js +52 -0
  130. package/dist/parsers/wasm-parser.d.ts +16 -0
  131. package/dist/parsers/wasm-parser.js +110 -0
  132. package/dist/types.d.ts +172 -0
  133. package/dist/types.js +0 -0
  134. package/dist/ui/index.html +270 -0
  135. package/dist/ui/main.js +581 -0
  136. package/dist/ui/main.js.map +7 -0
  137. package/dist/ui/styles.css +573 -0
  138. package/dist/ui-events.d.ts +36 -0
  139. package/dist/ui-events.js +61 -0
  140. package/dist/ui-server.d.ts +12 -0
  141. package/dist/ui-server.js +504 -0
  142. package/package.json +179 -0
  143. package/queries/bash/references.scm +22 -0
  144. package/queries/bash/symbols.scm +15 -0
  145. package/queries/c/references.scm +14 -0
  146. package/queries/c/symbols.scm +30 -0
  147. package/queries/c-sharp/references.scm +26 -0
  148. package/queries/c-sharp/symbols.scm +57 -0
  149. package/queries/cpp/references.scm +21 -0
  150. package/queries/cpp/symbols.scm +44 -0
  151. package/queries/dart/references.scm +33 -0
  152. package/queries/dart/symbols.scm +38 -0
  153. package/queries/elixir/references.scm +45 -0
  154. package/queries/elixir/symbols.scm +41 -0
  155. package/queries/go/references.scm +22 -0
  156. package/queries/go/symbols.scm +53 -0
  157. package/queries/java/references.scm +32 -0
  158. package/queries/java/symbols.scm +41 -0
  159. package/queries/javascript/references.scm +14 -0
  160. package/queries/javascript/symbols.scm +23 -0
  161. package/queries/kotlin/references.scm +31 -0
  162. package/queries/kotlin/symbols.scm +24 -0
  163. package/queries/lua/references.scm +19 -0
  164. package/queries/lua/symbols.scm +29 -0
  165. package/queries/pascal/references.scm +29 -0
  166. package/queries/pascal/symbols.scm +45 -0
  167. package/queries/php/references.scm +109 -0
  168. package/queries/php/symbols.scm +33 -0
  169. package/queries/python/references.scm +50 -0
  170. package/queries/python/symbols.scm +21 -0
  171. package/queries/ruby/references.scm +48 -0
  172. package/queries/ruby/symbols.scm +24 -0
  173. package/queries/rust/references.scm +31 -0
  174. package/queries/rust/symbols.scm +35 -0
  175. package/queries/scala/references.scm +30 -0
  176. package/queries/scala/symbols.scm +35 -0
  177. package/queries/svelte/references.scm +20 -0
  178. package/queries/svelte/symbols.scm +30 -0
  179. package/queries/swift/references.scm +22 -0
  180. package/queries/swift/symbols.scm +37 -0
  181. package/queries/typescript/references.scm +25 -0
  182. package/queries/typescript/symbols.scm +35 -0
  183. package/queries/vue/references.scm +20 -0
  184. package/queries/vue/symbols.scm +28 -0
  185. package/queries/zig/references.scm +20 -0
  186. package/queries/zig/symbols.scm +22 -0
  187. package/wasm/tree-sitter-c.wasm +0 -0
  188. package/wasm/tree-sitter-c_sharp.wasm +0 -0
  189. package/wasm/tree-sitter-cpp.wasm +0 -0
  190. package/wasm/tree-sitter-dart.wasm +0 -0
  191. package/wasm/tree-sitter-go.wasm +0 -0
  192. package/wasm/tree-sitter-java.wasm +0 -0
  193. package/wasm/tree-sitter-javascript.wasm +0 -0
  194. package/wasm/tree-sitter-kotlin.wasm +0 -0
  195. package/wasm/tree-sitter-php.wasm +0 -0
  196. package/wasm/tree-sitter-python.wasm +0 -0
  197. package/wasm/tree-sitter-ruby.wasm +0 -0
  198. package/wasm/tree-sitter-rust.wasm +0 -0
  199. package/wasm/tree-sitter-scala.wasm +0 -0
  200. package/wasm/tree-sitter-swift.wasm +0 -0
  201. package/wasm/tree-sitter-tsx.wasm +0 -0
  202. package/wasm/tree-sitter-typescript.wasm +0 -0
  203. package/wasm/tree-sitter-vue.wasm +0 -0
@@ -0,0 +1,118 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ class NextJSDetector {
5
+ name = "nextjs";
6
+ language = "typescript";
7
+ filePattern = /\.(js|jsx|ts|tsx)$/;
8
+ async detect(projectRoot, files) {
9
+ const packageJsonPath = join(projectRoot, "package.json");
10
+ if (existsSync(packageJsonPath)) {
11
+ try {
12
+ const pkg = JSON.parse(await readFile(packageJsonPath, "utf-8"));
13
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
14
+ if (deps && deps.next) {
15
+ return true;
16
+ }
17
+ } catch {
18
+ }
19
+ }
20
+ return false;
21
+ }
22
+ async extractRoutes(filePath, content, ctx) {
23
+ const routes = [];
24
+ const pathNormalized = filePath.replace(/\\/g, "/");
25
+ if (pathNormalized.includes("pages/")) {
26
+ const pagesIndex = pathNormalized.indexOf("pages/");
27
+ let routePart = pathNormalized.substring(pagesIndex + 6);
28
+ routePart = routePart.replace(/\.[a-zA-Z0-9]+$/, "");
29
+ let routePath = "/" + routePart;
30
+ routePath = routePath.replace(/\/index$/, "");
31
+ if (routePath === "") routePath = "/";
32
+ routePath = routePath.replace(/\[([a-zA-Z0-9_]+)\]/g, "{$1}");
33
+ const isApi = routePath.startsWith("/api/");
34
+ routes.push({
35
+ framework: this.name,
36
+ method: isApi ? "ALL" : "GET",
37
+ path: routePath,
38
+ handlerFile: filePath,
39
+ handlerSymbol: "default",
40
+ metadata: {
41
+ confidence: "inferred",
42
+ routeType: isApi ? "server" : "client"
43
+ }
44
+ });
45
+ }
46
+ if (pathNormalized.endsWith("/page.tsx") || pathNormalized.endsWith("/page.jsx")) {
47
+ const appIndex = pathNormalized.indexOf("app/");
48
+ if (appIndex !== -1) {
49
+ let routePart = pathNormalized.substring(appIndex + 4);
50
+ routePart = routePart.replace(/\/page\.[a-zA-Z0-9]+$/, "");
51
+ const segments = routePart.split("/").filter((seg) => !seg.startsWith("(") || !seg.endsWith(")"));
52
+ let routePath = "/" + segments.join("/");
53
+ if (routePath === "") routePath = "/";
54
+ routePath = routePath.replace(/\[([a-zA-Z0-9_]+)\]/g, "{$1}");
55
+ routes.push({
56
+ framework: this.name,
57
+ method: "GET",
58
+ path: routePath,
59
+ handlerFile: filePath,
60
+ handlerSymbol: "default",
61
+ metadata: {
62
+ confidence: "inferred",
63
+ routeType: "client"
64
+ }
65
+ });
66
+ }
67
+ }
68
+ if (pathNormalized.endsWith("/route.ts") || pathNormalized.endsWith("/route.js")) {
69
+ const appIndex = pathNormalized.indexOf("app/");
70
+ if (appIndex !== -1) {
71
+ let routePart = pathNormalized.substring(appIndex + 4);
72
+ routePart = routePart.replace(/\/route\.[a-zA-Z0-9]+$/, "");
73
+ const segments = routePart.split("/").filter((seg) => !seg.startsWith("(") || !seg.endsWith(")"));
74
+ let routePath = "/" + segments.join("/");
75
+ if (routePath === "") routePath = "/";
76
+ routePath = routePath.replace(/\[([a-zA-Z0-9_]+)\]/g, "{$1}");
77
+ const verbRegex = /export\s+(?:async\s+)?function\s+(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD)\b/g;
78
+ let match;
79
+ while ((match = verbRegex.exec(content)) !== null) {
80
+ const verb = match[1];
81
+ routes.push({
82
+ framework: this.name,
83
+ method: verb,
84
+ path: routePath,
85
+ handlerFile: filePath,
86
+ handlerSymbol: verb,
87
+ metadata: {
88
+ confidence: "inferred",
89
+ routeType: "server"
90
+ }
91
+ });
92
+ }
93
+ }
94
+ }
95
+ if (content.includes("'use server'") || content.includes('"use server"')) {
96
+ const actionRegex = /export\s+(?:async\s+)?function\s+([a-zA-Z0-9_]+)\b/g;
97
+ let match;
98
+ while ((match = actionRegex.exec(content)) !== null) {
99
+ const actionName = match[1];
100
+ routes.push({
101
+ framework: this.name,
102
+ method: "POST",
103
+ path: `/action/${actionName}`,
104
+ handlerFile: filePath,
105
+ handlerSymbol: actionName,
106
+ metadata: {
107
+ confidence: "inferred",
108
+ routeType: "server"
109
+ }
110
+ });
111
+ }
112
+ }
113
+ return routes;
114
+ }
115
+ }
116
+ export {
117
+ NextJSDetector
118
+ };
@@ -0,0 +1,12 @@
1
+ import { FrameworkDetector, ScanContext, RouteBinding } from '../../types.js';
2
+
3
+ declare class RailsDetector implements FrameworkDetector {
4
+ readonly name = "rails";
5
+ readonly language = "ruby";
6
+ readonly filePattern: RegExp;
7
+ detect(projectRoot: string, files: string[]): Promise<boolean>;
8
+ extractRoutes(filePath: string, content: string, ctx: ScanContext): Promise<RouteBinding[]>;
9
+ private createRouteBinding;
10
+ }
11
+
12
+ export { RailsDetector };
@@ -0,0 +1,76 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ class RailsDetector {
5
+ name = "rails";
6
+ language = "ruby";
7
+ filePattern = /(routes\.rb|Gemfile)$/;
8
+ async detect(projectRoot, files) {
9
+ const gemfilePath = join(projectRoot, "Gemfile");
10
+ if (existsSync(gemfilePath)) {
11
+ const content = await readFile(gemfilePath, "utf-8");
12
+ if (content.includes("rails")) return true;
13
+ }
14
+ return files.some((f) => f.endsWith("routes.rb"));
15
+ }
16
+ async extractRoutes(filePath, content, ctx) {
17
+ const routes = [];
18
+ if (!filePath.endsWith("routes.rb")) {
19
+ return [];
20
+ }
21
+ const routeRegex = /\b(get|post|put|patch|delete)\s+['"]([^'"]+)['"]\s*,\s*to:\s*['"]([^'"]+)['"]/g;
22
+ let match;
23
+ while ((match = routeRegex.exec(content)) !== null) {
24
+ const verb = match[1].toUpperCase();
25
+ let pathVal = match[2];
26
+ const controllerAction = match[3];
27
+ pathVal = pathVal.replace(/:([a-zA-Z0-9_]+)/g, "{$1}");
28
+ routes.push(this.createRouteBinding(verb, pathVal, controllerAction, filePath, ctx));
29
+ }
30
+ const resourcesRegex = /\bresources\s+:([a-zA-Z0-9_]+)/g;
31
+ while ((match = resourcesRegex.exec(content)) !== null) {
32
+ const resourceName = match[1];
33
+ const controllerPrefix = resourceName.charAt(0).toUpperCase() + resourceName.slice(1);
34
+ const controllerClass = `${controllerPrefix}Controller`;
35
+ const crudEndpoints = [
36
+ { verb: "GET", path: `/${resourceName}`, action: "index" },
37
+ { verb: "GET", path: `/${resourceName}/new`, action: "new" },
38
+ { verb: "POST", path: `/${resourceName}`, action: "create" },
39
+ { verb: "GET", path: `/${resourceName}/{id}`, action: "show" },
40
+ { verb: "GET", path: `/${resourceName}/{id}/edit`, action: "edit" },
41
+ { verb: "PUT", path: `/${resourceName}/{id}`, action: "update" },
42
+ { verb: "PATCH", path: `/${resourceName}/{id}`, action: "update" },
43
+ { verb: "DELETE", path: `/${resourceName}/{id}`, action: "destroy" }
44
+ ];
45
+ for (const endpoint of crudEndpoints) {
46
+ routes.push(this.createRouteBinding(endpoint.verb, endpoint.path, `${resourceName}#${endpoint.action}`, filePath, ctx));
47
+ }
48
+ }
49
+ return routes;
50
+ }
51
+ createRouteBinding(verb, path, controllerAction, filePath, ctx) {
52
+ const parts = controllerAction.split("#");
53
+ const controller = parts[0];
54
+ const action = parts[1] || "index";
55
+ const controllerClassName = controller.charAt(0).toUpperCase() + controller.slice(1) + "Controller";
56
+ let resolvedFile = filePath;
57
+ const resolvedPath = ctx.resolveSymbolToFile(controllerClassName);
58
+ if (resolvedPath) {
59
+ resolvedFile = resolvedPath;
60
+ }
61
+ return {
62
+ framework: this.name,
63
+ method: verb,
64
+ path,
65
+ handlerFile: resolvedFile,
66
+ handlerSymbol: `${controllerClassName}#${action}`,
67
+ metadata: {
68
+ confidence: "inferred",
69
+ routeType: "server"
70
+ }
71
+ };
72
+ }
73
+ }
74
+ export {
75
+ RailsDetector
76
+ };
@@ -0,0 +1,11 @@
1
+ import { FrameworkDetector, ScanContext, RouteBinding } from '../../types.js';
2
+
3
+ declare class ReactRouterDetector implements FrameworkDetector {
4
+ readonly name = "react-router";
5
+ readonly language = "typescript";
6
+ readonly filePattern: RegExp;
7
+ detect(projectRoot: string, files: string[]): Promise<boolean>;
8
+ extractRoutes(filePath: string, content: string, ctx: ScanContext): Promise<RouteBinding[]>;
9
+ }
10
+
11
+ export { ReactRouterDetector };
@@ -0,0 +1,115 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ class ReactRouterDetector {
5
+ name = "react-router";
6
+ language = "typescript";
7
+ filePattern = /\.(js|jsx|ts|tsx)$/;
8
+ async detect(projectRoot, files) {
9
+ const packageJsonPath = join(projectRoot, "package.json");
10
+ if (existsSync(packageJsonPath)) {
11
+ try {
12
+ const pkg = JSON.parse(await readFile(packageJsonPath, "utf-8"));
13
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
14
+ if (deps && (deps["react-router"] || deps["react-router-dom"])) {
15
+ return true;
16
+ }
17
+ } catch {
18
+ }
19
+ }
20
+ return false;
21
+ }
22
+ async extractRoutes(filePath, content, ctx) {
23
+ const routes = [];
24
+ if (!content.includes("Route") && !content.includes("createBrowserRouter")) {
25
+ return [];
26
+ }
27
+ const tagRegex = /<\/?Route\b|path=['"]([^'"]*)['"]|element=\{\s*<\s*([a-zA-Z0-9_]+)/g;
28
+ let match;
29
+ const pathStack = [];
30
+ let currentPath = null;
31
+ let currentElement = null;
32
+ let inOpeningTag = false;
33
+ const lines = content.split("\n");
34
+ for (const line of lines) {
35
+ if (line.includes("<Route")) {
36
+ const pathMatch = line.match(/\bpath=['"]([^'"]*)['"]/);
37
+ const elementMatch = line.match(/\belement=\{\s*<\s*([a-zA-Z0-9_]+)/);
38
+ const pathSeg = pathMatch ? pathMatch[1] : "";
39
+ const element = elementMatch ? elementMatch[1] : null;
40
+ if (line.trim().endsWith("/>")) {
41
+ const fullPath = "/" + [...pathStack, pathSeg].map((p) => p.replace(/^\/|\/$/g, "")).filter(Boolean).join("/");
42
+ if (element) {
43
+ let resolvedFile = filePath;
44
+ const resolvedPath = ctx.resolveSymbolToFile(element);
45
+ if (resolvedPath) {
46
+ resolvedFile = resolvedPath;
47
+ }
48
+ routes.push({
49
+ framework: this.name,
50
+ method: "GET",
51
+ path: fullPath,
52
+ handlerFile: resolvedFile,
53
+ handlerSymbol: element,
54
+ metadata: {
55
+ confidence: "inferred",
56
+ routeType: "client"
57
+ }
58
+ });
59
+ }
60
+ } else {
61
+ pathStack.push(pathSeg);
62
+ if (element) {
63
+ const fullPath = "/" + pathStack.map((p) => p.replace(/^\/|\/$/g, "")).filter(Boolean).join("/");
64
+ let resolvedFile = filePath;
65
+ const resolvedPath = ctx.resolveSymbolToFile(element);
66
+ if (resolvedPath) {
67
+ resolvedFile = resolvedPath;
68
+ }
69
+ routes.push({
70
+ framework: this.name,
71
+ method: "GET",
72
+ path: fullPath,
73
+ handlerFile: resolvedFile,
74
+ handlerSymbol: element,
75
+ metadata: {
76
+ confidence: "inferred",
77
+ routeType: "client"
78
+ }
79
+ });
80
+ }
81
+ }
82
+ } else if (line.includes("</Route>")) {
83
+ pathStack.pop();
84
+ }
85
+ }
86
+ const createRouterRegex = /createBrowserRouter\s*\(/;
87
+ if (createRouterRegex.test(content)) {
88
+ const configRegex = /path\s*:\s*['"]([^'"]+)['"]\s*,\s*element\s*:\s*<\s*([a-zA-Z0-9_]+)/g;
89
+ while ((match = configRegex.exec(content)) !== null) {
90
+ const routePath = match[1];
91
+ const element = match[2];
92
+ let resolvedFile = filePath;
93
+ const resolvedPath = ctx.resolveSymbolToFile(element);
94
+ if (resolvedPath) {
95
+ resolvedFile = resolvedPath;
96
+ }
97
+ routes.push({
98
+ framework: this.name,
99
+ method: "GET",
100
+ path: routePath,
101
+ handlerFile: resolvedFile,
102
+ handlerSymbol: element,
103
+ metadata: {
104
+ confidence: "inferred",
105
+ routeType: "client"
106
+ }
107
+ });
108
+ }
109
+ }
110
+ return routes;
111
+ }
112
+ }
113
+ export {
114
+ ReactRouterDetector
115
+ };
@@ -0,0 +1,11 @@
1
+ import { FrameworkDetector, ScanContext, RouteBinding } from '../../types.js';
2
+
3
+ declare class RustDetector implements FrameworkDetector {
4
+ readonly name = "rust-web";
5
+ readonly language = "rust";
6
+ readonly filePattern: RegExp;
7
+ detect(projectRoot: string, files: string[]): Promise<boolean>;
8
+ extractRoutes(filePath: string, content: string, ctx: ScanContext): Promise<RouteBinding[]>;
9
+ }
10
+
11
+ export { RustDetector };
@@ -0,0 +1,59 @@
1
+ import { join } from "node:path";
2
+ import { existsSync } from "node:fs";
3
+ class RustDetector {
4
+ name = "rust-web";
5
+ language = "rust";
6
+ filePattern = /\.rs$/;
7
+ async detect(projectRoot, files) {
8
+ const cargoPath = join(projectRoot, "Cargo.toml");
9
+ return existsSync(cargoPath) || files.some((f) => f.endsWith(".rs"));
10
+ }
11
+ async extractRoutes(filePath, content, ctx) {
12
+ const routes = [];
13
+ const axumRegex = /\.route\s*\(\s*['"]([^'"]+)['"]\s*,\s*([a-z0-9_]+)\s*\(\s*([a-zA-Z0-9_:]+)\s*\)\s*\)/g;
14
+ let match;
15
+ while ((match = axumRegex.exec(content)) !== null) {
16
+ const pathVal = match[1];
17
+ const verb = match[2].toUpperCase();
18
+ const handlerSymbol = match[3];
19
+ let resolvedFile = filePath;
20
+ const resolvedPath = ctx.resolveSymbolToFile(handlerSymbol);
21
+ if (resolvedPath) {
22
+ resolvedFile = resolvedPath;
23
+ }
24
+ routes.push({
25
+ framework: this.name,
26
+ method: verb,
27
+ path: pathVal,
28
+ handlerFile: resolvedFile,
29
+ handlerSymbol,
30
+ metadata: {
31
+ confidence: "inferred",
32
+ routeType: "server"
33
+ }
34
+ });
35
+ }
36
+ const attrRegex = /#\[(get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]\s*\)\]\s*(?:pub\s+)?(?:async\s+)?fn\s+([a-zA-Z0-9_]+)\b/g;
37
+ while ((match = attrRegex.exec(content)) !== null) {
38
+ const verb = match[1].toUpperCase();
39
+ let pathVal = match[2];
40
+ const handlerSymbol = match[3];
41
+ pathVal = pathVal.replace(/<([a-zA-Z0-9_]+)>/g, "{$1}");
42
+ routes.push({
43
+ framework: this.name,
44
+ method: verb,
45
+ path: pathVal,
46
+ handlerFile: filePath,
47
+ handlerSymbol,
48
+ metadata: {
49
+ confidence: "inferred",
50
+ routeType: "server"
51
+ }
52
+ });
53
+ }
54
+ return routes;
55
+ }
56
+ }
57
+ export {
58
+ RustDetector
59
+ };
@@ -0,0 +1,11 @@
1
+ import { FrameworkDetector, ScanContext, RouteBinding } from '../../types.js';
2
+
3
+ declare class SpringDetector implements FrameworkDetector {
4
+ readonly name = "spring";
5
+ readonly language = "java";
6
+ readonly filePattern: RegExp;
7
+ detect(projectRoot: string, files: string[]): Promise<boolean>;
8
+ extractRoutes(filePath: string, content: string, ctx: ScanContext): Promise<RouteBinding[]>;
9
+ }
10
+
11
+ export { SpringDetector };
@@ -0,0 +1,56 @@
1
+ import { join } from "node:path";
2
+ import { existsSync } from "node:fs";
3
+ class SpringDetector {
4
+ name = "spring";
5
+ language = "java";
6
+ filePattern = /\.(java|kt)$/;
7
+ async detect(projectRoot, files) {
8
+ const hasPom = existsSync(join(projectRoot, "pom.xml"));
9
+ const hasGradle = existsSync(join(projectRoot, "build.gradle"));
10
+ if (hasPom || hasGradle) return true;
11
+ return files.some((f) => f.endsWith(".java") || f.endsWith(".kt"));
12
+ }
13
+ async extractRoutes(filePath, content, ctx) {
14
+ const routes = [];
15
+ if (!content.includes("Controller") && !content.includes("RequestMapping")) {
16
+ return [];
17
+ }
18
+ let classPrefix = "";
19
+ const classMappingMatch = content.match(/@RequestMapping\s*\(\s*['"]([^'"]+)['"]\s*\)/);
20
+ if (classMappingMatch) {
21
+ classPrefix = classMappingMatch[1];
22
+ } else {
23
+ const restControllerMatch = content.match(/@RestController\s*\(\s*['"]([^'"]+)['"]\s*\)/);
24
+ if (restControllerMatch) {
25
+ classPrefix = restControllerMatch[1];
26
+ }
27
+ }
28
+ const classDeclMatch = content.match(/\bclass\s+([a-zA-Z0-9_]+)\b/);
29
+ const className = classDeclMatch ? classDeclMatch[1] : "Controller";
30
+ const mappingRegex = /@(Get|Post|Put|Delete|Patch)Mapping(?:\s*\(\s*['"]([^'"]+)['"]\s*\))?[^({]*?\b([a-zA-Z0-9_]+)\s*\(/g;
31
+ let match;
32
+ while ((match = mappingRegex.exec(content)) !== null) {
33
+ const mappingType = match[1].toUpperCase();
34
+ const mappingPath = match[2] || "";
35
+ const methodName = match[3];
36
+ const cleanClassPrefix = classPrefix.replace(/^\/|\/$/g, "");
37
+ const cleanMethodPath = mappingPath.replace(/^\/|\/$/g, "");
38
+ const combinedPath = "/" + [cleanClassPrefix, cleanMethodPath].filter(Boolean).join("/");
39
+ routes.push({
40
+ framework: this.name,
41
+ method: mappingType,
42
+ path: combinedPath,
43
+ handlerFile: filePath,
44
+ handlerSymbol: `${className}.${methodName}`,
45
+ metadata: {
46
+ confidence: "inferred",
47
+ routeType: "server"
48
+ }
49
+ });
50
+ }
51
+ return routes;
52
+ }
53
+ }
54
+ export {
55
+ SpringDetector
56
+ };
@@ -0,0 +1,11 @@
1
+ import { FrameworkDetector, ScanContext, RouteBinding } from '../../types.js';
2
+
3
+ declare class SvelteKitDetector implements FrameworkDetector {
4
+ readonly name = "sveltekit";
5
+ readonly language = "typescript";
6
+ readonly filePattern: RegExp;
7
+ detect(projectRoot: string, files: string[]): Promise<boolean>;
8
+ extractRoutes(filePath: string, content: string, ctx: ScanContext): Promise<RouteBinding[]>;
9
+ }
10
+
11
+ export { SvelteKitDetector };
@@ -0,0 +1,154 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ class SvelteKitDetector {
5
+ name = "sveltekit";
6
+ language = "typescript";
7
+ filePattern = /\.(svelte|ts|js)$/;
8
+ async detect(projectRoot, files) {
9
+ const svelteConfigPath = join(projectRoot, "svelte.config.js");
10
+ if (existsSync(svelteConfigPath)) {
11
+ return true;
12
+ }
13
+ const packageJsonPath = join(projectRoot, "package.json");
14
+ if (existsSync(packageJsonPath)) {
15
+ try {
16
+ const pkg = JSON.parse(await readFile(packageJsonPath, "utf-8"));
17
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
18
+ if (deps && (deps["@sveltejs/kit"] || deps["svelte"])) {
19
+ return true;
20
+ }
21
+ } catch {
22
+ }
23
+ }
24
+ return false;
25
+ }
26
+ async extractRoutes(filePath, content, ctx) {
27
+ const routes = [];
28
+ const pathNormalized = filePath.replace(/\\/g, "/");
29
+ if (pathNormalized.includes("src/routes/")) {
30
+ const routesIndex = pathNormalized.indexOf("src/routes/");
31
+ let routePart = pathNormalized.substring(routesIndex + 11);
32
+ if (routePart.endsWith("+page.svelte")) {
33
+ routePart = routePart.replace(/\/\+page\.svelte$/, "");
34
+ let routePath = "/" + routePart;
35
+ if (routePath === "/+") routePath = "/";
36
+ if (routePath === "") routePath = "/";
37
+ routePath = routePath.replace(/\[([a-zA-Z0-9_]+)\]/g, "{$1}");
38
+ routes.push({
39
+ framework: this.name,
40
+ method: "GET",
41
+ path: routePath,
42
+ handlerFile: filePath,
43
+ handlerSymbol: "default",
44
+ metadata: {
45
+ confidence: "inferred",
46
+ routeType: "client"
47
+ }
48
+ });
49
+ }
50
+ if (routePart.endsWith("+server.ts") || routePart.endsWith("+server.js")) {
51
+ routePart = routePart.replace(/\/\+server\.[a-zA-Z0-9]+$/, "");
52
+ let routePath = "/" + routePart;
53
+ if (routePath === "/+") routePath = "/";
54
+ if (routePath === "") routePath = "/";
55
+ routePath = routePath.replace(/\[([a-zA-Z0-9_]+)\]/g, "{$1}");
56
+ const verbRegex = /export\s+(?:const|async\s+function)\s+(GET|POST|PUT|DELETE|PATCH)\b/g;
57
+ let match;
58
+ while ((match = verbRegex.exec(content)) !== null) {
59
+ const verb = match[1];
60
+ routes.push({
61
+ framework: this.name,
62
+ method: verb,
63
+ path: routePath,
64
+ handlerFile: filePath,
65
+ handlerSymbol: verb,
66
+ metadata: {
67
+ confidence: "inferred",
68
+ routeType: "server"
69
+ }
70
+ });
71
+ }
72
+ }
73
+ if (routePart.endsWith("+page.server.ts") || routePart.endsWith("+page.server.js")) {
74
+ routePart = routePart.replace(/\/\+page\.server\.[a-zA-Z0-9]+$/, "");
75
+ let routePath = "/" + routePart;
76
+ if (routePath === "/+") routePath = "/";
77
+ if (routePath === "") routePath = "/";
78
+ routePath = routePath.replace(/\[([a-zA-Z0-9_]+)\]/g, "{$1}");
79
+ const actionsIndex = content.indexOf("export const actions");
80
+ if (actionsIndex !== -1) {
81
+ const braceStart = content.indexOf("{", actionsIndex);
82
+ if (braceStart !== -1) {
83
+ const bodyObj = getBracedContent(content, braceStart);
84
+ if (bodyObj) {
85
+ const actionKeysRegex = /\b([a-zA-Z0-9_]+)\s*:/g;
86
+ let keyMatch;
87
+ while ((keyMatch = actionKeysRegex.exec(bodyObj.content)) !== null) {
88
+ const actionName = keyMatch[1];
89
+ routes.push({
90
+ framework: this.name,
91
+ method: "POST",
92
+ path: actionName === "default" ? routePath : `${routePath}?/${actionName}`,
93
+ handlerFile: filePath,
94
+ handlerSymbol: `actions.${actionName}`,
95
+ metadata: {
96
+ confidence: "inferred",
97
+ routeType: "server"
98
+ }
99
+ });
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ return routes;
107
+ }
108
+ }
109
+ function getBracedContent(str, startIndex) {
110
+ let depth = 0;
111
+ let inString = false;
112
+ let stringChar = "";
113
+ let escape = false;
114
+ for (let i = startIndex; i < str.length; i++) {
115
+ const char = str[i];
116
+ if (escape) {
117
+ escape = false;
118
+ continue;
119
+ }
120
+ if (char === "\\") {
121
+ escape = true;
122
+ continue;
123
+ }
124
+ if (inString) {
125
+ if (char === stringChar) {
126
+ inString = false;
127
+ }
128
+ continue;
129
+ }
130
+ if (char === "'" || char === '"' || char === "`") {
131
+ inString = true;
132
+ stringChar = char;
133
+ continue;
134
+ }
135
+ if (char === "{") {
136
+ depth++;
137
+ if (depth === 1) {
138
+ startIndex = i + 1;
139
+ }
140
+ } else if (char === "}") {
141
+ depth--;
142
+ if (depth === 0) {
143
+ return {
144
+ content: str.substring(startIndex, i),
145
+ endIndex: i + 1
146
+ };
147
+ }
148
+ }
149
+ }
150
+ return null;
151
+ }
152
+ export {
153
+ SvelteKitDetector
154
+ };
@@ -0,0 +1,13 @@
1
+ import { FrameworkDetector, ScanContext, RouteBinding, HookBinding } from '../../types.js';
2
+
3
+ declare class SymfonyDetector implements FrameworkDetector {
4
+ readonly name = "symfony";
5
+ readonly language = "php";
6
+ readonly filePattern: RegExp;
7
+ detect(projectRoot: string, files: string[]): Promise<boolean>;
8
+ extractRoutes(filePath: string, content: string, ctx: ScanContext): Promise<RouteBinding[]>;
9
+ private createYamlRouteBindings;
10
+ extractHooks(filePath: string, content: string, ctx: ScanContext): Promise<HookBinding[]>;
11
+ }
12
+
13
+ export { SymfonyDetector };