@opencraw/core 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/README.md +1 -1
  2. package/dist/access/access-broker.use-case.d.ts +43 -0
  3. package/dist/access/access-config.error.d.ts +5 -0
  4. package/dist/access/access-config.repository.d.ts +11 -0
  5. package/dist/access/access-json-schema.mapper.d.ts +8 -0
  6. package/dist/access/access-plugin.contract.d.ts +48 -0
  7. package/dist/access/access-preset.store.d.ts +19 -0
  8. package/dist/access/access-profile.contract.d.ts +95 -0
  9. package/dist/access/index.d.ts +11 -0
  10. package/dist/access/render-access.mapper.d.ts +41 -0
  11. package/dist/access/session-id.algorithm.d.ts +8 -0
  12. package/dist/api-steps/extract-from-document.use-case.d.ts +41 -0
  13. package/dist/api-steps/index.d.ts +4 -0
  14. package/dist/api-steps/run-api-step.use-case.d.ts +20 -0
  15. package/dist/api-steps/send-request.use-case.d.ts +23 -0
  16. package/dist/browser-session/browser-profile.store.d.ts +52 -0
  17. package/dist/browser-session/browser-session.config.d.ts +21 -0
  18. package/dist/browser-session/browser.client.d.ts +75 -0
  19. package/dist/browser-session/index.d.ts +6 -0
  20. package/dist/captcha/captcha-budget.model.d.ts +21 -0
  21. package/dist/captcha/captcha-detection.client.d.ts +28 -0
  22. package/dist/captcha/captcha-guard.use-case.d.ts +64 -0
  23. package/dist/captcha/captcha-solver-registry.store.d.ts +19 -0
  24. package/dist/captcha/captcha-solver.contract.d.ts +47 -0
  25. package/dist/captcha/captcha.error.d.ts +13 -0
  26. package/dist/captcha/index.d.ts +10 -0
  27. package/dist/captcha/resolve-captcha.use-case.d.ts +40 -0
  28. package/dist/crawl-events/crawl-event.contract.d.ts +169 -0
  29. package/dist/crawl-events/event-bus.store.d.ts +10 -0
  30. package/dist/crawl-events/index.d.ts +4 -0
  31. package/dist/crawl-events/trace-line.mapper.d.ts +14 -0
  32. package/dist/crawl-execution/bootstrap-session.use-case.d.ts +79 -0
  33. package/dist/crawl-execution/crawl-options.config.d.ts +67 -0
  34. package/dist/crawl-execution/crawl-report.model.d.ts +33 -0
  35. package/dist/crawl-execution/create-crawler.use-case.d.ts +20 -0
  36. package/dist/crawl-execution/index.d.ts +8 -0
  37. package/dist/crawl-execution/rotating-runner.use-case.d.ts +66 -0
  38. package/dist/crawl-execution/run-crawl.use-case.d.ts +17 -0
  39. package/dist/crawl-execution/run-input-recipe.use-case.d.ts +51 -0
  40. package/dist/deck-document/deck-document.model.d.ts +58 -0
  41. package/dist/deck-document/deck-table.algorithm.d.ts +35 -0
  42. package/dist/deck-document/index.d.ts +6 -0
  43. package/dist/deck-document/read-pptx.client.d.ts +16 -0
  44. package/dist/docx-document/index.d.ts +2 -0
  45. package/dist/docx-document/read-docx-html.client.d.ts +23 -0
  46. package/dist/extraction-scope/extraction-scope.model.d.ts +77 -0
  47. package/dist/extraction-scope/index.d.ts +5 -0
  48. package/dist/extraction-scope/live-element.model.d.ts +28 -0
  49. package/dist/hooks/hook-registry.store.d.ts +16 -0
  50. package/dist/hooks/hook.contract.d.ts +17 -0
  51. package/dist/hooks/hook.error.d.ts +7 -0
  52. package/dist/hooks/index.d.ts +4 -0
  53. package/dist/http-session/http-response.contract.d.ts +58 -0
  54. package/dist/http-session/http.client.d.ts +42 -0
  55. package/dist/http-session/index.d.ts +5 -0
  56. package/dist/http-session/text-decoding.algorithm.d.ts +35 -0
  57. package/dist/index.esm.js +1410 -95
  58. package/dist/markdown-document/index.d.ts +3 -0
  59. package/dist/markdown-document/read-markdown.client.d.ts +39 -0
  60. package/dist/output-mapping/coerce-field.mapper.d.ts +19 -0
  61. package/dist/output-mapping/generated-field.mapper.d.ts +16 -0
  62. package/dist/output-mapping/index.d.ts +9 -0
  63. package/dist/output-mapping/map-record.use-case.d.ts +27 -0
  64. package/dist/output-mapping/mapping.error.d.ts +17 -0
  65. package/dist/output-mapping/missing-value.policy.d.ts +14 -0
  66. package/dist/output-mapping/output-field.validator.d.ts +10 -0
  67. package/dist/output-mapping/output-record.model.d.ts +20 -0
  68. package/dist/pdf-document/index.d.ts +7 -0
  69. package/dist/pdf-document/pdf-document.model.d.ts +51 -0
  70. package/dist/pdf-document/pdf-table.algorithm.d.ts +43 -0
  71. package/dist/pdf-document/read-pdf.client.d.ts +18 -0
  72. package/dist/pdf-document/row-assembly.algorithm.d.ts +26 -0
  73. package/dist/recipe-loading/index.d.ts +9 -0
  74. package/dist/recipe-loading/load-recipe-set.use-case.d.ts +45 -0
  75. package/dist/recipe-loading/read-recipe-source.use-case.d.ts +14 -0
  76. package/dist/recipe-loading/recipe-binding.error.d.ts +14 -0
  77. package/dist/recipe-loading/recipe-binding.validator.d.ts +34 -0
  78. package/dist/recipe-loading/recipe-file.repository.d.ts +11 -0
  79. package/dist/recipe-loading/recipe-set.model.d.ts +12 -0
  80. package/dist/recipe-loading/recipe-source.contract.d.ts +28 -0
  81. package/dist/recipe-loading/recipe-text.mapper.d.ts +14 -0
  82. package/dist/recipe-schema/index.d.ts +15 -0
  83. package/dist/recipe-schema/input-recipe.contract.d.ts +156 -0
  84. package/dist/recipe-schema/json-schema.mapper.d.ts +15 -0
  85. package/dist/recipe-schema/output-recipe.contract.d.ts +47 -0
  86. package/dist/recipe-schema/recipe-kind.enum.d.ts +35 -0
  87. package/dist/recipe-schema/recipe-validation.error.d.ts +17 -0
  88. package/dist/recipe-schema/recipe.validator.d.ts +28 -0
  89. package/dist/recipe-schema/step.contract.d.ts +206 -0
  90. package/dist/recipe-schema/transform-rule.contract.d.ts +120 -0
  91. package/dist/record-diff/index.d.ts +4 -0
  92. package/dist/record-diff/record-diff.algorithm.d.ts +82 -0
  93. package/dist/record-diff/records-file.repository.d.ts +10 -0
  94. package/dist/record-sink/dedupe.policy.d.ts +29 -0
  95. package/dist/record-sink/index.d.ts +9 -0
  96. package/dist/record-sink/json-lines-sink.repository.d.ts +19 -0
  97. package/dist/record-sink/memory-sink.repository.d.ts +9 -0
  98. package/dist/record-sink/record-sink.contract.d.ts +17 -0
  99. package/dist/selection/html-selector.algorithm.d.ts +19 -0
  100. package/dist/selection/index.d.ts +8 -0
  101. package/dist/selection/json-path.algorithm.d.ts +9 -0
  102. package/dist/selection/json-text.algorithm.d.ts +58 -0
  103. package/dist/selection/regex.algorithm.d.ts +12 -0
  104. package/dist/selection/take-value.mapper.d.ts +27 -0
  105. package/dist/src/access/access-profile.contract.d.ts +4 -0
  106. package/dist/src/access/index.d.ts +1 -1
  107. package/dist/src/api-steps/extract-from-document.use-case.d.ts +5 -5
  108. package/dist/src/api-steps/send-request.use-case.d.ts +3 -2
  109. package/dist/src/browser-session/browser-profile.store.d.ts +52 -0
  110. package/dist/src/browser-session/browser.client.d.ts +8 -0
  111. package/dist/src/browser-session/index.d.ts +1 -0
  112. package/dist/src/crawl-events/crawl-event.contract.d.ts +8 -0
  113. package/dist/src/crawl-execution/bootstrap-session.use-case.d.ts +23 -2
  114. package/dist/src/crawl-execution/crawl-options.config.d.ts +27 -0
  115. package/dist/src/crawl-execution/rotating-runner.use-case.d.ts +9 -0
  116. package/dist/src/crawl-execution/run-crawl.use-case.d.ts +6 -2
  117. package/dist/src/crawl-execution/run-input-recipe.use-case.d.ts +11 -3
  118. package/dist/src/docx-document/index.d.ts +2 -0
  119. package/dist/src/docx-document/read-docx-html.client.d.ts +23 -0
  120. package/dist/src/extraction-scope/extraction-scope.model.d.ts +2 -1
  121. package/dist/src/http-session/http-response.contract.d.ts +2 -1
  122. package/dist/src/index.d.ts +9 -4
  123. package/dist/src/markdown-document/index.d.ts +1 -1
  124. package/dist/src/markdown-document/read-markdown.client.d.ts +10 -0
  125. package/dist/src/recipe-schema/index.d.ts +2 -2
  126. package/dist/src/recipe-schema/input-recipe.contract.d.ts +24 -1
  127. package/dist/src/recipe-schema/recipe-kind.enum.d.ts +1 -1
  128. package/dist/src/recipe-schema/step.contract.d.ts +4 -0
  129. package/dist/src/record-diff/index.d.ts +4 -0
  130. package/dist/src/record-diff/record-diff.algorithm.d.ts +82 -0
  131. package/dist/src/record-diff/records-file.repository.d.ts +10 -0
  132. package/dist/src/record-sink/dedupe.policy.d.ts +19 -7
  133. package/dist/src/record-sink/index.d.ts +1 -0
  134. package/dist/src/selection/html-selector.algorithm.d.ts +2 -1
  135. package/dist/src/step-flow/for-each.use-case.d.ts +4 -2
  136. package/dist/src/step-flow/host-throttle.policy.d.ts +49 -0
  137. package/dist/src/step-flow/index.d.ts +5 -0
  138. package/dist/src/step-flow/run-gate.policy.d.ts +12 -1
  139. package/dist/src/step-flow/step-runner.contract.d.ts +13 -0
  140. package/dist/src/step-flow/transport-retry.policy.d.ts +76 -0
  141. package/dist/src/web-steps/navigate.use-case.d.ts +3 -2
  142. package/dist/src/web-steps/run-web-step.use-case.d.ts +8 -1
  143. package/dist/src/xml-document/index.d.ts +6 -0
  144. package/dist/src/xml-document/xml-document.model.d.ts +13 -0
  145. package/dist/src/xml-document/xml-parser.client.d.ts +49 -0
  146. package/dist/src/xml-document/xpath.algorithm.d.ts +30 -0
  147. package/dist/step-flow/block-rule.policy.d.ts +21 -0
  148. package/dist/step-flow/blocked.error.d.ts +9 -0
  149. package/dist/step-flow/for-each.use-case.d.ts +20 -0
  150. package/dist/step-flow/host-throttle.policy.d.ts +49 -0
  151. package/dist/step-flow/index.d.ts +15 -0
  152. package/dist/step-flow/paginate.use-case.d.ts +17 -0
  153. package/dist/step-flow/retry.policy.d.ts +21 -0
  154. package/dist/step-flow/run-gate.policy.d.ts +50 -0
  155. package/dist/step-flow/run-steps.use-case.d.ts +39 -0
  156. package/dist/step-flow/step-failure.error.d.ts +14 -0
  157. package/dist/step-flow/step-runner.contract.d.ts +52 -0
  158. package/dist/step-flow/transport-retry.policy.d.ts +76 -0
  159. package/dist/template/expression.algorithm.d.ts +53 -0
  160. package/dist/template/index.d.ts +7 -0
  161. package/dist/template/template.algorithm.d.ts +53 -0
  162. package/dist/template/value-path.algorithm.d.ts +29 -0
  163. package/dist/template/value-text.algorithm.d.ts +18 -0
  164. package/dist/transformation/apply-transform-chain.use-case.d.ts +17 -0
  165. package/dist/transformation/collection.algorithm.d.ts +15 -0
  166. package/dist/transformation/currency.algorithm.d.ts +15 -0
  167. package/dist/transformation/date.algorithm.d.ts +13 -0
  168. package/dist/transformation/index.d.ts +10 -0
  169. package/dist/transformation/lookup.algorithm.d.ts +24 -0
  170. package/dist/transformation/number.algorithm.d.ts +30 -0
  171. package/dist/transformation/string.algorithm.d.ts +18 -0
  172. package/dist/transformation/transform-registry.store.d.ts +36 -0
  173. package/dist/transformation/transform.error.d.ts +8 -0
  174. package/dist/transformation/url.algorithm.d.ts +20 -0
  175. package/dist/web-steps/evaluate-script.use-case.d.ts +10 -0
  176. package/dist/web-steps/extract-from-page.use-case.d.ts +10 -0
  177. package/dist/web-steps/index.d.ts +2 -0
  178. package/dist/web-steps/interact.use-case.d.ts +36 -0
  179. package/dist/web-steps/navigate.use-case.d.ts +15 -0
  180. package/dist/web-steps/run-web-step.use-case.d.ts +37 -0
  181. package/dist/web-steps/snapshot-elements.use-case.d.ts +13 -0
  182. package/dist/workbook-document/csv-parser.algorithm.d.ts +26 -0
  183. package/dist/workbook-document/csv-workbook.mapper.d.ts +24 -0
  184. package/dist/workbook-document/grid-table.algorithm.d.ts +53 -0
  185. package/dist/workbook-document/html-tables.mapper.d.ts +14 -0
  186. package/dist/workbook-document/index.d.ts +9 -0
  187. package/dist/workbook-document/read-xlsx.client.d.ts +18 -0
  188. package/dist/workbook-document/workbook-document.model.d.ts +51 -0
  189. package/dist/xml-document/index.d.ts +6 -0
  190. package/dist/xml-document/xml-document.model.d.ts +13 -0
  191. package/dist/xml-document/xml-parser.client.d.ts +49 -0
  192. package/dist/xml-document/xpath.algorithm.d.ts +30 -0
  193. package/dist/yaml-document/index.d.ts +3 -0
  194. package/dist/yaml-document/read-yaml.client.d.ts +25 -0
  195. package/package.json +4 -2
