seo-gravity-mcp 1.0.1 → 1.1.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 (55) hide show
  1. package/README.md +144 -9
  2. package/dist/adapters/adapterRegistry.d.ts +7 -0
  3. package/dist/adapters/adapterRegistry.js +30 -0
  4. package/dist/adapters/astroAdapter.d.ts +17 -0
  5. package/dist/adapters/astroAdapter.js +191 -0
  6. package/dist/adapters/nextAppAdapter.d.ts +17 -0
  7. package/dist/adapters/nextAppAdapter.js +265 -0
  8. package/dist/adapters/nextPagesAdapter.d.ts +17 -0
  9. package/dist/adapters/nextPagesAdapter.js +190 -0
  10. package/dist/adapters/remixAdapter.d.ts +16 -0
  11. package/dist/adapters/remixAdapter.js +118 -0
  12. package/dist/adapters/staticAdapter.d.ts +16 -0
  13. package/dist/adapters/staticAdapter.js +117 -0
  14. package/dist/adapters/svelteKitAdapter.d.ts +16 -0
  15. package/dist/adapters/svelteKitAdapter.js +122 -0
  16. package/dist/adapters/types.d.ts +33 -0
  17. package/dist/adapters/types.js +1 -0
  18. package/dist/adapters/viteReactAdapter.d.ts +16 -0
  19. package/dist/adapters/viteReactAdapter.js +172 -0
  20. package/dist/cli.d.ts +2 -0
  21. package/dist/cli.js +97 -0
  22. package/dist/index.js +140 -10
  23. package/dist/providers/providerRegistry.d.ts +65 -0
  24. package/dist/providers/providerRegistry.js +64 -0
  25. package/dist/providers/types.d.ts +22 -0
  26. package/dist/providers/types.js +1 -0
  27. package/dist/test.js +121 -59
  28. package/dist/tools/geo.js +36 -0
  29. package/dist/tools/orchestration.d.ts +29 -0
  30. package/dist/tools/orchestration.js +304 -0
  31. package/dist/types/canonical.d.ts +64 -0
  32. package/dist/types/canonical.js +1 -0
  33. package/dist/types/findings.d.ts +246 -0
  34. package/dist/types/findings.js +1 -0
  35. package/dist/types/seo.d.ts +39 -0
  36. package/dist/types/seo.js +1 -1
  37. package/dist/utils/astLocator.d.ts +14 -0
  38. package/dist/utils/astLocator.js +137 -0
  39. package/dist/utils/cacheManager.d.ts +15 -0
  40. package/dist/utils/cacheManager.js +37 -0
  41. package/dist/utils/crawlGraph.d.ts +19 -0
  42. package/dist/utils/crawlGraph.js +231 -0
  43. package/dist/utils/experimentEngine.d.ts +26 -0
  44. package/dist/utils/experimentEngine.js +41 -0
  45. package/dist/utils/findingEngine.d.ts +43 -0
  46. package/dist/utils/findingEngine.js +272 -0
  47. package/dist/utils/gitDiffEngine.d.ts +19 -0
  48. package/dist/utils/gitDiffEngine.js +72 -0
  49. package/dist/utils/opportunityEngine.d.ts +19 -0
  50. package/dist/utils/opportunityEngine.js +61 -0
  51. package/dist/utils/projectScanner.d.ts +6 -0
  52. package/dist/utils/projectScanner.js +21 -0
  53. package/dist/utils/snapshotEngine.d.ts +11 -0
  54. package/dist/utils/snapshotEngine.js +405 -0
  55. package/package.json +6 -4
package/README.md CHANGED
@@ -1,20 +1,155 @@
1
- # 🚀 SEO Gravity MCP Server
1
+ # 🚀 SEO Gravity (v1.1.0)
2
2
 
3
- The core Model Context Protocol (MCP) server for **SEO Gravity**.
3
+ <div align="center">
4
4
 
