@opencraw/mcp 0.1.45 → 0.1.47

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 (60) hide show
  1. package/README.md +27 -1
  2. package/dist/diff-tool/diff-tool.handler.d.ts +42 -0
  3. package/dist/diff-tool/index.d.ts +3 -0
  4. package/dist/docs-tool/docs-folder.repository.d.ts +24 -0
  5. package/dist/docs-tool/docs-root.client.d.ts +8 -0
  6. package/dist/docs-tool/docs-tool.handler.d.ts +21 -0
  7. package/dist/docs-tool/docs-topics.algorithm.d.ts +39 -0
  8. package/dist/docs-tool/index.d.ts +3 -0
  9. package/dist/index.d.ts +12 -1
  10. package/dist/index.esm.js +430 -1
  11. package/dist/list-tool/index.d.ts +3 -0
  12. package/dist/list-tool/list-recipes-tool.handler.d.ts +32 -0
  13. package/dist/main.d.ts +7 -0
  14. package/dist/probe-tool/index.d.ts +2 -0
  15. package/dist/probe-tool/probe-tool.handler.d.ts +27 -0
  16. package/dist/recipe-source/index.d.ts +3 -0
  17. package/dist/recipe-source/recipe-source.mapper.d.ts +21 -0
  18. package/dist/run-tool/index.d.ts +3 -0
  19. package/dist/run-tool/run-tool.handler.d.ts +51 -0
  20. package/dist/server/create-server.use-case.d.ts +11 -0
  21. package/dist/server/index.d.ts +2 -0
  22. package/dist/src/docs-tool/docs-folder.repository.d.ts +24 -0
  23. package/dist/src/docs-tool/docs-root.client.d.ts +8 -0
  24. package/dist/src/docs-tool/docs-tool.handler.d.ts +21 -0
  25. package/dist/src/docs-tool/docs-topics.algorithm.d.ts +39 -0
  26. package/dist/src/docs-tool/index.d.ts +3 -0
  27. package/dist/src/server/create-server.use-case.d.ts +1 -1
  28. package/dist/src/studio-tool/index.d.ts +2 -0
  29. package/dist/src/studio-tool/studio-command.client.d.ts +11 -0
  30. package/dist/src/studio-tool/studio-instance.repository.d.ts +22 -0
  31. package/dist/src/studio-tool/studio-tools.handler.d.ts +47 -0
  32. package/dist/studio-tool/index.d.ts +2 -0
  33. package/dist/studio-tool/studio-command.client.d.ts +11 -0
  34. package/dist/studio-tool/studio-instance.repository.d.ts +22 -0
  35. package/dist/studio-tool/studio-tools.handler.d.ts +47 -0
  36. package/dist/validate-tool/index.d.ts +3 -0
  37. package/dist/validate-tool/validate-tool.handler.d.ts +31 -0
  38. package/docs/how-it-works/01-recipes.md +698 -0
  39. package/docs/how-it-works/02-page-steps.md +672 -0
  40. package/docs/how-it-works/03-data-steps.md +813 -0
  41. package/docs/how-it-works/04-flow-steps.md +738 -0
  42. package/docs/how-it-works/05-templates-and-scope.md +428 -0
  43. package/docs/how-it-works/06-documents.md +1413 -0
  44. package/docs/how-it-works/07-mapping-and-transforms.md +699 -0
  45. package/docs/how-it-works/08-policies.md +405 -0
  46. package/docs/how-it-works/09-running.md +804 -0
  47. package/docs/how-it-works/README.md +225 -0
  48. package/docs/recipes/README.md +22 -0
  49. package/docs/recipes/access.md +440 -0
  50. package/docs/recipes/authoring.md +1503 -0
  51. package/docs/recipes/azure-durable.md +180 -0
  52. package/docs/recipes/captcha.md +204 -0
  53. package/docs/recipes/movies-example.md +12 -0
  54. package/docs/recipes/programmatic-authoring.md +101 -0
  55. package/docs/recipes/quick-guide.md +157 -0
  56. package/docs/recipes/vehicles-example.md +9 -0
  57. package/docs/recipes/worker-mode.md +221 -0
  58. package/docs/studio/manual.md +312 -0
  59. package/docs/studio/pdf-agreements.md +150 -0
  60. package/package.json +29 -4
