recker 1.0.73 → 1.0.75-next.a400152

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 (108) hide show
  1. package/README.md +5 -18
  2. package/dist/browser/core/client.d.ts +14 -8
  3. package/dist/browser/core/client.js +199 -17
  4. package/dist/browser/core/errors.d.ts +15 -1
  5. package/dist/browser/core/errors.js +140 -9
  6. package/dist/browser/core/request.d.ts +5 -0
  7. package/dist/browser/core/request.js +33 -2
  8. package/dist/browser/core-runtime/plugin-manifest.d.ts +24 -0
  9. package/dist/browser/core-runtime/plugin-manifest.js +159 -0
  10. package/dist/browser/core-runtime/request-context.d.ts +13 -0
  11. package/dist/browser/core-runtime/request-context.js +24 -0
  12. package/dist/browser/core-runtime/typed-events.d.ts +89 -0
  13. package/dist/browser/core-runtime/typed-events.js +34 -0
  14. package/dist/browser/index.iife.min.js +79 -79
  15. package/dist/browser/index.min.js +79 -79
  16. package/dist/browser/index.mini.iife.js +913 -97
  17. package/dist/browser/index.mini.iife.min.js +46 -46
  18. package/dist/browser/index.mini.min.js +46 -46
  19. package/dist/browser/index.mini.umd.js +913 -97
  20. package/dist/browser/index.mini.umd.min.js +46 -46
  21. package/dist/browser/index.umd.min.js +79 -79
  22. package/dist/browser/plugins/auth/aws-sigv4.d.ts +1 -0
  23. package/dist/browser/plugins/auth/aws-sigv4.js +19 -2
  24. package/dist/browser/plugins/retry.js +29 -1
  25. package/dist/browser/presets/aws.d.ts +1 -0
  26. package/dist/browser/presets/aws.js +62 -1
  27. package/dist/browser/runner/request-runner.d.ts +15 -5
  28. package/dist/browser/runner/request-runner.js +164 -30
  29. package/dist/browser/scrape/parser/nodes/html.d.ts +6 -0
  30. package/dist/browser/scrape/parser/nodes/html.js +70 -18
  31. package/dist/browser/scrape/parser/nodes/node.d.ts +1 -0
  32. package/dist/browser/scrape/parser/nodes/node.js +5 -0
  33. package/dist/browser/scrape/spider.d.ts +1 -0
  34. package/dist/browser/scrape/spider.js +39 -26
  35. package/dist/browser/search/google.d.ts +70 -0
  36. package/dist/browser/search/google.js +617 -0
  37. package/dist/browser/seo/analyzer.d.ts +1 -1
  38. package/dist/browser/seo/analyzer.js +73 -42
  39. package/dist/browser/seo/index.d.ts +3 -1
  40. package/dist/browser/seo/index.js +1 -0
  41. package/dist/browser/seo/keyword-campaign.d.ts +107 -0
  42. package/dist/browser/seo/keyword-campaign.js +380 -0
  43. package/dist/browser/seo/rules/types.d.ts +2 -0
  44. package/dist/browser/seo/seo-spider.d.ts +2 -3
  45. package/dist/browser/seo/seo-spider.js +26 -202
  46. package/dist/browser/seo/types.d.ts +4 -0
  47. package/dist/browser/seo/validators/sitemap.js +9 -2
  48. package/dist/browser/transport/fetch.js +38 -5
  49. package/dist/browser/transport/undici.js +73 -11
  50. package/dist/browser/transport/worker.d.ts +0 -1
  51. package/dist/browser/transport/worker.js +1 -3
  52. package/dist/browser/types/index.d.ts +24 -0
  53. package/dist/cli/commands/mcp.js +5 -3
  54. package/dist/core/client.d.ts +14 -8
  55. package/dist/core/client.js +199 -17
  56. package/dist/core/errors.d.ts +15 -1
  57. package/dist/core/errors.js +140 -9
  58. package/dist/core/request.d.ts +5 -0
  59. package/dist/core/request.js +33 -2
  60. package/dist/core-runtime/plugin-manifest.d.ts +24 -0
  61. package/dist/core-runtime/plugin-manifest.js +159 -0
  62. package/dist/core-runtime/request-context.d.ts +13 -0
  63. package/dist/core-runtime/request-context.js +24 -0
  64. package/dist/core-runtime/typed-events.d.ts +89 -0
  65. package/dist/core-runtime/typed-events.js +34 -0
  66. package/dist/index.d.ts +2 -1
  67. package/dist/index.js +2 -1
  68. package/dist/mcp/cli.js +10 -8
  69. package/dist/mcp/profiles.d.ts +1 -1
  70. package/dist/mcp/profiles.js +31 -6
  71. package/dist/mcp/tools/categories.js +0 -1
  72. package/dist/plugins/auth/aws-sigv4.d.ts +1 -0
  73. package/dist/plugins/auth/aws-sigv4.js +19 -2
  74. package/dist/plugins/retry.js +29 -1
  75. package/dist/presets/aws.d.ts +1 -0
  76. package/dist/presets/aws.js +62 -1
  77. package/dist/recker.d.ts +3 -0
  78. package/dist/recker.js +5 -0
  79. package/dist/runner/request-runner.d.ts +15 -5
  80. package/dist/runner/request-runner.js +164 -30
  81. package/dist/scrape/parser/nodes/html.d.ts +6 -0
  82. package/dist/scrape/parser/nodes/html.js +70 -18
  83. package/dist/scrape/parser/nodes/node.d.ts +1 -0
  84. package/dist/scrape/parser/nodes/node.js +5 -0
  85. package/dist/scrape/spider.d.ts +1 -0
  86. package/dist/scrape/spider.js +39 -26
  87. package/dist/search/google.d.ts +70 -0
  88. package/dist/search/google.js +617 -0
  89. package/dist/search/index.d.ts +3 -0
  90. package/dist/search/index.js +1 -0
  91. package/dist/seo/analyzer.d.ts +1 -1
  92. package/dist/seo/analyzer.js +73 -42
  93. package/dist/seo/index.d.ts +3 -1
  94. package/dist/seo/index.js +1 -0
  95. package/dist/seo/keyword-campaign.d.ts +107 -0
  96. package/dist/seo/keyword-campaign.js +380 -0
  97. package/dist/seo/rules/types.d.ts +2 -0
  98. package/dist/seo/seo-spider.d.ts +2 -3
  99. package/dist/seo/seo-spider.js +26 -202
  100. package/dist/seo/types.d.ts +4 -0
  101. package/dist/seo/validators/sitemap.js +9 -2
  102. package/dist/transport/fetch.js +38 -5
  103. package/dist/transport/undici.js +73 -11
  104. package/dist/transport/worker.d.ts +0 -1
  105. package/dist/transport/worker.js +1 -3
  106. package/dist/types/index.d.ts +24 -0
  107. package/dist/version.js +1 -1
  108. package/package.json +9 -1
