polite-crawler 0.1.4 → 0.2.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/README.md +28 -121
- package/build/{browser-CKRNbIDT.mjs → browser-56dBBLec.mjs} +7 -3
- package/build/browser-56dBBLec.mjs.map +1 -0
- package/build/classify.d.mts +50 -2
- package/build/{cli-daemon-ZYRdy6EW.mjs → cli-daemon-qS4PH2bM.mjs} +2 -2
- package/build/{cli-daemon-ZYRdy6EW.mjs.map → cli-daemon-qS4PH2bM.mjs.map} +1 -1
- package/build/{cli-recipes-Bci6HREA.mjs → cli-recipes-qbLRVThF.mjs} +2 -2
- package/build/{cli-recipes-Bci6HREA.mjs.map → cli-recipes-qbLRVThF.mjs.map} +1 -1
- package/build/cli.mjs +41 -16
- package/build/cli.mjs.map +1 -1
- package/build/{daemon-DK6U51_v.mjs → daemon-BXBbUrni.mjs} +11 -7
- package/build/daemon-BXBbUrni.mjs.map +1 -0
- package/build/{diff-BUM91Rev.mjs → diff-CpKN0Jdc.mjs} +7 -3
- package/build/{diff-BUM91Rev.mjs.map → diff-CpKN0Jdc.mjs.map} +1 -1
- package/build/{extract-CefB5Vfn.mjs → extract-DqeqiNHb.mjs} +12 -4
- package/build/extract-DqeqiNHb.mjs.map +1 -0
- package/build/{html-BACljlkf.d.mts → html-DMgIeQfJ.d.mts} +16 -2
- package/build/html.d.mts +1 -2
- package/build/index.d.mts +4227 -40
- package/build/index.mjs +17 -17
- package/build/{log-Dx-wXjJl.mjs → log-CmBWcyQm.mjs} +5 -1
- package/build/log-CmBWcyQm.mjs.map +1 -0
- package/build/mcp-package-Y0bREnOS.mjs +40 -0
- package/build/mcp-package-Y0bREnOS.mjs.map +1 -0
- package/build/{ollama-BJxkjny4.mjs → ollama-D1xRh3hY.mjs} +7 -3
- package/build/ollama-D1xRh3hY.mjs.map +1 -0
- package/build/{open-CbpSCEZc.mjs → open-CEo22Hpu.mjs} +78 -18
- package/build/open-CEo22Hpu.mjs.map +1 -0
- package/build/politeness.d.mts +366 -2
- package/build/{recipe-GiFQojmo.mjs → recipe-B2SkepDZ.mjs} +10 -4
- package/build/recipe-B2SkepDZ.mjs.map +1 -0
- package/build/{recipe-product-Dellqudu.mjs → recipe-product-CyZE3Ukd.mjs} +29 -7
- package/build/recipe-product-CyZE3Ukd.mjs.map +1 -0
- package/build/{render-C1CUraWF.mjs → render-CKE2LILH.mjs} +10 -3
- package/build/render-CKE2LILH.mjs.map +1 -0
- package/build/robots.d.mts +143 -2
- package/build/robots.mjs +1 -1
- package/build/robots.mjs.map +1 -1
- package/build/{rolldown-runtime-D7D4PA-g.mjs → rolldown-runtime-8H4AJuhK.mjs} +1 -0
- package/build/{runtime-D_r2SYge.mjs → runtime-C_2BQibH.mjs} +84 -18
- package/build/runtime-C_2BQibH.mjs.map +1 -0
- package/build/signals.d.mts +45 -2
- package/build/{transformers-BQMlaGj4.mjs → transformers-BNiuiLxT.mjs} +3 -3
- package/build/transformers-BNiuiLxT.mjs.map +1 -0
- package/build/{version-BPem-XZd.mjs → version-Djva7JkN.mjs} +7 -2
- package/build/version-Djva7JkN.mjs.map +1 -0
- package/build/{wacz-QyzLp4mC.mjs → wacz-DKk2HBmx.mjs} +4 -4
- package/build/{wacz-QyzLp4mC.mjs.map → wacz-DKk2HBmx.mjs.map} +1 -1
- package/build/{warc-BHcaRKHw.mjs → warc-C_ew76bV.mjs} +4 -4
- package/build/{warc-BHcaRKHw.mjs.map → warc-C_ew76bV.mjs.map} +1 -1
- package/build/{webbotauth-CjJdTAfE.mjs → webbotauth-Dq_dA_u3.mjs} +2 -2
- package/build/{webbotauth-CjJdTAfE.mjs.map → webbotauth-Dq_dA_u3.mjs.map} +1 -1
- package/package.json +5 -11
- package/build/browser-CKRNbIDT.mjs.map +0 -1
- package/build/classify-CrfyXcwo.d.mts +0 -51
- package/build/daemon-DK6U51_v.mjs.map +0 -1
- package/build/daemon-wwIWsUmU.d.mts +0 -3777
- package/build/extract-CefB5Vfn.mjs.map +0 -1
- package/build/log-Dx-wXjJl.mjs.map +0 -1
- package/build/loopback-C_QYowSf.mjs +0 -22
- package/build/loopback-C_QYowSf.mjs.map +0 -1
- package/build/mcp.d.mts +0 -199
- package/build/mcp.mjs +0 -1555
- package/build/mcp.mjs.map +0 -1
- package/build/ollama-BJxkjny4.mjs.map +0 -1
- package/build/open-CbpSCEZc.mjs.map +0 -1
- package/build/politeness-yI3xBEx9.d.mts +0 -367
- package/build/recipe-GiFQojmo.mjs.map +0 -1
- package/build/recipe-product-Dellqudu.mjs.map +0 -1
- package/build/render-C1CUraWF.mjs.map +0 -1
- package/build/robots-R-qaEBOb.d.mts +0 -144
- package/build/runtime-D_r2SYge.mjs.map +0 -1
- package/build/signals-BHpy2xb2.d.mts +0 -46
- package/build/transformers-BQMlaGj4.mjs.map +0 -1
- package/build/urls-BTDSgDLC.d.mts +0 -16
- package/build/version-BPem-XZd.mjs.map +0 -1
package/build/index.d.mts
CHANGED
|
@@ -1,45 +1,4173 @@
|
|
|
1
|
+
import { Outcome } from "./classify.mjs";
|
|
1
2
|
import { DAYS, Day, Hours, HoursComparison, Interval, Week, compareHours, describeWeek, disagreements, fromCompact, fromSchemaOrg, fromText, toHoursWeek } from "./hours.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
3
|
+
import { a as typesOf, c as decodeBody, l as ScopeOptions, n as PageLink, r as extractPage, s as bodyEncoding, t as PageExtract } from "./html-DMgIeQfJ.mjs";
|
|
4
|
+
import { AiInput, Signals } from "./signals.mjs";
|
|
5
|
+
import { Clock, DEFAULT_POLICY, HostCounts, HostResult, HostStateStore, HostStop, MemoryHostStateStore, PolicyOptions, PolitenessPolicy, VALIDATOR_MEMO, ValidatorAnswer, ValidatorLimits, ValidatorMemo, ValidatorReport, ValidatorVerdict, nextValidatorMemo, realClock } from "./politeness.mjs";
|
|
6
|
+
import { ALLOW_ALL, Robots, RobotsDecision, RobotsGroup, RobotsRule, RobotsSignal, decide, parseRobots } from "./robots.mjs";
|
|
7
|
+
import { JsonWebKeyInput, KeyObject } from "node:crypto";
|
|
8
|
+
import { Dispatcher } from "undici";
|
|
9
|
+
import { LookupAddress, LookupAllOptions } from "node:dns";
|
|
10
|
+
import { LookupFunction } from "node:net";
|
|
11
|
+
//#region src/blobs.d.ts
|
|
12
|
+
/**
|
|
13
|
+
* A blob store keeps the bytes of each response, and the hash of the bytes is the key. The crawler
|
|
14
|
+
* needs only these two operations, so an application can give its own store.
|
|
15
|
+
*/
|
|
16
|
+
interface BlobStore {
|
|
17
|
+
put(bytes: Uint8Array): Promise<{
|
|
18
|
+
readonly hash: string;
|
|
19
|
+
}>;
|
|
20
|
+
get(hash: string): Promise<Uint8Array | undefined>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* These options change a file blob store.
|
|
24
|
+
*
|
|
25
|
+
* @beta
|
|
26
|
+
*/
|
|
27
|
+
interface FileBlobStoreOptions {
|
|
28
|
+
/** When this value is true (the default), a blob is on the disk (`fsync`) before `put` finishes. */
|
|
29
|
+
readonly sync?: boolean | undefined;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* This store keeps each blob in a file: `<dir>/ab/cd/abcd…`, where the name is the SHA-256 hash
|
|
33
|
+
* of the bytes. These are its rules:
|
|
34
|
+
*
|
|
35
|
+
* - Equal bytes are written one time only. A file of the wrong size (a write that a crash cut) is
|
|
36
|
+
* written again.
|
|
37
|
+
* - A write goes to a temporary file with a random name, goes to the disk, and then gets its name.
|
|
38
|
+
* A crash thus cannot leave half a blob under its name.
|
|
39
|
+
* - Two writers can put the same bytes at the same time. When the rename of one fails because the
|
|
40
|
+
* other was first, the put succeeds if the stored file has the same hash.
|
|
41
|
+
* - The method `get` examines the hash of the bytes. A damaged file gets a new name
|
|
42
|
+
* (`.damaged`), and `get` gives no bytes, so the crawler fetches the body again.
|
|
43
|
+
*
|
|
44
|
+
* @beta
|
|
45
|
+
*/
|
|
46
|
+
export declare class FileBlobStore implements BlobStore {
|
|
47
|
+
readonly dir: string;
|
|
48
|
+
private readonly writing;
|
|
49
|
+
private readonly sync;
|
|
50
|
+
constructor(dir: string, options?: FileBlobStoreOptions);
|
|
51
|
+
put(bytes: Uint8Array): Promise<{
|
|
52
|
+
readonly hash: string;
|
|
53
|
+
readonly existed: boolean;
|
|
54
|
+
}>;
|
|
55
|
+
/** This method tells if a file holds exactly the bytes of a hash. */
|
|
56
|
+
private static holds;
|
|
57
|
+
private write;
|
|
58
|
+
get(hash: string): Promise<Uint8Array | undefined>;
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/render.d.ts
|
|
62
|
+
/**
|
|
63
|
+
* The renderer opens the headless browser only for a page that shows almost nothing without
|
|
64
|
+
* JavaScript. The settings:
|
|
65
|
+
*
|
|
66
|
+
* - The default `chromium-headless-shell` of Playwright, the lighter build.
|
|
67
|
+
* - One browser for many pages, and a new context for each page. Thus no cookie or storage goes
|
|
68
|
+
* from one site to the next. A context has its own HTTP cache, so routing each request costs
|
|
69
|
+
* no cache across sites.
|
|
70
|
+
* - Each request of the page goes through a route, the first navigation too. The route refuses
|
|
71
|
+
* images, fonts and media by their resource type. It asks `allowRequest` about each other
|
|
72
|
+
* request, and it sends `headers` with it.
|
|
73
|
+
* - Playwright routes only the first request of a redirect chain. Thus with `allowRequest`, the
|
|
74
|
+
* route sends each request itself, follows each redirect itself and asks about each step. The
|
|
75
|
+
* browser then gets each body whole, not as a stream.
|
|
76
|
+
* - No download, no popup, no service worker and no dialog.
|
|
77
|
+
* - A wait until the visible text stops growing, with a limit. Playwright discourages
|
|
78
|
+
* `networkidle`. The text includes open shadow roots and the frames of the same origin.
|
|
79
|
+
* - One deadline for the whole render: the navigation, the wait and the reading of the page.
|
|
80
|
+
* - After a crash or a disconnection, the next page launches a new browser.
|
|
81
|
+
* - The browser closes after two idle minutes. An idle Chromium holds hundreds of megabytes, so
|
|
82
|
+
* it stays open only while pages need it. The open browser keeps the process alive, so a
|
|
83
|
+
* script uses `close()` when it is done.
|
|
84
|
+
*/
|
|
85
|
+
/** @beta */
|
|
86
|
+
interface Rendered {
|
|
87
|
+
readonly html: string;
|
|
88
|
+
readonly finalUrl: string;
|
|
89
|
+
readonly status: number | null;
|
|
90
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* These are the options of one render.
|
|
94
|
+
*
|
|
95
|
+
* @beta
|
|
96
|
+
*/
|
|
97
|
+
interface RenderOptions {
|
|
98
|
+
readonly userAgent: string;
|
|
99
|
+
/** This value is the deadline of the whole render, in milliseconds. */
|
|
100
|
+
readonly timeoutMs: number;
|
|
101
|
+
/** These headers go with each request of the page, for example the `From` header. */
|
|
102
|
+
readonly headers?: Readonly<Record<string, string>> | undefined;
|
|
103
|
+
/**
|
|
104
|
+
* This function decides each request that the page makes, the first navigation included. When
|
|
105
|
+
* it gives false, the browser does not send the request. The crawler uses it to apply the
|
|
106
|
+
* egress policy and robots.txt to the requests of the page.
|
|
107
|
+
*/
|
|
108
|
+
readonly allowRequest?: ((url: string) => Promise<boolean> | boolean) | undefined;
|
|
109
|
+
/** This value is the maximum number of redirects of one request of the page. The default is 5, as for a plain fetch. */
|
|
110
|
+
readonly maxRedirects?: number | undefined;
|
|
111
|
+
/** When this signal aborts, the renderer stops the page: it closes the context, and the render rejects. */
|
|
112
|
+
readonly signal?: AbortSignal | undefined;
|
|
113
|
+
/**
|
|
114
|
+
* This function gives the signature headers of one request of the page (Web Bot Auth). The route
|
|
115
|
+
* adds them to the request and to each step of its redirects. When the function gives undefined,
|
|
116
|
+
* the browser does not send the request.
|
|
117
|
+
*/
|
|
118
|
+
readonly sign?: ((url: string) => Readonly<Record<string, string>> | undefined) | undefined;
|
|
119
|
+
}
|
|
120
|
+
/** @beta */
|
|
121
|
+
interface Renderer {
|
|
122
|
+
render(url: string, options: RenderOptions): Promise<Rendered>;
|
|
123
|
+
close(): Promise<void>;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The reason of a `RenderRefused`.
|
|
127
|
+
*
|
|
128
|
+
* @beta
|
|
129
|
+
*/
|
|
130
|
+
type RefusedReason = "refused" | "download" | "scheme" | "browser";
|
|
131
|
+
/**
|
|
132
|
+
* This error tells that the renderer did not render the page, and that the page or the site is
|
|
133
|
+
* not at fault. The reason is one of these:
|
|
134
|
+
*
|
|
135
|
+
* - `refused`: `allowRequest` refused the first navigation, a step of its redirects, or a later
|
|
136
|
+
* navigation of the page that left it on an error page. `url` names the refused URL.
|
|
137
|
+
* - `download`: the URL gives a file, not a page.
|
|
138
|
+
* - `scheme`: the URL is not an http or https URL.
|
|
139
|
+
* - `browser`: the renderer has no browser (`playwright-core` or its browser is not installed). The
|
|
140
|
+
* crawler then renders no more pages, and gives each one the note at once.
|
|
141
|
+
*
|
|
142
|
+
* A crawler does not count this error as a failure of the host.
|
|
143
|
+
*
|
|
144
|
+
* @beta
|
|
145
|
+
*/
|
|
146
|
+
export declare class RenderRefused extends Error {
|
|
147
|
+
readonly reason: RefusedReason;
|
|
148
|
+
/** This URL is the request that `allowRequest` or the limit of redirects refused, when the reason is `refused`. */
|
|
149
|
+
readonly url: string | undefined;
|
|
150
|
+
constructor(reason: RefusedReason, message: string, url?: string);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The text of the error when the optional package `playwright-core` is not installed.
|
|
154
|
+
*
|
|
155
|
+
* @beta
|
|
156
|
+
*/
|
|
157
|
+
export declare const NO_PLAYWRIGHT = "rendering needs the package playwright-core and a browser: npm install playwright-core, then npx playwright-core install chromium-headless-shell. For the MCP server with npx, start it with npx -y -p polite-crawler-mcp -p playwright-core polite-crawler-mcp";
|
|
158
|
+
/** @beta */
|
|
159
|
+
interface PlaywrightRenderer extends Renderer {
|
|
160
|
+
/** This function tells if a browser is open. */
|
|
161
|
+
readonly open: () => boolean;
|
|
162
|
+
}
|
|
163
|
+
/** @beta */
|
|
164
|
+
export declare const playwrightRenderer: (options?: {
|
|
165
|
+
readonly idleMs?: number;
|
|
166
|
+
/**
|
|
167
|
+
* This function starts the browser: it gives a `Browser` of `playwright-core`. The default is
|
|
168
|
+
* the headless shell of Playwright. The type names no type of `playwright-core`, so a program
|
|
169
|
+
* with no `playwright-core` can check its types.
|
|
170
|
+
*/
|
|
171
|
+
readonly launch?: (() => Promise<object>) | undefined;
|
|
172
|
+
}) => PlaywrightRenderer;
|
|
173
|
+
//#endregion
|
|
8
174
|
//#region src/browser.d.ts
|
|
9
175
|
/**
|
|
10
|
-
* This function
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
176
|
+
* This function tells if the default renderer can start a browser. It gives no value when it can,
|
|
177
|
+
* and the reason when it cannot. The command line and the MCP server use it one time at start.
|
|
178
|
+
* Then a missing browser costs no wait and no error capture for each page that needs JavaScript.
|
|
179
|
+
*
|
|
180
|
+
* The check does not start the browser. It finds the full Chromium build or the headless shell of
|
|
181
|
+
* the same revision on the disk. When the check cannot find the answer, it gives no value, and the
|
|
182
|
+
* renderer then tries.
|
|
183
|
+
*
|
|
184
|
+
* @beta
|
|
185
|
+
*/
|
|
186
|
+
export declare const browserMissing: (load?: () => Promise<{
|
|
187
|
+
readonly chromium: {
|
|
188
|
+
executablePath(): string;
|
|
189
|
+
};
|
|
190
|
+
}>) => Promise<string | undefined>;
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region src/duration.d.ts
|
|
193
|
+
/** A duration is a count of milliseconds, or a text, for example `"30m"`, `"24h"` or `"7d"`. */
|
|
194
|
+
type Duration = number | string;
|
|
195
|
+
/**
|
|
196
|
+
* This function changes a duration into milliseconds. The units are `ms`, `s`, `m` (minutes), `h`,
|
|
197
|
+
* `d` and `w`, in any case. The unit `M` is an error, because a month is not a unit. A bare `0`
|
|
198
|
+
* is zero. The function throws an error for a text that it cannot read.
|
|
199
|
+
*
|
|
200
|
+
* The options take a `Duration` (for example `"10m"`), and the crawler reads it. polite-crawler-mcp
|
|
201
|
+
* reads the durations of its tools with it.
|
|
202
|
+
*
|
|
203
|
+
* @beta
|
|
204
|
+
*/
|
|
205
|
+
export declare const durationMs: (duration: Duration) => number;
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region src/record.d.ts
|
|
208
|
+
/**
|
|
209
|
+
* A capture record is one line of the capture log. Each fetch decision makes one record, and a
|
|
210
|
+
* fetch that the crawler declines makes one too.
|
|
211
|
+
*
|
|
212
|
+
* The record id comes from a hash of its content, so the same decision always gets the same id.
|
|
213
|
+
* A citation names this id.
|
|
214
|
+
*
|
|
215
|
+
* The id has 48 bits: the source and the first 12 hexadecimal digits of the hash. Approximately
|
|
216
|
+
* 1 million records give a chance of a collision of 0.2 percent. The package keeps this length on purpose,
|
|
217
|
+
* because the ids of the earlier capture logs and their citations must stay the same. A later
|
|
218
|
+
* version can make the ids of new records longer behind a record version. It must still read a
|
|
219
|
+
* 12-digit id.
|
|
220
|
+
*/
|
|
221
|
+
interface CaptureRecord {
|
|
222
|
+
readonly id: string;
|
|
223
|
+
/** The source of the record. The crawler writes `fetch`, and an application can add its own sources. */
|
|
224
|
+
readonly source: string;
|
|
225
|
+
readonly kind: string;
|
|
226
|
+
readonly external_id?: string | undefined;
|
|
227
|
+
readonly occurred_at?: string | undefined;
|
|
228
|
+
readonly received_at: string;
|
|
229
|
+
readonly payload: unknown;
|
|
230
|
+
readonly content_hash: string;
|
|
231
|
+
/** This label comes from the application. The crawler writes it as it is and does not read it. */
|
|
232
|
+
readonly scope?: string | undefined;
|
|
233
|
+
/**
|
|
234
|
+
* These labels come from the application, for example the lineage of a request. The crawler writes
|
|
235
|
+
* them as they are and does not read them. Like `scope`, they are not part of the hash.
|
|
236
|
+
*/
|
|
237
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* These are the fields of a new record. The function `makeRecord` calculates the id and the hash.
|
|
241
|
+
*
|
|
242
|
+
* @beta
|
|
243
|
+
*/
|
|
244
|
+
interface RecordInput {
|
|
245
|
+
readonly source: string;
|
|
246
|
+
readonly kind: string;
|
|
247
|
+
readonly payload: unknown;
|
|
248
|
+
readonly received_at: string;
|
|
249
|
+
readonly external_id?: string | undefined;
|
|
250
|
+
readonly occurred_at?: string | undefined;
|
|
251
|
+
readonly scope?: string | undefined;
|
|
252
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* This function makes a capture record. The hash covers the source, the kind, the external id
|
|
256
|
+
* and the payload. The id is the source and the first 12 digits of the hash. The times, `scope`
|
|
257
|
+
* and `labels` are not part of the hash.
|
|
258
|
+
*
|
|
259
|
+
* @beta
|
|
260
|
+
*/
|
|
261
|
+
export declare const makeRecord: (input: RecordInput) => CaptureRecord;
|
|
262
|
+
//#endregion
|
|
263
|
+
//#region src/log.d.ts
|
|
264
|
+
/** A scan filter selects the records of one source, one kind, or the records after one time. */
|
|
265
|
+
interface CaptureFilter {
|
|
266
|
+
readonly source?: string | undefined;
|
|
267
|
+
readonly kind?: string | undefined;
|
|
268
|
+
/** This value is an ISO 8601 time. The scan gives the records received at this time or later. */
|
|
269
|
+
readonly since?: string | undefined;
|
|
270
|
+
}
|
|
271
|
+
/** A damaged line is a line of the log that is not a record: a torn append, or bytes that a crash left. */
|
|
272
|
+
interface DamagedLine {
|
|
273
|
+
/** This value is the path of the file. */
|
|
274
|
+
readonly path: string;
|
|
275
|
+
/** This value is the number of the line in the file, from 1. */
|
|
276
|
+
readonly line: number;
|
|
277
|
+
}
|
|
278
|
+
/** These options change a scan. */
|
|
279
|
+
interface ScanOptions {
|
|
280
|
+
/** The scan skips a damaged line, and it gives the line to this function. */
|
|
281
|
+
readonly onDamaged?: ((damaged: DamagedLine) => void) | undefined;
|
|
282
|
+
}
|
|
283
|
+
/** The read side of the capture log is what `reindex` replays. */
|
|
284
|
+
interface CaptureReader {
|
|
285
|
+
scan(filter?: CaptureFilter, options?: ScanOptions): AsyncIterable<CaptureRecord>;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* These options change a capture log.
|
|
289
|
+
*
|
|
290
|
+
* @beta
|
|
291
|
+
*/
|
|
292
|
+
interface CaptureLogOptions {
|
|
293
|
+
/**
|
|
294
|
+
* When this value is true (the default), the log writes each batch of appends to the disk
|
|
295
|
+
* (`fdatasync`) before the appends finish. Thus a power cut does not lose a record that the
|
|
296
|
+
* crawler reported.
|
|
297
|
+
*/
|
|
298
|
+
readonly sync?: boolean | undefined;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* The capture log keeps each fetch decision as one line of JSON in
|
|
302
|
+
* `<dir>/<source>/<yyyy>/<mm>.ndjson`. It is the record of each access that the crawler makes.
|
|
303
|
+
*
|
|
304
|
+
* These are the rules of the log:
|
|
305
|
+
*
|
|
306
|
+
* - The log appends a batch of records with one write, and it writes the batch to the disk before
|
|
307
|
+
* the appends finish.
|
|
308
|
+
* - An append always starts on a new line. After a crash leaves a partial line, the next append
|
|
309
|
+
* starts after a line break. The partial line stays as a damaged line, and the record is good.
|
|
310
|
+
* - A scan reads each file as a stream. It skips a damaged line, counts it, and continues.
|
|
311
|
+
* - The log does not remove or change a line.
|
|
312
|
+
*
|
|
313
|
+
* Two processes can append to one log, because each batch is one write to a file in append mode.
|
|
314
|
+
*
|
|
315
|
+
* @beta
|
|
316
|
+
*/
|
|
317
|
+
export declare class CaptureLog implements CaptureReader {
|
|
318
|
+
readonly dir: string;
|
|
319
|
+
private readonly handles;
|
|
320
|
+
private readonly pending;
|
|
321
|
+
private draining;
|
|
322
|
+
private readonly sync;
|
|
323
|
+
constructor(dir: string, options?: CaptureLogOptions);
|
|
324
|
+
private pathOf;
|
|
325
|
+
private handleFor;
|
|
326
|
+
/** This method tells if the last byte of a file is not a line break: a partial line that a crash left. */
|
|
327
|
+
private static needsBreak;
|
|
328
|
+
append(record: CaptureRecord): Promise<void>;
|
|
329
|
+
/** This method starts the writer when it is not in progress. A record that comes as the writer stops starts it again. */
|
|
330
|
+
private kick;
|
|
331
|
+
/** This method writes the waiting records in batches: one write and one sync for each file. */
|
|
332
|
+
private drain;
|
|
333
|
+
/** This method waits until each append in progress is on the disk. */
|
|
334
|
+
flush(): Promise<void>;
|
|
335
|
+
/** This method gives the record files of one source, in the sequence of time. It skips the months before `since`. */
|
|
336
|
+
private filesOf;
|
|
337
|
+
/** This method reads the records of one source in the sequence of the files. */
|
|
338
|
+
private readSource;
|
|
339
|
+
/**
|
|
340
|
+
* This method reads the records that agree with the filter. It first waits for the appends that
|
|
341
|
+
* are in progress. It reads each file as a stream, so its memory does not grow with the log.
|
|
342
|
+
*
|
|
343
|
+
* The records of one source come in the sequence of the files. The records of two or more sources
|
|
344
|
+
* merge in the sequence of `received_at`.
|
|
345
|
+
*/
|
|
346
|
+
scan(filter?: CaptureFilter, options?: ScanOptions): AsyncIterable<CaptureRecord>;
|
|
347
|
+
/** This method writes the appends in progress, then closes the files. A later append opens them again. */
|
|
348
|
+
close(): Promise<void>;
|
|
349
|
+
}
|
|
350
|
+
//#endregion
|
|
351
|
+
//#region src/runtime.d.ts
|
|
352
|
+
/**
|
|
353
|
+
* An extractor changes one captured body into the documents that it holds. It has no side effects,
|
|
354
|
+
* so `reindex` can replay each capture through it and build the index again with no request. It can
|
|
355
|
+
* answer later, because the crawler reads a PDF asynchronously.
|
|
356
|
+
*
|
|
357
|
+
* An extractor that throws does not stop a fetch, a render or a rebuild. The runtime records the
|
|
358
|
+
* error and gives no document for that capture.
|
|
359
|
+
*/
|
|
360
|
+
type Extractor = (body: Uint8Array, capture: {
|
|
361
|
+
readonly url: string;
|
|
362
|
+
readonly contentType: string | null;
|
|
363
|
+
}) => ReadonlyArray<ExtractedDoc> | Promise<ReadonlyArray<ExtractedDoc>>;
|
|
364
|
+
/** @beta */
|
|
365
|
+
export declare class Extractors {
|
|
366
|
+
private readonly byRecipe;
|
|
367
|
+
private readonly replaceable;
|
|
368
|
+
register(recipe: string, extractor: Extractor): void;
|
|
369
|
+
get(recipe: string): Extractor | undefined;
|
|
370
|
+
/** This method gives the names of the recipes that have an extractor. */
|
|
371
|
+
recipes(): string[];
|
|
372
|
+
}
|
|
373
|
+
/** @beta */
|
|
374
|
+
interface CrawlRuntime {
|
|
375
|
+
readonly crawler: Crawler;
|
|
376
|
+
/** This index keeps the documents and the cache entries. `DocIndex` is the SQLite index. */
|
|
377
|
+
readonly index: ReadingIndex;
|
|
378
|
+
readonly blobs: BlobStore;
|
|
379
|
+
/** This reader is the read side of the capture log. The function `reindex` replays it. */
|
|
380
|
+
readonly captures: CaptureReader;
|
|
381
|
+
/** This log is the write side of the capture log. */
|
|
382
|
+
readonly log: CaptureLog;
|
|
383
|
+
readonly extractors: Extractors;
|
|
384
|
+
readonly now: () => string;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* This is the payload of a capture record, as the crawler writes it.
|
|
388
|
+
*
|
|
389
|
+
* @beta
|
|
390
|
+
*/
|
|
391
|
+
interface CapturePayload {
|
|
392
|
+
readonly url: string;
|
|
393
|
+
readonly recipe: string;
|
|
394
|
+
readonly mode: "api" | "page";
|
|
395
|
+
readonly status?: number;
|
|
396
|
+
readonly outcome?: string;
|
|
397
|
+
readonly blob?: string | null;
|
|
398
|
+
readonly content_type?: string | null;
|
|
399
|
+
readonly etag?: string | null;
|
|
400
|
+
readonly last_modified?: string | null;
|
|
401
|
+
readonly fetched_at: string;
|
|
402
|
+
readonly signals?: {
|
|
403
|
+
readonly ai_input?: AiInput;
|
|
404
|
+
};
|
|
405
|
+
/** This value is true when the headless browser made the capture, not a plain request. */
|
|
406
|
+
readonly rendered?: boolean;
|
|
407
|
+
/** This value is true when the body is only the first part of the response. */
|
|
408
|
+
readonly truncated?: boolean;
|
|
409
|
+
/** This value is true when the server cut a compressed body. The capture is not a copy to keep. */
|
|
410
|
+
readonly cut?: boolean;
|
|
411
|
+
/** This value is true when an API answered with a web page that the request did not accept. The capture is not a copy to keep. */
|
|
412
|
+
readonly unasked?: boolean;
|
|
413
|
+
/** This time (ISO 8601) is the end of the freshness that the server gave. Captures before version 0.2 do not have it. */
|
|
414
|
+
readonly fresh_until?: string | null;
|
|
415
|
+
/** This text holds the request headers that the response varies on. Captures before version 0.2 do not have it. */
|
|
416
|
+
readonly vary_key?: string | null;
|
|
417
|
+
/** This URL is the target of a redirect, with its secrets as `REDACTED`. */
|
|
418
|
+
readonly location?: string | null;
|
|
419
|
+
readonly note?: string;
|
|
420
|
+
/** This text is the byte range of a range request (`bytes=0-99`). The capture is a part of the resource, not its stored copy. */
|
|
421
|
+
readonly range?: string;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* The note of a document that is held back.
|
|
425
|
+
*
|
|
426
|
+
* @beta
|
|
427
|
+
*/
|
|
428
|
+
export declare const HELD_BACK = "This page asked not to be used as AI input, so its text is held back. Open the URL to read it, or read again with userProvided when a person gave this URL directly (the crawler logs that).";
|
|
429
|
+
/**
|
|
430
|
+
* This function tells if a document is held back: the page asked not to be AI input, and no person
|
|
431
|
+
* gave its URL directly. The only way to read a held-back document is `userProvided`.
|
|
432
|
+
*
|
|
433
|
+
* @beta
|
|
434
|
+
*/
|
|
435
|
+
export declare const isHeldBack: (doc: {
|
|
436
|
+
readonly ai_input: AiInput;
|
|
437
|
+
}, userProvided?: boolean) => boolean;
|
|
438
|
+
/**
|
|
439
|
+
* This function gives the metadata that a held-back document shows: only the structural fields, which hold no text of the page.
|
|
440
|
+
*
|
|
441
|
+
* @beta
|
|
442
|
+
*/
|
|
443
|
+
export declare const heldBackMeta: (meta: Readonly<Record<string, unknown>>) => Record<string, unknown>;
|
|
444
|
+
/**
|
|
445
|
+
* The result of `fetchAndIndex` and `renderAndIndex`.
|
|
446
|
+
*
|
|
447
|
+
* @beta
|
|
448
|
+
*/
|
|
449
|
+
interface Indexed {
|
|
450
|
+
readonly fetched: Fetched;
|
|
451
|
+
readonly docs: readonly Doc[];
|
|
452
|
+
/** This text tells why the capture gave no document: the error of the extractor, or a bot check. */
|
|
453
|
+
readonly error?: string | undefined;
|
|
454
|
+
/** This value names the bot check or the firewall that sent its page in place of the content. */
|
|
455
|
+
readonly blocked?: string | undefined;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* This function fetches one URL and adds its documents to the index. Each recipe uses it. A fetch
|
|
459
|
+
* from the cache gives the documents that the index has for that capture. Thus a repeated
|
|
460
|
+
* question sends no request and reads nothing again. An extractor that throws gives no document and an
|
|
461
|
+
* `error`, and the fetch still succeeds.
|
|
462
|
+
*
|
|
463
|
+
* @beta
|
|
464
|
+
*/
|
|
465
|
+
export declare const fetchAndIndex: (runtime: CrawlRuntime, request: FetchRequest) => Promise<Indexed>;
|
|
466
|
+
interface PageResult {
|
|
467
|
+
readonly url: string;
|
|
468
|
+
readonly doc: string | null;
|
|
469
|
+
readonly outcome: string;
|
|
470
|
+
readonly status: number;
|
|
471
|
+
/** This value is the title of the page. A held-back page shows no title. */
|
|
472
|
+
readonly title: string | null;
|
|
473
|
+
readonly chars: number;
|
|
474
|
+
readonly rendered: boolean;
|
|
475
|
+
/** These are the schema.org types that the JSON-LD of the page declares. */
|
|
476
|
+
readonly structured: readonly string[];
|
|
477
|
+
readonly links: number;
|
|
478
|
+
/** These ids are the captures behind the current text, the newest first, 10 or fewer. */
|
|
479
|
+
readonly captures: readonly string[];
|
|
480
|
+
/** This id is the capture of the text that the document shows. A citation names it. */
|
|
481
|
+
readonly capture: string | null;
|
|
482
|
+
/** This value is the number of captures of the document. */
|
|
483
|
+
readonly capture_count: number;
|
|
484
|
+
/** This time (ISO 8601) is the fetch time of the text that the document shows. */
|
|
485
|
+
readonly fetched_at: string | null;
|
|
486
|
+
/** This value is true when the stored copy answered, and the crawler sent no request. */
|
|
487
|
+
readonly from_cache: boolean;
|
|
488
|
+
/** This value is true when the page asked not to be AI input. Its title and text are then held back. */
|
|
489
|
+
readonly held_back: boolean;
|
|
490
|
+
readonly note: string | null;
|
|
491
|
+
/**
|
|
492
|
+
* This value tells that the document is an archived copy, not a fresh fetch: the archive, the date
|
|
493
|
+
* of the copy and its URL there. The outcome and the status stay those of the live fetch.
|
|
494
|
+
*/
|
|
495
|
+
readonly archived?: Archived | undefined;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* These options control one page.
|
|
499
|
+
*
|
|
500
|
+
* @beta
|
|
501
|
+
*/
|
|
502
|
+
interface PageOptions {
|
|
503
|
+
readonly render: "auto" | "never" | "always";
|
|
504
|
+
readonly maxAgeMs?: number | "auto" | undefined;
|
|
505
|
+
readonly onBehalfOf?: readonly string[] | undefined;
|
|
506
|
+
readonly job?: string | undefined;
|
|
507
|
+
/** When this signal aborts, the crawler stops the fetch and the render. */
|
|
508
|
+
readonly signal?: AbortSignal | undefined;
|
|
509
|
+
readonly scope?: string | undefined;
|
|
510
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
511
|
+
/** This time (milliseconds since the epoch) is the end of a budget. No request of the page starts at or after it (`FetchRequest.deadline`). */
|
|
512
|
+
readonly deadline?: number | undefined;
|
|
513
|
+
/** The value `archive` gives a copy from a public web archive when the site refuses the page, or is down. The default is `none`. */
|
|
514
|
+
readonly fallback?: "archive" | "none" | undefined;
|
|
515
|
+
/** The value true also gives an archived copy of a page that the current robots.txt of its site disallows. The default is false. */
|
|
516
|
+
readonly archiveDespiteRobots?: boolean | undefined;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* This function reads one web page. It first sends a plain request. It opens the browser only when
|
|
520
|
+
* the page shows almost nothing without JavaScript, or when the caller asks for it. It renders
|
|
521
|
+
* only an HTML page: not a PDF, a different file or a bot check. A page that sends the reader on
|
|
522
|
+
* with a `<meta http-equiv="refresh">` gets one more plain fetch, of its target.
|
|
523
|
+
*
|
|
524
|
+
* @beta
|
|
525
|
+
*/
|
|
526
|
+
export declare const fetchPage: (runtime: CrawlRuntime, url: string, options: PageOptions) => Promise<PageResult>;
|
|
527
|
+
/** This result is a part of one stored document. */
|
|
528
|
+
interface ReadResult {
|
|
529
|
+
readonly doc: string;
|
|
530
|
+
readonly kind: string;
|
|
531
|
+
/** This value is the title of the document. A held-back document shows no title. */
|
|
532
|
+
readonly title: string;
|
|
533
|
+
readonly url: string | null;
|
|
534
|
+
/** This value is the metadata of the document. A held-back document shows only the structural fields. */
|
|
535
|
+
readonly meta: Readonly<Record<string, unknown>>;
|
|
536
|
+
readonly from: number;
|
|
537
|
+
readonly to: number;
|
|
538
|
+
readonly total: number;
|
|
539
|
+
readonly text: string;
|
|
540
|
+
readonly captures: readonly string[];
|
|
541
|
+
/** This id is the capture of the text that the result reads. */
|
|
542
|
+
readonly capture: string;
|
|
543
|
+
/** This time (ISO 8601) is the fetch time of the text that the result reads. */
|
|
544
|
+
readonly fetched_at: string;
|
|
545
|
+
readonly ai_input: AiInput;
|
|
546
|
+
/** This value is true when the page asked not to be AI input. The text is then empty. */
|
|
547
|
+
readonly held_back: boolean;
|
|
548
|
+
readonly note: string | null;
|
|
549
|
+
/** This time (ISO 8601) is the `at` of the read. The result has it only when the read gave `at`. */
|
|
550
|
+
readonly at?: string | undefined;
|
|
551
|
+
/**
|
|
552
|
+
* This value is true when `at` is before the first capture of the document. Then no version
|
|
553
|
+
* answers: the text, the title, `capture` and `fetched_at` are empty, and the note tells why.
|
|
554
|
+
*/
|
|
555
|
+
readonly before_first?: boolean | undefined;
|
|
556
|
+
/** This value tells that the text is an archived copy, with the archive and the date of the copy. */
|
|
557
|
+
readonly archived?: Archived | undefined;
|
|
558
|
+
}
|
|
559
|
+
/** This result tells if a quote is in a stored document, and where. */
|
|
560
|
+
interface VerifyResult {
|
|
561
|
+
readonly doc: string;
|
|
562
|
+
/** This value is true when the quote is in the text. A held-back document gives false. */
|
|
563
|
+
readonly found: boolean;
|
|
564
|
+
/** These values are the positions of the quote in the text of `capture`. */
|
|
565
|
+
readonly start: number | null;
|
|
566
|
+
readonly end: number | null;
|
|
567
|
+
readonly excerpt: string | null;
|
|
568
|
+
readonly url: string | null;
|
|
569
|
+
readonly captures: readonly string[];
|
|
570
|
+
/** This id is the capture whose text holds the quote, or the capture that the result examined. */
|
|
571
|
+
readonly capture: string | null;
|
|
572
|
+
/** This hash is the blob of that capture. */
|
|
573
|
+
readonly blob: string | null;
|
|
574
|
+
/** This time (ISO 8601) is the fetch time of that capture. */
|
|
575
|
+
readonly fetched_at: string | null;
|
|
576
|
+
/** This value is true when only an earlier version of the document holds the quote. */
|
|
577
|
+
readonly stale: boolean;
|
|
578
|
+
/** This value is true when the quote is in the whole text of a page that the reader cut. The positions are then in that whole text. */
|
|
579
|
+
readonly in_full_text: boolean;
|
|
580
|
+
/** This value is false when the examined text is not complete (a PDF that the reader stopped). A quote that is not found can then be in the file. */
|
|
581
|
+
readonly complete: boolean;
|
|
582
|
+
/** This value is the number of places where the quote is. */
|
|
583
|
+
readonly count: number;
|
|
584
|
+
readonly held_back: boolean;
|
|
585
|
+
readonly note: string | null;
|
|
586
|
+
/** This time (ISO 8601) is the `at` of the verification. The result has it only when the verification gave `at`. */
|
|
587
|
+
readonly at?: string | undefined;
|
|
588
|
+
/** This value is true when `at` is before the first capture of the document. Then no version holds the quote, and the note tells why. */
|
|
589
|
+
readonly before_first?: boolean | undefined;
|
|
590
|
+
/**
|
|
591
|
+
* This value tells that the examined text is an archived copy, with the archive and the date of
|
|
592
|
+
* the copy. A citation of it names the archive and that date, not a date of a fresh fetch.
|
|
593
|
+
*/
|
|
594
|
+
readonly archived?: Archived | undefined;
|
|
595
|
+
}
|
|
596
|
+
/** These options control a read. */
|
|
597
|
+
interface ReadOptions {
|
|
598
|
+
/** This value is the first character to read. The default is 0. */
|
|
599
|
+
readonly from?: number | undefined;
|
|
600
|
+
/** This value is the number of characters to read. The default is 4000. */
|
|
601
|
+
readonly length?: number | undefined;
|
|
602
|
+
/** This value is true when a person gave this URL directly. It is the only way to read a held-back document. The crawler logs it. */
|
|
603
|
+
readonly userProvided?: boolean | undefined;
|
|
604
|
+
/** This id reads the version of one capture, not the current version. */
|
|
605
|
+
readonly capture?: string | undefined;
|
|
606
|
+
/**
|
|
607
|
+
* This time reads the version that was current at that time: an ISO 8601 text or a `Date`. A
|
|
608
|
+
* date with no time is the start of that day in UTC. Give `capture` or `at`, not the two.
|
|
609
|
+
*/
|
|
610
|
+
readonly at?: string | Date | undefined;
|
|
611
|
+
}
|
|
612
|
+
/** These options control a verification. */
|
|
613
|
+
interface VerifyOptions {
|
|
614
|
+
/** This id examines the version of one capture only. A citation of that capture thus stays true after a new fetch. */
|
|
615
|
+
readonly capture?: string | undefined;
|
|
616
|
+
/** This value is true when a person gave this URL directly. It is the only way to verify a quote in a held-back document. */
|
|
617
|
+
readonly userProvided?: boolean | undefined;
|
|
618
|
+
/** This time examines only the version that was current at that time (`ReadOptions.at`). Give `capture` or `at`, not the two. */
|
|
619
|
+
readonly at?: string | Date | undefined;
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* This function gives the ISO 8601 form of a time. A text that is not a time throws a `RangeError`.
|
|
623
|
+
* A date with no time is the start of that day in UTC.
|
|
624
|
+
*/
|
|
625
|
+
export declare const isoTime: (name: string, value: string | Date) => string;
|
|
626
|
+
/**
|
|
627
|
+
* This function gives the version of a document that was current at a time (ISO 8601). It uses
|
|
628
|
+
* `ReadingIndex.versionAt` when the index has it. Else it takes the newest version at or before
|
|
629
|
+
* the time.
|
|
630
|
+
*
|
|
631
|
+
* @beta
|
|
632
|
+
*/
|
|
633
|
+
export declare const versionAtTime: (runtime: CrawlRuntime, id: string, at: string) => VersionAt;
|
|
634
|
+
/**
|
|
635
|
+
* This function writes an `override` record to the capture log. A person gave the URL of a
|
|
636
|
+
* held-back document directly, and the hold stops for one action (`read` or `verify`). The record
|
|
637
|
+
* takes the scope and the labels of `marks`, or else those of the document, as its captures do.
|
|
638
|
+
*
|
|
639
|
+
* @beta
|
|
640
|
+
*/
|
|
641
|
+
export declare const recordOverride: (runtime: CrawlRuntime, doc: {
|
|
642
|
+
readonly doc: string;
|
|
643
|
+
readonly url: string | null;
|
|
644
|
+
readonly scope?: string | undefined;
|
|
645
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
646
|
+
}, action: string, marks?: {
|
|
647
|
+
readonly scope?: string | undefined;
|
|
648
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
649
|
+
}) => Promise<void>;
|
|
650
|
+
/**
|
|
651
|
+
* This function reads a range of characters of one stored document. It applies the rule for a
|
|
652
|
+
* held-back document: no title, no text, and only the structural metadata, unless `userProvided`.
|
|
653
|
+
* The positions do not cut a character in two.
|
|
654
|
+
*
|
|
655
|
+
* @beta
|
|
656
|
+
*/
|
|
657
|
+
export declare const readDocument: (runtime: CrawlRuntime, id: string, options?: ReadOptions) => Promise<ReadResult>;
|
|
658
|
+
/**
|
|
659
|
+
* This function finds a quote in a stored document, with `locate`. It tells which capture holds the
|
|
660
|
+
* quote, with its blob, its fetch time and the positions:
|
|
661
|
+
*
|
|
662
|
+
* - With `capture`, it examines the version of that capture only.
|
|
663
|
+
* - With no capture, it examines the current text first. When the quote is not there, it examines
|
|
664
|
+
* the earlier texts, the newest first. A match there has `stale: true`.
|
|
665
|
+
* - When the reader cut a text, it also examines the whole text.
|
|
666
|
+
*
|
|
667
|
+
* A held-back document gives `found: false` and no positions, so a quote cannot test its text,
|
|
668
|
+
* unless `userProvided`. The same rule applies to each version. A version that asked not to be AI
|
|
669
|
+
* input stays held back after a later capture drops the signal. The crawler logs an override when
|
|
670
|
+
* `userProvided` opens a held-back version.
|
|
671
|
+
*
|
|
672
|
+
* @beta
|
|
673
|
+
*/
|
|
674
|
+
export declare const verifyQuote: (runtime: CrawlRuntime, id: string, quote: string, options?: VerifyOptions) => Promise<VerifyResult>;
|
|
675
|
+
/**
|
|
676
|
+
* This function searches the stored documents. The syntax of the query is the syntax of
|
|
677
|
+
* `parseQuery`. A held-back document is not in the results. The option `rank` chooses the
|
|
678
|
+
* ranking (`RankMode`), and the default is `exact`.
|
|
679
|
+
*
|
|
680
|
+
* @beta
|
|
681
|
+
*/
|
|
682
|
+
export declare const findDocuments: (runtime: CrawlRuntime, q: string, options?: {
|
|
683
|
+
readonly limit?: number | undefined;
|
|
684
|
+
readonly kind?: string | null | undefined;
|
|
685
|
+
readonly rank?: RankMode | undefined;
|
|
686
|
+
}) => Promise<Found[]>;
|
|
687
|
+
/** The counts of a rebuild. */
|
|
688
|
+
interface ReindexResult {
|
|
689
|
+
/** This value is the number of captures that the rebuild read again. */
|
|
690
|
+
readonly captures: number;
|
|
691
|
+
readonly docs: number;
|
|
692
|
+
readonly urls: number;
|
|
693
|
+
/** This value is the number of captures whose extractor threw. */
|
|
694
|
+
readonly failed: number;
|
|
695
|
+
/** This value is the number of captures whose blob is missing or damaged. */
|
|
696
|
+
readonly missing: number;
|
|
697
|
+
/** This value is the number of damaged lines of the log that the rebuild skipped. */
|
|
698
|
+
readonly damaged: number;
|
|
699
|
+
/** This value is the number of documents of recipes with no extractor, kept from the old index. */
|
|
700
|
+
readonly carried: number;
|
|
701
|
+
/** These recipes have captures in the log and no extractor in this runtime. */
|
|
702
|
+
readonly unknown_recipes: readonly string[];
|
|
703
|
+
/** These are the first errors of the extractors. */
|
|
704
|
+
readonly errors: ReadonlyArray<{
|
|
705
|
+
readonly capture: string;
|
|
706
|
+
readonly error: string;
|
|
707
|
+
}>;
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* This function builds the reading index again from the capture log and the blobs. The index is a
|
|
711
|
+
* projection, and this function is the proof that its deletion loses nothing. These are its rules:
|
|
712
|
+
*
|
|
713
|
+
* - It builds a new generation of the tables and commits it in batches. The old index answers
|
|
714
|
+
* until the end, and a failed rebuild leaves it as it was.
|
|
715
|
+
* - It reads the log as a stream, so its memory does not grow with the log.
|
|
716
|
+
* - It counts and skips a damaged line, a missing blob and an extractor that throws.
|
|
717
|
+
* - The documents of a recipe that has no extractor in this runtime stay, from the old index.
|
|
718
|
+
* - A capture that a different writer adds during the rebuild goes into the new index.
|
|
719
|
+
*
|
|
720
|
+
* @beta
|
|
721
|
+
*/
|
|
722
|
+
export declare const reindex: (runtime: CrawlRuntime, options?: {
|
|
723
|
+
readonly batch?: number | undefined;
|
|
724
|
+
}) => Promise<ReindexResult>;
|
|
725
|
+
/**
|
|
726
|
+
* The result of `catchUpIndex`.
|
|
727
|
+
*
|
|
728
|
+
* @beta
|
|
729
|
+
*/
|
|
730
|
+
interface CatchUpResult extends ReindexResult {
|
|
731
|
+
/**
|
|
732
|
+
* The value `rebuild` tells that the index was stale, and the function built it again from the
|
|
733
|
+
* whole log. The value `tail` tells that it read only the captures that the index missed.
|
|
734
|
+
*/
|
|
735
|
+
readonly mode: "rebuild" | "tail";
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* This function brings the reading index up to the capture log, with no request. `openCrawler`
|
|
739
|
+
* uses it at open. An application that uses `createRuntime` uses it the same way:
|
|
740
|
+
*
|
|
741
|
+
* - A stale index (a new schema, or a deleted file) is built again with `reindex`.
|
|
742
|
+
* - An index that is behind the log gets the captures that it missed. A process can stop between
|
|
743
|
+
* the log and the index, and SQLite can lose its last transactions after a power cut. The
|
|
744
|
+
* function reads the log from a short time before the newest cache entry.
|
|
745
|
+
*
|
|
746
|
+
* It gives the counts, or undefined when the index missed nothing.
|
|
747
|
+
*
|
|
748
|
+
* @beta
|
|
749
|
+
*/
|
|
750
|
+
export declare const catchUpIndex: (runtime: CrawlRuntime) => Promise<CatchUpResult | undefined>;
|
|
751
|
+
//#endregion
|
|
752
|
+
//#region src/fallback.d.ts
|
|
753
|
+
/**
|
|
754
|
+
* The archived fallback. When a site refuses a page, or is down, the crawler can give a copy from a
|
|
755
|
+
* public web archive: the Wayback Machine, then Common Crawl. The crawler does not show the copy as a fresh fetch:
|
|
756
|
+
*
|
|
757
|
+
* - It is a document of its own (`archive:<url>`), of the kind `archived`. Its metadata and each
|
|
758
|
+
* result that shows it have `archived`: the archive, the date of the copy and its URL there.
|
|
759
|
+
* - The result keeps the outcome of the live fetch (`blocked`, for example), and its note names the
|
|
760
|
+
* archive and the date.
|
|
761
|
+
*
|
|
762
|
+
* The fallback is opt-in (`fallback: "archive"`). The archives are third-party APIs with their own
|
|
763
|
+
* politeness, so each request to them is a fetch of the same crawler. Their robots.txt, the pace of
|
|
764
|
+
* each host, the egress policy and the identity of the crawler apply, and the capture log records
|
|
765
|
+
* each request.
|
|
766
|
+
*
|
|
767
|
+
* By default, the crawler gives no archived copy of a page that the current robots.txt of its site
|
|
768
|
+
* disallows for this agent. Only a stop, an error or a dead host gets the fallback.
|
|
769
|
+
* `archiveDespiteRobots` widens it. The usage signals of the copy apply as for a live page.
|
|
770
|
+
*/
|
|
771
|
+
/** The public web archives of the fallback. */
|
|
772
|
+
type WebArchive = "wayback" | "commoncrawl";
|
|
773
|
+
/** An archived copy: the archive, the time when the archive captured the page, and where the copy is. */
|
|
774
|
+
interface Archived {
|
|
775
|
+
readonly source: WebArchive;
|
|
776
|
+
/** This time (ISO 8601) is when the archive captured the page. It is not the time of the fetch from the archive. */
|
|
777
|
+
readonly timestamp: string;
|
|
778
|
+
/** This URL is the copy in the archive: a Wayback Machine page, or the Common Crawl WARC file. */
|
|
779
|
+
readonly url: string;
|
|
780
|
+
/** This URL is the page that the archive captured. */
|
|
781
|
+
readonly original: string;
|
|
782
|
+
/** This id is the `WARC-Record-ID` of a Common Crawl copy in its WARC file. */
|
|
783
|
+
readonly record?: string | undefined;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* This function gives the document id of the archived copy of a page.
|
|
787
|
+
*
|
|
788
|
+
* @beta
|
|
789
|
+
*/
|
|
790
|
+
export declare const archivedDocId: (url: string) => string;
|
|
791
|
+
/**
|
|
792
|
+
* This extractor reads the captures of the recipe `archive-copy`. It has no side effects, so
|
|
793
|
+
* `reindex` replays them. It reads two forms:
|
|
794
|
+
*
|
|
795
|
+
* - A Wayback Machine copy in the raw form (`/web/<time>id_/<url>`). The body is the body of the page.
|
|
796
|
+
* - A Common Crawl WARC record (a gzip member). It reads the HTTP headers and the body from the record.
|
|
797
|
+
*
|
|
798
|
+
* It reads the body with the page reader, and gives a document of the kind `archived` with
|
|
799
|
+
* `archived` in its metadata. The usage signals of the HTTP headers in a WARC record apply too.
|
|
800
|
+
*
|
|
801
|
+
* @beta
|
|
802
|
+
*/
|
|
803
|
+
export declare const archiveExtractor: Extractor;
|
|
804
|
+
//#endregion
|
|
805
|
+
//#region src/fold.d.ts
|
|
806
|
+
/** @beta */
|
|
807
|
+
interface Verified {
|
|
808
|
+
readonly found: boolean;
|
|
809
|
+
/** These values are the character positions in the text of the document, when the quote is there. */
|
|
810
|
+
readonly start: number | null;
|
|
811
|
+
readonly end: number | null;
|
|
812
|
+
/** This text is the exact quote as stored, with some text before and after it. */
|
|
813
|
+
readonly excerpt: string | null;
|
|
814
|
+
/** This value is the number of places where the quote is, with no overlap. */
|
|
815
|
+
readonly count: number;
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* This function finds a quote in a text, in the `quote` mode of `foldText`. It ignores case, runs
|
|
819
|
+
* of spaces, typographic punctuation, soft hyphens, zero-width characters and the differences of
|
|
820
|
+
* NFKC, NFC and NFD. The positions are in the original text.
|
|
821
|
+
*
|
|
822
|
+
* @beta
|
|
823
|
+
*/
|
|
824
|
+
export declare const locate: (text: string, quote: string) => Verified;
|
|
825
|
+
//#endregion
|
|
826
|
+
//#region src/docs.d.ts
|
|
827
|
+
/** This is one document, as an extractor gives it. */
|
|
828
|
+
interface ExtractedDoc {
|
|
829
|
+
readonly doc: string;
|
|
830
|
+
readonly kind: string;
|
|
831
|
+
readonly title: string;
|
|
832
|
+
/** This text is the readable text. For a paper, it is the title and the abstract. */
|
|
833
|
+
readonly text: string;
|
|
834
|
+
readonly url: string | null;
|
|
835
|
+
readonly meta: Readonly<Record<string, unknown>>;
|
|
836
|
+
/** This rule chooses between the texts of two recipes. The default is `richest`: the longer text. */
|
|
837
|
+
readonly merge?: "latest" | "richest" | undefined;
|
|
838
|
+
/** When the extractor cut the text, this text is the whole text. The function `verify` looks for a quote here too. */
|
|
839
|
+
readonly full_text?: string | undefined;
|
|
840
|
+
/**
|
|
841
|
+
* The value `no` holds the document back from the AI reader, also when the capture said nothing.
|
|
842
|
+
* An extractor sets it when the body states it, for example the headers in an archived response.
|
|
843
|
+
*
|
|
844
|
+
* @beta
|
|
845
|
+
*/
|
|
846
|
+
readonly ai_input?: AiInput | undefined;
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* This is one version of a document: the document as one capture gave it.
|
|
850
|
+
*
|
|
851
|
+
* @beta
|
|
852
|
+
*/
|
|
853
|
+
interface DocInput extends ExtractedDoc {
|
|
854
|
+
/** These ids are the capture records that this version came from. The first id names the version. */
|
|
855
|
+
readonly captures: readonly string[];
|
|
856
|
+
readonly ai_input?: AiInput | undefined;
|
|
857
|
+
/** This id is the capture of the version. The default is the first id of `captures`. */
|
|
858
|
+
readonly capture?: string | undefined;
|
|
859
|
+
/** This time (ISO 8601) is the time of the fetch. The default is the time of the upsert. */
|
|
860
|
+
readonly fetched_at?: string | undefined;
|
|
861
|
+
readonly blob?: string | null | undefined;
|
|
862
|
+
/** This value is the recipe that read the capture. A version with no recipe has the recipe `""`. */
|
|
863
|
+
readonly recipe?: string | undefined;
|
|
864
|
+
/** This value is true when the headless browser made the capture. */
|
|
865
|
+
readonly rendered?: boolean | undefined;
|
|
866
|
+
/** This label comes from the capture. The index writes it as it is and does not read it. */
|
|
867
|
+
readonly scope?: string | undefined;
|
|
868
|
+
/** These labels come from the capture. The index writes them as they are and does not read them. */
|
|
869
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
870
|
+
}
|
|
871
|
+
/** This is the current version of a document. */
|
|
872
|
+
interface Doc {
|
|
873
|
+
readonly doc: string;
|
|
874
|
+
readonly kind: string;
|
|
875
|
+
readonly title: string;
|
|
876
|
+
readonly text: string;
|
|
877
|
+
readonly url: string | null;
|
|
878
|
+
readonly meta: Readonly<Record<string, unknown>>;
|
|
879
|
+
/** These ids are the captures behind the current text, the newest first, `MAX_CAPTURES` or fewer. */
|
|
880
|
+
readonly captures: readonly string[];
|
|
881
|
+
/** This value is the number of versions of the document. */
|
|
882
|
+
readonly capture_count: number;
|
|
883
|
+
/** This id is the capture of the current text. */
|
|
884
|
+
readonly capture: string;
|
|
885
|
+
readonly blob: string | null;
|
|
886
|
+
/** This time (ISO 8601) is the fetch time of the current text. */
|
|
887
|
+
readonly fetched_at: string;
|
|
888
|
+
readonly recipe: string;
|
|
889
|
+
readonly rendered: boolean;
|
|
890
|
+
readonly ai_input: AiInput;
|
|
891
|
+
/** This value is true when the extractor cut the text, and the index keeps the whole text for `verify`. */
|
|
892
|
+
readonly cut: boolean;
|
|
893
|
+
readonly scope?: string | undefined;
|
|
894
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
895
|
+
readonly updated_at: string;
|
|
896
|
+
}
|
|
897
|
+
/** This is one version of a document, with no text. */
|
|
898
|
+
interface DocVersion {
|
|
899
|
+
readonly doc: string;
|
|
900
|
+
readonly capture: string;
|
|
901
|
+
readonly recipe: string;
|
|
902
|
+
readonly rendered: boolean;
|
|
903
|
+
readonly fetched_at: string;
|
|
904
|
+
readonly blob: string | null;
|
|
905
|
+
readonly kind: string;
|
|
906
|
+
readonly title: string;
|
|
907
|
+
readonly url: string | null;
|
|
908
|
+
readonly ai_input: AiInput;
|
|
909
|
+
/** This value is the length of the text of the version. */
|
|
910
|
+
readonly chars: number;
|
|
911
|
+
/** This value is true when the index keeps a whole text that is longer than the text. */
|
|
912
|
+
readonly cut: boolean;
|
|
913
|
+
}
|
|
914
|
+
/** This is the text of one version, with its metadata. */
|
|
915
|
+
interface VersionText extends DocVersion {
|
|
916
|
+
readonly text: string;
|
|
917
|
+
/** This text is the whole text when the extractor cut `text`. */
|
|
918
|
+
readonly full_text: string | undefined;
|
|
919
|
+
readonly meta: Readonly<Record<string, unknown>>;
|
|
920
|
+
}
|
|
921
|
+
/** This is the version of a document that was current at a time (`ReadingIndex.versionAt`). */
|
|
922
|
+
interface VersionAt {
|
|
923
|
+
/**
|
|
924
|
+
* This version was current at the time, or it is undefined when the time is before the first
|
|
925
|
+
* version. Its `ai_input` is `no` when the document was held back at that time.
|
|
926
|
+
*/
|
|
927
|
+
readonly version: VersionText | undefined;
|
|
928
|
+
/** This time (ISO 8601) is the fetch time of the first version of the document, or undefined for an unknown document. */
|
|
929
|
+
readonly first: string | undefined;
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* This value chooses the ranking of a search:
|
|
933
|
+
*
|
|
934
|
+
* - `exact` (the default): BM25 ranks each document that matches.
|
|
935
|
+
* - `fast`: the form of the query that needs one part leaves out the common parts. A common part
|
|
936
|
+
* is in more than `FAST_RANK_SHARE` of the documents. The rarest part stays when each part is common.
|
|
937
|
+
*
|
|
938
|
+
* The documents that hold each part come first in the two modes, with the same rank. In the mode
|
|
939
|
+
* `fast`, the rest of the results leaves out a document that holds only common parts. The common
|
|
940
|
+
* parts also do not add to the score of the rest. Thus `fast` is faster for a long question, and
|
|
941
|
+
* its results can be different. The README gives the measured difference.
|
|
942
|
+
*/
|
|
943
|
+
type RankMode = "exact" | "fast";
|
|
944
|
+
/**
|
|
945
|
+
* In the mode `fast`, a part is common when more than this share of the documents hold it.
|
|
946
|
+
*
|
|
947
|
+
* @beta
|
|
948
|
+
*/
|
|
949
|
+
export declare const FAST_RANK_SHARE = .2;
|
|
950
|
+
/**
|
|
951
|
+
* This value chooses the ranking of a search of the crawler: a `RankMode` of the index, or
|
|
952
|
+
* `hybrid`. The mode `hybrid` fuses the BM25 ranking with the ranking of the vectors of an
|
|
953
|
+
* embedder (`OpenOptions.embedder`). Without an embedder, it throws an error.
|
|
954
|
+
*/
|
|
955
|
+
type SearchRank = RankMode | "hybrid";
|
|
956
|
+
/** The text of the error of hybrid search with no embedder. */
|
|
957
|
+
export declare const NO_EMBEDDER = "hybrid search needs an embedder: open the crawler with the option embedder (transformersEmbedder or ollamaEmbedder), or give --embedder transformers or ollama to the command line";
|
|
958
|
+
/**
|
|
959
|
+
* These are the options of a search of the index.
|
|
960
|
+
*
|
|
961
|
+
* @beta
|
|
962
|
+
*/
|
|
963
|
+
interface FindIndexOptions {
|
|
964
|
+
/** This value is the maximum number of results. The index gives 1000 or fewer. */
|
|
965
|
+
readonly limit: number;
|
|
966
|
+
/** This value keeps only the documents of one kind, for example `page` or `pdf`. */
|
|
967
|
+
readonly kind?: string | null | undefined;
|
|
968
|
+
/** This value chooses the ranking. The default is `exact`. */
|
|
969
|
+
readonly rank?: RankMode | undefined;
|
|
970
|
+
}
|
|
971
|
+
/** This is the passage of a hit of a hybrid search: the chunk of the text that is closest to the question. */
|
|
972
|
+
interface Passage {
|
|
973
|
+
/** These values are the positions of the chunk in the text of the capture of the hit, in UTF-16 units. */
|
|
974
|
+
readonly start: number;
|
|
975
|
+
readonly end: number;
|
|
976
|
+
/** This value is the cosine of the vectors of the chunk and the question, from -1 to 1. */
|
|
977
|
+
readonly similarity: number;
|
|
978
|
+
}
|
|
979
|
+
interface Found {
|
|
980
|
+
readonly doc: string;
|
|
981
|
+
readonly kind: string;
|
|
982
|
+
readonly title: string;
|
|
983
|
+
readonly url: string | null;
|
|
984
|
+
readonly snippet: string;
|
|
985
|
+
readonly score: number;
|
|
986
|
+
readonly ai_input: AiInput;
|
|
987
|
+
/** This time (ISO 8601) is the fetch time of the current text. */
|
|
988
|
+
readonly fetched_at: string;
|
|
989
|
+
/** This id is the capture of the current text. */
|
|
990
|
+
readonly capture: string;
|
|
991
|
+
/** This value tells that the document is an archived copy (the kind `archived`), with the archive and the date of the copy. */
|
|
992
|
+
readonly archived?: Archived | undefined;
|
|
993
|
+
/** This value is the passage that a hybrid search found. A search of the other modes does not give it. */
|
|
994
|
+
readonly passage?: Passage | undefined;
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* This is the current text of a document in short: its hash, with no text.
|
|
998
|
+
*
|
|
999
|
+
* @beta
|
|
1000
|
+
*/
|
|
1001
|
+
interface DocDigest {
|
|
1002
|
+
readonly doc: string;
|
|
1003
|
+
readonly kind: string;
|
|
1004
|
+
readonly ai_input: AiInput;
|
|
1005
|
+
/** This value is the SHA-256 of the current text of the document, in hex. */
|
|
1006
|
+
readonly text_sha: string;
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* These options choose the documents of `ReadingIndex.digests`.
|
|
1010
|
+
*
|
|
1011
|
+
* @beta
|
|
1012
|
+
*/
|
|
1013
|
+
interface DigestOptions {
|
|
1014
|
+
/** These documents, in place of a list of each document. A document that is not in the index gives nothing. */
|
|
1015
|
+
readonly docs?: readonly string[] | undefined;
|
|
1016
|
+
/** In a list of each document, the documents with an id after this id, in the sequence of the ids. */
|
|
1017
|
+
readonly after?: string | undefined;
|
|
1018
|
+
/** In a list of each document, this number of documents at most. The default is 500. */
|
|
1019
|
+
readonly limit?: number | undefined;
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* The variant of a cache entry: the plain response, or the page that the browser rendered.
|
|
1023
|
+
*
|
|
1024
|
+
* @beta
|
|
1025
|
+
*/
|
|
1026
|
+
type Variant = "plain" | "rendered";
|
|
1027
|
+
/** @beta */
|
|
1028
|
+
interface Latest {
|
|
1029
|
+
readonly url: string;
|
|
1030
|
+
readonly capture: string;
|
|
1031
|
+
readonly blob: string | null;
|
|
1032
|
+
readonly etag: string | null;
|
|
1033
|
+
readonly last_modified: string | null;
|
|
1034
|
+
readonly content_type: string | null;
|
|
1035
|
+
readonly fetched_at: string;
|
|
1036
|
+
readonly status: number;
|
|
1037
|
+
/** This value is what the page said about AI input at the time of the fetch. */
|
|
1038
|
+
readonly ai_input: AiInput;
|
|
1039
|
+
/** This URL is the target of a stored redirect. Its secrets show as `REDACTED`. */
|
|
1040
|
+
readonly location?: string | null | undefined;
|
|
1041
|
+
/** This value is true when the stored body is only the first part of the response. */
|
|
1042
|
+
readonly truncated?: boolean | undefined;
|
|
1043
|
+
/** This time (ISO 8601) is the end of the freshness that the server gave. */
|
|
1044
|
+
readonly fresh_until?: string | null | undefined;
|
|
1045
|
+
/** This text holds the request headers that the response varies on (`Vary`), with their values. */
|
|
1046
|
+
readonly vary_key?: string | null | undefined;
|
|
1047
|
+
/** The default is `plain`. A key that ends in `#rendered` (the form of version 0.1) is `rendered`. */
|
|
1048
|
+
readonly variant?: Variant | undefined;
|
|
1049
|
+
}
|
|
1050
|
+
/** These are the counts of the index. */
|
|
1051
|
+
interface Counts {
|
|
1052
|
+
readonly docs: number;
|
|
1053
|
+
readonly urls: number;
|
|
1054
|
+
}
|
|
1055
|
+
/** A rebuild of the index writes a new generation of the tables. The old generation answers until the commit. */
|
|
1056
|
+
interface IndexBuild {
|
|
1057
|
+
/** This method adds a version to the new generation. The build writes the versions in batches. */
|
|
1058
|
+
upsert(input: DocInput, now: string): void;
|
|
1059
|
+
/** This method sets a cache entry in the new generation, when it is newer than the stored entry. */
|
|
1060
|
+
setLatest(entry: Latest): void;
|
|
1061
|
+
/** This method reads a cache entry of the new generation. */
|
|
1062
|
+
latest(url: string, variant?: Variant): Latest | undefined;
|
|
1063
|
+
/** This method writes the waiting versions in one transaction. */
|
|
1064
|
+
flush(): void;
|
|
1065
|
+
/**
|
|
1066
|
+
* This method copies the versions of each recipe that is not in `recipes` from the current
|
|
1067
|
+
* generation. A runtime with no extractor for a recipe thus keeps the documents of that recipe.
|
|
1068
|
+
* It gives the number of documents that it copied.
|
|
1069
|
+
*/
|
|
1070
|
+
carry(recipes: ReadonlySet<string>): number;
|
|
1071
|
+
/**
|
|
1072
|
+
* This method makes the new generation current. It first copies the rows that a different writer
|
|
1073
|
+
* added to the current generation since `since`. Then it removes the old generation.
|
|
1074
|
+
*/
|
|
1075
|
+
commit(since: string): Counts;
|
|
1076
|
+
/** This method removes the new generation. The current generation stays. */
|
|
1077
|
+
abort(): void;
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* A reading index keeps the documents, their versions, the search and the cache entries. `DocIndex`
|
|
1081
|
+
* is the SQLite index. A runtime takes any object with these methods, for example a search index
|
|
1082
|
+
* of a different kind that wraps a `DocIndex`.
|
|
1083
|
+
*
|
|
1084
|
+
* @beta
|
|
1085
|
+
*/
|
|
1086
|
+
interface ReadingIndex {
|
|
1087
|
+
/** This value is true when the index opened empty and a rebuild did not fill it yet. */
|
|
1088
|
+
readonly stale: boolean;
|
|
1089
|
+
get(doc: string): Doc | undefined;
|
|
1090
|
+
upsert(input: DocInput, now: string): Doc;
|
|
1091
|
+
versions(doc: string, options?: {
|
|
1092
|
+
readonly limit?: number;
|
|
1093
|
+
}): DocVersion[];
|
|
1094
|
+
versionText(doc: string, capture?: string): VersionText | undefined;
|
|
1095
|
+
/**
|
|
1096
|
+
* This optional method gives the version that was current at a time (ISO 8601), with the rules
|
|
1097
|
+
* that choose the current version. An index without it gets the newest version at or before the
|
|
1098
|
+
* time, from `versions` and `versionText`.
|
|
1099
|
+
*/
|
|
1100
|
+
versionAt?(doc: string, at: string): VersionAt;
|
|
1101
|
+
earlierTexts(doc: string, options?: {
|
|
1102
|
+
readonly limit?: number;
|
|
1103
|
+
}): VersionText[];
|
|
1104
|
+
docsOfCapture(capture: string): Doc[];
|
|
1105
|
+
/** This method tells if a capture gave a version of a document. */
|
|
1106
|
+
hasVersion(capture: string): boolean;
|
|
1107
|
+
/**
|
|
1108
|
+
* This optional method gives the hash of the current text of documents, with no text. Hybrid
|
|
1109
|
+
* search uses it to find the documents whose text has no vectors. An index without it gives the
|
|
1110
|
+
* same facts from `get`, more slowly.
|
|
1111
|
+
*/
|
|
1112
|
+
digests?(options: DigestOptions): DocDigest[];
|
|
1113
|
+
find(q: string, options: FindIndexOptions): Found[];
|
|
1114
|
+
rankWithin(q: string, docs: readonly string[], options?: {
|
|
1115
|
+
readonly rank?: RankMode | undefined;
|
|
1116
|
+
}): Array<{
|
|
1117
|
+
readonly doc: string;
|
|
1118
|
+
readonly score: number;
|
|
1119
|
+
}>;
|
|
1120
|
+
latest(url: string, variant?: Variant): Latest | undefined;
|
|
1121
|
+
setLatest(entry: Latest): void;
|
|
1122
|
+
/** This method gives the newest fetch time of the cache entries, or undefined for an empty index. */
|
|
1123
|
+
newestFetch(): string | undefined;
|
|
1124
|
+
counts(): Counts;
|
|
1125
|
+
beginRebuild(options?: {
|
|
1126
|
+
readonly batch?: number;
|
|
1127
|
+
}): IndexBuild;
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* These are the options of a reading index.
|
|
1131
|
+
*
|
|
1132
|
+
* @beta
|
|
1133
|
+
*/
|
|
1134
|
+
interface DocIndexOptions {
|
|
1135
|
+
/** This value is the time in milliseconds that a write waits for the lock of a different process. The default is 10 seconds. */
|
|
1136
|
+
readonly busyTimeoutMs?: number | undefined;
|
|
1137
|
+
}
|
|
1138
|
+
/** @beta */
|
|
1139
|
+
export declare class DocIndex implements ReadingIndex {
|
|
1140
|
+
readonly path: string;
|
|
1141
|
+
private readonly db;
|
|
1142
|
+
private readonly statements;
|
|
1143
|
+
private depth;
|
|
1144
|
+
private closed;
|
|
1145
|
+
private empty;
|
|
1146
|
+
/**
|
|
1147
|
+
* This value is true when the file had a different schema, or no tables, so the index opened
|
|
1148
|
+
* empty. A store with captures then needs `reindex`. The commit of a rebuild makes it false.
|
|
1149
|
+
*/
|
|
1150
|
+
get stale(): boolean;
|
|
1151
|
+
constructor(path: string, options?: DocIndexOptions);
|
|
1152
|
+
/** This method makes the tables of the schema. It gives true when the index opened empty. */
|
|
1153
|
+
private prepareSchema;
|
|
1154
|
+
private setMeta;
|
|
1155
|
+
private getMeta;
|
|
1156
|
+
private stmt;
|
|
1157
|
+
/**
|
|
1158
|
+
* This method does a write in one transaction. A transaction in progress takes the write in,
|
|
1159
|
+
* also a transaction that the caller opened on the connection.
|
|
1160
|
+
*/
|
|
1161
|
+
private write;
|
|
1162
|
+
private generation;
|
|
1163
|
+
/**
|
|
1164
|
+
* This method does a step on the tables of the current generation. A rebuild of a different
|
|
1165
|
+
* process can remove those tables in the meantime. Then the method reads the generation again
|
|
1166
|
+
* and does the step once more.
|
|
1167
|
+
*/
|
|
1168
|
+
private live;
|
|
1169
|
+
/** This method removes the tables of a generation. */
|
|
1170
|
+
private dropGeneration;
|
|
1171
|
+
/** This method removes the generations of builds that a stopped process left. */
|
|
1172
|
+
private dropAbandoned;
|
|
1173
|
+
private putText;
|
|
1174
|
+
private textOf;
|
|
1175
|
+
private putVersion;
|
|
1176
|
+
/** This method gives the newest version of a document for one recipe and one variant, at `until` or before when it is given. */
|
|
1177
|
+
private newest;
|
|
1178
|
+
/**
|
|
1179
|
+
* This method chooses the current version of a document with the rules of the class. With
|
|
1180
|
+
* `until`, it uses only the versions that the crawler fetched at that time or before. Thus it
|
|
1181
|
+
* gives the version that was current at that time. It gives undefined when no version agrees.
|
|
1182
|
+
*/
|
|
1183
|
+
private choose;
|
|
1184
|
+
/** This method chooses the current version of a document from its versions, and writes it with its search entry. */
|
|
1185
|
+
private recompute;
|
|
1186
|
+
private docOf;
|
|
1187
|
+
private toDoc;
|
|
1188
|
+
private versionTextOf;
|
|
1189
|
+
get(doc: string): Doc | undefined;
|
|
1190
|
+
/**
|
|
1191
|
+
* This method adds one version of a document, and it gives the current document. The rules of
|
|
1192
|
+
* the class choose the current version. An upsert of a known capture replaces that version.
|
|
1193
|
+
*/
|
|
1194
|
+
upsert(input: DocInput, now: string): Doc;
|
|
1195
|
+
/** This method gives the versions of a document, the newest first. */
|
|
1196
|
+
versions(doc: string, options?: {
|
|
1197
|
+
readonly limit?: number;
|
|
1198
|
+
}): DocVersion[];
|
|
1199
|
+
/**
|
|
1200
|
+
* This method gives the text of one version of a document. With no capture, it gives the current
|
|
1201
|
+
* version. It also gives the text of a held-back version: the caller applies `isHeldBack` to the
|
|
1202
|
+
* `ai_input` of the version.
|
|
1203
|
+
*/
|
|
1204
|
+
versionText(doc: string, capture?: string): VersionText | undefined;
|
|
1205
|
+
/**
|
|
1206
|
+
* This method gives the version of a document that was current at a time (ISO 8601). It applies
|
|
1207
|
+
* the rules of the class to the versions that the crawler fetched at that time or before. Thus
|
|
1208
|
+
* a later capture does not change the answer. It also gives a held-back version: its `ai_input`
|
|
1209
|
+
* is `no` when the document was held back at that time, and the caller applies `isHeldBack`.
|
|
1210
|
+
*/
|
|
1211
|
+
versionAt(doc: string, at: string): VersionAt;
|
|
1212
|
+
/**
|
|
1213
|
+
* This method gives the texts of the earlier versions of a document, the newest first. Each
|
|
1214
|
+
* text comes one time, with the newest version that has it. It skips the current text. It also
|
|
1215
|
+
* gives held-back texts: the caller applies `isHeldBack` to the `ai_input` of each version.
|
|
1216
|
+
*/
|
|
1217
|
+
earlierTexts(doc: string, options?: {
|
|
1218
|
+
readonly limit?: number;
|
|
1219
|
+
}): VersionText[];
|
|
1220
|
+
/** This method tells if a capture gave a version of a document. */
|
|
1221
|
+
hasVersion(capture: string): boolean;
|
|
1222
|
+
/**
|
|
1223
|
+
* This method gives the hash of the current text of documents, with no text. It gives the
|
|
1224
|
+
* documents of `docs`, or each document after `after` in the sequence of the ids, to `limit`.
|
|
1225
|
+
*/
|
|
1226
|
+
digests(options?: DigestOptions): DocDigest[];
|
|
1227
|
+
/** This method gives the newest fetch time of the cache entries, or undefined for an empty index. */
|
|
1228
|
+
newestFetch(): string | undefined;
|
|
1229
|
+
/** This method gives the current documents that a capture gave. */
|
|
1230
|
+
docsOfCapture(capture: string): Doc[];
|
|
1231
|
+
/**
|
|
1232
|
+
* This method searches the documents with BM25, the best first. The syntax of the query is the
|
|
1233
|
+
* syntax of `parseQuery`. It first asks for each part. When that gives too few documents, it
|
|
1234
|
+
* adds the documents that match one part. A document that asked not to be AI input is not in
|
|
1235
|
+
* the results, so a search cannot show its text. The option `rank` chooses the ranking (`RankMode`).
|
|
1236
|
+
*/
|
|
1237
|
+
find(q: string, options: FindIndexOptions): Found[];
|
|
1238
|
+
/**
|
|
1239
|
+
* This method scores a set of documents against a query, with the best first. It leaves out a
|
|
1240
|
+
* document that matches no term, and a document that asked not to be AI input. It ranks the
|
|
1241
|
+
* results of a source again, because the order of the source is only as good as its search engine.
|
|
1242
|
+
* In the mode `fast`, it also leaves out a document that holds only common parts (`RankMode`).
|
|
1243
|
+
*/
|
|
1244
|
+
rankWithin(q: string, docs: readonly string[], options?: {
|
|
1245
|
+
readonly rank?: RankMode | undefined;
|
|
1246
|
+
}): Array<{
|
|
1247
|
+
readonly doc: string;
|
|
1248
|
+
readonly score: number;
|
|
1249
|
+
}>;
|
|
1250
|
+
/**
|
|
1251
|
+
* This method gives the form of a query that needs one part, without the common parts (the mode
|
|
1252
|
+
* `fast` of `RankMode`). It counts the documents that hold each part. When each part is common,
|
|
1253
|
+
* it keeps the rarest part.
|
|
1254
|
+
*/
|
|
1255
|
+
private rareParts;
|
|
1256
|
+
/** This method gives the cache entry of a URL. The key is canonical, so two spellings of one URL give one entry. */
|
|
1257
|
+
latest(url: string, variant?: Variant): Latest | undefined;
|
|
1258
|
+
private putLatest;
|
|
1259
|
+
/** This method keeps the last answer for a URL. An entry that is older than the stored entry changes nothing. */
|
|
1260
|
+
setLatest(entry: Latest): void;
|
|
1261
|
+
counts(): Counts;
|
|
1262
|
+
/** This method empties the current generation. A rebuild uses `beginRebuild`, which keeps the index until its commit. */
|
|
1263
|
+
reset(): void;
|
|
1264
|
+
/**
|
|
1265
|
+
* This method starts a rebuild into a new generation of the tables. The current generation
|
|
1266
|
+
* answers each read and takes each write until the commit. A failed rebuild thus leaves the
|
|
1267
|
+
* index as it was.
|
|
1268
|
+
*/
|
|
1269
|
+
beginRebuild(options?: {
|
|
1270
|
+
readonly batch?: number;
|
|
1271
|
+
}): IndexBuild;
|
|
1272
|
+
/** This method closes the file. A second close does nothing. */
|
|
1273
|
+
close(): void;
|
|
1274
|
+
}
|
|
1275
|
+
//#endregion
|
|
1276
|
+
//#region src/egress.d.ts
|
|
1277
|
+
/**
|
|
1278
|
+
* The egress policy keeps the crawler on the public internet. A page or an agent can ask for an
|
|
1279
|
+
* internal address. Examples are a loopback service, a router on the LAN and the metadata
|
|
1280
|
+
* service of a cloud host. The crawler refuses these addresses by default.
|
|
1281
|
+
*
|
|
1282
|
+
* - The crawler fetches only `http:` and `https:` URLs.
|
|
1283
|
+
* - The check uses the address after DNS. The `lookup` hook of the connection checks the address
|
|
1284
|
+
* that the socket then uses, so a DNS answer that changes cannot pass the check.
|
|
1285
|
+
* - The crawler examines a literal IP host directly, in each spelling that the URL parser reads.
|
|
1286
|
+
* - The crawler examines each redirect hop again.
|
|
1287
|
+
*
|
|
1288
|
+
* A refusal is a skipped fetch with a clear note. It does not throw an error.
|
|
1289
|
+
*/
|
|
1290
|
+
/** These options change the egress policy. With no options, the crawler fetches only public addresses. */
|
|
1291
|
+
interface EgressOptions {
|
|
1292
|
+
/** The value true lets the crawler fetch loopback, private and link-local addresses. The default is false. */
|
|
1293
|
+
readonly allowPrivate?: boolean | undefined;
|
|
1294
|
+
/**
|
|
1295
|
+
* These hosts are permitted although their address is not public. An entry is a host name, a
|
|
1296
|
+
* name pattern (`*.corp.example`), an IP address or a CIDR range (`10.0.0.0/8`).
|
|
1297
|
+
*/
|
|
1298
|
+
readonly allowHosts?: readonly string[] | undefined;
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* The connection hook gives this error when the policy refuses each address of a host.
|
|
1302
|
+
*
|
|
1303
|
+
* @beta
|
|
1304
|
+
*/
|
|
1305
|
+
export declare class EgressRefused extends Error {
|
|
1306
|
+
readonly address: string;
|
|
1307
|
+
readonly kind: string;
|
|
1308
|
+
readonly code: string;
|
|
1309
|
+
constructor(address: string, kind: string);
|
|
1310
|
+
}
|
|
1311
|
+
/** This is the form of `dns.lookup` that the egress hook uses. Tests give a resolver of their own. */
|
|
1312
|
+
type Resolve = (host: string, options: LookupAllOptions, callback: (error: NodeJS.ErrnoException | null, addresses: LookupAddress[]) => void) => void;
|
|
1313
|
+
/**
|
|
1314
|
+
* This function gives a `lookup` hook for the connection options of an undici `Agent`. The hook
|
|
1315
|
+
* resolves the name, removes each address that the policy refuses, and gives the rest to the
|
|
1316
|
+
* socket. When no address stays, the connection fails with `EgressRefused`.
|
|
1317
|
+
*
|
|
1318
|
+
* @beta
|
|
1319
|
+
*/
|
|
1320
|
+
export declare const egressLookup: (policy: EgressOptions, resolve?: Resolve) => LookupFunction;
|
|
1321
|
+
//#endregion
|
|
1322
|
+
//#region src/webbotauth.d.ts
|
|
1323
|
+
/**
|
|
1324
|
+
* Signed requests: HTTP Message Signatures (RFC 9421), as the IETF Web Bot Auth profile tells
|
|
1325
|
+
* (`draft-ietf-webbotauth-httpsig-protocol-00`, 1 September 2026). A signature lets a site verify that
|
|
1326
|
+
* a request comes from the holder of a key that the operator publishes.
|
|
1327
|
+
*
|
|
1328
|
+
* - Each request gets three headers: `Signature-Agent`, `Signature-Input` and `Signature`.
|
|
1329
|
+
* - `Signature-Agent` is a dictionary. Its member names the key directory of the operator.
|
|
1330
|
+
* - The signature covers `@authority` and that member of `Signature-Agent`.
|
|
1331
|
+
* - The parameters are `created`, `keyid` (the JWK thumbprint of the key), `alg="ed25519"`,
|
|
1332
|
+
* `expires`, a random `nonce` and `tag="web-bot-auth"`.
|
|
1333
|
+
* - The key is an Ed25519 key, and `node:crypto` signs. The package adds no dependency.
|
|
1334
|
+
*
|
|
1335
|
+
* A signature adds an identity. It does not replace the `User-Agent` header, which stays honest.
|
|
1336
|
+
*/
|
|
1337
|
+
/** The path of the key directory on the host of the operator (the well-known URI of the draft). */
|
|
1338
|
+
export declare const KEY_DIRECTORY_PATH = "/.well-known/http-message-signatures-directory";
|
|
1339
|
+
/** The media type of the key directory. The host of the directory sends it as `Content-Type`. */
|
|
1340
|
+
export declare const KEY_DIRECTORY_TYPE = "application/http-message-signatures-directory+json";
|
|
1341
|
+
/**
|
|
1342
|
+
* This value tells how a site finds the keys from the URL of `Signature-Agent`:
|
|
1343
|
+
*
|
|
1344
|
+
* - `directory` (the default): the URL is an origin, and the keys are at `KEY_DIRECTORY_PATH` on it.
|
|
1345
|
+
* - `jwks_uri`: the URL is the address of a JSON Web Key Set, on any path.
|
|
1346
|
+
*/
|
|
1347
|
+
type SignatureAgentType = "directory" | "jwks_uri";
|
|
1348
|
+
/** A key in one of the forms that `node:crypto` reads: a `KeyObject`, PEM text, DER bytes (PKCS#8 or SPKI) or a JWK. */
|
|
1349
|
+
/**
|
|
1350
|
+
* This type is a JSON Web Key, as `createPrivateKey` of `node:crypto` reads it. The type comes from `JsonWebKeyInput`, which
|
|
1351
|
+
* `@types/node` 22 and 26 both have: version 26 has no `JsonWebKey` export in `node:crypto`.
|
|
1352
|
+
*/
|
|
1353
|
+
type JsonWebKey = JsonWebKeyInput["key"];
|
|
1354
|
+
type KeyInput = KeyObject | string | Uint8Array | JsonWebKey;
|
|
1355
|
+
/** These options make a signer for Web Bot Auth. */
|
|
1356
|
+
interface WebBotAuthOptions {
|
|
1357
|
+
/** This key signs. It is an Ed25519 private key: a `KeyObject`, PKCS#8 (PEM text or DER bytes) or a JWK with `d`. */
|
|
1358
|
+
readonly privateKey: KeyInput;
|
|
1359
|
+
/**
|
|
1360
|
+
* This text is the `keyid` of each signature: the JWK thumbprint of the key (RFC 8037). The signer
|
|
1361
|
+
* calculates it. A value that is not the thumbprint of the key throws an error.
|
|
1362
|
+
*/
|
|
1363
|
+
readonly keyId?: string | undefined;
|
|
1364
|
+
/**
|
|
1365
|
+
* This URL tells a site where the keys are. For the type `directory`, it is an https origin, for
|
|
1366
|
+
* example `https://bot.example.com`. For `jwks_uri`, it is the https URL of a JSON Web Key Set.
|
|
1367
|
+
*/
|
|
1368
|
+
readonly signatureAgent: string;
|
|
1369
|
+
/** This value tells how a site finds the keys from `signatureAgent`. The default is `directory`. */
|
|
1370
|
+
readonly agentType?: SignatureAgentType | undefined;
|
|
1371
|
+
/** This value is the time that a signature stays valid, in milliseconds. The default is 5 minutes, and the maximum is 24 hours. */
|
|
1372
|
+
readonly lifetimeMs?: number | undefined;
|
|
1373
|
+
/** This text is the label of the signature, and the key of the member of `Signature-Agent`. The default is `sig1`. */
|
|
1374
|
+
readonly label?: string | undefined;
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* A request signer gives the signature headers of one request. The crawler uses it for each
|
|
1378
|
+
* request, robots.txt too. `createWebBotAuthSigner` makes one. A signer of your own can sign with a
|
|
1379
|
+
* key in a hardware module, for example.
|
|
1380
|
+
*/
|
|
1381
|
+
interface RequestSigner {
|
|
1382
|
+
/** This text is the `keyid` of the signatures. */
|
|
1383
|
+
readonly keyId: string;
|
|
1384
|
+
/** This URL is the value of the member of `Signature-Agent`: where a site finds the keys. */
|
|
1385
|
+
readonly signatureAgent: string;
|
|
1386
|
+
/** This method gives the headers that sign a request to `url` at the time `now` (milliseconds since the epoch). */
|
|
1387
|
+
sign(url: URL, now: number): Readonly<Record<string, string>>;
|
|
1388
|
+
}
|
|
1389
|
+
/** The signer that `createWebBotAuthSigner` makes. It also gives its public key for the key directory. */
|
|
1390
|
+
interface WebBotAuthSigner extends RequestSigner {
|
|
1391
|
+
/** This JWK is the public key of the signer, with no private part. Give it to `keyDirectory`. */
|
|
1392
|
+
readonly publicKey: JsonWebKey;
|
|
1393
|
+
}
|
|
1394
|
+
/** One key of a key directory: an Ed25519 public key as a JWK, with its thumbprint as `kid`. */
|
|
1395
|
+
interface DirectoryKey {
|
|
1396
|
+
readonly kty: "OKP";
|
|
1397
|
+
readonly crv: "Ed25519";
|
|
1398
|
+
readonly kid: string;
|
|
1399
|
+
readonly x: string;
|
|
1400
|
+
readonly use: "sig";
|
|
1401
|
+
}
|
|
1402
|
+
/** A key directory: a JSON Web Key Set. The operator serves it as JSON at `KEY_DIRECTORY_PATH`, with the type `KEY_DIRECTORY_TYPE`. */
|
|
1403
|
+
interface KeyDirectory {
|
|
1404
|
+
readonly keys: readonly DirectoryKey[];
|
|
1405
|
+
}
|
|
1406
|
+
/** The parts of a request that `verifySignature` reads. */
|
|
1407
|
+
interface SignedRequest {
|
|
1408
|
+
readonly url: string;
|
|
1409
|
+
/** The default is `GET`. */
|
|
1410
|
+
readonly method?: string | undefined;
|
|
1411
|
+
/** These are the headers of the request. A name can be in any case, and a header can have more than one line. */
|
|
1412
|
+
readonly headers: Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
1413
|
+
}
|
|
1414
|
+
/** The result of `verifySignature`. */
|
|
1415
|
+
interface SignatureCheck {
|
|
1416
|
+
/** This value is true when a Web Bot Auth signature of the request verifies with a key of the directory. */
|
|
1417
|
+
readonly verified: boolean;
|
|
1418
|
+
/** This text is the label of the signature that the check examined last, or null. */
|
|
1419
|
+
readonly label: string | null;
|
|
1420
|
+
readonly keyId: string | null;
|
|
1421
|
+
/** This URL is the member of `Signature-Agent` that the signature covers, or null. */
|
|
1422
|
+
readonly agent: string | null;
|
|
1423
|
+
/** This text tells why the request does not verify. It is null when it verifies. */
|
|
1424
|
+
readonly reason: string | null;
|
|
1425
|
+
}
|
|
1426
|
+
/**
|
|
1427
|
+
* This function makes the key directory for the operator to host: a JSON Web Key Set with each
|
|
1428
|
+
* public key and its thumbprint as `kid`. It takes public keys, or private keys, and it writes only
|
|
1429
|
+
* their public parts. Serve the JSON at `KEY_DIRECTORY_PATH` with the type `KEY_DIRECTORY_TYPE`.
|
|
1430
|
+
*/
|
|
1431
|
+
export declare const keyDirectory: (publicKeys: readonly KeyInput[]) => KeyDirectory;
|
|
1432
|
+
/**
|
|
1433
|
+
* This function makes a signer for Web Bot Auth with an Ed25519 key. Each signature covers
|
|
1434
|
+
* `@authority` and the member of `Signature-Agent`. It has a new random nonce, and it expires after
|
|
1435
|
+
* `lifetimeMs`. The signer keeps the private key in a closure: no property of the signer holds it.
|
|
1436
|
+
*/
|
|
1437
|
+
export declare const createWebBotAuthSigner: (options: WebBotAuthOptions) => WebBotAuthSigner;
|
|
1438
|
+
/**
|
|
1439
|
+
* This function verifies the Web Bot Auth signatures of a request with the keys of a directory. It
|
|
1440
|
+
* applies RFC 9421 section 3.2 and the profile of the draft. The profile asks for the tag
|
|
1441
|
+
* `web-bot-auth`, `created`, `expires` and `@authority`. It also asks for the member of
|
|
1442
|
+
* `Signature-Agent`, and a key with the thumbprint of `keyid`. The tests use it, and an operator can check a setup with it. It sends no request.
|
|
1443
|
+
*/
|
|
1444
|
+
export declare const verifySignature: (request: SignedRequest, directory: KeyDirectory, options?: {
|
|
1445
|
+
readonly now?: number | undefined;
|
|
1446
|
+
}) => SignatureCheck;
|
|
1447
|
+
//#endregion
|
|
1448
|
+
//#region src/crawler.d.ts
|
|
1449
|
+
/**
|
|
1450
|
+
* The crawler is a polite fetcher. It sends one request at a time to each host, and it obeys
|
|
1451
|
+
* robots.txt for web pages. Its `User-Agent` header tells what it is. It asks again with the
|
|
1452
|
+
* validators of the stored copy, and it stops when a site refuses. Each decision goes into the
|
|
1453
|
+
* capture log, and a fetch that the crawler declines goes there too.
|
|
1454
|
+
*
|
|
1455
|
+
* There are two kinds of request. A documented public API (`api`) has its own terms and rate
|
|
1456
|
+
* limits. A web page (`page`) obeys robots.txt. The settings come from measurements:
|
|
1457
|
+
*
|
|
1458
|
+
* - The `request` function of undici on one `Agent`.
|
|
1459
|
+
* - A keep-alive time longer than the delay for each host. With the default of 4 seconds, each
|
|
1460
|
+
* polite request opens a new connection.
|
|
1461
|
+
* - A backoff with jitter in the queue, not the retry handler of undici. That handler limits
|
|
1462
|
+
* Retry-After to 30 seconds and adds no jitter.
|
|
1463
|
+
* - A maximum size for each body, after decompression. The crawler keeps the first part of a
|
|
1464
|
+
* longer body and marks it as truncated.
|
|
1465
|
+
*
|
|
1466
|
+
* A fetch and a render go through one admission. It examines the URL, the egress policy, the
|
|
1467
|
+
* product tokens, robots.txt, the cache and the stops of the host. Only the transport is
|
|
1468
|
+
* different: an HTTP request or the headless browser.
|
|
1469
|
+
*
|
|
1470
|
+
* The egress policy (`EgressOptions`) keeps the crawler on public addresses by default. The
|
|
1471
|
+
* politeness policy (`PolitenessPolicy`) holds each limit, and a host can have its own values.
|
|
1472
|
+
* The fetch and the render do not throw an error: each problem is an outcome with a note.
|
|
1473
|
+
*/
|
|
1474
|
+
/**
|
|
1475
|
+
* The outcome of a fetch or a render. A `skipped` request did not go out. A render of a URL that
|
|
1476
|
+
* gives a file and not a page is `not-html`.
|
|
1477
|
+
*/
|
|
1478
|
+
type FetchOutcome = Outcome | "skipped" | "not-html";
|
|
1479
|
+
/** @beta */
|
|
1480
|
+
interface FetchRequest {
|
|
1481
|
+
readonly url: string;
|
|
1482
|
+
/** The value `api` is a documented API with its own terms. The value `page` is a web page, and robots.txt applies. */
|
|
1483
|
+
readonly mode: "api" | "page";
|
|
1484
|
+
/** This recipe reads the capture again. The function `reindex` uses it to replay the capture. */
|
|
1485
|
+
readonly recipe: string;
|
|
1486
|
+
readonly accept?: string | undefined;
|
|
1487
|
+
/**
|
|
1488
|
+
* These are more request headers. The crawler does not write them into the capture. They
|
|
1489
|
+
* cannot replace `User-Agent`, `From`, `Accept-Encoding` or the validators. A redirect to a
|
|
1490
|
+
* different origin removes `Authorization`, `Cookie` and `Proxy-Authorization`.
|
|
1491
|
+
*/
|
|
1492
|
+
readonly headers?: Readonly<Record<string, string>> | undefined;
|
|
1493
|
+
/**
|
|
1494
|
+
* These query parameters contain secrets. Each capture shows `REDACTED` in their place. The
|
|
1495
|
+
* names in `DEFAULT_SECRET_PARAMS` get the same treatment with no list.
|
|
1496
|
+
*/
|
|
1497
|
+
readonly secretParams?: readonly string[] | undefined;
|
|
1498
|
+
/** This value is the documented pace of the source: the shortest time between two of its requests. */
|
|
1499
|
+
readonly minIntervalMs?: number | undefined;
|
|
1500
|
+
/**
|
|
1501
|
+
* A stored capture younger than this age answers, and the crawler sends no request. The value
|
|
1502
|
+
* `auto` uses the freshness that the server gave (RFC 9111): `Cache-Control`, `Expires`, or 10
|
|
1503
|
+
* percent of the age of `Last-Modified`.
|
|
1504
|
+
*/
|
|
1505
|
+
readonly maxAgeMs?: number | "auto" | undefined;
|
|
1506
|
+
readonly maxBytes?: number | undefined;
|
|
1507
|
+
/**
|
|
1508
|
+
* These are the product tokens of an agent that the fetch acts for, for example `Claude-User`.
|
|
1509
|
+
* The robots.txt rules for these tokens apply too.
|
|
1510
|
+
*/
|
|
1511
|
+
readonly onBehalfOf?: readonly string[] | undefined;
|
|
1512
|
+
/** This name connects the captures to the job that asked for them. */
|
|
1513
|
+
readonly job?: string | undefined;
|
|
1514
|
+
/** When this signal aborts, the crawler stops the fetch. The result is then `skipped` or `error`, with a note. */
|
|
1515
|
+
readonly signal?: AbortSignal | undefined;
|
|
1516
|
+
/** This label goes into each capture record of the request. It replaces the `scope` of the crawler. */
|
|
1517
|
+
readonly scope?: string | undefined;
|
|
1518
|
+
/** These labels go into each capture record of the request, for example its lineage. They are not part of the capture id. */
|
|
1519
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
1520
|
+
/**
|
|
1521
|
+
* This time (milliseconds since the epoch, on the clock of the crawler) is the end of a budget.
|
|
1522
|
+
* The request does not start at or after it. When the host is not free before it, the result is
|
|
1523
|
+
* `skipped`, and its note starts with `DEADLINE_NOTE`.
|
|
1524
|
+
*/
|
|
1525
|
+
readonly deadline?: number | undefined;
|
|
1526
|
+
/**
|
|
1527
|
+
* This byte range is the part of the resource to fetch (a `Range` request). The crawler then sends
|
|
1528
|
+
* no validators, keeps no stored copy for the URL, and reads `length` bytes at most. The capture
|
|
1529
|
+
* records the range.
|
|
1530
|
+
*/
|
|
1531
|
+
readonly range?: {
|
|
1532
|
+
readonly offset: number;
|
|
1533
|
+
readonly length: number;
|
|
1534
|
+
} | undefined;
|
|
1535
|
+
/** These lines of usage signals apply to the response too. A copy from a web archive gets the content signals of the robots.txt of its site. */
|
|
1536
|
+
readonly signalLines?: readonly string[] | undefined;
|
|
1537
|
+
/**
|
|
1538
|
+
* This prefix names the response headers that hold the headers of an archived response, for
|
|
1539
|
+
* example `x-archive-orig-` of the Wayback Machine. The usage signals of those headers apply too.
|
|
1540
|
+
*/
|
|
1541
|
+
readonly originalHeaderPrefix?: string | undefined;
|
|
1542
|
+
}
|
|
1543
|
+
/** @beta */
|
|
1544
|
+
interface Fetched {
|
|
1545
|
+
/** This URL is the URL of the capture. Its secrets show as `REDACTED`. */
|
|
1546
|
+
readonly url: string;
|
|
1547
|
+
readonly status: number;
|
|
1548
|
+
readonly outcome: FetchOutcome;
|
|
1549
|
+
/** This value is true when a stored capture answered, with no request. */
|
|
1550
|
+
readonly fromCache: boolean;
|
|
1551
|
+
/** This id is the capture record behind the body. A citation names it. */
|
|
1552
|
+
readonly capture: string | undefined;
|
|
1553
|
+
readonly blob: string | undefined;
|
|
1554
|
+
readonly body: Uint8Array | undefined;
|
|
1555
|
+
readonly contentType: string | undefined;
|
|
1556
|
+
readonly signals: Signals;
|
|
1557
|
+
readonly note: string | undefined;
|
|
1558
|
+
/** This value is true when the body was longer than the maximum, so the crawler kept only its first part. */
|
|
1559
|
+
readonly truncated?: boolean | undefined;
|
|
1560
|
+
/** This time (ISO 8601) is the time of the fetch of the body. For a stored copy, it is the time of that copy. */
|
|
1561
|
+
readonly fetchedAt?: string | undefined;
|
|
1562
|
+
/** This label is the scope of the capture. */
|
|
1563
|
+
readonly scope?: string | undefined;
|
|
1564
|
+
/** These labels are the labels of the capture. */
|
|
1565
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1568
|
+
* The crawler writes each record to a capture writer. The class `CaptureLog` is one.
|
|
1569
|
+
*
|
|
1570
|
+
* @beta
|
|
1571
|
+
*/
|
|
1572
|
+
interface CaptureWriter {
|
|
1573
|
+
append(record: CaptureRecord): Promise<void>;
|
|
1574
|
+
close?(): Promise<void>;
|
|
1575
|
+
}
|
|
1576
|
+
/**
|
|
1577
|
+
* A cache entry is the last answer for a URL: its capture, its body and its validators.
|
|
1578
|
+
*
|
|
1579
|
+
* @beta
|
|
1580
|
+
*/
|
|
1581
|
+
interface CacheEntry {
|
|
1582
|
+
/** This text is the cache key: the canonical URL, with its secrets as `REDACTED`. */
|
|
1583
|
+
readonly url: string;
|
|
1584
|
+
readonly capture: string;
|
|
1585
|
+
readonly blob: string | null;
|
|
1586
|
+
readonly etag: string | null;
|
|
1587
|
+
readonly last_modified: string | null;
|
|
1588
|
+
readonly content_type: string | null;
|
|
1589
|
+
readonly fetched_at: string;
|
|
1590
|
+
readonly status: number;
|
|
1591
|
+
/** This value is what the page said about AI input at the time of the fetch. */
|
|
1592
|
+
readonly ai_input: AiInput;
|
|
1593
|
+
/** This URL is the target of a stored redirect. Its secrets show as `REDACTED`. */
|
|
1594
|
+
readonly location?: string | null | undefined;
|
|
1595
|
+
/** This value is true when the stored body is only the first part of the response. */
|
|
1596
|
+
readonly truncated?: boolean | undefined;
|
|
1597
|
+
/** This time (ISO 8601) is the end of the freshness that the server gave. */
|
|
1598
|
+
readonly fresh_until?: string | null | undefined;
|
|
1599
|
+
/** This text holds the request headers that the response varies on (`Vary`), with their values. */
|
|
1600
|
+
readonly vary_key?: string | null | undefined;
|
|
1601
|
+
/** This value tells a plain response from a page that the browser rendered. The default is `plain`. */
|
|
1602
|
+
readonly variant?: Variant | undefined;
|
|
1603
|
+
}
|
|
1604
|
+
/**
|
|
1605
|
+
* The cache index holds the last answer for each URL. The crawler reads it before a request, and
|
|
1606
|
+
* it writes it after an answer. The class `DocIndex` is one. An index that keeps only the
|
|
1607
|
+
* necessary fields of `CacheEntry` works, and the crawler keeps the other fields in memory.
|
|
1608
|
+
*
|
|
1609
|
+
* @beta
|
|
1610
|
+
*/
|
|
1611
|
+
interface CacheIndex {
|
|
1612
|
+
latest(url: string, variant?: Variant): CacheEntry | undefined;
|
|
1613
|
+
setLatest(entry: CacheEntry): void;
|
|
1614
|
+
}
|
|
1615
|
+
/** An event tells a listener what the crawler does. The listener cannot change it. */
|
|
1616
|
+
type CrawlEvent = {
|
|
1617
|
+
readonly type: "request";
|
|
1618
|
+
readonly url: string;
|
|
1619
|
+
readonly host: string;
|
|
1620
|
+
readonly transport: "http" | "browser";
|
|
1621
|
+
} | {
|
|
1622
|
+
readonly type: "response";
|
|
1623
|
+
readonly url: string;
|
|
1624
|
+
readonly host: string;
|
|
1625
|
+
readonly status: number;
|
|
1626
|
+
readonly outcome: FetchOutcome;
|
|
1627
|
+
readonly elapsedMs: number;
|
|
1628
|
+
} | {
|
|
1629
|
+
readonly type: "wait";
|
|
1630
|
+
readonly host: string;
|
|
1631
|
+
readonly ms: number;
|
|
1632
|
+
readonly reason: string;
|
|
1633
|
+
} | {
|
|
1634
|
+
readonly type: "stop";
|
|
1635
|
+
readonly host: string;
|
|
1636
|
+
readonly until: number;
|
|
1637
|
+
readonly reason: string;
|
|
1638
|
+
} | {
|
|
1639
|
+
readonly type: "skip";
|
|
1640
|
+
readonly url: string;
|
|
1641
|
+
readonly note: string;
|
|
1642
|
+
};
|
|
1643
|
+
/** @beta */
|
|
1644
|
+
interface CrawlerOptions {
|
|
1645
|
+
/** This product token is the name that robots.txt rules match. It contains only letters, `_` and `-` (RFC 9309). */
|
|
1646
|
+
readonly token: string;
|
|
1647
|
+
/** This text is the full `User-Agent` header. */
|
|
1648
|
+
readonly userAgent: string;
|
|
1649
|
+
/** This contact goes into the `From` header. Without it, the crawler sends no `From` header. */
|
|
1650
|
+
readonly from?: string | undefined;
|
|
1651
|
+
readonly log: CaptureWriter;
|
|
1652
|
+
readonly blobs: BlobStore;
|
|
1653
|
+
readonly index: CacheIndex;
|
|
1654
|
+
readonly clock?: Clock | undefined;
|
|
1655
|
+
readonly random?: (() => number) | undefined;
|
|
1656
|
+
/** This value is the number of hosts that get requests at one time. The default is 16. */
|
|
1657
|
+
readonly maxActive?: number | undefined;
|
|
1658
|
+
/**
|
|
1659
|
+
* This value tells if the crawler also obeys the robots.txt group of `PoliteCrawler`. That applies
|
|
1660
|
+
* when the `User-Agent` links the bot page of polite-crawler (`BOT_PAGE`) and the token is different.
|
|
1661
|
+
* Site owners read that page, and they write their rules for `PoliteCrawler`. The most restrictive
|
|
1662
|
+
* of the groups applies, as for `onBehalfOf`. The default is true. Set false for a bot page of your own.
|
|
1663
|
+
*/
|
|
1664
|
+
readonly obeyDefaultToken?: boolean | undefined;
|
|
1665
|
+
/**
|
|
1666
|
+
* This value is the number of pages that the browser renders at one time. The default is 2. Each
|
|
1667
|
+
* render is a browser context of its own, which holds much memory. A render waits for a slot before
|
|
1668
|
+
* the lease of its host, so the wait does not count toward the time of the host.
|
|
1669
|
+
*/
|
|
1670
|
+
readonly maxRenders?: number | undefined;
|
|
1671
|
+
/**
|
|
1672
|
+
* This undici dispatcher sends the requests. With a dispatcher of your own, the crawler checks
|
|
1673
|
+
* only literal addresses. Put `egressLookup` in its connection options to check the address after DNS.
|
|
1674
|
+
*/
|
|
1675
|
+
readonly dispatcher?: Dispatcher | undefined;
|
|
1676
|
+
/** This label goes into each capture record as it is. */
|
|
1677
|
+
readonly scope?: string | undefined;
|
|
1678
|
+
/**
|
|
1679
|
+
* This function ranks the scopes of the requests, the most private scope highest. One fetch of a
|
|
1680
|
+
* robots.txt can serve requests of different scopes. Its record then takes the scope with the
|
|
1681
|
+
* highest rank, and the labels of each request. With no function, the first request gives the scope.
|
|
1682
|
+
*/
|
|
1683
|
+
readonly scopeRank?: ((scope: string) => number) | undefined;
|
|
1684
|
+
/** This browser renders the pages that need JavaScript. Without it, the crawler renders no page. */
|
|
1685
|
+
readonly renderer?: Renderer | undefined;
|
|
1686
|
+
/** This policy controls the addresses that the crawler fetches. By default, the crawler fetches only public addresses. */
|
|
1687
|
+
readonly egress?: EgressOptions | undefined;
|
|
1688
|
+
/** These values change the limits of the politeness policy, for each host or for some hosts. */
|
|
1689
|
+
readonly politeness?: PolicyOptions | undefined;
|
|
1690
|
+
/** This store keeps the pace, the counts and the stops of each host. The default keeps them in memory. */
|
|
1691
|
+
readonly hostState?: HostStateStore | undefined;
|
|
1692
|
+
/** This function gets an event for each request, response, wait, stop and skip. An error in it does not stop the crawler. */
|
|
1693
|
+
readonly onEvent?: ((event: CrawlEvent) => void) | undefined;
|
|
1694
|
+
/**
|
|
1695
|
+
* This signer signs each request, robots.txt and the requests of a rendered page too. Give the
|
|
1696
|
+
* options of `createWebBotAuthSigner`, or a `RequestSigner`. Without it, the crawler signs nothing.
|
|
1697
|
+
* A signature adds an identity: the `User-Agent` and `From` headers stay as they are.
|
|
1698
|
+
*/
|
|
1699
|
+
readonly signer?: RequestSigner | WebBotAuthOptions | undefined;
|
|
1700
|
+
}
|
|
1701
|
+
/** @beta */
|
|
1702
|
+
interface RenderRequest {
|
|
1703
|
+
readonly url: string;
|
|
1704
|
+
readonly recipe: string;
|
|
1705
|
+
/** A stored render younger than this age answers. The value `auto` uses the freshness that the server gave. */
|
|
1706
|
+
readonly maxAgeMs?: number | "auto" | undefined;
|
|
1707
|
+
readonly onBehalfOf?: readonly string[] | undefined;
|
|
1708
|
+
readonly job?: string | undefined;
|
|
1709
|
+
/** When this signal aborts, the crawler stops the render, and the browser stops the page. */
|
|
1710
|
+
readonly signal?: AbortSignal | undefined;
|
|
1711
|
+
/** This label goes into the capture record of the render. It replaces the `scope` of the crawler. */
|
|
1712
|
+
readonly scope?: string | undefined;
|
|
1713
|
+
/** These labels go into the capture record of the render. */
|
|
1714
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
1715
|
+
/** This time (milliseconds since the epoch) is the end of a budget. The render does not start at or after it (`FetchRequest.deadline`). */
|
|
1716
|
+
readonly deadline?: number | undefined;
|
|
1717
|
+
}
|
|
1718
|
+
/**
|
|
1719
|
+
* The note of a request starts with this text when its host was not free before its deadline, so the request did not start.
|
|
1720
|
+
*
|
|
1721
|
+
* @beta
|
|
1722
|
+
*/
|
|
1723
|
+
export declare const DEADLINE_NOTE = "deadline reached";
|
|
1724
|
+
/**
|
|
1725
|
+
* This value is the version of the payload of the records that the crawler writes. A record with no `schema` is version 1.
|
|
1726
|
+
*
|
|
1727
|
+
* @beta
|
|
1728
|
+
*/
|
|
1729
|
+
export declare const CAPTURE_SCHEMA = 2;
|
|
1730
|
+
/**
|
|
1731
|
+
* These query parameters hold secrets in many APIs. The crawler writes `REDACTED` in place of
|
|
1732
|
+
* their values in each capture, in addition to the names of `secretParams`.
|
|
1733
|
+
*
|
|
1734
|
+
* @beta
|
|
1735
|
+
*/
|
|
1736
|
+
export declare const DEFAULT_SECRET_PARAMS: readonly string[];
|
|
1737
|
+
/** @beta */
|
|
1738
|
+
export declare class Crawler {
|
|
1739
|
+
private readonly options;
|
|
1740
|
+
private readonly clock;
|
|
1741
|
+
private readonly random;
|
|
1742
|
+
private readonly scheduler;
|
|
1743
|
+
private readonly dispatcher;
|
|
1744
|
+
private readonly ownsDispatcher;
|
|
1745
|
+
private readonly egress;
|
|
1746
|
+
private readonly robots;
|
|
1747
|
+
/** The fields of a cache entry that the index does not keep, and the raw target of a redirect. */
|
|
1748
|
+
private readonly meta;
|
|
1749
|
+
private readonly inFlight;
|
|
1750
|
+
private readonly running;
|
|
1751
|
+
private readonly closing;
|
|
1752
|
+
private closed;
|
|
1753
|
+
private closingDone;
|
|
1754
|
+
/** This note tells why the renderer has no browser. After the first page that finds it, the crawler renders nothing. */
|
|
1755
|
+
private renderOff;
|
|
1756
|
+
/** The renders that run at one time (`maxRenders`). */
|
|
1757
|
+
private readonly renders;
|
|
1758
|
+
/** These tokens apply because the `User-Agent` links the bot page (`obeyDefaultToken`). */
|
|
1759
|
+
private readonly pageTokens;
|
|
1760
|
+
/** The deadlines of the requests that have one, by the signal that the scheduler gets for each of them. */
|
|
1761
|
+
private readonly deadlines;
|
|
1762
|
+
/** This signer signs each request. It holds the key, and the crawler writes only its key id and its agent. */
|
|
1763
|
+
private readonly signer;
|
|
1764
|
+
constructor(options: CrawlerOptions);
|
|
1765
|
+
private iso;
|
|
1766
|
+
private policyOf;
|
|
1767
|
+
/**
|
|
1768
|
+
* This method gives the signal of a request for the scheduler. A request with a deadline gets a
|
|
1769
|
+
* signal of its own, and the clock of the scheduler reads the deadline of that signal.
|
|
1770
|
+
*/
|
|
1771
|
+
private paced;
|
|
1772
|
+
/** This method throws `DeadlineReached` when the deadline of a request came before its slot. */
|
|
1773
|
+
private beforeDeadline;
|
|
1774
|
+
private emit;
|
|
1775
|
+
private track;
|
|
1776
|
+
/** This method gives the validator memo of a host. The memo is a hint, so an error of its store gives no memo. */
|
|
1777
|
+
private memoOf;
|
|
1778
|
+
private stopHost;
|
|
1779
|
+
private record;
|
|
1780
|
+
private skip;
|
|
1781
|
+
/** This method reads the cache entry of a URL, with the fields that the crawler keeps in memory. */
|
|
1782
|
+
private lookup;
|
|
1783
|
+
private store;
|
|
1784
|
+
/** This method gives the target of a stored redirect, when the crawler can follow it. */
|
|
1785
|
+
private followable;
|
|
1786
|
+
/** This method answers from the cache, when the stored entry is young or fresh enough. */
|
|
1787
|
+
private fromCache;
|
|
1788
|
+
/**
|
|
1789
|
+
* This method gives the signature headers of one request, and the facts of the capture: the key id
|
|
1790
|
+
* and the agent. With no signer, it gives nothing. A signer that throws gives the note of a skip,
|
|
1791
|
+
* because the crawler sends no unsigned request when it has a signer.
|
|
1792
|
+
*/
|
|
1793
|
+
private signFor;
|
|
1794
|
+
private requestHeaders;
|
|
1795
|
+
/**
|
|
1796
|
+
* This method records the result of a request for the host, and it gives the rest of the host.
|
|
1797
|
+
* The rest comes from `restAfter`. A 429 or 503 waits, an error counts toward a stop, and a
|
|
1798
|
+
* refusal stops the host for a time.
|
|
1799
|
+
*/
|
|
1800
|
+
private settle;
|
|
1801
|
+
/**
|
|
1802
|
+
* This method stops the host of a URL as a challenge does: for the `challengeStopMs` of the policy.
|
|
1803
|
+
* The runtime uses it when a page that answered 200 is a bot check (`meta.blocked`).
|
|
1804
|
+
*/
|
|
1805
|
+
reportBlocked(url: string, what: string): void;
|
|
1806
|
+
/**
|
|
1807
|
+
* This method applies the rules that a fetch and a render share. The sequence is the URL, the
|
|
1808
|
+
* options, the egress policy, the product tokens and the rules for an agent. Then come the
|
|
1809
|
+
* cache, the stops of the host and robots.txt. It gives the answer when a rule decides, or the
|
|
1810
|
+
* facts that the transport needs.
|
|
1811
|
+
*/
|
|
1812
|
+
private admit;
|
|
1813
|
+
/** This method fetches a URL. It follows the redirects up to the maximum, and each step is a separate decision. */
|
|
1814
|
+
fetch(request_: FetchRequest): Promise<Fetched>;
|
|
1815
|
+
private follow;
|
|
1816
|
+
/** This method joins a request to an equal request in progress, so one URL gets one request at a time. */
|
|
1817
|
+
private hop;
|
|
1818
|
+
private knownRobots;
|
|
1819
|
+
/**
|
|
1820
|
+
* This method gives the robots.txt of the origin of a URL. A good copy stays for the TTL of the
|
|
1821
|
+
* policy. An unreachable verdict stays for some minutes, with a backoff and the `Retry-After`
|
|
1822
|
+
* of the server. During that time the last good copy applies, when there is one (RFC 9309 §2.4).
|
|
1823
|
+
*/
|
|
1824
|
+
private robotsFor;
|
|
1825
|
+
/**
|
|
1826
|
+
* This method gives the robots.txt rules that apply to a URL, as the crawler applies them. They
|
|
1827
|
+
* come from its memory, or from a fetch of `/robots.txt` with its redirects. A robots.txt that
|
|
1828
|
+
* the crawler cannot read gives rules that permit nothing, and `unreachable` gives the reason.
|
|
1829
|
+
*/
|
|
1830
|
+
readRobots(url: string, marks?: {
|
|
1831
|
+
readonly scope?: string | undefined;
|
|
1832
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
1833
|
+
}): Promise<{
|
|
1834
|
+
readonly robots: Robots;
|
|
1835
|
+
readonly capture: string | undefined;
|
|
1836
|
+
readonly stale?: string | undefined;
|
|
1837
|
+
}>;
|
|
1838
|
+
/**
|
|
1839
|
+
* This method tells if robots.txt lets the crawler fetch a URL as a page. The rules of the token
|
|
1840
|
+
* of the crawler and of the agents of `onBehalfOf` apply, as for a fetch. It reads robots.txt as
|
|
1841
|
+
* a fetch does, so a robots.txt that the crawler cannot read permits nothing. When the signal
|
|
1842
|
+
* aborts, the promise rejects, and a fetch of robots.txt in progress continues.
|
|
1843
|
+
*/
|
|
1844
|
+
robotsAllow(url: string, options?: {
|
|
1845
|
+
readonly onBehalfOf?: readonly string[] | undefined;
|
|
1846
|
+
readonly scope?: string | undefined;
|
|
1847
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
1848
|
+
readonly signal?: AbortSignal | undefined;
|
|
1849
|
+
}): Promise<{
|
|
1850
|
+
readonly allowed: boolean;
|
|
1851
|
+
readonly note: string | undefined;
|
|
1852
|
+
/** These are the lines of usage signals (`Content-Signal`) of robots.txt that apply to the URL, when there are any. The archived fallback applies them to a copy. */
|
|
1853
|
+
readonly signals?: readonly string[] | undefined;
|
|
1854
|
+
}>;
|
|
1855
|
+
/** This method reads the stored copy of a robots.txt, when it is younger than the limit of the policy. */
|
|
1856
|
+
private storedRobots;
|
|
1857
|
+
/** This method adds the scope and the labels of one request to the requests that wait for a robots.txt. The most private scope wins. */
|
|
1858
|
+
private join;
|
|
1859
|
+
private fetchRobots;
|
|
1860
|
+
private fetchOnce;
|
|
1861
|
+
/**
|
|
1862
|
+
* This method gives the function that decides each request of a rendered page. It refuses an
|
|
1863
|
+
* address that the egress policy refuses, and a URL of the page's host that robots.txt
|
|
1864
|
+
* disallows. For a different host, it uses a robots.txt that the crawler holds, when there is one.
|
|
1865
|
+
*/
|
|
1866
|
+
private allowRequestFor;
|
|
1867
|
+
/**
|
|
1868
|
+
* This method tells why the browser did not go on to a URL that the page moved to: the egress
|
|
1869
|
+
* policy or robots.txt. For a different site, it uses a robots.txt that the crawler holds. It
|
|
1870
|
+
* gives undefined for the page itself, or for a URL that it cannot read.
|
|
1871
|
+
*/
|
|
1872
|
+
private movedRefusal;
|
|
1873
|
+
/**
|
|
1874
|
+
* This method renders a page in the headless browser and captures what the page shows. The rules
|
|
1875
|
+
* of a fetch apply:
|
|
1876
|
+
*
|
|
1877
|
+
* - the egress policy and robots.txt, for the page, its final URL and each request of the page
|
|
1878
|
+
* - one request at a time for each host
|
|
1879
|
+
* - the backoff after a 429 or a 503
|
|
1880
|
+
* - a capture for each decision
|
|
1881
|
+
*
|
|
1882
|
+
* A render costs the site more than a fetch, so the host rests longer after one. A render that
|
|
1883
|
+
* the browser refuses (`RenderRefused`) does not count against the host.
|
|
1884
|
+
*/
|
|
1885
|
+
render(request_: RenderRequest): Promise<Fetched>;
|
|
1886
|
+
private renderOnce;
|
|
1887
|
+
/**
|
|
1888
|
+
* This method removes the stops and the failure counts of one host, or of each host. It also
|
|
1889
|
+
* forgets the robots.txt verdicts that came from an unreachable robots.txt. The pace of each host
|
|
1890
|
+
* stays. A process that runs for a long time uses it, for example after the network comes back.
|
|
1891
|
+
*/
|
|
1892
|
+
resetHosts(host?: string): void;
|
|
1893
|
+
/**
|
|
1894
|
+
* This method gives the stop of the host of a URL, or undefined when the host is not stopped. A
|
|
1895
|
+
* watch uses it, so a page of a stopped host is due again at the end of the stop.
|
|
1896
|
+
*/
|
|
1897
|
+
hostStop(url: string): ReturnType<HostStateStore["stopped"]>;
|
|
1898
|
+
/**
|
|
1899
|
+
* This method closes the crawler. Work in the queue ends as `skipped` with the note
|
|
1900
|
+
* `crawler closed`, and the crawler writes no capture after the close. Then it closes the
|
|
1901
|
+
* browser, the log and its own dispatcher. A second close waits for the first.
|
|
1902
|
+
*/
|
|
1903
|
+
close(): Promise<void>;
|
|
1904
|
+
}
|
|
1905
|
+
//#endregion
|
|
1906
|
+
//#region src/diff.d.ts
|
|
1907
|
+
/**
|
|
1908
|
+
* A diff compares two versions of a stored document. It gives the changes of the text as a line
|
|
1909
|
+
* diff, with a word diff inside each changed line. It gives the changes of the title, the URL and
|
|
1910
|
+
* the metadata (the JSON-LD too) as changes at a path.
|
|
1911
|
+
*
|
|
1912
|
+
* The line diff is the algorithm of Myers (1986). Its time is linear in the length of the texts
|
|
1913
|
+
* when the change is small. A budget of steps (`DIFF_LIMITS`) bounds the time of a large change.
|
|
1914
|
+
* After the budget, the diff gives the rest of the change as one removal and one addition. Then
|
|
1915
|
+
* the result says that it is not minimal. A huge text gives its first part, and the result says so.
|
|
1916
|
+
*
|
|
1917
|
+
* A held-back version gives no text and no title to a diff, and only its structural metadata.
|
|
1918
|
+
*/
|
|
1919
|
+
/** These are the limits of a diff. Each limit bounds the time or the size of a result. */
|
|
1920
|
+
export declare const DIFF_LIMITS: {
|
|
1921
|
+
/** The characters of each text that a diff compares. A longer text gives its first part. */
|
|
1922
|
+
readonly maxChars: 1_000_000;
|
|
1923
|
+
/** The lines of each text that a diff compares. A longer text gives its first lines. */
|
|
1924
|
+
readonly maxLines: 50_000;
|
|
1925
|
+
/** The steps of the line diff. After them, the rest of the change is one removal and one addition. */
|
|
1926
|
+
readonly maxSteps: 1_000_000;
|
|
1927
|
+
/** The steps of the word diffs of all the changed lines together. */
|
|
1928
|
+
readonly maxWordSteps: 1_000_000;
|
|
1929
|
+
/** The words of one line that a word diff compares. A longer line gets no word diff. */
|
|
1930
|
+
readonly maxLineWords: 5_000;
|
|
1931
|
+
/** The lines of context before and after each change. */
|
|
1932
|
+
readonly context: 2;
|
|
1933
|
+
/** The lines that the hunks of a result show. The summary counts each line. */
|
|
1934
|
+
readonly maxHunkLines: 2_000;
|
|
1935
|
+
/** The changes of the metadata that a result shows. The summary counts each change. */
|
|
1936
|
+
readonly maxMetaChanges: 200;
|
|
1937
|
+
/** The depth of the metadata that a diff compares. A deeper change is a change of the whole value. */
|
|
1938
|
+
readonly maxMetaDepth: 16;
|
|
1939
|
+
/** The characters of the JSON of each value of a change of the metadata. */
|
|
1940
|
+
readonly maxValueChars: 300;
|
|
1941
|
+
};
|
|
1942
|
+
/** These are the limits of a diff, one by one. `DIFF_LIMITS` gives the defaults. */
|
|
1943
|
+
type DiffLimits = { readonly [K in keyof typeof DIFF_LIMITS]: number; };
|
|
1944
|
+
/** This is a part of a changed line: words that stay, words that the new text adds, or words that it removes. */
|
|
1945
|
+
interface WordPart {
|
|
1946
|
+
readonly op: "same" | "added" | "removed";
|
|
1947
|
+
readonly text: string;
|
|
1948
|
+
}
|
|
1949
|
+
/**
|
|
1950
|
+
* This is one line of a hunk. A `same` line is context. A `changed` line pairs a removed line with
|
|
1951
|
+
* an added line, and `words` gives the word diff, or null when the word budget ended.
|
|
1952
|
+
*/
|
|
1953
|
+
type DiffLine = {
|
|
1954
|
+
readonly op: "same" | "added" | "removed";
|
|
1955
|
+
readonly text: string;
|
|
1956
|
+
} | {
|
|
1957
|
+
readonly op: "changed";
|
|
1958
|
+
readonly before: string;
|
|
1959
|
+
readonly after: string;
|
|
1960
|
+
readonly words: readonly WordPart[] | null;
|
|
1961
|
+
};
|
|
1962
|
+
/** This is a part of the diff of the text: the changed lines with some context, as in a unified diff. */
|
|
1963
|
+
interface DiffHunk {
|
|
1964
|
+
/** This value is the number (from 1) of the first line of the hunk in the old text. */
|
|
1965
|
+
readonly from_line: number;
|
|
1966
|
+
/** This value is the number of lines of the old text in the hunk. */
|
|
1967
|
+
readonly from_count: number;
|
|
1968
|
+
/** This value is the number (from 1) of the first line of the hunk in the new text. */
|
|
1969
|
+
readonly to_line: number;
|
|
1970
|
+
/** This value is the number of lines of the new text in the hunk. */
|
|
1971
|
+
readonly to_count: number;
|
|
1972
|
+
readonly lines: readonly DiffLine[];
|
|
1973
|
+
}
|
|
1974
|
+
/**
|
|
1975
|
+
* This is one change of the title, the URL or the metadata. The path names the value, for
|
|
1976
|
+
* example `meta.jsonld[0].openingHours`. `before` and `after` are the JSON of the values, cut to
|
|
1977
|
+
* `DIFF_LIMITS.maxValueChars`.
|
|
1978
|
+
*/
|
|
1979
|
+
interface MetaChange {
|
|
1980
|
+
readonly path: string;
|
|
1981
|
+
readonly change: "added" | "removed" | "changed";
|
|
1982
|
+
/** This text is the JSON of the old value, or null when the new version adds the value. */
|
|
1983
|
+
readonly before: string | null;
|
|
1984
|
+
/** This text is the JSON of the new value, or null when the new version removes the value. */
|
|
1985
|
+
readonly after: string | null;
|
|
1986
|
+
}
|
|
1987
|
+
/** These are the counts of a diff. */
|
|
1988
|
+
interface DiffSummary {
|
|
1989
|
+
/** This value counts the lines that only the new text has. */
|
|
1990
|
+
readonly lines_added: number;
|
|
1991
|
+
/** This value counts the lines that only the old text has. */
|
|
1992
|
+
readonly lines_removed: number;
|
|
1993
|
+
/** This value counts the pairs of a removed line and an added line that the result shows as one changed line. */
|
|
1994
|
+
readonly lines_changed: number;
|
|
1995
|
+
/** This value counts the words that the new text adds, in the added lines and in the changed lines. */
|
|
1996
|
+
readonly words_added: number;
|
|
1997
|
+
/** This value counts the words that the new text removes, in the removed lines and in the changed lines. */
|
|
1998
|
+
readonly words_removed: number;
|
|
1999
|
+
/** This value counts the changes of the title, the URL and the metadata. */
|
|
2000
|
+
readonly meta_changes: number;
|
|
2001
|
+
}
|
|
2002
|
+
/** This is the input of `diffTexts`: the parts of a version that a diff compares. */
|
|
2003
|
+
interface DiffInput {
|
|
2004
|
+
readonly text: string;
|
|
2005
|
+
readonly fields: Readonly<Record<string, unknown>>;
|
|
2006
|
+
}
|
|
2007
|
+
/** This is the result of `diffTexts`. */
|
|
2008
|
+
interface TextDiff {
|
|
2009
|
+
readonly summary: DiffSummary;
|
|
2010
|
+
readonly hunks: readonly DiffHunk[];
|
|
2011
|
+
readonly meta: readonly MetaChange[];
|
|
2012
|
+
/** This value is true when the text and the fields are the same. */
|
|
2013
|
+
readonly same: boolean;
|
|
2014
|
+
/** This value is true when the line diff is minimal and each changed line has its word diff. */
|
|
2015
|
+
readonly exact: boolean;
|
|
2016
|
+
/** This value is true when the result leaves out a part. It can leave out the end of a long text, lines of the hunks or changes of the metadata. */
|
|
2017
|
+
readonly truncated: boolean;
|
|
2018
|
+
/** This value counts the steps of the line diff and of the word diffs. A test reads it: a step count does not change with the computer. */
|
|
2019
|
+
readonly steps: number;
|
|
2020
|
+
readonly notes: readonly string[];
|
|
2021
|
+
}
|
|
2022
|
+
/**
|
|
2023
|
+
* This function compares two versions: the lines of the text, the words of each changed line, and
|
|
2024
|
+
* the fields (the title, the URL and the metadata) at their paths. It writes nothing, and its time
|
|
2025
|
+
* and its result stay inside the limits.
|
|
2026
|
+
*/
|
|
2027
|
+
export declare const diffTexts: (before: DiffInput, after: DiffInput, given?: Partial<DiffLimits>) => TextDiff;
|
|
2028
|
+
/** A version of a document for a diff: a capture id, or the version that was current at a time. */
|
|
2029
|
+
type VersionChoice = string | {
|
|
2030
|
+
readonly at: string | Date;
|
|
2031
|
+
};
|
|
2032
|
+
/** These options choose the two versions of a diff. */
|
|
2033
|
+
interface DiffOptions {
|
|
2034
|
+
/**
|
|
2035
|
+
* This version is the old side: a capture id, or `{ at }`. The default is the version before
|
|
2036
|
+
* `to` with the same recipe and the same kind of capture (plain or rendered).
|
|
2037
|
+
*/
|
|
2038
|
+
readonly from?: VersionChoice | undefined;
|
|
2039
|
+
/** This version is the new side: a capture id, or `{ at }`. The default is the newest version. */
|
|
2040
|
+
readonly to?: VersionChoice | undefined;
|
|
2041
|
+
/** These values change the limits of the diff (`DIFF_LIMITS`). */
|
|
2042
|
+
readonly limits?: Partial<DiffLimits> | undefined;
|
|
2043
|
+
}
|
|
2044
|
+
/** This is one side of a diff: the version that it compares. */
|
|
2045
|
+
interface DiffSide {
|
|
2046
|
+
/** This id is the capture of the version. A citation names it. */
|
|
2047
|
+
readonly capture: string;
|
|
2048
|
+
/** This time (ISO 8601) is the fetch time of the version. */
|
|
2049
|
+
readonly fetched_at: string;
|
|
2050
|
+
/** This value is the length of the text of the version. */
|
|
2051
|
+
readonly chars: number;
|
|
2052
|
+
readonly rendered: boolean;
|
|
2053
|
+
/** This value is true when the version is held back, so the diff does not show its text. */
|
|
2054
|
+
readonly held_back: boolean;
|
|
2055
|
+
/** This time (ISO 8601) is the `at` that chose the version, or null for a capture id or a default. */
|
|
2056
|
+
readonly at: string | null;
|
|
2057
|
+
}
|
|
2058
|
+
/** The result of a diff of two versions of a stored document. */
|
|
2059
|
+
interface DiffResult {
|
|
2060
|
+
readonly doc: string;
|
|
2061
|
+
readonly url: string | null;
|
|
2062
|
+
readonly from: DiffSide;
|
|
2063
|
+
readonly to: DiffSide;
|
|
2064
|
+
/**
|
|
2065
|
+
* This value is true when the two versions have the same text, title, URL and metadata. It is
|
|
2066
|
+
* null when a side is held back, because then the diff does not compare the texts.
|
|
2067
|
+
*/
|
|
2068
|
+
readonly same: boolean | null;
|
|
2069
|
+
readonly summary: DiffSummary;
|
|
2070
|
+
readonly hunks: readonly DiffHunk[];
|
|
2071
|
+
readonly meta: readonly MetaChange[];
|
|
2072
|
+
/** This value is true when a side is held back. Then the result has no text and no title, and only the structural metadata. */
|
|
2073
|
+
readonly held_back: boolean;
|
|
2074
|
+
/** This value is true when the line diff is minimal and each changed line has its word diff. */
|
|
2075
|
+
readonly exact: boolean;
|
|
2076
|
+
/** This value is true when the result leaves out a part. The note tells which part. */
|
|
2077
|
+
readonly truncated: boolean;
|
|
2078
|
+
/** This value counts the steps of the diff. */
|
|
2079
|
+
readonly steps: number;
|
|
2080
|
+
readonly note: string | null;
|
|
2081
|
+
}
|
|
2082
|
+
/**
|
|
2083
|
+
* This function compares two versions of a stored document, and writes nothing. It applies the
|
|
2084
|
+
* rule for a held-back document. When the document or a version is held back, the diff compares
|
|
2085
|
+
* only the structural metadata, and it shows no text and no title.
|
|
2086
|
+
*
|
|
2087
|
+
* @beta
|
|
2088
|
+
*/
|
|
2089
|
+
export declare const diffDocument: (runtime: CrawlRuntime, id: string, options?: DiffOptions) => Promise<DiffResult>;
|
|
2090
|
+
/**
|
|
2091
|
+
* This function gives the text of a diff for a person or an assistant. The text has the counts, the
|
|
2092
|
+
* hunks as a unified diff, and the changes of the metadata. A changed line marks its words with `[-removed-]`
|
|
2093
|
+
* and `{+added+}`. The text holds the text of the pages, so the MCP server puts it in an envelope.
|
|
2094
|
+
*
|
|
2095
|
+
* @beta
|
|
2096
|
+
*/
|
|
2097
|
+
export declare const formatDiff: (diff: Pick<DiffResult, "summary" | "hunks" | "meta">) => string;
|
|
2098
|
+
//#endregion
|
|
2099
|
+
//#region src/sitemaps.d.ts
|
|
2100
|
+
/**
|
|
2101
|
+
* The sitemap reader reads the files that list the pages of a site. These are an XML sitemap, a
|
|
2102
|
+
* sitemap index, an RSS 2.0 or RSS 1.0 feed and an Atom feed. A text sitemap with one URL on each
|
|
2103
|
+
* line is a file of this kind too. The reader is small and strict, because the files come from the web:
|
|
2104
|
+
*
|
|
2105
|
+
* - It reads the text one time from the start to the end, and it builds no tree. Its time is
|
|
2106
|
+
* linear in the length of the text.
|
|
2107
|
+
* - It expands no entity. It knows only the five entities of XML and the character references. A
|
|
2108
|
+
* DTD in the file changes nothing.
|
|
2109
|
+
* - It stops at a maximum depth of elements, at a maximum number of entries and at the first
|
|
2110
|
+
* error in the markup. The entries before the stop stay.
|
|
2111
|
+
*
|
|
2112
|
+
* It imports no Node.js module, so a browser can use it.
|
|
2113
|
+
*/
|
|
2114
|
+
/** The kind of a file: the name of its root element, or `text` for a text sitemap. */
|
|
2115
|
+
type SitemapKind = "urlset" | "sitemapindex" | "rss" | "atom" | "rdf" | "text" | "unknown";
|
|
2116
|
+
/** The values of `<changefreq>` in the sitemap protocol. */
|
|
2117
|
+
type Changefreq = "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
2118
|
+
/**
|
|
2119
|
+
* One entry of a file: a page, or a sitemap of an index.
|
|
2120
|
+
*
|
|
2121
|
+
* @beta
|
|
2122
|
+
*/
|
|
2123
|
+
interface SitemapEntry {
|
|
2124
|
+
/** This URL is the canonical form of the URL of the entry. */
|
|
2125
|
+
readonly url: string;
|
|
2126
|
+
/** This time (ISO 8601) is the last change that the file gives, or null. */
|
|
2127
|
+
readonly lastmod: string | null;
|
|
2128
|
+
/** This value is the `<changefreq>` of a sitemap entry, or null. */
|
|
2129
|
+
readonly changefreq: Changefreq | null;
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* The result of one file.
|
|
2133
|
+
*
|
|
2134
|
+
* @beta
|
|
2135
|
+
*/
|
|
2136
|
+
interface SitemapRead {
|
|
2137
|
+
readonly kind: SitemapKind;
|
|
2138
|
+
/** These entries are the pages of the file, or the sitemaps of an index, in the order of the file. */
|
|
2139
|
+
readonly entries: readonly SitemapEntry[];
|
|
2140
|
+
/** This value counts the entries with no valid http(s) URL. */
|
|
2141
|
+
readonly invalid: number;
|
|
2142
|
+
/** This value is false when the reader stopped before the end of the file. */
|
|
2143
|
+
readonly complete: boolean;
|
|
2144
|
+
/** These notes tell why the reader stopped, or what it ignored. */
|
|
2145
|
+
readonly notes: readonly string[];
|
|
2146
|
+
}
|
|
2147
|
+
/**
|
|
2148
|
+
* These limits stop the reader on a large or a hostile file.
|
|
2149
|
+
*
|
|
2150
|
+
* @beta
|
|
2151
|
+
*/
|
|
2152
|
+
interface SitemapLimits {
|
|
2153
|
+
/** This value is the maximum number of entries. The default is 50,000, the limit of the sitemap protocol. */
|
|
2154
|
+
readonly maxEntries?: number | undefined;
|
|
2155
|
+
/** This value is the maximum depth of the elements. The default is 32. */
|
|
2156
|
+
readonly maxDepth?: number | undefined;
|
|
2157
|
+
}
|
|
2158
|
+
/**
|
|
2159
|
+
* These are the default limits of the reader, and the maximum length of one value.
|
|
2160
|
+
*
|
|
2161
|
+
* @beta
|
|
2162
|
+
*/
|
|
2163
|
+
export declare const SITEMAP_LIMITS: {
|
|
2164
|
+
readonly maxEntries: 50_000;
|
|
2165
|
+
readonly maxDepth: 32;
|
|
2166
|
+
/** A value (a URL or a date) longer than this number of characters is not valid. */
|
|
2167
|
+
readonly maxValue: 4096;
|
|
2168
|
+
};
|
|
2169
|
+
/**
|
|
2170
|
+
* This function changes a date of a sitemap or a feed into ISO 8601. It reads the W3C form of the
|
|
2171
|
+
* sitemap protocol and Atom, and the mail form of RSS. A time with no zone is UTC. It gives null
|
|
2172
|
+
* for a text that it cannot read.
|
|
2173
|
+
*
|
|
2174
|
+
* @beta
|
|
2175
|
+
*/
|
|
2176
|
+
export declare const feedDate: (value: string) => string | null;
|
|
2177
|
+
/**
|
|
2178
|
+
* This function reads a sitemap, a sitemap index, an RSS or Atom feed, or a text sitemap. The
|
|
2179
|
+
* root element of the file tells its kind. The entries of a feed can have URLs relative to `base`
|
|
2180
|
+
* (the URL of the file). The entries of a sitemap must have absolute URLs.
|
|
2181
|
+
*
|
|
2182
|
+
* @beta
|
|
2183
|
+
*/
|
|
2184
|
+
export declare const readSitemap: (text: string, base: string, limits?: SitemapLimits) => SitemapRead;
|
|
2185
|
+
//#endregion
|
|
2186
|
+
//#region src/discover.d.ts
|
|
2187
|
+
/**
|
|
2188
|
+
* Discovery finds the pages of a site from its sitemaps and its feeds. Each file is a fetch of the
|
|
2189
|
+
* crawler, so each polite rule applies to it. These rules are robots.txt, one request at a time for
|
|
2190
|
+
* each host, the pace, the stops, the egress policy and the capture log. These are the sources, in sequence:
|
|
2191
|
+
*
|
|
2192
|
+
* 1. The page at the URL of the site, for the RSS and Atom feeds that it links.
|
|
2193
|
+
* 2. The feeds that the caller gives.
|
|
2194
|
+
* 3. The sitemaps that the caller gives, and the `Sitemap:` lines of robots.txt. When there are
|
|
2195
|
+
* none, `/sitemap.xml`.
|
|
2196
|
+
* 4. The sitemaps of each sitemap index, the newest first, to a maximum depth.
|
|
2197
|
+
*
|
|
2198
|
+
* Discovery keeps a URL only when it is in scope (the same site by default) and robots.txt permits
|
|
2199
|
+
* it. It stops at a maximum of files, a maximum of URLs and a maximum time.
|
|
2200
|
+
*/
|
|
2201
|
+
/**
|
|
2202
|
+
* These are the default limits of `discover`.
|
|
2203
|
+
*
|
|
2204
|
+
* @beta
|
|
2205
|
+
*/
|
|
2206
|
+
export declare const DISCOVER_LIMITS: {
|
|
2207
|
+
/** The most sitemap and feed files that one discovery reads. */
|
|
2208
|
+
readonly maxFiles: 20;
|
|
2209
|
+
/** The most URLs that one discovery gives. */
|
|
2210
|
+
readonly maxUrls: 10_000;
|
|
2211
|
+
/** The most bytes of one file, after decompression: 10 MiB. */
|
|
2212
|
+
readonly maxBytes: 10_485_760;
|
|
2213
|
+
/** The most levels of sitemap index under a file that discovery found first. */
|
|
2214
|
+
readonly maxNesting: 3;
|
|
2215
|
+
/** The longest time of one discovery: 1 minute. */
|
|
2216
|
+
readonly budgetMs: 60_000;
|
|
2217
|
+
/** The most origins whose robots.txt one discovery reads for the URLs that it finds. */
|
|
2218
|
+
readonly maxOrigins: 10;
|
|
2219
|
+
/** The most feeds that discovery reads from the links of the page. */
|
|
2220
|
+
readonly maxLinkedFeeds: 5;
|
|
2221
|
+
};
|
|
2222
|
+
/** These options control a discovery. Each limit has a default in `DISCOVER_LIMITS`. */
|
|
2223
|
+
interface DiscoverOptions extends ScopeOptions {
|
|
2224
|
+
/** This value is the most sitemap and feed files to read. The default is 20. */
|
|
2225
|
+
readonly maxFiles?: number | undefined;
|
|
2226
|
+
/** This value is the most URLs to give. The default is 10,000. */
|
|
2227
|
+
readonly maxUrls?: number | undefined;
|
|
2228
|
+
/** This value is the most bytes of one file, after decompression. The default is 10 MiB. */
|
|
2229
|
+
readonly maxBytes?: number | undefined;
|
|
2230
|
+
/** This value is the most levels of sitemap index to follow. The default is 3. */
|
|
2231
|
+
readonly maxNesting?: number | undefined;
|
|
2232
|
+
/** This value is the longest time of the discovery, in milliseconds. The default is 1 minute. */
|
|
2233
|
+
readonly budgetMs?: number | undefined;
|
|
2234
|
+
/** These sitemaps are read too, in addition to the `Sitemap:` lines of robots.txt. */
|
|
2235
|
+
readonly sitemaps?: readonly string[] | undefined;
|
|
2236
|
+
/** These RSS or Atom feeds are read too. */
|
|
2237
|
+
readonly feeds?: readonly string[] | undefined;
|
|
2238
|
+
/** With the default value true, discovery reads the page of the site for the feeds that it links. */
|
|
2239
|
+
readonly linkedFeeds?: boolean | undefined;
|
|
2240
|
+
/**
|
|
2241
|
+
* A stored copy of a file younger than this age answers, with no request. The default is `24h`.
|
|
2242
|
+
* The value `auto` uses the freshness that the server gave.
|
|
2243
|
+
*/
|
|
2244
|
+
readonly maxAge?: Duration | undefined;
|
|
2245
|
+
/** These agents get their robots.txt rules applied to each fetch and to each URL. */
|
|
2246
|
+
readonly onBehalfOf?: readonly string[] | undefined;
|
|
2247
|
+
/** When this signal aborts, the discovery stops, and it gives what it found. */
|
|
2248
|
+
readonly signal?: AbortSignal | undefined;
|
|
2249
|
+
/** This label goes into the capture records of the discovery. */
|
|
2250
|
+
readonly scope?: string | undefined;
|
|
2251
|
+
/** These labels go into the capture records of the discovery. */
|
|
2252
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
2253
|
+
}
|
|
2254
|
+
/**
|
|
2255
|
+
* These options control `discoverSite`: the options of `discover`, and a deadline.
|
|
2256
|
+
*
|
|
2257
|
+
* @beta
|
|
2258
|
+
*/
|
|
2259
|
+
interface DiscoverRunOptions extends DiscoverOptions {
|
|
2260
|
+
/** This time (milliseconds since the epoch) ends the discovery when it comes before the end of `budgetMs`. */
|
|
2261
|
+
readonly deadline?: number | undefined;
|
|
2262
|
+
}
|
|
2263
|
+
/** One URL that a discovery found. */
|
|
2264
|
+
interface DiscoveredUrl {
|
|
2265
|
+
/** This URL is the canonical form of the URL of the page. */
|
|
2266
|
+
readonly url: string;
|
|
2267
|
+
/** This time (ISO 8601) is the last change that the file gave, or null. */
|
|
2268
|
+
readonly lastmod: string | null;
|
|
2269
|
+
readonly changefreq: Changefreq | null;
|
|
2270
|
+
/** This value tells if a sitemap or a feed listed the URL. */
|
|
2271
|
+
readonly via: "sitemap" | "feed";
|
|
2272
|
+
/** This URL is the file that listed the URL first. */
|
|
2273
|
+
readonly source: string;
|
|
2274
|
+
}
|
|
2275
|
+
/** One sitemap or feed file that a discovery read, or tried to read. */
|
|
2276
|
+
interface DiscoverSource {
|
|
2277
|
+
readonly url: string;
|
|
2278
|
+
readonly via: "sitemap" | "feed";
|
|
2279
|
+
/** This value tells how discovery found the file. */
|
|
2280
|
+
readonly from: "robots.txt" | "default" | "given" | "page" | "index";
|
|
2281
|
+
/** This value is the kind that the reader found, or null for a file that the crawler did not get. */
|
|
2282
|
+
readonly kind: SitemapKind | null;
|
|
2283
|
+
/** This value is the outcome of the fetch: `ok`, `not-modified`, `skipped`, `not-found` and others. */
|
|
2284
|
+
readonly outcome: string;
|
|
2285
|
+
readonly status: number;
|
|
2286
|
+
/** This id is the capture of the file. */
|
|
2287
|
+
readonly capture: string | null;
|
|
2288
|
+
readonly fetched_at: string | null;
|
|
2289
|
+
readonly from_cache: boolean;
|
|
2290
|
+
/** This value is the number of entries that the file listed. */
|
|
2291
|
+
readonly entries: number;
|
|
2292
|
+
/** This value is the number of new URLs that discovery kept from the file. */
|
|
2293
|
+
readonly urls: number;
|
|
2294
|
+
/** This value is true when the file was longer than `maxBytes`, so the reader read only its first part. */
|
|
2295
|
+
readonly truncated: boolean;
|
|
2296
|
+
readonly note: string | null;
|
|
2297
|
+
}
|
|
2298
|
+
/** The reason that a discovery stopped. */
|
|
2299
|
+
type DiscoverStop = "done" | "max-files" | "max-urls" | "budget" | "cancelled" | "closed" | "robots";
|
|
2300
|
+
/** The result of a discovery. */
|
|
2301
|
+
interface DiscoverResult {
|
|
2302
|
+
/** This URL is the origin of the site. */
|
|
2303
|
+
readonly site: string;
|
|
2304
|
+
/** These URLs are the pages that discovery found, the newest `lastmod` first. */
|
|
2305
|
+
readonly urls: readonly DiscoveredUrl[];
|
|
2306
|
+
/** These are the files that discovery read, in the sequence of the reads. */
|
|
2307
|
+
readonly sources: readonly DiscoverSource[];
|
|
2308
|
+
/** These values count the URLs that discovery did not keep, for each reason. */
|
|
2309
|
+
readonly dropped: {
|
|
2310
|
+
/** The URLs on a different site, and the files of an index on a different site. */
|
|
2311
|
+
readonly scope: number;
|
|
2312
|
+
/** The URLs that robots.txt does not permit. */
|
|
2313
|
+
readonly robots: number;
|
|
2314
|
+
/** The URLs that discovery found before. */
|
|
2315
|
+
readonly duplicate: number;
|
|
2316
|
+
/** The entries with no valid http(s) URL. */
|
|
2317
|
+
readonly invalid: number;
|
|
2318
|
+
/** The URLs and the files over a limit: the origins, the depth of the index, or the URLs. */
|
|
2319
|
+
readonly limit: number;
|
|
2320
|
+
};
|
|
2321
|
+
readonly stopped: DiscoverStop;
|
|
2322
|
+
/** This text tells why the discovery stopped early, or null. */
|
|
2323
|
+
readonly note: string | null;
|
|
2324
|
+
}
|
|
2325
|
+
/**
|
|
2326
|
+
* This function finds the pages of a site from its sitemaps and its feeds. `PoliteCrawler.discover`
|
|
2327
|
+
* uses it. Each file is a fetch of the crawler with the recipe `sitemap` or `feed`, so the
|
|
2328
|
+
* capture log records it, and robots.txt applies to it.
|
|
2329
|
+
*
|
|
2330
|
+
* @beta
|
|
2331
|
+
*/
|
|
2332
|
+
export declare const discoverSite: (runtime: CrawlRuntime, site: string, options?: DiscoverRunOptions) => Promise<DiscoverResult>;
|
|
2333
|
+
//#endregion
|
|
2334
|
+
//#region src/embed.d.ts
|
|
2335
|
+
/**
|
|
2336
|
+
* The parts of hybrid search that need no store: the embedder interface, the chunks of a text, and
|
|
2337
|
+
* the fusion of ranked lists.
|
|
2338
|
+
*
|
|
2339
|
+
* An embedder changes texts into vectors. Two texts with a similar meaning get vectors that point
|
|
2340
|
+
* in a similar direction. Hybrid search ranks the stored documents two times: one time with the
|
|
2341
|
+
* full-text index (BM25), one time by the cosine of the vectors. Reciprocal rank fusion (RRF) then
|
|
2342
|
+
* makes one list of the two.
|
|
2343
|
+
*
|
|
2344
|
+
* `Embedder` is the extension point. `transformersEmbedder` and `ollamaEmbedder` are two adapters,
|
|
2345
|
+
* and an application can give an embedder of its own, as it can give a renderer of its own.
|
|
2346
|
+
*/
|
|
2347
|
+
/** These options go to each call of `Embedder.embed`. */
|
|
2348
|
+
interface EmbedCallOptions {
|
|
2349
|
+
/** When this signal aborts, the embedder stops and rejects with the reason of the signal. */
|
|
2350
|
+
readonly signal?: AbortSignal | undefined;
|
|
2351
|
+
/**
|
|
2352
|
+
* This value tells what the texts are: `document` (parts of stored texts, the default) or `query`
|
|
2353
|
+
* (the words of a search). Some models want a different prefix for each.
|
|
2354
|
+
*/
|
|
2355
|
+
readonly purpose?: "document" | "query" | undefined;
|
|
2356
|
+
}
|
|
2357
|
+
/**
|
|
2358
|
+
* An embedder changes texts into vectors. The vector store keeps each vector with the `id` of its
|
|
2359
|
+
* embedder, so the vectors of two models do not mix.
|
|
2360
|
+
*/
|
|
2361
|
+
interface Embedder {
|
|
2362
|
+
/** This text names the model and its version. Change it when the vectors of the model change. */
|
|
2363
|
+
readonly id: string;
|
|
2364
|
+
/** This value is the length of each vector. The value 0 tells that the embedder learns it from its first answer. */
|
|
2365
|
+
readonly dimensions: number;
|
|
2366
|
+
/** This value is the largest input of the model, in tokens. The chunks of a text fit in it. The default is 256. */
|
|
2367
|
+
readonly maxInputTokens?: number | undefined;
|
|
2368
|
+
/** This method gives one vector for each text, in the same sequence. */
|
|
2369
|
+
embed(texts: readonly string[], options: EmbedCallOptions): Promise<Float32Array[]>;
|
|
2370
|
+
/** This optional method releases the model. A later call of `embed` loads it again. */
|
|
2371
|
+
close?(): Promise<void>;
|
|
2372
|
+
}
|
|
2373
|
+
/**
|
|
2374
|
+
* These are the limits of the chunks of a text.
|
|
2375
|
+
*
|
|
2376
|
+
* @beta
|
|
2377
|
+
*/
|
|
2378
|
+
export declare const CHUNK_LIMITS: {
|
|
2379
|
+
/** A chunk has this number of tokens at most, also for a model with a larger input. */
|
|
2380
|
+
readonly maxTokens: number;
|
|
2381
|
+
/** A chunk has this number of tokens when the embedder does not give `maxInputTokens`. */
|
|
2382
|
+
readonly defaultTokens: number;
|
|
2383
|
+
/** A chunk fills this share of its limit, because the count of the tokens is an estimate. */
|
|
2384
|
+
readonly fill: number;
|
|
2385
|
+
/** Two chunks next to each other share this share of the tokens of a chunk. */
|
|
2386
|
+
readonly overlap: number;
|
|
2387
|
+
/** A text gives this number of chunks at most. The rest of a longer text gets no vector. */
|
|
2388
|
+
readonly maxChunks: number;
|
|
2389
|
+
};
|
|
2390
|
+
/**
|
|
2391
|
+
* This is one chunk of a text: a window of paragraphs, with its position in the text.
|
|
2392
|
+
*
|
|
2393
|
+
* @beta
|
|
2394
|
+
*/
|
|
2395
|
+
interface TextChunk {
|
|
2396
|
+
/** This value is the position of the chunk in the list, from 0. */
|
|
2397
|
+
readonly index: number;
|
|
2398
|
+
/** These values are the positions of the chunk in the text, in UTF-16 units. */
|
|
2399
|
+
readonly start: number;
|
|
2400
|
+
readonly end: number;
|
|
2401
|
+
readonly text: string;
|
|
2402
|
+
}
|
|
2403
|
+
/**
|
|
2404
|
+
* These options control the chunks of a text.
|
|
2405
|
+
*
|
|
2406
|
+
* @beta
|
|
2407
|
+
*/
|
|
2408
|
+
interface ChunkOptions {
|
|
2409
|
+
/** This value is the largest number of tokens in a chunk. */
|
|
2410
|
+
readonly maxTokens: number;
|
|
2411
|
+
/** This value is the number of tokens that two chunks next to each other can share. The default is 10 percent of `maxTokens`. */
|
|
2412
|
+
readonly overlapTokens?: number | undefined;
|
|
2413
|
+
/** This value is the largest number of chunks. The default is `CHUNK_LIMITS.maxChunks`. */
|
|
2414
|
+
readonly maxChunks?: number | undefined;
|
|
2415
|
+
}
|
|
2416
|
+
/**
|
|
2417
|
+
* This function estimates the number of tokens of a part of a text. A word is one token for each
|
|
2418
|
+
* six letters. A Chinese, Japanese or Thai character is one token. Each other character that is
|
|
2419
|
+
* not a space is one token. The estimate is a little high for English text, so a chunk fits.
|
|
2420
|
+
*
|
|
2421
|
+
* @beta
|
|
2422
|
+
*/
|
|
2423
|
+
export declare const estimateTokens: (text: string, start?: number, end?: number) => number;
|
|
2424
|
+
/**
|
|
2425
|
+
* This function cuts a text into chunks for an embedder. A chunk is a window of whole sentences.
|
|
2426
|
+
* It ends at the end of a paragraph when the paragraph fills half of the window or more. A
|
|
2427
|
+
* sentence that is longer than the window is cut at its spaces. Two chunks next to each other share
|
|
2428
|
+
* a few sentences (`overlapTokens`), so a passage at a cut is in one chunk. Each chunk keeps its
|
|
2429
|
+
* position in the text, so a hit points at a passage.
|
|
2430
|
+
*
|
|
2431
|
+
* @beta
|
|
2432
|
+
*/
|
|
2433
|
+
export declare const chunkText: (text: string, options: ChunkOptions) => TextChunk[];
|
|
2434
|
+
/**
|
|
2435
|
+
* This function gives the number of tokens of a chunk for an embedder. It is the input limit with a
|
|
2436
|
+
* margin for the estimate, and `CHUNK_LIMITS.maxTokens` at most.
|
|
2437
|
+
*
|
|
2438
|
+
* @beta
|
|
2439
|
+
*/
|
|
2440
|
+
export declare const chunkTokensOf: (embedder: Pick<Embedder, "maxInputTokens">) => number;
|
|
2441
|
+
/**
|
|
2442
|
+
* This function gives a copy of a vector with a length of 1. A vector of zeros stays zeros. A value that is not finite throws a `RangeError`.
|
|
2443
|
+
*
|
|
2444
|
+
* @beta
|
|
2445
|
+
*/
|
|
2446
|
+
export declare const unitVector: (vector: ArrayLike<number>) => Float32Array;
|
|
2447
|
+
/** These are the defaults of a hybrid search. */
|
|
2448
|
+
export declare const HYBRID_DEFAULTS: {
|
|
2449
|
+
/** The full-text search gives this number of candidates. Each candidate gets vectors when it has none. */
|
|
2450
|
+
readonly candidates: number;
|
|
2451
|
+
/** The vector search gives this number of documents: the documents with the closest chunks. */
|
|
2452
|
+
readonly neighbors: number;
|
|
2453
|
+
/** This value is the constant k of reciprocal rank fusion. A larger k gives less weight to the top of each list. */
|
|
2454
|
+
readonly fusionK: number;
|
|
2455
|
+
};
|
|
2456
|
+
/**
|
|
2457
|
+
* This is one item of a fused list.
|
|
2458
|
+
*
|
|
2459
|
+
* @beta
|
|
2460
|
+
*/
|
|
2461
|
+
interface FusedRank {
|
|
2462
|
+
readonly id: string;
|
|
2463
|
+
/** This value is the sum of 1 / (k + rank) over the lists that hold the item. */
|
|
2464
|
+
readonly score: number;
|
|
2465
|
+
/** These values are the rank of the item in each list, from 1, or null when a list does not hold it. */
|
|
2466
|
+
readonly ranks: ReadonlyArray<number | null>;
|
|
2467
|
+
}
|
|
2468
|
+
/**
|
|
2469
|
+
* This function fuses ranked lists with reciprocal rank fusion. Each item gets the sum of
|
|
2470
|
+
* 1 / (k + rank) over the lists that hold it, with the rank from 1. The best item is first. A tie goes to
|
|
2471
|
+
* the item with the best rank in a list, then to the earlier list, then to the smaller id. An item
|
|
2472
|
+
* that is two times in one list counts at its first place.
|
|
2473
|
+
*
|
|
2474
|
+
* @beta
|
|
2475
|
+
*/
|
|
2476
|
+
export declare const fuseRanks: (lists: ReadonlyArray<readonly string[]>, k?: number) => FusedRank[];
|
|
2477
|
+
//#endregion
|
|
2478
|
+
//#region src/extract.d.ts
|
|
2479
|
+
/**
|
|
2480
|
+
* The field check takes the values that a caller proposes for the fields of a stored page. It finds
|
|
2481
|
+
* each value in the stored text or in the JSON-LD. A model or a person gives the values. The check
|
|
2482
|
+
* does not make values: it only checks and locates the values that it gets.
|
|
2483
|
+
*
|
|
2484
|
+
* Each field gets one status:
|
|
2485
|
+
*
|
|
2486
|
+
* - `verified`: the stored text holds the exact value. The result gives the span (or the JSON-LD
|
|
2487
|
+
* path) and the capture.
|
|
2488
|
+
* - `normalized`: the stored text holds the value under a stated normalization, for example a
|
|
2489
|
+
* different number format. The result names the normalization and gives the span of the stored text.
|
|
2490
|
+
* - `ambiguous`: the value matches, but the stored text does not give one meaning. The result gives
|
|
2491
|
+
* each span, and it does not choose one.
|
|
2492
|
+
* - `not-found`: the stored text does not hold the value.
|
|
2493
|
+
* - `held-back`: the page asked not to be AI input. The result tells nothing about the value.
|
|
2494
|
+
* - `invalid`: the value is not a value of its kind, so the check did not search for it.
|
|
2495
|
+
*
|
|
2496
|
+
* The time of the check is linear in the length of the text, for a fixed set of fields.
|
|
2497
|
+
*/
|
|
2498
|
+
/** The kinds of field that the check reads. `json-ld:<path>` reads a value at a path of the JSON-LD, for example `json-ld:Product.offers.price`. */
|
|
2499
|
+
type FieldKind = "text" | "number" | "money" | "date" | "url" | "email" | "phone" | `json-ld:${string}`;
|
|
2500
|
+
/**
|
|
2501
|
+
* These kinds read the text of the page. The kind `json-ld:<path>` reads the JSON-LD.
|
|
2502
|
+
*
|
|
2503
|
+
* @beta
|
|
2504
|
+
*/
|
|
2505
|
+
export declare const FIELD_KINDS: readonly string[];
|
|
2506
|
+
/** This is the value that a caller proposes for one field. */
|
|
2507
|
+
interface FieldClaim {
|
|
2508
|
+
/** This value is the proposal. The check removes the spaces at its start and at its end. */
|
|
2509
|
+
readonly value: string | number;
|
|
2510
|
+
/** This value is the kind of the field. The default is `number` for a number and `text` for a text. */
|
|
2511
|
+
readonly kind?: FieldKind | undefined;
|
|
2512
|
+
}
|
|
2513
|
+
/** The status of one field. The module comment of `extract.ts` and the README tell what each status means. */
|
|
2514
|
+
type FieldStatus = "verified" | "normalized" | "ambiguous" | "not-found" | "held-back" | "invalid";
|
|
2515
|
+
/** These positions locate a value in the text of a version. The end is not part of the value. */
|
|
2516
|
+
interface FieldSpan {
|
|
2517
|
+
readonly start: number;
|
|
2518
|
+
readonly end: number;
|
|
2519
|
+
/** This value is `full_text` when the value is only in the whole text of a page that the reader cut. */
|
|
2520
|
+
readonly in: "text" | "full_text";
|
|
2521
|
+
}
|
|
2522
|
+
/** This is one place that an ambiguous field matches. */
|
|
2523
|
+
interface FieldCandidate {
|
|
2524
|
+
readonly span: FieldSpan | null;
|
|
2525
|
+
/** This path locates a value of the JSON-LD (`jsonld[0].offers[1].price`) or a stored link (`links[3]`). */
|
|
2526
|
+
readonly path: string | null;
|
|
2527
|
+
/** This value is the exact stored text, or the stored value of the JSON-LD. */
|
|
2528
|
+
readonly raw: string | number | boolean;
|
|
2529
|
+
readonly excerpt: string;
|
|
2530
|
+
/** These are the meanings that the stored text can have, for example two dates for `03/04/2026`. */
|
|
2531
|
+
readonly readings: readonly string[];
|
|
2532
|
+
}
|
|
2533
|
+
/** This is the check of one field, before the runtime adds the capture. */
|
|
2534
|
+
interface FieldCheck {
|
|
2535
|
+
readonly status: FieldStatus;
|
|
2536
|
+
/** This value is the kind that the check used. */
|
|
2537
|
+
readonly kind: string;
|
|
2538
|
+
readonly span: FieldSpan | null;
|
|
2539
|
+
readonly path: string | null;
|
|
2540
|
+
/** This value is the exact stored text of the match, or the stored value of the JSON-LD. */
|
|
2541
|
+
readonly raw: string | number | boolean | null;
|
|
2542
|
+
/** This text is the stored text around the match. */
|
|
2543
|
+
readonly excerpt: string | null;
|
|
2544
|
+
/** This value names the normalization of a `normalized` match, for example `thousands-separator`. Two or more names are joined with `+`. */
|
|
2545
|
+
readonly normalization: string | null;
|
|
2546
|
+
/** This value is the meaning of the stored text that matched, for example `1234.5` or `2026-03-04`. */
|
|
2547
|
+
readonly reading: string | null;
|
|
2548
|
+
/** This value is the number of places that match. */
|
|
2549
|
+
readonly count: number;
|
|
2550
|
+
/** These are the places of an ambiguous field, one for each meaning, 5 or fewer. */
|
|
2551
|
+
readonly candidates: readonly FieldCandidate[];
|
|
2552
|
+
readonly note: string | null;
|
|
2553
|
+
}
|
|
2554
|
+
/** This is the result of one field: the check, and the capture of the text that the check read. */
|
|
2555
|
+
interface FieldResult extends FieldCheck {
|
|
2556
|
+
readonly capture: string | null;
|
|
2557
|
+
/** This time (ISO 8601) is the fetch time of that capture. */
|
|
2558
|
+
readonly fetched_at: string | null;
|
|
2559
|
+
}
|
|
2560
|
+
/** These are the counts of the statuses of the fields. */
|
|
2561
|
+
interface ExtractSummary {
|
|
2562
|
+
readonly fields: number;
|
|
2563
|
+
readonly verified: number;
|
|
2564
|
+
readonly normalized: number;
|
|
2565
|
+
readonly ambiguous: number;
|
|
2566
|
+
readonly not_found: number;
|
|
2567
|
+
readonly held_back: number;
|
|
2568
|
+
readonly invalid: number;
|
|
2569
|
+
}
|
|
2570
|
+
/**
|
|
2571
|
+
* This is the text of a document that `checkFields` reads, with its metadata.
|
|
2572
|
+
*
|
|
2573
|
+
* @beta
|
|
2574
|
+
*/
|
|
2575
|
+
interface ExtractSource {
|
|
2576
|
+
readonly text: string;
|
|
2577
|
+
/** This text is the whole text of a page that the reader cut. The check reads it when the value is not in `text`. */
|
|
2578
|
+
readonly full_text?: string | undefined;
|
|
2579
|
+
/** This URL is the address of the page. The check resolves a relative URL against it. */
|
|
2580
|
+
readonly url?: string | null | undefined;
|
|
2581
|
+
/** The check reads `jsonld`, `jsonld_dropped`, `links` and `truncated` of this metadata. */
|
|
2582
|
+
readonly meta?: Readonly<Record<string, unknown>> | undefined;
|
|
2583
|
+
}
|
|
2584
|
+
/**
|
|
2585
|
+
* This is the result of `checkFields`.
|
|
2586
|
+
*
|
|
2587
|
+
* @beta
|
|
2588
|
+
*/
|
|
2589
|
+
interface FieldsChecked {
|
|
2590
|
+
readonly fields: Readonly<Record<string, FieldCheck>>;
|
|
2591
|
+
readonly summary: ExtractSummary;
|
|
2592
|
+
/** This value counts the units of work of the check. It grows in proportion to the length of the text. */
|
|
2593
|
+
readonly steps: number;
|
|
2594
|
+
}
|
|
2595
|
+
/** These options control a check of fields against a stored document. */
|
|
2596
|
+
interface ExtractOptions {
|
|
2597
|
+
/** This id checks the version of one capture, not the current version. */
|
|
2598
|
+
readonly capture?: string | undefined;
|
|
2599
|
+
/**
|
|
2600
|
+
* This time checks the version that was current at that time: an ISO 8601 text or a `Date`. A date
|
|
2601
|
+
* with no time is the start of that day in UTC. Give `capture` or `at`, not the two.
|
|
2602
|
+
*/
|
|
2603
|
+
readonly at?: string | Date | undefined;
|
|
2604
|
+
/** This value is true when a person gave this URL directly. It is the only way to check a held-back document. The crawler logs it. */
|
|
2605
|
+
readonly userProvided?: boolean | undefined;
|
|
2606
|
+
}
|
|
2607
|
+
/** This is the result of a check of fields against a stored document. */
|
|
2608
|
+
interface ExtractResult {
|
|
2609
|
+
readonly doc: string;
|
|
2610
|
+
readonly url: string | null;
|
|
2611
|
+
readonly captures: readonly string[];
|
|
2612
|
+
/** This id is the capture of the text that the check read. A held-back document gives null. */
|
|
2613
|
+
readonly capture: string | null;
|
|
2614
|
+
/** This hash is the blob of that capture. */
|
|
2615
|
+
readonly blob: string | null;
|
|
2616
|
+
/** This time (ISO 8601) is the fetch time of that capture. */
|
|
2617
|
+
readonly fetched_at: string | null;
|
|
2618
|
+
readonly held_back: boolean;
|
|
2619
|
+
/** These are the results of the fields, with the names that the caller gave. */
|
|
2620
|
+
readonly fields: Readonly<Record<string, FieldResult>>;
|
|
2621
|
+
readonly summary: ExtractSummary;
|
|
2622
|
+
readonly note: string | null;
|
|
2623
|
+
/** This time (ISO 8601) is the `at` of the check. The result has it only when the check gave `at`. */
|
|
2624
|
+
readonly at?: string | undefined;
|
|
2625
|
+
/** This value is true when `at` is before the first capture of the document. Then no field is found. */
|
|
2626
|
+
readonly before_first?: boolean | undefined;
|
|
2627
|
+
/**
|
|
2628
|
+
* This value tells that the checked text is an archived copy, with the archive and the date of the
|
|
2629
|
+
* copy. The note says so too. A citation of a value names the archive and that date.
|
|
2630
|
+
*/
|
|
2631
|
+
readonly archived?: Archived | undefined;
|
|
2632
|
+
}
|
|
2633
|
+
/**
|
|
2634
|
+
* This function checks the proposed values of fields against the text and the metadata of a
|
|
2635
|
+
* document. It sends no request and writes nothing. It does not make values. A field that is not
|
|
2636
|
+
* valid throws a `RangeError`. The work grows in proportion to the length of the text.
|
|
2637
|
+
*
|
|
2638
|
+
* @beta
|
|
2639
|
+
*/
|
|
2640
|
+
export declare const checkFields: (source: ExtractSource, fields: Readonly<Record<string, FieldClaim>>) => FieldsChecked;
|
|
2641
|
+
/**
|
|
2642
|
+
* This function checks the proposed values of fields against a stored document. It does not make
|
|
2643
|
+
* values. It applies the rules of `verify`:
|
|
2644
|
+
*
|
|
2645
|
+
* - With no option, it checks the current version. `capture` checks the version of one capture,
|
|
2646
|
+
* and `at` checks the version that was current at a time.
|
|
2647
|
+
* - A held-back document gives `held-back` for each field, with no position and no capture, unless
|
|
2648
|
+
* `userProvided`. The crawler then logs an override.
|
|
2649
|
+
* - A field that is not valid throws a `RangeError`, and an unknown document throws an error.
|
|
2650
|
+
*
|
|
2651
|
+
* @beta
|
|
2652
|
+
*/
|
|
2653
|
+
export declare const extractDocument: (runtime: CrawlRuntime, id: string, fields: Readonly<Record<string, FieldClaim>>, options?: ExtractOptions) => Promise<ExtractResult>;
|
|
2654
|
+
//#endregion
|
|
2655
|
+
//#region src/frontier.d.ts
|
|
2656
|
+
/**
|
|
2657
|
+
* The frontier is a small crawl of a site. It starts at the seeds. It follows the links of each page
|
|
2658
|
+
* that it fetched, to a maximum of pages, a maximum depth and a maximum time.
|
|
2659
|
+
*
|
|
2660
|
+
* Each page is a fetch of the crawler, so each polite rule applies. These rules are robots.txt, one
|
|
2661
|
+
* request at a time for each host, the pace, the stops, the egress policy and the capture log. The
|
|
2662
|
+
* frontier does not start a second request to a host with a request in progress. The crawler
|
|
2663
|
+
* limits the hosts in progress (`maxActive`).
|
|
2664
|
+
*
|
|
2665
|
+
* The state of the frontier is in memory, for one call. A later version can make it resumable.
|
|
2666
|
+
*/
|
|
2667
|
+
/**
|
|
2668
|
+
* These are the default limits of `crawl`, and the limits of the memory of the frontier.
|
|
2669
|
+
*
|
|
2670
|
+
* @beta
|
|
2671
|
+
*/
|
|
2672
|
+
export declare const CRAWL_LIMITS: {
|
|
2673
|
+
/** The most pages that one crawl gives. */
|
|
2674
|
+
readonly maxPages: 100;
|
|
2675
|
+
/** The most links from a seed to a page. A seed has the depth 0. */
|
|
2676
|
+
readonly maxDepth: 3;
|
|
2677
|
+
/** The longest time of one crawl: 10 minutes. */
|
|
2678
|
+
readonly budgetMs: 600_000;
|
|
2679
|
+
/** The most hosts that get a request of the crawl at one time. */
|
|
2680
|
+
readonly hostsAtOnce: 16;
|
|
2681
|
+
/** The most URLs that wait in the frontier. */
|
|
2682
|
+
readonly maxQueue: 10_000;
|
|
2683
|
+
/** The most URLs that the frontier remembers, so it fetches none of them two times. */
|
|
2684
|
+
readonly maxSeen: 100_000;
|
|
2685
|
+
};
|
|
2686
|
+
/**
|
|
2687
|
+
* A pattern for the URLs of a crawl. A text that starts with `/` matches the path and the query.
|
|
2688
|
+
* A different text matches the whole URL. In a text, `*` matches any characters and a `$` at the
|
|
2689
|
+
* end matches the end, as in robots.txt. A regular expression tests the whole URL.
|
|
2690
|
+
*/
|
|
2691
|
+
type UrlPattern = string | RegExp;
|
|
2692
|
+
/** These options control a crawl. Each limit has a default in `CRAWL_LIMITS`. */
|
|
2693
|
+
interface CrawlOptions extends ScopeOptions {
|
|
2694
|
+
/** This value is the most pages that the crawl gives. The default is 100. */
|
|
2695
|
+
readonly maxPages?: number | undefined;
|
|
2696
|
+
/** This value is the most links from a seed to a page. The default is 3, and 0 fetches only the seeds. */
|
|
2697
|
+
readonly maxDepth?: number | undefined;
|
|
2698
|
+
/** This value is the longest time of the crawl in milliseconds. No request starts after it. The default is 10 minutes. */
|
|
2699
|
+
readonly budgetMs?: number | undefined;
|
|
2700
|
+
/** When this list is not empty, the crawl follows only a URL that matches a pattern of it. */
|
|
2701
|
+
readonly include?: readonly UrlPattern[] | undefined;
|
|
2702
|
+
/** The crawl follows no URL that matches a pattern of this list. */
|
|
2703
|
+
readonly exclude?: readonly UrlPattern[] | undefined;
|
|
2704
|
+
/** With the value true, the crawl adds the pages of the sitemaps of each site of the seeds (`discover`). */
|
|
2705
|
+
readonly useSitemaps?: boolean | undefined;
|
|
2706
|
+
/** The value `auto` renders a page only when it shows almost nothing without JavaScript. */
|
|
2707
|
+
readonly render?: "auto" | "never" | "always" | undefined;
|
|
2708
|
+
/** A stored copy younger than this age answers, with no request. The default is `24h`. */
|
|
2709
|
+
readonly maxAge?: Duration | undefined;
|
|
2710
|
+
/** These agents get their robots.txt rules applied to each fetch. */
|
|
2711
|
+
readonly onBehalfOf?: readonly string[] | undefined;
|
|
2712
|
+
/** When this signal aborts, the crawl stops the fetches in progress and ends. */
|
|
2713
|
+
readonly signal?: AbortSignal | undefined;
|
|
2714
|
+
/** This label goes into the capture records of the crawl. */
|
|
2715
|
+
readonly scope?: string | undefined;
|
|
2716
|
+
/** These labels go into the capture records of the crawl. */
|
|
2717
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
2718
|
+
}
|
|
2719
|
+
/**
|
|
2720
|
+
* These options control `crawlSite`: the options of `crawl`, and a step before the first fetch.
|
|
2721
|
+
*
|
|
2722
|
+
* @beta
|
|
2723
|
+
*/
|
|
2724
|
+
interface CrawlRunOptions extends CrawlOptions {
|
|
2725
|
+
/** This function operates before the first fetch. `openCrawler` waits there for the open of the store. */
|
|
2726
|
+
readonly ready?: (() => Promise<void>) | undefined;
|
|
2727
|
+
}
|
|
2728
|
+
/** A page of a crawl: the result of its fetch, and the place of the page in the crawl. */
|
|
2729
|
+
interface CrawlPage extends PageResult {
|
|
2730
|
+
/** This value is the number of links from a seed to the page. */
|
|
2731
|
+
readonly depth: number;
|
|
2732
|
+
/** This value tells if the page is a seed, a page of a sitemap, or the target of a link. */
|
|
2733
|
+
readonly source: "seed" | "sitemap" | "link";
|
|
2734
|
+
/** This URL is the page whose link the crawl followed, or null. */
|
|
2735
|
+
readonly referrer: string | null;
|
|
2736
|
+
}
|
|
2737
|
+
/** The reason that a crawl stopped. */
|
|
2738
|
+
type CrawlStop = "done" | "max-pages" | "budget" | "cancelled" | "closed" | "error";
|
|
2739
|
+
/** The summary of a crawl. */
|
|
2740
|
+
interface CrawlSummary {
|
|
2741
|
+
/** This value is the number of pages that the crawl gave. */
|
|
2742
|
+
readonly pages: number;
|
|
2743
|
+
/** This value counts the pages with a document: `ok` or `not-modified`, from the store too. */
|
|
2744
|
+
readonly fetched: number;
|
|
2745
|
+
/** This value counts the pages that a stored copy answered, with no request. */
|
|
2746
|
+
readonly from_cache: number;
|
|
2747
|
+
/**
|
|
2748
|
+
* This value counts the refusals. These are the links that robots.txt does not permit, and the
|
|
2749
|
+
* pages that the crawler declined (`skipped`). The pages that the site refused (`blocked`,
|
|
2750
|
+
* `challenge`, `payment-required`) are refusals too.
|
|
2751
|
+
*/
|
|
2752
|
+
readonly refused: number;
|
|
2753
|
+
/** This value counts the pages with a different outcome, for example `error` or `not-found`. */
|
|
2754
|
+
readonly failed: number;
|
|
2755
|
+
/** This value counts the links that the crawl did not follow. `skips` gives the reasons. */
|
|
2756
|
+
readonly skipped: number;
|
|
2757
|
+
readonly skips: {
|
|
2758
|
+
/** The links to a host that is not in scope. */
|
|
2759
|
+
readonly scope: number;
|
|
2760
|
+
/** The links that `include` or `exclude` left out. */
|
|
2761
|
+
readonly pattern: number;
|
|
2762
|
+
/** The links deeper than `maxDepth`. */
|
|
2763
|
+
readonly depth: number;
|
|
2764
|
+
/** The pages that asked not to be AI input: the crawl does not follow their links. */
|
|
2765
|
+
readonly held_back: number;
|
|
2766
|
+
/** The links over the limits of the memory of the frontier. */
|
|
2767
|
+
readonly limit: number;
|
|
2768
|
+
/** The URLs that a fetched page had as its final or its canonical URL. */
|
|
2769
|
+
readonly duplicate: number;
|
|
2770
|
+
};
|
|
2771
|
+
/** This value counts the URLs that waited in the frontier at the stop. */
|
|
2772
|
+
readonly queued: number;
|
|
2773
|
+
/** This value counts the URLs that the sitemaps gave, with `useSitemaps`. */
|
|
2774
|
+
readonly sitemap_urls: number;
|
|
2775
|
+
readonly stopped: CrawlStop;
|
|
2776
|
+
/** This text tells why the crawl stopped. */
|
|
2777
|
+
readonly reason: string;
|
|
2778
|
+
/** This value is the time of the crawl in milliseconds, on the clock of the crawler. */
|
|
2779
|
+
readonly elapsed_ms: number;
|
|
2780
|
+
}
|
|
2781
|
+
/**
|
|
2782
|
+
* A crawl in progress. A `for await` loop gets each page when its fetch ends. `summary` resolves
|
|
2783
|
+
* when the loop ends: at the last page, at a stop or at a `break`. The crawl starts at the first
|
|
2784
|
+
* step of the loop, and each crawl operates one time only.
|
|
2785
|
+
*/
|
|
2786
|
+
interface CrawlRun extends AsyncIterable<CrawlPage> {
|
|
2787
|
+
readonly summary: Promise<CrawlSummary>;
|
|
2788
|
+
}
|
|
2789
|
+
/**
|
|
2790
|
+
* This function crawls a site from its seeds. `PoliteCrawler.crawl` uses it. It gives each page
|
|
2791
|
+
* when its fetch ends, and a summary at the end. The seeds come first, then the pages of the
|
|
2792
|
+
* sitemaps (the newest first), then the links, the lower depth first.
|
|
2793
|
+
*
|
|
2794
|
+
* @beta
|
|
2795
|
+
*/
|
|
2796
|
+
export declare const crawlSite: (runtime: CrawlRuntime, seeds: string | readonly string[], options?: CrawlRunOptions) => CrawlRun;
|
|
2797
|
+
//#endregion
|
|
2798
|
+
//#region src/vectors.d.ts
|
|
2799
|
+
/**
|
|
2800
|
+
* The vector store keeps the vectors of hybrid search in `vectors.sqlite`, beside the index. It is
|
|
2801
|
+
* a projection, as the index is. Delete the file, and the next hybrid search or `embed` makes the
|
|
2802
|
+
* vectors again from the stored texts.
|
|
2803
|
+
*
|
|
2804
|
+
* - A row is one chunk of one text for one embedder. Its key is the SHA-256 of the text, the
|
|
2805
|
+
* number of the chunk and the id of the embedder. Thus a reindex keeps each vector, and the same
|
|
2806
|
+
* text gets its vectors one time for each model, also when two documents hold it.
|
|
2807
|
+
* - A text is complete when the table `texts` has it, with the plan of its chunks. A text with a
|
|
2808
|
+
* different plan gets new chunks.
|
|
2809
|
+
* - The table `docs` points each document at the text that has its vectors. A search uses only
|
|
2810
|
+
* the texts that a document points at, and the caller checks each document against the index.
|
|
2811
|
+
* - A search compares the vector of the question with each stored vector (a brute-force cosine).
|
|
2812
|
+
* The store keeps the vectors of each embedder in memory after the first search. It reads them
|
|
2813
|
+
* again when a different process writes the file.
|
|
2814
|
+
*
|
|
2815
|
+
* Each connection waits for the lock of a different process (`busy_timeout`), and each write is
|
|
2816
|
+
* one transaction (`BEGIN IMMEDIATE`). The file has a schema version (`VECTORS_SCHEMA`). A file of
|
|
2817
|
+
* a different version opens empty.
|
|
2818
|
+
*/
|
|
2819
|
+
/**
|
|
2820
|
+
* This value is the version of the schema of the vector file.
|
|
2821
|
+
*
|
|
2822
|
+
* @beta
|
|
2823
|
+
*/
|
|
2824
|
+
export declare const VECTORS_SCHEMA = 1;
|
|
2825
|
+
/**
|
|
2826
|
+
* This is one chunk of a text with its vector, as the store keeps it.
|
|
2827
|
+
*
|
|
2828
|
+
* @beta
|
|
2829
|
+
*/
|
|
2830
|
+
interface StoredChunk {
|
|
2831
|
+
readonly chunk: number;
|
|
2832
|
+
/** These values are the positions of the chunk in the text, in UTF-16 units. */
|
|
2833
|
+
readonly start: number;
|
|
2834
|
+
readonly end: number;
|
|
2835
|
+
/** This vector has a length of 1, or it is zeros. */
|
|
2836
|
+
readonly vector: Float32Array;
|
|
2837
|
+
}
|
|
2838
|
+
/**
|
|
2839
|
+
* This is the best chunk of one text for a question.
|
|
2840
|
+
*
|
|
2841
|
+
* @beta
|
|
2842
|
+
*/
|
|
2843
|
+
interface VectorHit {
|
|
2844
|
+
/** This value is the SHA-256 of the text, in hex. */
|
|
2845
|
+
readonly sha: string;
|
|
2846
|
+
readonly chunk: number;
|
|
2847
|
+
readonly start: number;
|
|
2848
|
+
readonly end: number;
|
|
2849
|
+
/** This value is the cosine of the chunk and the question, from -1 to 1. */
|
|
2850
|
+
readonly similarity: number;
|
|
2851
|
+
/** These documents point at the text. The caller checks each one against the index. */
|
|
2852
|
+
readonly docs: readonly string[];
|
|
2853
|
+
}
|
|
2854
|
+
/**
|
|
2855
|
+
* These are the counts of the store, for one embedder or for each embedder.
|
|
2856
|
+
*
|
|
2857
|
+
* @beta
|
|
2858
|
+
*/
|
|
2859
|
+
interface VectorCounts {
|
|
2860
|
+
readonly texts: number;
|
|
2861
|
+
readonly chunks: number;
|
|
2862
|
+
readonly docs: number;
|
|
2863
|
+
/** This value is the size of the vectors in bytes, with no overhead of SQLite. */
|
|
2864
|
+
readonly bytes: number;
|
|
2865
|
+
}
|
|
2866
|
+
/**
|
|
2867
|
+
* These options open a vector store.
|
|
2868
|
+
*
|
|
2869
|
+
* @beta
|
|
2870
|
+
*/
|
|
2871
|
+
interface VectorStoreOptions {
|
|
2872
|
+
/** This value is the time in milliseconds that a write waits for the lock of a different process. The default is 10 seconds. */
|
|
2873
|
+
readonly busyTimeoutMs?: number | undefined;
|
|
2874
|
+
}
|
|
2875
|
+
/**
|
|
2876
|
+
* These are the parts of one text in a write of the store.
|
|
2877
|
+
*
|
|
2878
|
+
* @beta
|
|
2879
|
+
*/
|
|
2880
|
+
interface VectorWrite {
|
|
2881
|
+
readonly embedder: string;
|
|
2882
|
+
readonly dimensions: number;
|
|
2883
|
+
/** This value is the SHA-256 of the text, in hex. */
|
|
2884
|
+
readonly sha: string;
|
|
2885
|
+
/** This text names the rule of the chunks. A text with a different plan gets new chunks. */
|
|
2886
|
+
readonly plan: string;
|
|
2887
|
+
/** This value is the length of the text. */
|
|
2888
|
+
readonly chars: number;
|
|
2889
|
+
readonly chunks: ReadonlyArray<Omit<StoredChunk, "chunk">>;
|
|
2890
|
+
/** These documents hold the text as their current text. The store points each one at it. */
|
|
2891
|
+
readonly docs: readonly string[];
|
|
2892
|
+
/** This time (ISO 8601) is the time of the write. */
|
|
2893
|
+
readonly at: string;
|
|
2894
|
+
}
|
|
2895
|
+
/** @beta */
|
|
2896
|
+
export declare class VectorStore {
|
|
2897
|
+
readonly path: string;
|
|
2898
|
+
private readonly db;
|
|
2899
|
+
private readonly statements;
|
|
2900
|
+
private readonly matrices;
|
|
2901
|
+
private closed;
|
|
2902
|
+
constructor(path: string, options?: VectorStoreOptions);
|
|
2903
|
+
private stmt;
|
|
2904
|
+
private write;
|
|
2905
|
+
private live;
|
|
2906
|
+
/** This method gives the plan of the chunks of a text when the store has each of its vectors, or undefined. */
|
|
2907
|
+
plan(embedder: string, sha: string): string | undefined;
|
|
2908
|
+
/** This method gives the text that a document points at, or undefined. */
|
|
2909
|
+
pointed(embedder: string, doc: string): string | undefined;
|
|
2910
|
+
/** This method gives the dimensions that the store keeps for an embedder, or undefined. */
|
|
2911
|
+
dimensions(embedder: string): number | undefined;
|
|
2912
|
+
/**
|
|
2913
|
+
* This method writes the chunks of one text and points its documents at it, in one transaction.
|
|
2914
|
+
* A vector with a length that is different from the dimensions of the embedder throws an error.
|
|
2915
|
+
*/
|
|
2916
|
+
put(input: VectorWrite): void;
|
|
2917
|
+
private pointRow;
|
|
2918
|
+
/** This method points a document at a text that the store has. */
|
|
2919
|
+
point(embedder: string, doc: string, sha: string, at: string): void;
|
|
2920
|
+
/** This method removes the pointer of a document, for example when the document is held back. Its text keeps its vectors. */
|
|
2921
|
+
forget(embedder: string, doc: string): void;
|
|
2922
|
+
/** This method gives the chunks of a text, in their sequence. */
|
|
2923
|
+
chunks(embedder: string, sha: string): StoredChunk[];
|
|
2924
|
+
private pointMatrix;
|
|
2925
|
+
private append;
|
|
2926
|
+
/** This method gives the vectors of an embedder in memory. It reads them again when a different process wrote the file. */
|
|
2927
|
+
private matrix;
|
|
2928
|
+
private load;
|
|
2929
|
+
/** This method gives the best chunk of one text for a question, or undefined when the store has no vector of the text. */
|
|
2930
|
+
best(embedder: string, sha: string, query: Float32Array): VectorHit | undefined;
|
|
2931
|
+
private hitOf;
|
|
2932
|
+
/**
|
|
2933
|
+
* This method compares a question with each stored vector of an embedder (a brute-force cosine).
|
|
2934
|
+
* It gives the best chunk of each text that a document points at, the best first. With `within`,
|
|
2935
|
+
* it compares only the texts of those documents.
|
|
2936
|
+
*/
|
|
2937
|
+
search(embedder: string, query: Float32Array, options?: {
|
|
2938
|
+
readonly within?: Iterable<string> | undefined;
|
|
2939
|
+
}): VectorHit[];
|
|
2940
|
+
/** This method gives the counts of the store, for one embedder or for each embedder. */
|
|
2941
|
+
counts(embedder?: string): VectorCounts;
|
|
2942
|
+
/** This method closes the file. A second close does nothing. */
|
|
2943
|
+
close(): void;
|
|
2944
|
+
}
|
|
2945
|
+
//#endregion
|
|
2946
|
+
//#region src/hybrid.d.ts
|
|
2947
|
+
/**
|
|
2948
|
+
* Hybrid search ranks the stored documents two times and fuses the two lists:
|
|
2949
|
+
*
|
|
2950
|
+
* 1. The full-text index gives the BM25 candidates (`candidates`, 50 by default). Each candidate
|
|
2951
|
+
* whose text has no vectors gets them from the embedder first.
|
|
2952
|
+
* 2. The vector store gives the documents with the closest chunks (`neighbors`, 50 by default),
|
|
2953
|
+
* with a brute-force cosine over the vectors of the embedder.
|
|
2954
|
+
* 3. Reciprocal rank fusion (`fusionK`, 60 by default) makes one list of the two.
|
|
2955
|
+
*
|
|
2956
|
+
* The snippet and the `passage` of each hit come from its best chunk. A held-back document gets
|
|
2957
|
+
* no vectors, and no result gives it, exactly as for `find`. The text of a held-back version does not
|
|
2958
|
+
* goes to the embedder.
|
|
2959
|
+
*/
|
|
2960
|
+
/** These options change a hybrid search. */
|
|
2961
|
+
interface HybridOptions {
|
|
2962
|
+
/** This value is the number of BM25 candidates. The default is `HYBRID_DEFAULTS.candidates`. */
|
|
2963
|
+
readonly candidates?: number | undefined;
|
|
2964
|
+
/** This value is the number of documents from the vector search. The default is `HYBRID_DEFAULTS.neighbors`. */
|
|
2965
|
+
readonly neighbors?: number | undefined;
|
|
2966
|
+
/** This value is the constant k of reciprocal rank fusion. The default is `HYBRID_DEFAULTS.fusionK`. */
|
|
2967
|
+
readonly fusionK?: number | undefined;
|
|
2968
|
+
/** When this signal aborts, the search stops the embedder and rejects. */
|
|
2969
|
+
readonly signal?: AbortSignal | undefined;
|
|
2970
|
+
}
|
|
2971
|
+
/** These options control a hybrid search of the store. */
|
|
2972
|
+
interface HybridFindOptions extends HybridOptions {
|
|
2973
|
+
/** This value is the maximum number of results. The default is 10, and the maximum is 1000. */
|
|
2974
|
+
readonly limit?: number | undefined;
|
|
2975
|
+
/** This value keeps only the documents of one kind, for example `page` or `pdf`. */
|
|
2976
|
+
readonly kind?: string | null | undefined;
|
|
2977
|
+
}
|
|
2978
|
+
/** This is one document of a ranking of a set of documents (`rankWithin`). */
|
|
2979
|
+
interface Ranked {
|
|
2980
|
+
readonly doc: string;
|
|
2981
|
+
/** This value is the score. A larger score is a better match. In the mode `hybrid`, it is the fused score. */
|
|
2982
|
+
readonly score: number;
|
|
2983
|
+
/** This value is the best chunk of the document, in the mode `hybrid`. */
|
|
2984
|
+
readonly passage?: Passage | undefined;
|
|
2985
|
+
}
|
|
2986
|
+
/** These options control `embed`, which gives vectors to the stored documents that have none. */
|
|
2987
|
+
interface EmbedOptions {
|
|
2988
|
+
/** This value is the longest time of the work, for example `"10m"` (the default). No embedding starts after it. */
|
|
2989
|
+
readonly budget?: Duration | undefined;
|
|
2990
|
+
/** This value is the largest number of texts that get new vectors. The default has no limit. */
|
|
2991
|
+
readonly maxDocs?: number | undefined;
|
|
2992
|
+
/** This value keeps only the documents of one kind, for example `page` or `pdf`. */
|
|
2993
|
+
readonly kind?: string | null | undefined;
|
|
2994
|
+
/** When this signal aborts, the work stops. The result then has `stopped: "signal"`. */
|
|
2995
|
+
readonly signal?: AbortSignal | undefined;
|
|
2996
|
+
}
|
|
2997
|
+
/** The reason of the end of `embed`. */
|
|
2998
|
+
type EmbedStop = "done" | "budget" | "max-docs" | "signal";
|
|
2999
|
+
/** The counts of `embed`. */
|
|
3000
|
+
interface EmbedResult {
|
|
3001
|
+
/** This value is the id of the embedder. */
|
|
3002
|
+
readonly embedder: string;
|
|
3003
|
+
/** This value is the number of documents that the work examined. */
|
|
3004
|
+
readonly docs: number;
|
|
3005
|
+
/** This value is the number of texts that got new vectors. */
|
|
3006
|
+
readonly embedded: number;
|
|
3007
|
+
/** This value is the number of documents whose text had vectors already. */
|
|
3008
|
+
readonly reused: number;
|
|
3009
|
+
/** This value is the number of documents that the work left out, because they are held back. */
|
|
3010
|
+
readonly held_back: number;
|
|
3011
|
+
/** This value is the number of new vectors: one for each chunk. */
|
|
3012
|
+
readonly chunks: number;
|
|
3013
|
+
readonly stopped: EmbedStop;
|
|
3014
|
+
/** This value is the time of the work in milliseconds. */
|
|
3015
|
+
readonly ms: number;
|
|
3016
|
+
}
|
|
3017
|
+
/** Hybrid search over a runtime and a vector store. `openCrawler` makes one when it has an embedder. */
|
|
3018
|
+
interface HybridSearch {
|
|
3019
|
+
readonly embedder: Embedder;
|
|
3020
|
+
/** This method searches the stored documents in the mode `hybrid`. It sends no request to a site. */
|
|
3021
|
+
find(q: string, options?: HybridFindOptions): Promise<Found[]>;
|
|
3022
|
+
/** This method ranks a set of documents in the mode `hybrid`, the best first. */
|
|
3023
|
+
rankWithin(q: string, docs: readonly string[], options?: HybridOptions): Promise<Ranked[]>;
|
|
3024
|
+
/** This method gives vectors to the stored documents that have none, within a budget. */
|
|
3025
|
+
embed(options?: EmbedOptions): Promise<EmbedResult>;
|
|
3026
|
+
}
|
|
3027
|
+
/**
|
|
3028
|
+
* These options make a hybrid search.
|
|
3029
|
+
*
|
|
3030
|
+
* @beta
|
|
3031
|
+
*/
|
|
3032
|
+
interface HybridSearchOptions {
|
|
3033
|
+
readonly embedder: Embedder;
|
|
3034
|
+
/** This store keeps the vectors. The caller opens it and closes it. */
|
|
3035
|
+
readonly store: VectorStore;
|
|
3036
|
+
}
|
|
3037
|
+
/**
|
|
3038
|
+
* This function makes a hybrid search over a runtime. The vectors go into the store, with the id of the embedder.
|
|
3039
|
+
*
|
|
3040
|
+
* @beta
|
|
3041
|
+
*/
|
|
3042
|
+
export declare const createHybridSearch: (runtime: CrawlRuntime, options: HybridSearchOptions) => HybridSearch;
|
|
3043
|
+
//#endregion
|
|
3044
|
+
//#region src/recipe.d.ts
|
|
3045
|
+
/** The capture that an extractor reads: the final URL of the response and its content type. */
|
|
3046
|
+
interface RecipeCapture {
|
|
3047
|
+
readonly url: string;
|
|
3048
|
+
readonly contentType: string | null;
|
|
3049
|
+
}
|
|
3050
|
+
/**
|
|
3051
|
+
* This function reads one captured body into the documents that it holds. It is pure: the same
|
|
3052
|
+
* body and capture always give the same documents. It sends no request. It can answer later.
|
|
3053
|
+
*/
|
|
3054
|
+
type RecipeExtract = (body: Uint8Array, capture: RecipeCapture) => ReadonlyArray<ExtractedDoc> | Promise<ReadonlyArray<ExtractedDoc>>;
|
|
3055
|
+
/** The request of a recipe for one input: the URL, and the parts that change with the input. */
|
|
3056
|
+
interface RecipeRequest {
|
|
3057
|
+
readonly url: string;
|
|
3058
|
+
/** This value replaces the `accept` of the recipe for this request. */
|
|
3059
|
+
readonly accept?: string | undefined;
|
|
3060
|
+
/** These headers go with the request. The crawler does not write them into the capture. */
|
|
3061
|
+
readonly headers?: Readonly<Record<string, string>> | undefined;
|
|
3062
|
+
/** These query parameters hold secrets. The capture shows `REDACTED` in their place. */
|
|
3063
|
+
readonly secretParams?: readonly string[] | undefined;
|
|
3064
|
+
}
|
|
3065
|
+
/** These values declare a recipe. Only `name` and `extract` are necessary. */
|
|
3066
|
+
interface RecipeDefinition<Input = string> {
|
|
3067
|
+
/**
|
|
3068
|
+
* This name goes into each capture record of the recipe, so it is part of each capture id.
|
|
3069
|
+
* `reindex` finds the extractor by it. Do not change it after the first capture.
|
|
3070
|
+
*/
|
|
3071
|
+
readonly name: string;
|
|
3072
|
+
/** This text tells what the recipe reads. */
|
|
3073
|
+
readonly description?: string | undefined;
|
|
3074
|
+
/**
|
|
3075
|
+
* The value `page` (the default) is a web page: robots.txt applies. The value `api` is a
|
|
3076
|
+
* documented API with terms of its own: robots.txt does not apply, and the pace still does.
|
|
3077
|
+
*/
|
|
3078
|
+
readonly mode?: "api" | "page" | undefined;
|
|
3079
|
+
/** This value is the `Accept` header. The defaults are HTML for a page and JSON for an API. */
|
|
3080
|
+
readonly accept?: string | undefined;
|
|
3081
|
+
/** This value is the documented pace of the source: the shortest time between two of its requests. */
|
|
3082
|
+
readonly minInterval?: Duration | undefined;
|
|
3083
|
+
/** A stored copy younger than this age answers with no request. The default is `24h`. */
|
|
3084
|
+
readonly maxAge?: Duration | undefined;
|
|
3085
|
+
/** This value is the most bytes of a body that the crawler keeps. */
|
|
3086
|
+
readonly maxBytes?: number | undefined;
|
|
3087
|
+
/** This function gives the request for an input. With no function, the input is the URL. */
|
|
3088
|
+
readonly fetch?: ((input: Input) => string | RecipeRequest) | undefined;
|
|
3089
|
+
/** This function reads a captured body into documents. It is pure, and it sends no request. */
|
|
3090
|
+
readonly extract: RecipeExtract;
|
|
3091
|
+
}
|
|
3092
|
+
/** These options build the request of a recipe for one input (`Recipe.request`). */
|
|
3093
|
+
interface RecipeRequestOptions {
|
|
3094
|
+
/** This value replaces the `maxAge` of the recipe. The value `0` asks the source again. */
|
|
3095
|
+
readonly maxAge?: Duration | undefined;
|
|
3096
|
+
/** These agents get their robots.txt rules applied too, for a recipe of the mode `page`. */
|
|
3097
|
+
readonly onBehalfOf?: readonly string[] | undefined;
|
|
3098
|
+
/** This name connects the captures to the job that asked for them. */
|
|
3099
|
+
readonly job?: string | undefined;
|
|
3100
|
+
/** When this signal aborts, the crawler stops the fetch. */
|
|
3101
|
+
readonly signal?: AbortSignal | undefined;
|
|
3102
|
+
/** This label goes into each capture record of the request. */
|
|
3103
|
+
readonly scope?: string | undefined;
|
|
3104
|
+
/** These labels go into each capture record of the request. They are not part of the capture id. */
|
|
3105
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
3106
|
+
/** No request starts at or after this time (milliseconds since the epoch, on the clock of the crawler). */
|
|
3107
|
+
readonly deadline?: number | undefined;
|
|
3108
|
+
}
|
|
3109
|
+
/** A recipe that `defineRecipe` made. Give it to `runRecipe`, to `registerRecipe` or to `openCrawler`. */
|
|
3110
|
+
interface Recipe<Input = string> {
|
|
3111
|
+
readonly name: string;
|
|
3112
|
+
readonly description: string | null;
|
|
3113
|
+
readonly mode: "api" | "page";
|
|
3114
|
+
readonly accept: string;
|
|
3115
|
+
/**
|
|
3116
|
+
* This extractor reads the captures of the recipe. It checks the documents of the definition.
|
|
3117
|
+
*
|
|
3118
|
+
* @beta
|
|
3119
|
+
*/
|
|
3120
|
+
readonly extractor: Extractor;
|
|
3121
|
+
/**
|
|
3122
|
+
* This method gives the request of the crawler for one input. A value that is not a URL throws a `TypeError`.
|
|
3123
|
+
*
|
|
3124
|
+
* @beta
|
|
3125
|
+
*/
|
|
3126
|
+
request(input: Input, options?: RecipeRequestOptions): FetchRequest;
|
|
3127
|
+
}
|
|
3128
|
+
/**
|
|
3129
|
+
* This function reads a body as JSON, in the encoding that the response declares. A body that is
|
|
3130
|
+
* not JSON (a page about maintenance, or a login page) throws an error that names the URL and the
|
|
3131
|
+
* content type. In an extractor, the runtime then reports that error.
|
|
3132
|
+
*/
|
|
3133
|
+
export declare const readJson: (body: Uint8Array, capture: RecipeCapture) => unknown;
|
|
3134
|
+
/**
|
|
3135
|
+
* These recipe names belong to the crawler: the page reader, the archived fallback, robots.txt,
|
|
3136
|
+
* and the sitemaps and feeds of `discover`. A recipe cannot use them.
|
|
3137
|
+
*/
|
|
3138
|
+
export declare const RESERVED_RECIPES: readonly string[];
|
|
3139
|
+
/**
|
|
3140
|
+
* These are the limits of a recipe.
|
|
3141
|
+
*
|
|
3142
|
+
* @beta
|
|
3143
|
+
*/
|
|
3144
|
+
export declare const RECIPE_LIMITS: {
|
|
3145
|
+
/** The longest recipe name. */
|
|
3146
|
+
readonly nameChars: number;
|
|
3147
|
+
/** The longest document id. */
|
|
3148
|
+
readonly docIdChars: number;
|
|
3149
|
+
};
|
|
3150
|
+
/**
|
|
3151
|
+
* This function declares a recipe. It checks the definition at once: a name that is not valid, a
|
|
3152
|
+
* reserved name or a duration that it cannot read throws an error. The extractor of the recipe
|
|
3153
|
+
* checks each document of `extract`. A document that is not valid makes the extractor throw, and
|
|
3154
|
+
* the runtime reports it as an error of the extractor.
|
|
3155
|
+
*/
|
|
3156
|
+
export declare const defineRecipe: <Input = string>(definition: RecipeDefinition<Input>) => Recipe<Input>;
|
|
3157
|
+
/**
|
|
3158
|
+
* This function gives the extractor of a recipe to the extractors of a runtime. A second
|
|
3159
|
+
* registration of the same recipe does nothing. A different extractor with the same name throws
|
|
3160
|
+
* an error, because `reindex` must read each capture with one extractor.
|
|
3161
|
+
*
|
|
3162
|
+
* @beta
|
|
3163
|
+
*/
|
|
3164
|
+
export declare const registerRecipe: <Input>(extractors: Extractors, recipe: Recipe<Input>) => void;
|
|
3165
|
+
/** These options control one run of a recipe. */
|
|
3166
|
+
interface RecipeRunOptions extends Omit<RecipeRequestOptions, "deadline"> {
|
|
3167
|
+
/** No request starts after this time from the start of the run, for example `"30s"`. */
|
|
3168
|
+
readonly budget?: Duration | undefined;
|
|
3169
|
+
/** This value is true when a person gave this input directly. It is the only way to see a held-back document. The crawler logs it. */
|
|
3170
|
+
readonly userProvided?: boolean | undefined;
|
|
3171
|
+
/**
|
|
3172
|
+
* The value `archive` gives a copy from a public web archive when the site refuses the page or is
|
|
3173
|
+
* down. Only a recipe of the mode `page` takes it. The page reader reads the copy.
|
|
3174
|
+
*/
|
|
3175
|
+
readonly fallback?: "archive" | "none" | undefined;
|
|
3176
|
+
/** The value true also gives an archived copy of a page that the current robots.txt disallows. */
|
|
3177
|
+
readonly archiveDespiteRobots?: boolean | undefined;
|
|
3178
|
+
}
|
|
3179
|
+
/** One document of a run of a recipe: the version of the capture of the run. */
|
|
3180
|
+
interface RecipeDoc {
|
|
3181
|
+
readonly doc: string;
|
|
3182
|
+
readonly kind: string;
|
|
3183
|
+
/** This value is the title. A held-back document shows no title. */
|
|
3184
|
+
readonly title: string;
|
|
3185
|
+
readonly url: string | null;
|
|
3186
|
+
/** This value is the metadata. A held-back document shows only the structural fields. */
|
|
3187
|
+
readonly meta: Readonly<Record<string, unknown>>;
|
|
3188
|
+
/** This value is the length of the text. `read` gives the text. */
|
|
3189
|
+
readonly chars: number;
|
|
3190
|
+
/** This id is the capture of this version. A citation names it. */
|
|
3191
|
+
readonly capture: string;
|
|
3192
|
+
readonly fetched_at: string;
|
|
3193
|
+
readonly held_back: boolean;
|
|
3194
|
+
/** This value tells that the document is an archived copy, with the archive and the date of the copy. */
|
|
3195
|
+
readonly archived?: Archived | undefined;
|
|
3196
|
+
}
|
|
3197
|
+
/** The result of `runRecipe`. */
|
|
3198
|
+
interface RecipeResult {
|
|
3199
|
+
readonly recipe: string;
|
|
3200
|
+
/** This URL is the URL of the capture. Its secrets show as `REDACTED`. */
|
|
3201
|
+
readonly url: string;
|
|
3202
|
+
readonly outcome: string;
|
|
3203
|
+
readonly status: number;
|
|
3204
|
+
/** These documents came from the capture of the run, in the order of the extractor. */
|
|
3205
|
+
readonly docs: readonly RecipeDoc[];
|
|
3206
|
+
/** This id is the capture of the documents. */
|
|
3207
|
+
readonly capture: string | null;
|
|
3208
|
+
readonly fetched_at: string | null;
|
|
3209
|
+
/** This value is true when the stored copy answered, and the crawler sent no request. */
|
|
3210
|
+
readonly from_cache: boolean;
|
|
3211
|
+
/** This value is true when a document is held back. */
|
|
3212
|
+
readonly held_back: boolean;
|
|
3213
|
+
/** This text is the error of the extractor, when it threw for the capture. */
|
|
3214
|
+
readonly error: string | null;
|
|
3215
|
+
/** This value tells that the documents are an archived copy, not a fresh fetch. */
|
|
3216
|
+
readonly archived?: Archived | undefined;
|
|
3217
|
+
readonly note: string | null;
|
|
3218
|
+
}
|
|
3219
|
+
/** A recipe operates on a runtime, or on a crawler that has one (`crawler.runtime`). */
|
|
3220
|
+
type RecipeTarget = CrawlRuntime | {
|
|
3221
|
+
readonly runtime: CrawlRuntime;
|
|
3222
|
+
};
|
|
3223
|
+
/**
|
|
3224
|
+
* This function starts a recipe for one input. It fetches the request through the crawler, and it
|
|
3225
|
+
* reads the capture with the extractor of the recipe. It gives the documents of that capture.
|
|
3226
|
+
* These are its rules:
|
|
3227
|
+
*
|
|
3228
|
+
* - The runtime registers the recipe the first time that it gets it. Give the recipe to
|
|
3229
|
+
* `openCrawler` too, so that `reindex` reads its captures before the first fetch.
|
|
3230
|
+
* - A stored copy younger than `maxAge` answers with no request. When the extractor cannot read a
|
|
3231
|
+
* stored copy, the crawler asks the source one more time.
|
|
3232
|
+
* - An extractor that throws gives no document, and `error` tells why. The fetch still succeeds.
|
|
3233
|
+
* - A document that asked not to be AI input shows no title and only its structural metadata,
|
|
3234
|
+
* unless `userProvided`. The crawler logs each `userProvided` read of a held-back document.
|
|
3235
|
+
* - With `fallback: "archive"`, a page that the site refuses gets an archived copy, with
|
|
3236
|
+
* `archived`. The page reader reads the copy. The extractor of the recipe reads only live
|
|
3237
|
+
* captures. Thus its documents do not mix a live answer and an old copy of a third party.
|
|
3238
|
+
*/
|
|
3239
|
+
export declare const runRecipe: <Input>(target: RecipeTarget, recipe: Recipe<Input>, input: Input, options?: RecipeRunOptions) => Promise<RecipeResult>;
|
|
3240
|
+
//#endregion
|
|
3241
|
+
//#region src/watch.d.ts
|
|
3242
|
+
/**
|
|
3243
|
+
* A watch list keeps the pages that the crawler checks again on a schedule. `watch.run` fetches the
|
|
3244
|
+
* pages that are due through the normal fetch, so each polite rule applies. It gives an event for
|
|
3245
|
+
* each page: new, changed, unchanged, gone, refused or error.
|
|
3246
|
+
*
|
|
3247
|
+
* These are the rules of a watch:
|
|
3248
|
+
*
|
|
3249
|
+
* - A watch compares the title, the text and the JSON-LD of the page with the last confirmed version.
|
|
3250
|
+
* - With `confirm: 2` (the default), a change is suspected when a check first sees it. The next
|
|
3251
|
+
* check confirms it when it sees the same new version. A check that sees the earlier version
|
|
3252
|
+
* again clears the suspicion, because one read can be a glitch. With `confirm: 1`, each check confirms.
|
|
3253
|
+
* - `every` has a floor of 1 hour by default. A check that a stop of the host refused is due again
|
|
3254
|
+
* at the end of the stop, or later.
|
|
3255
|
+
* - The list is in `watch.sqlite` in the store, so it stays after a restart. A run claims each entry
|
|
3256
|
+
* with a lease in that file, so two processes do not check one entry two times.
|
|
3257
|
+
*
|
|
3258
|
+
* A scheduler of the operator starts `watch.run`, or the daemon (`startDaemon`) starts it on a schedule.
|
|
3259
|
+
*/
|
|
3260
|
+
/** This value is the version of the schema of `watch.sqlite`. */
|
|
3261
|
+
export declare const WATCH_SCHEMA = 1;
|
|
3262
|
+
/** These are the defaults and the limits of the watch list. */
|
|
3263
|
+
export declare const WATCH_LIMITS: {
|
|
3264
|
+
/** The shortest `every` of a watch by default: 1 hour, so a watch cannot hammer a site. */
|
|
3265
|
+
readonly floorMs: 3_600_000;
|
|
3266
|
+
/** The default `every`: 24 hours. */
|
|
3267
|
+
readonly everyMs: 86_400_000;
|
|
3268
|
+
/** The default time of one run: 10 minutes. No request starts after it. */
|
|
3269
|
+
readonly budgetMs: 600_000;
|
|
3270
|
+
/** The default number of entries that one run checks. */
|
|
3271
|
+
readonly maxPages: 100;
|
|
3272
|
+
/** The most hosts that get a check of one run at one time. */
|
|
3273
|
+
readonly hostsAtOnce: 8;
|
|
3274
|
+
/** A run holds the lease of an entry until the end of its budget and this time more: 2 minutes. */
|
|
3275
|
+
readonly leaseMs: 120_000;
|
|
3276
|
+
};
|
|
3277
|
+
/** The kind of a check: the page is new, changed, unchanged or gone, or the check got no version. */
|
|
3278
|
+
type WatchKind = "new" | "changed" | "unchanged" | "gone" | "refused" | "error";
|
|
3279
|
+
/** These options add a page to the watch list, or change its watch. */
|
|
3280
|
+
interface WatchOptions {
|
|
3281
|
+
/** This value is the time between two checks. The default is `24h`, and the floor is `1h` (`watchFloor`). */
|
|
3282
|
+
readonly every?: Duration | undefined;
|
|
3283
|
+
/** The value `auto` renders a page only when it shows almost nothing without JavaScript. */
|
|
3284
|
+
readonly render?: "auto" | "never" | "always" | undefined;
|
|
3285
|
+
/** These labels go into the capture records of each check. */
|
|
3286
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
3287
|
+
/** This label goes into the capture records of each check. */
|
|
3288
|
+
readonly scope?: string | undefined;
|
|
3289
|
+
/** These agents get their robots.txt rules applied to each check. */
|
|
3290
|
+
readonly onBehalfOf?: readonly string[] | undefined;
|
|
3291
|
+
/** This value is the number of checks that must see a change before it is confirmed. The default is 2. */
|
|
3292
|
+
readonly confirm?: 1 | 2 | undefined;
|
|
3293
|
+
}
|
|
3294
|
+
/** A change that one check saw, and that the next check can confirm. */
|
|
3295
|
+
interface WatchPending {
|
|
3296
|
+
/** This value is `changed`, `gone` or `new` (a page that came back). */
|
|
3297
|
+
readonly kind: "changed" | "gone" | "new";
|
|
3298
|
+
/** This id is the capture of the suspected version, or null for a page that is gone. */
|
|
3299
|
+
readonly capture: string | null;
|
|
3300
|
+
/** This time (ISO 8601) is the time of the check that first saw the change. */
|
|
3301
|
+
readonly since: string;
|
|
3302
|
+
}
|
|
3303
|
+
/** This is one entry of the watch list. */
|
|
3304
|
+
interface WatchEntry {
|
|
3305
|
+
/** This URL is the page that the watch fetches. */
|
|
3306
|
+
readonly url: string;
|
|
3307
|
+
/** This value is the time between two checks in milliseconds. */
|
|
3308
|
+
readonly every_ms: number;
|
|
3309
|
+
readonly render: "auto" | "never" | "always";
|
|
3310
|
+
readonly confirm: 1 | 2;
|
|
3311
|
+
readonly scope: string | null;
|
|
3312
|
+
readonly labels: Readonly<Record<string, string>> | null;
|
|
3313
|
+
readonly on_behalf_of: readonly string[];
|
|
3314
|
+
/** This time (ISO 8601) is the time of the first add. */
|
|
3315
|
+
readonly added_at: string;
|
|
3316
|
+
/** This time (ISO 8601) is the time when the entry is due. */
|
|
3317
|
+
readonly next_at: string;
|
|
3318
|
+
/** This time (ISO 8601) is the time of the last check, or null before the first check. */
|
|
3319
|
+
readonly checked_at: string | null;
|
|
3320
|
+
readonly last_kind: WatchKind | null;
|
|
3321
|
+
readonly last_note: string | null;
|
|
3322
|
+
/** This value counts the checks. */
|
|
3323
|
+
readonly checks: number;
|
|
3324
|
+
/** This value counts the confirmed changes. */
|
|
3325
|
+
readonly changes: number;
|
|
3326
|
+
/** This id is the document of the page. */
|
|
3327
|
+
readonly doc: string | null;
|
|
3328
|
+
/** This id is the capture of the last confirmed version, or null. */
|
|
3329
|
+
readonly capture: string | null;
|
|
3330
|
+
/** This time (ISO 8601) is the fetch time of the last confirmed version, or null. */
|
|
3331
|
+
readonly fetched_at: string | null;
|
|
3332
|
+
/** This value is true when the last confirmed state is that the page is gone. */
|
|
3333
|
+
readonly gone: boolean;
|
|
3334
|
+
/** This value is the change that the last check suspected, or null. */
|
|
3335
|
+
readonly pending: WatchPending | null;
|
|
3336
|
+
/** This time (ISO 8601) is the end of the lease of a run that checks the entry, or null. */
|
|
3337
|
+
readonly leased_until: string | null;
|
|
3338
|
+
}
|
|
3339
|
+
/** These options control a run of the watch list. */
|
|
3340
|
+
interface WatchRunOptions {
|
|
3341
|
+
/** This value is the longest time of the run in milliseconds. No request starts after it. The default is 10 minutes. */
|
|
3342
|
+
readonly budgetMs?: number | undefined;
|
|
3343
|
+
/** This value is the most entries that the run checks. The default is 100. */
|
|
3344
|
+
readonly maxPages?: number | undefined;
|
|
3345
|
+
/** When this signal aborts, the run stops the checks in progress and ends. */
|
|
3346
|
+
readonly signal?: AbortSignal | undefined;
|
|
3347
|
+
/**
|
|
3348
|
+
* When this signal aborts, the run starts no new check. The checks in progress end and write
|
|
3349
|
+
* their state, and the run stops with `cancelled`. The daemon uses it for a clean stop.
|
|
3350
|
+
*/
|
|
3351
|
+
readonly drain?: AbortSignal | undefined;
|
|
3352
|
+
}
|
|
3353
|
+
/**
|
|
3354
|
+
* These options control `runWatch`: the options of a run, and the parts of `openCrawler`.
|
|
3355
|
+
*
|
|
3356
|
+
* @beta
|
|
14
3357
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
readonly
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
/** This
|
|
24
|
-
readonly
|
|
25
|
-
|
|
26
|
-
readonly
|
|
3358
|
+
interface WatchRunnerOptions extends WatchRunOptions {
|
|
3359
|
+
/** These agents get their robots.txt rules applied to each check, before the agents of the entry. */
|
|
3360
|
+
readonly onBehalfOf?: readonly string[] | undefined;
|
|
3361
|
+
/** This function operates before the first check. `openCrawler` waits there for the open of the store. */
|
|
3362
|
+
readonly ready?: (() => Promise<void>) | undefined;
|
|
3363
|
+
}
|
|
3364
|
+
/** This is the event of one check of a run. */
|
|
3365
|
+
interface WatchEvent {
|
|
3366
|
+
/** This URL is the page of the watch. */
|
|
3367
|
+
readonly url: string;
|
|
3368
|
+
readonly doc: string | null;
|
|
3369
|
+
readonly kind: WatchKind;
|
|
3370
|
+
/** This value is true when the check saw a change for the first time, and the next check must confirm it. */
|
|
3371
|
+
readonly suspected: boolean;
|
|
3372
|
+
/** This value is true when the check confirmed a change. Its version is then the last confirmed version. */
|
|
3373
|
+
readonly confirmed: boolean;
|
|
3374
|
+
/** This id is the capture that the check got, or null. */
|
|
3375
|
+
readonly capture: string | null;
|
|
3376
|
+
/** This time (ISO 8601) is the fetch time of that capture, or null. */
|
|
3377
|
+
readonly fetched_at: string | null;
|
|
3378
|
+
/** This id is the capture of the last confirmed version before the check, or null. */
|
|
3379
|
+
readonly previous: string | null;
|
|
3380
|
+
/** This time (ISO 8601) is the fetch time of that version, or null. */
|
|
3381
|
+
readonly previous_fetched_at: string | null;
|
|
3382
|
+
readonly outcome: string;
|
|
3383
|
+
readonly status: number;
|
|
3384
|
+
/** These are the counts of the diff from the last confirmed version, for a change. A held-back page gives null. */
|
|
3385
|
+
readonly diff: DiffSummary | null;
|
|
3386
|
+
/** This value is true when the page asked not to be AI input. */
|
|
3387
|
+
readonly held_back: boolean;
|
|
3388
|
+
/** This time (ISO 8601) is the time when the entry is due again. */
|
|
3389
|
+
readonly next_at: string;
|
|
3390
|
+
readonly note: string | null;
|
|
3391
|
+
}
|
|
3392
|
+
/** The reason that a run stopped. */
|
|
3393
|
+
type WatchStop = "done" | "max-pages" | "budget" | "cancelled" | "closed" | "error";
|
|
3394
|
+
/** The summary of a run. */
|
|
3395
|
+
interface WatchSummary {
|
|
3396
|
+
/** This value counts the checks that gave an event. */
|
|
3397
|
+
readonly checked: number;
|
|
3398
|
+
readonly new: number;
|
|
3399
|
+
readonly changed: number;
|
|
3400
|
+
readonly unchanged: number;
|
|
3401
|
+
readonly gone: number;
|
|
3402
|
+
readonly refused: number;
|
|
3403
|
+
readonly error: number;
|
|
3404
|
+
/** This value counts the changes that wait for the next check. */
|
|
3405
|
+
readonly suspected: number;
|
|
3406
|
+
/** This value counts the changes that the run confirmed. */
|
|
3407
|
+
readonly confirmed: number;
|
|
3408
|
+
/** This value counts the entries that are due at the end of the run. */
|
|
3409
|
+
readonly due: number;
|
|
3410
|
+
readonly stopped: WatchStop;
|
|
3411
|
+
/** This text tells why the run stopped. */
|
|
3412
|
+
readonly reason: string;
|
|
3413
|
+
/** This value is the time of the run in milliseconds, on the clock of the crawler. */
|
|
3414
|
+
readonly elapsed_ms: number;
|
|
3415
|
+
}
|
|
3416
|
+
/** A run in progress. A `for await` loop gets each event. `summary` resolves when the loop ends. */
|
|
3417
|
+
interface WatchRun extends AsyncIterable<WatchEvent> {
|
|
3418
|
+
readonly summary: Promise<WatchSummary>;
|
|
3419
|
+
}
|
|
3420
|
+
/** This is the watch list of `openCrawler`. Each method gives a promise, except `run`. */
|
|
3421
|
+
interface WatchList {
|
|
3422
|
+
/** This method adds a page, or changes the options of its watch. A new entry is due at once. */
|
|
3423
|
+
add(url: string, options?: WatchOptions): Promise<WatchEntry>;
|
|
3424
|
+
/** This method removes a page from the list. It gives false when the list does not have the page. */
|
|
3425
|
+
remove(url: string): Promise<boolean>;
|
|
3426
|
+
/** This method gives the entries, the first due first. */
|
|
3427
|
+
list(): Promise<WatchEntry[]>;
|
|
3428
|
+
/** This method checks the entries that are due, to a budget. A `for await` loop gets each event. */
|
|
3429
|
+
run(options?: WatchRunOptions): WatchRun;
|
|
27
3430
|
}
|
|
28
3431
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
3432
|
+
* These options open a watch store.
|
|
3433
|
+
*
|
|
3434
|
+
* @beta
|
|
32
3435
|
*/
|
|
33
|
-
|
|
3436
|
+
interface WatchStoreOptions {
|
|
3437
|
+
/** This value is the time in milliseconds that a step waits for the lock of a different process. The default is 10 seconds. */
|
|
3438
|
+
readonly busyTimeoutMs?: number | undefined;
|
|
3439
|
+
/** This value is the shortest `every` in milliseconds. The default is 1 hour. */
|
|
3440
|
+
readonly floorMs?: number | undefined;
|
|
3441
|
+
}
|
|
3442
|
+
interface Row {
|
|
3443
|
+
url: string;
|
|
3444
|
+
target: string;
|
|
3445
|
+
host: string;
|
|
3446
|
+
every_ms: number;
|
|
3447
|
+
render: string;
|
|
3448
|
+
confirm: number;
|
|
3449
|
+
scope: string | null;
|
|
3450
|
+
labels: string | null;
|
|
3451
|
+
on_behalf_of: string | null;
|
|
3452
|
+
added_at: number;
|
|
3453
|
+
next_at: number;
|
|
3454
|
+
lease_until: number;
|
|
3455
|
+
lease_owner: string | null;
|
|
3456
|
+
checks: number;
|
|
3457
|
+
changes: number;
|
|
3458
|
+
last_at: number | null;
|
|
3459
|
+
last_kind: string | null;
|
|
3460
|
+
last_note: string | null;
|
|
3461
|
+
doc: string | null;
|
|
3462
|
+
base_state: string | null;
|
|
3463
|
+
base_sha: string | null;
|
|
3464
|
+
base_capture: string | null;
|
|
3465
|
+
base_fetched_at: string | null;
|
|
3466
|
+
pend_state: string | null;
|
|
3467
|
+
pend_sha: string | null;
|
|
3468
|
+
pend_capture: string | null;
|
|
3469
|
+
pend_fetched_at: string | null;
|
|
3470
|
+
pend_since: number | null;
|
|
3471
|
+
}
|
|
3472
|
+
/** The state of a page after a check: what a later check compares. */
|
|
3473
|
+
interface State {
|
|
3474
|
+
readonly state: "present" | "gone";
|
|
3475
|
+
readonly sha: string | null;
|
|
3476
|
+
readonly capture: string | null;
|
|
3477
|
+
readonly fetched_at: string | null;
|
|
3478
|
+
}
|
|
3479
|
+
/** This is the change of an entry after a check. */
|
|
3480
|
+
interface Update {
|
|
3481
|
+
readonly next_at: number;
|
|
3482
|
+
readonly last_at: number;
|
|
3483
|
+
readonly last_kind: WatchKind;
|
|
3484
|
+
readonly last_note: string | null;
|
|
3485
|
+
readonly changed: boolean;
|
|
3486
|
+
readonly doc: string | null;
|
|
3487
|
+
/** The new confirmed state, or undefined when it stays. */
|
|
3488
|
+
readonly base?: State | undefined;
|
|
3489
|
+
/** The new suspected state, null to clear it, or undefined when it stays. */
|
|
3490
|
+
readonly pending?: (State & {
|
|
3491
|
+
readonly since: number;
|
|
3492
|
+
}) | null | undefined;
|
|
3493
|
+
}
|
|
3494
|
+
/**
|
|
3495
|
+
* This store keeps the watch list in a SQLite file (`watch.sqlite`). Two or more processes on one
|
|
3496
|
+
* store share it. Each step is one transaction (`BEGIN IMMEDIATE`), and it waits for the lock of a
|
|
3497
|
+
* different process (`busy_timeout`).
|
|
3498
|
+
*
|
|
3499
|
+
* The list is the decision of the operator, not a projection of the capture log. Thus it has its
|
|
3500
|
+
* own file: a deletion of `hosts.sqlite` or of the index does not lose it.
|
|
3501
|
+
*
|
|
3502
|
+
* @beta
|
|
3503
|
+
*/
|
|
3504
|
+
export declare class WatchStore {
|
|
3505
|
+
readonly path: string;
|
|
3506
|
+
private readonly db;
|
|
3507
|
+
private readonly statements;
|
|
3508
|
+
private closed;
|
|
3509
|
+
/** This value is the shortest `every` in milliseconds. */
|
|
3510
|
+
readonly floorMs: number;
|
|
3511
|
+
constructor(path: string, options?: WatchStoreOptions);
|
|
3512
|
+
private stmt;
|
|
3513
|
+
/** This method does one step in one transaction. */
|
|
3514
|
+
private step;
|
|
3515
|
+
private row;
|
|
3516
|
+
/**
|
|
3517
|
+
* This method adds a page, or changes the options of its watch and keeps its state. A new entry
|
|
3518
|
+
* is due at once. A changed entry is due one `every` after its last check. A bad option throws
|
|
3519
|
+
* a `RangeError`.
|
|
3520
|
+
*/
|
|
3521
|
+
add(url: string, options: WatchOptions, now: number): WatchEntry;
|
|
3522
|
+
/** This method removes a page. It gives false when the list does not have the page. */
|
|
3523
|
+
remove(url: string): boolean;
|
|
3524
|
+
/** This method gives the entry of a page, or undefined. */
|
|
3525
|
+
get(url: string, now: number): WatchEntry | undefined;
|
|
3526
|
+
/** This method gives the entries, the first due first. */
|
|
3527
|
+
list(now: number): WatchEntry[];
|
|
3528
|
+
/** This method counts the entries that are due at a time. */
|
|
3529
|
+
due(now: number): number;
|
|
3530
|
+
/**
|
|
3531
|
+
* This method claims the first due entry with no lease, on a host that is not in `busy`. The
|
|
3532
|
+
* lease of `owner` holds until `until`. Thus two processes do not get one entry at one time.
|
|
3533
|
+
*/
|
|
3534
|
+
claim(now: number, owner: string, until: number, busy: readonly string[]): Row | undefined;
|
|
3535
|
+
/** This method ends the lease of `owner` and changes nothing else, so the entry stays due. A closed store does nothing. */
|
|
3536
|
+
release(key: string, owner: string): void;
|
|
3537
|
+
/**
|
|
3538
|
+
* This method writes the result of a check and ends the lease. It gives false when `owner` does
|
|
3539
|
+
* not hold the lease, or when the store is closed.
|
|
3540
|
+
*/
|
|
3541
|
+
settle(key: string, owner: string, update: Update): boolean;
|
|
3542
|
+
/** This method closes the file. A second close does nothing. */
|
|
3543
|
+
close(): void;
|
|
3544
|
+
}
|
|
3545
|
+
/**
|
|
3546
|
+
* This function checks the entries of a watch store that are due. `PoliteCrawler.watch.run` uses
|
|
3547
|
+
* it. Each check is a fetch of the page with `fetchPage`, with no stored copy (`maxAgeMs: 0`), so
|
|
3548
|
+
* each polite rule applies. A check that the budget stops before its request stays due. The store
|
|
3549
|
+
* can be a function that opens it: the run uses it before the first check.
|
|
3550
|
+
*
|
|
3551
|
+
* @beta
|
|
3552
|
+
*/
|
|
3553
|
+
export declare const runWatch: (runtime: CrawlRuntime, source: WatchStore | (() => Promise<WatchStore>), options?: WatchRunnerOptions) => WatchRun;
|
|
34
3554
|
//#endregion
|
|
35
3555
|
//#region src/version.d.ts
|
|
36
3556
|
/** This is the version of the package. A test makes sure that it agrees with `package.json`. */
|
|
37
|
-
export declare const VERSION = "0.
|
|
3557
|
+
export declare const VERSION = "0.2.0";
|
|
38
3558
|
/** This is the product token that robots.txt rules match when an application gives no token. */
|
|
39
3559
|
export declare const DEFAULT_TOKEN = "PoliteCrawler";
|
|
40
3560
|
/** This page tells a site owner what the crawler is and how to control it. */
|
|
41
3561
|
export declare const BOT_PAGE = "https://mark1russell7.github.io/crawler/bot/";
|
|
42
3562
|
//#endregion
|
|
3563
|
+
//#region src/open.d.ts
|
|
3564
|
+
/**
|
|
3565
|
+
* This function gives the default `User-Agent` header. The header names the crawler, its version
|
|
3566
|
+
* and the page about it, so a site owner can find out what made each request.
|
|
3567
|
+
*/
|
|
3568
|
+
export declare const defaultUserAgent: (contact?: string) => string;
|
|
3569
|
+
/**
|
|
3570
|
+
* This function tells if a contact is a mailbox. The `From` header holds only a mailbox (RFC 9110,
|
|
3571
|
+
* section 10.1.2). Thus the crawler sends a mailbox in `From` and in the `User-Agent`, and a URL in
|
|
3572
|
+
* the `User-Agent` only.
|
|
3573
|
+
*
|
|
3574
|
+
* @beta
|
|
3575
|
+
*/
|
|
3576
|
+
export declare const isMailbox: (contact: string) => boolean;
|
|
3577
|
+
/**
|
|
3578
|
+
* These options make a runtime. A store needs a log, a blob store and an index. Give `dir`, and
|
|
3579
|
+
* the runtime makes the three parts in it, or give the parts. A part that you give replaces the
|
|
3580
|
+
* part in `dir`.
|
|
3581
|
+
*/
|
|
3582
|
+
interface RuntimeOptions {
|
|
3583
|
+
/** This folder keeps the capture log (`captures/`), the bodies (`blobs/`), the index (`index.sqlite`) and the state of each host (`hosts.sqlite`). */
|
|
3584
|
+
readonly dir?: string | undefined;
|
|
3585
|
+
/**
|
|
3586
|
+
* This log replaces `<dir>/captures`.
|
|
3587
|
+
*
|
|
3588
|
+
* @beta
|
|
3589
|
+
*/
|
|
3590
|
+
readonly log?: CaptureLog | undefined;
|
|
3591
|
+
/**
|
|
3592
|
+
* This blob store replaces `<dir>/blobs`.
|
|
3593
|
+
*
|
|
3594
|
+
* @beta
|
|
3595
|
+
*/
|
|
3596
|
+
readonly blobs?: BlobStore | undefined;
|
|
3597
|
+
/**
|
|
3598
|
+
* This index replaces `<dir>/index.sqlite`. A runtime takes any `ReadingIndex`.
|
|
3599
|
+
*
|
|
3600
|
+
* @beta
|
|
3601
|
+
*/
|
|
3602
|
+
readonly index?: ReadingIndex | undefined;
|
|
3603
|
+
/**
|
|
3604
|
+
* This store keeps the pace and the stops of each host. The default with `dir` is a SQLite file
|
|
3605
|
+
* (`hosts.sqlite`), so the processes on one store share the pace. With no `dir`, it is in memory.
|
|
3606
|
+
*/
|
|
3607
|
+
readonly hostState?: HostStateStore | undefined;
|
|
3608
|
+
/** This token is the name that robots.txt rules match. The default is `PoliteCrawler`. */
|
|
3609
|
+
readonly token?: string | undefined;
|
|
3610
|
+
/** This text is the full `User-Agent` header. The default names the crawler and its page. */
|
|
3611
|
+
readonly userAgent?: string | undefined;
|
|
3612
|
+
/**
|
|
3613
|
+
* This address goes into the `User-Agent` header. A mailbox (`ops@example.com`) also goes into the
|
|
3614
|
+
* `From` header, and a URL does not. The crawler sends no contact without it.
|
|
3615
|
+
*/
|
|
3616
|
+
readonly contact?: string | undefined;
|
|
3617
|
+
/**
|
|
3618
|
+
* This browser renders the pages that need JavaScript. The value `false` turns rendering off. The
|
|
3619
|
+
* default checks one time that `playwright-core` and its browser are installed. With no browser,
|
|
3620
|
+
* each page that needs JavaScript gets a note at once, with no wait.
|
|
3621
|
+
*
|
|
3622
|
+
* @beta
|
|
3623
|
+
*/
|
|
3624
|
+
readonly renderer?: Renderer | false | undefined;
|
|
3625
|
+
/** This value is the number of sites that the crawler fetches from at one time. The default is 16. */
|
|
3626
|
+
readonly maxActive?: number | undefined;
|
|
3627
|
+
/** This value is the number of pages that the browser renders at one time. The default is 2: each render holds a browser context. */
|
|
3628
|
+
readonly maxRenders?: number | undefined;
|
|
3629
|
+
/** This `undici` dispatcher sends the requests. Tests give a mock agent, and a proxy is possible. */
|
|
3630
|
+
readonly dispatcher?: Dispatcher | undefined;
|
|
3631
|
+
/** This policy controls the addresses that the crawler fetches. By default, it fetches only public addresses. */
|
|
3632
|
+
readonly egress?: EgressOptions | undefined;
|
|
3633
|
+
/** These values change the limits of the politeness policy, for each host or for some hosts. */
|
|
3634
|
+
readonly politeness?: PolicyOptions | undefined;
|
|
3635
|
+
/** This clock gives the time to the crawler and to the index. Tests give a clock that they control. */
|
|
3636
|
+
readonly clock?: Clock | undefined;
|
|
3637
|
+
/** This function gives the random numbers of the jitter. The default is `Math.random`. */
|
|
3638
|
+
readonly random?: (() => number) | undefined;
|
|
3639
|
+
/** This label goes into each capture record. */
|
|
3640
|
+
readonly scope?: string | undefined;
|
|
3641
|
+
/** With the value false, the crawler does not also obey the group of `PoliteCrawler` when its `User-Agent` links the bot page (`CrawlerOptions.obeyDefaultToken`). */
|
|
3642
|
+
readonly obeyDefaultToken?: boolean | undefined;
|
|
3643
|
+
/** This function ranks the scopes, the most private highest. A shared fetch of a robots.txt records the scope with the highest rank (`CrawlerOptions.scopeRank`). */
|
|
3644
|
+
readonly scopeRank?: ((scope: string) => number) | undefined;
|
|
3645
|
+
/** This function gets an event for each request, response, wait, stop and skip. */
|
|
3646
|
+
readonly onEvent?: ((event: CrawlEvent) => void) | undefined;
|
|
3647
|
+
/** These extractors read the captures of the recipes of an application. The recipes `page` and `archive-copy` are built in. */
|
|
3648
|
+
readonly extractors?: Readonly<Record<string, Extractor>> | undefined;
|
|
3649
|
+
/**
|
|
3650
|
+
* These recipes of `defineRecipe` get their extractors at open. Thus `reindex` reads their
|
|
3651
|
+
* captures before the first fetch. A recipe with the name of an entry of `extractors` throws an error.
|
|
3652
|
+
*/
|
|
3653
|
+
readonly recipes?: ReadonlyArray<Recipe<never>> | undefined;
|
|
3654
|
+
/**
|
|
3655
|
+
* This signer signs each request with Web Bot Auth (`CrawlerOptions.signer`). Give the options of
|
|
3656
|
+
* `createWebBotAuthSigner`, or a `RequestSigner`. Without it, the crawler signs nothing.
|
|
3657
|
+
*/
|
|
3658
|
+
readonly signer?: RequestSigner | WebBotAuthOptions | undefined;
|
|
3659
|
+
}
|
|
3660
|
+
/**
|
|
3661
|
+
* A runtime that a store made. It owns the parts that it made, and it closes them.
|
|
3662
|
+
*
|
|
3663
|
+
* @beta
|
|
3664
|
+
*/
|
|
3665
|
+
interface StoreRuntime extends CrawlRuntime {
|
|
3666
|
+
readonly hostState: HostStateStore;
|
|
3667
|
+
/** This method closes the crawler, the browser, the log, the index and the host state store that the runtime made. A second close does nothing. */
|
|
3668
|
+
close(): Promise<void>;
|
|
3669
|
+
}
|
|
3670
|
+
/**
|
|
3671
|
+
* This function makes a runtime. It is the one place that puts the log, the blob store, the index,
|
|
3672
|
+
* the host state, the crawler and the extractors together. The function `openCrawler` uses it.
|
|
3673
|
+
* An application that needs the parts uses it too. The runtime and the crawler share one clock.
|
|
3674
|
+
*
|
|
3675
|
+
* @beta
|
|
3676
|
+
*/
|
|
3677
|
+
export declare const createRuntime: (options: RuntimeOptions) => StoreRuntime;
|
|
3678
|
+
/** These options open a crawler on a folder. Only `dir` is necessary. */
|
|
3679
|
+
interface OpenOptions extends Omit<RuntimeOptions, "dir" | "log" | "blobs" | "index"> {
|
|
3680
|
+
/** This folder keeps the capture log, the response bodies, the search index and the state of each host. */
|
|
3681
|
+
readonly dir: string;
|
|
3682
|
+
/** These agents get their robots.txt rules applied to each fetch, for example `Claude-User`. */
|
|
3683
|
+
readonly onBehalfOf?: readonly string[] | undefined;
|
|
3684
|
+
/** This value is the shortest `every` of a watch. The default is `1h`, so a watch cannot hammer a site. */
|
|
3685
|
+
readonly watchFloor?: Duration | undefined;
|
|
3686
|
+
/**
|
|
3687
|
+
* This embedder makes the vectors of hybrid search (`rank: "hybrid"`): `transformersEmbedder`,
|
|
3688
|
+
* `ollamaEmbedder` or an embedder of your own. The vectors go into `vectors.sqlite` in the store.
|
|
3689
|
+
* Without it, `rank: "hybrid"` throws an error. `close` also closes the embedder.
|
|
3690
|
+
*/
|
|
3691
|
+
readonly embedder?: Embedder | undefined;
|
|
3692
|
+
}
|
|
3693
|
+
/** These options control one fetch. */
|
|
3694
|
+
interface FetchOptions {
|
|
3695
|
+
/** The value `auto` renders a page only when it shows almost nothing without JavaScript. */
|
|
3696
|
+
readonly render?: "auto" | "never" | "always" | undefined;
|
|
3697
|
+
/**
|
|
3698
|
+
* A stored copy younger than this age answers, and the crawler sends no request. The default is
|
|
3699
|
+
* `24h`. The value `auto` uses the freshness that the server gave. The value `0s` asks again.
|
|
3700
|
+
*/
|
|
3701
|
+
readonly maxAge?: Duration | undefined;
|
|
3702
|
+
/** These agents get their robots.txt rules applied to this fetch, in addition to the defaults. */
|
|
3703
|
+
readonly onBehalfOf?: readonly string[] | undefined;
|
|
3704
|
+
/** When this signal aborts, the crawler stops the fetch and the render. */
|
|
3705
|
+
readonly signal?: AbortSignal | undefined;
|
|
3706
|
+
/** This label goes into the capture records of this fetch. */
|
|
3707
|
+
readonly scope?: string | undefined;
|
|
3708
|
+
/** These labels go into the capture records of this fetch. They are not part of the capture ids. */
|
|
3709
|
+
readonly labels?: Readonly<Record<string, string>> | undefined;
|
|
3710
|
+
/**
|
|
3711
|
+
* The value `archive` gives a copy from a public web archive (the Wayback Machine, then Common
|
|
3712
|
+
* Crawl) when the site refuses the page or is down. The result then has `archived`. The default is `none`.
|
|
3713
|
+
*/
|
|
3714
|
+
readonly fallback?: "archive" | "none" | undefined;
|
|
3715
|
+
/**
|
|
3716
|
+
* The value true also gives an archived copy of a page that the current robots.txt of its site
|
|
3717
|
+
* disallows for this agent. The default is false: only a stop, an error or a dead host gets a copy.
|
|
3718
|
+
*/
|
|
3719
|
+
readonly archiveDespiteRobots?: boolean | undefined;
|
|
3720
|
+
}
|
|
3721
|
+
/** These options control a search. */
|
|
3722
|
+
interface FindOptions {
|
|
3723
|
+
/** This value is the maximum number of results. The default is 10. */
|
|
3724
|
+
readonly limit?: number | undefined;
|
|
3725
|
+
/** This value keeps only the documents of one kind, for example `page` or `pdf`. */
|
|
3726
|
+
readonly kind?: string | null | undefined;
|
|
3727
|
+
/**
|
|
3728
|
+
* This value chooses the ranking. The default `exact` ranks each match with BM25. `fast` leaves
|
|
3729
|
+
* the common words out of the second part of the search, so a long question is faster (`RankMode`).
|
|
3730
|
+
*/
|
|
3731
|
+
readonly rank?: RankMode | undefined;
|
|
3732
|
+
}
|
|
3733
|
+
/**
|
|
3734
|
+
* These options control a search of `openCrawler`: the options of `find`, and `rank: "hybrid"`
|
|
3735
|
+
* with the options of a hybrid search. The mode `hybrid` needs `OpenOptions.embedder`.
|
|
3736
|
+
*/
|
|
3737
|
+
interface SearchOptions extends Omit<FindOptions, "rank">, HybridFindOptions {
|
|
3738
|
+
/** This value chooses the ranking. `hybrid` fuses BM25 with the ranking of the vectors (`SearchRank`). */
|
|
3739
|
+
readonly rank?: SearchRank | undefined;
|
|
3740
|
+
}
|
|
3741
|
+
/** These options control `rankWithin` of a crawler. */
|
|
3742
|
+
interface RankOptions extends HybridOptions {
|
|
3743
|
+
/** This value chooses the ranking. The default is `exact`. */
|
|
3744
|
+
readonly rank?: SearchRank | undefined;
|
|
3745
|
+
}
|
|
3746
|
+
/** A polite crawler fetches each source one time, keeps it, and answers the next questions from the disk. */
|
|
3747
|
+
interface PoliteCrawler {
|
|
3748
|
+
/**
|
|
3749
|
+
* The runtime gives the parts to an application that needs more control.
|
|
3750
|
+
*
|
|
3751
|
+
* @beta
|
|
3752
|
+
*/
|
|
3753
|
+
readonly runtime: StoreRuntime;
|
|
3754
|
+
/** This method fetches a page, reads it and adds it to the index. */
|
|
3755
|
+
fetch(url: string, options?: FetchOptions): Promise<PageResult>;
|
|
3756
|
+
/**
|
|
3757
|
+
* This method searches the stored documents. It sends no request. A held-back document is not
|
|
3758
|
+
* in the results. The syntax of the query is the syntax of `parseQuery`.
|
|
3759
|
+
*/
|
|
3760
|
+
find(q: string, options?: FindOptions): Promise<Found[]>;
|
|
3761
|
+
/** This method reads a range of characters of one stored document, the current version or the version of one capture. */
|
|
3762
|
+
read(doc: string, options?: ReadOptions): Promise<ReadResult>;
|
|
3763
|
+
/**
|
|
3764
|
+
* This method finds a quote in a stored document. It ignores case, spaces, typographic
|
|
3765
|
+
* punctuation and Unicode forms. It tells which capture holds the quote.
|
|
3766
|
+
*/
|
|
3767
|
+
verify(doc: string, quote: string, options?: VerifyOptions): Promise<VerifyResult>;
|
|
3768
|
+
/** This method gives the versions of a stored document, the newest first. A held-back version shows no title. */
|
|
3769
|
+
versions(doc: string, options?: {
|
|
3770
|
+
readonly limit?: number;
|
|
3771
|
+
}): Promise<DocVersion[]>;
|
|
3772
|
+
/**
|
|
3773
|
+
* This method builds the index again from the capture log and the stored bodies, with no request.
|
|
3774
|
+
* When the crawler rebuilt a stale index at open, the first reindex gives the counts of that
|
|
3775
|
+
* rebuild, unless a fetch came before it.
|
|
3776
|
+
*/
|
|
3777
|
+
reindex(): Promise<ReindexResult>;
|
|
3778
|
+
/** This method closes the browser, the log files and the index. A second close does nothing. */
|
|
3779
|
+
close(): Promise<void>;
|
|
3780
|
+
/**
|
|
3781
|
+
* This optional method finds the pages of a site from its sitemaps and its feeds. Each file is a
|
|
3782
|
+
* polite fetch with its capture. It keeps the URLs on the site that robots.txt permits, to its
|
|
3783
|
+
* limits. `openCrawler` gives it. Over a runtime of your own, use `discoverSite`.
|
|
3784
|
+
*/
|
|
3785
|
+
discover?(site: string, options?: DiscoverOptions): Promise<DiscoverResult>;
|
|
3786
|
+
/**
|
|
3787
|
+
* This optional method crawls a site from its seeds. It follows the links of each page to a
|
|
3788
|
+
* maximum of pages, a maximum depth and a maximum time. A `for await` loop gets each page.
|
|
3789
|
+
* `summary` tells why the crawl stopped. `openCrawler` gives it. Over a runtime of your own, use
|
|
3790
|
+
* `crawlSite`.
|
|
3791
|
+
*/
|
|
3792
|
+
crawl?(seeds: string | readonly string[], options?: CrawlOptions): CrawlRun;
|
|
3793
|
+
/**
|
|
3794
|
+
* This optional method compares two versions of a stored document: the lines and the words of
|
|
3795
|
+
* the text, and the metadata at its paths. It sends no request. `openCrawler` gives it.
|
|
3796
|
+
*/
|
|
3797
|
+
diff?(doc: string, options?: DiffOptions): Promise<DiffResult>;
|
|
3798
|
+
/** This optional list keeps the pages that the crawler checks again on a schedule. `openCrawler` gives it. */
|
|
3799
|
+
readonly watch?: WatchList;
|
|
3800
|
+
/**
|
|
3801
|
+
* This optional method checks the proposed values of fields against a stored document, and it
|
|
3802
|
+
* locates each value. It does not make values. It sends no request. `openCrawler` gives it.
|
|
3803
|
+
*/
|
|
3804
|
+
extract?(doc: string, fields: Readonly<Record<string, FieldClaim>>, options?: ExtractOptions): Promise<ExtractResult>;
|
|
3805
|
+
/** This optional search ranks with the vectors of an embedder. `openCrawler` gives it when it has an embedder. */
|
|
3806
|
+
readonly hybrid?: HybridSearch | undefined;
|
|
3807
|
+
/** This optional method gives vectors to the stored documents that have none, within a budget. */
|
|
3808
|
+
embed?(options?: EmbedOptions): Promise<EmbedResult>;
|
|
3809
|
+
/** This optional method ranks a set of stored documents against a query, the best first. */
|
|
3810
|
+
rankWithin?(q: string, docs: readonly string[], options?: RankOptions): Promise<Ranked[]>;
|
|
3811
|
+
}
|
|
3812
|
+
/** This crawler is the result of `openCrawler` in version 0.1.2: a `PoliteCrawler` with `discover`, `crawl`, `diff`, `watch` and `extract`. */
|
|
3813
|
+
interface OpenedCrawler extends PoliteCrawler {
|
|
3814
|
+
/** This method finds the pages of a site from its sitemaps and its feeds. */
|
|
3815
|
+
discover(site: string, options?: DiscoverOptions): Promise<DiscoverResult>;
|
|
3816
|
+
/** This method crawls a site from its seeds, within its limits. A `for await` loop gets each page. */
|
|
3817
|
+
crawl(seeds: string | readonly string[], options?: CrawlOptions): CrawlRun;
|
|
3818
|
+
/** This method compares two versions of a stored document. It sends no request. */
|
|
3819
|
+
diff(doc: string, options?: DiffOptions): Promise<DiffResult>;
|
|
3820
|
+
/** This list keeps the pages that the crawler checks again on a schedule, in `watch.sqlite`. */
|
|
3821
|
+
readonly watch: WatchList;
|
|
3822
|
+
/**
|
|
3823
|
+
* This method checks the proposed values of fields against a stored document. Each field gets
|
|
3824
|
+
* `verified`, `normalized`, `ambiguous`, `not-found`, `held-back` or `invalid`.
|
|
3825
|
+
*/
|
|
3826
|
+
extract(doc: string, fields: Readonly<Record<string, FieldClaim>>, options?: ExtractOptions): Promise<ExtractResult>;
|
|
3827
|
+
}
|
|
3828
|
+
/**
|
|
3829
|
+
* This crawler is the result of `openCrawler`: an `OpenedCrawler` with hybrid search. Its `find`
|
|
3830
|
+
* takes `rank: "hybrid"`, and it has `hybrid`, `embed` and `rankWithin`. `OpenedCrawler` does not
|
|
3831
|
+
* change, so an object of your own that implements the `OpenedCrawler` of 0.1.2 stays correct.
|
|
3832
|
+
*/
|
|
3833
|
+
interface HybridCrawler extends OpenedCrawler {
|
|
3834
|
+
/** This method searches the stored documents. It also takes `rank: "hybrid"`, when the crawler has an embedder. */
|
|
3835
|
+
find(q: string, options?: SearchOptions): Promise<Found[]>;
|
|
3836
|
+
/** This search ranks with the vectors of the embedder, or it is undefined when the crawler has no embedder. */
|
|
3837
|
+
readonly hybrid: HybridSearch | undefined;
|
|
3838
|
+
/** This method gives vectors to the stored documents that have none, within a budget. It rejects when the crawler has no embedder. */
|
|
3839
|
+
embed(options?: EmbedOptions): Promise<EmbedResult>;
|
|
3840
|
+
/**
|
|
3841
|
+
* This method ranks a set of stored documents against a query, the best first. The modes
|
|
3842
|
+
* `exact` and `fast` are `DocIndex.rankWithin`. The mode `hybrid` needs an embedder.
|
|
3843
|
+
*/
|
|
3844
|
+
rankWithin(q: string, docs: readonly string[], options?: RankOptions): Promise<Ranked[]>;
|
|
3845
|
+
}
|
|
3846
|
+
/**
|
|
3847
|
+
* This function opens a crawler on a folder. The folder keeps the capture log (`captures/`), the
|
|
3848
|
+
* response bodies (`blobs/`), the search index (`index.sqlite`) and the state of each host
|
|
3849
|
+
* (`hosts.sqlite`). The index is a projection: `reindex` makes it again from the log and the bodies.
|
|
3850
|
+
* At open, `catchUpIndex` rebuilds an index of an earlier version, and gives an index that is
|
|
3851
|
+
* behind the log the captures that it missed.
|
|
3852
|
+
*
|
|
3853
|
+
* Each method gives a promise, and none throws before its promise. After `close`, each method
|
|
3854
|
+
* rejects.
|
|
3855
|
+
*/
|
|
3856
|
+
export declare const openCrawler: (options: OpenOptions) => HybridCrawler;
|
|
3857
|
+
//#endregion
|
|
3858
|
+
//#region src/daemon.d.ts
|
|
3859
|
+
/** These are the defaults of the daemon. */
|
|
3860
|
+
export declare const DAEMON_DEFAULTS: {
|
|
3861
|
+
/** The longest wait between two ticks: 1 minute. A tick comes sooner when an entry is due sooner. */
|
|
3862
|
+
readonly tickMs: 60_000;
|
|
3863
|
+
/** The most random time that the daemon adds to each wait: 10 seconds. */
|
|
3864
|
+
readonly jitterMs: 10_000;
|
|
3865
|
+
/** The budget of the checks of one tick: 10 minutes. No request starts after it. */
|
|
3866
|
+
readonly budgetMs: 600_000;
|
|
3867
|
+
/** The most checks of one tick. */
|
|
3868
|
+
readonly maxPages: 100;
|
|
3869
|
+
/** At a stop, the checks in progress get this time to end: 10 seconds. Then they abort. */
|
|
3870
|
+
readonly graceMs: 10_000;
|
|
3871
|
+
/** The shortest wait between two ticks: 1 second. */
|
|
3872
|
+
readonly minGapMs: 1_000;
|
|
3873
|
+
/** The shortest tick: 1 second. */
|
|
3874
|
+
readonly minTickMs: 1_000;
|
|
3875
|
+
/** The status lists this number of the next due entries. */
|
|
3876
|
+
readonly dueShown: 10;
|
|
3877
|
+
/** An on-change command that runs longer than this time stops: 1 minute. */
|
|
3878
|
+
readonly hookTimeoutMs: 60_000;
|
|
3879
|
+
};
|
|
3880
|
+
/** This value is the version of the format of the state file. */
|
|
3881
|
+
export declare const DAEMON_STATE_SCHEMA = 1;
|
|
3882
|
+
/** This is a watch entry that is due soon, as the status shows it. */
|
|
3883
|
+
interface DaemonDue {
|
|
3884
|
+
readonly url: string;
|
|
3885
|
+
/** This time (ISO 8601) is the time when the entry is due. */
|
|
3886
|
+
readonly next_at: string;
|
|
3887
|
+
readonly every_ms: number;
|
|
3888
|
+
/** This value is the kind of the last check, or null before the first check. */
|
|
3889
|
+
readonly last_kind: string | null;
|
|
3890
|
+
}
|
|
3891
|
+
/** This is one tick of the schedule: one run of the watch list. */
|
|
3892
|
+
interface DaemonTick {
|
|
3893
|
+
/** This time (ISO 8601) is the start of the run. */
|
|
3894
|
+
readonly started_at: string;
|
|
3895
|
+
/** This time (ISO 8601) is the end of the run. */
|
|
3896
|
+
readonly ended_at: string;
|
|
3897
|
+
/** This value is the summary of the run, or null when the run failed. */
|
|
3898
|
+
readonly summary: WatchSummary | null;
|
|
3899
|
+
/** This text tells why the run failed, or null. */
|
|
3900
|
+
readonly error: string | null;
|
|
3901
|
+
/** This time (ISO 8601) is the time of the next tick, or null when the schedule stops. */
|
|
3902
|
+
readonly next_at: string | null;
|
|
3903
|
+
/** These entries are the next due entries after the run, the first due first. */
|
|
3904
|
+
readonly due: readonly DaemonDue[];
|
|
3905
|
+
}
|
|
3906
|
+
/** These options control the schedule of the watch list. */
|
|
3907
|
+
interface ScheduleOptions {
|
|
3908
|
+
/** This value is the longest wait between two ticks. The default is `1m`, and the floor is `1s`. */
|
|
3909
|
+
readonly tick?: Duration | undefined;
|
|
3910
|
+
/** This value is the most random time that the schedule adds to each wait. The default is `10s`. */
|
|
3911
|
+
readonly jitter?: Duration | undefined;
|
|
3912
|
+
/** This value is the budget of the checks of one tick. The default is `10m`. */
|
|
3913
|
+
readonly budget?: Duration | undefined;
|
|
3914
|
+
/** This value is the most checks of one tick. The default is 100. */
|
|
3915
|
+
readonly maxPages?: number | undefined;
|
|
3916
|
+
/** This clock gives the time and the waits. Give the clock of the crawler. The default is the real clock. */
|
|
3917
|
+
readonly clock?: Clock | undefined;
|
|
3918
|
+
/** This function gives the random numbers of the jitter. The default is `Math.random`. */
|
|
3919
|
+
readonly random?: (() => number) | undefined;
|
|
3920
|
+
/** This function gets each event of each check. */
|
|
3921
|
+
readonly onEvent?: ((event: WatchEvent) => void) | undefined;
|
|
3922
|
+
/** This function gets each tick when its run ends. */
|
|
3923
|
+
readonly onTick?: ((tick: DaemonTick) => void) | undefined;
|
|
3924
|
+
/** This function gets the error of a run that failed. The schedule goes on at the next tick. */
|
|
3925
|
+
readonly onError?: ((error: Error) => void) | undefined;
|
|
3926
|
+
}
|
|
3927
|
+
/** This is the result of a schedule: the counts so far, and the last tick. */
|
|
3928
|
+
interface ScheduleResult {
|
|
3929
|
+
readonly ticks: number;
|
|
3930
|
+
/** This value counts the checks that gave an event. */
|
|
3931
|
+
readonly checked: number;
|
|
3932
|
+
/** This value counts the confirmed changes. */
|
|
3933
|
+
readonly changes: number;
|
|
3934
|
+
readonly last: DaemonTick | null;
|
|
3935
|
+
}
|
|
3936
|
+
/** A schedule that runs. `stop` ends it, and `done` resolves when it ends. */
|
|
3937
|
+
interface WatchSchedule {
|
|
3938
|
+
/** This promise resolves when the schedule ends. It does not reject. */
|
|
3939
|
+
readonly done: Promise<ScheduleResult>;
|
|
3940
|
+
/**
|
|
3941
|
+
* This method stops the schedule. The run in progress starts no new check. The checks in
|
|
3942
|
+
* progress get `grace` (10 seconds by default) to end and write their state. Then they abort,
|
|
3943
|
+
* and the run releases their leases. A second call with a shorter grace shortens it.
|
|
3944
|
+
*/
|
|
3945
|
+
stop(grace?: Duration): Promise<ScheduleResult>;
|
|
3946
|
+
/** This method gives the counts so far. */
|
|
3947
|
+
result(): ScheduleResult;
|
|
3948
|
+
}
|
|
3949
|
+
/**
|
|
3950
|
+
* This function checks a watch list on a schedule. The first tick starts at once. Each tick is one
|
|
3951
|
+
* `watch.run` with the budget and `maxPages` of a tick. Thus each polite rule of the run applies:
|
|
3952
|
+
* one request at a time to each host, the stops of a host, `Retry-After` and robots.txt.
|
|
3953
|
+
*
|
|
3954
|
+
* After a run, the schedule waits for the next tick (`nextTickAt`). An error of a run goes to
|
|
3955
|
+
* `onError`, and the schedule goes on.
|
|
3956
|
+
*/
|
|
3957
|
+
export declare const scheduleWatches: (watch: WatchList, options?: ScheduleOptions) => WatchSchedule;
|
|
3958
|
+
/** This is a task of the MCP server, as the status shows it. */
|
|
3959
|
+
interface DaemonTask {
|
|
3960
|
+
readonly id: string;
|
|
3961
|
+
/** This value is the name of the tool, for example `crawl_site`. */
|
|
3962
|
+
readonly tool: string;
|
|
3963
|
+
/** This value is `working`, `input_required`, `completed`, `failed` or `cancelled`. */
|
|
3964
|
+
readonly status: string;
|
|
3965
|
+
/** This text tells the progress, for example `3 of 10 done`, or null. */
|
|
3966
|
+
readonly message: string | null;
|
|
3967
|
+
readonly created_at: string;
|
|
3968
|
+
readonly updated_at: string;
|
|
3969
|
+
}
|
|
3970
|
+
/** This is the status of a daemon. The state file (`<store>/daemon/state.json`) holds the same JSON. */
|
|
3971
|
+
interface DaemonStatus {
|
|
3972
|
+
/** This value is the version of the format (`DAEMON_STATE_SCHEMA`). */
|
|
3973
|
+
readonly schema: number;
|
|
3974
|
+
/** This value is the version of polite-crawler. */
|
|
3975
|
+
readonly version: string;
|
|
3976
|
+
/** This value is the id of the process of the daemon. */
|
|
3977
|
+
readonly pid: number;
|
|
3978
|
+
readonly store: string;
|
|
3979
|
+
/** This value is false after the daemon stopped. */
|
|
3980
|
+
readonly running: boolean;
|
|
3981
|
+
readonly started_at: string;
|
|
3982
|
+
readonly updated_at: string;
|
|
3983
|
+
readonly stopped_at: string | null;
|
|
3984
|
+
readonly tick_ms: number;
|
|
3985
|
+
readonly jitter_ms: number;
|
|
3986
|
+
readonly budget_ms: number;
|
|
3987
|
+
readonly max_pages: number;
|
|
3988
|
+
/** This value counts the ticks. */
|
|
3989
|
+
readonly ticks: number;
|
|
3990
|
+
/** This value counts the checks. */
|
|
3991
|
+
readonly checked: number;
|
|
3992
|
+
/** This value counts the confirmed changes. */
|
|
3993
|
+
readonly changes: number;
|
|
3994
|
+
readonly last_tick: DaemonTick | null;
|
|
3995
|
+
/** This time (ISO 8601) is the time of the next tick, or null. */
|
|
3996
|
+
readonly next_tick_at: string | null;
|
|
3997
|
+
/** These entries are the next due entries at the last tick, the first due first. */
|
|
3998
|
+
readonly next_due: readonly DaemonDue[];
|
|
3999
|
+
/** These tasks of the MCP server did not end. */
|
|
4000
|
+
readonly tasks: readonly DaemonTask[];
|
|
4001
|
+
/** This value is the MCP endpoint and the file of its token, or null with no MCP server. */
|
|
4002
|
+
readonly mcp: {
|
|
4003
|
+
readonly url: string;
|
|
4004
|
+
readonly token_file: string | null;
|
|
4005
|
+
} | null;
|
|
4006
|
+
/** This value is the last error: of a tick, of the event log, of an on-change hook or of the MCP server. */
|
|
4007
|
+
readonly last_error: {
|
|
4008
|
+
readonly at: string;
|
|
4009
|
+
readonly message: string;
|
|
4010
|
+
} | null;
|
|
4011
|
+
}
|
|
4012
|
+
/** This is a status that `readDaemonState` read from a state file. */
|
|
4013
|
+
interface DaemonState extends DaemonStatus {
|
|
4014
|
+
/** This value is true when the daemon runs: the state says so, and its process is alive. */
|
|
4015
|
+
readonly alive: boolean;
|
|
4016
|
+
}
|
|
4017
|
+
/** This function gives the path of the state file of a store. */
|
|
4018
|
+
export declare const daemonStateFile: (dir: string) => string;
|
|
4019
|
+
/**
|
|
4020
|
+
* This function reads the state file of a daemon. It gives undefined when the file is not there,
|
|
4021
|
+
* or when it is not a state file. `alive` tells if the daemon runs.
|
|
4022
|
+
*/
|
|
4023
|
+
export declare const readDaemonState: (dir: string, file?: string) => DaemonState | undefined;
|
|
4024
|
+
/** These options start an on-change command. */
|
|
4025
|
+
interface ChangeCommandOptions {
|
|
4026
|
+
/** A command that runs longer than this time stops. The default is `1m`. */
|
|
4027
|
+
readonly timeout?: Duration | undefined;
|
|
4028
|
+
/** This folder is the working folder of the command. The default is the working folder of the daemon. */
|
|
4029
|
+
readonly cwd?: string | undefined;
|
|
4030
|
+
}
|
|
4031
|
+
/**
|
|
4032
|
+
* This function gives an `onChange` hook that starts a command of the operator for each change.
|
|
4033
|
+
* The command operates in the shell of the system, as the user of the daemon. It gets the event as
|
|
4034
|
+
* one JSON line on its standard input, not in its command line. Thus no text of a page goes into
|
|
4035
|
+
* the command line. The output of the command goes nowhere. A command that exits with a code that
|
|
4036
|
+
* is not 0, or that runs too long, rejects with the end of its standard error.
|
|
4037
|
+
*
|
|
4038
|
+
* The command is the decision of the operator. Do not take it from a page or from an assistant.
|
|
4039
|
+
*/
|
|
4040
|
+
export declare const changeCommand: (command: string, options?: ChangeCommandOptions) => ((event: WatchEvent) => Promise<void>);
|
|
4041
|
+
/**
|
|
4042
|
+
* These options serve the MCP tools over HTTP.
|
|
4043
|
+
*
|
|
4044
|
+
* @beta
|
|
4045
|
+
*/
|
|
4046
|
+
interface DaemonMcpOptions {
|
|
4047
|
+
/** This value is the port. The default is 9307. The value 0 takes a free port. */
|
|
4048
|
+
readonly port?: number | undefined;
|
|
4049
|
+
/** This value is the host to bind. The default is `127.0.0.1`. A host that is not loopback needs `allowRemote`. */
|
|
4050
|
+
readonly host?: string | undefined;
|
|
4051
|
+
/** The value true permits a host that is not loopback. Read the warning in the README first. */
|
|
4052
|
+
readonly allowRemote?: boolean | undefined;
|
|
4053
|
+
/** This value is the bearer token. The default makes a new token at each start, in `<store>/daemon/mcp-token`. */
|
|
4054
|
+
readonly token?: string | undefined;
|
|
4055
|
+
/** This file holds the bearer token of the operator. The daemon reads it and writes nothing. */
|
|
4056
|
+
readonly tokenFile?: string | undefined;
|
|
4057
|
+
/** These tokens get their robots.txt rules applied to each fetch of a tool. The default is `["Claude-User"]`. */
|
|
4058
|
+
readonly onBehalfOf?: readonly string[] | undefined;
|
|
4059
|
+
/** The value true lets `user_provided` release held-back text when the client cannot ask the user. */
|
|
4060
|
+
readonly allowUserProvided?: boolean | undefined;
|
|
4061
|
+
/** This text tells why the server renders no page. */
|
|
4062
|
+
readonly renderOff?: string | undefined;
|
|
4063
|
+
}
|
|
4064
|
+
/**
|
|
4065
|
+
* These functions connect an MCP host to the daemon.
|
|
4066
|
+
*
|
|
4067
|
+
* @beta
|
|
4068
|
+
*/
|
|
4069
|
+
interface DaemonMcpHooks {
|
|
4070
|
+
/** This function gives the status of the daemon, for the status route. */
|
|
4071
|
+
readonly status: () => DaemonStatus;
|
|
4072
|
+
/** This function operates when a task starts, changes or ends. */
|
|
4073
|
+
readonly onTasks: () => void;
|
|
4074
|
+
/** This function writes a message of the server. */
|
|
4075
|
+
readonly log: (line: string) => void;
|
|
4076
|
+
}
|
|
4077
|
+
/**
|
|
4078
|
+
* This is the MCP server of the daemon, as the MCP host gives it.
|
|
4079
|
+
*
|
|
4080
|
+
* @beta
|
|
4081
|
+
*/
|
|
4082
|
+
interface DaemonMcpService {
|
|
4083
|
+
/** This URL is the MCP endpoint. */
|
|
4084
|
+
readonly url: string;
|
|
4085
|
+
/** This method gives the tasks that did not end. */
|
|
4086
|
+
tasks(): readonly DaemonTask[];
|
|
4087
|
+
/** This method stops the server and cancels its tasks. */
|
|
4088
|
+
close(): Promise<void>;
|
|
4089
|
+
}
|
|
4090
|
+
/**
|
|
4091
|
+
* This is the seam between the daemon and its MCP server. The default host (`mcpHttpHost` of the
|
|
4092
|
+
* package polite-crawler-mcp) serves the tools over Streamable HTTP. A different MCP host can
|
|
4093
|
+
* replace it.
|
|
4094
|
+
*
|
|
4095
|
+
* @beta
|
|
4096
|
+
*/
|
|
4097
|
+
interface DaemonMcpHost {
|
|
4098
|
+
start(crawler: OpenedCrawler, options: DaemonMcpOptions, hooks: DaemonMcpHooks): Promise<DaemonMcpService>;
|
|
4099
|
+
}
|
|
4100
|
+
/** These options start a daemon on a store. Only `dir` is necessary. */
|
|
4101
|
+
interface DaemonOptions extends OpenOptions {
|
|
4102
|
+
/** This value is the longest wait between two ticks. The default is `1m`, and the floor is `1s`. */
|
|
4103
|
+
readonly tick?: Duration | undefined;
|
|
4104
|
+
/** This value is the most random time that the daemon adds to each wait. The default is `10s`. */
|
|
4105
|
+
readonly jitter?: Duration | undefined;
|
|
4106
|
+
/** This value is the budget of the checks of one tick. The default is `10m`. */
|
|
4107
|
+
readonly budget?: Duration | undefined;
|
|
4108
|
+
/** This value is the most checks of one tick. The default is 100. */
|
|
4109
|
+
readonly maxPages?: number | undefined;
|
|
4110
|
+
/** At a stop, the checks in progress get this time to end. The default is `10s`. */
|
|
4111
|
+
readonly grace?: Duration | undefined;
|
|
4112
|
+
/**
|
|
4113
|
+
* The daemon writes each change event as one JSON line here: a file, a function, or the standard
|
|
4114
|
+
* output (the default). The value false writes no line. A change is an event with `confirmed: true`.
|
|
4115
|
+
*/
|
|
4116
|
+
readonly events?: string | ((line: string) => void) | false | undefined;
|
|
4117
|
+
/** This function gets each change event. The daemon starts it in order, one at a time, and logs its error. */
|
|
4118
|
+
readonly onChange?: ((event: WatchEvent) => void | Promise<void>) | undefined;
|
|
4119
|
+
/**
|
|
4120
|
+
* These options serve the MCP tools over HTTP. Without them, the daemon serves no MCP.
|
|
4121
|
+
*
|
|
4122
|
+
* @beta
|
|
4123
|
+
*/
|
|
4124
|
+
readonly mcp?: DaemonMcpOptions | undefined;
|
|
4125
|
+
/**
|
|
4126
|
+
* This host serves the MCP tools. The default serves them over Streamable HTTP.
|
|
4127
|
+
*
|
|
4128
|
+
* @beta
|
|
4129
|
+
*/
|
|
4130
|
+
readonly mcpHost?: DaemonMcpHost | undefined;
|
|
4131
|
+
/** The value false installs no handler of `SIGINT` and `SIGTERM`. The default is true. */
|
|
4132
|
+
readonly signals?: boolean | undefined;
|
|
4133
|
+
/** This file keeps the state of the daemon. The default is `<store>/daemon/state.json`. */
|
|
4134
|
+
readonly stateFile?: string | undefined;
|
|
4135
|
+
/** This function writes the messages of the daemon. The default writes them to the standard error. */
|
|
4136
|
+
readonly log?: ((line: string) => void) | undefined;
|
|
4137
|
+
}
|
|
4138
|
+
/** This is a daemon that runs. */
|
|
4139
|
+
interface Daemon {
|
|
4140
|
+
/** This crawler is the crawler of the daemon. The daemon closes it when it stops. */
|
|
4141
|
+
readonly crawler: OpenedCrawler;
|
|
4142
|
+
/** This file keeps the state of the daemon. */
|
|
4143
|
+
readonly stateFile: string;
|
|
4144
|
+
/**
|
|
4145
|
+
* This value is the MCP server, or undefined.
|
|
4146
|
+
*
|
|
4147
|
+
* @beta
|
|
4148
|
+
*/
|
|
4149
|
+
readonly mcp: DaemonMcpService | undefined;
|
|
4150
|
+
/** This method gives the status at this time. */
|
|
4151
|
+
status(): DaemonStatus;
|
|
4152
|
+
/**
|
|
4153
|
+
* This method stops the daemon. The checks in progress get the grace time to end, then they
|
|
4154
|
+
* abort and the run releases their leases. The MCP server stops and cancels its tasks. A second
|
|
4155
|
+
* call with `0` aborts the checks at once.
|
|
4156
|
+
*/
|
|
4157
|
+
stop(grace?: Duration): Promise<DaemonStatus>;
|
|
4158
|
+
/** This promise resolves with the last status when the daemon stopped. */
|
|
4159
|
+
readonly done: Promise<DaemonStatus>;
|
|
4160
|
+
}
|
|
4161
|
+
/**
|
|
4162
|
+
* This function starts a daemon on a store. It opens the crawler, serves MCP when `mcp` is given,
|
|
4163
|
+
* and checks the watch list on a schedule (`scheduleWatches`). It writes each change event as one
|
|
4164
|
+
* JSON line, and it gives each change to `onChange`. The state file tells `daemon status` what runs.
|
|
4165
|
+
*
|
|
4166
|
+
* Only one daemon operates on a store: a second start rejects. At `SIGINT` or `SIGTERM`, the daemon
|
|
4167
|
+
* stops cleanly (`stop`). A second signal aborts the checks at once.
|
|
4168
|
+
*/
|
|
4169
|
+
export declare const startDaemon: (options: DaemonOptions) => Promise<Daemon>;
|
|
4170
|
+
//#endregion
|
|
43
4171
|
//#region src/ollama.d.ts
|
|
44
4172
|
/**
|
|
45
4173
|
* The Ollama embedder sends texts to the local HTTP API of Ollama (`POST /api/embed`). The package
|
|
@@ -86,7 +4214,11 @@ interface OllamaEmbedderOptions {
|
|
|
86
4214
|
}
|
|
87
4215
|
/** The text of the error when no Ollama server answers. */
|
|
88
4216
|
export declare const NO_OLLAMA = "Ollama does not answer: install it from https://ollama.com, then start it with ollama serve";
|
|
89
|
-
/**
|
|
4217
|
+
/**
|
|
4218
|
+
* This function tells if a host name is a loopback address: `localhost`, `127.0.0.0/8` or `::1`.
|
|
4219
|
+
*
|
|
4220
|
+
* @beta
|
|
4221
|
+
*/
|
|
90
4222
|
export declare const isLoopback: (host: string) => boolean;
|
|
91
4223
|
/**
|
|
92
4224
|
* This function makes an embedder that sends texts to Ollama (`POST {baseUrl}/api/embed`). It
|
|
@@ -107,7 +4239,7 @@ export declare const ollamaEmbedder: (options?: OllamaEmbedderOptions) => Embedd
|
|
|
107
4239
|
* (its default is `node_modules/@huggingface/transformers/.cache/`), so the next use reads the disk.
|
|
108
4240
|
*/
|
|
109
4241
|
/** The text of the error when the optional package `@huggingface/transformers` is not installed. */
|
|
110
|
-
export declare const NO_TRANSFORMERS = "the transformers embedder needs the package @huggingface/transformers: npm install @huggingface/transformers. For the MCP server with npx, start it with npx -y -p polite-crawler -p @huggingface/transformers polite-crawler
|
|
4242
|
+
export declare const NO_TRANSFORMERS = "the transformers embedder needs the package @huggingface/transformers: npm install @huggingface/transformers. For the MCP server with npx, start it with npx -y -p polite-crawler-mcp -p @huggingface/transformers polite-crawler-mcp --embedder transformers";
|
|
111
4243
|
/** The pooling of the vectors of the tokens: their mean, or the vector of the first token (`cls`). */
|
|
112
4244
|
type Pooling = "mean" | "cls";
|
|
113
4245
|
/** These are the facts of the models that the embedder knows. Each one is on the Hugging Face Hub with ONNX files. */
|
|
@@ -137,7 +4269,11 @@ interface TransformersEmbedderOptions {
|
|
|
137
4269
|
/** This value is the number of texts that the model reads at one time. The default is 8. */
|
|
138
4270
|
readonly batch?: number | undefined;
|
|
139
4271
|
}
|
|
140
|
-
/**
|
|
4272
|
+
/**
|
|
4273
|
+
* This is the part of a feature-extraction pipeline of transformers.js that the embedder uses.
|
|
4274
|
+
*
|
|
4275
|
+
* @beta
|
|
4276
|
+
*/
|
|
141
4277
|
interface FeatureExtractor {
|
|
142
4278
|
(texts: string[], options: {
|
|
143
4279
|
readonly pooling: Pooling;
|
|
@@ -148,7 +4284,11 @@ interface FeatureExtractor {
|
|
|
148
4284
|
}>;
|
|
149
4285
|
dispose?(): Promise<unknown>;
|
|
150
4286
|
}
|
|
151
|
-
/**
|
|
4287
|
+
/**
|
|
4288
|
+
* This is the part of the module `@huggingface/transformers` that the embedder uses.
|
|
4289
|
+
*
|
|
4290
|
+
* @beta
|
|
4291
|
+
*/
|
|
152
4292
|
interface TransformersModule {
|
|
153
4293
|
pipeline(task: "feature-extraction", model: string, options: {
|
|
154
4294
|
readonly dtype: "q8" | "fp32";
|
|
@@ -166,7 +4306,11 @@ interface TransformersModule {
|
|
|
166
4306
|
export declare const transformersEmbedder: (options?: TransformersEmbedderOptions, load?: () => Promise<TransformersModule>) => Embedder;
|
|
167
4307
|
//#endregion
|
|
168
4308
|
//#region src/hoststate.d.ts
|
|
169
|
-
/**
|
|
4309
|
+
/**
|
|
4310
|
+
* These options change a SQLite host state store.
|
|
4311
|
+
*
|
|
4312
|
+
* @beta
|
|
4313
|
+
*/
|
|
170
4314
|
interface SqliteHostStateOptions {
|
|
171
4315
|
/** This value is the time in milliseconds that a step waits for the lock of a different process. The default is 10 seconds. */
|
|
172
4316
|
readonly busyTimeoutMs?: number | undefined;
|
|
@@ -188,6 +4332,8 @@ interface SqliteHostStateOptions {
|
|
|
188
4332
|
*
|
|
189
4333
|
* The state is not a projection of the capture log. A deletion of the file loses only the pace,
|
|
190
4334
|
* the counts, the stops and the memos, and the crawler starts them again.
|
|
4335
|
+
*
|
|
4336
|
+
* @beta
|
|
191
4337
|
*/
|
|
192
4338
|
export declare class SqliteHostStateStore implements HostStateStore {
|
|
193
4339
|
readonly path: string;
|
|
@@ -237,6 +4383,8 @@ export declare class SqliteHostStateStore implements HostStateStore {
|
|
|
237
4383
|
* - Reads one page at a time, and lets other work run between the pages.
|
|
238
4384
|
* - Stops at a page limit, a text limit and a time limit, and says so (`truncated`). The time limit
|
|
239
4385
|
* stops a page in the middle too, and the text limit cuts the text of a page.
|
|
4386
|
+
*
|
|
4387
|
+
* @beta
|
|
240
4388
|
*/
|
|
241
4389
|
interface PdfText {
|
|
242
4390
|
/** This is the title in the file, or null when the file names no real title ("Microsoft Word - menu.docx"). */
|
|
@@ -248,7 +4396,11 @@ interface PdfText {
|
|
|
248
4396
|
/** This note tells why the reader stopped or refused the file, or it is null. */
|
|
249
4397
|
readonly note: string | null;
|
|
250
4398
|
}
|
|
251
|
-
/**
|
|
4399
|
+
/**
|
|
4400
|
+
* These are the limits of one read.
|
|
4401
|
+
*
|
|
4402
|
+
* @beta
|
|
4403
|
+
*/
|
|
252
4404
|
interface PdfOptions {
|
|
253
4405
|
/** The reader reads no more pages than this. The default is 500. */
|
|
254
4406
|
readonly maxPages?: number | undefined;
|
|
@@ -259,12 +4411,21 @@ interface PdfOptions {
|
|
|
259
4411
|
/** The reader refuses a file whose streams inflate to more bytes than this. The default is 8 MB. */
|
|
260
4412
|
readonly maxInflatedBytes?: number | undefined;
|
|
261
4413
|
}
|
|
262
|
-
/**
|
|
4414
|
+
/**
|
|
4415
|
+
* This function finds a PDF from its first bytes or its content type. Some servers put bytes before `%PDF-`.
|
|
4416
|
+
*
|
|
4417
|
+
* @beta
|
|
4418
|
+
*/
|
|
263
4419
|
export declare const isPdf: (body: Uint8Array, contentType: string | null) => boolean;
|
|
4420
|
+
/** @beta */
|
|
264
4421
|
export declare const readPdf: (bytes: Uint8Array, options?: PdfOptions) => Promise<PdfText>;
|
|
265
4422
|
//#endregion
|
|
266
4423
|
//#region src/recipe-venue.d.ts
|
|
267
|
-
/**
|
|
4424
|
+
/**
|
|
4425
|
+
* These are the limits of the venue recipe.
|
|
4426
|
+
*
|
|
4427
|
+
* @beta
|
|
4428
|
+
*/
|
|
268
4429
|
export declare const VENUE_LIMITS: {
|
|
269
4430
|
/** The pages of the site that a read fetches after the home page, by default. */
|
|
270
4431
|
readonly pages: number;
|
|
@@ -283,6 +4444,8 @@ export declare const VENUE_LIMITS: {
|
|
|
283
4444
|
* This function tells if a page names a venue: the page has the first word of the name and 60
|
|
284
4445
|
* percent of its words. A domain that a venue let go can show a different business. It gives
|
|
285
4446
|
* undefined for a name with no word.
|
|
4447
|
+
*
|
|
4448
|
+
* @beta
|
|
286
4449
|
*/
|
|
287
4450
|
export declare const namesVenue: (name: string, page: {
|
|
288
4451
|
readonly title: string;
|
|
@@ -292,6 +4455,8 @@ export declare const namesVenue: (name: string, page: {
|
|
|
292
4455
|
* This function ranks the links of a page by the words of their text and path. The links to
|
|
293
4456
|
* the hours or a menu come first. It keeps only the links on the site of `home`,
|
|
294
4457
|
* and it leaves out the files (a PDF, an image) and the home page itself.
|
|
4458
|
+
*
|
|
4459
|
+
* @beta
|
|
295
4460
|
*/
|
|
296
4461
|
export declare const rankHoursLinks: (links: readonly PageLink[], home: string) => PageLink[];
|
|
297
4462
|
/** A link to a menu. */
|
|
@@ -309,6 +4474,8 @@ interface VenueLocation {
|
|
|
309
4474
|
/**
|
|
310
4475
|
* This function tells if a JSON-LD location record is the venue. The record must have the same
|
|
311
4476
|
* number on a street that shares a word, or a position near the venue (`VENUE_LIMITS.nearMetres`).
|
|
4477
|
+
*
|
|
4478
|
+
* @beta
|
|
312
4479
|
*/
|
|
313
4480
|
export declare const locationMatches: (record: Readonly<Record<string, unknown>>, location: VenueLocation) => boolean;
|
|
314
4481
|
/** One reading of the hours, with the source that it came from. */
|
|
@@ -363,7 +4530,11 @@ interface VenueRead {
|
|
|
363
4530
|
readonly phone: string | null;
|
|
364
4531
|
readonly menus: readonly VenueLink[];
|
|
365
4532
|
readonly notes: readonly string[];
|
|
366
|
-
/**
|
|
4533
|
+
/**
|
|
4534
|
+
* This value counts the work of the reader. The tests use it, because it does not change with the computer.
|
|
4535
|
+
*
|
|
4536
|
+
* @beta
|
|
4537
|
+
*/
|
|
367
4538
|
readonly steps: number;
|
|
368
4539
|
}
|
|
369
4540
|
/**
|
|
@@ -473,7 +4644,11 @@ export declare const readVenue: (target: RecipeTarget, url: string, options?: Ve
|
|
|
473
4644
|
//#endregion
|
|
474
4645
|
//#region src/recipe-product.d.ts
|
|
475
4646
|
type Json = Readonly<Record<string, unknown>>;
|
|
476
|
-
/**
|
|
4647
|
+
/**
|
|
4648
|
+
* These are the limits of the product reader. A page with more is hostile or broken, and the reader stops there.
|
|
4649
|
+
*
|
|
4650
|
+
* @beta
|
|
4651
|
+
*/
|
|
477
4652
|
export declare const PRODUCT_LIMITS: {
|
|
478
4653
|
/** The most JSON-LD records that the reader examines. */
|
|
479
4654
|
readonly records: number;
|
|
@@ -525,7 +4700,11 @@ interface ProductRead {
|
|
|
525
4700
|
readonly seller: string | null;
|
|
526
4701
|
readonly image: string | null;
|
|
527
4702
|
readonly notes: readonly string[];
|
|
528
|
-
/**
|
|
4703
|
+
/**
|
|
4704
|
+
* This value counts the work of the reader. The tests use it, because it does not change with the computer.
|
|
4705
|
+
*
|
|
4706
|
+
* @beta
|
|
4707
|
+
*/
|
|
529
4708
|
readonly steps: number;
|
|
530
4709
|
}
|
|
531
4710
|
/**
|
|
@@ -534,6 +4713,8 @@ interface ProductRead {
|
|
|
534
4713
|
* separator, each followed by three digits, separates thousands: `1.299` is 1299. For a
|
|
535
4714
|
* currency with three decimal digits, one point with three digits is the decimal point. A text
|
|
536
4715
|
* with two numbers (a range), a negative amount or zero gives undefined.
|
|
4716
|
+
*
|
|
4717
|
+
* @beta
|
|
537
4718
|
*/
|
|
538
4719
|
export declare const readAmount: (value: unknown, currency?: string) => number | undefined;
|
|
539
4720
|
/**
|
|
@@ -545,12 +4726,18 @@ export declare const readAmount: (value: unknown, currency?: string) => number |
|
|
|
545
4726
|
* 3. Else the largest product.
|
|
546
4727
|
*
|
|
547
4728
|
* It also reads the `mainEntity` of a record (a `WebPage` or an `ItemPage` that names its product).
|
|
4729
|
+
*
|
|
4730
|
+
* @beta
|
|
548
4731
|
*/
|
|
549
4732
|
export declare const pickProduct: (records: readonly Json[], page: string) => {
|
|
550
4733
|
readonly product: Json | undefined;
|
|
551
4734
|
readonly steps: number;
|
|
552
4735
|
};
|
|
553
|
-
/**
|
|
4736
|
+
/**
|
|
4737
|
+
* This function gives the variant that a URL names: the `variant` parameter of Shopify, or a `sku` or `gtin` parameter.
|
|
4738
|
+
*
|
|
4739
|
+
* @beta
|
|
4740
|
+
*/
|
|
554
4741
|
export declare const pickVariant: (product: Json, page: string) => Json | undefined;
|
|
555
4742
|
/**
|
|
556
4743
|
* This function reads the Product data of a page from its JSON-LD records. It is pure, and it sends
|
|
@@ -707,5 +4894,5 @@ interface WaczResult extends ArchiveCounts {
|
|
|
707
4894
|
*/
|
|
708
4895
|
export declare const exportWacz: (source: ArchiveSource, options: ArchiveOptions) => Promise<WaczResult>;
|
|
709
4896
|
//#endregion
|
|
710
|
-
export { ALLOW_ALL, type AiInput, type ArchiveCounts, type ArchiveFilter, type ArchiveOptions, type ArchiveSource, type Archived, type BlobStore,
|
|
4897
|
+
export { ALLOW_ALL, type AiInput, type ArchiveCounts, type ArchiveFilter, type ArchiveOptions, type ArchiveSource, type Archived, type BlobStore, type CacheEntry, type CacheIndex, type CaptureFilter, type CaptureLogOptions, type CapturePayload, type CaptureReader, type CaptureRecord, type CaptureWriter, type CatchUpResult, type ChangeCommandOptions, type Changefreq, type ChunkOptions, type Clock, type CrawlEvent, type CrawlOptions, type CrawlPage, type CrawlRun, type CrawlRunOptions, type CrawlRuntime, type CrawlStop, type CrawlSummary, type CrawlerOptions, DAYS, DEFAULT_POLICY, type Daemon, type DaemonDue, type DaemonMcpHooks, type DaemonMcpHost, type DaemonMcpOptions, type DaemonMcpService, type DaemonOptions, type DaemonState, type DaemonStatus, type DaemonTask, type DaemonTick, type DamagedLine, type Day, type DiffHunk, type DiffInput, type DiffLimits, type DiffLine, type DiffOptions, type DiffResult, type DiffSide, type DiffSummary, type DigestOptions, type DirectoryKey, type DiscoverOptions, type DiscoverResult, type DiscoverRunOptions, type DiscoverSource, type DiscoverStop, type DiscoveredUrl, type Doc, type DocDigest, type DocIndexOptions, type DocInput, type DocVersion, type Duration, type EgressOptions, type EmbedCallOptions, type EmbedOptions, type EmbedResult, type EmbedStop, type Embedder, type EmbeddingModel, type ExtractOptions, type ExtractResult, type ExtractSource, type ExtractSummary, type ExtractedDoc, type Extractor, type FeatureExtractor, type FetchOptions, type FetchOutcome, type FetchRequest, type Fetched, type FieldCandidate, type FieldCheck, type FieldClaim, type FieldKind, type FieldResult, type FieldSpan, type FieldStatus, type FieldsChecked, type FileBlobStoreOptions, type FindIndexOptions, type FindOptions, type Found, type FusedRank, type HostCounts, type HostResult, type HostStateStore, type HostStop, type Hours, type HoursComparison, type HybridCrawler, type HybridFindOptions, type HybridOptions, type HybridSearch, type HybridSearchOptions, type Indexed, type Interval, type KeyDirectory, type KeyInput, type Latest, MemoryHostStateStore, type MetaChange, type Money, type OllamaEmbedderOptions, type OpenOptions, type OpenedCrawler, type Outcome, type PageExtract, type PageLink, type PageOptions, type PageResult, type Passage, type PdfOptions, type PdfText, type PlaywrightRenderer, type PolicyOptions, type PoliteCrawler, type PolitenessPolicy, type Pooling, type ProductOptions, type ProductRead, type ProductResult, type ProductVariant, type RankMode, type RankOptions, type Ranked, type ReadOptions, type ReadResult, type ReadingIndex, type Recipe, type RecipeCapture, type RecipeDefinition, type RecipeDoc, type RecipeExtract, type RecipeRequest, type RecipeRequestOptions, type RecipeResult, type RecipeRunOptions, type RecipeTarget, type RecordInput, type RefusedReason, type ReindexResult, type RenderOptions, type RenderRequest, type Rendered, type Renderer, type RequestSigner, type Robots, type RobotsDecision, type RobotsGroup, type RobotsRule, type RobotsSignal, type RuntimeOptions, type ScanOptions, type ScheduleOptions, type ScheduleResult, type ScopeOptions, type SearchOptions, type SearchRank, type Signals, type SignatureAgentType, type SignatureCheck, type SignedRequest, type SitemapEntry, type SitemapKind, type SitemapLimits, type SitemapRead, type SqliteHostStateOptions, type StoreRuntime, type StoredChunk, type TextChunk, type TextDiff, type TransformersEmbedderOptions, type TransformersModule, type UrlPattern, VALIDATOR_MEMO, type ValidatorAnswer, type ValidatorLimits, type ValidatorMemo, type ValidatorReport, type ValidatorVerdict, type Variant, type VectorCounts, type VectorHit, type VectorStoreOptions, type VectorWrite, type VenueLink, type VenueLocation, type VenueOptions, type VenueOsm, type VenuePage, type VenueRead, type VenueReading, type VenueResult, type VenueSource, type Verified, type VerifyOptions, type VerifyResult, type VersionAt, type VersionChoice, type VersionText, type WaczResult, type WarcResult, type WatchEntry, type WatchEvent, type WatchKind, type WatchList, type WatchOptions, type WatchPending, type WatchRun, type WatchRunOptions, type WatchRunnerOptions, type WatchSchedule, type WatchStop, type WatchStoreOptions, type WatchSummary, type WebArchive, type WebBotAuthOptions, type WebBotAuthSigner, type Week, type WordPart, bodyEncoding, compareHours, decide, decodeBody, describeWeek, disagreements, extractPage, fromCompact, fromSchemaOrg, fromText, nextValidatorMemo, parseRobots, realClock, toHoursWeek, typesOf };
|
|
711
4898
|
//# sourceMappingURL=index.d.mts.map
|