rusteerio 0.0.0-stage → 0.1.0
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/CONTRIBUTING.md +73 -0
- package/Cargo.lock +1051 -0
- package/Cargo.toml +36 -0
- package/LICENSE +21 -0
- package/README.md +252 -2
- package/RELEASING.md +138 -0
- package/THIRD_PARTY_NOTICES.md +7443 -0
- package/build.rs +3 -0
- package/dist/chunk-DKULX6VO.mjs +3185 -0
- package/dist/chunk-DKULX6VO.mjs.map +1 -0
- package/dist/chunk-OHPGEY3E.mjs +32 -0
- package/dist/chunk-OHPGEY3E.mjs.map +1 -0
- package/dist/index.cjs +3230 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +113 -0
- package/dist/index.d.ts +113 -0
- package/dist/index.mjs +38 -0
- package/dist/index.mjs.map +1 -0
- package/dist/slim.cjs +2441 -0
- package/dist/slim.cjs.map +1 -0
- package/dist/slim.d.mts +11 -0
- package/dist/slim.d.ts +11 -0
- package/dist/slim.mjs +19 -0
- package/dist/slim.mjs.map +1 -0
- package/dist/types-BP6TpxE5.d.mts +532 -0
- package/dist/types-BP6TpxE5.d.ts +532 -0
- package/dist/utils.cjs +61 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.d.mts +13 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.mjs +17 -0
- package/dist/utils.mjs.map +1 -0
- package/native/src/arena.rs +1781 -0
- package/native/src/attributes.rs +923 -0
- package/native/src/codec.rs +172 -0
- package/native/src/diagnostics.rs +1249 -0
- package/native/src/extraction.rs +226 -0
- package/native/src/gc.rs +166 -0
- package/native/src/hook_sink.rs +727 -0
- package/native/src/hook_stream.rs +513 -0
- package/native/src/lib.rs +2095 -0
- package/native/src/location.rs +392 -0
- package/native/src/runtime.rs +189 -0
- package/native/src/selector.rs +1287 -0
- package/native/src/sink.rs +261 -0
- package/native/src/stream.rs +334 -0
- package/native/src/tokenizer.rs +355 -0
- package/native/src/tracker.rs +232 -0
- package/native/vendor/README.md +25 -0
- package/native/vendor/html5ever-0.35.0/.cargo_vcs_info.json +6 -0
- package/native/vendor/html5ever-0.35.0/Cargo.lock +828 -0
- package/native/vendor/html5ever-0.35.0/Cargo.toml +85 -0
- package/native/vendor/html5ever-0.35.0/LICENSE-APACHE +201 -0
- package/native/vendor/html5ever-0.35.0/LICENSE-MIT +25 -0
- package/native/vendor/html5ever-0.35.0/README.md +60 -0
- package/native/vendor/html5ever-0.35.0/benches/html5ever.rs +86 -0
- package/native/vendor/html5ever-0.35.0/data/bench/lipsum-zh.html +19 -0
- package/native/vendor/html5ever-0.35.0/data/bench/lipsum.html +40 -0
- package/native/vendor/html5ever-0.35.0/data/bench/medium-fragment.html +24 -0
- package/native/vendor/html5ever-0.35.0/data/bench/small-fragment.html +7 -0
- package/native/vendor/html5ever-0.35.0/data/bench/strong.html +1 -0
- package/native/vendor/html5ever-0.35.0/data/bench/tiny-fragment.html +1 -0
- package/native/vendor/html5ever-0.35.0/examples/arena.rs +356 -0
- package/native/vendor/html5ever-0.35.0/examples/capi/tokenize.c +74 -0
- package/native/vendor/html5ever-0.35.0/examples/noop-tokenize.rs +47 -0
- package/native/vendor/html5ever-0.35.0/examples/noop-tree-builder.rs +132 -0
- package/native/vendor/html5ever-0.35.0/examples/print-tree-actions.rs +185 -0
- package/native/vendor/html5ever-0.35.0/examples/tokenize.rs +111 -0
- package/native/vendor/html5ever-0.35.0/src/driver.rs +148 -0
- package/native/vendor/html5ever-0.35.0/src/lib.rs +30 -0
- package/native/vendor/html5ever-0.35.0/src/macros.rs +34 -0
- package/native/vendor/html5ever-0.35.0/src/serialize/mod.rs +255 -0
- package/native/vendor/html5ever-0.35.0/src/tokenizer/char_ref/mod.rs +451 -0
- package/native/vendor/html5ever-0.35.0/src/tokenizer/interface.rs +99 -0
- package/native/vendor/html5ever-0.35.0/src/tokenizer/mod.rs +2305 -0
- package/native/vendor/html5ever-0.35.0/src/tokenizer/states.rs +97 -0
- package/native/vendor/html5ever-0.35.0/src/tree_builder/data.rs +170 -0
- package/native/vendor/html5ever-0.35.0/src/tree_builder/mod.rs +1848 -0
- package/native/vendor/html5ever-0.35.0/src/tree_builder/rules.rs +1494 -0
- package/native/vendor/html5ever-0.35.0/src/tree_builder/tag_sets.rs +114 -0
- package/native/vendor/html5ever-0.35.0/src/tree_builder/types.rs +91 -0
- package/native/vendor/html5ever-0.35.0/src/util/str.rs +52 -0
- package/native/vendor/html5gum-0.8.4/Cargo.lock +1006 -0
- package/native/vendor/html5gum-0.8.4/Cargo.toml +109 -0
- package/native/vendor/html5gum-0.8.4/LICENSE +19 -0
- package/native/vendor/html5gum-0.8.4/README.md +110 -0
- package/native/vendor/html5gum-0.8.4/benches/files/0050.html +2131 -0
- package/native/vendor/html5gum-0.8.4/benches/files/6D30.html +1004 -0
- package/native/vendor/html5gum-0.8.4/benches/files.rs +32 -0
- package/native/vendor/html5gum-0.8.4/benches/patterns.rs +31 -0
- package/native/vendor/html5gum-0.8.4/src/arrayvec.rs +29 -0
- package/native/vendor/html5gum-0.8.4/src/char_validator.rs +134 -0
- package/native/vendor/html5gum-0.8.4/src/emitters/callback.rs +676 -0
- package/native/vendor/html5gum-0.8.4/src/emitters/default.rs +207 -0
- package/native/vendor/html5gum-0.8.4/src/emitters/emitter.rs +344 -0
- package/native/vendor/html5gum-0.8.4/src/emitters/html5ever.rs +306 -0
- package/native/vendor/html5gum-0.8.4/src/emitters/mod.rs +25 -0
- package/native/vendor/html5gum-0.8.4/src/entities.rs +3342 -0
- package/native/vendor/html5gum-0.8.4/src/error.rs +96 -0
- package/native/vendor/html5gum-0.8.4/src/htmlstring.rs +130 -0
- package/native/vendor/html5gum-0.8.4/src/lib.rs +66 -0
- package/native/vendor/html5gum-0.8.4/src/machine.rs +2136 -0
- package/native/vendor/html5gum-0.8.4/src/machine_helper.rs +277 -0
- package/native/vendor/html5gum-0.8.4/src/read_helper.rs +244 -0
- package/native/vendor/html5gum-0.8.4/src/reader.rs +440 -0
- package/native/vendor/html5gum-0.8.4/src/span.rs +98 -0
- package/native/vendor/html5gum-0.8.4/src/state.rs +16 -0
- package/native/vendor/html5gum-0.8.4/src/testutils.rs +71 -0
- package/native/vendor/html5gum-0.8.4/src/tokenizer.rs +189 -0
- package/native/vendor/html5gum-0.8.4/src/utils.rs +88 -0
- package/native/vendor/markup5ever-0.35.0/.cargo_vcs_info.json +6 -0
- package/native/vendor/markup5ever-0.35.0/Cargo.toml +45 -0
- package/native/vendor/markup5ever-0.35.0/LICENSE-APACHE +201 -0
- package/native/vendor/markup5ever-0.35.0/LICENSE-MIT +25 -0
- package/native/vendor/markup5ever-0.35.0/interface/mod.rs +422 -0
- package/native/vendor/markup5ever-0.35.0/interface/tree_builder.rs +280 -0
- package/native/vendor/markup5ever-0.35.0/lib.rs +59 -0
- package/native/vendor/markup5ever-0.35.0/serialize.rs +77 -0
- package/native/vendor/markup5ever-0.35.0/util/buffer_queue.rs +333 -0
- package/native/vendor/markup5ever-0.35.0/util/smallcharset.rs +88 -0
- package/package.json +137 -4
- package/rusteerio.darwin-arm64.node +0 -0
- package/rusteerio.darwin-x64.node +0 -0
- package/rusteerio.linux-arm64-gnu.node +0 -0
- package/rusteerio.linux-arm64-musl.node +0 -0
- package/rusteerio.linux-x64-gnu.node +0 -0
- package/rusteerio.linux-x64-musl.node +0 -0
- package/rusteerio.win32-arm64-msvc.node +0 -0
- package/rusteerio.win32-x64-msvc.node +0 -0
- package/scripts/check-native.mjs +15 -0
- package/scripts/check-release.mjs +235 -0
- package/scripts/fix-declarations.mjs +27 -0
- package/scripts/generate-notices.mjs +176 -0
- package/scripts/notices/README.md +13 -0
- package/scripts/notices/cheerio-MIT.txt +21 -0
- package/scripts/notices/html5ever-AUTHORS.txt +20 -0
- package/scripts/notices/html5ever-COPYRIGHT.txt +8 -0
- package/scripts/notices/html5ever-LICENSE-APACHE.txt +201 -0
- package/scripts/notices/html5ever-LICENSE-MIT.txt +25 -0
- package/scripts/notices/napi-rs-MIT.txt +43 -0
- package/scripts/notices/neon-MIT.txt +19 -0
- package/scripts/notices/runtime-01c266bced4a434da005.txt +3 -0
- package/scripts/notices/runtime-0a54e647fe54104658b5.txt +14 -0
- package/scripts/notices/runtime-0b74dfa0bcee5c420c6b.txt +25 -0
- package/scripts/notices/runtime-0d542e0c8804e39aa7f3.txt +177 -0
- package/scripts/notices/runtime-0f96a83840e146e43c0e.txt +21 -0
- package/scripts/notices/runtime-123a331b5dbf04c30097.txt +25 -0
- package/scripts/notices/runtime-172020dbfd5b53a226df.txt +34 -0
- package/scripts/notices/runtime-209fbbe0ad52d9235e37.txt +26 -0
- package/scripts/notices/runtime-23f18e03dc49df91622f.txt +23 -0
- package/scripts/notices/runtime-268872b9816f90fd8e85.txt +220 -0
- package/scripts/notices/runtime-35242e7a83f69875e6ed.txt +176 -0
- package/scripts/notices/runtime-378f5840b258e2779c39.txt +25 -0
- package/scripts/notices/runtime-4108245a1f2df9d4e94d.txt +25 -0
- package/scripts/notices/runtime-4455bf75a91154108304.txt +21 -0
- package/scripts/notices/runtime-553fffcd9b1cb158bc3e.txt +13 -0
- package/scripts/notices/runtime-62c7a1e35f56406896d7.txt +176 -0
- package/scripts/notices/runtime-6485b8ed310d3f0340bf.txt +25 -0
- package/scripts/notices/runtime-69fef7b0f322a6555415.txt +201 -0
- package/scripts/notices/runtime-6df43f6f4b5d4587f3d8.txt +187 -0
- package/scripts/notices/runtime-799e9ca9d179295ef372.txt +24 -0
- package/scripts/notices/runtime-7b63ecd5f1902af1b637.txt +25 -0
- package/scripts/notices/runtime-7bc7d5de2a0b793d4e1a.txt +21 -0
- package/scripts/notices/runtime-7e12e5df4bae12cb2158.txt +24 -0
- package/scripts/notices/runtime-861399f8c21c042b1105.txt +12 -0
- package/scripts/notices/runtime-8ada45cd9f843acf64e4.txt +201 -0
- package/scripts/notices/runtime-90eb64f0279b0d9432ac.txt +12 -0
- package/scripts/notices/runtime-a60eea817514531668d7.txt +201 -0
- package/scripts/notices/runtime-a7c936ff1ed8fa340172.txt +25 -0
- package/scripts/notices/runtime-ab6eec6caf0fa5775e41.txt +275 -0
- package/scripts/notices/runtime-b1181a40b2a7b25cf66f.txt +19 -0
- package/scripts/notices/runtime-b71bd43a069ca0641a9e.txt +25 -0
- package/scripts/notices/runtime-c16f8dcf1a368b83be78.txt +201 -0
- package/scripts/notices/runtime-c2cfccb812fe482101a8.txt +21 -0
- package/scripts/notices/runtime-e99d88d232bf57d70f0f.txt +25 -0
- package/scripts/notices/runtime-f32fb3b417a194167cfa.txt +9 -0
- package/scripts/notices/runtime-f5062c9a188d81dfe66b.txt +39 -0
- package/scripts/notices/runtime-f9070a832833fa7488fd.txt +4 -0
- package/scripts/notices/runtime-fab3dd6bdab226f1c086.txt +373 -0
- package/scripts/notices/runtime-fba3318d0c600380177e.txt +62 -0
- package/scripts/notices/runtime-ff8f68cb076caf8cefe7.txt +25 -0
- package/scripts/notices/runtime-ff92bed461f50338dd70.txt +74 -0
- package/scripts/notices/runtime.json +1632 -0
- package/scripts/notices/sources.json +555 -0
- package/scripts/notices/xray-MIT.txt +21 -0
- package/src/attributes.ts +271 -0
- package/src/codec.ts +74 -0
- package/src/dom.ts +87 -0
- package/src/extract-plan.ts +97 -0
- package/src/extract.ts +130 -0
- package/src/hooks.ts +220 -0
- package/src/index.ts +223 -0
- package/src/io.ts +491 -0
- package/src/native.ts +135 -0
- package/src/nodes.ts +290 -0
- package/src/parse.ts +53 -0
- package/src/parser-types.ts +157 -0
- package/src/router.ts +327 -0
- package/src/selection.ts +566 -0
- package/src/slim.ts +12 -0
- package/src/transport.ts +104 -0
- package/src/types.ts +186 -0
- package/src/utils.ts +29 -0
- package/src/workers.ts +21 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +16 -0
package/Cargo.toml
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "rusteerio"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
description = "Rust HTML5 parser and CSS selector engine for the rusteerio Node.js package"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
|
|
8
|
+
[lib]
|
|
9
|
+
crate-type = ["cdylib", "rlib"]
|
|
10
|
+
path = "native/src/lib.rs"
|
|
11
|
+
|
|
12
|
+
[dependencies]
|
|
13
|
+
ego-tree = "0.10"
|
|
14
|
+
html5ever = "0.35"
|
|
15
|
+
html5gum = "0.8.4"
|
|
16
|
+
url = "2"
|
|
17
|
+
napi = { version = "2", default-features = false, features = ["napi8", "async"] }
|
|
18
|
+
napi-derive = "2"
|
|
19
|
+
rayon = "1.10"
|
|
20
|
+
scraper = { version = "0.24", default-features = false, features = ["atomic", "deterministic"] }
|
|
21
|
+
serde = { version = "1", features = ["derive"] }
|
|
22
|
+
serde_json = { version = "1", features = ["preserve_order"] }
|
|
23
|
+
|
|
24
|
+
[build-dependencies]
|
|
25
|
+
napi-build = "2"
|
|
26
|
+
|
|
27
|
+
[profile.release]
|
|
28
|
+
lto = "thin"
|
|
29
|
+
codegen-units = 1
|
|
30
|
+
|
|
31
|
+
# Observer hooks retain the upstream HTML5 algorithm while exposing the actual
|
|
32
|
+
# open-element stack to user-supplied parse5-compatible tree adapters.
|
|
33
|
+
[patch.crates-io]
|
|
34
|
+
html5ever = { path = "native/vendor/html5ever-0.35.0" }
|
|
35
|
+
markup5ever = { path = "native/vendor/markup5ever-0.35.0" }
|
|
36
|
+
html5gum = { path = "native/vendor/html5gum-0.8.4" }
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Context.dev and rusteerio contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,253 @@
|
|
|
1
|
-
#
|
|
1
|
+
# rusteerio
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Heavily inspired by [Cheerio](https://cheerio.js.org/)** and its familiar,
|
|
4
|
+
flexible HTML API. Built by the **[context.dev team](https://context.dev)**.
|
|
5
|
+
|
|
6
|
+
[](https://github.com/context-dot-dev/rusteerio/actions/workflows/ci.yml)
|
|
7
|
+
[](test/upstream/README.md)
|
|
8
|
+
[](https://www.npmjs.com/package/rusteerio)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](https://context.dev)
|
|
11
|
+
[](package.json)
|
|
12
|
+
|
|
13
|
+
Cheerio's interface, a Rust HTML/XML engine, and configurable parallel workers.
|
|
14
|
+
Rusteerio keeps the DOM, parsing, selectors, mutations and serialization in a
|
|
15
|
+
native addon, with **zero npm runtime dependencies**. TypeScript supplies the
|
|
16
|
+
API, stable node views, user callbacks and Node.js I/O.
|
|
17
|
+
|
|
18
|
+
## Why this exists
|
|
19
|
+
|
|
20
|
+
Cheerio makes server-side HTML processing approachable. We wanted to keep that
|
|
21
|
+
experience while making large scraping and extraction workloads faster and
|
|
22
|
+
giving applications a way to move CPU work off the JavaScript main thread.
|
|
23
|
+
|
|
24
|
+
Rusteerio combines a familiar synchronous API with additional async batch APIs.
|
|
25
|
+
Use the synchronous API for ordinary document work, or set a worker limit when
|
|
26
|
+
processing many documents. The implementation is independent of Cheerio's
|
|
27
|
+
JavaScript engine; Cheerio is a development dependency used to check behavior.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
The npm package name is **`rusteerio`**:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npm install rusteerio
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Node.js **20.19 or newer** is required. Release tarballs bundle native binaries
|
|
38
|
+
for macOS arm64/x64, Linux glibc and musl arm64/x64, and Windows arm64/x64. Supported
|
|
39
|
+
platforms need no Rust installation and no install script. For a source checkout,
|
|
40
|
+
follow [Development](#development).
|
|
41
|
+
|
|
42
|
+
Linux GNU binaries require **glibc 2.28 or newer**. Musl binaries are tested on
|
|
43
|
+
**Alpine 3.24**. Linux also needs `libgcc_s.so.1`; minimal Alpine images can
|
|
44
|
+
install it with `apk add --no-cache libgcc`.
|
|
45
|
+
|
|
46
|
+
## Familiar Cheerio API
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import * as rusteerio from 'rusteerio';
|
|
50
|
+
|
|
51
|
+
const $ = rusteerio.load('<ul><li class="fruit">Apple</li></ul>');
|
|
52
|
+
|
|
53
|
+
$('li').addClass('fresh').attr('data-id', '1');
|
|
54
|
+
console.log($('.fruit').text()); // Apple
|
|
55
|
+
console.log($.html());
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
CommonJS works too:
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
const rusteerio = require('rusteerio');
|
|
62
|
+
const $ = rusteerio.load('<h1>Hello</h1>');
|
|
63
|
+
console.log($('h1').text());
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The API includes selectors and traversal, DOM insertion and removal, cloning,
|
|
67
|
+
attributes and properties, classes, styles, form values and serialization,
|
|
68
|
+
HTML/XML output, extraction, buffers, streams and URL loading. The
|
|
69
|
+
`rusteerio/slim` and `rusteerio/utils` entry points are included. TypeScript
|
|
70
|
+
declarations are self-contained, with ESM and CommonJS support.
|
|
71
|
+
|
|
72
|
+
XML parsing preserves case and supports CDATA:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
const $ = rusteerio.load('<Root><Item Id="1"/></Root>', { xml: true });
|
|
76
|
+
console.log($('Item').attr('Id')); // 1
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Parallel extraction and worker limits
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { configureWorkers, loadAsync, loadAll, extractAll, workerPoolInfo } from 'rusteerio';
|
|
83
|
+
|
|
84
|
+
// Set one process-wide CPU budget before starting asynchronous native work.
|
|
85
|
+
configureWorkers({ threads: 4 });
|
|
86
|
+
|
|
87
|
+
const $ = await loadAsync(largeHtml);
|
|
88
|
+
const documents = await loadAll(htmlPages, { threads: 4 });
|
|
89
|
+
|
|
90
|
+
const rows = await extractAll(htmlPages, {
|
|
91
|
+
title: 'h1',
|
|
92
|
+
headings: ['h2'],
|
|
93
|
+
links: [{ selector: 'a', value: 'href' }],
|
|
94
|
+
}, { threads: 4 });
|
|
95
|
+
|
|
96
|
+
console.log(workerPoolInfo()); // configured budget, active work and peak usage
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
| API | Result | Native work |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| `loadAsync(content, options?, isDocument?)` | `Promise<CheerioAPI>` | Parses off-thread |
|
|
102
|
+
| `loadAll(strings, options?, isDocument?)` | `Promise<CheerioAPI[]>` | Parses documents in parallel |
|
|
103
|
+
| `extractAll(strings, map, options?)` | Extracted objects in input order | Parses and extracts in parallel |
|
|
104
|
+
|
|
105
|
+
`configureWorkers({ threads: 4 })` (or `configureWorkers(4)`) caps active native
|
|
106
|
+
CPU work across concurrent batches, single-document async parsing and streams.
|
|
107
|
+
Set it before starting asynchronous native work. You can also set
|
|
108
|
+
`RUSTEERIO_THREADS=4` before starting Node. The default is available CPUs, capped
|
|
109
|
+
at 256. Configuration accepts integers from **1 to 256**; changing the budget
|
|
110
|
+
after work starts throws. Reapplying the same budget is allowed.
|
|
111
|
+
|
|
112
|
+
The per-batch `threads` option can lower a batch's parallelism within that shared
|
|
113
|
+
budget. Results preserve input order. `{ xml: true, threads: 4 }` works too.
|
|
114
|
+
`workerPoolInfo()` reports `threads`, `activeBatches`, `activeWorkers` and
|
|
115
|
+
`peakActiveWorkers`. The cap measures active CPU work; streams can have additional
|
|
116
|
+
parked threads waiting for input or a JavaScript callback.
|
|
117
|
+
|
|
118
|
+
Synchronous `load()`, queries, mutations and serialization **block the main
|
|
119
|
+
thread** while they run. The async APIs move heavy native work onto workers;
|
|
120
|
+
input copying, result decoding and user callbacks still run on the JavaScript
|
|
121
|
+
thread. Pure extraction maps, including nested maps, execute entirely in Rust
|
|
122
|
+
before their results are decoded. Callback extraction yields between groups of
|
|
123
|
+
elements, preserving closures and callback order. A single expensive callback
|
|
124
|
+
can still block the main thread.
|
|
125
|
+
|
|
126
|
+
## Streams and parser extensions
|
|
127
|
+
|
|
128
|
+
`stringStream()` incrementally parses strings in Rust. `decodeStream()` sniffs a
|
|
129
|
+
bounded encoding prefix and decodes incoming byte chunks; `fromURL()` feeds the
|
|
130
|
+
same parser as the response arrives. Writable backpressure bounds queued input.
|
|
131
|
+
HTTP loading supports redirects, abort signals, header/body timeouts and response
|
|
132
|
+
size limits. A caller can supply an Undici-compatible dispatcher.
|
|
133
|
+
|
|
134
|
+
Custom HTML5 `treeAdapter` methods participate in tree construction, and
|
|
135
|
+
`onParseError` receives parser diagnostics. The HTML/XML tokenizer's custom
|
|
136
|
+
`Tokenizer` constructor and callbacks are supported as well. User code runs in
|
|
137
|
+
JavaScript. Arbitrary custom tokenizers can refer to earlier source offsets, so
|
|
138
|
+
that path retains the source until completion. Source locations retain extra
|
|
139
|
+
metadata; avoid enabling them when you do not need it.
|
|
140
|
+
|
|
141
|
+
DOM views expose writable Cheerio-compatible fields and stable identity. Removed
|
|
142
|
+
branches stay usable while their selections or nodes are held; unreachable
|
|
143
|
+
branches can be reclaimed and their Rust storage reused. The optional `dom`
|
|
144
|
+
namespace exports constructors such as `dom.Element`, `dom.Text` and
|
|
145
|
+
`dom.Document`. Nodes imported from another DOM retain their original prototype.
|
|
146
|
+
Fresh Rust-backed nodes have Rusteerio's constructors: an `instanceof` check
|
|
147
|
+
against an independently installed `domhandler.Element` requires importing those
|
|
148
|
+
nodes or supplying a tree adapter that creates that class.
|
|
149
|
+
|
|
150
|
+
## Benchmarks
|
|
151
|
+
|
|
152
|
+
These measurements compare **Cheerio 1.2.0** against rusteerio on a Linux x64
|
|
153
|
+
**VM with 8 vCPUs and 16 GB RAM**, running Node 24.18.1. Both engines have the
|
|
154
|
+
same enforced CPU budget. Batch comparisons give Cheerio a reused Node worker
|
|
155
|
+
pool and include each engine's input and result conversion. Values are medians
|
|
156
|
+
of nine samples, with equal output checked for all **810 measured samples**.
|
|
157
|
+
|
|
158
|
+
Rusteerio was faster in **39 of 45 cases**. Representative results, including
|
|
159
|
+
each kind of measured loss:
|
|
160
|
+
|
|
161
|
+
| Workload | CPUs | Cheerio | rusteerio | Rust latency |
|
|
162
|
+
| --- | ---: | ---: | ---: | --- |
|
|
163
|
+
| Load/query, 500 articles | 1 | 35.25 ms | 4.08 ms | 8.64× faster |
|
|
164
|
+
| Load/query, 5,000 articles | 1 | 150.57 ms | 41.64 ms | 3.62× faster |
|
|
165
|
+
| HTML mutations, 5,000 articles | 1 | 239.65 ms | 189.49 ms | 1.26× faster |
|
|
166
|
+
| Extract 16 documents × 5,000 articles | 1 | 2,887.61 ms | 511.48 ms | 5.65× faster |
|
|
167
|
+
| Extract 16 documents × 5,000 articles | 8 | 797.55 ms | 114.70 ms | 6.95× faster |
|
|
168
|
+
| Nested callback extraction, 8 × 500 articles | 8 | 54.30 ms | 174.06 ms | 3.21× slower |
|
|
169
|
+
| Cold extraction, 16 × 20 articles | 8 | 169.23 ms | 27.37 ms | 6.18× faster |
|
|
170
|
+
| HTML stream, 50,000 elements | 1 | 1,107.78 ms | 346.21 ms | 3.20× faster |
|
|
171
|
+
| XML stream, 50,000 elements | 8 | 119.85 ms | 162.28 ms | 1.35× slower |
|
|
172
|
+
| Append/update/remove, 4,000 cycles | 1 | 593.28 ms | 614.76 ms | 1.04× slower |
|
|
173
|
+
|
|
174
|
+
Cold measurements include engine import, pool initialization and the first
|
|
175
|
+
batch. Warm extraction excludes initialization. Large extraction improved
|
|
176
|
+
from 511.48 ms with one Rust worker to 114.70 ms with eight, a **4.46×** scaling
|
|
177
|
+
gain. Concurrent batches also respected the shared native CPU cap.
|
|
178
|
+
|
|
179
|
+
Callbacks remain JavaScript work. Cheerio's benchmark workers execute callbacks
|
|
180
|
+
on their own threads; rusteerio preserves user closures on the calling thread.
|
|
181
|
+
All four callback workloads were slower than the Cheerio worker pool. Native
|
|
182
|
+
async work also involves main-thread conversion: the large eight CPU extraction
|
|
183
|
+
case had 24.06 ms median maximum timer lag, versus 3.49 ms for Cheerio's workers.
|
|
184
|
+
|
|
185
|
+
Memory results vary. The HTML stream's median end RSS was 372.6 MB for rusteerio
|
|
186
|
+
and 698.5 MB for Cheerio at one CPU; the XML stream used 275.4 MB versus 228.7 MB
|
|
187
|
+
at eight CPUs. Removed-node storage plateaued after warm-up in the retention
|
|
188
|
+
workload, but RSS still grew initially. RSS includes initialization and resident
|
|
189
|
+
input, and sampled peaks do not establish a universal memory bound.
|
|
190
|
+
|
|
191
|
+
These deterministic generated fixtures describe this VM and revision. Hardware,
|
|
192
|
+
runtime and input shape affect results. See [all 45 results](bench/results/fair/comparison.json)
|
|
193
|
+
and [the methodology, raw samples and reproduction steps](bench/README.md),
|
|
194
|
+
including regressions against the previous Rust implementation.
|
|
195
|
+
|
|
196
|
+
## Compatibility and tests
|
|
197
|
+
|
|
198
|
+
The compatibility target is **Cheerio 1.2.0**. All **777 upstream tests across all
|
|
199
|
+
14 files** run against rusteerio, including parser and utility tests. Assertions
|
|
200
|
+
and fixtures are unchanged; implementation imports are redirected. An integrity
|
|
201
|
+
check verifies the complete inventory and source checksums before each run.
|
|
202
|
+
|
|
203
|
+
The additional **418 runtime and release checks** cover custom parser hooks,
|
|
204
|
+
streaming encodings and HTTP, detached-node retention, extraction callbacks and
|
|
205
|
+
shared CPU limits. **53 Rust tests**, strict TypeScript consumer checks, and clean
|
|
206
|
+
npm tarball installation checks run as well. CI builds and tests all eight
|
|
207
|
+
native targets
|
|
208
|
+
and checks Node 20.19, 22 and 24. GNU binaries are built and run on glibc 2.28;
|
|
209
|
+
release checks inspect their actual ELF version requirements.
|
|
210
|
+
|
|
211
|
+
See [the upstream test suite](test/upstream/README.md) and [the latest validation
|
|
212
|
+
record](bench/results/fair/validation.json). Compatibility tests establish
|
|
213
|
+
coverage for that version; they do not establish behavior for every possible
|
|
214
|
+
input or private extension.
|
|
215
|
+
|
|
216
|
+
## Development
|
|
217
|
+
|
|
218
|
+
Use Node.js 20.19+ and stable Rust:
|
|
219
|
+
|
|
220
|
+
```sh
|
|
221
|
+
git clone https://github.com/context-dot-dev/rusteerio.git
|
|
222
|
+
cd rusteerio
|
|
223
|
+
npm ci
|
|
224
|
+
npm run build
|
|
225
|
+
npm test
|
|
226
|
+
npm run test:upstream
|
|
227
|
+
npm run check
|
|
228
|
+
npm run test:types
|
|
229
|
+
npm run test:package
|
|
230
|
+
cargo test
|
|
231
|
+
cargo fmt --all -- --check
|
|
232
|
+
cargo clippy --all-targets -- -D warnings
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
The Rust engine uses html5ever and scraper for HTML5 parsing and fast queries,
|
|
236
|
+
alongside its mutable arena, HTML/XML tokenizer and selector implementation.
|
|
237
|
+
Rust dependencies are compiled into the addon and pinned in `Cargo.lock`.
|
|
238
|
+
Development dependencies supply build tools and compatibility oracles; package
|
|
239
|
+
consumers install no additional npm packages.
|
|
240
|
+
|
|
241
|
+
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for local
|
|
242
|
+
checks and test conventions, and [RELEASING.md](RELEASING.md) for the npm release
|
|
243
|
+
process.
|
|
244
|
+
|
|
245
|
+
## Credits and license
|
|
246
|
+
|
|
247
|
+
Created by the **[context.dev team](https://context.dev)**, with thanks to
|
|
248
|
+
[Cheerio and its contributors](https://github.com/cheeriojs/cheerio) for the API
|
|
249
|
+
and upstream test suite that inspired this project.
|
|
250
|
+
|
|
251
|
+
Rusteerio's original code is **[MIT licensed](LICENSE)**. Vendored Cheerio tests
|
|
252
|
+
retain their [upstream MIT license](test/upstream/LICENSE). Native dependencies
|
|
253
|
+
retain their own terms, collected in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
package/RELEASING.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Releasing rusteerio
|
|
2
|
+
|
|
3
|
+
The public npm package name is **`rusteerio`**, maintained by the context.dev
|
|
4
|
+
team. Releases include all eight native binaries, self-contained TypeScript
|
|
5
|
+
declarations, source files and license notices. Consumers install no additional
|
|
6
|
+
npm packages and need no Rust toolchain on supported platforms.
|
|
7
|
+
|
|
8
|
+
## First release setup
|
|
9
|
+
|
|
10
|
+
The repository must be publicly visible for the publishing workflow's
|
|
11
|
+
provenance checks. The package name was unpublished when this release setup was
|
|
12
|
+
prepared; a registry lookup does not reserve ownership.
|
|
13
|
+
|
|
14
|
+
For the first release, use the workflow's explicit bootstrap mode to establish
|
|
15
|
+
ownership of `rusteerio@0.1.0`. It requires the package to be absent from the npm
|
|
16
|
+
registry before any builds start. The default mode uses OIDC and remains token
|
|
17
|
+
free.
|
|
18
|
+
|
|
19
|
+
Create a granular npm token with **Read and write (publish and stage)** access,
|
|
20
|
+
**Bypass two-factor authentication**, and a one-day expiration. Select only
|
|
21
|
+
`rusteerio` if npm permits it; an unpublished package may require temporary
|
|
22
|
+
**All Packages** access. Organization-management access is unnecessary. Store
|
|
23
|
+
the value only in the GitHub `npm` environment's `NPM_BOOTSTRAP_TOKEN` secret. Never commit the
|
|
24
|
+
token or put its value in commands or logs. See [npm's token
|
|
25
|
+
documentation](https://docs.npmjs.com/creating-and-viewing-access-tokens/).
|
|
26
|
+
|
|
27
|
+
Prepare and tag the exact release commit as described below, then dispatch:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
gh workflow run publish.yml \
|
|
31
|
+
--repo context-dot-dev/rusteerio \
|
|
32
|
+
--ref main \
|
|
33
|
+
-f version=0.1.0 \
|
|
34
|
+
-f npm_tag=latest \
|
|
35
|
+
-f bootstrap=true
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Bootstrap mode runs the same fresh eight-platform matrix, manifest/source
|
|
39
|
+
checks and clean-consumer tests as OIDC mode. Only its final publish step
|
|
40
|
+
receives the token. Both modes publish the exact tested tarball with GitHub
|
|
41
|
+
Actions provenance. A locally built tarball may contain only one platform's
|
|
42
|
+
binary and is not the release artifact.
|
|
43
|
+
|
|
44
|
+
After the package exists, configure its GitHub Actions trusted publisher in npm
|
|
45
|
+
package settings:
|
|
46
|
+
|
|
47
|
+
| Field | Value |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| Organization | `context-dot-dev` |
|
|
50
|
+
| Repository | `rusteerio` |
|
|
51
|
+
| Workflow filename | `publish.yml` |
|
|
52
|
+
| Environment | `npm` |
|
|
53
|
+
| Allowed action | Direct publishing with `npm publish` |
|
|
54
|
+
|
|
55
|
+
After configuring the trusted publisher, revoke the bootstrap token in npm and
|
|
56
|
+
delete `NPM_BOOTSTRAP_TOKEN` from the GitHub environment. Leave `bootstrap` false
|
|
57
|
+
for subsequent releases; the registry-existence guard prevents its reuse.
|
|
58
|
+
|
|
59
|
+
Configure the GitHub `npm` environment for maintainers and permit releases from
|
|
60
|
+
`main`. The workflow uses GitHub-hosted runners, Node 24, npm 11.5.1 or newer,
|
|
61
|
+
and OIDC authentication. A new trusted-publisher configuration must complete
|
|
62
|
+
its first publish within two days. See [npm's trusted-publishing
|
|
63
|
+
documentation](https://docs.npmjs.com/trusted-publishers/) for configuration and
|
|
64
|
+
expiry details.
|
|
65
|
+
|
|
66
|
+
## Prepare a version
|
|
67
|
+
|
|
68
|
+
Update `package.json`, `package-lock.json`, `Cargo.toml` and `Cargo.lock` to the
|
|
69
|
+
same semantic version. Regenerate the notices with `npm run notices:generate`
|
|
70
|
+
and merge the release changes to `main`. Use `next` for prerelease versions.
|
|
71
|
+
Confirm the build, tests and packaging checks pass.
|
|
72
|
+
|
|
73
|
+
Create a version tag on the current `main` commit:
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
git switch main
|
|
77
|
+
git pull --ff-only
|
|
78
|
+
git tag v0.1.0
|
|
79
|
+
git push origin v0.1.0
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Replace `0.1.0` with the intended version. Dispatch publication before making
|
|
83
|
+
another commit to `main`: the tag, package version, Cargo version and the
|
|
84
|
+
workflow's `main` commit must identify the same release.
|
|
85
|
+
|
|
86
|
+
## Publish a subsequent release
|
|
87
|
+
|
|
88
|
+
Run the **Publish to npm** workflow from `main`, with the exact package version
|
|
89
|
+
and npm dist-tag:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
gh workflow run publish.yml \
|
|
93
|
+
--repo context-dot-dev/rusteerio \
|
|
94
|
+
--ref main \
|
|
95
|
+
-f version=0.1.0 \
|
|
96
|
+
-f npm_tag=latest
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The workflow validates the public repository and exact release commit, runs the
|
|
100
|
+
complete build/test matrix, assembles one tarball, and checks its source,
|
|
101
|
+
license, metadata, checksum and all eight native binary architectures. It
|
|
102
|
+
installs and tests that exact artifact before publishing it with provenance.
|
|
103
|
+
|
|
104
|
+
Pushes, pull requests, and ordinary CI runs build and test without publishing.
|
|
105
|
+
Publication requires the separate manual workflow and any configured `npm`
|
|
106
|
+
environment protection. Normal OIDC releases require no stored npm token.
|
|
107
|
+
|
|
108
|
+
## Package checks
|
|
109
|
+
|
|
110
|
+
For a local development build:
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
npm run build
|
|
114
|
+
npm run test:package
|
|
115
|
+
npm run release:check -- --local
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
For a complete release artifact containing all targets:
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
npm run release:check -- --tarball release --commit <release-commit>
|
|
122
|
+
npm run test:package -- release
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`release:check` rejects missing binaries, incorrect architectures, accidental
|
|
126
|
+
package files, missing notices, version mismatches and npm runtime dependencies.
|
|
127
|
+
The CI-produced `manifest.json` records the commit and tarball checksum for
|
|
128
|
+
publication verification. Provenance is generated by npm's supported CI
|
|
129
|
+
publishing flow; see [npm's provenance
|
|
130
|
+
documentation](https://docs.npmjs.com/generating-provenance-statements/).
|
|
131
|
+
|
|
132
|
+
Linux GNU builds run inside pinned glibc 2.28 images on both architectures.
|
|
133
|
+
The binary audit reads ELF symbol-version requirements and rejects binaries
|
|
134
|
+
requiring newer glibc, a C++ runtime, or the wrong libc. Windows releases include
|
|
135
|
+
both x64 and arm64; their PE machine headers are checked. The package includes
|
|
136
|
+
the exact patched html5ever/markup5ever/html5gum sources and original notices alongside
|
|
137
|
+
Rusteerio's source. Regenerate third-party notices after any Cargo dependency
|
|
138
|
+
or compiler change.
|