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
@@ -0,0 +1,235 @@
1
+ import assert from 'node:assert/strict';
2
+ import { createHash } from 'node:crypto';
3
+ import { execFileSync } from 'node:child_process';
4
+ import { readFileSync, readdirSync, writeFileSync } from 'node:fs';
5
+ import { basename, join, resolve } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { gunzipSync } from 'node:zlib';
8
+
9
+ export const nativeTargets = ['darwin-x64', 'darwin-arm64', 'linux-x64-gnu', 'linux-arm64-gnu', 'linux-x64-musl', 'linux-arm64-musl', 'win32-x64-msvc', 'win32-arm64-msvc'];
10
+ const requiredFiles = ['package.json', 'LICENSE', 'README.md', 'CONTRIBUTING.md', 'RELEASING.md', 'THIRD_PARTY_NOTICES.md', 'Cargo.toml', 'Cargo.lock', 'build.rs', 'native/src/lib.rs', 'native/vendor/README.md', 'native/vendor/html5ever-0.35.0/Cargo.toml', 'native/vendor/html5ever-0.35.0/src/lib.rs', 'native/vendor/html5ever-0.35.0/LICENSE-MIT', 'native/vendor/html5ever-0.35.0/LICENSE-APACHE', 'native/vendor/markup5ever-0.35.0/Cargo.toml', 'native/vendor/markup5ever-0.35.0/lib.rs', 'native/vendor/markup5ever-0.35.0/LICENSE-MIT', 'native/vendor/markup5ever-0.35.0/LICENSE-APACHE', 'src/index.ts', 'tsconfig.json', 'tsup.config.ts', 'scripts/check-native.mjs', 'scripts/check-release.mjs', 'scripts/fix-declarations.mjs', 'scripts/generate-notices.mjs', 'scripts/notices/sources.json', 'scripts/notices/runtime.json', 'scripts/notices/cheerio-MIT.txt', 'dist/index.cjs', 'dist/index.mjs', 'dist/index.d.ts', 'dist/index.d.mts', 'dist/slim.cjs', 'dist/slim.mjs', 'dist/utils.cjs', 'dist/utils.mjs'];
11
+ const root = fileURLToPath(new URL('../', import.meta.url));
12
+
13
+ export function validateVersion(version, distTag = 'latest') {
14
+ assert.equal(typeof version, 'string', 'A release version is required');
15
+ assert.match(version, /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*))?$/, 'Use an exact semantic version without a v prefix or build metadata');
16
+ const prerelease = version.split('-').slice(1).join('-');
17
+ for (const identifier of prerelease.split('.')) assert.ok(!/^0\d+$/.test(identifier), 'Numeric prerelease identifiers cannot have leading zeroes');
18
+ assert.ok(distTag === 'latest' || distTag === 'next', 'The npm dist-tag must be latest or next');
19
+ assert.ok(!prerelease || distTag !== 'latest', 'Prereleases must use the next npm dist-tag');
20
+ return `v${version}`;
21
+ }
22
+
23
+ export function validateMetadata(metadata, cargo) {
24
+ assert.equal(metadata.name, 'rusteerio');
25
+ validateVersion(metadata.version, metadata.version.includes('-') ? 'next' : 'latest');
26
+ assert.equal(metadata.license, 'MIT');
27
+ assert.notEqual(metadata.private, true, 'The release package cannot be private');
28
+ assert.equal(metadata.author?.name, 'Context.dev team');
29
+ assert.equal(metadata.author?.url, 'https://context.dev');
30
+ assert.equal(metadata.repository?.url, 'git+https://github.com/context-dot-dev/rusteerio.git');
31
+ assert.equal(metadata.engines?.node, '>=20.19.0');
32
+ assert.equal(metadata.publishConfig?.access, 'public');
33
+ assert.equal(metadata.publishConfig?.registry, 'https://registry.npmjs.org/');
34
+ for (const key of ['dependencies', 'optionalDependencies', 'peerDependencies']) assert.deepEqual(Object.keys(metadata[key] ?? {}), [], `${key} must remain empty`);
35
+ for (const key of ['bundledDependencies', 'bundleDependencies']) assert.ok(!metadata[key] || Array.isArray(metadata[key]) && !metadata[key].length, `${key} must remain empty`);
36
+ for (const lifecycle of ['preinstall', 'install', 'postinstall']) assert.equal(metadata.scripts?.[lifecycle], undefined, `The package must not require an ${lifecycle} hook`);
37
+ assert.equal(cargo.match(/\[package\][\s\S]*?\nversion\s*=\s*"([^"]+)"/)?.[1], metadata.version, 'Cargo and npm versions must match');
38
+ }
39
+
40
+ /** Read ELF version requirements from SHT_GNU_verneed, rather than strings in code/data. */
41
+ export function elfRequirements(data) {
42
+ const versions = new Set(), libraries = new Set(), requirements = [];
43
+ const table = Number(data.readBigUInt64LE(40)), entrySize = data.readUInt16LE(58), count = data.readUInt16LE(60);
44
+ if (!table || !count) return { versions: [], libraries: [], requirements: [] };
45
+ assert.ok(entrySize >= 64 && table + entrySize * count <= data.length, 'Truncated ELF section table');
46
+ const sections = Array.from({ length: count }, (_, index) => {
47
+ const at = table + index * entrySize;
48
+ return { type: data.readUInt32LE(at + 4), offset: Number(data.readBigUInt64LE(at + 24)), size: Number(data.readBigUInt64LE(at + 32)), link: data.readUInt32LE(at + 40) };
49
+ });
50
+ function string(section, offset) {
51
+ assert.ok(section && offset >= 0 && offset < section.size && section.offset + section.size <= data.length, 'Invalid ELF string table');
52
+ const start = section.offset + offset, end = data.indexOf(0, start);
53
+ assert.ok(end >= start && end < section.offset + section.size, 'Unterminated ELF symbol version');
54
+ return data.toString('utf8', start, end);
55
+ }
56
+ for (const section of sections) {
57
+ assert.ok(section.offset >= 0 && section.offset + section.size <= data.length || section.type === 8, 'Truncated ELF section');
58
+ if (section.type === 6) {
59
+ for (let at = section.offset; at + 16 <= section.offset + section.size; at += 16) {
60
+ if (data.readBigInt64LE(at) === 1n) libraries.add(string(sections[section.link], Number(data.readBigUInt64LE(at + 8))));
61
+ }
62
+ }
63
+ if (section.type !== 0x6ffffffe) continue;
64
+ let at = section.offset;
65
+ for (let visited = 0; at + 16 <= section.offset + section.size && visited < section.size / 16; visited++) {
66
+ const entries = data.readUInt16LE(at + 2), next = data.readUInt32LE(at + 12);
67
+ const library = string(sections[section.link], data.readUInt32LE(at + 4));
68
+ let aux = at + data.readUInt32LE(at + 8);
69
+ for (let index = 0; index < entries; index++) {
70
+ assert.ok(aux >= section.offset && aux + 16 <= section.offset + section.size, 'Truncated ELF version requirement');
71
+ const version = string(sections[section.link], data.readUInt32LE(aux + 8));
72
+ versions.add(version);
73
+ requirements.push({ library, version });
74
+ const step = data.readUInt32LE(aux + 12);
75
+ if (!step) break;
76
+ aux += step;
77
+ }
78
+ if (!next) break;
79
+ at += next;
80
+ }
81
+ }
82
+ return { versions: [...versions].sort(), libraries: [...libraries].sort(), requirements };
83
+ }
84
+
85
+ export function validateNativeBinary(name, data) {
86
+ assert.ok(data.length >= 64, `${name} is empty or truncated`);
87
+ if (name.includes('.linux-')) {
88
+ assert.equal(data.subarray(0, 6).toString('hex'), '7f454c460201', `${name} must be a little-endian 64-bit ELF binary`);
89
+ assert.equal(data.readUInt16LE(18), name.includes('-arm64-') ? 183 : 62, `${name} has the wrong CPU architecture`);
90
+ const { versions, libraries, requirements } = elfRequirements(data);
91
+ if (name.includes('-gnu.')) {
92
+ for (const version of versions.filter(version => version.startsWith('GLIBC_'))) {
93
+ const numbers = version.slice(6).split('.').map(Number);
94
+ assert.ok(numbers[0] < 2 || numbers[0] === 2 && numbers[1] <= 28, `${name} requires ${version}; releases must support glibc 2.28`);
95
+ }
96
+ assert.ok(!versions.some(version => version.startsWith('GLIBCXX_')), `${name} unexpectedly requires a C++ runtime`);
97
+ } else {
98
+ // Alpine's ARM64 libgcc versions its frame-registration exports as
99
+ // GLIBC_2.0. The version name belongs to libgcc, not a glibc dependency.
100
+ assert.ok(!requirements.some(({ library, version }) => version.startsWith('GLIBC_') && !(library === 'libgcc_s.so.1' && version === 'GLIBC_2.0')), `${name} links glibc instead of musl`);
101
+ assert.ok(!libraries.some(library => library === 'libc.so.6'), `${name} links glibc instead of musl`);
102
+ }
103
+ } else if (name.includes('.darwin-')) {
104
+ assert.equal(data.readUInt32LE(0), 0xfeedfacf, `${name} must be a 64-bit Mach-O binary`);
105
+ assert.equal(data.readUInt32LE(4), name.includes('-arm64.') ? 0x0100000c : 0x01000007, `${name} has the wrong CPU architecture`);
106
+ } else {
107
+ assert.equal(data.subarray(0, 2).toString(), 'MZ', `${name} must be a Windows PE binary`);
108
+ const offset = data.readUInt32LE(0x3c);
109
+ assert.ok(offset + 6 <= data.length, `${name} has a truncated PE header`);
110
+ assert.equal(data.subarray(offset, offset + 4).toString('hex'), '50450000', `${name} has an invalid PE signature`);
111
+ assert.equal(data.readUInt16LE(offset + 4), name.includes('-arm64-') ? 0xaa64 : 0x8664, `${name} has the wrong Windows CPU architecture`);
112
+ }
113
+ }
114
+
115
+ function allowedFile(path) {
116
+ return requiredFiles.includes(path) || /^dist\/[\w./-]+\.(?:[cm]?js|map|[cm]?ts)$/.test(path)
117
+ || /^(?:src\/[\w/-]+\.ts|native\/src\/[\w/-]+\.rs)$/.test(path)
118
+ || /^native\/vendor\/(?:(?:html5ever|markup5ever)-0\.35\.0|html5gum-0\.8\.4)\/(?:[\w./-]+\.(?:rs|c|h|html|md|toml|orig|json)|Cargo\.lock|LICENSE(?:-(?:MIT|APACHE))?)$/.test(path)
119
+ || /^scripts\/notices\/[\w-]+\.(?:txt|md|json)$/.test(path)
120
+ || nativeTargets.some(target => path === `rusteerio.${target}.node`);
121
+ }
122
+
123
+ export function validatePackageFiles(files, { allTargets = true } = {}) {
124
+ for (const path of files.keys()) {
125
+ assert.ok(!path.split('/').some(part => part === '.' || part === '..' || part === 'target' || part === 'node_modules') && !path.includes('\\'), `Unsafe package path: ${path}`);
126
+ assert.ok(allowedFile(path), `Unexpected package file: ${path}`);
127
+ }
128
+ for (const path of requiredFiles) assert.ok(files.get(path)?.length > 0, `Package is missing ${path}`);
129
+ for (const path of ['native/vendor/html5gum-0.8.4/Cargo.toml', 'native/vendor/html5gum-0.8.4/src/lib.rs', 'native/vendor/html5gum-0.8.4/LICENSE']) assert.ok(files.get(path)?.length > 0, `Package is missing ${path}`);
130
+ assert.match(files.get('LICENSE').toString(), /MIT License/);
131
+ assert.match(files.get('THIRD_PARTY_NOTICES.md').toString(), /cheerio/i);
132
+ assert.match(files.get('README.md').toString(), /context\.dev/i);
133
+ const binaries = [...files].filter(([path]) => path.endsWith('.node'));
134
+ assert.ok(binaries.length > 0, 'Package has no native binary');
135
+ if (allTargets) for (const target of nativeTargets) assert.ok(files.has(`rusteerio.${target}.node`), `Release is missing the ${target} binary`);
136
+ for (const [path, data] of binaries) validateNativeBinary(path, data);
137
+ const metadata = JSON.parse(files.get('package.json'));
138
+ validateMetadata(metadata, files.get('Cargo.toml').toString());
139
+ return metadata;
140
+ }
141
+
142
+ /** Read regular files only; links and archive paths outside package/ are rejected. */
143
+ export function readTarball(path) {
144
+ const tar = gunzipSync(readFileSync(path), { maxOutputLength: 256 * 1024 * 1024 });
145
+ const files = new Map();
146
+ let extended = {};
147
+ const string = bytes => bytes.toString('utf8').replace(/\0.*$/s, '');
148
+ for (let offset = 0; offset + 512 <= tar.length;) {
149
+ const header = tar.subarray(offset, offset + 512);
150
+ if (header.every(byte => byte === 0)) {
151
+ assert.ok(tar.subarray(offset).every(byte => byte === 0), 'Unexpected data after the tar archive terminator');
152
+ break;
153
+ }
154
+ const checksum = [...header].reduce((sum, value, index) => sum + (index >= 148 && index < 156 ? 32 : value), 0);
155
+ assert.equal(checksum, Number.parseInt(string(header.subarray(148, 156)).trim(), 8), 'Invalid tar header checksum');
156
+ const size = Number.parseInt(string(header.subarray(124, 136)).trim(), 8);
157
+ assert.ok(Number.isSafeInteger(size) && size >= 0 && offset + 512 + size <= tar.length, 'Invalid tar entry size');
158
+ const data = tar.subarray(offset + 512, offset + 512 + size);
159
+ const type = header[156];
160
+ let path = string(header.subarray(0, 100));
161
+ const prefix = string(header.subarray(345, 500));
162
+ if (prefix) path = `${prefix}/${path}`;
163
+ if (type === 120) {
164
+ extended = {};
165
+ let index = 0;
166
+ while (index < data.length) {
167
+ const space = data.indexOf(32, index);
168
+ const length = Number(data.subarray(index, space).toString());
169
+ assert.ok(space > index && Number.isSafeInteger(length) && length > space - index && index + length <= data.length, 'Invalid PAX header');
170
+ const entry = data.subarray(space + 1, index + length - 1).toString();
171
+ const separator = entry.indexOf('=');
172
+ assert.ok(separator > 0, 'Invalid PAX attribute');
173
+ extended[entry.slice(0, separator)] = entry.slice(separator + 1);
174
+ index += length;
175
+ }
176
+ } else if (type !== 53) {
177
+ assert.ok(type === 0 || type === 48, 'Package tarball must contain regular files, not links');
178
+ path = extended.path ?? path;
179
+ extended = {};
180
+ assert.ok(path.startsWith('package/'), `Unexpected tarball path: ${path}`);
181
+ path = path.slice('package/'.length);
182
+ assert.ok(!files.has(path), `Duplicate tarball entry: ${path}`);
183
+ files.set(path, data);
184
+ }
185
+ offset += 512 + Math.ceil(size / 512) * 512;
186
+ }
187
+ return files;
188
+ }
189
+
190
+ function run() {
191
+ const args = process.argv.slice(2);
192
+ const options = {};
193
+ for (let index = 0; index < args.length; index++) {
194
+ const argument = args[index];
195
+ if (argument === '--local') options.local = true;
196
+ else {
197
+ assert.ok(['--tarball', '--manifest', '--verify-manifest', '--commit'].includes(argument), `Unknown argument: ${argument}`);
198
+ assert.ok(args[index + 1], `${argument} requires a value`);
199
+ options[argument.slice(2)] = args[++index];
200
+ }
201
+ }
202
+ if (options.commit) {
203
+ assert.match(options.commit, /^[a-f0-9]{40}$/);
204
+ assert.equal(execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim(), options.commit, 'Checkout and release commit differ');
205
+ }
206
+ let tarball = options.tarball;
207
+ if (tarball && !tarball.endsWith('.tgz')) {
208
+ const candidates = readdirSync(tarball).filter(path => path.endsWith('.tgz'));
209
+ assert.equal(candidates.length, 1, 'The release directory must contain exactly one npm tarball');
210
+ tarball = join(tarball, candidates[0]);
211
+ }
212
+ let files;
213
+ if (tarball) files = readTarball(tarball);
214
+ else {
215
+ const npm = process.env.npm_execpath;
216
+ const result = npm ? execFileSync(process.execPath, [npm, 'pack', '--dry-run', '--ignore-scripts', '--json'], { cwd: root, encoding: 'utf8' })
217
+ : execFileSync('npm', ['pack', '--dry-run', '--ignore-scripts', '--json'], { cwd: root, encoding: 'utf8' });
218
+ files = new Map(JSON.parse(result)[0].files.map(({ path }) => [path, readFileSync(join(root, path))]));
219
+ }
220
+ const metadata = validatePackageFiles(files, { allTargets: !options.local });
221
+ if (options.commit) for (const [path, contents] of files) {
222
+ if (path.startsWith('dist/') || path.endsWith('.node')) continue;
223
+ const committed = execFileSync('git', ['show', `${options.commit}:${path}`], { cwd: root });
224
+ assert.ok(committed.equals(contents), `${path} differs from the declared release commit`);
225
+ }
226
+ const manifest = { format: 1, name: metadata.name, version: metadata.version, commit: options.commit, filename: tarball ? basename(tarball) : undefined, sha256: tarball ? createHash('sha256').update(readFileSync(tarball)).digest('hex') : undefined, targets: nativeTargets.filter(target => files.has(`rusteerio.${target}.node`)) };
227
+ if (options.manifest) {
228
+ assert.ok(tarball && options.commit && !options.local, 'Release manifests require a complete tarball and exact commit');
229
+ writeFileSync(options.manifest, `${JSON.stringify(manifest, null, 2)}\n`);
230
+ }
231
+ if (options['verify-manifest']) assert.deepEqual(JSON.parse(readFileSync(options['verify-manifest'])), manifest, 'Release artifact does not match its tested manifest');
232
+ console.log(`${metadata.name}@${metadata.version}: package contents, license, source, zero npm dependencies and ${manifest.targets.length} native targets verified${options.local ? ' (local platform check)' : ''}.`);
233
+ }
234
+
235
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) run();
@@ -0,0 +1,27 @@
1
+ import assert from 'node:assert/strict';
2
+ import { readFileSync, writeFileSync } from 'node:fs';
3
+ import ts from 'typescript';
4
+
5
+ // The declaration bundler can drop `type` when re-exporting classes or enums.
6
+ // Keep the public entry points faithful to source `export type` statements.
7
+ const typeOnly = new Set(['Cheerio', 'ElementType']);
8
+ for (const entry of ['index', 'slim']) {
9
+ for (const extension of ['d.ts', 'd.mts']) {
10
+ const file = new URL(`../dist/${entry}.${extension}`, import.meta.url);
11
+ let text = readFileSync(file, 'utf8');
12
+ const source = ts.createSourceFile(file.pathname, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
13
+ const edits = [];
14
+ const found = new Set();
15
+ for (const statement of source.statements) {
16
+ if (!ts.isExportDeclaration(statement) || !statement.exportClause || !ts.isNamedExports(statement.exportClause)) continue;
17
+ for (const specifier of statement.exportClause.elements) {
18
+ if (!typeOnly.has(specifier.name.text)) continue;
19
+ found.add(specifier.name.text);
20
+ if (!statement.isTypeOnly && !specifier.isTypeOnly) edits.push(specifier.getStart(source));
21
+ }
22
+ }
23
+ assert.deepEqual([...found].sort(), [...typeOnly].sort(), `${entry}.${extension} must expose its intended type exports`);
24
+ for (const position of edits.sort((left, right) => right - left)) text = text.slice(0, position) + 'type ' + text.slice(position);
25
+ if (edits.length) writeFileSync(file, text);
26
+ }
27
+ }
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env node
2
+ import { createHash } from 'node:crypto';
3
+ import { execFileSync } from 'node:child_process';
4
+ import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
5
+ import { dirname, join, relative, resolve } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
9
+ const output = join(root, 'THIRD_PARTY_NOTICES.md');
10
+ const cacheDir = join(root, 'scripts', 'notices');
11
+ const check = process.argv.includes('--check');
12
+ const args = process.argv.slice(2);
13
+ if (args.some((arg) => arg !== '--check')) throw new Error('Usage: node scripts/generate-notices.mjs [--check]');
14
+ const sha256 = (text) => createHash('sha256').update(text).digest('hex');
15
+ const read = (path) => readFileSync(path, 'utf8').replace(/\r\n?/g, '\n');
16
+ const compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
17
+ const legalName = /^(?:licen[cs]e(?:[-_.].*)?|unlicense|copying(?:[-_.].*)?|copyright(?:[-_.].*)?|notice(?:[-_.].*)?|authors(?:[-_.].*)?)$/i;
18
+ const metadata = JSON.parse(execFileSync(process.env.CARGO || 'cargo', [
19
+ 'metadata', '--locked', '--format-version', '1',
20
+ ], { cwd: root, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 }));
21
+ const vendored = new Set(['html5ever@0.35.0', 'markup5ever@0.35.0', 'html5gum@0.8.4']);
22
+ for (const pkg of metadata.packages) {
23
+ if (!pkg.source && pkg.name !== 'rusteerio' && !vendored.has(`${pkg.name}@${pkg.version}`)) throw new Error(`Review notices for the new local dependency ${pkg.name}@${pkg.version}`);
24
+ }
25
+ const packages = metadata.packages.filter((pkg) => pkg.source || vendored.has(`${pkg.name}@${pkg.version}`))
26
+ .sort((a, b) => compare(a.name, b.name) || compare(a.version, b.version));
27
+ const byName = new Map(packages.map((pkg) => [`${pkg.name}@${pkg.version}`, pkg]));
28
+ const supplements = JSON.parse(read(join(cacheDir, 'sources.json')));
29
+ if (supplements.schemaVersion !== 1) throw new Error('Unsupported supplemental notice schema');
30
+ const runtime = JSON.parse(read(join(cacheDir, 'runtime.json')));
31
+ if (runtime.schemaVersion !== 1) throw new Error('Unsupported Rust runtime notice schema');
32
+ if (check) {
33
+ const rustc = execFileSync(process.env.RUSTC || 'rustc', ['--version'], { encoding: 'utf8' }).trim();
34
+ const version = /^rustc (\d+\.\d+\.\d+)/.exec(rustc)?.[1];
35
+ if (!version || !runtime.releases[version]) throw new Error(`Review and add standard-library notices before releasing with ${rustc}`);
36
+ }
37
+
38
+ for (const [file, entry] of Object.entries(supplements.cache)) {
39
+ if (sha256(read(join(cacheDir, file))) !== entry.sha256) throw new Error(`Supplemental notice checksum mismatch: ${file}`);
40
+ }
41
+ for (const key of Object.keys(supplements.packages)) {
42
+ if (!byName.has(key)) throw new Error(`Remove or review stale supplemental notices for ${key}`);
43
+ }
44
+
45
+ function legalFiles(dir, base = dir, insideLegalDir = false) {
46
+ const files = [];
47
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
48
+ if (entry.name === '.git' || entry.name === 'target') continue;
49
+ const path = join(dir, entry.name);
50
+ if (entry.isDirectory()) files.push(...legalFiles(path, base, insideLegalDir || /^(?:licen[cs]es?|notices?)$/i.test(entry.name)));
51
+ else if (entry.isFile() && (insideLegalDir || legalName.test(entry.name))) files.push(relative(base, path).replaceAll('\\', '/'));
52
+ }
53
+ return files.sort(compare);
54
+ }
55
+
56
+ function crateSource(pkg, path) {
57
+ return `https://docs.rs/crate/${pkg.name}/${pkg.version}/source/${path}`;
58
+ }
59
+
60
+ const texts = new Map();
61
+ function notice(name, source, text) {
62
+ if (!text.trim()) throw new Error(`Empty notice: ${name}`);
63
+ const digest = sha256(text);
64
+ if (!texts.has(digest)) texts.set(digest, text);
65
+ return { name, source, digest };
66
+ }
67
+
68
+ function cachedNotice(entry) {
69
+ if (!supplements.cache[entry.cache]?.sources.includes(entry.source)) throw new Error(`Unknown notice provenance for ${entry.cache}`);
70
+ return notice(entry.name, entry.source, read(join(cacheDir, entry.cache)));
71
+ }
72
+
73
+ function packageNotices(pkg) {
74
+ const dir = dirname(pkg.manifest_path);
75
+ const files = legalFiles(dir);
76
+ if (pkg.license_file) {
77
+ const path = relative(dir, resolve(dir, pkg.license_file)).replaceAll('\\', '/');
78
+ if (!files.includes(path)) files.push(path);
79
+ }
80
+ const notices = files.sort(compare).map((file) => notice(file, crateSource(pkg, file), read(join(dir, file))));
81
+ const extra = supplements.packages[`${pkg.name}@${pkg.version}`];
82
+ for (const entry of extra?.notices || []) {
83
+ if (entry.cache) {
84
+ notices.push(cachedNotice(entry));
85
+ } else {
86
+ const sourcePackage = entry.package ? byName.get(entry.package) : pkg;
87
+ if (!sourcePackage) throw new Error(`Missing supplemental source package ${entry.package}`);
88
+ let text = read(join(dirname(sourcePackage.manifest_path), entry.file));
89
+ if (entry.startLine) text = text.split('\n').slice(entry.startLine - 1, entry.endLine).join('\n') + '\n';
90
+ notices.push(notice(entry.name, crateSource(sourcePackage, entry.file), text));
91
+ }
92
+ }
93
+ if (!pkg.license) throw new Error(`Review missing license expression for ${pkg.name}@${pkg.version}`);
94
+ if (!notices.length) throw new Error(`No license notices found for ${pkg.name}@${pkg.version}; add a reviewed supplemental source`);
95
+ return { notices, reason: extra?.reason };
96
+ }
97
+
98
+ const upstreamLicense = notice('Cheerio MIT LICENSE',
99
+ 'https://github.com/cheeriojs/cheerio/blob/e3c7aaf9ed64fe3cb9a181e58a41c0fdd6dbfbfc/LICENSE',
100
+ read(join(cacheDir, 'cheerio-MIT.txt')));
101
+ const vendoredCheerioLicense = join(root, 'test', 'upstream', 'LICENSE');
102
+ if (existsSync(vendoredCheerioLicense) && read(vendoredCheerioLicense) !== read(join(cacheDir, 'cheerio-MIT.txt'))) {
103
+ throw new Error('Vendored Cheerio license differs from the pinned upstream notice');
104
+ }
105
+ const records = packages.map((pkg) => ({ pkg, ...packageNotices(pkg) }));
106
+ const runtimeReleases = Object.entries(runtime.releases).sort(([a], [b]) => compare(a, b))
107
+ .map(([version, entry]) => ({ version, ...entry, notices: entry.notices.map(cachedNotice) }));
108
+ const runtimePackages = Object.entries(runtime.packages).sort(([a], [b]) => compare(a, b))
109
+ .map(([key, entry]) => ({ key, ...entry, notices: entry.notices.map(cachedNotice) }));
110
+ const parts = [
111
+ '# Third-party notices',
112
+ '',
113
+ 'This file preserves third-party license and attribution notices for the Rust native addon and the upstream Cheerio compatibility tests. Rusteerio itself is distributed under its MIT LICENSE. Third-party components retain their own licenses.',
114
+ '',
115
+ 'Generated by `node scripts/generate-notices.mjs` from `cargo metadata --locked`, crate license files, and the pinned supplemental sources recorded in `scripts/notices/sources.json`. It includes build dependencies and dependencies for all target platforms; a particular binary may use a subset. Identical notice texts are reproduced once and referenced below. The html5ever, markup5ever and html5gum crates contain local observer-hook and opt-in UTF-16 transport patches; their complete patched source and original notices are included in `native/vendor`. Other dependency source files are unmodified.',
116
+ '',
117
+ `Cargo.lock SHA-256: \`${sha256(readFileSync(join(root, 'Cargo.lock')))}\`.`,
118
+ '',
119
+ '## Source availability',
120
+ '',
121
+ 'The native addon includes code from packages licensed under MPL-2.0. The complete, unmodified Source Code Form of each dependency is available without charge from the version-specific source download links below. These downloads contain the corresponding upstream source and notices. MPL-covered source remains available under MPL-2.0, whose full text is included in this file. The MIT license for Rusteerio does not restrict rights granted by third-party source licenses.',
122
+ '',
123
+ 'The locally patched html5ever and markup5ever components are licensed under MIT OR Apache-2.0; html5gum is licensed under MIT. Their upstream archives are linked below; the exact patched Source Code Form is shipped in the npm package under `native/vendor` and maintained in [this repository](https://github.com/context-dot-dev/rusteerio/tree/main/native/vendor). See `native/vendor/README.md` for the patch scope. Their original license and copyright files remain intact.',
124
+ '',
125
+ '## Cheerio compatibility tests',
126
+ '',
127
+ 'The vendored compatibility suite and fixtures come from Cheerio 1.2.0, commit `e3c7aaf9ed64fe3cb9a181e58a41c0fdd6dbfbfc`. Implementation imports are redirected to Rusteerio. Their original MIT notice is retained in `test/upstream/LICENSE` and reproduced here.',
128
+ '',
129
+ `- [${upstreamLicense.name}](#notice-${upstreamLicense.digest.slice(0, 16)}) — [upstream source](${upstreamLicense.source})`,
130
+ '',
131
+ `## Rust dependencies (${records.length} locked packages)`,
132
+ ];
133
+ for (const { pkg, notices, reason } of records) {
134
+ parts.push('', `### ${pkg.name} ${pkg.version}`, '', `Declared license: \`${pkg.license}\`.`, '',
135
+ `Source: [${pkg.name} ${pkg.version} source archive](https://crates.io/api/v1/crates/${pkg.name}/${pkg.version}/download).`);
136
+ if (reason) parts.push('', reason);
137
+ if (!pkg.source) {
138
+ const sourcePath = relative(root, dirname(pkg.manifest_path)).replaceAll('\\', '/');
139
+ if (sourcePath !== `native/vendor/${pkg.name}-${pkg.version}`) throw new Error(`Unexpected vendored dependency path: ${sourcePath}`);
140
+ parts.push('', `Locally patched source: \`${sourcePath}\` (included in the npm package). Original upstream licenses are retained.`);
141
+ }
142
+ parts.push('');
143
+ for (const entry of notices) parts.push(`- [${entry.name}](#notice-${entry.digest.slice(0, 16)}) — [notice source](${entry.source})`);
144
+ }
145
+ parts.push('', '## Rust standard-library runtime', '',
146
+ `The native addon builds used Rust ${runtimeReleases.map((entry) => entry.version).join(' and ')}. These curated notices preserve their standard-library copyright declarations, in-tree exceptions, compiler-builtins license, and the union of packages in their standard-library lockfiles. The lockfiles include dependencies for other targets and tests; a shipped binary uses a subset. New compiler releases require reviewing and refreshing this runtime notice snapshot.`, '',
147
+ 'The runtime snapshots in `scripts/notices/runtime.json` and their exact cached notice texts are independent of the local compiler used to regenerate this document. They contain no copied HTML documentation.');
148
+ for (const entry of runtimeReleases) {
149
+ parts.push('', `### Rust ${entry.version}`, '', `[Standard-library source](${entry.source}); [upstream lockfile](${entry.lockSource}); [upstream copyright metadata](${entry.annotationsSource}).`, '',
150
+ `Upstream standard-library Cargo.lock SHA-256: \`${entry.lockSha256}\`.`, '');
151
+ for (const text of entry.notices) parts.push(`- [${text.name}](#notice-${text.digest.slice(0, 16)}) — [notice source](${text.source})`);
152
+ parts.push('', 'In-tree attributions from the upstream copyright metadata:', '');
153
+ for (const item of entry.inTreeAttributions) {
154
+ parts.push(`- \`${item.path}\` — \`${item.spdx}\`; ${item.copyright.join('; ')}.`);
155
+ }
156
+ parts.push('', `Standard-library locked packages: ${entry.packages.map((key) => `\`${key}\``).join(', ')}.`);
157
+ }
158
+ parts.push('', '### Standard-library dependency notices');
159
+ for (const entry of runtimePackages) {
160
+ parts.push('', `#### ${entry.name} ${entry.version}`, '', `Declared license: \`${entry.license}\`. [Unmodified source archive](${entry.source}).`, '',
161
+ `Source archive SHA-256 from the upstream lockfile: \`${entry.checksum}\`.`, '');
162
+ for (const text of entry.notices) parts.push(`- [${text.name}](#notice-${text.digest.slice(0, 16)}) — [notice source](${text.source})`);
163
+ }
164
+ parts.push('', '## Full license and attribution texts');
165
+ for (const [digest, text] of [...texts].sort(([a], [b]) => compare(a, b))) {
166
+ const fence = '`'.repeat(Math.max(3, ...[...text.matchAll(/`+/g)].map((match) => match[0].length + 1)));
167
+ parts.push('', `<a id="notice-${digest.slice(0, 16)}"></a>`, '', `### Notice ${digest.slice(0, 16)}`, '', `${fence}text`, text.replace(/\n$/, ''), fence);
168
+ }
169
+ const generated = parts.join('\n') + '\n';
170
+ if (check) {
171
+ if (!existsSync(output) || read(output) !== generated) throw new Error('THIRD_PARTY_NOTICES.md is stale. Run npm run notices:generate and commit the updated file.');
172
+ console.log(`Third-party notices match ${records.length} locked Rust packages and Cheerio 1.2.0.`);
173
+ } else {
174
+ writeFileSync(output, generated);
175
+ console.log(`Generated THIRD_PARTY_NOTICES.md for ${records.length} locked Rust packages and Cheerio 1.2.0.`);
176
+ }
@@ -0,0 +1,13 @@
1
+ # License notice inputs
2
+
3
+ `npm run notices:generate` reads the full, locked Cargo dependency graph and copies license, notice, copyright, and attribution files from Cargo's local crate sources. Run `cargo fetch --locked` first on a fresh checkout. `npm run notices:check` compares the generated artifact without changing it. Neither command compiles code or changes the lockfile.
4
+
5
+ Some published crates omit their repository-level license files. `sources.json` records the exact package versions, primary upstream source URLs, SHA-256 checksums, and cached text used for those exceptions. Napi and html5ever repository commits come from each published crate's `.cargo_vcs_info.json`. The Neon and Xray MIT notices preserve origins credited in the upstream napi README files. A dependency update must review these version-specific exceptions; stale entries or missing notices fail generation.
6
+
7
+ For `fxhash` and `mac`, the published source declares an Apache-2.0 alternative without shipping the full license. Their original source/README notice is retained together with the standard Apache-2.0 text published in the locked `bitflags` crate. `selectors` ships its MPL-2.0 source notice without the full license; the identical license terms are reproduced from the locked `cssparser` crate. The generator also includes nested license files, including the Unicode notice in `regex-syntax`.
8
+
9
+ Cached texts are upstream legal notices, including original public author and copyright attributions. Preserve those attributions when refreshing the cache. The generated artifact intentionally contains no local registry paths, timestamps, or machine identifiers.
10
+
11
+ `runtime.json` separately records reviewed standard-library notices for Rust 1.96.1, 1.97.1, and 1.99.0, the releases used for the existing native builds. Their original `COPYRIGHT`, `LICENSE-MIT`, `LICENSE-APACHE`, `library/compiler-builtins/LICENSE.txt`, `license-metadata.json`, and `library/Cargo.lock` were obtained from the corresponding upstream release tags. Only standard-library in-tree attributions are retained from the copyright metadata. Published dependency source archives were checked against those lockfile checksums before retaining their legal notices. The union includes other targets and test dependencies so platform-specific subsets remain covered. Identical legal texts are cached once as `runtime-<digest>.txt`; no HTML documentation or Rust implementation sources are copied here. The check command rejects a compiler release that has no reviewed runtime notice snapshot.
12
+
13
+ To review another compiler release, obtain those same small upstream metadata/license files, collect each external standard-library package's original notices from its checksum-verified archive, and update `runtime.json` plus the checksum/provenance entries in `sources.json`. Packages that omit standalone licenses use their pinned upstream repository notices. `r-efi` and `r-efi-alloc` offer an Apache-2.0 alternative; the cache preserves their original AUTHORS and licensing statements together with that complete license text. Regenerate the artifact and review the changed notices before shipping binaries built with the new compiler.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 The Cheerio 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.
@@ -0,0 +1,20 @@
1
+ This software was written by the following people:
2
+
3
+ Adam Roben <adam@roben.org>
4
+ Akos Kiss <akiss@inf.u-szeged.hu>
5
+ Wojciech "Zarazek" Wiśniewski <balagane@op.pl>
6
+ Chris Paris <cap@chrisparis.org>
7
+ Clark Gaebel <cgaebel@mozilla.com>
8
+ Daniel Fath <daniel.fath7@gmail.com>
9
+ Huon Wilson <dbau.pp+github@gmail.com>
10
+ glennw <glennw@users.noreply.github.com>
11
+ Josh Matthews <josh@joshmatthews.net>
12
+ György Andrasek <jurily@gmail.com>
13
+ Keegan McAllister <kmcallister@mozilla.com>
14
+ Eunchong Yu <kroisse@gmail.com>
15
+ Manish Goregaokar <manishsmail@gmail.com>
16
+ Chris Morgan <me@chrismorgan.info>
17
+ Mátyás Mustoha <mmatyas@inf.u-szeged.hu>
18
+ Patrick Walton <pcwalton@mimiga.net>
19
+ Renato Zannon <renato@rrsz.com.br>
20
+ Simon Sapin <simon.sapin@exyr.org>
@@ -0,0 +1,8 @@
1
+ This project is Copyright 2014-2017 , The html5ever Project Developers (given in
2
+ the file AUTHORS).
3
+
4
+ Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5
+ http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
6
+ http://opensource.org/licenses/MIT>, at your option. All files in the project
7
+ carrying such notice may not be copied, modified, or distributed except
8
+ according to those terms.