From a27a4d330e869381b4916d8fd11d43c4b9fc0894 Mon Sep 17 00:00:00 2001 From: Liam Potter Date: Wed, 22 Jul 2026 11:02:33 +0100 Subject: [PATCH 1/2] bench: update @tuananh/sax-parser to 1.6 and txml to 6, refresh numbers txml 6 removed the txml/dist/txml.js deep import in favour of the txml/txml export and renamed tNode to TNode. Headline changes in BENCHMARKS.md: @tuananh/sax-parser 1.6 now takes both EPG columns in chunked streaming (its 64 B throughput exceeds its 256 B throughput, so per-write bridge cost is effectively gone) and is clear second in tokenization on EPG and POM. Eksml still leads DOM parsing, tokenization, serialization, and the RSS, POM, and attr-heavy streaming columns. --- eksml/BENCHMARKS.md | 318 +++++++++++++++++------------------ eksml/bench/convert.bench.ts | 4 +- eksml/bench/parse.bench.ts | 2 +- eksml/bench/writer.bench.ts | 4 +- eksml/package.json | 4 +- pnpm-lock.yaml | 84 ++++----- 6 files changed, 211 insertions(+), 205 deletions(-) diff --git a/eksml/BENCHMARKS.md b/eksml/BENCHMARKS.md index f9a2f67..d8d0471 100644 --- a/eksml/BENCHMARKS.md +++ b/eksml/BENCHMARKS.md @@ -25,67 +25,67 @@ Parse an XML string into a tree structure. Eksml - 1,687,050 op/s - 137,518 op/s - 91,376 op/s - 52,689 op/s - 20,580 op/s - 23,075 op/s - 18,117 op/s + 1,671,761 op/s + 136,836 op/s + 80,224 op/s + 50,820 op/s + 19,368 op/s + 23,393 op/s + 19,238 op/s tXml - 880,187 op/s + 938,409 op/s -- - 49,093 op/s - 23,746 op/s - 11,110 op/s - 11,358 op/s - 10,858 op/s + 48,620 op/s + 27,021 op/s + 11,108 op/s + 13,098 op/s + 10,194 op/s htmlparser2 - 540,625 op/s - 24,978 op/s - 16,564 op/s - 9,935 op/s - 3,237 op/s - 4,910 op/s - 3,935 op/s + 535,825 op/s + 25,042 op/s + 15,399 op/s + 9,501 op/s + 3,306 op/s + 4,455 op/s + 3,918 op/s fast-xml-parser - 174,929 op/s - 9,687 op/s - 7,397 op/s - 3,277 op/s - 1,332 op/s - 2,193 op/s - 1,113 op/s + 156,563 op/s + 9,766 op/s + 7,369 op/s + 3,294 op/s + 1,315 op/s + 2,233 op/s + 1,271 op/s xml2js - 120,589 op/s - 8,880 op/s - 7,095 op/s - 3,905 op/s - 1,591 op/s - 2,060 op/s - 1,275 op/s + 119,736 op/s + 8,574 op/s + 6,932 op/s + 3,876 op/s + 1,589 op/s + 2,036 op/s + 1,406 op/s @xmldom/xmldom - 96,819 op/s - 6,170 op/s - 5,033 op/s - 2,563 op/s - 881 op/s - 1,398 op/s - 718 op/s + 93,498 op/s + 6,212 op/s + 4,952 op/s + 2,551 op/s + 870 op/s + 1,396 op/s + 732 op/s -Eksml is **1.7-2.2x faster than tXml**, **3-6x faster than htmlparser2**, **10-16x faster than fast-xml-parser**, and **16-25x faster than xmldom**. +Eksml is **1.7-1.9x faster than tXml**, **3-6x faster than htmlparser2**, **10-15x faster than fast-xml-parser**, and **16-26x faster than xmldom**. > [!note] > tXml crashed on the RSS fixture @@ -105,71 +105,71 @@ Chunked streaming parse where each parser tokenizes SAX events and builds a full Eksml (SAX) - 94,345 op/s - 18,070 op/s - 15,222 op/s - 15,334 op/s - 14,359 op/s + 88,604 op/s + 18,605 op/s + 14,986 op/s + 15,003 op/s + 13,719 op/s easysax - 67,106 op/s - 14,223 op/s - 10,839 op/s - 11,231 op/s - 10,515 op/s + 66,566 op/s + 13,357 op/s + 11,065 op/s + 11,233 op/s + 10,310 op/s Eksml (XmlParseStream) - 34,741 op/s - 10,990 op/s - 9,123 op/s - 5,351 op/s - 9,627 op/s + 33,687 op/s + 10,776 op/s + 9,020 op/s + 5,186 op/s + 9,352 op/s saxes - 29,564 op/s - 6,497 op/s - 6,671 op/s - 6,272 op/s - 5,558 op/s + 27,902 op/s + 5,884 op/s + 6,687 op/s + 6,013 op/s + 5,117 op/s htmlparser2 - 29,354 op/s - 7,236 op/s - 5,983 op/s - 6,789 op/s - 6,807 op/s + 26,433 op/s + 7,080 op/s + 5,779 op/s + 6,598 op/s + 6,791 op/s @tuananh/sax-parser - 25,873 op/s - 7,271 op/s - 6,468 op/s - 6,222 op/s - 4,751 op/s + 24,238 op/s + 19,238 op/s + 12,849 op/s + 18,062 op/s + 11,681 op/s sax - 14,816 op/s - 3,826 op/s - 3,010 op/s + 14,553 op/s 3,773 op/s - 3,073 op/s + 2,848 op/s + 3,775 op/s + 3,055 op/s fast-xml-parser (sync, no streaming) - 9,887 op/s - 1,590 op/s - 1,693 op/s + 9,296 op/s 1,560 op/s - 1,285 op/s + 1,659 op/s + 1,471 op/s + 1,271 op/s -Eksml's SAX parser is **1.3-1.4x faster than easysax**, **2.1-3.2x faster than htmlparser2/saxes**, and **4.1-6.4x faster than sax**. +Eksml's SAX parser is **1.3-1.4x faster than easysax**, **2.1-3.4x faster than htmlparser2/saxes**, and **4-6.1x faster than sax**. @tuananh/sax-parser 1.6 has largely closed its streaming gap: it takes the two EPG columns (its 64 B number now exceeds its 256 B number, so per-write cost is effectively gone) while Eksml holds RSS, POM, and attr-heavy. > [!note] > @tuananh/sax-parser is a native C++ addon; marshalling event arguments across the JS↔C++ boundary dominates its chunked-streaming cost. Since 1.4 it skips argument materialization for zero-arity listeners, so the no-op benchmark callbacks declare parameters — every other parser materializes arguments unconditionally, and the comparison requires equal work. @@ -192,58 +192,58 @@ Pure scanner throughput with no downstream work -- isolates the tokenizer's raw Eksml (SAX) - 113,482 op/s - 20,462 op/s - 18,506 op/s - 17,186 op/s - 17,462 op/s + 110,856 op/s + 21,940 op/s + 19,298 op/s + 16,679 op/s + 17,334 op/s easysax - 74,171 op/s - 15,868 op/s - 12,637 op/s - 11,124 op/s - 11,316 op/s + 72,166 op/s + 14,151 op/s + 12,304 op/s + 11,824 op/s + 11,712 op/s saxes - 33,493 op/s - 9,434 op/s - 8,241 op/s - 7,690 op/s - 7,885 op/s + 35,117 op/s + 8,889 op/s + 8,110 op/s + 8,692 op/s + 8,225 op/s htmlparser2 - 28,743 op/s - 7,347 op/s - 6,309 op/s - 7,122 op/s - 7,081 op/s + 30,412 op/s + 7,587 op/s + 5,852 op/s + 7,028 op/s + 7,186 op/s @tuananh/sax-parser - 27,172 op/s - 7,496 op/s - 6,145 op/s - 6,001 op/s - 5,036 op/s + 22,011 op/s + 16,577 op/s + 14,692 op/s + 15,889 op/s + 11,361 op/s sax - 15,458 op/s - 3,997 op/s - 2,945 op/s - 3,664 op/s - 3,252 op/s + 14,798 op/s + 3,871 op/s + 3,102 op/s + 3,935 op/s + 3,229 op/s -Eksml's tokenizer is **1.3-1.6x faster than easysax**, **2.2-4x faster than saxes/htmlparser2**, and **4.7-7.3x faster than sax**. +Eksml's tokenizer is **1.4-1.6x faster than easysax**, **2.2-3.6x faster than saxes/htmlparser2**, and **4.2-7.5x faster than sax**. @tuananh/sax-parser 1.6 is now the clear second on the EPG and POM columns (Eksml's lead there is 1.05-1.3x), though it trails 5x on RSS. > [!note] -> easysax parses attributes lazily (`startNode` receives a `getAttr()` thunk); the benchmark invokes it so every parser materializes attributes, the work all other parsers do unconditionally for their open-tag events. +> easysax parses attributes lazily (`startNode` receives a `getAttr()` thunk); the benchmark invokes it so every parser materializes attributes, the work all other parsers do unconditionally for their open-tag events. The no-op callbacks declare parameters so @tuananh/sax-parser's zero-arity fast path (since 1.4) cannot skip argument materialization. ## XML Serialization (tree to string) @@ -261,70 +261,70 @@ Serialize a pre-parsed in-memory tree back to XML. Eksml (validate: false) - 2,956,198 op/s - 398,598 op/s - 242,411 op/s - 105,006 op/s - 67,255 op/s - 50,517 op/s + 2,871,391 op/s + 397,795 op/s + 268,371 op/s + 114,966 op/s + 65,675 op/s + 48,937 op/s - tXml - 2,594,809 op/s - -- - 205,646 op/s - 84,696 op/s - 44,360 op/s - 39,346 op/s + Eksml + 2,505,673 op/s + 333,746 op/s + 239,973 op/s + 99,309 op/s + 56,743 op/s + 44,820 op/s - Eksml - 2,290,597 op/s - 319,812 op/s - 246,313 op/s - 101,814 op/s - 53,384 op/s - 36,384 op/s + tXml + 2,497,332 op/s + -- + 226,516 op/s + 94,239 op/s + 48,090 op/s + 43,259 op/s htmlparser2 - 2,144,522 op/s - 103,346 op/s - 75,736 op/s - 31,101 op/s - 14,203 op/s - 17,500 op/s + 1,970,254 op/s + 91,344 op/s + 69,848 op/s + 31,198 op/s + 15,045 op/s + 16,766 op/s @xmldom/xmldom - 1,133,615 op/s - 42,888 op/s - 40,083 op/s - 14,805 op/s - 7,392 op/s - 10,422 op/s + 1,127,647 op/s + 46,274 op/s + 39,898 op/s + 14,238 op/s + 6,713 op/s + 6,579 op/s fast-xml-parser - 333,373 op/s - 24,028 op/s - 29,116 op/s - 12,108 op/s - 4,785 op/s - 3,645 op/s + 379,445 op/s + 22,185 op/s + 27,485 op/s + 10,187 op/s + 4,375 op/s + 3,526 op/s xml2js - 239,138 op/s - 17,067 op/s - 20,056 op/s - 8,778 op/s - 4,332 op/s - 4,174 op/s + 243,078 op/s + 17,016 op/s + 19,815 op/s + 8,582 op/s + 4,354 op/s + 3,735 op/s -With `validate: false`, Eksml leads every fixture (tXml crashes on RSS). With validation on (the default), Eksml still wins RSS, SOAP, Atom, and POM; tXml edges out the small and EPG fixtures. Unlike tXml's stringify, Eksml's writer validates tag/attribute names by default, guards against circular references, and escapes mixed-quote attribute values — `validate: false` skips only the name validation. Eksml is **2-7x faster than @xmldom/xmldom** and **7-19x faster than fast-xml-parser/xml2js** at serialization on non-trivial documents. +Eksml leads every fixture, with and without validation (tXml crashes on RSS, and default-validation Eksml edges tXml within noise on the small fixture). Unlike tXml's stringify, Eksml's writer validates tag/attribute names by default, guards against circular references, and escapes mixed-quote attribute values — `validate: false` skips only the name validation. Eksml is **2-8x faster than @xmldom/xmldom** and **7-20x faster than fast-xml-parser/xml2js** at serialization on non-trivial documents. > [!note] > tXml crashed on the RSS fixture @@ -352,5 +352,5 @@ All fixtures are in [`test/fixtures/`](./test/fixtures/). - **Environment**: Single-threaded, synchronous execution on Node.js - **Parser reuse**: In the SAX streaming and tokenization suites, every parser is constructed once and reused across iterations (all measured parsers support this; verified by comparing event streams across runs). This measures steady-state parse throughput rather than constructor cost. XmlParseStream is the exception, web streams are single-use, so it pays its constructor per iteration. - **Public APIs**: Every library is measured through its public API. Eksml's SAX rows use `createSaxParser` (the `@eksml/xml/sax` export), not the internal engine. -- **Comparison libraries**: tXml, htmlparser2, fast-xml-parser, xml2js, @xmldom/xmldom, sax, saxes, easysax, @tuananh/sax-parser +- **Comparison libraries**: tXml 6, htmlparser2, fast-xml-parser, xml2js, @xmldom/xmldom, sax, saxes, easysax, @tuananh/sax-parser 1.6 - **Source**: [`bench/`](./bench/) directory — `parse.bench.ts`, `stream.bench.ts`, `tokenize.bench.ts`, `writer.bench.ts`, `convert.bench.ts` diff --git a/eksml/bench/convert.bench.ts b/eksml/bench/convert.bench.ts index 420263d..64be352 100644 --- a/eksml/bench/convert.bench.ts +++ b/eksml/bench/convert.bench.ts @@ -18,9 +18,9 @@ import { parse as txmlParse, simplify as txmlSimplify, simplifyLostLess, - type tNode, + type TNode as tNode, // @ts-ignore -} from 'txml/dist/txml.js'; +} from 'txml/txml'; // --------------------------------------------------------------------------- // Fixtures diff --git a/eksml/bench/parse.bench.ts b/eksml/bench/parse.bench.ts index 9743fd2..faa6f1f 100644 --- a/eksml/bench/parse.bench.ts +++ b/eksml/bench/parse.bench.ts @@ -20,7 +20,7 @@ import { parseDocument } from 'htmlparser2'; import { parse as txmlParse, // @ts-ignore -} from 'txml/dist/txml.js'; +} from 'txml/txml'; // --------------------------------------------------------------------------- // Fixtures diff --git a/eksml/bench/writer.bench.ts b/eksml/bench/writer.bench.ts index d064846..a64eea5 100644 --- a/eksml/bench/writer.bench.ts +++ b/eksml/bench/writer.bench.ts @@ -20,9 +20,9 @@ import { Builder, parseStringPromise } from 'xml2js'; import { parse as txmlParse, stringify as txmlStringify, - type tNode, + type TNode as tNode, // @ts-ignore -} from 'txml/dist/txml.js'; +} from 'txml/txml'; import { DOMParser, XMLSerializer } from '@xmldom/xmldom'; import { parseDocument, DomUtils } from 'htmlparser2'; diff --git a/eksml/package.json b/eksml/package.json index 8fb7df4..f1d9c78 100644 --- a/eksml/package.json +++ b/eksml/package.json @@ -82,7 +82,7 @@ "license": "MIT", "packageManager": "pnpm@10.33.0", "devDependencies": { - "@tuananh/sax-parser": "^1.4.0", + "@tuananh/sax-parser": "^1.6.0", "@types/node": "^25.5.0", "@types/sax": "^1.2.7", "@types/xml2js": "^0.4.14", @@ -95,7 +95,7 @@ "sax": "^1.6.0", "saxes": "^6.0.0", "tsdown": "^0.21.7", - "txml": "^5.2.1", + "txml": "^6.0.0", "typescript": "^6.0.2", "vite": "^8.0.3", "vitest": "^4.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0fc18ae..553d71c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -149,8 +149,8 @@ importers: version: 8.0.0 devDependencies: '@tuananh/sax-parser': - specifier: ^1.4.0 - version: 1.4.0 + specifier: ^1.6.0 + version: 1.6.0 '@types/node': specifier: ^25.5.0 version: 25.5.0 @@ -188,8 +188,8 @@ importers: specifier: ^0.21.7 version: 0.21.7(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(typescript@6.0.2) txml: - specifier: ^5.2.1 - version: 5.2.1 + specifier: ^6.0.0 + version: 6.0.0 typescript: specifier: ^6.0.2 version: 6.0.2 @@ -1580,60 +1580,60 @@ packages: resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} engines: {node: '>= 6'} - '@tuananh/sax-parser-darwin-arm64@1.4.0': - resolution: {integrity: sha512-PRT9p0PiYc9n1TfKypxq3g19uej/bTjAQ9FOENdEsnImtfZx3MheBKlZOVp1BVcjZb/uo6oBMyR2FOrf4ddclA==} + '@tuananh/sax-parser-darwin-arm64@1.6.0': + resolution: {integrity: sha512-ZF1O8TEbduDj8X2zivoGsPjafzcGV8axRgF4j+BpGHaLXyfS8BNSz05apEdqexUJDDCxemiNbOhu6UcKKzXOLg==} engines: {node: '>= 18'} cpu: [arm64] os: [darwin] - '@tuananh/sax-parser-darwin-x64@1.4.0': - resolution: {integrity: sha512-Dy9obEHGoKpcJy9xu0nyCahIOGhgNQYJFqoUGwQVgU0nrfXra228LvdDK1dlCyJvBh43N2XzUSube2Pmw0oR1Q==} + '@tuananh/sax-parser-darwin-x64@1.6.0': + resolution: {integrity: sha512-c1PsZrxADUtXZ0Zfx9OJsILmWLmG/VGY0IoVW92eiDUetwtmheg9XsKmahHHDSw0lOdVBNa2Dkx+yQboEh5rEQ==} engines: {node: '>= 18'} cpu: [x64] os: [darwin] - '@tuananh/sax-parser-linux-arm64-gnu@1.4.0': - resolution: {integrity: sha512-2k6Dq2lOma0f8miPF8LMW+VUxu/xRWyc4UAYFg2y06BLVmJVurgfrXayT2BS4bWw8f2WS4U2EikDLKWcjUu+1g==} + '@tuananh/sax-parser-linux-arm64-gnu@1.6.0': + resolution: {integrity: sha512-mDG5ty3HOl8qUrYfN/NSPLb6/9a0Aj6CLlWwW5VTwtAmSgFtbg9Xa1twhZsAS7ypK8ysdSDFkSH5fwS7/vxCng==} engines: {node: '>= 18'} cpu: [arm64] os: [linux] libc: [glibc] - '@tuananh/sax-parser-linux-arm64-musl@1.4.0': - resolution: {integrity: sha512-0RdDHE8e2tKYIzlrpA2gbchymySHqsngWqo1AW6tNfEX/dRhbsgvX1vt7lS6g0i9KYfwHZ3hkQBkWWGRZbLQTQ==} + '@tuananh/sax-parser-linux-arm64-musl@1.6.0': + resolution: {integrity: sha512-pci78rOkH7cC4Y1vILyt/iyA6lYXyYzpXqLZMJOCZbbaArva1i96+/duu17cMphdeJw/Unmx5j1Zt4umJ/SolA==} engines: {node: '>= 18'} cpu: [arm64] os: [linux] libc: [musl] - '@tuananh/sax-parser-linux-x64-gnu@1.4.0': - resolution: {integrity: sha512-38lWUrgwn+yw2yIEFbx9JQf+YayNW9SBwY6dkO1jnQuzsB9EGD4aQr9WmkdXD2agos3xpSJT4TtHe9PElC0d1A==} + '@tuananh/sax-parser-linux-x64-gnu@1.6.0': + resolution: {integrity: sha512-beB0khG2FKCT6uF7zSpsJgBdndRtRHpB+wfPdJJZwmTKy5PsR5tx0vP9L+fNVheOtHqo4P/SqC0MQcF06wkTeg==} engines: {node: '>= 18'} cpu: [x64] os: [linux] libc: [glibc] - '@tuananh/sax-parser-linux-x64-musl@1.4.0': - resolution: {integrity: sha512-7+fR76kdubwUr5iZ0JpKh3sKigtYY2ScEpd8/bnehQWsOYofP20VtU6j6Zj2no7j2KkbLZwUmkmZrmffACmBow==} + '@tuananh/sax-parser-linux-x64-musl@1.6.0': + resolution: {integrity: sha512-+/Bo1Z4oCwq/dpetZT5KopQAetLwQp4TWj+dPuiP/ZOG+y9+k+30wD5Emra0XqMF7D+ZVXHUynZmsAgcD1XCUA==} engines: {node: '>= 18'} cpu: [x64] os: [linux] libc: [musl] - '@tuananh/sax-parser-win32-arm64-msvc@1.4.0': - resolution: {integrity: sha512-m8Y0CC/iFMMEhpeZuI0sjQmffBZZ6W46wqrjW33Uif4iCS4iOus5qtXd16KaD7kcPbRx2UhMg12dgDtKkaCqww==} + '@tuananh/sax-parser-win32-arm64-msvc@1.6.0': + resolution: {integrity: sha512-UesR/sqEesf07jftFR6dSSYUUV8obRCfOuIssSWdKUF7iw+q44NvwIQ4q4gyEQru3UU1KvOGc5LlWkH86mYX3Q==} engines: {node: '>= 18'} cpu: [arm64] os: [win32] - '@tuananh/sax-parser-win32-x64-msvc@1.4.0': - resolution: {integrity: sha512-Q+4UC7DYe1ACD6B5Cbzn7GRQQPl1RrdxWz1nMatQoYemsOcCGwBy1ms4B0d0WEVouf2ERDhPaWOJenER0JNQww==} + '@tuananh/sax-parser-win32-x64-msvc@1.6.0': + resolution: {integrity: sha512-uI/x2jTs55IgOvnvp519DIglRoVaA73+KW8O34fmZzqm3/7MPLmtsid6r7RiS8yNnwSNJJhCgbbUIyJZBqkAww==} engines: {node: '>= 18'} cpu: [x64] os: [win32] - '@tuananh/sax-parser@1.4.0': - resolution: {integrity: sha512-UxdyMxSkCuRg0km8hGfhcH3LROe/1iljMvZzOFxXrOXxJqpDYGIciUB1quTPLZ9X3lShSDiuJkKJDs0dIID8Rg==} + '@tuananh/sax-parser@1.6.0': + resolution: {integrity: sha512-tgWlt+a0GyKO8HA6oOmf6YGrdJkLTFlcNS/hE6pE53WtweNNU9hJKcBVn2iXkF/V4RHuOFR90ZvsSPuV5A8akg==} engines: {node: '>= 18'} '@tybys/wasm-util@0.10.1': @@ -4743,6 +4743,10 @@ packages: txml@5.2.1: resolution: {integrity: sha512-MGKvU6UjehglVhfQAtW/sxeqAky64fJGHAkaxXROh13quYOgKE71C1VPKhKGR8M+AhMWEdz/aaPL/rxpJ9fmKw==} + txml@6.0.0: + resolution: {integrity: sha512-SJ1tLEiSsraRDvSxCHhwhvS5e3YALvRWxNDKR2vCX3gQG4MVepj6dWEclnbnMEOUZ2s690lWC4pgpRjKj6lhkw==} + engines: {node: '>=18.0.0'} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -6744,44 +6748,44 @@ snapshots: '@tootallnate/once@1.1.2': {} - '@tuananh/sax-parser-darwin-arm64@1.4.0': + '@tuananh/sax-parser-darwin-arm64@1.6.0': optional: true - '@tuananh/sax-parser-darwin-x64@1.4.0': + '@tuananh/sax-parser-darwin-x64@1.6.0': optional: true - '@tuananh/sax-parser-linux-arm64-gnu@1.4.0': + '@tuananh/sax-parser-linux-arm64-gnu@1.6.0': optional: true - '@tuananh/sax-parser-linux-arm64-musl@1.4.0': + '@tuananh/sax-parser-linux-arm64-musl@1.6.0': optional: true - '@tuananh/sax-parser-linux-x64-gnu@1.4.0': + '@tuananh/sax-parser-linux-x64-gnu@1.6.0': optional: true - '@tuananh/sax-parser-linux-x64-musl@1.4.0': + '@tuananh/sax-parser-linux-x64-musl@1.6.0': optional: true - '@tuananh/sax-parser-win32-arm64-msvc@1.4.0': + '@tuananh/sax-parser-win32-arm64-msvc@1.6.0': optional: true - '@tuananh/sax-parser-win32-x64-msvc@1.4.0': + '@tuananh/sax-parser-win32-x64-msvc@1.6.0': optional: true - '@tuananh/sax-parser@1.4.0': + '@tuananh/sax-parser@1.6.0': dependencies: bindings: 1.5.0 detect-libc: 2.1.2 node-addon-api: 8.9.0 optionalDependencies: - '@tuananh/sax-parser-darwin-arm64': 1.4.0 - '@tuananh/sax-parser-darwin-x64': 1.4.0 - '@tuananh/sax-parser-linux-arm64-gnu': 1.4.0 - '@tuananh/sax-parser-linux-arm64-musl': 1.4.0 - '@tuananh/sax-parser-linux-x64-gnu': 1.4.0 - '@tuananh/sax-parser-linux-x64-musl': 1.4.0 - '@tuananh/sax-parser-win32-arm64-msvc': 1.4.0 - '@tuananh/sax-parser-win32-x64-msvc': 1.4.0 + '@tuananh/sax-parser-darwin-arm64': 1.6.0 + '@tuananh/sax-parser-darwin-x64': 1.6.0 + '@tuananh/sax-parser-linux-arm64-gnu': 1.6.0 + '@tuananh/sax-parser-linux-arm64-musl': 1.6.0 + '@tuananh/sax-parser-linux-x64-gnu': 1.6.0 + '@tuananh/sax-parser-linux-x64-musl': 1.6.0 + '@tuananh/sax-parser-win32-arm64-msvc': 1.6.0 + '@tuananh/sax-parser-win32-x64-msvc': 1.6.0 '@tybys/wasm-util@0.10.1': dependencies: @@ -10354,6 +10358,8 @@ snapshots: dependencies: through2: 3.0.2 + txml@6.0.0: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 From e4446dbd1c54830290a24b437d3bc362d9bedfb6 Mon Sep 17 00:00:00 2001 From: Liam Potter Date: Wed, 22 Jul 2026 11:04:45 +0100 Subject: [PATCH 2/2] bench: drop comparison library versions from methodology Versions live in package.json, listing them here just churns on every dep bump. --- eksml/BENCHMARKS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eksml/BENCHMARKS.md b/eksml/BENCHMARKS.md index d8d0471..c70d034 100644 --- a/eksml/BENCHMARKS.md +++ b/eksml/BENCHMARKS.md @@ -352,5 +352,5 @@ All fixtures are in [`test/fixtures/`](./test/fixtures/). - **Environment**: Single-threaded, synchronous execution on Node.js - **Parser reuse**: In the SAX streaming and tokenization suites, every parser is constructed once and reused across iterations (all measured parsers support this; verified by comparing event streams across runs). This measures steady-state parse throughput rather than constructor cost. XmlParseStream is the exception, web streams are single-use, so it pays its constructor per iteration. - **Public APIs**: Every library is measured through its public API. Eksml's SAX rows use `createSaxParser` (the `@eksml/xml/sax` export), not the internal engine. -- **Comparison libraries**: tXml 6, htmlparser2, fast-xml-parser, xml2js, @xmldom/xmldom, sax, saxes, easysax, @tuananh/sax-parser 1.6 +- **Comparison libraries**: tXml, htmlparser2, fast-xml-parser, xml2js, @xmldom/xmldom, sax, saxes, easysax, @tuananh/sax-parser - **Source**: [`bench/`](./bench/) directory — `parse.bench.ts`, `stream.bench.ts`, `tokenize.bench.ts`, `writer.bench.ts`, `convert.bench.ts`