@@ -0,0 +1,225 @@
1
+ <p align="center">
2
+ <img src="../assets/opencraw-logo.svg" alt="OpenCraw" width="360">
3
+ </p>
4
+
5
+ # How OpenCraw works
6
+
7
+ This guide follows data through the engine: which page or document it sees, what each step keeps, how a
8
+ value changes transform by transform, and why the record ends up the way it does. Every example runs against a
9
+ real site, API or document, and every screenshot, scope, value table and trace here was produced by running it
10
+ ([how](#how-this-guide-is-made)).
11
+
12
+ [authoring.md](../recipes/authoring.md) is the reference: every key, step and transform. This guide is the
13
+ explanation: read it to understand the engine, or when a recipe does something you didn't expect.
14
+
15
+ ## The parts
16
+
17
+ | Part | What it explains |
18
+ |---|---|
19
+ | **[1. A run, end to end](#a-run-end-to-end)** (this page) | One recipe on a real site, from the page to the record. |
20
+ | [2. Recipes](01-recipes.md) | The output recipe (fields, types, coercion, keys) and the input recipe (web or api mode, sessions). |
21
+ | [3. Page steps](02-page-steps.md) | Driving a browser: `goto`, `click`, `fill`, `press`, `select`, `scroll`, `wait`, `screenshot`, `evaluate`, `captcha`. |
22
+ | [4. Data steps](03-data-steps.md) | Getting values: `extract` (CSS, XPath, JSONPath, regex, tables), `request`, `set`, `hook`. |
23
+ | [5. Flow steps](04-flow-steps.md) | Loops and decisions: `forEach`, `paginate`, `if`, `collect`, `emit`. |
24
+ | [6. Templates and scope](05-templates-and-scope.md) | What a step can see, and the `{{ }}` language. |
25
+ | [7. Documents, under the hood](06-documents.md) | How PDFs, spreadsheets, CSV, PowerPoint, Word, HTML tables, XML, YAML and Markdown are read, down to the algorithms. |
26
+ | [8. Mapping and transforms](07-mapping-and-transforms.md) | From scope to record: `from`, `each`, and every transform with real before and after values. |
27
+ | [9. Policies](08-policies.md) | Missing values, values that can't be converted, transforms that fail: what's dropped and what stops a run. |
28
+ | [10. Running](09-running.md) | Sessions, access and proxies, blocks, throttling, retries, captchas, concurrency, worker mode, change detection, events. |
29
+
30
+ ## A run, end to end
31
+
32
+ The recipe: every book in the Mystery category of [books.toscrape.com](https://books.toscrape.com), a site
33
+ built for scraping practice, read in a browser. Two files:
34
+
35
+ - [`book.output.json`](recipes/books-list/book.output.json), the **output recipe**, says what a record is:
36
+ `url` (the key), `title`, `price` (in GBP), `stars`, `inStock`, `category`, `page`.
37
+ - [`mystery-web.input.json`](recipes/books-list/mystery-web.input.json), the **input recipe**, says where the
38
+ data is and how to turn it into records.
39
+
40
+ ```json
41
+ "steps": [
42
+ { "type": "goto", "url": "{{start.url}}" },
43
+ { "type": "set", "id": "starWords", "value": [{ "word": "One", "n": 1 }, "…", { "word": "Five", "n": 5 }] },
44
+ { "type": "paginate", "next": { "selector": "li.next a" }, "maxPages": 2, "steps": [
45
+ { "type": "extract", "id": "books", "selector": "article.product_pod", "kind": "css", "take": "html", "many": true },
46
+ { "type": "forEach", "over": "books", "as": "book", "emit": true, "steps": [
47
+ { "type": "extract", "id": "title", "from": "book", "selector": "h3 a", "kind": "css", "take": "attr:title" },
48
+ { "type": "extract", "id": "link", "from": "book", "selector": "h3 a", "kind": "css", "take": "attr:href" },
49
+ { "type": "extract", "id": "price", "from": "book", "selector": ".price_color", "kind": "css", "take": "text" },
50
+ { "type": "extract", "id": "rating", "from": "book", "selector": "p.star-rating", "kind": "css", "take": "attr:class" },
51
+ { "type": "extract", "id": "stock", "from": "book", "selector": ".availability", "kind": "css", "take": "text" }
52
+ ]}
53
+ ]}
54
+ ]
55
+ ```
56
+
57
+ ### 1. What the engine sees
58
+
59
+ `goto` opens the category page in Chromium. `extract` with `many: true` takes every element matching
60
+ `article.product_pod`, as HTML (`take: "html"`): one fragment per book. Inside `forEach`, each fragment is the
61
+ `book`, and the five `extract`s read from it (`from: "book"`), not from the page.
62
+
63
+ <!-- capture:books-list screenshot alt=The_Mystery_category,_with_what_each_extract_selects -->
64
+ ![The Mystery category, with what each extract selects](../assets/how-it-works/books-list.png)
65
+ <!-- /capture -->
66
+
67
+ ### 2. What the steps keep: the scope
68
+
69
+ Every step with an `id` binds its value under that name. When `forEach` emits, the engine takes a snapshot of
70
+ everything in scope: the loop's own values, then its parents' (`page`, `starWords`, `vars`, `start`). That
71
+ snapshot is what the mapping reads. For the first book:
72
+
73
+ <!-- capture:books-list scope ids=title,link,price,rating,stock,page,vars -->
74
+ ```json
75
+ {
76
+ "title": "Sharp Objects",
77
+ "link": "../../../sharp-objects_997/index.html",
78
+ "price": "£47.82",
79
+ "rating": "star-rating Four",
80
+ "stock": "In stock",
81
+ "page": {
82
+ "url": "https://books.toscrape.com/catalogue/category/books/mystery_3/index.html",
83
+ "number": 1
84
+ },
85
+ "vars": {
86
+ "category": "Mystery"
87
+ }
88
+ }
89
+ ```
90
+ <!-- /capture -->
91
+
92
+ `book` (the fragment) and `books` (all of them) are in the snapshot too, left out here for length.
93
+
94
+ ### 3. From scope to record: the mapping
95
+
96
+ Each output field has a rule: where to read (`from`) and what to do with it (`transform`). The table shows each
97
+ field's value as read, after each transform, and as it lands in the record, after coercion to the field's type:
98
+
99
+ <!-- capture:books-list mapping fields=url,price,stars,inStock,page -->
100
+ | Field | Step | Value |
101
+ |---|---|---|
102
+ | `url` | read | `"../../../sharp-objects_997/index.html"` |
103
+ | | `absoluteUrl` | `"https://books.toscrape.com/catalogue/sharp-objects_997/index.html"` |
104
+ | | **field** | `"https://books.toscrape.com/catalogue/sharp-objects_997/index.html"` |
105
+ | `price` | read | `"£47.82"` |
106
+ | | `currency` | `{"amount":47.82,"currency":"GBP"}` |
107
+ | | **field** | `{"amount":47.82,"currency":"GBP"}` |
108
+ | `stars` | read | `"star-rating Four"` |
109
+ | | `regex` | `"Four"` |
110
+ | | `lookup` | `4` |
111
+ | | **field** | `4` |
112
+ | `inStock` | read | `"In stock"` |
113
+ | | `trim` | `"In stock"` |
114
+ | | `boolean` | `true` |
115
+ | | **field** | `true` |
116
+ | `page` | read | `1` |
117
+ | | **field** | `1` |
118
+ <!-- /capture -->
119
+
120
+ Three things happen here that aren't obvious from the recipe:
121
+
122
+ - `absoluteUrl` resolves the link against the page it came from, so `../../../sharp-objects_997/index.html`
123
+ becomes a full URL.
124
+ - `currency` reads the `£` sign and returns `GBP`. When a text has no sign, the field's `currency` fills it in
125
+ at coercion.
126
+ - `stars` takes two steps: `regex` pulls the word out of the class (`Four`), then `lookup` finds it in the
127
+ `starWords` table the recipe `set` at the start, and returns its `n`.
128
+
129
+ ### 4. The record
130
+
131
+ <!-- capture:books-list record -->
132
+ ```json
133
+ {
134
+ "url": "https://books.toscrape.com/catalogue/sharp-objects_997/index.html",
135
+ "title": "Sharp Objects",
136
+ "price": {
137
+ "amount": 47.82,
138
+ "currency": "GBP"
139
+ },
140
+ "stars": 4,
141
+ "inStock": true,
142
+ "category": "Mystery",
143
+ "page": 1
144
+ }
145
+ ```
146
+ <!-- /capture -->
147
+
148
+ The output declares `url` as the key: a second record with the same URL would be dropped as a duplicate.
149
+
150
+ ### 5. The route: the trace
151
+
152
+ `traceLine` turns the engine's events into this, one line per event, indented by step depth. `⇢` is a page,
153
+ `·` a finished step with its path in the recipe (`steps.2.steps.1.steps.0` is the first step inside the loop
154
+ inside `paginate`), `✚` a record with its key:
155
+
156
+ <!-- capture:books-list trace lines=16 -->
157
+ ```text
158
+ ▶ mystery-web (web)
159
+ ⇄ access direct (direct)
160
+ ⇢ page 1 https://books.toscrape.com/catalogue/category/books/mystery_3/index.html
161
+ · steps.0 goto … ms
162
+ · steps.1 set starWords … ms
163
+ · steps.2.steps.0 extract books … ms
164
+ · steps.2.steps.1.steps.0 extract title … ms
165
+ · steps.2.steps.1.steps.1 extract link … ms
166
+ · steps.2.steps.1.steps.2 extract price … ms
167
+ · steps.2.steps.1.steps.3 extract rating … ms
168
+ · steps.2.steps.1.steps.4 extract stock … ms
169
+ ✚ record ["https://books.toscrape.com/catalogue/sharp-objects_997/index.html"]
170
+ · steps.2.steps.1.steps.0 extract title … ms
171
+ · steps.2.steps.1.steps.1 extract link … ms
172
+ · steps.2.steps.1.steps.2 extract price … ms
173
+ · steps.2.steps.1.steps.3 extract rating … ms
174
+ …
175
+ ```
176
+ <!-- /capture -->
177
+
178
+ <!-- capture:books-list summary -->
179
+ ```text
180
+ mystery-web: 32 emitted, 0 rejected, 0 duplicates, 2 pages
181
+ ```
182
+ <!-- /capture -->
183
+
184
+ 32 books over two pages: `paginate` clicked "next" once, then found no "next" on page 2 and stopped.
185
+
186
+ ## How the pipeline fits together
187
+
188
+ ```mermaid
189
+ flowchart LR
190
+ A[recipe files] -->|load + validate| B[recipe set]
191
+ B -->|bind: every output field fed| C[runs: one per input recipe and start]
192
+ C --> D{mode}
193
+ D -->|web| E[browser page]
194
+ D -->|api| F[HTTP session]
195
+ E & F --> G[steps: goto, extract, forEach, paginate…]
196
+ G -->|emit| H[scope snapshot]
197
+ H -->|mapping rules + transforms| I[raw values]
198
+ I -->|coerce + validate| J[record]
199
+ J -->|missing / failure policy| K{kept?}
200
+ K -->|key seen| L[duplicate]
201
+ K -->|new| M[sink]
202
+ ```
203
+
204
+ Each box is a part of this guide. The same pipeline runs whether the source is a web page, an API, or a PDF
205
+ fetched by URL: the document readers (part 7) turn every format into something `extract` can select from.
206
+
207
+ ## How this guide is made
208
+
209
+ Each example's recipes live in [`recipes/`](recipes), one folder per scene. The capture script,
210
+ [`capture/capture.mjs`](capture/capture.mjs), does the following for each scene:
211
+
212
+ - runs its recipes with `CrawlOptions.debug`, so every `record:emit` event carries the scope snapshot and each
213
+ field's mapping trace;
214
+ - saves the records, the trace and the summary to [`captures/`](captures);
215
+ - takes the screenshots, outlining what each selector matches;
216
+ - rewrites the regions of these pages marked `<!-- capture:… -->`.
217
+
218
+ ```sh
219
+ npm run docs:capture # every scene, against the live sites (a few minutes)
220
+ npm run docs:capture -- --only books-list
221
+ npm run docs:capture -- --render # rewrite the pages from the saved captures, offline
222
+ ```
223
+
224
+ CI loads and binds every scene's recipes on each pull request, so a change to the engine that breaks one fails
225
+ there. The capture itself runs on demand, since the sites are live.
@@ -0,0 +1,22 @@
1
+ <p align="center">
2
+ <img src="../assets/opencraw-logo.svg" alt="OpenCraw" width="360">
3
+ </p>
4
+
5
+ # Recipe documentation
6
+
7
+ | Document | Read it when |
8
+ |---|---|
9
+ | [authoring.md](./authoring.md) | You are writing a recipe and need every field, step, transform and rule, with the reasoning behind each. The reference. |
10
+ | [quick-guide.md](./quick-guide.md) | You want the short version: one page, the common shapes. |
11
+ | [programmatic-authoring.md](./programmatic-authoring.md) | You generate many similar recipes (per shop, region or login) and want to build them as objects in code instead of typing JSON. |
12
+ | [How OpenCraw works](../how-it-works/README.md) | You want to see what the engine does with each part of a recipe: every step, template, transform and policy run on real sites, APIs and documents, with screenshots, traces and the records they gave, and the engineering of the PDF, spreadsheet, PowerPoint and Word readers. |
13
+ | [examples/](../../examples/README.md) | You want recipes that run: every example, what it shows and how to start it. |
14
+ | [examples/movies](../../examples/movies/README.md), [examples/filmography](../../examples/filmography/README.md) | You want to see real recipes end to end: five movies from Netflix and IMDb, then movie → lead actor → filmography on TMDB and Letterboxd, with the decisions, the traces and the bugs met on the way. |
15
+ | [examples/vehicles](../../examples/vehicles/README.md) | You want the harder case: configurator data behind an inline `carPath`, three pages per model, nested loops emitting one record per priced combination, and the two engine features it forced (`regex` extracts, templated selectors). |
16
+ | [access.md](./access.md) | A site blocks the network you crawl from: proxies, provider presets, where credentials go, and the plugin seam. |
17
+ | [worker-mode.md](./worker-mode.md) | You crawl thousands of items (one report per date, region and filter set) from a queue: windows that stay busy, keep their page between items, and grow or shrink with the site's health. |
18
+ | [azure-durable.md](./azure-durable.md) | You want one OpenCraw service every crawler calls over HTTP: the Azure Durable Functions host, its container, `host.json`, scaling, security, and a caller that keeps a warm pool busy. |
19
+ | [captcha.md](./captcha.md) | A site shows a captcha: whether to solve it, the four places a challenge appears, the solve loop, the budget, and writing a solver. |
20
+ | [../requirements.md](../requirements.md) | You need the specification the engine is checked against. |
21
+
22
+ The JSON Schemas for editor validation are in `packages/core/schemas/`; point a recipe's (or an access config's) `$schema` at them.