polite-crawler 0.1.3 → 0.1.4

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 (36) hide show
  1. package/README.md +22 -0
  2. package/build/{cli-daemon--cTmq0Z5.mjs → cli-daemon-ZYRdy6EW.mjs} +3 -3
  3. package/build/{cli-daemon--cTmq0Z5.mjs.map → cli-daemon-ZYRdy6EW.mjs.map} +1 -1
  4. package/build/cli-recipes-Bci6HREA.mjs +59 -0
  5. package/build/cli-recipes-Bci6HREA.mjs.map +1 -0
  6. package/build/cli.mjs +397 -18
  7. package/build/cli.mjs.map +1 -1
  8. package/build/{daemon-fZTE24P5.mjs → daemon-DK6U51_v.mjs} +3 -3
  9. package/build/{daemon-fZTE24P5.mjs.map → daemon-DK6U51_v.mjs.map} +1 -1
  10. package/build/{daemon-DP4EqjFL.d.mts → daemon-wwIWsUmU.d.mts} +7 -2
  11. package/build/{diff-NQcQqKe9.mjs → diff-BUM91Rev.mjs} +2 -2
  12. package/build/{diff-NQcQqKe9.mjs.map → diff-BUM91Rev.mjs.map} +1 -1
  13. package/build/{extract-ByFfc8Zk.mjs → extract-CefB5Vfn.mjs} +2 -2
  14. package/build/{extract-ByFfc8Zk.mjs.map → extract-CefB5Vfn.mjs.map} +1 -1
  15. package/build/index.d.mts +2 -2
  16. package/build/index.mjs +10 -894
  17. package/build/mcp.d.mts +1 -1
  18. package/build/mcp.mjs +4 -4
  19. package/build/{open-C4fhXJgD.mjs → open-CbpSCEZc.mjs} +12 -284
  20. package/build/open-CbpSCEZc.mjs.map +1 -0
  21. package/build/recipe-GiFQojmo.mjs +279 -0
  22. package/build/recipe-GiFQojmo.mjs.map +1 -0
  23. package/build/recipe-product-Dellqudu.mjs +892 -0
  24. package/build/recipe-product-Dellqudu.mjs.map +1 -0
  25. package/build/{runtime-JToRO63o.mjs → runtime-D_r2SYge.mjs} +2 -2
  26. package/build/{runtime-JToRO63o.mjs.map → runtime-D_r2SYge.mjs.map} +1 -1
  27. package/build/{version-ntbY-6wh.mjs → version-BPem-XZd.mjs} +2 -2
  28. package/build/{version-ntbY-6wh.mjs.map → version-BPem-XZd.mjs.map} +1 -1
  29. package/build/{wacz-BFNG42Kt.mjs → wacz-QyzLp4mC.mjs} +3 -3
  30. package/build/{wacz-BFNG42Kt.mjs.map → wacz-QyzLp4mC.mjs.map} +1 -1
  31. package/build/{warc-CVS0t1fW.mjs → warc-BHcaRKHw.mjs} +2 -2
  32. package/build/{warc-CVS0t1fW.mjs.map → warc-BHcaRKHw.mjs.map} +1 -1
  33. package/build/webbotauth-CjJdTAfE.mjs.map +1 -1
  34. package/package.json +1 -1
  35. package/build/index.mjs.map +0 -1
  36. package/build/open-C4fhXJgD.mjs.map +0 -1
package/build/mcp.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { E as OpenOptions, O as PoliteCrawler, s as DaemonMcpHost } from "./daemon-DP4EqjFL.mjs";
1
+ import { E as OpenOptions, O as PoliteCrawler, s as DaemonMcpHost } from "./daemon-wwIWsUmU.mjs";
2
2
  import { McpServer, McpServer as McpServer$1 } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { Request, RequestId, Result, Task } from "@modelcontextprotocol/sdk/types.js";
4
4
  import { CreateTaskOptions, TaskStore } from "@modelcontextprotocol/sdk/experimental/tasks/interfaces.js";
