@opencraw/core 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +4 -2
  2. package/dist/index.esm.js +3010 -638
  3. package/dist/src/access/access-profile.contract.d.ts +4 -0
  4. package/dist/src/access/index.d.ts +1 -1
  5. package/dist/src/api-steps/extract-from-document.use-case.d.ts +16 -3
  6. package/dist/src/api-steps/index.d.ts +1 -1
  7. package/dist/src/api-steps/send-request.use-case.d.ts +5 -4
  8. package/dist/src/browser-session/browser-profile.store.d.ts +52 -0
  9. package/dist/src/browser-session/browser.client.d.ts +8 -0
  10. package/dist/src/browser-session/index.d.ts +1 -0
  11. package/dist/src/captcha/captcha-budget.model.d.ts +21 -0
  12. package/dist/src/captcha/captcha-detection.client.d.ts +28 -0
  13. package/dist/src/captcha/captcha-guard.use-case.d.ts +64 -0
  14. package/dist/src/captcha/captcha-solver-registry.store.d.ts +19 -0
  15. package/dist/src/captcha/captcha-solver.contract.d.ts +47 -0
  16. package/dist/src/captcha/captcha.error.d.ts +13 -0
  17. package/dist/src/captcha/index.d.ts +10 -0
  18. package/dist/src/captcha/resolve-captcha.use-case.d.ts +40 -0
  19. package/dist/src/crawl-events/crawl-event.contract.d.ts +48 -0
  20. package/dist/src/crawl-execution/bootstrap-session.use-case.d.ts +27 -3
  21. package/dist/src/crawl-execution/crawl-options.config.d.ts +30 -0
  22. package/dist/src/crawl-execution/crawl-report.model.d.ts +6 -0
  23. package/dist/src/crawl-execution/create-crawler.use-case.d.ts +2 -1
  24. package/dist/src/crawl-execution/rotating-runner.use-case.d.ts +9 -0
  25. package/dist/src/crawl-execution/run-crawl.use-case.d.ts +6 -2
  26. package/dist/src/crawl-execution/run-input-recipe.use-case.d.ts +14 -3
  27. package/dist/src/deck-document/deck-document.model.d.ts +58 -0
  28. package/dist/src/deck-document/deck-table.algorithm.d.ts +35 -0
  29. package/dist/src/deck-document/index.d.ts +6 -0
  30. package/dist/src/deck-document/read-pptx.client.d.ts +16 -0
  31. package/dist/src/extraction-scope/extraction-scope.model.d.ts +3 -1
  32. package/dist/src/http-session/http-response.contract.d.ts +13 -1
  33. package/dist/src/http-session/text-decoding.algorithm.d.ts +35 -0
  34. package/dist/src/index.d.ts +13 -4
  35. package/dist/src/markdown-document/index.d.ts +3 -0
  36. package/dist/src/markdown-document/read-markdown.client.d.ts +29 -0
  37. package/dist/src/pdf-document/index.d.ts +1 -1
  38. package/dist/src/pdf-document/row-assembly.algorithm.d.ts +10 -1
  39. package/dist/src/recipe-loading/recipe-binding.validator.d.ts +3 -1
  40. package/dist/src/recipe-schema/index.d.ts +3 -3
  41. package/dist/src/recipe-schema/input-recipe.contract.d.ts +57 -4
  42. package/dist/src/recipe-schema/recipe-kind.enum.d.ts +3 -2
  43. package/dist/src/recipe-schema/step.contract.d.ts +46 -2
  44. package/dist/src/record-sink/dedupe.policy.d.ts +19 -7
  45. package/dist/src/record-sink/index.d.ts +1 -0
  46. package/dist/src/selection/index.d.ts +1 -1
  47. package/dist/src/selection/json-text.algorithm.d.ts +30 -3
  48. package/dist/src/step-flow/for-each.use-case.d.ts +4 -2
  49. package/dist/src/step-flow/host-throttle.policy.d.ts +49 -0
  50. package/dist/src/step-flow/index.d.ts +5 -0
  51. package/dist/src/step-flow/run-gate.policy.d.ts +12 -1
  52. package/dist/src/step-flow/step-runner.contract.d.ts +13 -0
  53. package/dist/src/step-flow/transport-retry.policy.d.ts +76 -0
  54. package/dist/src/web-steps/navigate.use-case.d.ts +3 -2
  55. package/dist/src/web-steps/run-web-step.use-case.d.ts +18 -3
  56. package/dist/src/workbook-document/csv-parser.algorithm.d.ts +26 -0
  57. package/dist/src/workbook-document/csv-workbook.mapper.d.ts +24 -0
  58. package/dist/src/workbook-document/grid-table.algorithm.d.ts +53 -0
  59. package/dist/src/workbook-document/html-tables.mapper.d.ts +14 -0
  60. package/dist/src/workbook-document/index.d.ts +9 -0
  61. package/dist/src/workbook-document/read-xlsx.client.d.ts +18 -0
  62. package/dist/src/workbook-document/workbook-document.model.d.ts +51 -0
  63. package/dist/src/yaml-document/index.d.ts +3 -0
  64. package/dist/src/yaml-document/read-yaml.client.d.ts +25 -0
  65. package/package.json +16 -2