@@ -1,7 +1,7 @@
1
1
  import { parse } from '../scrape/parser/index.js';
2
2
  import { extractMeta, extractOpenGraph, extractTwitterCard, extractJsonLd, extractLinks, extractImages, } from '../scrape/extractors.js';
3
3
  import { generateKeywordCloud } from './keywords.js';
4
- import { createRulesEngine, SEO_THRESHOLDS, } from './rules/index.js';
4
+ import { createRulesEngine, SEO_THRESHOLDS, calculateWeightedScore, } from './rules/index.js';
5
5
  export class SeoAnalyzer {
6
6
  root;
7
7
  options;
@@ -47,7 +47,9 @@ export class SeoAnalyzer {
47
47
  const analytics = this.analyzeAnalytics();
48
48
  const feeds = this.analyzeFeeds();
49
49
  const conversion = this.analyzeConversionElements(links, visibleText);
50
+ const pageType = this.detectPageType(jsonLd);
50
51
  const context = this.buildRuleContext({
52
+ pageType,
51
53
  meta,
52
54
  og,
53
55
  twitter,
@@ -68,7 +70,7 @@ export class SeoAnalyzer {
68
70
  });
69
71
  const ruleResults = this.rulesEngine.evaluate(context);
70
72
  const checks = this.convertToCheckResults(ruleResults);
71
- const { score, grade } = this.calculateScore(checks);
73
+ const { score, grade } = this.calculateScore(ruleResults);
72
74
  const summary = this.buildSummary(ruleResults, checks, {
73
75
  content,
74
76
  imageAnalysis,
@@ -77,13 +79,17 @@ export class SeoAnalyzer {
77
79
  og,
78
80
  twitter,
79
81
  technical,
82
+ pageType,
83
+ timings: this.options.timings,
80
84
  });
81
85
  return {
82
86
  url,
83
87
  timestamp: new Date(),
84
88
  grade,
85
89
  score,
90
+ timing: this.options.timings,
86
91
  summary,
92
+ pageType,
87
93
  checks,
88
94
  title: meta.title
89
95
  ? { text: meta.title, length: meta.title.length }
@@ -134,6 +140,47 @@ export class SeoAnalyzer {
134
140
  return bodies[0];
135
141
  return bodies.reduce((prev, curr) => curr.text.length > prev.text.length ? curr : prev);
136
142
  }
143
+ detectPageType(jsonLd) {
144
+ if (!this.options.baseUrl) {
145
+ return 'other';
146
+ }
147
+ try {
148
+ const parsed = new URL(this.options.baseUrl);
149
+ const pathname = parsed.pathname.toLowerCase();
150
+ const hasQueryKeyword = (value) => parsed.searchParams.has(value);
151
+ if (pathname === '/' || pathname === '') {
152
+ return 'homepage';
153
+ }
154
+ if (/(^|\/)(search|busca|s|results|query)\b/.test(pathname) ||
155
+ hasQueryKeyword('q') ||
156
+ hasQueryKeyword('query') ||
157
+ hasQueryKeyword('search')) {
158
+ return 'search';
159
+ }
160
+ const productSignals = ['product', 'produto', 'item', 'sku', 'shop'];
161
+ if (productSignals.some((segment) => pathname.includes(`/${segment}/`))) {
162
+ return 'product';
163
+ }
164
+ const articleSignals = ['article', 'post', 'blog', 'noticia', 'news'];
165
+ if (articleSignals.some((segment) => pathname.includes(`/${segment}/`)) ||
166
+ this.root.querySelectorAll('article').length > 0) {
167
+ return 'article';
168
+ }
169
+ if (/(^|\/)(categoria|category|tag|section|topic)\b/.test(pathname)) {
170
+ return 'category';
171
+ }
172
+ const hasProductJsonLd = jsonLd
173
+ .map((node) => node['@type'])
174
+ .some((type) => typeof type === 'string' && type.toLowerCase() === 'product');
175
+ if (hasProductJsonLd) {
176
+ return 'product';
177
+ }
178
+ return 'other';
179
+ }
180
+ catch {
181
+ return 'other';
182
+ }
183
+ }
137
184
  getVisibleText() {
138
185
  const body = this.getMainBody();
139
186
  if (!body)
@@ -159,7 +206,7 @@ export class SeoAnalyzer {
159
206
  return clone.text.replace(/\s+/g, ' ').trim();
160
207
  }
161
208
  buildRuleContext(data) {
162
- const { meta, og, twitter, jsonLd, headings, content, linkAnalysis, imageAnalysis, links, keywords, resources, emailsFound, socialLinksFound, socialLinkDetails, analytics, feeds, conversion, } = data;
209
+ const { meta, og, twitter, jsonLd, headings, content, pageType, linkAnalysis, imageAnalysis, links, keywords, resources, emailsFound, socialLinksFound, socialLinkDetails, analytics, feeds, conversion, } = data;
163
210
  const html = this.root.querySelector('html');
164
211
  const htmlLang = html ? html.getAttribute('lang') : undefined;
165
212
  const hreflangTags = [];
@@ -198,7 +245,6 @@ export class SeoAnalyzer {
198
245
  const hasMixedContent = this.checkMixedContent();
199
246
  const h1Elements = this.root.querySelectorAll('h1');
200
247
  const h1Text = h1Elements.length > 0 ? h1Elements[0].text.trim() : '';
201
- const iframeCount = this.root.querySelectorAll('iframe').length;
202
248
  const topKeywords = keywords.topKeywords.slice(0, 5).map(k => k.word);
203
249
  const mainKeyword = topKeywords.length > 0 ? topKeywords[0] : undefined;
204
250
  const keywordsInTitle = topKeywords.some(kw => meta.title?.toLowerCase().includes(kw));
@@ -234,7 +280,6 @@ export class SeoAnalyzer {
234
280
  const structuralHtml = this.analyzeStructuralHtml();
235
281
  const breadcrumbs = this.analyzeBreadcrumbs(jsonLd.map((j) => j['@type']).filter(Boolean));
236
282
  const multimedia = this.analyzeMultimedia();
237
- const advancedImages = this.analyzeAdvancedImages();
238
283
  const responsiveImages = this.analyzeResponsiveImages();
239
284
  const inlineImages = this.analyzeInlineImages();
240
285
  const trustSignals = this.analyzeTrustSignals(links);
@@ -245,6 +290,7 @@ export class SeoAnalyzer {
245
290
  : 0;
246
291
  const textHtmlRatio = this.calculateTextHtmlRatio(content.characterCount);
247
292
  return {
293
+ pageType,
248
294
  jsFilesCount: resources.jsFilesCount,
249
295
  cssFilesCount: resources.cssFilesCount,
250
296
  unminifiedResources: resources.unminifiedResources,
@@ -774,7 +820,6 @@ export class SeoAnalyzer {
774
820
  }
775
821
  analyzeAnalytics() {
776
822
  const providers = [];
777
- const html = this.root.innerHTML || '';
778
823
  const scripts = this.root.querySelectorAll('script');
779
824
  const scriptSources = [];
780
825
  const scriptContents = [];
@@ -899,21 +944,6 @@ export class SeoAnalyzer {
899
944
  hasPhoneOnPage,
900
945
  };
901
946
  }
902
- analyzeAdvancedImages() {
903
- let imagesWithSrcset = 0;
904
- let largeBase64ImagesCount = 0;
905
- const imgs = this.root.querySelectorAll('img');
906
- imgs.forEach((img) => {
907
- if (img.getAttribute('srcset') || (img.parentNode && img.parentNode.tagName === 'PICTURE')) {
908
- imagesWithSrcset++;
909
- }
910
- const src = img.getAttribute('src') || '';
911
- if (src.startsWith('data:image') && src.length > 5 * 1024) {
912
- largeBase64ImagesCount++;
913
- }
914
- });
915
- return { imagesWithSrcset, largeBase64ImagesCount };
916
- }
917
947
  calculateTextHtmlRatio(bodyTextLength) {
918
948
  const htmlSize = this.root.innerHTML?.length;
919
949
  if (htmlSize && htmlSize > 0) {
@@ -926,6 +956,7 @@ export class SeoAnalyzer {
926
956
  id: r.id,
927
957
  name: r.name,
928
958
  category: r.category,
959
+ severity: r.severity,
929
960
  status: r.status,
930
961
  message: r.message,
931
962
  value: r.value,
@@ -934,6 +965,8 @@ export class SeoAnalyzer {
934
965
  }));
935
966
  }
936
967
  buildSummary(ruleResults, checks, data) {
968
+ const pageType = data.pageType;
969
+ const timings = data.timings;
937
970
  const passed = checks.filter((c) => c.status === 'pass').length;
938
971
  const warnings = checks.filter((c) => c.status === 'warn').length;
939
972
  const errors = checks.filter((c) => c.status === 'fail').length;
@@ -954,21 +987,25 @@ export class SeoAnalyzer {
954
987
  else if (result.status === 'fail')
955
988
  issuesByCategory[cat].errors++;
956
989
  }
957
- const topIssues = ruleResults
958
- .filter((r) => r.status === 'fail' || r.status === 'warn')
990
+ const topIssues = checks
991
+ .filter((c) => c.status === 'fail' || c.status === 'warn')
959
992
  .sort((a, b) => {
960
- if (a.status === 'fail' && b.status !== 'fail')
961
- return -1;
962
- if (a.status !== 'fail' && b.status === 'fail')
963
- return 1;
964
- return 0;
993
+ const severityOrder = (status) => status === 'fail' ? 2 : 1;
994
+ const statusDiff = severityOrder(b.status) - severityOrder(a.status);
995
+ if (statusDiff !== 0)
996
+ return statusDiff;
997
+ const aSeverity = a.severity || (a.status === 'fail' ? 'error' : 'warning');
998
+ const bSeverity = b.severity || (b.status === 'fail' ? 'error' : 'warning');
999
+ if (aSeverity === bSeverity)
1000
+ return 0;
1001
+ return aSeverity === 'error' ? -1 : 1;
965
1002
  })
966
1003
  .slice(0, 5)
967
1004
  .map((r) => ({
968
1005
  name: r.name,
969
1006
  message: r.message,
970
1007
  category: r.category,
971
- severity: (r.status === 'fail' ? 'error' : 'warning'),
1008
+ severity: (r.severity || (r.status === 'fail' ? 'error' : 'warning')),
972
1009
  }));
973
1010
  const quickWins = [];
974
1011
  if (!data.meta.title)
@@ -993,8 +1030,8 @@ export class SeoAnalyzer {
993
1030
  const vitals = {
994
1031
  htmlSize,
995
1032
  domElements,
996
- ttfb: this.options.responseHeaders ? undefined : undefined,
997
- totalTime: undefined,
1033
+ ttfb: timings?.ttfb,
1034
+ totalTime: timings?.total,
998
1035
  wordCount: data.content.wordCount,
999
1036
  totalWordCount: data.content.totalWordCount,
1000
1037
  readingTime: data.content.readingTimeMinutes,
@@ -1017,6 +1054,7 @@ export class SeoAnalyzer {
1017
1054
  infos,
1018
1055
  passRate,
1019
1056
  issuesByCategory,
1057
+ pageType: pageType,
1020
1058
  topIssues,
1021
1059
  quickWins: limitedQuickWins,
1022
1060
  vitals,
@@ -1387,18 +1425,11 @@ export class SeoAnalyzer {
1387
1425
  unminifiedResourceUrls: unminified
1388
1426
  };
1389
1427
  }
1390
- calculateScore(checks) {
1391
- const weights = {
1392
- pass: 100,
1393
- warn: 50,
1394
- fail: 0,
1395
- info: 100,
1396
- };
1397
- const scoringChecks = checks.filter((c) => c.status !== 'info');
1398
- if (scoringChecks.length === 0)
1428
+ calculateScore(results) {
1429
+ if (results.length === 0)
1399
1430
  return { score: 100, grade: 'A' };
1400
- const totalWeight = scoringChecks.reduce((sum, check) => sum + weights[check.status], 0);
1401
- const score = Math.round(totalWeight / scoringChecks.length);
1431
+ const { score: weightedScore } = calculateWeightedScore(results);
1432
+ const score = weightedScore;
1402
1433
  let grade;
1403
1434
  if (score >= 90)
1404
1435
  grade = 'A';
@@ -6,7 +6,7 @@ export type { SeoSpiderOptions, SeoPageResult, SiteWideIssue, SeoSpiderResult, }
6
6
  export { SeoRulesEngine, createRulesEngine, SEO_THRESHOLDS, ALL_SEO_RULES, } from './rules/index.js';
7
7
  export { generateSeoFilename, resolveOutputPath, writeReport, formatReportForJson, } from './output.js';
8
8
  export type { SeoOutputType, OutputOptions, WriteOptions } from './output.js';
9
- export type { SeoReport, SeoCheckResult, SeoStatus, SeoTiming, HeadingAnalysis, HeadingInfo, ContentMetrics, LinkAnalysis, ImageAnalysis, SocialMetaAnalysis, TechnicalSeo, SeoAnalyzerOptions, } from './types.js';
9
+ export type { SeoReport, SeoCheckResult, SeoStatus, SeoPageType, SeoTiming, HeadingAnalysis, HeadingInfo, ContentMetrics, LinkAnalysis, ImageAnalysis, SocialMetaAnalysis, TechnicalSeo, SeoAnalyzerOptions, } from './types.js';
10
10
  export type { SeoRule, RuleContext, RuleResult, RuleEvidence, RuleCategory, RuleSeverity, RulesEngineOptions, } from './rules/index.js';
11
11
  export type { SeoAnalyzerFullOptions } from './analyzer.js';
12
12
  export { parseRobotsTxt, validateRobotsTxt, isPathAllowed, fetchAndValidateRobotsTxt, } from './validators/robots.js';
@@ -15,3 +15,5 @@ export { parseSitemap, validateSitemap, discoverSitemaps, fetchAndValidateSitema
15
15
  export type { SitemapUrl, SitemapIndex, SitemapParseResult, SitemapValidationIssue, SitemapValidationResult, } from './validators/sitemap.js';
16
16
  export { parseLlmsTxt, validateLlmsTxt, fetchAndValidateLlmsTxt, generateLlmsTxtTemplate, } from './validators/llms-txt.js';
17
17
  export type { LlmsTxtLink, LlmsTxtSection, LlmsTxtParseResult, LlmsTxtValidationIssue, LlmsTxtValidationResult, } from './validators/llms-txt.js';
18
+ export { analyzeKeywordCampaign, extractKeywordCampaignSeedsFromReport, } from './keyword-campaign.js';
19
+ export type { CampaignActivitySignal, CampaignResultPlacement, KeywordCampaignCompetitorResult, KeywordCampaignCompetitorSummary, KeywordCampaignExtractionOptions, KeywordCampaignOptions, KeywordCampaignPageStats, KeywordCampaignReport, KeywordCampaignResult, KeywordCampaignSeed, KeywordCampaignSeedInput, KeywordCampaignSource, KeywordCampaignSummary, } from './keyword-campaign.js';
package/dist/seo/index.js CHANGED
@@ -6,3 +6,4 @@ export { generateSeoFilename, resolveOutputPath, writeReport, formatReportForJso
6
6
  export { parseRobotsTxt, validateRobotsTxt, isPathAllowed, fetchAndValidateRobotsTxt, } from './validators/robots.js';
7
7
  export { parseSitemap, validateSitemap, discoverSitemaps, fetchAndValidateSitemap, } from './validators/sitemap.js';
8
8
  export { parseLlmsTxt, validateLlmsTxt, fetchAndValidateLlmsTxt, generateLlmsTxtTemplate, } from './validators/llms-txt.js';
9
+ export { analyzeKeywordCampaign, extractKeywordCampaignSeedsFromReport, } from './keyword-campaign.js';
@@ -0,0 +1,107 @@
1
+ import type { SearchTransport } from '../search/google.js';
2
+ import type { SeoReport } from './types.js';
3
+ export type KeywordCampaignSource = 'discovered' | 'preset';
4
+ export type CampaignResultPlacement = 'ad' | 'organic' | 'unknown';
5
+ export interface KeywordCampaignSeedInput {
6
+ keyword: string;
7
+ source?: KeywordCampaignSource;
8
+ sourcePage?: string;
9
+ weight?: number;
10
+ }
11
+ export interface KeywordCampaignSeed {
12
+ keyword: string;
13
+ normalizedKeyword: string;
14
+ source: KeywordCampaignSource;
15
+ sourcePage?: string;
16
+ weight: number;
17
+ }
18
+ export interface KeywordCampaignOptions {
19
+ targetUrl: string;
20
+ discoveredKeywords?: KeywordCampaignSeedInput[] | string[];
21
+ presetKeywords?: string[];
22
+ minKeywordLength?: number;
23
+ maxQueries?: number;
24
+ maxResultsPerQuery?: number;
25
+ transport?: SearchTransport;
26
+ timeout?: number;
27
+ country?: string;
28
+ gl?: string;
29
+ hl?: string;
30
+ minWeight?: number;
31
+ }
32
+ export interface KeywordCampaignCompetitorResult {
33
+ domain: string;
34
+ rank: number;
35
+ placement: CampaignResultPlacement;
36
+ placementHint?: string;
37
+ url: string;
38
+ title: string;
39
+ }
40
+ export interface KeywordCampaignResult {
41
+ keyword: string;
42
+ source: KeywordCampaignSource;
43
+ sourcePage?: string;
44
+ sourceWeight: number;
45
+ found: boolean;
46
+ bestPosition: number | null;
47
+ totalChecked: number;
48
+ matchedUrl?: string;
49
+ matchedTitle?: string;
50
+ matchedDisplayUrl?: string;
51
+ placement: CampaignResultPlacement;
52
+ placementHint?: string;
53
+ searchUrl: string;
54
+ searchTransport: SearchTransport;
55
+ competitors: KeywordCampaignCompetitorResult[];
56
+ }
57
+ export interface KeywordCampaignPageStats {
58
+ pageUrl: string;
59
+ tracked: number;
60
+ found: number;
61
+ avgPosition: number | null;
62
+ top3: number;
63
+ top10: number;
64
+ }
65
+ export interface KeywordCampaignSummary {
66
+ queriesRequested: number;
67
+ queriesExecuted: number;
68
+ queriesFound: number;
69
+ avgTopPosition: number | null;
70
+ top3Count: number;
71
+ top10Count: 0 | number;
72
+ topOrganicCompetitors: KeywordCampaignCompetitorSummary[];
73
+ topPaidCompetitors: KeywordCampaignCompetitorSummary[];
74
+ competitorCoverage: {
75
+ organicUniqueDomains: number;
76
+ paidUniqueDomains: number;
77
+ };
78
+ }
79
+ export interface KeywordCampaignCompetitorSummary {
80
+ domain: string;
81
+ organicQueries: number;
82
+ paidQueries: number;
83
+ bestOrganicRank: number | null;
84
+ bestPaidRank: number | null;
85
+ matchedKeywords: number;
86
+ }
87
+ export interface CampaignActivitySignal {
88
+ active: boolean;
89
+ confidence: 'high' | 'medium' | 'low';
90
+ evidence: string[];
91
+ }
92
+ export interface KeywordCampaignReport {
93
+ targetUrl: string;
94
+ targetDomain: string;
95
+ results: KeywordCampaignResult[];
96
+ summary: KeywordCampaignSummary;
97
+ pageComparison: KeywordCampaignPageStats[];
98
+ campaign: CampaignActivitySignal;
99
+ }
100
+ export interface KeywordCampaignExtractionOptions {
101
+ maxKeywords?: number;
102
+ minKeywordLength?: number;
103
+ }
104
+ export declare function extractKeywordCampaignSeedsFromReport(report: SeoReport, options?: KeywordCampaignExtractionOptions & {
105
+ sourcePage?: string;
106
+ }): KeywordCampaignSeed[];
107
+ export declare function analyzeKeywordCampaign(options: KeywordCampaignOptions): Promise<KeywordCampaignReport>;