@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.
- package/README.md +90 -196
- package/dist/analyzer-GMURJADU.mjs +7 -0
- package/dist/chunk-2JADKV3Z.mjs +244 -0
- package/dist/chunk-3ZSCLNTW.mjs +557 -0
- package/dist/chunk-4E4MQOSP.mjs +374 -0
- package/dist/chunk-6BWS3CLP.mjs +16 -0
- package/dist/chunk-AK2IC22C.mjs +206 -0
- package/dist/chunk-K6VSXDD6.mjs +293 -0
- package/dist/chunk-M27NQCWW.mjs +303 -0
- package/dist/{chunk-YNZYHEYM.mjs → chunk-PJLNXOLN.mjs} +0 -14
- package/dist/chunk-VSQD74I7.mjs +474 -0
- package/dist/core-web-vitals-analyzer-TE6LQJMS.mjs +7 -0
- package/dist/geo-analyzer-D47LTMMA.mjs +25 -0
- package/dist/image-optimization-analyzer-XP4OQGRP.mjs +9 -0
- package/dist/index.d.mts +1523 -17
- package/dist/index.d.ts +1523 -17
- package/dist/index.js +9582 -2664
- package/dist/index.mjs +4812 -380
- package/dist/internal-linking-analyzer-MRMBV7NM.mjs +9 -0
- package/dist/mobile-seo-analyzer-67HNQ7IO.mjs +7 -0
- package/dist/security-headers-analyzer-3ZUQARS5.mjs +9 -0
- package/dist/structured-data-analyzer-2I4NQAUP.mjs +9 -0
- package/package.json +2 -2
- package/src/analyzers/core-web-vitals-analyzer.test.ts +236 -0
- package/src/analyzers/core-web-vitals-analyzer.ts +557 -0
- package/src/analyzers/geo-analyzer.test.ts +310 -0
- package/src/analyzers/geo-analyzer.ts +814 -0
- package/src/analyzers/image-optimization-analyzer.test.ts +145 -0
- package/src/analyzers/image-optimization-analyzer.ts +348 -0
- package/src/analyzers/index.ts +233 -0
- package/src/analyzers/internal-linking-analyzer.test.ts +141 -0
- package/src/analyzers/internal-linking-analyzer.ts +419 -0
- package/src/analyzers/mobile-seo-analyzer.test.ts +140 -0
- package/src/analyzers/mobile-seo-analyzer.ts +455 -0
- package/src/analyzers/security-headers-analyzer.test.ts +115 -0
- package/src/analyzers/security-headers-analyzer.ts +318 -0
- package/src/analyzers/structured-data-analyzer.test.ts +210 -0
- package/src/analyzers/structured-data-analyzer.ts +590 -0
- package/src/audit/engine.ts +3 -3
- package/src/audit/types.ts +3 -2
- package/src/fixer/framework-fixes.test.ts +489 -0
- package/src/fixer/framework-fixes.ts +3418 -0
- package/src/fixer/index.ts +1 -0
- package/src/fixer/schemas.ts +971 -0
- package/src/frameworks/detector.ts +642 -114
- package/src/frameworks/suggestion-engine.ts +38 -1
- package/src/index.ts +6 -0
- package/src/types.ts +15 -1
- package/dist/analyzer-2CSWIQGD.mjs +0 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rankcli/agent-runtime",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "RankCLI agent runtime - executes SEO audits and fixes with AI",
|
|
5
5
|
"homepage": "https://rankcli.dev",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"esbuild": "^0.20.0",
|
|
37
37
|
"tsup": "^8.0.0",
|
|
38
38
|
"tsx": "^4.0.0",
|
|
39
|
-
"typescript": "^5.3
|
|
39
|
+
"typescript": "^5.9.3",
|
|
40
40
|
"vitest": "^1.0.0"
|
|
41
41
|
},
|
|
42
42
|
"keywords": [
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { analyzeCoreWebVitals } from './core-web-vitals-analyzer.js';
|
|
3
|
+
|
|
4
|
+
describe('Core Web Vitals Analyzer', () => {
|
|
5
|
+
describe('LCP Analysis', () => {
|
|
6
|
+
it('detects lazy-loaded hero images', () => {
|
|
7
|
+
const html = `
|
|
8
|
+
<!DOCTYPE html>
|
|
9
|
+
<html>
|
|
10
|
+
<head><title>Test</title></head>
|
|
11
|
+
<body>
|
|
12
|
+
<header>
|
|
13
|
+
<img src="/hero.jpg" loading="lazy" width="1200" height="600">
|
|
14
|
+
</header>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
17
|
+
`;
|
|
18
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
19
|
+
expect(result.lcp.issues.some(i => i.includes('lazy'))).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('detects render-blocking scripts', () => {
|
|
23
|
+
const html = `
|
|
24
|
+
<!DOCTYPE html>
|
|
25
|
+
<html>
|
|
26
|
+
<head>
|
|
27
|
+
<script src="/app.js"></script>
|
|
28
|
+
<script src="/vendor.js"></script>
|
|
29
|
+
</head>
|
|
30
|
+
<body><h1>Test</h1></body>
|
|
31
|
+
</html>
|
|
32
|
+
`;
|
|
33
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
34
|
+
expect(result.lcp.issues.some(i => i.includes('render-blocking scripts'))).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('accepts async/defer scripts', () => {
|
|
38
|
+
const html = `
|
|
39
|
+
<!DOCTYPE html>
|
|
40
|
+
<html>
|
|
41
|
+
<head>
|
|
42
|
+
<script src="/app.js" async></script>
|
|
43
|
+
<script src="/analytics.js" defer></script>
|
|
44
|
+
</head>
|
|
45
|
+
<body><h1>Test</h1></body>
|
|
46
|
+
</html>
|
|
47
|
+
`;
|
|
48
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
49
|
+
expect(result.lcp.issues.some(i => i.includes('render-blocking scripts'))).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('detects multiple render-blocking stylesheets', () => {
|
|
53
|
+
const html = `
|
|
54
|
+
<!DOCTYPE html>
|
|
55
|
+
<html>
|
|
56
|
+
<head>
|
|
57
|
+
<link rel="stylesheet" href="/style1.css">
|
|
58
|
+
<link rel="stylesheet" href="/style2.css">
|
|
59
|
+
<link rel="stylesheet" href="/style3.css">
|
|
60
|
+
<link rel="stylesheet" href="/style4.css">
|
|
61
|
+
</head>
|
|
62
|
+
<body><h1>Test</h1></body>
|
|
63
|
+
</html>
|
|
64
|
+
`;
|
|
65
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
66
|
+
expect(result.lcp.issues.some(i => i.includes('render-blocking stylesheets'))).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('CLS Analysis', () => {
|
|
71
|
+
it('detects images without dimensions', () => {
|
|
72
|
+
const html = `
|
|
73
|
+
<!DOCTYPE html>
|
|
74
|
+
<html>
|
|
75
|
+
<body>
|
|
76
|
+
<img src="/photo1.jpg">
|
|
77
|
+
<img src="/photo2.jpg">
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|
|
80
|
+
`;
|
|
81
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
82
|
+
expect(result.cls.issues.some(i => i.includes('without width/height'))).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('passes images with dimensions', () => {
|
|
86
|
+
const html = `
|
|
87
|
+
<!DOCTYPE html>
|
|
88
|
+
<html>
|
|
89
|
+
<body>
|
|
90
|
+
<img src="/photo.jpg" width="800" height="600">
|
|
91
|
+
</body>
|
|
92
|
+
</html>
|
|
93
|
+
`;
|
|
94
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
95
|
+
expect(result.cls.issues.some(i => i.includes('without width/height'))).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('detects iframes and videos without dimensions', () => {
|
|
99
|
+
const html = `
|
|
100
|
+
<!DOCTYPE html>
|
|
101
|
+
<html>
|
|
102
|
+
<body>
|
|
103
|
+
<iframe src="https://youtube.com/embed/123"></iframe>
|
|
104
|
+
<video src="/video.mp4"></video>
|
|
105
|
+
</body>
|
|
106
|
+
</html>
|
|
107
|
+
`;
|
|
108
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
109
|
+
expect(result.cls.issues.some(i => i.includes('videos/iframes without dimensions'))).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('detects web fonts without font-display', () => {
|
|
113
|
+
const html = `
|
|
114
|
+
<!DOCTYPE html>
|
|
115
|
+
<html>
|
|
116
|
+
<head>
|
|
117
|
+
<link href="https://fonts.googleapis.com/css2?family=Open+Sans" rel="stylesheet">
|
|
118
|
+
</head>
|
|
119
|
+
<body><h1>Test</h1></body>
|
|
120
|
+
</html>
|
|
121
|
+
`;
|
|
122
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
123
|
+
expect(result.cls.issues.some(i => i.includes('font-display'))).toBe(true);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('FID/INP Analysis', () => {
|
|
128
|
+
it('detects many JavaScript bundles', () => {
|
|
129
|
+
const html = `
|
|
130
|
+
<!DOCTYPE html>
|
|
131
|
+
<html>
|
|
132
|
+
<head></head>
|
|
133
|
+
<body>
|
|
134
|
+
<script src="/bundle.1.js"></script>
|
|
135
|
+
<script src="/bundle.2.js"></script>
|
|
136
|
+
<script src="/vendor.bundle.js"></script>
|
|
137
|
+
<script src="/chunk.1.js"></script>
|
|
138
|
+
<script src="/chunk.2.js"></script>
|
|
139
|
+
<script src="/chunk.3.js"></script>
|
|
140
|
+
</body>
|
|
141
|
+
</html>
|
|
142
|
+
`;
|
|
143
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
144
|
+
expect(result.fid.issues.some(i => i.includes('bundle files'))).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('detects large inline JavaScript', () => {
|
|
148
|
+
const largeScript = 'x'.repeat(15000);
|
|
149
|
+
const html = `
|
|
150
|
+
<!DOCTYPE html>
|
|
151
|
+
<html>
|
|
152
|
+
<body>
|
|
153
|
+
<script>${largeScript}</script>
|
|
154
|
+
</body>
|
|
155
|
+
</html>
|
|
156
|
+
`;
|
|
157
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
158
|
+
expect(result.fid.issues.some(i => i.includes('Large inline JavaScript'))).toBe(true);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('detects many inline event handlers', () => {
|
|
162
|
+
const handlers = Array(15).fill('<button onclick="doSomething()">Click</button>').join('');
|
|
163
|
+
const html = `
|
|
164
|
+
<!DOCTYPE html>
|
|
165
|
+
<html>
|
|
166
|
+
<body>${handlers}</body>
|
|
167
|
+
</html>
|
|
168
|
+
`;
|
|
169
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
170
|
+
expect(result.fid.issues.some(i => i.includes('inline event handlers'))).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('TTFB Analysis', () => {
|
|
175
|
+
it('detects missing preconnect hints', () => {
|
|
176
|
+
const html = `
|
|
177
|
+
<!DOCTYPE html>
|
|
178
|
+
<html>
|
|
179
|
+
<head><title>Test</title></head>
|
|
180
|
+
<body><h1>Test</h1></body>
|
|
181
|
+
</html>
|
|
182
|
+
`;
|
|
183
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
184
|
+
expect(result.ttfb.issues.some(i => i.includes('preconnect'))).toBe(true);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('passes with preconnect hints', () => {
|
|
188
|
+
const html = `
|
|
189
|
+
<!DOCTYPE html>
|
|
190
|
+
<html>
|
|
191
|
+
<head>
|
|
192
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
193
|
+
<link rel="dns-prefetch" href="https://api.example.com">
|
|
194
|
+
</head>
|
|
195
|
+
<body><h1>Test</h1></body>
|
|
196
|
+
</html>
|
|
197
|
+
`;
|
|
198
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
199
|
+
expect(result.ttfb.issues.some(i => i.includes('preconnect'))).toBe(false);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe('Overall Score', () => {
|
|
204
|
+
it('calculates overall score', () => {
|
|
205
|
+
const html = `
|
|
206
|
+
<!DOCTYPE html>
|
|
207
|
+
<html>
|
|
208
|
+
<head>
|
|
209
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
210
|
+
</head>
|
|
211
|
+
<body>
|
|
212
|
+
<h1>Well Optimized Page</h1>
|
|
213
|
+
<img src="/photo.jpg" width="800" height="600">
|
|
214
|
+
</body>
|
|
215
|
+
</html>
|
|
216
|
+
`;
|
|
217
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
218
|
+
expect(result.overallScore).toBeGreaterThan(0);
|
|
219
|
+
expect(result.overallScore).toBeLessThanOrEqual(100);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('generates issues array', () => {
|
|
223
|
+
const html = `
|
|
224
|
+
<!DOCTYPE html>
|
|
225
|
+
<html>
|
|
226
|
+
<body>
|
|
227
|
+
<img src="/photo.jpg">
|
|
228
|
+
<script src="/app.js"></script>
|
|
229
|
+
</body>
|
|
230
|
+
</html>
|
|
231
|
+
`;
|
|
232
|
+
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
233
|
+
expect(Array.isArray(result.issues)).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|