@@ -2,12 +2,16 @@ import type { RecipeSet } from '../recipe-loading/index.js';
2
2
  import type { CrawlReport } from './crawl-report.model.js';
3
3
  import type { RecipeRunDependencies } from './run-input-recipe.use-case.js';
4
4
  /**
5
- * Runs every input recipe of a set, one after another, into one sink.
5
+ * Runs every input recipe of a set into one sink, `parallel` at a time
6
+ * (default one after another). Reports come back in the set's order whatever
7
+ * order the recipes finish in. Under `onRecipeError: 'stop'`, a failed recipe
8
+ * stops the ones not started yet; those already running finish.
6
9
  *
7
10
  * @param set - The bound recipes.
8
11
  * @param deps - Shared browser, hooks, events, sink and de-duplication.
9
12
  * @param onRecipeError - Whether a failed recipe stops the run.
13
+ * @param parallel - How many input recipes run at once.
10
14
  * @returns The report.
11
15
  */
12
- export declare function runCrawl(set: RecipeSet, deps: RecipeRunDependencies, onRecipeError: 'continue' | 'stop'): Promise<CrawlReport>;
16
+ export declare function runCrawl(set: RecipeSet, deps: RecipeRunDependencies, onRecipeError: 'continue' | 'stop', parallel?: number): Promise<CrawlReport>;
13
17
  //# sourceMappingURL=run-crawl.use-case.d.ts.map
@@ -1,9 +1,12 @@
1
1
  import type { AccessBroker } from '../access/index.js';
2
2
  import { BrowserClient } from '../browser-session/index.js';
3
+ import type { BrowserProfiles } from '../browser-session/index.js';
4
+ import { CaptchaSolverRegistry } from '../captcha/index.js';
3
5
  import type { EventBus } from '../crawl-events/index.js';
4
6
  import type { HookRegistry } from '../hooks/index.js';
5
- import type { InputRecipe, OutputRecipe } from '../recipe-schema/index.js';
7
+ import type { InputRecipe, OutputRecipe, RetryRule } from '../recipe-schema/index.js';
6
8
  import type { DedupePolicy, RecordSink } from '../record-sink/index.js';
9
+ import type { HostThrottle } from '../step-flow/index.js';
7
10
  import type { RecipeReport } from './crawl-report.model.js';