@@ -0,0 +1,82 @@
1
+ import type { OutputRecipe } from '../recipe-schema/index.js';
2
+ /** A record as a JSON Lines file holds it: the data, plus the sink's `_source` (and `_key` in append mode). */
3
+ export type StoredRecord = Record<string, unknown>;
4
+ /** One field whose value changed; nested objects are compared field by field (`price.amount`). */
5
+ export interface FieldChange {
6
+ field: string;
7
+ before: unknown;
8
+ after: unknown;
9
+ }
10
+ export type RecordChange = {
11
+ change: 'added';
12
+ key: string;
13
+ after: StoredRecord;
14
+ } | {
15
+ change: 'removed';
16
+ key: string;
17
+ before: StoredRecord;
18
+ } | {
19
+ change: 'changed';
20
+ key: string;
21
+ before: StoredRecord;
22
+ after: StoredRecord;
23
+ fields: FieldChange[];
24
+ };
25
+ /** How two runs differ, by record key. */
26
+ export interface RecordDiff {
27
+ added: number;
28
+ removed: number;
29
+ changed: number;
30
+ unchanged: number;
31
+ /** Every change, removed first, then changed, then added, each in file order. */
32
+ changes: RecordChange[];
33
+ /** Records on either side the key cannot tell apart from an earlier one: only the first counts. */
34
+ repeated: number;
35
+ /** How many records each side holds. */
36
+ counts: {
37
+ previous: number;
38
+ current: number;
39
+ };
40
+ /** Set when the current run holds far fewer records: the usual sign a site changed and a recipe broke quietly. */
41
+ shrunk?: {
42
+ previous: number;
43
+ current: number;
44
+ };
45
+ }
46
+ export interface DiffOptions {
47
+ /** The fields that identify a record. Default: the `_key` each line carries (a sink in append mode writes it). */
48
+ key?: readonly string[];
49
+ /** Fields not compared (their dotted paths match nested ones too). `_source` and `_key` never are. */
50
+ ignore?: readonly string[];
51
+ /** The share of records a run may lose before `shrunk` is set; default 0.5 (half). */
52
+ shrink?: number;
53
+ }
54
+ /**
55
+ * Compares two runs' records by key: what was added, what was removed, and
56
+ * for the records in both, which fields changed, before and after. Like a
57
+ * `git diff` of a price list, but row by row instead of line by line, so a
58
+ * reordered file is not a change.
59
+ *
60
+ * @param previous - The earlier run's records.
61
+ * @param current - The later run's records.
62
+ * @param options - The key fields, the fields to ignore, the shrink threshold.
63
+ * @returns The differences.
64
+ * @throws Error when records carry no key: without `key`, every line needs `_key`.
65
+ */
66
+ export declare function diffRecords(previous: readonly StoredRecord[], current: readonly StoredRecord[], options?: DiffOptions): RecordDiff;
67
+ /**
68
+ * The diff options an output recipe implies: its key fields, and its fields
69
+ * the engine fills differently every run (`generated: now`, `uuid`).
70
+ *
71
+ * @param output - The output recipe.
72
+ * @returns The key and the fields to ignore.
73
+ */
74
+ export declare function diffOptionsFor(output: OutputRecipe): Required<Pick<DiffOptions, 'key' | 'ignore'>>;
75
+ /**
76
+ * A key as people read it: the key fields' values joined (`Pandina · 1.0 Hybrid`).
77
+ *
78
+ * @param key - A record key (a JSON array of the key values).
79
+ * @returns The readable form.
80
+ */
81
+ export declare function readableKey(key: string): string;
82
+ //# sourceMappingURL=record-diff.algorithm.d.ts.map
@@ -0,0 +1,10 @@
1
+ import type { StoredRecord } from './record-diff.algorithm.js';
2
+ /**
3
+ * The records of a JSON Lines file, as a sink wrote them.
4
+ *
5
+ * @param path - The file.
6
+ * @returns One record per non-empty line.
7
+ * @throws Error naming the file and the line that is not a JSON object.
8
+ */
9
+ export declare function readRecordsFile(path: string): Promise<StoredRecord[]>;
10
+ //# sourceMappingURL=records-file.repository.d.ts.map
@@ -0,0 +1,29 @@
1
+ import type { OutputRecord } from '../output-mapping/index.js';
2
+ /** How far de-duplication reaches: the whole run, one input recipe, or not at all. */
3
+ export type DedupeScope = 'run' | 'recipe' | 'off';
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
+ */
18
+ export declare class DedupePolicy {
19
+ readonly scope: DedupeScope;
20
+ private readonly shared;
21
+ constructor(scope?: DedupeScope);
22
+ /**
23
+ * The de-duplication one input recipe run uses.
24
+ *
25
+ * @returns Its view: keys shared with the run, its own, or none checked.
26
+ */
27
+ forRecipe(): RecipeDedupe;
28
+ }
29
+ //# sourceMappingURL=dedupe.policy.d.ts.map
@@ -0,0 +1,9 @@
1
+ export type { RecordSink, SinkSummary } from './record-sink.contract.js';
2
+ export { memorySink } from './memory-sink.repository.js';
3
+ export type { MemorySink } from './memory-sink.repository.js';
4
+ export { jsonLinesSink } from './json-lines-sink.repository.js';
5
+ export type { JsonLinesSinkOptions } from './json-lines-sink.repository.js';
6
+ export { DedupePolicy } from './dedupe.policy.js';
7
+ export type { RecipeDedupe } from './dedupe.policy.js';
8
+ export type { DedupeScope } from './dedupe.policy.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,19 @@
1
+ import type { RecordSink } from './record-sink.contract.js';
2
+ export interface JsonLinesSinkOptions {
3
+ /**
4
+ * Keep what the file holds and add to it. Each line then carries the record
5
+ * key as `_key`, and `open` reads the keys already there so a resumed run
6
+ * (`CrawlOptions.resume`) can skip them.
7
+ */
8
+ append?: boolean;
9
+ }
10
+ /**
11
+ * A sink that writes one JSON object per line to a file (JSON Lines). Each line
12
+ * is the record's `data` plus a `_source` member (and `_key` in append mode).
13
+ *
14
+ * @param path - The file to write; created with its directories, truncated on open unless `append`.
15
+ * @param options - Append mode.
16
+ * @returns The sink.
17
+ */
18
+ export declare function jsonLinesSink(path: string, options?: JsonLinesSinkOptions): RecordSink;
19
+ //# sourceMappingURL=json-lines-sink.repository.d.ts.map
@@ -0,0 +1,9 @@
1
+ import type { OutputRecord } from '../output-mapping/index.js';
2
+ import type { RecordSink } from './record-sink.contract.js';
3
+ /** A sink that keeps records in memory; `records` is readable at any time. */
4
+ export interface MemorySink extends RecordSink {
5
+ readonly records: OutputRecord[];
6
+ }
7
+ /** @returns A new in-memory sink. */
8
+ export declare function memorySink(): MemorySink;
9
+ //# sourceMappingURL=memory-sink.repository.d.ts.map
@@ -0,0 +1,17 @@
1
+ import type { OutputRecord } from '../output-mapping/index.js';
2
+ import type { OutputRecipe } from '../recipe-schema/index.js';
3
+ /** What a sink reports when closed. */
4
+ export interface SinkSummary {
5
+ written: number;
6
+ /** Where the records went, when the sink has a location. */
7
+ location?: string;
8
+ }
9
+ /** Where validated records go. Opened once per run, written per record, closed once. */
10
+ export interface RecordSink {
11
+ open: (output: OutputRecipe) => Promise<void>;
12
+ write: (record: OutputRecord) => Promise<void>;
13
+ close: () => Promise<SinkSummary>;
14
+ /** Whether a record with this key is already there; what a resumed run asks before writing. */
15
+ has?: (key: string) => Promise<boolean>;
16
+ }
17
+ //# sourceMappingURL=record-sink.contract.d.ts.map
@@ -0,0 +1,19 @@
1
+ import type { Cheerio, CheerioAPI } from 'cheerio';
2
+ import type { AnyNode } from 'domhandler';
3
+ /** A matched HTML element together with the document it belongs to. */
4
+ export interface HtmlMatch {
5
+ api: CheerioAPI;
6
+ element: Cheerio<AnyNode>;
7
+ }
8
+ /**
9
+ * Runs a CSS selector on static HTML. A whole document is parsed as one; anything
10
+ * else (a table row, a list item taken with `take: "html"`) is parsed as a
11
+ * fragment, so cells and rows outside a table survive instead of being dropped.
12
+ *
13
+ * @param html - The markup (a whole document or a fragment).
14
+ * @param selector - A CSS selector.
15
+ * @param xml - Read the markup as XML: names keep their case, `take: "html"` gives XML.
16
+ * @returns Every match, in document order.
17
+ */
18
+ export declare function selectHtml(html: string, selector: string, xml?: boolean): HtmlMatch[];
19
+ //# sourceMappingURL=html-selector.algorithm.d.ts.map
@@ -0,0 +1,8 @@
1
+ export { selectJson } from './json-path.algorithm.js';
2
+ export { selectRegex } from './regex.algorithm.js';
3
+ export { selectHtml } from './html-selector.algorithm.js';
4
+ export type { HtmlMatch } from './html-selector.algorithm.js';
5
+ export { takeFromHtml, takeFromJson, collapse } from './take-value.mapper.js';
6
+ export type { Take } from './take-value.mapper.js';
7
+ export { tryParseJson, parseJsonText, dataItemsOf, parseJsonLike, parseJsonLines } from './json-text.algorithm.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Evaluates a JSONPath expression on a decoded JSON document.
3
+ *
4
+ * @param document - The JSON value.
5
+ * @param path - A JSONPath such as `$.items[*].url`.
6
+ * @returns Every match, in document order.
7
+ */
8
+ export declare function selectJson(document: unknown, path: string): unknown[];
9
+ //# sourceMappingURL=json-path.algorithm.d.ts.map
@@ -0,0 +1,58 @@
1
+ /**
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 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.
9
+ */
10
+ /**
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}).
35
+ *
36
+ * @param text - The text.
37
+ * @returns The value, or `undefined` when it is not JSON.
38
+ */
39
+ export declare function tryParseJson(text: string): unknown;
40
+ /**
41
+ * Parses text that must be JSON.
42
+ *
43
+ * @param text - The text.
44
+ * @param id - What the text is, for the error.
45
+ * @returns The value.
46
+ * @throws Error when it is not JSON.
47
+ */
48
+ export declare function parseJsonText(text: string, id: string): unknown;
49
+ /**
50
+ * The data a value holds, whatever shape it arrived in: JSON text is parsed, a
51
+ * list of texts becomes the list of its parsable entries, and an entry that
52
+ * parses to a list is spliced in. Data that is not text is kept as is.
53
+ *
54
+ * @param value - A bound value: data, text, or a list of either.
55
+ * @returns A list of items.
56
+ */
57
+ export declare function dataItemsOf(value: unknown): unknown[];
58
+ //# sourceMappingURL=json-text.algorithm.d.ts.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Evaluates a regular expression on text: the extract kind for values that
3
+ * live in inline scripts, attributes or prose rather than in elements or JSON.
4
+ *
5
+ * @param text - The document text.
6
+ * @param pattern - A regular expression source; group 1 is returned when the
7
+ * pattern has a capturing group, else the whole match.
8
+ * @returns Every match, in document order.
9
+ * @throws When the pattern is not a valid regular expression.
10
+ */
11
+ export declare function selectRegex(text: string, pattern: string): string[];
12
+ //# sourceMappingURL=regex.algorithm.d.ts.map
@@ -0,0 +1,27 @@
1
+ import type { HtmlMatch } from './html-selector.algorithm.js';
2
+ /** What `extract` takes from a match: text, inner HTML, an attribute, an input's value, or the node as data. */
3
+ export type Take = 'text' | 'html' | 'value' | 'json' | `attr:${string}`;
4
+ /**
5
+ * The value of an HTML match.
6
+ *
7
+ * @param match - A selected element.
8
+ * @param take - What to take; `text` collapses whitespace.
9
+ * @returns The value; `undefined` for a missing attribute.
10
+ */
11
+ export declare function takeFromHtml(match: HtmlMatch, take: Take): unknown;
12
+ /**
13
+ * The value of a JSON match.
14
+ *
15
+ * @param node - A JSONPath result.
16
+ * @param take - `json` keeps the node; `text` stringifies scalars.
17
+ * @returns The value.
18
+ */
19
+ export declare function takeFromJson(node: unknown, take: Take): unknown;
20
+ /**
21
+ * Text as a human reads it: runs of whitespace collapsed, ends trimmed.
22
+ *
23
+ * @param text - Raw text content.
24
+ * @returns The collapsed text.
25
+ */
26
+ export declare function collapse(text: string): string;
27
+ //# sourceMappingURL=take-value.mapper.d.ts.map
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import type { ThrottleConfig } from '../step-flow/index.js';
2
3
  /**
3
4
  * Where a crawl's traffic goes. Profiles live in the runner's access config,
4
5
  * never in a recipe: they hold the user's accounts. Every string may use
@@ -84,8 +85,11 @@ export interface AccessConfig {
84
85
  $schema?: string;
85
86
  profiles: Record<string, AccessProfile>;
86
87
  default?: string;
88
+ /** How gently each site is crawled, across every recipe: `{ delayMs?, concurrency?, domains? }`. */
89
+ throttle?: ThrottleConfig;
87
90
  }
