diff --git a/.changeset/faster-streaming-fences.md b/.changeset/faster-streaming-fences.md new file mode 100644 index 0000000..a970ebb --- /dev/null +++ b/.changeset/faster-streaming-fences.md @@ -0,0 +1,7 @@ +--- +'@tanstack/markdown': patch +--- + +Speed up plain fenced-code rendering with the React streaming extension by retaining completed code text nodes across updates. Reduce parser overhead and shrink every affected public bundle without adding runtime dependencies. + +Add unfinished-fence benchmarks at 4, 16, and 64 KiB, browser comparisons with Streamdown and streaming-markdown, and regression coverage for streamed text, custom code components, and hydration. diff --git a/docs/comparison.md b/docs/comparison.md index e2cb35c..5acb030 100644 --- a/docs/comparison.md +++ b/docs/comparison.md @@ -40,11 +40,11 @@ These repository benchmarks bundle representative browser entry points from pinn | Entry | Gzip | Brotli | | --- | ---: | ---: | -| `@tanstack/markdown/parser` | 5.0 KB | 4.6 KB | -| `@tanstack/markdown/html` | 6.8 KB | 6.2 KB | +| `@tanstack/markdown/parser` | 4.9 KB | 4.5 KB | +| `@tanstack/markdown/html` | 6.7 KB | 6.2 KB | | `@tanstack/markdown/react` | 6.7 KB | 6.2 KB | -| React with streaming extension | 6.9 KB | 6.4 KB | -| `@tanstack/markdown/octane` | 6.7 KB | 6.2 KB | +| React with streaming extension | 6.9 KB | 6.3 KB | +| `@tanstack/markdown/octane` | 6.6 KB | 6.1 KB | | Marked | 12.5 KB | 11.5 KB | | micromark | 15.4 KB | 13.7 KB | | markdown-wasm JS + WASM | 31.3 KB | 26.4 KB | diff --git a/docs/guides/ai-streaming.md b/docs/guides/ai-streaming.md index 349196c..fb3d5fe 100644 --- a/docs/guides/ai-streaming.md +++ b/docs/guides/ai-streaming.md @@ -65,6 +65,8 @@ Unclosed emphasis, code spans, links, and other inline delimiters remain literal The package reparses the accumulated response rather than maintaining parser state between updates. Batch very small transport tokens into normal UI updates when responses are unusually long or tokens arrive faster than the screen should repaint. +With the streaming extension enabled, React keeps completed groups of plain code lines in stable text nodes. Appending code updates the trailing group instead of replacing the entire block's text, which reduces browser layout work. Custom code components still receive string children, and highlighters keep their existing HTML rendering path. + ## Security AI output is untrusted content: diff --git a/docs/guides/performance.md b/docs/guides/performance.md index 070f162..d5bb2f6 100644 --- a/docs/guides/performance.md +++ b/docs/guides/performance.md @@ -12,11 +12,11 @@ The generated browser bundle report records: | Entry | Gzip | Brotli | | --- | ---: | ---: | -| parser | 5.0 KB | 4.6 KB | -| HTML renderer | 6.8 KB | 6.2 KB | +| parser | 4.9 KB | 4.5 KB | +| HTML renderer | 6.7 KB | 6.2 KB | | React adapter | 6.7 KB | 6.2 KB | -| Octane adapter | 6.7 KB | 6.2 KB | -| React adapter with streaming extension | 6.9 KB | 6.4 KB | +| Octane adapter | 6.6 KB | 6.1 KB | +| React adapter with streaming extension | 6.9 KB | 6.3 KB | | Streaming extension | 0.3 KB | 0.3 KB | | docs preset | 2.3 KB | 2.1 KB | | callouts extension | 0.3 KB | 0.3 KB | @@ -25,6 +25,10 @@ Framework runtimes are externalized from their adapters, and the highlighter mea The maintained benchmark records parse, pre-parsed rendering, parse-and-render, external-highlighter, and progressive AI-response paths against pinned comparison packages. Timings vary by runtime, CPU, fixture shape, and dependency version, so the generated [benchmark report](https://github.com/TanStack/markdown/blob/main/reports/benchmarks.md) is the only source of current CPU results. +Streaming cases include unfinished TypeScript fences at 4, 16, and 64 KiB, a tilde fence, and a fence that closes after 64 KiB. Every 32-character update reparses the accumulated source. The report records total replay time and per-update latency, including p50, p95, and maximum latency while the fence is open. Separate parse-only, plain HTML, and real `@tanstack/highlight` runs show how the costs change as code accumulates. These Node measurements exclude browser rendering and framework updates. + +The separate [streaming library comparison](https://github.com/TanStack/markdown/blob/main/reports/streaming-browser.md) measures TanStack Markdown React, Streamdown, and streaming-markdown in headless Chrome. It keeps React roots and incremental parser state alive between chunks, checks that the growing code stays visible, and records DOM update and forced-layout latency with plain code rendering. Run it with `pnpm run bench:streaming` after installing Chromium through `pnpm exec playwright install chromium`, or use an installed Chrome with `BENCH_BROWSER_CHANNEL=chrome pnpm run bench:streaming`. + ## Use the narrow entry point ```ts diff --git a/package.json b/package.json index b215175..9f45478 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "build": "rm -rf dist && tsc -p tsconfig.build.json", "typecheck": "tsc --noEmit", "test": "vitest run", + "test:streaming-browser": "tsx scripts/verify-streaming-browser.ts", "test:corpus": "vitest run tests/corpus.test.tsx", "corpus:audit": "tsx scripts/audit-corpus.ts", "corpus:audit:tanstack": "tsx scripts/audit-tanstack-corpus.ts", @@ -90,6 +91,7 @@ "release:publish": "pnpm run verify && changeset publish", "conformance": "tsx scripts/conformance.ts", "bench": "tsx scripts/bench.ts", + "bench:streaming": "tsx scripts/bench-streaming-browser.ts", "size": "tsx scripts/measure-size.ts", "research": "pnpm run conformance && pnpm run size && pnpm run bench", "verify": "pnpm test && pnpm run typecheck && pnpm run build && pnpm run docs:verify && pnpm run test:skills && pnpm run research && pnpm pack --dry-run", @@ -126,11 +128,14 @@ "marked": "^18.0.5", "micromark": "^4.0.2", "octane": "0.1.12", + "playwright": "1.63.0", "react": "^19.0.0", "react-dom": "^19.0.0", "rehype-stringify": "^10.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", + "streamdown": "2.6.0", + "streaming-markdown": "0.2.15", "tsx": "^4.20.0", "typescript": "^5.8.0", "unified": "^11.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b62a5ef..625b64c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,9 @@ importers: octane: specifier: 0.1.12 version: 0.1.12(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.5(@types/node@24.13.2)(tsx@4.22.4)(yaml@2.9.0)) + playwright: + specifier: 1.63.0 + version: 1.63.0 react: specifier: ^19.0.0 version: 19.2.7 @@ -65,6 +68,12 @@ importers: remark-rehype: specifier: ^11.1.2 version: 11.1.2 + streamdown: + specifier: 2.6.0 + version: 2.6.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + streaming-markdown: + specifier: 0.2.15 + version: 0.2.15 tsx: specifier: ^4.20.0 version: 4.22.4 @@ -829,6 +838,9 @@ packages: '@types/react@19.2.17': resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -903,10 +915,17 @@ packages: character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + check-error@2.1.3: resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} engines: {node: '>= 16'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -954,6 +973,10 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} @@ -972,6 +995,10 @@ packages: engines: {node: '>=18'} hasBin: true + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + esrap@2.3.0: resolution: {integrity: sha512-GQ/7RN8uOtEfNpzZzBMTzW9JBcX42oaSVtPzdF+6cEL8pqIL094iUpr9jzYGn4O4P/1S60dJ6izyT8F4LYARng==} peerDependencies: @@ -980,6 +1007,9 @@ packages: '@typescript-eslint/types': optional: true + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -1013,12 +1043,36 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-sanitize@5.0.2: + resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==} + hast-util-to-html@9.0.5: resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-to-parse5@8.0.1: + resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} + hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} @@ -1029,6 +1083,21 @@ packages: import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -1051,6 +1120,9 @@ packages: linkify-it@5.0.1: resolution: {integrity: sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loupe@3.2.1: resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} @@ -1061,20 +1133,64 @@ packages: resolution: {integrity: sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==} hasBin: true + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + markdown-wasm@1.2.0: resolution: {integrity: sha512-S12OTkyXCkOgI1n1rZY9cg4bK/PGu80Emjpvwp8BEjwCxhPV3yddF0U6+QhCitdBsI1tzWcoeahmW7k0Pq81OA==} + marked@17.0.6: + resolution: {integrity: sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==} + engines: {node: '>= 20'} + hasBin: true + marked@18.0.5: resolution: {integrity: sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==} engines: {node: '>= 20'} hasBin: true + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + mdast-util-from-markdown@2.0.3: resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + mdast-util-to-hast@13.2.1: resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} @@ -1087,6 +1203,27 @@ packages: micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.2: + resolution: {integrity: sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + micromark-factory-destination@2.0.1: resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} @@ -1176,6 +1313,12 @@ packages: package-manager-detector@1.8.0: resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -1190,6 +1333,16 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + playwright-core@1.63.0: + resolution: {integrity: sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==} + engines: {node: '>=20'} + hasBin: true + + playwright@1.63.0: + resolution: {integrity: sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==} + engines: {node: '>=20'} + hasBin: true + postcss@8.5.15: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} @@ -1210,15 +1363,33 @@ packages: resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} + rehype-harden@1.1.8: + resolution: {integrity: sha512-Qn7vR1xrf6fZCrkm9TDWi/AB4ylrHy+jqsNm1EHOAmbARYA6gsnVJBq/sdBh6kmT4NEZxH5vgIjrscefJAOXcw==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + rehype-sanitize@6.0.0: + resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==} + rehype-stringify@10.0.1: resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + remark-parse@11.0.0: resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} remark-rehype@11.1.2: resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + remend@1.3.1: + resolution: {integrity: sha512-N3DiY5qbRPoa5vkxn1oDLMyXOVTeo6Hp+XOj6SIqJAYUgLS0Q587gILPMom/qm86AQ/ZrcOdwEIzCz8V3J0nxQ==} + rollup@4.62.0: resolution: {integrity: sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -1258,12 +1429,31 @@ packages: std-env@4.2.0: resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + streamdown@2.6.0: + resolution: {integrity: sha512-nQZVUn4GvB2R5SAlDNph20iKZeW+RM4gv6G1H3ACypEnmQf8PgTHZ/1Ta2OACRwQ2coK7aW5AeIAa7Ls5zk+RA==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + streaming-markdown@0.2.15: + resolution: {integrity: sha512-eSWjdvLSTznl2+FIzuxw1TRXwhjZiY1RfSygGCMpcNY8CrsaUM2zHtxe/JFcPoXCQxeO3TSJ72QMDIN5B/Ui1w==} + engines: {node: '>=20'} + stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} strip-literal@3.1.0: resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + + tailwind-merge@3.6.0: + resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -1330,6 +1520,9 @@ packages: unist-util-visit@5.1.0: resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} @@ -1409,6 +1602,9 @@ packages: jsdom: optional: true + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -1941,6 +2137,8 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/unist@2.0.11': {} + '@types/unist@3.0.3': {} '@ungap/structured-clone@1.3.1': {} @@ -2015,8 +2213,12 @@ snapshots: character-entities@2.0.2: {} + character-reference-invalid@2.0.1: {} + check-error@2.1.3: {} + clsx@2.1.1: {} + comma-separated-tokens@2.0.3: {} commonmark-spec@0.31.2: {} @@ -2051,6 +2253,8 @@ snapshots: entities@4.5.0: {} + entities@6.0.1: {} + es-module-lexer@1.7.0: {} esbuild@0.25.12: @@ -2140,10 +2344,14 @@ snapshots: '@esbuild/win32-ia32': 0.28.1 '@esbuild/win32-x64': 0.28.1 + escape-string-regexp@5.0.0: {} + esrap@2.3.0: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + estree-util-is-identifier-name@3.0.0: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.9 @@ -2169,6 +2377,43 @@ snapshots: fsevents@2.3.3: optional: true + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.2.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.3.1 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.1 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-sanitize@5.0.2: + dependencies: + '@types/hast': 3.0.4 + '@ungap/structured-clone': 1.3.1 + unist-util-position: 5.0.0 + hast-util-to-html@9.0.5: dependencies: '@types/hast': 3.0.4 @@ -2183,16 +2428,69 @@ snapshots: stringify-entities: 4.0.4 zwitch: 2.0.4 + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-to-parse5@8.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + hast-util-whitespace@3.0.0: dependencies: '@types/hast': 3.0.4 + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + + html-url-attributes@3.0.1: {} + html-void-elements@3.0.0: {} human-id@4.2.1: {} import-meta-resolve@4.2.0: {} + inline-style-parser@0.2.7: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-decimal@2.0.1: {} + + is-hexadecimal@2.0.1: {} + is-plain-obj@4.1.0: {} is-reference@3.0.3: @@ -2214,6 +2512,8 @@ snapshots: dependencies: uc.micro: 2.1.0 + longest-streak@3.1.0: {} + loupe@3.2.1: {} magic-string@0.30.21: @@ -2229,10 +2529,21 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 + markdown-table@3.0.4: {} + markdown-wasm@1.2.0: {} + marked@17.0.6: {} + marked@18.0.5: {} + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + mdast-util-from-markdown@2.0.3: dependencies: '@types/mdast': 4.0.4 @@ -2250,6 +2561,107 @@ snapshots: transitivePeerDependencies: - supports-color + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + mdast-util-to-hast@13.2.1: dependencies: '@types/hast': 3.0.4 @@ -2262,6 +2674,18 @@ snapshots: unist-util-visit: 5.1.0 vfile: 6.0.3 + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + mdast-util-to-string@4.0.0: dependencies: '@types/mdast': 4.0.4 @@ -2289,6 +2713,64 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.2: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.2 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + micromark-factory-destination@2.0.1: dependencies: micromark-util-character: 2.1.1 @@ -2424,6 +2906,20 @@ snapshots: package-manager-detector@1.8.0: {} + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + pathe@2.0.3: {} pathval@2.0.1: {} @@ -2432,6 +2928,12 @@ snapshots: picomatch@4.0.4: {} + playwright-core@1.63.0: {} + + playwright@1.63.0: + dependencies: + playwright-core: 1.63.0 + postcss@8.5.15: dependencies: nanoid: 3.3.13 @@ -2449,12 +2951,38 @@ snapshots: react@19.2.7: {} + rehype-harden@1.1.8: + dependencies: + unist-util-visit: 5.1.0 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-sanitize@6.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-sanitize: 5.0.2 + rehype-stringify@10.0.1: dependencies: '@types/hast': 3.0.4 hast-util-to-html: 9.0.5 unified: 11.0.5 + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + remark-parse@11.0.0: dependencies: '@types/mdast': 4.0.4 @@ -2472,6 +3000,14 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + remend@1.3.1: {} + rollup@4.62.0: dependencies: '@types/estree': 1.0.9 @@ -2523,6 +3059,30 @@ snapshots: std-env@4.2.0: {} + streamdown@2.6.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + clsx: 2.1.1 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + marked: 17.0.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + rehype-harden: 1.1.8 + rehype-raw: 7.0.0 + rehype-sanitize: 6.0.0 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remend: 1.3.1 + tailwind-merge: 3.6.0 + unified: 11.0.5 + unist-util-visit: 5.1.0 + unist-util-visit-parents: 6.0.2 + transitivePeerDependencies: + - supports-color + + streaming-markdown@0.2.15: {} + stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 @@ -2532,6 +3092,16 @@ snapshots: dependencies: js-tokens: 9.0.1 + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + + tailwind-merge@3.6.0: {} + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -2598,6 +3168,11 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 @@ -2685,6 +3260,8 @@ snapshots: - tsx - yaml + web-namespaces@2.0.1: {} + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 diff --git a/reports/benchmarks.json b/reports/benchmarks.json index 1ba7450..ba5f8f8 100644 --- a/reports/benchmarks.json +++ b/reports/benchmarks.json @@ -1,6 +1,12 @@ { - "generatedAt": "2026-09-11T20:23:30.357Z", - "sink": 112892330, + "generatedAt": "2026-09-12T23:59:58.621Z", + "environment": { + "node": "v24.15.0", + "platform": "darwin", + "arch": "arm64", + "cpu": "Apple M5 Pro" + }, + "sink": 9056185263, "results": [ { "group": "markdown", @@ -8,9 +14,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.01844806249999999, + "msPerOp": 0.0230959165, "outputBytes": 15, - "heapDeltaKb": 898.9609375 + "heapDeltaKb": 35036.625 }, { "group": "markdown", @@ -18,9 +24,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.005263854499999994, + "msPerOp": 0.004967021000000003, "outputBytes": 1399, - "heapDeltaKb": 1238.703125 + "heapDeltaKb": 10160.7421875 }, { "group": "markdown", @@ -28,9 +34,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.00399779199999999, + "msPerOp": 0.003822896, "outputBytes": 1143, - "heapDeltaKb": -3027.4296875 + "heapDeltaKb": 133.8359375 }, { "group": "markdown", @@ -38,9 +44,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.023797770500000013, + "msPerOp": 0.025145104000000005, "outputBytes": 1399, - "heapDeltaKb": -2596.171875 + "heapDeltaKb": 11894.5078125 }, { "group": "markdown", @@ -48,9 +54,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.023606604000000003, + "msPerOp": 0.023205708500000016, "outputBytes": 1143, - "heapDeltaKb": -1860.5859375 + "heapDeltaKb": 44793.6796875 }, { "group": "markdown", @@ -58,9 +64,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.02952339599999999, + "msPerOp": 0.029327332999999983, "outputBytes": 1035, - "heapDeltaKb": -853.609375 + "heapDeltaKb": -92849.1171875 }, { "group": "markdown", @@ -68,9 +74,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.019339646000000016, + "msPerOp": 0.018661083499999988, "outputBytes": 1087, - "heapDeltaKb": 4088.890625 + "heapDeltaKb": 44907.1484375 }, { "group": "markdown", @@ -78,9 +84,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.17314687500000003, + "msPerOp": 0.16640525, "outputBytes": 825, - "heapDeltaKb": -8298.203125 + "heapDeltaKb": 51435.609375 }, { "group": "markdown", @@ -88,9 +94,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.021329833500000006, + "msPerOp": 0.013089541999999994, "outputBytes": 825, - "heapDeltaKb": 54.8671875 + "heapDeltaKb": -49166.7734375 }, { "group": "markdown", @@ -98,9 +104,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.010540166499999998, + "msPerOp": 0.008372937499999978, "outputBytes": 1117, - "heapDeltaKb": 4372.3984375 + "heapDeltaKb": 4376.671875 }, { "group": "markdown", @@ -108,9 +114,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.19910575, + "msPerOp": 0.21472016700000002, "outputBytes": 824, - "heapDeltaKb": 13756 + "heapDeltaKb": 7854.609375 }, { "group": "markdown", @@ -118,9 +124,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.006807167000000163, + "msPerOp": 0.00930816600000003, "outputBytes": 15, - "heapDeltaKb": -6378.1484375 + "heapDeltaKb": 23903.4453125 }, { "group": "markdown", @@ -128,9 +134,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.007484500000000026, + "msPerOp": 0.008788749999999936, "outputBytes": 4596, - "heapDeltaKb": -1122.5703125 + "heapDeltaKb": -30194.4296875 }, { "group": "markdown", @@ -138,9 +144,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.003155708999999888, + "msPerOp": 0.0040034169999999, "outputBytes": 1927, - "heapDeltaKb": 10476.2890625 + "heapDeltaKb": 10476.1640625 }, { "group": "markdown", @@ -148,9 +154,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.014249915999999984, + "msPerOp": 0.01934395799999993, "outputBytes": 4596, - "heapDeltaKb": -6339.6796875 + "heapDeltaKb": -5389.796875 }, { "group": "markdown", @@ -158,9 +164,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.009854332999999996, + "msPerOp": 0.01655070899999987, "outputBytes": 1927, - "heapDeltaKb": 4603.2109375 + "heapDeltaKb": -25821.4453125 }, { "group": "markdown", @@ -168,9 +174,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.006169208000000026, + "msPerOp": 0.007307250000000067, "outputBytes": 1330, - "heapDeltaKb": -19757.0859375 + "heapDeltaKb": 12886.8359375 }, { "group": "markdown", @@ -178,9 +184,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.008338584000000083, + "msPerOp": 0.009848250000000008, "outputBytes": 1330, - "heapDeltaKb": -5321.0390625 + "heapDeltaKb": 27313.4140625 }, { "group": "markdown", @@ -188,9 +194,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.13680366700000013, + "msPerOp": 0.16169662500000004, "outputBytes": 1330, - "heapDeltaKb": -11833.328125 + "heapDeltaKb": -5850.4296875 }, { "group": "markdown", @@ -198,9 +204,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.009240166999999928, + "msPerOp": 0.010917582999999922, "outputBytes": 1330, - "heapDeltaKb": 24577.0234375 + "heapDeltaKb": -40875.984375 }, { "group": "markdown", @@ -208,9 +214,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.004289874999999938, + "msPerOp": 0.0053693749999999905, "outputBytes": 1512, - "heapDeltaKb": 2559.9921875 + "heapDeltaKb": 2558.296875 }, { "group": "markdown", @@ -218,9 +224,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.15214312500000005, + "msPerOp": 0.15673112500000003, "outputBytes": 1200, - "heapDeltaKb": -7037.953125 + "heapDeltaKb": 24973.7109375 }, { "group": "markdown", @@ -228,9 +234,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.0027057704999999713, + "msPerOp": 0.002730000000000018, "outputBytes": 15, - "heapDeltaKb": 18580.609375 + "heapDeltaKb": -44947.8828125 }, { "group": "markdown", @@ -238,9 +244,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.0016762294999999768, + "msPerOp": 0.001634728999999993, "outputBytes": 1067, - "heapDeltaKb": -17067.3203125 + "heapDeltaKb": 15659.734375 }, { "group": "markdown", @@ -248,9 +254,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.0007344790000000785, + "msPerOp": 0.0006929999999999837, "outputBytes": 361, - "heapDeltaKb": 5314.8828125 + "heapDeltaKb": 5315.171875 }, { "group": "markdown", @@ -258,9 +264,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.004606062500000007, + "msPerOp": 0.004589437500000031, "outputBytes": 1067, - "heapDeltaKb": 1452.21875 + "heapDeltaKb": 33660.75 }, { "group": "markdown", @@ -268,9 +274,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.0036469999999999346, + "msPerOp": 0.00380916650000006, "outputBytes": 361, - "heapDeltaKb": -8892.2109375 + "heapDeltaKb": -34278.9375 }, { "group": "markdown", @@ -278,9 +284,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.005531416499999977, + "msPerOp": 0.005420104499999979, "outputBytes": 350, - "heapDeltaKb": 16140.453125 + "heapDeltaKb": 16657.734375 }, { "group": "markdown", @@ -288,9 +294,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.005058562499999994, + "msPerOp": 0.005576854499999968, "outputBytes": 300, - "heapDeltaKb": -7253.6015625 + "heapDeltaKb": -31630.0625 }, { "group": "markdown", @@ -298,9 +304,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.05207070850000002, + "msPerOp": 0.05042335449999996, "outputBytes": 300, - "heapDeltaKb": 7694.546875 + "heapDeltaKb": 11184.7890625 }, { "group": "markdown", @@ -308,9 +314,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.0038984790000000658, + "msPerOp": 0.003881750000000011, "outputBytes": 300, - "heapDeltaKb": -9655.921875 + "heapDeltaKb": 23073.0234375 }, { "group": "markdown", @@ -318,9 +324,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.0018649789999999485, + "msPerOp": 0.001838145499999996, "outputBytes": 408, - "heapDeltaKb": 2938.3984375 + "heapDeltaKb": 2938.671875 }, { "group": "markdown", @@ -328,9 +334,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.06917156250000005, + "msPerOp": 0.055197812500000054, "outputBytes": 297, - "heapDeltaKb": 2219.1953125 + "heapDeltaKb": -30681.640625 }, { "group": "markdown", @@ -338,9 +344,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.017162290999999868, + "msPerOp": 0.018113207999999985, "outputBytes": 15, - "heapDeltaKb": -17554.0625 + "heapDeltaKb": 1601.375 }, { "group": "markdown", @@ -348,9 +354,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.002673625000000129, + "msPerOp": 0.002141417000000047, "outputBytes": 1903, - "heapDeltaKb": 10295.9765625 + "heapDeltaKb": 10292.2421875 }, { "group": "markdown", @@ -358,9 +364,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.002687332999999853, + "msPerOp": 0.002436666999999943, "outputBytes": 1903, - "heapDeltaKb": 10321.3203125 + "heapDeltaKb": 10320.4609375 }, { "group": "markdown", @@ -368,9 +374,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.017694124999999984, + "msPerOp": 0.02010304199999996, "outputBytes": 1903, - "heapDeltaKb": -7354.9765625 + "heapDeltaKb": 18362.328125 }, { "group": "markdown", @@ -378,9 +384,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.016582792000000155, + "msPerOp": 0.020281833000000006, "outputBytes": 1903, - "heapDeltaKb": -7244.9453125 + "heapDeltaKb": 18847.21875 }, { "group": "markdown", @@ -388,9 +394,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.03447937500000012, + "msPerOp": 0.03812349999999992, "outputBytes": 1860, - "heapDeltaKb": 6729.9375 + "heapDeltaKb": -5593.0234375 }, { "group": "markdown", @@ -398,9 +404,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.01885091699999998, + "msPerOp": 0.02060416699999996, "outputBytes": 1860, - "heapDeltaKb": -18343.15625 + "heapDeltaKb": -8952.140625 }, { "group": "markdown", @@ -408,9 +414,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.24291566599999986, + "msPerOp": 0.2549411249999998, "outputBytes": 1862, - "heapDeltaKb": 24865.828125 + "heapDeltaKb": 21780.578125 }, { "group": "markdown", @@ -418,9 +424,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.011920082999999977, + "msPerOp": 0.013604624999999941, "outputBytes": 1862, - "heapDeltaKb": 46458.671875 + "heapDeltaKb": -14599.390625 }, { "group": "markdown", @@ -428,9 +434,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.006297250000000076, + "msPerOp": 0.006443499999999858, "outputBytes": 2340, - "heapDeltaKb": 3368.3671875 + "heapDeltaKb": 3369.3828125 }, { "group": "markdown", @@ -438,9 +444,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.28966795899999986, + "msPerOp": 0.3224618330000003, "outputBytes": 1859, - "heapDeltaKb": 50897.1328125 + "heapDeltaKb": -73200.890625 }, { "group": "markdown", @@ -448,9 +454,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.0093737500000002, + "msPerOp": 0.01270204149999995, "outputBytes": 15, - "heapDeltaKb": 7577.4453125 + "heapDeltaKb": 20112.46875 }, { "group": "markdown", @@ -458,9 +464,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.003080417000000125, + "msPerOp": 0.004287021000000096, "outputBytes": 1370, - "heapDeltaKb": 27814.0859375 + "heapDeltaKb": -24522.9140625 }, { "group": "markdown", @@ -468,9 +474,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.0026590625000001184, + "msPerOp": 0.0027589165000001686, "outputBytes": 1044, - "heapDeltaKb": -42766.875 + "heapDeltaKb": 21772.9453125 }, { "group": "markdown", @@ -478,9 +484,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.012628958000000011, + "msPerOp": 0.016400666499999942, "outputBytes": 1370, - "heapDeltaKb": 31414.875 + "heapDeltaKb": -9773.921875 }, { "group": "markdown", @@ -488,9 +494,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.012084312499999895, + "msPerOp": 0.016765562499999987, "outputBytes": 1044, - "heapDeltaKb": -40133.328125 + "heapDeltaKb": -7187.6171875 }, { "group": "markdown", @@ -498,9 +504,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.012065562499999943, + "msPerOp": 0.014704625000000079, "outputBytes": 724, - "heapDeltaKb": 50632.2109375 + "heapDeltaKb": 50669.171875 }, { "group": "markdown", @@ -508,9 +514,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.00776693750000004, + "msPerOp": 0.008663270999999894, "outputBytes": 776, - "heapDeltaKb": 3160.15625 + "heapDeltaKb": 4037.9140625 }, { "group": "markdown", @@ -518,9 +524,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.09741200000000004, + "msPerOp": 0.13299991649999993, "outputBytes": 535, - "heapDeltaKb": 44970.375 + "heapDeltaKb": 44880.390625 }, { "group": "markdown", @@ -528,9 +534,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.007062458500000048, + "msPerOp": 0.007399374999999963, "outputBytes": 535, - "heapDeltaKb": -15145.1796875 + "heapDeltaKb": -15136.8671875 }, { "group": "markdown", @@ -538,9 +544,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.003928562499999998, + "msPerOp": 0.0036898334999998497, "outputBytes": 854, - "heapDeltaKb": 3580.1796875 + "heapDeltaKb": 3631.15625 }, { "group": "markdown", @@ -548,9 +554,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.129259542, + "msPerOp": 0.1475164164999999, "outputBytes": 534, - "heapDeltaKb": 4179.84375 + "heapDeltaKb": 4013.40625 }, { "group": "markdown", @@ -558,9 +564,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.0232009579999999, + "msPerOp": 0.029478124999999862, "outputBytes": 15, - "heapDeltaKb": -43744.359375 + "heapDeltaKb": 12749.0625 }, { "group": "markdown", @@ -568,9 +574,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.004354895999999826, + "msPerOp": 0.005765270999999984, "outputBytes": 1315, - "heapDeltaKb": 42277.9921875 + "heapDeltaKb": -5483.21875 }, { "group": "markdown", @@ -578,9 +584,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.00404427099999998, + "msPerOp": 0.005156958500000201, "outputBytes": 1315, - "heapDeltaKb": -23161.15625 + "heapDeltaKb": -14400.9453125 }, { "group": "markdown", @@ -588,9 +594,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.02736354150000011, + "msPerOp": 0.036605395499999985, "outputBytes": 1315, - "heapDeltaKb": -1853.140625 + "heapDeltaKb": 6365.84375 }, { "group": "markdown", @@ -598,9 +604,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.027197875000000066, + "msPerOp": 0.03830779150000012, "outputBytes": 1315, - "heapDeltaKb": -2112.7109375 + "heapDeltaKb": -91885.7890625 }, { "group": "markdown", @@ -608,9 +614,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.029182874999999966, + "msPerOp": 0.03246374999999989, "outputBytes": 1102, - "heapDeltaKb": -14329.2421875 + "heapDeltaKb": -2774.734375 }, { "group": "markdown", @@ -618,9 +624,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.017972145500000123, + "msPerOp": 0.021280750000000126, "outputBytes": 1325, - "heapDeltaKb": 26395.5078125 + "heapDeltaKb": 26335.390625 }, { "group": "markdown", @@ -628,9 +634,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.16772906250000005, + "msPerOp": 0.18304518749999987, "outputBytes": 627, - "heapDeltaKb": -39153.3359375 + "heapDeltaKb": 43377.28125 }, { "group": "markdown", @@ -638,9 +644,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.011388625000000048, + "msPerOp": 0.011697375000000192, "outputBytes": 627, - "heapDeltaKb": 28363.8125 + "heapDeltaKb": -36650.7734375 }, { "group": "markdown", @@ -648,9 +654,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.005910791999999901, + "msPerOp": 0.005767812500000218, "outputBytes": 1202, - "heapDeltaKb": 4506.171875 + "heapDeltaKb": 4506.0546875 }, { "group": "markdown", @@ -658,9 +664,54 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.1964307080000001, + "msPerOp": 0.20902820799999972, "outputBytes": 622, - "heapDeltaKb": 7601.8671875 + "heapDeltaKb": -44097.953125 + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming parse", + "fixture": "ai-response.md", + "bytes": 652, + "iterations": 250, + "msPerOp": 0.21409083199997986, + "outputBytes": 0, + "heapDeltaKb": 27889.125, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 21, + "openFenceUpdatesPerReplay": 2, + "checksum": 26208, + "latency": { + "all": { + "samples": 5250, + "meanMs": 0.010083434857144831, + "p50Ms": 0.007499999999708962, + "p95Ms": 0.015916999999717518, + "maxMs": 2.5200840000006792 + }, + "openFence": { + "samples": 500, + "meanMs": 0.006220576000010624, + "p50Ms": 0.0058330000001660665, + "p95Ms": 0.0071250000000873115, + "maxMs": 0.04941599999983737 + }, + "lastTenPercentOpenFence": { + "samples": 250, + "meanMs": 0.0062961639999884935, + "p50Ms": 0.005834000000504602, + "p95Ms": 0.0070420000001831795, + "maxMs": 0.04941599999983737 + }, + "finalUpdate": { + "samples": 250, + "meanMs": 0.015879831999976887, + "p50Ms": 0.014874999999847205, + "p95Ms": 0.01895799999965675, + "maxMs": 0.0702080000000933 + } + } }, { "group": "streaming", @@ -668,9 +719,89 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 250, - "msPerOp": 0.22356500000000232, + "msPerOp": 0.2578975960000098, "outputBytes": 1112, - "heapDeltaKb": -8191.453125 + "heapDeltaKb": 65860.796875, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 21, + "openFenceUpdatesPerReplay": 2, + "checksum": 3173184, + "latency": { + "all": { + "samples": 5250, + "meanMs": 0.012171573523811232, + "p50Ms": 0.00891700000011042, + "p95Ms": 0.019290999999611813, + "maxMs": 2.499333000000661 + }, + "openFence": { + "samples": 500, + "meanMs": 0.007640497999973377, + "p50Ms": 0.00724999999965803, + "p95Ms": 0.008667000000059488, + "maxMs": 0.052958000000217 + }, + "lastTenPercentOpenFence": { + "samples": 250, + "meanMs": 0.007983831999979884, + "p50Ms": 0.00741699999980483, + "p95Ms": 0.009125000000494765, + "maxMs": 0.052958000000217 + }, + "finalUpdate": { + "samples": 250, + "meanMs": 0.026833788000047206, + "p50Ms": 0.018500000000130967, + "p95Ms": 0.02104200000030687, + "maxMs": 1.980333000000428 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming with @tanstack/highlight", + "fixture": "ai-response.md", + "bytes": 652, + "iterations": 250, + "msPerOp": 0.38105232800001976, + "outputBytes": 1274, + "heapDeltaKb": 26043.859375, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 21, + "openFenceUpdatesPerReplay": 2, + "checksum": 3703896, + "latency": { + "all": { + "samples": 5250, + "meanMs": 0.01806752114286272, + "p50Ms": 0.017582999999831372, + "p95Ms": 0.029458999999405933, + "maxMs": 2.0387080000000424 + }, + "openFence": { + "samples": 500, + "meanMs": 0.019441631999998207, + "p50Ms": 0.012625000000298314, + "p95Ms": 0.01741700000002311, + "maxMs": 1.8179999999993015 + }, + "lastTenPercentOpenFence": { + "samples": 250, + "meanMs": 0.02747746000004554, + "p50Ms": 0.01341599999977916, + "p95Ms": 0.02008399999976973, + "maxMs": 1.8179999999993015 + }, + "finalUpdate": { + "samples": 250, + "meanMs": 0.03449262800000724, + "p50Ms": 0.026125000000320142, + "p95Ms": 0.03470800000013696, + "maxMs": 1.784125000000131 + } + } }, { "group": "streaming", @@ -678,9 +809,944 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 250, - "msPerOp": 0.2804184999999998, + "msPerOp": 0.3046166559999801, "outputBytes": 1035, - "heapDeltaKb": 3232.0078125 + "heapDeltaKb": 19526.328125, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 21, + "openFenceUpdatesPerReplay": 2, + "checksum": 2962764, + "latency": { + "all": { + "samples": 5250, + "meanMs": 0.014406752571436213, + "p50Ms": 0.013124999999490683, + "p95Ms": 0.025375000000167347, + "maxMs": 1.5779169999996157 + }, + "openFence": { + "samples": 500, + "meanMs": 0.01029838800001744, + "p50Ms": 0.009833000000071479, + "p95Ms": 0.013332999999875028, + "maxMs": 0.02525000000059663 + }, + "lastTenPercentOpenFence": { + "samples": 250, + "meanMs": 0.0104275000000016, + "p50Ms": 0.009957999999642198, + "p95Ms": 0.013832999999976892, + "maxMs": 0.019166999999470136 + }, + "finalUpdate": { + "samples": 250, + "meanMs": 0.024599832000021707, + "p50Ms": 0.02345900000000256, + "p95Ms": 0.02995900000041729, + "maxMs": 0.05720800000017334 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming parse", + "fixture": "unfinished-backtick-4kib", + "bytes": 4096, + "iterations": 20, + "msPerOp": 0.4621396000000459, + "outputBytes": 0, + "heapDeltaKb": -34834.6953125, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 128, + "openFenceUpdatesPerReplay": 127, + "checksum": 5632, + "latency": { + "all": { + "samples": 2560, + "meanMs": 0.0034755386718696712, + "p50Ms": 0.0030829999996058177, + "p95Ms": 0.005083000000013271, + "maxMs": 0.6201670000000377 + }, + "openFence": { + "samples": 2540, + "meanMs": 0.0034912744094433615, + "p50Ms": 0.0030830000005153124, + "p95Ms": 0.005083000000013271, + "maxMs": 0.6201670000000377 + }, + "lastTenPercentOpenFence": { + "samples": 260, + "meanMs": 0.005197099999976182, + "p50Ms": 0.004874999999628926, + "p95Ms": 0.006124999999883585, + "maxMs": 0.020915999999488122 + }, + "finalUpdate": { + "samples": 20, + "meanMs": 0.005047750000085216, + "p50Ms": 0.005000000000109139, + "p95Ms": 0.005541000000448548, + "maxMs": 0.005624999999781721 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming profile", + "fixture": "unfinished-backtick-4kib", + "bytes": 4096, + "iterations": 20, + "msPerOp": 0.860104199999887, + "outputBytes": 4731, + "heapDeltaKb": 695.4375, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 128, + "openFenceUpdatesPerReplay": 127, + "checksum": 6817030, + "latency": { + "all": { + "samples": 2560, + "meanMs": 0.006617728125001321, + "p50Ms": 0.005625000000691216, + "p95Ms": 0.009707999999591266, + "maxMs": 2.0296669999997903 + }, + "openFence": { + "samples": 2540, + "meanMs": 0.006656301574804367, + "p50Ms": 0.0056670000003578025, + "p95Ms": 0.009708999999929802, + "maxMs": 2.0296669999997903 + }, + "lastTenPercentOpenFence": { + "samples": 260, + "meanMs": 0.00977183846150113, + "p50Ms": 0.00908400000025722, + "p95Ms": 0.013374999999541615, + "maxMs": 0.05837500000052387 + }, + "finalUpdate": { + "samples": 20, + "meanMs": 0.010637499999938882, + "p50Ms": 0.009500000000116415, + "p95Ms": 0.014249999999265128, + "maxMs": 0.017416000000594067 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming with @tanstack/highlight", + "fixture": "unfinished-backtick-4kib", + "bytes": 4096, + "iterations": 20, + "msPerOp": 22.06873735000004, + "outputBytes": 16010, + "heapDeltaKb": 62990.421875, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 128, + "openFenceUpdatesPerReplay": 127, + "checksum": 22759572, + "latency": { + "all": { + "samples": 2560, + "meanMs": 0.17212833007812875, + "p50Ms": 0.16845800000010058, + "p95Ms": 0.32120800000029703, + "maxMs": 0.7369170000001759 + }, + "openFence": { + "samples": 2540, + "meanMs": 0.17335691614173696, + "p50Ms": 0.16941600000063772, + "p95Ms": 0.32133299999986775, + "maxMs": 0.7369170000001759 + }, + "lastTenPercentOpenFence": { + "samples": 260, + "meanMs": 0.32346092692306716, + "p50Ms": 0.31554099999993923, + "p95Ms": 0.3592500000004293, + "maxMs": 0.7194170000002487 + }, + "finalUpdate": { + "samples": 20, + "meanMs": 0.35081459999978504, + "p50Ms": 0.33216600000014296, + "p95Ms": 0.36524999999983265, + "maxMs": 0.6940829999994094 + } + } + }, + { + "group": "streaming", + "name": "marked progressive parse+render", + "fixture": "unfinished-backtick-4kib", + "bytes": 4096, + "iterations": 20, + "msPerOp": 0.7148749500001031, + "outputBytes": 4702, + "heapDeltaKb": -36811.359375, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 128, + "openFenceUpdatesPerReplay": 127, + "checksum": 6735366, + "latency": { + "all": { + "samples": 2560, + "meanMs": 0.005508160156259834, + "p50Ms": 0.005333999999493244, + "p95Ms": 0.009250000000065484, + "maxMs": 0.3232909999996991 + }, + "openFence": { + "samples": 2540, + "meanMs": 0.005541573228355639, + "p50Ms": 0.005375000000640284, + "p95Ms": 0.009250000000065484, + "maxMs": 0.3232909999996991 + }, + "lastTenPercentOpenFence": { + "samples": 260, + "meanMs": 0.010631888461564534, + "p50Ms": 0.009125000000494765, + "p95Ms": 0.01116699999965931, + "maxMs": 0.3232909999996991 + }, + "finalUpdate": { + "samples": 20, + "meanMs": 0.009675050000214468, + "p50Ms": 0.009500000000116415, + "p95Ms": 0.010333000000173342, + "maxMs": 0.010709000000133528 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming parse", + "fixture": "unfinished-backtick-16kib", + "bytes": 16384, + "iterations": 10, + "msPerOp": 5.848141600000053, + "outputBytes": 0, + "heapDeltaKb": -165018.1953125, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 512, + "openFenceUpdatesPerReplay": 511, + "checksum": 12288, + "latency": { + "all": { + "samples": 5120, + "meanMs": 0.011320061523442249, + "p50Ms": 0.009500000000116415, + "p95Ms": 0.018124999999599822, + "maxMs": 2.3499999999994543 + }, + "openFence": { + "samples": 5110, + "meanMs": 0.011338096477499627, + "p50Ms": 0.009500000000116415, + "p95Ms": 0.018124999999599822, + "maxMs": 2.3499999999994543 + }, + "lastTenPercentOpenFence": { + "samples": 520, + "meanMs": 0.025568692307708193, + "p50Ms": 0.016749999999774445, + "p95Ms": 0.02420799999981682, + "maxMs": 2.262917000000016 + }, + "finalUpdate": { + "samples": 10, + "meanMs": 0.022812400000020716, + "p50Ms": 0.017582999999831372, + "p95Ms": 0.05129099999976461, + "maxMs": 0.05129099999976461 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming profile", + "fixture": "unfinished-backtick-16kib", + "bytes": 16384, + "iterations": 10, + "msPerOp": 10.330145699999866, + "outputBytes": 18667, + "heapDeltaKb": 66138.4765625, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 512, + "openFenceUpdatesPerReplay": 511, + "checksum": 57732972, + "latency": { + "all": { + "samples": 5120, + "meanMs": 0.02006602402343489, + "p50Ms": 0.018874999999752617, + "p95Ms": 0.03450000000066211, + "maxMs": 1.5924589999995078 + }, + "openFence": { + "samples": 5110, + "meanMs": 0.02009172387475263, + "p50Ms": 0.018915999999990163, + "p95Ms": 0.03450000000066211, + "maxMs": 1.5924589999995078 + }, + "lastTenPercentOpenFence": { + "samples": 520, + "meanMs": 0.036632036538470365, + "p50Ms": 0.033250000000407454, + "p95Ms": 0.041041999999833934, + "maxMs": 1.2621659999995245 + }, + "finalUpdate": { + "samples": 10, + "meanMs": 0.036491599999953904, + "p50Ms": 0.03608399999939138, + "p95Ms": 0.0415419999999358, + "maxMs": 0.0415419999999358 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming with @tanstack/highlight", + "fixture": "unfinished-backtick-16kib", + "bytes": 16384, + "iterations": 10, + "msPerOp": 433.25519150000036, + "outputBytes": 62868, + "heapDeltaKb": 35834.84375, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 512, + "openFenceUpdatesPerReplay": 511, + "checksum": 194725992, + "latency": { + "all": { + "samples": 5120, + "meanMs": 0.8447549888671946, + "p50Ms": 0.8175839999994423, + "p95Ms": 1.7402500000007421, + "maxMs": 3.7253749999999854 + }, + "openFence": { + "samples": 5110, + "meanMs": 0.8463017614481487, + "p50Ms": 0.8186249999998836, + "p95Ms": 1.7403749999994034, + "maxMs": 3.7253749999999854 + }, + "lastTenPercentOpenFence": { + "samples": 520, + "meanMs": 1.646597844230767, + "p50Ms": 1.6052500000005239, + "p95Ms": 2.194792000000234, + "maxMs": 3.7253749999999854 + }, + "finalUpdate": { + "samples": 10, + "meanMs": 1.7058375000000523, + "p50Ms": 1.6742920000006052, + "p95Ms": 2.0839579999992566, + "maxMs": 2.0839579999992566 + } + } + }, + { + "group": "streaming", + "name": "marked progressive parse+render", + "fixture": "unfinished-backtick-16kib", + "bytes": 16384, + "iterations": 10, + "msPerOp": 10.240053999999873, + "outputBytes": 18638, + "heapDeltaKb": -4537.4609375, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 512, + "openFenceUpdatesPerReplay": 511, + "checksum": 57554796, + "latency": { + "all": { + "samples": 5120, + "meanMs": 0.019879748437497825, + "p50Ms": 0.019417000001340057, + "p95Ms": 0.035875000001396984, + "maxMs": 0.2828749999989668 + }, + "openFence": { + "samples": 5110, + "meanMs": 0.019912887475535518, + "p50Ms": 0.019458999999187654, + "p95Ms": 0.035916000000725035, + "maxMs": 0.2828749999989668 + }, + "lastTenPercentOpenFence": { + "samples": 520, + "meanMs": 0.036673030769200594, + "p50Ms": 0.034790999998222105, + "p95Ms": 0.0476249999992433, + "maxMs": 0.16791600000033213 + }, + "finalUpdate": { + "samples": 10, + "meanMs": 0.03875830000015412, + "p50Ms": 0.03600000000005821, + "p95Ms": 0.05379200000061246, + "maxMs": 0.05379200000061246 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming parse", + "fixture": "unfinished-backtick-64kib", + "bytes": 65536, + "iterations": 5, + "msPerOp": 88.78324159999974, + "outputBytes": 0, + "heapDeltaKb": 58977.828125, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 2048, + "openFenceUpdatesPerReplay": 2047, + "checksum": 28672, + "latency": { + "all": { + "samples": 10240, + "meanMs": 0.04318977167969536, + "p50Ms": 0.036208999999871594, + "p95Ms": 0.08095899999898393, + "maxMs": 3.2906660000007832 + }, + "openFence": { + "samples": 10235, + "meanMs": 0.04320940097704749, + "p50Ms": 0.03620900000169058, + "p95Ms": 0.08100000000013097, + "maxMs": 3.2906660000007832 + }, + "lastTenPercentOpenFence": { + "samples": 1025, + "meanMs": 0.0844143346341493, + "p50Ms": 0.0648750000000291, + "p95Ms": 0.11779199999909906, + "maxMs": 2.9725839999991877 + }, + "finalUpdate": { + "samples": 5, + "meanMs": 0.0742999999998574, + "p50Ms": 0.06708299999991141, + "p95Ms": 0.08816699999988487, + "maxMs": 0.08816699999988487 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming profile", + "fixture": "unfinished-backtick-64kib", + "bytes": 65536, + "iterations": 5, + "msPerOp": 166.69010019999988, + "outputBytes": 74347, + "heapDeltaKb": -65495.1875, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 2048, + "openFenceUpdatesPerReplay": 2047, + "checksum": 534043559, + "latency": { + "all": { + "samples": 10240, + "meanMs": 0.08118271386719282, + "p50Ms": 0.07345799999893643, + "p95Ms": 0.1531250000007276, + "maxMs": 2.1279169999997976 + }, + "openFence": { + "samples": 10235, + "meanMs": 0.08121982081094817, + "p50Ms": 0.073500000000422, + "p95Ms": 0.15333299999838346, + "maxMs": 2.1279169999997976 + }, + "lastTenPercentOpenFence": { + "samples": 1025, + "meanMs": 0.15453332097562408, + "p50Ms": 0.1279580000009446, + "p95Ms": 0.22691699999995762, + "maxMs": 1.78583400000025 + }, + "finalUpdate": { + "samples": 5, + "meanMs": 0.1325332000003982, + "p50Ms": 0.13154100000065228, + "p95Ms": 0.14270900000155962, + "maxMs": 0.14270900000155962 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming with @tanstack/highlight", + "fixture": "unfinished-backtick-64kib", + "bytes": 65536, + "iterations": 5, + "msPerOp": 6953.515150000001, + "outputBytes": 249146, + "heapDeltaKb": -35723.3515625, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 2048, + "openFenceUpdatesPerReplay": 2047, + "checksum": 1793925812, + "latency": { + "all": { + "samples": 10240, + "meanMs": 3.392740948242209, + "p50Ms": 3.4198749999995925, + "p95Ms": 6.580291999998735, + "maxMs": 12.043541999999434 + }, + "openFence": { + "samples": 10235, + "meanMs": 3.394368667415751, + "p50Ms": 3.421582999995735, + "p95Ms": 6.583459000001312, + "maxMs": 12.043541999999434 + }, + "lastTenPercentOpenFence": { + "samples": 1025, + "meanMs": 6.55173894926834, + "p50Ms": 6.430832999998529, + "p95Ms": 7.7552080000023125, + "maxMs": 10.843958000004932 + }, + "finalUpdate": { + "samples": 5, + "meanMs": 6.907216400001198, + "p50Ms": 6.77204100000381, + "p95Ms": 7.664291000000958, + "maxMs": 7.664291000000958 + } + } + }, + { + "group": "streaming", + "name": "marked progressive parse+render", + "fixture": "unfinished-backtick-64kib", + "bytes": 65536, + "iterations": 5, + "msPerOp": 148.42968340000078, + "outputBytes": 74318, + "heapDeltaKb": 29715.09375, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 2048, + "openFenceUpdatesPerReplay": 2047, + "checksum": 533627815, + "latency": { + "all": { + "samples": 10240, + "meanMs": 0.07235159365235333, + "p50Ms": 0.07262500000069849, + "p95Ms": 0.134249999995518, + "maxMs": 0.4335419999988517 + }, + "openFence": { + "samples": 10235, + "meanMs": 0.07238514342941847, + "p50Ms": 0.07266700000036508, + "p95Ms": 0.134249999995518, + "maxMs": 0.4335419999988517 + }, + "lastTenPercentOpenFence": { + "samples": 1025, + "meanMs": 0.135444688780415, + "p50Ms": 0.1322920000020531, + "p95Ms": 0.15491599999950267, + "maxMs": 0.3325830000030692 + }, + "finalUpdate": { + "samples": 5, + "meanMs": 0.14232500000071013, + "p50Ms": 0.13716700000077253, + "p95Ms": 0.16033299999980954, + "maxMs": 0.16033299999980954 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming parse", + "fixture": "unfinished-tilde-64kib", + "bytes": 65536, + "iterations": 5, + "msPerOp": 79.82769179999741, + "outputBytes": 0, + "heapDeltaKb": 71313.5703125, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 2048, + "openFenceUpdatesPerReplay": 2047, + "checksum": 28672, + "latency": { + "all": { + "samples": 10240, + "meanMs": 0.03892056455080208, + "p50Ms": 0.033999999999650754, + "p95Ms": 0.06441699999413686, + "maxMs": 2.7499169999937294 + }, + "openFence": { + "samples": 10235, + "meanMs": 0.038938336394745345, + "p50Ms": 0.034040999998978805, + "p95Ms": 0.06445800000074087, + "maxMs": 2.7499169999937294 + }, + "lastTenPercentOpenFence": { + "samples": 1025, + "meanMs": 0.07508403219514748, + "p50Ms": 0.06274999999732245, + "p95Ms": 0.08191700000315905, + "maxMs": 2.7499169999937294 + }, + "finalUpdate": { + "samples": 5, + "meanMs": 0.06999179999838816, + "p50Ms": 0.06583299999329029, + "p95Ms": 0.08716699999786215, + "maxMs": 0.08716699999786215 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming profile", + "fixture": "unfinished-tilde-64kib", + "bytes": 65536, + "iterations": 5, + "msPerOp": 151.58304159999972, + "outputBytes": 74347, + "heapDeltaKb": -86751.4609375, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 2048, + "openFenceUpdatesPerReplay": 2047, + "checksum": 534043559, + "latency": { + "all": { + "samples": 10240, + "meanMs": 0.07392367548829241, + "p50Ms": 0.06974999999511056, + "p95Ms": 0.12745900000300026, + "maxMs": 1.701708000000508 + }, + "openFence": { + "samples": 10235, + "meanMs": 0.0739573055202851, + "p50Ms": 0.0697920000020531, + "p95Ms": 0.12749999999505235, + "maxMs": 1.701708000000508 + }, + "lastTenPercentOpenFence": { + "samples": 1025, + "meanMs": 0.13837541560961852, + "p50Ms": 0.12449999999807915, + "p95Ms": 0.1492080000025453, + "maxMs": 1.62275000000227 + }, + "finalUpdate": { + "samples": 5, + "meanMs": 0.1289249999987078, + "p50Ms": 0.1275420000019949, + "p95Ms": 0.13320799999928568, + "maxMs": 0.13320799999928568 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming with @tanstack/highlight", + "fixture": "unfinished-tilde-64kib", + "bytes": 65536, + "iterations": 5, + "msPerOp": 8161.249008199998, + "outputBytes": 249146, + "heapDeltaKb": -109512.4609375, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 2048, + "openFenceUpdatesPerReplay": 2047, + "checksum": 1793925812, + "latency": { + "all": { + "samples": 10240, + "meanMs": 3.982018991699145, + "p50Ms": 3.78270799999882, + "p95Ms": 8.104082999998354, + "maxMs": 92.9486670000042 + }, + "openFence": { + "samples": 10235, + "meanMs": 3.9839234586223, + "p50Ms": 3.784333000003244, + "p95Ms": 8.106708999999682, + "maxMs": 92.9486670000042 + }, + "lastTenPercentOpenFence": { + "samples": 1025, + "meanMs": 7.017661139512117, + "p50Ms": 6.327290999994148, + "p95Ms": 9.367958000002545, + "maxMs": 89.78366599998844 + }, + "finalUpdate": { + "samples": 5, + "meanMs": 7.071666600002208, + "p50Ms": 6.715166000009049, + "p95Ms": 8.302792000002228, + "maxMs": 8.302792000002228 + } + } + }, + { + "group": "streaming", + "name": "marked progressive parse+render", + "fixture": "unfinished-tilde-64kib", + "bytes": 65536, + "iterations": 5, + "msPerOp": 147.22933320000303, + "outputBytes": 74318, + "heapDeltaKb": 29615.7109375, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 2048, + "openFenceUpdatesPerReplay": 2047, + "checksum": 533627815, + "latency": { + "all": { + "samples": 10240, + "meanMs": 0.0717972814452807, + "p50Ms": 0.07083399999828544, + "p95Ms": 0.13324999999895226, + "maxMs": 0.3760000000038417 + }, + "openFence": { + "samples": 10235, + "meanMs": 0.07182997430382528, + "p50Ms": 0.07087499999033753, + "p95Ms": 0.13324999999895226, + "maxMs": 0.3760000000038417 + }, + "lastTenPercentOpenFence": { + "samples": 1025, + "meanMs": 0.13354970634142618, + "p50Ms": 0.13033300000824966, + "p95Ms": 0.1582909999997355, + "maxMs": 0.3410839999996824 + }, + "finalUpdate": { + "samples": 5, + "meanMs": 0.15028300000121816, + "p50Ms": 0.13937499999883585, + "p95Ms": 0.19883300000219606, + "maxMs": 0.19883300000219606 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming parse", + "fixture": "closing-backtick-64kib", + "bytes": 65566, + "iterations": 5, + "msPerOp": 83.45165839999973, + "outputBytes": 0, + "heapDeltaKb": 72340.4921875, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 2049, + "openFenceUpdatesPerReplay": 2047, + "checksum": 28693, + "latency": { + "all": { + "samples": 10245, + "meanMs": 0.04065191215222499, + "p50Ms": 0.034916999997221865, + "p95Ms": 0.0695830000040587, + "maxMs": 3.1708749999961583 + }, + "openFence": { + "samples": 10235, + "meanMs": 0.04065160058618007, + "p50Ms": 0.034916999997221865, + "p95Ms": 0.06949999999778811, + "maxMs": 3.1708749999961583 + }, + "lastTenPercentOpenFence": { + "samples": 1025, + "meanMs": 0.07932173170739922, + "p50Ms": 0.06325000000651926, + "p95Ms": 0.09729199999128468, + "maxMs": 2.7610419999982696 + }, + "finalUpdate": { + "samples": 5, + "meanMs": 0.0778249999973923, + "p50Ms": 0.07470899999316316, + "p95Ms": 0.08883300000161398, + "maxMs": 0.08883300000161398 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming profile", + "fixture": "closing-backtick-64kib", + "bytes": 65566, + "iterations": 5, + "msPerOp": 158.23815839999878, + "outputBytes": 74378, + "heapDeltaKb": 3098.046875, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 2049, + "openFenceUpdatesPerReplay": 2047, + "checksum": 534564205, + "latency": { + "all": { + "samples": 10245, + "meanMs": 0.07712069389948589, + "p50Ms": 0.06920800000079907, + "p95Ms": 0.1396670000103768, + "maxMs": 1.764999999999418 + }, + "openFence": { + "samples": 10235, + "meanMs": 0.07711955378605276, + "p50Ms": 0.06920800000079907, + "p95Ms": 0.13950000000477303, + "maxMs": 1.764999999999418 + }, + "lastTenPercentOpenFence": { + "samples": 1025, + "meanMs": 0.1427728409754828, + "p50Ms": 0.12816699998802505, + "p95Ms": 0.1669170000095619, + "maxMs": 1.6622079999942798 + }, + "finalUpdate": { + "samples": 5, + "meanMs": 0.15054999999701976, + "p50Ms": 0.146666999993613, + "p95Ms": 0.16754100000252947, + "maxMs": 0.16754100000252947 + } + } + }, + { + "group": "streaming", + "name": "@tanstack/markdown streaming with @tanstack/highlight", + "fixture": "closing-backtick-64kib", + "bytes": 65566, + "iterations": 5, + "msPerOp": 6534.3609, + "outputBytes": 249177, + "heapDeltaKb": -117985.0078125, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 2049, + "openFenceUpdatesPerReplay": 2047, + "checksum": 1795670051, + "latency": { + "all": { + "samples": 10245, + "meanMs": 3.187517166325207, + "p50Ms": 3.1581670000159647, + "p95Ms": 6.161041999992449, + "maxMs": 12.539707999996608 + }, + "openFence": { + "samples": 10235, + "meanMs": 3.1875759316074004, + "p50Ms": 3.1581670000159647, + "p95Ms": 6.159833000012441, + "maxMs": 12.539707999996608 + }, + "lastTenPercentOpenFence": { + "samples": 1025, + "meanMs": 6.201656636098093, + "p50Ms": 6.096417000022484, + "p95Ms": 7.122999999992317, + "maxMs": 12.539707999996608 + }, + "finalUpdate": { + "samples": 5, + "meanMs": 6.199541799991858, + "p50Ms": 6.250833999976749, + "p95Ms": 6.409249999996973, + "maxMs": 6.409249999996973 + } + } + }, + { + "group": "streaming", + "name": "marked progressive parse+render", + "fixture": "closing-backtick-64kib", + "bytes": 65566, + "iterations": 5, + "msPerOp": 146.64199140000272, + "outputBytes": 74349, + "heapDeltaKb": -43318, + "chunkSize": 32, + "warmupReplays": 2, + "updatesPerReplay": 2049, + "openFenceUpdatesPerReplay": 2047, + "checksum": 534148258, + "latency": { + "all": { + "samples": 10245, + "meanMs": 0.07149037306003303, + "p50Ms": 0.07129200000781566, + "p95Ms": 0.13333399998373352, + "maxMs": 0.3815420000173617 + }, + "openFence": { + "samples": 10235, + "meanMs": 0.07148188373229648, + "p50Ms": 0.07129200000781566, + "p95Ms": 0.13329199998406693, + "maxMs": 0.3815420000173617 + }, + "lastTenPercentOpenFence": { + "samples": 1025, + "meanMs": 0.1348610351218206, + "p50Ms": 0.13129200000548735, + "p95Ms": 0.1559170000255108, + "maxMs": 0.3815420000173617 + }, + "finalUpdate": { + "samples": 5, + "meanMs": 0.15603319999645465, + "p50Ms": 0.15587499999674037, + "p95Ms": 0.1638329999987036, + "maxMs": 0.1638329999987036 + } + } } ] } \ No newline at end of file diff --git a/reports/benchmarks.md b/reports/benchmarks.md index adb64ed..29b74d4 100644 --- a/reports/benchmarks.md +++ b/reports/benchmarks.md @@ -1,101 +1,160 @@ # Benchmark Results -Generated: 2026-09-11T20:23:30.359Z +Generated: 2026-09-12T23:59:58.625Z -Lower `ms/op` is better. Benchmarks run in Node with production package builds where available; heap delta is a coarse process-level signal, not an allocation profiler. Streaming rows replay the complete response in 32-character chunks, so one operation is one progressive response. +Environment: Node v24.15.0, darwin arm64, Apple M5 Pro. + +Lower `ms/op` is better. Benchmarks run in Node with production dependency builds where available and local Markdown source; heap delta is a coarse process-level signal, not an allocation profiler. Streaming rows replay the complete response in 32-character chunks, so one operation is one progressive response. Every prefix is parsed from scratch. Replay time includes slicing, timing, and collecting samples; per-update latency times only the parser or renderer call. Browser layout, framework updates, and network delays are excluded. ## Markdown | Name | Fixture | Bytes | Iterations | ms/op | Output bytes | Heap delta KB | | :--- | :--- | ---: | ---: | ---: | ---: | ---: | -| @tanstack/markdown parse | ai-response.md | 652 | 2000 | 0.0184 | 15 | 899.0 | -| @tanstack/markdown render AST with external highlighter | ai-response.md | 652 | 2000 | 0.0053 | 1399 | 1238.7 | -| @tanstack/markdown render AST | ai-response.md | 652 | 2000 | 0.0040 | 1143 | -3027.4 | -| @tanstack/markdown parse+render with external highlighter | ai-response.md | 652 | 2000 | 0.0238 | 1399 | -2596.2 | -| @tanstack/markdown parse+render | ai-response.md | 652 | 2000 | 0.0236 | 1143 | -1860.6 | -| marked parse+render | ai-response.md | 652 | 2000 | 0.0295 | 1035 | -853.6 | -| markdown-it parse+render | ai-response.md | 652 | 2000 | 0.0193 | 1087 | 4088.9 | -| micromark render | ai-response.md | 652 | 2000 | 0.1731 | 825 | -8298.2 | -| commonmark parse+render | ai-response.md | 652 | 2000 | 0.0213 | 825 | 54.9 | -| markdown-wasm render | ai-response.md | 652 | 2000 | 0.0105 | 1117 | 4372.4 | -| unified remark+rehype render | ai-response.md | 652 | 2000 | 0.1991 | 824 | 13756.0 | -| @tanstack/markdown parse | code-heavy.md | 1011 | 1000 | 0.0068 | 15 | -6378.1 | -| @tanstack/markdown render AST with external highlighter | code-heavy.md | 1011 | 1000 | 0.0075 | 4596 | -1122.6 | -| @tanstack/markdown render AST | code-heavy.md | 1011 | 1000 | 0.0032 | 1927 | 10476.3 | -| @tanstack/markdown parse+render with external highlighter | code-heavy.md | 1011 | 1000 | 0.0142 | 4596 | -6339.7 | -| @tanstack/markdown parse+render | code-heavy.md | 1011 | 1000 | 0.0099 | 1927 | 4603.2 | -| marked parse+render | code-heavy.md | 1011 | 1000 | 0.0062 | 1330 | -19757.1 | -| markdown-it parse+render | code-heavy.md | 1011 | 1000 | 0.0083 | 1330 | -5321.0 | -| micromark render | code-heavy.md | 1011 | 1000 | 0.1368 | 1330 | -11833.3 | -| commonmark parse+render | code-heavy.md | 1011 | 1000 | 0.0092 | 1330 | 24577.0 | -| markdown-wasm render | code-heavy.md | 1011 | 1000 | 0.0043 | 1512 | 2560.0 | -| unified remark+rehype render | code-heavy.md | 1011 | 1000 | 0.1521 | 1200 | -7038.0 | -| @tanstack/markdown parse | malformed.md | 237 | 2000 | 0.0027 | 15 | 18580.6 | -| @tanstack/markdown render AST with external highlighter | malformed.md | 237 | 2000 | 0.0017 | 1067 | -17067.3 | -| @tanstack/markdown render AST | malformed.md | 237 | 2000 | 0.0007 | 361 | 5314.9 | -| @tanstack/markdown parse+render with external highlighter | malformed.md | 237 | 2000 | 0.0046 | 1067 | 1452.2 | -| @tanstack/markdown parse+render | malformed.md | 237 | 2000 | 0.0036 | 361 | -8892.2 | -| marked parse+render | malformed.md | 237 | 2000 | 0.0055 | 350 | 16140.5 | -| markdown-it parse+render | malformed.md | 237 | 2000 | 0.0051 | 300 | -7253.6 | -| micromark render | malformed.md | 237 | 2000 | 0.0521 | 300 | 7694.5 | -| commonmark parse+render | malformed.md | 237 | 2000 | 0.0039 | 300 | -9655.9 | -| markdown-wasm render | malformed.md | 237 | 2000 | 0.0019 | 408 | 2938.4 | -| unified remark+rehype render | malformed.md | 237 | 2000 | 0.0692 | 297 | 2219.2 | -| @tanstack/markdown parse | prose-heavy.md | 1700 | 1000 | 0.0172 | 15 | -17554.1 | -| @tanstack/markdown render AST with external highlighter | prose-heavy.md | 1700 | 1000 | 0.0027 | 1903 | 10296.0 | -| @tanstack/markdown render AST | prose-heavy.md | 1700 | 1000 | 0.0027 | 1903 | 10321.3 | -| @tanstack/markdown parse+render with external highlighter | prose-heavy.md | 1700 | 1000 | 0.0177 | 1903 | -7355.0 | -| @tanstack/markdown parse+render | prose-heavy.md | 1700 | 1000 | 0.0166 | 1903 | -7244.9 | -| marked parse+render | prose-heavy.md | 1700 | 1000 | 0.0345 | 1860 | 6729.9 | -| markdown-it parse+render | prose-heavy.md | 1700 | 1000 | 0.0189 | 1860 | -18343.2 | -| micromark render | prose-heavy.md | 1700 | 1000 | 0.2429 | 1862 | 24865.8 | -| commonmark parse+render | prose-heavy.md | 1700 | 1000 | 0.0119 | 1862 | 46458.7 | -| markdown-wasm render | prose-heavy.md | 1700 | 1000 | 0.0063 | 2340 | 3368.4 | -| unified remark+rehype render | prose-heavy.md | 1700 | 1000 | 0.2897 | 1859 | 50897.1 | -| @tanstack/markdown parse | small-doc.md | 432 | 2000 | 0.0094 | 15 | 7577.4 | -| @tanstack/markdown render AST with external highlighter | small-doc.md | 432 | 2000 | 0.0031 | 1370 | 27814.1 | -| @tanstack/markdown render AST | small-doc.md | 432 | 2000 | 0.0027 | 1044 | -42766.9 | -| @tanstack/markdown parse+render with external highlighter | small-doc.md | 432 | 2000 | 0.0126 | 1370 | 31414.9 | -| @tanstack/markdown parse+render | small-doc.md | 432 | 2000 | 0.0121 | 1044 | -40133.3 | -| marked parse+render | small-doc.md | 432 | 2000 | 0.0121 | 724 | 50632.2 | -| markdown-it parse+render | small-doc.md | 432 | 2000 | 0.0078 | 776 | 3160.2 | -| micromark render | small-doc.md | 432 | 2000 | 0.0974 | 535 | 44970.4 | -| commonmark parse+render | small-doc.md | 432 | 2000 | 0.0071 | 535 | -15145.2 | -| markdown-wasm render | small-doc.md | 432 | 2000 | 0.0039 | 854 | 3580.2 | -| unified remark+rehype render | small-doc.md | 432 | 2000 | 0.1293 | 534 | 4179.8 | -| @tanstack/markdown parse | tables-lists.md | 454 | 2000 | 0.0232 | 15 | -43744.4 | -| @tanstack/markdown render AST with external highlighter | tables-lists.md | 454 | 2000 | 0.0044 | 1315 | 42278.0 | -| @tanstack/markdown render AST | tables-lists.md | 454 | 2000 | 0.0040 | 1315 | -23161.2 | -| @tanstack/markdown parse+render with external highlighter | tables-lists.md | 454 | 2000 | 0.0274 | 1315 | -1853.1 | -| @tanstack/markdown parse+render | tables-lists.md | 454 | 2000 | 0.0272 | 1315 | -2112.7 | -| marked parse+render | tables-lists.md | 454 | 2000 | 0.0292 | 1102 | -14329.2 | -| markdown-it parse+render | tables-lists.md | 454 | 2000 | 0.0180 | 1325 | 26395.5 | -| micromark render | tables-lists.md | 454 | 2000 | 0.1677 | 627 | -39153.3 | -| commonmark parse+render | tables-lists.md | 454 | 2000 | 0.0114 | 627 | 28363.8 | -| markdown-wasm render | tables-lists.md | 454 | 2000 | 0.0059 | 1202 | 4506.2 | -| unified remark+rehype render | tables-lists.md | 454 | 2000 | 0.1964 | 622 | 7601.9 | +| @tanstack/markdown parse | ai-response.md | 652 | 2000 | 0.0231 | 15 | 35036.6 | +| @tanstack/markdown render AST with external highlighter | ai-response.md | 652 | 2000 | 0.0050 | 1399 | 10160.7 | +| @tanstack/markdown render AST | ai-response.md | 652 | 2000 | 0.0038 | 1143 | 133.8 | +| @tanstack/markdown parse+render with external highlighter | ai-response.md | 652 | 2000 | 0.0251 | 1399 | 11894.5 | +| @tanstack/markdown parse+render | ai-response.md | 652 | 2000 | 0.0232 | 1143 | 44793.7 | +| marked parse+render | ai-response.md | 652 | 2000 | 0.0293 | 1035 | -92849.1 | +| markdown-it parse+render | ai-response.md | 652 | 2000 | 0.0187 | 1087 | 44907.1 | +| micromark render | ai-response.md | 652 | 2000 | 0.1664 | 825 | 51435.6 | +| commonmark parse+render | ai-response.md | 652 | 2000 | 0.0131 | 825 | -49166.8 | +| markdown-wasm render | ai-response.md | 652 | 2000 | 0.0084 | 1117 | 4376.7 | +| unified remark+rehype render | ai-response.md | 652 | 2000 | 0.2147 | 824 | 7854.6 | +| @tanstack/markdown parse | code-heavy.md | 1011 | 1000 | 0.0093 | 15 | 23903.4 | +| @tanstack/markdown render AST with external highlighter | code-heavy.md | 1011 | 1000 | 0.0088 | 4596 | -30194.4 | +| @tanstack/markdown render AST | code-heavy.md | 1011 | 1000 | 0.0040 | 1927 | 10476.2 | +| @tanstack/markdown parse+render with external highlighter | code-heavy.md | 1011 | 1000 | 0.0193 | 4596 | -5389.8 | +| @tanstack/markdown parse+render | code-heavy.md | 1011 | 1000 | 0.0166 | 1927 | -25821.4 | +| marked parse+render | code-heavy.md | 1011 | 1000 | 0.0073 | 1330 | 12886.8 | +| markdown-it parse+render | code-heavy.md | 1011 | 1000 | 0.0098 | 1330 | 27313.4 | +| micromark render | code-heavy.md | 1011 | 1000 | 0.1617 | 1330 | -5850.4 | +| commonmark parse+render | code-heavy.md | 1011 | 1000 | 0.0109 | 1330 | -40876.0 | +| markdown-wasm render | code-heavy.md | 1011 | 1000 | 0.0054 | 1512 | 2558.3 | +| unified remark+rehype render | code-heavy.md | 1011 | 1000 | 0.1567 | 1200 | 24973.7 | +| @tanstack/markdown parse | malformed.md | 237 | 2000 | 0.0027 | 15 | -44947.9 | +| @tanstack/markdown render AST with external highlighter | malformed.md | 237 | 2000 | 0.0016 | 1067 | 15659.7 | +| @tanstack/markdown render AST | malformed.md | 237 | 2000 | 0.0007 | 361 | 5315.2 | +| @tanstack/markdown parse+render with external highlighter | malformed.md | 237 | 2000 | 0.0046 | 1067 | 33660.8 | +| @tanstack/markdown parse+render | malformed.md | 237 | 2000 | 0.0038 | 361 | -34278.9 | +| marked parse+render | malformed.md | 237 | 2000 | 0.0054 | 350 | 16657.7 | +| markdown-it parse+render | malformed.md | 237 | 2000 | 0.0056 | 300 | -31630.1 | +| micromark render | malformed.md | 237 | 2000 | 0.0504 | 300 | 11184.8 | +| commonmark parse+render | malformed.md | 237 | 2000 | 0.0039 | 300 | 23073.0 | +| markdown-wasm render | malformed.md | 237 | 2000 | 0.0018 | 408 | 2938.7 | +| unified remark+rehype render | malformed.md | 237 | 2000 | 0.0552 | 297 | -30681.6 | +| @tanstack/markdown parse | prose-heavy.md | 1700 | 1000 | 0.0181 | 15 | 1601.4 | +| @tanstack/markdown render AST with external highlighter | prose-heavy.md | 1700 | 1000 | 0.0021 | 1903 | 10292.2 | +| @tanstack/markdown render AST | prose-heavy.md | 1700 | 1000 | 0.0024 | 1903 | 10320.5 | +| @tanstack/markdown parse+render with external highlighter | prose-heavy.md | 1700 | 1000 | 0.0201 | 1903 | 18362.3 | +| @tanstack/markdown parse+render | prose-heavy.md | 1700 | 1000 | 0.0203 | 1903 | 18847.2 | +| marked parse+render | prose-heavy.md | 1700 | 1000 | 0.0381 | 1860 | -5593.0 | +| markdown-it parse+render | prose-heavy.md | 1700 | 1000 | 0.0206 | 1860 | -8952.1 | +| micromark render | prose-heavy.md | 1700 | 1000 | 0.2549 | 1862 | 21780.6 | +| commonmark parse+render | prose-heavy.md | 1700 | 1000 | 0.0136 | 1862 | -14599.4 | +| markdown-wasm render | prose-heavy.md | 1700 | 1000 | 0.0064 | 2340 | 3369.4 | +| unified remark+rehype render | prose-heavy.md | 1700 | 1000 | 0.3225 | 1859 | -73200.9 | +| @tanstack/markdown parse | small-doc.md | 432 | 2000 | 0.0127 | 15 | 20112.5 | +| @tanstack/markdown render AST with external highlighter | small-doc.md | 432 | 2000 | 0.0043 | 1370 | -24522.9 | +| @tanstack/markdown render AST | small-doc.md | 432 | 2000 | 0.0028 | 1044 | 21772.9 | +| @tanstack/markdown parse+render with external highlighter | small-doc.md | 432 | 2000 | 0.0164 | 1370 | -9773.9 | +| @tanstack/markdown parse+render | small-doc.md | 432 | 2000 | 0.0168 | 1044 | -7187.6 | +| marked parse+render | small-doc.md | 432 | 2000 | 0.0147 | 724 | 50669.2 | +| markdown-it parse+render | small-doc.md | 432 | 2000 | 0.0087 | 776 | 4037.9 | +| micromark render | small-doc.md | 432 | 2000 | 0.1330 | 535 | 44880.4 | +| commonmark parse+render | small-doc.md | 432 | 2000 | 0.0074 | 535 | -15136.9 | +| markdown-wasm render | small-doc.md | 432 | 2000 | 0.0037 | 854 | 3631.2 | +| unified remark+rehype render | small-doc.md | 432 | 2000 | 0.1475 | 534 | 4013.4 | +| @tanstack/markdown parse | tables-lists.md | 454 | 2000 | 0.0295 | 15 | 12749.1 | +| @tanstack/markdown render AST with external highlighter | tables-lists.md | 454 | 2000 | 0.0058 | 1315 | -5483.2 | +| @tanstack/markdown render AST | tables-lists.md | 454 | 2000 | 0.0052 | 1315 | -14400.9 | +| @tanstack/markdown parse+render with external highlighter | tables-lists.md | 454 | 2000 | 0.0366 | 1315 | 6365.8 | +| @tanstack/markdown parse+render | tables-lists.md | 454 | 2000 | 0.0383 | 1315 | -91885.8 | +| marked parse+render | tables-lists.md | 454 | 2000 | 0.0325 | 1102 | -2774.7 | +| markdown-it parse+render | tables-lists.md | 454 | 2000 | 0.0213 | 1325 | 26335.4 | +| micromark render | tables-lists.md | 454 | 2000 | 0.1830 | 627 | 43377.3 | +| commonmark parse+render | tables-lists.md | 454 | 2000 | 0.0117 | 627 | -36650.8 | +| markdown-wasm render | tables-lists.md | 454 | 2000 | 0.0058 | 1202 | 4506.1 | +| unified remark+rehype render | tables-lists.md | 454 | 2000 | 0.2090 | 622 | -44098.0 | ## Streaming | Name | Fixture | Bytes | Iterations | ms/op | Output bytes | Heap delta KB | | :--- | :--- | ---: | ---: | ---: | ---: | ---: | -| @tanstack/markdown streaming profile | ai-response.md | 652 | 250 | 0.2236 | 1112 | -8191.5 | -| marked progressive parse+render | ai-response.md | 652 | 250 | 0.2804 | 1035 | 3232.0 | +| @tanstack/markdown streaming parse | ai-response.md | 652 | 250 | 0.2141 | 0 | 27889.1 | +| @tanstack/markdown streaming profile | ai-response.md | 652 | 250 | 0.2579 | 1112 | 65860.8 | +| @tanstack/markdown streaming with @tanstack/highlight | ai-response.md | 652 | 250 | 0.3811 | 1274 | 26043.9 | +| marked progressive parse+render | ai-response.md | 652 | 250 | 0.3046 | 1035 | 19526.3 | +| @tanstack/markdown streaming parse | unfinished-backtick-4kib | 4096 | 20 | 0.4621 | 0 | -34834.7 | +| @tanstack/markdown streaming profile | unfinished-backtick-4kib | 4096 | 20 | 0.8601 | 4731 | 695.4 | +| @tanstack/markdown streaming with @tanstack/highlight | unfinished-backtick-4kib | 4096 | 20 | 22.0687 | 16010 | 62990.4 | +| marked progressive parse+render | unfinished-backtick-4kib | 4096 | 20 | 0.7149 | 4702 | -36811.4 | +| @tanstack/markdown streaming parse | unfinished-backtick-16kib | 16384 | 10 | 5.8481 | 0 | -165018.2 | +| @tanstack/markdown streaming profile | unfinished-backtick-16kib | 16384 | 10 | 10.3301 | 18667 | 66138.5 | +| @tanstack/markdown streaming with @tanstack/highlight | unfinished-backtick-16kib | 16384 | 10 | 433.2552 | 62868 | 35834.8 | +| marked progressive parse+render | unfinished-backtick-16kib | 16384 | 10 | 10.2401 | 18638 | -4537.5 | +| @tanstack/markdown streaming parse | unfinished-backtick-64kib | 65536 | 5 | 88.7832 | 0 | 58977.8 | +| @tanstack/markdown streaming profile | unfinished-backtick-64kib | 65536 | 5 | 166.6901 | 74347 | -65495.2 | +| @tanstack/markdown streaming with @tanstack/highlight | unfinished-backtick-64kib | 65536 | 5 | 6953.5152 | 249146 | -35723.4 | +| marked progressive parse+render | unfinished-backtick-64kib | 65536 | 5 | 148.4297 | 74318 | 29715.1 | +| @tanstack/markdown streaming parse | unfinished-tilde-64kib | 65536 | 5 | 79.8277 | 0 | 71313.6 | +| @tanstack/markdown streaming profile | unfinished-tilde-64kib | 65536 | 5 | 151.5830 | 74347 | -86751.5 | +| @tanstack/markdown streaming with @tanstack/highlight | unfinished-tilde-64kib | 65536 | 5 | 8161.2490 | 249146 | -109512.5 | +| marked progressive parse+render | unfinished-tilde-64kib | 65536 | 5 | 147.2293 | 74318 | 29615.7 | +| @tanstack/markdown streaming parse | closing-backtick-64kib | 65566 | 5 | 83.4517 | 0 | 72340.5 | +| @tanstack/markdown streaming profile | closing-backtick-64kib | 65566 | 5 | 158.2382 | 74378 | 3098.0 | +| @tanstack/markdown streaming with @tanstack/highlight | closing-backtick-64kib | 65566 | 5 | 6534.3609 | 249177 | -117985.0 | +| marked progressive parse+render | closing-backtick-64kib | 65566 | 5 | 146.6420 | 74349 | -43318.0 | + +For persistent React and incremental DOM comparisons against streaming-focused libraries, see the [browser streaming report](./streaming-browser.md). + +## Streaming update latency + +All timings below are milliseconds, pooled across measured replays after two full warmup replays. Percentiles use nearest rank. Each update contributes its output to a checksum. Parse-only rows use the same streaming options as rendering rows and report zero HTML output bytes. + +Generated fixtures contain a growing TypeScript fence with an unfinished final line. Backtick fences cover 4, 16, and 64 KiB; a 64 KiB tilde fence checks the other delimiter. The closing case appends a closing fence and trailing prose to the same 64 KiB body. Open-fence membership uses known fixture offsets, independently of the parser. The late-open sample covers prefixes in the last 10% of the source up to the closing fence, or EOF when it never closes. + +The streaming highlighter is the installed @tanstack/highlight TypeScript tokenizer and HTML adapter, initialized before timing and called on every code block on every update. The non-streaming external-highlighter rows above use a line-wrapping stub. Parse-only and plain-render rows help compare parsing cost with rendering; these are separate runs, not an instrumented phase breakdown. + +| Name | Fixture | Updates/replay | Open updates/replay | Update p50 | Update p95 | Open p50 | Open p95 | Open max | Late open p95 | Final p50 | +| :--- | :--- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| @tanstack/markdown streaming parse | ai-response.md | 21 | 2 | 0.0075 | 0.0159 | 0.0058 | 0.0071 | 0.0494 | 0.0070 | 0.0149 | +| @tanstack/markdown streaming profile | ai-response.md | 21 | 2 | 0.0089 | 0.0193 | 0.0072 | 0.0087 | 0.0530 | 0.0091 | 0.0185 | +| @tanstack/markdown streaming with @tanstack/highlight | ai-response.md | 21 | 2 | 0.0176 | 0.0295 | 0.0126 | 0.0174 | 1.8180 | 0.0201 | 0.0261 | +| marked progressive parse+render | ai-response.md | 21 | 2 | 0.0131 | 0.0254 | 0.0098 | 0.0133 | 0.0253 | 0.0138 | 0.0235 | +| @tanstack/markdown streaming parse | unfinished-backtick-4kib | 128 | 127 | 0.0031 | 0.0051 | 0.0031 | 0.0051 | 0.6202 | 0.0061 | 0.0050 | +| @tanstack/markdown streaming profile | unfinished-backtick-4kib | 128 | 127 | 0.0056 | 0.0097 | 0.0057 | 0.0097 | 2.0297 | 0.0134 | 0.0095 | +| @tanstack/markdown streaming with @tanstack/highlight | unfinished-backtick-4kib | 128 | 127 | 0.1685 | 0.3212 | 0.1694 | 0.3213 | 0.7369 | 0.3593 | 0.3322 | +| marked progressive parse+render | unfinished-backtick-4kib | 128 | 127 | 0.0053 | 0.0093 | 0.0054 | 0.0093 | 0.3233 | 0.0112 | 0.0095 | +| @tanstack/markdown streaming parse | unfinished-backtick-16kib | 512 | 511 | 0.0095 | 0.0181 | 0.0095 | 0.0181 | 2.3500 | 0.0242 | 0.0176 | +| @tanstack/markdown streaming profile | unfinished-backtick-16kib | 512 | 511 | 0.0189 | 0.0345 | 0.0189 | 0.0345 | 1.5925 | 0.0410 | 0.0361 | +| @tanstack/markdown streaming with @tanstack/highlight | unfinished-backtick-16kib | 512 | 511 | 0.8176 | 1.7403 | 0.8186 | 1.7404 | 3.7254 | 2.1948 | 1.6743 | +| marked progressive parse+render | unfinished-backtick-16kib | 512 | 511 | 0.0194 | 0.0359 | 0.0195 | 0.0359 | 0.2829 | 0.0476 | 0.0360 | +| @tanstack/markdown streaming parse | unfinished-backtick-64kib | 2048 | 2047 | 0.0362 | 0.0810 | 0.0362 | 0.0810 | 3.2907 | 0.1178 | 0.0671 | +| @tanstack/markdown streaming profile | unfinished-backtick-64kib | 2048 | 2047 | 0.0735 | 0.1531 | 0.0735 | 0.1533 | 2.1279 | 0.2269 | 0.1315 | +| @tanstack/markdown streaming with @tanstack/highlight | unfinished-backtick-64kib | 2048 | 2047 | 3.4199 | 6.5803 | 3.4216 | 6.5835 | 12.0435 | 7.7552 | 6.7720 | +| marked progressive parse+render | unfinished-backtick-64kib | 2048 | 2047 | 0.0726 | 0.1342 | 0.0727 | 0.1342 | 0.4335 | 0.1549 | 0.1372 | +| @tanstack/markdown streaming parse | unfinished-tilde-64kib | 2048 | 2047 | 0.0340 | 0.0644 | 0.0340 | 0.0645 | 2.7499 | 0.0819 | 0.0658 | +| @tanstack/markdown streaming profile | unfinished-tilde-64kib | 2048 | 2047 | 0.0697 | 0.1275 | 0.0698 | 0.1275 | 1.7017 | 0.1492 | 0.1275 | +| @tanstack/markdown streaming with @tanstack/highlight | unfinished-tilde-64kib | 2048 | 2047 | 3.7827 | 8.1041 | 3.7843 | 8.1067 | 92.9487 | 9.3680 | 6.7152 | +| marked progressive parse+render | unfinished-tilde-64kib | 2048 | 2047 | 0.0708 | 0.1332 | 0.0709 | 0.1332 | 0.3760 | 0.1583 | 0.1394 | +| @tanstack/markdown streaming parse | closing-backtick-64kib | 2049 | 2047 | 0.0349 | 0.0696 | 0.0349 | 0.0695 | 3.1709 | 0.0973 | 0.0747 | +| @tanstack/markdown streaming profile | closing-backtick-64kib | 2049 | 2047 | 0.0692 | 0.1397 | 0.0692 | 0.1395 | 1.7650 | 0.1669 | 0.1467 | +| @tanstack/markdown streaming with @tanstack/highlight | closing-backtick-64kib | 2049 | 2047 | 3.1582 | 6.1610 | 3.1582 | 6.1598 | 12.5397 | 7.1230 | 6.2508 | +| marked progressive parse+render | closing-backtick-64kib | 2049 | 2047 | 0.0713 | 0.1333 | 0.0713 | 0.1333 | 0.3815 | 0.1559 | 0.1559 | ## Averages | Group | Name | Mean ms/op | | :--- | :--- | ---: | -| markdown | @tanstack/markdown parse | 0.0129 | -| markdown | @tanstack/markdown render AST with external highlighter | 0.0041 | -| markdown | @tanstack/markdown render AST | 0.0029 | -| markdown | @tanstack/markdown parse+render with external highlighter | 0.0167 | -| markdown | @tanstack/markdown parse+render | 0.0155 | -| markdown | marked parse+render | 0.0195 | -| markdown | markdown-it parse+render | 0.0129 | -| markdown | micromark render | 0.1450 | -| markdown | commonmark parse+render | 0.0108 | -| markdown | markdown-wasm render | 0.0055 | -| markdown | unified remark+rehype render | 0.1726 | -| streaming | @tanstack/markdown streaming profile | 0.2236 | -| streaming | marked progressive parse+render | 0.2804 | +| markdown | @tanstack/markdown parse | 0.0159 | +| markdown | @tanstack/markdown render AST with external highlighter | 0.0046 | +| markdown | @tanstack/markdown render AST | 0.0031 | +| markdown | @tanstack/markdown parse+render with external highlighter | 0.0204 | +| markdown | @tanstack/markdown parse+render | 0.0198 | +| markdown | marked parse+render | 0.0212 | +| markdown | markdown-it parse+render | 0.0141 | +| markdown | micromark render | 0.1583 | +| markdown | commonmark parse+render | 0.0101 | +| markdown | markdown-wasm render | 0.0052 | +| markdown | unified remark+rehype render | 0.1843 | diff --git a/reports/sizes.json b/reports/sizes.json index 5347214..286102b 100644 --- a/reports/sizes.json +++ b/reports/sizes.json @@ -1,40 +1,40 @@ { - "generatedAt": "2026-09-11T20:23:25.725Z", + "generatedAt": "2026-09-12T23:57:10.287Z", "results": [ { "group": "tanstack", "name": "parser only", - "minBytes": 13157, - "gzipBytes": 4975, - "brotliBytes": 4593 + "minBytes": 11949, + "gzipBytes": 4894, + "brotliBytes": 4539 }, { "group": "tanstack", "name": "html renderer no highlighter", - "minBytes": 18337, - "gzipBytes": 6807, - "brotliBytes": 6240 + "minBytes": 17139, + "gzipBytes": 6729, + "brotliBytes": 6158 }, { "group": "tanstack", "name": "html renderer with external highlighter stub", - "minBytes": 18373, - "gzipBytes": 6829, - "brotliBytes": 6260 + "minBytes": 17175, + "gzipBytes": 6751, + "brotliBytes": 6184 }, { "group": "tanstack", "name": "react adapter", - "minBytes": 18271, - "gzipBytes": 6722, - "brotliBytes": 6184 + "minBytes": 17147, + "gzipBytes": 6676, + "brotliBytes": 6170 }, { "group": "tanstack", "name": "octane adapter", - "minBytes": 18283, - "gzipBytes": 6727, - "brotliBytes": 6194 + "minBytes": 17084, + "gzipBytes": 6644, + "brotliBytes": 6143 }, { "group": "tanstack", @@ -60,9 +60,9 @@ { "group": "tanstack", "name": "react adapter with streaming extension", - "minBytes": 18962, - "gzipBytes": 6907, - "brotliBytes": 6356 + "minBytes": 17839, + "gzipBytes": 6860, + "brotliBytes": 6302 }, { "group": "tanstack", @@ -116,37 +116,37 @@ { "group": "tanstack-public", "name": ".", - "minBytes": 18598, - "gzipBytes": 6936, - "brotliBytes": 6345 + "minBytes": 17404, + "gzipBytes": 6852, + "brotliBytes": 6267 }, { "group": "tanstack-public", "name": "./html", - "minBytes": 18560, - "gzipBytes": 6920, - "brotliBytes": 6331 + "minBytes": 17364, + "gzipBytes": 6840, + "brotliBytes": 6283 }, { "group": "tanstack-public", "name": "./parser", - "minBytes": 13289, - "gzipBytes": 5061, - "brotliBytes": 4664 + "minBytes": 12082, + "gzipBytes": 4981, + "brotliBytes": 4599 }, { "group": "tanstack-public", "name": "./react", - "minBytes": 18470, - "gzipBytes": 6828, - "brotliBytes": 6279 + "minBytes": 17347, + "gzipBytes": 6790, + "brotliBytes": 6241 }, { "group": "tanstack-public", "name": "./octane", - "minBytes": 18485, - "gzipBytes": 6833, - "brotliBytes": 6287 + "minBytes": 17287, + "gzipBytes": 6752, + "brotliBytes": 6209 }, { "group": "tanstack-public", diff --git a/reports/sizes.md b/reports/sizes.md index a30d23f..c639246 100644 --- a/reports/sizes.md +++ b/reports/sizes.md @@ -1,20 +1,20 @@ # Bundle Size Results -Generated: 2026-09-11T20:23:25.726Z +Generated: 2026-09-12T23:57:10.289Z Bundles are ESM, browser-targeted, minified with esbuild, then gzip and brotli compressed. Framework runtimes are externalized for the React and Octane adapters. | Group | Entry | Min bytes | Gzip bytes | Brotli bytes | | :--- | :--- | ---: | ---: | ---: | -| tanstack | parser only | 13157 | 4975 | 4593 | -| tanstack | html renderer no highlighter | 18337 | 6807 | 6240 | -| tanstack | html renderer with external highlighter stub | 18373 | 6829 | 6260 | -| tanstack | react adapter | 18271 | 6722 | 6184 | -| tanstack | octane adapter | 18283 | 6727 | 6194 | +| tanstack | parser only | 11949 | 4894 | 4539 | +| tanstack | html renderer no highlighter | 17139 | 6729 | 6158 | +| tanstack | html renderer with external highlighter stub | 17175 | 6751 | 6184 | +| tanstack | react adapter | 17147 | 6676 | 6170 | +| tanstack | octane adapter | 17084 | 6644 | 6143 | | tanstack | docs extension preset | 6423 | 2292 | 2073 | | tanstack | callouts extension | 506 | 335 | 278 | | tanstack | streaming extension | 699 | 311 | 253 | -| tanstack | react adapter with streaming extension | 18962 | 6907 | 6356 | +| tanstack | react adapter with streaming extension | 17839 | 6860 | 6302 | | tanstack | tabs transforms | 3290 | 1221 | 1082 | | markdown | marked | 41415 | 12548 | 11509 | | markdown | markdown-it | 148242 | 52655 | 44023 | @@ -22,11 +22,11 @@ Bundles are ESM, browser-targeted, minified with esbuild, then gzip and brotli c | markdown | commonmark | 159687 | 48084 | 39793 | | markdown | markdown-wasm browser js+wasm | 66387 | 31275 | 26431 | | markdown | unified remark+rehype | 119588 | 36843 | 32686 | -| tanstack-public | . | 18598 | 6936 | 6345 | -| tanstack-public | ./html | 18560 | 6920 | 6331 | -| tanstack-public | ./parser | 13289 | 5061 | 4664 | -| tanstack-public | ./react | 18470 | 6828 | 6279 | -| tanstack-public | ./octane | 18485 | 6833 | 6287 | +| tanstack-public | . | 17404 | 6852 | 6267 | +| tanstack-public | ./html | 17364 | 6840 | 6283 | +| tanstack-public | ./parser | 12082 | 4981 | 4599 | +| tanstack-public | ./react | 17347 | 6790 | 6241 | +| tanstack-public | ./octane | 17287 | 6752 | 6209 | | tanstack-public | ./extensions/callouts | 660 | 432 | 360 | | tanstack-public | ./extensions/comment-components | 1073 | 647 | 542 | | tanstack-public | ./extensions/docs | 6587 | 2392 | 2162 | diff --git a/reports/streaming-browser.json b/reports/streaming-browser.json new file mode 100644 index 0000000..6dfad0b --- /dev/null +++ b/reports/streaming-browser.json @@ -0,0 +1,1381 @@ +{ + "generatedAt": "2026-09-12T17:00:04.103Z", + "environment": { + "node": "v24.15.0", + "browser": "152.0.7977.83", + "platform": "darwin", + "arch": "arm64", + "cpu": "Apple M5 Pro", + "isolated": true, + "dependencies": { + "react": "19.2.7", + "react-dom": "19.2.7", + "streamdown": "2.6.0", + "streaming-markdown": "0.2.15" + } + }, + "results": [ + { + "name": "TanStack Markdown React", + "fixture": "unfinished-backtick-4kib", + "bytes": 4096, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 127, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 0, + "finalCodeMatches": true, + "finalTextDiffers": false + }, + "replay": { + "samples": 5, + "meanMs": 4.528999972343445, + "p50Ms": 4.605000019073486, + "p95Ms": 4.684999942779541, + "maxMs": 4.684999942779541 + }, + "finish": { + "samples": 5, + "meanMs": 0.0009999990463256836, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 0.004999995231628418 + }, + "updates": { + "update": { + "samples": 640, + "meanMs": 0.00989062488079071, + "p50Ms": 0.009999990463256836, + "p95Ms": 0.015000104904174805, + "maxMs": 0.2850000858306885 + }, + "updateAndLayout": { + "samples": 640, + "meanMs": 0.034890624694526196, + "p50Ms": 0.034999966621398926, + "p95Ms": 0.05500006675720215, + "maxMs": 0.3250000476837158 + } + }, + "openFence": { + "update": { + "samples": 635, + "meanMs": 0.009677165324293722, + "p50Ms": 0.009999990463256836, + "p95Ms": 0.015000104904174805, + "maxMs": 0.2850000858306885 + }, + "updateAndLayout": { + "samples": 635, + "meanMs": 0.0346377952830998, + "p50Ms": 0.034999966621398926, + "p95Ms": 0.05500006675720215, + "maxMs": 0.3250000476837158 + } + }, + "lateOpenFence": { + "update": { + "samples": 65, + "meanMs": 0.01269231392787053, + "p50Ms": 0.009999990463256836, + "p95Ms": 0.019999980926513672, + "maxMs": 0.08000004291534424 + }, + "updateAndLayout": { + "samples": 65, + "meanMs": 0.0514615352337177, + "p50Ms": 0.04999995231628418, + "p95Ms": 0.06499993801116943, + "maxMs": 0.11500000953674316 + } + }, + "checksum": 775341 + }, + { + "name": "Streamdown React", + "fixture": "unfinished-backtick-4kib", + "bytes": 4096, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 127, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 1, + "finalCodeMatches": true, + "finalTextDiffers": true + }, + "replay": { + "samples": 5, + "meanMs": 30.30799994468689, + "p50Ms": 29.75499999523163, + "p95Ms": 32.359999895095825, + "maxMs": 32.359999895095825 + }, + "finish": { + "samples": 5, + "meanMs": 0.23099994659423828, + "p50Ms": 0.2049999237060547, + "p95Ms": 0.34999990463256836, + "maxMs": 0.34999990463256836 + }, + "updates": { + "update": { + "samples": 640, + "meanMs": 0.14968750011175871, + "p50Ms": 0.14499998092651367, + "p95Ms": 0.2549999952316284, + "maxMs": 1.1800000667572021 + }, + "updateAndLayout": { + "samples": 640, + "meanMs": 0.2342031253501773, + "p50Ms": 0.23500001430511475, + "p95Ms": 0.39499998092651367, + "maxMs": 1.2850000858306885 + } + }, + "openFence": { + "update": { + "samples": 635, + "meanMs": 0.14910236268531618, + "p50Ms": 0.14499998092651367, + "p95Ms": 0.2549999952316284, + "maxMs": 1.1800000667572021 + }, + "updateAndLayout": { + "samples": 635, + "meanMs": 0.23391338614966925, + "p50Ms": 0.23500001430511475, + "p95Ms": 0.39499998092651367, + "maxMs": 1.2850000858306885 + } + }, + "lateOpenFence": { + "update": { + "samples": 65, + "meanMs": 0.2404615328862117, + "p50Ms": 0.22499990463256836, + "p95Ms": 0.3799999952316284, + "maxMs": 0.44499993324279785 + }, + "updateAndLayout": { + "samples": 65, + "meanMs": 0.392692310993488, + "p50Ms": 0.375, + "p95Ms": 0.5399999618530273, + "maxMs": 0.6200000047683716 + } + }, + "checksum": 775341 + }, + { + "name": "streaming-markdown DOM", + "fixture": "unfinished-backtick-4kib", + "bytes": 4096, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 127, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 2, + "finalCodeMatches": true, + "finalTextDiffers": false + }, + "replay": { + "samples": 5, + "meanMs": 3.7110000133514403, + "p50Ms": 3.069999933242798, + "p95Ms": 4.810000061988831, + "maxMs": 4.810000061988831 + }, + "finish": { + "samples": 5, + "meanMs": 0.05500004291534424, + "p50Ms": 0.040000081062316895, + "p95Ms": 0.08500003814697266, + "maxMs": 0.08500003814697266 + }, + "updates": { + "update": { + "samples": 640, + "meanMs": 0.00123437512665987, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 0.029999971389770508 + }, + "updateAndLayout": { + "samples": 640, + "meanMs": 0.028304686956107616, + "p50Ms": 0.02499997615814209, + "p95Ms": 0.060000061988830566, + "maxMs": 0.38999998569488525 + } + }, + "openFence": { + "update": { + "samples": 635, + "meanMs": 0.0011653543457271545, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 0.029999971389770508 + }, + "updateAndLayout": { + "samples": 635, + "meanMs": 0.028330707925511157, + "p50Ms": 0.02499997615814209, + "p95Ms": 0.060000061988830566, + "maxMs": 0.38999998569488525 + } + }, + "lateOpenFence": { + "update": { + "samples": 65, + "meanMs": 0.0016153885768010066, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 0.005000114440917969 + }, + "updateAndLayout": { + "samples": 65, + "meanMs": 0.04623076548943153, + "p50Ms": 0.039999961853027344, + "p95Ms": 0.0700000524520874, + "maxMs": 0.0849999189376831 + } + }, + "checksum": 774753 + }, + { + "name": "TanStack Markdown React", + "fixture": "unfinished-backtick-16kib", + "bytes": 16384, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 511, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 0, + "finalCodeMatches": true, + "finalTextDiffers": false + }, + "replay": { + "samples": 5, + "meanMs": 47.487000012397765, + "p50Ms": 46.18999993801117, + "p95Ms": 53.3050000667572, + "maxMs": 53.3050000667572 + }, + "finish": { + "samples": 5, + "meanMs": 0, + "p50Ms": 0, + "p95Ms": 0, + "maxMs": 0 + }, + "updates": { + "update": { + "samples": 2560, + "meanMs": 0.016054687881842257, + "p50Ms": 0.014999985694885254, + "p95Ms": 0.029999971389770508, + "maxMs": 0.09500002861022949 + }, + "updateAndLayout": { + "samples": 2560, + "meanMs": 0.08960742205381393, + "p50Ms": 0.08000004291534424, + "p95Ms": 0.1549999713897705, + "maxMs": 5.034999966621399 + } + }, + "openFence": { + "update": { + "samples": 2555, + "meanMs": 0.016013698988464713, + "p50Ms": 0.014999985694885254, + "p95Ms": 0.029999971389770508, + "maxMs": 0.09500002861022949 + }, + "updateAndLayout": { + "samples": 2555, + "meanMs": 0.08963992189754479, + "p50Ms": 0.08000004291534424, + "p95Ms": 0.15500009059906006, + "maxMs": 5.034999966621399 + } + }, + "lateOpenFence": { + "update": { + "samples": 260, + "meanMs": 0.02742307552924523, + "p50Ms": 0.02499997615814209, + "p95Ms": 0.04999995231628418, + "maxMs": 0.09500002861022949 + }, + "updateAndLayout": { + "samples": 260, + "meanMs": 0.18905769128065844, + "p50Ms": 0.1499999761581421, + "p95Ms": 0.23500001430511475, + "maxMs": 5.034999966621399 + } + }, + "checksum": 10925523 + }, + { + "name": "Streamdown React", + "fixture": "unfinished-backtick-16kib", + "bytes": 16384, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 511, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 1, + "finalCodeMatches": true, + "finalTextDiffers": true + }, + "replay": { + "samples": 5, + "meanMs": 366.48600001335143, + "p50Ms": 365.5550000667572, + "p95Ms": 370.585000038147, + "maxMs": 370.585000038147 + }, + "finish": { + "samples": 5, + "meanMs": 0.6489999771118165, + "p50Ms": 0.6499999761581421, + "p95Ms": 0.6649999618530273, + "maxMs": 0.6649999618530273 + }, + "updates": { + "update": { + "samples": 2560, + "meanMs": 0.411314452951774, + "p50Ms": 0.3950001001358032, + "p95Ms": 0.7450000047683716, + "maxMs": 1.9800000190734863 + }, + "updateAndLayout": { + "samples": 2560, + "meanMs": 0.7138535155914724, + "p50Ms": 0.6950000524520874, + "p95Ms": 1.3200000524520874, + "maxMs": 2.534999966621399 + } + }, + "openFence": { + "update": { + "samples": 2555, + "meanMs": 0.41170254383535065, + "p50Ms": 0.3999999761581421, + "p95Ms": 0.7450000047683716, + "maxMs": 1.9800000190734863 + }, + "updateAndLayout": { + "samples": 2555, + "meanMs": 0.7147240704053068, + "p50Ms": 0.6999999284744263, + "p95Ms": 1.3200000524520874, + "maxMs": 2.534999966621399 + } + }, + "lateOpenFence": { + "update": { + "samples": 260, + "meanMs": 0.751346151645367, + "p50Ms": 0.7300000190734863, + "p95Ms": 0.875, + "maxMs": 1.9449999332427979 + }, + "updateAndLayout": { + "samples": 260, + "meanMs": 1.3240192303290734, + "p50Ms": 1.3049999475479126, + "p95Ms": 1.4500000476837158, + "maxMs": 2.534999966621399 + } + }, + "checksum": 10925530 + }, + { + "name": "streaming-markdown DOM", + "fixture": "unfinished-backtick-16kib", + "bytes": 16384, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 511, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 2, + "finalCodeMatches": true, + "finalTextDiffers": false + }, + "replay": { + "samples": 5, + "meanMs": 50.31999998092651, + "p50Ms": 50.63499999046326, + "p95Ms": 52.35000002384186, + "maxMs": 52.35000002384186 + }, + "finish": { + "samples": 5, + "meanMs": 0.17100000381469727, + "p50Ms": 0.1650000810623169, + "p95Ms": 0.19999992847442627, + "maxMs": 0.19999992847442627 + }, + "updates": { + "update": { + "samples": 2560, + "meanMs": 0.005187500081956386, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 1.1150000095367432 + }, + "updateAndLayout": { + "samples": 2560, + "meanMs": 0.09778906241990626, + "p50Ms": 0.08000004291534424, + "p95Ms": 0.1549999713897705, + "maxMs": 3.640000104904175 + } + }, + "openFence": { + "update": { + "samples": 2555, + "meanMs": 0.0051565558943029955, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 1.1150000095367432 + }, + "updateAndLayout": { + "samples": 2555, + "meanMs": 0.09788258318331844, + "p50Ms": 0.08000004291534424, + "p95Ms": 0.1549999713897705, + "maxMs": 3.640000104904175 + } + }, + "lateOpenFence": { + "update": { + "samples": 260, + "meanMs": 0.011942307765667255, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 1.1150000095367432 + }, + "updateAndLayout": { + "samples": 260, + "meanMs": 0.18648076974428618, + "p50Ms": 0.14500010013580322, + "p95Ms": 0.19000005722045898, + "maxMs": 2.509999990463257 + } + }, + "checksum": 10924641 + }, + { + "name": "TanStack Markdown React", + "fixture": "unfinished-backtick-64kib", + "bytes": 65536, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 2047, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 0, + "finalCodeMatches": true, + "finalTextDiffers": false + }, + "replay": { + "samples": 5, + "meanMs": 677.7850000143051, + "p50Ms": 678.7400000095367, + "p95Ms": 680.4900000095367, + "maxMs": 680.4900000095367 + }, + "finish": { + "samples": 5, + "meanMs": 0, + "p50Ms": 0, + "p95Ms": 0, + "maxMs": 0 + }, + "updates": { + "update": { + "samples": 10240, + "meanMs": 0.056908202916383745, + "p50Ms": 0.04500007629394531, + "p95Ms": 0.09000003337860107, + "maxMs": 1.409999966621399 + }, + "updateAndLayout": { + "samples": 10240, + "meanMs": 0.33075488277245313, + "p50Ms": 0.3049999475479126, + "p95Ms": 0.5699999332427979, + "maxMs": 3.8550000190734863 + } + }, + "openFence": { + "update": { + "samples": 10235, + "meanMs": 0.05691499733377446, + "p50Ms": 0.04500007629394531, + "p95Ms": 0.09000003337860107, + "maxMs": 1.409999966621399 + }, + "updateAndLayout": { + "samples": 10235, + "meanMs": 0.33087445036865293, + "p50Ms": 0.3049999475479126, + "p95Ms": 0.5699999332427979, + "maxMs": 3.8550000190734863 + } + }, + "lateOpenFence": { + "update": { + "samples": 1025, + "meanMs": 0.10408292619193472, + "p50Ms": 0.0849999189376831, + "p95Ms": 0.11500000953674316, + "maxMs": 1.2799999713897705 + }, + "updateAndLayout": { + "samples": 1025, + "meanMs": 0.6159707312467622, + "p50Ms": 0.5499999523162842, + "p95Ms": 0.6950000524520874, + "maxMs": 3.375 + } + }, + "checksum": 167105001 + }, + { + "name": "Streamdown React", + "fixture": "unfinished-backtick-64kib", + "bytes": 65536, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 2047, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 1, + "finalCodeMatches": true, + "finalTextDiffers": true + }, + "replay": { + "samples": 5, + "meanMs": 5932.687999987602, + "p50Ms": 5922.759999990463, + "p95Ms": 5979.625, + "maxMs": 5979.625 + }, + "finish": { + "samples": 5, + "meanMs": 3.165000009536743, + "p50Ms": 3.0449999570846558, + "p95Ms": 3.9149999618530273, + "maxMs": 3.9149999618530273 + }, + "updates": { + "update": { + "samples": 10240, + "meanMs": 1.6887353521306068, + "p50Ms": 1.6299999952316284, + "p95Ms": 3.3799999952316284, + "maxMs": 6.870000004768372 + }, + "updateAndLayout": { + "samples": 10240, + "meanMs": 2.8947646484710274, + "p50Ms": 2.8499999046325684, + "p95Ms": 5.684999942779541, + "maxMs": 9.205000042915344 + } + }, + "openFence": { + "update": { + "samples": 10235, + "meanMs": 1.6894215931587238, + "p50Ms": 1.6299999952316284, + "p95Ms": 3.3799999952316284, + "maxMs": 6.870000004768372 + }, + "updateAndLayout": { + "samples": 10235, + "meanMs": 2.8960068393231135, + "p50Ms": 2.850000023841858, + "p95Ms": 5.684999942779541, + "maxMs": 9.205000042915344 + } + }, + "lateOpenFence": { + "update": { + "samples": 1025, + "meanMs": 3.4095658515139324, + "p50Ms": 3.305000066757202, + "p95Ms": 4.049999952316284, + "maxMs": 6.870000004768372 + }, + "updateAndLayout": { + "samples": 1025, + "meanMs": 5.706985365588491, + "p50Ms": 5.580000042915344, + "p95Ms": 6.784999966621399, + "maxMs": 9.205000042915344 + } + }, + "checksum": 167105008 + }, + { + "name": "streaming-markdown DOM", + "fixture": "unfinished-backtick-64kib", + "bytes": 65536, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 2047, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 2, + "finalCodeMatches": true, + "finalTextDiffers": false + }, + "replay": { + "samples": 5, + "meanMs": 788.8239999771118, + "p50Ms": 788.9249999523163, + "p95Ms": 795.0750000476837, + "maxMs": 795.0750000476837 + }, + "finish": { + "samples": 5, + "meanMs": 0.7149999856948852, + "p50Ms": 0.7149999141693115, + "p95Ms": 0.7999999523162842, + "maxMs": 0.7999999523162842 + }, + "updates": { + "update": { + "samples": 10240, + "meanMs": 0.013867675850633532, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 1.0399999618530273 + }, + "updateAndLayout": { + "samples": 10240, + "meanMs": 0.38456298826495183, + "p50Ms": 0.3250000476837158, + "p95Ms": 0.6499999761581421, + "maxMs": 4.790000081062317 + } + }, + "openFence": { + "update": { + "samples": 10235, + "meanMs": 0.013867122694871807, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 1.0399999618530273 + }, + "updateAndLayout": { + "samples": 10235, + "meanMs": 0.3847288715123082, + "p50Ms": 0.3250000476837158, + "p95Ms": 0.6499999761581421, + "maxMs": 4.790000081062317 + } + }, + "lateOpenFence": { + "update": { + "samples": 1025, + "meanMs": 0.02483414591812506, + "p50Ms": 0, + "p95Ms": 0.009999990463256836, + "maxMs": 1.0199999809265137 + }, + "updateAndLayout": { + "samples": 1025, + "meanMs": 0.7283317070472531, + "p50Ms": 0.6000000238418579, + "p95Ms": 1.4950000047683716, + "maxMs": 4.350000023841858 + } + }, + "checksum": 167103237 + }, + { + "name": "TanStack Markdown React", + "fixture": "unfinished-tilde-64kib", + "bytes": 65536, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 2047, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 0, + "finalCodeMatches": true, + "finalTextDiffers": false + }, + "replay": { + "samples": 5, + "meanMs": 677.8530000209809, + "p50Ms": 678.5, + "p95Ms": 680.9800000190735, + "maxMs": 680.9800000190735 + }, + "finish": { + "samples": 5, + "meanMs": 0.0009999990463256836, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 0.004999995231628418 + }, + "updates": { + "update": { + "samples": 10240, + "meanMs": 0.05543408194789663, + "p50Ms": 0.04500007629394531, + "p95Ms": 0.09000003337860107, + "maxMs": 2.225000023841858 + }, + "updateAndLayout": { + "samples": 10240, + "meanMs": 0.33078222658950834, + "p50Ms": 0.30500006675720215, + "p95Ms": 0.5750000476837158, + "maxMs": 3.4850000143051147 + } + }, + "openFence": { + "update": { + "samples": 10235, + "meanMs": 0.055436248073475386, + "p50Ms": 0.04500007629394531, + "p95Ms": 0.09000003337860107, + "maxMs": 2.225000023841858 + }, + "updateAndLayout": { + "samples": 10235, + "meanMs": 0.330896433831193, + "p50Ms": 0.30500006675720215, + "p95Ms": 0.5750000476837158, + "maxMs": 3.4850000143051147 + } + }, + "lateOpenFence": { + "update": { + "samples": 1025, + "meanMs": 0.10096097376288438, + "p50Ms": 0.0849999189376831, + "p95Ms": 0.11000001430511475, + "maxMs": 1.190000057220459 + }, + "updateAndLayout": { + "samples": 1025, + "meanMs": 0.6201219512195122, + "p50Ms": 0.5549999475479126, + "p95Ms": 0.7400000095367432, + "maxMs": 3.4850000143051147 + } + }, + "checksum": 167105001 + }, + { + "name": "Streamdown React", + "fixture": "unfinished-tilde-64kib", + "bytes": 65536, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": false, + "openUpdates": 2047, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 2047, + "maxBufferedCharacters": 65503, + "finalCodeMatches": false, + "firstMismatch": { + "end": 64, + "expectedTail": "const route0: Route = { path: '", + "actualTail": "const route0: Route = { path: '~~" + }, + "finalTextDiffers": true, + "finalMismatch": { + "expectedTail": "onst route1091: Route = { path: '/api/1091', enabled: true }\nconst route1092: Ro", + "actualTail": "t route1091: Route = { path: '/api/1091', enabled: true }\nconst route1092: Ro~~\n" + } + }, + "replay": null, + "finish": null, + "updates": { + "update": null, + "updateAndLayout": null + }, + "openFence": { + "update": null, + "updateAndLayout": null + }, + "lateOpenFence": { + "update": null, + "updateAndLayout": null + }, + "checksum": 0 + }, + { + "name": "streaming-markdown DOM", + "fixture": "unfinished-tilde-64kib", + "bytes": 65536, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": false, + "openUpdates": 2047, + "missingFenceUpdates": 2047, + "mismatchedCodeUpdates": 2047, + "maxBufferedCharacters": 65503, + "finalCodeMatches": false, + "firstMismatch": { + "end": 64, + "expectedTail": "const route0: Route = { path: '", + "actualTail": "" + }, + "finalTextDiffers": true, + "finalMismatch": { + "expectedTail": "onst route1091: Route = { path: '/api/1091', enabled: true }\nconst route1092: Ro", + "actualTail": "" + } + }, + "replay": null, + "finish": null, + "updates": { + "update": null, + "updateAndLayout": null + }, + "openFence": { + "update": null, + "updateAndLayout": null + }, + "lateOpenFence": { + "update": null, + "updateAndLayout": null + }, + "checksum": 0 + }, + { + "name": "TanStack Markdown React", + "fixture": "closing-backtick-64kib", + "bytes": 65566, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 2047, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 0, + "finalCodeMatches": true, + "finalTextDiffers": false + }, + "replay": { + "samples": 5, + "meanMs": 678.2109999895096, + "p50Ms": 677.8999999761581, + "p95Ms": 681.4700000286102, + "maxMs": 681.4700000286102 + }, + "finish": { + "samples": 5, + "meanMs": 0, + "p50Ms": 0, + "p95Ms": 0, + "maxMs": 0 + }, + "updates": { + "update": { + "samples": 10245, + "meanMs": 0.04983748220245451, + "p50Ms": 0.04999995231628418, + "p95Ms": 0.09000003337860107, + "maxMs": 1.065000057220459 + }, + "updateAndLayout": { + "samples": 10245, + "meanMs": 0.3243035626795421, + "p50Ms": 0.3049999475479126, + "p95Ms": 0.5700000524520874, + "maxMs": 3.3899999856948853 + } + }, + "openFence": { + "update": { + "samples": 10235, + "meanMs": 0.04981240894250539, + "p50Ms": 0.04999995231628418, + "p95Ms": 0.09000003337860107, + "maxMs": 1.065000057220459 + }, + "updateAndLayout": { + "samples": 10235, + "meanMs": 0.324515388369502, + "p50Ms": 0.30500006675720215, + "p95Ms": 0.5700000524520874, + "maxMs": 3.3899999856948853 + } + }, + "lateOpenFence": { + "update": { + "samples": 1025, + "meanMs": 0.09007317054562453, + "p50Ms": 0.0849999189376831, + "p95Ms": 0.11000001430511475, + "maxMs": 0.8899999856948853 + }, + "updateAndLayout": { + "samples": 1025, + "meanMs": 0.605429268232206, + "p50Ms": 0.5499999523162842, + "p95Ms": 0.6549999713897705, + "maxMs": 3.1649999618530273 + } + }, + "checksum": 167266876 + }, + { + "name": "Streamdown React", + "fixture": "closing-backtick-64kib", + "bytes": 65566, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 2047, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 1, + "finalCodeMatches": true, + "finalTextDiffers": true + }, + "replay": { + "samples": 5, + "meanMs": 6081.446000003814, + "p50Ms": 6058.159999966621, + "p95Ms": 6236.325000047684, + "maxMs": 6236.325000047684 + }, + "finish": { + "samples": 5, + "meanMs": 0.020999979972839356, + "p50Ms": 0.019999980926513672, + "p95Ms": 0.029999971389770508, + "maxMs": 0.029999971389770508 + }, + "updates": { + "update": { + "samples": 10245, + "meanMs": 1.7450082967315086, + "p50Ms": 1.6649999618530273, + "p95Ms": 3.4499999284744263, + "maxMs": 6.400000095367432 + }, + "updateAndLayout": { + "samples": 10245, + "meanMs": 2.963551488371812, + "p50Ms": 2.875, + "p95Ms": 5.754999995231628, + "maxMs": 8.805000066757202 + } + }, + "openFence": { + "update": { + "samples": 10235, + "meanMs": 1.7447493893260424, + "p50Ms": 1.6649999618530273, + "p95Ms": 3.444999933242798, + "maxMs": 6.400000095367432 + }, + "updateAndLayout": { + "samples": 10235, + "meanMs": 2.9644298972417276, + "p50Ms": 2.875, + "p95Ms": 5.754999995231628, + "maxMs": 8.805000066757202 + } + }, + "lateOpenFence": { + "update": { + "samples": 1025, + "meanMs": 3.4744829260430685, + "p50Ms": 3.375, + "p95Ms": 4.315000057220459, + "maxMs": 6.400000095367432 + }, + "updateAndLayout": { + "samples": 1025, + "meanMs": 5.782839024125076, + "p50Ms": 5.65500009059906, + "p95Ms": 7.059999942779541, + "maxMs": 8.805000066757202 + } + }, + "checksum": 167266883 + }, + { + "name": "streaming-markdown DOM", + "fixture": "closing-backtick-64kib", + "bytes": 65566, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 2047, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 2, + "finalCodeMatches": true, + "finalTextDiffers": false + }, + "replay": { + "samples": 5, + "meanMs": 842.5479999780655, + "p50Ms": 848.7749999761581, + "p95Ms": 862.9599999189377, + "maxMs": 862.9599999189377 + }, + "finish": { + "samples": 5, + "meanMs": 0.0019999980926513673, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 0.004999995231628418 + }, + "updates": { + "update": { + "samples": 10245, + "meanMs": 0.026625182746153685, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 2.90500009059906 + }, + "updateAndLayout": { + "samples": 10245, + "meanMs": 0.41093557831599925, + "p50Ms": 0.33000004291534424, + "p95Ms": 0.6549999713897705, + "maxMs": 6.445000052452087 + } + }, + "openFence": { + "update": { + "samples": 10235, + "meanMs": 0.02663800656999328, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 2.90500009059906 + }, + "updateAndLayout": { + "samples": 10235, + "meanMs": 0.41095945285364777, + "p50Ms": 0.33000004291534424, + "p95Ms": 0.6500000953674316, + "maxMs": 6.445000052452087 + } + }, + "lateOpenFence": { + "update": { + "samples": 1025, + "meanMs": 0.048058535529346004, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 2.8550000190734863 + }, + "updateAndLayout": { + "samples": 1025, + "meanMs": 0.7703024389685654, + "p50Ms": 0.6050000190734863, + "p95Ms": 1.0399999618530273, + "maxMs": 6.445000052452087 + } + }, + "checksum": 167265112 + }, + { + "name": "TanStack Markdown React", + "fixture": "settled-prose-16kib-then-open-fence-16kib", + "bytes": 32770, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 512, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 0, + "finalCodeMatches": true, + "finalTextDiffers": false + }, + "replay": { + "samples": 5, + "meanMs": 327.65800004005433, + "p50Ms": 325.99000000953674, + "p95Ms": 334.3550000190735, + "maxMs": 334.3550000190735 + }, + "finish": { + "samples": 5, + "meanMs": 0, + "p50Ms": 0, + "p95Ms": 0, + "maxMs": 0 + }, + "updates": { + "update": { + "samples": 5125, + "meanMs": 0.24721658415910674, + "p50Ms": 0.2900000810623169, + "p95Ms": 0.3450000286102295, + "maxMs": 2.125 + }, + "updateAndLayout": { + "samples": 5125, + "meanMs": 0.3195121950986909, + "p50Ms": 0.3549998998641968, + "p95Ms": 0.4950000047683716, + "maxMs": 2.75 + } + }, + "openFence": { + "update": { + "samples": 2560, + "meanMs": 0.32893945183604956, + "p50Ms": 0.31499993801116943, + "p95Ms": 0.35500001907348633, + "maxMs": 2.125 + }, + "updateAndLayout": { + "samples": 2560, + "meanMs": 0.44414648436941206, + "p50Ms": 0.4249999523162842, + "p95Ms": 0.5150001049041748, + "maxMs": 2.75 + } + }, + "lateOpenFence": { + "update": { + "samples": 520, + "meanMs": 0.3400384604930878, + "p50Ms": 0.3200000524520874, + "p95Ms": 0.3700000047683716, + "maxMs": 2.125 + }, + "updateAndLayout": { + "samples": 520, + "meanMs": 0.513423076730508, + "p50Ms": 0.48500001430511475, + "p95Ms": 0.5600000619888306, + "maxMs": 2.75 + } + }, + "checksum": 89206215 + }, + { + "name": "Streamdown React", + "fixture": "settled-prose-16kib-then-open-fence-16kib", + "bytes": 32770, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 512, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 1, + "finalCodeMatches": true, + "finalTextDiffers": true + }, + "replay": { + "samples": 5, + "meanMs": 1196.574999976158, + "p50Ms": 1197.9749999046326, + "p95Ms": 1212.2549999952316, + "maxMs": 1212.2549999952316 + }, + "finish": { + "samples": 5, + "meanMs": 1.4390000104904175, + "p50Ms": 1.3650000095367432, + "p95Ms": 1.7450000047683716, + "maxMs": 1.7450000047683716 + }, + "updates": { + "update": { + "samples": 5125, + "meanMs": 0.9679200010299682, + "p50Ms": 0.9249999523162842, + "p95Ms": 1.8849999904632568, + "maxMs": 4.504999995231628 + }, + "updateAndLayout": { + "samples": 5125, + "meanMs": 1.1657765852532735, + "p50Ms": 1, + "p95Ms": 2.5, + "maxMs": 5.195000052452087 + } + }, + "openFence": { + "update": { + "samples": 2560, + "meanMs": 1.4375019538681955, + "p50Ms": 1.415000081062317, + "p95Ms": 2.0149999856948853, + "maxMs": 4.504999995231628 + }, + "updateAndLayout": { + "samples": 2560, + "meanMs": 1.7968203122727573, + "p50Ms": 1.7699999809265137, + "p95Ms": 2.6549999713897705, + "maxMs": 5.195000052452087 + } + }, + "lateOpenFence": { + "update": { + "samples": 520, + "meanMs": 1.9037115360681827, + "p50Ms": 1.8600000143051147, + "p95Ms": 2.174999952316284, + "maxMs": 4.504999995231628 + }, + "updateAndLayout": { + "samples": 520, + "meanMs": 2.5160384613734026, + "p50Ms": 2.465000033378601, + "p95Ms": 2.865000009536743, + "maxMs": 5.195000052452087 + } + }, + "checksum": 89206222 + }, + { + "name": "streaming-markdown DOM", + "fixture": "settled-prose-16kib-then-open-fence-16kib", + "bytes": 32770, + "chunkSize": 32, + "iterations": 5, + "warmupReplays": 2, + "validation": { + "passed": true, + "openUpdates": 512, + "missingFenceUpdates": 0, + "mismatchedCodeUpdates": 0, + "maxBufferedCharacters": 2, + "finalCodeMatches": true, + "finalTextDiffers": false + }, + "replay": { + "samples": 5, + "meanMs": 80.59500000476837, + "p50Ms": 81.6450001001358, + "p95Ms": 82.55000007152557, + "maxMs": 82.55000007152557 + }, + "finish": { + "samples": 5, + "meanMs": 0.1989999771118164, + "p50Ms": 0.1950000524520874, + "p95Ms": 0.2200000286102295, + "maxMs": 0.2200000286102295 + }, + "updates": { + "update": { + "samples": 5125, + "meanMs": 0.003562926618064322, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 1.1399999856948853 + }, + "updateAndLayout": { + "samples": 5125, + "meanMs": 0.07830536579504245, + "p50Ms": 0.04499995708465576, + "p95Ms": 0.1799999475479126, + "maxMs": 3.4250000715255737 + } + }, + "openFence": { + "update": { + "samples": 2560, + "meanMs": 0.0048828123603016135, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 1.1399999856948853 + }, + "updateAndLayout": { + "samples": 2560, + "meanMs": 0.13017187495715915, + "p50Ms": 0.11500000953674316, + "p95Ms": 0.19000005722045898, + "maxMs": 3.4250000715255737 + } + }, + "lateOpenFence": { + "update": { + "samples": 520, + "meanMs": 0.0073557681762255155, + "p50Ms": 0, + "p95Ms": 0.004999995231628418, + "maxMs": 0.8499999046325684 + }, + "updateAndLayout": { + "samples": 520, + "meanMs": 0.20069230726132026, + "p50Ms": 0.17499995231628418, + "p95Ms": 0.2200000286102295, + "maxMs": 2.5799999237060547 + } + }, + "checksum": 89198662 + } + ] +} diff --git a/reports/streaming-browser.md b/reports/streaming-browser.md new file mode 100644 index 0000000..2115b7f --- /dev/null +++ b/reports/streaming-browser.md @@ -0,0 +1,45 @@ +# Streaming library comparison + +Generated: 2026-09-12T17:00:04.103Z + +Chrome 152.0.7977.83, Apple M5 Pro, darwin arm64. Production React 19.2.7, Streamdown 2.6.0, streaming-markdown 0.2.15. Cross-origin isolation: true. + +Each response arrives in 32-character chunks. TanStack Markdown and Streamdown keep a React root mounted across updates and commit each update with flushSync. Streamdown uses streaming mode with incomplete-Markdown repair and block memoization enabled. streaming-markdown keeps one incremental parser and receives only new chunks through its default DOM renderer. Each replay starts with a fresh root or parser. + +Code is plain text for every library. Streamdown uses custom pre/code components, with animations, controls, and line numbers disabled. Its default parsing, GFM, and sanitization remain enabled. No syntax highlighter is installed in this browser harness. This isolates streaming rendering from highlighter choices and is not a comparison of default product interfaces or syntax coverage. + +Update latency includes parsing, rendering, and synchronous DOM commit. Update + layout also forces style and layout with offsetHeight after every chunk. Paint, network delays, animation frames, and bundle loading are excluded. All libraries use the same 800px-wide container and basic typography. Browser timer resolution limits very small measurements. + +Five measured replays follow two full warmups. Percentiles use nearest rank over pooled updates. Late-open samples cover the last 10% of source before the closing fence or EOF. Replay totals include update sampling, forced layout, and stream finalization, but exclude root/parser creation, cleanup, precomputed prefix/chunk slicing, and validation. End-of-stream finalization is also recorded separately in JSON. + +Before timing, a separate replay checks the code block at every open-fence update, ignoring serializer-added trailing newlines. Up to three trailing characters may be buffered for delimiter recognition, and the observed maximum is reported. Final code must match after trimming trailing whitespace; exact-text differences are recorded separately in JSON. Cases that fail this content check receive no timing. This validates the code-fence workload, not full Markdown conformance. + +| Renderer | Fixture | Replay mean ms | Open update p95 ms | Open update + layout p95 ms | Late open update + layout p95 ms | Max update + layout ms | Buffered chars | +| :--- | :--- | ---: | ---: | ---: | ---: | ---: | ---: | +| TanStack Markdown React | unfinished-backtick-4kib | 4.529 | 0.015 | 0.055 | 0.065 | 0.325 | 0 | +| Streamdown React | unfinished-backtick-4kib | 30.308 | 0.255 | 0.395 | 0.540 | 1.285 | 1 | +| streaming-markdown DOM | unfinished-backtick-4kib | 3.711 | 0.005 | 0.060 | 0.070 | 0.390 | 2 | +| TanStack Markdown React | unfinished-backtick-16kib | 47.487 | 0.030 | 0.155 | 0.235 | 5.035 | 0 | +| Streamdown React | unfinished-backtick-16kib | 366.486 | 0.745 | 1.320 | 1.450 | 2.535 | 1 | +| streaming-markdown DOM | unfinished-backtick-16kib | 50.320 | 0.005 | 0.155 | 0.190 | 3.640 | 2 | +| TanStack Markdown React | unfinished-backtick-64kib | 677.785 | 0.090 | 0.570 | 0.695 | 3.855 | 0 | +| Streamdown React | unfinished-backtick-64kib | 5932.688 | 3.380 | 5.685 | 6.785 | 9.205 | 1 | +| streaming-markdown DOM | unfinished-backtick-64kib | 788.824 | 0.005 | 0.650 | 1.495 | 4.790 | 2 | +| TanStack Markdown React | unfinished-tilde-64kib | 677.853 | 0.090 | 0.575 | 0.740 | 3.485 | 0 | +| Streamdown React | unfinished-tilde-64kib | n/a | n/a | n/a | n/a | n/a | failed content check | +| streaming-markdown DOM | unfinished-tilde-64kib | n/a | n/a | n/a | n/a | n/a | failed content check | +| TanStack Markdown React | closing-backtick-64kib | 678.211 | 0.090 | 0.570 | 0.655 | 3.390 | 0 | +| Streamdown React | closing-backtick-64kib | 6081.446 | 3.445 | 5.755 | 7.060 | 8.805 | 1 | +| streaming-markdown DOM | closing-backtick-64kib | 842.548 | 0.005 | 0.650 | 1.040 | 6.445 | 2 | +| TanStack Markdown React | settled-prose-16kib-then-open-fence-16kib | 327.658 | 0.355 | 0.515 | 0.560 | 2.750 | 0 | +| Streamdown React | settled-prose-16kib-then-open-fence-16kib | 1196.575 | 2.015 | 2.655 | 2.865 | 5.195 | 1 | +| streaming-markdown DOM | settled-prose-16kib-then-open-fence-16kib | 80.595 | 0.005 | 0.190 | 0.220 | 3.425 | 2 | + +Streamdown React, unfinished-tilde-64kib: 0 open updates missing a code fence; 2047 updates with incorrect or buffered code beyond the allowed three characters; final code match: false. +streaming-markdown DOM, unfinished-tilde-64kib: 2047 open updates missing a code fence; 2047 updates with incorrect or buffered code beyond the allowed three characters; final code match: false. + +The settled-prose case adds 16 KiB of completed prose before a growing 16 KiB fence, so retained blocks can benefit from memoization. The other cases contain one heading and one growing fence. Results apply to these fixtures and configurations. + +Reproduce with `pnpm exec playwright install chromium` followed by `pnpm run bench:streaming`. To use an installed Chrome instead, run `BENCH_BROWSER_CHANNEL=chrome pnpm run bench:streaming`. `--smoke` runs the smallest case and validates the harness without replacing reports. + +Library APIs: [Streamdown](https://streamdown.ai/docs/components), [streaming-markdown](https://github.com/thetarnav/streaming-markdown). diff --git a/reports/streaming-optimization.md b/reports/streaming-optimization.md new file mode 100644 index 0000000..10eacb4 --- /dev/null +++ b/reports/streaming-optimization.md @@ -0,0 +1,30 @@ +# Streaming optimization results + +Plain code, 32-character updates, persistent renderer state, production Chrome on Apple M5 Pro. Replay times include synchronous DOM updates and forced layout, but exclude paint and syntax highlighting. Before and after are separate runs on the same machine. See the [browser report](./streaming-browser.md) for configurations, validation, and per-update latency. + +| Fixture | TanStack before, ms | TanStack after, ms | streaming-markdown, ms | Streamdown, ms | +| :--- | ---: | ---: | ---: | ---: | +| unfinished-backtick-4kib | 11.59 | 4.53 | 3.71 | 30.31 | +| unfinished-backtick-16kib | 165.01 | 47.49 | 50.32 | 366.49 | +| unfinished-backtick-64kib | 2615.27 | 677.79 | 788.82 | 5932.69 | +| unfinished-tilde-64kib | 2601.32 | 677.85 | content check failed | content check failed | +| closing-backtick-64kib | 2717.50 | 678.21 | 842.55 | 6081.45 | +| settled-prose-16kib-then-open-fence-16kib | 443.06 | 327.66 | 80.60 | 1196.57 | + +The 64 KiB unfinished and closing backtick cases now have the lowest total replay time among the tested renderers. The 16 KiB result is close to streaming-markdown. TanStack still trails streaming-markdown on the 4 KiB and mixed-prose cases, so this does not establish a win across the full suite. Both competitors fail the tilde-fence content check. + +React streaming now retains completed groups of plain code lines in stable text nodes. Static rendering and custom code components retain string children. Parser state uses local variables, and block detection skips irrelevant checks on ordinary text and fenced code. Parsing still processes the full accumulated source on every update. + +| Entry | Minified before → after | Gzip before → after | Brotli before → after | +| :--- | ---: | ---: | ---: | +| parser only | 13157 → 11949 | 4975 → 4894 | 4593 → 4539 | +| html renderer no highlighter | 18337 → 17139 | 6807 → 6729 | 6240 → 6158 | +| react adapter | 18271 → 17147 | 6722 → 6676 | 6184 → 6170 | +| octane adapter | 18283 → 17084 | 6727 → 6644 | 6194 → 6143 | +| react adapter with streaming extension | 18962 → 17839 | 6907 → 6860 | 6356 → 6302 | + +Every measured public entry remains at or below its original minified, gzip, and Brotli size. Size-test ceilings were lowered to the new measurements. + +Validation: 255 tests, 24 browser checks covering hydration, updates, Unicode, overrides, and highlighting transitions, typechecking, a production build, and docs verification passed. The before/after CommonMark comparison retained all 403 previously passing examples. + +Reproduce the browser regression checks with `BENCH_BROWSER_CHANNEL=chrome pnpm run test:streaming-browser`, and the comparison with `BENCH_BROWSER_CHANNEL=chrome pnpm run bench:streaming`. Browser fixtures and competitor configurations are unchanged by the optimization. diff --git a/scripts/bench-streaming-browser.ts b/scripts/bench-streaming-browser.ts new file mode 100644 index 0000000..63a0224 --- /dev/null +++ b/scripts/bench-streaming-browser.ts @@ -0,0 +1,116 @@ +import { createServer } from 'node:http' +import { readFile, writeFile } from 'node:fs/promises' +import { cpus } from 'node:os' +import { build } from 'esbuild' +import { chromium } from 'playwright' +import { createFenceFixture, summarizeLatency } from './streaming-bench.js' +import type { RendererName, runBrowserCase } from './streaming-browser.js' + +const smoke = process.argv.includes('--smoke') +const rendererFilter = process.argv.find(arg => arg.startsWith('--renderer='))?.slice(11) +const fixtureFilter = process.argv.find(arg => arg.startsWith('--fixture='))?.slice(10) +const filtered = rendererFilter !== undefined || fixtureFilter !== undefined +const iterations = smoke ? 1 : 5 +const names: RendererName[] = ['TanStack Markdown React', 'Streamdown React', 'streaming-markdown DOM'] +const fixtures = [ + ...[4, 16, 64].map(kib => createFenceFixture(kib)), + createFenceFixture(64, '~~~'), + createFenceFixture(64, '```', true), +] +const openFence = createFenceFixture(16) +const prose = Array.from({ length: 160 }, (_, index) => + `## Finding ${index}\n\nThe completed response includes **important details** and [a reference](https://example.com/${index}).\n\n`, +).join('').slice(0, 16 * 1024) + '\n\n' +fixtures.push({ + ...openFence, + name: 'settled-prose-16kib-then-open-fence-16kib', + source: prose + openFence.source, + fenceOpenAt: prose.length + openFence.fenceOpenAt!, +}) + +const bundle = await build({ + entryPoints: ['scripts/streaming-browser.tsx'], bundle: true, write: false, + minify: true, platform: 'browser', format: 'iife', target: 'es2022', jsx: 'automatic', + define: { 'process.env.NODE_ENV': '"production"' }, +}) +const html = '
' +const server = createServer((request, response) => { + response.setHeader('Cross-Origin-Opener-Policy', 'same-origin') + response.setHeader('Cross-Origin-Embedder-Policy', 'require-corp') + response.setHeader('Content-Type', request.url === '/bench.js' ? 'text/javascript' : 'text/html') + response.end(request.url === '/bench.js' ? bundle.outputFiles[0]!.contents : html) +}) +await new Promise{children},
+ code: ({ children, className }: { children?: ReactNode; className?: string | undefined }) => {children},
+}
+
+function mount(name: RendererName, container: HTMLElement) {
+ if (name === 'streaming-markdown DOM') {
+ const parser = smd.parser(smd.default_renderer(container))
+ return {
+ update: (_prefix: string, chunk: string) => smd.parser_write(parser, chunk),
+ finish: () => smd.parser_end(parser),
+ dispose: () => container.replaceChildren(),
+ }
+ }
+ const root = createRoot(container)
+ let lastPrefix = ''
+ const render = (prefix: string, streaming: boolean) => flushSync(() => root.render(
+ name === 'TanStack Markdown React'
+ ? {children}
+ } } })
+ assert(customChildren === code, 'Custom code components must still receive string children')
+ render(source, { highlighter: value => value.replaceAll('&', '&').replaceAll('<', '<') })
+ render(source)
+ flushSync(() => root.unmount())
+
+ const errors: unknown[] = []
+ container.innerHTML = renderToString(createElement(Markdown, { children: source, extensions }))
+ const serverText = container.querySelector('code')!.firstChild
+ const hydrated = hydrateRoot(container,