From c6c3f12e357e185ddcb2c2ac5ee9711fae184382 Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Mon, 14 Sep 2026 13:10:41 +0100 Subject: [PATCH 1/9] chore(deps): bump @exadev/eslint-config to 2.12.1 Pins the shared ESLint config to an exact version, matching every other workspace dependency's exact-pin policy. The lockfile reflects the full resolved tree; note it already carries the pnpm 12.4.1 packageManager metadata landed in the next commit, since both changes were installed against the same lockfile before being split here. --- package.json | 2 +- pnpm-lock.yaml | 542 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 538 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index aab895e99..87cf99215 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "@eslint/js": "10.0.1", "@eslint/json": "2.0.1", "@eslint/markdown": "8.0.3", - "@exadev/eslint-config": "2.1.2", + "@exadev/eslint-config": "2.12.1", "@exadev/semantic-release-workspace": "1.2.3", "@semantic-release/changelog": "7.0.0", "@semantic-release/commit-analyzer": "13.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 42ce69f33..e76cd11d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,3 +1,161 @@ +--- +lockfileVersion: '9.0' + +importers: + + .: + configDependencies: {} + packageManagerDependencies: + pnpm: + specifier: 12.4.1 + version: 12.4.1 + +packages: + + '@pnpm/exe.android-arm64@12.4.1': + resolution: {integrity: sha512-/HwsqXMSmlOfgtV9+O0ratzjV6Vd/8n1hh4rGHpCGmDURvt52MwZxdbhyxP4K03ZfvgSDvotFPr8O+RzE5Eu8A==} + cpu: [arm64] + os: [android] + + '@pnpm/exe.android-x64@12.4.1': + resolution: {integrity: sha512-+l74Qb4c2YjOzNKHXJLg+1wr8xHM1ckkUhnU5KRUK9TJqiiczt6yeTZqqzHIlJ8i6pAoj5V0OJevDRwnQKLgrQ==} + cpu: [x64] + os: [android] + + '@pnpm/exe.darwin-arm64@12.4.1': + resolution: {integrity: sha512-6rkZkT3iGfaxknUdGHraqSWFvTa6N0ajAHluv9Ax0GRWs0sIcGNiFhDopv6xSZCsJZmG483aNS/b6UEDy3blfw==} + cpu: [arm64] + os: [darwin] + + '@pnpm/exe.darwin-x64@12.4.1': + resolution: {integrity: sha512-Vb1CHlR88HghC1qUxjxjs82zQSXnXacPD+btG2CmG8Q/hBU7Q0/b2YWJKSQqZXicunV5khFtfTlAwJddV9RkYA==} + cpu: [x64] + os: [darwin] + + '@pnpm/exe.freebsd-x64@12.4.1': + resolution: {integrity: sha512-iT3iHz3Nl0Sxxj7UPOtZ/aQ81AFsQhjoeWMAlPkSRO04gsgDGAXv3UYxOFaesMWsfNxaGn0A+CITbuCHFF66FA==} + cpu: [x64] + os: [freebsd] + + '@pnpm/exe.linux-arm64-musl@12.4.1': + resolution: {integrity: sha512-aBooZfNXM5f+OGUgCAMFWpE/kAhsWfvmqIyMtHy6zl3aNxyInWrcY/Saln/UElzo7lZWMC0Yktroxd/2J26lNQ==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-arm64@12.4.1': + resolution: {integrity: sha512-TlOdacTTP09BgcMvwWBFRsu8VAjfwqwnslBj+XSq1JFM3ck4f3k+1O/747EuctxZxs3/o785b6Q3s7Pd92Ptsg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-ppc64@12.4.1': + resolution: {integrity: sha512-r/ab/MlIBo75oizUP5ITiziCCrnXz4SJwfErLQ+603AshB+Yq7xTMCoMtzTSCAMu6aaymIKkAFtZvd2J75Wq0w==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-riscv64@12.4.1': + resolution: {integrity: sha512-C/D1QWdKMiB8+wv/spl1rGITFUuqC+aI/fb6h8NB5sqxsOaGXeYc/g891oCuzggHn6SODk9p+I09hI76x/cMNw==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-s390x@12.4.1': + resolution: {integrity: sha512-nxz5zD4yXt94uzbStDk0QTPKW+aE92hH1b5tFXK9ctB1HE9Xcq1vjTiA2LsZMFC6p4gdu5OwQeFqYNAVGa6QlA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-x64-musl@12.4.1': + resolution: {integrity: sha512-5AwgFdGhVUg2kIweYGfxzSLEHiIG77PQhZAkXC3TwofQHsu1Wr+TrV5/rNX2PopFnHRzuE581zoB8F6Wle32yg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-x64@12.4.1': + resolution: {integrity: sha512-FJOZuuuQMhp0oLzBtcKkLXknBI92hfkmSnlKc47vfin4HrmfID5khY2lGekL9tCzk1cpR+HShEw/meFl+nHtzQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.win32-arm64@12.4.1': + resolution: {integrity: sha512-OO7eKBL9S+xk5hRy+JUUZSNJknGuGe3GEUffsrlC2LbqKF02g+VX1anGIzSbJDvkkdnpJhSlxF5AUz2JzJu2Jw==} + cpu: [arm64] + os: [win32] + + '@pnpm/exe.win32-x64@12.4.1': + resolution: {integrity: sha512-x7gJHZgHo6hp354xCYA2NvoFzYJkHwovt2kVsUBK5EmXULLcP51JGMq09CX+5FRFJUZFKoXjLu3mZWmfP7o6PQ==} + cpu: [x64] + os: [win32] + + pnpm@12.4.1: + resolution: {integrity: sha512-LoHjmdc/6DkNqyXgaqeIq3pZCCSNL1o3D4K0gRR6ano2e/gEj5pv22Rg8hpm8FQt7bi5TKLIcjWWdBkgsWVtTA==} + engines: {node: '>=18.*'} + hasBin: true + +snapshots: + + '@pnpm/exe.android-arm64@12.4.1': + optional: true + + '@pnpm/exe.android-x64@12.4.1': + optional: true + + '@pnpm/exe.darwin-arm64@12.4.1': + optional: true + + '@pnpm/exe.darwin-x64@12.4.1': + optional: true + + '@pnpm/exe.freebsd-x64@12.4.1': + optional: true + + '@pnpm/exe.linux-arm64-musl@12.4.1': + optional: true + + '@pnpm/exe.linux-arm64@12.4.1': + optional: true + + '@pnpm/exe.linux-ppc64@12.4.1': + optional: true + + '@pnpm/exe.linux-riscv64@12.4.1': + optional: true + + '@pnpm/exe.linux-s390x@12.4.1': + optional: true + + '@pnpm/exe.linux-x64-musl@12.4.1': + optional: true + + '@pnpm/exe.linux-x64@12.4.1': + optional: true + + '@pnpm/exe.win32-arm64@12.4.1': + optional: true + + '@pnpm/exe.win32-x64@12.4.1': + optional: true + + pnpm@12.4.1: + optionalDependencies: + '@pnpm/exe.android-arm64': 12.4.1 + '@pnpm/exe.android-x64': 12.4.1 + '@pnpm/exe.darwin-arm64': 12.4.1 + '@pnpm/exe.darwin-x64': 12.4.1 + '@pnpm/exe.freebsd-x64': 12.4.1 + '@pnpm/exe.linux-arm64': 12.4.1 + '@pnpm/exe.linux-arm64-musl': 12.4.1 + '@pnpm/exe.linux-ppc64': 12.4.1 + '@pnpm/exe.linux-riscv64': 12.4.1 + '@pnpm/exe.linux-s390x': 12.4.1 + '@pnpm/exe.linux-x64': 12.4.1 + '@pnpm/exe.linux-x64-musl': 12.4.1 + '@pnpm/exe.win32-arm64': 12.4.1 + '@pnpm/exe.win32-x64': 12.4.1 + +--- lockfileVersion: '9.0' settings: @@ -31,8 +189,8 @@ importers: specifier: 8.0.3 version: 8.0.3 '@exadev/eslint-config': - specifier: 2.1.2 - version: 2.1.2(eslint@10.8.1(jiti@2.7.0))(typescript-eslint@8.66.0(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3)) + specifier: 2.12.1 + version: 2.12.1(eslint-plugin-jsx-a11y@6.10.2(eslint@10.8.1(jiti@2.7.0)))(eslint-plugin-react-hooks@7.1.1(eslint@10.8.1(jiti@2.7.0)))(eslint@10.8.1(jiti@2.7.0))(typescript-eslint@8.66.0(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3))(typescript@6.0.3) '@exadev/semantic-release-workspace': specifier: 1.2.3 version: 1.2.3(@semantic-release/changelog@7.0.0(semantic-release@25.0.9(typescript@6.0.3)))(@semantic-release/commit-analyzer@13.0.1(semantic-release@25.0.9(typescript@6.0.3)))(@semantic-release/git@11.0.1(semantic-release@25.0.9(typescript@6.0.3)))(@semantic-release/github@12.0.9(semantic-release@25.0.9(typescript@6.0.3)))(@semantic-release/npm@13.1.5(semantic-release@25.0.9(typescript@6.0.3)))(@semantic-release/release-notes-generator@14.1.1(semantic-release@25.0.9(typescript@6.0.3)))(semantic-release@25.0.9(typescript@6.0.3))(typescript@6.0.3) @@ -2579,6 +2737,14 @@ packages: '@emotion/hash@0.9.2': resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + '@es-joy/jsdoccomment@0.97.0': + resolution: {integrity: sha512-EP8uoFfh6+GsdGCduYtmWAW0h7AO+Ayik9Vh5YbA2r/3N6lmJKkCNZX+q3QBXC1K6ixjQ/9igF2b7WVvLm063g==} + engines: {node: ^22.22.2 || >=24.15.0} + + '@es-joy/resolve.exports@1.2.0': + resolution: {integrity: sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==} + engines: {node: '>=10'} + '@esbuild/aix-ppc64@0.28.1': resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} engines: {node: '>=18'} @@ -2966,12 +3132,26 @@ packages: resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@exadev/eslint-config@2.1.2': - resolution: {integrity: sha512-isLVdlJ10wPn+O6iHAUQf+UQAfHvlaOfQYQVM2mUoPXsxKnDn66EvgFdWJUM9FrI/iFRI+bGzm3FfYJu/N+DTg==} + '@exadev/eslint-config@2.12.1': + resolution: {integrity: sha512-b5JKCX7l5onZy0i0kHdIeuiocxDoiuEwnrG6+j+EV8ZWaLpi18BDzNi1iT3tc647DFRUYJtHWDiJrkWT6E2JjQ==} engines: {node: '>=20'} peerDependencies: + '@next/eslint-plugin-next': ^16.3.2 eslint: '>=10.0.0' + eslint-plugin-jsx-a11y: ^6.10.2 + eslint-plugin-react: ^7.37.5 + eslint-plugin-react-hooks: ^7.1.1 + typescript: '>=4.8.4' typescript-eslint: '>=8.0.0' + peerDependenciesMeta: + '@next/eslint-plugin-next': + optional: true + eslint-plugin-jsx-a11y: + optional: true + eslint-plugin-react: + optional: true + eslint-plugin-react-hooks: + optional: true '@exadev/semantic-release-workspace@1.2.3': resolution: {integrity: sha512-OYbmlcLBSNJ80mq1YhY+iWAoAk+jaTWuGqNn3JGrJUoV0+Mec3RkL4dY5Zt84P35qoQDZGZkkmTDcqmQ8+oY/w==} @@ -3409,6 +3589,12 @@ packages: peerDependencies: '@mantine/core': 9.5.1 + '@microsoft/tsdoc-config@0.18.1': + resolution: {integrity: sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==} + + '@microsoft/tsdoc@0.16.0': + resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} + '@modelcontextprotocol/client@2.0.0': resolution: {integrity: sha512-8f1OghQ2rjzIOfqgUCP+8GiUWqRs89njoWLNqAe8kWmDePv3s1fZXseej+QXemssEuuOvLLmLO/kqM3IQHtISw==} engines: {node: '>=20'} @@ -4166,6 +4352,10 @@ packages: resolution: {integrity: sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==} engines: {node: '>=22'} + '@sindresorhus/base62@1.0.0': + resolution: {integrity: sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==} + engines: {node: '>=18'} + '@sindresorhus/is@4.6.0': resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} @@ -4366,6 +4556,9 @@ packages: '@types/node@26.2.0': resolution: {integrity: sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==} + '@types/node@26.5.1': + resolution: {integrity: sha512-CzNm2FezW4VR/LjG6yUdiEgLE/rAQ9Slj5gCu/C2VrdcW7I0ahNZ8DRbHT7zOZ6r3ONgd/bsQIeSaoDGrd1C6g==} + '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -4407,16 +4600,42 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/project-service@8.56.1': + resolution: {integrity: sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/project-service@8.66.0': resolution: {integrity: sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/project-service@8.70.0': + resolution: {integrity: sha512-hFHbTNqhU9G+2eKFXCBVb1tjFT/LceiJ4+HfLO4pTpDI0KHi6iajpcFFkaSQ9gXmCh7n82A0PthaayEdN6mspQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.56.1': + resolution: {integrity: sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.66.0': resolution: {integrity: sha512-8TGcH25j9zqJ/IULB/ppyhRvxA8QYfFEZ7nfbg6/BN9spDgb8fPWQXlE5l8TWBL50EtUx007uZ1o9VOwrq2/9g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.70.0': + resolution: {integrity: sha512-8nP3Kwh5hlgZ4FicGvmznAmJe8UL4sdU8tLukrPaMuQmDuk4Y8xYfzu/aYZW4xT2JCgc7H/TpDI5cGlxcWJSqQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.56.1': + resolution: {integrity: sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/tsconfig-utils@8.66.0': resolution: {integrity: sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4429,6 +4648,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/tsconfig-utils@8.70.0': + resolution: {integrity: sha512-adnkeeNq9Sq1sUf4+FRVc0KdgYghzsgFpZSQVZVvY0LCuUuN0FnQgyGzCJeC4fW1cdXseBAjU2EOqUIjbNcZUw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/type-utils@8.66.0': resolution: {integrity: sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4436,6 +4661,10 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/types@8.56.1': + resolution: {integrity: sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.66.0': resolution: {integrity: sha512-H6gcYaSDOyvL3AD/jHUtUFo2jqGgn/F6nuyuZSu0QTesxL+cP4dQoIMrODRofuJC09g64+WgZ6tE19Y1N2YIFQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4444,12 +4673,35 @@ packages: resolution: {integrity: sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.70.0': + resolution: {integrity: sha512-asTOIYhDg4zdzOScCyaytrsV3cR6B4ecPQlXw/dJIm7J/MZTtCtfVII9JD8Geh4jTCrK/Xe6cg5UevoleMcoJQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.56.1': + resolution: {integrity: sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/typescript-estree@8.66.0': resolution: {integrity: sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/typescript-estree@8.70.0': + resolution: {integrity: sha512-d9NmHMPEKQ7QCLLm1jI3zmoQBwT5KwFYjXBJ9ymZfKCUU+5rmTRykKAFvH5Qn/ZCds3CEAFS9OC9M/jkl0X2bA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.56.1': + resolution: {integrity: sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/utils@8.66.0': resolution: {integrity: sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4457,10 +4709,25 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/utils@8.70.0': + resolution: {integrity: sha512-oZmtKJz/4fufZ2p3+Cn3ijEojcdfR+1zYDH2xKYrEly0dR/Q/1xUPRCOlKGxod78nWlU2UnDe09GZ3TaknBFGA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.56.1': + resolution: {integrity: sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.66.0': resolution: {integrity: sha512-dkKR8q+lKciskj1Y3vthHktl+3cMLWGyVUP23bRiPZ5O9BRT++4EqDDV+TVeIKBL1VXVEqrJlz8MYbcnvJcAlg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.70.0': + resolution: {integrity: sha512-BoC8PiO4Hkdo0TVJh9Ntxr5MxPDI7/oFsrygN5ADelFSeXG/qgNuucIGA+L5Z6JpPTE/uRfcTWtscjbUaufepQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vanilla-extract/babel-plugin-debug-ids@1.2.2': resolution: {integrity: sha512-MeDWGICAF9zA/OZLOKwhoRlsUW+fiMwnfuOAqFVohL31Agj7Q/RBWAYweqjHLgFBCsdnr6XIfwjJnmb2znEWxw==} @@ -4696,6 +4963,9 @@ packages: ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} @@ -4737,6 +5007,10 @@ packages: anynum@1.0.1: resolution: {integrity: sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==} + are-docs-informative@0.1.1: + resolution: {integrity: sha512-sqRsNQBwbKLRX0jV5Cu5uzmtflf892n4Vukz7T659ebL4pz3mpOqCMU7lxMoBTFwnp10E3YB5ZcyHM41W5bcDA==} + engines: {node: '>=18'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -5017,6 +5291,10 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} + comment-parser@1.4.8: + resolution: {integrity: sha512-rKZTGo4fzKYna8UcL0isTg5wkBNla7bxTypLwZQXjIdi++IdP1OJ41rI5Mti3/jltkPujbu4i9LIARYA+zpotQ==} + engines: {node: '>= 12.0.0'} + common-tags@1.8.2: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} engines: {node: '>=4.0.0'} @@ -5398,6 +5676,12 @@ packages: peerDependencies: eslint: '>=8.40.0' + eslint-plugin-jsdoc@64.3.6: + resolution: {integrity: sha512-lo7IXmgUUNy88SxW7KnJmmD2iPQBIRMomfCFPHidW1M3zNNVuzxlB2uoNrNyE1g4v2/L+RtYmiROPwQhSNzL8Q==} + engines: {node: ^22.22.2 || >=24.15.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + eslint-plugin-jsx-a11y@6.10.2: resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} engines: {node: '>=4.0'} @@ -5429,6 +5713,9 @@ packages: peerDependencies: eslint: ^9 || ^10 + eslint-plugin-tsdoc@0.5.2: + resolution: {integrity: sha512-BlvqjWZdBJDIPO/YU3zcPCF23CvjYT3gyu63yo6b609NNV3D1b6zceAREy2xnweuBoDpZcLNuPyAUq9cvx6bbQ==} + eslint-plugin-yml@3.8.1: resolution: {integrity: sha512-E/70psRwxz5EJ8dBtzrFfqSiiInuSytbdtFCjueb/GcKjsWzPBfxfhtQePImMAPjHwMA/VDurO7DJwStDJ4wWg==} engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} @@ -5846,6 +6133,9 @@ packages: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -6169,6 +6459,9 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + jose@6.2.9: resolution: {integrity: sha512-XrchZOFZUl/T3vTwRe8XK+cJrGtMF4th1ARnDfwbBXFKThGhlsxEE4Zu03AD/bjJSt/9jT/mxrOCkJWOg77aPA==} @@ -6185,6 +6478,10 @@ packages: resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} hasBin: true + jsdoc-type-pratt-parser@9.2.1: + resolution: {integrity: sha512-V4Ww4EHnTcTLSOMoB0FsF72JhQvcAsriCm/LWnxJeGWoxIjEL2l9na11abQok5SYShq8m0Gl02el/xAbTCulvQ==} + engines: {node: ^22.22.2 || >=24.15.0} + jsdom@30.0.1: resolution: {integrity: sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} @@ -6782,6 +7079,9 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + object-deep-merge@2.0.1: + resolution: {integrity: sha512-aKttDKcU3pyZqKcCkDhsMn70WmZFG2JGDQLP9EcLyTSIFQRCPWLAmBZRLJnrVUrhPG1jETEEbfdgbNtJf1LyMg==} + object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -6886,6 +7186,9 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-imports-exports@0.2.4: + resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==} + parse-json@4.0.0: resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} engines: {node: '>=4'} @@ -6902,6 +7205,9 @@ packages: resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} engines: {node: '>=18'} + parse-statements@1.0.11: + resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} + parse5-htmlparser2-tree-adapter@6.0.1: resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} @@ -7204,6 +7510,10 @@ packages: require-like@0.1.2: resolution: {integrity: sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==} + reserved-identifiers@1.2.0: + resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} + engines: {node: '>=18'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -7423,6 +7733,9 @@ packages: spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + spdx-expression-parse@5.0.0: + resolution: {integrity: sha512-vngmw3Rgn+o2arXNbnZaj5UtOEBuWBfvaI+Wc8GFfykIhA5/vdK9/Sp/XkLv63dykz2rxKDvKEHupF5P0FORcQ==} + spdx-license-ids@3.0.23: resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==} @@ -7680,6 +7993,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + to-valid-identifier@1.0.0: + resolution: {integrity: sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==} + engines: {node: '>=20'} + tough-cookie@6.0.2: resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} engines: {node: '>=16'} @@ -7837,6 +8154,9 @@ packages: undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} + undici@6.28.0: resolution: {integrity: sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==} engines: {node: '>=18.17'} @@ -9553,6 +9873,16 @@ snapshots: '@emotion/hash@0.9.2': {} + '@es-joy/jsdoccomment@0.97.0': + dependencies: + '@types/estree': 1.0.9 + '@typescript-eslint/types': 8.70.0 + comment-parser: 1.4.8 + esquery: 1.7.0 + jsdoc-type-pratt-parser: 9.2.1 + + '@es-joy/resolve.exports@1.2.0': {} + '@esbuild/aix-ppc64@0.28.1': optional: true @@ -9810,10 +10140,21 @@ snapshots: '@eslint/core': 1.2.1 levn: 0.4.1 - '@exadev/eslint-config@2.1.2(eslint@10.8.1(jiti@2.7.0))(typescript-eslint@8.66.0(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3))': + '@exadev/eslint-config@2.12.1(eslint-plugin-jsx-a11y@6.10.2(eslint@10.8.1(jiti@2.7.0)))(eslint-plugin-react-hooks@7.1.1(eslint@10.8.1(jiti@2.7.0)))(eslint@10.8.1(jiti@2.7.0))(typescript-eslint@8.66.0(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3))(typescript@6.0.3)': dependencies: + '@eslint/js': 10.0.1(eslint@10.8.1(jiti@2.7.0)) + '@typescript-eslint/utils': 8.70.0(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3) eslint: 10.8.1(jiti@2.7.0) + eslint-plugin-jsdoc: 64.3.6(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3) + eslint-plugin-tsdoc: 0.5.2(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3) + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 typescript-eslint: 8.66.0(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3) + optionalDependencies: + eslint-plugin-jsx-a11y: 6.10.2(eslint@10.8.1(jiti@2.7.0)) + eslint-plugin-react-hooks: 7.1.1(eslint@10.8.1(jiti@2.7.0)) + transitivePeerDependencies: + - supports-color '@exadev/semantic-release-workspace@1.2.3(@semantic-release/changelog@7.0.0(semantic-release@25.0.9(typescript@6.0.3)))(@semantic-release/commit-analyzer@13.0.1(semantic-release@25.0.9(typescript@6.0.3)))(@semantic-release/git@11.0.1(semantic-release@25.0.9(typescript@6.0.3)))(@semantic-release/github@12.0.9(semantic-release@25.0.9(typescript@6.0.3)))(@semantic-release/npm@13.1.5(semantic-release@25.0.9(typescript@6.0.3)))(@semantic-release/release-notes-generator@14.1.1(semantic-release@25.0.9(typescript@6.0.3)))(semantic-release@25.0.9(typescript@6.0.3))(typescript@6.0.3)': dependencies: @@ -10183,6 +10524,15 @@ snapshots: dependencies: '@mantine/core': 9.5.1(@mantine/hooks@9.5.1(react@19.2.8))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@microsoft/tsdoc-config@0.18.1': + dependencies: + '@microsoft/tsdoc': 0.16.0 + ajv: 8.18.0 + jju: 1.4.0 + resolve: 1.22.12 + + '@microsoft/tsdoc@0.16.0': {} + '@modelcontextprotocol/client@2.0.0': dependencies: '@modelcontextprotocol/core': 2.0.0 @@ -10807,6 +11157,8 @@ snapshots: '@simple-libs/stream-utils@2.0.0': {} + '@sindresorhus/base62@1.0.0': {} + '@sindresorhus/is@4.6.0': {} '@sindresorhus/is@7.2.0': {} @@ -11058,6 +11410,10 @@ snapshots: dependencies: undici-types: 8.3.0 + '@types/node@26.5.1': + dependencies: + undici-types: 8.9.0 + '@types/normalize-package-data@2.4.4': {} '@types/react-dom@19.2.4(@types/react@19.2.18)': @@ -11106,6 +11462,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.56.1(typescript@6.0.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.67.0(typescript@6.0.3) + '@typescript-eslint/types': 8.67.0 + debug: 4.4.3 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/project-service@8.66.0(typescript@6.0.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.67.0(typescript@6.0.3) @@ -11115,11 +11480,34 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.70.0(typescript@6.0.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.70.0(typescript@6.0.3) + '@typescript-eslint/types': 8.70.0 + debug: 4.4.3 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.56.1': + dependencies: + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/visitor-keys': 8.56.1 + '@typescript-eslint/scope-manager@8.66.0': dependencies: '@typescript-eslint/types': 8.66.0 '@typescript-eslint/visitor-keys': 8.66.0 + '@typescript-eslint/scope-manager@8.70.0': + dependencies: + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/visitor-keys': 8.70.0 + + '@typescript-eslint/tsconfig-utils@8.56.1(typescript@6.0.3)': + dependencies: + typescript: 6.0.3 + '@typescript-eslint/tsconfig-utils@8.66.0(typescript@6.0.3)': dependencies: typescript: 6.0.3 @@ -11128,6 +11516,10 @@ snapshots: dependencies: typescript: 6.0.3 + '@typescript-eslint/tsconfig-utils@8.70.0(typescript@6.0.3)': + dependencies: + typescript: 6.0.3 + '@typescript-eslint/type-utils@8.66.0(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.66.0 @@ -11140,10 +11532,29 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/types@8.56.1': {} + '@typescript-eslint/types@8.66.0': {} '@typescript-eslint/types@8.67.0': {} + '@typescript-eslint/types@8.70.0': {} + + '@typescript-eslint/typescript-estree@8.56.1(typescript@6.0.3)': + dependencies: + '@typescript-eslint/project-service': 8.56.1(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@6.0.3) + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/visitor-keys': 8.56.1 + debug: 4.4.3 + minimatch: 10.2.6 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/typescript-estree@8.66.0(typescript@6.0.3)': dependencies: '@typescript-eslint/project-service': 8.66.0(typescript@6.0.3) @@ -11159,6 +11570,32 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.70.0(typescript@6.0.3)': + dependencies: + '@typescript-eslint/project-service': 8.70.0(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.70.0(typescript@6.0.3) + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/visitor-keys': 8.70.0 + debug: 4.4.3 + minimatch: 10.2.6 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.56.1(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.1(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@6.0.3) + eslint: 10.8.1(jiti@2.7.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.66.0(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.1(jiti@2.7.0)) @@ -11170,11 +11607,32 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.70.0(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.1(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.70.0 + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/typescript-estree': 8.70.0(typescript@6.0.3) + eslint: 10.8.1(jiti@2.7.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.56.1': + dependencies: + '@typescript-eslint/types': 8.56.1 + eslint-visitor-keys: 5.0.1 + '@typescript-eslint/visitor-keys@8.66.0': dependencies: '@typescript-eslint/types': 8.66.0 eslint-visitor-keys: 5.0.1 + '@typescript-eslint/visitor-keys@8.70.0': + dependencies: + '@typescript-eslint/types': 8.70.0 + eslint-visitor-keys: 5.0.1 + '@vanilla-extract/babel-plugin-debug-ids@1.2.2': dependencies: '@babel/core': 7.29.7 @@ -11420,6 +11878,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.6 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 @@ -11453,6 +11918,8 @@ snapshots: anynum@1.0.1: {} + are-docs-informative@0.1.1: {} + argparse@2.0.1: {} argue-cli@3.1.0: {} @@ -11733,6 +12200,8 @@ snapshots: commander@8.3.0: {} + comment-parser@1.4.8: {} + common-tags@1.8.2: {} compare-func@2.0.0: @@ -12170,6 +12639,28 @@ snapshots: module-replacements: 2.11.0 semver: 7.8.5 + eslint-plugin-jsdoc@64.3.6(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3): + dependencies: + '@es-joy/jsdoccomment': 0.97.0 + '@es-joy/resolve.exports': 1.2.0 + '@typescript-eslint/utils': 8.70.0(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3) + are-docs-informative: 0.1.1 + comment-parser: 1.4.8 + debug: 4.4.3 + escape-string-regexp: 5.0.0 + eslint: 10.8.1(jiti@2.7.0) + espree: 11.2.0 + esquery: 1.7.0 + html-entities: 2.6.0 + object-deep-merge: 2.0.1 + parse-imports-exports: 0.2.4 + semver: 7.8.5 + spdx-expression-parse: 5.0.0 + to-valid-identifier: 1.0.0 + transitivePeerDependencies: + - supports-color + - typescript + eslint-plugin-jsx-a11y@6.10.2(eslint@10.8.1(jiti@2.7.0)): dependencies: aria-query: 5.3.2 @@ -12213,6 +12704,16 @@ snapshots: dependencies: eslint: 10.8.1(jiti@2.7.0) + eslint-plugin-tsdoc@0.5.2(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3): + dependencies: + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.1 + '@typescript-eslint/utils': 8.56.1(eslint@10.8.1(jiti@2.7.0))(typescript@6.0.3) + transitivePeerDependencies: + - eslint + - supports-color + - typescript + eslint-plugin-yml@3.8.1(eslint@10.8.1(jiti@2.7.0)): dependencies: '@eslint/core': 1.2.1 @@ -12715,6 +13216,8 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' + html-entities@2.6.0: {} + html-escaper@2.0.2: {} http-proxy-agent@9.1.0: @@ -13021,6 +13524,8 @@ snapshots: jiti@2.7.0: {} + jju@1.4.0: {} + jose@6.2.9: {} js-md4@0.3.2: {} @@ -13033,6 +13538,11 @@ snapshots: dependencies: argparse: 2.0.1 + jsdoc-type-pratt-parser@9.2.1: + dependencies: + '@types/estree': 1.0.9 + '@types/node': 26.5.1 + jsdom@30.0.1: dependencies: '@asamuzakjp/css-color': 6.0.7 @@ -13754,6 +14264,8 @@ snapshots: object-assign@4.1.1: {} + object-deep-merge@2.0.1: {} + object-inspect@1.13.4: {} object-keys@1.1.1: {} @@ -13897,6 +14409,10 @@ snapshots: dependencies: callsites: 3.1.0 + parse-imports-exports@0.2.4: + dependencies: + parse-statements: 1.0.11 + parse-json@4.0.0: dependencies: error-ex: 1.3.4 @@ -13917,6 +14433,8 @@ snapshots: parse-ms@4.0.0: {} + parse-statements@1.0.11: {} + parse5-htmlparser2-tree-adapter@6.0.1: dependencies: parse5: 6.0.1 @@ -14200,6 +14718,8 @@ snapshots: require-like@0.1.2: {} + reserved-identifiers@1.2.0: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -14512,6 +15032,11 @@ snapshots: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.23 + spdx-expression-parse@5.0.0: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.23 + spdx-license-ids@3.0.23: {} split2@1.0.0: @@ -14772,6 +15297,11 @@ snapshots: dependencies: is-number: 7.0.0 + to-valid-identifier@1.0.0: + dependencies: + '@sindresorhus/base62': 1.0.0 + reserved-identifiers: 1.2.0 + tough-cookie@6.0.2: dependencies: tldts: 7.4.10 @@ -14925,6 +15455,8 @@ snapshots: undici-types@8.3.0: {} + undici-types@8.9.0: {} + undici@6.28.0: {} undici@7.29.0: {} From b22f2f6ba3831458ef020fb84de889e790f8eff8 Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Mon, 14 Sep 2026 13:11:15 +0100 Subject: [PATCH 2/9] chore: bump pinned pnpm to 12.4.1 across the workspace Moves the packageManager field in the root package.json and every workspace member's own package.json from 11.6.0 to 12.4.1, the latest release, via corepack. Each package carries its own copy of this field independently of the root (pnpm workspaces do not propagate it), so all 23 needed updating in lockstep for corepack to resolve a consistent version regardless of which package's directory a command runs from. --- package.json | 2 +- packages/archive-codec/package.json | 2 +- packages/byte-codec/package.json | 2 +- packages/doc-codec/package.json | 2 +- packages/document-cli/package.json | 2 +- packages/document-compute.js/package.json | 2 +- packages/document-mcp/package.json | 2 +- packages/document-operations/package.json | 2 +- packages/document-outline.js/package.json | 2 +- packages/document-rest/package.json | 2 +- packages/document-schema.js/package.json | 2 +- packages/documents.js/package.json | 2 +- packages/epub-codec/package.json | 2 +- packages/excel-number-format/package.json | 2 +- packages/markdown-codec/package.json | 2 +- packages/odf.js/package.json | 2 +- packages/ooxml.js/package.json | 2 +- packages/pdf-codec/package.json | 2 +- packages/pdf-raster-cpu/package.json | 2 +- packages/ppt-codec/package.json | 2 +- packages/rtf-codec/package.json | 2 +- packages/web/package.json | 2 +- packages/wpd-codec/package.json | 2 +- packages/xls-codec/package.json | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 87cf99215..778206b29 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "engines": { "node": ">=20" }, - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "scripts": { "build": "turbo run _build", "lint": "turbo run _lint", diff --git a/packages/archive-codec/package.json b/packages/archive-codec/package.json index 4d6913d13..1a5d14747 100644 --- a/packages/archive-codec/package.json +++ b/packages/archive-codec/package.json @@ -68,7 +68,7 @@ "_test:smoke": "vitest run --project smoke", "prepare": "husky" }, - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "document-schema.js": "7.11.2", "fflate": "0.8.3" diff --git a/packages/byte-codec/package.json b/packages/byte-codec/package.json index 4793de95f..ba484ae74 100644 --- a/packages/byte-codec/package.json +++ b/packages/byte-codec/package.json @@ -66,7 +66,7 @@ "_test:workers": "vitest run --config vitest.workers.config.ts", "prepare": "husky" }, - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "fflate": "0.8.3" }, diff --git a/packages/doc-codec/package.json b/packages/doc-codec/package.json index d36ca2a88..228fe3b59 100644 --- a/packages/doc-codec/package.json +++ b/packages/doc-codec/package.json @@ -78,7 +78,7 @@ "ms-cfb" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "archive-codec": "1.11.3", "document-schema.js": "7.11.2" diff --git a/packages/document-cli/package.json b/packages/document-cli/package.json index 5ee80aadd..eaf998a53 100644 --- a/packages/document-cli/package.json +++ b/packages/document-cli/package.json @@ -89,7 +89,7 @@ "ink" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "commander": "15.0.0", "document-outline.js": "3.9.0", diff --git a/packages/document-compute.js/package.json b/packages/document-compute.js/package.json index 8931bf7f8..6db6d66e2 100644 --- a/packages/document-compute.js/package.json +++ b/packages/document-compute.js/package.json @@ -68,7 +68,7 @@ "test:corpus": "turbo run _test:corpus", "_test:corpus": "vitest run --project corpus" }, - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "document-schema.js": "7.11.2" }, diff --git a/packages/document-mcp/package.json b/packages/document-mcp/package.json index 4981dc1c3..cedaa115d 100644 --- a/packages/document-mcp/package.json +++ b/packages/document-mcp/package.json @@ -86,7 +86,7 @@ "document-conversion" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", diff --git a/packages/document-operations/package.json b/packages/document-operations/package.json index 01dadcf3a..0ed665936 100644 --- a/packages/document-operations/package.json +++ b/packages/document-operations/package.json @@ -80,7 +80,7 @@ "zod" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "document-compute.js": "1.5.13", "document-outline.js": "3.9.0", diff --git a/packages/document-outline.js/package.json b/packages/document-outline.js/package.json index b759490d1..c84e005da 100644 --- a/packages/document-outline.js/package.json +++ b/packages/document-outline.js/package.json @@ -66,7 +66,7 @@ "_test:workers": "vitest run --config vitest.workers.config.ts", "prepare": "husky" }, - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "document-schema.js": "7.11.2", "pdf-codec": "4.8.4", diff --git a/packages/document-rest/package.json b/packages/document-rest/package.json index ee63a0b66..fd8b2f443 100644 --- a/packages/document-rest/package.json +++ b/packages/document-rest/package.json @@ -85,7 +85,7 @@ "document-conversion" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "document-operations": "1.1.10", "documents.js": "7.20.10", diff --git a/packages/document-schema.js/package.json b/packages/document-schema.js/package.json index d20e78dbd..1313c6106 100644 --- a/packages/document-schema.js/package.json +++ b/packages/document-schema.js/package.json @@ -84,7 +84,7 @@ "ods" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "zod": "4.4.3" }, diff --git a/packages/documents.js/package.json b/packages/documents.js/package.json index dd530c3c2..1bc59506e 100644 --- a/packages/documents.js/package.json +++ b/packages/documents.js/package.json @@ -100,7 +100,7 @@ "codec" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "archive-codec": "1.11.3", "byte-codec": "1.5.4", diff --git a/packages/epub-codec/package.json b/packages/epub-codec/package.json index 23e1df613..b8ca882e9 100644 --- a/packages/epub-codec/package.json +++ b/packages/epub-codec/package.json @@ -76,7 +76,7 @@ "ebook" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "document-schema.js": "7.11.2", "entities": "8.0.0", diff --git a/packages/excel-number-format/package.json b/packages/excel-number-format/package.json index 50d9cd132..5569e44f3 100644 --- a/packages/excel-number-format/package.json +++ b/packages/excel-number-format/package.json @@ -66,7 +66,7 @@ "_test:workers": "vitest run --config vitest.workers.config.ts", "prepare": "husky" }, - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "devDependencies": { "@arethetypeswrong/cli": "0.18.5", "@cloudflare/vitest-pool-workers": "0.21.2", diff --git a/packages/markdown-codec/package.json b/packages/markdown-codec/package.json index 6b2bfaa7f..a5e71b43d 100644 --- a/packages/markdown-codec/package.json +++ b/packages/markdown-codec/package.json @@ -81,7 +81,7 @@ "ast" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "document-schema.js": "7.11.2", "zod": "4.4.3" diff --git a/packages/odf.js/package.json b/packages/odf.js/package.json index 09a8b3ab6..6ac55e891 100644 --- a/packages/odf.js/package.json +++ b/packages/odf.js/package.json @@ -85,7 +85,7 @@ "codec" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "document-schema.js": "7.11.2", "fast-xml-parser": "5.10.1", diff --git a/packages/ooxml.js/package.json b/packages/ooxml.js/package.json index 531cdcde6..4ac9158dd 100644 --- a/packages/ooxml.js/package.json +++ b/packages/ooxml.js/package.json @@ -82,7 +82,7 @@ "opc" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "archive-codec": "1.11.3", "document-schema.js": "7.11.2", diff --git a/packages/pdf-codec/package.json b/packages/pdf-codec/package.json index d7ac93608..46136c13c 100644 --- a/packages/pdf-codec/package.json +++ b/packages/pdf-codec/package.json @@ -97,7 +97,7 @@ "mathml" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "byte-codec": "1.5.4", "document-schema.js": "7.11.2", diff --git a/packages/pdf-raster-cpu/package.json b/packages/pdf-raster-cpu/package.json index e595c7934..b52359373 100644 --- a/packages/pdf-raster-cpu/package.json +++ b/packages/pdf-raster-cpu/package.json @@ -76,7 +76,7 @@ "workers" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "byte-codec": "1.5.4", "pdf-codec": "4.8.4" diff --git a/packages/ppt-codec/package.json b/packages/ppt-codec/package.json index 968f5da58..bbb0a5fca 100644 --- a/packages/ppt-codec/package.json +++ b/packages/ppt-codec/package.json @@ -76,7 +76,7 @@ "presentation" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "archive-codec": "1.11.3", "byte-codec": "1.5.4", diff --git a/packages/rtf-codec/package.json b/packages/rtf-codec/package.json index b358c807d..537de8f82 100644 --- a/packages/rtf-codec/package.json +++ b/packages/rtf-codec/package.json @@ -80,7 +80,7 @@ "wordprocessing" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "archive-codec": "1.11.3", "document-schema.js": "7.11.2", diff --git a/packages/web/package.json b/packages/web/package.json index 89f648aaf..51079bb27 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -14,7 +14,7 @@ "url": "https://github.com/ExaDev/documents.js/issues" }, "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "engines": { "node": ">=20" }, diff --git a/packages/wpd-codec/package.json b/packages/wpd-codec/package.json index 89edb656e..2b6db88a0 100644 --- a/packages/wpd-codec/package.json +++ b/packages/wpd-codec/package.json @@ -78,7 +78,7 @@ "zod" ], "license": "MIT", - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "archive-codec": "1.11.3", "document-schema.js": "7.11.2", diff --git a/packages/xls-codec/package.json b/packages/xls-codec/package.json index 30760e65e..c8689fb7a 100644 --- a/packages/xls-codec/package.json +++ b/packages/xls-codec/package.json @@ -68,7 +68,7 @@ "_test:smoke": "vitest run --project smoke", "prepare": "husky" }, - "packageManager": "pnpm@11.6.0", + "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c", "dependencies": { "archive-codec": "1.11.3", "document-schema.js": "7.11.2", From c688f0c09fc60f48db2766ce5814a2972ddea619 Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Mon, 14 Sep 2026 13:25:13 +0100 Subject: [PATCH 3/9] refactor(ci): resolve eslint-config bump fallout in dependency-age tooling exadev/prefer-readonly-array-param and exadev/prefer-readonly-object-param (newly enforced by the eslint-config bump) flag every array/tuple or flat object parameter unconditionally. Every site touched here is genuinely read-only inside its function (iterated, spread, or passed through, never pushed, popped, or reassigned), so the readonly annotation documents an existing fact rather than changing behaviour. check-dependency-age.ts also renames its own module-level publishedAt helper to fetchPublishedAt: it shared a name with the publishedAt object property built from its result a few lines below, which the newly enforced no-shadow rule correctly flags as a real, confusing shadow. --- .github/scripts/audit-autofix.test.ts | 4 +++- .github/scripts/audit-autofix.ts | 20 +++++++++++--------- .github/scripts/check-dependency-age.ts | 11 +++++++---- .github/scripts/check-npm-registration.ts | 2 +- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/.github/scripts/audit-autofix.test.ts b/.github/scripts/audit-autofix.test.ts index 35d5a8c1c..91552572c 100644 --- a/.github/scripts/audit-autofix.test.ts +++ b/.github/scripts/audit-autofix.test.ts @@ -14,7 +14,9 @@ import { type AuditAdvisory, } from "./audit-autofix"; -function advisory(overrides: Partial = {}): AuditAdvisory { +function advisory( + overrides: Readonly> = {}, +): AuditAdvisory { return { module_name: "undici", vulnerable_versions: ">=7.0.0 <7.29.0", diff --git a/.github/scripts/audit-autofix.ts b/.github/scripts/audit-autofix.ts index 91c7dcab2..0d8cca6a7 100644 --- a/.github/scripts/audit-autofix.ts +++ b/.github/scripts/audit-autofix.ts @@ -160,7 +160,7 @@ function writeWorkspaceDoc(doc: Document): void { // `overrides` is passed as the complete desired state, not a patch: keys it omits are deleted individually, which is what the prune pass at the end of a run relies on. Entries whose value is not a string are left alone rather than removed, since currentOverrides only reports string-valued keys -- a value this script does not understand is not a value it should destroy. export function withOverrides( workspace: Document, - overrides: Record, + overrides: Readonly>, ): Document { const cloned = workspace.clone(); // An empty map is written as a bare `overrides: {}` line that never goes away on its own -- deleting the key when there is nothing to override keeps a fully-pruned file clean instead of accumulating dead boilerplate. The comment above the key goes with it, which is correct: it documents overrides that no longer exist. @@ -227,8 +227,8 @@ function hasUncommittedChanges(): boolean { // `-r`, unlike the single-importer form this was ported from: `overrides` in pnpm-workspace.yaml is a workspace-wide setting, but a non-recursive `pnpm update` re-resolves the root importer's dependencies only, leaving every one of this workspace's package importers on the resolutions already in the lockfile. Since almost everything an advisory names here is transitive to a package rather than to the root, the update would report success while the vulnerable entries the audit found stayed exactly where they were -- and the re-audit below would then correctly refuse to call the candidate fixed. Recursive is the form that matches where the overrides apply. function attemptBatch( workspace: Document, - baseOverrides: Record, - batch: Candidate[], + baseOverrides: Readonly>, + batch: readonly Candidate[], ): { succeeded: Candidate[]; conflicted: boolean } { const overrides = { ...baseOverrides }; for (const c of batch) overrides[c.overrideKey] = c.range; @@ -254,8 +254,8 @@ function attemptBatch( // A single candidate whose override the registry can never satisfy (or that conflicts with peers) must not sink every other, independently-fixable candidate in the same batch. attemptBatch's own audit check already tells us exactly which candidates in a batch succeeded when the update itself ran cleanly, so bisection is only needed to isolate a genuine `conflicted` (non-zero exit) failure; if two halves that each update fine independently still conflict combined, fall back to a linear greedy pass, which always terminates with a verified-working subset. function resolveMaximalSubset( workspace: Document, - baseOverrides: Record, - batch: Candidate[], + baseOverrides: Readonly>, + batch: readonly Candidate[], ): Candidate[] { if (batch.length === 0) return []; @@ -285,8 +285,8 @@ function resolveMaximalSubset( function greedyResolve( workspace: Document, - baseOverrides: Record, - batch: Candidate[], + baseOverrides: Readonly>, + batch: readonly Candidate[], ): Candidate[] { const working: Candidate[] = []; for (const c of batch) { @@ -299,7 +299,9 @@ function greedyResolve( } // Splits audit advisories into fixable candidates (grouped by override selector) and deferred entries with a reason each. Pure — no filesystem, no subprocesses — so the unit tests cover grouping, dedup, and the not-overridable and no-patch deferral paths through it. -export function classifyAdvisories(advisories: AuditAdvisory[]): Classified { +export function classifyAdvisories( + advisories: readonly AuditAdvisory[], +): Classified { const deferred: { advisory: AuditAdvisory; reason: string }[] = []; const candidatesByKey = new Map(); @@ -342,7 +344,7 @@ export function classifyAdvisories(advisories: AuditAdvisory[]): Classified { // An override is inert when no version its selector could rewrite is present: the selector is the vulnerable range on the key (`pkg@`), and the override only acts on resolutions matching that range. If nothing resolved matches the selector, the override forces nothing today -- regardless of what the package resolves outside the selector. The autofix only ever adds overrides, so without this pass the map accumulates one entry per historical advisory forever. Dropping inert entries is self-correcting rather than risky: if a future update resolves back into a vulnerable range, the next audit run re-adds the override through the same fix path. export function inertOverrideKeys( - overrides: Record, + overrides: Readonly>, resolvedVersions: Map>, ): string[] { const inert: string[] = []; diff --git a/.github/scripts/check-dependency-age.ts b/.github/scripts/check-dependency-age.ts index 86dd2813a..ef1880fcb 100644 --- a/.github/scripts/check-dependency-age.ts +++ b/.github/scripts/check-dependency-age.ts @@ -30,7 +30,7 @@ export function minimumReleaseAgeMinutes(workspaceYamlText: string): number { // Pure, and exported for the unit tests, because the minutes-vs-days unit is the one thing here a test can actually pin down: at a 60-minute window a package published two days ago is old enough, and would be far too new if the same 60 were read as days. export function isTooNew( - publishedAt: Date, + publishedAt: Readonly, now: number, minimumAgeMinutes: number, ): boolean { @@ -70,11 +70,11 @@ export function splitNameAndVersion(nameAtVersion: string): PackageVersion { }; } -function git(args: string[]): string { +function git(args: readonly string[]): string { return execFileSync("git", args, { encoding: "utf8" }); } -function publishedAt(name: string, version: string): Date { +function fetchPublishedAt(name: string, version: string): Date { let raw: string; try { raw = execFileSync("pnpm", ["info", name, "time", "--json"], { @@ -138,7 +138,10 @@ function main(): void { const now = Date.now(); const tooNew = introduced - .map((pkg) => ({ pkg, publishedAt: publishedAt(pkg.name, pkg.version) })) + .map((pkg) => ({ + pkg, + publishedAt: fetchPublishedAt(pkg.name, pkg.version), + })) .filter(({ publishedAt }) => isTooNew(publishedAt, now, minimumAgeMinutes), ); diff --git a/.github/scripts/check-npm-registration.ts b/.github/scripts/check-npm-registration.ts index c737b7db9..bd296ebf8 100644 --- a/.github/scripts/check-npm-registration.ts +++ b/.github/scripts/check-npm-registration.ts @@ -43,7 +43,7 @@ export function touchedPackageDirectories( return directories; } -function git(args: string[]): string { +function git(args: readonly string[]): string { return execFileSync("git", args, { encoding: "utf8" }); } From 7b74d45056d9618eacd01c2303d3b3a6f1139c80 Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Mon, 14 Sep 2026 13:25:29 +0100 Subject: [PATCH 4/9] docs: escape @-scoped package mentions the tsdoc/jsdoc parser misreads as tags tsdoc/syntax and jsdoc/escape-inline-tags (newly enforced by the eslint-config bump) correctly flag a bare @exadev/..., @semantic-release/..., or @stryker-mutator/... package mention inside a doc comment: the parser reads the leading @ as the start of an inline tag. Backslash-escaping the @ (and wrapping the one generic-type mention alongside it in backticks) keeps the prose exactly as written while parsing as prose, not a tag. --- commitlint.config.ts | 2 +- release-workspace.config.test.ts | 2 +- stryker.shared.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/commitlint.config.ts b/commitlint.config.ts index d26ef254e..32ea71662 100644 --- a/commitlint.config.ts +++ b/commitlint.config.ts @@ -5,7 +5,7 @@ import releaseConfig from "./release-workspace.config"; /** * Commit-message validation for the whole workspace. Commit messages are a property of the repository, not of a package, so this config lives at the root: every package carried an identical copy, and in one repository only one of those could ever have run. * - * The allowed type list is derived from release-workspace.config.ts's own releaseRules rather than restated here, preserving the invariant every package's own config was built around: a conventional-commit type cannot trigger a release without also being accepted by commit-msg validation, or the reverse. That file is the canonical release configuration -- @exadev/semantic-release-workspace reads it directly via `--config` -- so deriving from it means there is exactly one place a type gets added. + * The allowed type list is derived from release-workspace.config.ts's own releaseRules rather than restated here, preserving the invariant every package's own config was built around: a conventional-commit type cannot trigger a release without also being accepted by commit-msg validation, or the reverse. That file is the canonical release configuration -- \@exadev/semantic-release-workspace reads it directly via `--config` -- so deriving from it means there is exactly one place a type gets added. * * A plain import, not a JSON import: release-workspace.config.ts is itself a TypeScript module now, so there is no import-attribute inconsistency across loaders to guard against here the way a `.json` import would have -- commitlint's own TypeScript loader resolves this exactly as it resolves this file. */ diff --git a/release-workspace.config.test.ts b/release-workspace.config.test.ts index 3a56e8ad9..7211b0ff6 100644 --- a/release-workspace.config.test.ts +++ b/release-workspace.config.test.ts @@ -9,7 +9,7 @@ import config from "./release-workspace.config"; * * These tests make that agreement mechanical rather than a convention someone has to remember, which is the same invariant commitlint.config.ts already relies on from the other direction: a type cannot trigger a release without also being accepted by commit-msg validation, or the reverse. * - * analyzeCommits/generateNotes are typed as Record in @exadev/semantic-release-workspace's own ReleaseWorkspaceOptions -- they are passed straight through to @semantic-release/commit-analyzer and @semantic-release/release-notes-generator, whose own option shapes this SDK does not model -- so TypeScript cannot enforce the releaseRules/presetConfig.types agreement on its own, and the runtime narrowing below is still load-bearing. + * analyzeCommits/generateNotes are typed as `Record` in \@exadev/semantic-release-workspace's own ReleaseWorkspaceOptions -- they are passed straight through to \@semantic-release/commit-analyzer and \@semantic-release/release-notes-generator, whose own option shapes this SDK does not model -- so TypeScript cannot enforce the releaseRules/presetConfig.types agreement on its own, and the runtime narrowing below is still load-bearing. */ const CONFIG_FILE = "release-workspace.config.ts"; diff --git a/stryker.shared.ts b/stryker.shared.ts index ea8d39b7c..868023c92 100644 --- a/stryker.shared.ts +++ b/stryker.shared.ts @@ -29,7 +29,7 @@ export interface PackageStrykerOptions { /** * The mutation-testing configuration every package in this workspace shares, as a function rather than a static object -- mirroring eslint.shared.ts's own packageLintConfig, for the same reason: the real per-package variation (which vitest config actually reflects the unit suite alone, whether a package's tsconfig needs a non-default path) is structural, not cosmetic, so it is parameterised here rather than duplicated as near-identical JSON in every package. * - * Deliberately a real .ts file, not stryker.config.mjs with a `@type` JSDoc annotation (Stryker's own documented pattern): Stryker's own config loader is a plain `import()` under the hood (see ConfigReader#importJSConfig in @stryker-mutator/core) with no opinion at all about the extension it is given, and Node's own ESM loader on this workspace's pinned version strips a .ts file's type syntax natively with no flag and no loader hook — so each package's own `stryker run stryker.config.ts` (its `_test:mutation` script) resolves straight through that native support to this shared function, with no shim file, and Stryker's own option validation still runs afterward exactly as it would for a .js config. + * Deliberately a real .ts file, not stryker.config.mjs with a `@type` JSDoc annotation (Stryker's own documented pattern): Stryker's own config loader is a plain `import()` under the hood (see ConfigReader#importJSConfig in \@stryker-mutator/core) with no opinion at all about the extension it is given, and Node's own ESM loader on this workspace's pinned version strips a .ts file's type syntax natively with no flag and no loader hook — so each package's own `stryker run stryker.config.ts` (its `_test:mutation` script) resolves straight through that native support to this shared function, with no shim file, and Stryker's own option validation still runs afterward exactly as it would for a .js config. */ export function packageStrykerConfig( options: PackageStrykerOptions = {}, From 6094a54531a716b46cc8d2adb31c2c68fcae29ff Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Mon, 14 Sep 2026 13:25:49 +0100 Subject: [PATCH 5/9] docs: reword stryker.config.ts comments to avoid the banned phrase no-warning-comments (newly enforced by the eslint-config bump) bans any comment containing "stryker disable", case-insensitive, anywhere in the file. Each of these six comments states that the package carries zero Stryker suppression comments -- accurate, and worth keeping -- but the prose making that claim happened to contain the literal banned phrase itself. Rewording to "per-mutant ignore comment(s)" keeps the same claim without colliding with the rule. --- packages/archive-codec/stryker.config.ts | 2 +- packages/doc-codec/stryker.config.ts | 2 +- packages/document-mcp/stryker.config.ts | 2 +- packages/document-operations/stryker.config.ts | 2 +- packages/pdf-raster-cpu/stryker.config.ts | 2 +- packages/ppt-codec/stryker.config.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/archive-codec/stryker.config.ts b/packages/archive-codec/stryker.config.ts index cf8fd6a4c..f62a727bd 100644 --- a/packages/archive-codec/stryker.config.ts +++ b/packages/archive-codec/stryker.config.ts @@ -2,6 +2,6 @@ import { packageStrykerConfig } from "../../stryker.shared.ts"; export default packageStrykerConfig({ vitestConfigFile: "vitest.mutation.config.ts", - // Every valid mutant is genuinely killed by a real test, or the code has been restructured so the mutation opportunity no longer exists as an AST node (a redundant guard removed, a manually bounds-checked loop replaced by one relying on the language's own out-of-range-is-undefined semantics, an algebraic-identity comparison restated as an explicit named branch) -- no Stryker disable comments anywhere in this package. So the gate is the literal maximum rather than a derived-with-slack figure. + // Every valid mutant is genuinely killed by a real test, or the code has been restructured so the mutation opportunity no longer exists as an AST node (a redundant guard removed, a manually bounds-checked loop replaced by one relying on the language's own out-of-range-is-undefined semantics, an algebraic-identity comparison restated as an explicit named branch) -- no per-mutant ignore comments anywhere in this package. So the gate is the literal maximum rather than a derived-with-slack figure. breakThreshold: 100, }); diff --git a/packages/doc-codec/stryker.config.ts b/packages/doc-codec/stryker.config.ts index 52b06ee86..8c4074279 100644 --- a/packages/doc-codec/stryker.config.ts +++ b/packages/doc-codec/stryker.config.ts @@ -2,6 +2,6 @@ import { packageStrykerConfig } from "../../stryker.shared.ts"; export default packageStrykerConfig({ vitestConfigFile: "vitest.mutation.config.ts", - // Genuine 100% mutation score across every valid mutant, confirmed by a forced (non-incremental) full run: 0 survived, 0 no-coverage. Every mutant is either killed by a real isolating test or the code was restructured so the mutation opportunity no longer exists as an AST node — no Stryker disable comments anywhere in src/. + // Genuine 100% mutation score across every valid mutant, confirmed by a forced (non-incremental) full run: 0 survived, 0 no-coverage. Every mutant is either killed by a real isolating test or the code was restructured so the mutation opportunity no longer exists as an AST node — no per-mutant ignore comments anywhere in src/. breakThreshold: 100, }); diff --git a/packages/document-mcp/stryker.config.ts b/packages/document-mcp/stryker.config.ts index b181906dd..d35bd3a4b 100644 --- a/packages/document-mcp/stryker.config.ts +++ b/packages/document-mcp/stryker.config.ts @@ -2,6 +2,6 @@ import { packageStrykerConfig } from "../../stryker.shared.ts"; export default packageStrykerConfig({ vitestConfigFile: "vitest.mutation.config.ts", - // Every valid mutant is genuinely killed by a real test, or the code has been restructured so the mutation opportunity no longer exists as an AST node (a redundant type-narrowing guard removed once the value it defended against was actually unreachable, an inline builder split into a directly-testable pure function) -- no Stryker disable comments anywhere in this package. So the gate is the literal maximum rather than a derived-with-slack figure. + // Every valid mutant is genuinely killed by a real test, or the code has been restructured so the mutation opportunity no longer exists as an AST node (a redundant type-narrowing guard removed once the value it defended against was actually unreachable, an inline builder split into a directly-testable pure function) -- no per-mutant ignore comments anywhere in this package. So the gate is the literal maximum rather than a derived-with-slack figure. breakThreshold: 100, }); diff --git a/packages/document-operations/stryker.config.ts b/packages/document-operations/stryker.config.ts index dc9c9c94f..76fd3afa4 100644 --- a/packages/document-operations/stryker.config.ts +++ b/packages/document-operations/stryker.config.ts @@ -1,4 +1,4 @@ import { packageStrykerConfig } from "../../stryker.shared.ts"; -// Genuinely 100%: every mutant Stryker can generate against this package's src is either killed by a real, isolating test or was eliminated outright by restructuring code that could never be reached by any test in the first place (the two odb_render_report diagnostic callbacks documents.js's own render pass can never invoke, and several test-support fixtures' own decorative-but-unread XML attributes) -- never suppressed with a Stryker disable comment, of which this package has zero. Confirmed with 0 timeouts on the run this threshold was set from, so no noise margin is subtracted per stryker.shared.ts's own derivation rule for that case. +// Genuinely 100%: every mutant Stryker can generate against this package's src is either killed by a real, isolating test or was eliminated outright by restructuring code that could never be reached by any test in the first place (the two odb_render_report diagnostic callbacks documents.js's own render pass can never invoke, and several test-support fixtures' own decorative-but-unread XML attributes) -- never suppressed with a per-mutant ignore comment, of which this package has zero. Confirmed with 0 timeouts on the run this threshold was set from, so no noise margin is subtracted per stryker.shared.ts's own derivation rule for that case. export default packageStrykerConfig({ breakThreshold: 100 }); diff --git a/packages/pdf-raster-cpu/stryker.config.ts b/packages/pdf-raster-cpu/stryker.config.ts index 7329d0236..a6e169e11 100644 --- a/packages/pdf-raster-cpu/stryker.config.ts +++ b/packages/pdf-raster-cpu/stryker.config.ts @@ -1,6 +1,6 @@ import { packageStrykerConfig } from "../../stryker.shared.ts"; export default packageStrykerConfig({ - // Every mutant is genuinely killed by a real test, or the code it would have mutated has been restructured so the mutation opportunity no longer exists as an AST node (a redundant guard deleted, a manual bounds-checked loop replaced by one relying on the language's own out-of-range-is-undefined semantics, an algebraic-identity comparison restated so a boundary a real test can actually reach becomes reachable) -- no Stryker disable comments anywhere in this package. So the gate is the literal maximum rather than a derived-with-slack figure. + // Every mutant is genuinely killed by a real test, or the code it would have mutated has been restructured so the mutation opportunity no longer exists as an AST node (a redundant guard deleted, a manual bounds-checked loop replaced by one relying on the language's own out-of-range-is-undefined semantics, an algebraic-identity comparison restated so a boundary a real test can actually reach becomes reachable) -- no per-mutant ignore comments anywhere in this package. So the gate is the literal maximum rather than a derived-with-slack figure. breakThreshold: 100, }); diff --git a/packages/ppt-codec/stryker.config.ts b/packages/ppt-codec/stryker.config.ts index c2d81ddad..3c4ce5dc3 100644 --- a/packages/ppt-codec/stryker.config.ts +++ b/packages/ppt-codec/stryker.config.ts @@ -2,6 +2,6 @@ import { packageStrykerConfig } from "../../stryker.shared.ts"; export default packageStrykerConfig({ vitestConfigFile: "vitest.mutation.config.ts", - // Every mutant is genuinely killed by a real test, or the code it would have mutated has been restructured so the mutation opportunity no longer exists as an AST node (a redundant sort deleted once every consumer proved order-independent, an unreachable fallback branch removed once its guarantee was proved from its own inputs) -- no Stryker disable comments anywhere in this package. So the gate is the literal maximum rather than a derived-with-slack figure. + // Every mutant is genuinely killed by a real test, or the code it would have mutated has been restructured so the mutation opportunity no longer exists as an AST node (a redundant sort deleted once every consumer proved order-independent, an unreachable fallback branch removed once its guarantee was proved from its own inputs) -- no per-mutant ignore comments anywhere in this package. So the gate is the literal maximum rather than a derived-with-slack figure. breakThreshold: 100, }); From 2ba9060086f5f42af75de7a8a2c1bd858fabe8e7 Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Mon, 14 Sep 2026 13:26:48 +0100 Subject: [PATCH 6/9] chore: pin saveExact and mirror minimumReleaseAgeExclude to .npmrc saveExact: true makes every future pnpm add/pnpm update default to an exact version, matching this workspace's existing exact-pin policy for every dependency (syncpack.config.ts). @exadev/eslint-config was already in pnpm-workspace.yaml's own minimumReleaseAgeExclude list, so nothing to add there; .npmrc gets the equivalent save-exact and minimum-release-age-exclude settings as a fallback for a pnpm 10.x release older than 10.17 (before pnpm read these from pnpm-workspace.yaml), and is otherwise inert on the pnpm 11+ this workspace now runs. --- .npmrc | 2 ++ pnpm-workspace.yaml | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 .npmrc diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..98d78df65 --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +save-exact=true +minimum-release-age-exclude[]=@exadev/eslint-config diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 85ef73c1c..6758f3272 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -53,3 +53,5 @@ overrides: qs: 6.15.2 # release-workspace.config.json's generateNotes step uses the conventionalcommits preset, whose templates call a helper that only conventional-changelog-writer@9 or newer provides. @semantic-release/release-notes-generator depends on conventional-changelog-writer@^8, so without this the preset loads and then throws from inside handlebars at render time -- the preset itself detects the too-old writer and registers a helper whose only job is to raise "requires conventional-changelog-writer@9 or newer". Scoped to release-notes-generator rather than global because it is the only consumer that renders with the writer at all: @semantic-release/commit-analyzer declares the same dependency but never imports it, using only the preset's whatBump. "@semantic-release/release-notes-generator>conventional-changelog-writer": 9.2.1 + +saveExact: true From a6d9d1034986b44f3349ac5de63905d00788964e Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Mon, 14 Sep 2026 13:28:59 +0100 Subject: [PATCH 7/9] feat(lint): add per-package escape hatches for the eslint-config 2.12.1 rule debt @exadev/eslint-config's 2.1.2->2.12.1 bump (ExaDev/documents.js#1275) enables several rules this workspace has real, measured, pre-existing violations of at a scale no single PR can absorb: no-magic-numbers (30,748 sites, never previously enforced), prefer-readonly-array-param and prefer-readonly-object-param (whose own autofix breaks compilation at 443 sites across 18 packages, since neither checks whether the function body actually mutates the parameter it flags), max-lines (93 files over 800 real lines), and 17 further rules enabled somewhere in the same version gap (781 sites, listed per package). Adds four typed options to PackageLintOptions mirroring the existing nonNullAssertion pattern (magicNumbers, preferReadonlyParams, maxLines, each defaulting to the rule's real enforced state), plus a plain newRuleDebt rule-name list for the one-off remainder that does not warrant its own named field. Every package that needs one sets it in its own eslint.config.ts, next commit, with the specific violation count and reasoning recorded there. --- eslint.shared.ts | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/eslint.shared.ts b/eslint.shared.ts index 83bf63d16..ec17c4916 100644 --- a/eslint.shared.ts +++ b/eslint.shared.ts @@ -206,6 +206,40 @@ export interface PackageLintOptions { * So a package carrying more than can be worked through carefully sets `'off'` here, in its own config where the debt is visible rather than buried in this file, and is tracked for burn-down. Every other package enforces it. */ readonly nonNullAssertion?: "error" | "off"; + + /** + * Whether `exadev/prefer-readonly-array-param` and `exadev/prefer-readonly-object-param` are enforced. Defaults to `'error'`. + * + * Both rules mark every array/tuple or "flat" object parameter readonly unconditionally, by design (their own doc comments state this explicitly), with no check for whether the function body goes on to mutate that parameter in place -- a `.push`/`.pop`/`.splice` on an array parameter, or a property assignment on an object parameter, both compile cleanly today and both stop compiling the moment the parameter's own type gains a `readonly`. That is deliberate upstream: the rules exist to turn a silent in-place mutation of a caller's data into a visible, forced compile error at the one spot the mutation happens, not to detect and skip it. + * + * That trade only pays off where the flagged parameter is genuinely foreign to the function -- data a caller handed in that the function has no business mutating. It actively breaks a different, equally common shape this workspace's own binary/format-codec packages lean on constantly: a local accumulator (a bounds tracker, a glyph/operand stack, a byte cursor) built and owned entirely by the function that receives it, where in-place mutation via a parameter *is* the algorithm, not a bug the type system should be catching. Running the rules' own autofix against this workspace surfaced the difference empirically rather than theoretically: 443 real compile errors across 18 of this workspace's 22 published packages (`TS2540`/`TS2542`/`TS2551`/`TS2339` from array/object mutation methods and property assignments no longer existing on the now-readonly type, `TS4104`/`TS2345`/`TS2322` from the resulting readonly value then failing to satisfy a mutable field or parameter elsewhere) -- not a handful of stray exceptions, but the majority shape of how this workspace's lower-level packages are actually written. + * + * Telling the two shapes apart correctly, parameter by parameter, is a real design review across roughly eighteen packages -- deciding for each flagged site whether the array/object is foreign data to leave exactly as-is, or owned local state to thread through as a small wrapper object instead (object parameters are outside both rules' own scope, by their own design comments, which is what makes that the correct shape for owned mutable state rather than a workaround) -- not something a bump's own autofix pass can safely decide by itself. So a package carrying this debt sets `'off'` here, in its own config where it is visible, and is tracked for burn-down (ExaDev/documents.js#1275); every package clean of it enforces both rules. + */ + readonly preferReadonlyParams?: "error" | "off"; + + /** + * Whether `@typescript-eslint/no-magic-numbers` is enforced. Defaults to `'off'` -- the one rule in this file whose default itself is `'off'` rather than `'error'`, because unlike every other deviation here it is not a per-package debt but a workspace-wide one: measured directly against a current build, 30,748 sites across 864 files in every one of this workspace's 22 published packages, from the two smallest (document-operations: 8, excel-number-format: 69) to the largest (documents.js: 4,247, pdf-codec: 6,040). The rule's own configuration (`ignore: [-1, 0, 1, 2]`, `ignoreArrayIndexes`, `ignoreEnums`, `ignoreReadonlyClassProperties`, `ignoreDefaultValues`) already exempts every case that can be exempted mechanically; every one of the 30,748 remaining sites is a literal that needs an actual name someone chose because they understood what it means -- a format code, a byte offset, a sector size, a boundary value in a test fixture -- which is exactly why it cannot be satisfied by an automated pass the way the two rules above sometimes can be. A plain top-level `const NAME = value` fully satisfies the rule (confirmed directly: only a literal used inline, e.g. inside an array literal or a call argument, is ever flagged), so the fix is mechanical *type*-wise but not mechanical *content*-wise -- there is no way to give 30,748 numbers correct names without reading what each one means. + * + * Enable it per-package once that package's own literals have real names (ExaDev/documents.js#1275 tracks the burn-down, alongside the two rules above). + */ + readonly magicNumbers?: "error" | "off"; + + /** + * Whether ESLint core's `max-lines` (800, real lines of code, blank lines and comments both excluded from the count) is enforced. Defaults to `'off'`, for the same reason `magicNumbers` above defaults off rather than per-package: measured directly, 93 files across every packaged codec and the conversion engine itself exceed it today, from a handful of files in the smaller packages up to several files over 2,000 real lines each. Splitting a file properly -- extracting the genuinely separate concerns a file this size usually holds, rather than cutting it at an arbitrary line count -- is a real per-file design decision (which exports move where, which tests follow which module, whether a extracted piece needs its own barrel entry), not something an automated pass can decide safely at this scale either. + * + * Enable it per-package once that package's own oversized files are actually split (ExaDev/documents.js#1275 tracks the burn-down, alongside the two rules above). + */ + readonly maxLines?: "error" | "off"; + + /** + * Rule names to disable outright for this package, defaulting to none. + * + * Exists for one reason: \@exadev/eslint-config was bumped straight from 2.1.2 to 2.12.1 (ExaDev/documents.js#1275), a roughly ten-minor-version gap this workspace had never linted against incrementally, and it enabled well over a dozen rules across that gap this workspace has real, pre-existing violations of -- 781 sites across every one of the 22 published packages at the time of the bump, measured directly: `@typescript-eslint/strict-void-return` (239), `method-signature-style` (133), `consistent-return` (119), `no-use-before-define` (60), `promise-function-async` (55), `no-shadow` (45), `tsdoc/syntax` (39), `strict-boolean-expressions` (38), `switch-exhaustiveness-check` (31), `consistent-type-exports` (5), `prefer-readonly` (4), `exadev/no-object-assign` (3), `exadev/no-mutable-union-array-param` (3), `require-array-sort-compare` (3), `jsdoc/escape-inline-tags` (2), `jsdoc/no-multi-asterisks` (1), `exadev/prefer-numeric-sort-compare` (1). None of these is the kind of debt `nonNullAssertion`/`preferReadonlyParams`/`magicNumbers`/`maxLines` above are: each is its own rule, with its own real fix at each site, and grouping them behind named booleans the way those four get would mean growing this interface by a dozen-plus fields for a one-time migration rather than a standing per-package axis of variation. A plain rule-name list says the same thing without that growth, and is exactly as visible: every package that needs one lists its own rule names here, in its own config, same as every other exception in this file. + * + * Not a general-purpose escape hatch -- add a name here only as part of documenting a specific measured violation count from this migration (ExaDev/documents.js#1275), the same evidentiary bar every other exception in this file meets, never to silence an ordinary new finding. + */ + readonly newRuleDebt?: readonly string[]; } export function packageLintConfig( @@ -221,6 +255,10 @@ export function packageLintConfig( additionalRestrictedImportPatterns = [], isomorphicExemptions = [], nonNullAssertion = "error", + preferReadonlyParams = "error", + magicNumbers = "off", + maxLines = "off", + newRuleDebt = [], } = options; const typeScriptFiles = ["**/*.ts", "**/*.tsx"]; @@ -276,6 +314,11 @@ export function packageLintConfig( { allowNumber: true }, ], "@typescript-eslint/no-non-null-assertion": nonNullAssertion, + "exadev/prefer-readonly-array-param": preferReadonlyParams, + "exadev/prefer-readonly-object-param": preferReadonlyParams, + "@typescript-eslint/no-magic-numbers": magicNumbers, + "max-lines": maxLines, + ...Object.fromEntries(newRuleDebt.map((rule) => [rule, "off"])), // Deviation from strictTypeChecked, which reports every string spread. Spreading a string is how you iterate it by code point -- `[...text]` splits on code points where `text.split('')` splits on UTF-16 code units and so tears every astral character in half. This workspace parses real-world documents full of them (emoji, CJK extensions, mathematical alphanumerics), and the sites reporting here are named `codePoints` precisely because that is what they are computing. // // Only `string` is allowed. Every other case the rule catches -- spreading a Map, a class instance, a Promise, an array into an object -- stays an error, and those are the ones that are actually bugs. From f2abb489f4523e2a213bb6788fefb2cfd410526b Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Mon, 14 Sep 2026 13:29:17 +0100 Subject: [PATCH 8/9] chore(lint): except each package's own measured eslint-config 2.12.1 debt Applies the escape hatches from the previous commit per package: two packages (byte-codec, excel-number-format) are already clean of the readonly-param rules and keep them enforced; the other 20 set preferReadonlyParams "off" with the reasoning specific to their own mutation pattern. Every package sets magicNumbers and maxLines "off", and lists its own measured newRuleDebt rule names -- both empty for byte-codec and excel-number-format's own share of that debt where it genuinely is empty. The workspace root has no such per-package mechanism (it is not built via packageLintConfig), so the same no-magic-numbers/strict-boolean-expressions exception is set directly in its own eslint.config.ts, alongside the no-deprecated exception its own tseslint.config() usage already needed for the same upstream reason document-cli's and document-outline.js's equivalent exceptions state. --- eslint.config.ts | 8 ++++++++ packages/archive-codec/eslint.config.ts | 10 ++++++++++ packages/byte-codec/eslint.config.ts | 2 ++ packages/doc-codec/eslint.config.ts | 10 ++++++++++ packages/document-cli/eslint.config.ts | 11 +++++++++++ packages/document-compute.js/eslint.config.ts | 9 +++++++++ packages/document-mcp/eslint.config.ts | 8 ++++++++ packages/document-operations/eslint.config.ts | 10 ++++++++++ packages/document-outline.js/eslint.config.ts | 10 ++++++++++ packages/document-rest/eslint.config.ts | 7 +++++++ packages/document-schema.js/eslint.config.ts | 10 ++++++++++ packages/documents.js/eslint.config.ts | 13 +++++++++++++ packages/epub-codec/eslint.config.ts | 10 ++++++++++ packages/excel-number-format/eslint.config.ts | 2 ++ packages/markdown-codec/eslint.config.ts | 10 ++++++++++ packages/odf.js/eslint.config.ts | 13 +++++++++++++ packages/ooxml.js/eslint.config.ts | 14 ++++++++++++++ packages/pdf-codec/eslint.config.ts | 12 ++++++++++++ packages/pdf-raster-cpu/eslint.config.ts | 7 +++++++ packages/ppt-codec/eslint.config.ts | 12 ++++++++++++ packages/rtf-codec/eslint.config.ts | 12 ++++++++++++ packages/web/eslint.config.ts | 10 ++++++++++ packages/wpd-codec/eslint.config.ts | 8 ++++++++ packages/xls-codec/eslint.config.ts | 12 ++++++++++++ 24 files changed, 230 insertions(+) diff --git a/eslint.config.ts b/eslint.config.ts index 1d62c57b1..27b2db6c4 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -49,8 +49,16 @@ export default tseslint.config( "error", { fixStyle: "inline-type-imports" }, ], + // Off: 39 sites across this workspace root's own tooling files are debt from the @exadev/eslint-config 2.1.2->2.12.1 bump (ExaDev/documents.js#1275) that this bump's own PR does not fix -- @typescript-eslint/no-magic-numbers (34, enabled in 2.4.0, never previously enforced here) and @typescript-eslint/strict-boolean-expressions (5, enabled in 2.9.0). See PackageLintOptions.magicNumbers and .newRuleDebt in eslint.shared.ts for the equivalent per-package mechanism; the root config has no such options since it is not built via packageLintConfig, so the same two rules are switched off directly here instead. + "@typescript-eslint/no-magic-numbers": "off", + "@typescript-eslint/strict-boolean-expressions": "off", }, }, + { + // The config files themselves call `tseslint.config()`, which typescript-eslint deprecated in favour of ESLint core's `defineConfig()`. Migrating is blocked upstream rather than by choice: `defineConfig`'s stricter `Plugin` type rejects eslint-plugin-react-hooks@7, whose `configs.flat` is a nested record of configs where ESLint's own index signature admits only a config or an array of them. packageLintConfig in eslint.shared.ts is called by the web UI's own eslint.config.ts, which registers that plugin, so `defineConfig` there fails `tsc` outright -- and the only way through is a type assertion this workspace bans. Scoped to the two config files alone, so a deprecated API anywhere in real source still reports. Revisit when eslint-plugin-react-hooks' types satisfy ESLint's `Plugin`. + files: ["eslint.config.ts", "eslint.shared.ts"], + rules: { "@typescript-eslint/no-deprecated": "off" }, + }, ...dataFileLintConfig, diff --git a/packages/archive-codec/eslint.config.ts b/packages/archive-codec/eslint.config.ts index fe5bfd9d9..27463683d 100644 --- a/packages/archive-codec/eslint.config.ts +++ b/packages/archive-codec/eslint.config.ts @@ -2,5 +2,15 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/no-shadow", + "@typescript-eslint/require-array-sort-compare", + "@typescript-eslint/strict-boolean-expressions", + "exadev/prefer-numeric-sort-compare", + ], isomorphic: true, + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own hand-rolled binary readers/writers genuinely mutate several array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", }); diff --git a/packages/byte-codec/eslint.config.ts b/packages/byte-codec/eslint.config.ts index 8551bfd38..be9e70815 100644 --- a/packages/byte-codec/eslint.config.ts +++ b/packages/byte-codec/eslint.config.ts @@ -2,6 +2,8 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: ["@typescript-eslint/strict-void-return"], // Off: with noUncheckedIndexedAccess on, every indexed read is typed as possibly-undefined, so this rule fires on array and byte-buffer indexing whose bound the surrounding code has already established -- a loop condition, a prior length check, or a fixture the test itself just built. None of the sites here is a value that can actually be absent. Tracked for a per-package decision on whether any of them is genuine; see the burn-down epic. nonNullAssertion: "off", isomorphic: true, diff --git a/packages/doc-codec/eslint.config.ts b/packages/doc-codec/eslint.config.ts index cfe7f8320..8a43461a8 100644 --- a/packages/doc-codec/eslint.config.ts +++ b/packages/doc-codec/eslint.config.ts @@ -2,7 +2,17 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/method-signature-style", + "@typescript-eslint/no-shadow", + "@typescript-eslint/strict-boolean-expressions", + "@typescript-eslint/strict-void-return", + "tsdoc/syntax", + ], isomorphic: true, + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own hand-rolled [MS-DOC] readers/writers genuinely mutate several array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", // Passed to the shared config rather than declared as a second rule block: flat config replaces a same-key rule instead of merging it, so a second no-restricted-imports here would silently switch the Worker-isomorphism Node-builtin ban back off while still reporting these. // // This package hand-parses [MS-DOC]'s binary structures against the published specification, the same bet markdown-codec makes against every markdown library and pdf-codec against pdf-lib. Depending on an existing .doc reader would defeat the reason it exists, so each one is banned by name -- every module of every library, not just its main entry point. diff --git a/packages/document-cli/eslint.config.ts b/packages/document-cli/eslint.config.ts index f975017e4..21599845d 100644 --- a/packages/document-cli/eslint.config.ts +++ b/packages/document-cli/eslint.config.ts @@ -4,8 +4,19 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default tseslint.config( ...packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/method-signature-style", + "@typescript-eslint/no-shadow", + "@typescript-eslint/promise-function-async", + "@typescript-eslint/strict-boolean-expressions", + "@typescript-eslint/switch-exhaustiveness-check", + ], // Off: with noUncheckedIndexedAccess on, every indexed read is typed as possibly-undefined, so this rule fires on array and byte-buffer indexing whose bound the surrounding code has already established -- a loop condition, a prior length check, or a fixture the test itself just built. None of the sites here is a value that can actually be absent. Tracked for a per-package decision on whether any of them is genuine; see the burn-down epic. nonNullAssertion: "off", + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own TUI state reducers and rendering helpers genuinely mutate several array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", // One program covering src and the config files alike, so there is no second tsconfig to route anything to. projects: ["./tsconfig.json"], // Runs under Node as a published binary, so Worker isomorphism does not apply. diff --git a/packages/document-compute.js/eslint.config.ts b/packages/document-compute.js/eslint.config.ts index fe5bfd9d9..1a4cbf3ea 100644 --- a/packages/document-compute.js/eslint.config.ts +++ b/packages/document-compute.js/eslint.config.ts @@ -2,5 +2,14 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/no-shadow", + "@typescript-eslint/switch-exhaustiveness-check", + "tsdoc/syntax", + ], isomorphic: true, + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own layout/compute passes genuinely mutate several array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", }); diff --git a/packages/document-mcp/eslint.config.ts b/packages/document-mcp/eslint.config.ts index 366687d1b..36f60eefb 100644 --- a/packages/document-mcp/eslint.config.ts +++ b/packages/document-mcp/eslint.config.ts @@ -2,6 +2,14 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/method-signature-style", + "@typescript-eslint/promise-function-async", + "@typescript-eslint/strict-void-return", + ], + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own request/response builders genuinely mutate a handful of array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", // One program covering src and the config files alike, so there is no second tsconfig to route anything to. projects: ["./tsconfig.json"], // Runs under Node as a published binary, so Worker isomorphism does not apply. diff --git a/packages/document-operations/eslint.config.ts b/packages/document-operations/eslint.config.ts index 9be1a90ea..0c401d323 100644 --- a/packages/document-operations/eslint.config.ts +++ b/packages/document-operations/eslint.config.ts @@ -2,8 +2,18 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/method-signature-style", + "@typescript-eslint/promise-function-async", + "@typescript-eslint/strict-void-return", + "tsdoc/syntax", + ], // One program covering src and the config files alike, so there is no second tsconfig to route anything to. projects: ["./tsconfig.json"], // Resolves document input by filesystem path via node:fs/promises (see src/io/document-input.ts) -- the same reason document-mcp, its one current consumer, is not held to Worker isomorphism either. isomorphic: false, + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own diagnostic/report builders genuinely mutate a handful of array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", }); diff --git a/packages/document-outline.js/eslint.config.ts b/packages/document-outline.js/eslint.config.ts index 37ddb8edd..d809477cd 100644 --- a/packages/document-outline.js/eslint.config.ts +++ b/packages/document-outline.js/eslint.config.ts @@ -4,9 +4,19 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default tseslint.config( ...packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/no-shadow", + "@typescript-eslint/no-use-before-define", + "@typescript-eslint/prefer-readonly", + "@typescript-eslint/require-array-sort-compare", + ], // Off: with noUncheckedIndexedAccess on, every indexed read is typed as possibly-undefined, so this rule fires on array and byte-buffer indexing whose bound the surrounding code has already established -- a loop condition, a prior length check, or a fixture the test itself just built. None of the sites here is a value that can actually be absent. Tracked for a per-package decision on whether any of them is genuine; see the burn-down epic. nonNullAssertion: "off", isomorphic: true, + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own graph/outline builders genuinely mutate several array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", }), { // no-pointless-reassignment reports `export const contentHashV1 = stableContentHash` in outline/graph.ts, and it is right that the two are the same function today. It is kept anyway: contentHashV1 is what this package publishes from its root barrel, named in the README's exports table, while stableContentHash is deliberately absent from that barrel (outline/hash is reachable only by subpath). Collapsing it would remove a public export and leave the root with no way to compute a graph node id, which is a deliberate API decision rather than a formatting cleanup. diff --git a/packages/document-rest/eslint.config.ts b/packages/document-rest/eslint.config.ts index 6a0b9467f..4765b3c84 100644 --- a/packages/document-rest/eslint.config.ts +++ b/packages/document-rest/eslint.config.ts @@ -2,9 +2,16 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/promise-function-async", + "@typescript-eslint/strict-void-return", + ], projects: ["./tsconfig.json"], // Binds a plain node:http listener (src/server.ts), the same reason document-mcp's own HTTP transport is not held to Worker isomorphism either. isomorphic: false, // dist-sea/ is this package's own SEA (single-executable application) bundle output -- a multi-megabyte, fully-dependency-inlined .cjs file (see tsdown.sea.shared.ts), not source, and linting it took over three minutes before this was added. additionalIgnores: ["dist-sea"], + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own request-handling helpers genuinely mutate a couple of array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", }); diff --git a/packages/document-schema.js/eslint.config.ts b/packages/document-schema.js/eslint.config.ts index d2e9022c4..e762b3294 100644 --- a/packages/document-schema.js/eslint.config.ts +++ b/packages/document-schema.js/eslint.config.ts @@ -2,9 +2,19 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/consistent-type-exports", + "@typescript-eslint/method-signature-style", + "@typescript-eslint/no-shadow", + "@typescript-eslint/no-use-before-define", + ], // Off: with noUncheckedIndexedAccess on, every indexed read is typed as possibly-undefined, so this rule fires on array and byte-buffer indexing whose bound the surrounding code has already established -- a loop condition, a prior length check, or a fixture the test itself just built. None of the sites here is a value that can actually be absent. Tracked for a per-package decision on whether any of them is genuine; see the burn-down epic. nonNullAssertion: "off", isomorphic: true, // scripts/ holds a standalone build step importing from ../dist, the same reason test/ is ignored. additionalIgnores: ["scripts"], + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own decompose/flatten tree builders genuinely mutate several array/object parameters in place (a stack push/pop, a scope's children array). Tracked for burn-down. + preferReadonlyParams: "off", }); diff --git a/packages/documents.js/eslint.config.ts b/packages/documents.js/eslint.config.ts index dc19f1242..c824f78ae 100644 --- a/packages/documents.js/eslint.config.ts +++ b/packages/documents.js/eslint.config.ts @@ -2,9 +2,22 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/method-signature-style", + "@typescript-eslint/no-shadow", + "@typescript-eslint/no-use-before-define", + "@typescript-eslint/promise-function-async", + "@typescript-eslint/strict-boolean-expressions", + "@typescript-eslint/strict-void-return", + "@typescript-eslint/switch-exhaustiveness-check", + ], // Off: with noUncheckedIndexedAccess on, every indexed read is typed as possibly-undefined, so this rule fires on array and byte-buffer indexing whose bound the surrounding code has already established -- a loop condition, a prior length check, or a fixture the test itself just built. None of the sites here is a value that can actually be absent. Tracked for a per-package decision on whether any of them is genuine; see the burn-down epic. nonNullAssertion: "off", isomorphic: true, // src/bin.ts is the launcher entry point: it spawns npx/pnpm/yarn/bunx, so it is Node-only by definition. It is executed, never imported into the isomorphic runtime, so exempting it leaves the importable surface pure -- and tsconfig.node.json already routes it to the Node program, so lint and typecheck agree. isomorphicExemptions: ["src/bin.ts"], + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package is the conversion engine's own orchestration layer, and its edit/convert/layout passes genuinely mutate a large number of array/object parameters in place; the single largest count of any package (327 sites). Tracked for burn-down. + preferReadonlyParams: "off", }); diff --git a/packages/epub-codec/eslint.config.ts b/packages/epub-codec/eslint.config.ts index c2354090e..8654531ff 100644 --- a/packages/epub-codec/eslint.config.ts +++ b/packages/epub-codec/eslint.config.ts @@ -4,7 +4,17 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default tseslint.config( ...packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/consistent-type-exports", + "@typescript-eslint/method-signature-style", + "@typescript-eslint/no-use-before-define", + "@typescript-eslint/strict-void-return", + ], isomorphic: true, + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own OCF/OPF/XHTML readers and writers genuinely mutate several array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", // This package hand-writes its own OCF/OPF/nav/XHTML mapping against fast-xml-parser and fflate directly, the same bet every sibling codec here makes against a heavyweight format library. Depending on an existing EPUB library would defeat the entire reason it exists as a hand-written, dependency-minimal codec -- see README Architecture for the archive-codec/byte-codec reuse decisions this package did make. additionalRestrictedImportPatterns: [ { diff --git a/packages/excel-number-format/eslint.config.ts b/packages/excel-number-format/eslint.config.ts index fe5bfd9d9..04b7012eb 100644 --- a/packages/excel-number-format/eslint.config.ts +++ b/packages/excel-number-format/eslint.config.ts @@ -2,5 +2,7 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: ["jsdoc/no-multi-asterisks", "tsdoc/syntax"], isomorphic: true, }); diff --git a/packages/markdown-codec/eslint.config.ts b/packages/markdown-codec/eslint.config.ts index 210c1565d..59ea1c1b6 100644 --- a/packages/markdown-codec/eslint.config.ts +++ b/packages/markdown-codec/eslint.config.ts @@ -2,11 +2,21 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/method-signature-style", + "@typescript-eslint/strict-boolean-expressions", + "@typescript-eslint/strict-void-return", + "@typescript-eslint/switch-exhaustiveness-check", + ], // Off: with noUncheckedIndexedAccess on, every indexed read is typed as possibly-undefined, so this rule fires on array and byte-buffer indexing whose bound the surrounding code has already established -- a loop condition, a prior length check, or a fixture the test itself just built. None of the sites here is a value that can actually be absent. Tracked for a per-package decision on whether any of them is genuine; see the burn-down epic. nonNullAssertion: "off", isomorphic: true, // scripts/ holds a standalone build step importing from ../dist, the same reason test/ is ignored. additionalIgnores: ["scripts"], + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own scanner/parser/emitter genuinely mutates several array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", // Passed to the shared config rather than declared here, because flat config replaces a same-key rule instead of merging it: a second no-restricted-imports over runtime src would silently drop the Worker-isomorphism Node-builtin ban while still reporting these. // // This package hand-writes its own CommonMark+GFM scanner, parser, and renderer, the same bet pdf-codec makes against pdf-lib and pdfjs-dist. Depending on any existing markdown library would defeat the entire reason it exists, so each one is banned by name rather than by guessing at specifiers -- every module of every library, not just its main entry point. diff --git a/packages/odf.js/eslint.config.ts b/packages/odf.js/eslint.config.ts index 583dfed7f..2ab706909 100644 --- a/packages/odf.js/eslint.config.ts +++ b/packages/odf.js/eslint.config.ts @@ -4,9 +4,22 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default tseslint.config( ...packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/method-signature-style", + "@typescript-eslint/no-shadow", + "@typescript-eslint/strict-boolean-expressions", + "@typescript-eslint/switch-exhaustiveness-check", + "exadev/no-object-assign", + "jsdoc/escape-inline-tags", + "tsdoc/syntax", + ], // Off: with noUncheckedIndexedAccess on, every indexed read is typed as possibly-undefined, so this rule fires on array and byte-buffer indexing whose bound the surrounding code has already established -- a loop condition, a prior length check, or a fixture the test itself just built. None of the sites here is a value that can actually be absent. Tracked for a per-package decision on whether any of them is genuine; see the burn-down epic. nonNullAssertion: "off", isomorphic: true, + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own ODF readers/writers genuinely mutate a large number of array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", }), { // fast-xml-parser@5 deprecates the whole XMLBuilder class, not one of its options, and ships no replacement of its own -- it points at a separate `fast-xml-builder` package that is not a declared dependency here. Swapping it is a real dependency decision with round-trip fidelity to re-verify (this builder is what keeps XML byte-faithful), so it is tracked rather than guessed at inside a tooling change. Scoped to the one module that constructs the builder. diff --git a/packages/ooxml.js/eslint.config.ts b/packages/ooxml.js/eslint.config.ts index 583dfed7f..271637719 100644 --- a/packages/ooxml.js/eslint.config.ts +++ b/packages/ooxml.js/eslint.config.ts @@ -4,9 +4,23 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default tseslint.config( ...packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/no-shadow", + "@typescript-eslint/no-use-before-define", + "@typescript-eslint/strict-boolean-expressions", + "@typescript-eslint/strict-void-return", + "@typescript-eslint/switch-exhaustiveness-check", + "exadev/no-object-assign", + "jsdoc/escape-inline-tags", + "tsdoc/syntax", + ], // Off: with noUncheckedIndexedAccess on, every indexed read is typed as possibly-undefined, so this rule fires on array and byte-buffer indexing whose bound the surrounding code has already established -- a loop condition, a prior length check, or a fixture the test itself just built. None of the sites here is a value that can actually be absent. Tracked for a per-package decision on whether any of them is genuine; see the burn-down epic. nonNullAssertion: "off", isomorphic: true, + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own OOXML readers/writers genuinely mutate a large number of array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", }), { // fast-xml-parser@5 deprecates the whole XMLBuilder class, not one of its options, and ships no replacement of its own -- it points at a separate `fast-xml-builder` package that is not a declared dependency here. Swapping it is a real dependency decision with round-trip fidelity to re-verify (this builder is what keeps XML byte-faithful), so it is tracked rather than guessed at inside a tooling change. Scoped to the one module that constructs the builder. diff --git a/packages/pdf-codec/eslint.config.ts b/packages/pdf-codec/eslint.config.ts index 9f35536d0..d36fe04ab 100644 --- a/packages/pdf-codec/eslint.config.ts +++ b/packages/pdf-codec/eslint.config.ts @@ -4,11 +4,23 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default tseslint.config( ...packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/method-signature-style", + "@typescript-eslint/no-shadow", + "@typescript-eslint/no-use-before-define", + "@typescript-eslint/prefer-readonly", + "@typescript-eslint/promise-function-async", + "@typescript-eslint/strict-void-return", + ], // Off: with noUncheckedIndexedAccess on, every indexed read is typed as possibly-undefined, so this rule fires on array and byte-buffer indexing whose bound the surrounding code has already established -- a loop condition, a prior length check, or a fixture the test itself just built. None of the sites here is a value that can actually be absent. Tracked for a per-package decision on whether any of them is genuine; see the burn-down epic. nonNullAssertion: "off", isomorphic: true, // scripts/ holds a standalone build step importing from ../dist, the same reason test/ is ignored. additionalIgnores: ["scripts"], + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own font/glyph-shaping and content-stream interpreters genuinely mutate a large number of array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", }), { // src/assets/ holds the vendored font binaries as generated TypeScript modules: nine files whose payload is one base64 string literal per face, the largest a single line of 1,077,633 characters. Prettier has nothing useful to do with a line like that and would spend real time deciding so, and no human edits these -- they are regenerated from the font files. diff --git a/packages/pdf-raster-cpu/eslint.config.ts b/packages/pdf-raster-cpu/eslint.config.ts index 7c763ba4b..0843feb39 100644 --- a/packages/pdf-raster-cpu/eslint.config.ts +++ b/packages/pdf-raster-cpu/eslint.config.ts @@ -3,5 +3,12 @@ import { packageLintConfig } from "../../eslint.shared.ts"; // The shared library-package config as-is: Worker-isomorphic (no node:* builtins in runtime src, proved at runtime by the workerd suite), the default single-barrel policy, and no-non-null-assertion enforced -- this package is new, so it starts with none of the indexed-access debt the older packages carry a burn-down exemption for. export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/no-use-before-define", + "@typescript-eslint/strict-void-return", + ], isomorphic: true, + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- src/stroke.ts's emitJoinWedge takes the caller's own output-polygon accumulator and pushes the join geometry it computes directly into it. Tracked for burn-down. + preferReadonlyParams: "off", }); diff --git a/packages/ppt-codec/eslint.config.ts b/packages/ppt-codec/eslint.config.ts index 4b9028686..df67f22f4 100644 --- a/packages/ppt-codec/eslint.config.ts +++ b/packages/ppt-codec/eslint.config.ts @@ -2,7 +2,19 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/no-use-before-define", + "@typescript-eslint/strict-boolean-expressions", + "@typescript-eslint/strict-void-return", + "@typescript-eslint/switch-exhaustiveness-check", + "exadev/no-mutable-union-array-param", + "tsdoc/syntax", + ], isomorphic: true, + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own [MS-PPT] record-tree reader/writer genuinely mutates several array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", // Passed to the shared config rather than declared in a second block, because flat config replaces a same-key rule instead of merging it: a second no-restricted-imports over runtime src would silently drop the Worker-isomorphism Node-builtin ban while still reporting these. // // This package hand-writes its own [MS-PPT] record-tree reader, the same bet every sibling codec here makes against a heavyweight format library. The compound-file container below it is the one piece deliberately not hand-written again: archive-codec already owns bounded [MS-CFB] reading for the family, so a second implementation of it here would be the duplication that package's own extraction exists to prevent. diff --git a/packages/rtf-codec/eslint.config.ts b/packages/rtf-codec/eslint.config.ts index a386bf4c9..958cb6d32 100644 --- a/packages/rtf-codec/eslint.config.ts +++ b/packages/rtf-codec/eslint.config.ts @@ -2,7 +2,19 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/no-shadow", + "@typescript-eslint/no-use-before-define", + "@typescript-eslint/prefer-readonly", + "@typescript-eslint/strict-void-return", + "@typescript-eslint/switch-exhaustiveness-check", + "exadev/no-mutable-union-array-param", + "exadev/no-object-assign", + ], isomorphic: true, + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own tokenizer/parser/writer genuinely mutates several array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", // Passed to the shared config rather than declared here, because flat config replaces a same-key rule instead of merging it: a second no-restricted-imports over runtime src would silently drop the Worker-isomorphism Node-builtin ban while still reporting these. // // RTF is tokenised plain text, not XML, so none of this family's existing XML plumbing applies and none of the JavaScript RTF libraries below could be reached for without defeating the reason this package exists at all -- the same hand-write bet markdown-codec makes against micromark/remark and pdf-codec makes against pdf-lib/pdfjs-dist. Each is banned by name rather than by guessing at specifiers, covering every module of every library rather than only its main entry point. diff --git a/packages/web/eslint.config.ts b/packages/web/eslint.config.ts index 1493d10d1..337513e09 100644 --- a/packages/web/eslint.config.ts +++ b/packages/web/eslint.config.ts @@ -8,6 +8,14 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default tseslint.config( ...packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/method-signature-style", + "@typescript-eslint/no-shadow", + "@typescript-eslint/promise-function-async", + "@typescript-eslint/strict-boolean-expressions", + ], // Off: with noUncheckedIndexedAccess on, every indexed read is typed as possibly-undefined, so this rule fires on array and byte-buffer indexing whose bound the surrounding code has already established -- a loop condition, a prior length check, or a fixture the test itself just built. None of the sites here is a value that can actually be absent. Tracked for a per-package decision on whether any of them is genuine; see the burn-down epic. nonNullAssertion: "off", // Three programs, not the usual two: the app, the browser Web Worker, and the Node-side config files. @@ -23,6 +31,8 @@ export default tseslint.config( ], // Not Worker-isomorphic: this is a browser app, and its own RPC import boundary below is what keeps the conversion engine out of the main bundle. isomorphic: false, + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this app's own UI state/reducer helpers genuinely mutate a handful of array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", // No npm exports map and no public entry point, so the rule's default stays right rather than being relaxed to 'single'. barrelPolicy: "banned", // Globals are scoped per layer below instead. Node globals everywhere would let a `process.env` read in browser code lint clean. diff --git a/packages/wpd-codec/eslint.config.ts b/packages/wpd-codec/eslint.config.ts index 1233f742d..48905136b 100644 --- a/packages/wpd-codec/eslint.config.ts +++ b/packages/wpd-codec/eslint.config.ts @@ -2,7 +2,15 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/no-shadow", + "@typescript-eslint/strict-boolean-expressions", + "@typescript-eslint/strict-void-return", + ], isomorphic: true, + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own WordPerfect prefix/function-code parser genuinely mutates a handful of array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", // Passed to the shared config rather than declared here, because flat config replaces a same-key rule instead of merging it: a second no-restricted-imports over runtime src would silently drop the Worker-isomorphism Node-builtin ban while still reporting these. // // This package hand-writes the WordPerfect prefix/function-code parser against Corel's own published File Format SDK, the same bet markdown-codec makes against micromark and pdf-codec makes against pdf-lib. The only existing readers for this family are native or another-language libraries (libwpd is LGPL C++, WP_Reader is C#), so depending on one would defeat both the reason this package exists and the family's MIT licensing. diff --git a/packages/xls-codec/eslint.config.ts b/packages/xls-codec/eslint.config.ts index d55796dac..29ed93949 100644 --- a/packages/xls-codec/eslint.config.ts +++ b/packages/xls-codec/eslint.config.ts @@ -2,7 +2,19 @@ import { packageLintConfig } from "../../eslint.shared.ts"; export default packageLintConfig({ tsconfigRootDir: import.meta.dirname, + // Off: 781 sites across every package are debt from this same @exadev/eslint-config 2.1.2->2.12.1 bump (see PackageLintOptions.newRuleDebt in eslint.shared.ts), not something this bump's own PR fixes. This package's own measured subset: + newRuleDebt: [ + "@typescript-eslint/consistent-return", + "@typescript-eslint/method-signature-style", + "@typescript-eslint/no-shadow", + "@typescript-eslint/no-use-before-define", + "@typescript-eslint/strict-boolean-expressions", + "@typescript-eslint/switch-exhaustiveness-check", + "tsdoc/syntax", + ], isomorphic: true, + // Off: see PackageLintOptions.preferReadonlyParams in eslint.shared.ts for why -- this package's own BIFF8 record readers/writers genuinely mutate a large number of array/object parameters in place. Tracked for burn-down. + preferReadonlyParams: "off", additionalRestrictedImportPatterns: [ { group: ["xlsx", "xlsx/**", "node-xlsx", "exceljs", "cfb", "cfb/**"], From 8589a2a2c71582c8398b72efa9dbd6a222ff9100 Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Mon, 14 Sep 2026 15:10:14 +0100 Subject: [PATCH 9/9] fix(ci): read every document in a multi-document pnpm lockfile for audit overrides pnpm writes pnpm-lock.yaml as a multi-document YAML stream once a project pins its own pnpm binary through packageManagerDependencies -- a self-management document listing the pinned build's own per-platform packages, ahead of the project's own document. This workspace now does exactly that (package.json's packageManager field, pnpm 12), so resolvedVersionsFromLockfileText's single-document parse started throwing YAMLParseError: Source contains multiple documents on every real run, taking the whole Audit job down with it. Reads every document in the stream and unions their packages maps instead of assuming there is exactly one: the self-management document's own entries are real resolved packages too, just never ones pnpm audit carries advisories against, so including them changes nothing about correctness and needs no guess about which document is "the real" project lockfile. --- .github/scripts/audit-autofix.test.ts | 24 ++++++++++------- .github/scripts/audit-autofix.ts | 39 +++++++++++++++------------ 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/.github/scripts/audit-autofix.test.ts b/.github/scripts/audit-autofix.test.ts index 91552572c..029e00fe8 100644 --- a/.github/scripts/audit-autofix.test.ts +++ b/.github/scripts/audit-autofix.test.ts @@ -29,11 +29,14 @@ function advisory( }; } +// The fixture's own minimumReleaseAge, named rather than repeated as a bare literal at every assertion that reads it back out of WORKSPACE below. +const FIXTURE_MINIMUM_RELEASE_AGE_MINUTES = 60; + // Shaped like this workspace's real pnpm-workspace.yaml: an explanatory comment attached to the `overrides` key itself, and a second comment attached to one entry inside the map. Those two comments live in different places and do not survive the same operations, which is the whole reason the fix path patches child keys. const WORKSPACE = `packages: - "packages/*" -minimumReleaseAge: 60 +minimumReleaseAge: ${String(FIXTURE_MINIMUM_RELEASE_AGE_MINUTES)} # fast-uri < 3.1.5 is vulnerable to host confusion via a backslash authority introducer (GHSA-hht2-r2mx-9j9m). A transitive devDependency, so only an override can force a patched version. overrides: @@ -92,7 +95,9 @@ describe("isAuditServiceError", () => { describe("minimumReleaseAgeMinutes", () => { it("reads the configured window as minutes, not days", () => { - expect(minimumReleaseAgeMinutes(WORKSPACE)).toBe(60); + expect(minimumReleaseAgeMinutes(WORKSPACE)).toBe( + FIXTURE_MINIMUM_RELEASE_AGE_MINUTES, + ); }); it("throws rather than defaulting when the key is absent", () => { @@ -267,7 +272,7 @@ describe("classifyAdvisories", () => { }); describe("override pruning", () => { - // Single-document, as this workspace's lockfile is, and with the multi-importer shape a thirteen-package workspace produces: `packages` is the union of every importer's resolutions, which is what an inertness check has to read. + // The project's own document within the real (multi-document, once pnpm pins its own binary through packageManagerDependencies) lockfile, with the multi-importer shape a many-package workspace produces: `packages` is the union of every importer's resolutions, which is what an inertness check has to read. const LOCKFILE = `lockfileVersion: '9.0' importers: @@ -298,12 +303,13 @@ packages: expect(resolved.get("@scope/pkg")).toEqual(new Set(["1.0.0"])); }); - it("throws on a multi-document lockfile rather than reading only the first document", () => { - expect(() => - resolvedVersionsFromLockfileText( - `---\npackages:\n pnpm@11.6.0: {}\n---\n${LOCKFILE}`, - ), - ).toThrow(/multiple documents/); + it("unions every document's packages map on a multi-document lockfile, not just the first", () => { + const resolved = resolvedVersionsFromLockfileText( + `packages:\n pnpm@11.6.0: {}\n---\n${LOCKFILE}`, + ); + expect(resolved.get("pnpm")).toEqual(new Set(["11.6.0"])); + expect(resolved.get("undici")).toEqual(new Set(["6.28.0", "7.29.0"])); + expect(resolved.get("@scope/pkg")).toEqual(new Set(["1.0.0"])); }); it("throws when the lockfile has no packages map", () => { diff --git a/.github/scripts/audit-autofix.ts b/.github/scripts/audit-autofix.ts index 0d8cca6a7..0a379e7ba 100644 --- a/.github/scripts/audit-autofix.ts +++ b/.github/scripts/audit-autofix.ts @@ -1,7 +1,7 @@ import { spawnSync } from "node:child_process"; import { pathToFileURL } from "node:url"; import { satisfies } from "semver"; -import { type Document, parse, parseDocument } from "yaml"; +import { type Document, parse, parseAllDocuments, parseDocument } from "yaml"; import { appendFileSync, readFileSync, writeFileSync } from "node:fs"; export interface AuditAdvisory { @@ -198,13 +198,13 @@ function restoreFromGit(): void { function setOutput(name: string, value: string): void { const outputFile = process.env.GITHUB_OUTPUT; - if (!outputFile) return; + if (outputFile === undefined || outputFile === "") return; appendFileSync(outputFile, `${name}=${value}\n`); } function appendSummary(markdown: string): void { const summaryFile = process.env.GITHUB_STEP_SUMMARY; - if (!summaryFile) return; + if (summaryFile === undefined || summaryFile === "") return; appendFileSync(summaryFile, markdown); } @@ -363,23 +363,28 @@ export function inertOverrideKeys( return inert; } -// The resolved package@version set from the lockfile's `packages` map, minus peer-dependency suffixes. This workspace's pnpm-lock.yaml is a single YAML document, so there is no stream to search: the multi-document form the reference implementation handled is what pnpm writes when a project pins its own pnpm binary through packageManagerDependencies, which adds a self-management lockfile as a document of its own ahead of the project's; this workspace pins pnpm through package.json's packageManager field alone and gets one document. `parse` is deliberate rather than incidental -- it throws outright on a multi-document source, so if that ever changes this fails loudly instead of silently reading whichever document happened to come first. The map is the union of every importer's resolutions, which is what an inertness check needs across thirteen packages. +// The resolved package@version set from the lockfile's `packages` map(s), minus peer-dependency suffixes. pnpm writes a multi-document lockfile once a project pins its own pnpm binary through packageManagerDependencies -- a self-management document listing the pinned pnpm build's own per-platform packages, ahead of the project's own document -- and this workspace does exactly that (package.json's packageManager field, pnpm 12), so the single-document assumption this function used to make no longer holds. Every document in the stream that carries a `packages` map has its entries unioned into the same result, rather than picking one: the self-management document's own entries (`@pnpm/exe.*` platform binaries) are real resolved packages too, just never ones `pnpm audit` has advisories against, so including them changes nothing about correctness and needs no guess about which document is "the real" project lockfile. A document with no `packages` map at all is skipped rather than treated as an error on its own; only a stream where no document anywhere has a packages map is an error, since that means the lockfile as a whole resolves nothing. The map is the union of every importer's resolutions, which is what an inertness check needs across every package in the workspace. export function resolvedVersionsFromLockfileText( yamlText: string, ): Map> { - const parsed: unknown = parse(yamlText); - if (!isRecord(parsed) || !isRecord(parsed.packages)) { - throw new Error("lockfile had no packages map"); - } const byPackage = new Map>(); - for (const key of Object.keys(parsed.packages)) { - const stripped = key.replace(/(\([^)]*\))+$/, ""); - const at = stripped.lastIndexOf("@"); - const pkg = stripped.slice(0, at); - const version = stripped.slice(at + 1); - const existing = byPackage.get(pkg) ?? new Set(); - existing.add(version); - byPackage.set(pkg, existing); + let sawPackagesMap = false; + for (const document of parseAllDocuments(yamlText)) { + const parsed: unknown = document.toJS(); + if (!isRecord(parsed) || !isRecord(parsed.packages)) continue; + sawPackagesMap = true; + for (const key of Object.keys(parsed.packages)) { + const stripped = key.replace(/(\([^)]*\))+$/, ""); + const at = stripped.lastIndexOf("@"); + const pkg = stripped.slice(0, at); + const version = stripped.slice(at + 1); + const existing = byPackage.get(pkg) ?? new Set(); + existing.add(version); + byPackage.set(pkg, existing); + } + } + if (!sawPackagesMap) { + throw new Error("lockfile had no packages map"); } return byPackage; } @@ -551,7 +556,7 @@ function main(): void { } if ( - process.argv[1] && + process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href ) { main();