webcrawl 1.2.4 → 1.2.5
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/cli.js +3 -2
- package/dist/core/engine/cache.d.ts +70 -0
- package/dist/core/engine/cache.js +297 -0
- package/dist/core/engine/crawl-policy.d.ts +36 -0
- package/dist/core/engine/crawl-policy.js +134 -0
- package/dist/core/engine/crawler.d.ts +151 -0
- package/dist/core/engine/crawler.js +885 -0
- package/dist/core/engine/fetcher.d.ts +66 -0
- package/dist/core/engine/fetcher.js +185 -0
- package/dist/core/engine/index.d.ts +4 -0
- package/dist/core/engine/index.js +4 -0
- package/dist/core/engine/markdown.d.ts +33 -0
- package/dist/core/engine/markdown.js +334 -0
- package/dist/core/engine/path-scope.d.ts +21 -0
- package/dist/core/engine/path-scope.js +88 -0
- package/dist/core/engine/safe-url.d.ts +23 -0
- package/dist/core/engine/safe-url.js +92 -0
- package/dist/core/engine/stealth.d.ts +79 -0
- package/dist/core/engine/stealth.js +155 -0
- package/dist/core/engine.d.ts +1 -0
- package/dist/core/engine.js +1 -0
- package/dist/core/extract/business.d.ts +28 -0
- package/dist/core/extract/business.js +344 -0
- package/dist/core/extract/currency.d.ts +15 -0
- package/dist/core/extract/currency.js +259 -0
- package/dist/core/extract/cve-cache.d.ts +62 -0
- package/dist/core/extract/cve-cache.js +277 -0
- package/dist/core/extract/cve.d.ts +57 -0
- package/dist/core/extract/cve.js +397 -0
- package/dist/core/extract/dedupe-key.d.ts +12 -0
- package/dist/core/extract/dedupe-key.js +36 -0
- package/dist/core/extract/flights.d.ts +37 -0
- package/dist/core/extract/flights.js +373 -0
- package/dist/core/extract/hotels.d.ts +35 -0
- package/dist/core/extract/hotels.js +473 -0
- package/dist/core/extract/index.d.ts +8 -0
- package/dist/core/extract/index.js +8 -0
- package/dist/core/extract/real-estate.d.ts +53 -0
- package/dist/core/extract/real-estate.js +835 -0
- package/dist/core/extract/scaledown.d.ts +14 -0
- package/dist/core/extract/scaledown.js +484 -0
- package/dist/core/extract/shopping.d.ts +44 -0
- package/dist/core/extract/shopping.js +393 -0
- package/dist/core/image-url.d.ts +5 -0
- package/dist/core/image-url.js +13 -0
- package/dist/core/interact/driver.d.ts +49 -0
- package/dist/core/interact/driver.js +264 -0
- package/dist/core/interact/remote.d.ts +41 -0
- package/dist/core/interact/remote.js +133 -0
- package/dist/core/interact/spans.d.ts +46 -0
- package/dist/core/interact/spans.js +32 -0
- package/dist/core/interact/validate.d.ts +34 -0
- package/dist/core/interact/validate.js +135 -0
- package/dist/core/interact/vm.d.ts +42 -0
- package/dist/core/interact/vm.js +296 -0
- package/dist/core/local-engine.d.ts +35 -0
- package/dist/core/local-engine.js +367 -0
- package/dist/core/parse/detect.d.ts +29 -0
- package/dist/core/parse/detect.js +152 -0
- package/dist/core/parse/fetch.d.ts +20 -0
- package/dist/core/parse/fetch.js +39 -0
- package/dist/core/parse/image.d.ts +13 -0
- package/dist/core/parse/image.js +100 -0
- package/dist/core/parse/index.d.ts +53 -0
- package/dist/core/parse/index.js +153 -0
- package/dist/core/parse/limits.d.ts +12 -0
- package/dist/core/parse/limits.js +29 -0
- package/dist/core/parse/media.d.ts +42 -0
- package/dist/core/parse/media.js +238 -0
- package/dist/core/parse/merge.d.ts +19 -0
- package/dist/core/parse/merge.js +48 -0
- package/dist/core/parse/ocr.d.ts +25 -0
- package/dist/core/parse/ocr.js +121 -0
- package/dist/core/parse/office.d.ts +28 -0
- package/dist/core/parse/office.js +83 -0
- package/dist/core/parse/pdf.d.ts +27 -0
- package/dist/core/parse/pdf.js +156 -0
- package/dist/core/parse/types.d.ts +38 -0
- package/dist/core/parse/types.js +1 -0
- package/dist/core/parse/youtube.d.ts +30 -0
- package/dist/core/parse/youtube.js +264 -0
- package/dist/core/ranker/decision.d.ts +28 -0
- package/dist/core/ranker/decision.js +151 -0
- package/dist/core/ranker/index.d.ts +5 -0
- package/dist/core/ranker/index.js +361 -0
- package/dist/core/ranker/lexical.d.ts +82 -0
- package/dist/core/ranker/lexical.js +642 -0
- package/dist/core/ranker/types.d.ts +90 -0
- package/dist/core/ranker/types.js +1 -0
- package/dist/core/ranker.d.ts +1 -0
- package/dist/core/ranker.js +1 -0
- package/dist/core/sandbox.d.ts +29 -0
- package/dist/core/sandbox.js +182 -0
- package/dist/core/search/catalog.d.ts +174 -0
- package/dist/core/search/catalog.js +488 -0
- package/dist/core/search/index.d.ts +3 -0
- package/dist/core/search/index.js +3 -0
- package/dist/core/search/pipeline.d.ts +7 -0
- package/dist/core/search/pipeline.js +704 -0
- package/dist/core/search/providers.d.ts +62 -0
- package/dist/core/search/providers.js +1175 -0
- package/dist/core/search.d.ts +1 -0
- package/dist/core/search.js +1 -0
- package/dist/core/targets/developer.d.ts +44 -0
- package/dist/core/targets/developer.js +338 -0
- package/dist/core/targets/index.d.ts +3 -0
- package/dist/core/targets/index.js +3 -0
- package/dist/core/targets/registry.d.ts +32 -0
- package/dist/core/targets/registry.js +411 -0
- package/dist/core/targets/types.d.ts +72 -0
- package/dist/core/targets/types.js +8 -0
- package/dist/core/targets.d.ts +1 -0
- package/dist/core/targets.js +1 -0
- package/package.json +82 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./search/index.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./search/index.js";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { CrawlTargetPlugin, TargetInspectResult } from "./types.js";
|
|
2
|
+
export interface GitHubTargetData {
|
|
3
|
+
kind: "repository" | "issue" | "pull_request" | "release" | "discussion" | "unknown";
|
|
4
|
+
owner: string;
|
|
5
|
+
repo: string;
|
|
6
|
+
number?: number;
|
|
7
|
+
tag?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
stars?: number;
|
|
10
|
+
forks?: number;
|
|
11
|
+
language?: string;
|
|
12
|
+
license?: string;
|
|
13
|
+
topics: string[];
|
|
14
|
+
readmeExcerpt?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function extractGitHubFromHtml(url: string, html: string): TargetInspectResult<GitHubTargetData>;
|
|
17
|
+
export interface ArxivPaperData {
|
|
18
|
+
arxivId: string;
|
|
19
|
+
title: string;
|
|
20
|
+
authors: string[];
|
|
21
|
+
abstract: string;
|
|
22
|
+
primaryCategory?: string;
|
|
23
|
+
publishedDate?: string;
|
|
24
|
+
pdfUrl: string;
|
|
25
|
+
doi?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function extractArxivFromHtml(url: string, html: string): TargetInspectResult<ArxivPaperData>;
|
|
28
|
+
export interface LlmsTxtData {
|
|
29
|
+
title: string;
|
|
30
|
+
summary?: string;
|
|
31
|
+
sections: Array<{
|
|
32
|
+
heading: string;
|
|
33
|
+
links: Array<{
|
|
34
|
+
title: string;
|
|
35
|
+
url: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
}>;
|
|
38
|
+
}>;
|
|
39
|
+
totalLinks: number;
|
|
40
|
+
}
|
|
41
|
+
export declare function extractLlmsTxt(url: string, text: string): TargetInspectResult<LlmsTxtData>;
|
|
42
|
+
export declare const githubTargetPlugin: CrawlTargetPlugin<GitHubTargetData>;
|
|
43
|
+
export declare const arxivTargetPlugin: CrawlTargetPlugin<ArxivPaperData>;
|
|
44
|
+
export declare const llmsTxtTargetPlugin: CrawlTargetPlugin<LlmsTxtData>;
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in Developer & Research Target Extractors:
|
|
3
|
+
* - GitHub Repositories, Issues, Pull Requests & Releases
|
|
4
|
+
* - ArXiv Papers (abstract, authors, PDF link, categories, submission history)
|
|
5
|
+
* - llms.txt / AI Documentation Manifests & Sitemaps
|
|
6
|
+
* - Hacker News Threads & Discussions
|
|
7
|
+
*/
|
|
8
|
+
import crypto from "node:crypto";
|
|
9
|
+
function sha256(text) {
|
|
10
|
+
return crypto.createHash("sha256").update(text).digest("hex");
|
|
11
|
+
}
|
|
12
|
+
function stripTags(html) {
|
|
13
|
+
return html
|
|
14
|
+
.replace(/<script[\s\S]*?<\/script>/gi, "")
|
|
15
|
+
.replace(/<style[\s\S]*?<\/style>/gi, "")
|
|
16
|
+
.replace(/<[^>]+>/g, " ")
|
|
17
|
+
.replace(/ /g, " ")
|
|
18
|
+
.replace(/&/g, "&")
|
|
19
|
+
.replace(/</g, "<")
|
|
20
|
+
.replace(/>/g, ">")
|
|
21
|
+
.replace(/"/g, '"')
|
|
22
|
+
.replace(/'/g, "'")
|
|
23
|
+
.replace(/\s+/g, " ")
|
|
24
|
+
.trim();
|
|
25
|
+
}
|
|
26
|
+
export function extractGitHubFromHtml(url, html) {
|
|
27
|
+
const matchedSignals = [];
|
|
28
|
+
const limitations = [];
|
|
29
|
+
let owner = "";
|
|
30
|
+
let repo = "";
|
|
31
|
+
let kind = "unknown";
|
|
32
|
+
let number;
|
|
33
|
+
let tag;
|
|
34
|
+
try {
|
|
35
|
+
const u = new URL(url);
|
|
36
|
+
const parts = u.pathname.split("/").filter(Boolean);
|
|
37
|
+
owner = parts[0] ?? "";
|
|
38
|
+
repo = parts[1] ?? "";
|
|
39
|
+
if (parts[2] === "issues" && parts[3]) {
|
|
40
|
+
kind = "issue";
|
|
41
|
+
number = Number(parts[3]);
|
|
42
|
+
matchedSignals.push("url:github-issue-path");
|
|
43
|
+
}
|
|
44
|
+
else if (parts[2] === "pull" && parts[3]) {
|
|
45
|
+
kind = "pull_request";
|
|
46
|
+
number = Number(parts[3]);
|
|
47
|
+
matchedSignals.push("url:github-pr-path");
|
|
48
|
+
}
|
|
49
|
+
else if (parts[2] === "releases" && parts[3] === "tag" && parts[4]) {
|
|
50
|
+
kind = "release";
|
|
51
|
+
tag = parts[4];
|
|
52
|
+
matchedSignals.push("url:github-release-tag");
|
|
53
|
+
}
|
|
54
|
+
else if (parts[2] === "discussions" && parts[3]) {
|
|
55
|
+
kind = "discussion";
|
|
56
|
+
number = Number(parts[3]);
|
|
57
|
+
matchedSignals.push("url:github-discussion-path");
|
|
58
|
+
}
|
|
59
|
+
else if (owner && repo) {
|
|
60
|
+
kind = "repository";
|
|
61
|
+
matchedSignals.push("url:github-repo-root");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
limitations.push("Could not parse canonical GitHub URL path");
|
|
66
|
+
}
|
|
67
|
+
const descMatch = html.match(/<meta[^>]+property=["']og:description["'][^>]+content=["']([^"']+)["']/i) ??
|
|
68
|
+
html.match(/<meta[^>]+name=["']description["'][^>]+content=["']([^"']+)["']/i);
|
|
69
|
+
const description = descMatch ? stripTags(descMatch[1]) : undefined;
|
|
70
|
+
if (description)
|
|
71
|
+
matchedSignals.push("meta:og:description");
|
|
72
|
+
const titleMatch = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
73
|
+
const title = titleMatch ? stripTags(titleMatch[1]) : `${owner}/${repo}`;
|
|
74
|
+
// Parse stars / forks from social count or JSON payload
|
|
75
|
+
let stars;
|
|
76
|
+
const starMatch = html.match(/id=["']repo-stars-counter-star["'][^>]*title=["']([\d,]+)["']/i) ??
|
|
77
|
+
html.match(/([\d,]+)\s+users?\s+starred\s+this\s+repository/i);
|
|
78
|
+
if (starMatch) {
|
|
79
|
+
stars = Number(starMatch[1].replace(/,/g, ""));
|
|
80
|
+
matchedSignals.push("dom:repo-stars-counter");
|
|
81
|
+
}
|
|
82
|
+
let forks;
|
|
83
|
+
const forkMatch = html.match(/id=["']repo-network-counter["'][^>]*title=["']([\d,]+)["']/i);
|
|
84
|
+
if (forkMatch) {
|
|
85
|
+
forks = Number(forkMatch[1].replace(/,/g, ""));
|
|
86
|
+
matchedSignals.push("dom:repo-network-counter");
|
|
87
|
+
}
|
|
88
|
+
const topics = [];
|
|
89
|
+
const topicRe = /class=["'][^"']*\btopic-tag\b[^"']*["'][^>]*>([\s\S]*?)<\/a>/gi;
|
|
90
|
+
for (const m of html.matchAll(topicRe)) {
|
|
91
|
+
const t = stripTags(m[1]);
|
|
92
|
+
if (t && !topics.includes(t))
|
|
93
|
+
topics.push(t);
|
|
94
|
+
}
|
|
95
|
+
if (topics.length > 0)
|
|
96
|
+
matchedSignals.push(`dom:topic-tag(${topics.length})`);
|
|
97
|
+
const langMatch = html.match(/itemprop=["']programmingLanguage["'][^>]*>([\s\S]*?)<\//i);
|
|
98
|
+
const language = langMatch ? stripTags(langMatch[1]) : undefined;
|
|
99
|
+
if (language)
|
|
100
|
+
matchedSignals.push("dom:itemprop=programmingLanguage");
|
|
101
|
+
const articleMatch = html.match(/<article[^>]*class=["'][^"']*\bmarkdown-body\b[^"']*["'][^>]*>([\s\S]*?)<\/article>/i);
|
|
102
|
+
const readmeExcerpt = articleMatch ? stripTags(articleMatch[1]).slice(0, 1200) : undefined;
|
|
103
|
+
if (readmeExcerpt)
|
|
104
|
+
matchedSignals.push("dom:article.markdown-body");
|
|
105
|
+
else
|
|
106
|
+
limitations.push("No rendered README <article class='markdown-body'> found in HTML snapshot");
|
|
107
|
+
return {
|
|
108
|
+
targetId: "github",
|
|
109
|
+
targetName: "GitHub Repositories, Issues & Releases",
|
|
110
|
+
category: "developer",
|
|
111
|
+
title,
|
|
112
|
+
structured: {
|
|
113
|
+
kind,
|
|
114
|
+
owner,
|
|
115
|
+
repo,
|
|
116
|
+
number,
|
|
117
|
+
tag,
|
|
118
|
+
description,
|
|
119
|
+
stars,
|
|
120
|
+
forks,
|
|
121
|
+
language,
|
|
122
|
+
topics,
|
|
123
|
+
readmeExcerpt,
|
|
124
|
+
},
|
|
125
|
+
evidence: {
|
|
126
|
+
targetId: "github",
|
|
127
|
+
tier: "static",
|
|
128
|
+
contentHash: sha256(html),
|
|
129
|
+
sourceUrl: url,
|
|
130
|
+
matchedSignals,
|
|
131
|
+
limitations,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
export function extractArxivFromHtml(url, html) {
|
|
136
|
+
const matchedSignals = [];
|
|
137
|
+
const limitations = [];
|
|
138
|
+
const idMatch = url.match(/arxiv\.org\/(?:abs|pdf|html)\/([0-9]{4}\.[0-9]{4,5}(?:v\d+)?|[a-z-]+\/[0-9]{7})/i);
|
|
139
|
+
const arxivId = idMatch ? idMatch[1] : "unknown";
|
|
140
|
+
if (idMatch)
|
|
141
|
+
matchedSignals.push("url:arxiv-id");
|
|
142
|
+
const citationTitle = html.match(/<meta[^>]+name=["']citation_title["'][^>]+content=["']([^"']+)["']/i)?.[1] ??
|
|
143
|
+
html.match(/<h1[^>]*class=["'][^"']*\btitle\b[^"']*["'][^>]*>([\s\S]*?)<\/h1>/i)?.[1];
|
|
144
|
+
const title = citationTitle ? stripTags(citationTitle.replace(/^Title:\s*/i, "")) : `arXiv:${arxivId}`;
|
|
145
|
+
if (citationTitle)
|
|
146
|
+
matchedSignals.push("meta:citation_title");
|
|
147
|
+
const authors = [];
|
|
148
|
+
for (const m of html.matchAll(/<meta[^>]+name=["']citation_author["'][^>]+content=["']([^"']+)["']/gi)) {
|
|
149
|
+
authors.push(stripTags(m[1]));
|
|
150
|
+
}
|
|
151
|
+
if (authors.length === 0) {
|
|
152
|
+
const authorsBlock = html.match(/<div[^>]*class=["'][^"']*\bauthors\b[^"']*["'][^>]*>([\s\S]*?)<\/div>/i);
|
|
153
|
+
if (authorsBlock) {
|
|
154
|
+
for (const a of authorsBlock[1].matchAll(/<a[^>]*>([\s\S]*?)<\/a>/gi)) {
|
|
155
|
+
authors.push(stripTags(a[1]));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (authors.length > 0)
|
|
160
|
+
matchedSignals.push(`meta:citation_author(${authors.length})`);
|
|
161
|
+
else
|
|
162
|
+
limitations.push("No citation_author meta tags or .authors block found");
|
|
163
|
+
const abstractMatch = html.match(/<blockquote[^>]*class=["'][^"']*\babstract\b[^"']*["'][^>]*>([\s\S]*?)<\/blockquote>/i)?.[1] ??
|
|
164
|
+
html.match(/<meta[^>]+property=["']og:description["'][^>]+content=["']([^"']+)["']/i)?.[1];
|
|
165
|
+
const abstract = abstractMatch ? stripTags(abstractMatch.replace(/^Abstract:\s*/i, "")) : "";
|
|
166
|
+
if (abstract)
|
|
167
|
+
matchedSignals.push("dom:blockquote.abstract");
|
|
168
|
+
else
|
|
169
|
+
limitations.push("Abstract text missing from HTML");
|
|
170
|
+
const dateMatch = html.match(/<meta[^>]+name=["']citation_date["'][^>]+content=["']([^"']+)["']/i);
|
|
171
|
+
const publishedDate = dateMatch ? dateMatch[1].trim() : undefined;
|
|
172
|
+
if (publishedDate)
|
|
173
|
+
matchedSignals.push("meta:citation_date");
|
|
174
|
+
const subjMatch = html.match(/<span[^>]*class=["'][^"']*\bprimary-subject\b[^"']*["'][^>]*>([\s\S]*?)<\/span>/i);
|
|
175
|
+
const primaryCategory = subjMatch ? stripTags(subjMatch[1]) : undefined;
|
|
176
|
+
if (primaryCategory)
|
|
177
|
+
matchedSignals.push("dom:span.primary-subject");
|
|
178
|
+
const doiMatch = html.match(/<meta[^>]+name=["']citation_doi["'][^>]+content=["']([^"']+)["']/i);
|
|
179
|
+
const doi = doiMatch ? doiMatch[1].trim() : undefined;
|
|
180
|
+
return {
|
|
181
|
+
targetId: "arxiv",
|
|
182
|
+
targetName: "ArXiv Research Papers",
|
|
183
|
+
category: "academic",
|
|
184
|
+
title,
|
|
185
|
+
structured: {
|
|
186
|
+
arxivId,
|
|
187
|
+
title,
|
|
188
|
+
authors,
|
|
189
|
+
abstract,
|
|
190
|
+
primaryCategory,
|
|
191
|
+
publishedDate,
|
|
192
|
+
pdfUrl: `https://arxiv.org/pdf/${arxivId}.pdf`,
|
|
193
|
+
doi,
|
|
194
|
+
},
|
|
195
|
+
evidence: {
|
|
196
|
+
targetId: "arxiv",
|
|
197
|
+
tier: "static",
|
|
198
|
+
contentHash: sha256(html),
|
|
199
|
+
sourceUrl: url,
|
|
200
|
+
matchedSignals,
|
|
201
|
+
limitations,
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
export function extractLlmsTxt(url, text) {
|
|
206
|
+
const matchedSignals = [];
|
|
207
|
+
const limitations = [];
|
|
208
|
+
const lines = text.split(/\r?\n/);
|
|
209
|
+
let title = "llms.txt";
|
|
210
|
+
let summary;
|
|
211
|
+
const sections = [];
|
|
212
|
+
let currentSection = { heading: "Root", links: [] };
|
|
213
|
+
for (const rawLine of lines) {
|
|
214
|
+
const line = rawLine.trim();
|
|
215
|
+
if (!line)
|
|
216
|
+
continue;
|
|
217
|
+
if (line.startsWith("# ") && title === "llms.txt") {
|
|
218
|
+
title = line.slice(2).trim();
|
|
219
|
+
matchedSignals.push("md:h1-title");
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (line.startsWith("> ") && !summary) {
|
|
223
|
+
summary = line.slice(2).trim();
|
|
224
|
+
matchedSignals.push("md:blockquote-summary");
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (line.startsWith("## ")) {
|
|
228
|
+
if (currentSection.links.length > 0 || currentSection.heading !== "Root") {
|
|
229
|
+
sections.push(currentSection);
|
|
230
|
+
}
|
|
231
|
+
currentSection = { heading: line.slice(3).trim(), links: [] };
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const linkMatch = line.match(/^[-*]\s+\[([^\]]+)\]\(([^)]+)\)(?:\s*:\s*(.*))?$/);
|
|
235
|
+
if (linkMatch) {
|
|
236
|
+
currentSection.links.push({
|
|
237
|
+
title: linkMatch[1].trim(),
|
|
238
|
+
url: linkMatch[2].trim(),
|
|
239
|
+
description: linkMatch[3]?.trim() || undefined,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (currentSection.links.length > 0 || sections.length === 0) {
|
|
244
|
+
sections.push(currentSection);
|
|
245
|
+
}
|
|
246
|
+
const totalLinks = sections.reduce((acc, s) => acc + s.links.length, 0);
|
|
247
|
+
if (totalLinks > 0)
|
|
248
|
+
matchedSignals.push(`md:links(${totalLinks})`);
|
|
249
|
+
else
|
|
250
|
+
limitations.push("No standard `- [Title](url): description` entries found in llms.txt");
|
|
251
|
+
return {
|
|
252
|
+
targetId: "llms-txt",
|
|
253
|
+
targetName: "llms.txt & AI Documentation Manifests",
|
|
254
|
+
category: "developer",
|
|
255
|
+
title,
|
|
256
|
+
markdown: text,
|
|
257
|
+
structured: {
|
|
258
|
+
title,
|
|
259
|
+
summary,
|
|
260
|
+
sections,
|
|
261
|
+
totalLinks,
|
|
262
|
+
},
|
|
263
|
+
evidence: {
|
|
264
|
+
targetId: "llms-txt",
|
|
265
|
+
tier: "static",
|
|
266
|
+
contentHash: sha256(text),
|
|
267
|
+
sourceUrl: url,
|
|
268
|
+
matchedSignals,
|
|
269
|
+
limitations,
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
export const githubTargetPlugin = {
|
|
274
|
+
id: "github",
|
|
275
|
+
name: "GitHub Repositories, Issues & Releases",
|
|
276
|
+
category: "developer",
|
|
277
|
+
returns: "Repository metadata, stars/forks, topics, language, issue/PR threads, release notes, and README markdown",
|
|
278
|
+
requirements: "Built-in zero-key HTML/JSON inspector via impit TLS fetch",
|
|
279
|
+
examples: [
|
|
280
|
+
"https://github.com/morluto/rea",
|
|
281
|
+
"https://github.com/vercel/next.js/releases",
|
|
282
|
+
],
|
|
283
|
+
matches({ url, query }) {
|
|
284
|
+
if (url && /^https?:\/\/(www\.)?github\.com\/[^/]+\/[^/]+/i.test(url)) {
|
|
285
|
+
return { matched: true, confidence: 0.98, reason: "GitHub repository/issue/release URL" };
|
|
286
|
+
}
|
|
287
|
+
if (query && /\b(github\.com\/|gh\s+repo|github\s+release|github\s+issue)\b/i.test(query)) {
|
|
288
|
+
return { matched: true, confidence: 0.75, reason: "GitHub query intent" };
|
|
289
|
+
}
|
|
290
|
+
return { matched: false, confidence: 0 };
|
|
291
|
+
},
|
|
292
|
+
extract(input) {
|
|
293
|
+
return extractGitHubFromHtml(input.url ?? "https://github.com", input.html ?? "");
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
export const arxivTargetPlugin = {
|
|
297
|
+
id: "arxiv",
|
|
298
|
+
name: "ArXiv Research Papers",
|
|
299
|
+
category: "academic",
|
|
300
|
+
returns: "Paper title, authors, abstract, primary subject, DOI, submission date, and direct PDF link",
|
|
301
|
+
requirements: "Built-in citation meta & abstract parser; combine with pdf-office target for full paper text",
|
|
302
|
+
examples: [
|
|
303
|
+
"https://arxiv.org/abs/2307.09288",
|
|
304
|
+
"webcrawl search \"test-time compute scaling\" -c academic",
|
|
305
|
+
],
|
|
306
|
+
matches({ url, query, category }) {
|
|
307
|
+
if (url && /arxiv\.org\/(abs|pdf|html)\//i.test(url)) {
|
|
308
|
+
return { matched: true, confidence: 0.99, reason: "ArXiv paper URL" };
|
|
309
|
+
}
|
|
310
|
+
if (category === "academic" || (query && /\barxiv:\d{4}\.\d{4,5}\b/i.test(query))) {
|
|
311
|
+
return { matched: true, confidence: 0.85, reason: "Academic / ArXiv query" };
|
|
312
|
+
}
|
|
313
|
+
return { matched: false, confidence: 0 };
|
|
314
|
+
},
|
|
315
|
+
extract(input) {
|
|
316
|
+
return extractArxivFromHtml(input.url ?? "https://arxiv.org", input.html ?? "");
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
export const llmsTxtTargetPlugin = {
|
|
320
|
+
id: "llms-txt",
|
|
321
|
+
name: "llms.txt & AI Documentation Indexes",
|
|
322
|
+
category: "developer",
|
|
323
|
+
returns: "Structured sections, doc links, summaries, and full markdown manifest for LLM context ingestion",
|
|
324
|
+
requirements: "Built-in zero-key markdown manifest parser",
|
|
325
|
+
examples: [
|
|
326
|
+
"https://docs.anthropic.com/llms.txt",
|
|
327
|
+
"https://cursor.com/llms-full.txt",
|
|
328
|
+
],
|
|
329
|
+
matches({ url }) {
|
|
330
|
+
if (url && /\/llms(-full)?\.txt(\?.*)?$/i.test(url)) {
|
|
331
|
+
return { matched: true, confidence: 0.99, reason: "llms.txt manifest URL" };
|
|
332
|
+
}
|
|
333
|
+
return { matched: false, confidence: 0 };
|
|
334
|
+
},
|
|
335
|
+
extract(input) {
|
|
336
|
+
return extractLlmsTxt(input.url ?? "https://example.com/llms.txt", input.markdown ?? input.html ?? "");
|
|
337
|
+
},
|
|
338
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CrawlTargetPlugin, TargetInspectInput, TargetInspectResult, TargetMatch } from "./types.js";
|
|
2
|
+
export declare const webSpaTargetPlugin: CrawlTargetPlugin;
|
|
3
|
+
export declare const pdfOfficeTargetPlugin: CrawlTargetPlugin;
|
|
4
|
+
export declare const youtubeMediaTargetPlugin: CrawlTargetPlugin;
|
|
5
|
+
export declare const shoppingTargetPlugin: CrawlTargetPlugin;
|
|
6
|
+
export declare const flightsTargetPlugin: CrawlTargetPlugin;
|
|
7
|
+
export declare const hotelsTargetPlugin: CrawlTargetPlugin;
|
|
8
|
+
export declare const realEstateTargetPlugin: CrawlTargetPlugin;
|
|
9
|
+
export declare const localBusinessTargetPlugin: CrawlTargetPlugin;
|
|
10
|
+
export declare const cveSecurityTargetPlugin: CrawlTargetPlugin;
|
|
11
|
+
/** Default ordered registry of all built-in targets (specific targets first, generic web-spa last). */
|
|
12
|
+
export declare const BUILTIN_TARGETS: CrawlTargetPlugin[];
|
|
13
|
+
/** Register a custom or community target plugin at runtime. */
|
|
14
|
+
export declare function registerTarget(plugin: CrawlTargetPlugin): void;
|
|
15
|
+
/** List all registered target plugins (community overrides + built-ins). */
|
|
16
|
+
export declare function listTargets(): CrawlTargetPlugin[];
|
|
17
|
+
export declare function getTargetById(id: string): CrawlTargetPlugin | undefined;
|
|
18
|
+
/** Resolve the highest-confidence target plugin for a URL, query, or category. */
|
|
19
|
+
export declare function resolveTarget(input: {
|
|
20
|
+
url?: string;
|
|
21
|
+
query?: string;
|
|
22
|
+
contentType?: string;
|
|
23
|
+
category?: string;
|
|
24
|
+
}): {
|
|
25
|
+
plugin: CrawlTargetPlugin;
|
|
26
|
+
match: TargetMatch;
|
|
27
|
+
};
|
|
28
|
+
/** Run target-aware extraction on HTML/text and attach REA-style Evidence & Provenance. */
|
|
29
|
+
export declare function inspectTarget(input: TargetInspectInput & {
|
|
30
|
+
targetId?: string;
|
|
31
|
+
category?: string;
|
|
32
|
+
}): Promise<TargetInspectResult>;
|