8
11
  export interface RecipeRunDependencies {
9
12
  browser: () => Promise<BrowserClient>;
@@ -20,6 +23,14 @@ export interface RecipeRunDependencies {
20
23
  debug?: boolean;
21
24
  /** Leases each recipe run its network access. */
22
25
  access: AccessBroker;
26
+ /** The solvers recipes name; none when omitted. */
27
+ captchaSolvers?: CaptchaSolverRegistry;
28
+ /** The crawler's per-site throttle, shared by every recipe. */
29
+ hosts?: HostThrottle;
30
+ /** The runner's persistent browser profiles, for `session.browserProfile`. */
31
+ profiles?: BrowserProfiles;
32
+ /** The crawler's retry rule, under each recipe's `limits.retry`. */
33
+ retry?: RetryRule;
23
34
  }
24
35
  /**
25
36
  * Runs one input recipe end to end: session, runner, the step walk, and for
@@ -31,10 +42,10 @@ export interface RecipeRunDependencies {
31
42
  * the sink sees one record at a time and `maxRecords` is exact: once reached,
32
43
  * every later emit returns `stop` before mapping.
33
44
  *
34
- * @param input - The input recipe.
45
+ * @param recipe - The input recipe.
35
46
  * @param output - The output recipe it feeds.
36
47
  * @param deps - Shared browser, hooks, events, sink and de-duplication.
37
48
  * @returns What happened.
38
49
  */
39
- export declare function runInputRecipe(input: InputRecipe, output: OutputRecipe, deps: RecipeRunDependencies): Promise<RecipeReport>;
50
+ export declare function runInputRecipe(recipe: InputRecipe, output: OutputRecipe, deps: RecipeRunDependencies): Promise<RecipeReport>;
40
51
  //# sourceMappingURL=run-input-recipe.use-case.d.ts.map
@@ -0,0 +1,58 @@
1
+ import type { Sheet } from '../workbook-document/index.js';
2
+ /** A text box on a slide, in points from the slide's top-left corner. */
3
+ export interface DeckShape {
4
+ x: number;
5
+ y: number;
6
+ width: number;
7
+ height: number;
8
+ text: string;
9
+ /** The placeholder it fills (`title`, `body`…). */
10
+ placeholder?: string;
11
+ }
12
+ /** A chart's data, from the values the chart caches. */
13
+ export interface DeckChart {
14
+ type: string;
15
+ title?: string;
16
+ series: {
17
+ name: string;
18
+ categories: string[];
19
+ values: (number | null)[];
20
+ }[];
21
+ }
22
+ /** One slide. */
23
+ export interface DeckSlide {
24
+ number: number;
25
+ title?: string;
26
+ hidden: boolean;
27
+ /** Text boxes in reading order. */
28
+ shapes: DeckShape[];
29
+ /** Native tables, as sheets (`table 1`…) with their merged cells. */
30
+ tables: Sheet[];
31
+ charts: DeckChart[];
32
+ notes: string;
33
+ }
34
+ /** A presentation read into slides: what `extract` works on. */
35
+ export interface DeckDocument {
36
+ kind: 'deck';
37
+ /** The slide size, in points. */
38
+ width: number;
39
+ height: number;
40
+ slides: DeckSlide[];
41
+ }
42
+ /**
43
+ * The text a `regex` extract reads: per visible slide, its title, its text
44
+ * boxes in reading order, its tables' rows (cells separated by a tab) and its
45
+ * notes after `Notes:`; slides separated by a blank line.
46
+ *
47
+ * @param document - The deck.
48
+ * @returns The text.
49
+ */
50
+ export declare function deckText(document: DeckDocument): string;
51
+ /**
52
+ * Whether a value bound in scope is a read deck (so `extract … from` can take it).
53
+ *
54
+ * @param value - Anything.
55
+ * @returns Whether it is a {@link DeckDocument}.
56
+ */
57
+ export declare function isDeckDocument(value: unknown): value is DeckDocument;
58
+ //# sourceMappingURL=deck-document.model.d.ts.map
@@ -0,0 +1,35 @@
1
+ import type { TableAlign } from '../pdf-document/index.js';
2
+ import type { GridTableQuery, WorkbookCell } from '../workbook-document/index.js';
3
+ import type { DeckDocument } from './deck-document.model.js';
4
+ /** What a table extract looks for in a deck. */
5
+ export interface DeckTableQuery extends Omit<GridTableQuery, 'sheet'> {
6
+ /** Matches the titles of the slides to read; default every slide. */
7
+ slide?: RegExp;
8
+ /** Read text boxes laid out as a table instead of native tables. */
9
+ shapes?: boolean;
10
+ /** With `shapes`: how a row's values sit against a box wrapped over several lines. */
11
+ align?: TableAlign;
12
+ }
13
+ /** One table found in a deck. */
14
+ export interface DeckTable {
15
+ /** The slide's number, from 1. */
16
+ slide: number;
17
+ slideTitle: string;
18
+ /** The first header cell. */
19
+ title: string;
20
+ header: string[];
21
+ rows: Record<string, WorkbookCell>[];
22
+ }
23
+ /**
24
+ * Finds tables in a deck: native tables through the workbook table reader
25
+ * (merged cells filled, a header over several rows joined), or, with
26
+ * `shapes`, text boxes laid out as a table through the PDF table reader (a box
27
+ * is a cell, boxes whose heights overlap a row, columns from where the body's
28
+ * boxes start). Hidden slides are skipped unless `includeHidden`.
29
+ *
30
+ * @param document - The deck.
31
+ * @param query - Which tables, on which slides, and how to name their columns.
32
+ * @returns The tables, slide by slide.
33
+ */
34
+ export declare function findDeckTables(document: DeckDocument, query: DeckTableQuery): DeckTable[];
35
+ //# sourceMappingURL=deck-table.algorithm.d.ts.map
@@ -0,0 +1,6 @@
1
+ export { deckText, isDeckDocument } from './deck-document.model.js';
2
+ export type { DeckDocument, DeckSlide, DeckShape, DeckChart } from './deck-document.model.js';
3
+ export { readPptxDeck } from './read-pptx.client.js';
4
+ export { findDeckTables } from './deck-table.algorithm.js';
5
+ export type { DeckTable, DeckTableQuery } from './deck-table.algorithm.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,16 @@
1
+ import type { DeckDocument } from './deck-document.model.js';
2
+ /**
3
+ * Reads a `.pptx` presentation into a deck document, through
4
+ * `@opencraw/office-reader`: every slide's text boxes with their positions,
5
+ * its tables with their merged cells, its charts' cached data and its notes.
6
+ * The reader is imported on first use, so recipes that never read a
7
+ * presentation never load it.
8
+ *
9
+ * @param bytes - The file.
10
+ * @param source - Where it came from, for messages.
11
+ * @returns The deck.
12
+ * @throws Error naming the source, and saying what to do, for a file that is
13
+ * not a readable presentation (a legacy `.ppt`, a password-protected file, an `.odp`…).
14
+ */
15
+ export declare function readPptxDeck(bytes: Uint8Array, source: string): Promise<DeckDocument>;
16
+ //# sourceMappingURL=read-pptx.client.d.ts.map
@@ -7,9 +7,11 @@
7
7
  * Page state (the current URL, page number and the document `extract` reads by
8
8
  * default) is scope state too, bound in the innermost scope that navigated.
9
9
  */
10
+ import type { DeckDocument } from '../deck-document/index.js';
10
11
  import type { PdfDocument } from '../pdf-document/index.js';
12
+ import type { WorkbookDocument } from '../workbook-document/index.js';
11
13
  /** A fetched or rendered document a later `extract` can read. */
12
- export type ScopeDocument = PdfDocument | {
14
+ export type ScopeDocument = PdfDocument | WorkbookDocument | DeckDocument | {
13
15
  kind: 'json';
14
16
  data: unknown;
15
17
  } | {
@@ -1,4 +1,6 @@
1
+ import type { DeckDocument } from '../deck-document/index.js';
1
2
  import type { PdfDocument } from '../pdf-document/index.js';
3
+ import type { WorkbookDocument } from '../workbook-document/index.js';
2
4
  import type { BodyKind, HttpMethod } from '../recipe-schema/index.js';
3
5
  /** One HTTP request as the api runner sends it, templates already rendered. */
4
6
  export interface HttpRequest {
@@ -9,10 +11,16 @@ export interface HttpRequest {
9
11
  body?: unknown;
10
12
  /** How to read the body; default: from the response content type. */
11
13
  as?: BodyKind;
14
+ /** The encoding of a text body (a WHATWG label); default: the BOM, the declared charset, UTF-8, else Windows-1252. */
15
+ encoding?: string;
16
+ /** A CSV body's delimiter (one character); default: detected. */
17
+ delimiter?: string;
18
+ /** A YAML body's scalars: `typed` (default) or `text`. */
19
+ scalars?: 'typed' | 'text';
12
20
  timeoutMs?: number;
13
21
  }
14
22
  /** A parsed response body. Structurally the same as a scope document, on purpose. */
15
- export type HttpBody = PdfDocument | {
23
+ export type HttpBody = PdfDocument | WorkbookDocument | DeckDocument | {
16
24
  kind: 'json';
17
25
  data: unknown;
18
26
  } | {
@@ -28,6 +36,10 @@ export interface HttpResponse {
28
36
  url: string;
29
37
  headers: Record<string, string>;
30
38
  body: HttpBody;
39
+ /** What reading the body noticed but read anyway (an unknown YAML tag…). */
40
+ warnings?: string[];
41
+ /** The format the body was read as (`yaml` and `jsonl` both give JSON data). */
42
+ format?: BodyKind;
31
43
  }
32
44
  /** The part of the client the api runner needs; tests fake it. */
33
45
  export interface HttpSender {
@@ -0,0 +1,35 @@
1
+ /** Decoded text and the encoding it was read as. */
2
+ export interface DecodedText {
3
+ text: string;
4
+ encoding: string;
5
+ }
6
+ /**
7
+ * Decodes a body, in this order: a byte-order mark (UTF-8, UTF-16 LE/BE; Excel's
8
+ * "Unicode text" export is UTF-16 LE), the encoding a recipe asks for, the
9
+ * charset the server declares, strict UTF-8, and Windows-1252 (a superset of
10
+ * Latin-1) for text that is not UTF-8.
11
+ *
12
+ * The Windows-1252 fallback is only taken when the text holds no valid UTF-8
13
+ * beyond ASCII: a UTF-8 page with one stray byte keeps its accents, with a
14
+ * replacement character for the stray byte, instead of turning every accent
15
+ * into mojibake.
16
+ *
17
+ * @param bytes - The body.
18
+ * @param options - `encoding`: the recipe's choice, a WHATWG label (wins over
19
+ * the charset, not over a BOM); `charset`: from the content type (ignored when
20
+ * not a known label).
21
+ * @returns The text, without its BOM, and the encoding used.
22
+ * @throws Error when `encoding` is not a known label.
23
+ */
24
+ export declare function decodeText(bytes: Uint8Array, options?: {
25
+ encoding?: string;
26
+ charset?: string;
27
+ }): DecodedText;
28
+ /**
29
+ * The charset a content type declares (`text/csv; charset=ISO-8859-1`).
30
+ *
31
+ * @param contentType - The header value.
32
+ * @returns The charset, or `undefined`.
33
+ */
34
+ export declare function charsetOf(contentType: string): string | undefined;
35
+ //# sourceMappingURL=text-decoding.algorithm.d.ts.map
@@ -1,11 +1,11 @@
1
1
  export { createCrawler } from './crawl-execution/index.js';
2
- export { AccessBroker, AccessConfigError, ACCESS_PRESETS, loadAccessConfig, accessConfigSchema, accessConfigJsonSchema } from './access/index.js';
2
+ export { AccessBroker, AccessConfigError, ACCESS_PRESETS, loadAccessConfig, accessConfigSchema, accessConfigJsonSchema, throttleConfigSchema } from './access/index.js';
3
3
  export type { AccessConfig, AccessProfile, AccessPlugin, AccessLease, LeaseRequest, PluginLeaseRequest, ProxySettings, AccessPreset } from './access/index.js';
4
4
  export type { Crawler, CrawlOptions, CrawlReport, RecipeReport } from './crawl-execution/index.js';
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
- 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';
7
+ export { parseInputRecipe, parseOutputRecipe, RecipeValidationError, inputRecipeJsonSchema, outputRecipeJsonSchema, inputRecipeSchema, outputRecipeSchema, retryRuleSchema } from './recipe-schema/index.js';
8
+ export type { InputRecipe, OutputRecipe, FieldSpec, Step, StepType, MappingRule, TransformRule, ErrorPolicy, PaginateNext, SessionSpec, SessionAccess, CaptchaSettings, CaptchaStep, RetryRule, 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,15 @@ 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 { StepFailure } from './step-flow/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';
30
+ export { StepFailure, HostThrottle, DEFAULT_RETRY_RULE } from './step-flow/index.js';
31
+ export type { ThrottleConfig, HostRule } from './step-flow/index.js';
25
32
  export { TransformError } from './transformation/index.js';
33
+ export { CaptchaError, DEFAULT_CAPTCHA_SELECTOR, detectChallenge } from './captcha/index.js';
34
+ export type { CaptchaSolver, CaptchaChallenge, CaptchaContext, CaptchaOutcome, CaptchaKind, CaptchaLog } from './captcha/index.js';
26
35
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,3 @@
1
+ export { readMarkdown } from './read-markdown.client.js';
2
+ export type { MarkdownRead } from './read-markdown.client.js';
3
+ //# 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
- 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';
8
+ export { inputRecipeSchema, sessionSpecSchema, startPointSchema, retryRuleSchema } from './input-recipe.contract.js';
9
+ export type { InputRecipe, SessionSpec, SessionBootstrap, SessionAccess, BlockRule, BlockRotation, CaptchaSettings, RetryRule, 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
- /** What to do when blocked: take a new access lease (a new IP), reopen the session, and retry the step. */
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: boolean;
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,14 +98,37 @@ export interface SessionSpec {
69
98
  access?: SessionAccess;
70
99
  blockedWhen?: BlockRule;
71
100
  onBlock?: BlockRotation;
101
+ captcha?: CaptchaSettings;
102
+ /**
103
+ * A browser profile of the runner that persists between runs (cookies,
104
+ * storage, cache): web recipes and bootstraps run in it. A name; the runner
105
+ * decides where profiles live.
106
+ */
107
+ browserProfile?: string;
108
+ }
109
+ /**
110
+ * How a request that fails in passing (a dropped connection, a timeout, a
111
+ * 503, a 429) is sent again. On by default: three tries in all.
112
+ */
113
+ export interface RetryRule {
114
+ /** Tries per request, the first included; `1` turns retrying off. Default 3. */
115
+ attempts?: number;
116
+ /** The first pause; it doubles on every retry. Default 1000. */
117
+ backoffMs?: number;
118
+ /** The longest pause, `Retry-After` included; a server asking for longer is not retried. Default 30000. */
119
+ maxDelayMs?: number;
120
+ /** The statuses retried. Default `[408, 425, 429, 500, 502, 503, 504]`. */
121
+ statuses?: number[];
72
122
  }
73
123
  export interface CrawlLimits {
74
124
  maxRecords?: number;
75
125
  /** Minimum interval between two request starts across the recipe, whatever runs in parallel. */
76
126
  delayMs?: number;
77
127
  timeoutMs?: number;
78
- /** How many `forEach` iterations may run at once (api mode; a web recipe drives one page). Default 1. */
128
+ /** How many `forEach` iterations over a list may run at once: requests in api mode, tabs in web mode. Default 1. */
79
129
  concurrency?: number;
130
+ /** How requests that fail in passing are sent again; the crawler's `retry`, else three tries, when omitted. */
131
+ retry?: RetryRule;
80
132
  }
81
133
  /** Where to start, how to navigate, what to extract, and how it maps to one output recipe. */
82
134
  export interface InputRecipe {
@@ -99,5 +151,6 @@ export interface InputRecipe {
99
151
  }
100
152
  export declare const startPointSchema: z.ZodType<StartPoint>;
101
153
  export declare const sessionSpecSchema: z.ZodType<SessionSpec>;
154
+ export declare const retryRuleSchema: z.ZodType<RetryRule>;
102
155
  export declare const inputRecipeSchema: z.ZodType<InputRecipe>;
103
156
  //# sourceMappingURL=input-recipe.contract.d.ts.map
@@ -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
- export type Step = GotoStep | ClickStep | FillStep | PressStep | SelectStep | ScrollStep | WaitStep | EvaluateStep | ScreenshotStep | RequestStep | ExtractStep | SetStep | CollectStep | ForEachStep | IfStep | PaginateStep | EmitStep | HookStep;
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
@@ -1,17 +1,29 @@
1
1
  import type { OutputRecord } from '../output-mapping/index.js';
2
2
  /** How far de-duplication reaches: the whole run, one input recipe, or not at all. */
3
3
  export type DedupeScope = 'run' | 'recipe' | 'off';
4
- /** Drops records whose key was already seen. First record wins; keyless records always pass. */
4
+ /** One input recipe's view of de-duplication: whether a record repeats a key already seen. */
5
+ export interface RecipeDedupe {
6
+ /**
7
+ * @param record - A validated record.
8
+ * @returns `true` when the record repeats an earlier key and must be dropped.
9
+ */
10
+ isDuplicate: (record: OutputRecord) => boolean;
11
+ }
12
+ /**
13
+ * Drops records whose key was already seen. First record wins; keyless
14
+ * records always pass. Recipes running in parallel each get their own view:
15
+ * under `recipe` scope they never see each other's keys, under `run` scope
16
+ * they share them (and whichever emits a key first keeps it).
17
+ */
5
18
  export declare class DedupePolicy {
6
19
  readonly scope: DedupeScope;
7
- private seen;
20
+ private readonly shared;
8
21
  constructor(scope?: DedupeScope);
9
- /** Called when an input recipe starts; forgets keys under `recipe` scope. */
10
- startRecipe(): void;
11
22
  /**
12
- * @param record - A validated record.
13
- * @returns `true` when the record repeats an earlier key and must be dropped.
23
+ * The de-duplication one input recipe run uses.
24
+ *
25
+ * @returns Its view: keys shared with the run, its own, or none checked.
14
26
  */
15
- isDuplicate(record: OutputRecord): boolean;
27
+ forRecipe(): RecipeDedupe;
16
28
  }
17
29
  //# sourceMappingURL=dedupe.policy.d.ts.map
@@ -4,5 +4,6 @@ export type { MemorySink } from './memory-sink.repository.js';
4
4
  export { jsonLinesSink } from './json-lines-sink.repository.js';
5
5
  export type { JsonLinesSinkOptions } from './json-lines-sink.repository.js';
6
6
  export { DedupePolicy } from './dedupe.policy.js';
7
+ export type { RecipeDedupe } from './dedupe.policy.js';
7
8
  export type { DedupeScope } from './dedupe.policy.js';
8
9
  //# sourceMappingURL=index.d.ts.map