diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000..f8a6e71 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@4.0.0/schema.json", + "baseBranch": "main", + "access": "public", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "ignore": [], + "updateInternalDependencies": "patch" +} diff --git a/.changeset/smaller-compatible-markdown.md b/.changeset/smaller-compatible-markdown.md new file mode 100644 index 0000000..2d0bfba --- /dev/null +++ b/.changeset/smaller-compatible-markdown.md @@ -0,0 +1,13 @@ +--- +'@tanstack/markdown': patch +--- + +Improve Markdown compatibility and parsing speed while reducing parser, renderer, and documentation extension bundles. + +- Preserve code span whitespace and exact backtick delimiters, handle escaped punctuation and nested emphasis, and retain formatted image alt text. +- Correct nested list tightness, ordered lists starting at zero, indented code fences, escaped table pipes, and link destinations and reference labels. Prevent nested link anchors. +- Avoid generated heading and footnote definition ID collisions, preserve headings across footnote content, and render backreferences for footnotes ending in non-paragraph blocks. +- Keep callouts within quoted content, preserve prototype-shaped comment attributes and package-manager names, and guard oversized code-line ranges. +- Reduce repeated scanning and allocations in inline parsing, React and Octane list rendering, and documentation extensions. + +Add regression coverage, ratchet 403 CommonMark examples, and enforce minified, gzip, and Brotli budgets for every public entry point. Existing public APIs and runtime dependencies are unchanged. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c21e40d..edfba6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: pull_request: push: branches: [main] + workflow_dispatch: permissions: contents: read diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0676899 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,102 @@ +name: Release + +on: + push: + branches: [main] + workflow_dispatch: + +concurrency: + group: release-main + cancel-in-progress: false + +permissions: {} + +jobs: + verify: + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + mode: ${{ steps.changesets.outputs.mode }} + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 + with: + version: 11.4.0 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: 24 + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm run verify + - name: Select release operation + id: changesets + uses: changesets/action/select-mode@ae32849d5ba541f9ae29e40e22a623bc13562f51 # v2.1.2 + + version: + needs: verify + if: needs.verify.outputs.mode == 'version' + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + actions: write + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + fetch-depth: 0 + persist-credentials: false + - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 + with: + version: 11.4.0 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: 24 + cache: pnpm + - run: pnpm install --frozen-lockfile + - name: Create or update release pull request + id: changesets + uses: changesets/action/version@ae32849d5ba541f9ae29e40e22a623bc13562f51 # v2.1.2 + with: + script: pnpm run release:version + commit-message: Release packages + pr-title: Release packages + pr-base-branch: main + - name: Run CI on the release pull request + env: + GH_TOKEN: ${{ github.token }} + RELEASE_PR: ${{ steps.changesets.outputs.pr-number }} + # Dispatch also works for branches created with GITHUB_TOKEN. + run: | + RELEASE_BRANCH=$(gh pr view "$RELEASE_PR" --json headRefName --jq .headRefName) + gh workflow run ci.yml --ref "$RELEASE_BRANCH" + + publish: + needs: verify + if: needs.verify.outputs.mode == 'publish' + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + fetch-depth: 0 + persist-credentials: false + - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 + with: + version: 11.4.0 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: 24 + cache: pnpm + - run: pnpm install --frozen-lockfile + - name: Publish to npm and create GitHub releases + uses: changesets/action/publish@ae32849d5ba541f9ae29e40e22a623bc13562f51 # v2.1.2 + with: + script: pnpm run release:publish + create-github-releases: true + push-git-tags: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0de1472 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,20 @@ +# Contributing + +TanStack Markdown targets blogs, documentation, and accumulated AI responses. Parser fixes need regression tests and must preserve the bundle-size budgets. Compare performance against the previous revision before adding work to a parsing hot path. + +```sh +pnpm install +pnpm run verify +``` + +The [testing guide](docs/guides/testing.md) covers conformance, corpus audits, and revision comparisons. + +## Releases + +Include a changeset for package changes with `pnpm changeset`. Use a patch for compatible fixes and performance improvements, a minor for new public APIs, and a major for breaking changes. Check the planned release with `pnpm run changeset:status`. + +After a change reaches `main` and verification passes, the Release workflow opens or updates a version PR. It updates the package version, changelog, and bundled skill versions, then dispatches CI on the generated branch. Merge that PR after its checks pass to publish through GitHub Actions and npm trusted publishing. + +The npm trusted publisher for `@tanstack/markdown` must allow direct publishing from GitHub repository `TanStack/markdown`, workflow `release.yml`, with no environment name. No npm token is stored in the repository. GitHub Actions must be allowed to create pull requests. + +`release:version` and `release:publish` are automation commands. If publishing is interrupted, inspect the workflow logs and npm before retrying. The Release workflow can be dispatched on `main`, and Changesets skips versions already on npm. Never bump or publish locally to work around a failed workflow. diff --git a/README.md b/README.md index 978f577..2b7e701 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,16 @@ A tiny, fast, deterministic Markdown parser and renderer for blogs and documenta - 4.9 KB gzip parser - 6.7 KB gzip HTML renderer -- 6.7 KB gzip React adapter -- 6.7 KB gzip Octane adapter +- 6.6 KB gzip React adapter +- 6.6 KB gzip Octane adapter - zero runtime dependencies - serializable AST - safe defaults for raw HTML and executable URLs - optional docs extensions and external syntax highlighting - optional AI streaming profile +Bundle sizes include the parser and exclude framework runtimes and syntax highlighters. + ```bash pnpm add @tanstack/markdown ``` diff --git a/docs/comparison.md b/docs/comparison.md index 0525a44..0201828 100644 --- a/docs/comparison.md +++ b/docs/comparison.md @@ -42,9 +42,9 @@ These repository benchmarks bundle representative browser entry points from pinn | --- | ---: | ---: | | `@tanstack/markdown/parser` | 4.9 KB | 4.6 KB | | `@tanstack/markdown/html` | 6.7 KB | 6.2 KB | -| `@tanstack/markdown/react` | 6.7 KB | 6.1 KB | +| `@tanstack/markdown/react` | 6.6 KB | 6.1 KB | | React with streaming extension | 6.8 KB | 6.3 KB | -| `@tanstack/markdown/octane` | 6.7 KB | 6.1 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 | @@ -56,7 +56,7 @@ The comparison does not represent equivalent feature sets. It shows the cost of ## Compatibility accounting -TanStack Markdown currently matches 348 of 652 CommonMark 0.31.2 examples after serializer normalization. That 53.4% figure is accounting, not a conformance claim or a target to maximize. The project also preserves selected official GFM examples for tables, task lists, and strikethrough. +The generated [compatibility report](../reports/conformance.md) tracks matches against all 652 CommonMark 0.31.2 examples after serializer normalization. Every established match is protected individually by the regression suite. The count is accounting, not a conformance claim or a target to maximize. Selected official GFM examples separately cover tables, task lists, and strikethrough. Use [commonmark.js](https://github.com/commonmark/commonmark.js), micromark, or a unified pipeline when exact specification behavior is a requirement. Use TanStack Markdown when your corpus fits the [documented profile](core-concepts/syntax-profile) and the smaller, controlled renderer is the better product tradeoff. diff --git a/docs/core-concepts/security.md b/docs/core-concepts/security.md index 2e60293..a4c6fc6 100644 --- a/docs/core-concepts/security.md +++ b/docs/core-concepts/security.md @@ -43,6 +43,14 @@ The tested [TanStack Highlight adapter](../guides/syntax-highlighting#tanstack-h An extension `renderHtml` hook also returns trusted HTML. React and Octane component replacements can enforce application-specific link, image, and navigation policies, but those replacements are outside core renderer parity. +## Document ASTs + +Renderers trust document ASTs supplied directly by the application. URL screening happens during Markdown parsing, not when rendering an arbitrary link or image node. Do not accept untrusted JSON as a document AST without validating its structure and applying your URL and HTML policies. + +## Resource limits + +The core limits parser nesting and inline delimiter scans. These are not a limit on total input size, footnote count, or work performed by extensions. Bound untrusted document sizes in the application, and batch streaming updates instead of rerendering on every incoming character. + ## Untrusted content For user-generated Markdown: diff --git a/docs/guides/docs-preset.md b/docs/guides/docs-preset.md index 1c21459..9c4db0e 100644 --- a/docs/guides/docs-preset.md +++ b/docs/guides/docs-preset.md @@ -19,7 +19,7 @@ const html = renderHtml(document, { }) ``` -The preset is a separate 2.4 KB gzip entry and is not imported by the parser or renderers. +The preset is a separate 2.3 KB gzip entry and is not imported by the parser or renderers. ## Callouts @@ -30,6 +30,8 @@ The preset is a separate 2.4 KB gzip entry and is not imported by the parser or This produces a `CalloutNode` and renders `markdown-alert`, `markdown-alert-title`, and `markdown-alert-content` classes. +Keep blank lines inside a callout quoted with `>`. An unquoted blank line ends the callout. Custom titles are plain text; put Markdown links in the body. + ## Heading collection Heading collection adds table-of-contents data to `document.headings`: diff --git a/docs/guides/performance.md b/docs/guides/performance.md index 8e30ef9..da533cb 100644 --- a/docs/guides/performance.md +++ b/docs/guides/performance.md @@ -14,12 +14,12 @@ The generated browser bundle report records: | --- | ---: | ---: | | parser | 4.9 KB | 4.6 KB | | HTML renderer | 6.7 KB | 6.2 KB | -| React adapter | 6.7 KB | 6.1 KB | -| Octane adapter | 6.7 KB | 6.1 KB | +| React adapter | 6.6 KB | 6.1 KB | +| Octane adapter | 6.6 KB | 6.1 KB | | React adapter with streaming extension | 6.8 KB | 6.3 KB | | Streaming extension | 0.3 KB | 0.3 KB | -| docs preset | 2.4 KB | 2.2 KB | -| callouts extension | 0.4 KB | 0.3 KB | +| docs preset | 2.3 KB | 2.1 KB | +| callouts extension | 0.3 KB | 0.3 KB | Framework runtimes are externalized from their adapters, and the highlighter measurement uses only a callback stub. Exact generated bytes live in the [size report](../../reports/sizes.md). diff --git a/docs/guides/testing.md b/docs/guides/testing.md index d9a2ea8..1e56b27 100644 --- a/docs/guides/testing.md +++ b/docs/guides/testing.md @@ -60,10 +60,26 @@ The command compares output against CommonMark 0.31.2 examples, preserves every Selected official GFM examples separately cover tables, task lists, and strikethrough in the supported profile. +## Comparing a change + +Compare the working tree with a Git revision or a saved directory containing `src/`, using the same compiler and runtime: + +```bash +node --import tsx scripts/compare-revision.mjs main +``` + +The script measures minified, gzip, and Brotli sizes, lists gained and lost CommonMark matches, and alternates old and new implementations over nine timing samples. It includes React and Octane node creation and SSR, and retains the medians and every sample in `artifacts/audit-comparison.json`. Run it without other CPU-heavy work. Small timing differences can still be noise, and Node results do not establish browser performance. + +Size tests protect both narrow imports and the complete namespace of every public entry point. Their ceilings are the smaller audited working tree, not the earlier npm release. A smaller gzip bundle does not excuse growth in minified or Brotli bytes. + +After running both corpus audits, add `--corpus` to compare both revisions in core and docs-extension modes against the same source files and Marked version. Use `--no-bench` when checking only output and size. Review changed output even when a file already differs from Marked. The corpus classification is a triage tool, not proof that two outputs behave identically. + ## Resilience The suite includes fixed-seed generated malformed documents and adversarial cases such as unmatched delimiters, deep blockquotes, nested lists, and parser depth exhaustion. Performance thresholds are generous enough for CI variation but strict enough to catch accidental superlinear behavior. +Synchronous hang regressions, including oversized numeric code-line ranges, run in a subprocess with a deadline. An ordinary test timeout cannot interrupt a blocked JavaScript event loop. + The AI streaming profile renders every character prefix of a representative response and verifies deterministic output, React/HTML parity, safe handling of executable URLs and raw HTML, unfinished-fence behavior, trailing-placeholder suppression, and bounded progressive rendering. ## Documentation coverage diff --git a/package.json b/package.json index 3d34372..1887a40 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,10 @@ "skills:check-version": "node scripts/sync-skill-version.mjs --check", "skills:sync-version": "node scripts/sync-skill-version.mjs", "skills:stale": "intent stale", + "changeset": "changeset", + "changeset:status": "changeset status", + "release:version": "changeset version && pnpm run skills:sync-version", + "release:publish": "pnpm run verify && changeset publish", "conformance": "tsx scripts/conformance.ts", "bench": "tsx scripts/bench.ts", "size": "tsx scripts/measure-size.ts", @@ -108,6 +112,7 @@ } }, "devDependencies": { + "@changesets/cli": "3.0.2", "@tanstack/highlight": "0.0.6", "@tanstack/intent": "^0.3.6", "@types/node": "^24.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72df7de..b62a5ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: devDependencies: + '@changesets/cli': + specifier: 3.0.2 + version: 3.0.2 '@tanstack/highlight': specifier: 0.0.6 version: 0.0.6 @@ -77,6 +80,75 @@ importers: packages: + '@changesets/apply-release-plan@8.1.0': + resolution: {integrity: sha512-M93HOGyX3ssg6He3b5NotaHtQVu6uajHS6UIQ28xKt2RzskCy73ayX4I914qBKjTJmrE4YFlELQjfcqxbmGLJw==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/assemble-release-plan@7.0.0': + resolution: {integrity: sha512-oEW8BxdA604kGGtDSCiHr5w9Tv4UWe9I2k61IBNZzCOE1kbYaJj4v+lFQNgcEZFkUc2pV/+hASErGDvpJOZCTg==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/changelog-git@1.0.0': + resolution: {integrity: sha512-3Dst2Ime2Op5nd4XmWJLPIgp11ZFqJqSkVug9izK6TDcIV4YlhPS4ECbEVR+eGI0bk0r1ItogD4j2Oli87bJrA==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/cli@3.0.2': + resolution: {integrity: sha512-t/omGJj/I+Jv0kmJAkj5cstYEdUQiJnpip2F+2m3F4lQ3kAZ3o8Exep4/Fm1qq4rvw6ovlhJvZNrKdwLJ4lkuQ==} + engines: {node: ^22.11 || ^24 || >=26, npm: '>=10.9.0', pnpm: '>=10.0.0', yarn: '>=4.5.2'} + hasBin: true + + '@changesets/config@4.0.0': + resolution: {integrity: sha512-mw95/YrkOuhZZxfnVAA4bSXOFUi+KlhzOBTM8C4x777NhUU6HWIl9Z+K+nME+E4PVsv5NQVQwTfiHihAS1A/ow==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/errors@1.0.0': + resolution: {integrity: sha512-ElN/mEzn6zmETgjwf5MclCMa9ef59sAR0lfO8VSYIsiRvbC2FbLB/92EoYw10Sl0kGixxHFiJZUSv7dA+YpR8g==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/format@0.1.2': + resolution: {integrity: sha512-Caez5XtNXCFS/G5bwyav3wuXL0tMxVd2ZGbaumWbzN08tyzO21asCw7JZhNtVsAZDCvDRUzZN+Iit9SyRITSYA==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/get-dependents-graph@3.0.0': + resolution: {integrity: sha512-ji/t5wFA1zREKXRUePE6Qi+Qu2UgxCeSSGQrphezwvQZrp49B7sJ+8+wvM0tA7zPeSxYKCojDy3WWgrl+s+awg==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/git@4.0.1': + resolution: {integrity: sha512-6vWpIAC4LkpmlqaIu37ViT5enyd9+uBwAiGqCGhASvkSHBgx4PrtTGXWTMFYpDmZbjgyonyVgMciPrW4MqtJsA==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/parse@1.0.0': + resolution: {integrity: sha512-P0iaMb9p9CRYZiTgAllEIF9AUMQHIy1G72tKlcIqJp61icZSsKQNiOPdxAMZG8m/DvZwt/oz5xEbTpike//dWg==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/pre@3.0.0': + resolution: {integrity: sha512-Zm/6YliV/a2oeWTqHJf6KxLrQwgcK1i/BRDl2m0EKZvbnxV5fG9QRhwJJGshjsZTUTS6dkfURQ2K6aAvgNw/3Q==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/read@1.0.1': + resolution: {integrity: sha512-nzvSC6RcTiWf/dsuwZFXpLzGGsRHYCL68U3uHV7srsulU93aVWY7u2GEJiDZ+4GIIElfaW5EqtKC0UHroY+LTQ==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/should-skip-package@1.0.0': + resolution: {integrity: sha512-pwqoJmbONn1XgXmZXPEExgAaT+HdZLjALFTDgIm+PnS5KeO2nLtzA2/Q+4aMFY14kFMuXKG30MObhvDzWgzDgg==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/types@7.0.0': + resolution: {integrity: sha512-c5GoiQyt3pxiXjrWSNoP8/GRf4kG+VnKzovx1OQM8dYYALlSwgedmkPmJ+ZqGxqwg9D3Bkj85Uo4KLd5BN3A0w==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/write@1.0.1': + resolution: {integrity: sha512-q/ThtP9gcnEP6xlv7LrY26C2mHqdGBti/MmOVngt/M/oIGYkssmQGxPK9WzBNt2juVcH/vml2WQ+ra8LXYOTaA==} + engines: {node: ^22.11 || ^24 || >=26} + + '@clack/core@1.5.0': + resolution: {integrity: sha512-zNikCcd8BbcEvzzG1sbXFrRHFk5kHPrpwZwksPvf9qyQO1Teb7JaXaOAxXZei9nZLDW0gaZawiuTCji88bTBhw==} + engines: {node: '>= 20.12.0'} + + '@clack/prompts@1.8.0': + resolution: {integrity: sha512-PXzLZ8N34rxmuo4dJg3xtOXhcBse94qGjDqsteoEYrFrrZ5FSjIGwMAuOcv64ln8rHVBBD06XeVGr+/JX+plcA==} + engines: {node: '>= 20.12.0'} + '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} @@ -548,10 +620,26 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@manypkg/find-root@3.1.0': + resolution: {integrity: sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA==} + engines: {node: '>=20.0.0'} + + '@manypkg/get-packages@3.1.0': + resolution: {integrity: sha512-0TbBVyvPrP7xGYBI/cP8UP+yl/z+HtbTttAD7FMAJgn/kXOTwh5/60TsqP9ZYY710forNfyV0N8P/IE/ujGZJg==} + engines: {node: '>=20.0.0'} + + '@manypkg/tools@2.1.2': + resolution: {integrity: sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ==} + engines: {node: '>=20.0.0'} + '@noble/hashes@2.2.0': resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==} engines: {node: '>= 20.19.0'} + '@pnpm/deps.graph-sequencer@1100.0.1': + resolution: {integrity: sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A==} + engines: {node: '>=22.13'} + '@rollup/rollup-android-arm-eabi@4.62.0': resolution: {integrity: sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==} cpu: [arm] @@ -795,6 +883,10 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -898,6 +990,15 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -921,6 +1022,13 @@ packages: html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + human-id@4.2.1: + resolution: {integrity: sha512-zPGsiS+dWoTZtZ4AtpA9Y+BdSFSNWvnouNlWNoUFyAM6xHOHmdCvqO3k8AIbdamCOv4gUFUVNPf6rJFfc4UiJw==} + hasBin: true + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -928,12 +1036,18 @@ packages: is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + js-tokens@9.0.1: resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + launch-editor@2.14.1: + resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==} + linkify-it@5.0.1: resolution: {integrity: sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==} @@ -1059,6 +1173,9 @@ packages: vite: optional: true + package-manager-detector@1.8.0: + resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -1115,9 +1232,16 @@ packages: engines: {node: '>=10'} hasBin: true + shell-quote@1.10.0: + resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} + engines: {node: '>= 0.4'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1146,6 +1270,10 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} + engines: {node: '>=18'} + tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} @@ -1299,6 +1427,122 @@ packages: snapshots: + '@changesets/apply-release-plan@8.1.0': + dependencies: + '@changesets/config': 4.0.0 + '@changesets/format': 0.1.2 + '@changesets/git': 4.0.1 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + import-meta-resolve: 4.2.0 + jsonc-parser: 3.3.1 + semver: 7.8.5 + + '@changesets/assemble-release-plan@7.0.0': + dependencies: + '@changesets/errors': 1.0.0 + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + semver: 7.8.5 + + '@changesets/changelog-git@1.0.0': + dependencies: + '@changesets/types': 7.0.0 + + '@changesets/cli@3.0.2': + dependencies: + '@changesets/apply-release-plan': 8.1.0 + '@changesets/assemble-release-plan': 7.0.0 + '@changesets/changelog-git': 1.0.0 + '@changesets/config': 4.0.0 + '@changesets/errors': 1.0.0 + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/git': 4.0.1 + '@changesets/pre': 3.0.0 + '@changesets/read': 1.0.1 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + '@changesets/write': 1.0.1 + '@clack/prompts': 1.8.0 + '@manypkg/get-packages': 3.1.0 + '@pnpm/deps.graph-sequencer': 1100.0.1 + cac: 7.0.0 + import-meta-resolve: 4.2.0 + launch-editor: 2.14.1 + package-manager-detector: 1.8.0 + semver: 7.8.5 + tinyexec: 1.3.1 + + '@changesets/config@4.0.0': + dependencies: + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 + picomatch: 4.0.4 + + '@changesets/errors@1.0.0': {} + + '@changesets/format@0.1.2': + dependencies: + package-manager-detector: 1.8.0 + tinyexec: 1.3.1 + + '@changesets/get-dependents-graph@3.0.0': + dependencies: + '@changesets/types': 7.0.0 + semver: 7.8.5 + + '@changesets/git@4.0.1': + dependencies: + '@changesets/errors': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 + picomatch: 4.0.4 + tinyexec: 1.3.1 + + '@changesets/parse@1.0.0': + dependencies: + '@changesets/types': 7.0.0 + yaml: 2.9.0 + + '@changesets/pre@3.0.0': + dependencies: + '@changesets/errors': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 + + '@changesets/read@1.0.1': + dependencies: + '@changesets/git': 4.0.1 + '@changesets/parse': 1.0.0 + '@changesets/types': 7.0.0 + + '@changesets/should-skip-package@1.0.0': + dependencies: + '@changesets/types': 7.0.0 + + '@changesets/types@7.0.0': {} + + '@changesets/write@1.0.1': + dependencies: + '@changesets/format': 0.1.2 + '@changesets/types': 7.0.0 + human-id: 4.2.1 + + '@clack/core@1.5.0': + dependencies: + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + + '@clack/prompts@1.8.0': + dependencies: + '@clack/core': 1.5.0 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + '@esbuild/aix-ppc64@0.25.12': optional: true @@ -1535,8 +1779,25 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.5': {} + '@manypkg/find-root@3.1.0': + dependencies: + '@manypkg/tools': 2.1.2 + + '@manypkg/get-packages@3.1.0': + dependencies: + '@manypkg/find-root': 3.1.0 + '@manypkg/tools': 2.1.2 + + '@manypkg/tools@2.1.2': + dependencies: + jju: 1.4.0 + tinyglobby: 0.2.17 + yaml: 2.9.0 + '@noble/hashes@2.2.0': {} + '@pnpm/deps.graph-sequencer@1100.0.1': {} + '@rollup/rollup-android-arm-eabi@4.62.0': optional: true @@ -1736,6 +1997,8 @@ snapshots: cac@6.7.14: {} + cac@7.0.0: {} + ccount@2.0.1: {} chai@5.3.3: @@ -1889,6 +2152,16 @@ snapshots: extend@3.0.2: {} + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -1916,16 +2189,27 @@ snapshots: html-void-elements@3.0.0: {} + human-id@4.2.1: {} + + import-meta-resolve@4.2.0: {} + is-plain-obj@4.1.0: {} is-reference@3.0.3: dependencies: '@types/estree': 1.0.9 + jju@1.4.0: {} + js-tokens@9.0.1: {} jsonc-parser@3.3.1: {} + launch-editor@2.14.1: + dependencies: + picocolors: 1.1.1 + shell-quote: 1.10.0 + linkify-it@5.0.1: dependencies: uc.micro: 2.1.0 @@ -2138,6 +2422,8 @@ snapshots: transitivePeerDependencies: - '@typescript-eslint/types' + package-manager-detector@1.8.0: {} + pathe@2.0.3: {} pathval@2.0.1: {} @@ -2221,8 +2507,12 @@ snapshots: semver@7.8.5: {} + shell-quote@1.10.0: {} + siginfo@2.0.0: {} + sisteransi@1.0.5: {} + source-map-js@1.2.1: {} space-separated-tokens@2.0.2: {} @@ -2246,6 +2536,8 @@ snapshots: tinyexec@0.3.2: {} + tinyexec@1.3.1: {} + tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.4) diff --git a/reports/audit-2026-09-11.md b/reports/audit-2026-09-11.md new file mode 100644 index 0000000..32946f7 --- /dev/null +++ b/reports/audit-2026-09-11.md @@ -0,0 +1,94 @@ +# Markdown Audit, September 11, 2026 + +This report records the first audit pass. The [follow-up compatibility pass](./compatibility-budget-2026-09-11.md) has newer measurements and fixes several items listed below as remaining work. + +Baseline: `e56d191`, with the runtime released in `@tanstack/markdown@0.0.13`. Measurements compare that revision with this working tree using the same esbuild version and Node 24.15.0 process. No runtime dependencies, public entry points, or rendering options were added or removed. + +## What Changed + +- Plain text is scanned in runs instead of one character at a time. Exhausted delimiter budgets skip further failed searches while preserving escape handling. +- Code spans require matching backtick-run lengths and preserve spaces, tabs, and nonbreaking spaces. Padding removal is linear, including long spans with only one padded end. +- Image alt text retains nested formatting and reference-link text without creating invisible footnote references. +- All ASCII punctuation escapes work, including even backslashes before closing link and emphasis delimiters. Nested triple emphasis is preserved. +- Reference labels normalize internal whitespace, and duplicate definitions keep the first destination. Fence-like lines with trailing content no longer expose code to definition extraction. +- Ordered lists starting at zero retain their start value. HTML now unwraps every direct paragraph in tight list ASTs, matching React and Octane. +- Escaped pipes at the ends of table rows remain cell content. +- Generated heading and footnote definition IDs avoid collisions with previously generated suffixes. Headings inside footnotes share the document slugger. +- Unsafe numeric highlight ranges cannot trap the parser in a non-advancing loop. +- Package-manager tabs accept prototype-shaped names without throwing. Large heading collections no longer exceed the JavaScript argument limit. +- Docs transforms avoid repeated scans, allocations, and regex compilation. URL screening has the same policy with fewer checks. + +## Size + +All measured entries are smaller or unchanged in minified, gzip, and Brotli bytes. Framework runtimes remain external. These are total entry-point bundles, not additional adapter costs. + +| Entry | Gzip Before | Gzip After | Brotli Before | Brotli After | +| --- | ---: | ---: | ---: | ---: | +| Parser | 4,969 | 4,948 | 4,573 | 4,559 | +| HTML | 6,790 | 6,737 | 6,211 | 6,169 | +| HTML with highlighter stub | 6,811 | 6,759 | 6,231 | 6,195 | +| React | 6,711 | 6,682 | 6,165 | 6,153 | +| Octane | 6,707 | 6,681 | 6,171 | 6,145 | +| React with streaming | 6,890 | 6,868 | 6,322 | 6,316 | +| Docs preset | 2,398 | 2,334 | 2,188 | 2,121 | +| Tabs | 1,255 | 1,232 | 1,103 | 1,094 | +| Callouts | 372 | 372 | 329 | 329 | +| Streaming extension | 311 | 311 | 253 | 253 | + +The bundle tests now enforce the pre-audit minified, gzip, and Brotli ceilings. No budget was increased. + +## Performance + +Nine interleaved samples per operation, with warmup and alternating execution order. Times below are median milliseconds for parsing and HTML rendering, except the streaming row, which replays one response in 32-character chunks. + +| Fixture | Before | After | +| --- | ---: | ---: | +| AI response | 0.0272 | 0.0171 | +| Streaming response | 0.3195 | 0.1988 | +| Code-heavy document | 0.0108 | 0.0104 | +| Malformed document | 0.0051 | 0.0031 | +| Prose-heavy document | 0.0554 | 0.0154 | +| Small document | 0.0162 | 0.0115 | +| Tables and lists | 0.0326 | 0.0257 | + +Prose and streaming improvements held across repeated runs. Code-heavy parsing and AST-only rendering were roughly unchanged, with small timing fluctuations. A few percent at these durations is not proof of an improvement or regression. These are Node measurements, not browser benchmarks or evidence of being the fastest Markdown library. + +Synthetic stress cases also improved: 32,000 unmatched brackets rendered in about 0.64 ms instead of 3.31 ms, and 64 KB of plain prose in about 0.02 ms instead of 1.70 ms. They exercise specific paths and should not be presented as typical-document speedups. + +## Compatibility And Tests + +- CommonMark matches increased from 358 to 377 of 652, or 57.8%, after the existing serializer normalization. No previous match was lost. The test baseline now protects all 377 individually, rather than only the original 287. +- 156 tests pass, including 29 new audit regressions. New applicable rendering cases check HTML, React SSR, and Octane SSR. Synchronous hang cases use subprocess deadlines. +- The full `pnpm run verify` gate covers tests, types, build, docs, skills, conformance, sizes, benchmarks, and package dry run. No package was published. +- The corpus audits parsed 6,308 TanStack Markdown files across 26 local repositories and 10,517 external Markdown files across 20 pinned repositories without errors or nondeterminism. MDX was inventoried, not parsed. The local audit skipped 696 unavailable tracked paths. +- Comparing both revisions on those same files found 126 changed documents, 25 gained normalized exact matches against Marked, zero lost exact matches, and zero new text-content differences according to the corpus classifier. + +The corpus gates do not mean universal compatibility. The external audit still reports 2,718 content differences and 1,875 structure differences against Marked. Templates, unsupported syntax, raw HTML, and intentional extensions account for some differences; the classifier does not establish that all remaining differences are harmless. There are zero unexplained target-profile content differences under the existing filter, which deliberately excludes documents using unsupported or review-required features. + +## Remaining Work + +These are existing gaps, not fixed by this patch. They need focused designs and their own size, compatibility, and performance comparisons. + +| Priority | Area | Finding And Next Step | +| --- | --- | --- | +| High | Comment components | Repeated unmatched `` lines repeatedly scan the remaining document. Same-name nesting also stops at the first closing marker. Index and balance component boundaries once per block input, with fence awareness. The inline scan budget does not protect this extension. | +| High | Nested links | `[outer [inner](/i)](/o)` produces nested anchors, which browsers repair and can cause hydration failures. Resolve link nesting at parse time instead of trying to fix the HTML afterward. | +| Medium | Nested list looseness | Blank lines inside a child list or fenced block can incorrectly make the outer list loose. Track blank separation at the owning block level. For example, `- outer\n - inner\n\n - second\n- next` should keep the outer list tight. | +| Medium | Definition context | The global definition prepass still lacks full container context. A fence opened on a list-marker line, such as `- ```md`, can expose its reference-looking content to extraction. A block-aware definition pass is needed. | +| Medium | Footnote navigation | Repeated references to `x` can collide with the first reference to a distinct `x-2` footnote. A footnote ending in a code block or list gets no back-reference. Reference ID allocation and back-reference placement need their own fixes across all renderers. | +| Medium | Link destinations | `[empty]()` loses its link. Destination escaping, title delimiters, and nested punctuation are still incomplete. Keep protocol rejection distinct from a valid empty destination. | +| Medium | Resource ceilings | Footnote ordering uses linear searches, numeric range expansion is bounded per range rather than per document, and optional transforms have no total work budget. Bound application input sizes; do not advertise a general linear-time guarantee. | +| Measurement | Browser and memory behavior | This audit measures Node and SSR, not browser hydration, allocation profiles, older React versions, or sustained streaming UI workloads. The older benchmark script's heap deltas are not an allocation profiler. | + +Setext headings, indented code, general autolinking, full entity handling, MDX execution, and complete CommonMark/GFM coverage remain outside the current profile. Do not expand that profile merely to increase the conformance percentage. Prioritize the correctness and resource issues above, then use actual target documents to decide which additional syntax is worth its cost. + +## Reproduce + +```bash +pnpm run verify +pnpm run corpus:audit:external +pnpm run corpus:audit:tanstack +node --import tsx scripts/compare-revision.mjs e56d191 --corpus +``` + +The last command compares both revisions in one process and writes sizes, raw timing samples, CommonMark changes, and document-difference details to `artifacts/audit-comparison.json`. Run it without competing CPU-heavy work. Local corpus results depend on the sibling checkouts recorded in `reports/tanstack-corpus.json`; external revisions are pinned in `corpus/external-repositories.json`. diff --git a/reports/benchmarks.json b/reports/benchmarks.json index fffb26a..4241839 100644 --- a/reports/benchmarks.json +++ b/reports/benchmarks.json @@ -1,5 +1,5 @@ { - "generatedAt": "2026-07-26T03:30:51.327Z", + "generatedAt": "2026-09-11T18:44:31.005Z", "sink": 112177610, "results": [ { @@ -8,9 +8,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.018874521000000002, + "msPerOp": 0.016327895999999995, "outputBytes": 15, - "heapDeltaKb": 1376.234375 + "heapDeltaKb": 315.3359375 }, { "group": "markdown", @@ -18,9 +18,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.003888417000000004, + "msPerOp": 0.004077645500000003, "outputBytes": 1399, - "heapDeltaKb": -2717.7890625 + "heapDeltaKb": 4611.7109375 }, { "group": "markdown", @@ -28,9 +28,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.0033255834999999932, + "msPerOp": 0.003343041999999997, "outputBytes": 1143, - "heapDeltaKb": 722.8828125 + "heapDeltaKb": 23.9765625 }, { "group": "markdown", @@ -38,9 +38,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.021341854499999997, + "msPerOp": 0.020055771000000007, "outputBytes": 1399, - "heapDeltaKb": 1964.4921875 + "heapDeltaKb": 545.7890625 }, { "group": "markdown", @@ -48,9 +48,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.020104812499999992, + "msPerOp": 0.018646020999999992, "outputBytes": 1143, - "heapDeltaKb": 3244.875 + "heapDeltaKb": 1626.9609375 }, { "group": "markdown", @@ -58,9 +58,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.024445416500000008, + "msPerOp": 0.025917020500000006, "outputBytes": 1035, - "heapDeltaKb": -611.3828125 + "heapDeltaKb": -584.203125 }, { "group": "markdown", @@ -68,9 +68,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.016478687500000005, + "msPerOp": 0.017147250000000013, "outputBytes": 1087, - "heapDeltaKb": -3661.8984375 + "heapDeltaKb": -3580.1484375 }, { "group": "markdown", @@ -78,9 +78,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.139046604, + "msPerOp": 0.14661381250000002, "outputBytes": 825, - "heapDeltaKb": 9400.1640625 + "heapDeltaKb": 6402.0078125 }, { "group": "markdown", @@ -88,9 +88,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.011386437499999999, + "msPerOp": 0.011903479000000005, "outputBytes": 825, - "heapDeltaKb": 136.6328125 + "heapDeltaKb": 155.6796875 }, { "group": "markdown", @@ -98,9 +98,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.007102000000000032, + "msPerOp": 0.007521978999999988, "outputBytes": 1117, - "heapDeltaKb": -11973.265625 + "heapDeltaKb": 4372.796875 }, { "group": "markdown", @@ -108,9 +108,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 2000, - "msPerOp": 0.15757802100000004, + "msPerOp": 0.16969152099999996, "outputBytes": 824, - "heapDeltaKb": 30108.1171875 + "heapDeltaKb": 14038.59375 }, { "group": "markdown", @@ -118,9 +118,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.006155375000000049, + "msPerOp": 0.006406916000000137, "outputBytes": 15, - "heapDeltaKb": -3895.7890625 + "heapDeltaKb": -6268.078125 }, { "group": "markdown", @@ -128,9 +128,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.007021707999999989, + "msPerOp": 0.007081665999999814, "outputBytes": 4504, - "heapDeltaKb": 2304.4609375 + "heapDeltaKb": 347.3984375 }, { "group": "markdown", @@ -138,9 +138,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.0032562500000000226, + "msPerOp": 0.0034035000000001217, "outputBytes": 1835, - "heapDeltaKb": -18930.171875 + "heapDeltaKb": 12183.8671875 }, { "group": "markdown", @@ -148,9 +148,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.013210709000000065, + "msPerOp": 0.01388537499999984, "outputBytes": 4504, - "heapDeltaKb": -1353.2734375 + "heapDeltaKb": -5203.2265625 }, { "group": "markdown", @@ -158,9 +158,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.009465082999999937, + "msPerOp": 0.010949084000000085, "outputBytes": 1835, - "heapDeltaKb": 9930.2890625 + "heapDeltaKb": -26450.2265625 }, { "group": "markdown", @@ -168,9 +168,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.005430749999999989, + "msPerOp": 0.005920415999999932, "outputBytes": 1330, - "heapDeltaKb": -19833.4140625 + "heapDeltaKb": 12896.25 }, { "group": "markdown", @@ -178,9 +178,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.007604666999999836, + "msPerOp": 0.00877541599999995, "outputBytes": 1330, - "heapDeltaKb": 27308.1484375 + "heapDeltaKb": -5356.265625 }, { "group": "markdown", @@ -188,9 +188,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.12219466599999987, + "msPerOp": 0.14078658300000008, "outputBytes": 1330, - "heapDeltaKb": -11584.3828125 + "heapDeltaKb": -11812.15625 }, { "group": "markdown", @@ -198,9 +198,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.00846829200000002, + "msPerOp": 0.009368208000000095, "outputBytes": 1330, - "heapDeltaKb": 24578.390625 + "heapDeltaKb": -8107.4375 }, { "group": "markdown", @@ -208,9 +208,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.004065499999999929, + "msPerOp": 0.004259957999999869, "outputBytes": 1512, - "heapDeltaKb": 2558.8125 + "heapDeltaKb": 2558.390625 }, { "group": "markdown", @@ -218,9 +218,9 @@ "fixture": "code-heavy.md", "bytes": 1011, "iterations": 1000, - "msPerOp": 0.12834387500000002, + "msPerOp": 0.14871712500000012, "outputBytes": 1200, - "heapDeltaKb": -6735.171875 + "heapDeltaKb": -6915 }, { "group": "markdown", @@ -228,9 +228,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.0029249375000000555, + "msPerOp": 0.002287645999999995, "outputBytes": 15, - "heapDeltaKb": 23936.5 + "heapDeltaKb": 18583.7578125 }, { "group": "markdown", @@ -238,9 +238,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.001504312499999969, + "msPerOp": 0.0018336250000000973, "outputBytes": 1067, - "heapDeltaKb": -15520.8203125 + "heapDeltaKb": -16217.9140625 }, { "group": "markdown", @@ -248,9 +248,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.0007086664999999357, + "msPerOp": 0.0007485414999999875, "outputBytes": 361, - "heapDeltaKb": 6986.2265625 + "heapDeltaKb": 6298.890625 }, { "group": "markdown", @@ -258,9 +258,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.004500062500000013, + "msPerOp": 0.0042109375, "outputBytes": 1067, - "heapDeltaKb": 8059.1640625 + "heapDeltaKb": 2330.59375 }, { "group": "markdown", @@ -268,9 +268,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.003602708000000007, + "msPerOp": 0.0031345420000000106, "outputBytes": 361, - "heapDeltaKb": -2208.1171875 + "heapDeltaKb": -7910.234375 }, { "group": "markdown", @@ -278,9 +278,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.004533125000000041, + "msPerOp": 0.004964625000000069, "outputBytes": 350, - "heapDeltaKb": -16761.9140625 + "heapDeltaKb": -16034.546875 }, { "group": "markdown", @@ -288,9 +288,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.004057645999999977, + "msPerOp": 0.004126375000000053, "outputBytes": 300, - "heapDeltaKb": -7844.484375 + "heapDeltaKb": 24762.1640625 }, { "group": "markdown", @@ -298,9 +298,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.04268487500000003, + "msPerOp": 0.04548495850000006, "outputBytes": 300, - "heapDeltaKb": 7691.921875 + "heapDeltaKb": -24868.6953125 }, { "group": "markdown", @@ -308,9 +308,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.0032291664999999055, + "msPerOp": 0.003408083499999975, "outputBytes": 300, - "heapDeltaKb": -9658.84375 + "heapDeltaKb": 23073.328125 }, { "group": "markdown", @@ -318,9 +318,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.0016747080000000096, + "msPerOp": 0.0018331875000000082, "outputBytes": 408, - "heapDeltaKb": 2938.6484375 + "heapDeltaKb": 2938.84375 }, { "group": "markdown", @@ -328,9 +328,9 @@ "fixture": "malformed.md", "bytes": 237, "iterations": 2000, - "msPerOp": 0.04591433300000006, + "msPerOp": 0.04937491649999993, "outputBytes": 297, - "heapDeltaKb": 1749.2734375 + "heapDeltaKb": 1974.71875 }, { "group": "markdown", @@ -338,9 +338,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.02172895799999992, + "msPerOp": 0.013079917000000022, "outputBytes": 15, - "heapDeltaKb": 1528.3984375 + "heapDeltaKb": 15263.734375 }, { "group": "markdown", @@ -348,9 +348,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.0019378340000000662, + "msPerOp": 0.002199666999999863, "outputBytes": 1903, - "heapDeltaKb": 11772.6328125 + "heapDeltaKb": -20948.8515625 }, { "group": "markdown", @@ -358,9 +358,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.0020207909999999172, + "msPerOp": 0.0022082499999999073, "outputBytes": 1903, - "heapDeltaKb": -20955.1796875 + "heapDeltaKb": 11793.546875 }, { "group": "markdown", @@ -368,9 +368,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.025636584000000084, + "msPerOp": 0.016069999999999935, "outputBytes": 1903, - "heapDeltaKb": 14688.1953125 + "heapDeltaKb": -5901.640625 }, { "group": "markdown", @@ -378,9 +378,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.025585791999999857, + "msPerOp": 0.01590337499999987, "outputBytes": 1903, - "heapDeltaKb": -18061.1328125 + "heapDeltaKb": -5896.0078125 }, { "group": "markdown", @@ -388,9 +388,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.03133520900000008, + "msPerOp": 0.035356667000000015, "outputBytes": 1860, - "heapDeltaKb": 6664.578125 + "heapDeltaKb": 6719.390625 }, { "group": "markdown", @@ -398,9 +398,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.016169833000000154, + "msPerOp": 0.01706950000000006, "outputBytes": 1860, - "heapDeltaKb": 14340.546875 + "heapDeltaKb": 14302.8671875 }, { "group": "markdown", @@ -408,9 +408,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.21124504100000013, + "msPerOp": 0.22802695800000014, "outputBytes": 1862, - "heapDeltaKb": 5863.40625 + "heapDeltaKb": 84947.015625 }, { "group": "markdown", @@ -418,9 +418,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.010868584000000056, + "msPerOp": 0.012153291999999965, "outputBytes": 1862, - "heapDeltaKb": -17640.3125 + "heapDeltaKb": -13384.6015625 }, { "group": "markdown", @@ -428,7 +428,7 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.005820832999999993, + "msPerOp": 0.006072957999999971, "outputBytes": 2340, "heapDeltaKb": 3368.1953125 }, @@ -438,9 +438,9 @@ "fixture": "prose-heavy.md", "bytes": 1700, "iterations": 1000, - "msPerOp": 0.23011033300000008, + "msPerOp": 0.249780209, "outputBytes": 1859, - "heapDeltaKb": 51836.859375 + "heapDeltaKb": 50851.1171875 }, { "group": "markdown", @@ -448,9 +448,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.009464228999999932, + "msPerOp": 0.00894247949999999, "outputBytes": 15, - "heapDeltaKb": 24039.15625 + "heapDeltaKb": 7608.5390625 }, { "group": "markdown", @@ -458,9 +458,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.002842104000000063, + "msPerOp": 0.0029711039999999684, "outputBytes": 1328, - "heapDeltaKb": 31783.3203125 + "heapDeltaKb": 30267.296875 }, { "group": "markdown", @@ -468,9 +468,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.0023894789999999375, + "msPerOp": 0.0025746250000001965, "outputBytes": 1002, - "heapDeltaKb": 26132.578125 + "heapDeltaKb": -39909.1640625 }, { "group": "markdown", @@ -478,9 +478,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.013009583499999963, + "msPerOp": 0.01206379199999992, "outputBytes": 1328, - "heapDeltaKb": -14446.7890625 + "heapDeltaKb": 33857.9609375 }, { "group": "markdown", @@ -488,9 +488,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.01235895800000003, + "msPerOp": 0.011751125000000001, "outputBytes": 1002, - "heapDeltaKb": -20381.421875 + "heapDeltaKb": -37329.8828125 }, { "group": "markdown", @@ -498,9 +498,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.011366500000000087, + "msPerOp": 0.012103583000000072, "outputBytes": 724, - "heapDeltaKb": -14855.609375 + "heapDeltaKb": -14790.46875 }, { "group": "markdown", @@ -508,9 +508,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.007051770999999917, + "msPerOp": 0.007528125000000045, "outputBytes": 776, - "heapDeltaKb": 3163.8125 + "heapDeltaKb": 3163.140625 }, { "group": "markdown", @@ -518,9 +518,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.08688233300000002, + "msPerOp": 0.09641347900000005, "outputBytes": 535, - "heapDeltaKb": -20923.640625 + "heapDeltaKb": 45302.109375 }, { "group": "markdown", @@ -528,9 +528,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.005424354499999936, + "msPerOp": 0.006531604500000185, "outputBytes": 535, - "heapDeltaKb": 50226.15625 + "heapDeltaKb": -15211.2890625 }, { "group": "markdown", @@ -538,9 +538,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.0030923124999999345, + "msPerOp": 0.0033195419999999557, "outputBytes": 854, - "heapDeltaKb": 3722.4921875 + "heapDeltaKb": 3642.8125 }, { "group": "markdown", @@ -548,9 +548,9 @@ "fixture": "small-doc.md", "bytes": 432, "iterations": 2000, - "msPerOp": 0.09951858349999998, + "msPerOp": 0.11191214549999995, "outputBytes": 534, - "heapDeltaKb": 3726.28125 + "heapDeltaKb": -61627.6484375 }, { "group": "markdown", @@ -558,9 +558,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.022308083500000065, + "msPerOp": 0.022367541500000015, "outputBytes": 15, - "heapDeltaKb": 48781.484375 + "heapDeltaKb": 21773.0546875 }, { "group": "markdown", @@ -568,9 +568,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.0039127085000000075, + "msPerOp": 0.004276541500000121, "outputBytes": 1315, - "heapDeltaKb": -17723.796875 + "heapDeltaKb": -18808.4296875 }, { "group": "markdown", @@ -578,9 +578,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.0037410624999999983, + "msPerOp": 0.003825853999999936, "outputBytes": 1315, - "heapDeltaKb": -17690.0390625 + "heapDeltaKb": 46672.375 }, { "group": "markdown", @@ -588,9 +588,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.026292646000000103, + "msPerOp": 0.026598353999999973, "outputBytes": 1315, - "heapDeltaKb": 30714.5625 + "heapDeltaKb": 2395.0546875 }, { "group": "markdown", @@ -598,9 +598,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.027844062500000065, + "msPerOp": 0.0264140625, "outputBytes": 1315, - "heapDeltaKb": -34604.4296875 + "heapDeltaKb": 2248.7734375 }, { "group": "markdown", @@ -608,9 +608,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.025883312499999873, + "msPerOp": 0.02775885449999987, "outputBytes": 1102, - "heapDeltaKb": -14274.421875 + "heapDeltaKb": -14314.8203125 }, { "group": "markdown", @@ -618,9 +618,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.01628933350000011, + "msPerOp": 0.01780399999999986, "outputBytes": 1325, - "heapDeltaKb": 26392.0234375 + "heapDeltaKb": -39188.6875 }, { "group": "markdown", @@ -628,9 +628,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.14734077100000012, + "msPerOp": 0.16020520850000003, "outputBytes": 627, - "heapDeltaKb": -38940.6015625 + "heapDeltaKb": 26000.8984375 }, { "group": "markdown", @@ -638,9 +638,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.010315520999999989, + "msPerOp": 0.011145854000000099, "outputBytes": 627, - "heapDeltaKb": 28388.3828125 + "heapDeltaKb": -37171.28125 }, { "group": "markdown", @@ -648,9 +648,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.005329791500000056, + "msPerOp": 0.00554710399999999, "outputBytes": 1202, - "heapDeltaKb": 4505.421875 + "heapDeltaKb": 4506 }, { "group": "markdown", @@ -658,9 +658,9 @@ "fixture": "tables-lists.md", "bytes": 454, "iterations": 2000, - "msPerOp": 0.17302383300000018, + "msPerOp": 0.1868297500000001, "outputBytes": 622, - "heapDeltaKb": 7657.609375 + "heapDeltaKb": 7536.3515625 }, { "group": "streaming", @@ -668,9 +668,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 250, - "msPerOp": 0.23448650000000087, + "msPerOp": 0.2125391640000016, "outputBytes": 1112, - "heapDeltaKb": -23405.6796875 + "heapDeltaKb": 62452.2109375 }, { "group": "streaming", @@ -678,9 +678,9 @@ "fixture": "ai-response.md", "bytes": 652, "iterations": 250, - "msPerOp": 0.2556091680000009, + "msPerOp": 0.2893066680000011, "outputBytes": 1035, - "heapDeltaKb": 3231.4921875 + "heapDeltaKb": 3190 } ] } \ No newline at end of file diff --git a/reports/benchmarks.md b/reports/benchmarks.md index 966e21f..3249da0 100644 --- a/reports/benchmarks.md +++ b/reports/benchmarks.md @@ -1,6 +1,6 @@ # Benchmark Results -Generated: 2026-07-26T03:30:51.330Z +Generated: 2026-09-11T18:44:31.009Z 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. @@ -8,94 +8,94 @@ Lower `ms/op` is better. Benchmarks run in Node with production package builds w | Name | Fixture | Bytes | Iterations | ms/op | Output bytes | Heap delta KB | | :--- | :--- | ---: | ---: | ---: | ---: | ---: | -| @tanstack/markdown parse | ai-response.md | 652 | 2000 | 0.0189 | 15 | 1376.2 | -| @tanstack/markdown render AST with external highlighter | ai-response.md | 652 | 2000 | 0.0039 | 1399 | -2717.8 | -| @tanstack/markdown render AST | ai-response.md | 652 | 2000 | 0.0033 | 1143 | 722.9 | -| @tanstack/markdown parse+render with external highlighter | ai-response.md | 652 | 2000 | 0.0213 | 1399 | 1964.5 | -| @tanstack/markdown parse+render | ai-response.md | 652 | 2000 | 0.0201 | 1143 | 3244.9 | -| marked parse+render | ai-response.md | 652 | 2000 | 0.0244 | 1035 | -611.4 | -| markdown-it parse+render | ai-response.md | 652 | 2000 | 0.0165 | 1087 | -3661.9 | -| micromark render | ai-response.md | 652 | 2000 | 0.1390 | 825 | 9400.2 | -| commonmark parse+render | ai-response.md | 652 | 2000 | 0.0114 | 825 | 136.6 | -| markdown-wasm render | ai-response.md | 652 | 2000 | 0.0071 | 1117 | -11973.3 | -| unified remark+rehype render | ai-response.md | 652 | 2000 | 0.1576 | 824 | 30108.1 | -| @tanstack/markdown parse | code-heavy.md | 1011 | 1000 | 0.0062 | 15 | -3895.8 | -| @tanstack/markdown render AST with external highlighter | code-heavy.md | 1011 | 1000 | 0.0070 | 4504 | 2304.5 | -| @tanstack/markdown render AST | code-heavy.md | 1011 | 1000 | 0.0033 | 1835 | -18930.2 | -| @tanstack/markdown parse+render with external highlighter | code-heavy.md | 1011 | 1000 | 0.0132 | 4504 | -1353.3 | -| @tanstack/markdown parse+render | code-heavy.md | 1011 | 1000 | 0.0095 | 1835 | 9930.3 | -| marked parse+render | code-heavy.md | 1011 | 1000 | 0.0054 | 1330 | -19833.4 | -| markdown-it parse+render | code-heavy.md | 1011 | 1000 | 0.0076 | 1330 | 27308.1 | -| micromark render | code-heavy.md | 1011 | 1000 | 0.1222 | 1330 | -11584.4 | -| commonmark parse+render | code-heavy.md | 1011 | 1000 | 0.0085 | 1330 | 24578.4 | -| markdown-wasm render | code-heavy.md | 1011 | 1000 | 0.0041 | 1512 | 2558.8 | -| unified remark+rehype render | code-heavy.md | 1011 | 1000 | 0.1283 | 1200 | -6735.2 | -| @tanstack/markdown parse | malformed.md | 237 | 2000 | 0.0029 | 15 | 23936.5 | -| @tanstack/markdown render AST with external highlighter | malformed.md | 237 | 2000 | 0.0015 | 1067 | -15520.8 | -| @tanstack/markdown render AST | malformed.md | 237 | 2000 | 0.0007 | 361 | 6986.2 | -| @tanstack/markdown parse+render with external highlighter | malformed.md | 237 | 2000 | 0.0045 | 1067 | 8059.2 | -| @tanstack/markdown parse+render | malformed.md | 237 | 2000 | 0.0036 | 361 | -2208.1 | -| marked parse+render | malformed.md | 237 | 2000 | 0.0045 | 350 | -16761.9 | -| markdown-it parse+render | malformed.md | 237 | 2000 | 0.0041 | 300 | -7844.5 | -| micromark render | malformed.md | 237 | 2000 | 0.0427 | 300 | 7691.9 | -| commonmark parse+render | malformed.md | 237 | 2000 | 0.0032 | 300 | -9658.8 | -| markdown-wasm render | malformed.md | 237 | 2000 | 0.0017 | 408 | 2938.6 | -| unified remark+rehype render | malformed.md | 237 | 2000 | 0.0459 | 297 | 1749.3 | -| @tanstack/markdown parse | prose-heavy.md | 1700 | 1000 | 0.0217 | 15 | 1528.4 | -| @tanstack/markdown render AST with external highlighter | prose-heavy.md | 1700 | 1000 | 0.0019 | 1903 | 11772.6 | -| @tanstack/markdown render AST | prose-heavy.md | 1700 | 1000 | 0.0020 | 1903 | -20955.2 | -| @tanstack/markdown parse+render with external highlighter | prose-heavy.md | 1700 | 1000 | 0.0256 | 1903 | 14688.2 | -| @tanstack/markdown parse+render | prose-heavy.md | 1700 | 1000 | 0.0256 | 1903 | -18061.1 | -| marked parse+render | prose-heavy.md | 1700 | 1000 | 0.0313 | 1860 | 6664.6 | -| markdown-it parse+render | prose-heavy.md | 1700 | 1000 | 0.0162 | 1860 | 14340.5 | -| micromark render | prose-heavy.md | 1700 | 1000 | 0.2112 | 1862 | 5863.4 | -| commonmark parse+render | prose-heavy.md | 1700 | 1000 | 0.0109 | 1862 | -17640.3 | -| markdown-wasm render | prose-heavy.md | 1700 | 1000 | 0.0058 | 2340 | 3368.2 | -| unified remark+rehype render | prose-heavy.md | 1700 | 1000 | 0.2301 | 1859 | 51836.9 | -| @tanstack/markdown parse | small-doc.md | 432 | 2000 | 0.0095 | 15 | 24039.2 | -| @tanstack/markdown render AST with external highlighter | small-doc.md | 432 | 2000 | 0.0028 | 1328 | 31783.3 | -| @tanstack/markdown render AST | small-doc.md | 432 | 2000 | 0.0024 | 1002 | 26132.6 | -| @tanstack/markdown parse+render with external highlighter | small-doc.md | 432 | 2000 | 0.0130 | 1328 | -14446.8 | -| @tanstack/markdown parse+render | small-doc.md | 432 | 2000 | 0.0124 | 1002 | -20381.4 | -| marked parse+render | small-doc.md | 432 | 2000 | 0.0114 | 724 | -14855.6 | -| markdown-it parse+render | small-doc.md | 432 | 2000 | 0.0071 | 776 | 3163.8 | -| micromark render | small-doc.md | 432 | 2000 | 0.0869 | 535 | -20923.6 | -| commonmark parse+render | small-doc.md | 432 | 2000 | 0.0054 | 535 | 50226.2 | -| markdown-wasm render | small-doc.md | 432 | 2000 | 0.0031 | 854 | 3722.5 | -| unified remark+rehype render | small-doc.md | 432 | 2000 | 0.0995 | 534 | 3726.3 | -| @tanstack/markdown parse | tables-lists.md | 454 | 2000 | 0.0223 | 15 | 48781.5 | -| @tanstack/markdown render AST with external highlighter | tables-lists.md | 454 | 2000 | 0.0039 | 1315 | -17723.8 | -| @tanstack/markdown render AST | tables-lists.md | 454 | 2000 | 0.0037 | 1315 | -17690.0 | -| @tanstack/markdown parse+render with external highlighter | tables-lists.md | 454 | 2000 | 0.0263 | 1315 | 30714.6 | -| @tanstack/markdown parse+render | tables-lists.md | 454 | 2000 | 0.0278 | 1315 | -34604.4 | -| marked parse+render | tables-lists.md | 454 | 2000 | 0.0259 | 1102 | -14274.4 | -| markdown-it parse+render | tables-lists.md | 454 | 2000 | 0.0163 | 1325 | 26392.0 | -| micromark render | tables-lists.md | 454 | 2000 | 0.1473 | 627 | -38940.6 | -| commonmark parse+render | tables-lists.md | 454 | 2000 | 0.0103 | 627 | 28388.4 | -| markdown-wasm render | tables-lists.md | 454 | 2000 | 0.0053 | 1202 | 4505.4 | -| unified remark+rehype render | tables-lists.md | 454 | 2000 | 0.1730 | 622 | 7657.6 | +| @tanstack/markdown parse | ai-response.md | 652 | 2000 | 0.0163 | 15 | 315.3 | +| @tanstack/markdown render AST with external highlighter | ai-response.md | 652 | 2000 | 0.0041 | 1399 | 4611.7 | +| @tanstack/markdown render AST | ai-response.md | 652 | 2000 | 0.0033 | 1143 | 24.0 | +| @tanstack/markdown parse+render with external highlighter | ai-response.md | 652 | 2000 | 0.0201 | 1399 | 545.8 | +| @tanstack/markdown parse+render | ai-response.md | 652 | 2000 | 0.0186 | 1143 | 1627.0 | +| marked parse+render | ai-response.md | 652 | 2000 | 0.0259 | 1035 | -584.2 | +| markdown-it parse+render | ai-response.md | 652 | 2000 | 0.0171 | 1087 | -3580.1 | +| micromark render | ai-response.md | 652 | 2000 | 0.1466 | 825 | 6402.0 | +| commonmark parse+render | ai-response.md | 652 | 2000 | 0.0119 | 825 | 155.7 | +| markdown-wasm render | ai-response.md | 652 | 2000 | 0.0075 | 1117 | 4372.8 | +| unified remark+rehype render | ai-response.md | 652 | 2000 | 0.1697 | 824 | 14038.6 | +| @tanstack/markdown parse | code-heavy.md | 1011 | 1000 | 0.0064 | 15 | -6268.1 | +| @tanstack/markdown render AST with external highlighter | code-heavy.md | 1011 | 1000 | 0.0071 | 4504 | 347.4 | +| @tanstack/markdown render AST | code-heavy.md | 1011 | 1000 | 0.0034 | 1835 | 12183.9 | +| @tanstack/markdown parse+render with external highlighter | code-heavy.md | 1011 | 1000 | 0.0139 | 4504 | -5203.2 | +| @tanstack/markdown parse+render | code-heavy.md | 1011 | 1000 | 0.0109 | 1835 | -26450.2 | +| marked parse+render | code-heavy.md | 1011 | 1000 | 0.0059 | 1330 | 12896.3 | +| markdown-it parse+render | code-heavy.md | 1011 | 1000 | 0.0088 | 1330 | -5356.3 | +| micromark render | code-heavy.md | 1011 | 1000 | 0.1408 | 1330 | -11812.2 | +| commonmark parse+render | code-heavy.md | 1011 | 1000 | 0.0094 | 1330 | -8107.4 | +| markdown-wasm render | code-heavy.md | 1011 | 1000 | 0.0043 | 1512 | 2558.4 | +| unified remark+rehype render | code-heavy.md | 1011 | 1000 | 0.1487 | 1200 | -6915.0 | +| @tanstack/markdown parse | malformed.md | 237 | 2000 | 0.0023 | 15 | 18583.8 | +| @tanstack/markdown render AST with external highlighter | malformed.md | 237 | 2000 | 0.0018 | 1067 | -16217.9 | +| @tanstack/markdown render AST | malformed.md | 237 | 2000 | 0.0007 | 361 | 6298.9 | +| @tanstack/markdown parse+render with external highlighter | malformed.md | 237 | 2000 | 0.0042 | 1067 | 2330.6 | +| @tanstack/markdown parse+render | malformed.md | 237 | 2000 | 0.0031 | 361 | -7910.2 | +| marked parse+render | malformed.md | 237 | 2000 | 0.0050 | 350 | -16034.5 | +| markdown-it parse+render | malformed.md | 237 | 2000 | 0.0041 | 300 | 24762.2 | +| micromark render | malformed.md | 237 | 2000 | 0.0455 | 300 | -24868.7 | +| commonmark parse+render | malformed.md | 237 | 2000 | 0.0034 | 300 | 23073.3 | +| markdown-wasm render | malformed.md | 237 | 2000 | 0.0018 | 408 | 2938.8 | +| unified remark+rehype render | malformed.md | 237 | 2000 | 0.0494 | 297 | 1974.7 | +| @tanstack/markdown parse | prose-heavy.md | 1700 | 1000 | 0.0131 | 15 | 15263.7 | +| @tanstack/markdown render AST with external highlighter | prose-heavy.md | 1700 | 1000 | 0.0022 | 1903 | -20948.9 | +| @tanstack/markdown render AST | prose-heavy.md | 1700 | 1000 | 0.0022 | 1903 | 11793.5 | +| @tanstack/markdown parse+render with external highlighter | prose-heavy.md | 1700 | 1000 | 0.0161 | 1903 | -5901.6 | +| @tanstack/markdown parse+render | prose-heavy.md | 1700 | 1000 | 0.0159 | 1903 | -5896.0 | +| marked parse+render | prose-heavy.md | 1700 | 1000 | 0.0354 | 1860 | 6719.4 | +| markdown-it parse+render | prose-heavy.md | 1700 | 1000 | 0.0171 | 1860 | 14302.9 | +| micromark render | prose-heavy.md | 1700 | 1000 | 0.2280 | 1862 | 84947.0 | +| commonmark parse+render | prose-heavy.md | 1700 | 1000 | 0.0122 | 1862 | -13384.6 | +| markdown-wasm render | prose-heavy.md | 1700 | 1000 | 0.0061 | 2340 | 3368.2 | +| unified remark+rehype render | prose-heavy.md | 1700 | 1000 | 0.2498 | 1859 | 50851.1 | +| @tanstack/markdown parse | small-doc.md | 432 | 2000 | 0.0089 | 15 | 7608.5 | +| @tanstack/markdown render AST with external highlighter | small-doc.md | 432 | 2000 | 0.0030 | 1328 | 30267.3 | +| @tanstack/markdown render AST | small-doc.md | 432 | 2000 | 0.0026 | 1002 | -39909.2 | +| @tanstack/markdown parse+render with external highlighter | small-doc.md | 432 | 2000 | 0.0121 | 1328 | 33858.0 | +| @tanstack/markdown parse+render | small-doc.md | 432 | 2000 | 0.0118 | 1002 | -37329.9 | +| marked parse+render | small-doc.md | 432 | 2000 | 0.0121 | 724 | -14790.5 | +| markdown-it parse+render | small-doc.md | 432 | 2000 | 0.0075 | 776 | 3163.1 | +| micromark render | small-doc.md | 432 | 2000 | 0.0964 | 535 | 45302.1 | +| commonmark parse+render | small-doc.md | 432 | 2000 | 0.0065 | 535 | -15211.3 | +| markdown-wasm render | small-doc.md | 432 | 2000 | 0.0033 | 854 | 3642.8 | +| unified remark+rehype render | small-doc.md | 432 | 2000 | 0.1119 | 534 | -61627.6 | +| @tanstack/markdown parse | tables-lists.md | 454 | 2000 | 0.0224 | 15 | 21773.1 | +| @tanstack/markdown render AST with external highlighter | tables-lists.md | 454 | 2000 | 0.0043 | 1315 | -18808.4 | +| @tanstack/markdown render AST | tables-lists.md | 454 | 2000 | 0.0038 | 1315 | 46672.4 | +| @tanstack/markdown parse+render with external highlighter | tables-lists.md | 454 | 2000 | 0.0266 | 1315 | 2395.1 | +| @tanstack/markdown parse+render | tables-lists.md | 454 | 2000 | 0.0264 | 1315 | 2248.8 | +| marked parse+render | tables-lists.md | 454 | 2000 | 0.0278 | 1102 | -14314.8 | +| markdown-it parse+render | tables-lists.md | 454 | 2000 | 0.0178 | 1325 | -39188.7 | +| micromark render | tables-lists.md | 454 | 2000 | 0.1602 | 627 | 26000.9 | +| commonmark parse+render | tables-lists.md | 454 | 2000 | 0.0111 | 627 | -37171.3 | +| markdown-wasm render | tables-lists.md | 454 | 2000 | 0.0055 | 1202 | 4506.0 | +| unified remark+rehype render | tables-lists.md | 454 | 2000 | 0.1868 | 622 | 7536.4 | ## Streaming | Name | Fixture | Bytes | Iterations | ms/op | Output bytes | Heap delta KB | | :--- | :--- | ---: | ---: | ---: | ---: | ---: | -| @tanstack/markdown streaming profile | ai-response.md | 652 | 250 | 0.2345 | 1112 | -23405.7 | -| marked progressive parse+render | ai-response.md | 652 | 250 | 0.2556 | 1035 | 3231.5 | +| @tanstack/markdown streaming profile | ai-response.md | 652 | 250 | 0.2125 | 1112 | 62452.2 | +| marked progressive parse+render | ai-response.md | 652 | 250 | 0.2893 | 1035 | 3190.0 | ## Averages | Group | Name | Mean ms/op | | :--- | :--- | ---: | -| markdown | @tanstack/markdown parse | 0.0136 | -| markdown | @tanstack/markdown render AST with external highlighter | 0.0035 | -| markdown | @tanstack/markdown render AST | 0.0026 | -| markdown | @tanstack/markdown parse+render with external highlighter | 0.0173 | -| markdown | @tanstack/markdown parse+render | 0.0165 | -| markdown | marked parse+render | 0.0172 | -| markdown | markdown-it parse+render | 0.0113 | -| markdown | micromark render | 0.1249 | -| markdown | commonmark parse+render | 0.0083 | -| markdown | markdown-wasm render | 0.0045 | -| markdown | unified remark+rehype render | 0.1391 | -| streaming | @tanstack/markdown streaming profile | 0.2345 | -| streaming | marked progressive parse+render | 0.2556 | +| markdown | @tanstack/markdown parse | 0.0116 | +| markdown | @tanstack/markdown render AST with external highlighter | 0.0037 | +| markdown | @tanstack/markdown render AST | 0.0027 | +| markdown | @tanstack/markdown parse+render with external highlighter | 0.0155 | +| markdown | @tanstack/markdown parse+render | 0.0145 | +| markdown | marked parse+render | 0.0187 | +| markdown | markdown-it parse+render | 0.0121 | +| markdown | micromark render | 0.1363 | +| markdown | commonmark parse+render | 0.0091 | +| markdown | markdown-wasm render | 0.0048 | +| markdown | unified remark+rehype render | 0.1527 | +| streaming | @tanstack/markdown streaming profile | 0.2125 | +| streaming | marked progressive parse+render | 0.2893 | diff --git a/reports/compatibility-budget-2026-09-11.md b/reports/compatibility-budget-2026-09-11.md new file mode 100644 index 0000000..c03d331 --- /dev/null +++ b/reports/compatibility-budget-2026-09-11.md @@ -0,0 +1,79 @@ +# Compatibility Within the Audited Budget + +This pass starts from the working tree produced by the [first September 11 audit](./audit-2026-09-11.md), not the larger published 0.0.13 build. The source snapshot is `/private/tmp/markdown-compat-baseline.Ql4J5n/src`. No dependencies, public options, package versions, or bundle ceilings were increased. + +## What Changed + +- Tight outer lists stay tight when a nested list is loose. Paragraph separation at the item's own level still makes it loose, and task labels remain inline with their checkboxes. +- Indented fences remove the opening fence's indentation from their code. Blank lines inside list code blocks are preserved instead of collapsed. +- Empty link destinations remain links. Inline and reference destinations share parsing for quoted and parenthesized titles, reject malformed destinations, and unescape ASCII punctuation before protocol screening. +- Inner Markdown links disable their outer opener, including links inside emphasis. Linked images still work, and image descriptions do not create nested anchors or invisible footnote references. +- Footnotes ending in code, lists, blockquotes, or no blocks receive back-links. Existing reference IDs and AST structure are preserved. +- Callouts stop at unquoted blank lines, so they do not consume a following callout or hide paragraph separation in a containing list. Quoted blank lines remain inside a callout. +- Comment attributes preserve prototype-shaped names as data. Matched comment bodies are copied once; unmatched bodies no longer accumulate an unused line array. +- React and Octane list rendering avoids rest-array copies and per-block `flatMap` arrays. Shared footnote helpers, simpler underscore checks, and consolidated block checks recover bytes without changing their supported behavior. +- Native searches reject missing closing delimiters before a character-by-character balance scan. These searches still charge the shared work budget. + +## Size + +Every one of the 22 measured import shapes is smaller or unchanged in minified, gzip, and Brotli bytes. This includes narrow imports and complete namespace imports of all 12 public entry points. Framework runtimes are externalized. + +| Entry | Gzip Before | Gzip After | Brotli Before | Brotli After | +| --- | ---: | ---: | ---: | ---: | +| Parser | 4,948 | 4,941 | 4,559 | 4,555 | +| HTML | 6,737 | 6,730 | 6,169 | 6,161 | +| React | 6,682 | 6,648 | 6,153 | 6,107 | +| Octane | 6,681 | 6,649 | 6,145 | 6,108 | +| React with streaming | 6,868 | 6,835 | 6,316 | 6,282 | +| Docs preset | 2,334 | 2,292 | 2,121 | 2,073 | +| Callouts | 372 | 335 | 329 | 278 | +| Streaming | 311 | 311 | 253 | 253 | +| Tabs | 1,232 | 1,221 | 1,094 | 1,082 | + +The permanent tests enforce the starting values in all three formats. The [generated size report](./sizes.md) also includes full namespace imports. + +## Performance + +Two independent comparisons used Node 24.15.0, the same esbuild version, 1,000 warmup calls per path, and nine alternating timing samples. Normal-document parse-and-HTML times stayed within about 3% of the starting implementation. Streaming stayed within about 1%. React and Octane SSR showed no repeatable material slowdown; their small timing differences varied between runs. + +Rendering 32,000 unmatched brackets improved from 0.647 to 0.048 ms in one warmed run and 0.674 to 0.054 ms in the other, roughly 12-13 times faster. This is a synthetic stress case, not a typical-document speedup. + +An earlier, lightly warmed run had isolated SSR outliers. The stronger warmup and independent repeat did not reproduce those slowdowns. Raw runs are retained alongside the source snapshot as `comparison-final-1.json`, `comparison-warm-1.json`, and `comparison-warm-2.json`. These measurements do not guarantee unchanged performance for every input or establish browser or allocation behavior. + +## Compatibility And Verification + +- CommonMark matches increased from 377 to 403 of 652, with no lost matches. All 403 are individually protected. These are normalized comparisons against [CommonMark 0.31.2](https://spec.commonmark.org/0.31.2/), not full conformance. +- All 203 tests pass. This pass adds 46 focused regression cases and a full-public-entry bundle test. Applicable rendering cases check HTML, React SSR, and Octane SSR. +- `pnpm run verify` passed, including tests, typechecking, build, docs, skills, conformance, sizes, benchmarks, and package dry run. +- Both full corpus audits passed: 10,517 external Markdown files across 20 pinned repositories and 6,308 TanStack Markdown files across 26 local repositories. The local audit still skips unavailable tracked paths. MDX is inventoried, not parsed. + +Side-by-side output comparisons use the same files and Marked version for both parser revisions: + +| Corpus | Profile | Changed Files | Gained Exact Matches | Lost Exact Matches | New Content Differences | +| --- | --- | ---: | ---: | ---: | ---: | +| External | Core | 418 | 107 | 0 | 0 | +| External | Docs extensions | 489 | 68 | 0 | 0 | +| TanStack | Core | 1 | 0 | 0 | 0 | +| TanStack | Docs extensions | 23 | 0 | 0 | 0 | + +These are profile-specific comparisons, not counts of unique newly supported documents. Existing differences against Marked remain, including unsupported syntax and intentional extensions. A zero in the last column means the classifier found no newly introduced content-difference category, not proof of identical browser behavior. + +## Remaining Work + +- Balanced, fence-aware comment components and elimination of their repeated unmatched scans. This pass reduces allocation, not their worst-case complexity. +- Container-aware reference-definition collection, including fences opened on list-marker lines. +- Collision-free IDs when repeated footnote references overlap another footnote's normalized name. Existing IDs were not renamed to work around the collision. +- Further destination grammar, emphasis precedence, hard breaks, and quoted HTML handling. +- Inline Markdown in callout titles. A quote-separation prototype gained one CommonMark example but made an existing link-bearing custom title render literally. That change was removed, preserving the existing quote grouping until both behaviors can be handled together. +- Browser hydration, allocation profiling, and long-running streaming UI measurements. Node and SSR benchmarks do not establish these properties. + +## Reproduce + +```bash +pnpm run verify +node --import tsx scripts/audit-external-corpus.ts +node --import tsx scripts/audit-tanstack-corpus.ts +node --import tsx scripts/compare-revision.mjs /private/tmp/markdown-compat-baseline.Ql4J5n --corpus +``` + +The comparison command also accepts a Git revision. It writes sizes, compatibility gains, raw timing samples, and per-document changes to `artifacts/audit-comparison.json`. Run it without competing CPU-heavy work. No release, commit, or push was performed in this pass. diff --git a/reports/conformance.json b/reports/conformance.json index f3c77b9..1ee38fa 100644 --- a/reports/conformance.json +++ b/reports/conformance.json @@ -1,84 +1,14 @@ { - "generatedAt": "2026-08-04T22:15:31.714Z", + "generatedAt": "2026-09-11T18:44:25.803Z", "commonMarkVersion": "0.31.2", - "baseline": 287, + "baseline": 403, "lost": [], - "gained": [ - 55, - 75, - 76, - 177, - 179, - 192, - 203, - 205, - 214, - 216, - 304, - 358, - 359, - 360, - 362, - 371, - 372, - 374, - 375, - 376, - 383, - 384, - 385, - 386, - 387, - 388, - 393, - 397, - 398, - 399, - 400, - 401, - 402, - 406, - 410, - 411, - 414, - 415, - 416, - 418, - 467, - 516, - 527, - 530, - 539, - 542, - 543, - 553, - 554, - 555, - 556, - 557, - 558, - 559, - 560, - 561, - 562, - 563, - 566, - 570, - 584, - 586, - 587, - 588, - 591, - 592, - 593, - 607, - 608, - 618, - 621 - ], + "gained": [], "total": 652, "passing": [ + 12, 13, + 14, 15, 17, 19, @@ -147,6 +77,9 @@ 128, 129, 130, + 131, + 132, + 133, 135, 136, 137, @@ -188,7 +121,10 @@ 192, 197, 199, + 200, + 201, 203, + 204, 205, 207, 209, @@ -228,6 +164,7 @@ 263, 265, 266, + 267, 268, 269, 275, @@ -250,11 +187,13 @@ 304, 305, 306, + 307, 308, 314, 315, 316, 317, + 319, 320, 321, 322, @@ -265,12 +204,18 @@ 327, 328, 329, + 330, + 331, + 332, + 333, 334, 338, 339, + 340, 343, 345, 348, + 349, 350, 355, 356, @@ -354,9 +299,22 @@ 482, 483, 484, + 485, + 486, + 487, + 488, + 490, + 491, + 494, + 495, 496, 497, + 498, + 500, 501, + 505, + 508, + 509, 510, 511, 512, @@ -365,6 +323,9 @@ 515, 516, 517, + 518, + 519, + 520, 522, 523, 527, @@ -372,14 +333,18 @@ 529, 530, 531, + 532, + 533, 535, 539, 542, 543, + 544, 545, 547, 548, 549, + 550, 551, 552, 553, @@ -395,10 +360,16 @@ 563, 565, 566, + 567, 569, 570, 571, 572, + 573, + 574, + 575, + 576, + 577, 578, 579, 580, @@ -406,9 +377,11 @@ 582, 583, 584, + 585, 586, 587, 588, + 589, 590, 591, 592, @@ -437,7 +410,7 @@ 651, 652 ], - "percent": 54.9, + "percent": 61.8, "sections": [ { "section": "Tabs", @@ -448,8 +421,8 @@ { "section": "Backslash escapes", "total": 13, - "passing": 5, - "percent": 38.5 + "passing": 7, + "percent": 53.8 }, { "section": "Entity and numeric character references", @@ -490,8 +463,8 @@ { "section": "Fenced code blocks", "total": 29, - "passing": 21, - "percent": 72.4 + "passing": 24, + "percent": 82.8 }, { "section": "HTML blocks", @@ -502,8 +475,8 @@ { "section": "Link reference definitions", "total": 27, - "passing": 11, - "percent": 40.7 + "passing": 14, + "percent": 51.9 }, { "section": "Paragraphs", @@ -526,14 +499,14 @@ { "section": "List items", "total": 48, - "passing": 26, - "percent": 54.2 + "passing": 27, + "percent": 56.3 }, { "section": "Lists", "total": 26, - "passing": 18, - "percent": 69.2 + "passing": 20, + "percent": 76.9 }, { "section": "Inlines", @@ -544,8 +517,8 @@ { "section": "Code spans", "total": 22, - "passing": 8, - "percent": 36.4 + "passing": 14, + "percent": 63.6 }, { "section": "Emphasis and strong emphasis", @@ -556,14 +529,14 @@ { "section": "Links", "total": 90, - "passing": 47, - "percent": 52.2 + "passing": 68, + "percent": 75.6 }, { "section": "Images", "total": 22, - "passing": 15, - "percent": 68.2 + "passing": 22, + "percent": 100 }, { "section": "Autolinks", diff --git a/reports/conformance.md b/reports/conformance.md index 4aabc55..6ef69ce 100644 --- a/reports/conformance.md +++ b/reports/conformance.md @@ -1,36 +1,36 @@ # Conformance Report -Generated: 2026-08-04T22:15:31.716Z +Generated: 2026-09-11T18:44:25.804Z This is compatibility accounting against CommonMark 0.31.2, not a claim of CommonMark conformance. -- exact normalized matches: 358/652 (54.9%) -- preserved baseline examples: 287/287 -- newly matching examples: 71 +- exact normalized matches: 403/652 (61.8%) +- preserved baseline examples: 403/403 +- newly matching examples: 0 | Section | Passing | Total | Percent | | :--- | ---: | ---: | ---: | | Tabs | 0 | 11 | 0% | -| Backslash escapes | 5 | 13 | 38.5% | +| Backslash escapes | 7 | 13 | 53.8% | | Entity and numeric character references | 5 | 17 | 29.4% | | Precedence | 1 | 1 | 100% | | Thematic breaks | 16 | 19 | 84.2% | | ATX headings | 17 | 18 | 94.4% | | Setext headings | 10 | 27 | 37% | | Indented code blocks | 3 | 12 | 25% | -| Fenced code blocks | 21 | 29 | 72.4% | +| Fenced code blocks | 24 | 29 | 82.8% | | HTML blocks | 29 | 44 | 65.9% | -| Link reference definitions | 11 | 27 | 40.7% | +| Link reference definitions | 14 | 27 | 51.9% | | Paragraphs | 6 | 8 | 75% | | Blank lines | 1 | 1 | 100% | | Block quotes | 15 | 25 | 60% | -| List items | 26 | 48 | 54.2% | -| Lists | 18 | 26 | 69.2% | +| List items | 27 | 48 | 56.3% | +| Lists | 20 | 26 | 76.9% | | Inlines | 1 | 1 | 100% | -| Code spans | 8 | 22 | 36.4% | +| Code spans | 14 | 22 | 63.6% | | Emphasis and strong emphasis | 80 | 132 | 60.6% | -| Links | 47 | 90 | 52.2% | -| Images | 15 | 22 | 68.2% | +| Links | 68 | 90 | 75.6% | +| Images | 22 | 22 | 100% | | Autolinks | 4 | 19 | 21.1% | | Raw HTML | 9 | 20 | 45% | | Hard line breaks | 5 | 15 | 33.3% | diff --git a/reports/external-corpus.json b/reports/external-corpus.json index 15d3d08..cbfa23b 100644 --- a/reports/external-corpus.json +++ b/reports/external-corpus.json @@ -1,8 +1,8 @@ { - "generatedAt": "2026-07-22T18:11:02.179Z", + "generatedAt": "2026-09-11T18:23:48.314Z", "name": "Representative external docs and blogs corpus", "roots": 20, - "durationMs": 13442.426666000001, + "durationMs": 15875.995333, "totals": { "name": "Representative external docs and blogs corpus", "bytes": 87469380, @@ -11,10 +11,10 @@ "skipped": 0, "errors": 0, "comparisons": { - "exact": 5296, - "serialization": 600, - "structure": 1857, - "content": 2764 + "exact": 5384, + "serialization": 626, + "structure": 1876, + "content": 2631 }, "features": { "atx-heading": 7353, @@ -121,10 +121,10 @@ "skipped": 0, "errors": 0, "comparisons": { - "exact": 149, + "exact": 150, "serialization": 12, "structure": 46, - "content": 20 + "content": 19 }, "features": { "atx-heading": 218, @@ -263,9 +263,9 @@ "skipped": 0, "errors": 0, "comparisons": { - "exact": 185, + "exact": 187, "serialization": 19, - "structure": 52, + "structure": 50, "content": 93 }, "features": { @@ -298,9 +298,9 @@ "skipped": 0, "errors": 0, "comparisons": { - "exact": 40, + "exact": 42, "serialization": 1, - "structure": 13, + "structure": 11, "content": 5 }, "features": { @@ -328,10 +328,10 @@ "skipped": 0, "errors": 0, "comparisons": { - "exact": 2045, - "serialization": 71, - "structure": 262, - "content": 1345 + "exact": 2080, + "serialization": 72, + "structure": 245, + "content": 1326 }, "features": { "atx-heading": 2737, @@ -369,10 +369,10 @@ "skipped": 0, "errors": 0, "comparisons": { - "exact": 14, - "serialization": 1, - "structure": 39, - "content": 26 + "exact": 16, + "serialization": 0, + "structure": 47, + "content": 17 }, "features": { "angle-autolink": 10, @@ -401,10 +401,10 @@ "skipped": 0, "errors": 0, "comparisons": { - "exact": 1249, - "serialization": 226, - "structure": 705, - "content": 250 + "exact": 1280, + "serialization": 238, + "structure": 729, + "content": 183 }, "features": { "atx-heading": 1526, @@ -445,10 +445,10 @@ "skipped": 0, "errors": 0, "comparisons": { - "exact": 271, - "serialization": 24, - "structure": 72, - "content": 387 + "exact": 273, + "serialization": 25, + "structure": 71, + "content": 385 }, "features": { "atx-heading": 653, @@ -516,8 +516,8 @@ "skipped": 0, "errors": 0, "comparisons": { - "exact": 96, - "serialization": 14, + "exact": 97, + "serialization": 13, "structure": 18, "content": 7 }, @@ -551,9 +551,9 @@ "skipped": 0, "errors": 0, "comparisons": { - "exact": 86, + "exact": 87, "serialization": 5, - "structure": 26, + "structure": 25, "content": 45 }, "features": { @@ -587,8 +587,8 @@ "comparisons": { "exact": 245, "serialization": 3, - "structure": 106, - "content": 7 + "structure": 107, + "content": 6 }, "features": { "atx-heading": 229, @@ -620,10 +620,10 @@ "skipped": 0, "errors": 0, "comparisons": { - "exact": 625, - "serialization": 80, - "structure": 73, - "content": 215 + "exact": 632, + "serialization": 82, + "structure": 69, + "content": 210 }, "features": { "atx-heading": 309, @@ -656,10 +656,10 @@ "skipped": 0, "errors": 0, "comparisons": { - "exact": 111, - "serialization": 107, - "structure": 333, - "content": 192 + "exact": 115, + "serialization": 119, + "structure": 346, + "content": 163 }, "features": { "atx-heading": 547, @@ -752,7 +752,7 @@ "actual": "...pression matching. Popular Envoy-based Gateway API implementations such as Istio1, Envoy Gateway, and Kgateway do a full case-sensitive match. Thus, if you are unaware that Ingress-NGINX patterns are prefix and case-insensitive, and, unbekn...", "reference": "...pression matching. Popular Envoy-based Gateway API implementations such as Istio[^1], Envoy Gateway, and Kgateway do a full case-sensitive match. Thus, if you are unaware that Ingress-NGINX patterns are prefix and case-insensitive, and, unb..." }, - "durationMs": 2.2245419999999285 + "durationMs": 2.916333000001032 }, { "repository": "rust-blog", @@ -771,7 +771,7 @@ "actual": "... fixes have been made since the original post, the text is otherwise unaltered. 1 As is noted in the summary, the next steps are to take the findings we have so far and begin crafting proposals for how the Rust project will be governed in t...", "reference": "... fixes have been made since the original post, the text is otherwise unaltered. [^1] As is noted in the summary, the next steps are to take the findings we have so far and begin crafting proposals for how the Rust project will be governed i..." }, - "durationMs": 2.8050830000011047 + "durationMs": 2.4650000000001455 }, { "repository": "overreacted", @@ -788,7 +788,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 11.481458999999631 + "durationMs": 9.637832999999773 }, { "repository": "github-docs", @@ -802,22 +802,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 4.861625000000458 - }, - { - "repository": "overreacted", - "path": "public/a-lean-syntax-primer/index.md", - "kind": "other", - "format": "md", - "bytes": 36460, - "features": [ - "atx-heading", - "code-metadata", - "fenced-code", - "frontmatter" - ], - "comparison": "structure", - "durationMs": 7.067332999999962 + "durationMs": 3.5552920000000086 }, { "repository": "deno-docs", @@ -833,7 +818,7 @@ "table" ], "comparison": "structure", - "durationMs": 5.566790999999284 + "durationMs": 6.1148749999993015 }, { "repository": "kubernetes-docs", @@ -848,7 +833,7 @@ "table" ], "comparison": "structure", - "durationMs": 6.019666000000143 + "durationMs": 6.132708000000093 }, { "repository": "typescript-docs", @@ -862,7 +847,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 3.929666999998517 + "durationMs": 3.9006250000002183 }, { "repository": "overreacted", @@ -877,7 +862,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 4.029667000000245 + "durationMs": 3.1699579999999514 }, { "repository": "docker-docs", @@ -892,7 +877,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 5.502709000000323 + "durationMs": 7.430958999999348 }, { "repository": "kubernetes-docs", @@ -906,7 +891,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 4.599500000000262 + "durationMs": 3.8100000000013097 }, { "repository": "kubernetes-docs", @@ -920,7 +905,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 3.0507079999997586 + "durationMs": 3.2704160000002958 }, { "repository": "github-docs", @@ -933,7 +918,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 3.313333000000057 + "durationMs": 2.660041000000092 }, { "repository": "kubernetes-docs", @@ -950,7 +935,7 @@ "table" ], "comparison": "structure", - "durationMs": 2.899999999999636 + "durationMs": 2.688415999999961 }, { "repository": "overreacted", @@ -965,7 +950,7 @@ "table" ], "comparison": "structure", - "durationMs": 3.9387499999998 + "durationMs": 3.338999999999942 }, { "repository": "docker-docs", @@ -981,7 +966,7 @@ "table" ], "comparison": "structure", - "durationMs": 5.359749999999622 + "durationMs": 5.433875000000626 }, { "repository": "pnpm-site", @@ -999,7 +984,7 @@ "table" ], "comparison": "structure", - "durationMs": 3.3477079999993293 + "durationMs": 5.317375000000538 }, { "repository": "typescript-docs", @@ -1013,7 +998,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 3.0200409999997646 + "durationMs": 3.043875000001208 }, { "repository": "deno-docs", @@ -1027,7 +1012,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 3.128541000000041 + "durationMs": 2.9512500000000728 }, { "repository": "deno-docs", @@ -1042,7 +1027,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 2.4671670000006998 + "durationMs": 2.506875000000946 }, { "repository": "vite", @@ -1057,7 +1042,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 4.630416999999994 + "durationMs": 3.6188749999999743 }, { "repository": "overreacted", @@ -1072,7 +1057,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 3.2066670000003796 + "durationMs": 2.6689590000000862 }, { "repository": "deno-docs", @@ -1087,7 +1072,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 2.9343749999989086 + "durationMs": 2.8883330000007845 }, { "repository": "kubernetes-docs", @@ -1100,7 +1085,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 2.0450000000000728 + "durationMs": 1.6490420000000086 }, { "repository": "kubernetes-docs", @@ -1113,7 +1098,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 2.2767909999993208 + "durationMs": 3.1517499999999927 }, { "repository": "deno-docs", @@ -1126,7 +1111,7 @@ "hard-break" ], "comparison": "structure", - "durationMs": 2.294541000001118 + "durationMs": 2.0752080000002024 }, { "repository": "deno-docs", @@ -1142,7 +1127,7 @@ "table" ], "comparison": "structure", - "durationMs": 2.3195830000004207 + "durationMs": 2.9173329999994166 }, { "repository": "kubernetes-docs", @@ -1156,7 +1141,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 2.634208000000399 + "durationMs": 2.2432920000001104 }, { "repository": "kubernetes-docs", @@ -1171,7 +1156,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 1.9814160000005359 + "durationMs": 1.8175419999988662 }, { "repository": "kubernetes-docs", @@ -1186,7 +1171,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 2.1583330000003116 + "durationMs": 1.9119170000003578 }, { "repository": "docker-docs", @@ -1200,7 +1185,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.7044589999986783 + "durationMs": 1.639791999999943 }, { "repository": "rust-blog", @@ -1213,7 +1198,7 @@ "fenced-code" ], "comparison": "structure", - "durationMs": 2.3832500000007713 + "durationMs": 2.6714580000007118 }, { "repository": "rust-blog", @@ -1227,7 +1212,7 @@ "reference-link" ], "comparison": "structure", - "durationMs": 2.0585420000006707 + "durationMs": 3.0761249999995925 }, { "repository": "kubernetes-docs", @@ -1240,21 +1225,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.4957500000000437 - }, - { - "repository": "overreacted", - "path": "public/how-to-fix-any-bug/index.md", - "kind": "other", - "format": "md", - "bytes": 14929, - "features": [ - "atx-heading", - "fenced-code", - "frontmatter" - ], - "comparison": "structure", - "durationMs": 2.572708000000148 + "durationMs": 1.5136249999995925 }, { "repository": "github-docs", @@ -1267,7 +1238,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.4981250000000728 + "durationMs": 1.147791999999754 }, { "repository": "kubernetes-docs", @@ -1281,7 +1252,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.6923750000005384 + "durationMs": 2.840458000000581 }, { "repository": "deno-docs", @@ -1296,7 +1267,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 2.0724580000005517 + "durationMs": 2.4970419999990554 }, { "repository": "kubernetes-docs", @@ -1309,7 +1280,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.9647910000003321 + "durationMs": 1.5909590000001117 }, { "repository": "overreacted", @@ -1324,7 +1295,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 2.547917000000325 + "durationMs": 2.5851659999998446 }, { "repository": "rust-blog", @@ -1337,7 +1308,7 @@ "blockquote" ], "comparison": "structure", - "durationMs": 2.4885839999988093 + "durationMs": 3.459416000001511 }, { "repository": "kubernetes-docs", @@ -1350,7 +1321,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.7772919999997612 + "durationMs": 1.680000000000291 }, { "repository": "kubernetes-docs", @@ -1366,7 +1337,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 1.8238750000000437 + "durationMs": 1.9991250000002765 }, { "repository": "kubernetes-docs", @@ -1379,21 +1350,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.828750000000582 - }, - { - "repository": "svelte-dev", - "path": "apps/svelte.dev/content/docs/kit/60-appendix/30-migrating-to-sveltekit-2.md", - "kind": "docs", - "format": "md", - "bytes": 13233, - "features": [ - "atx-heading", - "fenced-code", - "frontmatter" - ], - "comparison": "structure", - "durationMs": 2.0671250000000327 + "durationMs": 1.6140000000004875 }, { "repository": "kubernetes-docs", @@ -1408,7 +1365,7 @@ "table" ], "comparison": "structure", - "durationMs": 1.500915999999961 + "durationMs": 1.2834590000002208 }, { "repository": "kubernetes-docs", @@ -1421,7 +1378,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.950832999999875 + "durationMs": 1.545124999998734 }, { "repository": "deno-docs", @@ -1438,7 +1395,7 @@ "table" ], "comparison": "structure", - "durationMs": 2.3212080000012065 + "durationMs": 2.4031660000000556 }, { "repository": "deno-docs", @@ -1453,7 +1410,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.6402080000007118 + "durationMs": 2.9570000000003347 }, { "repository": "kubernetes-docs", @@ -1467,7 +1424,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.3635839999997188 + "durationMs": 1.7811670000000959 }, { "repository": "kubernetes-docs", @@ -1481,7 +1438,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.683999999999287 + "durationMs": 2.119666999999936 }, { "repository": "kubernetes-docs", @@ -1496,7 +1453,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 1.6124170000002778 + "durationMs": 1.4283329999998386 }, { "repository": "github-docs", @@ -1510,7 +1467,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 1.8286669999997684 + "durationMs": 1.5787499999996726 }, { "repository": "kubernetes-docs", @@ -1523,7 +1480,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.6356660000001284 + "durationMs": 1.5308749999994689 }, { "repository": "rust-blog", @@ -1536,7 +1493,7 @@ "blockquote" ], "comparison": "structure", - "durationMs": 1.868625000000975 + "durationMs": 1.8179159999999683 }, { "repository": "kubernetes-docs", @@ -1550,7 +1507,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.5516660000002958 + "durationMs": 1.6083749999997963 }, { "repository": "kubernetes-docs", @@ -1563,7 +1520,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.8036249999995562 + "durationMs": 1.2049580000002607 }, { "repository": "kubernetes-docs", @@ -1577,7 +1534,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.3873329999996713 + "durationMs": 1.6752500000002328 }, { "repository": "kubernetes-docs", @@ -1592,7 +1549,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 1.743916999999783 + "durationMs": 1.8719579999997222 }, { "repository": "kubernetes-docs", @@ -1605,7 +1562,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.5467090000001917 + "durationMs": 1.7269589999996242 }, { "repository": "kubernetes-docs", @@ -1618,7 +1575,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.5850000000000364 + "durationMs": 1.5285830000011629 }, { "repository": "kubernetes-docs", @@ -1632,7 +1589,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.687249999999949 + "durationMs": 1.079874999999447 }, { "repository": "kubernetes-docs", @@ -1646,7 +1603,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.5474999999996726 + "durationMs": 1.3955839999998716 }, { "repository": "svelte-dev", @@ -1659,7 +1616,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 2.270958000000064 + "durationMs": 2.1256250000001273 }, { "repository": "kubernetes-docs", @@ -1674,7 +1631,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 1.3856249999998909 + "durationMs": 1.4412090000005264 }, { "repository": "kubernetes-docs", @@ -1688,7 +1645,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.4268750000001091 + "durationMs": 1.080207999999402 }, { "repository": "kubernetes-docs", @@ -1701,7 +1658,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.3669589999999516 + "durationMs": 1.0621250000003783 }, { "repository": "svelte-dev", @@ -1714,7 +1671,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 2.134832999999844 + "durationMs": 2.108165999999983 }, { "repository": "kubernetes-docs", @@ -1728,7 +1685,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.3659580000003189 + "durationMs": 1.2033750000009604 }, { "repository": "kubernetes-docs", @@ -1743,7 +1700,7 @@ "table" ], "comparison": "structure", - "durationMs": 1.6466669999999795 + "durationMs": 1.5403749999995853 }, { "repository": "kubernetes-docs", @@ -1758,7 +1715,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 1.7067919999999503 + "durationMs": 1.6869580000002316 }, { "repository": "svelte-dev", @@ -1772,21 +1729,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 2.2020840000000135 - }, - { - "repository": "kubernetes-docs", - "path": "content/en/blog/_posts/2018/kubedirector.md", - "kind": "blog", - "format": "md", - "bytes": 10749, - "features": [ - "atx-heading", - "fenced-code", - "frontmatter" - ], - "comparison": "structure", - "durationMs": 1.125583000000006 + "durationMs": 2.195417000000134 }, { "repository": "deno-docs", @@ -1801,7 +1744,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.1272079999998823 + "durationMs": 1.2545829999999114 }, { "repository": "deno-docs", @@ -1816,7 +1759,7 @@ "table" ], "comparison": "structure", - "durationMs": 1.789958999999726 + "durationMs": 3.1370420000002923 }, { "repository": "svelte-dev", @@ -1830,7 +1773,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 2.1802500000001146 + "durationMs": 2.0390420000001086 }, { "repository": "kubernetes-docs", @@ -1844,7 +1787,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.1205840000002354 + "durationMs": 0.906290999999328 }, { "repository": "svelte-dev", @@ -1858,7 +1801,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 2.2447919999999613 + "durationMs": 2.5510830000000624 }, { "repository": "kubernetes-docs", @@ -1873,7 +1816,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 1.4435000000003129 + "durationMs": 1.345166999999492 }, { "repository": "kubernetes-docs", @@ -1886,7 +1829,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.154457999999977 + "durationMs": 0.9491250000000946 }, { "repository": "deno-docs", @@ -1902,7 +1845,7 @@ "table" ], "comparison": "structure", - "durationMs": 1.3920410000009724 + "durationMs": 2.626333000000159 }, { "repository": "svelte-dev", @@ -1915,7 +1858,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 2.042208999999957 + "durationMs": 1.8280830000003334 }, { "repository": "svelte-dev", @@ -1929,7 +1872,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 2.0688749999999345 + "durationMs": 1.9007500000000164 }, { "repository": "kubernetes-docs", @@ -1944,7 +1887,7 @@ "table" ], "comparison": "structure", - "durationMs": 1.158792000000176 + "durationMs": 1.2182920000004742 }, { "repository": "svelte-dev", @@ -1958,7 +1901,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 1.9784159999999247 + "durationMs": 1.7927499999998417 }, { "repository": "svelte-dev", @@ -1972,7 +1915,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 2.0502080000001115 + "durationMs": 1.9733329999999114 }, { "repository": "docker-docs", @@ -1987,7 +1930,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 1.8656250000003638 + "durationMs": 1.8537080000005517 }, { "repository": "kubernetes-docs", @@ -2000,7 +1943,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.205500000000029 + "durationMs": 0.9855829999996786 }, { "repository": "docker-docs", @@ -2014,7 +1957,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.284750000000713 + "durationMs": 1.3032920000005106 }, { "repository": "kubernetes-docs", @@ -2028,7 +1971,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.2829160000001139 + "durationMs": 1.1472080000003189 }, { "repository": "svelte-dev", @@ -2042,7 +1985,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 2.0405000000000655 + "durationMs": 1.7943330000000515 }, { "repository": "deno-docs", @@ -2057,7 +2000,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.642874999999549 + "durationMs": 1.652249999999185 }, { "repository": "kubernetes-docs", @@ -2071,7 +2014,7 @@ "table" ], "comparison": "structure", - "durationMs": 1.0889170000000377 + "durationMs": 0.9461249999994834 }, { "repository": "svelte-dev", @@ -2085,7 +2028,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 1.9113749999999072 + "durationMs": 1.8976669999999558 }, { "repository": "kubernetes-docs", @@ -2098,7 +2041,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.2163329999993948 + "durationMs": 1.4656670000003942 }, { "repository": "docker-docs", @@ -2113,7 +2056,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.4286659999997937 + "durationMs": 1.4337500000001455 }, { "repository": "kubernetes-docs", @@ -2127,7 +2070,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.0129170000000158 + "durationMs": 1.0808749999996508 }, { "repository": "kubernetes-docs", @@ -2140,7 +2083,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.3097920000000158 + "durationMs": 1.2083339999999225 }, { "repository": "kubernetes-docs", @@ -2154,7 +2097,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 1.5318749999996726 + "durationMs": 1.765082999999322 }, { "repository": "deno-docs", @@ -2170,7 +2113,68 @@ "table" ], "comparison": "structure", - "durationMs": 1.4168339999996533 + "durationMs": 1.9496669999989535 + }, + { + "repository": "kubernetes-docs", + "path": "content/en/docs/tasks/access-application-cluster/access-cluster.md", + "kind": "docs", + "format": "md", + "bytes": 9278, + "features": [ + "atx-heading", + "fenced-code", + "frontmatter", + "nested-list" + ], + "comparison": "structure", + "durationMs": 1.50091699999939 + }, + { + "repository": "deno-docs", + "path": "runtime/reference/jsx.md", + "kind": "other", + "format": "md", + "bytes": 9145, + "features": [ + "atx-heading", + "code-metadata", + "fenced-code", + "frontmatter", + "table" + ], + "comparison": "structure", + "durationMs": 2.116582999999082 + }, + { + "repository": "deno-docs", + "path": "runtime/reference/cli/unstable_flags.md", + "kind": "other", + "format": "md", + "bytes": 9104, + "features": [ + "atx-heading", + "code-metadata", + "fenced-code", + "frontmatter" + ], + "comparison": "structure", + "durationMs": 1.528041999999914 + }, + { + "repository": "deno-docs", + "path": "deploy/getting_started.md", + "kind": "other", + "format": "md", + "bytes": 9074, + "features": [ + "atx-heading", + "blockquote", + "frontmatter", + "nested-list" + ], + "comparison": "structure", + "durationMs": 1.7729589999999007 } ] } diff --git a/reports/external-corpus.md b/reports/external-corpus.md index 8a4d365..8026979 100644 --- a/reports/external-corpus.md +++ b/reports/external-corpus.md @@ -1,6 +1,6 @@ # Representative external docs and blogs corpus -Generated: 2026-07-22T18:11:02.179Z +Generated: 2026-09-11T18:23:48.314Z This report measures practical corpus behavior. Marked is a differential reference, not a correctness oracle. MDX is inventoried but not parsed. @@ -11,16 +11,16 @@ This report measures practical corpus behavior. Marked is a differential referen - parse or determinism errors: 0 - content differences using only profile syntax: 2 - unexplained target docs/blog/README content differences using only profile syntax: 0 -- audit time: 13442.4 ms +- audit time: 15876.0 ms ## Output comparison | Result | Documents | Percent | Meaning | | :--- | ---: | ---: | :--- | -| exact | 5296 | 50.4% | Normalized HTML matches Marked | -| serialization | 600 | 5.7% | Tag and text shape matches; attributes or serialization differ | -| structure | 1857 | 17.7% | Text matches; element structure differs | -| content | 2764 | 26.3% | Rendered text differs and requires triage | +| exact | 5384 | 51.2% | Normalized HTML matches Marked | +| serialization | 626 | 6.0% | Tag and text shape matches; attributes or serialization differ | +| structure | 1876 | 17.8% | Text matches; element structure differs | +| content | 2631 | 25.0% | Rendered text differs and requires triage | ## Repositories @@ -28,23 +28,23 @@ This report measures practical corpus behavior. Marked is a differential referen | :--- | :--- | :---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | | [vite](https://github.com/vitejs/vite) | [428bda3b](https://github.com/vitejs/vite/tree/428bda3b98886708006f4f2f409095f89d705df1) | no | 59 | 0 | 39 | 5 | 11 | 4 | 0 | | [vue-docs](https://github.com/vuejs/docs) | [01e098a9](https://github.com/vuejs/docs/tree/01e098a967951ea92d09b100b8e7d82c7a3369a2) | no | 120 | 0 | 75 | 20 | 19 | 6 | 0 | -| [react-dev](https://github.com/reactjs/react.dev) | [7b6c3ceb](https://github.com/reactjs/react.dev/tree/7b6c3ceb9dd97249e9dce4a8a94e61aed6424698) | no | 227 | 0 | 149 | 12 | 46 | 20 | 0 | +| [react-dev](https://github.com/reactjs/react.dev) | [7b6c3ceb](https://github.com/reactjs/react.dev/tree/7b6c3ceb9dd97249e9dce4a8a94e61aed6424698) | no | 227 | 0 | 150 | 12 | 46 | 19 | 0 | | [rust-book](https://github.com/rust-lang/book) | [91754488](https://github.com/rust-lang/book/tree/917544888a55e4da7109bdba8c88c893c0da70f4) | no | 117 | 0 | 29 | 11 | 68 | 9 | 0 | | [docusaurus](https://github.com/facebook/docusaurus) | [a0bc3221](https://github.com/facebook/docusaurus/tree/a0bc32214436d52a5ac9de9be1a515d872987366) | no | 7 | 123 | 2 | 0 | 4 | 1 | 0 | | [astro-docs](https://github.com/withastro/docs) | [c316ac57](https://github.com/withastro/docs/tree/c316ac570277bd5abd36a237c97adad8cede0c1b) | no | 2 | 417 | 0 | 0 | 2 | 0 | 0 | | [tailwind-site](https://github.com/tailwindlabs/tailwindcss.com) | [1e700c43](https://github.com/tailwindlabs/tailwindcss.com/tree/1e700c43f5f270a1a55c4a33e71f01952f24b8c2) | no | 1 | 262 | 1 | 0 | 0 | 0 | 0 | -| [svelte-dev](https://github.com/sveltejs/svelte.dev) | [2f9fc2a2](https://github.com/sveltejs/svelte.dev/tree/2f9fc2a2431dd74b25eb9b6ae5d9051b45635f57) | no | 349 | 0 | 185 | 19 | 52 | 93 | 0 | -| [overreacted](https://github.com/gaearon/overreacted.io) | [ac1ad4fe](https://github.com/gaearon/overreacted.io/tree/ac1ad4fe9168b350d3dc59ac0cbe0d53405702bf) | no | 59 | 0 | 40 | 1 | 13 | 5 | 0 | -| [github-docs](https://github.com/github/docs) | [f455adb9](https://github.com/github/docs/tree/f455adb9ecb48ec7212177af292758bb2f32c1d7) | no | 3723 | 0 | 2045 | 71 | 262 | 1345 | 0 | -| [node-docs](https://github.com/nodejs/node) | [2deb0a1b](https://github.com/nodejs/node/tree/2deb0a1b0f542c4b7fedc16dda1149c65e546f0d) | no | 80 | 0 | 14 | 1 | 39 | 26 | 0 | -| [kubernetes-docs](https://github.com/kubernetes/website) | [f2987ba1](https://github.com/kubernetes/website/tree/f2987ba1cceaa85fcd44cd1a221010d745d7335c) | no | 2430 | 0 | 1249 | 226 | 705 | 250 | 0 | -| [docker-docs](https://github.com/docker/docs) | [d19a384f](https://github.com/docker/docs/tree/d19a384f8dd3b16ab3a4b5f3885b398e76f8638c) | no | 754 | 0 | 271 | 24 | 72 | 387 | 0 | +| [svelte-dev](https://github.com/sveltejs/svelte.dev) | [2f9fc2a2](https://github.com/sveltejs/svelte.dev/tree/2f9fc2a2431dd74b25eb9b6ae5d9051b45635f57) | no | 349 | 0 | 187 | 19 | 50 | 93 | 0 | +| [overreacted](https://github.com/gaearon/overreacted.io) | [ac1ad4fe](https://github.com/gaearon/overreacted.io/tree/ac1ad4fe9168b350d3dc59ac0cbe0d53405702bf) | no | 59 | 0 | 42 | 1 | 11 | 5 | 0 | +| [github-docs](https://github.com/github/docs) | [f455adb9](https://github.com/github/docs/tree/f455adb9ecb48ec7212177af292758bb2f32c1d7) | no | 3723 | 0 | 2080 | 72 | 245 | 1326 | 0 | +| [node-docs](https://github.com/nodejs/node) | [2deb0a1b](https://github.com/nodejs/node/tree/2deb0a1b0f542c4b7fedc16dda1149c65e546f0d) | no | 80 | 0 | 16 | 0 | 47 | 17 | 0 | +| [kubernetes-docs](https://github.com/kubernetes/website) | [f2987ba1](https://github.com/kubernetes/website/tree/f2987ba1cceaa85fcd44cd1a221010d745d7335c) | no | 2430 | 0 | 1280 | 238 | 729 | 183 | 0 | +| [docker-docs](https://github.com/docker/docs) | [d19a384f](https://github.com/docker/docs/tree/d19a384f8dd3b16ab3a4b5f3885b398e76f8638c) | no | 754 | 0 | 273 | 25 | 71 | 385 | 0 | | [laravel-docs](https://github.com/laravel/docs) | [ce4a1bf0](https://github.com/laravel/docs/tree/ce4a1bf093c2c09e3a029090136d6bea88b07d48) | no | 104 | 0 | 2 | 1 | 0 | 101 | 0 | -| [typescript-docs](https://github.com/microsoft/TypeScript-Website) | [c8170c35](https://github.com/microsoft/TypeScript-Website/tree/c8170c35bda4811c9516cbb69c39241ae4beb6d9) | no | 135 | 0 | 96 | 14 | 18 | 7 | 0 | -| [pnpm-site](https://github.com/pnpm/pnpm.io) | [ff58b983](https://github.com/pnpm/pnpm.io/tree/ff58b98362334807536e91e38ed66d0f14a87576) | no | 162 | 9 | 86 | 5 | 26 | 45 | 0 | -| [deno-docs](https://github.com/denoland/docs) | [bcefb75f](https://github.com/denoland/docs/tree/bcefb75f3e49e19af0ac9d79b436df346a3b477b) | no | 361 | 2 | 245 | 3 | 106 | 7 | 0 | -| [hugo-docs](https://github.com/gohugoio/hugoDocs) | [620696ab](https://github.com/gohugoio/hugoDocs/tree/620696ab3b07f66262e860e84b3793946d1660bc) | no | 993 | 0 | 625 | 80 | 73 | 215 | 0 | -| [rust-blog](https://github.com/rust-lang/blog.rust-lang.org) | [f35e1174](https://github.com/rust-lang/blog.rust-lang.org/tree/f35e1174d2c15be7b692863b874a8a0046722081) | no | 743 | 0 | 111 | 107 | 333 | 192 | 0 | +| [typescript-docs](https://github.com/microsoft/TypeScript-Website) | [c8170c35](https://github.com/microsoft/TypeScript-Website/tree/c8170c35bda4811c9516cbb69c39241ae4beb6d9) | no | 135 | 0 | 97 | 13 | 18 | 7 | 0 | +| [pnpm-site](https://github.com/pnpm/pnpm.io) | [ff58b983](https://github.com/pnpm/pnpm.io/tree/ff58b98362334807536e91e38ed66d0f14a87576) | no | 162 | 9 | 87 | 5 | 25 | 45 | 0 | +| [deno-docs](https://github.com/denoland/docs) | [bcefb75f](https://github.com/denoland/docs/tree/bcefb75f3e49e19af0ac9d79b436df346a3b477b) | no | 361 | 2 | 245 | 3 | 107 | 6 | 0 | +| [hugo-docs](https://github.com/gohugoio/hugoDocs) | [620696ab](https://github.com/gohugoio/hugoDocs/tree/620696ab3b07f66262e860e84b3793946d1660bc) | no | 993 | 0 | 632 | 82 | 69 | 210 | 0 | +| [rust-blog](https://github.com/rust-lang/blog.rust-lang.org) | [f35e1174](https://github.com/rust-lang/blog.rust-lang.org/tree/f35e1174d2c15be7b692863b874a8a0046722081) | no | 743 | 0 | 115 | 119 | 346 | 163 | 0 | | [mdn-guides](https://github.com/mdn/content) | [3b763f8f](https://github.com/mdn/content/tree/3b763f8f076c053b7a44e261c3a19a1879bc11ff) | no | 91 | 0 | 32 | 0 | 8 | 51 | 0 | ## Content kinds @@ -100,7 +100,6 @@ These are triage leads, ordered by parser errors, content differences, structura | rust-blog | content/inside-rust/governance-update@0.md | docs | content | blockquote, footnote, hard-break, reference-link | | overreacted | public/a-complete-guide-to-useeffect/index.md | other | structure | atx-heading, blockquote, code-metadata, fenced-code, frontmatter, nested-list | | github-docs | content/site-policy/github-terms/github-marketplace-developer-agreement.md | docs | structure | atx-heading, frontmatter, nested-list | -| overreacted | public/a-lean-syntax-primer/index.md | other | structure | atx-heading, code-metadata, fenced-code, frontmatter | | deno-docs | runtime/fundamentals/workspaces.md | other | structure | atx-heading, code-metadata, fenced-code, frontmatter, table | | kubernetes-docs | content/en/blog/_posts/2020/deploying-external-openstack-cloud-provider-with-kubeadm.md | blog | structure | atx-heading, fenced-code, frontmatter, table | | typescript-docs | packages/documentation/copy/en/release-notes/TypeScript 5.6.md | docs | structure | atx-heading, fenced-code, frontmatter | @@ -130,12 +129,13 @@ These are triage leads, ordered by parser errors, content differences, structura | rust-blog | content/Rust-1.87.0/index.md | docs | structure | atx-heading, fenced-code | | rust-blog | content/State-of-Rust-Survey-2016/index.md | docs | structure | atx-heading, blockquote, reference-link | | kubernetes-docs | content/en/blog/_posts/2016/stateful-applications-using-kubernetes-datera.md | blog | structure | fenced-code, frontmatter | -| overreacted | public/how-to-fix-any-bug/index.md | other | structure | atx-heading, fenced-code, frontmatter | | github-docs | content/site-policy/other-site-policies/github-and-trade-controls.md | docs | structure | atx-heading, frontmatter | | kubernetes-docs | content/en/blog/_posts/2018/kubevirt-crds-for-virtualization.md | blog | structure | atx-heading, fenced-code, frontmatter | | deno-docs | runtime/fundamentals/http_server.md | other | structure | atx-heading, code-metadata, fenced-code, frontmatter | | kubernetes-docs | content/en/blog/_posts/2017/multi-stage-canary-deployments-with-kubernetes-in-the-cloud-onprem.md | blog | structure | fenced-code, frontmatter | | overreacted | public/the-math-is-haunted/index.md | other | structure | atx-heading, code-metadata, fenced-code, frontmatter | +| rust-blog | content/Rust-survey-2019/index.md | docs | structure | atx-heading, blockquote | +| kubernetes-docs | content/en/blog/_posts/2025/kubernetes-v1-35-sneak-peek.md | blog | structure | atx-heading, frontmatter | ## Interpretation policy diff --git a/reports/sizes.json b/reports/sizes.json index cc33776..213f8af 100644 --- a/reports/sizes.json +++ b/reports/sizes.json @@ -1,54 +1,54 @@ { - "generatedAt": "2026-08-04T22:30:23.939Z", + "generatedAt": "2026-09-11T18:44:26.815Z", "results": [ { "group": "tanstack", "name": "parser only", - "minBytes": 14083, - "gzipBytes": 4969, - "brotliBytes": 4573 + "minBytes": 13084, + "gzipBytes": 4941, + "brotliBytes": 4555 }, { "group": "tanstack", "name": "html renderer no highlighter", - "minBytes": 19161, - "gzipBytes": 6790, - "brotliBytes": 6211 + "minBytes": 18066, + "gzipBytes": 6730, + "brotliBytes": 6161 }, { "group": "tanstack", "name": "html renderer with external highlighter stub", - "minBytes": 19197, - "gzipBytes": 6811, - "brotliBytes": 6231 + "minBytes": 18102, + "gzipBytes": 6751, + "brotliBytes": 6179 }, { "group": "tanstack", "name": "react adapter", - "minBytes": 19126, - "gzipBytes": 6711, - "brotliBytes": 6165 + "minBytes": 18030, + "gzipBytes": 6648, + "brotliBytes": 6107 }, { "group": "tanstack", "name": "octane adapter", - "minBytes": 19129, - "gzipBytes": 6707, - "brotliBytes": 6171 + "minBytes": 18033, + "gzipBytes": 6649, + "brotliBytes": 6108 }, { "group": "tanstack", "name": "docs extension preset", - "minBytes": 6792, - "gzipBytes": 2398, - "brotliBytes": 2188 + "minBytes": 6423, + "gzipBytes": 2292, + "brotliBytes": 2073 }, { "group": "tanstack", "name": "callouts extension", - "minBytes": 556, - "gzipBytes": 372, - "brotliBytes": 329 + "minBytes": 506, + "gzipBytes": 335, + "brotliBytes": 278 }, { "group": "tanstack", @@ -60,16 +60,16 @@ { "group": "tanstack", "name": "react adapter with streaming extension", - "minBytes": 19817, - "gzipBytes": 6890, - "brotliBytes": 6322 + "minBytes": 18721, + "gzipBytes": 6835, + "brotliBytes": 6282 }, { "group": "tanstack", "name": "tabs transforms", - "minBytes": 3398, - "gzipBytes": 1255, - "brotliBytes": 1103 + "minBytes": 3290, + "gzipBytes": 1221, + "brotliBytes": 1082 }, { "group": "markdown", @@ -112,6 +112,90 @@ "minBytes": 119588, "gzipBytes": 36843, "brotliBytes": 32686 + }, + { + "group": "tanstack-public", + "name": ".", + "minBytes": 18328, + "gzipBytes": 6853, + "brotliBytes": 6262 + }, + { + "group": "tanstack-public", + "name": "./html", + "minBytes": 18289, + "gzipBytes": 6841, + "brotliBytes": 6258 + }, + { + "group": "tanstack-public", + "name": "./parser", + "minBytes": 13216, + "gzipBytes": 5027, + "brotliBytes": 4630 + }, + { + "group": "tanstack-public", + "name": "./react", + "minBytes": 18229, + "gzipBytes": 6756, + "brotliBytes": 6200 + }, + { + "group": "tanstack-public", + "name": "./octane", + "minBytes": 18235, + "gzipBytes": 6756, + "brotliBytes": 6207 + }, + { + "group": "tanstack-public", + "name": "./extensions/callouts", + "minBytes": 660, + "gzipBytes": 432, + "brotliBytes": 360 + }, + { + "group": "tanstack-public", + "name": "./extensions/comment-components", + "minBytes": 1073, + "gzipBytes": 647, + "brotliBytes": 542 + }, + { + "group": "tanstack-public", + "name": "./extensions/docs", + "minBytes": 6587, + "gzipBytes": 2392, + "brotliBytes": 2162 + }, + { + "group": "tanstack-public", + "name": "./extensions/framework", + "minBytes": 1470, + "gzipBytes": 734, + "brotliBytes": 630 + }, + { + "group": "tanstack-public", + "name": "./extensions/headings", + "minBytes": 1038, + "gzipBytes": 573, + "brotliBytes": 480 + }, + { + "group": "tanstack-public", + "name": "./extensions/streaming", + "minBytes": 838, + "gzipBytes": 403, + "brotliBytes": 334 + }, + { + "group": "tanstack-public", + "name": "./extensions/tabs", + "minBytes": 3537, + "gzipBytes": 1338, + "brotliBytes": 1185 } ] } \ No newline at end of file diff --git a/reports/sizes.md b/reports/sizes.md index b1a9bf1..f9c8e9c 100644 --- a/reports/sizes.md +++ b/reports/sizes.md @@ -1,24 +1,36 @@ # Bundle Size Results -Generated: 2026-08-04T22:30:23.940Z +Generated: 2026-09-11T18:44:26.816Z 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 | 14083 | 4969 | 4573 | -| tanstack | html renderer no highlighter | 19161 | 6790 | 6211 | -| tanstack | html renderer with external highlighter stub | 19197 | 6811 | 6231 | -| tanstack | react adapter | 19126 | 6711 | 6165 | -| tanstack | octane adapter | 19129 | 6707 | 6171 | -| tanstack | docs extension preset | 6792 | 2398 | 2188 | -| tanstack | callouts extension | 556 | 372 | 329 | +| tanstack | parser only | 13084 | 4941 | 4555 | +| tanstack | html renderer no highlighter | 18066 | 6730 | 6161 | +| tanstack | html renderer with external highlighter stub | 18102 | 6751 | 6179 | +| tanstack | react adapter | 18030 | 6648 | 6107 | +| tanstack | octane adapter | 18033 | 6649 | 6108 | +| 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 | 19817 | 6890 | 6322 | -| tanstack | tabs transforms | 3398 | 1255 | 1103 | +| tanstack | react adapter with streaming extension | 18721 | 6835 | 6282 | +| tanstack | tabs transforms | 3290 | 1221 | 1082 | | markdown | marked | 41415 | 12548 | 11509 | | markdown | markdown-it | 148242 | 52655 | 44023 | | markdown | micromark | 53283 | 15420 | 13712 | | markdown | commonmark | 159687 | 48084 | 39793 | | markdown | markdown-wasm browser js+wasm | 66387 | 31275 | 26431 | | markdown | unified remark+rehype | 119588 | 36843 | 32686 | +| tanstack-public | . | 18328 | 6853 | 6262 | +| tanstack-public | ./html | 18289 | 6841 | 6258 | +| tanstack-public | ./parser | 13216 | 5027 | 4630 | +| tanstack-public | ./react | 18229 | 6756 | 6200 | +| tanstack-public | ./octane | 18235 | 6756 | 6207 | +| tanstack-public | ./extensions/callouts | 660 | 432 | 360 | +| tanstack-public | ./extensions/comment-components | 1073 | 647 | 542 | +| tanstack-public | ./extensions/docs | 6587 | 2392 | 2162 | +| tanstack-public | ./extensions/framework | 1470 | 734 | 630 | +| tanstack-public | ./extensions/headings | 1038 | 573 | 480 | +| tanstack-public | ./extensions/streaming | 838 | 403 | 334 | +| tanstack-public | ./extensions/tabs | 3537 | 1338 | 1185 | diff --git a/reports/tanstack-corpus.json b/reports/tanstack-corpus.json index 05be84b..3e64dd0 100644 --- a/reports/tanstack-corpus.json +++ b/reports/tanstack-corpus.json @@ -1,86 +1,87 @@ { - "generatedAt": "2026-07-22T18:10:53.920Z", + "generatedAt": "2026-09-11T18:24:03.577Z", "name": "TanStack repository corpus", - "roots": 25, - "durationMs": 4740.735291999999, + "roots": 26, + "durationMs": 8920.839249999999, "totals": { "name": "TanStack repository corpus", - "bytes": 18378547, - "markdown": 4826, + "bytes": 32642258, + "markdown": 6308, "mdx": 2, - "skipped": 7, + "skipped": 696, "errors": 0, "comparisons": { - "exact": 3582, - "serialization": 287, - "structure": 667, - "content": 290 + "exact": 4996, + "serialization": 189, + "structure": 753, + "content": 370 }, "features": { - "frontmatter": 2922, - "atx-heading": 4453, - "task-list": 79, - "fenced-code": 3060, - "nested-list": 424, - "combined-emphasis": 7, - "delimiter-edge": 41, - "literal-autolink": 392, + "atx-heading": 5947, + "frontmatter": 4898, + "four-space-indent": 316, + "nested-list": 485, + "fenced-code": 5055, + "raw-html": 1236, + "table": 647, + "blockquote": 851, + "literal-autolink": 516, + "delimiter-edge": 111, + "task-list": 101, + "combined-emphasis": 38, "reference-link": 165, - "four-space-indent": 203, - "raw-html": 661, - "tab-indentation": 48, - "blockquote": 498, - "table": 385, - "setext-heading": 2, - "strikethrough": 39, - "callout": 212, - "entity-reference": 10, - "site-template": 9, - "comment-component": 60, + "comment-component": 131, + "code-metadata": 151, + "angle-autolink": 6, + "callout": 253, + "strikethrough": 80, + "entity-reference": 11, + "tab-indentation": 49, + "site-template": 6, "hard-break": 2, "fence-info-edge": 3, - "code-metadata": 40, "multiline-link": 1, "section-marker": 146, - "angle-autolink": 4, "footnote": 3 } }, "repositories": [ { "name": "ai", - "bytes": 1190587, - "markdown": 323, + "bytes": 11212636, + "markdown": 941, "mdx": 0, "skipped": 0, "errors": 0, "comparisons": { - "exact": 242, - "serialization": 23, - "structure": 58, - "content": 0 + "exact": 697, + "serialization": 26, + "structure": 152, + "content": 66 }, "features": { - "frontmatter": 252, - "atx-heading": 320, + "atx-heading": 932, + "frontmatter": 729, + "four-space-indent": 132, + "nested-list": 98, + "fenced-code": 855, + "raw-html": 169, + "table": 220, + "blockquote": 125, + "literal-autolink": 74, + "delimiter-edge": 68, "task-list": 1, - "fenced-code": 287, - "nested-list": 35, - "combined-emphasis": 2, - "delimiter-edge": 5, - "literal-autolink": 27, + "combined-emphasis": 35, "reference-link": 1, - "four-space-indent": 20, - "raw-html": 52, - "tab-indentation": 17, - "blockquote": 15, - "table": 21, - "setext-heading": 1, - "strikethrough": 1 + "comment-component": 63, + "code-metadata": 99, + "angle-autolink": 2, + "callout": 1, + "strikethrough": 9, + "entity-reference": 2 }, - "dirty": true, "remote": "https://github.com/tanstack/ai", - "revision": "51c66147dd543e0cdd4f35e7970d52bb1cc43add" + "revision": "44a73e0e8790f478d853bf3d843c44f1f501762e" }, { "name": "bling", @@ -108,6 +109,35 @@ "remote": "https://github.com/TanStack/bling.git", "revision": "62703ba3d204a0315ef6043d0f59c5b70c27e73d" }, + { + "name": "charts", + "bytes": 1591931, + "markdown": 205, + "mdx": 0, + "skipped": 0, + "errors": 0, + "comparisons": { + "exact": 187, + "serialization": 5, + "structure": 12, + "content": 1 + }, + "features": { + "atx-heading": 205, + "table": 119, + "blockquote": 74, + "fenced-code": 159, + "four-space-indent": 4, + "nested-list": 1, + "callout": 1, + "literal-autolink": 73, + "raw-html": 63, + "frontmatter": 162 + }, + "dirty": true, + "remote": "https://github.com/TanStack/charts.git", + "revision": "dc4bc708bac2e90d9669e8bf139dda97ad10603d" + }, { "name": "cli", "bytes": 472640, @@ -170,37 +200,37 @@ }, { "name": "db", - "bytes": 2481463, - "markdown": 547, + "bytes": 2780781, + "markdown": 606, "mdx": 0, "skipped": 0, "errors": 0, "comparisons": { - "exact": 354, + "exact": 395, "serialization": 14, - "structure": 173, - "content": 6 + "structure": 190, + "content": 7 }, "features": { - "frontmatter": 471, + "frontmatter": 527, "task-list": 2, - "atx-heading": 546, - "literal-autolink": 127, - "blockquote": 9, - "fenced-code": 494, - "four-space-indent": 17, - "raw-html": 124, + "atx-heading": 604, + "literal-autolink": 138, + "blockquote": 10, + "fenced-code": 552, + "callout": 4, + "four-space-indent": 21, + "raw-html": 146, "tab-indentation": 1, "nested-list": 44, - "table": 19, + "table": 20, "delimiter-edge": 3, - "strikethrough": 1, - "callout": 3, + "strikethrough": 3, "entity-reference": 1, "site-template": 1 }, "remote": "https://github.com/tanstack/db", - "revision": "f9b4f34b3eee273bbd956a1a9473fc6af6cad58f" + "revision": "12cbc90a32a121a858051b69798a602cc7ddc43a" }, { "name": "form", @@ -210,9 +240,9 @@ "skipped": 0, "errors": 0, "comparisons": { - "exact": 170, + "exact": 171, "serialization": 3, - "structure": 11, + "structure": 10, "content": 2 }, "features": { @@ -235,25 +265,26 @@ }, { "name": "highlight", - "bytes": 90142, - "markdown": 35, + "bytes": 179750, + "markdown": 51, "mdx": 0, "skipped": 0, "errors": 0, "comparisons": { - "exact": 29, - "serialization": 6, - "structure": 0, + "exact": 39, + "serialization": 7, + "structure": 5, "content": 0 }, "features": { - "atx-heading": 35, - "fenced-code": 29, - "table": 15, - "frontmatter": 32 + "atx-heading": 51, + "fenced-code": 42, + "table": 19, + "frontmatter": 37, + "code-metadata": 5 }, "remote": "https://github.com/TanStack/highlight.git", - "revision": "a8ed3e6c585165c9166e39afd1bd7d835a0ddf95" + "revision": "8aab48b5da618caae7c98728042dbe9cb38bb2d0" }, { "name": "hotkeys", @@ -325,31 +356,35 @@ }, { "name": "markdown", - "bytes": 77469, - "markdown": 35, + "bytes": 210192, + "markdown": 48, "mdx": 0, "skipped": 0, "errors": 0, "comparisons": { - "exact": 25, - "serialization": 6, - "structure": 4, + "exact": 27, + "serialization": 10, + "structure": 11, "content": 0 }, "features": { - "atx-heading": 35, - "fenced-code": 25, - "frontmatter": 27, - "table": 12, - "code-metadata": 2, - "literal-autolink": 1, + "atx-heading": 48, + "fenced-code": 35, + "four-space-indent": 1, + "literal-autolink": 2, + "raw-html": 1, + "frontmatter": 34, + "table": 17, + "code-metadata": 8, + "blockquote": 3, "multiline-link": 1, - "blockquote": 2, - "nested-list": 1 + "nested-list": 1, + "comment-component": 1, + "task-list": 1 }, "dirty": true, "remote": "https://github.com/TanStack/markdown.git", - "revision": "9bbe1220c5b77ca89cadaa41d83f8172dfc1da82" + "revision": "e56d191c0f3229b1d1a640b8c58245c788c6f644" }, { "name": "pacer", @@ -445,9 +480,8 @@ "angle-autolink": 1, "entity-reference": 1 }, - "dirty": true, "remote": "https://github.com/TanStack/query.git", - "revision": "f727aa9bcef3b77b086888583ca04f6a19f6bcb9" + "revision": "8a930246ee250db03e4d8472605e8525238f936f" }, { "name": "ranger", @@ -504,26 +538,26 @@ }, { "name": "redact", - "bytes": 38306, - "markdown": 3, + "bytes": 32212, + "markdown": 5, "mdx": 0, "skipped": 0, "errors": 0, "comparisons": { - "exact": 0, - "serialization": 2, - "structure": 1, + "exact": 2, + "serialization": 3, + "structure": 0, "content": 0 }, "features": { - "atx-heading": 3, - "delimiter-edge": 1, + "atx-heading": 5, "fenced-code": 2, "raw-html": 1, "table": 3 }, + "dirty": true, "remote": "https://github.com/TanStack/redact.git", - "revision": "e1620a13aab8935c806238f117ba58559b7cd002" + "revision": "2e3c75c483365d26ae326ec0fdae96fa5c690451" }, { "name": "router", @@ -535,8 +569,8 @@ "comparisons": { "exact": 308, "serialization": 12, - "structure": 88, - "content": 98 + "structure": 89, + "content": 97 }, "features": { "frontmatter": 275, @@ -585,36 +619,27 @@ }, { "name": "start-evals", - "bytes": 3445858, - "markdown": 741, + "bytes": 187608, + "markdown": 70, "mdx": 0, - "skipped": 0, + "skipped": 689, "errors": 0, "comparisons": { - "exact": 474, - "serialization": 134, - "structure": 103, - "content": 30 + "exact": 63, + "serialization": 7, + "structure": 0, + "content": 0 }, "features": { - "atx-heading": 727, - "fenced-code": 103, - "table": 146, - "literal-autolink": 30, - "blockquote": 7, - "site-template": 2, - "raw-html": 18, - "four-space-indent": 27, - "nested-list": 35, - "entity-reference": 1, - "footnote": 1, - "frontmatter": 1, - "reference-link": 1, - "setext-heading": 1 + "atx-heading": 70, + "fenced-code": 11, + "table": 13, + "four-space-indent": 1, + "nested-list": 1 }, "dirty": true, "remote": "https://github.com/TanStack/start-evals.git", - "revision": "cb854d8c8a1a3754880913e99fbf2452c9ea1557" + "revision": "e30b88faf4a5d3e017bccadc7fe2594710dfe1e0" }, { "name": "start-rsc", @@ -626,8 +651,8 @@ "comparisons": { "exact": 290, "serialization": 15, - "structure": 46, - "content": 82 + "structure": 47, + "content": 81 }, "features": { "atx-heading": 368, @@ -681,69 +706,72 @@ }, { "name": "table", - "bytes": 414692, - "markdown": 167, + "bytes": 5570405, + "markdown": 1384, "mdx": 0, "skipped": 0, "errors": 0, "comparisons": { - "exact": 152, - "serialization": 4, - "structure": 11, - "content": 0 + "exact": 1269, + "serialization": 14, + "structure": 59, + "content": 42 }, "features": { - "atx-heading": 161, - "frontmatter": 76, - "literal-autolink": 3, + "atx-heading": 1384, + "task-list": 16, + "frontmatter": 1338, + "literal-autolink": 21, "reference-link": 1, - "nested-list": 10, - "blockquote": 38, - "fenced-code": 70, - "raw-html": 7, - "table": 2, - "four-space-indent": 1, - "site-template": 1, - "combined-emphasis": 2, - "delimiter-edge": 2 + "nested-list": 40, + "blockquote": 205, + "callout": 37, + "fenced-code": 1334, + "four-space-indent": 19, + "raw-html": 396, + "tab-indentation": 18, + "table": 59, + "comment-component": 6, + "delimiter-edge": 5, + "strikethrough": 31 }, "remote": "https://github.com/TanStack/table.git", - "revision": "965b55ec0bfb3983596d56835bc81a9e29b4ee72" + "revision": "50feff8584d48972329d2d2b5795e152cf522178" }, { "name": "tanstack.com", - "bytes": 1027164, - "markdown": 80, + "bytes": 1263877, + "markdown": 103, "mdx": 0, "skipped": 0, "errors": 0, "comparisons": { - "exact": 58, - "serialization": 10, - "structure": 7, - "content": 5 + "exact": 68, + "serialization": 15, + "structure": 13, + "content": 7 }, "features": { - "atx-heading": 79, - "fenced-code": 60, - "table": 21, - "nested-list": 7, - "blockquote": 18, - "callout": 3, - "literal-autolink": 4, - "raw-html": 9, - "task-list": 2, - "frontmatter": 56, - "delimiter-edge": 4, - "code-metadata": 1, + "atx-heading": 101, + "fenced-code": 75, + "table": 31, + "literal-autolink": 8, + "blockquote": 25, + "frontmatter": 64, + "task-list": 7, + "delimiter-edge": 9, + "nested-list": 8, + "callout": 4, + "raw-html": 10, + "code-metadata": 2, + "comment-component": 3, "combined-emphasis": 2, "four-space-indent": 1, - "comment-component": 2, - "footnote": 2, - "reference-link": 2 + "footnote": 3, + "reference-link": 3 }, "remote": "https://github.com/TanStack/tanstack.com.git", - "revision": "b0c08c86704b677ab5ed720a2ce27b51db4da1f9" + "revision": "84f0b9c453ea741787a6162c13896c9492d97194" }, { "name": "virtual", @@ -812,661 +840,375 @@ } ], "kinds": { - "blog": 62, - "changes": 196, - "docs": 2798, - "fixture": 75, - "other": 820, - "readme": 875 + "blog": 69, + "changes": 263, + "docs": 4590, + "fixture": 115, + "other": 438, + "readme": 833 }, - "profileContentDifferences": 30, + "profileContentDifferences": 0, "targetProfileContentDifferences": 0, "notableFiles": [ { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-05-31T21-11-29-191Z-codex-context-v2-strict-baseline/executions/r2__codex-desktop-gpt-5__agent-native-framework-tools__server-boundary-secret__nextjs-app-router/transcript.md", - "kind": "other", + "repository": "db", + "path": "packages/react-db/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 6931, + "bytes": 58320, "features": [ - "atx-heading" + "atx-heading", + "fenced-code", + "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead.\" Started: 2026-06-01T02:49:22.001Z Ended: 2026-06-01T02:58:36.877Z Exit code: 0 Signa...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead.\" Started: 2026-06-01T02:49:22.001Z Ended: 2026-06-01T02:58:36.877Z Exit code: 0 Si..." - }, - "durationMs": 1.2550419999997757 + "comparison": "structure", + "durationMs": 14.529125000000022 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-05-31T21-11-29-191Z-codex-context-v2-strict-baseline/executions/r1__codex-desktop-gpt-5__baseline__protected-settings__nextjs-app-router/transcript.md", - "kind": "other", + "repository": "db", + "path": "packages/vue-db/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 6306, + "bytes": 44617, "features": [ "atx-heading", + "fenced-code", "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead.\" Started: 2026-06-01T03:33:27.634Z Ended: 2026-06-01T03:41:45.823Z Exit code: 0 Signa...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead.\" Started: 2026-06-01T03:33:27.634Z Ended: 2026-06-01T03:41:45.823Z Exit code: 0 Si..." - }, - "durationMs": 1.2914579999996931 + "comparison": "structure", + "durationMs": 11.269624999999905 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-05-31T21-11-29-191Z-codex-context-v2-strict-baseline/executions/r1__codex-desktop-gpt-5__agent-native-framework-tools__protected-settings__tanstack-start/transcript.md", - "kind": "other", + "repository": "router", + "path": "packages/react-start/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 6082, + "bytes": 37934, "features": [ "atx-heading", "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead.\" Started: 2026-06-01T03:01:09.414Z Ended: 2026-06-01T03:11:59.376Z Exit code: 0 Signa...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead.\" Started: 2026-06-01T03:01:09.414Z Ended: 2026-06-01T03:11:59.376Z Exit code: 0 Si..." - }, - "durationMs": 1.2829999999999018 + "comparison": "structure", + "durationMs": 13.121292000000722 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-05-31T21-11-29-191Z-codex-context-v2-strict-baseline/executions/r1__codex-desktop-gpt-5__agent-native-framework-tools__protected-settings__nextjs-app-router/transcript.md", - "kind": "other", + "repository": "db", + "path": "docs/collections/powersync-collection.md", + "kind": "docs", "format": "md", - "bytes": 5450, + "bytes": 37385, "features": [ "atx-heading", - "nested-list" + "fenced-code", + "frontmatter", + "table" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead.\" Started: 2026-06-01T03:15:19.199Z Ended: 2026-06-01T03:30:36.496Z Exit code: 0 Signa...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead.\" Started: 2026-06-01T03:15:19.199Z Ended: 2026-06-01T03:30:36.496Z Exit code: 0 Si..." - }, - "durationMs": 1.1159159999997428 + "comparison": "structure", + "durationMs": 5.9004589999995005 }, { - "repository": "router", - "path": "packages/eslint-plugin-start/no-client-code-in-server-component.md", - "kind": "other", + "repository": "db", + "path": "packages/svelte-db/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 3939, + "bytes": 35324, "features": [ "atx-heading", "fenced-code", "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...specific violating node (where possible) and includes a \"Triggered by rendering \\\"X\\\"\" hint naming the entry component. Options type Options = [ { allowedServerHooks?: string[] }, ] allowedServerHooks Add additional hook names that should b...", - "reference": "...specific violating node (where possible) and includes a \"Triggered by rendering \"X\"\" hint naming the entry component. Options type Options = [ { allowedServerHooks?: string[] }, ] allowedServerHooks Add additional hook names that should be ..." - }, - "durationMs": 1.0327910000000884 + "comparison": "structure", + "durationMs": 8.862957999999708 }, { - "repository": "start-rsc", - "path": "packages/eslint-plugin-start/no-client-code-in-server-component.md", + "repository": "table", + "path": "perf-skipped.md", "kind": "other", "format": "md", - "bytes": 3939, + "bytes": 34625, "features": [ "atx-heading", "fenced-code", - "nested-list" + "table" ], - "comparison": "content", - "contentDifference": { - "actual": "...specific violating node (where possible) and includes a \"Triggered by rendering \\\"X\\\"\" hint naming the entry component. Options type Options = [ { allowedServerHooks?: string[] }, ] allowedServerHooks Add additional hook names that should b...", - "reference": "...specific violating node (where possible) and includes a \"Triggered by rendering \"X\"\" hint naming the entry component. Options type Options = [ { allowedServerHooks?: string[] }, ] allowedServerHooks Add additional hook names that should be ..." - }, - "durationMs": 0.9969590000000608 + "comparison": "structure", + "durationMs": 5.274457999999868 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-05-31T21-11-29-191Z-codex-context-v2-strict-baseline/executions/r1__codex-desktop-gpt-5__official-docs__typed-search-dashboard__nextjs-app-router/transcript.md", - "kind": "other", + "repository": "router", + "path": "packages/start-plugin-core/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 3337, + "bytes": 33931, "features": [ "atx-heading", "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead.\" Started: 2026-06-01T03:44:40.876Z Ended: 2026-06-01T03:55:53.181Z Exit code: 0 Signa...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead.\" Started: 2026-06-01T03:44:40.876Z Ended: 2026-06-01T03:55:53.181Z Exit code: 0 Si..." - }, - "durationMs": 0.4983750000001237 + "comparison": "structure", + "durationMs": 10.375625000000582 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r1__codex-desktop-gpt-5__baseline__error-and-validation-flow__nextjs-app-router/transcript.md", - "kind": "other", + "repository": "db", + "path": "packages/solid-db/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 2797, + "bytes": 33698, "features": [ - "atx-heading" + "atx-heading", + "fenced-code", + "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.2172499999996944 + "comparison": "structure", + "durationMs": 8.366208999999799 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r1__codex-desktop-gpt-5__baseline__server-boundary-secret__nextjs-app-router/transcript.md", - "kind": "other", + "repository": "db", + "path": "packages/trailbase-db-collection/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 2785, + "bytes": 33653, "features": [ - "atx-heading" + "atx-heading", + "fenced-code", + "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.219916000000012 + "comparison": "structure", + "durationMs": 9.332167000000481 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r2__codex-desktop-gpt-5__baseline__server-boundary-secret__nextjs-app-router/transcript.md", - "kind": "other", + "repository": "router", + "path": "packages/solid-start/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 2785, + "bytes": 32828, "features": [ - "atx-heading" + "atx-heading", + "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.23266599999988102 + "comparison": "structure", + "durationMs": 14.556166999999732 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r1__codex-desktop-gpt-5__baseline__error-and-validation-flow__tanstack-start/transcript.md", - "kind": "other", + "repository": "ai", + "path": "docs/chat/connection-adapters.md", + "kind": "docs", "format": "md", - "bytes": 2784, + "bytes": 31847, "features": [ - "atx-heading" + "atx-heading", + "blockquote", + "code-metadata", + "fenced-code", + "frontmatter", + "table" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.23549999999977445 + "comparison": "structure", + "durationMs": 5.7643339999999625 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-06T03-49-20-675Z-publishable-second-topoff-codex-desktop-gpt-5-baseline-robust/executions/r1__codex-desktop-gpt-5__baseline__error-and-validation-flow__nextjs-app-router/transcript.md", - "kind": "other", + "repository": "router", + "path": "packages/vue-start/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 2778, + "bytes": 30537, "features": [ - "atx-heading" + "atx-heading", + "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.24716700000044511 + "comparison": "structure", + "durationMs": 10.741874999999709 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-06T03-49-20-675Z-publishable-second-topoff-codex-desktop-gpt-5-baseline-robust/executions/r2__codex-desktop-gpt-5__baseline__error-and-validation-flow__nextjs-app-router/transcript.md", - "kind": "other", + "repository": "db", + "path": "docs/guides/collection-options-creator.md", + "kind": "docs", "format": "md", - "bytes": 2778, + "bytes": 28924, "features": [ - "atx-heading" + "atx-heading", + "fenced-code", + "frontmatter" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.2244170000003578 + "comparison": "structure", + "durationMs": 5.417292000000089 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r1__codex-desktop-gpt-5__baseline__server-boundary-secret__tanstack-start/transcript.md", - "kind": "other", + "repository": "db", + "path": "packages/angular-db/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 2772, + "bytes": 26688, "features": [ - "atx-heading" + "atx-heading", + "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.2437500000005457 + "comparison": "structure", + "durationMs": 7.320208999999522 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r2__codex-desktop-gpt-5__baseline__server-boundary-secret__tanstack-start/transcript.md", - "kind": "other", + "repository": "virtual", + "path": "docs/api/virtualizer.md", + "kind": "docs", "format": "md", - "bytes": 2772, + "bytes": 26259, "features": [ - "atx-heading" + "atx-heading", + "blockquote", + "fenced-code", + "frontmatter" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.22466699999949924 + "comparison": "structure", + "durationMs": 4.165041000000201 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r1__codex-desktop-gpt-5__baseline__protected-settings__nextjs-app-router/transcript.md", - "kind": "other", + "repository": "query", + "path": "docs/framework/react/guides/ssr.md", + "kind": "docs", "format": "md", - "bytes": 2769, + "bytes": 25342, "features": [ - "atx-heading" + "atx-heading", + "blockquote", + "fenced-code", + "frontmatter", + "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.22512499999993452 + "comparison": "structure", + "durationMs": 3.3418750000000728 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-06T03-49-20-675Z-publishable-second-topoff-codex-desktop-gpt-5-baseline-robust/executions/r1__codex-desktop-gpt-5__baseline__server-boundary-secret__nextjs-app-router/transcript.md", + "repository": "ai", + "path": "packages/ai/skills/ai-core/adapter-configuration/SKILL.md", "kind": "other", "format": "md", - "bytes": 2766, + "bytes": 25006, "features": [ - "atx-heading" + "atx-heading", + "blockquote", + "code-metadata", + "fenced-code", + "frontmatter", + "table" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.2402910000000702 + "comparison": "structure", + "durationMs": 4.018459000000348 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r1__codex-desktop-gpt-5__baseline__error-and-validation-flow__nextjs-app-router/transcript.md", - "kind": "other", + "repository": "router", + "path": "packages/react-start-rsc/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 2765, + "bytes": 23084, "features": [ - "atx-heading" + "atx-heading", + "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.27074999999967986 + "comparison": "structure", + "durationMs": 7.620249999999942 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-06T03-49-20-675Z-publishable-second-topoff-codex-desktop-gpt-5-baseline-robust/executions/r1__codex-desktop-gpt-5__baseline__error-and-validation-flow__tanstack-start/transcript.md", - "kind": "other", + "repository": "db", + "path": "packages/rxdb-db-collection/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 2765, + "bytes": 22500, "features": [ - "atx-heading" + "atx-heading", + "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.2360420000004524 + "comparison": "structure", + "durationMs": 5.492083000000093 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-06T03-49-20-675Z-publishable-second-topoff-codex-desktop-gpt-5-baseline-robust/executions/r2__codex-desktop-gpt-5__baseline__error-and-validation-flow__tanstack-start/transcript.md", - "kind": "other", + "repository": "db", + "path": "packages/offline-transactions/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 2765, + "bytes": 20741, "features": [ - "atx-heading" + "atx-heading", + "fenced-code", + "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.2509170000002996 + "comparison": "structure", + "durationMs": 4.733083999999508 }, { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r1__codex-desktop-gpt-5__baseline__protected-settings__tanstack-start/transcript.md", - "kind": "other", + "repository": "router", + "path": "packages/start-server-core/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 2756, + "bytes": 19964, "features": [ - "atx-heading" + "atx-heading", + "nested-list" ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.21774999999979627 - }, - { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r1__codex-desktop-gpt-5__baseline__server-boundary-secret__nextjs-app-router/transcript.md", - "kind": "other", - "format": "md", - "bytes": 2753, - "features": [ - "atx-heading" - ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.2727919999997539 - }, - { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r2__codex-desktop-gpt-5__baseline__server-boundary-secret__nextjs-app-router/transcript.md", - "kind": "other", - "format": "md", - "bytes": 2753, - "features": [ - "atx-heading" - ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.21883299999990413 - }, - { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-06T03-49-20-675Z-publishable-second-topoff-codex-desktop-gpt-5-baseline-robust/executions/r1__codex-desktop-gpt-5__baseline__server-boundary-secret__tanstack-start/transcript.md", - "kind": "other", - "format": "md", - "bytes": 2753, - "features": [ - "atx-heading" - ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.2209999999995489 - }, - { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r1__codex-desktop-gpt-5__baseline__error-and-validation-flow__tanstack-start/transcript.md", - "kind": "other", - "format": "md", - "bytes": 2752, - "features": [ - "atx-heading" - ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.23624999999992724 - }, - { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r1__codex-desktop-gpt-5__baseline__server-boundary-secret__tanstack-start/transcript.md", - "kind": "other", - "format": "md", - "bytes": 2740, - "features": [ - "atx-heading" - ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.23462500000005093 - }, - { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r2__codex-desktop-gpt-5__baseline__server-boundary-secret__tanstack-start/transcript.md", - "kind": "other", - "format": "md", - "bytes": 2740, - "features": [ - "atx-heading" - ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.2364159999997355 - }, - { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r1__codex-desktop-gpt-5__baseline__protected-settings__nextjs-app-router/transcript.md", - "kind": "other", - "format": "md", - "bytes": 2737, - "features": [ - "atx-heading" - ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.2391250000000582 - }, - { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r1__codex-desktop-gpt-5__baseline__protected-settings__tanstack-start/transcript.md", - "kind": "other", - "format": "md", - "bytes": 2724, - "features": [ - "atx-heading" - ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are inten...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead. Workspace-only runner mode: the repository root and sibling run directories are in..." - }, - "durationMs": 0.24250000000029104 - }, - { - "repository": "start-evals", - "path": "archive/legacy-harness/runs/2026-05-31T21-11-29-191Z-codex-context-v2-strict-baseline/executions/r1__codex-desktop-gpt-5__official-docs__typed-search-dashboard__tanstack-start/transcript.md", - "kind": "other", - "format": "md", - "bytes": 1825, - "features": [ - "atx-heading" - ], - "comparison": "content", - "contentDifference": { - "actual": "...utions. Commands that traverse upward or list sibling execution folders, such asfind .., are prohibited; usefind .inside this execution workspace instead.\" Started: 2026-06-01T03:58:21.278Z Ended: 2026-06-01T04:10:19.500Z Exit code: 124 Sig...", - "reference": "...utions. Commands that traverse upward or list sibling execution folders, such as find .., are prohibited; use find . inside this execution workspace instead.\" Started: 2026-06-01T03:58:21.278Z Ended: 2026-06-01T04:10:19.500Z Exit code: 124 ..." - }, - "durationMs": 0.18283299999984592 - }, - { - "repository": "db", - "path": "packages/react-db/CHANGELOG.md", - "kind": "changes", - "format": "md", - "bytes": 51874, - "features": [ - "atx-heading", - "fenced-code", - "nested-list" - ], - "comparison": "structure", - "durationMs": 14.497041999999965 - }, - { - "repository": "db", - "path": "packages/vue-db/CHANGELOG.md", - "kind": "changes", - "format": "md", - "bytes": 38607, - "features": [ - "atx-heading", - "fenced-code", - "nested-list" - ], - "comparison": "structure", - "durationMs": 11.28779099999997 + "comparison": "structure", + "durationMs": 5.265374999999949 }, { "repository": "router", - "path": "packages/react-start/CHANGELOG.md", - "kind": "changes", - "format": "md", - "bytes": 37934, - "features": [ - "atx-heading", - "nested-list" - ], - "comparison": "structure", - "durationMs": 14.179000000000087 - }, - { - "repository": "db", - "path": "docs/collections/powersync-collection.md", + "path": "docs/router/how-to/integrate-chakra-ui.md", "kind": "docs", "format": "md", - "bytes": 37385, + "bytes": 19856, "features": [ "atx-heading", "fenced-code", "frontmatter", - "table" - ], - "comparison": "structure", - "durationMs": 6.82808399999999 - }, - { - "repository": "router", - "path": "packages/start-plugin-core/CHANGELOG.md", - "kind": "changes", - "format": "md", - "bytes": 33931, - "features": [ - "atx-heading", - "nested-list" - ], - "comparison": "structure", - "durationMs": 11.561750000000302 - }, - { - "repository": "router", - "path": "packages/solid-start/CHANGELOG.md", - "kind": "changes", - "format": "md", - "bytes": 32828, - "features": [ - "atx-heading", - "nested-list" - ], - "comparison": "structure", - "durationMs": 12.749458000000232 - }, - { - "repository": "db", - "path": "packages/trailbase-db-collection/CHANGELOG.md", - "kind": "changes", - "format": "md", - "bytes": 30572, - "features": [ - "atx-heading", - "fenced-code", - "nested-list" - ], - "comparison": "structure", - "durationMs": 9.155749999999898 - }, - { - "repository": "router", - "path": "packages/vue-start/CHANGELOG.md", - "kind": "changes", - "format": "md", - "bytes": 30537, - "features": [ - "atx-heading", - "nested-list" + "task-list" ], "comparison": "structure", - "durationMs": 11.835415999999896 + "durationMs": 2.15954099999999 }, { - "repository": "db", - "path": "docs/guides/collection-options-creator.md", + "repository": "start-rsc", + "path": "docs/router/framework/react/how-to/integrate-chakra-ui.md", "kind": "docs", "format": "md", - "bytes": 28909, + "bytes": 19856, "features": [ "atx-heading", "fenced-code", - "frontmatter" + "frontmatter", + "task-list" ], "comparison": "structure", - "durationMs": 4.117124999999987 + "durationMs": 2.7223750000002838 }, { - "repository": "db", - "path": "packages/svelte-db/CHANGELOG.md", + "repository": "ai", + "path": "examples/ts-vue-chat/CHANGELOG.md", "kind": "changes", "format": "md", - "bytes": 28654, + "bytes": 19691, "features": [ "atx-heading", - "fenced-code", "nested-list" ], "comparison": "structure", - "durationMs": 8.634249999999838 + "durationMs": 7.354874999999993 }, { - "repository": "db", - "path": "packages/solid-db/CHANGELOG.md", - "kind": "changes", + "repository": "tanstack.com", + "path": ".agents/skills/design-motion-principles/references/output-format.md", + "kind": "other", "format": "md", - "bytes": 28090, + "bytes": 19591, "features": [ "atx-heading", "fenced-code", - "nested-list" + "table" ], "comparison": "structure", - "durationMs": 7.959999999999809 + "durationMs": 3.046667000000525 }, { - "repository": "virtual", - "path": "docs/api/virtualizer.md", + "repository": "table", + "path": "docs/framework/react/guide/table-state.md", "kind": "docs", "format": "md", - "bytes": 26259, + "bytes": 19281, "features": [ "atx-heading", "blockquote", @@ -1474,69 +1216,57 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 4.835500000000138 - }, - { - "repository": "query", - "path": "docs/framework/react/guides/ssr.md", - "kind": "docs", - "format": "md", - "bytes": 25342, - "features": [ - "atx-heading", - "blockquote", - "fenced-code", - "frontmatter", - "nested-list" - ], - "comparison": "structure", - "durationMs": 3.5062089999996715 + "durationMs": 2.671957999999904 }, { "repository": "router", - "path": "packages/react-start-rsc/CHANGELOG.md", + "path": "packages/react-start-server/CHANGELOG.md", "kind": "changes", "format": "md", - "bytes": 23084, + "bytes": 19043, "features": [ "atx-heading", "nested-list" ], "comparison": "structure", - "durationMs": 8.286917000000358 + "durationMs": 6.862415999999939 }, { - "repository": "db", - "path": "packages/angular-db/CHANGELOG.md", - "kind": "changes", + "repository": "start-rsc", + "path": "docs/router/framework/react/how-to/use-environment-variables.md", + "kind": "docs", "format": "md", - "bytes": 20580, + "bytes": 18884, "features": [ "atx-heading", - "nested-list" + "fenced-code", + "frontmatter", + "task-list" ], "comparison": "structure", - "durationMs": 5.467416999999841 + "durationMs": 3.1569999999992433 }, { "repository": "router", - "path": "packages/start-server-core/CHANGELOG.md", - "kind": "changes", + "path": "docs/router/how-to/use-environment-variables.md", + "kind": "docs", "format": "md", - "bytes": 19964, + "bytes": 18872, "features": [ "atx-heading", - "nested-list" + "fenced-code", + "frontmatter", + "task-list" ], "comparison": "structure", - "durationMs": 6.422333999999864 + "durationMs": 2.510750000000371 }, { "repository": "router", - "path": "docs/router/how-to/integrate-chakra-ui.md", + "path": "docs/router/how-to/integrate-framer-motion.md", "kind": "docs", "format": "md", - "bytes": 19856, + "bytes": 18822, "features": [ "atx-heading", "fenced-code", @@ -1544,14 +1274,14 @@ "task-list" ], "comparison": "structure", - "durationMs": 2.188249999999698 + "durationMs": 2.544958999999835 }, { "repository": "start-rsc", - "path": "docs/router/framework/react/how-to/integrate-chakra-ui.md", + "path": "docs/router/framework/react/how-to/integrate-framer-motion.md", "kind": "docs", "format": "md", - "bytes": 19856, + "bytes": 18822, "features": [ "atx-heading", "fenced-code", @@ -1559,119 +1289,108 @@ "task-list" ], "comparison": "structure", - "durationMs": 2.2557080000005953 + "durationMs": 2.5683329999992566 }, { - "repository": "db", - "path": "packages/rxdb-db-collection/CHANGELOG.md", + "repository": "router", + "path": "packages/router-plugin/CHANGELOG.md", "kind": "changes", "format": "md", - "bytes": 19419, + "bytes": 18752, "features": [ "atx-heading", "nested-list" ], "comparison": "structure", - "durationMs": 5.589666000000079 + "durationMs": 6.233292000000802 }, { "repository": "router", - "path": "packages/react-start-server/CHANGELOG.md", + "path": "packages/solid-start-server/CHANGELOG.md", "kind": "changes", "format": "md", - "bytes": 19043, + "bytes": 18718, "features": [ "atx-heading", "nested-list" ], "comparison": "structure", - "durationMs": 6.603374999999687 + "durationMs": 6.188750000000255 }, { - "repository": "start-rsc", - "path": "docs/router/framework/react/how-to/use-environment-variables.md", + "repository": "ai", + "path": "docs/media/text-to-speech.md", "kind": "docs", "format": "md", - "bytes": 18884, + "bytes": 18592, "features": [ "atx-heading", + "blockquote", + "code-metadata", "fenced-code", "frontmatter", - "task-list" + "table" ], "comparison": "structure", - "durationMs": 2.729833000000326 + "durationMs": 3.1944169999999303 }, { - "repository": "router", - "path": "docs/router/how-to/use-environment-variables.md", - "kind": "docs", + "repository": "ai", + "path": "examples/ts-svelte-chat/CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 18872, + "bytes": 18548, "features": [ "atx-heading", - "fenced-code", - "frontmatter", - "task-list" + "nested-list" ], "comparison": "structure", - "durationMs": 2.68362500000012 + "durationMs": 7.032874999999876 }, { - "repository": "router", - "path": "docs/router/how-to/integrate-framer-motion.md", - "kind": "docs", + "repository": "ai", + "path": ".agents/skills/pr-sweep/SKILL.md", + "kind": "other", "format": "md", - "bytes": 18822, + "bytes": 18458, "features": [ "atx-heading", "fenced-code", "frontmatter", - "task-list" + "table" ], "comparison": "structure", - "durationMs": 2.14712499999996 + "durationMs": 4.696290999999974 }, { - "repository": "start-rsc", - "path": "docs/router/framework/react/how-to/integrate-framer-motion.md", - "kind": "docs", + "repository": "ai", + "path": ".claude/skills/pr-sweep/SKILL.md", + "kind": "other", "format": "md", - "bytes": 18822, + "bytes": 18458, "features": [ "atx-heading", "fenced-code", "frontmatter", - "task-list" - ], - "comparison": "structure", - "durationMs": 2.3285419999992882 - }, - { - "repository": "router", - "path": "packages/router-plugin/CHANGELOG.md", - "kind": "changes", - "format": "md", - "bytes": 18752, - "features": [ - "atx-heading", - "nested-list" + "table" ], "comparison": "structure", - "durationMs": 6.100458000000344 + "durationMs": 3.267749999999978 }, { - "repository": "router", - "path": "packages/solid-start-server/CHANGELOG.md", - "kind": "changes", + "repository": "ai", + "path": ".grok/skills/pr-sweep/SKILL.md", + "kind": "other", "format": "md", - "bytes": 18718, + "bytes": 18458, "features": [ "atx-heading", - "nested-list" + "fenced-code", + "frontmatter", + "table" ], "comparison": "structure", - "durationMs": 6.683332999999948 + "durationMs": 4.373082999999951 }, { "repository": "workflow", @@ -1687,7 +1406,21 @@ "table" ], "comparison": "structure", - "durationMs": 4.149416000000201 + "durationMs": 6.5335420000010345 + }, + { + "repository": "db", + "path": "packages/powersync-db-collection/CHANGELOG.md", + "kind": "changes", + "format": "md", + "bytes": 18185, + "features": [ + "atx-heading", + "fenced-code", + "nested-list" + ], + "comparison": "structure", + "durationMs": 4.519584000000577 }, { "repository": "router", @@ -1704,7 +1437,7 @@ "task-list" ], "comparison": "structure", - "durationMs": 3.6637080000000424 + "durationMs": 3.4595829999998386 }, { "repository": "start-rsc", @@ -1721,7 +1454,7 @@ "task-list" ], "comparison": "structure", - "durationMs": 3.5244999999995343 + "durationMs": 4.088624999999411 }, { "repository": "router", @@ -1734,7 +1467,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 6.158957999999984 + "durationMs": 5.566584000000148 }, { "repository": "router", @@ -1749,7 +1482,7 @@ "task-list" ], "comparison": "structure", - "durationMs": 2.198707999999897 + "durationMs": 2.3763749999998254 }, { "repository": "start-rsc", @@ -1764,21 +1497,7 @@ "task-list" ], "comparison": "structure", - "durationMs": 2.196708999999828 - }, - { - "repository": "db", - "path": "packages/offline-transactions/CHANGELOG.md", - "kind": "changes", - "format": "md", - "bytes": 17284, - "features": [ - "atx-heading", - "fenced-code", - "nested-list" - ], - "comparison": "structure", - "durationMs": 4.674584000000095 + "durationMs": 2.571125000000393 }, { "repository": "router", @@ -1791,77 +1510,48 @@ "nested-list" ], "comparison": "structure", - "durationMs": 5.810875000000124 + "durationMs": 5.607332999999926 }, { "repository": "router", "path": "packages/solid-start-client/CHANGELOG.md", "kind": "changes", "format": "md", - "bytes": 16563, - "features": [ - "atx-heading", - "nested-list" - ], - "comparison": "structure", - "durationMs": 5.851249999999709 - }, - { - "repository": "preact", - "path": "CONTRIBUTING.md", - "kind": "other", - "format": "md", - "bytes": 15457, - "features": [ - "atx-heading", - "blockquote", - "fenced-code", - "nested-list" - ], - "comparison": "structure", - "durationMs": 3.3512919999998303 - }, - { - "repository": "router", - "path": "packages/react-router/CHANGELOG.md", - "kind": "changes", - "format": "md", - "bytes": 15119, + "bytes": 16563, "features": [ "atx-heading", "nested-list" ], "comparison": "structure", - "durationMs": 4.2952909999999065 + "durationMs": 7.481249999999818 }, { - "repository": "table", - "path": "docs/guide/column-filtering.md", - "kind": "docs", + "repository": "preact", + "path": "CONTRIBUTING.md", + "kind": "other", "format": "md", - "bytes": 14898, + "bytes": 15457, "features": [ "atx-heading", "blockquote", "fenced-code", - "frontmatter" + "nested-list" ], "comparison": "structure", - "durationMs": 2.4047499999996944 + "durationMs": 2.2513749999998254 }, { - "repository": "db", - "path": "packages/powersync-db-collection/CHANGELOG.md", + "repository": "router", + "path": "packages/react-router/CHANGELOG.md", "kind": "changes", "format": "md", - "bytes": 14576, + "bytes": 15119, "features": [ "atx-heading", - "fenced-code", "nested-list" ], "comparison": "structure", - "durationMs": 3.8635000000001583 + "durationMs": 4.120750000000044 }, { "repository": "start-rsc", @@ -1877,7 +1567,7 @@ "table" ], "comparison": "structure", - "durationMs": 2.724124999999731 + "durationMs": 6.987374999999702 }, { "repository": "router", @@ -1890,7 +1580,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 4.949791000000005 + "durationMs": 7.521291999999448 }, { "repository": "router", @@ -1903,7 +1593,37 @@ "nested-list" ], "comparison": "structure", - "durationMs": 4.9730829999998605 + "durationMs": 4.69116699999995 + }, + { + "repository": "ai", + "path": "docs/tools/tools.md", + "kind": "docs", + "format": "md", + "bytes": 14270, + "features": [ + "atx-heading", + "blockquote", + "code-metadata", + "fenced-code", + "frontmatter" + ], + "comparison": "structure", + "durationMs": 4.137249999999767 + }, + { + "repository": "tanstack.com", + "path": "src/blog/tanstack-ai-rc.md", + "kind": "blog", + "format": "md", + "bytes": 14265, + "features": [ + "atx-heading", + "blockquote", + "frontmatter" + ], + "comparison": "structure", + "durationMs": 1.5314160000016273 }, { "repository": "router", @@ -1918,7 +1638,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 4.068250000000262 + "durationMs": 3.6390410000003612 }, { "repository": "router", @@ -1931,21 +1651,22 @@ "nested-list" ], "comparison": "structure", - "durationMs": 4.122791000000234 + "durationMs": 5.527374999999665 }, { "repository": "ai", - "path": "CHANGELOG.md", - "kind": "changes", + "path": "CONTRIBUTING.md", + "kind": "other", "format": "md", - "bytes": 13732, + "bytes": 13984, "features": [ "atx-heading", "fenced-code", - "nested-list" + "nested-list", + "table" ], "comparison": "structure", - "durationMs": 12.674874999999929 + "durationMs": 8.413125000000036 }, { "repository": "router", @@ -1960,7 +1681,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.7465420000003178 + "durationMs": 1.6022910000001502 }, { "repository": "router", @@ -1973,7 +1694,20 @@ "nested-list" ], "comparison": "structure", - "durationMs": 3.9356250000000728 + "durationMs": 3.479334000000563 + }, + { + "repository": "markdown", + "path": "reports/external-corpus.md", + "kind": "other", + "format": "md", + "bytes": 13352, + "features": [ + "atx-heading", + "table" + ], + "comparison": "structure", + "durationMs": 3.330417000000125 }, { "repository": "pacer", @@ -1989,7 +1723,23 @@ "nested-list" ], "comparison": "structure", - "durationMs": 2.2150000000001455 + "durationMs": 1.4622920000001614 + }, + { + "repository": "ai", + "path": "packages/ai-persistence/skills/ai-persistence/SKILL.md", + "kind": "other", + "format": "md", + "bytes": 13050, + "features": [ + "atx-heading", + "blockquote", + "fenced-code", + "frontmatter", + "table" + ], + "comparison": "structure", + "durationMs": 3.7708330000000387 }, { "repository": "pacer", @@ -2005,7 +1755,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 2.0933749999999236 + "durationMs": 1.5108749999999418 }, { "repository": "pacer", @@ -2018,7 +1768,24 @@ "nested-list" ], "comparison": "structure", - "durationMs": 3.590874999999869 + "durationMs": 3.050709000000097 + }, + { + "repository": "ai", + "path": "docs/structured-outputs/streaming.md", + "kind": "docs", + "format": "md", + "bytes": 12670, + "features": [ + "atx-heading", + "blockquote", + "code-metadata", + "fenced-code", + "frontmatter", + "table" + ], + "comparison": "structure", + "durationMs": 2.0407500000001164 }, { "repository": "start-rsc", @@ -2033,7 +1800,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.829541999999492 + "durationMs": 2.370458000000326 }, { "repository": "pacer", @@ -2049,7 +1816,53 @@ "nested-list" ], "comparison": "structure", - "durationMs": 2.275916999999936 + "durationMs": 1.6101249999992433 + }, + { + "repository": "ai", + "path": "packages/ai-mistral/CHANGELOG.md", + "kind": "changes", + "format": "md", + "bytes": 12513, + "features": [ + "atx-heading", + "nested-list" + ], + "comparison": "structure", + "durationMs": 2.7114999999998872 + }, + { + "repository": "ai", + "path": "docs/tools/server-tools.md", + "kind": "docs", + "format": "md", + "bytes": 12480, + "features": [ + "atx-heading", + "blockquote", + "code-metadata", + "fenced-code", + "frontmatter", + "nested-list" + ], + "comparison": "structure", + "durationMs": 2.3199580000000424 + }, + { + "repository": "table", + "path": "docs/guide/column-defs.md", + "kind": "docs", + "format": "md", + "bytes": 12479, + "features": [ + "atx-heading", + "blockquote", + "fenced-code", + "frontmatter", + "nested-list" + ], + "comparison": "structure", + "durationMs": 2.57208300000093 }, { "repository": "router", @@ -2064,7 +1877,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.444416000000274 + "durationMs": 1.3152919999993173 }, { "repository": "pacer", @@ -2077,7 +1890,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 3.573790999999801 + "durationMs": 3.141708000000108 }, { "repository": "query", @@ -2090,7 +1903,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 4.61220800000001 + "durationMs": 4.1431249999996 }, { "repository": "query", @@ -2103,7 +1916,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 4.6050420000001395 + "durationMs": 4.354916999999659 }, { "repository": "router", @@ -2116,7 +1929,23 @@ "nested-list" ], "comparison": "structure", - "durationMs": 3.879374999999982 + "durationMs": 3.6676250000000437 + }, + { + "repository": "ai", + "path": "docs/structured-outputs/multi-turn.md", + "kind": "docs", + "format": "md", + "bytes": 12120, + "features": [ + "atx-heading", + "blockquote", + "code-metadata", + "fenced-code", + "frontmatter" + ], + "comparison": "structure", + "durationMs": 1.607332999999926 }, { "repository": "router", @@ -2131,7 +1960,7 @@ "task-list" ], "comparison": "structure", - "durationMs": 1.58916599999975 + "durationMs": 1.5085419999995793 }, { "repository": "start-rsc", @@ -2146,7 +1975,7 @@ "task-list" ], "comparison": "structure", - "durationMs": 1.60162500000024 + "durationMs": 1.9504580000002534 }, { "repository": "router", @@ -2159,23 +1988,21 @@ "nested-list" ], "comparison": "structure", - "durationMs": 4.824082999999973 + "durationMs": 4.8255419999995866 }, { - "repository": "ai", - "path": "docs/guides/tool-architecture.md", + "repository": "tanstack.com", + "path": "docs/cloudflare-workers-migration.md", "kind": "docs", "format": "md", - "bytes": 11554, + "bytes": 11591, "features": [ "atx-heading", "fenced-code", - "frontmatter", - "nested-list", - "table" + "nested-list" ], "comparison": "structure", - "durationMs": 2.35120900000004 + "durationMs": 2.125958000000537 }, { "repository": "query", @@ -2188,22 +2015,21 @@ "nested-list" ], "comparison": "structure", - "durationMs": 4.392249999999876 + "durationMs": 3.8738750000002256 }, { "repository": "ai", - "path": "docs/guides/client-tools.md", - "kind": "docs", + "path": "CHANGELOG.md", + "kind": "changes", "format": "md", - "bytes": 11208, + "bytes": 11241, "features": [ "atx-heading", "fenced-code", - "frontmatter", "nested-list" ], "comparison": "structure", - "durationMs": 1.8804580000000897 + "durationMs": 3.9812090000000353 }, { "repository": "start-rsc", @@ -2217,7 +2043,7 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 3.0974169999999503 + "durationMs": 3.4201249999996435 }, { "repository": "router", @@ -2234,7 +2060,7 @@ "task-list" ], "comparison": "structure", - "durationMs": 1.8556250000001455 + "durationMs": 1.8501249999999345 }, { "repository": "router", @@ -2247,21 +2073,36 @@ "nested-list" ], "comparison": "structure", - "durationMs": 2.7304579999999987 + "durationMs": 2.1948750000001382 }, { - "repository": "tanstack.com", - "path": "docs/cloudflare-workers-migration.md", + "repository": "ai", + "path": "docs/tools/mcp-manual.md", "kind": "docs", "format": "md", - "bytes": 10489, + "bytes": 11045, "features": [ "atx-heading", + "blockquote", + "code-metadata", "fenced-code", + "frontmatter" + ], + "comparison": "structure", + "durationMs": 3.494374999999991 + }, + { + "repository": "db", + "path": "packages/db-sqlite-persistence-core/CHANGELOG.md", + "kind": "changes", + "format": "md", + "bytes": 10620, + "features": [ + "atx-heading", "nested-list" ], "comparison": "structure", - "durationMs": 2.057166999999936 + "durationMs": 2.4230420000003505 }, { "repository": "query", @@ -2274,7 +2115,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 4.286707999999635 + "durationMs": 3.68395800000053 }, { "repository": "query", @@ -2287,7 +2128,7 @@ "nested-list" ], "comparison": "structure", - "durationMs": 4.913874999999734 + "durationMs": 3.6910420000003796 }, { "repository": "router", @@ -2302,7 +2143,141 @@ "frontmatter" ], "comparison": "structure", - "durationMs": 1.8760829999996531 + "durationMs": 1.6543750000000728 + }, + { + "repository": "query", + "path": "packages/react-query/CHANGELOG.md", + "kind": "changes", + "format": "md", + "bytes": 10102, + "features": [ + "atx-heading", + "nested-list" + ], + "comparison": "structure", + "durationMs": 3.5619580000002316 + }, + { + "repository": "query", + "path": "packages/react-query-persist-client/CHANGELOG.md", + "kind": "changes", + "format": "md", + "bytes": 9813, + "features": [ + "atx-heading", + "nested-list" + ], + "comparison": "structure", + "durationMs": 4.086083000000144 + }, + { + "repository": "query", + "path": "packages/angular-query-experimental/CHANGELOG.md", + "kind": "changes", + "format": "md", + "bytes": 9725, + "features": [ + "atx-heading", + "nested-list" + ], + "comparison": "structure", + "durationMs": 3.40099999999984 + }, + { + "repository": "query", + "path": "packages/svelte-query-persist-client/CHANGELOG.md", + "kind": "changes", + "format": "md", + "bytes": 9488, + "features": [ + "atx-heading", + "nested-list" + ], + "comparison": "structure", + "durationMs": 4.153167000000394 + }, + { + "repository": "query", + "path": "packages/query-persist-client-core/CHANGELOG.md", + "kind": "changes", + "format": "md", + "bytes": 9445, + "features": [ + "atx-heading", + "nested-list" + ], + "comparison": "structure", + "durationMs": 3.8244580000000497 + }, + { + "repository": "query", + "path": "packages/solid-query-persist-client/CHANGELOG.md", + "kind": "changes", + "format": "md", + "bytes": 9441, + "features": [ + "atx-heading", + "nested-list" + ], + "comparison": "structure", + "durationMs": 3.9095420000003287 + }, + { + "repository": "router", + "path": "packages/eslint-plugin-start/no-async-client-component.md", + "kind": "other", + "format": "md", + "bytes": 9321, + "features": [ + "atx-heading", + "fenced-code", + "nested-list" + ], + "comparison": "structure", + "durationMs": 1.4457499999998618 + }, + { + "repository": "start-rsc", + "path": "packages/eslint-plugin-start/no-async-client-component.md", + "kind": "other", + "format": "md", + "bytes": 9321, + "features": [ + "atx-heading", + "fenced-code", + "nested-list" + ], + "comparison": "structure", + "durationMs": 1.45837500000016 + }, + { + "repository": "query", + "path": "packages/svelte-query/CHANGELOG.md", + "kind": "changes", + "format": "md", + "bytes": 9298, + "features": [ + "atx-heading", + "nested-list" + ], + "comparison": "structure", + "durationMs": 3.2303340000007665 + }, + { + "repository": "router", + "path": "packages/router-core/skills/router-core/path-params/SKILL.md", + "kind": "other", + "format": "md", + "bytes": 9268, + "features": [ + "atx-heading", + "blockquote", + "fenced-code", + "frontmatter" + ], + "comparison": "structure", + "durationMs": 1.1572079999996276 } ] } diff --git a/reports/tanstack-corpus.md b/reports/tanstack-corpus.md index 4fc8520..86da221 100644 --- a/reports/tanstack-corpus.md +++ b/reports/tanstack-corpus.md @@ -1,54 +1,55 @@ # TanStack repository corpus -Generated: 2026-07-22T18:10:53.920Z +Generated: 2026-09-11T18:24:03.577Z This report measures practical corpus behavior. Marked is a differential reference, not a correctness oracle. MDX is inventoried but not parsed. -- repositories or roots: 25 -- Markdown documents: 4826 +- repositories or roots: 26 +- Markdown documents: 6308 - MDX documents inventoried: 2 -- source bytes: 18378547 +- source bytes: 32642258 - parse or determinism errors: 0 -- content differences using only profile syntax: 30 +- content differences using only profile syntax: 0 - unexplained target docs/blog/README content differences using only profile syntax: 0 -- audit time: 4740.7 ms +- audit time: 8920.8 ms ## Output comparison | Result | Documents | Percent | Meaning | | :--- | ---: | ---: | :--- | -| exact | 3582 | 74.2% | Normalized HTML matches Marked | -| serialization | 287 | 5.9% | Tag and text shape matches; attributes or serialization differ | -| structure | 667 | 13.8% | Text matches; element structure differs | -| content | 290 | 6.0% | Rendered text differs and requires triage | +| exact | 4996 | 79.2% | Normalized HTML matches Marked | +| serialization | 189 | 3.0% | Tag and text shape matches; attributes or serialization differ | +| structure | 753 | 11.9% | Text matches; element structure differs | +| content | 370 | 5.9% | Rendered text differs and requires triage | ## Repositories | Repository | Revision | Dirty | Markdown | MDX | Exact | Serialization | Structure | Content | Errors | | :--- | :--- | :---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | -| [ai](https://github.com/tanstack/ai) | [51c66147](https://github.com/tanstack/ai/tree/51c66147dd543e0cdd4f35e7970d52bb1cc43add) | yes | 323 | 0 | 242 | 23 | 58 | 0 | 0 | +| [ai](https://github.com/tanstack/ai) | [44a73e0e](https://github.com/tanstack/ai/tree/44a73e0e8790f478d853bf3d843c44f1f501762e) | no | 941 | 0 | 697 | 26 | 152 | 66 | 0 | | [bling](https://github.com/TanStack/bling) | [62703ba3](https://github.com/TanStack/bling/tree/62703ba3d204a0315ef6043d0f59c5b70c27e73d) | yes | 4 | 0 | 3 | 0 | 1 | 0 | 0 | +| [charts](https://github.com/TanStack/charts) | [dc4bc708](https://github.com/TanStack/charts/tree/dc4bc708bac2e90d9669e8bf139dda97ad10603d) | yes | 205 | 0 | 187 | 5 | 12 | 1 | 0 | | [cli](https://github.com/TanStack/cli) | [8711a8b4](https://github.com/TanStack/cli/tree/8711a8b4428722a417960050871a014b3a9bc839) | yes | 111 | 2 | 79 | 0 | 23 | 9 | 0 | | [config](https://github.com/tanstack/config) | [c7356fbf](https://github.com/tanstack/config/tree/c7356fbf42f988b1b690cc9cf57c55f14ddd3487) | no | 16 | 0 | 12 | 2 | 1 | 1 | 0 | -| [db](https://github.com/tanstack/db) | [f9b4f34b](https://github.com/tanstack/db/tree/f9b4f34b3eee273bbd956a1a9473fc6af6cad58f) | no | 547 | 0 | 354 | 14 | 173 | 6 | 0 | -| [form](https://github.com/TanStack/form) | [90118027](https://github.com/TanStack/form/tree/901180274c05ed3672fd280337499f7104fdd8cf) | no | 186 | 0 | 170 | 3 | 11 | 2 | 0 | -| [highlight](https://github.com/TanStack/highlight) | [a8ed3e6c](https://github.com/TanStack/highlight/tree/a8ed3e6c585165c9166e39afd1bd7d835a0ddf95) | no | 35 | 0 | 29 | 6 | 0 | 0 | 0 | +| [db](https://github.com/tanstack/db) | [12cbc90a](https://github.com/tanstack/db/tree/12cbc90a32a121a858051b69798a602cc7ddc43a) | no | 606 | 0 | 395 | 14 | 190 | 7 | 0 | +| [form](https://github.com/TanStack/form) | [90118027](https://github.com/TanStack/form/tree/901180274c05ed3672fd280337499f7104fdd8cf) | no | 186 | 0 | 171 | 3 | 10 | 2 | 0 | +| [highlight](https://github.com/TanStack/highlight) | [8aab48b5](https://github.com/TanStack/highlight/tree/8aab48b5da618caae7c98728042dbe9cb38bb2d0) | no | 51 | 0 | 39 | 7 | 5 | 0 | 0 | | [hotkeys](https://github.com/TanStack/hotkeys) | [6adcc3a1](https://github.com/TanStack/hotkeys/tree/6adcc3a1b0a28350eb421f6aeebcf0f94d177817) | no | 329 | 0 | 280 | 10 | 16 | 23 | 0 | | [intent](https://github.com/tanstack/intent) | [e5745472](https://github.com/tanstack/intent/tree/e57454726b97f489336283d30ff7ae43e1817af4) | no | 24 | 0 | 10 | 3 | 10 | 1 | 0 | -| [markdown](https://github.com/TanStack/markdown) | [9bbe1220](https://github.com/TanStack/markdown/tree/9bbe1220c5b77ca89cadaa41d83f8172dfc1da82) | yes | 35 | 0 | 25 | 6 | 4 | 0 | 0 | +| [markdown](https://github.com/TanStack/markdown) | [e56d191c](https://github.com/TanStack/markdown/tree/e56d191c0f3229b1d1a640b8c58245c788c6f644) | yes | 48 | 0 | 27 | 10 | 11 | 0 | 0 | | [pacer](https://github.com/tanstack/pacer) | [556125d7](https://github.com/tanstack/pacer/tree/556125d73ff020b1f854bded14cb82e58581e3b6) | no | 334 | 0 | 301 | 1 | 13 | 19 | 0 | | [preact](https://github.com/TanStack/preact) | [4dc9b508](https://github.com/TanStack/preact/tree/4dc9b508dd37544b858bfa52d28e12d219effdbb) | yes | 8 | 0 | 3 | 0 | 5 | 0 | 0 | -| [query](https://github.com/TanStack/query) | [f727aa9b](https://github.com/TanStack/query/tree/f727aa9bcef3b77b086888583ca04f6a19f6bcb9) | yes | 671 | 0 | 585 | 18 | 66 | 2 | 0 | +| [query](https://github.com/TanStack/query) | [8a930246](https://github.com/TanStack/query/tree/8a930246ee250db03e4d8472605e8525238f936f) | no | 671 | 0 | 585 | 18 | 66 | 2 | 0 | | [ranger](https://github.com/TanStack/ranger) | [5bdf4862](https://github.com/TanStack/ranger/tree/5bdf486292418cc6e71f72fa023ddb1295051f04) | no | 20 | 0 | 19 | 0 | 1 | 0 | 0 | | [react-charts](https://github.com/TanStack/react-charts) | [e4b1c9c6](https://github.com/TanStack/react-charts/tree/e4b1c9c6306862847e2a9cfa91fcb3e42163c8d3) | no | 10 | 0 | 10 | 0 | 0 | 0 | 0 | -| [redact](https://github.com/TanStack/redact) | [e1620a13](https://github.com/TanStack/redact/tree/e1620a13aab8935c806238f117ba58559b7cd002) | no | 3 | 0 | 0 | 2 | 1 | 0 | 0 | -| [router](https://github.com/tanstack/router) | [6f882b7d](https://github.com/tanstack/router/tree/6f882b7dae8870e0f51791ec89015f75c74e4c9b) | no | 506 | 0 | 308 | 12 | 88 | 98 | 0 | +| [redact](https://github.com/TanStack/redact) | [2e3c75c4](https://github.com/TanStack/redact/tree/2e3c75c483365d26ae326ec0fdae96fa5c690451) | yes | 5 | 0 | 2 | 3 | 0 | 0 | 0 | +| [router](https://github.com/tanstack/router) | [6f882b7d](https://github.com/tanstack/router/tree/6f882b7dae8870e0f51791ec89015f75c74e4c9b) | no | 506 | 0 | 308 | 12 | 89 | 97 | 0 | | [select](https://github.com/TanStack/select) | [1aa8f85f](https://github.com/TanStack/select/tree/1aa8f85f3312f7f891b8808f1b9688cae4a9ef2a) | yes | 1 | 0 | 1 | 0 | 0 | 0 | 0 | -| [start-evals](https://github.com/TanStack/start-evals) | [cb854d8c](https://github.com/TanStack/start-evals/tree/cb854d8c8a1a3754880913e99fbf2452c9ea1557) | yes | 741 | 0 | 474 | 134 | 103 | 30 | 0 | -| [start-rsc](https://github.com/tanstack/start-rsc) | [b5ef4abf](https://github.com/tanstack/start-rsc/tree/b5ef4abfe883d072b78e5cba24cc321b42faed10) | yes | 433 | 0 | 290 | 15 | 46 | 82 | 0 | +| [start-evals](https://github.com/TanStack/start-evals) | [e30b88fa](https://github.com/TanStack/start-evals/tree/e30b88faf4a5d3e017bccadc7fe2594710dfe1e0) | yes | 70 | 0 | 63 | 7 | 0 | 0 | 0 | +| [start-rsc](https://github.com/tanstack/start-rsc) | [b5ef4abf](https://github.com/tanstack/start-rsc/tree/b5ef4abfe883d072b78e5cba24cc321b42faed10) | yes | 433 | 0 | 290 | 15 | 47 | 81 | 0 | | [store](https://github.com/TanStack/store) | [88283a26](https://github.com/TanStack/store/tree/88283a2647a2fb2abb422cb15e3bdc4226853ac4) | no | 49 | 0 | 41 | 5 | 3 | 0 | 0 | -| [table](https://github.com/TanStack/table) | [965b55ec](https://github.com/TanStack/table/tree/965b55ec0bfb3983596d56835bc81a9e29b4ee72) | no | 167 | 0 | 152 | 4 | 11 | 0 | 0 | -| [tanstack.com](https://github.com/TanStack/tanstack.com) | [b0c08c86](https://github.com/TanStack/tanstack.com/tree/b0c08c86704b677ab5ed720a2ce27b51db4da1f9) | no | 80 | 0 | 58 | 10 | 7 | 5 | 0 | +| [table](https://github.com/TanStack/table) | [50feff85](https://github.com/TanStack/table/tree/50feff8584d48972329d2d2b5795e152cf522178) | no | 1384 | 0 | 1269 | 14 | 59 | 42 | 0 | +| [tanstack.com](https://github.com/TanStack/tanstack.com) | [84f0b9c4](https://github.com/TanStack/tanstack.com/tree/84f0b9c453ea741787a6162c13896c9492d97194) | no | 103 | 0 | 68 | 15 | 13 | 7 | 0 | | [virtual](https://github.com/TanStack/virtual) | [43ef13c5](https://github.com/TanStack/virtual/tree/43ef13c5002f64fa7a6f78cc3676670401370f83) | yes | 65 | 0 | 52 | 2 | 10 | 1 | 0 | | [workflow](https://github.com/TanStack/workflow) | [70ced3cc](https://github.com/TanStack/workflow/tree/70ced3ccc78c1899a9f2ddfb6e9a2a6243ae973d) | yes | 128 | 0 | 84 | 17 | 16 | 11 | 0 | @@ -56,42 +57,41 @@ This report measures practical corpus behavior. Marked is a differential referen | Kind | Documents | | :--- | ---: | -| blog | 62 | -| changes | 196 | -| docs | 2798 | -| fixture | 75 | -| other | 820 | -| readme | 875 | +| blog | 69 | +| changes | 263 | +| docs | 4590 | +| fixture | 115 | +| other | 438 | +| readme | 833 | ## Syntax usage | Feature | Support | Documents | Description | | :--- | :--- | ---: | :--- | -| atx-heading | profile | 4453 | ATX heading | -| fenced-code | profile | 3060 | Fenced code block | -| frontmatter | profile | 2922 | Leading YAML-style frontmatter | -| raw-html | opt-in | 661 | Raw HTML tag | -| blockquote | profile | 498 | Block quote | -| nested-list | profile | 424 | Nested list item | -| literal-autolink | unsupported | 392 | Bare URL outside explicit link syntax | -| table | profile | 385 | Pipe table | -| callout | extension | 212 | GitHub-style callout | -| four-space-indent | review | 203 | Four-space indentation, possibly indented code | +| atx-heading | profile | 5947 | ATX heading | +| fenced-code | profile | 5055 | Fenced code block | +| frontmatter | profile | 4898 | Leading YAML-style frontmatter | +| raw-html | opt-in | 1236 | Raw HTML tag | +| blockquote | profile | 851 | Block quote | +| table | profile | 647 | Pipe table | +| literal-autolink | unsupported | 516 | Bare URL outside explicit link syntax | +| nested-list | profile | 485 | Nested list item | +| four-space-indent | review | 316 | Four-space indentation, possibly indented code | +| callout | extension | 253 | GitHub-style callout | | reference-link | profile | 165 | Reference link or image definition | +| code-metadata | profile | 151 | Metadata after a fenced-code language | | section-marker | extension | 146 | TanStack section marker written as a reference definition | -| task-list | profile | 79 | Task list item | -| comment-component | extension | 60 | Comment-delimited docs component | -| tab-indentation | unsupported | 48 | Tab-indented content | -| delimiter-edge | review | 41 | Malformed or legacy emphasis or code delimiter usage | -| code-metadata | profile | 40 | Metadata after a fenced-code language | -| strikethrough | profile | 39 | Strikethrough delimiter | -| entity-reference | partial | 10 | Named or numeric HTML entity | -| site-template | site-extension | 9 | Liquid, JSX-comment, or site template directive | -| combined-emphasis | profile | 7 | Combined strong and emphasis delimiters | -| angle-autolink | unsupported | 4 | Angle-bracket URL or email autolink | +| comment-component | extension | 131 | Comment-delimited docs component | +| delimiter-edge | review | 111 | Malformed or legacy emphasis or code delimiter usage | +| task-list | profile | 101 | Task list item | +| strikethrough | profile | 80 | Strikethrough delimiter | +| tab-indentation | unsupported | 49 | Tab-indented content | +| combined-emphasis | profile | 38 | Combined strong and emphasis delimiters | +| entity-reference | partial | 11 | Named or numeric HTML entity | +| angle-autolink | unsupported | 6 | Angle-bracket URL or email autolink | +| site-template | site-extension | 6 | Liquid, JSX-comment, or site template directive | | fence-info-edge | review | 3 | Generated or nonstandard fenced-code info string | | footnote | profile | 3 | Footnote reference or definition | -| setext-heading | unsupported | 2 | Setext heading | | hard-break | profile | 2 | Backslash hard line break | | multiline-link | review | 1 | Link or image destination split across lines | @@ -101,46 +101,46 @@ These are triage leads, ordered by parser errors, content differences, structura | Repository | Path | Kind | Result | Features | | :--- | :--- | :--- | :--- | :--- | -| start-evals | archive/legacy-harness/runs/2026-05-31T21-11-29-191Z-codex-context-v2-strict-baseline/executions/r2__codex-desktop-gpt-5__agent-native-framework-tools__server-boundary-secret__nextjs-app-router/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-05-31T21-11-29-191Z-codex-context-v2-strict-baseline/executions/r1__codex-desktop-gpt-5__baseline__protected-settings__nextjs-app-router/transcript.md | other | content | atx-heading, nested-list | -| start-evals | archive/legacy-harness/runs/2026-05-31T21-11-29-191Z-codex-context-v2-strict-baseline/executions/r1__codex-desktop-gpt-5__agent-native-framework-tools__protected-settings__tanstack-start/transcript.md | other | content | atx-heading, nested-list | -| start-evals | archive/legacy-harness/runs/2026-05-31T21-11-29-191Z-codex-context-v2-strict-baseline/executions/r1__codex-desktop-gpt-5__agent-native-framework-tools__protected-settings__nextjs-app-router/transcript.md | other | content | atx-heading, nested-list | -| router | packages/eslint-plugin-start/no-client-code-in-server-component.md | other | content | atx-heading, fenced-code, nested-list | -| start-rsc | packages/eslint-plugin-start/no-client-code-in-server-component.md | other | content | atx-heading, fenced-code, nested-list | -| start-evals | archive/legacy-harness/runs/2026-05-31T21-11-29-191Z-codex-context-v2-strict-baseline/executions/r1__codex-desktop-gpt-5__official-docs__typed-search-dashboard__nextjs-app-router/transcript.md | other | content | atx-heading, nested-list | -| start-evals | archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r1__codex-desktop-gpt-5__baseline__error-and-validation-flow__nextjs-app-router/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r1__codex-desktop-gpt-5__baseline__server-boundary-secret__nextjs-app-router/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r2__codex-desktop-gpt-5__baseline__server-boundary-secret__nextjs-app-router/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r1__codex-desktop-gpt-5__baseline__error-and-validation-flow__tanstack-start/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-06T03-49-20-675Z-publishable-second-topoff-codex-desktop-gpt-5-baseline-robust/executions/r1__codex-desktop-gpt-5__baseline__error-and-validation-flow__nextjs-app-router/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-06T03-49-20-675Z-publishable-second-topoff-codex-desktop-gpt-5-baseline-robust/executions/r2__codex-desktop-gpt-5__baseline__error-and-validation-flow__nextjs-app-router/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r1__codex-desktop-gpt-5__baseline__server-boundary-secret__tanstack-start/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r2__codex-desktop-gpt-5__baseline__server-boundary-secret__tanstack-start/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r1__codex-desktop-gpt-5__baseline__protected-settings__nextjs-app-router/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-06T03-49-20-675Z-publishable-second-topoff-codex-desktop-gpt-5-baseline-robust/executions/r1__codex-desktop-gpt-5__baseline__server-boundary-secret__nextjs-app-router/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r1__codex-desktop-gpt-5__baseline__error-and-validation-flow__nextjs-app-router/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-06T03-49-20-675Z-publishable-second-topoff-codex-desktop-gpt-5-baseline-robust/executions/r1__codex-desktop-gpt-5__baseline__error-and-validation-flow__tanstack-start/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-06T03-49-20-675Z-publishable-second-topoff-codex-desktop-gpt-5-baseline-robust/executions/r2__codex-desktop-gpt-5__baseline__error-and-validation-flow__tanstack-start/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-08T05-58-21-915Z-evidence-debt-replace-codex-desktop-gpt-5__baseline-codex-desktop-gpt-5-baseline/executions/r1__codex-desktop-gpt-5__baseline__protected-settings__tanstack-start/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r1__codex-desktop-gpt-5__baseline__server-boundary-secret__nextjs-app-router/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r2__codex-desktop-gpt-5__baseline__server-boundary-secret__nextjs-app-router/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-06T03-49-20-675Z-publishable-second-topoff-codex-desktop-gpt-5-baseline-robust/executions/r1__codex-desktop-gpt-5__baseline__server-boundary-secret__tanstack-start/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r1__codex-desktop-gpt-5__baseline__error-and-validation-flow__tanstack-start/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r1__codex-desktop-gpt-5__baseline__server-boundary-secret__tanstack-start/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r2__codex-desktop-gpt-5__baseline__server-boundary-secret__tanstack-start/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r1__codex-desktop-gpt-5__baseline__protected-settings__nextjs-app-router/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-06-04T15-39-36-840Z-2026-06-04-codex-baseline-publishable-topoff-min/executions/r1__codex-desktop-gpt-5__baseline__protected-settings__tanstack-start/transcript.md | other | content | atx-heading | -| start-evals | archive/legacy-harness/runs/2026-05-31T21-11-29-191Z-codex-context-v2-strict-baseline/executions/r1__codex-desktop-gpt-5__official-docs__typed-search-dashboard__tanstack-start/transcript.md | other | content | atx-heading | | db | packages/react-db/CHANGELOG.md | changes | structure | atx-heading, fenced-code, nested-list | | db | packages/vue-db/CHANGELOG.md | changes | structure | atx-heading, fenced-code, nested-list | | router | packages/react-start/CHANGELOG.md | changes | structure | atx-heading, nested-list | | db | docs/collections/powersync-collection.md | docs | structure | atx-heading, fenced-code, frontmatter, table | +| db | packages/svelte-db/CHANGELOG.md | changes | structure | atx-heading, fenced-code, nested-list | +| table | perf-skipped.md | other | structure | atx-heading, fenced-code, table | | router | packages/start-plugin-core/CHANGELOG.md | changes | structure | atx-heading, nested-list | -| router | packages/solid-start/CHANGELOG.md | changes | structure | atx-heading, nested-list | +| db | packages/solid-db/CHANGELOG.md | changes | structure | atx-heading, fenced-code, nested-list | | db | packages/trailbase-db-collection/CHANGELOG.md | changes | structure | atx-heading, fenced-code, nested-list | +| router | packages/solid-start/CHANGELOG.md | changes | structure | atx-heading, nested-list | +| ai | docs/chat/connection-adapters.md | docs | structure | atx-heading, blockquote, code-metadata, fenced-code, frontmatter, table | | router | packages/vue-start/CHANGELOG.md | changes | structure | atx-heading, nested-list | | db | docs/guides/collection-options-creator.md | docs | structure | atx-heading, fenced-code, frontmatter | -| db | packages/svelte-db/CHANGELOG.md | changes | structure | atx-heading, fenced-code, nested-list | +| db | packages/angular-db/CHANGELOG.md | changes | structure | atx-heading, nested-list | +| virtual | docs/api/virtualizer.md | docs | structure | atx-heading, blockquote, fenced-code, frontmatter | +| query | docs/framework/react/guides/ssr.md | docs | structure | atx-heading, blockquote, fenced-code, frontmatter, nested-list | +| ai | packages/ai/skills/ai-core/adapter-configuration/SKILL.md | other | structure | atx-heading, blockquote, code-metadata, fenced-code, frontmatter, table | +| router | packages/react-start-rsc/CHANGELOG.md | changes | structure | atx-heading, nested-list | +| db | packages/rxdb-db-collection/CHANGELOG.md | changes | structure | atx-heading, nested-list | +| db | packages/offline-transactions/CHANGELOG.md | changes | structure | atx-heading, fenced-code, nested-list | +| router | packages/start-server-core/CHANGELOG.md | changes | structure | atx-heading, nested-list | +| router | docs/router/how-to/integrate-chakra-ui.md | docs | structure | atx-heading, fenced-code, frontmatter, task-list | +| start-rsc | docs/router/framework/react/how-to/integrate-chakra-ui.md | docs | structure | atx-heading, fenced-code, frontmatter, task-list | +| ai | examples/ts-vue-chat/CHANGELOG.md | changes | structure | atx-heading, nested-list | +| tanstack.com | .agents/skills/design-motion-principles/references/output-format.md | other | structure | atx-heading, fenced-code, table | +| table | docs/framework/react/guide/table-state.md | docs | structure | atx-heading, blockquote, fenced-code, frontmatter | +| router | packages/react-start-server/CHANGELOG.md | changes | structure | atx-heading, nested-list | +| start-rsc | docs/router/framework/react/how-to/use-environment-variables.md | docs | structure | atx-heading, fenced-code, frontmatter, task-list | +| router | docs/router/how-to/use-environment-variables.md | docs | structure | atx-heading, fenced-code, frontmatter, task-list | +| router | docs/router/how-to/integrate-framer-motion.md | docs | structure | atx-heading, fenced-code, frontmatter, task-list | +| start-rsc | docs/router/framework/react/how-to/integrate-framer-motion.md | docs | structure | atx-heading, fenced-code, frontmatter, task-list | +| router | packages/router-plugin/CHANGELOG.md | changes | structure | atx-heading, nested-list | +| router | packages/solid-start-server/CHANGELOG.md | changes | structure | atx-heading, nested-list | +| ai | docs/media/text-to-speech.md | docs | structure | atx-heading, blockquote, code-metadata, fenced-code, frontmatter, table | +| ai | examples/ts-svelte-chat/CHANGELOG.md | changes | structure | atx-heading, nested-list | +| ai | .agents/skills/pr-sweep/SKILL.md | other | structure | atx-heading, fenced-code, frontmatter, table | +| ai | .claude/skills/pr-sweep/SKILL.md | other | structure | atx-heading, fenced-code, frontmatter, table | +| ai | .grok/skills/pr-sweep/SKILL.md | other | structure | atx-heading, fenced-code, frontmatter, table | +| workflow | research/SRC_SKEW_AND_RESUMPTION.md | other | structure | atx-heading, blockquote, fenced-code, nested-list, table | +| db | packages/powersync-db-collection/CHANGELOG.md | changes | structure | atx-heading, fenced-code, nested-list | ## Interpretation policy diff --git a/scripts/audit-corpus.ts b/scripts/audit-corpus.ts index a1d4317..8003a37 100644 --- a/scripts/audit-corpus.ts +++ b/scripts/audit-corpus.ts @@ -619,7 +619,7 @@ function decodeBasicEntities(value: string): string { .replace(/&/g, '&') } -function stripFrontmatter(source: string): string { +export function stripFrontmatter(source: string): string { const normalized = source.replace(/\r\n?/g, '\n') if (!normalized.startsWith('---\n')) return normalized const match = /\n---(?:\n|$)/.exec(normalized.slice(4)) diff --git a/scripts/compare-revision.mjs b/scripts/compare-revision.mjs new file mode 100644 index 0000000..b8ee413 --- /dev/null +++ b/scripts/compare-revision.mjs @@ -0,0 +1,185 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises' +import { createRequire } from 'node:module' +import { relative, resolve } from 'node:path' +import { gzipSync, brotliCompressSync } from 'node:zlib' +import { build } from 'esbuild' +import { normalizeConformanceHtml } from './conformance-data.ts' +import { entries, publicEntries } from './measure-size.ts' +import { compareRenderedHtml, stripFrontmatter } from './audit-corpus.ts' +import { marked } from 'marked' + +// Compare both revisions with the same compiler, runtime, fixtures, and process. +const root = process.cwd() +const revision = process.argv[2] +assert(revision, 'Usage: node --import tsx scripts/compare-revision.mjs [--no-bench] [--corpus]') +const directory = await stat(revision).then(value => value.isDirectory()).catch(() => false) +const baseline = directory ? resolve(revision) : execFileSync('git', ['rev-parse', '--verify', `${revision}^{commit}`], { encoding: 'utf8' }).trim() +const sourceCache = new Map() +const baselinePlugin = { + name: 'baseline-source', + setup(builder) { + builder.onLoad({ filter: /\.ts$/ }, async ({ path }) => { + const name = relative(root, path).replaceAll('\\', '/') + if (!name.startsWith('src/')) return + if (!sourceCache.has(name)) { + sourceCache.set(name, directory ? await readFile(resolve(baseline, name), 'utf8') : execFileSync('git', ['show', `${baseline}:${name}`], { encoding: 'utf8' })) + } + return { contents: sourceCache.get(name), loader: 'ts' } + }) + }, +} + +async function bundle(contents, previous, external = [], format = 'esm') { + const result = await build({ + stdin: { contents, resolveDir: root, loader: 'ts' }, + bundle: true, + write: false, + minify: true, + treeShaking: true, + platform: 'browser', + format, + target: 'es2022', + external, + plugins: previous ? [baselinePlugin] : [], + logLevel: 'silent', + }) + return result.outputFiles[0].contents +} + +const sizes = [] +for (const entry of [...entries.filter(entry => entry.group === 'tanstack'), ...publicEntries]) { + const results = [] + for (const previous of [true, false]) { + const code = await bundle(entry.contents, previous, entry.external) + results.push({ min: code.length, gzip: gzipSync(code, { level: 9 }).length, brotli: brotliCompressSync(code).length }) + } + sizes.push({ name: entry.name, before: results[0], after: results[1] }) +} +console.table(sizes.map(row => ({ name: row.name, before: row.before.gzip, after: row.after.gzip, delta: row.after.gzip - row.before.gzip }))) + +const apis = [] +for (const previous of [true, false]) { + const code = await bundle("export * from './src/index.ts'; export * from './src/extensions/streaming.ts'; export * from './src/extensions/docs.ts'", previous) + apis.push(await import(`data:text/javascript;base64,${Buffer.from(code).toString('base64')}#${previous}`)) +} +const require = createRequire(import.meta.url) +const passing = apis.map(api => require('commonmark-spec').tests.filter(example => + normalizeConformanceHtml(api.renderHtml(example.markdown, { allowHtml: true, headingIds: false })) === normalizeConformanceHtml(example.html), +).map(example => example.number)) +const compatibility = { + before: passing[0].length, + after: passing[1].length, + gained: passing[1].filter(number => !passing[0].includes(number)), + lost: passing[0].filter(number => !passing[1].includes(number)), +} +console.log('CommonMark:', compatibility) + +const fixtures = [] +for (const name of (await readdir('fixtures/benchmark')).filter(name => name.endsWith('.md')).sort()) { + fixtures.push({ name, source: await readFile(`fixtures/benchmark/${name}`, 'utf8') }) +} +fixtures.push({ name: 'long-prose', source: 'Ordinary prose with words and punctuation, without inline markup. '.repeat(1000) }) +fixtures.push({ name: 'unmatched-brackets', source: '['.repeat(32000) }) +let sink = 0 +function sample(run, iterations) { + const start = performance.now() + for (let index = 0; index < iterations; index++) { + const output = run() + sink += typeof output === 'string' ? output.length : output.children.length + } + return (performance.now() - start) / iterations +} +function median(values) { + return [...values].sort((a, b) => a - b)[Math.floor(values.length / 2)] +} +const timings = [] +function benchmark(name, mode, runs) { + // Warm both JIT paths, then alternate which implementation runs first. + for (const run of runs) sample(run, 1000) + const iterations = Math.max(10, Math.min(100000, Math.ceil(25 / Math.max(sample(runs[0], 100), sample(runs[1], 100))))) + const samples = [[], []] + for (let round = 0; round < 9; round++) { + for (const index of round % 2 ? [1, 0] : [0, 1]) samples[index].push(sample(runs[index], iterations)) + } + const before = median(samples[0]) + const after = median(samples[1]) + const result = { fixture: name, mode, iterations, beforeMs: before, afterMs: after, ratio: after / before, samples } + timings.push(result) + console.log(`${name} ${mode}: ${before.toFixed(4)} -> ${after.toFixed(4)} ms (${result.ratio.toFixed(2)}x)`) +} + +for (const { name, source } of process.argv.includes('--no-bench') ? [] : fixtures) { + const modes = ['parse', 'renderAst', 'parseRender'] + if (name === 'ai-response.md') modes.push('streaming') + for (const mode of modes) { + const runs = apis.map(api => { + const ast = api.parseMarkdown(source) + const options = { extensions: [api.streamingMarkdownExtension()], headingIds: false, frontmatter: false } + if (mode === 'parse') return () => api.parseMarkdown(source) + if (mode === 'renderAst') return () => api.renderHtml(ast) + if (mode === 'parseRender') return () => api.renderHtml(source) + return () => { + for (let end = 32; end < source.length; end += 32) sink += api.renderHtml(source.slice(0, end), options).length + return api.renderHtml(source, options) + } + }) + benchmark(name, mode, runs) + } +} + +if (!process.argv.includes('--no-bench')) { + for (const adapter of ['react', 'octane']) { + const suffix = adapter === 'react' ? 'React' : 'Octane' + const source = `import { Markdown, renderMarkdown${suffix} as render } from './src/${adapter}.ts'; export { render }; ` + (adapter === 'react' + ? "import {createElement} from 'react'; import {renderToStaticMarkup} from 'react-dom/server'; export const ssr = input => renderToStaticMarkup(createElement(Markdown, {children: input}));" + : "import {renderToStaticMarkup} from 'octane/server'; export const ssr = input => renderToStaticMarkup(Markdown, {children: input}).html;") + const adapters = [] + for (const previous of [true, false]) { + const code = await bundle(source, previous, ['react', 'react-dom/server', 'octane', 'octane/server'], 'cjs') + const module = { exports: {} } + new Function('require', 'module', 'exports', Buffer.from(code).toString())(require, module, module.exports) + adapters.push(module.exports) + } + for (const { name, source } of fixtures.filter(fixture => /^(ai-response|small-doc|prose-heavy|tables-lists)\.md$/.test(fixture.name))) { + benchmark(name, `${adapter}:nodes`, adapters.map((api, index) => { + const ast = apis[index].parseMarkdown(source) + return () => ({ children: api.render(ast) }) + })) + benchmark(name, `${adapter}:ssr`, adapters.map(api => () => api.ssr(source))) + } + } +} +await mkdir('artifacts', { recursive: true }) +const corpus = [] +if (process.argv.includes('--corpus')) { + for (const name of ['external-corpus', 'tanstack-corpus']) { + const files = JSON.parse(await readFile(`artifacts/corpus/${name}.files.json`, 'utf8')) + for (const profile of ['core', 'docs']) { + const result = { name, profile, compared: 0, changed: 0, lostExact: [], gainedExact: [], newContentDifferences: [], changes: [] } + const options = apis.map(api => ({ allowHtml: true, headingIds: false, extensions: profile === 'docs' ? api.docsMarkdownExtensions() : [] })) + for (const file of files.filter(file => file.format === 'md' && !file.error)) { + const path = resolve(name === 'external-corpus' ? '.corpus/external' : '..', file.repository, file.path) + const source = await readFile(path, 'utf8') + const outputs = apis.map((api, index) => api.renderHtml(source, options[index])) + result.compared++ + if (outputs[0] === outputs[1]) continue + result.changed++ + const reference = String(marked.parse(stripFrontmatter(source), { gfm: true })) + const comparisons = outputs.map(output => compareRenderedHtml(output, reference)) + const id = `${file.repository}/${file.path}` + if (comparisons[0] === 'exact' && comparisons[1] !== 'exact') result.lostExact.push(id) + if (comparisons[0] !== 'exact' && comparisons[1] === 'exact') result.gainedExact.push(id) + if (comparisons[0] !== 'content' && comparisons[1] === 'content') result.newContentDifferences.push(id) + let offset = 0 + while (offset < Math.min(outputs[0].length, outputs[1].length) && outputs[0][offset] === outputs[1][offset]) offset++ + result.changes.push({ path: id, comparisons, before: outputs[0].slice(Math.max(0, offset - 80), offset + 200), after: outputs[1].slice(Math.max(0, offset - 80), offset + 200) }) + } + corpus.push(result) + console.log('Corpus:', { name, profile, compared: result.compared, changed: result.changed, lostExact: result.lostExact.length, gainedExact: result.gainedExact.length, newContentDifferences: result.newContentDifferences }) + } + } +} +await writeFile(resolve('artifacts/audit-comparison.json'), JSON.stringify({ baseline, node: process.version, generatedAt: new Date().toISOString(), sizes, compatibility, timings, corpus, sink }, null, 2) + '\n') +assert.deepEqual(compatibility.lost, [], 'Previously passing CommonMark examples must not regress') diff --git a/scripts/conformance-data.ts b/scripts/conformance-data.ts index 9e9dc0c..220e592 100644 --- a/scripts/conformance-data.ts +++ b/scripts/conformance-data.ts @@ -29,7 +29,7 @@ export interface ConformanceResult { } const baselineRanges = - '13,15,17,19,21,28-31,35,42-47,49-54,56-58,61-68,70-74,77-79,87-88,92,94,97,99,101,104-106,108-109,113,119-130,135-137,139-140,142-143,146-147,149-167,174-175,178,186-190,197,199,207,209-210,212,219-224,227-230,234-235,237,239-241,243-246,248-249,255-256,258-263,265-266,268-269,275-277,279,281-284,291,294,296-299,301-303,305-306,308,314-317,320-329,334,338-339,343,345,348,350,355-357,361,363-365,370,377-378,381-382,390,394-396,403-405,420,422-424,428-430,433-434,436-438,440-441,443,446-450,452-453,455,458-463,469,482-484,496-497,501,510-515,517,522-523,528-529,531,535,545,547-549,551-552,565,569,571-572,578-583,590,611-612,617,625-628,630-631,641,644-652' + '12-15,17,19,21,28-31,35,42-47,49-58,61-68,70-79,87-88,92,94,97,99,101,104-106,108-109,113,119-133,135-137,139-140,142-143,146-147,149-167,174-175,177-179,186-190,192,197,199-201,203-205,207,209-210,212,214,216,219-224,227-230,234-235,237,239-241,243-246,248-249,255-256,258-263,265-269,275-277,279,281-284,291,294,296-299,301-308,314-317,319-334,338-340,343,345,348-350,355-365,370-372,374-378,381-388,390,393-406,410-411,414-416,418,420,422-424,428-430,433-434,436-438,440-441,443,446-450,452-453,455,458-463,467,469,482-488,490-491,494-498,500-501,505,508-520,522-523,527-533,535,539,542-545,547-563,565-567,569-593,607-608,611-612,617-618,621,625-628,630-631,641,644-652' export const commonMarkBaseline = expandRanges(baselineRanges) diff --git a/scripts/measure-size.ts b/scripts/measure-size.ts index a25cba9..cb63f38 100644 --- a/scripts/measure-size.ts +++ b/scripts/measure-size.ts @@ -2,11 +2,20 @@ import { brotliCompressSync, gzipSync } from 'node:zlib' import { mkdir, readFile, writeFile } from 'node:fs/promises' import { join } from 'node:path' +import { fileURLToPath } from 'node:url' import { build } from 'esbuild' const reportsDir = join(process.cwd(), 'reports') -const entries = [ +const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8')) +export const publicEntries = Object.keys(packageJson.exports).map(name => ({ + group: 'tanstack-public', + name, + contents: `import * as entry from './src/${name === '.' ? 'index' : name.slice(2)}.ts'; console.log(entry)`, + external: ['react', 'octane'], +})) + +export const entries = [ { group: 'tanstack', name: 'parser only', @@ -97,7 +106,7 @@ async function main() { await mkdir(reportsDir, { recursive: true }) const results = [] - for (const entry of entries) { + for (const entry of [...entries, ...publicEntries]) { try { if (entry.files) { const files = await Promise.all(entry.files.map(file => readFile(join(process.cwd(), file)))) @@ -177,7 +186,9 @@ function renderSizeReport(results) { return lines.join('\n') } -main().catch(error => { - console.error(error) - process.exitCode = 1 -}) +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(error => { + console.error(error) + process.exitCode = 1 + }) +} diff --git a/src/extensions/callouts.ts b/src/extensions/callouts.ts index aa577b6..4a18a2f 100644 --- a/src/extensions/callouts.ts +++ b/src/extensions/callouts.ts @@ -19,12 +19,6 @@ export function parseCalloutBlock(context: Parameters\s?(.*)$/) if (!quoted) break body.push(quoted[1]!) diff --git a/src/extensions/comment-components.ts b/src/extensions/comment-components.ts index 6f435f7..7540b0c 100644 --- a/src/extensions/comment-components.ts +++ b/src/extensions/comment-components.ts @@ -25,44 +25,27 @@ export function parseCommentComponentBlock( const start = parseComponentComment(line) if (!start) return undefined - if (!start.block) { - context.consume(1) - return transform( - { - type: 'component', - name: start.name, - attributes: start.attributes, - children: [], - }, - options, - ) - } - - const body: string[] = [] - let cursor = context.index + 1 - let foundEnd = false - - while (cursor < context.lines.length) { - const candidate = context.lines[cursor] ?? '' - if (isEndComment(candidate, start.name)) { - foundEnd = true - cursor++ - break + let body: string | undefined + let consumed = 1 + if (start.block) { + const end = new RegExp(`^ {0,3}\\s*$`, 'i') + for (let cursor = context.index + 1; cursor < context.lines.length; cursor++) { + if (end.test(context.lines[cursor]!)) { + body = context.lines.slice(context.index + 1, cursor).join('\n') + consumed = cursor - context.index + 1 + break + } } - body.push(candidate) - cursor++ } - context.consume(foundEnd ? cursor - context.index : 1) - return transform( - { - type: 'component', - name: start.name, - attributes: start.attributes, - children: foundEnd ? context.parseBlocks(body.join('\n')) : [], - }, - options, - ) + context.consume(consumed) + const node: ComponentNode = { + type: 'component', + name: start.name, + attributes: start.attributes, + children: body === undefined ? [] : context.parseBlocks(body), + } + return options.transformComponent?.(node) ?? node } export function parseComponentComment(line: string): ComponentComment | undefined { @@ -76,22 +59,10 @@ export function parseComponentComment(line: string): ComponentComment | undefine } export function parseAttributes(value: string): Record { - const attrs: Record = {} + const attrs: Record = Object.create(null) const regex = /([A-Za-z_][\w:-]*)(?:=(?:"([^"]*)"|'([^']*)'|([^\s"']+)))?/g for (const match of value.matchAll(regex)) { attrs[match[1]!] = match[2] ?? match[3] ?? match[4] ?? 'true' } return attrs } - -function transform(node: ComponentNode, options: CommentComponentOptions) { - return options.transformComponent?.(node) ?? node -} - -function isEndComment(line: string, name: string): boolean { - return new RegExp(`^ {0,3}\\s*$`, 'i').test(line) -} - -function escapeRegex(value: string) { - return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') -} diff --git a/src/extensions/headings.ts b/src/extensions/headings.ts index 9ebd8d3..0176cdc 100644 --- a/src/extensions/headings.ts +++ b/src/extensions/headings.ts @@ -20,7 +20,7 @@ export function headingCollectionExtension(options: HeadingCollectionOptions = { export function collectMarkdownHeadings(document: MarkdownDocument, options: HeadingCollectionOptions = {}): MarkdownHeading[] { const headings: MarkdownHeading[] = [] const skip = new Set(options.skipComponentNames ?? ['tabs']) - collectFromBlocks(document.children, headings, undefined, false, skip) + collectFromBlocks(document.children, headings, undefined, skip) return headings } @@ -28,12 +28,11 @@ function collectFromBlocks( blocks: BlockNode[], headings: MarkdownHeading[], framework: string | undefined, - insideSkippedComponent: boolean, skipComponentNames: Set, ) { for (const block of blocks) { if (block.type === 'heading') { - if (!insideSkippedComponent && block.id) { + if (block.id) { const heading: MarkdownHeading = { id: block.id, text: plainText(block.children), @@ -47,20 +46,20 @@ function collectFromBlocks( } if (block.type === 'list') { - for (const item of block.items) collectFromBlocks(item.children, headings, framework, insideSkippedComponent, skipComponentNames) + for (const item of block.items) collectFromBlocks(item.children, headings, framework, skipComponentNames) continue } if (block.type === 'blockquote' || block.type === 'callout') { - collectFromBlocks(block.children, headings, framework, insideSkippedComponent, skipComponentNames) + collectFromBlocks(block.children, headings, framework, skipComponentNames) continue } if (block.type === 'component') { const name = block.name.toLowerCase() - const nextInsideSkippedComponent = insideSkippedComponent || skipComponentNames.has(name) + if (skipComponentNames.has(name)) continue const nextFramework = block.tagName === 'md-framework-panel' ? block.properties?.['data-framework'] ?? framework : framework - collectFromBlocks(block.children, headings, nextFramework, nextInsideSkippedComponent, skipComponentNames) + collectFromBlocks(block.children, headings, nextFramework, skipComponentNames) } } } diff --git a/src/extensions/shared.ts b/src/extensions/shared.ts index cd0b6fb..459e45a 100644 --- a/src/extensions/shared.ts +++ b/src/extensions/shared.ts @@ -1,4 +1,4 @@ -import type { BlockNode, ComponentNode, HeadingNode } from '../types.js' +import type { BlockNode, ComponentNode } from '../types.js' import { plainText } from '../utils.js' export interface HeadingSection { @@ -32,8 +32,7 @@ export function blocksToText(blocks: BlockNode[]): string { } export function splitByHeading(children: BlockNode[], forcedDepth?: number): HeadingSection[] { - const headings = children.filter((child): child is HeadingNode => child.type === 'heading') - const depth = forcedDepth ?? Math.min(...headings.map(heading => heading.depth)) + const depth = forcedDepth ?? children.reduce((depth, child) => child.type === 'heading' ? Math.min(depth, child.depth) : depth, Infinity) if (!Number.isFinite(depth)) return [] const sections: HeadingSection[] = [] @@ -77,7 +76,7 @@ export function markFrameworkHeadings(blocks: BlockNode[], framework: string): B } } - if (block.type === 'blockquote' || block.type === 'callout') { + if (block.type === 'blockquote' || block.type === 'callout' || block.type === 'component') { return { ...block, children: markFrameworkHeadings(block.children, framework), @@ -94,13 +93,6 @@ export function markFrameworkHeadings(blocks: BlockNode[], framework: string): B } } - if (block.type === 'component') { - return { - ...block, - children: markFrameworkHeadings(block.children, framework), - } - } - return block }) } diff --git a/src/extensions/tabs.ts b/src/extensions/tabs.ts index b7994cd..28aba43 100644 --- a/src/extensions/tabs.ts +++ b/src/extensions/tabs.ts @@ -50,15 +50,13 @@ export function transformFileTabs(node: ComponentNode): ComponentNode { } export function transformPackageManagerTabs(node: ComponentNode): ComponentNode { - const packagesByFramework: Record = {} + const packagesByFramework: Record = Object.create(null) for (const line of blocksToText(node.children).split('\n')) { - const trimmed = line.trim() - if (!trimmed) continue - const colon = trimmed.indexOf(':') + const colon = line.indexOf(':') if (colon === -1) continue - const framework = trimmed.slice(0, colon).trim().toLowerCase() - const packages = trimmed.slice(colon + 1).trim().split(/\s+/).filter(Boolean) + const framework = line.slice(0, colon).trim().toLowerCase() + const packages = line.slice(colon + 1).trim().split(/\s+/).filter(Boolean) if (!framework || packages.length === 0) continue packagesByFramework[framework] ??= [] packagesByFramework[framework]!.push(packages) @@ -81,13 +79,13 @@ export function transformPackageManagerTabs(node: ComponentNode): ComponentNode export function transformBundlerTabs(node: ComponentNode): ComponentNode { const sections = splitByHeading(node.children) - const selected = sections.filter(section => isBundler(section.name.toLowerCase())) + const selected = bundlers.flatMap(bundler => { + const section = sections.find(section => section.name.toLowerCase() === bundler) + return section ? [section] : [] + }) if (!selected.length) return node - const tabs = bundlers - .map(bundler => selected.find(section => section.name.toLowerCase() === bundler)) - .filter((section): section is NonNullable => Boolean(section)) - .map(section => ({ slug: section.name.toLowerCase(), name: section.name.toLowerCase() })) + const tabs = selected.map(section => ({ slug: section.name.toLowerCase(), name: section.name.toLowerCase() })) return { ...node, @@ -96,15 +94,14 @@ export function transformBundlerTabs(node: ComponentNode): ComponentNode { 'data-attributes': JSON.stringify({ tabs }), 'data-bundler-meta': JSON.stringify({ bundlers: tabs.map(tab => tab.slug) }), }, - children: tabs.map((tab, index): ComponentNode => { - const section = selected.find(section => section.name.toLowerCase() === tab.slug)! + children: selected.map((section, index): ComponentNode => { return { type: 'component', name: 'tab-panel', tagName: 'md-tab-panel', attributes: {}, properties: { - 'data-tab-slug': tab.slug, + 'data-tab-slug': section.name.toLowerCase(), 'data-tab-index': String(index), 'data-content': section.children.length === 1 && section.children[0]?.type === 'code' ? 'code-only' : 'mixed', }, @@ -148,7 +145,3 @@ function resolveInstallMode(value: string | undefined): string { if (mode === 'dev-install' || mode === 'local-install') return mode return 'install' } - -function isBundler(value: string): value is (typeof bundlers)[number] { - return (bundlers as readonly string[]).includes(value) -} diff --git a/src/html.ts b/src/html.ts index 5862998..2666468 100644 --- a/src/html.ts +++ b/src/html.ts @@ -1,6 +1,6 @@ import { parseMarkdown } from './parser.js' import type { BlockNode, ComponentNode, FootnoteItemNode, HeadingAnchorOptions, InlineNode, MarkdownDocument, MarkdownInput, RenderOptions, TableCellNode } from './types.js' -import { escapeAttr, escapeHtml } from './utils.js' +import { escapeAttr, escapeHtml, footnoteReferenceId } from './utils.js' export function renderHtml(input: MarkdownInput, options: RenderOptions = {}): string { const document = typeof input === 'string' ? parseMarkdown(input, options) : input @@ -23,7 +23,7 @@ export function renderBlock(node: BlockNode, options: RenderOptions = {}): strin return renderCodeBlock(node, options) case 'list': { const tag = node.ordered ? 'ol' : 'ul' - const start = node.ordered && node.start && node.start !== 1 ? ` start="${node.start}"` : '' + const start = node.ordered && node.start !== undefined && node.start !== 1 ? ` start="${node.start}"` : '' const items = node.items.map(item => `
  • ${renderListItemChildren(item.children, item.checked, node.loose, options)}
  • `).join('\n') return `<${tag}${start}>\n${items}\n` } @@ -67,7 +67,7 @@ export function renderInline(node: InlineNode, options: RenderOptions = {}): str case 'strike': return `${renderInlines(node.children, options)}` case 'footnoteReference': - return `${node.number}` + return `${node.number}` case 'link': return `${renderInlines(node.children, options)}` case 'image': @@ -89,15 +89,10 @@ function renderInlines(nodes: InlineNode[], options: RenderOptions): string { function renderCodeBlock(node: Extract, options: RenderOptions): string { const lang = node.lang ?? 'plaintext' - const preAttrs = [ - `class="tm-code${options.codeLineNumbers ? ' tm-code--line-numbers' : ''}"`, - `data-lang="${escapeAttr(lang)}"`, - node.title ? `data-code-title="${escapeAttr(node.title)}"` : '', - node.file ? `data-filename="${escapeAttr(node.file)}"` : '', - node.framework ? `data-framework="${escapeAttr(node.framework)}"` : '', - ] - .filter(Boolean) - .join(' ') + const preAttrs = `class="tm-code${options.codeLineNumbers ? ' tm-code--line-numbers' : ''}" data-lang="${escapeAttr(lang)}"` + + (node.title ? ` data-code-title="${escapeAttr(node.title)}"` : '') + + (node.file ? ` data-filename="${escapeAttr(node.file)}"` : '') + + (node.framework ? ` data-framework="${escapeAttr(node.framework)}"` : '') const html = options.highlighter?.(node.value, lang, { ...(node.highlightLines && { highlightLines: node.highlightLines }), ...(options.codeLineNumbers !== undefined && { lineNumbers: options.codeLineNumbers }), @@ -110,14 +105,15 @@ function renderCodeBlock(node: Extract, options: Re } function renderListItemChildren(children: BlockNode[], checked: boolean | undefined, loose: boolean | undefined, options: RenderOptions): string { - const first = children[0] - const task = checked === undefined ? '' : ` ` - if (first?.type === 'paragraph') { - const content = `${task}${renderInlines(first.children, options)}` - const rest = children.length > 1 ? `\n${children.slice(1).map(child => renderBlock(child, options)).join('\n')}` : '' - return `${loose ? `

    ${content}

    ` : content}${rest}` + let result = checked === undefined ? '' : ` ` + for (let index = 0; index < children.length; index++) { + const child = children[index]! + if (child.type === 'paragraph' && (!loose || index === 0)) { + result += renderInlines(child.children, options) + if (loose) result = `

    ${result}

    ` + } else result += (index ? '\n' : '') + renderBlock(child, options) } - return `${task}${children.map(child => renderBlock(child, options)).join('\n')}` + return result } function renderCallout(node: Extract, options: RenderOptions): string { @@ -135,32 +131,27 @@ function renderFootnoteItem(item: FootnoteItemNode, options: RenderOptions): str const backref = renderFootnoteBackrefs(item) const lastIndex = item.children.length - 1 - if (lastIndex < 0) return `

    ${backref.trimStart()}

    ` - - return item.children + const result = item.children .map((child, index) => { if (index === lastIndex && child.type === 'paragraph') { return `

    ${renderInlines(child.children, options)}${backref}

    ` } return renderBlock(child, options) }) - .join('\n') + if (item.children[lastIndex]?.type !== 'paragraph') result.push(`

    ${backref.trimStart()}

    `) + return result.join('\n') } function renderFootnoteBackrefs(item: FootnoteItemNode): string { let result = '' for (let index = 1; index <= (item.referenceCount ?? 1); index++) { - const referenceId = index === 1 ? item.id : `${item.id}-${index}` + const referenceId = footnoteReferenceId(item.id, index) const label = index === 1 ? `${item.number}` : `${item.number}-${index}` result += ` ` } return result } -function footnoteReferenceId(node: Extract): string { - return node.referenceIndex && node.referenceIndex > 1 ? `${node.id}-${node.referenceIndex}` : node.id -} - function renderComponent(node: ComponentNode, options: RenderOptions): string { const tag = node.tagName ?? 'md-comment-component' const attrs = renderComponentAttrs(node) @@ -207,7 +198,5 @@ function renderComponentAttrs(node: ComponentNode): string { if (!props['data-attributes']) props['data-attributes'] = JSON.stringify(node.attributes) } - const entries = Object.entries(props) - if (!entries.length) return '' - return ` ${entries.map(([key, value]) => `${key}="${escapeAttr(value)}"`).join(' ')}` + return Object.entries(props).map(([key, value]) => ` ${key}="${escapeAttr(value)}"`).join('') } diff --git a/src/inline.ts b/src/inline.ts index 59ce3a0..3495bb8 100644 --- a/src/inline.ts +++ b/src/inline.ts @@ -1,9 +1,8 @@ import type { InlineNode, ParseOptions } from './types.js' -import { footnoteId, normalizeReferenceLabel, sanitizeUrl } from './utils.js' +import { footnoteId, normalizeReferenceLabel, parseDestination, plainText, sanitizeUrl } from './utils.js' export function parseInline(value: string, options: ParseOptions = {}): InlineNode[] { - const nodes = parseInlineRaw(value, options) - let result = mergeText(nodes) + let result = parseInlineRaw(value, options) for (const extension of options.extensions ?? []) { result = extension.transformInline?.(result, { options }) ?? result @@ -15,15 +14,17 @@ export function parseInline(value: string, options: ParseOptions = {}): InlineNo interface InlineParseBudget { scans: number depth: number + links: number } const maxInlineDepth = 32 const scansPerCharacter = 16 +const inlineMarker = /[\\`!\[_*~<]/g function parseInlineRaw( value: string, options: ParseOptions, - budget: InlineParseBudget = { scans: Math.max(value.length * scansPerCharacter, 1024), depth: 0 }, + budget: InlineParseBudget = { scans: Math.max(value.length * scansPerCharacter, 1024), depth: 0, links: 0 }, ): InlineNode[] { if (budget.depth >= maxInlineDepth) return value ? [{ type: 'text', value }] : [] budget.depth++ @@ -51,7 +52,7 @@ function parseInlineRaw( continue } - if (next && /[\\`*_[\]{}()#+\-.!|~<>]/.test(next)) { + if (next && /[!-/:-@\[-`{-~]/.test(next)) { text += next index += 2 continue @@ -65,7 +66,8 @@ function parseInlineRaw( pushText() nodes.push({ type: 'inlineCode', - value: value.slice(index + tickCount, close).replace(/\s+/g, ' ').trim(), + // Strip one padding space at each end, except in an all-space span. + value: value.slice(index + tickCount, close).replace(/\n/g, ' ').replace(/^ (?! *$)(.*) $/s, '$1'), }) index = close + tickCount continue @@ -75,45 +77,42 @@ function parseInlineRaw( continue } - if (char === '!' && next === '[') { - const parsed = parseLinkish(value, index + 1, options, budget) - if (parsed) { - pushText() - nodes.push({ - type: 'image', - src: sanitizeUrl(parsed.href), - alt: textFromMarkdown(parsed.label, budget), - ...(parsed.title ? { title: parsed.title } : {}), - }) - index = parsed.end - continue - } - } - - if (char === '[') { - const footnote = parseFootnoteReference(value, index, options, budget) + if (char === '[' || (char === '!' && next === '[')) { + const image = char === '!' + const footnote = next === '^' && parseFootnoteReference(value, index, options, budget) if (footnote) { pushText() nodes.push(footnote.node) + budget.links++ index = footnote.end continue } - const parsed = parseLinkish(value, index, options, budget) + const parsed = parseLinkish(value, index + Number(image), options, budget) if (parsed) { + const links = budget.links + const children = parseInlineRaw(parsed.label, image ? (options.references ? { references: options.references } : {}) : options, budget) + const nested = !image && budget.links !== links const href = sanitizeUrl(parsed.href) - pushText() - if (href) { + if (image || (!nested && (href || !parsed.href))) { + pushText() nodes.push({ - type: 'link', - href, + ...(image ? { type: 'image' as const, src: href, alt: plainText(children) } : { type: 'link' as const, href, children }), ...(parsed.title ? { title: parsed.title } : {}), - children: parseInlineRaw(parsed.label, options, budget), }) } else { - nodes.push(...parseInlineRaw(parsed.label, options, budget)) + if (nested) text += '[' + for (const child of children) { + if (child.type === 'text') text += child.value + else { + pushText() + nodes.push(child) + } + } } - index = parsed.end + // An inner link disables its outer opener, but not the trailing markup. + budget.links = image ? links : budget.links + 1 + index = nested ? index + parsed.label.length + 1 : parsed.end continue } } @@ -131,22 +130,6 @@ function parseInlineRaw( } } - if ((char === '*' && next === '*') || (char === '_' && next === '_')) { - const close = char === '_' && !canUseUnderscore(value, index, 2, true) ? -1 : findDelimiter(value, index + 2, char + char, budget) - if (close !== -1) { - pushText() - nodes.push({ - type: 'strong', - children: parseInlineRaw(value.slice(index + 2, close), options, budget), - }) - index = close + 2 - continue - } - text += char + next - index += 2 - continue - } - if (char === '~' && next === '~' && value[index + 2] === '~') { const run = countRun(value, index, '~') text += value.slice(index, index + run) @@ -154,48 +137,23 @@ function parseInlineRaw( continue } - if (char === '~' && next === '~') { - const close = findDelimiter(value, index + 2, '~~', budget) + if (char === '*' || char === '_' || char === '~') { + const size = next === char ? 2 : 1 + const close = char === '_' && !canUseUnderscore(value, index, size, true) ? -1 : findDelimiter(value, index + size, char.repeat(size), budget) if (close !== -1) { pushText() nodes.push({ - type: 'strike', - children: parseInlineRaw(value.slice(index + 2, close), options, budget), + type: char === '~' ? 'strike' : size === 2 ? 'strong' : 'emphasis', + children: parseInlineRaw(value.slice(index + size, close), options, budget), }) - index = close + 2 - continue + index = close + size + } else { + text += char.repeat(size) + index += size } - text += '~~' - index += 2 continue } - if (char === '~') { - const close = findSingleTildeDelimiter(value, index + 1, budget) - if (close !== -1) { - pushText() - nodes.push({ - type: 'strike', - children: parseInlineRaw(value.slice(index + 1, close), options, budget), - }) - index = close + 1 - continue - } - } - - if (char === '*' || char === '_') { - const close = char === '_' && !canUseUnderscore(value, index, 1, true) ? -1 : findDelimiter(value, index + 1, char, budget) - if (close !== -1) { - pushText() - nodes.push({ - type: 'emphasis', - children: parseInlineRaw(value.slice(index + 1, close), options, budget), - }) - index = close + 1 - continue - } - } - if (char === '<' && options.allowHtml) { const close = findCharacter(value, index + 1, '>', budget) if (close !== -1 && isInlineHtml(value.slice(index, close + 1))) { @@ -206,8 +164,11 @@ function parseInlineRaw( } } - text += char - index++ + // Reset after recursive parsing; exhausted budgets still allow escapes. + inlineMarker.lastIndex = index + 1 + const end = budget.scans > 0 ? inlineMarker.exec(value)?.index ?? value.length : value.indexOf('\\', index + 1) + text += value.slice(index, end < 0 ? value.length : end) + index = end < 0 ? value.length : end } pushText() @@ -228,8 +189,6 @@ interface ParsedLink { } function parseFootnoteReference(value: string, open: number, options: ParseOptions, budget: InlineParseBudget): ParsedFootnoteReference | undefined { - if (value[open + 1] !== '^') return undefined - const close = findCharacter(value, open + 2, ']', budget) if (close === -1) return undefined @@ -238,11 +197,8 @@ function parseFootnoteReference(value: string, open: number, options: ParseOptio const definition = options.footnotes?.[key] if (!definition || !options.footnoteOrder) return undefined - let orderIndex = options.footnoteOrder.indexOf(key) - if (orderIndex === -1) { - options.footnoteOrder.push(key) - orderIndex = options.footnoteOrder.length - 1 - } + let number = options.footnoteOrder.indexOf(key) + 1 + if (!number) number = options.footnoteOrder.push(key) const referenceIndex = (options.footnoteCounts?.[key] ?? 0) + 1 if (options.footnoteCounts) options.footnoteCounts[key] = referenceIndex @@ -250,8 +206,8 @@ function parseFootnoteReference(value: string, open: number, options: ParseOptio return { node: { type: 'footnoteReference', - id: definition.id ?? (footnoteId(definition.label) || 'footnote'), - number: orderIndex + 1, + id: definition.id ?? footnoteId(definition.label), + number, ...(referenceIndex > 1 ? { referenceIndex } : {}), }, end: close + 1, @@ -263,46 +219,30 @@ function parseLinkish(value: string, open: number, options: ParseOptions, budget if (closeBracket === -1) return undefined const label = value.slice(open + 1, closeBracket) + let end = closeBracket + 1 + let definition: { href: string; title?: string } | undefined - if (value[closeBracket + 1] === '(') { - const closeParen = findBalanced(value, closeBracket + 1, '(', ')', budget) + if (value[end] === '(') { + const closeParen = findBalanced(value, end, '(', ')', budget) if (closeParen === -1) return undefined - const destination = value.slice(closeBracket + 2, closeParen).trim() - const parsed = parseDestination(destination) - if (!parsed) return undefined - - return { - label, - href: parsed.href, - ...(parsed.title ? { title: parsed.title } : {}), - end: closeParen + 1, - } - } - - if (value[closeBracket + 1] === '[') { - const closeReference = findBalanced(value, closeBracket + 1, '[', ']', budget) + definition = parseDestination(value.slice(end + 1, closeParen).trim()) + end = closeParen + 1 + } else if (value[end] === '[') { + const closeReference = findBalanced(value, end, '[', ']', budget) if (closeReference === -1) return undefined - const referenceLabel = value.slice(closeBracket + 2, closeReference) - const definition = options.references?.[normalizeReferenceLabel(referenceLabel || label)] - if (!definition) return undefined - - return { - label, - href: definition.href, - ...(definition.title ? { title: definition.title } : {}), - end: closeReference + 1, - } + definition = options.references?.[normalizeReferenceLabel(value.slice(end + 1, closeReference) || label)] + end = closeReference + 1 + } else { + definition = options.references?.[normalizeReferenceLabel(label)] } - const definition = options.references?.[normalizeReferenceLabel(label)] if (definition) { return { + ...definition, label, - href: definition.href, - ...(definition.title ? { title: definition.title } : {}), - end: closeBracket + 1, + end, } } @@ -310,26 +250,19 @@ function parseLinkish(value: string, open: number, options: ParseOptions, budget } function isInlineHtml(value: string): boolean { - if (/^\n\n`__proto__`: example\nconstructor: another\n\n', { extensions: docsMarkdownExtensions() }) + expect(document.children[0]).toMatchObject({ properties: { + 'data-package-manager-meta': '{"packagesByFramework":{"__proto__":[["example"]],"constructor":[["another"]]},"mode":"install"}', + } }) + equivalent(document) + }) + + it('splits large heading collections without exceeding the argument limit', () => { + const headings = Array.from({ length: 150000 }, () => ({ type: 'heading' as const, depth: 2 as const, children: [] })) + expect(splitByHeading(headings)).toHaveLength(headings.length) + }) + + it('keeps bundler tab ordering, first matches, and unknown-section filtering', () => { + const node = { type: 'component' as const, name: 'tabs', attributes: { variant: 'bundler' }, children: parseMarkdown('# Rsbuild\nFirst\n# Other\nIgnored\n# VITE\nSecond\n# vite\nDuplicate').children } + const result = transformBundlerTabs(node) + expect(result.properties?.['data-bundler-meta']).toBe('{"bundlers":["vite","rsbuild"]}') + expect(equivalent({ type: 'root', children: [result] })).not.toMatch(/Ignored|Duplicate/) + const unknown = { ...node, children: parseMarkdown('# Other\nIgnored').children } + expect(transformBundlerTabs(unknown)).toBe(unknown) + }) +}) diff --git a/tests/bundle-size.test.ts b/tests/bundle-size.test.ts index f05a35c..c14e6eb 100644 --- a/tests/bundle-size.test.ts +++ b/tests/bundle-size.test.ts @@ -1,29 +1,68 @@ -import { gzipSync } from 'node:zlib' +import { brotliCompressSync, gzipSync } from 'node:zlib' import { build } from 'esbuild' import { describe, expect, it } from 'vitest' +import { publicEntries } from '../scripts/measure-size.js' describe('bundle budgets', () => { it('keeps parser and renderers small without bundling a highlighter', async () => { const parser = await bundle("import { parseMarkdown } from './src/parser.ts'; console.log(parseMarkdown)") const html = await bundle("import { renderHtml } from './src/html.ts'; console.log(renderHtml)") - const react = await bundle("import { Markdown } from './src/react.ts'; console.log(Markdown)", ['react']) - const octane = await bundle("import { Markdown } from './src/octane.ts'; console.log(Markdown)", ['octane']) + const react = await bundle("import { Markdown, renderMarkdownReact } from './src/react.ts'; console.log(Markdown, renderMarkdownReact)", ['react']) + const octane = await bundle("import { Markdown, renderMarkdownOctane } from './src/octane.ts'; console.log(Markdown, renderMarkdownOctane)", ['octane']) const pluggable = await bundle("import { renderHtml } from './src/html.ts'; const highlighter = (code) => code; console.log(renderHtml('# x', { highlighter }))") const streaming = await bundle("import { streamingMarkdownExtension } from './src/extensions/streaming.ts'; console.log(streamingMarkdownExtension)") + const callouts = await bundle("import { calloutsExtension } from './src/extensions/callouts.ts'; console.log(calloutsExtension)") + const docs = await bundle("import { docsMarkdownExtensions } from './src/extensions/docs.ts'; console.log(docsMarkdownExtensions)") + const tabs = await bundle("import { transformTabsComponent } from './src/extensions/tabs.ts'; console.log(transformTabsComponent)") const reactStreaming = await bundle( "import { Markdown } from './src/react.ts'; import { streamingMarkdownExtension } from './src/extensions/streaming.ts'; console.log(Markdown, streamingMarkdownExtension)", ['react'], ) - expect(parser.gzipBytes).toBeLessThan(5_000) - expect(html.gzipBytes).toBeLessThan(6_825) - expect(react.gzipBytes).toBeLessThan(6_750) - expect(octane.gzipBytes).toBeLessThan(6_750) - expect(pluggable.gzipBytes).toBeLessThan(6_850) - expect(streaming.gzipBytes).toBeLessThan(400) - expect(reactStreaming.gzipBytes).toBeLessThan(7_000) + // The September 11 audited working tree is the ceiling, not the larger npm release. + for (const [result, min, gzip, brotli] of [ + [parser, 13440, 4948, 4559], + [html, 18453, 6737, 6169], + [react, 18491, 6682, 6153], + [octane, 18494, 6681, 6145], + [pluggable, 18489, 6759, 6195], + [streaming, 699, 311, 253], + [callouts, 556, 372, 329], + [reactStreaming, 19182, 6868, 6316], + [docs, 6589, 2334, 2121], + [tabs, 3320, 1232, 1094], + ] as const) { + expect(result.minBytes).toBeLessThanOrEqual(min) + expect(result.gzipBytes).toBeLessThanOrEqual(gzip) + expect(result.brotliBytes).toBeLessThanOrEqual(brotli) + } expect(html.code).not.toContain('external-line') }) + + it('also protects the complete namespace of every public entry point', async () => { + const budgets: Record = { + '.': [18714, 6864, 6272], + './html': [18676, 6845, 6267], + './parser': [13572, 5032, 4635], + './react': [18691, 6790, 6242], + './octane': [18697, 6790, 6239], + './extensions/callouts': [710, 467, 390], + './extensions/comment-components': [1159, 656, 554], + './extensions/docs': [6753, 2433, 2198], + './extensions/framework': [1470, 734, 630], + './extensions/headings': [1038, 573, 480], + './extensions/streaming': [838, 403, 334], + './extensions/tabs': [3567, 1350, 1197], + } + for (const entry of publicEntries) { + const result = await bundle(entry.contents, entry.external) + const budget = budgets[entry.name]! + expect(budget, entry.name).toBeDefined() + expect(result.minBytes, entry.name).toBeLessThanOrEqual(budget[0]!) + expect(result.gzipBytes, entry.name).toBeLessThanOrEqual(budget[1]!) + expect(result.brotliBytes, entry.name).toBeLessThanOrEqual(budget[2]!) + } + }) }) async function bundle(contents: string, external: string[] = []) { @@ -47,6 +86,8 @@ async function bundle(contents: string, external: string[] = []) { const code = Buffer.from(output.outputFiles[0]!.contents).toString('utf8') return { code, + minBytes: Buffer.byteLength(code), gzipBytes: gzipSync(code, { level: 9 }).length, + brotliBytes: brotliCompressSync(code).length, } } diff --git a/tests/compatibility-budget.test.tsx b/tests/compatibility-budget.test.tsx new file mode 100644 index 0000000..0ba521c --- /dev/null +++ b/tests/compatibility-budget.test.tsx @@ -0,0 +1,189 @@ +import { execFileSync } from 'node:child_process' +import { renderToStaticMarkup } from 'react-dom/server' +import { renderToStaticMarkup as renderOctane } from 'octane/server' +import { describe, expect, it } from 'vitest' +import { parseInline, parseMarkdown, renderHtml } from '../src/index.js' +import { Markdown } from '../src/react.js' +import { Markdown as OctaneMarkdown } from '../src/octane.js' +import { commentComponentsExtension, parseCommentComponentBlock, parseComponentComment } from '../src/extensions/comment-components.js' +import { docsMarkdownExtensions } from '../src/extensions/docs.js' +import type { BlockNode, BlockParseContext, MarkdownInput } from '../src/types.js' +import { normalizeStaticMarkup } from './helpers/normalize-html.js' + +function equivalent(input: MarkdownInput) { + const html = renderHtml(input) + expect(normalizeStaticMarkup(renderToStaticMarkup({input}))).toBe(normalizeStaticMarkup(html)) + expect(normalizeStaticMarkup(renderOctane(OctaneMarkdown, { children: input }).html)).toBe(normalizeStaticMarkup(html)) + return html +} + +describe('compatibility within the audited bundle budget', () => { + it.each(['[empty]()', '[empty]( )', '[empty](<>)', '[empty][ref]\n\n[ref]: <>'])('preserves an empty destination in %j', source => { + expect(equivalent(source)).toBe('

    empty

    ') + }) + + it.each([ + ['"double quoted"', 'double quoted'], + ["'single quoted'", 'single quoted'], + ['(parenthesized)', 'parenthesized'], + ['"one\'s title"', "one's title"], + ["'a \"quoted\" title'", 'a "quoted" title'], + ])('shares destination parsing for inline and reference titles %s', (title, expected) => { + const inline = `[label](/target ${title})` + const reference = `[label][ref]\n\n[ref]: /target ${title}` + expect(equivalent(inline)).toBe(equivalent(reference)) + expect(parseMarkdown(inline).children[0]).toMatchObject({ children: [{ type: 'link', href: '/target', title: expected }] }) + }) + + it.each(['[label](/not a url)', '[label](foo\nbar)', '[label](/url "mismatched\')', '[label](/url "title "and" title")'])('keeps invalid destinations literal in %j', source => { + expect(parseInline(source)).toEqual([{ type: 'text', value: source }]) + equivalent(source) + }) + + it.each([ + [String.raw`\(foo\)`, '(foo)'], + [String.raw`foo\(and\(bar\)`, 'foo(and(bar)'], + [String.raw`foo\)\:`, 'foo):'], + [String.raw`/path\#part`, '/path#part'], + [String.raw`/path\z`, String.raw`/path\z`], + ])('unescapes only punctuation in destination %s', (destination, href) => { + const source = `[label](${destination})` + expect(parseInline(source)).toEqual([{ type: 'link', href, children: [{ type: 'text', value: 'label' }] }]) + expect(equivalent(source)).toBe(equivalent(`[label][ref]\n\n[ref]: ${destination}`)) + }) + + it.each(['javascript\\:alert(1)', 'data\\:text/html,evil', 'vbscript\\:evil'])('still rejects executable protocols after unescaping %s', destination => { + expect(equivalent(`[label](${destination})`)).toBe('

    label

    ') + expect(equivalent(`[label][ref]\n\n[ref]: ${destination}`)).toBe('

    label

    ') + }) + + it.each([ + ['[outer [inner](/i)](/o)', '

    [outer inner](/o)

    '], + ['[outer **[inner](/i)**](/o)', '

    [outer inner](/o)

    '], + ['[outer [inner][i]][o]\n\n[i]: /i\n[o]: /o', '

    [outer inner]o

    '], + ['[outer [inner](/i)](**tail**)', '

    [outer inner](tail)

    '], + ['[![alt [inner](/i)](/image)](/o)', '

    alt inner

    '], + ['[outer `code` and \\[text\\]](/o)', '

    outer code and [text]

    '], + ])('resolves link nesting in %j', (source, expected) => { + expect(equivalent(source)).toBe(expected) + }) + + it('does not put a footnote anchor inside a link anchor', () => { + expect(equivalent('[label[^n]](/outer)\n\n[^n]: Note')).not.toContain('href="/outer"') + }) + + it.each(['- outer\n - inner\n\n - second\n- next', '1. outer\n - inner\n\n - second\n2. next'])('keeps looseness with the nested list in %j', source => { + const document = parseMarkdown(source) + expect(document.children[0]).not.toHaveProperty('loose') + expect(document.children[0]).toMatchObject({ items: [{ children: [{ type: 'paragraph' }, { type: 'list', loose: true }] }, { children: [{ type: 'paragraph' }] }] }) + const html = equivalent(source) + expect(html).toContain('
  • outer\n') + expect(html).toContain('
  • inner

  • ') + expect(html).toContain('
  • next
  • ') + }) + + it('keeps paragraph separation and task labels in loose outer lists', () => { + expect(equivalent('- [x] First\n\n Second paragraph\n- Next')).toBe('
      \n
    • First

      \n

      Second paragraph

    • \n
    • Next

    • \n
    ') + }) + + it('preserves blank lines and indentation inside list code blocks without loosening the list', () => { + const source = '- first\n- ```\n code\n\n\n ```\n- last' + const document = parseMarkdown(source) + expect(document.children[0]).not.toHaveProperty('loose') + expect(document.children[0]).toMatchObject({ items: [{}, { children: [{ type: 'code', value: 'code\n\n' }] }, {}] }) + equivalent(source) + }) + + it.each([1, 2, 3])('removes up to %i fence-indent spaces from code lines', indent => { + const pad = ' '.repeat(indent) + const source = `${pad}\`\`\`\n${pad}code\n${pad} nested\nunindented\n${pad}\`\`\`` + expect(parseMarkdown(source).children).toMatchObject([{ type: 'code', value: 'code\n nested\nunindented' }]) + equivalent(source) + }) + + it('preserves existing quote grouping while retaining paragraph separation', () => { + expect(equivalent('> First\n\n> Second')).toBe('
    \n

    First

    \n

    Second

    \n
    ') + expect(equivalent('> First\n>\n> Second')).toBe('
    \n

    First

    \n

    Second

    \n
    ') + expect(parseMarkdown('- > Quote\n\n Paragraph\n- Next').children[0]).toMatchObject({ type: 'list', loose: true }) + }) + + it('keeps callout boundaries and outer list paragraph separation consistent', () => { + const options = { extensions: docsMarkdownExtensions() } + const source = '- > [!NOTE]\n > Body\n\n Next paragraph\n- Last' + const document = parseMarkdown(source, options) + expect(document.children[0]).toMatchObject({ type: 'list', loose: true }) + expect(equivalent(document)).toContain('

    Next paragraph

    ') + const separated = parseMarkdown('> [!NOTE]\n> First\n\n> [!TIP]\n> Second', options) + expect(separated.children.map(child => child.type)).toEqual(['callout', 'callout']) + equivalent(separated) + }) + + it.each([ + [], + [{ type: 'code', value: 'code' }], + [{ type: 'list', ordered: false, items: [{ type: 'listItem', children: [{ type: 'paragraph', children: [{ type: 'text', value: 'item' }] }] }] }], + [{ type: 'blockquote', children: [{ type: 'paragraph', children: [{ type: 'text', value: 'quote' }] }] }], + ])('adds footnote navigation after non-paragraph blocks without mutating the AST: %j', (...children) => { + const document = { type: 'root' as const, children: [{ type: 'footnotes' as const, items: [{ id: 'note', number: 1, referenceCount: 2, children }] }] } + const before = JSON.stringify(document) + const html = equivalent(document) + expect(html).toContain('

    { + for (const source of ['a_b_c', '\u00e9_word_', '_word_\u4e2d', '\u0661_word_']) { + expect(parseInline(source)).toEqual([{ type: 'text', value: source }]) + } + expect(equivalent('\u00a0_word_')).toContain('word') + }) + + it('preserves prototype-shaped component attributes as ordinary data', () => { + const source = '' + expect(parseComponentComment(source)?.attributes).toEqual(Object.fromEntries([['__proto__', 'safe'], ['constructor', 'literal']])) + expect(renderHtml(source, { extensions: [commentComponentsExtension()] })).toContain('"__proto__":"safe"') + expect(Object.getPrototypeOf({})).toBe(Object.prototype) + }) + + it('copies a matched component body before consuming its input', () => { + const context: BlockParseContext = { + lines: ['', 'Body', ''], + index: 0, + options: {}, + parseInline, + parseBlocks: source => parseMarkdown(source).children, + consume: count => { + context.index += count + context.lines.length = 0 + }, + } + expect(parseCommentComponentBlock(context)).toMatchObject({ children: [{ type: 'paragraph', children: [{ type: 'text', value: 'Body' }] }] }) + expect(context.index).toBe(3) + }) + + it('keeps component transforms and unmatched-block consumption unchanged', () => { + for (const source of ['', '\nFollowing paragraph']) { + const calls: string[] = [] + const document = parseMarkdown(source, { extensions: [commentComponentsExtension({ transformComponent(node) { + calls.push(node.name) + return { ...node, tagName: 'custom-widget' } + } })] }) + expect(calls).toEqual(['widget']) + expect(document.children[0]).toMatchObject({ type: 'component', tagName: 'custom-widget', children: [] }) + if (source.includes('\n')) expect(document.children[1]).toMatchObject({ type: 'paragraph' }) + } + }) + + it('bounds malformed destinations and long runs of list blank lines', () => { + const output = execFileSync(process.execPath, ['--import', 'tsx', '--input-type=module', '-e', + `import {parseMarkdown} from './src/parser.ts'; + for (const source of ['[label](/url "' + 'a'.repeat(100000) + ')', '- code\\n' + '\\n'.repeat(150000) + ' continuation']) { + parseMarkdown(source); + } + console.log('ok');`, + ], { encoding: 'utf8', timeout: 3000 }) + expect(output.trim()).toBe('ok') + }) +})