package/build/mcp.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { t as browserMissing } from "./browser-CKRNbIDT.mjs";
2
- import { i as durationMs, r as VERSION } from "./version-ntbY-6wh.mjs";
3
- import { A as crawlSite, M as discoverSite, i as openCrawler } from "./open-C4fhXJgD.mjs";
4
- import { a as formatDiff, n as diffDocument } from "./diff-NQcQqKe9.mjs";
5
- import { r as extractDocument } from "./extract-ByFfc8Zk.mjs";
2
+ import { i as durationMs, r as VERSION } from "./version-BPem-XZd.mjs";
3
+ import { S as crawlSite, i as openCrawler, w as discoverSite } from "./open-CbpSCEZc.mjs";
4
+ import { a as formatDiff, n as diffDocument } from "./diff-BUM91Rev.mjs";
5
+ import { r as extractDocument } from "./extract-CefB5Vfn.mjs";
6
6
  import { n as urlHost, t as isLoopbackHost } from "./loopback-C_QYowSf.mjs";
7
7
  import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
8
8
  import { readFileSync, statSync } from "node:fs";
@@ -2,14 +2,15 @@ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { n as FileBlobStore, t as CaptureLog } from "./log-Dx-wXjJl.mjs";
3
3
  import { r as playwrightRenderer } from "./render-C1CUraWF.mjs";
4
4
  import { r as checkedRenderer } from "./browser-CKRNbIDT.mjs";
5
- import { i as durationMs, n as DEFAULT_TOKEN, r as VERSION, t as BOT_PAGE } from "./version-ntbY-6wh.mjs";
6
- import { A as snippetOf, C as fallbackReason, E as fold, L as sha256$1, M as Crawler, N as DEADLINE_NOTE, R as stableStringify, S as archivedOf, a as fetchPage, c as isHeldBack, f as readDocument, g as verifyQuote, i as fetchAndIndex, k as parseQuery, m as reindex, n as HELD_BACK, o as findDocuments, p as recordOverride, r as catchUpIndex, s as heldBackMeta, t as Extractors, y as archivedCopy } from "./runtime-JToRO63o.mjs";
5
+ import { i as durationMs, n as DEFAULT_TOKEN, r as VERSION, t as BOT_PAGE } from "./version-BPem-XZd.mjs";
6
+ import { A as snippetOf, E as fold, L as sha256$1, M as Crawler, N as DEADLINE_NOTE, R as stableStringify, S as archivedOf, a as fetchPage, c as isHeldBack, f as readDocument, g as verifyQuote, i as fetchAndIndex, k as parseQuery, m as reindex, o as findDocuments, r as catchUpIndex, t as Extractors } from "./runtime-D_r2SYge.mjs";
7
7
  import { MemoryHostStateStore, nextValidatorMemo, realClock } from "./politeness.mjs";
8
8
  import { matchesPattern } from "./robots.mjs";
9
9
  import { n as canonicalUrl, r as scopeTest, t as cacheKeyOf } from "./urls-BHFeSyEK.mjs";
10
10
  import { o as decodeBody, t as extractPage } from "./html-DkQUAE_d.mjs";
11
- import { n as diffDocument, r as diffTexts } from "./diff-NQcQqKe9.mjs";
12
- import { r as extractDocument } from "./extract-ByFfc8Zk.mjs";
11
+ import { n as diffDocument, r as diffTexts } from "./diff-BUM91Rev.mjs";
12
+ import { r as extractDocument } from "./extract-CefB5Vfn.mjs";
13
+ import { o as registerRecipe } from "./recipe-GiFQojmo.mjs";
13
14
  import { createHash, randomUUID } from "node:crypto";
14
15
  import { dirname, join } from "node:path";
15
16
  import { existsSync, lstatSync, mkdirSync, realpathSync, statSync } from "node:fs";
@@ -847,7 +848,7 @@ const XML_ENTITIES = {
847
848
  };
848
849
  /** A tag keeps this number of attributes at most. The reader reads the others and keeps nothing of them. */
849
850
  const MAX_ATTRIBUTES = 64;