88
91
  export declare const accessProfileSchema: z.ZodType<AccessProfile>;
92
+ export declare const throttleConfigSchema: z.ZodType<ThrottleConfig>;
89
93
  export declare const accessConfigSchema: z.ZodType<AccessConfig>;
90
94
  export {};
91
95
  //# sourceMappingURL=access-profile.contract.d.ts.map
@@ -3,7 +3,7 @@ export { loadAccessConfig } from './access-config.repository.js';
3
3
  export { AccessConfigError } from './access-config.error.js';
4
4
  export { ACCESS_PRESETS } from './access-preset.store.js';
5
5
  export type { AccessPreset } from './access-preset.store.js';
6
- export { accessConfigSchema, accessProfileSchema, BLOCKABLE_RESOURCES } from './access-profile.contract.js';
6
+ export { accessConfigSchema, accessProfileSchema, throttleConfigSchema, BLOCKABLE_RESOURCES } from './access-profile.contract.js';
7
7
  export type { AccessConfig, AccessProfile, ProxyProfile, PoolProfile, CdpProfile, PluginProfile, DirectProfile, ProxySettings, BlockableResource } from './access-profile.contract.js';
8
8
  export type { AccessPlugin, AccessLease, LeaseRequest, PluginLeaseRequest } from './access-plugin.contract.js';
