recker 1.0.27 → 1.0.28-next.3bf98c7
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/dist/browser/scrape/extractors.js +2 -1
- package/dist/browser/scrape/types.d.ts +2 -1
- package/dist/cli/index.js +142 -3
- package/dist/cli/tui/shell.d.ts +2 -0
- package/dist/cli/tui/shell.js +492 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/scrape/extractors.js +2 -1
- package/dist/scrape/index.d.ts +2 -0
- package/dist/scrape/index.js +1 -0
- package/dist/scrape/spider.d.ts +61 -0
- package/dist/scrape/spider.js +250 -0
- package/dist/scrape/types.d.ts +2 -1
- package/dist/seo/analyzer.d.ts +42 -0
- package/dist/seo/analyzer.js +742 -0
- package/dist/seo/index.d.ts +7 -0
- package/dist/seo/index.js +3 -0
- package/dist/seo/rules/accessibility.d.ts +2 -0
- package/dist/seo/rules/accessibility.js +694 -0
- package/dist/seo/rules/best-practices.d.ts +2 -0
- package/dist/seo/rules/best-practices.js +188 -0
- package/dist/seo/rules/content.d.ts +2 -0
- package/dist/seo/rules/content.js +236 -0
- package/dist/seo/rules/crawl.d.ts +2 -0
- package/dist/seo/rules/crawl.js +307 -0
- package/dist/seo/rules/cwv.d.ts +2 -0
- package/dist/seo/rules/cwv.js +337 -0
- package/dist/seo/rules/ecommerce.d.ts +2 -0
- package/dist/seo/rules/ecommerce.js +252 -0
- package/dist/seo/rules/i18n.d.ts +2 -0
- package/dist/seo/rules/i18n.js +222 -0
- package/dist/seo/rules/images.d.ts +2 -0
- package/dist/seo/rules/images.js +180 -0
- package/dist/seo/rules/index.d.ts +52 -0
- package/dist/seo/rules/index.js +143 -0
- package/dist/seo/rules/internal-linking.d.ts +2 -0
- package/dist/seo/rules/internal-linking.js +375 -0
- package/dist/seo/rules/links.d.ts +2 -0
- package/dist/seo/rules/links.js +150 -0
- package/dist/seo/rules/local.d.ts +2 -0
- package/dist/seo/rules/local.js +265 -0
- package/dist/seo/rules/meta.d.ts +2 -0
- package/dist/seo/rules/meta.js +523 -0
- package/dist/seo/rules/mobile.d.ts +2 -0
- package/dist/seo/rules/mobile.js +71 -0
- package/dist/seo/rules/performance.d.ts +2 -0
- package/dist/seo/rules/performance.js +246 -0
- package/dist/seo/rules/pwa.d.ts +2 -0
- package/dist/seo/rules/pwa.js +302 -0
- package/dist/seo/rules/readability.d.ts +2 -0
- package/dist/seo/rules/readability.js +255 -0
- package/dist/seo/rules/schema.d.ts +2 -0
- package/dist/seo/rules/schema.js +54 -0
- package/dist/seo/rules/security.d.ts +2 -0
- package/dist/seo/rules/security.js +525 -0
- package/dist/seo/rules/social.d.ts +2 -0
- package/dist/seo/rules/social.js +373 -0
- package/dist/seo/rules/structural.d.ts +2 -0
- package/dist/seo/rules/structural.js +155 -0
- package/dist/seo/rules/technical.d.ts +2 -0
- package/dist/seo/rules/technical.js +223 -0
- package/dist/seo/rules/thresholds.d.ts +196 -0
- package/dist/seo/rules/thresholds.js +118 -0
- package/dist/seo/rules/types.d.ts +346 -0
- package/dist/seo/rules/types.js +11 -0
- package/dist/seo/seo-spider.d.ts +47 -0
- package/dist/seo/seo-spider.js +362 -0
- package/dist/seo/types.d.ts +184 -0
- package/dist/seo/types.js +1 -0
- package/dist/utils/columns.d.ts +14 -0
- package/dist/utils/columns.js +69 -0
- package/package.json +1 -1
|
@@ -76,6 +76,7 @@ export function extractImages($, options) {
|
|
|
76
76
|
height: height ? parseInt(height, 10) : undefined,
|
|
77
77
|
srcset: $el.attr('srcset'),
|
|
78
78
|
loading: $el.attr('loading'),
|
|
79
|
+
decoding: $el.attr('decoding'),
|
|
79
80
|
});
|
|
80
81
|
});
|
|
81
82
|
return images;
|
|
@@ -117,7 +118,7 @@ export function extractMeta($) {
|
|
|
117
118
|
meta.author = content;
|
|
118
119
|
break;
|
|
119
120
|
case 'robots':
|
|
120
|
-
meta.robots = content;
|
|
121
|
+
meta.robots = content.split(',').map((r) => r.trim().toLowerCase());
|
|
121
122
|
break;
|
|
122
123
|
case 'viewport':
|
|
123
124
|
meta.viewport = content;
|
|
@@ -14,13 +14,14 @@ export interface ExtractedImage {
|
|
|
14
14
|
height?: number;
|
|
15
15
|
srcset?: string;
|
|
16
16
|
loading?: 'lazy' | 'eager';
|
|
17
|
+
decoding?: 'async' | 'auto' | 'sync';
|
|
17
18
|
}
|
|
18
19
|
export interface ExtractedMeta {
|
|
19
20
|
title?: string;
|
|
20
21
|
description?: string;
|
|
21
22
|
keywords?: string[];
|
|
22
23
|
author?: string;
|
|
23
|
-
robots?: string;
|
|
24
|
+
robots?: string[];
|
|
24
25
|
canonical?: string;
|
|
25
26
|
viewport?: string;
|
|
26
27
|
charset?: string;
|
package/dist/cli/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { program } from 'commander';
|
|
|
3
3
|
import { promises as fs } from 'node:fs';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import colors from '../utils/colors.js';
|
|
6
|
+
import { formatColumns } from '../utils/columns.js';
|
|
6
7
|
async function readStdin() {
|
|
7
8
|
if (process.stdin.isTTY) {
|
|
8
9
|
return null;
|
|
@@ -120,7 +121,13 @@ async function main() {
|
|
|
120
121
|
}
|
|
121
122
|
return { method, url, headers, data };
|
|
122
123
|
}
|
|
123
|
-
const
|
|
124
|
+
const utilityFunctions = [
|
|
125
|
+
'registry', 'presetRegistry', 'detectPreset', 'getPreset',
|
|
126
|
+
'listPresets', 'listAIPresets', 'listCloudPresets', 'listSaaSPresets', 'listDevToolsPresets'
|
|
127
|
+
];
|
|
128
|
+
const PRESET_NAMES = Object.keys(presets)
|
|
129
|
+
.filter(k => !utilityFunctions.includes(k) && !k.startsWith('_') && typeof presets[k] === 'function')
|
|
130
|
+
.sort();
|
|
124
131
|
program
|
|
125
132
|
.name('rek')
|
|
126
133
|
.description('The HTTP Client for Humans (and Robots)')
|
|
@@ -131,7 +138,7 @@ async function main() {
|
|
|
131
138
|
.option('-o, --output <file>', 'Write response body to file')
|
|
132
139
|
.option('-j, --json', 'Force JSON content-type')
|
|
133
140
|
.option('-e, --env [path]', 'Load .env file from current directory or specified path')
|
|
134
|
-
.addHelpText('after', `
|
|
141
|
+
.addHelpText('after', () => `
|
|
135
142
|
${colors.bold(colors.yellow('Examples:'))}
|
|
136
143
|
${colors.green('$ rek httpbin.org/json')}
|
|
137
144
|
${colors.green('$ rek post api.com/users name="Cyber" role="Admin"')}
|
|
@@ -139,7 +146,7 @@ ${colors.bold(colors.yellow('Examples:'))}
|
|
|
139
146
|
${colors.green('$ rek @openai/v1/chat/completions model="gpt-5.1"')}
|
|
140
147
|
|
|
141
148
|
${colors.bold(colors.yellow('Available Presets:'))}
|
|
142
|
-
|
|
149
|
+
${formatColumns(PRESET_NAMES, { prefix: '@', indent: 2, minWidth: 16, transform: colors.cyan })}
|
|
143
150
|
`)
|
|
144
151
|
.action(async (args, options) => {
|
|
145
152
|
if (args.length === 0) {
|
|
@@ -381,6 +388,138 @@ ${colors.bold('Details:')}`);
|
|
|
381
388
|
process.exit(1);
|
|
382
389
|
}
|
|
383
390
|
});
|
|
391
|
+
program
|
|
392
|
+
.command('seo')
|
|
393
|
+
.description('Analyze page SEO (title, meta, headings, links, images, structured data)')
|
|
394
|
+
.argument('<url>', 'URL to analyze')
|
|
395
|
+
.option('-a, --all', 'Show all checks including passed ones')
|
|
396
|
+
.option('--format <format>', 'Output format: text (default) or json', 'text')
|
|
397
|
+
.addHelpText('after', `
|
|
398
|
+
${colors.bold(colors.yellow('Examples:'))}
|
|
399
|
+
${colors.green('$ rek seo example.com')} ${colors.gray('Basic SEO analysis')}
|
|
400
|
+
${colors.green('$ rek seo example.com -a')} ${colors.gray('Show all checks')}
|
|
401
|
+
${colors.green('$ rek seo example.com --format json')} ${colors.gray('Output as JSON')}
|
|
402
|
+
${colors.green('$ rek seo example.com --format json | jq')} ${colors.gray('Pipe to jq for processing')}
|
|
403
|
+
|
|
404
|
+
${colors.bold(colors.yellow('Checks:'))}
|
|
405
|
+
${colors.cyan('Title Tag')} Length and presence
|
|
406
|
+
${colors.cyan('Meta Description')} Length and presence
|
|
407
|
+
${colors.cyan('Headings')} H1 presence and hierarchy
|
|
408
|
+
${colors.cyan('Images')} Alt text coverage
|
|
409
|
+
${colors.cyan('Links')} Internal/external distribution
|
|
410
|
+
${colors.cyan('OpenGraph')} Social sharing meta tags
|
|
411
|
+
${colors.cyan('Twitter Card')} Twitter sharing meta tags
|
|
412
|
+
${colors.cyan('Structured Data')} JSON-LD presence
|
|
413
|
+
${colors.cyan('Technical')} Canonical, viewport, lang
|
|
414
|
+
`)
|
|
415
|
+
.action(async (url, options) => {
|
|
416
|
+
if (!url.startsWith('http'))
|
|
417
|
+
url = `https://${url}`;
|
|
418
|
+
const isJson = options.format === 'json';
|
|
419
|
+
const { createClient } = await import('../core/client.js');
|
|
420
|
+
const { analyzeSeo } = await import('../seo/analyzer.js');
|
|
421
|
+
if (!isJson) {
|
|
422
|
+
console.log(colors.gray(`Analyzing SEO for ${url}...`));
|
|
423
|
+
}
|
|
424
|
+
try {
|
|
425
|
+
const client = createClient({ timeout: 30000 });
|
|
426
|
+
const res = await client.get(url);
|
|
427
|
+
const html = await res.text();
|
|
428
|
+
const report = await analyzeSeo(html, { baseUrl: url });
|
|
429
|
+
if (isJson) {
|
|
430
|
+
const jsonOutput = {
|
|
431
|
+
url,
|
|
432
|
+
analyzedAt: new Date().toISOString(),
|
|
433
|
+
score: report.score,
|
|
434
|
+
grade: report.grade,
|
|
435
|
+
title: report.title,
|
|
436
|
+
metaDescription: report.metaDescription,
|
|
437
|
+
content: report.content,
|
|
438
|
+
headings: report.headings,
|
|
439
|
+
links: report.links,
|
|
440
|
+
images: report.images,
|
|
441
|
+
openGraph: report.social.openGraph,
|
|
442
|
+
twitterCard: report.social.twitterCard,
|
|
443
|
+
jsonLd: report.jsonLd,
|
|
444
|
+
technical: report.technical,
|
|
445
|
+
checks: report.checks,
|
|
446
|
+
summary: {
|
|
447
|
+
total: report.checks.length,
|
|
448
|
+
passed: report.checks.filter(c => c.status === 'pass').length,
|
|
449
|
+
warnings: report.checks.filter(c => c.status === 'warn').length,
|
|
450
|
+
errors: report.checks.filter(c => c.status === 'fail').length,
|
|
451
|
+
info: report.checks.filter(c => c.status === 'info').length,
|
|
452
|
+
},
|
|
453
|
+
};
|
|
454
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
let gradeColor = colors.red;
|
|
458
|
+
if (report.grade === 'A')
|
|
459
|
+
gradeColor = colors.green;
|
|
460
|
+
else if (report.grade === 'B')
|
|
461
|
+
gradeColor = colors.blue;
|
|
462
|
+
else if (report.grade === 'C')
|
|
463
|
+
gradeColor = colors.yellow;
|
|
464
|
+
console.log(`
|
|
465
|
+
${colors.bold(colors.cyan('🔍 SEO Analysis Report'))}
|
|
466
|
+
${colors.gray('URL:')} ${url}
|
|
467
|
+
${colors.gray('Grade:')} ${gradeColor(colors.bold(report.grade))} ${colors.gray('Score:')} ${report.score}/100
|
|
468
|
+
`);
|
|
469
|
+
if (report.title) {
|
|
470
|
+
console.log(`${colors.bold('Title:')} ${colors.gray(report.title.text.slice(0, 60))}${report.title.text.length > 60 ? '...' : ''} ${colors.gray(`(${report.title.length} chars)`)}`);
|
|
471
|
+
}
|
|
472
|
+
if (report.metaDescription) {
|
|
473
|
+
console.log(`${colors.bold('Description:')} ${colors.gray(report.metaDescription.text.slice(0, 80))}${report.metaDescription.text.length > 80 ? '...' : ''}`);
|
|
474
|
+
}
|
|
475
|
+
console.log('');
|
|
476
|
+
console.log(`${colors.bold('Content Metrics:')}`);
|
|
477
|
+
console.log(` ${colors.gray('Words:')} ${report.content.wordCount} ${colors.gray('Reading time:')} ~${report.content.readingTimeMinutes} min`);
|
|
478
|
+
console.log(` ${colors.gray('Headings:')} H1×${report.headings.h1Count}, total ${report.headings.structure.length}`);
|
|
479
|
+
console.log(` ${colors.gray('Links:')} ${report.links.total} (${report.links.internal} internal, ${report.links.external} external)`);
|
|
480
|
+
console.log(` ${colors.gray('Images:')} ${report.images.total} (${report.images.withAlt} with alt, ${report.images.withoutAlt} without)`);
|
|
481
|
+
console.log('');
|
|
482
|
+
console.log(`${colors.bold('Checks:')}`);
|
|
483
|
+
const checksToShow = options.all
|
|
484
|
+
? report.checks
|
|
485
|
+
: report.checks.filter(c => c.status !== 'pass' && c.status !== 'info');
|
|
486
|
+
if (checksToShow.length === 0 && !options.all) {
|
|
487
|
+
console.log(colors.green(' All checks passed! Use -a to see details.'));
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
for (const check of checksToShow) {
|
|
491
|
+
const icon = check.status === 'pass' ? colors.green('✔')
|
|
492
|
+
: check.status === 'warn' ? colors.yellow('⚠')
|
|
493
|
+
: check.status === 'fail' ? colors.red('✖')
|
|
494
|
+
: colors.gray('ℹ');
|
|
495
|
+
const name = colors.bold(check.name.padEnd(18));
|
|
496
|
+
console.log(` ${icon} ${name} ${check.message}`);
|
|
497
|
+
if (check.recommendation && check.status !== 'pass') {
|
|
498
|
+
console.log(` ${colors.gray('→')} ${colors.gray(check.recommendation)}`);
|
|
499
|
+
}
|
|
500
|
+
const evidence = check.evidence;
|
|
501
|
+
if (evidence && check.status !== 'pass') {
|
|
502
|
+
if (evidence.found && Array.isArray(evidence.found) && evidence.found.length > 0) {
|
|
503
|
+
const items = evidence.found.slice(0, 3);
|
|
504
|
+
console.log(` ${colors.gray('Found:')} ${colors.red(items.join(', '))}${evidence.found.length > 3 ? ` (+${evidence.found.length - 3} more)` : ''}`);
|
|
505
|
+
}
|
|
506
|
+
if (evidence.example) {
|
|
507
|
+
console.log(` ${colors.gray('Example:')} ${colors.cyan(evidence.example.split('\n')[0])}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
console.log('');
|
|
513
|
+
if (report.jsonLd.count > 0) {
|
|
514
|
+
console.log(`${colors.bold('Structured Data:')} ${report.jsonLd.types.join(', ') || 'Present'}`);
|
|
515
|
+
console.log('');
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
console.error(colors.red(`SEO analysis failed: ${error.message}`));
|
|
520
|
+
process.exit(1);
|
|
521
|
+
}
|
|
522
|
+
});
|
|
384
523
|
program
|
|
385
524
|
.command('ip')
|
|
386
525
|
.description('Get IP address intelligence using local GeoLite2 database')
|
package/dist/cli/tui/shell.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ export declare class RekShell {
|
|
|
43
43
|
private runWhois;
|
|
44
44
|
private runTLS;
|
|
45
45
|
private runSecurityGrader;
|
|
46
|
+
private runSeo;
|
|
46
47
|
private runIpIntelligence;
|
|
47
48
|
private runDNS;
|
|
48
49
|
private runDNSPropagation;
|
|
@@ -50,6 +51,7 @@ export declare class RekShell {
|
|
|
50
51
|
private runRDAP;
|
|
51
52
|
private runPing;
|
|
52
53
|
private runScrap;
|
|
54
|
+
private runSpider;
|
|
53
55
|
private runSelect;
|
|
54
56
|
private runSelectText;
|
|
55
57
|
private runSelectAttr;
|