850
- const NAME$1 = /^[A-Za-z_][\w.:-]*$/;
851
+ const NAME = /^[A-Za-z_][\w.:-]*$/;
851
852
  const W3C_DATE = /^\d{4}(?:-\d{2}(?:-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?)?)?$/;
852
853
  const MAIL_DATE = /^(?:[A-Za-z]{3},?\s+)?\d{1,2}\s+[A-Za-z]{3,9}\s+\d{2,4}\s+\d{1,2}:\d{2}(?::\d{2})?(?:\s*(?:[A-Za-z]{1,5}|[+-]\d{4}))?$/;
853
854
  const short = (name) => name === void 0 ? "nothing" : name.length > 40 ? `${name.slice(0, 40)}...` : name;
@@ -920,7 +921,7 @@ const tagAt = (text, from) => {
920
921
  at += 1;
921
922
  }
922
923
  const name = text.slice(from, Math.min(at, from + 256));
923
- if (at - from > 256 || !NAME$1.test(name)) return void 0;
924
+ if (at - from > 256 || !NAME.test(name)) return void 0;
924
925
  const attributes = [];
925
926
  for (;;) {
926
927
  while (at < n && isSpace(text.charCodeAt(at))) at += 1;
@@ -945,7 +946,7 @@ const tagAt = (text, from) => {
945
946
  at += 1;
946
947
  }
947
948
  const key = text.slice(start, Math.min(at, start + 256));
948
- if (at - start > 256 || !NAME$1.test(key)) return void 0;
949
+ if (at - start > 256 || !NAME.test(key)) return void 0;
949
950
  while (at < n && isSpace(text.charCodeAt(at))) at += 1;
950
951
  if (text.charCodeAt(at) !== 61) return void 0;
951
952
  at += 1;
@@ -1339,7 +1340,7 @@ const DISCOVER_LIMITS = {
1339
1340
  /** The most feeds that discovery reads from the links of the page. */
1340
1341
  maxLinkedFeeds: 5
1341
1342
  };
1342
- const PAGE_ACCEPT$1 = "text/html,application/xhtml+xml;q=0.9,*/*;q=0.5";
1343
+ const PAGE_ACCEPT = "text/html,application/xhtml+xml;q=0.9,*/*;q=0.5";
1343
1344
  const SITEMAP_ACCEPT = "application/xml,text/xml;q=0.9,text/plain;q=0.8,*/*;q=0.5";
1344
1345
  const FEED_ACCEPT = "application/rss+xml,application/atom+xml,application/xml;q=0.9,text/xml;q=0.8,*/*;q=0.5";
1345
1346
  const FEED_KINDS = /* @__PURE__ */ new Set([
@@ -1534,7 +1535,7 @@ const discoverSite = async (runtime, site, options = {}) => {
1534
1535
  url: start.href,
1535
1536
  mode: "page",
1536
1537
  recipe: "page",
1537
- accept: PAGE_ACCEPT$1,
1538
+ accept: PAGE_ACCEPT,
1538
1539
  maxAgeMs,
1539
1540
  onBehalfOf: options.onBehalfOf,
1540
1541
  signal,
@@ -3018,279 +3019,6 @@ async function* pagesOf(runtime) {
3018
3019
  if (page.length > 0) yield page;
3019
3020
  }
3020
3021
  //#endregion
3021
- //#region src/recipe.ts
3022
- /**
3023
- * This function reads a body as JSON, in the encoding that the response declares. A body that is
3024
- * not JSON (a page about maintenance, or a login page) throws an error that names the URL and the
3025
- * content type. In an extractor, the runtime then reports that error.
3026
- */
3027
- const readJson = (body, capture) => {
3028
- const text = decodeBody(body, capture.contentType);
3029
- try {
3030
- return JSON.parse(text);
3031
- } catch {
3032
- throw new Error(`${capture.url} answered ${capture.contentType ?? "a body of no stated type"}, not JSON`);
3033
- }
3034
- };
3035
- /**
3036
- * These recipe names belong to the crawler: the page reader, the archived fallback, robots.txt,
3037
- * and the sitemaps and feeds of `discover`. A recipe cannot use them.
3038
- */
3039
- const RESERVED_RECIPES = [
3040
- "page",
3041
- "archive-copy",
3042
- "archive-index",
3043
- "robots.txt",
3044
- "sitemap",
3045
- "feed"
3046
- ];
3047
- /** These are the limits of a recipe. */
3048
- const RECIPE_LIMITS = {
3049
- nameChars: 64,
3050
- docIdChars: 2048
3051
- };
3052
- const NAME = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
3053
- const PAGE_ACCEPT = "text/html,application/xhtml+xml;q=0.9,*/*;q=0.5";
3054
- const API_ACCEPT = "application/json";
3055
- const AI_INPUTS = [
3056
- "yes",
3057
- "no",
3058
- "unset"
3059
- ];
3060
- /** This function tells what is wrong with one document of an extractor, or gives undefined. */
3061
- const faultOf = (value, seen) => {
3062
- if (value === null || typeof value !== "object" || Array.isArray(value)) return "a document must be an object";
3063
- const doc = value;
3064
- const id = doc["doc"];
3065
- if (typeof id !== "string" || id.length === 0 || id.length > RECIPE_LIMITS.docIdChars) return `doc must be a text of 1 to ${String(RECIPE_LIMITS.docIdChars)} characters`;
3066
- if (id.startsWith("archive:")) return `the id ${id} starts with archive:, which is for the archived copies`;
3067
- if (seen.has(id)) return `two documents have the id ${id}`;
3068
- seen.add(id);
3069
- const kind = doc["kind"];
3070
- if (typeof kind !== "string" || kind.length === 0) return "kind must be a text that is not empty";
3071
- if (kind === "archived") return "the kind archived is for the archived copies";
3072
- if (typeof doc["title"] !== "string") return "title must be a text";
3073
- if (typeof doc["text"] !== "string") return "text must be a text";
3074
- if (doc["url"] !== null && typeof doc["url"] !== "string") return "url must be a text or null";
3075
- const meta = doc["meta"];
3076
- if (meta === null || typeof meta !== "object" || Array.isArray(meta)) return "meta must be an object";
3077
- try {
3078
- JSON.stringify(meta);
3079
- } catch {
3080
- return "meta must be JSON: no cycle and no BigInt";
3081
- }
3082
- const merge = doc["merge"];
3083
- if (merge !== void 0 && merge !== "latest" && merge !== "richest") return "merge must be \"latest\" or \"richest\"";
3084
- if (doc["full_text"] !== void 0 && typeof doc["full_text"] !== "string") return "full_text must be a text";
3085
- const aiInput = doc["ai_input"];
3086
- if (aiInput !== void 0 && !AI_INPUTS.includes(aiInput)) return "ai_input must be \"yes\", \"no\" or \"unset\"";
3087
- };
3088
- /** This function gives the documents of an extractor, or throws an error that names the first fault. */
3089
- const checkedDocs = (name, docs) => {
3090
- if (!Array.isArray(docs)) throw new TypeError(`the recipe ${name} gave ${typeof docs}, not an array of documents`);
3091
- const seen = /* @__PURE__ */ new Set();
3092
- for (const [index, doc] of docs.entries()) {
3093
- const fault = faultOf(doc, seen);
3094
- if (fault !== void 0) throw new TypeError(`the recipe ${name} gave a document that is not valid (${String(index)}): ${fault}`);
3095
- }
3096
- return docs;
3097
- };
3098
- /**
3099
- * This function declares a recipe. It checks the definition at once: a name that is not valid, a
3100
- * reserved name or a duration that it cannot read throws an error. The extractor of the recipe
3101
- * checks each document of `extract`. A document that is not valid makes the extractor throw, and
3102
- * the runtime reports it as an error of the extractor.
3103
- */
3104
- const defineRecipe = (definition) => {
3105
- const name = definition.name;
3106
- if (typeof name !== "string" || name.length > RECIPE_LIMITS.nameChars || !NAME.test(name)) throw new RangeError(`a recipe name is 1 to ${String(RECIPE_LIMITS.nameChars)} letters, digits and . _ : -, and it starts with a letter or a digit, not ${JSON.stringify(name) ?? "undefined"}`);
3107
- if (RESERVED_RECIPES.includes(name)) throw new RangeError(`the recipe name ${name} belongs to the crawler: give the recipe a name of its own`);
3108
- if (typeof definition.extract !== "function") throw new TypeError(`the recipe ${name} needs extract, a function that reads a body into documents`);
3109
- const mode = definition.mode ?? "page";
3110
- if (mode !== "page" && mode !== "api") throw new RangeError(`the mode of the recipe ${name} must be "page" or "api", not ${String(mode)}`);
3111
- const accept = definition.accept ?? (mode === "api" ? API_ACCEPT : PAGE_ACCEPT);
3112
- const minIntervalMs = definition.minInterval === void 0 ? void 0 : durationMs(definition.minInterval);
3113
- const maxAgeOf = (value) => value === "auto" ? "auto" : durationMs(value);
3114
- const maxAge = maxAgeOf(definition.maxAge ?? "24h");
3115
- const maxBytes = definition.maxBytes;
3116
- if (maxBytes !== void 0 && (!Number.isSafeInteger(maxBytes) || maxBytes < 1)) throw new RangeError(`maxBytes of the recipe ${name} must be a whole number of 1 or more`);
3117
- const extract = definition.extract;
3118
- const extractor = async (body, capture) => checkedDocs(name, await extract(body, {
3119
- url: capture.url,
3120
- contentType: capture.contentType
3121
- }));
3122
- const fetch = definition.fetch;
3123
- return {
3124
- name,
3125
- description: definition.description ?? null,
3126
- mode,
3127
- accept,
3128
- extractor,
3129
- request(input, options = {}) {
3130
- const asked = fetch === void 0 ? input : fetch(input);
3131
- const target = typeof asked === "string" ? { url: asked } : asked;
3132
- const url = target?.url;
3133
- let parsed;
3134
- try {
3135
- parsed = typeof url === "string" ? new URL(url) : void 0;
3136
- } catch {
3137
- parsed = void 0;
3138
- }
3139
- if (parsed === void 0 || parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new TypeError(`the recipe ${name} needs an http or https URL for its input, not ${JSON.stringify(url) ?? String(url)}`);
3140
- return {
3141
- url: parsed.href,
3142
- mode,
3143
- recipe: name,
3144
- accept: target?.accept ?? accept,
3145
- headers: target?.headers,
3146
- secretParams: target?.secretParams,
3147
- minIntervalMs,
3148
- maxAgeMs: options.maxAge === void 0 ? maxAge : maxAgeOf(options.maxAge),
3149
- maxBytes,
3150
- onBehalfOf: options.onBehalfOf,
3151
- job: options.job,
3152
- signal: options.signal,
3153
- scope: options.scope,
3154
- labels: options.labels,
3155
- deadline: options.deadline
3156
- };
3157
- }
3158
- };
3159
- };
3160
- /**
3161
- * This function gives the extractor of a recipe to the extractors of a runtime. A second
3162
- * registration of the same recipe does nothing. A different extractor with the same name throws
3163
- * an error, because `reindex` must read each capture with one extractor.
3164
- */
3165
- const registerRecipe = (extractors, recipe) => {
3166
- const known = extractors.get(recipe.name);
3167
- if (known === recipe.extractor) return;
3168
- if (known !== void 0) throw new Error(`the recipe ${recipe.name} has a different extractor in this runtime: give each recipe a name of its own`);
3169
- extractors.register(recipe.name, recipe.extractor);
3170
- };
3171
- /** This function gives the runtime of a target. */
3172
- const runtimeOf = (target) => "crawler" in target ? target : target.runtime;
3173
- /** This function gives the deadline of a budget, on the clock of the runtime. */
3174
- const deadlineOf = (runtime, budget) => budget === void 0 ? void 0 : Date.parse(runtime.now()) + durationMs(budget);
3175
- /** This function gives one document of a run, with the rule for a held-back document. */
3176
- const recipeDoc = async (runtime, doc, capture, userProvided, action) => {
3177
- const current = runtime.index.get(doc.doc) ?? doc;
3178
- const version = (capture === void 0 ? void 0 : runtime.index.versionText(doc.doc, capture)) ?? current;
3179
- const aiInput = current.ai_input === "no" || version.ai_input === "no" ? "no" : version.ai_input;
3180
- const held = isHeldBack({ ai_input: aiInput }, userProvided);
3181
- if (aiInput === "no" && userProvided) await recordOverride(runtime, current, action);
3182
- const archived = archivedOf(version.meta);
3183
- return {
3184
- doc: doc.doc,
3185
- kind: version.kind,
3186
- title: held ? "" : version.title,
3187
- url: version.url,
3188
- meta: held ? heldBackMeta(version.meta) : version.meta,
3189
- chars: version.text.length,
3190
- capture: version.capture,
3191
- fetched_at: version.fetched_at,
3192
- held_back: held,
3193
- ...archived === void 0 ? {} : { archived }
3194
- };
3195
- };
3196
- const noteOf = (parts) => {
3197
- const kept = parts.filter((part) => typeof part === "string" && part.length > 0);
3198
- return kept.length > 0 ? kept.join("; ") : null;
3199
- };
3200
- /**
3201
- * This function starts a recipe for one input. It fetches the request through the crawler, and it
3202
- * reads the capture with the extractor of the recipe. It gives the documents of that capture.
3203
- * These are its rules:
3204
- *
3205
- * - The runtime registers the recipe the first time that it gets it. Give the recipe to
3206
- * `openCrawler` too, so that `reindex` reads its captures before the first fetch.
3207
- * - A stored copy younger than `maxAge` answers with no request. When the extractor cannot read a
3208
- * stored copy, the crawler asks the source one more time.
3209
- * - An extractor that throws gives no document, and `error` tells why. The fetch still succeeds.
3210
- * - A document that asked not to be AI input shows no title and only its structural metadata,
3211
- * unless `userProvided`. The crawler logs each `userProvided` read of a held-back document.
3212
- * - With `fallback: "archive"`, a page that the site refuses gets an archived copy, with
3213
- * `archived`. The page reader reads the copy. The extractor of the recipe reads only live
3214
- * captures. Thus its documents do not mix a live answer and an old copy of a third party.
3215
- */
3216
- const runRecipe = async (target, recipe, input, options = {}) => {
3217
- const runtime = runtimeOf(target);
3218
- if (options.fallback === "archive" && recipe.mode !== "page") throw new RangeError(`the recipe ${recipe.name} reads an API: an archived copy is for a page, so give fallback only to a recipe of the mode page`);
3219
- registerRecipe(runtime.extractors, recipe);
3220
- const deadline = deadlineOf(runtime, options.budget);
3221
- const request = recipe.request(input, {
3222
- ...options,
3223
- deadline
3224
- });
3225
- let indexed = await fetchAndIndex(runtime, request);
3226
- if (indexed.error !== void 0 && indexed.blocked === void 0 && indexed.fetched.fromCache) indexed = await fetchAndIndex(runtime, {
3227
- ...request,
3228
- maxAgeMs: 0
3229
- });
3230
- const userProvided = options.userProvided ?? false;
3231
- const action = options.job ?? `recipe ${recipe.name}`;
3232
- const fetched = indexed.fetched;
3233
- const notes = [fetched.note, indexed.error];
3234
- if (indexed.docs.length === 0 && options.fallback === "archive") {
3235
- const reason = fallbackReason(indexed);
3236
- if (reason !== void 0) {
3237
- const page = {
3238
- render: "never",
3239
- maxAgeMs: request.maxAgeMs,
3240
- onBehalfOf: request.onBehalfOf,
3241
- job: request.job,
3242
- signal: request.signal,
3243
- scope: request.scope,
3244
- labels: request.labels,
3245
- deadline,
3246
- fallback: "archive",
3247
- archiveDespiteRobots: options.archiveDespiteRobots
3248
- };
3249
- const answer = await archivedCopy(runtime, request.url, reason, page);
3250
- if (answer.doc !== void 0 && answer.archived !== void 0) {
3251
- const doc = await recipeDoc(runtime, answer.doc, answer.doc.capture, userProvided, action);
3252
- return {
3253
- recipe: recipe.name,
3254
- url: fetched.url,
3255
- outcome: fetched.outcome,
3256
- status: fetched.status,
3257
- docs: [doc],
3258
- capture: doc.capture,
3259
- fetched_at: doc.fetched_at,
3260
- from_cache: answer.fromCache,
3261
- held_back: doc.held_back,
3262
- error: indexed.error ?? null,
3263
- archived: answer.archived,
3264
- note: noteOf([
3265
- ...notes,
3266
- ...answer.notes,
3267
- "the page reader read the archived copy, not the extractor of the recipe",
3268
- doc.held_back ? HELD_BACK : void 0
3269
- ])
3270
- };
3271
- }
3272
- notes.push(...answer.notes);
3273
- }
3274
- }
3275
- const capture = fetched.capture;
3276
- const docs = [];
3277
- for (const doc of indexed.docs) docs.push(await recipeDoc(runtime, doc, capture, userProvided, action));
3278
- const held = docs.some((doc) => doc.held_back);
3279
- return {
3280
- recipe: recipe.name,
3281
- url: fetched.url,
3282
- outcome: fetched.outcome,
3283
- status: fetched.status,
3284
- docs,
3285
- capture: docs[0]?.capture ?? capture ?? null,
3286
- fetched_at: docs[0]?.fetched_at ?? fetched.fetchedAt ?? null,
3287
- from_cache: fetched.fromCache,
3288
- held_back: held,
3289
- error: indexed.error ?? null,
3290
- note: noteOf([...notes, held ? HELD_BACK : void 0])
3291
- };
3292
- };
3293
- //#endregion
3294
3022
  //#region src/vectors.ts
3295
3023
  /**
3296
3024
  * The vector store keeps the vectors of hybrid search in `vectors.sqlite`, beside the index. It is
@@ -4539,6 +4267,6 @@ const openCrawler = (options) => {
4539
4267
  };
4540
4268
  };
4541
4269
  //#endregion
4542
- export { crawlSite as A, chunkText as C, unitVector as D, fuseRanks as E, readSitemap as F, DocIndex as I, FAST_RANK_SHARE as L, discoverSite as M, SITEMAP_LIMITS as N, SqliteHostStateStore as O, feedDate as P, NO_EMBEDDER as R, HYBRID_DEFAULTS as S, estimateTokens as T, registerRecipe as _, open_exports as a, createHybridSearch as b, WatchStore as c, VectorStore as d, RECIPE_LIMITS as f, readJson as g, defineRecipe as h, openCrawler as i, DISCOVER_LIMITS as j, CRAWL_LIMITS as k, runWatch as l, deadlineOf as m, defaultUserAgent as n, WATCH_LIMITS as o, RESERVED_RECIPES as p, isMailbox as r, WATCH_SCHEMA as s, createRuntime as t, VECTORS_SCHEMA as u, runRecipe as v, chunkTokensOf as w, CHUNK_LIMITS as x, runtimeOf as y };
4270
+ export { NO_EMBEDDER as A, DISCOVER_LIMITS as C, readSitemap as D, feedDate as E, DocIndex as O, crawlSite as S, SITEMAP_LIMITS as T, estimateTokens as _, open_exports as a, SqliteHostStateStore as b, WatchStore as c, VectorStore as d, createHybridSearch as f, chunkTokensOf as g, chunkText as h, openCrawler as i, FAST_RANK_SHARE as k, runWatch as l, HYBRID_DEFAULTS as m, defaultUserAgent as n, WATCH_LIMITS as o, CHUNK_LIMITS as p, isMailbox as r, WATCH_SCHEMA as s, createRuntime as t, VECTORS_SCHEMA as u, fuseRanks as v, discoverSite as w, CRAWL_LIMITS as x, unitVector as y };
4543
4271
 
4544
- //# sourceMappingURL=open-C4fhXJgD.mjs.map
4272
+ //# sourceMappingURL=open-CbpSCEZc.mjs.map