mcp-scraper 0.7.0 → 0.8.0
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/bin/api-server.cjs +1541 -1481
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +1 -1
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +1 -1
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +3 -3
- package/dist/chunk-3MKSXDJ7.js +7 -0
- package/dist/chunk-3MKSXDJ7.js.map +1 -0
- package/dist/{chunk-YGRZU7IR.js → chunk-ITJC3NN4.js} +4 -3
- package/dist/{chunk-GWRIO6JT.js → chunk-R7EETU7Z.js} +134 -134
- package/dist/chunk-R7EETU7Z.js.map +1 -0
- package/dist/{extract-bundle-COS56ZDO.js → extract-bundle-U4D5LW5W.js} +4 -2
- package/dist/extract-bundle-U4D5LW5W.js.map +1 -0
- package/dist/{server-JNY4XPZE.js → server-WBDVVHR6.js} +73 -23
- package/dist/server-WBDVVHR6.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-EGWJ74EX.js +0 -7
- package/dist/chunk-EGWJ74EX.js.map +0 -1
- package/dist/chunk-GWRIO6JT.js.map +0 -1
- package/dist/extract-bundle-COS56ZDO.js.map +0 -1
- package/dist/server-JNY4XPZE.js.map +0 -1
- /package/dist/{chunk-YGRZU7IR.js.map → chunk-ITJC3NN4.js.map} +0 -0
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import {
|
|
23
23
|
HttpMcpToolExecutor,
|
|
24
24
|
MemoryMcpToolExecutor,
|
|
25
|
+
buildLinkGraph,
|
|
25
26
|
buildPaaExtractorMcpServer,
|
|
26
27
|
configureReportSaving,
|
|
27
28
|
exportFanout,
|
|
@@ -33,11 +34,13 @@ import {
|
|
|
33
34
|
registerSerpIntelligenceCaptureTools,
|
|
34
35
|
sanitizeAttempts,
|
|
35
36
|
sanitizeHarvestResult
|
|
36
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-ITJC3NN4.js";
|
|
37
38
|
import {
|
|
38
39
|
auditImages,
|
|
40
|
+
buildLinkReport,
|
|
41
|
+
computeIssues,
|
|
39
42
|
getBlobStore
|
|
40
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-R7EETU7Z.js";
|
|
41
44
|
import {
|
|
42
45
|
browserServiceApiKey,
|
|
43
46
|
browserServiceProfileName,
|
|
@@ -67,7 +70,7 @@ import {
|
|
|
67
70
|
RawMapsOverviewSchema,
|
|
68
71
|
RawMapsReviewStatsSchema
|
|
69
72
|
} from "./chunk-XGIPATLV.js";
|
|
70
|
-
import "./chunk-
|
|
73
|
+
import "./chunk-3MKSXDJ7.js";
|
|
71
74
|
import {
|
|
72
75
|
completeExtractJob,
|
|
73
76
|
countSuccessfulPages,
|
|
@@ -4780,7 +4783,8 @@ async function spiderSite(opts) {
|
|
|
4780
4783
|
totalFound: results.length,
|
|
4781
4784
|
durationMs: Date.now() - startMs,
|
|
4782
4785
|
truncated: queue.length > 0 || results.length >= maxUrls,
|
|
4783
|
-
browserRetries
|
|
4786
|
+
browserRetries,
|
|
4787
|
+
sitemapUrls: sitemapUrls.slice(0, maxUrls)
|
|
4784
4788
|
};
|
|
4785
4789
|
}
|
|
4786
4790
|
|
|
@@ -4904,6 +4908,23 @@ async function crawlWithRotation(urls, opts) {
|
|
|
4904
4908
|
return urls.map((u) => byUrl.get(u) ?? { url: u, html: null, status: 0, via: "browser" });
|
|
4905
4909
|
}
|
|
4906
4910
|
|
|
4911
|
+
// src/api/seo-audit.ts
|
|
4912
|
+
function buildSeoAudit(pages, startUrl) {
|
|
4913
|
+
const { edges, metrics } = buildLinkGraph(pages, startUrl);
|
|
4914
|
+
const issues = computeIssues(pages, metrics);
|
|
4915
|
+
const { summary } = buildLinkReport(edges, [...metrics.values()], startUrl);
|
|
4916
|
+
return {
|
|
4917
|
+
issues,
|
|
4918
|
+
linkSummary: summary,
|
|
4919
|
+
pageMetrics: [...metrics.values()].map((m) => ({
|
|
4920
|
+
url: m.url,
|
|
4921
|
+
inboundInternal: m.inlinks,
|
|
4922
|
+
outboundInternal: m.outlinksInternal,
|
|
4923
|
+
orphan: m.orphan
|
|
4924
|
+
}))
|
|
4925
|
+
};
|
|
4926
|
+
}
|
|
4927
|
+
|
|
4907
4928
|
// src/api/site-extractor.ts
|
|
4908
4929
|
var PIXEL_WIDTHS = { i: 4, l: 4, j: 4, ".": 4, ",": 4, "'": 4, t: 6, f: 6, r: 6, " ": 4, m: 14, w: 13, W: 16, M: 16 };
|
|
4909
4930
|
function estimatePixels(s) {
|
|
@@ -4947,6 +4968,7 @@ function emptyPageData(url, status, via) {
|
|
|
4947
4968
|
imageLinks: [],
|
|
4948
4969
|
indexable: status === 200,
|
|
4949
4970
|
indexabilityReason: status === 200 ? null : "non-200",
|
|
4971
|
+
inSitemap: null,
|
|
4950
4972
|
schemaTypes: [],
|
|
4951
4973
|
canonicalUrl: null,
|
|
4952
4974
|
internalLinks: 0,
|
|
@@ -4963,7 +4985,8 @@ function makeSeedSpider(startUrl, urls) {
|
|
|
4963
4985
|
totalFound: urls.length,
|
|
4964
4986
|
durationMs: 0,
|
|
4965
4987
|
truncated: false,
|
|
4966
|
-
browserRetries: 0
|
|
4988
|
+
browserRetries: 0,
|
|
4989
|
+
sitemapUrls: []
|
|
4967
4990
|
};
|
|
4968
4991
|
}
|
|
4969
4992
|
var UA2 = "Mozilla/5.0 (compatible; ThorbitBot/1.0; +https://thorbit.ai)";
|
|
@@ -5134,6 +5157,7 @@ function parsePageData(url, html, status, via, resp) {
|
|
|
5134
5157
|
imageLinks,
|
|
5135
5158
|
indexable,
|
|
5136
5159
|
indexabilityReason,
|
|
5160
|
+
inSitemap: null,
|
|
5137
5161
|
schemaTypes,
|
|
5138
5162
|
canonicalUrl,
|
|
5139
5163
|
internalLinks,
|
|
@@ -5147,11 +5171,11 @@ async function extractPagesRotating(urls, opts) {
|
|
|
5147
5171
|
const fetched = await crawlWithRotation(urls, { apiKey: opts.kernelApiKey, concurrency: opts.concurrency, urlsPerBrowser: opts.urlsPerBrowser });
|
|
5148
5172
|
return fetched.map((r) => r.html ? parsePageData(r.url, r.html, r.status || 200, "browser", { headers: r.headers, responseTimeMs: r.responseTimeMs, redirectUrl: r.redirectUrl }) : emptyPageData(r.url, r.status || null, "browser"));
|
|
5149
5173
|
}
|
|
5150
|
-
async function
|
|
5174
|
+
async function discoverUrlsWithSitemap(startUrl, maxPages, kernelApiKey) {
|
|
5151
5175
|
const sitemapUrls = await discoverSitemapUrls2(startUrl, maxPages);
|
|
5152
|
-
if (sitemapUrls.length > 0) return sitemapUrls;
|
|
5176
|
+
if (sitemapUrls.length > 0) return { urls: sitemapUrls, sitemapUrls };
|
|
5153
5177
|
const spider = await spiderSite({ startUrl, maxUrls: maxPages, concurrency: 12, kernelApiKey });
|
|
5154
|
-
return spider.urls.filter((u) => u.status === 200 || u.status === null).slice(0, maxPages).map((u) => u.url);
|
|
5178
|
+
return { urls: spider.urls.filter((u) => u.status === 200 || u.status === null).slice(0, maxPages).map((u) => u.url), sitemapUrls: spider.sitemapUrls };
|
|
5155
5179
|
}
|
|
5156
5180
|
async function fetchAndParse(url, kernelApiKey) {
|
|
5157
5181
|
const startedAt = Date.now();
|
|
@@ -5210,6 +5234,7 @@ async function extractSite(opts) {
|
|
|
5210
5234
|
let browserRetries = 0;
|
|
5211
5235
|
let truncated = false;
|
|
5212
5236
|
let spider;
|
|
5237
|
+
let sitemapUrls = [];
|
|
5213
5238
|
if (rotating) {
|
|
5214
5239
|
const browserConcurrency = Math.max(1, Math.min(opts.parallelism ?? 1, 8));
|
|
5215
5240
|
const urlsPerBrowser = opts.rotateProxyEvery ?? 10;
|
|
@@ -5227,7 +5252,8 @@ async function extractSite(opts) {
|
|
|
5227
5252
|
for (const u of opts.seedUrls) enqueue(u);
|
|
5228
5253
|
} else {
|
|
5229
5254
|
enqueue(opts.startUrl);
|
|
5230
|
-
|
|
5255
|
+
sitemapUrls = await discoverSitemapUrls2(opts.startUrl, maxPages);
|
|
5256
|
+
for (const u of sitemapUrls) enqueue(u);
|
|
5231
5257
|
}
|
|
5232
5258
|
const waveCap = Math.max(browserConcurrency * urlsPerBrowser, 30);
|
|
5233
5259
|
let done = 0;
|
|
@@ -5256,6 +5282,7 @@ async function extractSite(opts) {
|
|
|
5256
5282
|
spider = makeSeedSpider(opts.startUrl, [...seen]);
|
|
5257
5283
|
} else {
|
|
5258
5284
|
spider = seeded ? makeSeedSpider(opts.startUrl, opts.seedUrls) : await spiderSite({ startUrl: opts.startUrl, maxUrls: maxPages, concurrency: 12, kernelApiKey: opts.kernelApiKey });
|
|
5285
|
+
sitemapUrls = spider.sitemapUrls;
|
|
5259
5286
|
const urlsToExtract = spider.urls.filter((u) => u.status === 200 || u.status === null).slice(0, maxPages).map((u) => u.url);
|
|
5260
5287
|
browserRetries = spider.browserRetries;
|
|
5261
5288
|
await runWithConcurrency(urlsToExtract, concurrency, async (url) => {
|
|
@@ -5265,10 +5292,17 @@ async function extractSite(opts) {
|
|
|
5265
5292
|
});
|
|
5266
5293
|
truncated = spider.truncated || pages.length >= maxPages;
|
|
5267
5294
|
}
|
|
5295
|
+
const sitemapSet = sitemapUrls.length > 0 ? new Set(sitemapUrls.map((u) => normalizeUrl(u, opts.startUrl) ?? u)) : null;
|
|
5296
|
+
for (const p of pages) p.inSitemap = sitemapSet ? sitemapSet.has(normalizeUrl(p.url, opts.startUrl) ?? p.url) : null;
|
|
5268
5297
|
let branding = null;
|
|
5269
5298
|
if (opts.formats?.includes("branding") && opts.kernelApiKey) {
|
|
5270
5299
|
branding = await extractBranding(opts.startUrl, opts.kernelApiKey).catch(() => null);
|
|
5271
5300
|
}
|
|
5301
|
+
const seoAudit = opts.formats?.includes("issues") ? buildSeoAudit(pages, opts.startUrl) : null;
|
|
5302
|
+
let imageAudit = null;
|
|
5303
|
+
if (opts.formats?.includes("images")) {
|
|
5304
|
+
imageAudit = await auditImages(pages, { concurrency: 12, timeoutMs: 12e3 });
|
|
5305
|
+
}
|
|
5272
5306
|
return {
|
|
5273
5307
|
startUrl: opts.startUrl,
|
|
5274
5308
|
pages,
|
|
@@ -5277,7 +5311,9 @@ async function extractSite(opts) {
|
|
|
5277
5311
|
durationMs: Date.now() - startMs,
|
|
5278
5312
|
truncated,
|
|
5279
5313
|
browserRetries,
|
|
5280
|
-
branding
|
|
5314
|
+
branding,
|
|
5315
|
+
seoAudit,
|
|
5316
|
+
imageAudit
|
|
5281
5317
|
};
|
|
5282
5318
|
}
|
|
5283
5319
|
|
|
@@ -7519,12 +7555,13 @@ var siteExtractFn = inngest.createFunction(
|
|
|
7519
7555
|
const maxPages = Number(job.options.maxPages ?? 1e4);
|
|
7520
7556
|
const concurrency = Number(job.options.concurrency ?? 1);
|
|
7521
7557
|
const urlsPerBrowser = Number(job.options.urlsPerBrowser ?? job.options.rotateProxyEvery ?? 10);
|
|
7522
|
-
const
|
|
7523
|
-
const discovered = await
|
|
7524
|
-
const
|
|
7525
|
-
await setExtractJobTotal(jobId,
|
|
7526
|
-
return
|
|
7558
|
+
const seedRaw = await step.run("discover", async () => {
|
|
7559
|
+
const discovered = await discoverUrlsWithSitemap(job.startUrl, maxPages, key);
|
|
7560
|
+
const urls = discovered.urls.length ? discovered.urls : [job.startUrl];
|
|
7561
|
+
await setExtractJobTotal(jobId, urls.length);
|
|
7562
|
+
return { urls, sitemapUrls: discovered.sitemapUrls };
|
|
7527
7563
|
});
|
|
7564
|
+
const seed = Array.isArray(seedRaw) ? { urls: seedRaw, sitemapUrls: [] } : seedRaw;
|
|
7528
7565
|
const siteHost = (() => {
|
|
7529
7566
|
try {
|
|
7530
7567
|
return new URL(job.startUrl).hostname.replace(/^www\./, "");
|
|
@@ -7548,8 +7585,9 @@ var siteExtractFn = inngest.createFunction(
|
|
|
7548
7585
|
return false;
|
|
7549
7586
|
}
|
|
7550
7587
|
};
|
|
7551
|
-
const
|
|
7552
|
-
const
|
|
7588
|
+
const sitemapSet = seed.sitemapUrls.length > 0 ? new Set(seed.sitemapUrls.map(norm)) : null;
|
|
7589
|
+
const queue = [...seed.urls];
|
|
7590
|
+
const seen = new Set(seed.urls.map(norm));
|
|
7553
7591
|
let crawled = 0;
|
|
7554
7592
|
const MAX_BATCHES = Math.ceil(maxPages / BATCH) + 30;
|
|
7555
7593
|
for (let i = 0; i < MAX_BATCHES && crawled < maxPages && crawled < queue.length; i++) {
|
|
@@ -7557,7 +7595,7 @@ var siteExtractFn = inngest.createFunction(
|
|
|
7557
7595
|
if (!batch.length) break;
|
|
7558
7596
|
const newLinks = await step.run(`crawl-batch-${i}`, async () => {
|
|
7559
7597
|
const pages = await extractPagesRotating(batch, { kernelApiKey: key, concurrency, urlsPerBrowser });
|
|
7560
|
-
await saveExtractPages(jobId, pages);
|
|
7598
|
+
await saveExtractPages(jobId, pages.map((p) => ({ ...p, inSitemap: sitemapSet ? sitemapSet.has(norm(p.url)) : null })));
|
|
7561
7599
|
const found = [];
|
|
7562
7600
|
for (const p of pages) for (const l of p.outlinks ?? []) {
|
|
7563
7601
|
if (l.internal && l.href && sameSite(l.href)) found.push(l.href);
|
|
@@ -7581,9 +7619,13 @@ var siteExtractFn = inngest.createFunction(
|
|
|
7581
7619
|
const pages = await getExtractedPages(jobId);
|
|
7582
7620
|
return auditImages(pages, { concurrency: 12, timeoutMs: 12e3 });
|
|
7583
7621
|
}) : null;
|
|
7622
|
+
const seoAudit = Array.isArray(job.options.formats) && job.options.formats.includes("issues") ? await step.run("seo-audit", async () => {
|
|
7623
|
+
const pages = await getExtractedPages(jobId);
|
|
7624
|
+
return buildSeoAudit(pages, job.startUrl);
|
|
7625
|
+
}) : null;
|
|
7584
7626
|
const artifacts = await step.run("finalize", async () => {
|
|
7585
|
-
const { assembleExtractArtifacts } = await import("./extract-bundle-
|
|
7586
|
-
const stored = await assembleExtractArtifacts(job, { branding, imageAudit });
|
|
7627
|
+
const { assembleExtractArtifacts } = await import("./extract-bundle-U4D5LW5W.js");
|
|
7628
|
+
const stored = await assembleExtractArtifacts(job, { branding, imageAudit, seoAudit });
|
|
7587
7629
|
await completeExtractJob(jobId, stored);
|
|
7588
7630
|
return stored;
|
|
7589
7631
|
});
|
|
@@ -9089,7 +9131,7 @@ var ExtractSiteBodySchema = z11.object({
|
|
|
9089
9131
|
rotateProxies: z11.boolean().optional(),
|
|
9090
9132
|
rotateProxyEvery: z11.number().int().min(1).max(100).optional(),
|
|
9091
9133
|
background: z11.boolean().optional(),
|
|
9092
|
-
formats: z11.array(z11.enum(["markdown", "links", "json", "images", "branding"])).optional()
|
|
9134
|
+
formats: z11.array(z11.enum(["markdown", "links", "json", "images", "branding", "issues"])).optional()
|
|
9093
9135
|
});
|
|
9094
9136
|
var YoutubeHarvestBodySchema = z11.object({
|
|
9095
9137
|
mode: z11.enum(["search", "channel"]),
|
|
@@ -20458,6 +20500,7 @@ app.use("*", async (c, next) => {
|
|
|
20458
20500
|
});
|
|
20459
20501
|
function opForCostPath(p) {
|
|
20460
20502
|
if (p === "/harvest" || p === "/harvest/sync") return "harvest";
|
|
20503
|
+
if (p === "/diff-page") return "diff_page";
|
|
20461
20504
|
if (p === "/extract-url") return "page_scrape";
|
|
20462
20505
|
if (p === "/extract-site") return "extract_site";
|
|
20463
20506
|
if (p === "/map-urls") return "url_map";
|
|
@@ -20630,6 +20673,13 @@ app.get("/catalog", (c) => {
|
|
|
20630
20673
|
c.header("Cache-Control", "public, max-age=60");
|
|
20631
20674
|
return c.json(CATALOG);
|
|
20632
20675
|
});
|
|
20676
|
+
app.get("/rates", (c) => {
|
|
20677
|
+
const costs = CREDIT_COST_CATALOG.map(({ aliases, ...cost }) => ({
|
|
20678
|
+
...cost,
|
|
20679
|
+
...cost.notes ? { notes: cost.notes.replace(/ via fal\.ai/g, "") } : {}
|
|
20680
|
+
}));
|
|
20681
|
+
return c.json({ creditsPerDollarStarter: Math.round(1 / 15e-5), costs });
|
|
20682
|
+
});
|
|
20633
20683
|
app.get("/me", async (c) => {
|
|
20634
20684
|
const token = getCookie2(c, "session");
|
|
20635
20685
|
if (!token) return c.json({ authenticated: false });
|
|
@@ -21905,7 +21955,7 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
|
|
|
21905
21955
|
}
|
|
21906
21956
|
const jobId = c.req.param("id");
|
|
21907
21957
|
const { getExtractJob: getExtractJob2, completeExtractJob: completeExtractJob2 } = await import("./site-extract-repository-NVSZH35Y.js");
|
|
21908
|
-
const { assembleExtractArtifacts } = await import("./extract-bundle-
|
|
21958
|
+
const { assembleExtractArtifacts } = await import("./extract-bundle-U4D5LW5W.js");
|
|
21909
21959
|
const job = await getExtractJob2(jobId);
|
|
21910
21960
|
if (!job) return c.json({ error: "job not found" }, 404);
|
|
21911
21961
|
const stored = await assembleExtractArtifacts(job, {});
|
|
@@ -22076,4 +22126,4 @@ app.get("/blog/:slug/", (c) => {
|
|
|
22076
22126
|
export {
|
|
22077
22127
|
app
|
|
22078
22128
|
};
|
|
22079
|
-
//# sourceMappingURL=server-
|
|
22129
|
+
//# sourceMappingURL=server-WBDVVHR6.js.map
|