@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,175 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ class SymfonyDetector {
5
+ name = "symfony";
6
+ language = "php";
7
+ filePattern = /\.(php|yaml|yml)$/;
8
+ async detect(projectRoot, files) {
9
+ const lockPath = join(projectRoot, "symfony.lock");
10
+ if (existsSync(lockPath)) return true;
11
+ const composerPath = join(projectRoot, "composer.json");
12
+ if (existsSync(composerPath)) {
13
+ try {
14
+ const pkg = JSON.parse(await readFile(composerPath, "utf-8"));
15
+ const reqs = { ...pkg.require, ...pkg["require-dev"] };
16
+ if (reqs && Object.keys(reqs).some((k) => k.startsWith("symfony/"))) {
17
+ return true;
18
+ }
19
+ } catch {
20
+ }
21
+ }
22
+ return files.some((f) => f.includes("config/routes.yaml") || f.includes("src/Controller/"));
23
+ }
24
+ async extractRoutes(filePath, content, ctx) {
25
+ const routes = [];
26
+ if (filePath.endsWith("routes.yaml") || filePath.endsWith("routes.yml")) {
27
+ const lines2 = content.split("\n");
28
+ let currentPath = "";
29
+ let currentController = "";
30
+ let currentMethods = [];
31
+ for (const line of lines2) {
32
+ if (/^[a-zA-Z0-9_.-]+:/.test(line)) {
33
+ if (currentPath && currentController) {
34
+ routes.push(...this.createYamlRouteBindings(currentPath, currentController, currentMethods, filePath, ctx));
35
+ }
36
+ currentPath = "";
37
+ currentController = "";
38
+ currentMethods = [];
39
+ continue;
40
+ }
41
+ const pathMatch = line.match(/^\s+path:\s*['"]?([^'"]+)['"]?/);
42
+ if (pathMatch) {
43
+ currentPath = pathMatch[1];
44
+ continue;
45
+ }
46
+ const controllerMatch = line.match(/^\s+controller:\s*['"]?([^'"]+)['"]?/);
47
+ if (controllerMatch) {
48
+ currentController = controllerMatch[1];
49
+ continue;
50
+ }
51
+ const methodsMatch = line.match(/^\s+methods:\s*\[?([^\]]+)\]?/);
52
+ if (methodsMatch) {
53
+ currentMethods = methodsMatch[1].split(",").map((m) => m.trim().replace(/['"]/g, "").toUpperCase());
54
+ }
55
+ }
56
+ if (currentPath && currentController) {
57
+ routes.push(...this.createYamlRouteBindings(currentPath, currentController, currentMethods, filePath, ctx));
58
+ }
59
+ return routes;
60
+ }
61
+ if (!filePath.endsWith(".php")) {
62
+ return [];
63
+ }
64
+ let classPrefix = "";
65
+ const classRouteMatch = content.match(/(?:#\[Route|@Route)\s*\(\s*['"]([^'"]+)['"]/);
66
+ const classDeclMatch = content.match(/\bclass\s+([a-zA-Z0-9_]+)\b/);
67
+ if (!classDeclMatch) return [];
68
+ const className = classDeclMatch[1];
69
+ if (classRouteMatch) {
70
+ const routeIndex = content.indexOf(classRouteMatch[0]);
71
+ const classIndex = content.indexOf(classDeclMatch[0]);
72
+ if (routeIndex !== -1 && routeIndex < classIndex) {
73
+ classPrefix = classRouteMatch[1];
74
+ }
75
+ }
76
+ const lines = content.split("\n");
77
+ let pendingRoute = null;
78
+ for (const line of lines) {
79
+ const routeAttrMatch = line.match(/(?:#\[Route|@Route)\s*\(\s*['"]([^'"]+)['"]/);
80
+ if (routeAttrMatch) {
81
+ const pathVal = routeAttrMatch[1];
82
+ const methods = [];
83
+ const methodsMatch = line.match(/methods\s*[=:]\s*\[?([^\]}]+)\]?/);
84
+ if (methodsMatch) {
85
+ methods.push(...methodsMatch[1].split(",").map((m) => m.trim().replace(/['"{}]+/g, "").toUpperCase()));
86
+ }
87
+ const nameMatch = line.match(/name\s*[=:]\s*['"]([^'"]+)['"]/);
88
+ const routeName = nameMatch ? nameMatch[1] : void 0;
89
+ pendingRoute = { path: pathVal, methods, name: routeName };
90
+ continue;
91
+ }
92
+ if (pendingRoute) {
93
+ const methodDeclMatch = line.match(/\bfunction\s+([a-zA-Z0-9_]+)\s*\(/);
94
+ if (methodDeclMatch) {
95
+ const methodName = methodDeclMatch[1];
96
+ const cleanClassPrefix = classPrefix.replace(/^\/|\/$/g, "");
97
+ const cleanMethodPath = pendingRoute.path.replace(/^\/|\/$/g, "");
98
+ const combinedPath = "/" + [cleanClassPrefix, cleanMethodPath].filter(Boolean).join("/");
99
+ const verbs = pendingRoute.methods.length > 0 ? pendingRoute.methods : ["GET"];
100
+ for (const verb of verbs) {
101
+ routes.push({
102
+ framework: this.name,
103
+ method: verb,
104
+ path: combinedPath,
105
+ handlerFile: filePath,
106
+ handlerSymbol: `${className}::${methodName}`,
107
+ metadata: {
108
+ confidence: "inferred",
109
+ routeType: "server",
110
+ routeName: pendingRoute.name
111
+ }
112
+ });
113
+ }
114
+ pendingRoute = null;
115
+ }
116
+ }
117
+ }
118
+ return routes;
119
+ }
120
+ createYamlRouteBindings(path, controller, methods, filePath, ctx) {
121
+ const bindings = [];
122
+ const parts = controller.split("::");
123
+ const controllerClass = parts[0];
124
+ const controllerMethod = parts[1] || "default";
125
+ const symbol = controllerClass.split("\\").pop() || "Controller";
126
+ let resolvedFile = filePath;
127
+ const resolvedPath = ctx.resolveSymbolToFile(symbol);
128
+ if (resolvedPath) {
129
+ resolvedFile = resolvedPath;
130
+ }
131
+ const verbs = methods.length > 0 ? methods : ["ALL"];
132
+ for (const verb of verbs) {
133
+ bindings.push({
134
+ framework: this.name,
135
+ method: verb,
136
+ path,
137
+ handlerFile: resolvedFile,
138
+ handlerSymbol: `${symbol}::${controllerMethod}`,
139
+ metadata: {
140
+ confidence: "inferred",
141
+ routeType: "server"
142
+ }
143
+ });
144
+ }
145
+ return bindings;
146
+ }
147
+ async extractHooks(filePath, content, ctx) {
148
+ if (!filePath.endsWith(".php")) {
149
+ return [];
150
+ }
151
+ if (!content.includes("getSubscribedEvents")) {
152
+ return [];
153
+ }
154
+ const hooks = [];
155
+ const eventRegex = /['"]([^'"]+)['"]\s*=>\s*(?:['"]([^'"]+)['"]|\[\s*['"]([^'"]+)['"])/g;
156
+ let match;
157
+ while ((match = eventRegex.exec(content)) !== null) {
158
+ const eventName = match[1];
159
+ const handlerSymbol = match[2] || match[3];
160
+ if (handlerSymbol) {
161
+ hooks.push({
162
+ framework: this.name,
163
+ hookName: eventName,
164
+ handlerFile: filePath,
165
+ handlerSymbol,
166
+ hookType: "event_subscriber"
167
+ });
168
+ }
169
+ }
170
+ return hooks;
171
+ }
172
+ }
173
+ export {
174
+ SymfonyDetector
175
+ };
@@ -0,0 +1,12 @@
1
+ import { FrameworkDetector, ScanContext, RouteBinding } from '../../types.js';
2
+
3
+ declare class TanstackRouterDetector implements FrameworkDetector {
4
+ readonly name = "tanstack-router";
5
+ readonly language = "typescript";
6
+ readonly filePattern: RegExp;
7
+ private projectFiles;
8
+ detect(projectRoot: string, files: string[]): Promise<boolean>;
9
+ extractRoutes(filePath: string, content: string, ctx: ScanContext): Promise<RouteBinding[]>;
10
+ }
11
+
12
+ export { TanstackRouterDetector };
@@ -0,0 +1,80 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ class TanstackRouterDetector {
5
+ name = "tanstack-router";
6
+ language = "typescript";
7
+ filePattern = /\.(js|jsx|ts|tsx)$/;
8
+ projectFiles = [];
9
+ async detect(projectRoot, files) {
10
+ this.projectFiles = files;
11
+ const packageJsonPath = join(projectRoot, "package.json");
12
+ if (existsSync(packageJsonPath)) {
13
+ try {
14
+ const pkg = JSON.parse(await readFile(packageJsonPath, "utf-8"));
15
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
16
+ if (deps && deps["@tanstack/react-router"]) {
17
+ return true;
18
+ }
19
+ } catch {
20
+ }
21
+ }
22
+ return false;
23
+ }
24
+ async extractRoutes(filePath, content, ctx) {
25
+ const routes = [];
26
+ if (filePath.includes("routes/")) {
27
+ const routesDirIndex = filePath.indexOf("routes/");
28
+ let routePart = filePath.substring(routesDirIndex + 7);
29
+ routePart = routePart.replace(/\.[a-zA-Z0-9]+$/, "");
30
+ let routePath = "/" + routePart.replace(/\./g, "/");
31
+ routePath = routePath.replace(/\/index$/, "");
32
+ if (routePath === "") routePath = "/";
33
+ routePath = routePath.replace(/\$([a-zA-Z0-9_]+)/g, "{$1}");
34
+ routes.push({
35
+ framework: this.name,
36
+ method: "GET",
37
+ path: routePath,
38
+ handlerFile: filePath,
39
+ handlerSymbol: "Route",
40
+ metadata: {
41
+ confidence: "inferred",
42
+ routeType: "client"
43
+ }
44
+ });
45
+ }
46
+ const routeRegex = /new\s+Route\s*\(\s*\{([^}]+)\}/g;
47
+ let match;
48
+ while ((match = routeRegex.exec(content)) !== null) {
49
+ const configBody = match[1];
50
+ const pathMatch = configBody.match(/\bpath\s*:\s*['"]([^'"]+)['"]/);
51
+ const compMatch = configBody.match(/\bcomponent\s*:\s*([a-zA-Z0-9_]+)/);
52
+ if (pathMatch) {
53
+ const routePath = pathMatch[1];
54
+ const componentSymbol = compMatch ? compMatch[1] : void 0;
55
+ let resolvedFile = filePath;
56
+ if (componentSymbol) {
57
+ const resolvedPath = ctx.resolveSymbolToFile(componentSymbol);
58
+ if (resolvedPath) {
59
+ resolvedFile = resolvedPath;
60
+ }
61
+ }
62
+ routes.push({
63
+ framework: this.name,
64
+ method: "GET",
65
+ path: routePath,
66
+ handlerFile: resolvedFile,
67
+ handlerSymbol: componentSymbol || "Route",
68
+ metadata: {
69
+ confidence: "inferred",
70
+ routeType: "client"
71
+ }
72
+ });
73
+ }
74
+ }
75
+ return routes;
76
+ }
77
+ }
78
+ export {
79
+ TanstackRouterDetector
80
+ };
@@ -0,0 +1,11 @@
1
+ import { FrameworkDetector, ScanContext, RouteBinding } from '../../types.js';
2
+
3
+ declare class VaporDetector implements FrameworkDetector {
4
+ readonly name = "vapor";
5
+ readonly language = "swift";
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 { VaporDetector };
@@ -0,0 +1,52 @@
1
+ import { join } from "node:path";
2
+ import { existsSync } from "node:fs";
3
+ class VaporDetector {
4
+ name = "vapor";
5
+ language = "swift";
6
+ filePattern = /\.swift$/;
7
+ async detect(projectRoot, files) {
8
+ const pkgSwift = join(projectRoot, "Package.swift");
9
+ return existsSync(pkgSwift) || files.some((f) => f.endsWith(".swift"));
10
+ }
11
+ async extractRoutes(filePath, content, ctx) {
12
+ const routes = [];
13
+ if (!content.includes("import Vapor") && !content.includes("routes(")) {
14
+ return [];
15
+ }
16
+ const routeRegex = /\b[a-zA-Z0-9_]+\.(get|post|put|delete|patch)\s*\(\s*([^)]+)\s*,\s*use\s*:\s*([a-zA-Z0-9_.]+)\s*\)/g;
17
+ let match;
18
+ while ((match = routeRegex.exec(content)) !== null) {
19
+ const verb = match[1].toUpperCase();
20
+ const rawSegments = match[2];
21
+ const handlerSymbol = match[3];
22
+ const segments = rawSegments.split(",").map((s) => s.trim()).map((s) => s.replace(/['"]/g, "")).filter((s) => s !== "");
23
+ const mappedSegments = segments.map((seg) => {
24
+ if (seg.startsWith(":")) {
25
+ return `{${seg.substring(1)}}`;
26
+ }
27
+ return seg;
28
+ });
29
+ const pathVal = "/" + mappedSegments.join("/");
30
+ let resolvedFile = filePath;
31
+ const resolvedPath = ctx.resolveSymbolToFile(handlerSymbol);
32
+ if (resolvedPath) {
33
+ resolvedFile = resolvedPath;
34
+ }
35
+ routes.push({
36
+ framework: this.name,
37
+ method: verb,
38
+ path: pathVal,
39
+ handlerFile: resolvedFile,
40
+ handlerSymbol,
41
+ metadata: {
42
+ confidence: "inferred",
43
+ routeType: "server"
44
+ }
45
+ });
46
+ }
47
+ return routes;
48
+ }
49
+ }
50
+ export {
51
+ VaporDetector
52
+ };
@@ -0,0 +1,12 @@
1
+ import { FrameworkDetector, ScanContext, RouteBinding } from '../../types.js';
2
+
3
+ declare class VueRouterDetector implements FrameworkDetector {
4
+ readonly name = "vue-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
+ private parseRoutesArray;
10
+ }
11
+
12
+ export { VueRouterDetector };
@@ -0,0 +1,237 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join, dirname } from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ class VueRouterDetector {
5
+ name = "vue-router";
6
+ language = "typescript";
7
+ filePattern = /\.(js|ts|vue)$/;
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["vue-router"] || deps["vue"])) {
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("VueRouter") && !content.includes("createRouter") && !content.includes("routes") && !content.includes("routerMap") && !content.includes("routeMap")) {
25
+ return [];
26
+ }
27
+ let index = 0;
28
+ while (true) {
29
+ const match = content.substring(index).match(/\b[a-zA-Z0-9_]*(?:routes|Routes|routesList|routerMap|routeMap|RouteMap)(?:\s*:[^=]+)?\s*[=:]\s*\[/);
30
+ if (!match) break;
31
+ const startOfArray = index + match.index + match[0].length - 1;
32
+ const arrayObj = getBracketedContent(content, startOfArray);
33
+ if (!arrayObj) {
34
+ index = startOfArray + 1;
35
+ continue;
36
+ }
37
+ const parsedRoutes = this.parseRoutesArray(arrayObj.content, filePath, ctx);
38
+ routes.push(...parsedRoutes);
39
+ index = arrayObj.endIndex;
40
+ }
41
+ return routes;
42
+ }
43
+ parseRoutesArray(content, filePath, ctx, prefixParts = []) {
44
+ const routes = [];
45
+ let index = 0;
46
+ while (index < content.length) {
47
+ const startOfBrace = content.indexOf("{", index);
48
+ if (startOfBrace === -1) break;
49
+ const obj = getBracedContent(content, startOfBrace);
50
+ if (!obj) {
51
+ index = startOfBrace + 1;
52
+ continue;
53
+ }
54
+ const objBody = obj.content;
55
+ const pathMatch = objBody.match(/\bpath\s*:\s*['"`]([^'"`]*)['"`]/);
56
+ const pathVal = pathMatch ? pathMatch[1] : "";
57
+ const componentsList = [];
58
+ const compMatch = objBody.match(/\bcomponent\s*:\s*([a-zA-Z0-9_]+)\b(?!\s*=>)/);
59
+ if (compMatch && compMatch[1] !== "import" && compMatch[1] !== "require") {
60
+ const componentSymbol = compMatch[1];
61
+ let resolvedFile = filePath;
62
+ const resolvedPath = ctx.resolveSymbolToFile(componentSymbol);
63
+ if (resolvedPath) {
64
+ resolvedFile = resolvedPath;
65
+ }
66
+ componentsList.push({ symbol: componentSymbol, file: resolvedFile });
67
+ }
68
+ const importMatches = objBody.matchAll(/import\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g);
69
+ for (const impMatch of importMatches) {
70
+ const importPath = impMatch[1];
71
+ let resolvedFile = filePath;
72
+ if (importPath.startsWith(".")) {
73
+ resolvedFile = join(dirname(filePath), importPath);
74
+ }
75
+ const symbol = importPath.split("/").pop()?.replace(/\.[a-zA-Z0-9]+$/, "") || "Component";
76
+ componentsList.push({ symbol, file: resolvedFile });
77
+ }
78
+ const requireMatches = objBody.matchAll(/require\s*\(\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g);
79
+ for (const reqMatch of requireMatches) {
80
+ const importPath = reqMatch[1];
81
+ let resolvedFile = filePath;
82
+ if (importPath.startsWith(".")) {
83
+ resolvedFile = join(dirname(filePath), importPath);
84
+ }
85
+ const symbol = importPath.split("/").pop()?.replace(/\.[a-zA-Z0-9]+$/, "") || "Component";
86
+ componentsList.push({ symbol, file: resolvedFile });
87
+ }
88
+ const componentsMatch = objBody.match(/\bcomponents\s*:\s*\{([^}]+)\}/);
89
+ if (componentsMatch) {
90
+ const compsBody = componentsMatch[1];
91
+ const symbolRegex = /\b[a-zA-Z0-9_]+\s*:\s*([a-zA-Z0-9_]+)\b(?!\s*=>)/g;
92
+ let match;
93
+ while ((match = symbolRegex.exec(compsBody)) !== null) {
94
+ const compSym = match[1];
95
+ if (compSym !== "import" && compSym !== "require") {
96
+ let resolvedFile = filePath;
97
+ const resolvedPath = ctx.resolveSymbolToFile(compSym);
98
+ if (resolvedPath) {
99
+ resolvedFile = resolvedPath;
100
+ }
101
+ componentsList.push({ symbol: compSym, file: resolvedFile });
102
+ }
103
+ }
104
+ }
105
+ const cleanParentPrefix = prefixParts.map((p) => p.replace(/^\/|\/$/g, "")).filter(Boolean);
106
+ const cleanPath = pathVal.replace(/^\/|\/$/g, "");
107
+ const fullPath = "/" + [...cleanParentPrefix, cleanPath].filter(Boolean).join("/");
108
+ if (componentsList.length > 0) {
109
+ for (const comp of componentsList) {
110
+ routes.push({
111
+ framework: this.name,
112
+ method: "GET",
113
+ path: fullPath,
114
+ handlerFile: comp.file,
115
+ handlerSymbol: comp.symbol,
116
+ metadata: {
117
+ confidence: "inferred",
118
+ routeType: "client"
119
+ }
120
+ });
121
+ }
122
+ } else if (objBody.includes("children")) {
123
+ routes.push({
124
+ framework: this.name,
125
+ method: "GET",
126
+ path: fullPath,
127
+ handlerFile: filePath,
128
+ handlerSymbol: "Component",
129
+ metadata: {
130
+ confidence: "inferred",
131
+ routeType: "client"
132
+ }
133
+ });
134
+ }
135
+ const childrenMatch = objBody.match(/\bchildren\s*:\s*\[/);
136
+ if (childrenMatch) {
137
+ const childrenStart = objBody.indexOf("children") + childrenMatch[0].length - 1;
138
+ const childrenArray = getBracketedContent(objBody, childrenStart);
139
+ if (childrenArray) {
140
+ const childRoutes = this.parseRoutesArray(childrenArray.content, filePath, ctx, [...prefixParts, pathVal]);
141
+ routes.push(...childRoutes);
142
+ }
143
+ }
144
+ index = obj.endIndex;
145
+ }
146
+ return routes;
147
+ }
148
+ }
149
+ function getBracketedContent(str, startIndex) {
150
+ let depth = 0;
151
+ let inString = false;
152
+ let stringChar = "";
153
+ let escape = false;
154
+ for (let i = startIndex; i < str.length; i++) {
155
+ const char = str[i];
156
+ if (escape) {
157
+ escape = false;
158
+ continue;
159
+ }
160
+ if (char === "\\") {
161
+ escape = true;
162
+ continue;
163
+ }
164
+ if (inString) {
165
+ if (char === stringChar) {
166
+ inString = false;
167
+ }
168
+ continue;
169
+ }
170
+ if (char === "'" || char === '"' || char === "`") {
171
+ inString = true;
172
+ stringChar = char;
173
+ continue;
174
+ }
175
+ if (char === "[") {
176
+ depth++;
177
+ if (depth === 1) {
178
+ startIndex = i + 1;
179
+ }
180
+ } else if (char === "]") {
181
+ depth--;
182
+ if (depth === 0) {
183
+ return {
184
+ content: str.substring(startIndex, i),
185
+ endIndex: i + 1
186
+ };
187
+ }
188
+ }
189
+ }
190
+ return null;
191
+ }
192
+ function getBracedContent(str, startIndex) {
193
+ let depth = 0;
194
+ let inString = false;
195
+ let stringChar = "";
196
+ let escape = false;
197
+ for (let i = startIndex; i < str.length; i++) {
198
+ const char = str[i];
199
+ if (escape) {
200
+ escape = false;
201
+ continue;
202
+ }
203
+ if (char === "\\") {
204
+ escape = true;
205
+ continue;
206
+ }
207
+ if (inString) {
208
+ if (char === stringChar) {
209
+ inString = false;
210
+ }
211
+ continue;
212
+ }
213
+ if (char === "'" || char === '"' || char === "`") {
214
+ inString = true;
215
+ stringChar = char;
216
+ continue;
217
+ }
218
+ if (char === "{") {
219
+ depth++;
220
+ if (depth === 1) {
221
+ startIndex = i + 1;
222
+ }
223
+ } else if (char === "}") {
224
+ depth--;
225
+ if (depth === 0) {
226
+ return {
227
+ content: str.substring(startIndex, i),
228
+ endIndex: i + 1
229
+ };
230
+ }
231
+ }
232
+ }
233
+ return null;
234
+ }
235
+ export {
236
+ VueRouterDetector
237
+ };
@@ -0,0 +1,13 @@
1
+ import { FrameworkDetector, ScanContext, RouteBinding, HookBinding } from '../../types.js';
2
+
3
+ declare class WordPressDetector implements FrameworkDetector {
4
+ readonly name = "wordpress";
5
+ readonly language = "php";
6
+ readonly filePattern: RegExp;
7
+ private static readonly TEMPLATE_FILES;
8
+ detect(projectRoot: string, files: string[]): Promise<boolean>;
9
+ extractRoutes(filePath: string, content: string, ctx: ScanContext): Promise<RouteBinding[]>;
10
+ extractHooks(filePath: string, content: string, ctx: ScanContext): Promise<HookBinding[]>;
11
+ }
12
+
13
+ export { WordPressDetector };