@opencraw/core 0.1.1 → 0.1.2
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 +4 -2
- package/dist/index.esm.js +2313 -632
- package/dist/src/api-steps/extract-from-document.use-case.d.ts +16 -3
- package/dist/src/api-steps/index.d.ts +1 -1
- package/dist/src/api-steps/send-request.use-case.d.ts +2 -2
- package/dist/src/captcha/captcha-budget.model.d.ts +21 -0
- package/dist/src/captcha/captcha-detection.client.d.ts +28 -0
- package/dist/src/captcha/captcha-guard.use-case.d.ts +64 -0
- package/dist/src/captcha/captcha-solver-registry.store.d.ts +19 -0
- package/dist/src/captcha/captcha-solver.contract.d.ts +47 -0
- package/dist/src/captcha/captcha.error.d.ts +13 -0
- package/dist/src/captcha/index.d.ts +10 -0
- package/dist/src/captcha/resolve-captcha.use-case.d.ts +40 -0
- package/dist/src/crawl-events/crawl-event.contract.d.ts +40 -0
- package/dist/src/crawl-execution/bootstrap-session.use-case.d.ts +5 -2
- package/dist/src/crawl-execution/crawl-options.config.d.ts +3 -0
- package/dist/src/crawl-execution/crawl-report.model.d.ts +6 -0
- package/dist/src/crawl-execution/create-crawler.use-case.d.ts +2 -1
- package/dist/src/crawl-execution/run-input-recipe.use-case.d.ts +3 -0
- package/dist/src/deck-document/deck-document.model.d.ts +58 -0
- package/dist/src/deck-document/deck-table.algorithm.d.ts +35 -0
- package/dist/src/deck-document/index.d.ts +6 -0
- package/dist/src/deck-document/read-pptx.client.d.ts +16 -0
- package/dist/src/extraction-scope/extraction-scope.model.d.ts +3 -1
- package/dist/src/http-session/http-response.contract.d.ts +13 -1
- package/dist/src/http-session/text-decoding.algorithm.d.ts +35 -0
- package/dist/src/index.d.ts +9 -1
- package/dist/src/markdown-document/index.d.ts +3 -0
- package/dist/src/markdown-document/read-markdown.client.d.ts +29 -0
- package/dist/src/pdf-document/index.d.ts +1 -1
- package/dist/src/pdf-document/row-assembly.algorithm.d.ts +10 -1
- package/dist/src/recipe-loading/recipe-binding.validator.d.ts +3 -1
- package/dist/src/recipe-schema/index.d.ts +2 -2
- package/dist/src/recipe-schema/input-recipe.contract.d.ts +33 -3
- package/dist/src/recipe-schema/recipe-kind.enum.d.ts +3 -2
- package/dist/src/recipe-schema/step.contract.d.ts +46 -2
- package/dist/src/selection/index.d.ts +1 -1
- package/dist/src/selection/json-text.algorithm.d.ts +30 -3
- package/dist/src/web-steps/run-web-step.use-case.d.ts +10 -2
- package/dist/src/workbook-document/csv-parser.algorithm.d.ts +26 -0
- package/dist/src/workbook-document/csv-workbook.mapper.d.ts +24 -0
- package/dist/src/workbook-document/grid-table.algorithm.d.ts +53 -0
- package/dist/src/workbook-document/html-tables.mapper.d.ts +14 -0
- package/dist/src/workbook-document/index.d.ts +9 -0
- package/dist/src/workbook-document/read-xlsx.client.d.ts +18 -0
- package/dist/src/workbook-document/workbook-document.model.d.ts +51 -0
- package/dist/src/yaml-document/index.d.ts +3 -0
- package/dist/src/yaml-document/read-yaml.client.d.ts +25 -0
- package/package.json +16 -2
package/dist/src/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type { Crawler, CrawlOptions, CrawlReport, RecipeReport } from './crawl-e
|
|
|
5
5
|
export { loadRecipeSet, loadRecipes, readRecipeSource, bindRecipeSet, RecipeSet, RecipeBindingError, validateBinding } from './recipe-loading/index.js';
|
|
6
6
|
export type { RecipeSetSource, RecipeSource, RecipeBytes, RecipeDocument, BindingIssue } from './recipe-loading/index.js';
|
|
7
7
|
export { parseInputRecipe, parseOutputRecipe, RecipeValidationError, inputRecipeJsonSchema, outputRecipeJsonSchema, inputRecipeSchema, outputRecipeSchema } from './recipe-schema/index.js';
|
|
8
|
-
export type { InputRecipe, OutputRecipe, FieldSpec, Step, StepType, MappingRule, TransformRule, ErrorPolicy, PaginateNext, SessionSpec, SessionAccess, RecipeIssue } from './recipe-schema/index.js';
|
|
8
|
+
export type { InputRecipe, OutputRecipe, FieldSpec, Step, StepType, MappingRule, TransformRule, ErrorPolicy, PaginateNext, SessionSpec, SessionAccess, CaptchaSettings, CaptchaStep, RecipeIssue } from './recipe-schema/index.js';
|
|
9
9
|
export type { Hook, HookMap, HookContext } from './hooks/index.js';
|
|
10
10
|
export { UnknownHookError } from './hooks/index.js';
|
|
11
11
|
export type { OutputRecord } from './output-mapping/index.js';
|
|
@@ -21,6 +21,14 @@ export type { HttpClientOptions, HttpRequest, HttpResponse, HttpBody } from './h
|
|
|
21
21
|
export { tryParseJson } from './selection/index.js';
|
|
22
22
|
export { readPdf, PdfReadError, findTables, pdfText } from './pdf-document/index.js';
|
|
23
23
|
export type { PdfDocument, PdfPage, PdfRow, PdfCell, PdfTable, TableQuery, TableAlign } from './pdf-document/index.js';
|
|
24
|
+
export { parseCsv, detectDelimiter, csvWorkbook, findGridTables, fillDown, workbookText, isWorkbookDocument, htmlTableSheets } from './workbook-document/index.js';
|
|
25
|
+
export { readMarkdown } from './markdown-document/index.js';
|
|
26
|
+
export { readYaml } from './yaml-document/index.js';
|
|
27
|
+
export { findDeckTables, deckText, isDeckDocument } from './deck-document/index.js';
|
|
28
|
+
export type { DeckDocument, DeckSlide, DeckShape, DeckChart, DeckTable, DeckTableQuery } from './deck-document/index.js';
|
|
29
|
+
export type { WorkbookDocument, WorkbookCell, Sheet, CsvFormat, GridTable, GridTableQuery } from './workbook-document/index.js';
|
|
24
30
|
export { StepFailure } from './step-flow/index.js';
|
|
25
31
|
export { TransformError } from './transformation/index.js';
|
|
32
|
+
export { CaptchaError, DEFAULT_CAPTCHA_SELECTOR, detectChallenge } from './captcha/index.js';
|
|
33
|
+
export type { CaptchaSolver, CaptchaChallenge, CaptchaContext, CaptchaOutcome, CaptchaKind, CaptchaLog } from './captcha/index.js';
|
|
26
34
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Markdown, rendered. */
|
|
2
|
+
export interface MarkdownRead {
|
|
3
|
+
/** A full HTML document: front matter in the head, the rendered body wrapped in sections. */
|
|
4
|
+
html: string;
|
|
5
|
+
/** The front matter's data, when there is some. */
|
|
6
|
+
frontMatter: unknown;
|
|
7
|
+
warnings: string[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Renders Markdown (GitHub-flavoured: tables, task lists, strikethrough,
|
|
11
|
+
* autolinks) to HTML with `marked`, imported on first use, so every `css`
|
|
12
|
+
* selector works on it:
|
|
13
|
+
*
|
|
14
|
+
* - each heading and everything up to the next heading of the same or a higher
|
|
15
|
+
* level is wrapped in `<section data-heading="…" data-level="…">`, sections
|
|
16
|
+
* nesting, so "the table under *Prezzi*" is one selector;
|
|
17
|
+
* - headings get slug ids (`<h2 id="prezzi">`);
|
|
18
|
+
* - a leading `---` YAML block is parsed (YAML 1.2, as `as: "yaml"` reads it)
|
|
19
|
+
* and put in the head as `<script type="application/json" data-front-matter>`.
|
|
20
|
+
*
|
|
21
|
+
* Raw HTML in the Markdown is kept: it is data, parsed by cheerio, never run.
|
|
22
|
+
*
|
|
23
|
+
* @param text - The Markdown.
|
|
24
|
+
* @param source - Where it came from, for messages.
|
|
25
|
+
* @returns The HTML, the front matter's data and the YAML parser's warnings.
|
|
26
|
+
* @throws Error naming the source when the front matter is not YAML.
|
|
27
|
+
*/
|
|
28
|
+
export declare function readMarkdown(text: string, source: string): Promise<MarkdownRead>;
|
|
29
|
+
//# sourceMappingURL=read-markdown.client.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { readPdf, PdfReadError } from './read-pdf.client.js';
|
|
2
2
|
export { pdfText, isPdfDocument } from './pdf-document.model.js';
|
|
3
3
|
export type { PdfDocument, PdfPage, PdfRow, PdfCell, PositionedText } from './pdf-document.model.js';
|
|
4
|
-
export { assembleRows } from './row-assembly.algorithm.js';
|
|
4
|
+
export { assembleRows, rowsOfCells } from './row-assembly.algorithm.js';
|
|
5
5
|
export { findTables } from './pdf-table.algorithm.js';
|
|
6
6
|
export type { PdfTable, TableQuery, TableAlign } from './pdf-table.algorithm.js';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PdfRow, PositionedText } from './pdf-document.model.js';
|
|
1
|
+
import type { PdfCell, PdfRow, PositionedText } from './pdf-document.model.js';
|
|
2
2
|
/**
|
|
3
3
|
* Turns a page's text runs into rows of cells, top to bottom.
|
|
4
4
|
*
|
|
@@ -12,6 +12,15 @@ import type { PdfRow, PositionedText } from './pdf-document.model.js';
|
|
|
12
12
|
* @returns The rows.
|
|
13
13
|
*/
|
|
14
14
|
export declare function assembleRows(runs: readonly PositionedText[]): PdfRow[];
|
|
15
|
+
/**
|
|
16
|
+
* Groups finished cells into rows, top to bottom: cells whose vertical extents
|
|
17
|
+
* overlap share a row. For cells that need no joining, such as a slide's text
|
|
18
|
+
* boxes, each already a cell.
|
|
19
|
+
*
|
|
20
|
+
* @param cells - The cells, in any order.
|
|
21
|
+
* @returns The rows.
|
|
22
|
+
*/
|
|
23
|
+
export declare function rowsOfCells(cells: readonly PdfCell[]): PdfRow[];
|
|
15
24
|
/** `Array#findLast`, which the es2022 library does not declare. */
|
|
16
25
|
export declare function lastOf<T>(items: readonly T[], test: (item: T) => boolean): T | undefined;
|
|
17
26
|
//# sourceMappingURL=row-assembly.algorithm.d.ts.map
|
|
@@ -14,7 +14,9 @@ import type { BindingIssue } from './recipe-binding.error.js';
|
|
|
14
14
|
* - web-only steps appear only in web recipes or inside a bootstrap, api-only
|
|
15
15
|
* steps only in api recipes, and `next.selector` only in web mode;
|
|
16
16
|
* - exactly one emitting construct exists on any path (the two branches of an
|
|
17
|
-
* `if` are separate paths)
|
|
17
|
+
* `if` are separate paths);
|
|
18
|
+
* - a `captcha` step, and `onBlock.solve`, have a solver: their own or
|
|
19
|
+
* `session.captcha.solver`.
|
|
18
20
|
*
|
|
19
21
|
* @param input - A parsed input recipe.
|
|
20
22
|
* @param output - The parsed output recipe it names.
|
|
@@ -2,11 +2,11 @@ export * from './recipe-kind.enum.js';
|
|
|
2
2
|
export { fieldSpecSchema, outputRecipeSchema } from './output-recipe.contract.js';
|
|
3
3
|
export type { FieldSpec, OutputRecipe } from './output-recipe.contract.js';
|
|
4
4
|
export { stepSchema, errorPolicySchema, paginateNextSchema } from './step.contract.js';
|
|
5
|
-
export type { Step, StepType, StepBaseFields, TargetFields, ErrorPolicy, PaginateNext, TakeKind, GotoStep, ClickStep, FillStep, PressStep, SelectStep, ScrollStep, WaitStep, EvaluateStep, ScreenshotStep, RequestStep, ExtractStep, SetStep, CollectStep, ForEachStep, IfStep, PaginateStep, EmitStep, HookStep, } from './step.contract.js';
|
|
5
|
+
export type { Step, StepType, StepBaseFields, TargetFields, ErrorPolicy, PaginateNext, TakeKind, GotoStep, ClickStep, FillStep, PressStep, SelectStep, ScrollStep, WaitStep, EvaluateStep, ScreenshotStep, RequestStep, ExtractStep, SetStep, CollectStep, ForEachStep, IfStep, PaginateStep, EmitStep, HookStep, CaptchaStep, CaptchaCheck, } from './step.contract.js';
|
|
6
6
|
export { transformRuleSchema, mappingRuleSchema } from './transform-rule.contract.js';
|
|
7
7
|
export type { TransformRule, TransformOp, MappingRule, FromRule, EachRule } from './transform-rule.contract.js';
|
|
8
8
|
export { inputRecipeSchema, sessionSpecSchema, startPointSchema } from './input-recipe.contract.js';
|
|
9
|
-
export type { InputRecipe, SessionSpec, SessionBootstrap, SessionAccess, BlockRule, BlockRotation, StartPoint, CrawlLimits, RecipeCookie } from './input-recipe.contract.js';
|
|
9
|
+
export type { InputRecipe, SessionSpec, SessionBootstrap, SessionAccess, BlockRule, BlockRotation, CaptchaSettings, StartPoint, CrawlLimits, RecipeCookie } from './input-recipe.contract.js';
|
|
10
10
|
export { parseInputRecipe, parseOutputRecipe, recipeKindOf } from './recipe.validator.js';
|
|
11
11
|
export { RecipeValidationError } from './recipe-validation.error.js';
|
|
12
12
|
export type { RecipeIssue } from './recipe-validation.error.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { CrawlMode, KeepKind } from './recipe-kind.enum.js';
|
|
3
|
-
import type { ErrorPolicy, Step } from './step.contract.js';
|
|
3
|
+
import type { CaptchaCheck, ErrorPolicy, Step } from './step.contract.js';
|
|
4
4
|
import type { MappingRule } from './transform-rule.contract.js';
|
|
5
5
|
/** A URL the crawl starts from, with variables visible to its templates as `vars.*`. */
|
|
6
6
|
export interface StartPoint {
|
|
@@ -49,12 +49,41 @@ export interface BlockRule {
|
|
|
49
49
|
/** A regular expression the response body must match (case-insensitive). */
|
|
50
50
|
text?: string;
|
|
51
51
|
}
|
|
52
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* What to do when blocked. `solve`: when the block page shows a captcha, the
|
|
54
|
+
* `session.captcha` solver solves it on the spot. `rotate`: take a new access
|
|
55
|
+
* lease (a new IP), reopen the session, and retry the step; with `solve`, only
|
|
56
|
+
* once solving failed.
|
|
57
|
+
*/
|
|
53
58
|
export interface BlockRotation {
|
|
54
|
-
rotate
|
|
59
|
+
rotate?: boolean;
|
|
60
|
+
solve?: boolean;
|
|
55
61
|
/** How many rotations a recipe run may use. Default 2. */
|
|
56
62
|
attempts?: number;
|
|
57
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* How a web recipe gets past captchas: a solver the runner registered (a
|
|
66
|
+
* plugin's `captchaSolvers`), where to look for challenges, how to confirm
|
|
67
|
+
* one is solved, and what the run may spend. With it, the engine checks for a
|
|
68
|
+
* challenge after each navigation, click and key press, and solves it before
|
|
69
|
+
* the next step runs.
|
|
70
|
+
*/
|
|
71
|
+
export interface CaptchaSettings {
|
|
72
|
+
/** The name of a registered captcha solver. */
|
|
73
|
+
solver: string;
|
|
74
|
+
/** Where challenges are (a Playwright selector); the common widgets when omitted. */
|
|
75
|
+
detect?: {
|
|
76
|
+
selector: string;
|
|
77
|
+
};
|
|
78
|
+
/** How a solve is confirmed. Default: the challenge is gone. */
|
|
79
|
+
verify?: CaptchaCheck;
|
|
80
|
+
/** Solves tried per challenge before it counts as a block. Default 3. */
|
|
81
|
+
attempts?: number;
|
|
82
|
+
/** How long one solve may take. Default 120000. */
|
|
83
|
+
timeoutMs?: number;
|
|
84
|
+
/** Solves the whole run may spend (a paid solver bills each). Default 10; 0 detects without solving. */
|
|
85
|
+
maxSolves?: number;
|
|
86
|
+
}
|
|
58
87
|
export interface SessionSpec {
|
|
59
88
|
headers?: Record<string, string>;
|
|
60
89
|
cookies?: RecipeCookie[];
|
|
@@ -69,6 +98,7 @@ export interface SessionSpec {
|
|
|
69
98
|
access?: SessionAccess;
|
|
70
99
|
blockedWhen?: BlockRule;
|
|
71
100
|
onBlock?: BlockRotation;
|
|
101
|
+
captcha?: CaptchaSettings;
|
|
72
102
|
}
|
|
73
103
|
export interface CrawlLimits {
|
|
74
104
|
maxRecords?: number;
|
|
@@ -4,7 +4,8 @@ export declare const CRAWL_MODES: readonly ["web", "api"];
|
|
|
4
4
|
export declare const SELECTOR_KINDS: readonly ["css", "xpath", "jsonpath", "regex", "table"];
|
|
5
5
|
/** `take` also accepts `attr:<name>`, which is validated by pattern rather than listed. */
|
|
6
6
|
export declare const TAKE_KINDS: readonly ["text", "html", "value", "json"];
|
|
7
|
-
export declare const BODY_KINDS: readonly ["json", "html", "text", "pdf"];
|
|
7
|
+
export declare const BODY_KINDS: readonly ["json", "jsonl", "html", "text", "pdf", "csv", "xlsx", "pptx", "yaml", "markdown"];
|
|
8
|
+
export declare const YAML_SCALARS: readonly ["typed", "text"];
|
|
8
9
|
/** How a PDF table aligns a row's values against a cell wrapped over several lines. */
|
|
9
10
|
export declare const TABLE_ALIGNS: readonly ["auto", "top", "center", "bottom"];
|
|
10
11
|
export declare const FIELD_TYPES: readonly ["string", "number", "integer", "boolean", "date", "datetime", "currency", "url", "enum", "array", "object", "json"];
|
|
@@ -16,7 +17,7 @@ export declare const KEEP_KINDS: readonly ["cookies", "localStorage"];
|
|
|
16
17
|
export declare const WAIT_UNTIL: readonly ["load", "domcontentloaded", "networkidle", "commit"];
|
|
17
18
|
export declare const HTTP_METHODS: readonly ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"];
|
|
18
19
|
/** Steps that only make sense with a live browser page. */
|
|
19
|
-
export declare const WEB_ONLY_STEPS: readonly ["goto", "click", "fill", "press", "select", "scroll", "wait", "evaluate", "screenshot"];
|
|
20
|
+
export declare const WEB_ONLY_STEPS: readonly ["goto", "click", "fill", "press", "select", "scroll", "wait", "evaluate", "screenshot", "captcha"];
|
|
20
21
|
/** Steps that only make sense against an HTTP request context. */
|
|
21
22
|
export declare const API_ONLY_STEPS: readonly ["request"];
|
|
22
23
|
export type RecipeKind = typeof RECIPE_KINDS[number];
|
|
@@ -86,6 +86,12 @@ export interface RequestStep extends StepBaseFields {
|
|
|
86
86
|
headers?: Record<string, string>;
|
|
87
87
|
body?: unknown;
|
|
88
88
|
as?: BodyKind;
|
|
89
|
+
/** The body's text encoding (a WHATWG label, `windows-1252`); default: the BOM, the declared charset, UTF-8, else Windows-1252. */
|
|
90
|
+
encoding?: string;
|
|
91
|
+
/** A CSV body's delimiter (one character); default: detected among `,` `;` tab `|`. */
|
|
92
|
+
delimiter?: string;
|
|
93
|
+
/** A YAML body's scalars: `typed` (default, YAML 1.2) or `text`, every scalar as written (`0123` stays `"0123"`). */
|
|
94
|
+
scalars?: 'typed' | 'text';
|
|
89
95
|
}
|
|
90
96
|
export interface ExtractStep extends StepBaseFields {
|
|
91
97
|
type: 'extract';
|
|
@@ -99,8 +105,20 @@ export interface ExtractStep extends StepBaseFields {
|
|
|
99
105
|
columns?: Record<string, string>;
|
|
100
106
|
/** `table` only: a pattern (case-insensitive) for the row that ends a table. */
|
|
101
107
|
until?: string;
|
|
102
|
-
/** `table` only: how a row's values sit against a cell wrapped over several lines; default `auto`. */
|
|
108
|
+
/** `table` only: how a row's values sit against a cell wrapped over several lines (PDF); default `auto`. */
|
|
103
109
|
align?: TableAlign;
|
|
110
|
+
/** `table` only: a pattern (case-insensitive) for the names of the sheets to read (workbook); default every sheet. */
|
|
111
|
+
sheet?: string;
|
|
112
|
+
/** `table` only: how many rows the header spans (workbook); a column's key joins its header texts. Default 1. */
|
|
113
|
+
headerRows?: number;
|
|
114
|
+
/** `table` only: output keys whose empty cells take the value of the row above. */
|
|
115
|
+
fillDown?: string[];
|
|
116
|
+
/** `table` only: read hidden sheets and rows (workbook) or hidden slides (deck) too. */
|
|
117
|
+
includeHidden?: boolean;
|
|
118
|
+
/** `table` only: a pattern (case-insensitive) for the titles of the slides to read (deck); default every slide. */
|
|
119
|
+
slide?: string;
|
|
120
|
+
/** `table` only: read text boxes laid out as a table instead of native tables (deck). */
|
|
121
|
+
shapes?: boolean;
|
|
104
122
|
}
|
|
105
123
|
export interface SetStep extends StepBaseFields {
|
|
106
124
|
type: 'set';
|
|
@@ -150,9 +168,35 @@ export interface HookStep extends StepBaseFields {
|
|
|
150
168
|
name: string;
|
|
151
169
|
args?: Record<string, unknown>;
|
|
152
170
|
}
|
|
153
|
-
|
|
171
|
+
/**
|
|
172
|
+
* How the engine confirms a captcha was solved (it never takes the solver's
|
|
173
|
+
* word): the challenge is gone, and/or an element appears.
|
|
174
|
+
*/
|
|
175
|
+
export interface CaptchaCheck {
|
|
176
|
+
/** The detected challenge must be off the page. Default `true`. */
|
|
177
|
+
gone?: boolean;
|
|
178
|
+
/** An element that must appear once solved. */
|
|
179
|
+
selector?: string;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Solves the captcha on the live page, if there is one (none is not an error):
|
|
183
|
+
* a challenge known to sit at one point of the crawl, a login form. The solver
|
|
184
|
+
* and the defaults come from `session.captcha`, unless the step names its own.
|
|
185
|
+
*/
|
|
186
|
+
export interface CaptchaStep extends StepBaseFields {
|
|
187
|
+
type: 'captcha';
|
|
188
|
+
/** A captcha solver the runner registered; `session.captcha.solver` when omitted. */
|
|
189
|
+
solver?: string;
|
|
190
|
+
/** Where the challenge is (a Playwright selector); the common widgets when omitted. */
|
|
191
|
+
selector?: string;
|
|
192
|
+
verify?: CaptchaCheck;
|
|
193
|
+
attempts?: number;
|
|
194
|
+
timeoutMs?: number;
|
|
195
|
+
}
|
|
196
|
+
export type Step = GotoStep | ClickStep | FillStep | PressStep | SelectStep | ScrollStep | WaitStep | EvaluateStep | ScreenshotStep | RequestStep | ExtractStep | SetStep | CollectStep | ForEachStep | IfStep | PaginateStep | EmitStep | HookStep | CaptchaStep;
|
|
154
197
|
export type StepType = Step['type'];
|
|
155
198
|
export declare const errorPolicySchema: z.ZodType<ErrorPolicy>;
|
|
156
199
|
export declare const paginateNextSchema: z.ZodType<PaginateNext>;
|
|
200
|
+
export declare const captchaCheckSchema: z.ZodType<CaptchaCheck>;
|
|
157
201
|
export declare const stepSchema: z.ZodType<Step>;
|
|
158
202
|
//# sourceMappingURL=step.contract.d.ts.map
|
|
@@ -4,5 +4,5 @@ export { selectHtml } from './html-selector.algorithm.js';
|
|
|
4
4
|
export type { HtmlMatch } from './html-selector.algorithm.js';
|
|
5
5
|
export { takeFromHtml, takeFromJson, collapse } from './take-value.mapper.js';
|
|
6
6
|
export type { Take } from './take-value.mapper.js';
|
|
7
|
-
export { tryParseJson, parseJsonText, dataItemsOf } from './json-text.algorithm.js';
|
|
7
|
+
export { tryParseJson, parseJsonText, dataItemsOf, parseJsonLike, parseJsonLines } from './json-text.algorithm.js';
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JSON that arrives as text: a `<script type="application/ld+json">` body, a
|
|
3
|
-
* `data-*` attribute, a fetched document read as text. Sites wrap
|
|
4
|
-
*
|
|
3
|
+
* `data-*` attribute, a fetched document read as text. Sites wrap JSON in
|
|
4
|
+
* things that are not JSON: comment guards around inline JSON-LD, prefixes
|
|
5
|
+
* that stop a page from loading an API as a script, a JSONP callback, an
|
|
6
|
+
* assignment in an inline script. Those wrappers are removed, but only after
|
|
7
|
+
* the text failed to parse as it is, and what is left must still be strict
|
|
8
|
+
* JSON: nothing is evaluated.
|
|
5
9
|
*/
|
|
6
10
|
/**
|
|
7
|
-
* Parses text as JSON,
|
|
11
|
+
* Parses text as JSON, or as JSON inside one of the wrappers sites put around
|
|
12
|
+
* it: comment guards, an anti-hijacking prefix, a JSONP call, an assignment.
|
|
13
|
+
* Valid JSON is always read as it is; a wrapper is only removed when that
|
|
14
|
+
* fails.
|
|
15
|
+
*
|
|
16
|
+
* @param text - The text.
|
|
17
|
+
* @returns The value, or the error the text as it is gave.
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseJsonLike(text: string): {
|
|
20
|
+
value: unknown;
|
|
21
|
+
} | {
|
|
22
|
+
error: Error;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Parses JSON Lines (NDJSON): one JSON value per non-blank line.
|
|
26
|
+
*
|
|
27
|
+
* @param text - The text.
|
|
28
|
+
* @param source - Where it came from, for the error.
|
|
29
|
+
* @returns The values, in order.
|
|
30
|
+
* @throws Error naming the source and the line that does not parse.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseJsonLines(text: string, source: string): unknown[];
|
|
33
|
+
/**
|
|
34
|
+
* Parses text as JSON, wrappers removed (see {@link parseJsonLike}).
|
|
8
35
|
*
|
|
9
36
|
* @param text - The text.
|
|
10
37
|
* @returns The value, or `undefined` when it is not JSON.
|
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
import type { BrowserSession } from '../browser-session/index.js';
|
|
2
|
+
import type { CaptchaGuard } from '../captcha/index.js';
|
|
2
3
|
import type { EventBus } from '../crawl-events/index.js';
|
|
3
4
|
import type { ExtractionScope, LiveElement } from '../extraction-scope/index.js';
|
|
4
5
|
import type { InputRecipe, PaginateNext, Step } from '../recipe-schema/index.js';
|
|
5
6
|
import { RunGate } from '../step-flow/index.js';
|
|
6
7
|
import type { NextPageResult, StepRunner } from '../step-flow/index.js';
|
|
7
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Runs web-mode leaf steps on a browser page. With a captcha guard, a page a
|
|
10
|
+
* navigation, click or key press leads to is checked for a challenge, solved
|
|
11
|
+
* before the next step runs.
|
|
12
|
+
*/
|
|
8
13
|
export declare class WebStepRunner implements StepRunner {
|
|
9
14
|
private readonly session;
|
|
10
15
|
private readonly recipe;
|
|
11
16
|
private readonly events;
|
|
12
17
|
private readonly gate;
|
|
18
|
+
private readonly captcha?;
|
|
13
19
|
private readonly page;
|
|
14
|
-
constructor(session: BrowserSession, recipe: InputRecipe, events: EventBus, gate?: RunGate);
|
|
20
|
+
constructor(session: BrowserSession, recipe: InputRecipe, events: EventBus, gate?: RunGate, captcha?: CaptchaGuard | undefined);
|
|
15
21
|
/** Clicks and key presses can navigate; keep `page.url` honest after every leaf step. */
|
|
16
22
|
private trackUrl;
|
|
23
|
+
/** Navigates; a block page showing a captcha is solved under `onBlock.solve`, and a page reached is checked for one. */
|
|
24
|
+
private visit;
|
|
17
25
|
runLeaf(step: Step, scope: ExtractionScope): Promise<void>;
|
|
18
26
|
nextPage(next: PaginateNext, scope: ExtractionScope): Promise<NextPageResult>;
|
|
19
27
|
elements(selector: string): Promise<LiveElement[]>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** The delimiters detection chooses between, in order of preference on a tie. */
|
|
2
|
+
export declare const CSV_DELIMITERS: readonly [",", ";", "\t", "|"];
|
|
3
|
+
/**
|
|
4
|
+
* Parses CSV text (RFC 4180, tolerant): a field in double quotes may hold the
|
|
5
|
+
* delimiter, line breaks and `""` for a quote; a quote inside an unquoted
|
|
6
|
+
* field is taken literally (`1.0 Hybrid "Cross"`); CRLF, LF and CR all end a
|
|
7
|
+
* record. Rows are kept as read: ragged rows stay ragged, nothing is trimmed.
|
|
8
|
+
*
|
|
9
|
+
* @param text - The decoded file.
|
|
10
|
+
* @param delimiter - One character.
|
|
11
|
+
* @returns The rows; a trailing empty line adds no row.
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseCsv(text: string, delimiter: string): string[][];
|
|
14
|
+
/**
|
|
15
|
+
* Chooses the delimiter of a CSV: the candidate whose field count (above one)
|
|
16
|
+
* is the most consistent over the first lines, so a title line or two above
|
|
17
|
+
* the header does not mislead it. A file of one column gets `,`.
|
|
18
|
+
*
|
|
19
|
+
* `;` with decimal commas (`Panda;15.950,00`), the usual European export,
|
|
20
|
+
* scores `;`: a comma split gives rows of uneven width.
|
|
21
|
+
*
|
|
22
|
+
* @param text - The decoded file.
|
|
23
|
+
* @returns The delimiter.
|
|
24
|
+
*/
|
|
25
|
+
export declare function detectDelimiter(text: string): string;
|
|
26
|
+
//# sourceMappingURL=csv-parser.algorithm.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { WorkbookDocument } from './workbook-document.model.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reads decoded CSV text into a workbook of one sheet, named after the file.
|
|
4
|
+
*
|
|
5
|
+
* @param text - The decoded file.
|
|
6
|
+
* @param options - The sheet name, the encoding it was decoded from (for a
|
|
7
|
+
* probe to report) and a delimiter; without one it is detected.
|
|
8
|
+
* @returns The workbook.
|
|
9
|
+
* @throws Error when the delimiter given is not one character.
|
|
10
|
+
*/
|
|
11
|
+
export declare function csvWorkbook(text: string, options: {
|
|
12
|
+
name: string;
|
|
13
|
+
encoding: string;
|
|
14
|
+
delimiter?: string;
|
|
15
|
+
}): WorkbookDocument;
|
|
16
|
+
/**
|
|
17
|
+
* The name a CSV's sheet takes: the file name without its extension
|
|
18
|
+
* (`…/prezzo_alle_8.csv` → `prezzo_alle_8`), else `csv`.
|
|
19
|
+
*
|
|
20
|
+
* @param url - Where the file came from.
|
|
21
|
+
* @returns The name.
|
|
22
|
+
*/
|
|
23
|
+
export declare function sheetNameOf(url: string): string;
|
|
24
|
+
//# sourceMappingURL=csv-workbook.mapper.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { WorkbookCell, WorkbookDocument } from './workbook-document.model.js';
|
|
2
|
+
/** What a table extract looks for in a workbook. */
|
|
3
|
+
export interface GridTableQuery {
|
|
4
|
+
/** Matches a table's (first) header row: its non-empty cells joined by spaces, whitespace collapsed. */
|
|
5
|
+
header: RegExp;
|
|
6
|
+
/** Matches the row that ends a table; a table also ends at the next header or the sheet's end. `^$` ends it at the first empty row. */
|
|
7
|
+
until?: RegExp;
|
|
8
|
+
/** Output key -> a pattern for that column's header; unmatched columns are dropped. Without it, the headers are the keys. */
|
|
9
|
+
columns?: Record<string, RegExp>;
|
|
10
|
+
/** Matches the names of the sheets to read; default every sheet. */
|
|
11
|
+
sheet?: RegExp;
|
|
12
|
+
/** How many rows the header spans (default 1): a column's key joins its header texts. */
|
|
13
|
+
headerRows?: number;
|
|
14
|
+
/** Output keys whose empty cells take the value of the row above. */
|
|
15
|
+
fillDown?: string[];
|
|
16
|
+
/** Read hidden sheets and hidden rows too. */
|
|
17
|
+
includeHidden?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** One table found in a workbook. */
|
|
20
|
+
export interface GridTable {
|
|
21
|
+
sheet: string;
|
|
22
|
+
/** The first header cell: the table's name when it has one. */
|
|
23
|
+
title: string;
|
|
24
|
+
/** The column keys, left to right. */
|
|
25
|
+
header: string[];
|
|
26
|
+
/** One object per row, keyed by column; text trimmed, numbers and booleans as they are. */
|
|
27
|
+
rows: Record<string, WorkbookCell>[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Finds every table whose header row matches, in every sheet the query
|
|
31
|
+
* selects, and reads its rows by column. Unlike a PDF, a grid needs no
|
|
32
|
+
* geometry: column *i* of a row belongs to header *i*.
|
|
33
|
+
*
|
|
34
|
+
* Merged ranges are filled first (the file stores their value in the top-left
|
|
35
|
+
* cell only), so a brand merged down its models' rows reads on every row, and
|
|
36
|
+
* a group header merged across its sub-columns names each of them. Empty rows
|
|
37
|
+
* are skipped.
|
|
38
|
+
*
|
|
39
|
+
* @param document - The workbook.
|
|
40
|
+
* @param query - Which tables, and how to name their columns.
|
|
41
|
+
* @returns The tables, sheet by sheet, top to bottom.
|
|
42
|
+
*/
|
|
43
|
+
export declare function findGridTables(document: WorkbookDocument, query: GridTableQuery): GridTable[];
|
|
44
|
+
/**
|
|
45
|
+
* Fills blank cells in the given columns with the value of the row above,
|
|
46
|
+
* within one table: pivot exports write a group's name on its first row only.
|
|
47
|
+
*
|
|
48
|
+
* @param rows - The table's rows, in order.
|
|
49
|
+
* @param keys - The columns to fill.
|
|
50
|
+
* @returns The rows, filled (new objects; the input is not changed).
|
|
51
|
+
*/
|
|
52
|
+
export declare function fillDown<Row extends Record<string, unknown>>(rows: readonly Row[], keys: readonly string[]): Row[];
|
|
53
|
+
//# sourceMappingURL=grid-table.algorithm.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Sheet } from './workbook-document.model.js';
|
|
2
|
+
/**
|
|
3
|
+
* Every `<table>` of an HTML document as a sheet (`table 1`, `table 2`…, in
|
|
4
|
+
* document order), so the workbook table reader works on web pages and
|
|
5
|
+
* rendered Markdown: rows in order (`thead`, `tbody`, `tfoot` alike), `th` and
|
|
6
|
+
* `td` alike, cell text with whitespace collapsed, `colspan` and `rowspan` as
|
|
7
|
+
* merged ranges. A table inside a table is a sheet of its own, and its rows
|
|
8
|
+
* are not its parent's.
|
|
9
|
+
*
|
|
10
|
+
* @param html - The document.
|
|
11
|
+
* @returns The tables.
|
|
12
|
+
*/
|
|
13
|
+
export declare function htmlTableSheets(html: string): Sheet[];
|
|
14
|
+
//# sourceMappingURL=html-tables.mapper.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { workbookText, isWorkbookDocument } from './workbook-document.model.js';
|
|
2
|
+
export type { WorkbookDocument, Sheet, CsvFormat, WorkbookCell } from './workbook-document.model.js';
|
|
3
|
+
export { parseCsv, detectDelimiter, CSV_DELIMITERS } from './csv-parser.algorithm.js';
|
|
4
|
+
export { csvWorkbook, sheetNameOf } from './csv-workbook.mapper.js';
|
|
5
|
+
export { readXlsxWorkbook } from './read-xlsx.client.js';
|
|
6
|
+
export { findGridTables, fillDown } from './grid-table.algorithm.js';
|
|
7
|
+
export { htmlTableSheets } from './html-tables.mapper.js';
|
|
8
|
+
export type { GridTable, GridTableQuery } from './grid-table.algorithm.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { WorkbookDocument } from './workbook-document.model.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reads an `.xlsx` workbook into a workbook document, through
|
|
4
|
+
* `@opencraw/office-reader`: every worksheet's cells, with hidden sheets,
|
|
5
|
+
* hidden rows and merged ranges. Numbers and booleans keep their type (a
|
|
6
|
+
* cell's `13955.625` is unambiguous; as text, a locale guess could read it as
|
|
7
|
+
* thirteen million), dates become ISO text, errors their text, empty cells
|
|
8
|
+
* `''`. Formulas give their cached value. The reader is imported on first use,
|
|
9
|
+
* so recipes that never read a spreadsheet never load it.
|
|
10
|
+
*
|
|
11
|
+
* @param bytes - The file.
|
|
12
|
+
* @param source - Where it came from, for messages.
|
|
13
|
+
* @returns The workbook.
|
|
14
|
+
* @throws Error naming the source, and saying what to do, for a file that is
|
|
15
|
+
* not a readable workbook (a legacy `.xls`, a password-protected file, an `.ods`…).
|
|
16
|
+
*/
|
|
17
|
+
export declare function readXlsxWorkbook(bytes: Uint8Array, source: string): Promise<WorkbookDocument>;
|
|
18
|
+
//# sourceMappingURL=read-xlsx.client.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A cell: text, or, from a spreadsheet, the number or boolean it holds
|
|
3
|
+
* (`13955.625` stays a number, so no locale guess can misread it). Dates are
|
|
4
|
+
* ISO text (`2026-06-01`, `2026-06-01T09:30:00`), errors their text
|
|
5
|
+
* (`#DIV/0!`), an empty cell `''`. A CSV's cells are all text.
|
|
6
|
+
*/
|
|
7
|
+
export type WorkbookCell = string | number | boolean;
|
|
8
|
+
/** One sheet of a workbook. A CSV is a workbook of one sheet. */
|
|
9
|
+
export interface Sheet {
|
|
10
|
+
name: string;
|
|
11
|
+
/** Top to bottom; a row holds as many cells as were read (rows can be ragged). */
|
|
12
|
+
rows: WorkbookCell[][];
|
|
13
|
+
/** A sheet hidden in the workbook: `table` skips it unless `includeHidden`. */
|
|
14
|
+
hidden?: boolean;
|
|
15
|
+
/** Rows hidden in the sheet (0-based): `table` skips them unless `includeHidden`. */
|
|
16
|
+
hiddenRows?: number[];
|
|
17
|
+
/**
|
|
18
|
+
* Merged ranges as A1 references (`B10:B13`). The value of a merged range
|
|
19
|
+
* sits in its top-left cell only, as the file stores it; `table` copies it
|
|
20
|
+
* into every cell the range covers.
|
|
21
|
+
*/
|
|
22
|
+
merges?: string[];
|
|
23
|
+
}
|
|
24
|
+
/** How a CSV was read, for a probe to report. */
|
|
25
|
+
export interface CsvFormat {
|
|
26
|
+
encoding: string;
|
|
27
|
+
delimiter: string;
|
|
28
|
+
}
|
|
29
|
+
/** A spreadsheet or a CSV read into sheets of cells: what `extract` works on. */
|
|
30
|
+
export interface WorkbookDocument {
|
|
31
|
+
kind: 'workbook';
|
|
32
|
+
sheets: Sheet[];
|
|
33
|
+
/** Present when the workbook came from a CSV. */
|
|
34
|
+
csv?: CsvFormat;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The text a `regex` extract reads: the visible rows of the visible sheets,
|
|
38
|
+
* cells separated by a tab, sheets separated by a blank line.
|
|
39
|
+
*
|
|
40
|
+
* @param document - The workbook.
|
|
41
|
+
* @returns The text.
|
|
42
|
+
*/
|
|
43
|
+
export declare function workbookText(document: WorkbookDocument): string;
|
|
44
|
+
/**
|
|
45
|
+
* Whether a value bound in scope is a read workbook (so `extract … from` can take it).
|
|
46
|
+
*
|
|
47
|
+
* @param value - Anything.
|
|
48
|
+
* @returns Whether it is a {@link WorkbookDocument}.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isWorkbookDocument(value: unknown): value is WorkbookDocument;
|
|
51
|
+
//# sourceMappingURL=workbook-document.model.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** How YAML scalars are read: typed as YAML 1.2 types them, or as the text written. */
|
|
2
|
+
export type YamlScalars = 'typed' | 'text';
|
|
3
|
+
/** A YAML text, read. */
|
|
4
|
+
export interface YamlRead {
|
|
5
|
+
/** The document's value; several documents (`---`) give an array of their values. */
|
|
6
|
+
data: unknown;
|
|
7
|
+
documents: number;
|
|
8
|
+
/** What the parser noticed but read anyway: an unknown tag (`!!js/function`) is kept as its plain value. */
|
|
9
|
+
warnings: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Parses YAML with the `yaml` package, imported on first use. The version is
|
|
13
|
+
* pinned to YAML 1.2 (core schema) whatever the document declares: under a
|
|
14
|
+
* `%YAML 1.1` directive, `NO` would read as `false` and `0123` as octal `83`.
|
|
15
|
+
* Merge keys (`<<: *base`) are applied, duplicate keys are an error, aliases
|
|
16
|
+
* are capped, and custom tags never build values: nothing in the text runs.
|
|
17
|
+
*
|
|
18
|
+
* @param text - The YAML.
|
|
19
|
+
* @param source - Where it came from, for messages.
|
|
20
|
+
* @param scalars - `typed` (default), or `text` to keep every scalar as written (`0123` stays `"0123"`).
|
|
21
|
+
* @returns The data, the number of documents and the warnings.
|
|
22
|
+
* @throws Error naming the source, with the line and column, for YAML that does not parse.
|
|
23
|
+
*/
|
|
24
|
+
export declare function readYaml(text: string, source: string, scalars?: YamlScalars): Promise<YamlRead>;
|
|
25
|
+
//# sourceMappingURL=read-yaml.client.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opencraw/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.esm.js",
|
|
6
6
|
"module": "./dist/index.esm.js",
|
|
@@ -20,11 +20,14 @@
|
|
|
20
20
|
"!**/*.js.map"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
+
"@opencraw/office-reader": "^0.0.2",
|
|
23
24
|
"cheerio": "^1.2.0",
|
|
24
25
|
"domhandler": "^6.0.1",
|
|
25
26
|
"jsonpath-plus": "^10.4.0",
|
|
27
|
+
"marked": "^18.0.14",
|
|
26
28
|
"pdfjs-dist": "^6.3.289",
|
|
27
29
|
"playwright": "^1.63.0",
|
|
30
|
+
"yaml": "^2.9.1",
|
|
28
31
|
"zod": "^4.6.5"
|
|
29
32
|
},
|
|
30
33
|
"publishConfig": {
|
|
@@ -54,7 +57,8 @@
|
|
|
54
57
|
"e2e": {
|
|
55
58
|
"executor": "nx:run-commands",
|
|
56
59
|
"dependsOn": [
|
|
57
|
-
"build"
|
|
60
|
+
"build",
|
|
61
|
+
"^build"
|
|
58
62
|
],
|
|
59
63
|
"options": {
|
|
60
64
|
"command": "tsc -p tsconfig.e2e.json && jest --config jest.e2e.config.cts",
|
|
@@ -70,6 +74,16 @@
|
|
|
70
74
|
"command": "node tools/emit-json-schemas.mjs",
|
|
71
75
|
"cwd": "packages/core"
|
|
72
76
|
}
|
|
77
|
+
},
|
|
78
|
+
"typecheck": {
|
|
79
|
+
"dependsOn": [
|
|
80
|
+
"^build"
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
"lint": {
|
|
84
|
+
"dependsOn": [
|
|
85
|
+
"^build"
|
|
86
|
+
]
|
|
73
87
|
}
|
|
74
88
|
}
|
|
75
89
|
}
|