crawlforge-extractors 1.5.2 → 1.5.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.
- package/README.md +2 -0
- package/package.json +1 -1
- package/src/body.js +11 -2
- package/src/jsonPath.js +1 -1
- package/src/templates.js +3 -1
package/README.md
CHANGED
|
@@ -40,6 +40,8 @@ const template = registry.get('shopify-product');
|
|
|
40
40
|
const url = 'https://shop.example.com/products/some-handle';
|
|
41
41
|
const fetchUrl = template.resolveUrl ? template.resolveUrl(url) : url;
|
|
42
42
|
|
|
43
|
+
// Apply your SSRF policy to fetchUrl (the RETURNED url) and to any redirect the
|
|
44
|
+
// fetch follows — not just to `url`. resolveUrl/listUrl may rewrite the target.
|
|
43
45
|
const body = await (await fetch(fetchUrl)).text();
|
|
44
46
|
const result = await registry.run('shopify-product', body, url, fetchUrl);
|
|
45
47
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crawlforge-extractors",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"description": "Extraction logic shared by the CrawlForge MCP server and REST API — scrape templates, charset-correct capped body reading, structural fingerprinting, and embedded-state extraction. One implementation, so the two surfaces cannot drift apart.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
package/src/body.js
CHANGED
|
@@ -70,9 +70,18 @@ export async function readBody(response, options = {}) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// Only the byte-count guard needs a stream. Responses that are already
|
|
73
|
-
// buffered (and test doubles)
|
|
73
|
+
// buffered (and test doubles) have no reader to meter, but a server can omit
|
|
74
|
+
// or lie about Content-Length, so enforce the cap on the read result too.
|
|
74
75
|
if (!response.body || typeof response.body.getReader !== 'function') {
|
|
75
|
-
|
|
76
|
+
const text = await response.text();
|
|
77
|
+
const size = Buffer.byteLength(text, 'utf8');
|
|
78
|
+
if (size > maxBytes) {
|
|
79
|
+
throw new BodyTooLargeError(
|
|
80
|
+
`Response body too large: ${size} bytes exceeds limit of ${maxBytes} bytes`,
|
|
81
|
+
{ limit: maxBytes, size }
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
return text;
|
|
76
85
|
}
|
|
77
86
|
|
|
78
87
|
const reader = response.body.getReader();
|
package/src/jsonPath.js
CHANGED
|
@@ -66,7 +66,7 @@ export function selectJsonPath(root, path) {
|
|
|
66
66
|
for (const segment of segments) {
|
|
67
67
|
const container = current !== null && typeof current === 'object';
|
|
68
68
|
const key = Array.isArray(current) ? Number(segment) : segment;
|
|
69
|
-
if (!container || !(key
|
|
69
|
+
if (!container || !Object.hasOwn(current, key)) {
|
|
70
70
|
const at = walked.length === 0 ? 'the result' : `"${walked.join('.')}"`;
|
|
71
71
|
throw new Error(
|
|
72
72
|
`Path "${path}" not found: ${at} has no "${segment}" (${describeOptions(current)})`
|
package/src/templates.js
CHANGED
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Templates do NOT make network calls. The caller fetches the page and passes
|
|
16
16
|
* the body in; that keeps SSRF policy, timeouts and billing with the surface
|
|
17
|
-
* that owns them.
|
|
17
|
+
* that owns them. resolveUrl/listUrl rewrite or build the target, so the caller
|
|
18
|
+
* must apply its SSRF policy to the URL they RETURN (and to every redirect the
|
|
19
|
+
* fetch follows), not just to the URL the caller started with.
|
|
18
20
|
*
|
|
19
21
|
* Two optional hooks let a template read a machine-readable endpoint instead of
|
|
20
22
|
* scraping the rendered page, without taking the fetch into its own hands:
|