@rankcli/agent-runtime 0.0.8 → 0.0.11

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 (49) hide show
  1. package/README.md +90 -196
  2. package/dist/analyzer-GMURJADU.mjs +7 -0
  3. package/dist/chunk-2JADKV3Z.mjs +244 -0
  4. package/dist/chunk-3ZSCLNTW.mjs +557 -0
  5. package/dist/chunk-4E4MQOSP.mjs +374 -0
  6. package/dist/chunk-6BWS3CLP.mjs +16 -0
  7. package/dist/chunk-AK2IC22C.mjs +206 -0
  8. package/dist/chunk-K6VSXDD6.mjs +293 -0
  9. package/dist/chunk-M27NQCWW.mjs +303 -0
  10. package/dist/{chunk-YNZYHEYM.mjs → chunk-PJLNXOLN.mjs} +0 -14
  11. package/dist/chunk-VSQD74I7.mjs +474 -0
  12. package/dist/core-web-vitals-analyzer-TE6LQJMS.mjs +7 -0
  13. package/dist/geo-analyzer-D47LTMMA.mjs +25 -0
  14. package/dist/image-optimization-analyzer-XP4OQGRP.mjs +9 -0
  15. package/dist/index.d.mts +1523 -17
  16. package/dist/index.d.ts +1523 -17
  17. package/dist/index.js +9582 -2664
  18. package/dist/index.mjs +4812 -380
  19. package/dist/internal-linking-analyzer-MRMBV7NM.mjs +9 -0
  20. package/dist/mobile-seo-analyzer-67HNQ7IO.mjs +7 -0
  21. package/dist/security-headers-analyzer-3ZUQARS5.mjs +9 -0
  22. package/dist/structured-data-analyzer-2I4NQAUP.mjs +9 -0
  23. package/package.json +2 -2
  24. package/src/analyzers/core-web-vitals-analyzer.test.ts +236 -0
  25. package/src/analyzers/core-web-vitals-analyzer.ts +557 -0
  26. package/src/analyzers/geo-analyzer.test.ts +310 -0
  27. package/src/analyzers/geo-analyzer.ts +814 -0
  28. package/src/analyzers/image-optimization-analyzer.test.ts +145 -0
  29. package/src/analyzers/image-optimization-analyzer.ts +348 -0
  30. package/src/analyzers/index.ts +233 -0
  31. package/src/analyzers/internal-linking-analyzer.test.ts +141 -0
  32. package/src/analyzers/internal-linking-analyzer.ts +419 -0
  33. package/src/analyzers/mobile-seo-analyzer.test.ts +140 -0
  34. package/src/analyzers/mobile-seo-analyzer.ts +455 -0
  35. package/src/analyzers/security-headers-analyzer.test.ts +115 -0
  36. package/src/analyzers/security-headers-analyzer.ts +318 -0
  37. package/src/analyzers/structured-data-analyzer.test.ts +210 -0
  38. package/src/analyzers/structured-data-analyzer.ts +590 -0
  39. package/src/audit/engine.ts +3 -3
  40. package/src/audit/types.ts +3 -2
  41. package/src/fixer/framework-fixes.test.ts +489 -0
  42. package/src/fixer/framework-fixes.ts +3418 -0
  43. package/src/fixer/index.ts +1 -0
  44. package/src/fixer/schemas.ts +971 -0
  45. package/src/frameworks/detector.ts +642 -114
  46. package/src/frameworks/suggestion-engine.ts +38 -1
  47. package/src/index.ts +6 -0
  48. package/src/types.ts +15 -1
  49. package/dist/analyzer-2CSWIQGD.mjs +0 -6