5
- ## Installation
5
+ [![npm version](https://img.shields.io/npm/v/seo-gravity-mcp?style=for-the-badge&logo=npm&color=CB3837)](https://www.npmjs.com/package/seo-gravity-mcp)
6
+ [![npm downloads](https://img.shields.io/npm/dm/seo-gravity-mcp?style=for-the-badge&logo=npm&color=CB3837)](https://www.npmjs.com/package/seo-gravity-mcp)
7
+ ![MCP Protocol](https://img.shields.io/badge/MCP-Protocol-blue?style=for-the-badge&logo=anthropic)
8
+ ![CLI Ready](https://img.shields.io/badge/CLI-CI%2FCD_Ready-success?style=for-the-badge)
9
+ ![TypeScript](https://img.shields.io/badge/TypeScript-5.7+-3178C6?style=for-the-badge&logo=typescript&logoColor=white)
10
+ ![Zero API Cost](https://img.shields.io/badge/Zero_API_Cost-100%25_Free-orange?style=for-the-badge)
11
+
12
+ **The Enterprise SEO Engineering Layer for AI Coding Agents & CI/CD Pipelines.**
13
+
14
+ [⚡ Instant Start](#-instant-start) • [💻 CLI & CI/CD](#-command-line--cicd-runner) • [🏛 5-Layer Architecture](#-5-layer-system-architecture) • [🧩 Framework Adapters](#-modular-framework-adapters) • [🛠 Tool Catalog (35 Tools)](#-complete-tool-catalog) • [📄 License](#-license)
15
+
16
+ </div>
17
+
18
+ ---
19
+
20
+ ## 🌟 What is SEO Gravity?
21
+
22
+ **SEO Gravity** is an **SEO engineering infrastructure layer** designed specifically for AI coding agents (Antigravity, Cursor, Claude Code, Windsurf, Cline, Roo Code, etc.) and automated CI/CD pipelines.
23
+
24
+ Instead of operating as a passive SEO reporting tool, SEO Gravity connects **rendered web observations** directly to **source code AST nodes and line numbers** across modern web frameworks (Next.js App/Pages Router, Astro, Vite/React, Remix, SvelteKit), generates actionable code fix blueprints, and enforces invariant-based regression checks.
25
+
26
+ ```text
27
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
28
+ │ 1. DISCOVER │ ──▶ │ 2. OBSERVE │ ──▶ │ 3. DIAGNOSE │ ──▶ │ 4. FIX │ ──▶ │ 5. VERIFY │
29
+ │ Framework │ │ Live/Dev SSR │ │ AST Line Nos │ │ AI Modifies │ │ Invariant │
30
+ │ Adapters │ │ Crawl Graph │ │ Prioritized │ │ Components │ │ Regression │
31
+ │ Source Map │ │ Provenance │ │ Action Plan │ │ Snippets │ │ Git Stamping │
32
+ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
33
+ ```
34
+
35
+ ---
36
+
37
+ ## 🏛 5-Layer System Architecture
38
+
39
+ ```text
40
+ ┌─────────────────────────────────────────────────────────────┐
41
+ │ 1. AGENT & CLI INTERFACE │
42
+ │ MCP Server (35 tools) + Standalone CLI (`seo-gravity`) │
43
+ ├─────────────────────────────────────────────────────────────┤
44
+ │ 2. INTELLIGENCE & REASONING LAYER │
45
+ │ Root-cause diagnosis, deterministic priority, fix plans │
46
+ ├─────────────────────────────────────────────────────────────┤
47
+ │ 3. ANALYSIS & INVARIANT LAYER │
48
+ │ SEO Invariants (HTTP 200, canonical, schema, noindex) │
49
+ ├─────────────────────────────────────────────────────────────┤
50
+ │ 4. OBSERVATION LAYER (Factual Raw State) │
51
+ │ Observations ≠ Findings; AST inspection, headers, DOM │
52
+ ├─────────────────────────────────────────────────────────────┤
53
+ │ 5. PROVIDER & ADAPTER LAYER │
54
+ │ Framework Adapters (Next, Astro, Vite), Providers, Cache │
55
+ └─────────────────────────────────────────────────────────────┘
56
+ ```
57
+
58
+ ---
59
+
60
+ ## 🧩 Modular Framework Adapters
61
+
62
+ | Framework Adapter | Detection | Route Discovery | AST Inspection |
63
+ | :--- | :--- | :--- | :--- |
64
+ | **`NextAppAdapter`** | `next` + `app/` | App Router hierarchy, route groups `(group)`, dynamic `[slug]` | Pinpoints `metadata` export & `generateMetadata()` lines |
65
+ | **`NextPagesAdapter`** | `next` + `pages/` | Pages router hierarchy, dynamic `[id]` | Scans `<Head>` & `<NextSeo>` JSX components |
66
+ | **`AstroAdapter`** | `astro` + `src/pages` | `.astro`, `.md`, `.mdx` pages | Scans Astro frontmatter & `<title>` tags |
67
+ | **`ViteReactAdapter`** | `vite` / `react` | `index.html` + React Router routes | Scans `<Helmet>` & route definition trees |
68
+ | **`RemixAdapter`** | `@remix-run/react` | `app/routes/` | Inspects `meta` and `links` export functions |
69
+ | **`SvelteKitAdapter`** | `@sveltejs/kit` | `src/routes/` (`+page.svelte`) | Scans `<svelte:head>` components |
70
+ | **`StaticAdapter`** | Static HTML files | Recursive HTML directory traversal | Direct DOM / meta element inspection |
71
+
72
+ ---
73
+
74
+ ## 💻 Command-Line & CI/CD Runner
75
+
76
+ SEO Gravity includes a standalone CLI binary (`seo-gravity`) for direct terminal use and GitHub Actions / CI/CD quality gates:
6
77
 
7
78
  ```bash
8
- npm install
9
- npm run build
79
+ # 1. Audit project
80
+ npx seo-gravity-mcp audit --project ./my-app
81
+
82
+ # 2. Create baseline snapshot
83
+ npx seo-gravity-mcp snapshot --project ./my-app --output baseline.json
84
+
85
+ # 3. Check for SEO regressions in CI/CD PR gate (Exits with 0 or 1)
86
+ npx seo-gravity-mcp check --project ./my-app --baseline baseline.json
87
+
88
+ # 4. Targeted Git differential audit on modified routes only
89
+ npx seo-gravity-mcp diff --project ./my-app --base-ref origin/main
10
90
  ```
11
91
 
12
- ## Running the MCP Server
92
+ ---
93
+
94
+ ## ⚡ Instant Start (MCP Server)
13
95
 
14
96
  ```bash
15
- node dist/index.js
97
+ npx -y seo-gravity-mcp
98
+ ```
99
+
100
+ ### AI Client Configurations
101
+
102
+ #### Cursor (`.cursor/mcp.json`)
103
+ ```json
104
+ {
105
+ "mcpServers": {
106
+ "seo-gravity": {
107
+ "command": "npx",
108
+ "args": ["-y", "seo-gravity-mcp"]
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ #### Claude Desktop (`claude_desktop_config.json`)
115
+ ```json
116
+ {
117
+ "mcpServers": {
118
+ "seo-gravity": {
119
+ "command": "npx",
120
+ "args": ["-y", "seo-gravity-mcp"]
121
+ }
122
+ }
123
+ }
16
124
  ```
17
125
 
18
- ## Available Tools (28 Tools)
126
+ ---
127
+
128
+ ## 🛠 Complete Tool Catalog
129
+
130
+ ### Layer 0: Agent Orchestration & Remediation (Flagship)
131
+ | Tool Name | Operational Class | Description |
132
+ | :--- | :--- | :--- |
133
+ | `seo_project_audit` | **Planning / Audit** | Scans workspace framework, discovers routes, correlates source files, builds crawl graph, and computes multidimensional SEO health. |
134
+ | `seo_diagnose` | **Planning / Diagnose** | Deep root-cause AST inspection linking an observed issue/URL back to specific source code line ranges with ready-to-use fix blueprints. |
135
+ | `seo_prioritize` | **Planning / Strategy** | Ranks findings into Quick Wins, Critical Blockers, and Architectural Improvements using `(Impact × Confidence × Reach) / Effort`. |
136
+ | `seo_fix_plan` | **Planning / Execution** | Generates an end-to-end code modification and verification plan for AI coding agents. |
137
+ | `seo_snapshot_create` | **Verification** | Creates and saves a project SEO baseline snapshot with Git commit stamping adhering to schema `seo.gravity/v1`. |
138
+ | `seo_snapshot_compare` | **Verification** | Invariant-based diffing of two snapshots to isolate resolved findings, new regressions, and multidimensional score deltas. |
139
+ | `seo_regression_check` | **Verification / CI** | Automated pass/fail regression check with alert messages for CI/CD gates and PR bots. |
140
+
141
+ ### Layers 1–8: Specialized SEO & GEO Engines
142
+ - **Layer 1 (SERP & Competitors)**: `seo_serp_analyze`, `seo_competitor_content_gap`, `seo_competitor_profile`, `seo_competitor_diff`, `seo_forum_discussions_pulse`
143
+ - **Layer 2 (GEO & AI Search)**: `seo_geo_ai_readiness_audit`, `seo_llms_txt_generate`, `seo_ai_bots_robots_audit`
144
+ - **Layer 3 (Info Gain & E-E-A-T)**: `seo_information_gain_score`, `seo_eeat_audit`
145
+ - **Layer 4 (On-Page)**: `seo_onpage_audit`, `seo_content_brief_generate`, `seo_readability_score`
146
+ - **Layer 5 (Technical SEO)**: `seo_technical_audit`, `seo_js_rendering_diff`, `seo_robots_txt_validate`, `seo_sitemap_inspect`, `seo_internal_links_analyze`
147
+ - **Layer 6 (Keywords & Intent)**: `seo_keyword_suggestions`, `seo_questions_find`, `seo_keyword_cluster`, `seo_search_intent_classify`
148
+ - **Layer 7 (Schema & Entity)**: `seo_entity_salience_map`, `seo_schema_generate`, `seo_schema_validate`
149
+ - **Layer 8 (Speed & Maintenance)**: `seo_pagespeed_audit`, `seo_indexnow_submit`, `seo_content_decay_audit`
150
+
151
+ ---
152
+
153
+ ## 📄 License
19
154
 
20
- See root [README.md](../README.md) or [SEO_GRAVITY_MCP_SPEC.md](../SEO_GRAVITY_MCP_SPEC.md) for full documentation of all 28 tools.
155
+ MIT License. Free for open-source and commercial use.
@@ -0,0 +1,7 @@
1
+ import { FrameworkAdapter } from './types.js';
2
+ export declare class AdapterRegistry {
3
+ private adapters;
4
+ getAdapterForProject(projectDir: string): FrameworkAdapter;
5
+ getAdapterById(id: string): FrameworkAdapter | undefined;
6
+ }
7
+ export declare const defaultAdapterRegistry: AdapterRegistry;
@@ -0,0 +1,30 @@
1
+ import { NextAppAdapter } from './nextAppAdapter.js';
2
+ import { NextPagesAdapter } from './nextPagesAdapter.js';
3
+ import { AstroAdapter } from './astroAdapter.js';
4
+ import { ViteReactAdapter } from './viteReactAdapter.js';
5
+ import { RemixAdapter } from './remixAdapter.js';
6
+ import { SvelteKitAdapter } from './svelteKitAdapter.js';
7
+ import { StaticAdapter } from './staticAdapter.js';
8
+ export class AdapterRegistry {
9
+ adapters = [
10
+ new NextAppAdapter(),
11
+ new NextPagesAdapter(),
12
+ new AstroAdapter(),
13
+ new RemixAdapter(),
14
+ new SvelteKitAdapter(),
15
+ new ViteReactAdapter(),
16
+ new StaticAdapter() // fallback
17
+ ];
18
+ getAdapterForProject(projectDir) {
19
+ for (const adapter of this.adapters) {
20
+ if (adapter.detect(projectDir)) {
21
+ return adapter;
22
+ }
23
+ }
24
+ return this.adapters[this.adapters.length - 1]; // fallback static
25
+ }
26
+ getAdapterById(id) {
27
+ return this.adapters.find(a => a.id === id);
28
+ }
29
+ }
30
+ export const defaultAdapterRegistry = new AdapterRegistry();
@@ -0,0 +1,17 @@
1
+ import { FrameworkAdapter, MetadataLocationInfo, CanonicalLocationInfo, SchemaLocationInfo } from './types.js';
2
+ import { DiscoveredRoute, ProjectFrameworkInfo, RouteSourceMapping } from '../types/findings.js';
3
+ export declare class AstroAdapter implements FrameworkAdapter {
4
+ id: string;
5
+ name: string;
6
+ detect(projectDir: string): boolean;
7
+ getProjectInfo(projectDir: string): ProjectFrameworkInfo;
8
+ discoverRoutes(projectDir: string): DiscoveredRoute[];
9
+ mapRouteToSource(targetUrl: string, routes: DiscoveredRoute[]): RouteSourceMapping;
10
+ findMetadataImplementation(projectDir: string, route: DiscoveredRoute): Promise<MetadataLocationInfo>;
11
+ findCanonicalDeclaration(projectDir: string, route: DiscoveredRoute): Promise<CanonicalLocationInfo>;
12
+ findSchemaDeclaration(projectDir: string, route: DiscoveredRoute): Promise<SchemaLocationInfo>;
13
+ findRobotsConfig(projectDir: string): string | null;
14
+ findSitemapConfig(projectDir: string): string | null;
15
+ findLlmsTxt(projectDir: string): string | null;
16
+ private extractDynamicParams;
17
+ }
@@ -0,0 +1,191 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ export class AstroAdapter {
4
+ id = 'astro';
5
+ name = 'Astro Framework';
6
+ detect(projectDir) {
7
+ const hasConfig = fs.existsSync(path.join(projectDir, 'astro.config.mjs')) ||
8
+ fs.existsSync(path.join(projectDir, 'astro.config.ts'));
9
+ if (hasConfig)
10
+ return true;
11
+ const pkgPath = path.join(projectDir, 'package.json');
12
+ if (fs.existsSync(pkgPath)) {
13
+ try {
14
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
15
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
16
+ return Boolean(deps['astro']);
17
+ }
18
+ catch { }
19
+ }
20
+ return false;
21
+ }
22
+ getProjectInfo(projectDir) {
23
+ const pkgPath = path.join(projectDir, 'package.json');
24
+ let pkg = {};
25
+ if (fs.existsSync(pkgPath)) {
26
+ try {
27
+ pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
28
+ }
29
+ catch { }
30
+ }
31
+ const hasSrcPages = fs.existsSync(path.join(projectDir, 'src/pages'));
32
+ const routesDir = hasSrcPages ? 'src/pages' : 'pages';
33
+ return {
34
+ framework: 'astro',
35
+ name: pkg.name || path.basename(projectDir),
36
+ version: pkg.dependencies?.astro || pkg.devDependencies?.astro,
37
+ packageManager: fs.existsSync(path.join(projectDir, 'pnpm-lock.yaml'))
38
+ ? 'pnpm'
39
+ : fs.existsSync(path.join(projectDir, 'yarn.lock'))
40
+ ? 'yarn'
41
+ : 'npm',
42
+ hasTypeScript: fs.existsSync(path.join(projectDir, 'tsconfig.json')),
43
+ hasSitemapConfig: Boolean(this.findSitemapConfig(projectDir)),
44
+ hasRobotsConfig: Boolean(this.findRobotsConfig(projectDir)),
45
+ hasLlmsTxt: Boolean(this.findLlmsTxt(projectDir)),
46
+ rootDir: path.resolve(projectDir),
47
+ routesDir,
48
+ devCommand: 'npm run dev',
49
+ defaultDevPort: 4321
50
+ };
51
+ }
52
+ discoverRoutes(projectDir) {
53
+ const info = this.getProjectInfo(projectDir);
54
+ const fullRoutesDir = path.join(projectDir, info.routesDir || 'src/pages');
55
+ const discovered = [];
56
+ if (!fs.existsSync(fullRoutesDir))
57
+ return discovered;
58
+ const scanDir = (currentDir, routePrefix) => {
59
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
60
+ for (const entry of entries) {
61
+ const entryPath = path.join(currentDir, entry.name);
62
+ if (entry.isDirectory()) {
63
+ const nextPrefix = routePrefix === '/' ? `/${entry.name}` : `${routePrefix}/${entry.name}`;
64
+ scanDir(entryPath, nextPrefix);
65
+ }
66
+ else if (entry.isFile() && /\.(astro|md|mdx)$/.test(entry.name)) {
67
+ const baseName = entry.name.replace(/\.(astro|md|mdx)$/, '');
68
+ let routePath = routePrefix;
69
+ if (baseName !== 'index') {
70
+ routePath = routePrefix === '/' ? `/${baseName}` : `${routePrefix}/${baseName}`;
71
+ }
72
+ if (routePath === '')
73
+ routePath = '/';
74
+ const relFile = path.relative(projectDir, entryPath).replace(/\\/g, '/');
75
+ const content = fs.readFileSync(entryPath, 'utf-8');
76
+ const dynamicParams = this.extractDynamicParams(routePath);
77
+ discovered.push({
78
+ routePath,
79
+ sourceFilePath: relFile,
80
+ isDynamic: dynamicParams.length > 0,
81
+ dynamicParams,
82
+ routeType: 'page',
83
+ hasMetadataExport: /title\s*=|description\s*=|astro:head|<title/.test(content),
84
+ hasGenerateMetadata: false,
85
+ hasSchemaMarkup: /application\/ld\+json/.test(content),
86
+ hasHeadComponent: /<head|astro:head|<title/.test(content),
87
+ isClientComponent: false
88
+ });
89
+ }
90
+ }
91
+ };
92
+ scanDir(fullRoutesDir, '/');
93
+ return discovered;
94
+ }
95
+ mapRouteToSource(targetUrl, routes) {
96
+ let urlPath = targetUrl;
97
+ try {
98
+ if (targetUrl.startsWith('http')) {
99
+ urlPath = new URL(targetUrl).pathname;
100
+ }
101
+ }
102
+ catch {
103
+ urlPath = targetUrl;
104
+ }
105
+ urlPath = urlPath.replace(/\/$/, '') || '/';
106
+ for (const r of routes) {
107
+ const clean = r.routePath.replace(/\/$/, '') || '/';
108
+ if (clean === urlPath) {
109
+ return {
110
+ urlPath,
111
+ matchedRoute: r,
112
+ sourceFilePath: r.sourceFilePath,
113
+ confidence: 1.0,
114
+ resolutionMethod: 'exact_match'
115
+ };
116
+ }
117
+ }
118
+ return {
119
+ urlPath,
120
+ confidence: 0.0,
121
+ resolutionMethod: 'unmapped'
122
+ };
123
+ }
124
+ async findMetadataImplementation(projectDir, route) {
125
+ const fullPath = path.join(projectDir, route.sourceFilePath);
126
+ if (!fs.existsSync(fullPath))
127
+ return { hasMetadata: false, type: 'none' };
128
+ const content = fs.readFileSync(fullPath, 'utf-8');
129
+ const hasMeta = /<title\b|<meta\s+name=["']description["']/i.test(content) || /title\s*:/i.test(content);
130
+ return {
131
+ hasMetadata: hasMeta,
132
+ type: hasMeta ? 'jsx_head' : 'none'
133
+ };
134
+ }
135
+ async findCanonicalDeclaration(projectDir, route) {
136
+ const fullPath = path.join(projectDir, route.sourceFilePath);
137
+ if (!fs.existsSync(fullPath))
138
+ return { hasCanonical: false, type: 'none' };
139
+ const content = fs.readFileSync(fullPath, 'utf-8');
140
+ const hasCanonical = /rel=["']canonical["']/i.test(content);
141
+ return {
142
+ hasCanonical,
143
+ type: hasCanonical ? 'link_jsx_tag' : 'none'
144
+ };
145
+ }
146
+ async findSchemaDeclaration(projectDir, route) {
147
+ const fullPath = path.join(projectDir, route.sourceFilePath);
148
+ if (!fs.existsSync(fullPath))
149
+ return { hasSchema: false, typesFound: [] };
150
+ const content = fs.readFileSync(fullPath, 'utf-8');
151
+ const hasSchema = /application\/ld\+json/i.test(content);
152
+ return {
153
+ hasSchema,
154
+ typesFound: hasSchema ? ['JSON-LD'] : []
155
+ };
156
+ }
157
+ findRobotsConfig(projectDir) {
158
+ const paths = ['public/robots.txt', 'robots.txt'];
159
+ for (const p of paths) {
160
+ if (fs.existsSync(path.join(projectDir, p)))
161
+ return p;
162
+ }
163
+ return null;
164
+ }
165
+ findSitemapConfig(projectDir) {
166
+ const paths = ['public/sitemap.xml', 'sitemap.xml', 'src/pages/sitemap.xml.ts'];
167
+ for (const p of paths) {
168
+ if (fs.existsSync(path.join(projectDir, p)))
169
+ return p;
170
+ }
171
+ return null;
172
+ }
173
+ findLlmsTxt(projectDir) {
174
+ const paths = ['public/llms.txt', 'llms.txt'];
175
+ for (const p of paths) {
176
+ if (fs.existsSync(path.join(projectDir, p)))
177
+ return p;
178
+ }
179
+ return null;
180
+ }
181
+ extractDynamicParams(routePath) {
182
+ const params = [];
183
+ const matches = routePath.match(/\[\.{0,3}([a-zA-Z0-9_-]+)\]/g);
184
+ if (matches) {
185
+ for (const m of matches) {
186
+ params.push(m.replace(/[\[\].]/g, ''));
187
+ }
188
+ }
189
+ return params;
190
+ }
191
+ }
@@ -0,0 +1,17 @@
1
+ import { FrameworkAdapter, MetadataLocationInfo, CanonicalLocationInfo, SchemaLocationInfo } from './types.js';
2
+ import { DiscoveredRoute, ProjectFrameworkInfo, RouteSourceMapping } from '../types/findings.js';
3
+ export declare class NextAppAdapter implements FrameworkAdapter {
4
+ id: string;
5
+ name: string;
6
+ detect(projectDir: string): boolean;
7
+ getProjectInfo(projectDir: string): ProjectFrameworkInfo;
8
+ discoverRoutes(projectDir: string): DiscoveredRoute[];
9
+ mapRouteToSource(targetUrl: string, routes: DiscoveredRoute[]): RouteSourceMapping;
10
+ findMetadataImplementation(projectDir: string, route: DiscoveredRoute): Promise<MetadataLocationInfo>;
11
+ findCanonicalDeclaration(projectDir: string, route: DiscoveredRoute): Promise<CanonicalLocationInfo>;
12
+ findSchemaDeclaration(projectDir: string, route: DiscoveredRoute): Promise<SchemaLocationInfo>;
13
+ findRobotsConfig(projectDir: string): string | null;
14
+ findSitemapConfig(projectDir: string): string | null;
15
+ findLlmsTxt(projectDir: string): string | null;
16
+ private extractDynamicParams;
17
+ }