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.
Files changed (206) hide show
  1. package/CONTRIBUTING.md +73 -0
  2. package/Cargo.lock +1051 -0
  3. package/Cargo.toml +36 -0
  4. package/LICENSE +21 -0
  5. package/README.md +252 -2
  6. package/RELEASING.md +138 -0
  7. package/THIRD_PARTY_NOTICES.md +7443 -0
  8. package/build.rs +3 -0
  9. package/dist/chunk-DKULX6VO.mjs +3185 -0
  10. package/dist/chunk-DKULX6VO.mjs.map +1 -0
  11. package/dist/chunk-OHPGEY3E.mjs +32 -0
  12. package/dist/chunk-OHPGEY3E.mjs.map +1 -0
  13. package/dist/index.cjs +3230 -0
  14. package/dist/index.cjs.map +1 -0
  15. package/dist/index.d.mts +113 -0
  16. package/dist/index.d.ts +113 -0
  17. package/dist/index.mjs +38 -0
  18. package/dist/index.mjs.map +1 -0
  19. package/dist/slim.cjs +2441 -0
  20. package/dist/slim.cjs.map +1 -0
  21. package/dist/slim.d.mts +11 -0
  22. package/dist/slim.d.ts +11 -0
  23. package/dist/slim.mjs +19 -0
  24. package/dist/slim.mjs.map +1 -0
  25. package/dist/types-BP6TpxE5.d.mts +532 -0
  26. package/dist/types-BP6TpxE5.d.ts +532 -0
  27. package/dist/utils.cjs +61 -0
  28. package/dist/utils.cjs.map +1 -0
  29. package/dist/utils.d.mts +13 -0
  30. package/dist/utils.d.ts +13 -0
  31. package/dist/utils.mjs +17 -0
  32. package/dist/utils.mjs.map +1 -0
  33. package/native/src/arena.rs +1781 -0
  34. package/native/src/attributes.rs +923 -0
  35. package/native/src/codec.rs +172 -0
  36. package/native/src/diagnostics.rs +1249 -0
  37. package/native/src/extraction.rs +226 -0
  38. package/native/src/gc.rs +166 -0
  39. package/native/src/hook_sink.rs +727 -0
  40. package/native/src/hook_stream.rs +513 -0
  41. package/native/src/lib.rs +2095 -0
  42. package/native/src/location.rs +392 -0
  43. package/native/src/runtime.rs +189 -0
  44. package/native/src/selector.rs +1287 -0
  45. package/native/src/sink.rs +261 -0
  46. package/native/src/stream.rs +334 -0
  47. package/native/src/tokenizer.rs +355 -0
  48. package/native/src/tracker.rs +232 -0
  49. package/native/vendor/README.md +25 -0
  50. package/native/vendor/html5ever-0.35.0/.cargo_vcs_info.json +6 -0
  51. package/native/vendor/html5ever-0.35.0/Cargo.lock +828 -0
  52. package/native/vendor/html5ever-0.35.0/Cargo.toml +85 -0
  53. package/native/vendor/html5ever-0.35.0/LICENSE-APACHE +201 -0
  54. package/native/vendor/html5ever-0.35.0/LICENSE-MIT +25 -0
  55. package/native/vendor/html5ever-0.35.0/README.md +60 -0
  56. package/native/vendor/html5ever-0.35.0/benches/html5ever.rs +86 -0
  57. package/native/vendor/html5ever-0.35.0/data/bench/lipsum-zh.html +19 -0
  58. package/native/vendor/html5ever-0.35.0/data/bench/lipsum.html +40 -0
  59. package/native/vendor/html5ever-0.35.0/data/bench/medium-fragment.html +24 -0
  60. package/native/vendor/html5ever-0.35.0/data/bench/small-fragment.html +7 -0
  61. package/native/vendor/html5ever-0.35.0/data/bench/strong.html +1 -0
  62. package/native/vendor/html5ever-0.35.0/data/bench/tiny-fragment.html +1 -0
  63. package/native/vendor/html5ever-0.35.0/examples/arena.rs +356 -0
  64. package/native/vendor/html5ever-0.35.0/examples/capi/tokenize.c +74 -0
  65. package/native/vendor/html5ever-0.35.0/examples/noop-tokenize.rs +47 -0
  66. package/native/vendor/html5ever-0.35.0/examples/noop-tree-builder.rs +132 -0
  67. package/native/vendor/html5ever-0.35.0/examples/print-tree-actions.rs +185 -0
  68. package/native/vendor/html5ever-0.35.0/examples/tokenize.rs +111 -0
  69. package/native/vendor/html5ever-0.35.0/src/driver.rs +148 -0
  70. package/native/vendor/html5ever-0.35.0/src/lib.rs +30 -0
  71. package/native/vendor/html5ever-0.35.0/src/macros.rs +34 -0
  72. package/native/vendor/html5ever-0.35.0/src/serialize/mod.rs +255 -0
  73. package/native/vendor/html5ever-0.35.0/src/tokenizer/char_ref/mod.rs +451 -0
  74. package/native/vendor/html5ever-0.35.0/src/tokenizer/interface.rs +99 -0
  75. package/native/vendor/html5ever-0.35.0/src/tokenizer/mod.rs +2305 -0
  76. package/native/vendor/html5ever-0.35.0/src/tokenizer/states.rs +97 -0
  77. package/native/vendor/html5ever-0.35.0/src/tree_builder/data.rs +170 -0
  78. package/native/vendor/html5ever-0.35.0/src/tree_builder/mod.rs +1848 -0
  79. package/native/vendor/html5ever-0.35.0/src/tree_builder/rules.rs +1494 -0
  80. package/native/vendor/html5ever-0.35.0/src/tree_builder/tag_sets.rs +114 -0
  81. package/native/vendor/html5ever-0.35.0/src/tree_builder/types.rs +91 -0
  82. package/native/vendor/html5ever-0.35.0/src/util/str.rs +52 -0
  83. package/native/vendor/html5gum-0.8.4/Cargo.lock +1006 -0
  84. package/native/vendor/html5gum-0.8.4/Cargo.toml +109 -0
  85. package/native/vendor/html5gum-0.8.4/LICENSE +19 -0
  86. package/native/vendor/html5gum-0.8.4/README.md +110 -0
  87. package/native/vendor/html5gum-0.8.4/benches/files/0050.html +2131 -0
  88. package/native/vendor/html5gum-0.8.4/benches/files/6D30.html +1004 -0
  89. package/native/vendor/html5gum-0.8.4/benches/files.rs +32 -0
  90. package/native/vendor/html5gum-0.8.4/benches/patterns.rs +31 -0
  91. package/native/vendor/html5gum-0.8.4/src/arrayvec.rs +29 -0
  92. package/native/vendor/html5gum-0.8.4/src/char_validator.rs +134 -0
  93. package/native/vendor/html5gum-0.8.4/src/emitters/callback.rs +676 -0
  94. package/native/vendor/html5gum-0.8.4/src/emitters/default.rs +207 -0
  95. package/native/vendor/html5gum-0.8.4/src/emitters/emitter.rs +344 -0
  96. package/native/vendor/html5gum-0.8.4/src/emitters/html5ever.rs +306 -0
  97. package/native/vendor/html5gum-0.8.4/src/emitters/mod.rs +25 -0
  98. package/native/vendor/html5gum-0.8.4/src/entities.rs +3342 -0
  99. package/native/vendor/html5gum-0.8.4/src/error.rs +96 -0
  100. package/native/vendor/html5gum-0.8.4/src/htmlstring.rs +130 -0
  101. package/native/vendor/html5gum-0.8.4/src/lib.rs +66 -0
  102. package/native/vendor/html5gum-0.8.4/src/machine.rs +2136 -0
  103. package/native/vendor/html5gum-0.8.4/src/machine_helper.rs +277 -0
  104. package/native/vendor/html5gum-0.8.4/src/read_helper.rs +244 -0
  105. package/native/vendor/html5gum-0.8.4/src/reader.rs +440 -0
  106. package/native/vendor/html5gum-0.8.4/src/span.rs +98 -0
  107. package/native/vendor/html5gum-0.8.4/src/state.rs +16 -0
  108. package/native/vendor/html5gum-0.8.4/src/testutils.rs +71 -0
  109. package/native/vendor/html5gum-0.8.4/src/tokenizer.rs +189 -0
  110. package/native/vendor/html5gum-0.8.4/src/utils.rs +88 -0
  111. package/native/vendor/markup5ever-0.35.0/.cargo_vcs_info.json +6 -0
  112. package/native/vendor/markup5ever-0.35.0/Cargo.toml +45 -0
  113. package/native/vendor/markup5ever-0.35.0/LICENSE-APACHE +201 -0
  114. package/native/vendor/markup5ever-0.35.0/LICENSE-MIT +25 -0
  115. package/native/vendor/markup5ever-0.35.0/interface/mod.rs +422 -0
  116. package/native/vendor/markup5ever-0.35.0/interface/tree_builder.rs +280 -0
  117. package/native/vendor/markup5ever-0.35.0/lib.rs +59 -0
  118. package/native/vendor/markup5ever-0.35.0/serialize.rs +77 -0
  119. package/native/vendor/markup5ever-0.35.0/util/buffer_queue.rs +333 -0
  120. package/native/vendor/markup5ever-0.35.0/util/smallcharset.rs +88 -0
  121. package/package.json +137 -4
  122. package/rusteerio.darwin-arm64.node +0 -0
  123. package/rusteerio.darwin-x64.node +0 -0
  124. package/rusteerio.linux-arm64-gnu.node +0 -0
  125. package/rusteerio.linux-arm64-musl.node +0 -0
  126. package/rusteerio.linux-x64-gnu.node +0 -0
  127. package/rusteerio.linux-x64-musl.node +0 -0
  128. package/rusteerio.win32-arm64-msvc.node +0 -0
  129. package/rusteerio.win32-x64-msvc.node +0 -0
  130. package/scripts/check-native.mjs +15 -0
  131. package/scripts/check-release.mjs +235 -0
  132. package/scripts/fix-declarations.mjs +27 -0
  133. package/scripts/generate-notices.mjs +176 -0
  134. package/scripts/notices/README.md +13 -0
  135. package/scripts/notices/cheerio-MIT.txt +21 -0
  136. package/scripts/notices/html5ever-AUTHORS.txt +20 -0
  137. package/scripts/notices/html5ever-COPYRIGHT.txt +8 -0
  138. package/scripts/notices/html5ever-LICENSE-APACHE.txt +201 -0
  139. package/scripts/notices/html5ever-LICENSE-MIT.txt +25 -0
  140. package/scripts/notices/napi-rs-MIT.txt +43 -0
  141. package/scripts/notices/neon-MIT.txt +19 -0
  142. package/scripts/notices/runtime-01c266bced4a434da005.txt +3 -0
  143. package/scripts/notices/runtime-0a54e647fe54104658b5.txt +14 -0
  144. package/scripts/notices/runtime-0b74dfa0bcee5c420c6b.txt +25 -0
  145. package/scripts/notices/runtime-0d542e0c8804e39aa7f3.txt +177 -0
  146. package/scripts/notices/runtime-0f96a83840e146e43c0e.txt +21 -0
  147. package/scripts/notices/runtime-123a331b5dbf04c30097.txt +25 -0
  148. package/scripts/notices/runtime-172020dbfd5b53a226df.txt +34 -0
  149. package/scripts/notices/runtime-209fbbe0ad52d9235e37.txt +26 -0
  150. package/scripts/notices/runtime-23f18e03dc49df91622f.txt +23 -0
  151. package/scripts/notices/runtime-268872b9816f90fd8e85.txt +220 -0
  152. package/scripts/notices/runtime-35242e7a83f69875e6ed.txt +176 -0
  153. package/scripts/notices/runtime-378f5840b258e2779c39.txt +25 -0
  154. package/scripts/notices/runtime-4108245a1f2df9d4e94d.txt +25 -0
  155. package/scripts/notices/runtime-4455bf75a91154108304.txt +21 -0
  156. package/scripts/notices/runtime-553fffcd9b1cb158bc3e.txt +13 -0
  157. package/scripts/notices/runtime-62c7a1e35f56406896d7.txt +176 -0
  158. package/scripts/notices/runtime-6485b8ed310d3f0340bf.txt +25 -0
  159. package/scripts/notices/runtime-69fef7b0f322a6555415.txt +201 -0
  160. package/scripts/notices/runtime-6df43f6f4b5d4587f3d8.txt +187 -0
  161. package/scripts/notices/runtime-799e9ca9d179295ef372.txt +24 -0
  162. package/scripts/notices/runtime-7b63ecd5f1902af1b637.txt +25 -0
  163. package/scripts/notices/runtime-7bc7d5de2a0b793d4e1a.txt +21 -0
  164. package/scripts/notices/runtime-7e12e5df4bae12cb2158.txt +24 -0
  165. package/scripts/notices/runtime-861399f8c21c042b1105.txt +12 -0
  166. package/scripts/notices/runtime-8ada45cd9f843acf64e4.txt +201 -0
  167. package/scripts/notices/runtime-90eb64f0279b0d9432ac.txt +12 -0
  168. package/scripts/notices/runtime-a60eea817514531668d7.txt +201 -0
  169. package/scripts/notices/runtime-a7c936ff1ed8fa340172.txt +25 -0
  170. package/scripts/notices/runtime-ab6eec6caf0fa5775e41.txt +275 -0
  171. package/scripts/notices/runtime-b1181a40b2a7b25cf66f.txt +19 -0
  172. package/scripts/notices/runtime-b71bd43a069ca0641a9e.txt +25 -0
  173. package/scripts/notices/runtime-c16f8dcf1a368b83be78.txt +201 -0
  174. package/scripts/notices/runtime-c2cfccb812fe482101a8.txt +21 -0
  175. package/scripts/notices/runtime-e99d88d232bf57d70f0f.txt +25 -0
  176. package/scripts/notices/runtime-f32fb3b417a194167cfa.txt +9 -0
  177. package/scripts/notices/runtime-f5062c9a188d81dfe66b.txt +39 -0
  178. package/scripts/notices/runtime-f9070a832833fa7488fd.txt +4 -0
  179. package/scripts/notices/runtime-fab3dd6bdab226f1c086.txt +373 -0
  180. package/scripts/notices/runtime-fba3318d0c600380177e.txt +62 -0
  181. package/scripts/notices/runtime-ff8f68cb076caf8cefe7.txt +25 -0
  182. package/scripts/notices/runtime-ff92bed461f50338dd70.txt +74 -0
  183. package/scripts/notices/runtime.json +1632 -0
  184. package/scripts/notices/sources.json +555 -0
  185. package/scripts/notices/xray-MIT.txt +21 -0
  186. package/src/attributes.ts +271 -0
  187. package/src/codec.ts +74 -0
  188. package/src/dom.ts +87 -0
  189. package/src/extract-plan.ts +97 -0
  190. package/src/extract.ts +130 -0
  191. package/src/hooks.ts +220 -0
  192. package/src/index.ts +223 -0
  193. package/src/io.ts +491 -0
  194. package/src/native.ts +135 -0
  195. package/src/nodes.ts +290 -0
  196. package/src/parse.ts +53 -0
  197. package/src/parser-types.ts +157 -0
  198. package/src/router.ts +327 -0
  199. package/src/selection.ts +566 -0
  200. package/src/slim.ts +12 -0
  201. package/src/transport.ts +104 -0
  202. package/src/types.ts +186 -0
  203. package/src/utils.ts +29 -0
  204. package/src/workers.ts +21 -0
  205. package/tsconfig.json +15 -0
  206. 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
- # Temporary Holding Version
1
+ # rusteerio
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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
+ [![CI](https://github.com/context-dot-dev/rusteerio/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/context-dot-dev/rusteerio/actions/workflows/ci.yml)
7
+ [![Cheerio tests](https://img.shields.io/badge/Cheerio_tests-777_passing-brightgreen)](test/upstream/README.md)
8
+ [![npm version](https://img.shields.io/npm/v/rusteerio)](https://www.npmjs.com/package/rusteerio)
9
+ [![MIT license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
10
+ [![Built by context.dev](https://img.shields.io/badge/built_by-context.dev-202020)](https://context.dev)
11
+ [![Runtime dependencies](https://img.shields.io/badge/npm_runtime_dependencies-0-brightgreen)](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.