@@ -0,0 +1,233 @@
1
+ /**
2
+ * RankCLI Analyzers
3
+ *
4
+ * Industry-leading SEO analysis suite.
5
+ * Includes traditional SEO, Core Web Vitals, GEO (AI Search), and more.
6
+ */
7
+
8
+ // GEO (Generative Engine Optimization) - AI Search
9
+ export {
10
+ analyzeGEO,
11
+ analyzeRobotsTxtForAI,
12
+ detectRenderingMode,
13
+ analyzeContentStructure,
14
+ analyzeCitationReadiness,
15
+ analyzeEntityExtraction,
16
+ calculateLLMSignals,
17
+ generateAIFriendlyRobotsTxt,
18
+ AI_CRAWLERS_INFO,
19
+ type GEOAnalysisResult,
20
+ type AIAccessResult,
21
+ type ContentStructureResult,
22
+ type CitationResult,
23
+ type EntityResult,
24
+ type LLMSignalResult,
25
+ } from './geo-analyzer.js';
26
+
27
+ // Core Web Vitals
28
+ export {
29
+ analyzeCoreWebVitals,
30
+ type CoreWebVitalsEstimate,
31
+ } from './core-web-vitals-analyzer.js';
32
+
33
+ // Security Headers
34
+ export {
35
+ analyzeSecurityHeaders,
36
+ generateSecurityHeaders,
37
+ type SecurityHeadersResult,
38
+ } from './security-headers-analyzer.js';
39
+
40
+ // Structured Data / Schema.org
41
+ export {
42
+ analyzeStructuredData,
43
+ generateSchemaTemplate,
44
+ type StructuredDataResult,
45
+ type SchemaItem,
46
+ } from './structured-data-analyzer.js';
47
+
48
+ // Image Optimization
49
+ export {
50
+ analyzeImages,
51
+ generateResponsiveImage,
52
+ type ImageAnalysisResult,
53
+ type ImageInfo,
54
+ } from './image-optimization-analyzer.js';
55
+
56
+ // Internal Linking
57
+ export {
58
+ analyzeInternalLinking,
59
+ suggestInternalLinks,
60
+ type InternalLinkingResult,
61
+ type LinkInfo,
62
+ type AnchorTextStats,
63
+ } from './internal-linking-analyzer.js';
64
+
65
+ // Mobile SEO
66
+ export {
67
+ analyzeMobileSEO,
68
+ type MobileSEOResult,
69
+ type ViewportAnalysis,
70
+ type TouchTargetAnalysis,
71
+ type FontSizeAnalysis,
72
+ type ContentWidthAnalysis,
73
+ type MobileSpecificAnalysis,
74
+ } from './mobile-seo-analyzer.js';
75
+
76
+ /**
77
+ * Comprehensive SEO Analysis
78
+ *
79
+ * Runs all analyzers and produces a unified report.
80
+ */
81
+ export interface ComprehensiveSEOResult {
82
+ url: string;
83
+ timestamp: string;
84
+ overallScore: number;
85
+ grades: {
86
+ geo: number;
87
+ coreWebVitals: number;
88
+ security: string;
89
+ structuredData: number;
90
+ images: number;
91
+ internalLinking: number;
92
+ mobile: number;
93
+ };
94
+ geo: import('./geo-analyzer.js').GEOAnalysisResult;
95
+ coreWebVitals: import('./core-web-vitals-analyzer.js').CoreWebVitalsEstimate;
96
+ security: import('./security-headers-analyzer.js').SecurityHeadersResult;
97
+ structuredData: import('./structured-data-analyzer.js').StructuredDataResult;
98
+ images: import('./image-optimization-analyzer.js').ImageAnalysisResult;
99
+ internalLinking: import('./internal-linking-analyzer.js').InternalLinkingResult;
100
+ mobile: import('./mobile-seo-analyzer.js').MobileSEOResult;
101
+ allIssues: import('../audit/types.js').AuditIssue[];
102
+ prioritizedRecommendations: string[];
103
+ }
104
+
105
+ /**
106
+ * Run comprehensive SEO analysis
107
+ */
108
+ export async function analyzeComprehensive(
109
+ html: string,
110
+ url: string,
111
+ options: {
112
+ robotsTxt?: string;
113
+ headers?: Record<string, string>;
114
+ } = {}
115
+ ): Promise<ComprehensiveSEOResult> {
116
+ const { robotsTxt, headers } = options;
117
+
118
+ // Import analyzers
119
+ const { analyzeGEO } = await import('./geo-analyzer.js');
120
+ const { analyzeCoreWebVitals } = await import('./core-web-vitals-analyzer.js');
121
+ const { analyzeSecurityHeaders } = await import('./security-headers-analyzer.js');
122
+ const { analyzeStructuredData } = await import('./structured-data-analyzer.js');
123
+ const { analyzeImages } = await import('./image-optimization-analyzer.js');
124
+ const { analyzeInternalLinking } = await import('./internal-linking-analyzer.js');
125
+ const { analyzeMobileSEO } = await import('./mobile-seo-analyzer.js');
126
+
127
+ // Run all analyzers
128
+ const geo = await analyzeGEO(html, url, robotsTxt);
129
+ const coreWebVitals = analyzeCoreWebVitals(html, url, headers);
130
+ const security = analyzeSecurityHeaders(headers || {}, url);
131
+ const structuredData = analyzeStructuredData(html, url);
132
+ const images = analyzeImages(html, url);
133
+ const internalLinking = analyzeInternalLinking(html, url);
134
+ const mobile = analyzeMobileSEO(html, url);
135
+
136
+ // Collect all issues
137
+ const allIssues = [
138
+ ...geo.issues,
139
+ ...coreWebVitals.issues,
140
+ ...security.issues,
141
+ ...structuredData.issues,
142
+ ...images.issues,
143
+ ...internalLinking.issues,
144
+ ...mobile.issues,
145
+ ];
146
+
147
+ // Sort by severity
148
+ const severityOrder: Record<string, number> = { critical: 0, error: 1, warning: 2, info: 3, notice: 4 };
149
+ allIssues.sort((a, b) => (severityOrder[a.severity] ?? 5) - (severityOrder[b.severity] ?? 5));
150
+
151
+ // Calculate overall score (weighted average)
152
+ const weights = {
153
+ geo: 0.20, // AI search is crucial
154
+ cwv: 0.20, // Core Web Vitals
155
+ security: 0.10, // Security headers
156
+ schema: 0.15, // Structured data
157
+ images: 0.10, // Image optimization
158
+ links: 0.10, // Internal linking
159
+ mobile: 0.15, // Mobile SEO
160
+ };
161
+
162
+ const securityScore =
163
+ security.grade === 'A+' ? 100 :
164
+ security.grade === 'A' ? 90 :
165
+ security.grade === 'B' ? 75 :
166
+ security.grade === 'C' ? 55 :
167
+ security.grade === 'D' ? 35 : 20;
168
+
169
+ const overallScore = Math.round(
170
+ (geo.score * weights.geo) +
171
+ (coreWebVitals.overallScore * weights.cwv) +
172
+ (securityScore * weights.security) +
173
+ (structuredData.score * weights.schema) +
174
+ (images.score * weights.images) +
175
+ (internalLinking.score * weights.links) +
176
+ (mobile.score * weights.mobile)
177
+ );
178
+
179
+ // Prioritize recommendations
180
+ const prioritizedRecommendations: string[] = [];
181
+
182
+ // Critical GEO issues first
183
+ if (geo.aiCrawlerAccess.blockedCrawlers.length > 0) {
184
+ prioritizedRecommendations.push('🚨 URGENT: Unblock AI crawlers (GPTBot, PerplexityBot) in robots.txt');
185
+ }
186
+ if (geo.aiCrawlerAccess.jsRenderingRequired && !geo.aiCrawlerAccess.hasPrerendering) {
187
+ prioritizedRecommendations.push('🚨 URGENT: Implement SSR - AI crawlers cannot see your JS-rendered content');
188
+ }
189
+
190
+ // CWV critical issues
191
+ const criticalCWV = coreWebVitals.issues.filter(i => i.severity === 'critical');
192
+ criticalCWV.forEach(i => prioritizedRecommendations.push(`⚡ ${i.title}`));
193
+
194
+ // Security critical
195
+ if (!security.https.enabled) {
196
+ prioritizedRecommendations.push('🔒 Enable HTTPS immediately');
197
+ }
198
+
199
+ // Mobile critical
200
+ if (!mobile.viewport.hasViewport) {
201
+ prioritizedRecommendations.push('📱 Add viewport meta tag for mobile rendering');
202
+ }
203
+
204
+ // Add top recommendations from each analyzer
205
+ geo.recommendations.slice(0, 2).forEach(r => prioritizedRecommendations.push(r));
206
+ structuredData.recommendations.slice(0, 2).forEach(r => prioritizedRecommendations.push(r));
207
+ images.recommendations.slice(0, 2).forEach(r => prioritizedRecommendations.push(r));
208
+ internalLinking.recommendations.slice(0, 2).forEach(r => prioritizedRecommendations.push(r));
209
+
210
+ return {
211
+ url,
212
+ timestamp: new Date().toISOString(),
213
+ overallScore,
214
+ grades: {
215
+ geo: geo.score,
216
+ coreWebVitals: coreWebVitals.overallScore,
217
+ security: security.grade,
218
+ structuredData: structuredData.score,
219
+ images: images.score,
220
+ internalLinking: internalLinking.score,
221
+ mobile: mobile.score,
222
+ },
223
+ geo,
224
+ coreWebVitals,
225
+ security,
226
+ structuredData,
227
+ images,
228
+ internalLinking,
229
+ mobile,
230
+ allIssues,
231
+ prioritizedRecommendations,
232
+ };
233
+ }
@@ -0,0 +1,141 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { analyzeInternalLinking, suggestInternalLinks } from './internal-linking-analyzer.js';
3
+
4
+ describe('Internal Linking Analyzer', () => {
5
+ describe('analyzeInternalLinking', () => {
6
+ it('counts internal and external links', () => {
7
+ const html = `
8
+ <html><body>
9
+ <a href="/about">About</a>
10
+ <a href="/products">Products</a>
11
+ <a href="https://external.com">External</a>
12
+ </body></html>
13
+ `;
14
+ const result = analyzeInternalLinking(html, 'https://example.com/page');
15
+ expect(result.internalLinks).toBe(2);
16
+ expect(result.externalLinks).toBe(1);
17
+ expect(result.totalLinks).toBe(3);
18
+ });
19
+
20
+ it('distinguishes navigation from content links', () => {
21
+ const html = `
22
+ <html><body>
23
+ <nav>
24
+ <a href="/home">Home</a>
25
+ <a href="/about">About</a>
26
+ </nav>
27
+ <main>
28
+ <a href="/products">See our products</a>
29
+ </main>
30
+ </body></html>
31
+ `;
32
+ const result = analyzeInternalLinking(html, 'https://example.com');
33
+ expect(result.navigationLinks).toBe(2);
34
+ expect(result.contentLinks).toBe(1);
35
+ });
36
+
37
+ it('detects generic anchor text', () => {
38
+ const html = `
39
+ <html><body>
40
+ <a href="/about">Click here</a>
41
+ <a href="/products">Read more</a>
42
+ <a href="/services">Learn more</a>
43
+ <a href="/contact">here</a>
44
+ </body></html>
45
+ `;
46
+ const result = analyzeInternalLinking(html, 'https://example.com');
47
+ expect(result.linksWithGenericText).toBe(4);
48
+ expect(result.issues.some(i => i.code === 'LINK_GENERIC_ANCHOR')).toBe(true);
49
+ });
50
+
51
+ it('detects descriptive anchor text', () => {
52
+ const html = `
53
+ <html><body>
54
+ <a href="/about">About Our Company</a>
55
+ <a href="/products">Our Product Catalog</a>
56
+ </body></html>
57
+ `;
58
+ const result = analyzeInternalLinking(html, 'https://example.com');
59
+ expect(result.linksWithKeywords).toBe(2);
60
+ });
61
+
62
+ it('detects empty anchor text', () => {
63
+ const html = `
64
+ <html><body>
65
+ <a href="/about"></a>
66
+ <a href="/products"> </a>
67
+ </body></html>
68
+ `;
69
+ const result = analyzeInternalLinking(html, 'https://example.com');
70
+ expect(result.anchorTextAnalysis.empty).toBe(2);
71
+ expect(result.issues.some(i => i.code === 'LINK_EMPTY_ANCHOR')).toBe(true);
72
+ });
73
+
74
+ it('detects nofollow on internal links', () => {
75
+ const html = `
76
+ <html><body>
77
+ <a href="/about" rel="nofollow">About</a>
78
+ </body></html>
79
+ `;
80
+ const result = analyzeInternalLinking(html, 'https://example.com');
81
+ expect(result.issues.some(i => i.code === 'LINK_NOFOLLOW_INTERNAL')).toBe(true);
82
+ });
83
+
84
+ it('tracks unique internal targets', () => {
85
+ const html = `
86
+ <html><body>
87
+ <a href="/about">About</a>
88
+ <a href="/about">About Us</a>
89
+ <a href="/products">Products</a>
90
+ </body></html>
91
+ `;
92
+ const result = analyzeInternalLinking(html, 'https://example.com');
93
+ expect(result.uniqueInternalTargets).toBe(2);
94
+ });
95
+
96
+ it('detects orphan risk', () => {
97
+ const html = `
98
+ <html><body>
99
+ <nav><a href="/home">Home</a></nav>
100
+ <p>Content without links</p>
101
+ </body></html>
102
+ `;
103
+ const result = analyzeInternalLinking(html, 'https://example.com/orphan');
104
+ expect(result.orphanRisk).toBe(true);
105
+ });
106
+
107
+ it('handles image links', () => {
108
+ const html = `
109
+ <html><body>
110
+ <a href="/products"><img src="/logo.png" alt="Products"></a>
111
+ <a href="/about"><img src="/icon.png"></a>
112
+ </body></html>
113
+ `;
114
+ const result = analyzeInternalLinking(html, 'https://example.com');
115
+ expect(result.anchorTextAnalysis.imageLinks).toBe(2);
116
+ });
117
+ });
118
+
119
+ describe('suggestInternalLinks', () => {
120
+ it('suggests links based on keyword matches', () => {
121
+ const content = '<p>Learn about SEO optimization techniques.</p>';
122
+ const pages = [
123
+ { url: '/seo-guide', title: 'SEO Guide', keywords: ['seo', 'optimization'] },
124
+ { url: '/about', title: 'About', keywords: ['company', 'team'] },
125
+ ];
126
+
127
+ const suggestions = suggestInternalLinks(content, pages);
128
+ expect(suggestions.some(s => s.suggestedUrl === '/seo-guide')).toBe(true);
129
+ });
130
+
131
+ it('does not suggest already linked pages', () => {
132
+ const content = '<p>Learn about SEO optimization. <a href="/seo-guide">Guide</a></p>';
133
+ const pages = [
134
+ { url: '/seo-guide', title: 'SEO Guide', keywords: ['seo'] },
135
+ ];
136
+
137
+ const suggestions = suggestInternalLinks(content, pages);
138
+ expect(suggestions.length).toBe(0);
139
+ });
140
+ });
141
+ });