9
9
  export { newSessionId } from './session-id.algorithm.js';
@@ -2,11 +2,11 @@ import type { ExtractionScope, ScopeDocument } from '../extraction-scope/index.j
2
2
  import type { ExtractStep } from '../recipe-schema/index.js';
3
3
  /**
4
4
  * Runs an `extract` step against a static document: the value bound under
5
- * `from`, else the scope's current document. `css` reads HTML, `jsonpath`
6
- * reads JSON (or a read PDF, workbook or deck as data), `table` reads the
7
- * tables of a PDF, a workbook (a spreadsheet, a CSV), a deck (a presentation)
8
- * or HTML (its `<table>`s), `regex` reads any document as text; `xpath` needs
9
- * a live page and is refused here.
5
+ * `from`, else the scope's current document. `css` reads HTML (or XML),
6
+ * `xpath` reads XML (a feed, a sitemap) or HTML parsed as a browser parses
7
+ * it, `jsonpath` reads JSON (or a read PDF, workbook or deck as data), `table`
8
+ * reads the tables of a PDF, a workbook (a spreadsheet, a CSV), a deck (a
9
+ * presentation) or HTML (its `<table>`s), `regex` reads any document as text.
10
10
  *
11
11
  * A `jsonpath` extract whose `from` is text parses that text as JSON, and a
12
12
  * list of texts (every `<script type="application/ld+json">` of a page) becomes
@@ -5,8 +5,9 @@ import type { InputRecipe, RequestStep } from '../recipe-schema/index.js';
5
5
  import type { RunGate } from '../step-flow/index.js';
6
6
  /**
7
7
  * Sends a `request` step: renders its templates, waits for the gate's throttle,
8
- * sends, checks the response against the recipe's block rule, then binds it as
9
- * the scope's current document (and under the step id).
8
+ * sends (again, after a pause, while it fails in passing: `limits.retry`),
9
+ * checks the response against the recipe's block rule, then binds it as the
10
+ * scope's current document (and under the step id).
10
11
  *
11
12
  * @param step - The request step.
12
13
  * @param scope - The scope to render in and bind into.
@@ -0,0 +1,52 @@
1
+ import type { BrowserSessionConfig } from './browser-session.config.js';
2
+ import { BrowserSession } from './browser.client.js';
3
+ import type { SessionOptions } from './browser.client.js';
4
+ /** A browser profile name: it becomes a directory, so no separators or dots. */
5
+ export declare const BROWSER_PROFILE_NAME: RegExp;
6
+ /**
7
+ * Browser profiles that persist between runs: each is a directory of a real
8
+ * browser's user data (cookies, local storage, IndexedDB, cache, service
9
+ * workers), so a login, a consent choice or a site's trust in a returning
10
+ * visitor carries over to the next run. The browser equivalent of a user who
11
+ * never clears their history.
12
+ *
13
+ * A profile directory can be open in one browser at a time. Within this
14
+ * crawler, a second use waits for the first to close; the same owner (one
15
+ * recipe run reopening after a rotation) takes it over instead. Another
16
+ * crawler holding it, in this process or another, is reported, not waited
17
+ * for: a lock file in the profile names the process, and one left by a
18
+ * process that died is taken over. (Chromium's own profile lock is not
19
+ * enough: headless builds do not take it.)
20
+ */
21
+ export declare class BrowserProfiles {
22
+ readonly directory: string;
23
+ private readonly config;
24
+ private readonly held;
25
+ private readonly waiting;
26
+ /**
27
+ * @param directory - Where the profiles live, one subdirectory each.
28
+ * @param config - The crawler's browser settings (type, binary, headless, timeouts).
29
+ */
30
+ constructor(directory: string, config?: BrowserSessionConfig);
31
+ private take;
32
+ private free;
33
+ private launch;
34
+ /**
35
+ * The profile's directory.
36
+ *
37
+ * @param name - A profile name.
38
+ * @returns The absolute path.
39
+ */
40
+ pathOf(name: string): string;
41
+ /**
42
+ * Opens a profile in its own browser, waiting while another run of this
43
+ * crawler uses it.
44
+ *
45
+ * @param name - The profile.
46
+ * @param options - Proxy, headers, viewport, cookies to add. `storageState` is ignored: the profile has its own.
47
+ * @param owner - Who opens it; the same owner reopening closes its previous session first.
48
+ * @returns The session; closing it frees the profile.
49
+ */
50
+ open(name: string, options: SessionOptions, owner: object): Promise<BrowserSession>;
51
+ }
52
+ //# sourceMappingURL=browser-profile.store.d.ts.map
@@ -39,6 +39,14 @@ export declare class BrowserSession {
39
39
  storageState(): Promise<StorageState>;
40
40
  close(): Promise<void>;
41
41
  }
