@rankcli/agent-runtime 0.0.1
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 +242 -0
- package/dist/analyzer-2CSWIQGD.mjs +6 -0
- package/dist/chunk-YNZYHEYM.mjs +774 -0
- package/dist/index.d.mts +4012 -0
- package/dist/index.d.ts +4012 -0
- package/dist/index.js +29672 -0
- package/dist/index.mjs +28602 -0
- package/package.json +53 -0
- package/scripts/build-deno.ts +134 -0
- package/src/audit/ai/analyzer.ts +347 -0
- package/src/audit/ai/index.ts +29 -0
- package/src/audit/ai/prompts/content-analysis.ts +271 -0
- package/src/audit/ai/types.ts +179 -0
- package/src/audit/checks/additional-checks.ts +439 -0
- package/src/audit/checks/ai-citation-worthiness.ts +399 -0
- package/src/audit/checks/ai-content-structure.ts +325 -0
- package/src/audit/checks/ai-readiness.ts +339 -0
- package/src/audit/checks/anchor-text.ts +179 -0
- package/src/audit/checks/answer-conciseness.ts +322 -0
- package/src/audit/checks/asset-minification.ts +270 -0
- package/src/audit/checks/bing-optimization.ts +206 -0
- package/src/audit/checks/brand-mention-optimization.ts +349 -0
- package/src/audit/checks/caching-headers.ts +305 -0
- package/src/audit/checks/canonical-advanced.ts +150 -0
- package/src/audit/checks/canonical-domain.ts +196 -0
- package/src/audit/checks/citation-quality.ts +358 -0
- package/src/audit/checks/client-rendering.ts +542 -0
- package/src/audit/checks/color-contrast.ts +342 -0
- package/src/audit/checks/content-freshness.ts +170 -0
- package/src/audit/checks/content-science.ts +589 -0
- package/src/audit/checks/conversion-elements.ts +526 -0
- package/src/audit/checks/crawlability.ts +220 -0
- package/src/audit/checks/directory-listing.ts +172 -0
- package/src/audit/checks/dom-analysis.ts +191 -0
- package/src/audit/checks/dom-size.ts +246 -0
- package/src/audit/checks/duplicate-content.ts +194 -0
- package/src/audit/checks/eeat-signals.ts +990 -0
- package/src/audit/checks/entity-seo.ts +396 -0
- package/src/audit/checks/featured-snippet.ts +473 -0
- package/src/audit/checks/freshness-signals.ts +443 -0
- package/src/audit/checks/funnel-intent.ts +463 -0
- package/src/audit/checks/hreflang.ts +174 -0
- package/src/audit/checks/html-compliance.ts +302 -0
- package/src/audit/checks/image-dimensions.ts +167 -0
- package/src/audit/checks/images.ts +160 -0
- package/src/audit/checks/indexnow.ts +275 -0
- package/src/audit/checks/interactive-tools.ts +475 -0
- package/src/audit/checks/internal-link-graph.ts +436 -0
- package/src/audit/checks/keyword-analysis.ts +239 -0
- package/src/audit/checks/keyword-cannibalization.ts +385 -0
- package/src/audit/checks/keyword-placement.ts +471 -0
- package/src/audit/checks/links.ts +203 -0
- package/src/audit/checks/llms-txt.ts +224 -0
- package/src/audit/checks/local-seo.ts +296 -0
- package/src/audit/checks/mobile.ts +167 -0
- package/src/audit/checks/modern-images.ts +226 -0
- package/src/audit/checks/navboost-signals.ts +395 -0
- package/src/audit/checks/on-page.ts +209 -0
- package/src/audit/checks/page-resources.ts +285 -0
- package/src/audit/checks/pagination.ts +180 -0
- package/src/audit/checks/performance.ts +153 -0
- package/src/audit/checks/platform-presence.ts +580 -0
- package/src/audit/checks/redirect-analysis.ts +153 -0
- package/src/audit/checks/redirect-chain.ts +389 -0
- package/src/audit/checks/resource-hints.ts +420 -0
- package/src/audit/checks/responsive-css.ts +247 -0
- package/src/audit/checks/responsive-images.ts +396 -0
- package/src/audit/checks/review-ecosystem.ts +415 -0
- package/src/audit/checks/robots-validation.ts +373 -0
- package/src/audit/checks/security-headers.ts +172 -0
- package/src/audit/checks/security.ts +144 -0
- package/src/audit/checks/serp-preview.ts +251 -0
- package/src/audit/checks/site-maturity.ts +444 -0
- package/src/audit/checks/social-meta.test.ts +275 -0
- package/src/audit/checks/social-meta.ts +134 -0
- package/src/audit/checks/soft-404.ts +151 -0
- package/src/audit/checks/structured-data.ts +238 -0
- package/src/audit/checks/tech-detection.ts +496 -0
- package/src/audit/checks/topical-clusters.ts +435 -0
- package/src/audit/checks/tracker-bloat.ts +462 -0
- package/src/audit/checks/tracking-verification.test.ts +371 -0
- package/src/audit/checks/tracking-verification.ts +636 -0
- package/src/audit/checks/url-safety.ts +682 -0
- package/src/audit/deno-entry.ts +66 -0
- package/src/audit/discovery/index.ts +15 -0
- package/src/audit/discovery/link-crawler.ts +232 -0
- package/src/audit/discovery/repo-routes.ts +347 -0
- package/src/audit/engine.ts +620 -0
- package/src/audit/fixes/index.ts +209 -0
- package/src/audit/fixes/social-meta-fixes.test.ts +329 -0
- package/src/audit/fixes/social-meta-fixes.ts +463 -0
- package/src/audit/index.ts +74 -0
- package/src/audit/runner.test.ts +299 -0
- package/src/audit/runner.ts +130 -0
- package/src/audit/types.ts +1953 -0
- package/src/content/featured-snippet.ts +367 -0
- package/src/content/generator.test.ts +534 -0
- package/src/content/generator.ts +501 -0
- package/src/content/headline.ts +317 -0
- package/src/content/index.ts +62 -0
- package/src/content/intent.ts +258 -0
- package/src/content/keyword-density.ts +349 -0
- package/src/content/readability.ts +262 -0
- package/src/executor.ts +336 -0
- package/src/fixer.ts +416 -0
- package/src/frameworks/detector.test.ts +248 -0
- package/src/frameworks/detector.ts +371 -0
- package/src/frameworks/index.ts +68 -0
- package/src/frameworks/recipes/angular.yaml +171 -0
- package/src/frameworks/recipes/astro.yaml +206 -0
- package/src/frameworks/recipes/django.yaml +180 -0
- package/src/frameworks/recipes/laravel.yaml +137 -0
- package/src/frameworks/recipes/nextjs.yaml +268 -0
- package/src/frameworks/recipes/nuxt.yaml +175 -0
- package/src/frameworks/recipes/rails.yaml +188 -0
- package/src/frameworks/recipes/react.yaml +202 -0
- package/src/frameworks/recipes/sveltekit.yaml +154 -0
- package/src/frameworks/recipes/vue.yaml +137 -0
- package/src/frameworks/recipes/wordpress.yaml +209 -0
- package/src/frameworks/suggestion-engine.ts +320 -0
- package/src/geo/geo-content.test.ts +305 -0
- package/src/geo/geo-content.ts +266 -0
- package/src/geo/geo-history.test.ts +473 -0
- package/src/geo/geo-history.ts +433 -0
- package/src/geo/geo-tracker.test.ts +359 -0
- package/src/geo/geo-tracker.ts +411 -0
- package/src/geo/index.ts +10 -0
- package/src/git/commit-helper.test.ts +261 -0
- package/src/git/commit-helper.ts +329 -0
- package/src/git/index.ts +12 -0
- package/src/git/pr-helper.test.ts +284 -0
- package/src/git/pr-helper.ts +307 -0
- package/src/index.ts +66 -0
- package/src/keywords/ai-keyword-engine.ts +1062 -0
- package/src/keywords/ai-summarizer.ts +387 -0
- package/src/keywords/ci-mode.ts +555 -0
- package/src/keywords/engine.ts +359 -0
- package/src/keywords/index.ts +151 -0
- package/src/keywords/llm-judge.ts +357 -0
- package/src/keywords/nlp-analysis.ts +706 -0
- package/src/keywords/prioritizer.ts +295 -0
- package/src/keywords/site-crawler.ts +342 -0
- package/src/keywords/sources/autocomplete.ts +139 -0
- package/src/keywords/sources/competitive-search.ts +450 -0
- package/src/keywords/sources/competitor-analysis.ts +374 -0
- package/src/keywords/sources/dataforseo.ts +206 -0
- package/src/keywords/sources/free-sources.ts +294 -0
- package/src/keywords/sources/gsc.ts +123 -0
- package/src/keywords/topic-grouping.ts +327 -0
- package/src/keywords/types.ts +144 -0
- package/src/keywords/wizard.ts +457 -0
- package/src/loader.ts +40 -0
- package/src/reports/index.ts +7 -0
- package/src/reports/report-generator.test.ts +293 -0
- package/src/reports/report-generator.ts +713 -0
- package/src/scheduler/alerts.test.ts +458 -0
- package/src/scheduler/alerts.ts +328 -0
- package/src/scheduler/index.ts +8 -0
- package/src/scheduler/scheduled-audit.test.ts +377 -0
- package/src/scheduler/scheduled-audit.ts +149 -0
- package/src/test/integration-test.ts +325 -0
- package/src/tools/analyzer.ts +373 -0
- package/src/tools/crawl.ts +293 -0
- package/src/tools/files.ts +301 -0
- package/src/tools/h1-fixer.ts +249 -0
- package/src/tools/index.ts +67 -0
- package/src/tracking/github-action.ts +326 -0
- package/src/tracking/google-analytics.ts +265 -0
- package/src/tracking/index.ts +45 -0
- package/src/tracking/report-generator.ts +386 -0
- package/src/tracking/search-console.ts +335 -0
- package/src/types.ts +134 -0
- package/src/utils/http.ts +302 -0
- package/src/wasm-adapter.ts +297 -0
- package/src/wasm-entry.ts +14 -0
- package/tsconfig.json +17 -0
- package/tsup.wasm.config.ts +26 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import * as cheerio from 'cheerio';
|
|
2
|
+
import type { AuditIssue } from '../types.js';
|
|
3
|
+
import { ISSUE_DEFINITIONS } from '../types.js';
|
|
4
|
+
|
|
5
|
+
export interface SchemaItem {
|
|
6
|
+
type: string;
|
|
7
|
+
data: Record<string, unknown>;
|
|
8
|
+
errors: string[];
|
|
9
|
+
warnings: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface StructuredDataData {
|
|
13
|
+
schemas: SchemaItem[];
|
|
14
|
+
hasSchema: boolean;
|
|
15
|
+
schemaTypes: string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Required properties for common schema types
|
|
19
|
+
const SCHEMA_REQUIRED_PROPERTIES: Record<string, string[]> = {
|
|
20
|
+
'Organization': ['name', 'url'],
|
|
21
|
+
'WebSite': ['name', 'url'],
|
|
22
|
+
'WebPage': ['name'],
|
|
23
|
+
'Article': ['headline', 'author', 'datePublished'],
|
|
24
|
+
'BlogPosting': ['headline', 'author', 'datePublished'],
|
|
25
|
+
'NewsArticle': ['headline', 'author', 'datePublished'],
|
|
26
|
+
'Product': ['name', 'image'],
|
|
27
|
+
'LocalBusiness': ['name', 'address'],
|
|
28
|
+
'Person': ['name'],
|
|
29
|
+
'Event': ['name', 'startDate', 'location'],
|
|
30
|
+
'Recipe': ['name', 'image', 'recipeIngredient'],
|
|
31
|
+
'FAQPage': ['mainEntity'],
|
|
32
|
+
'HowTo': ['name', 'step'],
|
|
33
|
+
'BreadcrumbList': ['itemListElement'],
|
|
34
|
+
'SoftwareApplication': ['name', 'operatingSystem'],
|
|
35
|
+
'VideoObject': ['name', 'description', 'thumbnailUrl', 'uploadDate'],
|
|
36
|
+
'ImageObject': ['contentUrl'],
|
|
37
|
+
'Review': ['itemReviewed', 'author'],
|
|
38
|
+
'AggregateRating': ['ratingValue', 'reviewCount'],
|
|
39
|
+
'Offer': ['price', 'priceCurrency'],
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Recommended properties for common schema types
|
|
43
|
+
const SCHEMA_RECOMMENDED_PROPERTIES: Record<string, string[]> = {
|
|
44
|
+
'Organization': ['logo', 'sameAs', 'contactPoint'],
|
|
45
|
+
'Article': ['image', 'publisher', 'dateModified'],
|
|
46
|
+
'Product': ['description', 'offers', 'brand', 'review', 'aggregateRating'],
|
|
47
|
+
'LocalBusiness': ['telephone', 'openingHours', 'geo'],
|
|
48
|
+
'SoftwareApplication': ['applicationCategory', 'offers', 'aggregateRating'],
|
|
49
|
+
'VideoObject': ['duration', 'embedUrl'],
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export function analyzeStructuredData(html: string, url: string): { issues: AuditIssue[]; data: StructuredDataData } {
|
|
53
|
+
const issues: AuditIssue[] = [];
|
|
54
|
+
const $ = cheerio.load(html);
|
|
55
|
+
const schemas: SchemaItem[] = [];
|
|
56
|
+
|
|
57
|
+
// Find all JSON-LD scripts
|
|
58
|
+
$('script[type="application/ld+json"]').each((_, el) => {
|
|
59
|
+
const content = $(el).html();
|
|
60
|
+
if (!content) return;
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const parsed = JSON.parse(content);
|
|
64
|
+
const items = Array.isArray(parsed) ? parsed : [parsed];
|
|
65
|
+
|
|
66
|
+
for (const item of items) {
|
|
67
|
+
if (item['@graph']) {
|
|
68
|
+
// Handle @graph format
|
|
69
|
+
for (const graphItem of item['@graph']) {
|
|
70
|
+
const schemaItem = validateSchemaItem(graphItem);
|
|
71
|
+
schemas.push(schemaItem);
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
const schemaItem = validateSchemaItem(item);
|
|
75
|
+
schemas.push(schemaItem);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} catch (e) {
|
|
79
|
+
issues.push({
|
|
80
|
+
...ISSUE_DEFINITIONS.SCHEMA_INVALID,
|
|
81
|
+
affectedUrls: [url],
|
|
82
|
+
details: {
|
|
83
|
+
error: e instanceof Error ? e.message : 'Invalid JSON',
|
|
84
|
+
content: content.substring(0, 200),
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Check if any schema exists
|
|
91
|
+
if (schemas.length === 0) {
|
|
92
|
+
issues.push({
|
|
93
|
+
...ISSUE_DEFINITIONS.SCHEMA_MISSING,
|
|
94
|
+
affectedUrls: [url],
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check for required properties
|
|
99
|
+
for (const schema of schemas) {
|
|
100
|
+
if (schema.errors.length > 0) {
|
|
101
|
+
issues.push({
|
|
102
|
+
...ISSUE_DEFINITIONS.SCHEMA_MISSING_REQUIRED,
|
|
103
|
+
affectedUrls: [url],
|
|
104
|
+
details: {
|
|
105
|
+
type: schema.type,
|
|
106
|
+
missingProperties: schema.errors,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const schemaTypes = schemas.map(s => s.type).filter(Boolean);
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
issues,
|
|
116
|
+
data: {
|
|
117
|
+
schemas,
|
|
118
|
+
hasSchema: schemas.length > 0,
|
|
119
|
+
schemaTypes,
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function validateSchemaItem(item: Record<string, unknown>): SchemaItem {
|
|
125
|
+
const type = (item['@type'] as string) || 'Unknown';
|
|
126
|
+
const errors: string[] = [];
|
|
127
|
+
const warnings: string[] = [];
|
|
128
|
+
|
|
129
|
+
// Check required properties
|
|
130
|
+
const required = SCHEMA_REQUIRED_PROPERTIES[type] || [];
|
|
131
|
+
for (const prop of required) {
|
|
132
|
+
if (!item[prop]) {
|
|
133
|
+
errors.push(`Missing required property: ${prop}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check recommended properties
|
|
138
|
+
const recommended = SCHEMA_RECOMMENDED_PROPERTIES[type] || [];
|
|
139
|
+
for (const prop of recommended) {
|
|
140
|
+
if (!item[prop]) {
|
|
141
|
+
warnings.push(`Missing recommended property: ${prop}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Validate specific types
|
|
146
|
+
if (type === 'BreadcrumbList') {
|
|
147
|
+
const items = item['itemListElement'] as unknown[];
|
|
148
|
+
if (items && Array.isArray(items)) {
|
|
149
|
+
for (let i = 0; i < items.length; i++) {
|
|
150
|
+
const crumb = items[i] as Record<string, unknown>;
|
|
151
|
+
if (!crumb['position']) {
|
|
152
|
+
errors.push(`Breadcrumb item ${i + 1} missing position`);
|
|
153
|
+
}
|
|
154
|
+
if (!crumb['name'] && !crumb['item']) {
|
|
155
|
+
errors.push(`Breadcrumb item ${i + 1} missing name or item`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (type === 'FAQPage') {
|
|
162
|
+
const mainEntity = item['mainEntity'] as unknown[];
|
|
163
|
+
if (!mainEntity || !Array.isArray(mainEntity) || mainEntity.length === 0) {
|
|
164
|
+
errors.push('FAQPage must have at least one question in mainEntity');
|
|
165
|
+
} else {
|
|
166
|
+
for (let i = 0; i < mainEntity.length; i++) {
|
|
167
|
+
const qa = mainEntity[i] as Record<string, unknown>;
|
|
168
|
+
if (!qa['name']) {
|
|
169
|
+
errors.push(`FAQ item ${i + 1} missing question (name)`);
|
|
170
|
+
}
|
|
171
|
+
if (!qa['acceptedAnswer']) {
|
|
172
|
+
errors.push(`FAQ item ${i + 1} missing answer (acceptedAnswer)`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (type === 'Product' && item['offers']) {
|
|
179
|
+
const offers = item['offers'] as Record<string, unknown>;
|
|
180
|
+
if (!offers['price'] && !offers['lowPrice']) {
|
|
181
|
+
warnings.push('Product offers should include price');
|
|
182
|
+
}
|
|
183
|
+
if (!offers['availability']) {
|
|
184
|
+
warnings.push('Product offers should include availability');
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
type,
|
|
190
|
+
data: item,
|
|
191
|
+
errors,
|
|
192
|
+
warnings,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Suggest appropriate schema types based on page content
|
|
197
|
+
export function suggestSchemaTypes(html: string, url: string): string[] {
|
|
198
|
+
const $ = cheerio.load(html);
|
|
199
|
+
const suggestions: string[] = [];
|
|
200
|
+
const path = new URL(url).pathname.toLowerCase();
|
|
201
|
+
|
|
202
|
+
// Always suggest Organization and WebSite for homepage
|
|
203
|
+
if (path === '/' || path === '') {
|
|
204
|
+
suggestions.push('Organization', 'WebSite');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Check for blog/article patterns
|
|
208
|
+
if (path.includes('/blog') || path.includes('/article') || path.includes('/post') || path.includes('/news')) {
|
|
209
|
+
suggestions.push('Article', 'BlogPosting');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Check for product patterns
|
|
213
|
+
if (path.includes('/product') || path.includes('/shop') || path.includes('/item')) {
|
|
214
|
+
suggestions.push('Product');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Check for FAQ patterns
|
|
218
|
+
if (path.includes('/faq') || path.includes('/help') || $('h2:contains("FAQ"), h3:contains("FAQ")').length > 0) {
|
|
219
|
+
suggestions.push('FAQPage');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Check for contact/about patterns
|
|
223
|
+
if (path.includes('/contact') || path.includes('/about')) {
|
|
224
|
+
suggestions.push('Organization', 'LocalBusiness');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Check for SaaS/software patterns
|
|
228
|
+
if (path.includes('/pricing') || $('meta[name="application-name"]').length > 0) {
|
|
229
|
+
suggestions.push('SoftwareApplication');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Check for breadcrumbs
|
|
233
|
+
if ($('[class*="breadcrumb"], nav[aria-label*="breadcrumb"]').length > 0) {
|
|
234
|
+
suggestions.push('BreadcrumbList');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return [...new Set(suggestions)];
|
|
238
|
+
}
|