42
+ /**
43
+ * What a context gets after it opened: the cookies to add and the resource
44
+ * types to skip.
45
+ *
46
+ * @param context - The context.
47
+ * @param options - The session options.
48
+ */
49
+ export declare function applySessionExtras(context: BrowserContext, options: SessionOptions): Promise<void>;
42
50
  /** A launched browser; sessions are opened from it and closed independently. */
43
51
  export declare class BrowserClient {
44
52
  private readonly browser;
@@ -1,4 +1,5 @@
1
1
  export { BrowserClient, BrowserSession } from './browser.client.js';
2
+ export { BrowserProfiles, BROWSER_PROFILE_NAME } from './browser-profile.store.js';
2
3
  export type { StorageState, SessionOptions } from './browser.client.js';
3
4
  export { DEFAULT_BROWSER_CONFIG } from './browser-session.config.js';
4
5
  export type { BrowserSessionConfig } from './browser-session.config.js';
@@ -49,6 +49,14 @@ export type CrawlEvent = (Base & {
49
49
  attempt: number;
50
50
  reason: string;
51
51
  }) |
52
+ /** A request failed in passing (a dropped connection, a 503, a 429) and is sent again after `delayMs`. */
53
+ (Base & {
54
+ type: 'request:retry';
55
+ url: string;
56
+ attempt: number;
57
+ reason: string;
58
+ delayMs: number;
59
+ }) |
52
60
  /** A captcha challenge is on the page. */
53
61
  (Base & {
54
62
  type: 'captcha:detected';
@@ -1,15 +1,20 @@
1
1
  import type { AccessLease } from '../access/index.js';
2
- import type { BrowserClient, BrowserSession, SessionOptions, StorageState } from '../browser-session/index.js';
2
+ import type { BrowserClient, BrowserProfiles, BrowserSession, SessionOptions, StorageState } from '../browser-session/index.js';
3
3
  import type { CaptchaGuard } from '../captcha/index.js';
4
4
  import type { EventBus } from '../crawl-events/index.js';
5
5
  import type { HookRegistry } from '../hooks/index.js';
6
6
  import type { InputRecipe } from '../recipe-schema/index.js';
7
+ import type { HostThrottle } from '../step-flow/index.js';
7
8
  export interface BootstrapDependencies {
8
9
  /** Launches (or returns) the shared browser; only called when a browser is needed. */
9
10
  browser: () => Promise<BrowserClient>;
10
11
  hooks: HookRegistry;
11
12
  events: EventBus;
12
13
  storageStateDir?: string;
14
+ /** The crawler's per-site throttle: the bootstrap's pages count too. */
15
+ hosts?: HostThrottle;
16
+ /** The runner's persistent browser profiles, for `session.browserProfile`. */
17
+ profiles?: BrowserProfiles;
13
18
  }
14
19
  /**
15
20
  * The session options an access lease contributes: its proxy, TLS leniency,
@@ -28,13 +33,29 @@ export declare function accessOptions(lease: AccessLease | undefined, headers: R
28
33
  * The bootstrap runs through the same access lease as the crawl that follows,
29
34
  * so a login and the requests that use its cookies come from one IP.
30
35
  *
36
+ * With `session.browserProfile`, the bootstrap runs in that profile, and
37
+ * without a bootstrap the profile's own cookies and storage are the state: an
38
+ * api recipe picks up a login a browser left in the profile.
39
+ *
31
40
  * @param recipe - The input recipe.
32
41
  * @param deps - Browser, hooks, events.
33
42
  * @param lease - The recipe run's access; direct when omitted.
34
43
  * @param captcha - Solves the bootstrap's captchas (a login form's).
44
+ * @param owner - The recipe run, which a browser profile is held by.
35
45
  * @returns The state, or `undefined` when the recipe declares none.
36
46
  */
37
- export declare function resolveStorageState(recipe: InputRecipe, deps: BootstrapDependencies, lease?: AccessLease, captcha?: CaptchaGuard): Promise<StorageState | undefined>;
47
+ export declare function resolveStorageState(recipe: InputRecipe, deps: BootstrapDependencies, lease?: AccessLease, captcha?: CaptchaGuard, owner?: object): Promise<StorageState | undefined>;
48
+ /**
49
+ * Opens the recipe's `session.browserProfile` with its session options and
50
+ * the lease's proxy.
51
+ *
52
+ * @param recipe - A recipe with `session.browserProfile`.
53
+ * @param deps - For `profiles`.
54
+ * @param lease - The access lease.
55
+ * @param owner - The recipe run.
56
+ * @returns The session in the profile.
57
+ */
58
+ export declare function openBrowserProfile(recipe: InputRecipe, deps: Pick<BootstrapDependencies, 'profiles'>, lease: AccessLease | undefined, owner: object): Promise<BrowserSession>;
38
59
  /**
39
60
  * The storage state saved by an earlier bootstrap (`session.storageStatePath`), if the recipe names one.
40
61
  *
@@ -3,6 +3,8 @@ import type { BrowserSessionConfig } from '../browser-session/index.js';
3
3
  import type { CaptchaSolver } from '../captcha/index.js';
4
4
  import type { CrawlListener } from '../crawl-events/index.js';
5
5
  import type { HookMap } from '../hooks/index.js';
6
+ import type { ThrottleConfig } from '../step-flow/index.js';
7
+ import type { RetryRule } from '../recipe-schema/index.js';
6
8
  import type { DedupeScope, RecordSink } from '../record-sink/index.js';
7
9
  /** How a crawler is created. Everything is optional. */
8
10
  export interface CrawlOptions {
@@ -15,10 +17,21 @@ export interface CrawlOptions {
15
17
  onEvent?: CrawlListener;
16
18
  /** Default `run`: a key seen once is dropped for the rest of the run. */
17
19
  dedupe?: DedupeScope;
20
+ /**
21
+ * How many input recipes of a set run at once; default 1, one after
22
+ * another. Each has its own browser context or HTTP session; the browser,
23
+ * the sink and the per-site `throttle` are shared.
24
+ */
25
+ parallel?: number;
18
26
  /** Whether a failed input recipe stops the run; default `continue`. */
19
27
  onRecipeError?: 'continue' | 'stop';
20
28
  /** Base directory for relative `storageStatePath` and `saveTo` values. */
21
29
  storageStateDir?: string;
30
+ /**
31
+ * Where `session.browserProfile` profiles live, one directory each. Default:
32
+ * `.opencraw/profiles` under `storageStateDir` (or the working directory).
33
+ */
34
+ profilesDir?: string;
22
35
  /**
23
36
  * Skip records whose key the sink already has (`sink.has`), reporting them as
24
37
  * `skipped`. Needs a sink that can answer, such as `jsonLinesSink(path, { append: true })`.
@@ -32,6 +45,20 @@ export interface CrawlOptions {
32
45
  * with `session.access.profile`. Without it every recipe goes direct.
33
46
  */
34
47
  access?: AccessConfig;
48
+ /**
49
+ * How gently each site is crawled, across every recipe this crawler runs:
50
+ * `delayMs` between request starts and `concurrency` requests in flight,
51
+ * per site, with `domains` for site-specific rules. Defaults to
52
+ * `access.throttle`; without either, only each recipe's `limits` apply.
53
+ */
54
+ throttle?: ThrottleConfig;
55
+ /**
56
+ * How a request that fails in passing (a dropped connection, a timeout, a
57
+ * 503, a 429) is sent again, for recipes whose `limits.retry` says
58
+ * nothing: `{ attempts?, backoffMs?, maxDelayMs?, statuses? }`. Default:
59
+ * three tries, one then two seconds apart, `Retry-After` honoured.
60
+ */
61
+ retry?: RetryRule;
35
62
  /** Plugins `{ kind: 'plugin', name }` profiles refer to. */
36
63
  accessPlugins?: AccessPlugin[];
37
64
  /** Solvers recipes name in `session.captcha.solver` and `captcha` steps. */
@@ -50,6 +50,15 @@ export declare class RotatingRunner implements StepRunner {
50
50
  private note;
51
51
  runLeaf(step: Step, scope: ExtractionScope): Promise<void>;
52
52
  nextPage(next: PaginateNext, scope: ExtractionScope): Promise<NextPageResult>;
53
+ /**
54
+ * A runner for one parallel iteration, forked from whichever runner is
55
+ * current when it runs a step: after a rotation it forks again from the new
56
+ * one, since the old context is gone (or going). Blocks are noted and
57
+ * rotated like the main runner's.
58
+ *
59
+ * @returns The iteration's runner.
60
+ */
61
+ fork(): Promise<StepRunner>;
53
62
  elements(selector: string, scope: ExtractionScope): Promise<LiveElement[]>;
54
63
  rotate(error: BlockedError): Promise<boolean>;
55
64
  dispose(): Promise<void>;
@@ -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